#![cfg(test)]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test modules may unwrap"
)]
use super::*;
struct BitWriter {
bytes: Vec<u8>,
cur: u8,
nbits: u8,
}
impl BitWriter {
fn new() -> Self {
Self {
bytes: Vec::new(),
cur: 0,
nbits: 0,
}
}
fn push_bit(&mut self, bit: u32) {
let bit_u8 = u8::from(bit & 1 == 1);
self.cur = (self.cur << 1) | bit_u8;
self.nbits += 1;
if self.nbits == 8 {
self.bytes.push(self.cur);
self.cur = 0;
self.nbits = 0;
}
}
fn write_bits(&mut self, value: u32, count: u32) {
for i in (0..count).rev() {
self.push_bit(value >> i);
}
}
fn write_ue(&mut self, value: u32) {
let code = value + 1;
let len = u32::BITS - code.leading_zeros();
for _ in 0..(len - 1) {
self.push_bit(0);
}
self.write_bits(code, len);
}
fn write_se(&mut self, value: i32) {
let magnitude = value.unsigned_abs();
let code = if value <= 0 {
magnitude * 2
} else {
magnitude * 2 - 1
};
self.write_ue(code);
}
fn finish(mut self) -> Vec<u8> {
while self.nbits != 0 {
self.push_bit(0);
}
self.bytes
}
}
fn baseline_sps(log2_max_frame_num: u32, pic_order_cnt_type: u32) -> Sps {
Sps {
profile_idc: 66,
constraint_flags: 0,
level_idc: 30,
seq_parameter_set_id: 0,
width: 16,
height: 16,
frame_mbs_only: true,
chroma_format_idc: 1,
log2_max_frame_num,
pic_order_cnt_type,
log2_max_pic_order_cnt_lsb: 4,
pic_width_in_mbs: 1,
pic_height_in_mbs: 1,
}
}
fn baseline_pps(deblocking_filter_control_present: bool) -> Pps {
Pps {
pic_parameter_set_id: 0,
seq_parameter_set_id: 0,
entropy_coding_mode: false,
num_ref_idx_l0_default_active: 1,
num_ref_idx_l1_default_active: 1,
pic_init_qp: 26,
chroma_qp_index_offset: 0,
deblocking_filter_control_present,
constrained_intra_pred: false,
}
}
#[test]
fn parse_extracts_idr_i_slice_fields_and_stops_at_slice_data() {
let sps = baseline_sps(4, 0);
let pps = baseline_pps(false);
let mut writer = BitWriter::new();
writer.write_ue(0); writer.write_ue(2); writer.write_ue(0); writer.write_bits(5, 4); writer.write_ue(7); writer.write_bits(3, 4); writer.push_bit(0); writer.push_bit(1); writer.write_se(-2); writer.write_bits(0b101, 3);
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
let header = SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::IdrSlice, 1).unwrap();
assert_eq!(header.first_mb_in_slice, 0);
assert_eq!(header.slice_type, SliceType::I);
assert_eq!(header.pic_parameter_set_id, 0);
assert_eq!(header.frame_num, 5);
assert_eq!(header.idr_pic_id, Some(7));
assert_eq!(header.pic_order_cnt_lsb, Some(3));
assert_eq!(header.slice_qp_delta, -2);
assert_eq!(reader.read_bits(3).unwrap(), 0b101);
}
#[test]
fn parse_rejects_p_slice_type() {
let sps = baseline_sps(4, 0);
let pps = baseline_pps(false);
let mut writer = BitWriter::new();
writer.write_ue(0); writer.write_ue(0); let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
assert_eq!(
SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::NonIdrSlice, 1),
Err(H264Error::UnsupportedSliceType)
);
}
#[test]
fn parse_rejects_non_zero_pic_order_cnt_type_before_reading_bits() {
let sps = baseline_sps(4, 1);
let pps = baseline_pps(false);
let mut reader = BitReader::new(&[]);
assert_eq!(
SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::IdrSlice, 1),
Err(H264Error::UnsupportedPicOrderCntType)
);
}
#[test]
fn parse_rejects_field_coded_pictures_before_reading_bits() {
let mut sps = baseline_sps(4, 0);
sps.frame_mbs_only = false;
let pps = baseline_pps(false);
let mut reader = BitReader::new(&[]);
assert_eq!(
SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::IdrSlice, 1),
Err(H264Error::UnsupportedFieldCoding)
);
}
#[test]
fn parse_consumes_adaptive_ref_pic_marking_loop_for_non_idr_ref_slice() {
let sps = baseline_sps(4, 0);
let pps = baseline_pps(false);
let mut writer = BitWriter::new();
writer.write_ue(0); writer.write_ue(7); writer.write_ue(0); writer.write_bits(2, 4); writer.write_bits(1, 4); writer.push_bit(1); writer.write_ue(1); writer.write_ue(0); writer.write_ue(0); writer.write_se(0);
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
let header = SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::NonIdrSlice, 2).unwrap();
assert_eq!(header.slice_type, SliceType::I);
assert_eq!(header.frame_num, 2);
assert_eq!(header.idr_pic_id, None);
assert_eq!(header.pic_order_cnt_lsb, Some(1));
assert_eq!(header.slice_qp_delta, 0);
}
#[test]
fn parse_consumes_deblocking_offsets_when_filter_not_disabled() {
let sps = baseline_sps(4, 0);
let pps = baseline_pps(true);
let mut writer = BitWriter::new();
writer.write_ue(0); writer.write_ue(2); writer.write_ue(0); writer.write_bits(0, 4); writer.write_bits(0, 4); writer.write_se(1); writer.write_ue(0); writer.write_se(2); writer.write_se(-1); writer.write_bits(0b11, 2);
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
let header = SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::NonIdrSlice, 0).unwrap();
assert_eq!(header.slice_qp_delta, 1);
assert_eq!(reader.read_bits(2).unwrap(), 0b11);
}
#[test]
fn parse_skips_deblocking_offsets_when_filter_disabled() {
let sps = baseline_sps(4, 0);
let pps = baseline_pps(true);
let mut writer = BitWriter::new();
writer.write_ue(0); writer.write_ue(2); writer.write_ue(0); writer.write_bits(0, 4); writer.write_bits(0, 4); writer.write_se(0); writer.write_ue(1); writer.write_bits(0b10, 2);
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
let header = SliceHeader::parse(&mut reader, &sps, &pps, NalUnitType::NonIdrSlice, 0).unwrap();
assert_eq!(header.slice_qp_delta, 0);
assert_eq!(reader.read_bits(2).unwrap(), 0b10);
}