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}