Skip to main content

atmos/os_lib/
h264_cabac.rs

1// h264_cabac.rs - CABAC の算術復号エンジン(9.3.1.2 初期化・9.3.3.2 復号)。純粋モジュール。
2//
3// 【2026-10-17】YouTube の H.264(Main、avc1.4D40xx)は CABAC なので新設した第 1 段。
4// ここはエンジン(DecodeDecision / DecodeBypass / DecodeTerminate とコンテキスト初期化)だけで、
5// 構文要素ごとのコンテキスト選択・二値化は `h264_decode.rs` 側に置く予定(未実装)。
6
7use super::h264_cabac_tables::{CABAC_INIT_I, CABAC_INIT_PB, CTX_COUNT, RANGE_TAB_LPS, TRANS_IDX_LPS};
8
9/// CABAC の復号エンジンとコンテキスト状態。
10pub struct CabacEngine<'a> {
11    data: &'a [u8],
12    /// 次に読むビット位置(先頭から)。
13    pos: usize,
14    range: u32,
15    offset: u32,
16    /// 各 ctxIdx の状態(pStateIdx << 1 | valMPS)。
17    state: [u8; CTX_COUNT],
18}
19
20impl<'a> CabacEngine<'a> {
21    /// `bit_pos` はスライスヘッダ直後の位置(cabac_alignment_one_bit を飛ばしてバイト境界へ揃える)。
22    /// `cabac_init_idc` は I スライスなら `None`。
23    pub fn new(data: &'a [u8], bit_pos: usize, slice_qp: i32, cabac_init_idc: Option<u32>) -> Self {
24        let table = match cabac_init_idc {
25            None => &CABAC_INIT_I,
26            Some(i) => &CABAC_INIT_PB[(i as usize).min(2)],
27        };
28        let qp = slice_qp.clamp(0, 51);
29        let mut state = [0u8; CTX_COUNT];
30        for (s, &(m, n)) in state.iter_mut().zip(table.iter()) {
31            // 9.3.1.1: preCtxState = Clip3(1, 126, ((m * Clip3(0, 51, SliceQPY)) >> 4) + n)
32            let pre = (((m as i32 * qp) >> 4) + n as i32).clamp(1, 126);
33            *s = if pre <= 63 { ((63 - pre) << 1) as u8 } else { (((pre - 64) << 1) | 1) as u8 };
34        }
35        let mut e = Self { data, pos: (bit_pos + 7) & !7, range: 510, offset: 0, state };
36        e.init_engine();
37        e
38    }
39
40    /// 9.3.1.2: codIRange = 510、codIOffset = read_bits(9)。I_PCM の後にも呼ぶ。
41    fn init_engine(&mut self) {
42        self.range = 510;
43        self.offset = 0;
44        for _ in 0..9 {
45            self.offset = (self.offset << 1) | self.bit();
46        }
47    }
48
49    fn bit(&mut self) -> u32 {
50        let b = self.data.get(self.pos / 8).map_or(0, |&b| (b >> (7 - (self.pos % 8))) & 1);
51        self.pos += 1;
52        b as u32
53    }
54
55    /// DecodeDecision(9.3.3.2.1)。
56    pub fn decision(&mut self, ctx: usize) -> u32 {
57        let Some(&s) = self.state.get(ctx) else { return 0 };
58        let p = (s >> 1) as usize;
59        let mps = (s & 1) as u32;
60        let lps = RANGE_TAB_LPS[p][((self.range >> 6) & 3) as usize] as u32;
61        self.range -= lps;
62        let bin;
63        if self.offset >= self.range {
64            bin = 1 - mps;
65            self.offset -= self.range;
66            self.range = lps;
67            let new_mps = if p == 0 { 1 - mps } else { mps };
68            self.state[ctx] = (TRANS_IDX_LPS[p] << 1) | new_mps as u8;
69        } else {
70            bin = mps;
71            let np = if p < 62 { p + 1 } else { p };
72            self.state[ctx] = ((np as u8) << 1) | mps as u8;
73        }
74        while self.range < 256 {
75            self.range <<= 1;
76            self.offset = (self.offset << 1) | self.bit();
77        }
78        bin
79    }
80
81    /// DecodeBypass(9.3.3.2.3)。
82    pub fn bypass(&mut self) -> u32 {
83        self.offset = (self.offset << 1) | self.bit();
84        if self.offset >= self.range {
85            self.offset -= self.range;
86            1
87        } else {
88            0
89        }
90    }
91
92    /// DecodeTerminate(9.3.3.2.2)。end_of_slice_flag と I_PCM の mb_type の判定に使う。
93    pub fn terminate(&mut self) -> u32 {
94        self.range -= 2;
95        if self.offset >= self.range {
96            1
97        } else {
98            while self.range < 256 {
99                self.range <<= 1;
100                self.offset = (self.offset << 1) | self.bit();
101            }
102            0
103        }
104    }
105
106    /// I_PCM: terminate = 1 の直後に呼ぶ。バイト境界からの `out.len()` バイトを読み、エンジンを初期化し直す。
107    pub fn read_pcm(&mut self, out: &mut [u8]) {
108        self.pos = (self.pos + 7) & !7;
109        for o in out.iter_mut() {
110            *o = self.data.get(self.pos / 8).copied().unwrap_or(0);
111            self.pos += 8;
112        }
113        self.init_engine();
114    }
115}