flowly_mp4/mp4box/
hev1.rs

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