// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! `olive::ExportCodec` — the export codec enum and name table.
//!
//! Mirrors `src/codec/src/exportcodec.h`. The enum is the raw int contract
//! the C ABI documents (oakengine/encoding.h); values must NOT be
//! reordered (they are used in serialized files).
/// `olive::ExportCodec::Codec`. Only append (never insert/reorder); the
/// integer values are part of the file format.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum Codec {
/// Apple ProRes.
DNxHD = 0,
/// H.264.
H264 = 1,
/// H.264 with RGB color.
H264RGB = 2,
/// H.265/HEVC.
H265 = 3,
/// OpenEXR.
OpenEXR = 4,
/// PNG.
PNG = 5,
/// Apple ProRes.
ProRes = 6,
/// CineForm.
CineForm = 7,
/// TIFF.
TIFF = 8,
/// VP9.
VP9 = 9,
/// MPEG-2.
MP2 = 10,
/// MPEG-3.
MP3 = 11,
/// AAC.
AAC = 12,
/// Uncompressed PCM.
PCM = 13,
/// Opus.
Opus = 14,
/// Vorbis.
Vorbis = 15,
/// FLAC.
FLAC = 16,
/// SubRip subtitles.
SRT = 17,
/// AV1.
AV1 = 18,
/// Sentinel count (not a real codec).
Count = 19,
}
impl Codec {
/// Convert an `i32` code to a [`Codec`]; `None` outside the valid
/// `0..=18` range (incl. `Count`).
pub fn from_i32(v: i32) -> Option {
match v {
0 => Some(Codec::DNxHD),
1 => Some(Codec::H264),
2 => Some(Codec::H264RGB),
3 => Some(Codec::H265),
4 => Some(Codec::OpenEXR),
5 => Some(Codec::PNG),
6 => Some(Codec::ProRes),
7 => Some(Codec::CineForm),
8 => Some(Codec::TIFF),
9 => Some(Codec::VP9),
10 => Some(Codec::MP2),
11 => Some(Codec::MP3),
12 => Some(Codec::AAC),
13 => Some(Codec::PCM),
14 => Some(Codec::Opus),
15 => Some(Codec::Vorbis),
16 => Some(Codec::FLAC),
17 => Some(Codec::SRT),
18 => Some(Codec::AV1),
_ => None,
}
}
/// Human-readable name for a codec.
///
/// CPP-PARITY: `src/codec/src/exportcodec.cpp` `get_codec_name`; unknown /
/// `Count` returns "Unknown".
pub fn get_codec_name(c: Codec) -> String {
match c {
Codec::DNxHD => "DNxHD".to_string(),
Codec::H264 => "H.264".to_string(),
Codec::H264RGB => "H.264 RGB".to_string(),
Codec::H265 => "H.265".to_string(),
Codec::OpenEXR => "OpenEXR".to_string(),
Codec::PNG => "PNG".to_string(),
Codec::ProRes => "ProRes".to_string(),
Codec::CineForm => "Cineform".to_string(),
Codec::TIFF => "TIFF".to_string(),
Codec::VP9 => "VP9".to_string(),
Codec::MP2 => "MP2".to_string(),
Codec::MP3 => "MP3".to_string(),
Codec::AAC => "AAC".to_string(),
Codec::PCM => "PCM (Uncompressed)".to_string(),
Codec::Opus => "Opus".to_string(),
Codec::Vorbis => "Vorbis".to_string(),
Codec::FLAC => "FLAC".to_string(),
Codec::SRT => "SubRip SRT".to_string(),
Codec::AV1 => "AV1".to_string(),
Codec::Count => "Unknown".to_string(),
}
}
/// Whether the codec produces a still image.
///
/// CPP-PARITY: `src/codec/src/exportcodec.cpp` `is_codec_a_still_image`;
/// OpenEXR / PNG / TIFF are still images, everything else is not.
pub fn is_codec_a_still_image(c: Codec) -> bool {
match c {
Codec::OpenEXR | Codec::PNG | Codec::TIFF => true,
_ => false,
}
}
/// Whether the codec is lossless.
///
/// CPP-PARITY: `src/codec/src/exportcodec.cpp` `is_codec_lossless`;
/// only PCM and FLAC are lossless.
pub fn is_codec_lossless(c: Codec) -> bool {
match c {
Codec::PCM | Codec::FLAC => true,
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_i32_maps_valid_and_rejects_others() {
for v in 0..=18 {
assert!(Codec::from_i32(v).is_some(), "codec {v}");
}
assert!(Codec::from_i32(19).is_none()); // Count
assert!(Codec::from_i32(-1).is_none());
assert!(Codec::from_i32(99).is_none());
}
#[test]
fn codec_name_mapping() {
assert_eq!(Codec::get_codec_name(Codec::DNxHD), "DNxHD");
assert_eq!(Codec::get_codec_name(Codec::H264), "H.264");
assert_eq!(Codec::get_codec_name(Codec::H264RGB), "H.264 RGB");
assert_eq!(Codec::get_codec_name(Codec::H265), "H.265");
assert_eq!(Codec::get_codec_name(Codec::OpenEXR), "OpenEXR");
assert_eq!(Codec::get_codec_name(Codec::PNG), "PNG");
assert_eq!(Codec::get_codec_name(Codec::ProRes), "ProRes");
assert_eq!(Codec::get_codec_name(Codec::CineForm), "Cineform");
assert_eq!(Codec::get_codec_name(Codec::TIFF), "TIFF");
assert_eq!(Codec::get_codec_name(Codec::VP9), "VP9");
assert_eq!(Codec::get_codec_name(Codec::MP2), "MP2");
assert_eq!(Codec::get_codec_name(Codec::MP3), "MP3");
assert_eq!(Codec::get_codec_name(Codec::AAC), "AAC");
assert_eq!(Codec::get_codec_name(Codec::PCM), "PCM (Uncompressed)");
assert_eq!(Codec::get_codec_name(Codec::Opus), "Opus");
assert_eq!(Codec::get_codec_name(Codec::Vorbis), "Vorbis");
assert_eq!(Codec::get_codec_name(Codec::FLAC), "FLAC");
assert_eq!(Codec::get_codec_name(Codec::SRT), "SubRip SRT");
assert_eq!(Codec::get_codec_name(Codec::AV1), "AV1");
assert_eq!(Codec::get_codec_name(Codec::Count), "Unknown");
}
#[test]
fn still_image_and_lossless_flags() {
assert!(Codec::is_codec_a_still_image(Codec::OpenEXR));
assert!(Codec::is_codec_a_still_image(Codec::PNG));
assert!(Codec::is_codec_a_still_image(Codec::TIFF));
assert!(!Codec::is_codec_a_still_image(Codec::H264));
assert!(!Codec::is_codec_a_still_image(Codec::PCM));
assert!(Codec::is_codec_lossless(Codec::PCM));
assert!(Codec::is_codec_lossless(Codec::FLAC));
assert!(!Codec::is_codec_lossless(Codec::MP3));
assert!(!Codec::is_codec_lossless(Codec::H264));
}
}