Skip to main content

h264_reader/nal/
subset_sps.rs

1//! Parser for `subset_seq_parameter_set_rbsp()` (NAL type 15, spec 7.3.2.1.3).
2//!
3//! A subset SPS wraps a base `SeqParameterSet` plus a profile-dependent extension:
4//! - SVC extension (profiles 83/86, spec Annex F)
5//! - MVC extension (profiles 118/128/134, spec Annex G)
6//!
7//! SVC VUI parameter extensions are detected but not parsed; when present, `finish_rbsp()`
8//! validation is skipped and `additional_extension2_flag` defaults to `false`.
9//! MVC VUI parameters (spec G.14.1) are fully parsed.
10
11use crate::nal::sps::{HrdParameters, SeqParameterSet, SpsError, TimingInfo};
12use crate::rbsp::BitRead;
13
14/// Profile-dependent extension data within a subset SPS.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub enum SubsetSpsExtension {
17    Svc(SvcSpsExtension),
18    Mvc {
19        ext: MvcSpsExtension,
20        mvc_vui_parameters: Option<MvcVuiParametersExtension>,
21    },
22    /// MVCD extension (profiles 135/138/139). Parsing not implemented - fields not read.
23    Mvcd,
24}
25
26/// SVC SPS extension (spec F.7.3.2.1.4, `seq_parameter_set_svc_extension`).
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct SvcSpsExtension {
29    pub inter_layer_deblocking_filter_control_present_flag: bool,
30    pub extended_spatial_scalability_idc: u8,
31    pub chroma_phase_x_plus1_flag: bool,
32    pub chroma_phase_y_plus1: u8,
33    pub seq_ref_layer_chroma_phase_x_plus1_flag: bool,
34    pub seq_ref_layer_chroma_phase_y_plus1: u8,
35    pub seq_scaled_ref_layer_left_offset: i32,
36    pub seq_scaled_ref_layer_top_offset: i32,
37    pub seq_scaled_ref_layer_right_offset: i32,
38    pub seq_scaled_ref_layer_bottom_offset: i32,
39    pub seq_tcoeff_level_prediction_flag: bool,
40    pub adaptive_tcoeff_level_prediction_flag: bool,
41    pub slice_header_restriction_flag: bool,
42    pub svc_vui_parameters_present_flag: bool,
43}
44
45/// A single view in the MVC SPS extension.
46#[derive(Clone, Debug, PartialEq, Eq)]
47pub struct MvcView {
48    pub view_id: u16,
49    pub anchor_refs_l0: Vec<u16>,
50    pub anchor_refs_l1: Vec<u16>,
51    pub non_anchor_refs_l0: Vec<u16>,
52    pub non_anchor_refs_l1: Vec<u16>,
53}
54
55/// A single level-value entry with its applicable operations.
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct MvcLevelValue {
58    pub level_idc: u8,
59    pub applicable_ops: Vec<MvcApplicableOp>,
60}
61
62/// An applicable operation within an MVC level value.
63#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct MvcApplicableOp {
65    pub temporal_id: u8,
66    pub num_target_views_minus1: u16,
67    pub target_view_ids: Vec<u16>,
68    pub num_views_minus1: u16,
69}
70
71/// MVC SPS extension (spec G.7.3.2.1.4, `seq_parameter_set_mvc_extension`).
72#[derive(Clone, Debug, PartialEq, Eq)]
73pub struct MvcSpsExtension {
74    pub views: Vec<MvcView>,
75    pub level_values: Vec<MvcLevelValue>,
76}
77
78/// A single operation in the MVC VUI parameters extension (spec G.14.1).
79#[derive(Clone, Debug, PartialEq, Eq)]
80pub struct MvcVuiOp {
81    pub temporal_id: u8,
82    pub target_output_view_ids: Vec<u16>,
83    pub timing_info: Option<TimingInfo>,
84    pub nal_hrd_parameters: Option<HrdParameters>,
85    pub vcl_hrd_parameters: Option<HrdParameters>,
86    pub low_delay_hrd_flag: Option<bool>,
87    pub pic_struct_present_flag: bool,
88}
89
90/// MVC VUI parameters extension (spec G.14.1, `mvc_vui_parameters_extension`).
91#[derive(Clone, Debug, PartialEq, Eq)]
92pub struct MvcVuiParametersExtension {
93    pub ops: Vec<MvcVuiOp>,
94}
95
96/// Parsed `subset_seq_parameter_set_rbsp()` (NAL unit type 15).
97#[derive(Clone, Debug, PartialEq, Eq)]
98pub struct SubsetSps {
99    pub sps: SeqParameterSet,
100    pub extension: Option<SubsetSpsExtension>,
101    pub additional_extension2_flag: bool,
102}
103
104/// Read a ue value and validate it fits in u16 with given max.
105fn read_ue_bounded<R: BitRead>(r: &mut R, name: &'static str, max: u32) -> Result<u16, SpsError> {
106    let val = r.read_ue(name)?;
107    if val > max {
108        return Err(SpsError::FieldValueTooLarge { name, value: val });
109    }
110    Ok(val as u16)
111}
112
113impl SubsetSps {
114    pub fn from_bits<R: BitRead>(mut r: R) -> Result<SubsetSps, SpsError> {
115        let sps = SeqParameterSet::read_seq_parameter_set_data(&mut r)?;
116        let profile_idc: u8 = sps.profile_idc.into();
117
118        let (extension, has_unparsed_vui) = match profile_idc {
119            83 | 86 => {
120                // bit_equal_to_one f(1) per spec F.7.3.2.1.3
121                let _bit_equal_to_one = r.read_bit("bit_equal_to_one")?;
122                let ext = read_svc_extension(&mut r, &sps)?;
123                let has_vui = ext.svc_vui_parameters_present_flag;
124                (Some(SubsetSpsExtension::Svc(ext)), has_vui)
125            }
126            118 | 128 | 134 => {
127                // bit_equal_to_one f(1) per spec G.7.3.2.1.3
128                let _bit_equal_to_one = r.read_bit("bit_equal_to_one")?;
129                let ext = read_mvc_extension(&mut r)?;
130                let mvc_vui_parameters_present_flag =
131                    r.read_bit("mvc_vui_parameters_present_flag")?;
132                let mvc_vui_parameters = if mvc_vui_parameters_present_flag {
133                    Some(read_mvc_vui_parameters_extension(&mut r)?)
134                } else {
135                    None
136                };
137                (
138                    Some(SubsetSpsExtension::Mvc {
139                        ext,
140                        mvc_vui_parameters,
141                    }),
142                    false,
143                )
144            }
145            135 | 138 | 139 => {
146                // bit_equal_to_one f(1) per spec I.7.3.2.1.3
147                let _bit_equal_to_one = r.read_bit("bit_equal_to_one")?;
148                // MVCD extension -- parsing deferred, skip remaining data.
149                (Some(SubsetSpsExtension::Mvcd), true)
150            }
151            _ => (None, false),
152        };
153
154        let additional_extension2_flag = if has_unparsed_vui {
155            // VUI extension data follows but is not parsed; skip finish_rbsp() validation.
156            false
157        } else {
158            let flag = r.read_bit("additional_extension2_flag")?;
159            r.finish_rbsp()?;
160            flag
161        };
162
163        Ok(SubsetSps {
164            sps,
165            extension,
166            additional_extension2_flag,
167        })
168    }
169}
170
171fn read_svc_extension<R: BitRead>(
172    r: &mut R,
173    sps: &SeqParameterSet,
174) -> Result<SvcSpsExtension, SpsError> {
175    let inter_layer_deblocking_filter_control_present_flag =
176        r.read_bit("inter_layer_deblocking_filter_control_present_flag")?;
177    let extended_spatial_scalability_idc: u8 =
178        r.read::<2, _>("extended_spatial_scalability_idc")?;
179
180    let chroma_array_type = sps.chroma_info.chroma_array_type();
181
182    let chroma_phase_x_plus1_flag = if chroma_array_type == 1 || chroma_array_type == 2 {
183        r.read_bit("chroma_phase_x_plus1_flag")?
184    } else {
185        false
186    };
187    let chroma_phase_y_plus1 = if chroma_array_type == 1 {
188        r.read::<2, _>("chroma_phase_y_plus1")?
189    } else {
190        // Default: 0 for Monochrome, 1 for YUV422/444
191        if chroma_array_type == 0 {
192            0
193        } else {
194            1
195        }
196    };
197
198    let (
199        seq_ref_layer_chroma_phase_x_plus1_flag,
200        seq_ref_layer_chroma_phase_y_plus1,
201        seq_scaled_ref_layer_left_offset,
202        seq_scaled_ref_layer_top_offset,
203        seq_scaled_ref_layer_right_offset,
204        seq_scaled_ref_layer_bottom_offset,
205    ) = if extended_spatial_scalability_idc == 1 {
206        let ref_phase_x = if chroma_array_type == 1 || chroma_array_type == 2 {
207            r.read_bit("seq_ref_layer_chroma_phase_x_plus1_flag")?
208        } else {
209            false
210        };
211        let ref_phase_y = if chroma_array_type == 1 {
212            r.read::<2, _>("seq_ref_layer_chroma_phase_y_plus1")?
213        } else {
214            if chroma_array_type == 0 {
215                0
216            } else {
217                1
218            }
219        };
220        (
221            ref_phase_x,
222            ref_phase_y,
223            r.read_se("seq_scaled_ref_layer_left_offset")?,
224            r.read_se("seq_scaled_ref_layer_top_offset")?,
225            r.read_se("seq_scaled_ref_layer_right_offset")?,
226            r.read_se("seq_scaled_ref_layer_bottom_offset")?,
227        )
228    } else {
229        (
230            false,
231            if chroma_array_type == 0 { 0 } else { 1 },
232            0,
233            0,
234            0,
235            0,
236        )
237    };
238
239    let seq_tcoeff_level_prediction_flag = r.read_bit("seq_tcoeff_level_prediction_flag")?;
240    let adaptive_tcoeff_level_prediction_flag = if seq_tcoeff_level_prediction_flag {
241        r.read_bit("adaptive_tcoeff_level_prediction_flag")?
242    } else {
243        false
244    };
245    let slice_header_restriction_flag = r.read_bit("slice_header_restriction_flag")?;
246    let svc_vui_parameters_present_flag = r.read_bit("svc_vui_parameters_present_flag")?;
247
248    Ok(SvcSpsExtension {
249        inter_layer_deblocking_filter_control_present_flag,
250        extended_spatial_scalability_idc,
251        chroma_phase_x_plus1_flag,
252        chroma_phase_y_plus1,
253        seq_ref_layer_chroma_phase_x_plus1_flag,
254        seq_ref_layer_chroma_phase_y_plus1,
255        seq_scaled_ref_layer_left_offset,
256        seq_scaled_ref_layer_top_offset,
257        seq_scaled_ref_layer_right_offset,
258        seq_scaled_ref_layer_bottom_offset,
259        seq_tcoeff_level_prediction_flag,
260        adaptive_tcoeff_level_prediction_flag,
261        slice_header_restriction_flag,
262        svc_vui_parameters_present_flag,
263    })
264}
265
266fn read_mvc_extension<R: BitRead>(r: &mut R) -> Result<MvcSpsExtension, SpsError> {
267    let num_views_minus1 = r.read_ue("num_views_minus1")?;
268    if num_views_minus1 > 1023 {
269        return Err(SpsError::FieldValueTooLarge {
270            name: "num_views_minus1",
271            value: num_views_minus1,
272        });
273    }
274
275    let mut views = Vec::with_capacity(num_views_minus1 as usize + 1);
276    for _ in 0..=num_views_minus1 {
277        let view_id = read_ue_bounded(r, "view_id", 1023)?;
278        views.push(MvcView {
279            view_id,
280            anchor_refs_l0: Vec::new(),
281            anchor_refs_l1: Vec::new(),
282            non_anchor_refs_l0: Vec::new(),
283            non_anchor_refs_l1: Vec::new(),
284        });
285    }
286
287    // anchor refs
288    for i in 1..=num_views_minus1 as usize {
289        let num_anchor_refs_l0 = r.read_ue("num_anchor_refs_l0")?;
290        if num_anchor_refs_l0 > 15 {
291            return Err(SpsError::FieldValueTooLarge {
292                name: "num_anchor_refs_l0",
293                value: num_anchor_refs_l0,
294            });
295        }
296        for _ in 0..num_anchor_refs_l0 {
297            views[i]
298                .anchor_refs_l0
299                .push(read_ue_bounded(r, "anchor_ref_l0", 1023)?);
300        }
301        let num_anchor_refs_l1 = r.read_ue("num_anchor_refs_l1")?;
302        if num_anchor_refs_l1 > 15 {
303            return Err(SpsError::FieldValueTooLarge {
304                name: "num_anchor_refs_l1",
305                value: num_anchor_refs_l1,
306            });
307        }
308        for _ in 0..num_anchor_refs_l1 {
309            views[i]
310                .anchor_refs_l1
311                .push(read_ue_bounded(r, "anchor_ref_l1", 1023)?);
312        }
313    }
314
315    // non-anchor refs
316    for i in 1..=num_views_minus1 as usize {
317        let num_non_anchor_refs_l0 = r.read_ue("num_non_anchor_refs_l0")?;
318        if num_non_anchor_refs_l0 > 15 {
319            return Err(SpsError::FieldValueTooLarge {
320                name: "num_non_anchor_refs_l0",
321                value: num_non_anchor_refs_l0,
322            });
323        }
324        for _ in 0..num_non_anchor_refs_l0 {
325            views[i]
326                .non_anchor_refs_l0
327                .push(read_ue_bounded(r, "non_anchor_ref_l0", 1023)?);
328        }
329        let num_non_anchor_refs_l1 = r.read_ue("num_non_anchor_refs_l1")?;
330        if num_non_anchor_refs_l1 > 15 {
331            return Err(SpsError::FieldValueTooLarge {
332                name: "num_non_anchor_refs_l1",
333                value: num_non_anchor_refs_l1,
334            });
335        }
336        for _ in 0..num_non_anchor_refs_l1 {
337            views[i]
338                .non_anchor_refs_l1
339                .push(read_ue_bounded(r, "non_anchor_ref_l1", 1023)?);
340        }
341    }
342
343    // level values
344    let num_level_values_signalled_minus1 = r.read_ue("num_level_values_signalled_minus1")?;
345    if num_level_values_signalled_minus1 > 63 {
346        return Err(SpsError::FieldValueTooLarge {
347            name: "num_level_values_signalled_minus1",
348            value: num_level_values_signalled_minus1,
349        });
350    }
351
352    let mut level_values = Vec::with_capacity(num_level_values_signalled_minus1 as usize + 1);
353    for _ in 0..=num_level_values_signalled_minus1 {
354        let level_idc: u8 = r.read::<8, _>("level_idc")?;
355        let num_applicable_ops_minus1 = r.read_ue("num_applicable_ops_minus1")?;
356        if num_applicable_ops_minus1 > 1023 {
357            return Err(SpsError::FieldValueTooLarge {
358                name: "num_applicable_ops_minus1",
359                value: num_applicable_ops_minus1,
360            });
361        }
362        let mut applicable_ops = Vec::with_capacity(num_applicable_ops_minus1 as usize + 1);
363        for _ in 0..=num_applicable_ops_minus1 {
364            let temporal_id: u8 = r.read::<3, _>("applicable_op_temporal_id")?;
365            let num_target_views_minus1 =
366                read_ue_bounded(r, "applicable_op_num_target_views_minus1", 1023)?;
367            let mut target_view_ids = Vec::with_capacity(num_target_views_minus1 as usize + 1);
368            for _ in 0..=num_target_views_minus1 {
369                target_view_ids.push(read_ue_bounded(r, "applicable_op_target_view_id", 1023)?);
370            }
371            let num_views_minus1 = read_ue_bounded(r, "applicable_op_num_views_minus1", 1023)?;
372            applicable_ops.push(MvcApplicableOp {
373                temporal_id,
374                num_target_views_minus1,
375                target_view_ids,
376                num_views_minus1,
377            });
378        }
379        level_values.push(MvcLevelValue {
380            level_idc,
381            applicable_ops,
382        });
383    }
384
385    Ok(MvcSpsExtension {
386        views,
387        level_values,
388    })
389}
390
391fn read_mvc_vui_parameters_extension<R: BitRead>(
392    r: &mut R,
393) -> Result<MvcVuiParametersExtension, SpsError> {
394    let vui_mvc_num_ops_minus1 = r.read_ue("vui_mvc_num_ops_minus1")?;
395    if vui_mvc_num_ops_minus1 > 1023 {
396        return Err(SpsError::FieldValueTooLarge {
397            name: "vui_mvc_num_ops_minus1",
398            value: vui_mvc_num_ops_minus1,
399        });
400    }
401    let mut ops = Vec::with_capacity(vui_mvc_num_ops_minus1 as usize + 1);
402    for _ in 0..=vui_mvc_num_ops_minus1 {
403        let temporal_id: u8 = r.read::<3, _>("vui_mvc_temporal_id")?;
404        let vui_mvc_num_target_output_views_minus1 =
405            r.read_ue("vui_mvc_num_target_output_views_minus1")?;
406        if vui_mvc_num_target_output_views_minus1 > 1023 {
407            return Err(SpsError::FieldValueTooLarge {
408                name: "vui_mvc_num_target_output_views_minus1",
409                value: vui_mvc_num_target_output_views_minus1,
410            });
411        }
412        let mut target_output_view_ids =
413            Vec::with_capacity(vui_mvc_num_target_output_views_minus1 as usize + 1);
414        for _ in 0..=vui_mvc_num_target_output_views_minus1 {
415            target_output_view_ids.push(read_ue_bounded(r, "vui_mvc_view_id", 1023)?);
416        }
417        let timing_info = TimingInfo::read(r)?;
418        let mut hrd_parameters_present = false;
419        let nal_hrd_parameters = HrdParameters::read(r, &mut hrd_parameters_present)?;
420        let vcl_hrd_parameters = HrdParameters::read(r, &mut hrd_parameters_present)?;
421        let low_delay_hrd_flag = if hrd_parameters_present {
422            Some(r.read_bit("vui_mvc_low_delay_hrd_flag")?)
423        } else {
424            None
425        };
426        let pic_struct_present_flag = r.read_bit("vui_mvc_pic_struct_present_flag")?;
427        ops.push(MvcVuiOp {
428            temporal_id,
429            target_output_view_ids,
430            timing_info,
431            nal_hrd_parameters,
432            vcl_hrd_parameters,
433            low_delay_hrd_flag,
434            pic_struct_present_flag,
435        });
436    }
437    Ok(MvcVuiParametersExtension { ops })
438}
439
440#[cfg(test)]
441mod test {
442    use super::*;
443    use crate::rbsp::BitReader;
444
445    #[test]
446    fn parse_subset_sps_unknown_profile() {
447        // profile_idc=66 (Baseline, not an extension profile)
448        // constraint_flags=0xC0
449        // level_idc=30
450        // seq_parameter_set_id=0 (ue: '1')
451        // log2_max_frame_num_minus4=0 (ue: '1')
452        // pic_order_cnt_type=0 (ue: '1')
453        // log2_max_pic_order_cnt_lsb_minus4=0 (ue: '1')
454        // max_num_ref_frames=0 (ue: '1')
455        // gaps_in_frame_num_value_allowed_flag=0
456        // pic_width_in_mbs_minus1=0 (ue: '1')
457        // pic_height_in_map_units_minus1=0 (ue: '1')
458        // frame_mbs_only_flag=1
459        // direct_8x8_inference_flag=0
460        // frame_cropping_flag=0
461        // vui_parameters_present_flag=0
462        // additional_extension2_flag=0
463        // rbsp_stop_one_bit=1
464        #[rustfmt::skip]
465        let data = [
466            0x42, // profile_idc=66
467            0xC0, // constraint_flags
468            0x1E, // level_idc=30
469            // ue(0) x5: sps_id, log2_max_frame_num, poc_type, log2_poc_lsb, max_ref
470            // 0: gaps_in_frame_num
471            // ue(0) x2: pic_width, pic_height
472            // bits so far: 1 1 1 1 1 0 1 1 = 0xFB
473            0xFB,
474            // 1: frame_mbs_only_flag (Frames)
475            // 0: direct_8x8_inference_flag
476            // 0: frame_cropping_flag
477            // 0: vui_parameters_present_flag
478            // 0: additional_extension2_flag
479            // 1: rbsp_stop_one_bit
480            // 00: padding
481            // bits: 1 0 0 0 0 1 0 0 = 0x84
482            0x84,
483        ];
484        let subset = SubsetSps::from_bits(BitReader::new(&data[..])).unwrap();
485        assert_eq!(u8::from(subset.sps.profile_idc), 66);
486        assert!(subset.extension.is_none());
487        assert!(!subset.additional_extension2_flag);
488    }
489}