#![allow(clippy::unwrap_used, clippy::expect_used, reason = "unit tests")]
use super::parse_sequence_header;
use crate::DecodeError;
#[derive(Default)]
struct BitWriter {
bytes: Vec<u8>,
bit_pos: usize,
}
impl BitWriter {
fn write_bit(&mut self, bit: u32) {
let byte_index = self.bit_pos / 8;
if byte_index >= self.bytes.len() {
self.bytes.push(0);
}
if bit != 0 {
let shift = 7 - (self.bit_pos % 8);
self.bytes[byte_index] |= 1 << shift;
}
self.bit_pos += 1;
}
fn write_bits(&mut self, value: u32, count: u32) {
for i in (0..count).rev() {
self.write_bit((value >> i) & 1);
}
}
fn finish(self) -> Vec<u8> {
self.bytes
}
}
struct Ov {
seq_profile: u32,
reduced_still_picture_header: u32,
timing_info_present_flag: u32,
initial_display_delay_present_flag: u32,
operating_points_cnt_minus_1: u32,
frame_id_numbers_present_flag: u32,
seq_force_screen_content_tools: u32,
enable_superres: u32,
enable_cdef: u32,
enable_restoration: u32,
high_bitdepth: u32,
mono_chrome: u32,
color_description_present_flag: u32,
color_primaries: u32,
transfer_characteristics: u32,
matrix_coefficients: u32,
film_grain_params_present: u32,
}
impl Default for Ov {
fn default() -> Self {
Self {
seq_profile: 0,
reduced_still_picture_header: 0,
timing_info_present_flag: 0,
initial_display_delay_present_flag: 0,
operating_points_cnt_minus_1: 0,
frame_id_numbers_present_flag: 0,
seq_force_screen_content_tools: 0,
enable_superres: 0,
enable_cdef: 0,
enable_restoration: 0,
high_bitdepth: 0,
mono_chrome: 0,
color_description_present_flag: 0,
color_primaries: 2,
transfer_characteristics: 2,
matrix_coefficients: 2,
film_grain_params_present: 0,
}
}
}
const WIDTH: u32 = 352;
const HEIGHT: u32 = 288;
fn build(ov: &Ov) -> Vec<u8> {
let mut w = BitWriter::default();
w.write_bits(ov.seq_profile, 3);
w.write_bit(0); w.write_bit(ov.reduced_still_picture_header);
w.write_bit(ov.timing_info_present_flag);
w.write_bit(ov.initial_display_delay_present_flag);
w.write_bits(ov.operating_points_cnt_minus_1, 5);
w.write_bits(0, 12); w.write_bits(0, 5);
let width_bits = 32 - (WIDTH - 1).leading_zeros();
let height_bits = 32 - (HEIGHT - 1).leading_zeros();
w.write_bits(width_bits - 1, 4);
w.write_bits(height_bits - 1, 4);
w.write_bits(WIDTH - 1, width_bits);
w.write_bits(HEIGHT - 1, height_bits);
w.write_bit(ov.frame_id_numbers_present_flag);
w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bit(0); w.write_bits(ov.seq_force_screen_content_tools, 1);
w.write_bit(ov.enable_superres);
w.write_bit(ov.enable_cdef);
w.write_bit(ov.enable_restoration);
w.write_bit(ov.high_bitdepth);
w.write_bit(ov.mono_chrome);
w.write_bit(ov.color_description_present_flag);
if ov.color_description_present_flag != 0 {
w.write_bits(ov.color_primaries, 8);
w.write_bits(ov.transfer_characteristics, 8);
w.write_bits(ov.matrix_coefficients, 8);
}
let is_identity_branch =
ov.color_primaries == 1 && ov.transfer_characteristics == 13 && ov.matrix_coefficients == 0;
if !is_identity_branch {
w.write_bit(0); w.write_bits(0, 2); }
w.write_bit(0);
w.write_bit(ov.film_grain_params_present);
w.finish()
}
#[test]
fn accepts_the_encoder_shaped_fixture() {
let bytes = build(&Ov::default());
let sh = parse_sequence_header(&bytes).unwrap();
assert_eq!(sh.max_frame_width, WIDTH);
assert_eq!(sh.max_frame_height, HEIGHT);
assert_eq!(sh.order_hint_bits, 0);
assert!(!sh.enable_order_hint);
assert!(!sh.separate_uv_delta_q);
}
#[test]
fn rejects_non_main_profile() {
let bytes = build(&Ov {
seq_profile: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_reduced_still_picture_header() {
let bytes = build(&Ov {
reduced_still_picture_header: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_timing_info_present_flag() {
let bytes = build(&Ov {
timing_info_present_flag: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_initial_display_delay_present_flag() {
let bytes = build(&Ov {
initial_display_delay_present_flag: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_multiple_operating_points() {
let bytes = build(&Ov {
operating_points_cnt_minus_1: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_frame_id_numbers_present_flag() {
let bytes = build(&Ov {
frame_id_numbers_present_flag: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_screen_content_tools() {
let bytes = build(&Ov {
seq_force_screen_content_tools: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_enable_superres() {
let bytes = build(&Ov {
enable_superres: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_enable_cdef() {
let bytes = build(&Ov {
enable_cdef: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_enable_restoration() {
let bytes = build(&Ov {
enable_restoration: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_high_bitdepth() {
let bytes = build(&Ov {
high_bitdepth: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_mono_chrome() {
let bytes = build(&Ov {
mono_chrome: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_non_4_2_0_subsampling_via_identity_matrix_branch() {
let bytes = build(&Ov {
color_description_present_flag: 1,
color_primaries: 1,
transfer_characteristics: 13,
matrix_coefficients: 0,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_film_grain_params_present() {
let bytes = build(&Ov {
film_grain_params_present: 1,
..Ov::default()
});
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::Unsupported)
));
}
#[test]
fn rejects_truncated_input() {
let bytes = [0u8; 1];
assert!(matches!(
parse_sequence_header(&bytes),
Err(DecodeError::InvalidInput)
));
}