#![allow(clippy::unwrap_used, clippy::expect_used, reason = "unit tests")]
use super::{RefPicListModOp, SliceType, parse_slice_header};
use crate::windows::d3d12_video_decode::h264_sps_pps::{Pps, Sps};
use mediaway_sw::h264::NalUnitType;
struct BitWriter {
bits: Vec<bool>,
}
impl BitWriter {
fn new() -> Self {
Self { bits: Vec::new() }
}
fn write_bit(&mut self, value: bool) {
self.bits.push(value);
}
fn write_bits(&mut self, value: u32, count: u32) {
for i in (0..count).rev() {
self.write_bit((value >> i) & 1 == 1);
}
}
fn write_ue(&mut self, value: u32) {
let code = value + 1;
let bit_len = 32 - code.leading_zeros();
for _ in 0..bit_len - 1 {
self.write_bit(false);
}
self.write_bits(code, bit_len);
}
fn write_se(&mut self, value: i32) {
#[allow(
clippy::cast_sign_loss,
reason = "magnitude-only cast after explicit sign branch, test helper"
)]
let k = if value <= 0 {
(-value as u32) * 2
} else {
(value as u32) * 2 - 1
};
self.write_ue(k);
}
fn finish(mut self) -> Vec<u8> {
self.bits.push(true);
while self.bits.len() % 8 != 0 {
self.bits.push(false);
}
let mut out = vec![0u8; self.bits.len() / 8];
for (i, &bit) in self.bits.iter().enumerate() {
if bit {
out[i / 8] |= 1 << (7 - i % 8);
}
}
out
}
}
fn sps_fixture() -> Sps {
Sps {
log2_max_frame_num: 4,
pic_order_cnt_type: 0,
log2_max_pic_order_cnt_lsb: 8,
..Sps::default()
}
}
fn pps_fixture() -> Pps {
Pps {
num_ref_idx_l0_default_active_minus1: 1,
..Pps::default()
}
}
#[test]
fn parse_idr_i_slice() {
let mut w = BitWriter::new();
w.write_ue(0); w.write_ue(7); w.write_ue(0); w.write_bits(0, 4); w.write_ue(0); w.write_bits(0, 8); w.write_bit(false);
w.write_bit(false);
w.write_se(0); let bytes = w.finish();
let sps = sps_fixture();
let pps = pps_fixture();
let (sh, _bits_read) = parse_slice_header(&bytes, NalUnitType::IdrSlice, 1, &sps, &pps)
.expect("valid hand-built IDR slice header");
assert_eq!(sh.slice_type, SliceType::I);
assert_eq!(sh.frame_num, 0);
assert_eq!(sh.idr_pic_id, Some(0));
assert_eq!(sh.pic_order_cnt_lsb, 0);
assert!(!sh.no_output_of_prior_pics_flag);
assert_eq!(sh.slice_qp_delta, 0);
}
#[test]
fn parse_p_slice_with_ref_pic_list_modification_and_deblocking() {
let mut w = BitWriter::new();
w.write_ue(0); w.write_ue(5); w.write_ue(0); w.write_bits(3, 4); w.write_bits(6, 8); w.write_bit(false); w.write_bit(true); w.write_ue(0); w.write_ue(0); w.write_ue(3); w.write_bit(false);
w.write_se(2); w.write_ue(0); w.write_se(1); w.write_se(-1); let bytes = w.finish();
let sps = sps_fixture();
let pps = Pps {
deblocking_filter_control_present_flag: true,
..pps_fixture()
};
let (sh, _bits_read) = parse_slice_header(&bytes, NalUnitType::NonIdrSlice, 1, &sps, &pps)
.expect("valid hand-built P slice header");
assert_eq!(sh.slice_type, SliceType::P);
assert_eq!(sh.frame_num, 3);
assert_eq!(sh.pic_order_cnt_lsb, 6);
assert_eq!(sh.num_ref_idx_l0_active_minus1, 1); assert_eq!(
sh.ref_pic_list_modification_l0.as_slice(),
&[RefPicListModOp {
add: false,
abs_diff_pic_num_minus1: 0,
}]
);
assert_eq!(sh.slice_qp_delta, 2);
assert_eq!(sh.disable_deblocking_filter_idc, 0);
assert_eq!(sh.slice_alpha_c0_offset_div2, 1);
assert_eq!(sh.slice_beta_offset_div2, -1);
}
#[test]
fn parse_rejects_adaptive_ref_pic_marking() {
let mut w = BitWriter::new();
w.write_ue(0);
w.write_ue(5); w.write_ue(0);
w.write_bits(1, 4);
w.write_bits(0, 8);
w.write_bit(false); w.write_bit(false); w.write_bit(true); let bytes = w.finish();
let sps = sps_fixture();
let pps = pps_fixture();
let err = parse_slice_header(&bytes, NalUnitType::NonIdrSlice, 1, &sps, &pps)
.expect_err("adaptive marking must be rejected");
assert_eq!(err, crate::DecodeError::Unsupported);
}
#[test]
fn parse_rejects_explicit_weighted_prediction() {
let mut w = BitWriter::new();
w.write_ue(0);
w.write_ue(5); w.write_ue(0);
w.write_bits(1, 4);
w.write_bits(0, 8);
w.write_bit(false); w.write_bit(false); let bytes = w.finish();
let sps = sps_fixture();
let pps = Pps {
weighted_pred_flag: true,
..pps_fixture()
};
let err = parse_slice_header(&bytes, NalUnitType::NonIdrSlice, 1, &sps, &pps)
.expect_err("explicit weighted prediction must be rejected");
assert_eq!(err, crate::DecodeError::Unsupported);
}