Skip to main content

h264_reader/nal/
prefix.rs

1//! Parser for `prefix_nal_unit_rbsp()` (NAL unit type 14, spec 7.3.2.12).
2//!
3//! A prefix NAL unit carries an MVC/SVC NAL header extension. When the
4//! extension is SVC and `nal_ref_idc != 0`, it also carries reference
5//! picture base marking information (`prefix_nal_unit_svc()`, spec
6//! F.7.3.2.12.1). MVC prefix NALs have an empty RBSP body.
7
8use crate::nal::{parse_nal_header_extension, Nal, NalHeaderError, NalHeaderExtension};
9use crate::rbsp::{BitRead, BitReaderError};
10
11/// Parsed `prefix_nal_unit_rbsp()` (NAL unit type 14).
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct PrefixNalUnit {
14    pub header_extension: NalHeaderExtension,
15    /// Present when `nal_ref_idc != 0` and the extension is SVC.
16    pub ref_base_pic: Option<PrefixNalUnitRef>,
17}
18
19/// Reference base picture information within an SVC prefix NAL unit
20/// (spec F.7.3.2.12.1, `prefix_nal_unit_svc()` when `nal_ref_idc != 0`).
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct PrefixNalUnitRef {
23    pub store_ref_base_pic_flag: bool,
24    pub dec_ref_base_pic_marking: Option<DecRefBasePicMarking>,
25    pub additional_prefix_nal_unit_extension_flag: bool,
26}
27
28/// Decoded reference base picture marking syntax (spec G.7.3.3.5,
29/// `dec_ref_base_pic_marking`).
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct DecRefBasePicMarking {
32    pub operations: Vec<DecRefBasePicMarkingOp>,
33}
34
35/// A single operation in `dec_ref_base_pic_marking()`.
36#[derive(Clone, Debug, PartialEq, Eq)]
37pub enum DecRefBasePicMarkingOp {
38    /// `memory_management_base_control_operation == 1`:
39    /// Mark a short-term reference base picture as unused.
40    ShortTermUnusedForRef {
41        difference_of_base_pic_nums_minus1: u32,
42    },
43    /// `memory_management_base_control_operation == 2`:
44    /// Mark a long-term reference base picture as unused.
45    LongTermUnusedForRef { long_term_base_pic_num: u32 },
46}
47
48#[derive(Debug)]
49pub enum PrefixNalUnitError {
50    RbspError(BitReaderError),
51    IoError(std::io::Error),
52    HeaderError(NalHeaderError),
53    /// `memory_management_base_control_operation` had an invalid value.
54    InvalidMemoryManagementBaseControlOperation(u32),
55}
56impl From<BitReaderError> for PrefixNalUnitError {
57    fn from(e: BitReaderError) -> Self {
58        PrefixNalUnitError::RbspError(e)
59    }
60}
61impl From<NalHeaderError> for PrefixNalUnitError {
62    fn from(e: NalHeaderError) -> Self {
63        PrefixNalUnitError::HeaderError(e)
64    }
65}
66impl From<std::io::Error> for PrefixNalUnitError {
67    fn from(e: std::io::Error) -> Self {
68        PrefixNalUnitError::IoError(e)
69    }
70}
71
72impl PrefixNalUnit {
73    /// Parse a prefix NAL unit from its raw NAL representation.
74    ///
75    /// The NAL must have `nal_unit_type == 14`. The 3-byte header extension
76    /// is read first, then the RBSP body is parsed if applicable.
77    pub fn from_nal<N: Nal>(nal: &N) -> Result<PrefixNalUnit, PrefixNalUnitError> {
78        let header = nal.header()?;
79        let (header_extension, rbsp_reader) = parse_nal_header_extension(nal)?;
80        let mut r = crate::rbsp::BitReader::new(rbsp_reader);
81
82        let ref_base_pic = match &header_extension {
83            NalHeaderExtension::Svc(_) => {
84                // SVC prefix NAL: parse prefix_nal_unit_svc() per F.7.3.2.12.1
85                if header.nal_ref_idc() != 0 {
86                    let store_ref_base_pic_flag = r.read_bit("store_ref_base_pic_flag")?;
87                    let dec_ref_base_pic_marking = if store_ref_base_pic_flag {
88                        Some(read_dec_ref_base_pic_marking(&mut r)?)
89                    } else {
90                        None
91                    };
92                    let additional_prefix_nal_unit_extension_flag =
93                        r.read_bit("additional_prefix_nal_unit_extension_flag")?;
94                    Some(PrefixNalUnitRef {
95                        store_ref_base_pic_flag,
96                        dec_ref_base_pic_marking,
97                        additional_prefix_nal_unit_extension_flag,
98                    })
99                } else {
100                    None
101                }
102            }
103            NalHeaderExtension::Mvc(_) => {
104                // MVC prefix NAL: RBSP body is empty per 7.3.2.12
105                None
106            }
107        };
108
109        Ok(PrefixNalUnit {
110            header_extension,
111            ref_base_pic,
112        })
113    }
114}
115
116/// Read `dec_ref_base_pic_marking()` syntax (spec G.7.3.3.5).
117fn read_dec_ref_base_pic_marking<R: BitRead>(
118    r: &mut R,
119) -> Result<DecRefBasePicMarking, PrefixNalUnitError> {
120    let mut operations = Vec::new();
121    loop {
122        let op = r.read_ue("memory_management_base_control_operation")?;
123        match op {
124            0 => break,
125            1 => {
126                let difference_of_base_pic_nums_minus1 =
127                    r.read_ue("difference_of_base_pic_nums_minus1")?;
128                operations.push(DecRefBasePicMarkingOp::ShortTermUnusedForRef {
129                    difference_of_base_pic_nums_minus1,
130                });
131            }
132            2 => {
133                let long_term_base_pic_num = r.read_ue("long_term_base_pic_num")?;
134                operations.push(DecRefBasePicMarkingOp::LongTermUnusedForRef {
135                    long_term_base_pic_num,
136                });
137            }
138            _ => {
139                return Err(PrefixNalUnitError::InvalidMemoryManagementBaseControlOperation(op));
140            }
141        }
142    }
143    Ok(DecRefBasePicMarking { operations })
144}
145
146#[cfg(test)]
147mod test {
148    use super::*;
149    use crate::nal::RefNal;
150
151    #[test]
152    fn parse_prefix_nal_mvc_no_ref() {
153        // NAL header: nal_ref_idc=0, nal_unit_type=14
154        // Header byte: 0b0_00_01110 = 0x0E
155        // MVC extension: svc=0, non_idr=1, priority_id=0, view_id=1, temporal_id=0,
156        //   anchor=0, inter_view=1, reserved=1
157        // No RBSP body since nal_ref_idc=0
158        let data: &[u8] = &[
159            0x0E,        // NAL header: ref_idc=0, type=14
160            0b0100_0000, // svc=0, non_idr=1, priority_id=0
161            0x00,        // view_id high 8 = 0
162            0b0100_0011, // view_id low 2 = 01 (view_id=1), temporal=0, anchor=0, inter_view=1, reserved=1
163        ];
164        let nal = RefNal::new(data, &[], true);
165        let prefix = PrefixNalUnit::from_nal(&nal).unwrap();
166        match prefix.header_extension {
167            NalHeaderExtension::Mvc(mvc) => {
168                assert!(mvc.non_idr_flag());
169                assert_eq!(mvc.view_id(), 1);
170                assert!(mvc.inter_view_flag());
171            }
172            _ => panic!("expected MVC extension"),
173        }
174        assert!(prefix.ref_base_pic.is_none());
175    }
176
177    #[test]
178    fn parse_prefix_nal_mvc_with_ref() {
179        // NAL header: nal_ref_idc=3, nal_unit_type=14
180        // Header byte: 0b0_11_01110 = 0x6E
181        // MVC extension: svc=0, all zeros, view_id=0, reserved=1
182        // MVC prefix NALs have no RBSP body, so ref_base_pic should be None.
183        let data: &[u8] = &[
184            0x6E,        // NAL header: ref_idc=3, type=14
185            0x00,        // svc=0, non_idr=0, priority_id=0
186            0x00,        // view_id high 8 = 0
187            0b0000_0001, // view_id=0, temporal=0, anchor=0, inter_view=0, reserved=1
188        ];
189        let nal = RefNal::new(data, &[], true);
190        let prefix = PrefixNalUnit::from_nal(&nal).unwrap();
191        assert!(prefix.ref_base_pic.is_none());
192    }
193
194    #[test]
195    fn parse_prefix_nal_svc_no_ref() {
196        // NAL header: nal_ref_idc=0, nal_unit_type=14 → 0b0_00_01110 = 0x0E
197        // SVC extension: svc=1, idr=0, priority_id=0
198        //   no_inter_layer_pred=0, dependency_id=0, quality_id=0
199        //   temporal_id=0, use_ref_base=0, discardable=0, output=0, reserved=0b11
200        let data: &[u8] = &[
201            0x0E, // NAL header: ref_idc=0
202            0x80, // svc=1, idr=0, priority_id=0
203            0x00, // no_inter_layer=0, dep_id=0, quality_id=0
204            0x03, // temporal=0, use_ref=0, discard=0, output=0, reserved=0b11
205        ];
206        let nal = RefNal::new(data, &[], true);
207        let prefix = PrefixNalUnit::from_nal(&nal).unwrap();
208        match prefix.header_extension {
209            NalHeaderExtension::Svc(svc) => {
210                assert!(!svc.idr_flag());
211                assert_eq!(svc.priority_id(), 0);
212            }
213            _ => panic!("expected SVC extension"),
214        }
215        // nal_ref_idc=0, so ref_base_pic is None
216        assert!(prefix.ref_base_pic.is_none());
217    }
218
219    #[test]
220    fn parse_prefix_nal_svc_with_ref_no_marking() {
221        // NAL header: nal_ref_idc=3, nal_unit_type=14
222        // Header byte: 0b0_11_01110 = 0x6E
223        // SVC extension: svc=1, idr=0, priority_id=0
224        // RBSP: store_ref_base_pic_flag=0, additional_prefix_nal_unit_extension_flag=0
225        //       then rbsp_trailing_bits (1 + padding)
226        let data: &[u8] = &[
227            0x6E, // NAL header: ref_idc=3, type=14
228            0x80, // svc=1, idr=0, priority_id=0
229            0x00, // no_inter_layer=0, dep_id=0, quality_id=0
230            0x03, // temporal=0, use_ref=0, discard=0, output=0, reserved=0b11
231            // RBSP body: store_ref_base_pic_flag=0, additional=0, rbsp_trailing=1, padding=00000
232            0b0010_0000,
233        ];
234        let nal = RefNal::new(data, &[], true);
235        let prefix = PrefixNalUnit::from_nal(&nal).unwrap();
236        let ref_base = prefix.ref_base_pic.unwrap();
237        assert!(!ref_base.store_ref_base_pic_flag);
238        assert!(ref_base.dec_ref_base_pic_marking.is_none());
239        assert!(!ref_base.additional_prefix_nal_unit_extension_flag);
240    }
241
242    #[test]
243    fn parse_prefix_nal_svc_with_marking() {
244        // NAL header: nal_ref_idc=2, nal_unit_type=14 → 0b0_10_01110 = 0x4E
245        // SVC extension: svc=1, idr=0, priority_id=0
246        // RBSP body:
247        //   store_ref_base_pic_flag=1
248        //   dec_ref_base_pic_marking:
249        //     op=1 (ue: 010), difference_of_base_pic_nums_minus1=0 (ue: 1)
250        //     op=0 (ue: 1) -- end
251        //   additional_prefix_nal_unit_extension_flag=0
252        //   rbsp_trailing_bits: 1 + padding
253        //
254        // Bits: 1  010 1  1  0  1 00000
255        //       ^  ^^^ ^  ^  ^  ^ ^^^^^
256        //       |  |   |  |  |  | padding
257        //       |  |   |  |  |  rbsp stop bit
258        //       |  |   |  |  additional=0
259        //       |  |   |  op=0 (end)
260        //       |  |   diff_minus1=0
261        //       |  op=1
262        //       store=1
263        let data: &[u8] = &[
264            0x4E, // NAL header
265            0x80, // svc=1, idr=0, priority_id=0
266            0x00, // no_inter_layer=0, dep_id=0, quality_id=0
267            0x03, // temporal=0, use_ref=0, discard=0, output=0, reserved=0b11
268            // RBSP: 1_010_1_1_0_1_00000 = 0b1010_1101 0b0000_0xxx
269            0b1010_1101,
270            0b0000_0000, // trailing bits with padding
271        ];
272        let nal = RefNal::new(data, &[], true);
273        let prefix = PrefixNalUnit::from_nal(&nal).unwrap();
274        let ref_base = prefix.ref_base_pic.unwrap();
275        assert!(ref_base.store_ref_base_pic_flag);
276        assert!(!ref_base.additional_prefix_nal_unit_extension_flag);
277        let marking = ref_base.dec_ref_base_pic_marking.unwrap();
278        assert_eq!(marking.operations.len(), 1);
279        match &marking.operations[0] {
280            DecRefBasePicMarkingOp::ShortTermUnusedForRef {
281                difference_of_base_pic_nums_minus1,
282            } => {
283                assert_eq!(*difference_of_base_pic_nums_minus1, 0);
284            }
285            _ => panic!("expected ShortTermUnusedForRef"),
286        }
287    }
288}