use super::cavlc_writer::{encode_cavlc_block, CavlcBlockType};
use super::encoder::bitstream_writer::{BitSink, BitSizer};
pub fn residual_block_bits_cavlc(
coeffs: &[i32],
nc: i8,
block_type: CavlcBlockType,
) -> Option<u32> {
let mut sizer = BitSizer::new();
encode_cavlc_block(&mut sizer, coeffs, nc, block_type).ok()?;
Some(sizer.bits_written() as u32)
}
pub fn mvd_component_bits(mvd: i32) -> u32 {
let mut sizer = BitSizer::new();
sizer.write_se(mvd);
sizer.bits_written() as u32
}
pub fn partition_mvd_bits(mvd_x: i32, mvd_y: i32) -> u32 {
mvd_component_bits(mvd_x) + mvd_component_bits(mvd_y)
}
#[allow(clippy::too_many_arguments)]
pub fn macroblock_header_bits_cavlc(
mb_type_codenum: u32,
sub_mb_type_codenums: &[u32],
mvds: &[(i32, i32)],
coded_block_pattern_codenum: Option<u32>,
mb_qp_delta: Option<i32>,
transform_8x8_flag: Option<bool>,
) -> u32 {
let mut sizer = BitSizer::new();
sizer.write_ue(mb_type_codenum);
for &cn in sub_mb_type_codenums {
sizer.write_ue(cn);
}
for &(x, y) in mvds {
sizer.write_se(x);
sizer.write_se(y);
}
if let Some(cbp) = coded_block_pattern_codenum {
sizer.write_ue(cbp);
}
if let Some(flag) = transform_8x8_flag {
sizer.write_bit(flag);
}
if let Some(delta) = mb_qp_delta {
sizer.write_se(delta);
}
sizer.bits_written() as u32
}
pub fn mb_skip_run_bits(skip_run: u32) -> u32 {
let mut sizer = BitSizer::new();
sizer.write_ue(skip_run);
sizer.bits_written() as u32
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::h264::encoder::bitstream_writer::BitWriter;
use crate::codec::h264::encoder::bitstream_writer::BitSink as _;
fn writer_bits<F: FnOnce(&mut BitWriter)>(f: F) -> u32 {
let mut w = BitWriter::new();
f(&mut w);
w.bits_written() as u32
}
#[test]
fn size_matches_writer_all_zero_luma4x4() {
let coeffs = [0i32; 16];
let real = writer_bits(|w| {
encode_cavlc_block(w, &coeffs, 0, CavlcBlockType::Luma4x4).unwrap()
});
let sized = residual_block_bits_cavlc(&coeffs, 0, CavlcBlockType::Luma4x4).unwrap();
assert_eq!(sized, real, "all-zero luma4x4: size={sized} real={real}");
}
#[test]
fn size_matches_writer_single_t1_luma4x4() {
let mut coeffs = [0i32; 16];
coeffs[15] = 1; let real = writer_bits(|w| {
encode_cavlc_block(w, &coeffs, 0, CavlcBlockType::Luma4x4).unwrap()
});
let sized = residual_block_bits_cavlc(&coeffs, 0, CavlcBlockType::Luma4x4).unwrap();
assert_eq!(sized, real, "single T1 luma4x4: size={sized} real={real}");
}
#[test]
fn size_matches_writer_dense_intra16_ac() {
let coeffs: [i32; 15] = [2, -3, 1, 0, 1, -1, 0, 2, 0, 0, -1, 0, 0, 0, 0];
let real = writer_bits(|w| {
encode_cavlc_block(w, &coeffs, 4, CavlcBlockType::Intra16x16Ac).unwrap()
});
let sized =
residual_block_bits_cavlc(&coeffs, 4, CavlcBlockType::Intra16x16Ac).unwrap();
assert_eq!(sized, real, "dense intra16 AC: size={sized} real={real}");
}
#[test]
fn size_matches_writer_chroma_dc() {
let coeffs: [i32; 4] = [5, -1, 0, 2];
let real = writer_bits(|w| {
encode_cavlc_block(w, &coeffs, -1, CavlcBlockType::ChromaDc).unwrap()
});
let sized = residual_block_bits_cavlc(&coeffs, -1, CavlcBlockType::ChromaDc).unwrap();
assert_eq!(sized, real, "chroma DC: size={sized} real={real}");
}
#[test]
fn size_matches_writer_escape_magnitude_luma4x4() {
let mut coeffs = [0i32; 16];
coeffs[0] = 63; coeffs[1] = -17;
coeffs[2] = 8;
coeffs[3] = -4;
coeffs[4] = 1; let real = writer_bits(|w| {
encode_cavlc_block(w, &coeffs, 8, CavlcBlockType::Luma4x4).unwrap()
});
let sized = residual_block_bits_cavlc(&coeffs, 8, CavlcBlockType::Luma4x4).unwrap();
assert_eq!(sized, real, "escape-regime luma4x4: size={sized} real={real}");
}
#[test]
fn mvd_bits_match_writer() {
for &v in &[0i32, 1, -1, 7, -8, 15, -32, 127, -128, 513, -2047] {
let real = writer_bits(|w| w.write_se(v));
assert_eq!(
mvd_component_bits(v),
real,
"mvd={v}: size={} real={real}",
mvd_component_bits(v)
);
}
}
#[test]
fn partition_mvd_bits_match_writer() {
let real = writer_bits(|w| {
w.write_se(3);
w.write_se(-5);
});
assert_eq!(partition_mvd_bits(3, -5), real);
}
#[test]
fn header_bits_p16x16_typical() {
let real = writer_bits(|w| {
w.write_ue(0); w.write_se(2); w.write_se(-1); w.write_ue(15); w.write_se(0); });
let sized = macroblock_header_bits_cavlc(
0,
&[],
&[(2, -1)],
Some(15),
Some(0),
None,
);
assert_eq!(sized, real);
}
#[test]
fn header_bits_p8x8_with_sub_mb_types() {
let real = writer_bits(|w| {
w.write_ue(3);
w.write_ue(0); w.write_ue(1); w.write_ue(2); w.write_ue(0);
w.write_se(1); w.write_se(0);
w.write_se(-2); w.write_se(1);
w.write_se(0); w.write_se(-1);
w.write_se(3); w.write_se(2);
w.write_ue(3);
w.write_se(-2);
});
let sized = macroblock_header_bits_cavlc(
3,
&[0, 1, 2, 0],
&[(1, 0), (-2, 1), (0, -1), (3, 2)],
Some(3),
Some(-2),
None,
);
assert_eq!(sized, real);
}
}