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        };
177        let mut buf = Vec::new();
178        expected.encode(&mut buf).unwrap();
179
180        let mut buf = buf.as_ref();
181        let decoded = Hev1::decode(&mut buf).unwrap();
182        assert_eq!(decoded, expected);
183    }
184
185    // From libheif encoded image, essentially x265 underneath
186    const ENCODED_HVCC_LIBHEIF: &[u8] = &[
187        0x00, 0x00, 0x00, 0x7e, 0x68, 0x76, 0x63, 0x43, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90,
188        0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f,
189        0x03, 0x20, 0x00, 0x01, 0x00, 0x19, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00,
190        0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x99, 0x8a, 0x02,
191        0x40, 0x21, 0x00, 0x01, 0x00, 0x30, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00,
192        0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xa0, 0x02, 0x80, 0x80, 0x35, 0x9f,
193        0x59, 0x66, 0x62, 0xa4, 0x91, 0x26, 0xbf, 0xfc, 0x1a, 0xb0, 0x1a, 0xac, 0x04, 0x00, 0x00,
194        0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x64, 0x20, 0x22, 0x00, 0x01, 0x00, 0x07, 0x44,
195        0x01, 0xc1, 0x72, 0xb6, 0x62, 0x40,
196    ];
197
198    #[test]
199    fn test_hvcc_libheif_decode() {
200        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_HVCC_LIBHEIF);
201
202        let hvcc = Hvcc {
203            configuration_version: 1,
204            general_profile_space: 0,
205            general_tier_flag: false,
206            general_profile_idc: 1,
207            general_profile_compatibility_flags: [96, 0, 0, 0],
208            general_constraint_indicator_flags: [144, 0, 0, 0, 0, 0],
209            general_level_idc: 120,
210            min_spatial_segmentation_idc: 0,
211            parallelism_type: 0,
212            chroma_format_idc: 1,
213            bit_depth_luma_minus8: 0,
214            bit_depth_chroma_minus8: 0,
215            avg_frame_rate: 0,
216            constant_frame_rate: 0,
217            num_temporal_layers: 1,
218            temporal_id_nested: true,
219            length_size_minus_one: 3,
220            arrays: vec![
221                HvcCArray {
222                    completeness: false,
223                    nal_unit_type: 32,
224                    nalus: vec![vec![
225                        64, 1, 12, 1, 255, 255, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120,
226                        153, 138, 2, 64,
227                    ]],
228                },
229                HvcCArray {
230                    completeness: false,
231                    nal_unit_type: 33,
232                    nalus: vec![vec![
233                        66, 1, 1, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120, 160, 2, 128,
234                        128, 53, 159, 89, 102, 98, 164, 145, 38, 191, 252, 26, 176, 26, 172, 4, 0,
235                        0, 3, 0, 4, 0, 0, 3, 0, 100, 32,
236                    ]],
237                },
238                HvcCArray {
239                    completeness: false,
240                    nal_unit_type: 34,
241                    nalus: vec![vec![68, 1, 193, 114, 182, 98, 64]],
242                },
243            ],
244        };
245        let decoded = Hvcc::decode(buf).unwrap();
246        assert_eq!(decoded, hvcc);
247    }
248
249    #[test]
250    fn test_hvcc_libheif_encode() {
251        let hvcc = Hvcc {
252            configuration_version: 1,
253            general_profile_space: 0,
254            general_tier_flag: false,
255            general_profile_idc: 1,
256            general_profile_compatibility_flags: [96, 0, 0, 0],
257            general_constraint_indicator_flags: [144, 0, 0, 0, 0, 0],
258            general_level_idc: 120,
259            min_spatial_segmentation_idc: 0,
260            parallelism_type: 0,
261            chroma_format_idc: 1,
262            bit_depth_luma_minus8: 0,
263            bit_depth_chroma_minus8: 0,
264            avg_frame_rate: 0,
265            constant_frame_rate: 0,
266            num_temporal_layers: 1,
267            temporal_id_nested: true,
268            length_size_minus_one: 3,
269            arrays: vec![
270                HvcCArray {
271                    completeness: false,
272                    nal_unit_type: 32,
273                    nalus: vec![vec![
274                        64, 1, 12, 1, 255, 255, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120,
275                        153, 138, 2, 64,
276                    ]],
277                },
278                HvcCArray {
279                    completeness: false,
280                    nal_unit_type: 33,
281                    nalus: vec![vec![
282                        66, 1, 1, 1, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, 0, 3, 0, 120, 160, 2, 128,
283                        128, 53, 159, 89, 102, 98, 164, 145, 38, 191, 252, 26, 176, 26, 172, 4, 0,
284                        0, 3, 0, 4, 0, 0, 3, 0, 100, 32,
285                    ]],
286                },
287                HvcCArray {
288                    completeness: false,
289                    nal_unit_type: 34,
290                    nalus: vec![vec![68, 1, 193, 114, 182, 98, 64]],
291                },
292            ],
293        };
294        let mut buf = Vec::new();
295        hvcc.encode(&mut buf).unwrap();
296
297        assert_eq!(buf.as_slice(), ENCODED_HVCC_LIBHEIF);
298    }
299
300    // From MPEG file format tests - published/nalu/hevc/hvc1_only.mp4
301    // probably gpac over x265
302    const ENCODED_HVCC_MPEG: &[u8] = &[
303        0x00, 0x00, 0x00, 0xa8, 0x68, 0x76, 0x63, 0x43, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00,
304        0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xf0, 0x00, 0xfd, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f,
305        0x03, 0xa0, 0x00, 0x01, 0x00, 0x41, 0x40, 0x01, 0x0c, 0x11, 0xff, 0xff, 0x01, 0x60, 0x00,
306        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7b, 0x94, 0x90,
307        0x57, 0x00, 0x00, 0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x7f, 0x7b, 0x10, 0x00, 0x04,
308        0x30, 0x24, 0xcf, 0x75, 0x88, 0x0f, 0x00, 0x08, 0x82, 0x80, 0x7b, 0x07, 0x80, 0x04, 0x38,
309        0xa0, 0x0e, 0x0a, 0x52, 0x7b, 0x90, 0xa0, 0x20, 0x20, 0x2c, 0x10, 0xa1, 0x00, 0x01, 0x00,
310        0x31, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
311        0x00, 0x00, 0x03, 0x00, 0x7b, 0xa0, 0x07, 0x82, 0x00, 0x88, 0x7d, 0xe5, 0x94, 0x99, 0x24,
312        0x6d, 0x86, 0x96, 0x22, 0xaa, 0x4c, 0x4c, 0x32, 0xfb, 0x3e, 0xbc, 0xdf, 0x96, 0x7d, 0x78,
313        0x51, 0x18, 0x9c, 0xbb, 0x64, 0xa2, 0x00, 0x01, 0x00, 0x08, 0x44, 0x01, 0xc1, 0xa5, 0x58,
314        0x11, 0xd0, 0x2a,
315    ];
316
317    #[test]
318    fn test_hvcc_mpeg_decode() {
319        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_HVCC_MPEG);
320
321        /* From MPEG file dump (essentially GPAC):
322                      "HEVCDecoderConfigurationRecord": {
323                      "@nal_unit_size": "4",
324                      "@configurationVersion": "1",
325                      "@profile_space": "0",
326                      "@tier_flag": "0",
327                      "@profile_idc": "1",
328                      "@general_profile_compatibility_flags": "60000000",
329                      "@progressive_source_flag": "0",
330                      "@interlaced_source_flag": "0",
331                      "@non_packed_constraint_flag": "0",
332                      "@frame_only_constraint_flag": "0",
333                      "@constraint_indicator_flags": "0",
334                      "@level_idc": "123",
335                      "@min_spatial_segmentation_idc": "0",
336                      "@parallelismType": "1",
337                      "@chroma_format": "YUV 4:2:0",
338                      "@luma_bit_depth": "8",
339                      "@chroma_bit_depth": "8",
340                      "@avgFrameRate": "0",
341                      "@constantFrameRate": "0",
342                      "@numTemporalLayers": "1",
343                      "@temporalIdNested": "1",
344                      "ParameterSetArray": [
345                        {
346                          "@nalu_type": "32",
347                          "@complete_set": "1",
348                          "ParameterSet": {
349                            "@size": "65",
350                            "@content": "data:application/octet-string,40010C11FFFF0160000003000003000003000003007B94905700000303E90000EA607F7B1000043024CF75880F000882807B07800438A00E0A527B90A020202C10"
351                          }
352                        },
353                        {
354                          "@nalu_type": "33",
355                          "@complete_set": "1",
356                          "ParameterSet": {
357                            "@size": "49",
358                            "@content": "data:application/octet-string,4201010160000003000003000003000003007BA0078200887DE59499246D869622AA4C4C32FB3EBCDF967D7851189CBB64"
359                          }
360                        },
361                        {
362                          "@nalu_type": "34",
363                          "@complete_set": "1",
364                          "ParameterSet": {
365                            "@size": "8",
366                            "@content": "data:application/octet-string,4401C1A55811D02A"
367                          }
368                        }
369                      ]
370                    }
371                  },
372        */
373        let hvcc = Hvcc {
374            configuration_version: 1,
375            general_profile_space: 0,
376            general_tier_flag: false,
377            general_profile_idc: 1,
378            general_profile_compatibility_flags: [0x60, 0x00, 0x00, 0x00],
379            general_constraint_indicator_flags: [0, 0, 0, 0, 0, 0],
380            general_level_idc: 123,
381            min_spatial_segmentation_idc: 0,
382            parallelism_type: 1,
383            chroma_format_idc: 1,
384            bit_depth_luma_minus8: 0,
385            bit_depth_chroma_minus8: 0,
386            avg_frame_rate: 0,
387            constant_frame_rate: 0,
388            num_temporal_layers: 1,
389            temporal_id_nested: true,
390            length_size_minus_one: 3,
391            arrays: vec![
392                HvcCArray {
393                    completeness: true,
394                    nal_unit_type: 32,
395                    nalus: vec![vec![
396                        0x40, 0x01, 0x0C, 0x11, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00,
397                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0x94, 0x90,
398                        0x57, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x7F, 0x7B,
399                        0x10, 0x00, 0x04, 0x30, 0x24, 0xCF, 0x75, 0x88, 0x0F, 0x00, 0x08, 0x82,
400                        0x80, 0x7B, 0x07, 0x80, 0x04, 0x38, 0xA0, 0x0E, 0x0A, 0x52, 0x7B, 0x90,
401                        0xA0, 0x20, 0x20, 0x2C, 0x10,
402                    ]],
403                },
404                HvcCArray {
405                    completeness: true,
406                    nal_unit_type: 33,
407                    nalus: vec![vec![
408                        0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
409                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0xA0, 0x07, 0x82, 0x00, 0x88,
410                        0x7D, 0xE5, 0x94, 0x99, 0x24, 0x6D, 0x86, 0x96, 0x22, 0xAA, 0x4C, 0x4C,
411                        0x32, 0xFB, 0x3E, 0xBC, 0xDF, 0x96, 0x7D, 0x78, 0x51, 0x18, 0x9C, 0xBB,
412                        0x64,
413                    ]],
414                },
415                HvcCArray {
416                    completeness: true,
417                    nal_unit_type: 34,
418                    nalus: vec![vec![0x44, 0x01, 0xC1, 0xA5, 0x58, 0x11, 0xD0, 0x2A]],
419                },
420            ],
421        };
422        let decoded = Hvcc::decode(buf).unwrap();
423        assert_eq!(decoded, hvcc);
424    }
425
426    #[test]
427    fn test_hvcc_mpeg_encode() {
428        let hvcc = Hvcc {
429            configuration_version: 1,
430            general_profile_space: 0,
431            general_tier_flag: false,
432            general_profile_idc: 1,
433            general_profile_compatibility_flags: [0x60, 0x00, 0x00, 0x00],
434            general_constraint_indicator_flags: [0, 0, 0, 0, 0, 0],
435            general_level_idc: 123,
436            min_spatial_segmentation_idc: 0,
437            parallelism_type: 1,
438            chroma_format_idc: 1,
439            bit_depth_luma_minus8: 0,
440            bit_depth_chroma_minus8: 0,
441            avg_frame_rate: 0,
442            constant_frame_rate: 0,
443            num_temporal_layers: 1,
444            temporal_id_nested: true,
445            length_size_minus_one: 3,
446            arrays: vec![
447                HvcCArray {
448                    completeness: true,
449                    nal_unit_type: 32,
450                    nalus: vec![vec![
451                        0x40, 0x01, 0x0C, 0x11, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00,
452                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0x94, 0x90,
453                        0x57, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x7F, 0x7B,
454                        0x10, 0x00, 0x04, 0x30, 0x24, 0xCF, 0x75, 0x88, 0x0F, 0x00, 0x08, 0x82,
455                        0x80, 0x7B, 0x07, 0x80, 0x04, 0x38, 0xA0, 0x0E, 0x0A, 0x52, 0x7B, 0x90,
456                        0xA0, 0x20, 0x20, 0x2C, 0x10,
457                    ]],
458                },
459                HvcCArray {
460                    completeness: true,
461                    nal_unit_type: 33,
462                    nalus: vec![vec![
463                        0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
464                        0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x7B, 0xA0, 0x07, 0x82, 0x00, 0x88,
465                        0x7D, 0xE5, 0x94, 0x99, 0x24, 0x6D, 0x86, 0x96, 0x22, 0xAA, 0x4C, 0x4C,
466                        0x32, 0xFB, 0x3E, 0xBC, 0xDF, 0x96, 0x7D, 0x78, 0x51, 0x18, 0x9C, 0xBB,
467                        0x64,
468                    ]],
469                },
470                HvcCArray {
471                    completeness: true,
472                    nal_unit_type: 34,
473                    nalus: vec![vec![0x44, 0x01, 0xC1, 0xA5, 0x58, 0x11, 0xD0, 0x2A]],
474                },
475            ],
476        };
477        let mut buf = Vec::new();
478        hvcc.encode(&mut buf).unwrap();
479
480        assert_eq!(buf.as_slice(), ENCODED_HVCC_MPEG);
481    }
482}