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 isobmff::IsoSized;
270    use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
271    use scuffle_bytes_util::{BitWriter, BytesCow};
272
273    use crate::config::{AVCDecoderConfigurationRecord, AvccExtendedConfig};
274    use crate::sps::SpsExtended;
275
276    #[test]
277    fn test_config_parse() {
278        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";
279        let mut data = Vec::new();
280        let mut writer = BitWriter::new(&mut data);
281
282        // configuration_version
283        writer.write_bits(1, 8).unwrap();
284        // profile_indication
285        writer.write_bits(100, 8).unwrap();
286        // profile_compatibility
287        writer.write_bits(0, 8).unwrap();
288        // level_indication
289        writer.write_bits(31, 8).unwrap();
290        // length_size_minus_one
291        writer.write_bits(3, 8).unwrap();
292
293        // num_of_sequence_parameter_sets
294        writer.write_bits(1, 8).unwrap();
295        // sps_length
296        writer.write_u16::<BigEndian>(sample_sps.len() as u16).unwrap();
297        // sps
298        // this was from the old test
299        writer.write_all(sample_sps).unwrap();
300
301        // num_of_picture_parameter_sets
302        writer.write_bits(1, 8).unwrap();
303        // pps_length
304        writer.write_bits(6, 16).unwrap();
305        writer.write_all(b"h\xeb\xe3\xcb\"\xc0\x00\x00").unwrap();
306
307        // chroma_format_idc
308        writer.write_bits(1, 8).unwrap();
309        // bit_depth_luma_minus8
310        writer.write_bits(0, 8).unwrap();
311        // bit_depth_chroma_minus8
312        writer.write_bits(0, 8).unwrap();
313        // number_of_sequence_parameter_set_ext
314        writer.write_bits(0, 8).unwrap();
315        writer.finish().unwrap();
316
317        let result =
318            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
319
320        let sps = &result.sps[0];
321
322        assert_eq!(sps.as_bytes(), *sample_sps);
323    }
324
325    #[test]
326    fn test_config_build() {
327        // these may not be the same size due to the natural reduction from the SPS parsing.
328        // in specific, the sps size function may return a lower size than the original bitstring.
329        // reduction will occur from rebuilding the sps and from rebuilding the sps_ext.
330        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";
331
332        let config =
333            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
334
335        assert_eq!(config.size(), data.len());
336
337        let mut buf = Vec::new();
338        config.serialize(&mut buf).unwrap();
339
340        assert_eq!(buf, data.to_vec());
341    }
342
343    #[test]
344    fn test_no_ext_cfg_for_profiles_66_77_88() {
345        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";
346        let config =
347            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
348
349        assert_eq!(config.extended_config, None);
350    }
351
352    #[test]
353    fn test_size_calculation_with_sequence_parameter_set_ext() {
354        let extended_config = AvccExtendedConfig {
355            chroma_format_idc: 1,
356            bit_depth_luma_minus8: 0,
357            bit_depth_chroma_minus8: 0,
358            sequence_parameter_set_ext: vec![SpsExtended {
359                chroma_format_idc: 1,
360                separate_color_plane_flag: false,
361                bit_depth_luma_minus8: 2,
362                bit_depth_chroma_minus8: 3,
363                qpprime_y_zero_transform_bypass_flag: false,
364                scaling_matrix: vec![],
365            }],
366        };
367        let config = AVCDecoderConfigurationRecord {
368            configuration_version: 1,
369            profile_indication: 100,
370            profile_compatibility: 0,
371            level_indication: 31,
372            length_size_minus_one: 3,
373            sps: vec![BytesCow::from_static(
374                b"\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x00\x08\x00\x00\x01\xE0",
375            )],
376            pps: vec![BytesCow::from_static(b"ppsdata")],
377            extended_config: Some(extended_config),
378        };
379
380        assert_eq!(config.size(), 49);
381        insta::assert_debug_snapshot!(config, @r#"
382        AVCDecoderConfigurationRecord {
383            configuration_version: 1,
384            profile_indication: 100,
385            profile_compatibility: 0,
386            level_indication: 31,
387            length_size_minus_one: 3,
388            sps: [
389                b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\0\x08\0\0\x01\xe0",
390            ],
391            pps: [
392                b"ppsdata",
393            ],
394            extended_config: Some(
395                AvccExtendedConfig {
396                    chroma_format_idc: 1,
397                    bit_depth_luma_minus8: 0,
398                    bit_depth_chroma_minus8: 0,
399                    sequence_parameter_set_ext: [
400                        SpsExtended {
401                            chroma_format_idc: 1,
402                            separate_color_plane_flag: false,
403                            bit_depth_luma_minus8: 2,
404                            bit_depth_chroma_minus8: 3,
405                            qpprime_y_zero_transform_bypass_flag: false,
406                            scaling_matrix: [],
407                        },
408                    ],
409                },
410            ),
411        }
412        "#);
413    }
414
415    #[test]
416    fn test_build_with_sequence_parameter_set_ext() {
417        let extended_config = AvccExtendedConfig {
418            chroma_format_idc: 1,
419            bit_depth_luma_minus8: 0,
420            bit_depth_chroma_minus8: 0,
421            sequence_parameter_set_ext: vec![SpsExtended {
422                chroma_format_idc: 1,
423                separate_color_plane_flag: false,
424                bit_depth_luma_minus8: 2,
425                bit_depth_chroma_minus8: 3,
426                qpprime_y_zero_transform_bypass_flag: false,
427                scaling_matrix: vec![],
428            }],
429        };
430        let config = AVCDecoderConfigurationRecord {
431            configuration_version: 1,
432            profile_indication: 100,
433            profile_compatibility: 0,
434            level_indication: 31,
435            length_size_minus_one: 3,
436            sps: vec![BytesCow::from_static(
437                b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
438            )],
439            pps: vec![BytesCow::from_static(b"ppsdata")],
440            extended_config: Some(extended_config),
441        };
442
443        let mut buf = Vec::new();
444        config.serialize(&mut buf).unwrap();
445
446        let parsed =
447            AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&buf[..])).unwrap();
448        assert_eq!(parsed.extended_config.unwrap().sequence_parameter_set_ext.len(), 1);
449        insta::assert_debug_snapshot!(config, @r#"
450        AVCDecoderConfigurationRecord {
451            configuration_version: 1,
452            profile_indication: 100,
453            profile_compatibility: 0,
454            level_indication: 31,
455            length_size_minus_one: 3,
456            sps: [
457                b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0  (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
458            ],
459            pps: [
460                b"ppsdata",
461            ],
462            extended_config: Some(
463                AvccExtendedConfig {
464                    chroma_format_idc: 1,
465                    bit_depth_luma_minus8: 0,
466                    bit_depth_chroma_minus8: 0,
467                    sequence_parameter_set_ext: [
468                        SpsExtended {
469                            chroma_format_idc: 1,
470                            separate_color_plane_flag: false,
471                            bit_depth_luma_minus8: 2,
472                            bit_depth_chroma_minus8: 3,
473                            qpprime_y_zero_transform_bypass_flag: false,
474                            scaling_matrix: [],
475                        },
476                    ],
477                },
478            ),
479        }
480        "#);
481    }
482}