Files
effectcraft/crates/hevcenc/src/cabac.rs
T

221 lines
6.4 KiB
Rust

//! CABAC: context initialisation (9.3.2.2), the arithmetic encoder (9.3.4.x, informative encoder
//! description: EncodeDecision, EncodeBypass, EncodeTerminate, EncodeFlush) and a fractional-bit
//! estimator with the same context-state evolution, used for rate-distortion decisions.
use crate::bitstream::BitWriter;
use crate::tables::{CABAC_INIT, NEXT_STATE, NUM_CTX, RANGE_TAB_LPS};
use std::sync::OnceLock;
/// Context states, each `pStateIdx << 1 | valMps`.
pub type Contexts = [u8; NUM_CTX];
/// Initialise all context variables for `slice_qp` and `init_type` (9.3.2.2).
pub fn init_contexts(slice_qp: i32, init_type: usize) -> Contexts {
let mut ctx = [0u8; NUM_CTX];
let qp = slice_qp.clamp(0, 51);
for (c, &v) in ctx.iter_mut().zip(CABAC_INIT[init_type].iter()) {
let slope = (v >> 4) as i32;
let offset = (v & 15) as i32;
let m = slope * 5 - 45;
let n = (offset << 3) - 16;
let pre = (((m * qp) >> 4) + n).clamp(1, 126);
*c = if pre <= 63 { ((63 - pre) << 1) as u8 } else { (((pre - 64) << 1) | 1) as u8 };
}
ctx
}
/// Destination of binarised syntax elements: the real arithmetic coder or the bit estimator.
pub trait Sink {
fn decision(&mut self, ctx: usize, bin: u32);
fn bypass(&mut self, bin: u32);
/// `n` bypass bins carrying the low `n` bits of `v`, MSB first.
fn bypass_bits(&mut self, v: u32, n: u32) {
for i in (0..n).rev() {
self.bypass((v >> i) & 1);
}
}
fn terminate(&mut self, bin: u32);
}
/// Arithmetic encoder writing into a bit writer.
pub struct CabacEncoder {
pub ctx: Contexts,
low: u32,
range: u32,
outstanding: u32,
first: bool,
pub w: BitWriter,
}
impl CabacEncoder {
/// Start encoding slice data into `w` (which must be byte aligned).
pub fn new(w: BitWriter, ctx: Contexts) -> Self {
debug_assert!(w.is_aligned());
CabacEncoder { ctx, low: 0, range: 510, outstanding: 0, first: true, w }
}
#[inline]
fn put_bit(&mut self, b: u32) {
if self.first {
self.first = false;
} else {
self.w.bit(b);
}
while self.outstanding > 0 {
self.w.bit(1 - b);
self.outstanding -= 1;
}
}
#[inline]
fn renorm(&mut self) {
while self.range < 256 {
if self.low < 256 {
self.put_bit(0);
} else if self.low >= 512 {
self.low -= 512;
self.put_bit(1);
} else {
self.low -= 256;
self.outstanding += 1;
}
self.range <<= 1;
self.low <<= 1;
}
}
/// EncodeFlush after a terminating bin equal to 1; the final bit written is the
/// rbsp_stop_one_bit. Returns the writer (byte aligned with zero bits).
pub fn finish(mut self) -> BitWriter {
self.range = 2;
self.renorm();
let b = (self.low >> 9) & 1;
self.put_bit(b);
self.w.put(((self.low >> 7) & 3) | 1, 2);
self.w.align_zero();
self.w
}
}
impl Sink for CabacEncoder {
#[inline]
fn decision(&mut self, ctx_idx: usize, bin: u32) {
let s = self.ctx[ctx_idx] as usize;
let mps = (s & 1) as u32;
let q = ((self.range >> 6) & 3) as usize;
let lps = RANGE_TAB_LPS[s >> 1][q] as u32;
self.range -= lps;
if bin != mps {
self.low += self.range;
self.range = lps;
self.ctx[ctx_idx] = NEXT_STATE[s][1];
} else {
self.ctx[ctx_idx] = NEXT_STATE[s][0];
}
self.renorm();
}
#[inline]
fn bypass(&mut self, bin: u32) {
self.low <<= 1;
if bin != 0 {
self.low += self.range;
}
if self.low >= 1024 {
self.put_bit(1);
self.low -= 1024;
} else if self.low < 512 {
self.put_bit(0);
} else {
self.low -= 512;
self.outstanding += 1;
}
}
fn terminate(&mut self, bin: u32) {
self.range -= 2;
if bin != 0 {
self.low += self.range;
// the caller flushes with finish()
} else {
self.renorm();
}
}
}
/// Fractional bit cost (1/32768 bit units) of coding the MPS / LPS in each state.
fn cost_table() -> &'static [[u32; 2]; 64] {
static T: OnceLock<[[u32; 2]; 64]> = OnceLock::new();
T.get_or_init(|| {
let mut t = [[0u32; 2]; 64];
// pLPS(s) = 0.5 * alpha^s with alpha = (0.01875 / 0.5)^(1/63) (9.3.4.3 design)
let alpha = (0.01875f64 / 0.5).powf(1.0 / 63.0);
for (s, e) in t.iter_mut().enumerate() {
let p = 0.5 * alpha.powi(s as i32);
e[0] = (-(1.0 - p).log2() * 32768.0).round() as u32;
e[1] = (-p.log2() * 32768.0).round() as u32;
}
t
})
}
pub const BIT: u64 = 32768;
/// Bit estimator: tracks context states like the encoder and accumulates the ideal code length.
#[derive(Clone)]
pub struct Estimator {
pub ctx: Contexts,
/// Accumulated cost in 1/32768 bits.
pub bits: u64,
table: &'static [[u32; 2]; 64],
}
impl Estimator {
pub fn new(ctx: Contexts) -> Self {
Estimator { ctx, bits: 0, table: cost_table() }
}
}
impl Sink for Estimator {
#[inline]
fn decision(&mut self, ctx_idx: usize, bin: u32) {
let s = self.ctx[ctx_idx] as usize;
let lps = (bin != (s & 1) as u32) as usize;
self.bits += self.table[s >> 1][lps] as u64;
self.ctx[ctx_idx] = NEXT_STATE[s][lps];
}
#[inline]
fn bypass(&mut self, _bin: u32) {
self.bits += BIT;
}
#[inline]
fn bypass_bits(&mut self, _v: u32, n: u32) {
self.bits += BIT * n as u64;
}
fn terminate(&mut self, _bin: u32) {
self.bits += 7; // ~0 bits for a 0 bin
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn context_init_formula() {
let ctx = init_contexts(30, 0);
// initValue 154: m = 0, n = 64 -> pStateIdx 0, valMps 1
assert_eq!(ctx[crate::tables::CU_TRANSQUANT_BYPASS], 1);
// split_cu_flag ctx 0 (initValue 139) at QP 30: preCtxState 62
assert_eq!(ctx[crate::tables::SPLIT_CU], (63 - 62) << 1);
}
#[test]
fn costs_are_sane() {
let t = cost_table();
assert_eq!(t[0][0], 32768);
assert_eq!(t[0][1], 32768);
assert!(t[62][0] < 1000 && t[62][1] > 5 * 32768);
}
}