scuffle_h265/
sps.rs

1use std::io;
2
3use byteorder::ReadBytesExt;
4use bytes::Bytes;
5use scuffle_bytes_util::BitReader;
6use scuffle_expgolomb::BitReaderExpGolombExt;
7
8#[derive(Debug, Clone, PartialEq)]
9/// The Sequence Parameter Set.
10/// ISO/IEC-14496-10-2022 - 7.3.2
11pub struct Sps {
12    /// The width as a u64.
13    pub width: u64,
14    /// The height as a u64.
15    pub height: u64,
16    /// The framerate as a f64.
17    pub frame_rate: f64,
18    /// An optional `ColorConfig`. Refer to the ColorConfig struct for more info.
19    pub color_config: Option<ColorConfig>,
20}
21
22#[derive(Debug, Clone, PartialEq)]
23/// The color config for SPS.
24pub struct ColorConfig {
25    /// The `video_full_range_flag` as a bool.
26    pub full_range: bool,
27    /// The `colour_primaries` bits as a u8.
28    pub color_primaries: u8,
29    /// The `transfer_characteristics` bits as a u8.
30    pub transfer_characteristics: u8,
31    /// The `matrix_coefficients` bits as a u8.
32    pub matrix_coefficients: u8,
33}
34
35impl Sps {
36    /// Parses an SPS from the input bytes.
37    /// Returns an `Sps` struct.
38    pub fn parse(data: Bytes) -> io::Result<Self> {
39        let mut vec = Vec::with_capacity(data.len());
40
41        // ISO/IEC-23008-2-2022 - 7.3.1.1
42        let mut i = 0;
43        while i < data.len() {
44            if i + 2 < data.len() && data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x03 {
45                vec.push(0x00);
46                vec.push(0x00);
47            } else {
48                vec.push(data[i]);
49                i += 1;
50            }
51        }
52
53        let mut bit_reader = BitReader::new_from_slice(vec);
54
55        let forbidden_zero_bit = bit_reader.read_bit()?;
56        if forbidden_zero_bit {
57            return Err(io::Error::new(io::ErrorKind::InvalidData, "forbidden_zero_bit is not zero"));
58        }
59
60        let nalu_type = bit_reader.read_bits(6)?;
61        if nalu_type != 33 {
62            return Err(io::Error::new(
63                io::ErrorKind::InvalidData,
64                "nalu_type is not 33", // SPS
65            ));
66        }
67
68        bit_reader.seek_bits(
69            6 // nuh_layer_id
70            + 3 // nuh_temporal_id_plus1
71            + 4, // sps_video_parameter_set_id
72        )?;
73
74        let sps_max_sub_layers_minus1 = bit_reader.read_bits(3)?;
75        bit_reader.seek_bits(1)?; // sps_temporal_id_nesting_flag
76        {
77            bit_reader.seek_bits(
78                2 // general_profile_space
79                + 1 // general_tier_flag
80                + 5 // general_profile_idc
81                + 32 // general_profile_compatibility_flag
82                + 1 // general_progressive_source_flag
83                + 1 // general_interlaced_source_flag
84                + 1 // general_non_packed_constraint_flag
85                + 1 // general_frame_only_constraint_flag
86                + 43 // general_reserved_zero_43bits
87                + 1 // general_reserved_zero_bit
88                + 8, // general_level_idc
89            )?;
90
91            let mut sub_layer_level_present_flags = vec![false; sps_max_sub_layers_minus1 as usize];
92            for v in sub_layer_level_present_flags.iter_mut() {
93                bit_reader.seek_bits(1)?; // sub_layer_profile_present_flag
94                *v = bit_reader.read_bit()?; // sub_layer_level_present_flag
95            }
96
97            if sps_max_sub_layers_minus1 > 0 && sps_max_sub_layers_minus1 < 8 {
98                bit_reader.seek_bits(2 * (8 - sps_max_sub_layers_minus1 as i64))?;
99                // reserved_zero_2bits
100            }
101
102            for v in sub_layer_level_present_flags.drain(..) {
103                bit_reader.seek_bits(
104                    2 // sub_layer_profile_space
105                    + 1 // sub_layer_tier_flag
106                    + 5 // sub_layer_profile_idc
107                    + 32 // sub_layer_profile_compatibility_flag[32]
108                    + 1 // sub_layer_progressive_source_flag
109                    + 1 // sub_layer_interlaced_source_flag
110                    + 1 // sub_layer_non_packed_constraint_flag
111                    + 1 // sub_layer_frame_only_constraint_flag
112                    + 43 // sub_layer_reserved_zero_44bits
113                    + 1, // sub_layer_reserved_zero_bit
114                )?;
115                if v {
116                    bit_reader.seek_bits(8)?; // sub_layer_level_idc
117                }
118            }
119        }
120
121        bit_reader.read_exp_golomb()?; // sps_seq_parameter_set_id
122        let chroma_format_idc = bit_reader.read_exp_golomb()?;
123        if chroma_format_idc == 3 {
124            bit_reader.read_bit()?;
125        }
126        let pic_width_in_luma_samples = bit_reader.read_exp_golomb()?;
127        let pic_height_in_luma_samples = bit_reader.read_exp_golomb()?;
128        let conformance_window_flag = bit_reader.read_bit()?;
129
130        let conf_win_left_offset;
131        let conf_win_right_offset;
132        let conf_win_top_offset;
133        let conf_win_bottom_offset;
134
135        if conformance_window_flag {
136            conf_win_left_offset = bit_reader.read_exp_golomb()?;
137            conf_win_right_offset = bit_reader.read_exp_golomb()?;
138            conf_win_top_offset = bit_reader.read_exp_golomb()?;
139            conf_win_bottom_offset = bit_reader.read_exp_golomb()?;
140        } else {
141            conf_win_left_offset = 0;
142            conf_win_right_offset = 0;
143            conf_win_top_offset = 0;
144            conf_win_bottom_offset = 0;
145        }
146
147        let (sub_width_c, sub_height_c) = match chroma_format_idc {
148            0 => (1, 1),
149            1 => (2, 2),
150            2 => (2, 1),
151            3 => (1, 1),
152            _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "chroma_format_idc is not 0-3")),
153        };
154
155        let width = pic_width_in_luma_samples - sub_width_c * (conf_win_left_offset + conf_win_right_offset);
156        let height = pic_height_in_luma_samples - sub_height_c * (conf_win_top_offset + conf_win_bottom_offset);
157
158        bit_reader.read_exp_golomb()?; // bit_depth_luma_minus8
159        bit_reader.read_exp_golomb()?; // bit_depth_chroma_minus8
160        bit_reader.read_exp_golomb()?; // log2_max_pic_order_cnt_lsb_minus4
161        let sps_sub_layer_ordering_info_present_flag = bit_reader.read_bit()?;
162
163        if sps_sub_layer_ordering_info_present_flag {
164            for _ in 0..=sps_max_sub_layers_minus1 {
165                bit_reader.read_exp_golomb()?; // sps_max_dec_pic_buffering_minus1
166                bit_reader.read_exp_golomb()?; // sps_max_num_reorder_pics
167                bit_reader.read_exp_golomb()?; // sps_max_latency_increase_plus1
168            }
169        };
170
171        bit_reader.read_exp_golomb()?; // log2_min_luma_coding_block_size_minus3
172        bit_reader.read_exp_golomb()?; // log2_diff_max_min_luma_coding_block_size
173        bit_reader.read_exp_golomb()?; // log2_min_transform_block_size_minus2
174        bit_reader.read_exp_golomb()?; // log2_diff_max_min_transform_block_size
175        bit_reader.read_exp_golomb()?; // max_transform_hierarchy_depth_inter
176        bit_reader.read_exp_golomb()?; // max_transform_hierarchy_depth_intra
177
178        let scaling_list_enabled_flag = bit_reader.read_bit()?;
179        if scaling_list_enabled_flag {
180            let sps_scaling_list_data_present_flag = bit_reader.read_bit()?;
181            if sps_scaling_list_data_present_flag {
182                for size_id in 0..4 {
183                    let mut matrix_id = 0;
184                    while matrix_id < 6 {
185                        let scaling_list_pred_mode_flag = bit_reader.read_bit()?;
186                        if !scaling_list_pred_mode_flag {
187                            bit_reader.read_exp_golomb()?; // scaling_list_pred_matrix_id_delta
188                        } else {
189                            let coef_num = 64.min(1 << (4 + (size_id << 1)));
190                            let mut next_coef = 8;
191                            if size_id > 1 {
192                                let scaling_list_dc_coef_minus8 = bit_reader.read_signed_exp_golomb()?;
193                                next_coef = 8 + scaling_list_dc_coef_minus8;
194                            }
195                            for _ in 0..coef_num {
196                                let scaling_list_delta_coef = bit_reader.read_signed_exp_golomb()?;
197                                next_coef = (next_coef + scaling_list_delta_coef + 256) % 256;
198                            }
199                        }
200                        matrix_id += if size_id == 3 { 3 } else { 1 };
201                    }
202                }
203            }
204        }
205
206        bit_reader.seek_bits(1)?; // amp_enabled_flag
207        bit_reader.seek_bits(1)?; // sample_adaptive_offset_enabled_flag
208
209        if bit_reader.read_bit()? {
210            // pcm_enabled_flag
211            bit_reader.seek_bits(4)?; // pcm_sample_bit_depth_luma_minus1
212            bit_reader.seek_bits(4)?; // pcm_sample_bit_depth_chroma_minus1
213            bit_reader.read_exp_golomb()?; // log2_min_pcm_luma_coding_block_size_minus3
214            bit_reader.read_exp_golomb()?; // log2_diff_max_min_pcm_luma_coding_block_size
215            bit_reader.seek_bits(1)?; // pcm_loop_filter_disabled_flag
216        }
217
218        let num_short_term_ref_pic_sets = bit_reader.read_exp_golomb()?;
219        let mut num_delta_pocs = vec![0; num_short_term_ref_pic_sets as usize];
220        for st_rps_idx in 0..num_short_term_ref_pic_sets {
221            if st_rps_idx != 0 && bit_reader.read_bit()? {
222                bit_reader.seek_bits(1)?;
223                bit_reader.read_exp_golomb()?; // delta_rps_sign
224
225                num_delta_pocs[st_rps_idx as usize] = 0;
226
227                for _ in 0..num_delta_pocs[(st_rps_idx - 1) as usize] {
228                    let used_by_curr_pic_flag = bit_reader.read_bit()?;
229                    let use_delta_flag = if !used_by_curr_pic_flag {
230                        bit_reader.read_bit()? // use_delta_flag
231                    } else {
232                        false
233                    };
234
235                    if used_by_curr_pic_flag || use_delta_flag {
236                        num_delta_pocs[st_rps_idx as usize] += 1;
237                    }
238                }
239            } else {
240                let num_negative_pics = bit_reader.read_exp_golomb()?;
241                let num_positive_pics = bit_reader.read_exp_golomb()?;
242
243                num_delta_pocs[st_rps_idx as usize] = num_negative_pics + num_positive_pics;
244                for _ in 0..num_negative_pics {
245                    bit_reader.read_exp_golomb()?; // delta_poc_s0_minus1
246                    bit_reader.seek_bits(1)?; // used_by_curr_pic_s0_flag
247                }
248                for _ in 0..num_positive_pics {
249                    bit_reader.read_exp_golomb()?; // delta_poc_s1_minus1
250                    bit_reader.seek_bits(1)?; // used_by_curr_pic_s1_flag
251                }
252            }
253        }
254
255        let long_term_ref_pics_present_flag = bit_reader.read_bit()?;
256        if long_term_ref_pics_present_flag {
257            let num_long_term_ref_pics_sps = bit_reader.read_exp_golomb()?;
258            for _ in 0..num_long_term_ref_pics_sps {
259                bit_reader.read_exp_golomb()?; // lt_ref_pic_poc_lsb_sps
260                bit_reader.seek_bits(1)?; // used_by_curr_pic_lt_sps_flag
261            }
262        }
263
264        bit_reader.seek_bits(1)?; // sps_temporal_mvp_enabled_flag
265        bit_reader.seek_bits(1)?; // strong_intra_smoothing_enabled_flag
266        let vui_parameters_present_flag = bit_reader.read_bit()?;
267
268        let mut color_config = None;
269
270        let mut frame_rate = 0.0;
271        if vui_parameters_present_flag {
272            let aspect_ratio_info_present_flag = bit_reader.read_bit()?;
273            if aspect_ratio_info_present_flag {
274                let aspect_ratio_idc = bit_reader.read_bits(8)?;
275                if aspect_ratio_idc == 255 {
276                    bit_reader.seek_bits(16)?; // sar_width
277                    bit_reader.seek_bits(16)?; // sar_height
278                }
279            }
280
281            let overscan_info_present_flag = bit_reader.read_bit()?;
282            if overscan_info_present_flag {
283                bit_reader.seek_bits(1)?; // overscan_appropriate_flag
284            }
285
286            let video_signal_type_present_flag = bit_reader.read_bit()?;
287            if video_signal_type_present_flag {
288                bit_reader.seek_bits(3)?; // video_format
289                let full_range = bit_reader.read_bit()?; // video_full_range_flag
290                let color_primaries;
291                let transfer_characteristics;
292                let matrix_coefficients;
293
294                let colour_description_present_flag = bit_reader.read_bit()?;
295                if colour_description_present_flag {
296                    color_primaries = bit_reader.read_u8()?; // colour_primaries
297                    transfer_characteristics = bit_reader.read_u8()?; // transfer_characteristics
298                    matrix_coefficients = bit_reader.read_u8()?; // matrix_coeffs
299                } else {
300                    color_primaries = 2; // Unspecified
301                    transfer_characteristics = 2; // Unspecified
302                    matrix_coefficients = 2; // Unspecified
303                }
304
305                color_config = Some(ColorConfig {
306                    full_range,
307                    color_primaries,
308                    transfer_characteristics,
309                    matrix_coefficients,
310                });
311            }
312
313            let chroma_loc_info_present_flag = bit_reader.read_bit()?;
314            if chroma_loc_info_present_flag {
315                bit_reader.read_exp_golomb()?; // chroma_sample_loc_type_top_field
316                bit_reader.read_exp_golomb()?; // chroma_sample_loc_type_bottom_field
317            }
318
319            bit_reader.seek_bits(1)?;
320            bit_reader.seek_bits(1)?;
321            bit_reader.seek_bits(1)?;
322            let default_display_window_flag = bit_reader.read_bit()?;
323
324            if default_display_window_flag {
325                bit_reader.read_exp_golomb()?; // def_disp_win_left_offset
326                bit_reader.read_exp_golomb()?; // def_disp_win_right_offset
327                bit_reader.read_exp_golomb()?; // def_disp_win_top_offset
328                bit_reader.read_exp_golomb()?; // def_disp_win_bottom_offset
329            }
330
331            let vui_timing_info_present_flag = bit_reader.read_bit()?;
332            if vui_timing_info_present_flag {
333                let num_units_in_tick = bit_reader.read_bits(32)?; // vui_num_units_in_tick
334                let time_scale = bit_reader.read_bits(32)?; // vui_time_scale
335
336                if num_units_in_tick == 0 {
337                    return Err(io::Error::new(
338                        io::ErrorKind::InvalidData,
339                        "vui_num_units_in_tick cannot be zero",
340                    ));
341                }
342
343                frame_rate = time_scale as f64 / num_units_in_tick as f64;
344            }
345        }
346
347        Ok(Sps {
348            width,
349            height,
350            frame_rate,
351            color_config,
352        })
353    }
354}
355
356#[cfg(test)]
357#[cfg_attr(all(test, coverage_nightly), coverage(off))]
358mod tests {
359    use bytes::Bytes;
360
361    use crate::{ColorConfig, Sps};
362
363    #[test]
364    fn test_sps_parse() {
365        let data = b"B\x01\x01\x01@\0\0\x03\0\x90\0\0\x03\0\0\x03\0\x99\xa0\x01@ \x05\xa1e\x95R\x90\x84d_\xf8\xc0Z\x80\x80\x80\x82\0\0\x03\0\x02\0\0\x03\x01 \xc0\x0b\xbc\xa2\0\x02bX\0\x011-\x08".to_vec();
366
367        let sps = Sps::parse(Bytes::from(data.to_vec())).unwrap();
368        assert_eq!(
369            sps,
370            Sps {
371                color_config: Some(ColorConfig {
372                    full_range: false,
373                    color_primaries: 1,
374                    matrix_coefficients: 1,
375                    transfer_characteristics: 1,
376                }),
377                frame_rate: 144.0,
378                width: 2560,
379                height: 1440,
380            }
381        );
382    }
383
384    #[test]
385    fn test_parse_sps_with_zero_vui_num_units_in_tick() {
386        let sps = Bytes::from(b"B\x01\x01\x01@\0\0\x03\0\x90\0\0\x03\0\0\x03\0\x99\xa0\x01@ \x05\xa1e\x95R\x90\x84d_\xf8\xc0Z\x80\0\x80\x82\0\0\x03\0\0\0\0\0\x01 \xc0\x0b\xbc\xa2\0\x02bX\0\x011-\x08".to_vec());
387        let sps = Sps::parse(sps);
388
389        match sps {
390            Ok(_) => panic!("Expected error for vui_num_units_in_tick = 0, but got Ok"),
391            Err(e) => assert_eq!(
392                e.kind(),
393                std::io::ErrorKind::InvalidData,
394                "Expected InvalidData error, got {:?}",
395                e
396            ),
397        }
398    }
399}