scuffle_h264/
config.rs

1use std::io::{
2    Write, {self},
3};
4
5use byteorder::{BigEndian, WriteBytesExt};
6use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
7use scuffle_bytes_util::{BitReader, BitWriter, BytesCow, IoResultExt};
8
9use crate::sps::SpsExtended;
10
11/// The AVC (H.264) Decoder Configuration Record.
12/// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct AVCDecoderConfigurationRecord<'a> {
15    /// The `configuration_version` is set to 1 (as a u8) defined by the h264 spec until further notice.
16    ///
17    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
18    pub configuration_version: u8,
19
20    /// The `profile_indication` (aka AVCProfileIndication) contains the `profile_idc` u8 from SPS.
21    ///
22    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
23    pub profile_indication: u8,
24
25    /// The `profile_compatibility` is a u8, similar to the `profile_idc` and `level_idc` bytes from SPS.
26    ///
27    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
28    pub profile_compatibility: u8,
29
30    /// The `level_indication` (aka AVCLevelIndication) contains the `level_idc` u8 from SPS.
31    ///
32    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
33    pub level_indication: u8,
34
35    /// The `length_size_minus_one` is the u8 length of the NALUnitLength minus one.
36    ///
37    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
38    pub length_size_minus_one: u8,
39
40    /// The `sps` is a vec of SPS Bytes.
41    ///
42    /// Note that these should be ordered by ascending SPS ID.
43    ///
44    /// Refer to the [`crate::Sps`] struct in the SPS docs for more info.
45    pub sps: Vec<BytesCow<'a>>,
46
47    /// The `pps` is a vec of PPS Bytes.
48    ///
49    /// These contain syntax elements that can apply layer repesentation(s).
50    ///
51    /// Note that these should be ordered by ascending PPS ID.
52    ///
53    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
54    pub pps: Vec<BytesCow<'a>>,
55
56    /// An optional `AvccExtendedConfig`.
57    ///
58    /// Refer to the AvccExtendedConfig for more info.
59    pub extended_config: Option<AvccExtendedConfig>,
60}
61
62/// The AVC (H.264) Extended Configuration.
63/// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct AvccExtendedConfig {
66    /// The `chroma_format_idc` as a u8.
67    ///
68    /// Also labelled as `chroma_format`, this contains the `chroma_format_idc` from
69    /// ISO/IEC 14496-10.
70    ///
71    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
72    pub chroma_format_idc: u8,
73
74    /// The `bit_depth_luma_minus8` is the bit depth of samples in the Luma arrays as a u8.
75    ///
76    /// The value of this ranges from \[0, 4\].
77    ///
78    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
79    pub bit_depth_luma_minus8: u8,
80
81    /// The `bit_depth_chroma_minus8` is the bit depth of the samples in the Chroma arrays as a u8.
82    ///
83    /// The value of this ranges from \[0, 4\].
84    ///
85    /// ISO/IEC 14496-15:2022(E) - 5.3.2.1.2
86    pub bit_depth_chroma_minus8: u8,
87
88    /// The `sequence_parameter_set_ext` is a vec of SpsExtended Bytes.
89    ///
90    /// Refer to the [`crate::SpsExtended`] struct in the SPS docs for more info.
91    pub sequence_parameter_set_ext: Vec<SpsExtended>,
92}
93
94impl<'a> Deserialize<'a> for AVCDecoderConfigurationRecord<'a> {
95    fn deserialize<R>(mut reader: R) -> io::Result<Self>
96    where
97        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
98    {
99        let configuration_version = u8::deserialize(&mut reader)?;
100        let profile_indication = u8::deserialize(&mut reader)?;
101        let profile_compatibility = u8::deserialize(&mut reader)?;
102        let level_indication = u8::deserialize(&mut reader)?;
103        let length_size_minus_one = u8::deserialize(&mut reader)? & 0b00000011;
104        let num_of_sequence_parameter_sets = u8::deserialize(&mut reader)? & 0b00011111;
105
106        let mut sps = Vec::with_capacity(num_of_sequence_parameter_sets as usize);
107        for _ in 0..num_of_sequence_parameter_sets {
108            let sps_length = u16::deserialize(&mut reader)?;
109            let sps_data = reader.try_read(sps_length as usize)?;
110            sps.push(sps_data);
111        }
112
113        let num_of_picture_parameter_sets = u8::deserialize(&mut reader)?;
114        let mut pps = Vec::with_capacity(num_of_picture_parameter_sets as usize);
115        for _ in 0..num_of_picture_parameter_sets {
116            let pps_length = u16::deserialize(&mut reader)?;
117            let pps_data = reader.try_read(pps_length as usize)?;
118            pps.push(pps_data);
119        }
120
121        // It turns out that sometimes the extended config is not present, even though
122        // the avc_profile_indication is not 66, 77 or 88. We need to be lenient here on
123        // decoding.
124        let extended_config = match profile_indication {
125            66 | 77 | 88 => None,
126            _ => {
127                let chroma_format_idc = u8::deserialize(&mut reader).eof_to_none()?;
128                if let Some(chroma_format_idc) = chroma_format_idc {
129                    let chroma_format_idc = chroma_format_idc & 0b00000011; // 2 bits (6 bits reserved)
130                    let bit_depth_luma_minus8 = u8::deserialize(&mut reader)? & 0b00000111; // 3 bits (5 bits reserved)
131                    let bit_depth_chroma_minus8 = u8::deserialize(&mut reader)? & 0b00000111; // 3 bits (5 bits reserved)
132                    let number_of_sequence_parameter_set_ext = u8::deserialize(&mut reader)?; // 8 bits
133
134                    let mut sequence_parameter_set_ext = Vec::with_capacity(number_of_sequence_parameter_set_ext as usize);
135                    for _ in 0..number_of_sequence_parameter_set_ext {
136                        let sps_ext_length = u16::deserialize(&mut reader)?;
137                        let sps_ext_data = reader.try_read(sps_ext_length as usize)?;
138
139                        let mut bit_reader = BitReader::new_from_slice(sps_ext_data);
140                        let sps_ext_parsed = SpsExtended::parse(&mut bit_reader)?;
141                        sequence_parameter_set_ext.push(sps_ext_parsed);
142                    }
143
144                    Some(AvccExtendedConfig {
145                        chroma_format_idc,
146                        bit_depth_luma_minus8,
147                        bit_depth_chroma_minus8,
148                        sequence_parameter_set_ext,
149                    })
150                } else {
151                    // No extended config present even though avc_profile_indication is not 66, 77
152                    // or 88
153                    None
154                }
155            }
156        };
157
158        Ok(Self {
159            configuration_version,
160            profile_indication,
161            profile_compatibility,
162            level_indication,
163            length_size_minus_one,
164            sps,
165            pps,
166            extended_config,
167        })
168    }
169}
170
171impl Serialize for AVCDecoderConfigurationRecord<'_> {
172    fn serialize<W>(&self, writer: W) -> io::Result<()>
173    where
174        W: std::io::Write,
175    {
176        let mut bit_writer = BitWriter::new(writer);
177
178        bit_writer.write_u8(self.configuration_version)?;
179        bit_writer.write_u8(self.profile_indication)?;
180        bit_writer.write_u8(self.profile_compatibility)?;
181        bit_writer.write_u8(self.level_indication)?;
182        bit_writer.write_bits(0b111111, 6)?;
183        bit_writer.write_bits(self.length_size_minus_one as u64, 2)?;
184        bit_writer.write_bits(0b111, 3)?;
185
186        bit_writer.write_bits(self.sps.len() as u64, 5)?;
187        for sps in &self.sps {
188            bit_writer.write_u16::<BigEndian>(sps.len() as u16)?;
189            bit_writer.write_all(sps.as_bytes())?;
190        }
191
192        bit_writer.write_bits(self.pps.len() as u64, 8)?;
193        for pps in &self.pps {
194            bit_writer.write_u16::<BigEndian>(pps.len() as u16)?;
195            bit_writer.write_all(pps.as_bytes())?;
196        }
197
198        if let Some(config) = &self.extended_config {
199            bit_writer.write_bits(0b111111, 6)?;
200            bit_writer.write_bits(config.chroma_format_idc as u64, 2)?;
201            bit_writer.write_bits(0b11111, 5)?;
202            bit_writer.write_bits(config.bit_depth_luma_minus8 as u64, 3)?;
203            bit_writer.write_bits(0b11111, 5)?;
204            bit_writer.write_bits(config.bit_depth_chroma_minus8 as u64, 3)?;
205
206            bit_writer.write_bits(config.sequence_parameter_set_ext.len() as u64, 8)?;
207            for sps_ext in &config.sequence_parameter_set_ext {
208                bit_writer.write_u16::<BigEndian>(sps_ext.bytesize() as u16)?;
209                // SpsExtended::build() does not automatically align the writer
210                // due to the fact that it's used when building the Sps.
211                // If SpsExtended::build() were to align the writer, it could
212                // potentially cause a mismatch as it might introduce 0-padding in
213                // the middle of the bytestream, as the bytestream should only be aligned
214                // at the very end.
215                // In this case however, we want to intentionally align the writer as
216                // the sps is the only thing here.
217                sps_ext.build(&mut bit_writer)?;
218                bit_writer.align()?;
219            }
220        }
221
222        bit_writer.finish()?;
223
224        Ok(())
225    }
226}
227
228#[cfg(feature = "isobmff")]
229impl isobmff::IsoSized for AVCDecoderConfigurationRecord<'_> {
230    /// Returns the total byte size of the AVCDecoderConfigurationRecord.
231    fn size(&self) -> usize {
232        1 // configuration_version
233        + 1 // avc_profile_indication
234        + 1 // profile_compatibility
235        + 1 // avc_level_indication
236        + 1 // length_size_minus_one
237        + 1 // num_of_sequence_parameter_sets (5 bits reserved, 3 bits)
238        + self.sps.iter().map(|sps| {
239            2 // sps_length
240            + sps.len()
241        }).sum::<usize>() // sps
242        + 1 // num_of_picture_parameter_sets
243        + self.pps.iter().map(|pps| {
244            2 // pps_length
245            + pps.len()
246        }).sum::<usize>() // pps
247        + match &self.extended_config {
248            Some(config) => {
249                1 // chroma_format_idc (6 bits reserved, 2 bits)
250                + 1 // bit_depth_luma_minus8 (5 bits reserved, 3 bits)
251                + 1 // bit_depth_chroma_minus8 (5 bits reserved, 3 bits)
252                + 1 // number_of_sequence_parameter_set_ext
253                + config.sequence_parameter_set_ext.iter().map(|sps_ext| {
254                    2 // sps_ext_length
255                    + sps_ext.bytesize() as usize // sps_ext
256                }).sum::<usize>()
257            }
258            None => 0,
259        }
260    }
261}
262
263#[cfg(test)]
264#[cfg_attr(all(test, coverage_nightly), coverage(off))]
265mod tests {
266    use std::io::Write;
267
268    use byteorder::{BigEndian, WriteBytesExt};
269    use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
270    use scuffle_bytes_util::{BitWriter, BytesCow};
271
272    use crate::config::{AVCDecoderConfigurationRecord, AvccExtendedConfig};
273    use crate::sps::SpsExtended;
274
275    #[test]
276    fn test_config_parse() {
277        let sample_sps = b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0";
278        let mut data = Vec::new();
279        let mut writer = BitWriter::new(&mut data);
280
281        // configuration_version
282        writer.write_bits(1, 8).unwrap();
283        // profile_indication
284        writer.write_bits(100, 8).unwrap();
285        // profile_compatibility
286        writer.write_bits(0, 8).unwrap();
287        // level_indication
288        writer.write_bits(31, 8).unwrap();
289        // length_size_minus_one
290        writer.write_bits(3, 8).unwrap();
291
292        // num_of_sequence_parameter_sets
293        writer.write_bits(1, 8).unwrap();
294        // sps_length
295        writer.write_u16::<BigEndian>(sample_sps.len() as u16).unwrap();
296        // sps
297        // this was from the old test
298        writer.write_all(sample_sps).unwrap();
299
300        // num_of_picture_parameter_sets
301        writer.write_bits(1, 8).unwrap();
302        // pps_length
303        writer.write_bits(6, 16).unwrap();
304        writer.write_all(b"h\xeb\xe3\xcb\"\xc0\x00\x00").unwrap();
305
306        // chroma_format_idc
307        writer.write_bits(1, 8).unwrap();
308        // bit_depth_luma_minus8
309        writer.write_bits(0, 8).unwrap();
310        // bit_depth_chroma_minus8
311        writer.write_bits(0, 8).unwrap();
312        // number_of_sequence_parameter_set_ext
313        writer.write_bits(0, 8).unwrap();
314        writer.finish().unwrap();
315
316        let result =
317            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
318
319        let sps = &result.sps[0];
320
321        assert_eq!(sps.as_bytes(), *sample_sps);
322    }
323
324    #[test]
325    #[cfg(feature = "isobmff")]
326    fn test_config_build() {
327        use isobmff::IsoSized;
328
329        // these may not be the same size due to the natural reduction from the SPS parsing.
330        // in specific, the sps size function may return a lower size than the original bitstring.
331        // reduction will occur from rebuilding the sps and from rebuilding the sps_ext.
332        let data = b"\x01d\0\x1f\xff\xe1\0\x19\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x03\x00\x08\x00\x00\x03\x01\xE0\x01\0\x06h\xeb\xe3\xcb\"\xc0\xfd\xf8\xf8\0";
333
334        let config =
335            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
336
337        assert_eq!(config.size(), data.len());
338
339        let mut buf = Vec::new();
340        config.serialize(&mut buf).unwrap();
341
342        assert_eq!(buf, data.to_vec());
343    }
344
345    #[test]
346    fn test_no_ext_cfg_for_profiles_66_77_88() {
347        let data = b"\x01B\x00\x1F\xFF\xE1\x00\x1Dgd\x00\x1F\xAC\xD9A\xE0m\xF9\xE6\xA0  (\x00\x00\x03\x00\x08\x00\x00\x03\x01\xE0x\xC1\x8C\xB0\x01\x00\x06h\xEB\xE3\xCB\"\xC0\xFD\xF8\xF8\x00";
348        let config =
349            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
350
351        assert_eq!(config.extended_config, None);
352    }
353
354    #[test]
355    #[cfg(feature = "isobmff")]
356    fn test_size_calculation_with_sequence_parameter_set_ext() {
357        use isobmff::IsoSized;
358
359        let extended_config = AvccExtendedConfig {
360            chroma_format_idc: 1,
361            bit_depth_luma_minus8: 0,
362            bit_depth_chroma_minus8: 0,
363            sequence_parameter_set_ext: vec![SpsExtended {
364                chroma_format_idc: 1,
365                separate_color_plane_flag: false,
366                bit_depth_luma_minus8: 2,
367                bit_depth_chroma_minus8: 3,
368                qpprime_y_zero_transform_bypass_flag: false,
369                scaling_matrix: vec![],
370            }],
371        };
372        let config = AVCDecoderConfigurationRecord {
373            configuration_version: 1,
374            profile_indication: 100,
375            profile_compatibility: 0,
376            level_indication: 31,
377            length_size_minus_one: 3,
378            sps: vec![BytesCow::from_static(
379                b"\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x00\x08\x00\x00\x01\xE0",
380            )],
381            pps: vec![BytesCow::from_static(b"ppsdata")],
382            extended_config: Some(extended_config),
383        };
384
385        assert_eq!(config.size(), 49);
386        insta::assert_debug_snapshot!(config, @r#"
387        AVCDecoderConfigurationRecord {
388            configuration_version: 1,
389            profile_indication: 100,
390            profile_compatibility: 0,
391            level_indication: 31,
392            length_size_minus_one: 3,
393            sps: [
394                b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\0\x08\0\0\x01\xe0",
395            ],
396            pps: [
397                b"ppsdata",
398            ],
399            extended_config: Some(
400                AvccExtendedConfig {
401                    chroma_format_idc: 1,
402                    bit_depth_luma_minus8: 0,
403                    bit_depth_chroma_minus8: 0,
404                    sequence_parameter_set_ext: [
405                        SpsExtended {
406                            chroma_format_idc: 1,
407                            separate_color_plane_flag: false,
408                            bit_depth_luma_minus8: 2,
409                            bit_depth_chroma_minus8: 3,
410                            qpprime_y_zero_transform_bypass_flag: false,
411                            scaling_matrix: [],
412                        },
413                    ],
414                },
415            ),
416        }
417        "#);
418    }
419
420    #[test]
421    fn test_build_with_sequence_parameter_set_ext() {
422        let extended_config = AvccExtendedConfig {
423            chroma_format_idc: 1,
424            bit_depth_luma_minus8: 0,
425            bit_depth_chroma_minus8: 0,
426            sequence_parameter_set_ext: vec![SpsExtended {
427                chroma_format_idc: 1,
428                separate_color_plane_flag: false,
429                bit_depth_luma_minus8: 2,
430                bit_depth_chroma_minus8: 3,
431                qpprime_y_zero_transform_bypass_flag: false,
432                scaling_matrix: vec![],
433            }],
434        };
435        let config = AVCDecoderConfigurationRecord {
436            configuration_version: 1,
437            profile_indication: 100,
438            profile_compatibility: 0,
439            level_indication: 31,
440            length_size_minus_one: 3,
441            sps: vec![BytesCow::from_static(
442                b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
443            )],
444            pps: vec![BytesCow::from_static(b"ppsdata")],
445            extended_config: Some(extended_config),
446        };
447
448        let mut buf = Vec::new();
449        config.serialize(&mut buf).unwrap();
450
451        let parsed =
452            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&buf[..])).unwrap();
453        assert_eq!(parsed.extended_config.unwrap().sequence_parameter_set_ext.len(), 1);
454        insta::assert_debug_snapshot!(config, @r#"
455        AVCDecoderConfigurationRecord {
456            configuration_version: 1,
457            profile_indication: 100,
458            profile_compatibility: 0,
459            level_indication: 31,
460            length_size_minus_one: 3,
461            sps: [
462                b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
463            ],
464            pps: [
465                b"ppsdata",
466            ],
467            extended_config: Some(
468                AvccExtendedConfig {
469                    chroma_format_idc: 1,
470                    bit_depth_luma_minus8: 0,
471                    bit_depth_chroma_minus8: 0,
472                    sequence_parameter_set_ext: [
473                        SpsExtended {
474                            chroma_format_idc: 1,
475                            separate_color_plane_flag: false,
476                            bit_depth_luma_minus8: 2,
477                            bit_depth_chroma_minus8: 3,
478                            qpprime_y_zero_transform_bypass_flag: false,
479                            scaling_matrix: [],
480                        },
481                    ],
482                },
483            ),
484        }
485        "#);
486    }
487}