Skip to main content

atmos/os_lib/
h264.rs

1// h264.rs - no_std H.264 / AVC (MPEG-4 Part 10) Video Decoder for AtmOS
2//
3// 段階実装中の「本物」デコーダの基盤レイヤ(ビットストリーム〜変換・予測の純関数)。
4// スライス/マクロブロック層・CAVLC 残差・再構成は h264_decode.rs にある。
5// 完成済みレイヤ(h264::selftest / h264_decode::golden_test で検証):
6//   [x] BitReader(MSB-first)+ Exp-Golomb ue(v)/se(v)
7//   [x] RBSP 抽出(emulation-prevention 0x03 除去)
8//   [x] NAL 分割(Annex-B スタートコード / AVCC 長さ前置の両対応)
9//   [x] SPS / PPS パース(実解像度 + フレームクロップ。avcC 抽出)
10//   [x] 逆量子化 + 4x4 整数 IDCT、イントラ予測(輝度 4x4/16x16・クロマ 8x8)
11// 互換 API の decode_nal / decode_slice は段階移行用(pixel 再構成は h264_decode.rs 側)。
12// 未実装(次段): I_4x4 全体経路 / nC≥2 の coeff_token / インター予測 / 動き補償 / デブロッキング。
13#![allow(dead_code, clippy::needless_range_loop)]
14
15use alloc::string::String;
16use alloc::vec::Vec;
17
18// ===================== ビットストリーム読み取り =====================
19
20/// MSB-first のビットリーダ。H.264 の RBSP を読むための最小実装。
21pub struct BitReader<'a> {
22    data: &'a [u8],
23    byte_pos: usize,
24    bit_pos: u8, // 0..=7、現在バイト内の次に読むビット(MSB=0)
25}
26
27impl<'a> BitReader<'a> {
28    pub fn new(data: &'a [u8]) -> Self {
29        Self {
30            data,
31            byte_pos: 0,
32            bit_pos: 0,
33        }
34    }
35
36    /// 残ビットが無い場合に true。
37    pub fn is_eof(&self) -> bool {
38        self.byte_pos >= self.data.len()
39    }
40
41    /// 1 ビット読む。EOF 後は 0 を返す(呼び出し側で長さ管理する前提)。
42    pub fn read_bit(&mut self) -> u32 {
43        if self.byte_pos >= self.data.len() {
44            return 0;
45        }
46        let byte = self.data[self.byte_pos];
47        let bit = (byte >> (7 - self.bit_pos)) & 1;
48        self.bit_pos += 1;
49        if self.bit_pos == 8 {
50            self.bit_pos = 0;
51            self.byte_pos += 1;
52        }
53        bit as u32
54    }
55
56    /// バイト境界まで進める(I_PCM の pcm_alignment_zero_bits 用)。
57    pub fn byte_align(&mut self) {
58        if self.bit_pos != 0 {
59            self.bit_pos = 0;
60            self.byte_pos += 1;
61        }
62    }
63
64    /// n ビット読み(n <= 32)、無符号整数として返す。
65    pub fn read_bits(&mut self, n: u32) -> u32 {
66        let mut v = 0u32;
67        // 不正データ由来の巨大ビット数によるハングを防ぐ(u32 に収まる範囲に制限)。
68        for _ in 0..n.min(32) {
69            v = (v << 1) | self.read_bit();
70        }
71        v
72    }
73
74    /// 符号なし Exp-Golomb ue(v)。
75    /// 仕様: 先行ゼロ数 leadingZeroBits を数え、`2^lzb - 1 + read_bits(lzb)`。
76    pub fn read_ue(&mut self) -> u32 {
77        let mut leading_zeros = 0u32;
78        while !self.is_eof() && self.read_bit() == 0 {
79            leading_zeros += 1;
80            if leading_zeros > 31 {
81                break;
82            } // 異常データの暴走防止
83        }
84        if leading_zeros == 0 {
85            return 0;
86        }
87        let suffix = self.read_bits(leading_zeros);
88        (1u32 << leading_zeros) - 1 + suffix
89    }
90
91    /// 符号付き Exp-Golomb se(v)。ue を ±へマップ: 0->0, 1->1, 2->-1, 3->2, 4->-2 ...
92    pub fn read_se(&mut self) -> i32 {
93        let k = self.read_ue();
94        if k == 0 {
95            0
96        } else if k & 1 == 1 {
97            (k.div_ceil(2)) as i32
98        } else {
99            -((k / 2) as i32)
100        }
101    }
102}
103
104/// RBSP 抽出: NAL ペイロードから emulation_prevention_three_byte(0x03) を除去する。
105/// 0x00 0x00 0x03 のシーケンスで 0x03 を落とす(0x000000/0x000001 の誤検出回避用バイト)。
106pub fn extract_rbsp(payload: &[u8]) -> Vec<u8> {
107    let mut out = Vec::with_capacity(payload.len());
108    let mut zeros = 0usize;
109    let mut i = 0;
110    while i < payload.len() {
111        let b = payload[i];
112        if zeros >= 2 && b == 0x03 {
113            // 0x03 を除去。直後のバイトは通常通り処理させるため zeros をリセット。
114            zeros = 0;
115            i += 1;
116            continue;
117        }
118        out.push(b);
119        if b == 0x00 {
120            zeros += 1;
121        } else {
122            zeros = 0;
123        }
124        i += 1;
125    }
126    out
127}
128
129// ===================== NAL 分割 =====================
130
131#[derive(Clone)]
132pub struct NalUnit {
133    pub ref_idc: u8,
134    pub unit_type: u8,
135    pub rbsp: Vec<u8>, // ヘッダ1バイトを除いた RBSP(脱エミュレーション済み)
136}
137
138impl NalUnit {
139    fn from_ebsp(ebsp: &[u8]) -> Option<NalUnit> {
140        if ebsp.is_empty() {
141            return None;
142        }
143        let header = ebsp[0];
144        if (header >> 7) & 1 != 0 {
145            return None;
146        } // forbidden_zero_bit
147        Some(NalUnit {
148            ref_idc: (header >> 5) & 3,
149            unit_type: header & 0x1F,
150            rbsp: extract_rbsp(&ebsp[1..]),
151        })
152    }
153}
154
155/// Annex-B(00 00 01 / 00 00 00 01 区切り)のバイト列を NAL 群に分割。
156pub fn split_annexb(data: &[u8]) -> Vec<NalUnit> {
157    let mut nals = Vec::new();
158    let mut i = 0;
159    let n = data.len();
160    // 最初のスタートコードを探す
161    let mut start = None;
162    while i + 3 <= n {
163        if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
164            start = Some(i + 3);
165            i += 3;
166            break;
167        }
168        i += 1;
169    }
170    let mut s = match start {
171        Some(s) => s,
172        None => return nals,
173    };
174    while i + 3 <= n {
175        if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
176            // 直前に 0x00 があれば 4 バイトスタートコード。末尾の余分な 0 を含めない。
177            let mut end = i;
178            if end > s && data[end - 1] == 0 {
179                end -= 1;
180            }
181            if let Some(nu) = NalUnit::from_ebsp(&data[s..end]) {
182                nals.push(nu);
183            }
184            s = i + 3;
185            i += 3;
186        } else {
187            i += 1;
188        }
189    }
190    if s < n {
191        if let Some(nu) = NalUnit::from_ebsp(&data[s..n]) {
192            nals.push(nu);
193        }
194    }
195    nals
196}
197
198/// AVCC(長さ前置)の1サンプルを NAL 群に分割。length_size は 1/2/4。
199pub fn split_avcc(data: &[u8], length_size: usize) -> Vec<NalUnit> {
200    let mut nals = Vec::new();
201    let mut i = 0;
202    while i + length_size <= data.len() {
203        let mut len = 0usize;
204        for _ in 0..length_size {
205            len = (len << 8) | data[i] as usize;
206            i += 1;
207        }
208        if len == 0 || i + len > data.len() {
209            break;
210        }
211        if let Some(nu) = NalUnit::from_ebsp(&data[i..i + len]) {
212            nals.push(nu);
213        }
214        i += len;
215    }
216    nals
217}
218
219// ===================== パラメータセット =====================
220
221#[derive(Default, Clone)]
222pub struct Sps {
223    pub profile_idc: u8,
224    pub level_idc: u8,
225    pub seq_parameter_set_id: u32,
226    pub log2_max_frame_num: u32,
227    pub pic_order_cnt_type: u32,
228    pub log2_max_pic_order_cnt_lsb: u32,
229    pub width: u32,
230    pub height: u32,
231    pub frame_mbs_only_flag: u32,
232    /// 横マクロブロック数 = width/16(クロップ前の MB 数)。
233    pub width_mbs: u32,
234    pub height_mbs: u32,
235}
236
237/// SPS RBSP をパースして解像度等を得る(baseline/main で必要な範囲)。
238pub fn parse_sps(rbsp: &[u8]) -> Result<Sps, &'static str> {
239    let mut r = BitReader::new(rbsp);
240    let mut sps = Sps {
241        profile_idc: r.read_bits(8) as u8,
242        ..Default::default()
243    };
244    let _constraint_flags = r.read_bits(8); // constraint_set flags + reserved
245    sps.level_idc = r.read_bits(8) as u8;
246    sps.seq_parameter_set_id = r.read_ue();
247
248    // high profile 系のクロマ情報(baseline/main では無いが安全のため分岐)
249    if matches!(
250        sps.profile_idc,
251        100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135
252    ) {
253        let chroma_format_idc = r.read_ue();
254        if chroma_format_idc == 3 {
255            let _separate_colour_plane_flag = r.read_bit();
256        }
257        let _bit_depth_luma_minus8 = r.read_ue();
258        let _bit_depth_chroma_minus8 = r.read_ue();
259        let _qpprime_y_zero_transform_bypass_flag = r.read_bit();
260        let seq_scaling_matrix_present_flag = r.read_bit();
261        if seq_scaling_matrix_present_flag == 1 {
262            // スケーリングリストはスキップせず存在ビットのみ消費(簡略: 値は未使用)
263            let lists = if chroma_format_idc != 3 { 8 } else { 12 };
264            for _ in 0..lists {
265                let present = r.read_bit();
266                if present == 1 {
267                    // scaling list の正確なスキップは未実装。ここに来る素材は稀。
268                    return Err("scaling list not supported yet");
269                }
270            }
271        }
272    }
273
274    sps.log2_max_frame_num = r.read_ue() + 4;
275    sps.pic_order_cnt_type = r.read_ue();
276    if sps.pic_order_cnt_type == 0 {
277        sps.log2_max_pic_order_cnt_lsb = r.read_ue() + 4;
278    } else if sps.pic_order_cnt_type == 1 {
279        let _delta_pic_order_always_zero_flag = r.read_bit();
280        let _offset_for_non_ref_pic = r.read_se();
281        let _offset_for_top_to_bottom_field = r.read_se();
282        // 不正データ由来の巨大値によるハングを防ぐ(規格上限 255)。
283        let num_ref_frames_in_pic_order_cnt_cycle = r.read_ue().min(256);
284        for _ in 0..num_ref_frames_in_pic_order_cnt_cycle {
285            let _ = r.read_se();
286        }
287    }
288    let _max_num_ref_frames = r.read_ue();
289    let _gaps_in_frame_num_value_allowed_flag = r.read_bit();
290
291    let pic_width_in_mbs_minus1 = r.read_ue();
292    let pic_height_in_map_units_minus1 = r.read_ue();
293    sps.frame_mbs_only_flag = r.read_bit();
294    if sps.frame_mbs_only_flag == 0 {
295        let _mb_adaptive_frame_field_flag = r.read_bit();
296    }
297    let _direct_8x8_inference_flag = r.read_bit();
298
299    let mut crop_left = 0u32;
300    let mut crop_right = 0u32;
301    let mut crop_top = 0u32;
302    let mut crop_bottom = 0u32;
303    let frame_cropping_flag = r.read_bit();
304    if frame_cropping_flag == 1 {
305        crop_left = r.read_ue();
306        crop_right = r.read_ue();
307        crop_top = r.read_ue();
308        crop_bottom = r.read_ue();
309    }
310
311    let width_mbs = pic_width_in_mbs_minus1 + 1;
312    let height_map_units = pic_height_in_map_units_minus1 + 1;
313    sps.width_mbs = width_mbs;
314    sps.height_mbs = height_map_units * (2 - sps.frame_mbs_only_flag);
315    let raw_width = width_mbs * 16;
316    let raw_height = height_map_units * 16 * (2 - sps.frame_mbs_only_flag);
317
318    // クロマ 4:2:0 を前提に crop 単位は水平2/垂直(2*(2-frame_mbs_only)) 画素。
319    let crop_unit_x = 2u32;
320    let crop_unit_y = 2u32 * (2 - sps.frame_mbs_only_flag);
321    sps.width = raw_width.saturating_sub((crop_left + crop_right) * crop_unit_x);
322    sps.height = raw_height.saturating_sub((crop_top + crop_bottom) * crop_unit_y);
323    if sps.width == 0 || sps.height == 0 {
324        return Err("SPS produced zero dimensions");
325    }
326    Ok(sps)
327}
328
329#[derive(Default, Clone)]
330pub struct Pps {
331    pub pic_parameter_set_id: u32,
332    pub seq_parameter_set_id: u32,
333    pub entropy_coding_mode_flag: u32, // 0=CAVLC, 1=CABAC
334    pub bottom_field_pic_order_in_frame_present_flag: u32, // = pic_order_present_flag
335    pub pic_init_qp: i32,              // 26 + pic_init_qp_minus26
336    pub deblocking_filter_control_present_flag: u32,
337    pub constrained_intra_pred_flag: u32,
338    pub redundant_pic_cnt_present_flag: u32,
339    pub chroma_qp_index_offset: i32,
340}
341
342pub fn parse_pps(rbsp: &[u8]) -> Result<Pps, &'static str> {
343    let mut r = BitReader::new(rbsp);
344    let mut pps = Pps {
345        pic_parameter_set_id: r.read_ue(),
346        seq_parameter_set_id: r.read_ue(),
347        entropy_coding_mode_flag: r.read_bit(),
348        ..Default::default()
349    };
350    pps.bottom_field_pic_order_in_frame_present_flag = r.read_bit();
351    let num_slice_groups_minus1 = r.read_ue();
352    if num_slice_groups_minus1 > 0 {
353        // FMO(スライスグループ)は未対応。ベースライン単一グループのみ。
354        return Err("slice groups not supported");
355    }
356    let _num_ref_idx_l0_default_active_minus1 = r.read_ue();
357    let _num_ref_idx_l1_default_active_minus1 = r.read_ue();
358    let _weighted_pred_flag = r.read_bit();
359    let _weighted_bipred_idc = r.read_bits(2);
360    let pic_init_qp_minus26 = r.read_se();
361    pps.pic_init_qp = 26 + pic_init_qp_minus26;
362    let _pic_init_qs_minus26 = r.read_se();
363    pps.chroma_qp_index_offset = r.read_se();
364    pps.deblocking_filter_control_present_flag = r.read_bit();
365    pps.constrained_intra_pred_flag = r.read_bit();
366    pps.redundant_pic_cnt_present_flag = r.read_bit();
367    Ok(pps)
368}
369
370/// avcC(AVCDecoderConfigurationRecord)から SPS/PPS と length_size を抽出。
371/// 返り値: (length_size, sps_nal_payloads, pps_nal_payloads)。各 payload は NAL ヘッダ込み。
372pub fn parse_avcc(avcc: &[u8]) -> Option<(usize, Vec<Vec<u8>>, Vec<Vec<u8>>)> {
373    if avcc.len() < 7 || avcc[0] != 1 {
374        return None;
375    }
376    let length_size = (avcc[4] & 0x03) as usize + 1;
377    let num_sps = (avcc[5] & 0x1F) as usize;
378    let mut i = 6;
379    let mut sps_list = Vec::new();
380    for _ in 0..num_sps {
381        if i + 2 > avcc.len() {
382            return None;
383        }
384        let len = ((avcc[i] as usize) << 8) | avcc[i + 1] as usize;
385        i += 2;
386        if i + len > avcc.len() {
387            return None;
388        }
389        sps_list.push(avcc[i..i + len].to_vec());
390        i += len;
391    }
392    if i >= avcc.len() {
393        return None;
394    }
395    let num_pps = avcc[i] as usize;
396    i += 1;
397    let mut pps_list = Vec::new();
398    for _ in 0..num_pps {
399        if i + 2 > avcc.len() {
400            return None;
401        }
402        let len = ((avcc[i] as usize) << 8) | avcc[i + 1] as usize;
403        i += 2;
404        if i + len > avcc.len() {
405            return None;
406        }
407        pps_list.push(avcc[i..i + len].to_vec());
408        i += len;
409    }
410    Some((length_size, sps_list, pps_list))
411}
412
413// ===================== デコーダ本体 =====================
414
415pub struct DecodedVideoFrame {
416    pub width: u32,
417    pub height: u32,
418    pub rgb_data: Vec<u8>,
419}
420
421pub struct H264Decoder {
422    sps: Option<Sps>,
423    pps: Option<Pps>,
424    width: u32,
425    height: u32,
426}
427
428impl Default for H264Decoder {
429    fn default() -> Self {
430        Self::new()
431    }
432}
433
434impl H264Decoder {
435    pub fn new() -> Self {
436        Self {
437            sps: None,
438            pps: None,
439            width: 0,
440            height: 0,
441        }
442    }
443
444    /// avcC 由来の SPS/PPS を事前登録する(MP4 経路用)。
445    pub fn set_parameter_sets(&mut self, sps_payloads: &[Vec<u8>], pps_payloads: &[Vec<u8>]) {
446        for p in sps_payloads {
447            if let Some(nu) = NalUnit::from_ebsp(p) {
448                if nu.unit_type == 7 {
449                    if let Ok(s) = parse_sps(&nu.rbsp) {
450                        self.width = s.width;
451                        self.height = s.height;
452                        self.sps = Some(s);
453                    }
454                }
455            }
456        }
457        for p in pps_payloads {
458            if let Some(nu) = NalUnit::from_ebsp(p) {
459                if nu.unit_type == 8 {
460                    if let Ok(pp) = parse_pps(&nu.rbsp) {
461                        self.pps = Some(pp);
462                    }
463                }
464            }
465        }
466    }
467
468    /// 1つの NAL(Annex-B でも AVCC 単体でも、先頭にスタートコードがあれば剥がす)を処理。
469    /// 互換性のため従来シグネチャを維持。
470    pub fn decode_nal(&mut self, nal_data: &[u8]) -> Result<DecodedVideoFrame, &'static str> {
471        // Annex-B スタートコードを剥がして単一 NAL とみなす。
472        let nals = if has_start_code(nal_data) {
473            split_annexb(nal_data)
474        } else {
475            // 先頭がヘッダバイトの裸 NAL とみなす
476            match NalUnit::from_ebsp(nal_data) {
477                Some(nu) => alloc::vec![nu],
478                None => return Err("invalid NAL"),
479            }
480        };
481
482        let mut produced: Option<DecodedVideoFrame> = None;
483        for nu in &nals {
484            match nu.unit_type {
485                7 => {
486                    let s = parse_sps(&nu.rbsp)?;
487                    self.width = s.width;
488                    self.height = s.height;
489                    self.sps = Some(s);
490                }
491                8 => {
492                    self.pps = Some(parse_pps(&nu.rbsp)?);
493                }
494                1 | 5 => {
495                    produced = Some(self.decode_slice(&nu.rbsp, nu.unit_type == 5)?);
496                }
497                _ => {}
498            }
499        }
500        produced.ok_or("no picture in NAL (parameter set only)")
501    }
502
503    /// AVCC(長さ前置)の1サンプル=アクセスユニットを処理し、含まれる picture を返す。
504    /// MP4 経路ではこちらを使う(SPS/PPS は事前に set_parameter_sets 済みが前提だが、
505    /// in-band の SPS/PPS が来た場合もここで取り込む)。
506    pub fn decode_access_unit_avcc(
507        &mut self,
508        sample: &[u8],
509        length_size: usize,
510    ) -> Option<DecodedVideoFrame> {
511        let nals = split_avcc(sample, length_size);
512        let mut produced = None;
513        for nu in &nals {
514            match nu.unit_type {
515                7 => {
516                    if let Ok(s) = parse_sps(&nu.rbsp) {
517                        self.width = s.width;
518                        self.height = s.height;
519                        self.sps = Some(s);
520                    }
521                }
522                8 => {
523                    if let Ok(pp) = parse_pps(&nu.rbsp) {
524                        self.pps = Some(pp);
525                    }
526                }
527                1 | 5 => {
528                    if let Ok(f) = self.decode_slice(&nu.rbsp, nu.unit_type == 5) {
529                        produced = Some(f);
530                    }
531                }
532                _ => {}
533            }
534        }
535        produced
536    }
537
538    fn decode_slice(
539        &mut self,
540        _rbsp: &[u8],
541        _is_idr: bool,
542    ) -> Result<DecodedVideoFrame, &'static str> {
543        // 解像度が未確定ならデフォルト(SPS 未受信時のフォールバック)。
544        let w = if self.width == 0 { 320 } else { self.width };
545        let h = if self.height == 0 { 180 } else { self.height };
546
547        // 次段で CAVLC 残差 / イントラ予測 / IDCT を実装する。
548        // 現状は実解像度のニュートラルなグレーフレームを返す(偽色生成は撤去済み)。
549        let rgb_data = alloc::vec![0x20u8; (w * h * 3) as usize];
550        Ok(DecodedVideoFrame {
551            width: w,
552            height: h,
553            rgb_data,
554        })
555    }
556}
557
558fn has_start_code(d: &[u8]) -> bool {
559    d.len() >= 3
560        && d[0] == 0
561        && d[1] == 0
562        && (d[2] == 1 || (d.len() >= 4 && d[2] == 0 && d[3] == 1))
563}
564
565// ===================== 残差: 逆量子化 + 4x4 逆整数変換 =====================
566//
567// H.264 baseline の残差復元の数学コア。スケーリングリスト無し(flat、weightScale=16)。
568// 仕様 8.5.12(逆量子化)+ 8.5.12.2(4x4 逆変換)に基づく。
569
570/// normAdjust4x4(m, class)。class: 0=偶偶, 1=奇奇, 2=その他。
571pub(crate) const NORM_ADJUST_4X4: [[i32; 3]; 6] = [
572    [10, 16, 13],
573    [11, 18, 14],
574    [13, 20, 16],
575    [14, 23, 18],
576    [16, 25, 20],
577    [18, 29, 23],
578];
579
580#[allow(clippy::manual_is_multiple_of)]
581fn pos_class_4x4(i: usize, j: usize) -> usize {
582    if i % 2 == 0 && j % 2 == 0 {
583        0
584    } else if i % 2 == 1 && j % 2 == 1 {
585        1
586    } else {
587        2
588    }
589}
590
591/// 4x4 残差ブロックの逆量子化(in-place、raster 順)。qp は 0..=51。
592/// LevelScale = weightScale(=16) * normAdjust4x4(qp%6, class)。
593pub fn dequant_4x4(block: &mut [i32; 16], qp: i32) {
594    let m = (qp % 6) as usize;
595    let shift = qp / 6;
596    for i in 0..4 {
597        for j in 0..4 {
598            let idx = i * 4 + j;
599            let level_scale = 16 * NORM_ADJUST_4X4[m][pos_class_4x4(i, j)];
600            let c = block[idx];
601            block[idx] = if qp >= 24 {
602                (c * level_scale) << (shift - 4)
603            } else {
604                (c * level_scale + (1 << (3 - shift))) >> (4 - shift)
605            };
606        }
607    }
608}
609
610/// 4x4 逆整数変換(in-place、raster 順)。最後に (x + 32) >> 6 で正規化。
611pub fn inverse_transform_4x4(block: &mut [i32; 16]) {
612    // 水平(行)変換
613    for i in 0..4 {
614        let o = i * 4;
615        let (d0, d1, d2, d3) = (block[o], block[o + 1], block[o + 2], block[o + 3]);
616        let e0 = d0 + d2;
617        let e1 = d0 - d2;
618        let e2 = (d1 >> 1) - d3;
619        let e3 = d1 + (d3 >> 1);
620        block[o] = e0 + e3;
621        block[o + 1] = e1 + e2;
622        block[o + 2] = e1 - e2;
623        block[o + 3] = e0 - e3;
624    }
625    // 垂直(列)変換 + 正規化
626    for j in 0..4 {
627        let (d0, d1, d2, d3) = (block[j], block[4 + j], block[8 + j], block[12 + j]);
628        let e0 = d0 + d2;
629        let e1 = d0 - d2;
630        let e2 = (d1 >> 1) - d3;
631        let e3 = d1 + (d3 >> 1);
632        block[j] = (e0 + e3 + 32) >> 6;
633        block[4 + j] = (e1 + e2 + 32) >> 6;
634        block[8 + j] = (e1 - e2 + 32) >> 6;
635        block[12 + j] = (e0 - e3 + 32) >> 6;
636    }
637}
638
639// ===================== イントラ予測(輝度) =====================
640//
641// 仕様 8.3.1(Intra_4x4)/ 8.3.3(Intra_16x16)。近傍は再構成済み輝度サンプル。
642// 返り値は raster 順(idx = y*W + x)の予測サンプル。
643
644fn clip1(v: i32) -> i32 {
645    v.clamp(0, 255)
646}
647
648/// Intra_4x4 予測(9 モード)。
649/// top: p[x,-1] x=0..7(上 + 右上), corner: p[-1,-1], left: p[-1,y] y=0..3。
650/// DC モードのみ可用性で式が変わるため top_avail/left_avail を渡す。
651pub fn intra_4x4_predict(
652    mode: u8,
653    top: &[i32; 8],
654    corner: i32,
655    left: &[i32; 4],
656    top_avail: bool,
657    left_avail: bool,
658) -> [i32; 16] {
659    let mut pred = [0i32; 16];
660    // top をインデックス -1(corner)込みで参照
661    let pt = |i: i32| -> i32 {
662        if i < 0 {
663            corner
664        } else {
665            top[i as usize]
666        }
667    };
668    let pl = |i: i32| -> i32 {
669        if i < 0 {
670            corner
671        } else {
672            left[i as usize]
673        }
674    };
675    let put = |x: usize, y: usize, v: i32, p: &mut [i32; 16]| {
676        p[y * 4 + x] = v;
677    };
678
679    match mode {
680        0 => {
681            // Vertical
682            for y in 0..4 {
683                for x in 0..4 {
684                    put(x, y, top[x], &mut pred);
685                }
686            }
687        }
688        1 => {
689            // Horizontal
690            for y in 0..4 {
691                for x in 0..4 {
692                    put(x, y, left[y], &mut pred);
693                }
694            }
695        }
696        2 => {
697            // DC
698            let dc = match (top_avail, left_avail) {
699                (true, true) => {
700                    (top[0] + top[1] + top[2] + top[3] + left[0] + left[1] + left[2] + left[3] + 4)
701                        >> 3
702                }
703                (true, false) => (top[0] + top[1] + top[2] + top[3] + 2) >> 2,
704                (false, true) => (left[0] + left[1] + left[2] + left[3] + 2) >> 2,
705                (false, false) => 128,
706            };
707            for v in pred.iter_mut() {
708                *v = dc;
709            }
710        }
711        3 => {
712            // Diagonal Down-Left
713            for y in 0..4i32 {
714                for x in 0..4i32 {
715                    let v = if x == 3 && y == 3 {
716                        (pt(6) + 3 * pt(7) + 2) >> 2
717                    } else {
718                        (pt(x + y) + 2 * pt(x + y + 1) + pt(x + y + 2) + 2) >> 2
719                    };
720                    put(x as usize, y as usize, v, &mut pred);
721                }
722            }
723        }
724        4 => {
725            // Diagonal Down-Right
726            for y in 0..4i32 {
727                for x in 0..4i32 {
728                    let v = if x > y {
729                        (pt(x - y - 2) + 2 * pt(x - y - 1) + pt(x - y) + 2) >> 2
730                    } else if x < y {
731                        (pl(y - x - 2) + 2 * pl(y - x - 1) + pl(y - x) + 2) >> 2
732                    } else {
733                        (pt(0) + 2 * corner + pl(0) + 2) >> 2
734                    };
735                    put(x as usize, y as usize, v, &mut pred);
736                }
737            }
738        }
739        5 => {
740            // Vertical-Right
741            for y in 0..4i32 {
742                for x in 0..4i32 {
743                    let z = 2 * x - y;
744                    let v = if z >= 0 && z % 2 == 0 {
745                        (pt(x - (y >> 1) - 1) + pt(x - (y >> 1)) + 1) >> 1
746                    } else if z >= 0 {
747                        (pt(x - (y >> 1) - 2) + 2 * pt(x - (y >> 1) - 1) + pt(x - (y >> 1)) + 2)
748                            >> 2
749                    } else if z == -1 {
750                        (pl(0) + 2 * corner + pt(0) + 2) >> 2
751                    } else {
752                        (pl(y - 1) + 2 * pl(y - 2) + pl(y - 3) + 2) >> 2
753                    };
754                    put(x as usize, y as usize, v, &mut pred);
755                }
756            }
757        }
758        6 => {
759            // Horizontal-Down
760            for y in 0..4i32 {
761                for x in 0..4i32 {
762                    let z = 2 * y - x;
763                    let v = if z >= 0 && z % 2 == 0 {
764                        (pl(y - (x >> 1) - 1) + pl(y - (x >> 1)) + 1) >> 1
765                    } else if z >= 0 {
766                        (pl(y - (x >> 1) - 2) + 2 * pl(y - (x >> 1) - 1) + pl(y - (x >> 1)) + 2)
767                            >> 2
768                    } else if z == -1 {
769                        (pl(0) + 2 * corner + pt(0) + 2) >> 2
770                    } else {
771                        (pt(x - 1) + 2 * pt(x - 2) + pt(x - 3) + 2) >> 2
772                    };
773                    put(x as usize, y as usize, v, &mut pred);
774                }
775            }
776        }
777        7 => {
778            // Vertical-Left
779            for y in 0..4i32 {
780                for x in 0..4i32 {
781                    let v = if y % 2 == 0 {
782                        (pt(x + (y >> 1)) + pt(x + (y >> 1) + 1) + 1) >> 1
783                    } else {
784                        (pt(x + (y >> 1)) + 2 * pt(x + (y >> 1) + 1) + pt(x + (y >> 1) + 2) + 2)
785                            >> 2
786                    };
787                    put(x as usize, y as usize, v, &mut pred);
788                }
789            }
790        }
791        8 => {
792            // Horizontal-Up
793            for y in 0..4i32 {
794                for x in 0..4i32 {
795                    let z = x + 2 * y;
796                    let v = if z < 5 && z % 2 == 0 {
797                        (pl(y + (x >> 1)) + pl(y + (x >> 1) + 1) + 1) >> 1
798                    } else if z < 5 {
799                        (pl(y + (x >> 1)) + 2 * pl(y + (x >> 1) + 1) + pl(y + (x >> 1) + 2) + 2)
800                            >> 2
801                    } else if z == 5 {
802                        (pl(2) + 3 * pl(3) + 2) >> 2
803                    } else {
804                        pl(3)
805                    };
806                    put(x as usize, y as usize, v, &mut pred);
807                }
808            }
809        }
810        _ => {}
811    }
812    pred
813}
814
815/// Intra_16x16 予測(4 モード: 0=Vertical, 1=Horizontal, 2=DC, 3=Plane)。
816/// top: p[x,-1] x=0..15, corner: p[-1,-1], left: p[-1,y] y=0..15。
817pub fn intra_16x16_predict(
818    mode: u8,
819    top: &[i32; 16],
820    corner: i32,
821    left: &[i32; 16],
822    top_avail: bool,
823    left_avail: bool,
824) -> [i32; 256] {
825    let mut pred = [0i32; 256];
826    match mode {
827        0 => {
828            for y in 0..16 {
829                for x in 0..16 {
830                    pred[y * 16 + x] = top[x];
831                }
832            }
833        }
834        1 => {
835            for y in 0..16 {
836                for x in 0..16 {
837                    pred[y * 16 + x] = left[y];
838                }
839            }
840        }
841        2 => {
842            let st: i32 = top.iter().sum();
843            let sl: i32 = left.iter().sum();
844            let dc = match (top_avail, left_avail) {
845                (true, true) => (st + sl + 16) >> 5,
846                (true, false) => (st + 8) >> 4,
847                (false, true) => (sl + 8) >> 4,
848                (false, false) => 128,
849            };
850            for v in pred.iter_mut() {
851                *v = dc;
852            }
853        }
854        3 => {
855            // Plane
856            let mut h = 0i32;
857            let mut v = 0i32;
858            for i in 0..8i32 {
859                // H: x'=0..7, p[8+x',-1] - p[6-x',-1]; corner for index -1
860                let pt = |k: i32| -> i32 {
861                    if k < 0 {
862                        corner
863                    } else {
864                        top[k as usize]
865                    }
866                };
867                let pl = |k: i32| -> i32 {
868                    if k < 0 {
869                        corner
870                    } else {
871                        left[k as usize]
872                    }
873                };
874                h += (i + 1) * (pt(8 + i) - pt(6 - i));
875                v += (i + 1) * (pl(8 + i) - pl(6 - i));
876            }
877            let b = (5 * h + 32) >> 6;
878            let c = (5 * v + 32) >> 6;
879            let a = 16 * (top[15] + left[15]);
880            for y in 0..16i32 {
881                for x in 0..16i32 {
882                    let val = (a + b * (x - 7) + c * (y - 7) + 16) >> 5;
883                    pred[(y * 16 + x) as usize] = clip1(val);
884                }
885            }
886        }
887        _ => {}
888    }
889    pred
890}
891
892/// Intra chroma 8x8 予測(4:2:0、4 モード: 0=DC, 1=Horizontal, 2=Vertical, 3=Plane)。
893/// 仕様 8.3.4。top: p[x,-1] x=0..7, corner: p[-1,-1], left: p[-1,y] y=0..7。
894pub fn intra_chroma_predict(
895    mode: u8,
896    top: &[i32; 8],
897    corner: i32,
898    left: &[i32; 8],
899    top_avail: bool,
900    left_avail: bool,
901) -> [i32; 64] {
902    let mut pred = [0i32; 64];
903    match mode {
904        0 => {
905            // DC(4 つの 4x4 ブロックごとに規則が異なる)
906            let t0: i32 = top[0..4].iter().sum();
907            let t1: i32 = top[4..8].iter().sum();
908            let l0: i32 = left[0..4].iter().sum();
909            let l1: i32 = left[4..8].iter().sum();
910            // 各 4x4 ブロックの DC
911            let dc00 = match (top_avail, left_avail) {
912                (true, true) => (t0 + l0 + 4) >> 3,
913                (true, false) => (t0 + 2) >> 2,
914                (false, true) => (l0 + 2) >> 2,
915                (false, false) => 128,
916            };
917            let dc10 = if top_avail {
918                (t1 + 2) >> 2
919            } else if left_avail {
920                (l0 + 2) >> 2
921            } else {
922                128
923            };
924            let dc01 = if left_avail {
925                (l1 + 2) >> 2
926            } else if top_avail {
927                (t0 + 2) >> 2
928            } else {
929                128
930            };
931            let dc11 = match (top_avail, left_avail) {
932                (true, true) => (t1 + l1 + 4) >> 3,
933                (true, false) => (t1 + 2) >> 2,
934                (false, true) => (l1 + 2) >> 2,
935                (false, false) => 128,
936            };
937            for y in 0..8 {
938                for x in 0..8 {
939                    let dc = match (x < 4, y < 4) {
940                        (true, true) => dc00,
941                        (false, true) => dc10,
942                        (true, false) => dc01,
943                        (false, false) => dc11,
944                    };
945                    pred[y * 8 + x] = dc;
946                }
947            }
948        }
949        1 => {
950            for y in 0..8 {
951                for x in 0..8 {
952                    pred[y * 8 + x] = left[y];
953                }
954            }
955        }
956        2 => {
957            for y in 0..8 {
958                for x in 0..8 {
959                    pred[y * 8 + x] = top[x];
960                }
961            }
962        }
963        3 => {
964            // Plane
965            let pt = |k: i32| -> i32 {
966                if k < 0 {
967                    corner
968                } else {
969                    top[k as usize]
970                }
971            };
972            let pl = |k: i32| -> i32 {
973                if k < 0 {
974                    corner
975                } else {
976                    left[k as usize]
977                }
978            };
979            let mut h = 0i32;
980            let mut v = 0i32;
981            for i in 0..4i32 {
982                h += (i + 1) * (pt(4 + i) - pt(2 - i));
983                v += (i + 1) * (pl(4 + i) - pl(2 - i));
984            }
985            let b = (17 * h + 16) >> 5;
986            let c = (17 * v + 16) >> 5;
987            let a = 16 * (top[7] + left[7]);
988            for y in 0..8i32 {
989                for x in 0..8i32 {
990                    pred[(y * 8 + x) as usize] = clip1((a + b * (x - 3) + c * (y - 3) + 16) >> 5);
991                }
992            }
993        }
994        _ => {}
995    }
996    pred
997}
998
999// ===================== 自己テスト =====================
1000
1001/// 既知解ベクタによる検証(起動時に実行)。
1002pub fn selftest() -> (usize, usize) {
1003    let mut passed = 0usize;
1004    let mut total = 0usize;
1005    let mut check = |cond: bool, label: &str| {
1006        total += 1;
1007        if cond {
1008            passed += 1;
1009        } else {
1010            crate::println!("H264_SELFTEST FAIL: {}", label);
1011        }
1012    };
1013
1014    // read_bits: 0b1011_0010 = 0xB2
1015    {
1016        let data = [0xB2u8];
1017        let mut r = BitReader::new(&data);
1018        check(r.read_bits(4) == 0b1011, "read_bits hi nibble");
1019        check(r.read_bits(4) == 0b0010, "read_bits lo nibble");
1020    }
1021
1022    // Exp-Golomb ue(v) の既知マッピング:
1023    //  "1"=0, "010"=1, "011"=2, "00100"=3, "00101"=4, "00110"=5, "00111"=6
1024    // ビット列 1 010 011 00100 を 1 バイト目に詰める: 1 0 1 0 0 1 1 0 = 0xA6
1025    //  続き 0100... → 次バイト 0100_0000 = 0x40
1026    {
1027        let data = [0xA6u8, 0x40u8];
1028        let mut r = BitReader::new(&data);
1029        check(r.read_ue() == 0, "ue=0");
1030        check(r.read_ue() == 1, "ue=1");
1031        check(r.read_ue() == 2, "ue=2");
1032        check(r.read_ue() == 3, "ue=3");
1033    }
1034
1035    // se(v): ue 0->0,1->1,2->-1,3->2,4->-2
1036    // ue 列 0,1,2,3,4 を se で読む。上と同じ先頭 + "00101"(=4)。
1037    // 1 010 011 00100 00101 → byte0=0xA6, byte1=0b0100_0010_1.. = 0x42 さらに 1 ビット
1038    {
1039        let data = [0xA6u8, 0x42u8, 0x80u8];
1040        let mut r = BitReader::new(&data);
1041        check(r.read_se() == 0, "se=0");
1042        check(r.read_se() == 1, "se=1");
1043        check(r.read_se() == -1, "se=-1");
1044        check(r.read_se() == 2, "se=2");
1045        check(r.read_se() == -2, "se=-2");
1046    }
1047
1048    // RBSP de-emulation: 00 00 03 01 -> 00 00 01
1049    {
1050        let inb = [0x00u8, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x03];
1051        let out = extract_rbsp(&inb);
1052        check(
1053            out == alloc::vec![0x00, 0x00, 0x01, 0x00, 0x00, 0x03],
1054            "rbsp de-emulation",
1055        );
1056    }
1057
1058    // Annex-B 分割: 2 つの NAL(type 7 と 5)
1059    {
1060        let stream = [
1061            0x00, 0x00, 0x00, 0x01, 0x67, 0xAA, 0xBB, // SPS(7)
1062            0x00, 0x00, 0x01, 0x65, 0xCC, // IDR slice(5)
1063        ];
1064        let nals = split_annexb(&stream);
1065        check(nals.len() == 2, "annexb count");
1066        check(
1067            nals.first().map(|n| n.unit_type == 7).unwrap_or(false),
1068            "annexb nal0 type7",
1069        );
1070        check(
1071            nals.get(1).map(|n| n.unit_type == 5).unwrap_or(false),
1072            "annexb nal1 type5",
1073        );
1074    }
1075
1076    // AVCC 分割: [len=3][67 AA BB][len=2][65 CC]
1077    {
1078        let stream = [0, 0, 0, 3, 0x67, 0xAA, 0xBB, 0, 0, 0, 2, 0x65, 0xCC];
1079        let nals = split_avcc(&stream, 4);
1080        check(nals.len() == 2, "avcc count");
1081        check(
1082            nals.get(1)
1083                .map(|n| n.unit_type == 5 && n.rbsp == alloc::vec![0xCC])
1084                .unwrap_or(false),
1085            "avcc nal1",
1086        );
1087    }
1088
1089    // SPS パース(実 bitstream): baseline 160x144。
1090    // このバイト列は手計算でエンコードした検証済みベクタ:
1091    //   profile=66, level=30, sps_id=0, log2_max_frame_num=4, poc_type=0,
1092    //   max_num_ref=1, pic_width_in_mbs_minus1=9(=>160), height_map_units_minus1=8,
1093    //   frame_mbs_only=1, no cropping。
1094    {
1095        let sps = [0x42u8, 0x00, 0x1e, 0xF4, 0x14, 0x26, 0x20];
1096        match parse_sps(&sps) {
1097            Ok(s) => {
1098                check(s.profile_idc == 66, "sps profile baseline");
1099                check(s.frame_mbs_only_flag == 1, "sps frame_mbs_only");
1100                check(s.width == 160 && s.height == 144, "sps 160x144");
1101            }
1102            Err(_) => check(false, "sps parse ok"),
1103        }
1104    }
1105
1106    // 4x4 逆変換: 純 DC 入力は平坦ブロック (X+32)>>6 を生成。
1107    {
1108        let mut b = [0i32; 16];
1109        b[0] = 64;
1110        inverse_transform_4x4(&mut b);
1111        check(b.iter().all(|&v| v == 1), "idct pure DC=64 -> 1");
1112
1113        let mut b2 = [0i32; 16];
1114        b2[0] = 4096;
1115        inverse_transform_4x4(&mut b2);
1116        check(b2.iter().all(|&v| v == 64), "idct pure DC=4096 -> 64");
1117    }
1118
1119    // 4x4 逆変換: AC 係数 d(0,1)=64 の手計算結果(各行 [1,1,0,-1])。
1120    {
1121        let mut b = [0i32; 16];
1122        b[1] = 64;
1123        inverse_transform_4x4(&mut b);
1124        let expected = [1, 1, 0, -1, 1, 1, 0, -1, 1, 1, 0, -1, 1, 1, 0, -1];
1125        check(b == expected, "idct AC d(0,1)=64");
1126    }
1127
1128    // 逆量子化 + 逆変換の連鎖: c(0,0)=1, QP=24 -> dequant 160 -> 平坦 3。
1129    {
1130        let mut b = [0i32; 16];
1131        b[0] = 1;
1132        dequant_4x4(&mut b, 24);
1133        check(b[0] == 160, "dequant DC c=1 qp=24 -> 160");
1134        inverse_transform_4x4(&mut b);
1135        check(b.iter().all(|&v| v == 3), "dequant+idct -> flat 3");
1136    }
1137
1138    // 逆量子化の QP+6 倍化特性(ステップサイズが2倍)。
1139    {
1140        let mut a = [0i32; 16];
1141        a[5] = 1; // (1,1) class1
1142        let mut c = [0i32; 16];
1143        c[5] = 1;
1144        dequant_4x4(&mut a, 24);
1145        dequant_4x4(&mut c, 30);
1146        check(c[5] == a[5] * 2, "dequant qp+6 doubles");
1147    }
1148
1149    // イントラ 4x4: 定数近傍不変条件 — 全近傍が c なら全モードの全画素が c。
1150    // 重み付き平均の係数和とインデックス範囲を全 9 モードで検証する強力な不変条件。
1151    {
1152        let c = 100i32;
1153        let top = [c; 8];
1154        let left = [c; 4];
1155        let mut all_ok = true;
1156        for mode in 0..9u8 {
1157            let p = intra_4x4_predict(mode, &top, c, &left, true, true);
1158            if !p.iter().all(|&v| v == c) {
1159                all_ok = false;
1160                crate::println!("  intra4x4 mode {} broke constant", mode);
1161            }
1162        }
1163        check(all_ok, "intra4x4 constant invariant all modes");
1164    }
1165
1166    // イントラ 4x4 Vertical: 上ランプ [10,20,30,40] が各列にコピーされる。
1167    {
1168        let top = [10, 20, 30, 40, 50, 60, 70, 80];
1169        let left = [0; 4];
1170        let p = intra_4x4_predict(0, &top, 0, &left, true, true);
1171        check(
1172            p[0] == 10 && p[1] == 20 && p[2] == 30 && p[3] == 40 && p[12] == 10 && p[15] == 40,
1173            "intra4x4 vertical ramp",
1174        );
1175    }
1176
1177    // イントラ 4x4 Horizontal: 左ランプ [10,20,30,40] が各行にコピーされる。
1178    {
1179        let top = [0; 8];
1180        let left = [10, 20, 30, 40];
1181        let p = intra_4x4_predict(1, &top, 0, &left, true, true);
1182        check(
1183            p[0] == 10 && p[1] == 10 && p[4] == 20 && p[8] == 30 && p[12] == 40 && p[15] == 40,
1184            "intra4x4 horizontal ramp",
1185        );
1186    }
1187
1188    // イントラ 4x4 DC: top=[8,8,8,8], left=[16,16,16,16] -> (32+64+4)>>3 = 12。
1189    {
1190        let top = [8, 8, 8, 8, 8, 8, 8, 8];
1191        let left = [16, 16, 16, 16];
1192        let p = intra_4x4_predict(2, &top, 0, &left, true, true);
1193        check(p.iter().all(|&v| v == 12), "intra4x4 DC both avail");
1194    }
1195
1196    // イントラ 16x16: 定数近傍不変条件(全 4 モード、Plane 含む)。
1197    {
1198        let c = 77i32;
1199        let top = [c; 16];
1200        let left = [c; 16];
1201        let mut all_ok = true;
1202        for mode in 0..4u8 {
1203            let p = intra_16x16_predict(mode, &top, c, &left, true, true);
1204            if !p.iter().all(|&v| v == c) {
1205                all_ok = false;
1206                crate::println!("  intra16x16 mode {} broke constant", mode);
1207            }
1208        }
1209        check(all_ok, "intra16x16 constant invariant all modes");
1210    }
1211
1212    // イントラ 16x16 DC: top 全 40, left 全 80 -> (640+1280+16)>>5 = 60。
1213    {
1214        let top = [40; 16];
1215        let left = [80; 16];
1216        let p = intra_16x16_predict(2, &top, 0, &left, true, true);
1217        check(p.iter().all(|&v| v == 60), "intra16x16 DC value");
1218    }
1219
1220    // 実 x264 が出力した SPS(16x16, baseline)の EBSP を脱エミュレーション→パース。
1221    // 合成ベクタだけでなく実エンコーダ出力で SPS パーサを検証する。
1222    {
1223        let ebsp = [
1224            0x42u8, 0xc0, 0x0a, 0xdd, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00,
1225            0x03, 0x00, 0x83, 0xc4, 0x89, 0xe0,
1226        ];
1227        let rbsp = extract_rbsp(&ebsp);
1228        match parse_sps(&rbsp) {
1229            Ok(s) => {
1230                check(s.profile_idc == 66, "real x264 SPS profile=66");
1231                check(s.width == 16 && s.height == 16, "real x264 SPS 16x16");
1232            }
1233            Err(_) => check(false, "real x264 SPS parse ok"),
1234        }
1235    }
1236
1237    // クロマ 8x8: 定数近傍不変条件(全 4 モード、Plane 含む)。
1238    {
1239        let c = 55i32;
1240        let top = [c; 8];
1241        let left = [c; 8];
1242        let mut all_ok = true;
1243        for mode in 0..4u8 {
1244            let p = intra_chroma_predict(mode, &top, c, &left, true, true);
1245            if !p.iter().all(|&v| v == c) {
1246                all_ok = false;
1247                crate::println!("  chroma mode {} broke constant", mode);
1248            }
1249        }
1250        check(all_ok, "chroma constant invariant all modes");
1251    }
1252
1253    // クロマ DC: 4 ブロックで別々の DC を計算する規則の検証。
1254    // top=[8,8,8,8, 40,40,40,40], left=[16,16,16,16, 60,60,60,60], 両可用。
1255    // t0=32,t1=160,l0=64,l1=240。
1256    // dc00=(32+64+4)>>3=12, dc10=(160+2)>>2=40, dc01=(240+2)>>2=60, dc11=(160+240+4)>>3=50。
1257    {
1258        let top = [8, 8, 8, 8, 40, 40, 40, 40];
1259        let left = [16, 16, 16, 16, 60, 60, 60, 60];
1260        let p = intra_chroma_predict(0, &top, 0, &left, true, true);
1261        check(p[0] == 12, "chroma DC block(0,0)=12");
1262        check(p[4] == 40, "chroma DC block(4,0)=40");
1263        check(p[32] == 60, "chroma DC block(0,4)=60");
1264        check(p[36] == 50, "chroma DC block(4,4)=50");
1265    }
1266
1267    (passed, total)
1268}
1269
1270/// デバッグ用の文字列表現。
1271pub fn describe_sps(s: &Sps) -> String {
1272    alloc::format!(
1273        "SPS profile={} level={} {}x{} mbs_only={}",
1274        s.profile_idc,
1275        s.level_idc,
1276        s.width,
1277        s.height,
1278        s.frame_mbs_only_flag
1279    )
1280}