use crate::codec::h264::encoder::bitstream_writer::BitWriter;
pub fn align_for_cabac(w: &mut BitWriter) {
while !w.byte_aligned() {
w.write_bit(true);
}
}
pub fn append_rbsp_trailing_aligned(rbsp: &mut Vec<u8>) {
rbsp.push(0x80);
}
pub fn assemble_cabac_slice_rbsp(mut header_writer: BitWriter, cabac_body: &[u8]) -> Vec<u8> {
align_for_cabac(&mut header_writer);
let mut out = header_writer.finish();
out.extend_from_slice(cabac_body);
append_rbsp_trailing_aligned(&mut out);
out
}
pub fn append_cabac_zero_words(rbsp: &mut Vec<u8>, bin_count: u32, pic_size_mbs: u32) {
let threshold = bin_count.saturating_sub(96u32.saturating_mul(pic_size_mbs));
while 3 * (rbsp.len() as u32) < threshold {
rbsp.extend_from_slice(&[0x00, 0x00]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn align_for_cabac_no_op_when_already_aligned() {
let mut w = BitWriter::new();
w.write_bits(0xAB, 8); align_for_cabac(&mut w);
assert!(w.byte_aligned());
assert_eq!(w.bits_written(), 8);
}
#[test]
fn align_for_cabac_emits_ones_to_byte_boundary() {
let mut w = BitWriter::new();
w.write_bits(0b00000, 5);
align_for_cabac(&mut w);
assert!(w.byte_aligned());
assert_eq!(w.bits_written(), 8);
let bytes = w.finish();
assert_eq!(bytes, vec![0x07]);
}
#[test]
fn align_for_cabac_three_alignment_bits_all_ones() {
let mut w = BitWriter::new();
w.write_bit(true);
align_for_cabac(&mut w);
assert!(w.byte_aligned());
let bytes = w.finish();
assert_eq!(bytes, vec![0xFF]);
}
#[test]
fn append_rbsp_trailing_aligned_adds_single_0x80_byte() {
let mut rbsp = vec![0x42, 0x7F];
append_rbsp_trailing_aligned(&mut rbsp);
assert_eq!(rbsp, vec![0x42, 0x7F, 0x80]);
}
#[test]
fn assemble_cabac_slice_rbsp_header_body_trailing() {
let mut hdr = BitWriter::new();
hdr.write_bits(0b1010, 4);
let body = [0x12, 0x34];
let rbsp = assemble_cabac_slice_rbsp(hdr, &body);
assert_eq!(rbsp, vec![0xAF, 0x12, 0x34, 0x80]);
}
#[test]
fn assemble_cabac_slice_rbsp_aligned_header() {
let mut hdr = BitWriter::new();
hdr.write_bits(0xAB, 8);
let body = [0xCD];
let rbsp = assemble_cabac_slice_rbsp(hdr, &body);
assert_eq!(rbsp, vec![0xAB, 0xCD, 0x80]);
}
#[test]
fn append_cabac_zero_words_no_op_when_bins_fit() {
let mut rbsp = vec![0u8; 100];
append_cabac_zero_words(&mut rbsp, 10, 1);
assert_eq!(rbsp.len(), 100);
}
#[test]
fn append_cabac_zero_words_pads_dense_bin_counts() {
let mut rbsp = vec![0u8; 10];
append_cabac_zero_words(&mut rbsp, 10000, 1);
assert!(rbsp.len() >= 3302, "expected padding, got len {}", rbsp.len());
for i in 10..rbsp.len() {
assert_eq!(rbsp[i], 0);
}
assert_eq!((rbsp.len() - 10) % 2, 0);
}
#[test]
fn full_cabac_slice_i_mb_then_end_of_slice() {
use super::super::context::CabacInitSlot;
use super::super::encoder::{encode_end_of_slice_flag, encode_mb_type_i, CabacEncoder};
let mut enc = CabacEncoder::new_slice(CabacInitSlot::ISI, 26, 4);
encode_mb_type_i(&mut enc, 0, 0); encode_end_of_slice_flag(&mut enc, true);
let body = enc.finish();
assert!(!body.is_empty());
let mut hdr = BitWriter::new();
hdr.write_bits(0xAB, 8);
let rbsp = assemble_cabac_slice_rbsp(hdr, &body);
assert_eq!(rbsp[0], 0xAB);
assert_eq!(rbsp[rbsp.len() - 1], 0x80);
assert_eq!(rbsp.len(), 2 + body.len());
}
}