Skip to main content

mp4/mp4box/
hev1.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, Write};
4
5use crate::mp4box::*;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
8pub struct Hev1Box {
9    pub data_reference_index: u16,
10    pub width: u16,
11    pub height: u16,
12
13    #[serde(with = "value_u32")]
14    pub horizresolution: FixedPointU16,
15
16    #[serde(with = "value_u32")]
17    pub vertresolution: FixedPointU16,
18    pub frame_count: u16,
19    pub depth: u16,
20    pub hvcc: HvcCBox,
21}
22
23impl Default for Hev1Box {
24    fn default() -> Self {
25        Hev1Box {
26            data_reference_index: 0,
27            width: 0,
28            height: 0,
29            horizresolution: FixedPointU16::new(0x48),
30            vertresolution: FixedPointU16::new(0x48),
31            frame_count: 1,
32            depth: 0x0018,
33            hvcc: HvcCBox::default(),
34        }
35    }
36}
37
38impl Hev1Box {
39    pub fn new(config: &HevcConfig) -> Self {
40        Hev1Box {
41            data_reference_index: 1,
42            width: config.width,
43            height: config.height,
44            horizresolution: FixedPointU16::new(0x48),
45            vertresolution: FixedPointU16::new(0x48),
46            frame_count: 1,
47            depth: 0x0018,
48            hvcc: HvcCBox::new(),
49        }
50    }
51
52    pub fn get_type(&self) -> BoxType {
53        BoxType::Hev1Box
54    }
55
56    pub fn get_size(&self) -> u64 {
57        HEADER_SIZE + 8 + 70 + self.hvcc.box_size()
58    }
59}
60
61impl Mp4Box for Hev1Box {
62    fn box_type(&self) -> BoxType {
63        self.get_type()
64    }
65
66    fn box_size(&self) -> u64 {
67        self.get_size()
68    }
69
70    fn to_json(&self) -> Result<String> {
71        Ok(serde_json::to_string(&self).unwrap())
72    }
73
74    fn summary(&self) -> Result<String> {
75        let s = format!(
76            "data_reference_index={} width={} height={} frame_count={}",
77            self.data_reference_index, self.width, self.height, self.frame_count
78        );
79        Ok(s)
80    }
81}
82
83impl<R: Read + Seek> ReadBox<&mut R> for Hev1Box {
84    fn read_box(reader: &mut R, size: u64) -> Result<Self> {
85        let start = box_start(reader)?;
86
87        reader.read_u32::<BigEndian>()?; // reserved
88        reader.read_u16::<BigEndian>()?; // reserved
89        let data_reference_index = reader.read_u16::<BigEndian>()?;
90
91        reader.read_u32::<BigEndian>()?; // pre-defined, reserved
92        reader.read_u64::<BigEndian>()?; // pre-defined
93        reader.read_u32::<BigEndian>()?; // pre-defined
94        let width = reader.read_u16::<BigEndian>()?;
95        let height = reader.read_u16::<BigEndian>()?;
96        let horizresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
97        let vertresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
98        reader.read_u32::<BigEndian>()?; // reserved
99        let frame_count = reader.read_u16::<BigEndian>()?;
100        skip_bytes(reader, 32)?; // compressorname
101        let depth = reader.read_u16::<BigEndian>()?;
102        reader.read_i16::<BigEndian>()?; // pre-defined
103
104        let header = BoxHeader::read(reader)?;
105        let BoxHeader { name, size: s } = header;
106        if s > size {
107            return Err(Error::InvalidData(
108                "hev1 box contains a box with a larger size than it",
109            ));
110        }
111        if name == BoxType::HvcCBox {
112            let hvcc = HvcCBox::read_box(reader, s)?;
113
114            skip_bytes_to(reader, start + size)?;
115
116            Ok(Hev1Box {
117                data_reference_index,
118                width,
119                height,
120                horizresolution,
121                vertresolution,
122                frame_count,
123                depth,
124                hvcc,
125            })
126        } else {
127            Err(Error::InvalidData("hvcc not found"))
128        }
129    }
130}
131
132impl<W: Write> WriteBox<&mut W> for Hev1Box {
133    fn write_box(&self, writer: &mut W) -> Result<u64> {
134        let size = self.box_size();
135        BoxHeader::new(self.box_type(), size).write(writer)?;
136
137        writer.write_u32::<BigEndian>(0)?; // reserved
138        writer.write_u16::<BigEndian>(0)?; // reserved
139        writer.write_u16::<BigEndian>(self.data_reference_index)?;
140
141        writer.write_u32::<BigEndian>(0)?; // pre-defined, reserved
142        writer.write_u64::<BigEndian>(0)?; // pre-defined
143        writer.write_u32::<BigEndian>(0)?; // pre-defined
144        writer.write_u16::<BigEndian>(self.width)?;
145        writer.write_u16::<BigEndian>(self.height)?;
146        writer.write_u32::<BigEndian>(self.horizresolution.raw_value())?;
147        writer.write_u32::<BigEndian>(self.vertresolution.raw_value())?;
148        writer.write_u32::<BigEndian>(0)?; // reserved
149        writer.write_u16::<BigEndian>(self.frame_count)?;
150        // skip compressorname
151        write_zeros(writer, 32)?;
152        writer.write_u16::<BigEndian>(self.depth)?;
153        writer.write_i16::<BigEndian>(-1)?; // pre-defined
154
155        self.hvcc.write_box(writer)?;
156
157        Ok(size)
158    }
159}
160
161#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize)]
162pub struct HvcCBox {
163    pub configuration_version: u8,
164    pub general_profile_space: u8,
165    pub general_tier_flag: bool,
166    pub general_profile_idc: u8,
167    pub general_profile_compatibility_flags: u32,
168    pub general_constraint_indicator_flag: u64,
169    pub general_level_idc: u8,
170    pub min_spatial_segmentation_idc: u16,
171    pub parallelism_type: u8,
172    pub chroma_format_idc: u8,
173    pub bit_depth_luma_minus8: u8,
174    pub bit_depth_chroma_minus8: u8,
175    pub avg_frame_rate: u16,
176    pub constant_frame_rate: u8,
177    pub num_temporal_layers: u8,
178    pub temporal_id_nested: bool,
179    pub length_size_minus_one: u8,
180    pub arrays: Vec<HvcCArray>,
181}
182
183impl HvcCBox {
184    pub fn new() -> Self {
185        Self {
186            configuration_version: 1,
187            ..Default::default()
188        }
189    }
190}
191
192impl Mp4Box for HvcCBox {
193    fn box_type(&self) -> BoxType {
194        BoxType::HvcCBox
195    }
196
197    fn box_size(&self) -> u64 {
198        HEADER_SIZE
199            + 23
200            + self
201                .arrays
202                .iter()
203                .map(|a| 3 + a.nalus.iter().map(|x| 2 + x.data.len() as u64).sum::<u64>())
204                .sum::<u64>()
205    }
206
207    fn to_json(&self) -> Result<String> {
208        Ok(serde_json::to_string(&self).unwrap())
209    }
210
211    fn summary(&self) -> Result<String> {
212        Ok(format!("configuration_version={} general_profile_space={} general_tier_flag={} general_profile_idc={} general_profile_compatibility_flags={} general_constraint_indicator_flag={} general_level_idc={} min_spatial_segmentation_idc={} parallelism_type={} chroma_format_idc={} bit_depth_luma_minus8={} bit_depth_chroma_minus8={} avg_frame_rate={} constant_frame_rate={} num_temporal_layers={} temporal_id_nested={} length_size_minus_one={}", 
213            self.configuration_version,
214            self.general_profile_space,
215            self.general_tier_flag,
216            self.general_profile_idc,
217            self.general_profile_compatibility_flags,
218            self.general_constraint_indicator_flag,
219            self.general_level_idc,
220            self.min_spatial_segmentation_idc,
221            self.parallelism_type,
222            self.chroma_format_idc,
223            self.bit_depth_luma_minus8,
224            self.bit_depth_chroma_minus8,
225            self.avg_frame_rate,
226            self.constant_frame_rate,
227            self.num_temporal_layers,
228            self.temporal_id_nested,
229            self.length_size_minus_one
230        ))
231    }
232}
233
234#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
235pub struct HvcCArrayNalu {
236    pub size: u16,
237    pub data: Vec<u8>,
238}
239
240#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
241pub struct HvcCArray {
242    pub completeness: bool,
243    pub nal_unit_type: u8,
244    pub nalus: Vec<HvcCArrayNalu>,
245}
246
247impl<R: Read + Seek> ReadBox<&mut R> for HvcCBox {
248    fn read_box(reader: &mut R, _size: u64) -> Result<Self> {
249        let configuration_version = reader.read_u8()?;
250        let params = reader.read_u8()?;
251        let general_profile_space = params & 0b11000000 >> 6;
252        let general_tier_flag = (params & 0b00100000 >> 5) > 0;
253        let general_profile_idc = params & 0b00011111;
254
255        let general_profile_compatibility_flags = reader.read_u32::<BigEndian>()?;
256        let general_constraint_indicator_flag = reader.read_u48::<BigEndian>()?;
257        let general_level_idc = reader.read_u8()?;
258        let min_spatial_segmentation_idc = reader.read_u16::<BigEndian>()? & 0x0FFF;
259        let parallelism_type = reader.read_u8()? & 0b11;
260        let chroma_format_idc = reader.read_u8()? & 0b11;
261        let bit_depth_luma_minus8 = reader.read_u8()? & 0b111;
262        let bit_depth_chroma_minus8 = reader.read_u8()? & 0b111;
263        let avg_frame_rate = reader.read_u16::<BigEndian>()?;
264
265        let params = reader.read_u8()?;
266        let constant_frame_rate = params & 0b11000000 >> 6;
267        let num_temporal_layers = params & 0b00111000 >> 3;
268        let temporal_id_nested = (params & 0b00000100 >> 2) > 0;
269        let length_size_minus_one = params & 0b000011;
270
271        let num_of_arrays = reader.read_u8()?;
272
273        let mut arrays = Vec::with_capacity(num_of_arrays as _);
274        for _ in 0..num_of_arrays {
275            let params = reader.read_u8()?;
276            let num_nalus = reader.read_u16::<BigEndian>()?;
277            let mut nalus = Vec::with_capacity(num_nalus as usize);
278
279            for _ in 0..num_nalus {
280                let size = reader.read_u16::<BigEndian>()?;
281                let mut data = vec![0; size as usize];
282
283                reader.read_exact(&mut data)?;
284
285                nalus.push(HvcCArrayNalu { size, data })
286            }
287
288            arrays.push(HvcCArray {
289                completeness: (params & 0b10000000) > 0,
290                nal_unit_type: params & 0b111111,
291                nalus,
292            });
293        }
294
295        Ok(HvcCBox {
296            configuration_version,
297            general_profile_space,
298            general_tier_flag,
299            general_profile_idc,
300            general_profile_compatibility_flags,
301            general_constraint_indicator_flag,
302            general_level_idc,
303            min_spatial_segmentation_idc,
304            parallelism_type,
305            chroma_format_idc,
306            bit_depth_luma_minus8,
307            bit_depth_chroma_minus8,
308            avg_frame_rate,
309            constant_frame_rate,
310            num_temporal_layers,
311            temporal_id_nested,
312            length_size_minus_one,
313            arrays,
314        })
315    }
316}
317
318impl<W: Write> WriteBox<&mut W> for HvcCBox {
319    fn write_box(&self, writer: &mut W) -> Result<u64> {
320        let size = self.box_size();
321        BoxHeader::new(self.box_type(), size).write(writer)?;
322
323        writer.write_u8(self.configuration_version)?;
324        let general_profile_space = (self.general_profile_space & 0b11) << 6;
325        let general_tier_flag = u8::from(self.general_tier_flag) << 5;
326        let general_profile_idc = self.general_profile_idc & 0b11111;
327
328        writer.write_u8(general_profile_space | general_tier_flag | general_profile_idc)?;
329        writer.write_u32::<BigEndian>(self.general_profile_compatibility_flags)?;
330        writer.write_u48::<BigEndian>(self.general_constraint_indicator_flag)?;
331        writer.write_u8(self.general_level_idc)?;
332
333        writer.write_u16::<BigEndian>(self.min_spatial_segmentation_idc & 0x0FFF)?;
334        writer.write_u8(self.parallelism_type & 0b11)?;
335        writer.write_u8(self.chroma_format_idc & 0b11)?;
336        writer.write_u8(self.bit_depth_luma_minus8 & 0b111)?;
337        writer.write_u8(self.bit_depth_chroma_minus8 & 0b111)?;
338        writer.write_u16::<BigEndian>(self.avg_frame_rate)?;
339
340        let constant_frame_rate = (self.constant_frame_rate & 0b11) << 6;
341        let num_temporal_layers = (self.num_temporal_layers & 0b111) << 3;
342        let temporal_id_nested = u8::from(self.temporal_id_nested) << 2;
343        let length_size_minus_one = self.length_size_minus_one & 0b11;
344        writer.write_u8(
345            constant_frame_rate | num_temporal_layers | temporal_id_nested | length_size_minus_one,
346        )?;
347        writer.write_u8(self.arrays.len() as u8)?;
348        for arr in &self.arrays {
349            writer.write_u8((arr.nal_unit_type & 0b111111) | u8::from(arr.completeness) << 7)?;
350            writer.write_u16::<BigEndian>(arr.nalus.len() as _)?;
351
352            for nalu in &arr.nalus {
353                writer.write_u16::<BigEndian>(nalu.size)?;
354                writer.write_all(&nalu.data)?;
355            }
356        }
357
358        Ok(size)
359    }
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365    use crate::mp4box::BoxHeader;
366    use std::io::Cursor;
367
368    #[test]
369    fn test_hev1() {
370        let src_box = Hev1Box {
371            data_reference_index: 1,
372            width: 320,
373            height: 240,
374            horizresolution: FixedPointU16::new(0x48),
375            vertresolution: FixedPointU16::new(0x48),
376            frame_count: 1,
377            depth: 24,
378            hvcc: HvcCBox {
379                configuration_version: 1,
380                ..Default::default()
381            },
382        };
383        let mut buf = Vec::new();
384        src_box.write_box(&mut buf).unwrap();
385        assert_eq!(buf.len(), src_box.box_size() as usize);
386
387        let mut reader = Cursor::new(&buf);
388        let header = BoxHeader::read(&mut reader).unwrap();
389        assert_eq!(header.name, BoxType::Hev1Box);
390        assert_eq!(src_box.box_size(), header.size);
391
392        let dst_box = Hev1Box::read_box(&mut reader, header.size).unwrap();
393        assert_eq!(src_box, dst_box);
394    }
395}