use crate::pixel_format::{Av1SequenceHeader, H264SpsInfo, HevcSpsInfo};
pub fn av1_codec_string(h: &Av1SequenceHeader) -> String {
let tier_char = if h.seq_tier_0 == 0 { 'M' } else { 'H' };
let at_defaults = !h.monochrome
&& h.color_primaries == 1
&& h.transfer_characteristics == 1
&& h.matrix_coefficients == 1
&& !h.color_range;
if at_defaults {
format!(
"av01.{}.{:02}{}.{:02}",
h.seq_profile, h.seq_level_idx_0, tier_char, h.bit_depth,
)
} else {
format!(
"av01.{}.{:02}{}.{:02}.{}.{:03}.{:03}.{:03}.{}",
h.seq_profile,
h.seq_level_idx_0,
tier_char,
h.bit_depth,
u8::from(h.monochrome),
h.color_primaries,
h.transfer_characteristics,
h.matrix_coefficients,
u8::from(h.color_range),
)
}
}
pub fn avc_codec_string(fourcc: &str, sps: &H264SpsInfo) -> String {
format!(
"{}.{:02X}{:02X}{:02X}",
fourcc, sps.profile_idc, sps.constraint_set_flags, sps.level_idc
)
}
pub fn hevc_codec_string(fourcc: &str, sps: &HevcSpsInfo) -> String {
let space = match sps.general_profile_space {
0 => String::new(),
n => ((b'A' + n - 1) as char).to_string(),
};
let compat = sps.profile_compatibility_flags.reverse_bits();
let tier = if sps.tier_flag { 'H' } else { 'L' };
let bytes = [
(sps.general_constraint_flags >> 40) as u8,
(sps.general_constraint_flags >> 32) as u8,
(sps.general_constraint_flags >> 24) as u8,
(sps.general_constraint_flags >> 16) as u8,
(sps.general_constraint_flags >> 8) as u8,
sps.general_constraint_flags as u8,
];
let mut cons = String::new();
if let Some(end) = bytes.iter().rposition(|&b| b != 0) {
for b in &bytes[..=end] {
cons.push_str(&format!(".{b:02X}"));
}
}
format!(
"{}.{}{}.{:X}.{}{}{}",
fourcc, space, sps.profile_idc, compat, tier, sps.level_idc, cons
)
}
pub const AAC_LC_CODEC_STRING: &str = "mp4a.40.2";
pub fn hls_codecs_attribute(video: &str, audio: &str) -> String {
format!("{video},{audio}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn avc_codec_string_high_l40() {
let sps = H264SpsInfo {
profile_idc: 100, constraint_set_flags: 0x00,
level_idc: 40, ..Default::default()
};
assert_eq!(avc_codec_string("avc1", &sps), "avc1.640028");
assert_eq!(avc_codec_string("avc3", &sps), "avc3.640028");
}
#[test]
fn avc_codec_string_baseline_constrained() {
let sps = H264SpsInfo {
profile_idc: 66, constraint_set_flags: 0xC0, level_idc: 30, ..Default::default()
};
assert_eq!(avc_codec_string("avc1", &sps), "avc1.42C01E");
}
#[test]
fn hevc_codec_string_main_l31() {
let sps = HevcSpsInfo {
general_profile_space: 0,
profile_idc: 1,
profile_compatibility_flags: 0x6000_0000, tier_flag: false,
level_idc: 93,
general_constraint_flags: 0xB000_0000_0000, ..Default::default()
};
assert_eq!(hevc_codec_string("hvc1", &sps), "hvc1.1.6.L93.B0");
assert_eq!(hevc_codec_string("hev1", &sps), "hev1.1.6.L93.B0");
}
#[test]
fn hevc_codec_string_main10_high_tier_no_constraints() {
let sps = HevcSpsInfo {
general_profile_space: 0,
profile_idc: 2, profile_compatibility_flags: 0x2000_0000, tier_flag: true, level_idc: 120, general_constraint_flags: 0, ..Default::default()
};
assert_eq!(hevc_codec_string("hvc1", &sps), "hvc1.2.4.H120");
}
fn synth_seq_header(
seq_profile: u8,
seq_level_idx_0: u8,
seq_tier_0: u8,
bit_depth: u8,
monochrome: bool,
color_primaries: u8,
transfer_characteristics: u8,
matrix_coefficients: u8,
color_range: bool,
) -> Av1SequenceHeader {
Av1SequenceHeader {
seq_profile,
still_picture: false,
reduced_still_picture_header: false,
max_frame_width_minus1: 0,
max_frame_height_minus1: 0,
seq_level_idx_0,
seq_tier_0,
bit_depth,
monochrome,
color_primaries,
transfer_characteristics,
matrix_coefficients,
color_range,
chroma_subsampling_x: true,
chroma_subsampling_y: true,
film_grain_params_present: false,
enable_filter_intra: false,
enable_intra_edge_filter: false,
enable_interintra_compound: false,
enable_masked_compound: false,
enable_warped_motion: false,
enable_dual_filter: false,
enable_order_hint: false,
enable_jnt_comp: false,
enable_ref_frame_mvs: false,
enable_superres: false,
enable_cdef: false,
enable_restoration: false,
order_hint_bits: 0,
seq_force_screen_content_tools: 0,
seq_force_integer_mv: 0,
frame_width_bits_minus_1: 0,
frame_height_bits_minus_1: 0,
use_128x128_superblock: false,
separate_uv_delta_q: false,
}
}
#[test]
fn av1_string_short_form_at_bt709_defaults() {
let h = synth_seq_header(0, 8, 0, 8, false, 1, 1, 1, false);
assert_eq!(av1_codec_string(&h), "av01.0.08M.08");
}
#[test]
fn av1_string_high_tier_renders_h_character() {
let h = synth_seq_header(0, 16, 1, 10, false, 9, 16, 9, false);
assert_eq!(av1_codec_string(&h), "av01.0.16H.10.0.009.016.009.0");
}
#[test]
fn av1_string_hdr10_bt2020_pq_full_range() {
let h = synth_seq_header(0, 12, 0, 10, false, 9, 16, 9, true);
assert_eq!(av1_codec_string(&h), "av01.0.12M.10.0.009.016.009.1");
}
#[test]
fn av1_string_monochrome_uses_long_form() {
let h = synth_seq_header(0, 8, 0, 8, true, 1, 1, 1, false);
assert_eq!(av1_codec_string(&h), "av01.0.08M.08.1.001.001.001.0");
}
#[test]
fn av1_string_full_range_at_8bit_bt709_uses_long_form() {
let h = synth_seq_header(0, 8, 0, 8, false, 1, 1, 1, true);
assert_eq!(av1_codec_string(&h), "av01.0.08M.08.0.001.001.001.1");
}
#[test]
fn av1_string_two_digit_level_padding() {
let h = synth_seq_header(0, 0, 0, 8, false, 1, 1, 1, false);
let s = av1_codec_string(&h);
assert!(s.starts_with("av01.0.00M."), "got: {s}");
}
#[test]
fn av1_string_two_digit_bit_depth_padding() {
let h_8 = synth_seq_header(0, 8, 0, 8, false, 1, 1, 1, false);
let h_10 = synth_seq_header(0, 8, 0, 10, false, 1, 1, 1, false);
let h_12 = synth_seq_header(2, 8, 0, 12, false, 1, 1, 1, false);
assert_eq!(av1_codec_string(&h_8), "av01.0.08M.08");
assert_eq!(av1_codec_string(&h_10), "av01.0.08M.10");
assert_eq!(av1_codec_string(&h_12), "av01.2.08M.12");
}
#[test]
fn aac_lc_constant_is_canonical() {
assert_eq!(AAC_LC_CODEC_STRING, "mp4a.40.2");
}
#[test]
fn hls_codecs_attribute_concatenates_video_then_audio() {
let s = hls_codecs_attribute("av01.0.08M.08.0.001.001.001.0", AAC_LC_CODEC_STRING);
assert_eq!(s, "av01.0.08M.08.0.001.001.001.0,mp4a.40.2");
}
}