#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Av1DdInfo {
pub spatial_id: u8,
pub temporal_id: u8,
pub start_of_frame: bool,
pub end_of_frame: bool,
}
pub fn parse(bytes: &[u8]) -> Option<Av1DdInfo> {
let first = *bytes.first()?;
let start_of_frame = (first >> 7) & 1 == 1;
let end_of_frame = (first >> 6) & 1 == 1;
let template_id = first & 0x3F;
let (spatial_id, temporal_id) = if template_id < 9 {
(template_id / 3, template_id % 3)
} else {
(0, 0)
};
Some(Av1DdInfo {
spatial_id,
temporal_id,
start_of_frame,
end_of_frame,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn make_byte(sof: bool, eof: bool, template_id: u8) -> u8 {
let sof_bit = if sof { 1u8 << 7 } else { 0 };
let eof_bit = if eof { 1u8 << 6 } else { 0 };
sof_bit | eof_bit | (template_id & 0x3F)
}
#[test]
fn empty_returns_none() {
assert!(parse(&[]).is_none());
}
#[test]
fn base_layer_s0t0() {
let byte = make_byte(true, false, 0); let info = parse(&[byte]).unwrap();
assert_eq!(info.spatial_id, 0);
assert_eq!(info.temporal_id, 0);
assert!(info.start_of_frame);
assert!(!info.end_of_frame);
}
#[test]
fn s1t2_layer() {
let byte = make_byte(false, true, 5); let info = parse(&[byte]).unwrap();
assert_eq!(info.spatial_id, 1);
assert_eq!(info.temporal_id, 2);
assert!(!info.start_of_frame);
assert!(info.end_of_frame);
}
#[test]
fn s2t1_layer() {
let byte = make_byte(true, true, 7); let info = parse(&[byte]).unwrap();
assert_eq!(info.spatial_id, 2);
assert_eq!(info.temporal_id, 1);
}
#[test]
fn unknown_template_falls_back_to_base() {
let byte = make_byte(false, false, 63); let info = parse(&[byte]).unwrap();
assert_eq!(info.spatial_id, 0);
assert_eq!(info.temporal_id, 0);
}
#[test]
fn all_nine_l3t3_templates_map_correctly() {
let expected: [(u8, u8); 9] = [
(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2), ];
for (template_id, (exp_spatial, exp_temporal)) in expected.iter().enumerate() {
let byte = make_byte(false, false, template_id as u8);
let info = parse(&[byte]).unwrap();
assert_eq!(
info.spatial_id, *exp_spatial,
"template {template_id}: expected spatial {exp_spatial}, got {}",
info.spatial_id
);
assert_eq!(
info.temporal_id, *exp_temporal,
"template {template_id}: expected temporal {exp_temporal}, got {}",
info.temporal_id
);
}
}
#[test]
fn template_id_8_is_last_valid_l3t3() {
let byte8 = make_byte(false, false, 8);
let info8 = parse(&[byte8]).unwrap();
assert_eq!(
(info8.spatial_id, info8.temporal_id),
(2, 2),
"template 8 must map to S2T2"
);
let byte9 = make_byte(false, false, 9);
let info9 = parse(&[byte9]).unwrap();
assert_eq!(
(info9.spatial_id, info9.temporal_id),
(0, 0),
"template 9 is out of L3T3 range, must fall back to S0T0"
);
}
#[test]
fn multi_byte_payload_uses_only_first_byte() {
let payload = [make_byte(true, true, 4), 0xFF, 0xFF, 0xFF];
let info = parse(&payload).unwrap();
assert_eq!(info.spatial_id, 1);
assert_eq!(info.temporal_id, 1);
assert!(info.start_of_frame);
assert!(info.end_of_frame);
}
#[test]
fn template_id_63_highest_unknown_is_base() {
let byte = make_byte(true, false, 0x3F);
let info = parse(&[byte]).unwrap();
assert_eq!((info.spatial_id, info.temporal_id), (0, 0));
}
}