mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-28 12:07:48 -06:00
136 lines
3.2 KiB
Rust
136 lines
3.2 KiB
Rust
//! RBSP bit writer (7.2, 9.2 Exp-Golomb) and NAL unit encapsulation with emulation prevention (7.3.1).
|
|
|
|
/// MSB-first bit writer.
|
|
#[derive(Default)]
|
|
pub struct BitWriter {
|
|
buf: Vec<u8>,
|
|
acc: u64,
|
|
nbits: u32,
|
|
}
|
|
|
|
impl BitWriter {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// Write the `n` (<= 32) low bits of `v`.
|
|
#[inline]
|
|
pub fn put(&mut self, v: u32, n: u32) {
|
|
debug_assert!(n <= 32);
|
|
if n == 0 {
|
|
return;
|
|
}
|
|
let v = (v as u64) & ((1u64 << n) - 1);
|
|
self.acc = (self.acc << n) | v;
|
|
self.nbits += n;
|
|
while self.nbits >= 8 {
|
|
self.nbits -= 8;
|
|
self.buf.push((self.acc >> self.nbits) as u8);
|
|
}
|
|
self.acc &= (1u64 << self.nbits) - 1;
|
|
}
|
|
|
|
#[inline]
|
|
pub fn bit(&mut self, b: u32) {
|
|
self.put(b & 1, 1);
|
|
}
|
|
|
|
pub fn flag(&mut self, b: bool) {
|
|
self.put(b as u32, 1);
|
|
}
|
|
|
|
/// ue(v).
|
|
pub fn ue(&mut self, v: u32) {
|
|
let x = v as u64 + 1;
|
|
let len = 64 - x.leading_zeros(); // bits of x
|
|
// len - 1 leading zeros, then x in len bits
|
|
self.put(0, len - 1);
|
|
if len > 32 {
|
|
self.put((x >> 32) as u32, len - 32);
|
|
self.put(x as u32, 32);
|
|
} else {
|
|
self.put(x as u32, len);
|
|
}
|
|
}
|
|
|
|
/// se(v).
|
|
pub fn se(&mut self, v: i32) {
|
|
let k = if v > 0 { 2 * v as u32 - 1 } else { (-(v as i64) as u32) * 2 };
|
|
self.ue(k);
|
|
}
|
|
|
|
pub fn is_aligned(&self) -> bool {
|
|
self.nbits == 0
|
|
}
|
|
|
|
/// rbsp_trailing_bits(): stop bit and zero alignment.
|
|
pub fn trailing(&mut self) {
|
|
self.bit(1);
|
|
self.align_zero();
|
|
}
|
|
|
|
pub fn align_zero(&mut self) {
|
|
if self.nbits > 0 {
|
|
self.put(0, 8 - self.nbits);
|
|
}
|
|
}
|
|
|
|
pub fn into_bytes(mut self) -> Vec<u8> {
|
|
self.align_zero();
|
|
self.buf
|
|
}
|
|
}
|
|
|
|
/// Build a NAL unit (2-byte header, `nuh_layer_id` 0, `TemporalId` 0) from an RBSP, inserting
|
|
/// emulation prevention bytes.
|
|
pub fn nal_unit(nal_type: u8, rbsp: &[u8]) -> Vec<u8> {
|
|
let mut out = Vec::with_capacity(rbsp.len() + rbsp.len() / 64 + 4);
|
|
out.push(nal_type << 1);
|
|
out.push(1);
|
|
let mut zeros = 0;
|
|
for &b in rbsp {
|
|
if zeros >= 2 && b <= 3 {
|
|
out.push(3);
|
|
zeros = 0;
|
|
}
|
|
out.push(b);
|
|
if b == 0 {
|
|
zeros += 1;
|
|
} else {
|
|
zeros = 0;
|
|
}
|
|
}
|
|
// cabac_zero_words / trailing zero bytes never occur: every RBSP ends with the stop bit.
|
|
out
|
|
}
|
|
|
|
pub const NAL_TRAIL_R: u8 = 1;
|
|
pub const NAL_IDR_N_LP: u8 = 20;
|
|
pub const NAL_VPS: u8 = 32;
|
|
pub const NAL_SPS: u8 = 33;
|
|
pub const NAL_PPS: u8 = 34;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn exp_golomb() {
|
|
let mut w = BitWriter::new();
|
|
w.ue(0); // 1
|
|
w.ue(1); // 010
|
|
w.ue(2); // 011
|
|
w.ue(3); // 00100
|
|
w.se(-1); // ue(2) = 011
|
|
w.trailing();
|
|
// 1 010 011 00100 011 + stop bit: 1010 0110 0100 0111
|
|
assert_eq!(w.into_bytes(), vec![0xa6, 0x47]);
|
|
}
|
|
|
|
#[test]
|
|
fn emulation_prevention() {
|
|
let n = nal_unit(1, &[0, 0, 1, 0, 0, 0, 0, 0, 4]);
|
|
assert_eq!(&n[2..], &[0, 0, 3, 1, 0, 0, 3, 0, 0, 3, 0, 4]);
|
|
}
|
|
}
|