isobmff/boxes/
video_media.rs

1//! Video media specific boxes defined in ISO/IEC 14496-12 - 12.1
2
3use fixed::FixedU32;
4use fixed::types::extra::U16;
5use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize};
6use scuffle_bytes_util::{BitWriter, BytesCow};
7
8use super::SampleEntry;
9use crate::{BoxHeader, FullBoxHeader, IsoBox, IsoSized};
10
11/// Video media header
12///
13/// ISO/IEC 14496-12 - 12.1.2
14#[derive(IsoBox, Debug, PartialEq, Eq, Default)]
15#[iso_box(box_type = b"vmhd", crate_path = crate)]
16pub struct VideoMediaHeaderBox {
17    /// The full box header.
18    pub full_header: FullBoxHeader,
19    /// A composition mode for this video track, from the following enumerated set,
20    /// which may be extended by derived specifications:
21    ///
22    /// - copy = 0 copy over the existing image
23    pub graphicsmode: u16,
24    /// A set of 3 colour values (red, green, blue) available for use by graphics modes.
25    pub opcolor: [u16; 3],
26}
27
28/// Visual sample entry
29///
30/// ISO/IEC 14496-12 - 12.1.3
31///
32/// Sub boxes:
33/// - [`btrt`](super::BitRateBox)
34/// - [`clap`](CleanApertureBox)
35/// - [`pasp`](PixelAspectRatioBox)
36/// - [`colr`](ColourInformationBox)
37/// - [`clli`](ContentLightLevelBox)
38/// - [`mdcv`](MasteringDisplayColourVolumeBox)
39/// - [`cclv`](ContentColourVolumeBox)
40/// - [`amve`](AmbientViewingEnvironmentBox)
41/// - Any other boxes
42#[derive(Debug, PartialEq, Eq)]
43pub struct VisualSampleEntry {
44    /// The sample entry that this box inherits from.
45    pub sample_entry: SampleEntry,
46    /// Pre-defined 16 bits, must be 0.
47    pub pre_defined: u16,
48    /// Reserved 16 bits, must be 0.
49    pub reserved1: u16,
50    /// Pre-defined 3 * 32 bits, must be 0.
51    pub pre_defined2: [u32; 3],
52    /// The maximum visual `width` and `height` of the stream described by this sample description, in pixels.
53    pub width: u16,
54    /// See [`width`](Self::width).
55    pub height: u16,
56    /// Must be set to `0x00480000` (72 dpi).
57    pub horiz_resolution: FixedU32<U16>,
58    /// Must be set to `0x00480000` (72 dpi).
59    pub vert_resolution: FixedU32<U16>,
60    /// Reserved 32 bits, must be 0.
61    pub reserved2: u32,
62    /// How many frames of compressed video are stored in each sample. The default is
63    /// 1, for one frame per sample; it may be more than 1 for multiple frames per sample.
64    pub frame_count: u16,
65    /// A name, for informative purposes. It is formatted in a fixed 32-byte field, with the
66    /// first byte set to the number of bytes to be displayed, followed by that number of bytes of displayable
67    /// data encoded using UTF-8, and then padding to complete 32 bytes total (including the size byte).
68    /// The field may be set to 0.
69    pub compressor_name: [u8; 32],
70    /// One of the following values:
71    ///
72    /// - `0x0018`: images are in colour with no alpha.
73    pub depth: u16,
74    /// Pre-defined 16 bits, must be -1.
75    pub pre_defined4: i16,
76}
77
78impl VisualSampleEntry {
79    /// Creates a new [`VisualSampleEntry`] with the given parameters.
80    pub fn new(sample_entry: SampleEntry, width: u16, height: u16, compressor_name: [u8; 32]) -> Self {
81        Self {
82            sample_entry,
83            pre_defined: 0,
84            reserved1: 0,
85            pre_defined2: [0; 3],
86            width,
87            height,
88            horiz_resolution: FixedU32::from_bits(0x00480000),
89            vert_resolution: FixedU32::from_bits(0x00480000),
90            reserved2: 0,
91            frame_count: 1,
92            compressor_name,
93            depth: 0x0018,
94            pre_defined4: -1,
95        }
96    }
97}
98
99impl<'a> Deserialize<'a> for VisualSampleEntry {
100    fn deserialize<R>(mut reader: R) -> std::io::Result<Self>
101    where
102        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
103    {
104        let sample_entry = SampleEntry::deserialize(&mut reader)?;
105        let pre_defined = u16::deserialize(&mut reader)?;
106        let reserved1 = u16::deserialize(&mut reader)?;
107        let pre_defined2 = <[u32; 3]>::deserialize(&mut reader)?;
108        let width = u16::deserialize(&mut reader)?;
109        let height = u16::deserialize(&mut reader)?;
110        let horiz_resolution = FixedU32::from_bits(u32::deserialize(&mut reader)?);
111        let vert_resolution = FixedU32::from_bits(u32::deserialize(&mut reader)?);
112        let reserved2 = u32::deserialize(&mut reader)?;
113        let frame_count = u16::deserialize(&mut reader)?;
114        let compressor_name = <[u8; 32]>::deserialize(&mut reader)?;
115        let depth = u16::deserialize(&mut reader)?;
116        let pre_defined4 = i16::deserialize(&mut reader)?;
117
118        Ok(Self {
119            sample_entry,
120            pre_defined,
121            reserved1,
122            pre_defined2,
123            width,
124            height,
125            horiz_resolution,
126            vert_resolution,
127            reserved2,
128            frame_count,
129            compressor_name,
130            depth,
131            pre_defined4,
132        })
133    }
134}
135
136impl Serialize for VisualSampleEntry {
137    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
138    where
139        W: std::io::Write,
140    {
141        self.sample_entry.serialize(&mut writer)?;
142        self.pre_defined.serialize(&mut writer)?;
143        self.reserved1.serialize(&mut writer)?;
144        self.pre_defined2.serialize(&mut writer)?;
145        self.width.serialize(&mut writer)?;
146        self.height.serialize(&mut writer)?;
147        self.horiz_resolution.to_bits().serialize(&mut writer)?;
148        self.vert_resolution.to_bits().serialize(&mut writer)?;
149        self.reserved2.serialize(&mut writer)?;
150        self.frame_count.serialize(&mut writer)?;
151        self.compressor_name.serialize(&mut writer)?;
152        self.depth.serialize(&mut writer)?;
153        self.pre_defined4.serialize(&mut writer)?;
154        Ok(())
155    }
156}
157
158impl IsoSized for VisualSampleEntry {
159    fn size(&self) -> usize {
160        self.sample_entry.size()
161            + 2 // pre_defined
162            + 2 // reserved1
163            + self.pre_defined2.size() // pre_defined2
164            + 2 // width
165            + 2 // height
166            + 4 // horiz_resolution
167            + 4 // vert_resolution
168            + 4 // reserved2
169            + 2 // frame_count
170            + self.compressor_name.size() // compressor_name
171            + 2 // depth
172            + 2 // pre_defined4
173    }
174}
175
176/// Clean aperture box
177///
178/// ISO/IEC 14496-12 - 12.1.4
179#[derive(IsoBox, Debug, PartialEq, Eq)]
180#[iso_box(box_type = b"clap", crate_path = crate)]
181pub struct CleanApertureBox {
182    /// A fractional number which defines the width of the clean aperture image.
183    pub clean_aperture_width_n: u32,
184    /// A fractional number which defines the width of the clean aperture image.
185    pub clean_aperture_width_d: u32,
186    /// A fractional number which defines the height of the clean aperture image.
187    pub clean_aperture_height_n: u32,
188    /// A fractional number which defines the height of the clean aperture image.
189    pub clean_aperture_height_d: u32,
190    /// A fractional number which defines the horizontal offset between the clean
191    /// aperture image centre and the full aperture image centre. Typically 0.
192    pub horiz_off_n: u32,
193    /// A fractional number which defines the horizontal offset between the clean
194    /// aperture image centre and the full aperture image centre. Typically 0.
195    pub horiz_off_d: u32,
196    /// A fractional number which defines the vertical offset between clean aperture image
197    /// centre and the full aperture image centre. Typically 0.
198    pub vert_off_n: u32,
199    /// A fractional number which defines the vertical offset between clean aperture image
200    /// centre and the full aperture image centre. Typically 0.
201    pub vert_off_d: u32,
202}
203
204/// Pixel aspect ratio box
205///
206/// ISO/IEC 14496-12 - 12.1.4
207#[derive(IsoBox, Debug, PartialEq, Eq)]
208#[iso_box(box_type = b"pasp", crate_path = crate)]
209pub struct PixelAspectRatioBox {
210    /// Define the relative width and height of a pixel.
211    pub h_spacing: u32,
212    /// Define the relative width and height of a pixel.
213    pub v_spacing: u32,
214}
215
216impl Default for PixelAspectRatioBox {
217    fn default() -> Self {
218        Self {
219            h_spacing: 1,
220            v_spacing: 1,
221        }
222    }
223}
224
225/// Colour information
226///
227/// ISO/IEC 14496-12 - 12.1.5
228#[derive(IsoBox, Debug, PartialEq, Eq)]
229#[iso_box(box_type = b"colr", crate_path = crate)]
230pub struct ColourInformationBox<'a> {
231    /// The colour information.
232    pub colour_info: ColourInformation<'a>,
233}
234
235/// Colour information in the [`ColourInformationBox`] box.
236#[derive(Debug, PartialEq, Eq)]
237pub enum ColourInformation<'a> {
238    /// On-screen colour.
239    Nclx(NclxColourInformation),
240    /// Restricted ICC profile.
241    RIcc {
242        /// An ICC profile as defined in ISO 15076-1 or ICC.1 is supplied.
243        icc_profile: BytesCow<'a>,
244    },
245    /// Restricted ICC profile.
246    Prof {
247        /// An ICC profile as defined in ISO 15076-1 or ICC.1 is supplied.
248        icc_profile: BytesCow<'a>,
249    },
250    /// Other colour information.
251    Other {
252        /// The colour type.
253        colour_type: [u8; 4],
254        /// The colour info data.
255        data: BytesCow<'a>,
256    },
257}
258
259impl<'a> Deserialize<'a> for ColourInformation<'a> {
260    fn deserialize<R>(mut reader: R) -> std::io::Result<Self>
261    where
262        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
263    {
264        let colour_type = <[u8; 4]>::deserialize(&mut reader)?;
265
266        match &colour_type {
267            b"nclx" => {
268                let colour_info = NclxColourInformation::deserialize(&mut reader)?;
269                Ok(ColourInformation::Nclx(colour_info))
270            }
271            b"rICC" => {
272                let icc_profile = reader.try_read_to_end()?;
273                Ok(ColourInformation::RIcc { icc_profile })
274            }
275            b"prof" => {
276                let icc_profile = reader.try_read_to_end()?;
277                Ok(ColourInformation::Prof { icc_profile })
278            }
279            _ => Ok(Self::Other {
280                colour_type,
281                data: reader.try_read_to_end()?,
282            }),
283        }
284    }
285}
286
287impl Serialize for ColourInformation<'_> {
288    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
289    where
290        W: std::io::Write,
291    {
292        match self {
293            ColourInformation::Nclx(info) => {
294                b"nclx".serialize(&mut writer)?;
295                info.serialize(&mut writer)?;
296            }
297            ColourInformation::RIcc { icc_profile } => {
298                b"rICC".serialize(&mut writer)?;
299                icc_profile.serialize(&mut writer)?;
300            }
301            ColourInformation::Prof { icc_profile } => {
302                b"prof".serialize(&mut writer)?;
303                icc_profile.serialize(&mut writer)?;
304            }
305            ColourInformation::Other { colour_type, data } => {
306                colour_type.serialize(&mut writer)?;
307                data.serialize(&mut writer)?;
308            }
309        }
310        Ok(())
311    }
312}
313
314impl IsoSized for ColourInformation<'_> {
315    fn size(&self) -> usize {
316        match self {
317            ColourInformation::Nclx(info) => 4 + info.size(),
318            ColourInformation::RIcc { icc_profile } => 4 + icc_profile.size(),
319            ColourInformation::Prof { icc_profile } => 4 + icc_profile.size(),
320            ColourInformation::Other { data, .. } => 4 + data.size(),
321        }
322    }
323}
324
325/// NCLX colour information in the [`ColourInformationBox`].
326#[derive(Debug, PartialEq, Eq)]
327pub struct NclxColourInformation {
328    /// Carries a `ColourPrimaries` value as defined in ISO/IEC 23091-2.
329    pub colour_primaries: u16,
330    /// Carries a `TransferCharacteristics` value as defined in ISO/IEC 23091-2.
331    pub transfer_characteristics: u16,
332    /// Carries a `MatrixCoefficients` value as defined in ISO/IEC 23091-2.
333    pub matrix_coefficients: u16,
334    /// Carries a `VideoFullRangeFlag` as defined in ISO/IEC 23091-2.
335    pub full_range_flag: bool,
336}
337
338impl<'a> Deserialize<'a> for NclxColourInformation {
339    fn deserialize<R>(mut reader: R) -> std::io::Result<Self>
340    where
341        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
342    {
343        Ok(Self {
344            colour_primaries: u16::deserialize(&mut reader)?,
345            transfer_characteristics: u16::deserialize(&mut reader)?,
346            matrix_coefficients: u16::deserialize(&mut reader)?,
347            full_range_flag: u8::deserialize(&mut reader)? >> 7 != 0,
348        })
349    }
350}
351
352impl Serialize for NclxColourInformation {
353    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
354    where
355        W: std::io::Write,
356    {
357        self.colour_primaries.serialize(&mut writer)?;
358        self.transfer_characteristics.serialize(&mut writer)?;
359        self.matrix_coefficients.serialize(&mut writer)?;
360        ((self.full_range_flag as u8) << 7).serialize(&mut writer)?;
361        Ok(())
362    }
363}
364
365impl IsoSized for NclxColourInformation {
366    fn size(&self) -> usize {
367        2 + 2 + 2 + 1
368    }
369}
370
371/// Content light level
372///
373/// ISO/IEC 144496-12 - 12.1.6
374///
375/// It is functionally equivalent to, and shall be as described in, the Content light level
376/// information SEI message in ITU-T H.265 | ISO/IEC 23008-2, with the addition that the provisions of
377/// CTA-861-G, in which zero in some cases codes an unknown value, may be used.
378#[derive(IsoBox, Debug, PartialEq, Eq)]
379#[iso_box(box_type = b"clli", crate_path = crate)]
380pub struct ContentLightLevelBox {
381    /// See [`ContentLightLevelBox`].
382    pub max_content_light_level: u16,
383    /// See [`ContentLightLevelBox`].
384    pub max_pic_average_light_level: u16,
385}
386
387/// Mastering display colour volume
388///
389/// ISO/IEC 144496-12 - 12.1.7
390///
391/// It is functionally equivalent to, and shall be as described in, the mastering display colour volume SEI message in
392/// ITU-T H.265 | ISO/IEC 23008-2, with the addition that the provisions of CTA-861-G in which zero in
393/// some cases codes an unknown value may be used.
394#[derive(IsoBox, Debug, PartialEq, Eq)]
395#[iso_box(box_type = b"mdcv", crate_path = crate)]
396pub struct MasteringDisplayColourVolumeBox {
397    /// See [`MasteringDisplayColourVolumeBox`].
398    pub display_primaries: [[u16; 2]; 6],
399    /// See [`MasteringDisplayColourVolumeBox`].
400    pub white_point_x: u16,
401    /// See [`MasteringDisplayColourVolumeBox`].
402    pub white_point_y: u16,
403    /// See [`MasteringDisplayColourVolumeBox`].
404    pub max_display_mastering_luminance: u32,
405    /// See [`MasteringDisplayColourVolumeBox`].
406    pub min_display_mastering_luminance: u32,
407}
408
409/// Content colour volume
410///
411/// ISO/IEC 144496-12 - 12.1.8
412///
413/// It is functionally equivalent to, and shall be as described in, the content colour volume SEI message
414/// in Rec. ITU-T H.265 | ISO/IEC 23008-2 except that the box, in a sample entry, applies to the associated
415/// content and hence the initial two bits (corresponding to the `ccv_cancel_flag` and `ccv_persistence_flag`)
416/// take the value 0.
417#[derive(IsoBox, Debug, PartialEq, Eq)]
418#[iso_box(box_type = b"cclv", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
419pub struct ContentColourVolumeBox {
420    /// Reserved 1 bit, must be 0.
421    pub reserved1: bool,
422    /// Reserved 1 bit, must be 0.
423    pub reserved2: bool,
424    /// See [`ContentColourVolumeBox`].
425    pub ccv_reserved_zero_2bits: u8,
426    /// See [`ContentColourVolumeBox`].
427    pub ccv_primaries: Option<[[i32; 2]; 3]>,
428    /// See [`ContentColourVolumeBox`].
429    pub ccv_min_luminance_value: Option<u32>,
430    /// See [`ContentColourVolumeBox`].
431    pub ccv_max_luminance_value: Option<u32>,
432    /// See [`ContentColourVolumeBox`].
433    pub ccv_avg_luminance_value: Option<u32>,
434}
435
436impl<'a> DeserializeSeed<'a, BoxHeader> for ContentColourVolumeBox {
437    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
438    where
439        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
440    {
441        let byte = u8::deserialize(&mut reader)?;
442        let reserved1 = ((byte >> 7) & 0b1) != 0;
443        let reserved2 = ((byte >> 6) & 0b1) != 0;
444        let ccv_primaries_present_flag = ((byte >> 5) & 0b1) != 0;
445        let ccv_min_luminance_value_present_flag = ((byte >> 4) & 0b1) != 0;
446        let ccv_max_luminance_value_present_flag = ((byte >> 3) & 0b1) != 0;
447        let ccv_avg_luminance_value_present_flag = ((byte >> 2) & 0b1) != 0;
448        let ccv_reserved_zero_2bits = byte & 0b11;
449
450        let ccv_primaries = if ccv_primaries_present_flag {
451            Some(<[[i32; 2]; 3]>::deserialize(&mut reader)?)
452        } else {
453            None
454        };
455
456        let ccv_min_luminance_value = if ccv_min_luminance_value_present_flag {
457            Some(u32::deserialize(&mut reader)?)
458        } else {
459            None
460        };
461
462        let ccv_max_luminance_value = if ccv_max_luminance_value_present_flag {
463            Some(u32::deserialize(&mut reader)?)
464        } else {
465            None
466        };
467
468        let ccv_avg_luminance_value = if ccv_avg_luminance_value_present_flag {
469            Some(u32::deserialize(&mut reader)?)
470        } else {
471            None
472        };
473
474        Ok(Self {
475            reserved1,
476            reserved2,
477            ccv_reserved_zero_2bits,
478            ccv_primaries,
479            ccv_min_luminance_value,
480            ccv_max_luminance_value,
481            ccv_avg_luminance_value,
482        })
483    }
484}
485
486impl Serialize for ContentColourVolumeBox {
487    fn serialize<W>(&self, writer: W) -> std::io::Result<()>
488    where
489        W: std::io::Write,
490    {
491        let mut bit_writer = BitWriter::new(writer);
492
493        bit_writer.write_bit(self.reserved1)?;
494        bit_writer.write_bit(self.reserved2)?;
495        bit_writer.write_bit(self.ccv_primaries.is_some())?;
496        bit_writer.write_bit(self.ccv_min_luminance_value.is_some())?;
497        bit_writer.write_bit(self.ccv_max_luminance_value.is_some())?;
498        bit_writer.write_bit(self.ccv_avg_luminance_value.is_some())?;
499        bit_writer.write_bits(self.ccv_reserved_zero_2bits as u64, 2)?;
500
501        if let Some(ccv_primaries) = &self.ccv_primaries {
502            ccv_primaries.serialize(&mut bit_writer)?;
503        }
504        if let Some(ccv_min_luminance_value) = &self.ccv_min_luminance_value {
505            ccv_min_luminance_value.serialize(&mut bit_writer)?;
506        }
507        if let Some(ccv_max_luminance_value) = &self.ccv_max_luminance_value {
508            ccv_max_luminance_value.serialize(&mut bit_writer)?;
509        }
510        if let Some(ccv_avg_luminance_value) = &self.ccv_avg_luminance_value {
511            ccv_avg_luminance_value.serialize(&mut bit_writer)?;
512        }
513
514        Ok(())
515    }
516}
517
518impl IsoSized for ContentColourVolumeBox {
519    fn size(&self) -> usize {
520        let mut size = 0;
521        size += 1; // flags
522        if let Some(ccv_primaries) = self.ccv_primaries {
523            size += ccv_primaries.size();
524        }
525        if let Some(ccv_min_luminance_value) = self.ccv_min_luminance_value {
526            size += ccv_min_luminance_value.size();
527        }
528        if let Some(ccv_max_luminance_value) = self.ccv_max_luminance_value {
529            size += ccv_max_luminance_value.size();
530        }
531        if let Some(ccv_avg_luminance_value) = self.ccv_avg_luminance_value {
532            size += ccv_avg_luminance_value.size();
533        }
534
535        Self::add_header_size(size)
536    }
537}
538
539/// Ambient viewing environment
540///
541/// ISO/IEC 144496-12 - 12.1.9
542///
543/// It is functionally equivalent to, and shall be as described in, the ambient viewing environment SEI message
544/// in ITU-T H.265 |I ISO/IEC 23008-2.
545#[derive(IsoBox, Debug, PartialEq, Eq)]
546#[iso_box(box_type = b"amve", crate_path = crate)]
547pub struct AmbientViewingEnvironmentBox {
548    /// See [`AmbientViewingEnvironmentBox`].
549    pub ambient_illuminance: u32,
550    /// See [`AmbientViewingEnvironmentBox`].
551    pub ambient_light_x: u16,
552    /// See [`AmbientViewingEnvironmentBox`].
553    pub ambient_light_y: u16,
554}