pub fn binarize_unary(v: u32, emit: &mut impl FnMut(u8)) {
for _ in 0..v {
emit(1);
}
emit(0);
}
pub fn binarize_tu(v: u32, c_max: u32, emit: &mut impl FnMut(u8)) {
debug_assert!(v <= c_max, "TU value {v} exceeds cMax {c_max}");
if v < c_max {
binarize_unary(v, emit);
} else {
for _ in 0..c_max {
emit(1);
}
}
}
pub fn binarize_fl(v: u32, c_max: u32, emit: &mut impl FnMut(u8)) {
let fixed_length = fl_bit_length(c_max);
if fixed_length == 0 {
return;
}
for i in (0..fixed_length).rev() {
emit(((v >> i) & 1) as u8);
}
}
#[inline]
pub fn fl_bit_length(c_max: u32) -> u32 {
if c_max == 0 {
0
} else {
32 - (c_max).leading_zeros()
}
}
pub fn binarize_egk_suffix(mut suf_s: u32, mut k: u32, emit: &mut impl FnMut(u8)) {
loop {
if suf_s >= (1u32 << k) {
emit(1);
suf_s -= 1u32 << k;
k += 1;
} else {
emit(0);
while k > 0 {
k -= 1;
emit(((suf_s >> k) & 1) as u8);
}
break;
}
}
}
pub fn binarize_uegk(
syn_el: i32,
k: u32,
signed_val_flag: bool,
u_coff: u32,
emit_prefix: &mut impl FnMut(u8),
emit_suffix: &mut impl FnMut(u8),
) {
let abs_v = syn_el.unsigned_abs();
let prefix_val = abs_v.min(u_coff);
binarize_tu(prefix_val, u_coff, emit_prefix);
if abs_v >= u_coff {
let suf_s = abs_v - u_coff;
binarize_egk_suffix(suf_s, k, emit_suffix);
}
if signed_val_flag && syn_el != 0 {
emit_suffix(if syn_el > 0 { 0 } else { 1 });
}
}
#[inline]
pub fn mb_qp_delta_remap(qp_delta: i32) -> u32 {
if qp_delta > 0 {
(2 * qp_delta as u32) - 1
} else {
2 * qp_delta.unsigned_abs()
}
}
pub fn mb_type_i_bins(mb_type: u32) -> &'static [u8] {
debug_assert!(mb_type <= 25, "I-slice mb_type {mb_type} out of range");
MB_TYPE_I_BINS[mb_type as usize]
}
const MB_TYPE_I_BINS: &[&[u8]] = &[
&[0],
&[1, 0, 0, 0, 0, 0],
&[1, 0, 0, 0, 0, 1],
&[1, 0, 0, 0, 1, 0],
&[1, 0, 0, 0, 1, 1],
&[1, 0, 0, 1, 0, 0, 0],
&[1, 0, 0, 1, 0, 0, 1],
&[1, 0, 0, 1, 0, 1, 0],
&[1, 0, 0, 1, 0, 1, 1],
&[1, 0, 0, 1, 1, 0, 0],
&[1, 0, 0, 1, 1, 0, 1],
&[1, 0, 0, 1, 1, 1, 0],
&[1, 0, 0, 1, 1, 1, 1],
&[1, 0, 1, 0, 0, 0],
&[1, 0, 1, 0, 0, 1],
&[1, 0, 1, 0, 1, 0],
&[1, 0, 1, 0, 1, 1],
&[1, 0, 1, 1, 0, 0, 0],
&[1, 0, 1, 1, 0, 0, 1],
&[1, 0, 1, 1, 0, 1, 0],
&[1, 0, 1, 1, 0, 1, 1],
&[1, 0, 1, 1, 1, 0, 0],
&[1, 0, 1, 1, 1, 0, 1],
&[1, 0, 1, 1, 1, 1, 0],
&[1, 0, 1, 1, 1, 1, 1],
&[1, 1],
];
pub fn mb_type_p_bins_prefix(mb_type: u32) -> &'static [u8] {
match mb_type {
0 => &[0, 0, 0], 1 => &[0, 1, 1], 2 => &[0, 1, 0], 3 => &[0, 0, 1], 4 => {
debug_assert!(false, "P_8x8ref0 (mb_type=4) is forbidden in CABAC");
&[]
}
_ => &[1], }
}
pub fn mb_type_b_bins(mb_type: u32) -> &'static [u8] {
debug_assert!(mb_type <= 22, "B-slice non-intra mb_type must be 0..=22");
MB_TYPE_B_BINS[mb_type as usize]
}
pub fn mb_type_b_intra_prefix() -> &'static [u8] {
&[1, 1, 1, 1, 0, 1]
}
const MB_TYPE_B_BINS: [&[u8]; 23] = [
&[0], &[1, 0, 0], &[1, 0, 1], &[1, 1, 0, 0, 0, 0], &[1, 1, 0, 0, 0, 1], &[1, 1, 0, 0, 1, 0], &[1, 1, 0, 0, 1, 1], &[1, 1, 0, 1, 0, 0], &[1, 1, 0, 1, 0, 1], &[1, 1, 0, 1, 1, 0], &[1, 1, 0, 1, 1, 1], &[1, 1, 0, 1, 0, 0, 0], &[1, 1, 0, 1, 0, 0, 0], &[1, 1, 0, 1, 0, 0, 1], &[1, 1, 0, 1, 0, 1, 0], &[1, 1, 0, 1, 0, 1, 1], &[1, 1, 0, 1, 1, 0, 0], &[1, 1, 0, 1, 1, 0, 1], &[1, 1, 0, 1, 1, 1, 0], &[1, 1, 0, 1, 1, 1, 1], &[1, 1, 1, 0, 0, 0, 0], &[1, 1, 1, 0, 0, 0, 1], &[1, 1, 1, 1, 1, 1], ];
pub fn sub_mb_type_p_bins(sub_mb_type: u32) -> &'static [u8] {
match sub_mb_type {
0 => &[1], 1 => &[0, 0], 2 => &[0, 1, 1], 3 => &[0, 1, 0], _ => {
debug_assert!(false, "P-slice sub_mb_type {sub_mb_type} out of range");
&[]
}
}
}
pub fn unary_to_bins(v: u32) -> Vec<u8> {
let mut out = Vec::with_capacity((v + 1) as usize);
binarize_unary(v, &mut |b| out.push(b));
out
}
pub fn tu_to_bins(v: u32, c_max: u32) -> Vec<u8> {
let mut out = Vec::with_capacity((c_max + 1) as usize);
binarize_tu(v, c_max, &mut |b| out.push(b));
out
}
pub fn fl_to_bins(v: u32, c_max: u32) -> Vec<u8> {
let mut out = Vec::new();
binarize_fl(v, c_max, &mut |b| out.push(b));
out
}
pub fn uegk_to_bins(
syn_el: i32,
k: u32,
signed_val_flag: bool,
u_coff: u32,
) -> (Vec<u8>, Vec<u8>) {
let mut prefix = Vec::new();
let mut suffix = Vec::new();
binarize_uegk(
syn_el,
k,
signed_val_flag,
u_coff,
&mut |b| prefix.push(b),
&mut |b| suffix.push(b),
);
(prefix, suffix)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unary_spec_examples() {
assert_eq!(unary_to_bins(0), vec![0]);
assert_eq!(unary_to_bins(1), vec![1, 0]);
assert_eq!(unary_to_bins(2), vec![1, 1, 0]);
assert_eq!(unary_to_bins(3), vec![1, 1, 1, 0]);
assert_eq!(unary_to_bins(5), vec![1, 1, 1, 1, 1, 0]);
}
#[test]
fn unary_length_is_v_plus_one() {
for v in 0..20 {
assert_eq!(unary_to_bins(v).len(), (v + 1) as usize);
}
}
#[test]
fn tu_below_cmax_matches_unary() {
assert_eq!(tu_to_bins(0, 3), vec![0]);
assert_eq!(tu_to_bins(1, 3), vec![1, 0]);
assert_eq!(tu_to_bins(2, 3), vec![1, 1, 0]);
}
#[test]
fn tu_at_cmax_truncates_no_trailing_zero() {
assert_eq!(tu_to_bins(3, 3), vec![1, 1, 1]);
assert_eq!(tu_to_bins(5, 5), vec![1, 1, 1, 1, 1]);
}
#[test]
fn tu_cmax_3_intra_chroma_pred_mode() {
assert_eq!(tu_to_bins(0, 3), vec![0]);
assert_eq!(tu_to_bins(1, 3), vec![1, 0]);
assert_eq!(tu_to_bins(2, 3), vec![1, 1, 0]);
assert_eq!(tu_to_bins(3, 3), vec![1, 1, 1]);
}
#[test]
fn fl_bit_length_spec_examples() {
assert_eq!(fl_bit_length(0), 0);
assert_eq!(fl_bit_length(1), 1);
assert_eq!(fl_bit_length(3), 2);
assert_eq!(fl_bit_length(7), 3);
assert_eq!(fl_bit_length(15), 4);
}
#[test]
fn fl_is_msb_first() {
assert_eq!(fl_to_bins(5, 15), vec![0, 1, 0, 1]);
assert_eq!(fl_to_bins(7, 7), vec![1, 1, 1]);
assert_eq!(fl_to_bins(2, 3), vec![1, 0]);
assert_eq!(fl_to_bins(4, 7), vec![1, 0, 0]);
}
#[test]
fn fl_1bit_flags() {
assert_eq!(fl_to_bins(0, 1), vec![0]);
assert_eq!(fl_to_bins(1, 1), vec![1]);
}
#[test]
fn uegk_uego_small_values() {
let (p, s) = uegk_to_bins(0, 0, false, 14);
assert_eq!(p, vec![0]);
assert!(s.is_empty());
let (p, s) = uegk_to_bins(13, 0, false, 14);
assert_eq!(p, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
assert!(s.is_empty());
}
#[test]
fn uegk_uego_at_ucoff_boundary() {
let (p, s) = uegk_to_bins(14, 0, false, 14);
assert_eq!(p, vec![1; 14]);
assert_eq!(s, vec![0]);
}
#[test]
fn uegk_uego_above_ucoff() {
let (p, s) = uegk_to_bins(15, 0, false, 14);
assert_eq!(p, vec![1; 14]);
assert_eq!(s, vec![1, 0, 0]);
}
#[test]
fn uegk_uge3_zero() {
let (p, s) = uegk_to_bins(0, 3, true, 9);
assert_eq!(p, vec![0]); assert!(s.is_empty());
}
#[test]
fn uegk_uge3_positive_small() {
let (p, s) = uegk_to_bins(1, 3, true, 9);
assert_eq!(p, vec![1, 0]);
assert_eq!(s, vec![0]); }
#[test]
fn uegk_uge3_negative_small() {
let (p, s) = uegk_to_bins(-3, 3, true, 9);
assert_eq!(p, vec![1, 1, 1, 0]);
assert_eq!(s, vec![1]); }
#[test]
fn uegk_uge3_at_ucoff() {
let (p, s) = uegk_to_bins(9, 3, true, 9);
assert_eq!(p, vec![1; 9]);
assert_eq!(s, vec![0, 0, 0, 0, 0]); }
#[test]
fn uegk_uge3_above_ucoff() {
let (p, s) = uegk_to_bins(17, 3, true, 9);
assert_eq!(p, vec![1; 9]);
assert_eq!(s, vec![1, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn mb_qp_delta_remap_spec_examples() {
assert_eq!(mb_qp_delta_remap(0), 0);
assert_eq!(mb_qp_delta_remap(1), 1);
assert_eq!(mb_qp_delta_remap(-1), 2);
assert_eq!(mb_qp_delta_remap(2), 3);
assert_eq!(mb_qp_delta_remap(-2), 4);
assert_eq!(mb_qp_delta_remap(5), 9);
assert_eq!(mb_qp_delta_remap(-5), 10);
}
#[test]
fn mb_type_i_all_values_have_bins() {
for v in 0..=25 {
let bins = mb_type_i_bins(v);
assert!(!bins.is_empty(), "mb_type_i_bins({v}) empty");
}
}
#[test]
fn mb_type_i_spec_fixed_points() {
assert_eq!(mb_type_i_bins(0), &[0][..]);
assert_eq!(mb_type_i_bins(1), &[1, 0, 0, 0, 0, 0][..]);
assert_eq!(mb_type_i_bins(2), &[1, 0, 0, 0, 0, 1][..]);
assert_eq!(mb_type_i_bins(24), &[1, 0, 1, 1, 1, 1, 1][..]);
assert_eq!(mb_type_i_bins(25), &[1, 1][..]);
}
#[test]
fn mb_type_p_prefix_spec_values() {
assert_eq!(mb_type_p_bins_prefix(0), &[0, 0, 0][..]);
assert_eq!(mb_type_p_bins_prefix(1), &[0, 1, 1][..]);
assert_eq!(mb_type_p_bins_prefix(2), &[0, 1, 0][..]);
assert_eq!(mb_type_p_bins_prefix(3), &[0, 0, 1][..]);
assert_eq!(mb_type_p_bins_prefix(5), &[1][..]);
assert_eq!(mb_type_p_bins_prefix(30), &[1][..]);
}
#[test]
fn sub_mb_type_p_spec_values() {
assert_eq!(sub_mb_type_p_bins(0), &[1][..]);
assert_eq!(sub_mb_type_p_bins(1), &[0, 0][..]);
assert_eq!(sub_mb_type_p_bins(2), &[0, 1, 1][..]);
assert_eq!(sub_mb_type_p_bins(3), &[0, 1, 0][..]);
}
#[test]
fn mb_type_b_bins_active_set() {
assert_eq!(mb_type_b_bins(0), &[0][..]);
assert_eq!(mb_type_b_bins(1), &[1, 0, 0][..]);
assert_eq!(mb_type_b_bins(2), &[1, 0, 1][..]);
assert_eq!(mb_type_b_bins(3), &[1, 1, 0, 0, 0, 0][..]);
assert_eq!(mb_type_b_bins(22), &[1, 1, 1, 1, 1, 1][..]);
}
#[test]
fn mb_type_b_intra_prefix_matches_v13() {
assert_eq!(mb_type_b_intra_prefix(), &[1, 1, 1, 1, 0, 1][..]);
}
#[test]
fn mb_type_b_bins_all_non_empty() {
for v in 0..=22u32 {
let bins = mb_type_b_bins(v);
assert!(!bins.is_empty(), "mb_type_b_bins({v}) empty");
}
}
#[test]
fn mb_type_b_bins_16x16_family_starts_correctly() {
assert_eq!(mb_type_b_bins(1)[0], 1);
assert_eq!(mb_type_b_bins(1)[1], 0);
assert_eq!(mb_type_b_bins(2)[0], 1);
assert_eq!(mb_type_b_bins(2)[1], 0);
assert_eq!(mb_type_b_bins(3)[0], 1);
assert_eq!(mb_type_b_bins(3)[1], 1);
}
}