#![cfg(test)]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test modules may unwrap"
)]
use super::*;
#[derive(Default)]
struct BitWriter {
bits: Vec<u8>,
}
impl BitWriter {
fn push_bit(&mut self, bit: u8) {
self.bits.push(bit & 1);
}
fn push_bits(&mut self, value: u32, count: u32) {
for i in (0..count).rev() {
self.push_bit(((value >> i) & 1) as u8);
}
}
fn push_ue(&mut self, value: u32) {
let value_plus1 = value + 1;
let num_bits = 32 - value_plus1.leading_zeros();
for _ in 0..num_bits - 1 {
self.push_bit(0);
}
self.push_bits(value_plus1, num_bits);
}
fn push_se(&mut self, value: i32) {
#[allow(
clippy::cast_sign_loss,
reason = "magnitude fits u32 for the small test values used here"
)]
let code = if value > 0 {
2 * value as u32 - 1
} else {
(-value) as u32 * 2
};
self.push_ue(code);
}
fn into_bytes(self) -> Vec<u8> {
let mut out = vec![0u8; self.bits.len().div_ceil(8)];
for (i, bit) in self.bits.iter().enumerate() {
if *bit != 0 {
out[i / 8] |= 1 << (7 - (i % 8));
}
}
out
}
}
#[allow(
clippy::struct_field_names,
reason = "every field is the real ITU-T H.265 § 7.3.2.3.1 syntax element name (all of which \
happen to end in _flag) — renaming to avoid the shared suffix would obscure the \
1:1 spec mapping this test module relies on for review"
)]
struct PpsKnobs {
tiles_enabled_flag: u8,
entropy_coding_sync_enabled_flag: u8,
deblocking_filter_control_present_flag: u8,
pps_scaling_list_data_present_flag: u8,
lists_modification_present_flag: u8,
slice_segment_header_extension_present_flag: u8,
pps_extension_present_flag: u8,
}
impl Default for PpsKnobs {
fn default() -> Self {
Self {
tiles_enabled_flag: 0,
entropy_coding_sync_enabled_flag: 0,
deblocking_filter_control_present_flag: 0,
pps_scaling_list_data_present_flag: 0,
lists_modification_present_flag: 1,
slice_segment_header_extension_present_flag: 0,
pps_extension_present_flag: 0,
}
}
}
fn pps_rbsp(knobs: &PpsKnobs) -> Vec<u8> {
let mut w = BitWriter::default();
w.push_ue(0); w.push_ue(0); w.push_bit(0); w.push_bit(1); w.push_bits(2, 3); w.push_bit(0); w.push_bit(1); w.push_ue(0); w.push_ue(0); w.push_se(-3); w.push_bit(1); w.push_bit(0); w.push_bit(0); w.push_se(1); w.push_se(-1); w.push_bit(1); w.push_bit(0); w.push_bit(0); w.push_bit(0); w.push_bit(knobs.tiles_enabled_flag);
w.push_bit(knobs.entropy_coding_sync_enabled_flag);
w.push_bit(1); w.push_bit(knobs.deblocking_filter_control_present_flag);
w.push_bit(knobs.pps_scaling_list_data_present_flag);
w.push_bit(knobs.lists_modification_present_flag);
w.push_ue(1); w.push_bit(knobs.slice_segment_header_extension_present_flag);
w.push_bit(knobs.pps_extension_present_flag);
w.into_bytes()
}
fn valid_pps_rbsp() -> Vec<u8> {
pps_rbsp(&PpsKnobs::default())
}
#[test]
fn parses_every_field_correctly() {
let pps = HevcPps::parse(&valid_pps_rbsp()).expect("valid PPS parses");
assert_eq!(pps.pps_pic_parameter_set_id, 0);
assert!(!pps.dependent_slice_segments_enabled_flag);
assert!(pps.output_flag_present_flag);
assert_eq!(pps.num_extra_slice_header_bits, 2);
assert!(!pps.sign_data_hiding_enabled_flag);
assert!(pps.cabac_init_present_flag);
assert_eq!(pps.num_ref_idx_l0_default_active, 1);
assert_eq!(pps.num_ref_idx_l1_default_active, 1);
assert_eq!(pps.init_qp, 23);
assert!(pps.constrained_intra_pred_flag);
assert!(!pps.transform_skip_enabled_flag);
assert!(!pps.cu_qp_delta_enabled_flag);
assert_eq!(pps.diff_cu_qp_delta_depth, 0);
assert_eq!(pps.pps_cb_qp_offset, 1);
assert_eq!(pps.pps_cr_qp_offset, -1);
assert!(pps.pps_slice_chroma_qp_offsets_present_flag);
assert!(!pps.weighted_pred_flag);
assert!(!pps.weighted_bipred_flag);
assert!(!pps.transquant_bypass_enabled_flag);
assert!(pps.pps_loop_filter_across_slices_enabled_flag);
assert!(pps.lists_modification_present_flag);
assert_eq!(pps.log2_parallel_merge_level_minus2, 1);
}
#[test]
fn rejects_tiles_enabled() {
let knobs = PpsKnobs {
tiles_enabled_flag: 1,
..PpsKnobs::default()
};
assert_eq!(
HevcPps::parse(&pps_rbsp(&knobs)),
Err(DecodeError::Unsupported)
);
}
#[test]
fn rejects_entropy_coding_sync_wpp() {
let knobs = PpsKnobs {
entropy_coding_sync_enabled_flag: 1,
..PpsKnobs::default()
};
assert_eq!(
HevcPps::parse(&pps_rbsp(&knobs)),
Err(DecodeError::Unsupported)
);
}
#[test]
fn rejects_deblocking_filter_control_present() {
let knobs = PpsKnobs {
deblocking_filter_control_present_flag: 1,
..PpsKnobs::default()
};
assert_eq!(
HevcPps::parse(&pps_rbsp(&knobs)),
Err(DecodeError::Unsupported)
);
}
#[test]
fn rejects_scaling_list_data_present() {
let knobs = PpsKnobs {
pps_scaling_list_data_present_flag: 1,
..PpsKnobs::default()
};
assert_eq!(
HevcPps::parse(&pps_rbsp(&knobs)),
Err(DecodeError::Unsupported)
);
}
#[test]
fn rejects_slice_segment_header_extension_present() {
let knobs = PpsKnobs {
slice_segment_header_extension_present_flag: 1,
..PpsKnobs::default()
};
assert_eq!(
HevcPps::parse(&pps_rbsp(&knobs)),
Err(DecodeError::Unsupported)
);
}
#[test]
fn rejects_pps_extension_present() {
let knobs = PpsKnobs {
pps_extension_present_flag: 1,
..PpsKnobs::default()
};
assert_eq!(
HevcPps::parse(&pps_rbsp(&knobs)),
Err(DecodeError::Unsupported)
);
}
#[test]
fn retains_lists_modification_present_flag_when_unset() {
let knobs = PpsKnobs {
lists_modification_present_flag: 0,
..PpsKnobs::default()
};
let pps = HevcPps::parse(&pps_rbsp(&knobs)).expect("valid PPS parses");
assert!(!pps.lists_modification_present_flag);
}
#[test]
fn rejects_truncated_data() {
assert_eq!(HevcPps::parse(&[]), Err(DecodeError::InvalidInput));
}