Files
oak-editor/crates/oakcodec/src/exportcodec.rs
T
Mike-Solar 013a175707 refactor: workspace layout — crates/, app at root, legacy C++ removed
Single mechanical restructure commit:
- root Cargo.toml = oakapp bin + workspace; one cargo build produces
  oakapp, oak-cli, oak-worker, liboakengine.dylib
- app/rust/src -> src/ (app at repo root, no rust/ nesting)
- src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore;
  src/bindings/oakotio -> crates/oakotio; src/engine/rust ->
  crates/oakengine (keeps cdylib+staticlib+rlib)
- public C headers include/<mod>/ -> crates/oakengine/include/<mod>/
- OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone)
- legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/,
  app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets
  submodule, otio-install, all build-* output (~40GB)
- oakstorage kept but excluded from the workspace (skeleton w/ todos);
  gpui excluded (own workspace)
- verified: cargo build green, cargo test --workspace 1845/0
  (with the documented OCIO_RS_* env override for the homebrew OCIO)
2026-08-10 20:24:25 +08:00

202 lines
6.0 KiB
Rust

// 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 <http://www.gnu.org/licenses/>.
//! `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<Codec> {
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));
}
}