#![allow(dead_code)]
use crate::cabac::{CtxState, RANGE_TAB_LPS, TRANS_IDX_LPS, TRANS_IDX_MPS};
#[inline]
fn bin_trace_enabled() -> bool {
use std::sync::OnceLock;
static CACHED: OnceLock<bool> = OnceLock::new();
*CACHED.get_or_init(|| std::env::var_os("OXIDEAV_H264_BIN_TRACE").is_some())
}
#[derive(Debug, Clone)]
pub struct CabacEncoder {
cod_i_range: u32,
cod_i_low: u32,
outstanding_bits: u32,
bytes: Vec<u8>,
bit_pos: u8,
bin_count: u64,
first_bit_flag: bool,
}
impl Default for CabacEncoder {
fn default() -> Self {
Self::new()
}
}
impl CabacEncoder {
pub fn new() -> Self {
Self {
cod_i_range: 510,
cod_i_low: 0,
outstanding_bits: 0,
bytes: Vec::new(),
bit_pos: 0,
bin_count: 0,
first_bit_flag: true,
}
}
pub fn bin_count(&self) -> u64 {
self.bin_count
}
pub fn bits_emitted(&self) -> usize {
let full_bytes = if self.bit_pos == 0 {
self.bytes.len()
} else {
self.bytes.len() - 1
};
full_bytes * 8 + self.bit_pos as usize
}
pub fn encode_decision(&mut self, ctx: &mut CtxState, bin: u8) {
debug_assert!(bin <= 1);
let trace_on = bin_trace_enabled();
let pre_state = ctx.state_idx;
let pre_mps = ctx.val_mps;
let pre_range = self.cod_i_range;
let pre_low = self.cod_i_low;
let q_idx = ((self.cod_i_range >> 6) & 0b11) as usize;
let cod_i_range_lps = RANGE_TAB_LPS[ctx.state_idx as usize][q_idx] as u32;
self.cod_i_range -= cod_i_range_lps;
if bin != ctx.val_mps {
self.cod_i_low += self.cod_i_range;
self.cod_i_range = cod_i_range_lps;
if ctx.state_idx == 0 {
ctx.val_mps = 1 - ctx.val_mps;
}
ctx.state_idx = TRANS_IDX_LPS[ctx.state_idx as usize];
} else {
ctx.state_idx = TRANS_IDX_MPS[ctx.state_idx as usize];
}
self.renorm_e();
self.bin_count = self.bin_count.wrapping_add(1);
if trace_on {
eprintln!(
"[ENC {:>8}] DD pre_state={:>2} pre_mps={} pre_range={:>4} pre_low={:>4} bin={} post_state={:>2} post_mps={} post_range={:>4} post_low={:>4}",
self.bin_count,
pre_state, pre_mps, pre_range, pre_low,
bin,
ctx.state_idx, ctx.val_mps, self.cod_i_range, self.cod_i_low,
);
}
}
pub fn encode_bypass(&mut self, bin: u8) {
debug_assert!(bin <= 1);
let trace_on = bin_trace_enabled();
let pre_range = self.cod_i_range;
let pre_low = self.cod_i_low;
self.cod_i_low <<= 1;
if bin == 1 {
self.cod_i_low += self.cod_i_range;
}
if self.cod_i_low >= 1024 {
self.put_bit(1);
self.cod_i_low -= 1024;
} else if self.cod_i_low < 512 {
self.put_bit(0);
} else {
self.cod_i_low -= 512;
self.outstanding_bits += 1;
}
self.bin_count = self.bin_count.wrapping_add(1);
if trace_on {
eprintln!(
"[ENC {:>8}] BP pre_range={:>4} pre_low={:>4} bin={} post_range={:>4} post_low={:>4}",
self.bin_count,
pre_range, pre_low,
bin,
self.cod_i_range, self.cod_i_low,
);
}
}
pub fn encode_terminate(&mut self, bin: u8) {
debug_assert!(bin <= 1);
let trace_on = bin_trace_enabled();
let pre_range = self.cod_i_range;
let pre_low = self.cod_i_low;
self.cod_i_range -= 2;
if bin != 0 {
self.cod_i_low += self.cod_i_range;
self.encode_flush();
} else {
self.renorm_e();
}
self.bin_count = self.bin_count.wrapping_add(1);
if trace_on {
eprintln!(
"[ENC {:>8}] TT pre_range={:>4} pre_low={:>4} bin={} post_range={:>4} post_low={:>4}",
self.bin_count,
pre_range, pre_low,
bin,
self.cod_i_range, self.cod_i_low,
);
}
}
fn encode_flush(&mut self) {
self.cod_i_range = 2;
self.renorm_e();
self.put_bit(((self.cod_i_low >> 9) & 1) as u8);
self.write_bits_literal(((self.cod_i_low >> 7) & 0b11) as u8, 2);
}
pub fn finish(mut self) -> Vec<u8> {
self.write_bits_literal(1, 1);
while self.bit_pos != 0 {
self.write_bits_literal(0, 1);
}
self.bytes
}
pub fn finish_no_trailing(self) -> Vec<u8> {
self.bytes
}
fn renorm_e(&mut self) {
while self.cod_i_range < 256 {
if self.cod_i_low < 256 {
self.put_bit(0);
} else if self.cod_i_low >= 512 {
self.cod_i_low -= 512;
self.put_bit(1);
} else {
self.cod_i_low -= 256;
self.outstanding_bits += 1;
}
self.cod_i_range <<= 1;
self.cod_i_low <<= 1;
}
}
fn put_bit(&mut self, bit: u8) {
debug_assert!(bit <= 1);
if self.first_bit_flag {
self.first_bit_flag = false;
} else {
self.write_bits_literal(bit, 1);
}
let comp = 1 - bit;
while self.outstanding_bits > 0 {
self.write_bits_literal(comp, 1);
self.outstanding_bits -= 1;
}
}
fn write_bits_literal(&mut self, value: u8, bits: u32) {
debug_assert!(bits <= 8);
for i in (0..bits).rev() {
let b = (value >> i) & 1;
if self.bit_pos == 0 {
self.bytes.push(0);
}
let idx = self.bytes.len() - 1;
self.bytes[idx] |= b << (7 - self.bit_pos);
self.bit_pos += 1;
if self.bit_pos == 8 {
self.bit_pos = 0;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitstream::BitReader;
use crate::cabac::CabacDecoder;
fn round_trip_decisions(bins: &[(CtxState, u8)]) {
let mut enc = CabacEncoder::new();
let mut enc_ctxs: Vec<CtxState> = bins.iter().map(|(c, _)| *c).collect();
for (i, (_, bin)) in bins.iter().enumerate() {
enc.encode_decision(&mut enc_ctxs[i], *bin);
}
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut dec_ctxs: Vec<CtxState> = bins.iter().map(|(c, _)| *c).collect();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
for (i, (_, expected)) in bins.iter().enumerate() {
let got = dec.decode_decision(&mut dec_ctxs[i]).unwrap();
assert_eq!(
got, *expected,
"bin {i}: expected {expected}, got {got} (bytes={:?})",
bytes
);
}
let term = dec.decode_terminate().unwrap();
assert_eq!(term, 1, "trailing terminate should be 1");
}
#[test]
fn round_trip_single_mps_decision() {
let ctx = CtxState {
state_idx: 0,
val_mps: 0,
};
round_trip_decisions(&[(ctx, 0)]);
}
#[test]
fn round_trip_single_lps_decision() {
let ctx = CtxState {
state_idx: 0,
val_mps: 0,
};
round_trip_decisions(&[(ctx, 1)]);
}
#[test]
fn round_trip_alternating_bins() {
let ctx = CtxState {
state_idx: 30,
val_mps: 0,
};
let bins: Vec<_> = (0..32).map(|i| (ctx, (i & 1) as u8)).collect();
round_trip_decisions(&bins);
}
#[test]
fn round_trip_long_lps_run() {
let ctx = CtxState {
state_idx: 0,
val_mps: 0,
};
let bins: Vec<_> = (0..16).map(|_| (ctx, 1)).collect();
round_trip_decisions(&bins);
}
#[test]
fn round_trip_mixed_with_bypass() {
let mut ctx_a = CtxState {
state_idx: 10,
val_mps: 0,
};
let mut ctx_b = CtxState {
state_idx: 0,
val_mps: 1,
};
let mut enc = CabacEncoder::new();
let mut a1 = ctx_a;
let mut b1 = ctx_b;
enc.encode_decision(&mut a1, 0);
enc.encode_bypass(1);
enc.encode_bypass(0);
enc.encode_decision(&mut b1, 1);
enc.encode_bypass(1);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
assert_eq!(dec.decode_decision(&mut ctx_a).unwrap(), 0);
assert_eq!(dec.decode_bypass().unwrap(), 1);
assert_eq!(dec.decode_bypass().unwrap(), 0);
assert_eq!(dec.decode_decision(&mut ctx_b).unwrap(), 1);
assert_eq!(dec.decode_bypass().unwrap(), 1);
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
#[test]
fn round_trip_terminate_zero_then_more_decisions() {
let mut ctx = CtxState {
state_idx: 5,
val_mps: 1,
};
let mut enc = CabacEncoder::new();
for _ in 0..5 {
enc.encode_terminate(0);
let mut c = ctx;
enc.encode_decision(&mut c, 1);
ctx = c;
}
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut dec_ctx = CtxState {
state_idx: 5,
val_mps: 1,
};
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
for _ in 0..5 {
assert_eq!(dec.decode_terminate().unwrap(), 0);
assert_eq!(dec.decode_decision(&mut dec_ctx).unwrap(), 1);
}
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
#[test]
fn round_trip_random_bins_against_decoder() {
let mut state = 0x12345678u32;
let mut rng = || {
state = state.wrapping_mul(1103515245).wrapping_add(12345);
state
};
let mut enc = CabacEncoder::new();
let mut ctxs = [
CtxState {
state_idx: 20,
val_mps: 0,
},
CtxState {
state_idx: 40,
val_mps: 1,
},
];
let mut record: Vec<(usize, u8)> = Vec::new();
for _ in 0..200 {
let r = rng();
let kind = (r >> 24) & 0b11;
let bin = ((r >> 16) & 1) as u8;
match kind {
0 | 1 => {
let ci = ((r >> 8) & 1) as usize;
record.push((ci, bin));
enc.encode_decision(&mut ctxs[ci], bin);
}
2 => {
record.push((100, bin));
enc.encode_bypass(bin);
}
_ => {
let ci = ((r >> 8) & 1) as usize;
record.push((ci, bin));
enc.encode_decision(&mut ctxs[ci], bin);
}
}
}
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut dec_ctxs = [
CtxState {
state_idx: 20,
val_mps: 0,
},
CtxState {
state_idx: 40,
val_mps: 1,
},
];
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
for (i, (which, bin)) in record.iter().enumerate() {
if *which == 100 {
let got = dec.decode_bypass().unwrap();
assert_eq!(got, *bin, "bypass bin {i} mismatch");
} else {
let got = dec.decode_decision(&mut dec_ctxs[*which]).unwrap();
assert_eq!(got, *bin, "decision bin {i} mismatch (ctx {which})");
}
}
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}