mp4_atom/moov/trak/mdia/minf/stbl/stsd/hevc/
hvcc.rs

1use crate::*;
2
3#[derive(Default, Debug, Clone, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Hvcc {
6    pub configuration_version: u8,
7    pub general_profile_space: u8,
8    pub general_tier_flag: bool,
9    pub general_profile_idc: u8,
10    pub general_profile_compatibility_flags: [u8; 4],
11    pub general_constraint_indicator_flags: [u8; 6],
12    pub general_level_idc: u8,
13    pub min_spatial_segmentation_idc: u16,
14    pub parallelism_type: u8,
15    pub chroma_format_idc: u8,
16    pub bit_depth_luma_minus8: u8,
17    pub bit_depth_chroma_minus8: u8,
18    pub avg_frame_rate: u16,
19    pub constant_frame_rate: u8,
20    pub num_temporal_layers: u8,
21    pub temporal_id_nested: bool,
22    pub length_size_minus_one: u8,
23    pub arrays: Vec<HvcCArray>,
24}
25
26impl Hvcc {
27    pub fn new() -> Self {
28        Self {
29            configuration_version: 1,
30            ..Default::default()
31        }
32    }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Default)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct HvcCArray {
38    pub completeness: bool,
39    pub nal_unit_type: u8,
40    pub nalus: Vec<Vec<u8>>,
41}
42
43impl Atom for Hvcc {
44    const KIND: FourCC = FourCC::new(b"hvcC");
45
46    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
47        let configuration_version = u8::decode(buf)?;
48        let params = u8::decode(buf)?;
49        let general_profile_space = (params & 0b11000000) >> 6;
50        let general_tier_flag = ((params & 0b00100000) >> 5) != 0;
51        let general_profile_idc = params & 0b00011111;
52
53        let general_profile_compatibility_flags = <[u8; 4]>::decode(buf)?;
54        let general_constraint_indicator_flags = <[u8; 6]>::decode(buf)?;
55        let general_level_idc = u8::decode(buf)?;
56        let min_spatial_segmentation_idc = u16::decode(buf)? & 0x0FFF;
57        let parallelism_type = u8::decode(buf)? & 0b11;
58        let chroma_format_idc = u8::decode(buf)? & 0b11;
59        let bit_depth_luma_minus8 = u8::decode(buf)? & 0b111;
60        let bit_depth_chroma_minus8 = u8::decode(buf)? & 0b111;
61        let avg_frame_rate = u16::decode(buf)?;
62
63        let params = u8::decode(buf)?;
64        let constant_frame_rate = (params & 0b11000000) >> 6;
65        let num_temporal_layers = (params & 0b00111000) >> 3;
66        let temporal_id_nested = ((params & 0b00000100) >> 2) != 0;
67        let length_size_minus_one = params & 0b000011;
68
69        let num_of_arrays = u8::decode(buf)?;
70
71        let mut arrays = Vec::with_capacity(num_of_arrays as _);
72        for _ in 0..num_of_arrays {
73            let params = u8::decode(buf)?;
74            let num_nalus = u16::decode(buf)?;
75            let mut nalus = Vec::with_capacity(num_nalus as usize);
76
77            for _ in 0..num_nalus {
78                let size = u16::decode(buf)? as usize;
79                let data = Vec::decode_exact(buf, size)?;
80                nalus.push(data)
81            }
82
83            arrays.push(HvcCArray {
84                completeness: (params & 0b10000000) > 0,
85                nal_unit_type: params & 0b111111,
86                nalus,
87            });
88        }
89
90        Ok(Hvcc {
91            configuration_version,
92            general_profile_space,
93            general_tier_flag,
94            general_profile_idc,
95            general_profile_compatibility_flags,
96            general_constraint_indicator_flags,
97            general_level_idc,
98            min_spatial_segmentation_idc,
99            parallelism_type,
100            chroma_format_idc,
101            bit_depth_luma_minus8,
102            bit_depth_chroma_minus8,
103            avg_frame_rate,
104            constant_frame_rate,
105            num_temporal_layers,
106            temporal_id_nested,
107            length_size_minus_one,
108            arrays,
109        })
110    }
111
112    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
113        self.configuration_version.encode(buf)?;
114        let general_profile_space = (self.general_profile_space & 0b11) << 6;
115        let general_tier_flag = u8::from(self.general_tier_flag) << 5;
116        let general_profile_idc = self.general_profile_idc & 0b11111;
117
118        (general_profile_space | general_tier_flag | general_profile_idc).encode(buf)?;
119        self.general_profile_compatibility_flags.encode(buf)?;
120        self.general_constraint_indicator_flags.encode(buf)?;
121        self.general_level_idc.encode(buf)?;
122
123        (0xF000 | (self.min_spatial_segmentation_idc & 0x0FFF)).encode(buf)?;
124        (0b11111100 | (self.parallelism_type & 0b11)).encode(buf)?;
125        (0b11111100 | (self.chroma_format_idc & 0b11)).encode(buf)?;
126        (0b11111000 | (self.bit_depth_luma_minus8 & 0b111)).encode(buf)?;
127        (0b11111000 | (self.bit_depth_chroma_minus8 & 0b111)).encode(buf)?;
128        self.avg_frame_rate.encode(buf)?;
129
130        let constant_frame_rate = (self.constant_frame_rate & 0b11) << 6;
131        let num_temporal_layers = (self.num_temporal_layers & 0b111) << 3;
132        let temporal_id_nested = u8::from(self.temporal_id_nested) << 2;
133        let length_size_minus_one = self.length_size_minus_one & 0b11;
134        (constant_frame_rate | num_temporal_layers | temporal_id_nested | length_size_minus_one)
135            .encode(buf)?;
136        (self.arrays.len() as u8).encode(buf)?;
137        for arr in &self.arrays {
138            ((arr.nal_unit_type & 0b111111) | (u8::from(arr.completeness) << 7)).encode(buf)?;
139            (arr.nalus.len() as u16).encode(buf)?;
140
141            for nalu in &arr.nalus {
142                (nalu.len() as u16).encode(buf)?;
143                nalu.encode(buf)?;
144            }
145        }
146
147        Ok(())
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_hev1() {
157        let expected = Hev1 {
158            visual: Visual {
159                data_reference_index: 1,
160                width: 320,
161                height: 240,
162                horizresolution: 0x48.into(),
163                vertresolution: 0x48.into(),
164                frame_count: 1,
165                compressor: "ya boy".into(),
166                depth: 24,
167            },
168            hvcc: Hvcc {
169                configuration_version: 1,
170                ..Default::default()
171            },
172            btrt: None,
173            colr: None,
174            pasp: None,
175            taic: None,
176            fiel: None,
177        };
178        let mut buf = Vec::new();
179        expected.encode(&mut buf).unwrap();
180
181        let mut buf = buf.as_ref();
182        let decoded = Hev1::decode(&mut buf).unwrap();
183        assert_eq!(decoded, expected);
184    }
185
186    // From libheif encoded image, essentially x265 underneath
187    const ENCODED_HVCC_LIBHEIF: &[u8] = &[
188        0x00, 0x00, 0x00, 0x7e, 0x68, 0x76, 0x63, 0x43, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90,
189        0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f,
190        0x03, 0x20, 0x00, 0x01, 0x00, 0x19, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00,
191        0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x99, 0x8a, 0x02,
192        0x40, 0x21, 0x00, 0x01, 0x00, 0x30, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00,
193        0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xa0, 0x02, 0x80, 0x80, 0x35, 0x9f,
194        0x59, 0x66, 0x62, 0xa4, 0x91, 0x26, 0xbf, 0xfc, 0x1a, 0xb0, 0x1a, 0xac, 0x04, 0x00, 0x00,
195        0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x64, 0x20, 0x22, 0x00, 0x01, 0x00, 0x07, 0x44,
196        0x01, 0xc1, 0x72, 0xb6, 0x62, 0x40,
197    ];
198
199    #[test]
200    fn test_hvcc_libheif_decode() {
201        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_HVCC_LIBHEIF);
202
203        let hvcc = Hvcc {
204            configuration_version: 1,
205            general_profile_space: 0,
206            general_tier_flag: false,
207            general_profile_idc: 1,
208            general_profile_compatibility_flags: [96, 0, 0, 0],
209            general_constraint_indicator_flags: [144, 0, 0, 0, 0, 0],
210            general_level_idc: 120,
211            min_spatial_segmentation_idc: 0,
212            parallelism_type: 0,
213            chroma_format_idc: 1,
214            bit_depth_luma_minus8: 0,
215            bit_depth_chroma_minus8: 0,
216            avg_frame_rate: 0,
217            constant_frame_rate: 0,
218            num_temporal_layers: 1,
219            temporal_id_nested: true,
220            length_size_minus_one: 3,
221            arrays: vec![
222                HvcCArray {
223                    completeness: false,
224                    nal_unit_type: 32,
225                    nalus: vec![vec![
226                        64, 1, 12, 1, 255, 255, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120,
227                        153, 138, 2, 64,
228                    ]],
229                },
230                HvcCArray {
231                    completeness: false,
232                    nal_unit_type: 33,
233                    nalus: vec![vec![
234                        66, 1, 1, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120, 160, 2, 128,
235                        128, 53, 159, 89, 102, 98, 164, 145, 38, 191, 252, 26, 176, 26, 172, 4, 0,
236                        0, 3, 0, 4, 0, 0, 3, 0, 100, 32,
237                    ]],
238                },
239                HvcCArray {
240                    completeness: false,
241                    nal_unit_type: 34,
242                    nalus: vec![vec![68, 1, 193, 114, 182, 98, 64]],
243                },
244            ],
245        };
246        let decoded = Hvcc::decode(buf).unwrap();
247        assert_eq!(decoded, hvcc);
248    }
249
250    #[test]
251    fn test_hvcc_libheif_encode() {
252        let hvcc = Hvcc {
253            configuration_version: 1,
254            general_profile_space: 0,
255            general_tier_flag: false,
256            general_profile_idc: 1,
257            general_profile_compatibility_flags: [96, 0, 0, 0],
258            general_constraint_indicator_flags: [144, 0, 0, 0, 0, 0],
259            general_level_idc: 120,
260            min_spatial_segmentation_idc: 0,
261            parallelism_type: 0,
262            chroma_format_idc: 1,
263            bit_depth_luma_minus8: 0,
264            bit_depth_chroma_minus8: 0,
265            avg_frame_rate: 0,
266            constant_frame_rate: 0,
267            num_temporal_layers: 1,
268            temporal_id_nested: true,
269            length_size_minus_one: 3,
270            arrays: vec![
271                HvcCArray {
272                    completeness: false,
273                    nal_unit_type: 32,
274                    nalus: vec![vec![
275                        64, 1, 12, 1, 255, 255, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120,
276                        153, 138, 2, 64,
277                    ]],
278                },
279                HvcCArray {
280                    completeness: false,
281                    nal_unit_type: 33,
282                    nalus: vec![vec![
283                        66, 1, 1, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120, 160, 2, 128,
284                        128, 53, 159, 89, 102, 98, 164, 145, 38, 191, 252, 26, 176, 26, 172, 4, 0,
285                        0, 3, 0, 4, 0, 0, 3, 0, 100, 32,
286                    ]],
287                },
288                HvcCArray {
289                    completeness: false,
290                    nal_unit_type: 34,
291                    nalus: vec![vec![68, 1, 193, 114, 182, 98, 64]],
292                },
293            ],
294        };
295        let mut buf = Vec::new();
296        hvcc.encode(&mut buf).unwrap();
297
298        assert_eq!(buf.as_slice(), ENCODED_HVCC_LIBHEIF);
299    }
300
301    // From MPEG file format tests - published/nalu/hevc/hvc1_only.mp4
302    // probably gpac over x265
303    const ENCODED_HVCC_MPEG: &[u8] = &[
304        0x00, 0x00, 0x00, 0xa8, 0x68, 0x76, 0x63, 0x43, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00,
305        0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xf0, 0x00, 0xfd, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f,
306        0x03, 0xa0, 0x00, 0x01, 0x00, 0x41, 0x40, 0x01, 0x0c, 0x11, 0xff, 0xff, 0x01, 0x60, 0x00,
307        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7b, 0x94, 0x90,
308        0x57, 0x00, 0x00, 0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x7f, 0x7b, 0x10, 0x00, 0x04,
309        0x30, 0x24, 0xcf, 0x75, 0x88, 0x0f, 0x00, 0x08, 0x82, 0x80, 0x7b, 0x07, 0x80, 0x04, 0x38,
310        0xa0, 0x0e, 0x0a, 0x52, 0x7b, 0x90, 0xa0, 0x20, 0x20, 0x2c, 0x10, 0xa1, 0x00, 0x01, 0x00,
311        0x31, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
312        0x00, 0x00, 0x03, 0x00, 0x7b, 0xa0, 0x07, 0x82, 0x00, 0x88, 0x7d, 0xe5, 0x94, 0x99, 0x24,
313        0x6d, 0x86, 0x96, 0x22, 0xaa, 0x4c, 0x4c, 0x32, 0xfb, 0x3e, 0xbc, 0xdf, 0x96, 0x7d, 0x78,
314        0x51, 0x18, 0x9c, 0xbb, 0x64, 0xa2, 0x00, 0x01, 0x00, 0x08, 0x44, 0x01, 0xc1, 0xa5, 0x58,
315        0x11, 0xd0, 0x2a,
316    ];
317
318    #[test]
319    fn test_hvcc_mpeg_decode() {
320        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_HVCC_MPEG);
321
322        /* From MPEG file dump (essentially GPAC):
323                      "HEVCDecoderConfigurationRecord": {
324                      "@nal_unit_size": "4",
325                      "@configurationVersion": "1",
326                      "@profile_space": "0",
327                      "@tier_flag": "0",
328                      "@profile_idc": "1",
329                      "@general_profile_compatibility_flags": "60000000",
330                      "@progressive_source_flag": "0",
331                      "@interlaced_source_flag": "0",
332                      "@non_packed_constraint_flag": "0",
333                      "@frame_only_constraint_flag": "0",
334                      "@constraint_indicator_flags": "0",
335                      "@level_idc": "123",
336                      "@min_spatial_segmentation_idc": "0",
337                      "@parallelismType": "1",
338                      "@chroma_format": "YUV 4:2:0",
339                      "@luma_bit_depth": "8",
340                      "@chroma_bit_depth": "8",
341                      "@avgFrameRate": "0",
342                      "@constantFrameRate": "0",
343                      "@numTemporalLayers": "1",
344                      "@temporalIdNested": "1",
345                      "ParameterSetArray": [
346                        {
347                          "@nalu_type": "32",
348                          "@complete_set": "1",
349                          "ParameterSet": {
350                            "@size": "65",
351                            "@content": "data:application/octet-string,40010C11FFFF0160000003000003000003000003007B94905700000303E90000EA607F7B1000043024CF75880F000882807B07800438A00E0A527B90A020202C10"
352                          }
353                        },
354                        {
355                          "@nalu_type": "33",
356                          "@complete_set": "1",
357                          "ParameterSet": {
358                            "@size": "49",
359                            "@content": "data:application/octet-string,4201010160000003000003000003000003007BA0078200887DE59499246D869622AA4C4C32FB3EBCDF967D7851189CBB64"
360                          }
361                        },
362                        {
363                          "@nalu_type": "34",
364                          "@complete_set": "1",
365                          "ParameterSet": {
366                            "@size": "8",
367                            "@content": "data:application/octet-string,4401C1A55811D02A"
368                          }
369                        }
370                      ]
371                    }
372                  },
373        */
374        let hvcc = Hvcc {
375            configuration_version: 1,
376            general_profile_space: 0,
377            general_tier_flag: false,
378            general_profile_idc: 1,
379            general_profile_compatibility_flags: [0x60, 0x00, 0x00, 0x00],
380            general_constraint_indicator_flags: [0, 0, 0, 0, 0, 0],
381            general_level_idc: 123,
382            min_spatial_segmentation_idc: 0,
383            parallelism_type: 1,
384            chroma_format_idc: 1,
385            bit_depth_luma_minus8: 0,
386            bit_depth_chroma_minus8: 0,
387            avg_frame_rate: 0,
388            constant_frame_rate: 0,
389            num_temporal_layers: 1,
390            temporal_id_nested: true,
391            length_size_minus_one: 3,
392            arrays: vec![
393                HvcCArray {
394                    completeness: true,
395                    nal_unit_type: 32,
396                    nalus: vec![vec![
397                        0x40, 0x01, 0x0C, 0x11, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00,
398                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0x94, 0x90,
399                        0x57, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x7F, 0x7B,
400                        0x10, 0x00, 0x04, 0x30, 0x24, 0xCF, 0x75, 0x88, 0x0F, 0x00, 0x08, 0x82,
401                        0x80, 0x7B, 0x07, 0x80, 0x04, 0x38, 0xA0, 0x0E, 0x0A, 0x52, 0x7B, 0x90,
402                        0xA0, 0x20, 0x20, 0x2C, 0x10,
403                    ]],
404                },
405                HvcCArray {
406                    completeness: true,
407                    nal_unit_type: 33,
408                    nalus: vec![vec![
409                        0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
410                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0xA0, 0x07, 0x82, 0x00, 0x88,
411                        0x7D, 0xE5, 0x94, 0x99, 0x24, 0x6D, 0x86, 0x96, 0x22, 0xAA, 0x4C, 0x4C,
412                        0x32, 0xFB, 0x3E, 0xBC, 0xDF, 0x96, 0x7D, 0x78, 0x51, 0x18, 0x9C, 0xBB,
413                        0x64,
414                    ]],
415                },
416                HvcCArray {
417                    completeness: true,
418                    nal_unit_type: 34,
419                    nalus: vec![vec![0x44, 0x01, 0xC1, 0xA5, 0x58, 0x11, 0xD0, 0x2A]],
420                },
421            ],
422        };
423        let decoded = Hvcc::decode(buf).unwrap();
424        assert_eq!(decoded, hvcc);
425    }
426
427    #[test]
428    fn test_hvcc_mpeg_encode() {
429        let hvcc = Hvcc {
430            configuration_version: 1,
431            general_profile_space: 0,
432            general_tier_flag: false,
433            general_profile_idc: 1,
434            general_profile_compatibility_flags: [0x60, 0x00, 0x00, 0x00],
435            general_constraint_indicator_flags: [0, 0, 0, 0, 0, 0],
436            general_level_idc: 123,
437            min_spatial_segmentation_idc: 0,
438            parallelism_type: 1,
439            chroma_format_idc: 1,
440            bit_depth_luma_minus8: 0,
441            bit_depth_chroma_minus8: 0,
442            avg_frame_rate: 0,
443            constant_frame_rate: 0,
444            num_temporal_layers: 1,
445            temporal_id_nested: true,
446            length_size_minus_one: 3,
447            arrays: vec![
448                HvcCArray {
449                    completeness: true,
450                    nal_unit_type: 32,
451                    nalus: vec![vec![
452                        0x40, 0x01, 0x0C, 0x11, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00,
453                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0x94, 0x90,
454                        0x57, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x7F, 0x7B,
455                        0x10, 0x00, 0x04, 0x30, 0x24, 0xCF, 0x75, 0x88, 0x0F, 0x00, 0x08, 0x82,
456                        0x80, 0x7B, 0x07, 0x80, 0x04, 0x38, 0xA0, 0x0E, 0x0A, 0x52, 0x7B, 0x90,
457                        0xA0, 0x20, 0x20, 0x2C, 0x10,
458                    ]],
459                },
460                HvcCArray {
461                    completeness: true,
462                    nal_unit_type: 33,
463                    nalus: vec![vec![
464                        0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
465                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0xA0, 0x07, 0x82, 0x00, 0x88,
466                        0x7D, 0xE5, 0x94, 0x99, 0x24, 0x6D, 0x86, 0x96, 0x22, 0xAA, 0x4C, 0x4C,
467                        0x32, 0xFB, 0x3E, 0xBC, 0xDF, 0x96, 0x7D, 0x78, 0x51, 0x18, 0x9C, 0xBB,
468                        0x64,
469                    ]],
470                },
471                HvcCArray {
472                    completeness: true,
473                    nal_unit_type: 34,
474                    nalus: vec![vec![0x44, 0x01, 0xC1, 0xA5, 0x58, 0x11, 0xD0, 0x2A]],
475                },
476            ],
477        };
478        let mut buf = Vec::new();
479        hvcc.encode(&mut buf).unwrap();
480
481        assert_eq!(buf.as_slice(), ENCODED_HVCC_MPEG);
482    }
483}