use oxideav_core::CodecId;
pub fn from_sample_entry(fourcc: &[u8; 4]) -> CodecId {
let id = match fourcc {
b"mp4a" => "aac",
b"alac" => "alac",
b"fLaC" | b"flac" => "flac",
b"Opus" | b"opus" => "opus",
b"avc1" | b"avc3" => "h264",
b"hvc1" | b"hev1" => "h265",
b"vp08" => "vp8",
b"vp09" => "vp9",
b"av01" => "av1",
b"jpeg" | b"mjpa" | b"mjpb" => "mjpeg",
b"apco" | b"APCO" => "prores",
b"apcs" | b"APCS" => "prores",
b"apcn" | b"APCN" => "prores",
b"apch" | b"APCH" => "prores",
b"ap4h" | b"AP4H" => "prores",
b"ap4x" | b"AP4X" => "prores",
b"mp4v" => "mpeg4video",
b"s263" | b"h263" => "h263",
b"lpcm" | b"sowt" | b"twos" => "pcm_s16le",
other => {
let s = std::str::from_utf8(other).unwrap_or("????");
return CodecId::new(format!("mp4:{s}"));
}
};
CodecId::new(id)
}
pub fn from_sample_entry_with_oti(fourcc: &[u8; 4], oti: u8) -> CodecId {
match fourcc {
b"mp4a" => {
let id = match oti {
0x40 | 0x66 | 0x67 | 0x68 => "aac",
0x69 | 0x6B => "mp3",
_ => {
"aac"
}
};
CodecId::new(id)
}
b"mp4v" => {
let id = match oti {
0x6A => "mpeg1video",
0x60..=0x65 => "mpeg2video",
0x21 => "h264",
0x23 => "h265",
0x6C => "mjpeg",
_ => "mpeg4video",
};
CodecId::new(id)
}
_ => from_sample_entry(fourcc),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mp4a_default_is_aac() {
assert_eq!(from_sample_entry(b"mp4a"), CodecId::new("aac"));
}
#[test]
fn mp4a_with_aac_oti_is_aac() {
assert_eq!(
from_sample_entry_with_oti(b"mp4a", 0x40),
CodecId::new("aac")
);
}
#[test]
fn mp4a_with_mpeg1_audio_oti_is_mp3() {
assert_eq!(
from_sample_entry_with_oti(b"mp4a", 0x6B),
CodecId::new("mp3")
);
}
#[test]
fn mp4a_with_mpeg2_audio_oti_is_mp3() {
assert_eq!(
from_sample_entry_with_oti(b"mp4a", 0x69),
CodecId::new("mp3")
);
}
#[test]
fn mp4v_default_is_mpeg4video() {
assert_eq!(from_sample_entry(b"mp4v"), CodecId::new("mpeg4video"));
}
#[test]
fn mp4v_with_mpeg1_oti_is_mpeg1video() {
assert_eq!(
from_sample_entry_with_oti(b"mp4v", 0x6A),
CodecId::new("mpeg1video")
);
}
#[test]
fn mp4v_with_mpeg2_oti_is_mpeg2video() {
assert_eq!(
from_sample_entry_with_oti(b"mp4v", 0x61),
CodecId::new("mpeg2video")
);
}
#[test]
fn mp4v_with_part2_oti_is_mpeg4video() {
assert_eq!(
from_sample_entry_with_oti(b"mp4v", 0x20),
CodecId::new("mpeg4video")
);
}
#[test]
fn oti_is_ignored_for_non_mp4_fourccs() {
assert_eq!(
from_sample_entry_with_oti(b"avc1", 0x6A),
CodecId::new("h264")
);
}
#[test]
fn unknown_fourcc_preserves_fallback() {
let id = from_sample_entry(b"xyzw");
assert_eq!(id.as_str(), "mp4:xyzw");
}
#[test]
fn prores_fourccs_map_to_prores() {
for fc in [b"apco", b"apcs", b"apcn", b"apch", b"ap4h", b"ap4x"] {
assert_eq!(
from_sample_entry(fc),
CodecId::new("prores"),
"lower-case fourcc {fc:?}",
);
}
for fc in [b"APCO", b"APCS", b"APCN", b"APCH", b"AP4H", b"AP4X"] {
assert_eq!(
from_sample_entry(fc),
CodecId::new("prores"),
"upper-case fourcc {fc:?}",
);
}
}
#[test]
fn prores_fourccs_with_oti_still_map_to_prores() {
assert_eq!(
from_sample_entry_with_oti(b"ap4h", 0x42),
CodecId::new("prores")
);
}
}