scuffle_mp4/boxes/types/
colr.rs

1use std::io::{
2    Read, {self},
3};
4
5use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
6use bytes::Bytes;
7
8use crate::boxes::header::BoxHeader;
9use crate::boxes::traits::BoxType;
10
11#[derive(Debug, Clone, PartialEq)]
12/// Color Box
13/// ISO/IEC 14496-12:2022(E) - 12.1.5
14pub struct Colr {
15    pub header: BoxHeader,
16    pub color_type: ColorType,
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum ColorType {
21    Nclx {
22        color_primaries: u16,
23        transfer_characteristics: u16,
24        matrix_coefficients: u16,
25        full_range_flag: bool,
26    },
27    Unknown(([u8; 4], Bytes)),
28}
29
30impl Colr {
31    pub fn new(color_type: ColorType) -> Self {
32        Self {
33            header: BoxHeader::new(Self::NAME),
34            color_type,
35        }
36    }
37}
38
39impl BoxType for Colr {
40    const NAME: [u8; 4] = *b"colr";
41
42    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
43        let mut reader = io::Cursor::new(data);
44
45        let mut color_type = [0; 4];
46        reader.read_exact(&mut color_type)?;
47
48        let color_type = match &color_type {
49            b"nclx" => {
50                let color_primaries = reader.read_u16::<BigEndian>()?;
51                let transfer_characteristics = reader.read_u16::<BigEndian>()?;
52                let matrix_coefficients = reader.read_u16::<BigEndian>()?;
53                let full_range_flag = (reader.read_u8()? >> 7) == 1;
54
55                ColorType::Nclx {
56                    color_primaries,
57                    transfer_characteristics,
58                    matrix_coefficients,
59                    full_range_flag,
60                }
61            }
62            _ => {
63                let pos = reader.position() as usize;
64                let data = reader.into_inner().slice(pos..);
65                ColorType::Unknown((color_type, data))
66            }
67        };
68
69        Ok(Self { header, color_type })
70    }
71
72    fn primitive_size(&self) -> u64 {
73        let size = match &self.color_type {
74            ColorType::Nclx { .. } => 2 + 2 + 2 + 1,            // 7 bytes
75            ColorType::Unknown((_, data)) => data.len() as u64, // unknown size
76        };
77
78        size + 4 // color type
79    }
80
81    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
82        match &self.color_type {
83            ColorType::Nclx {
84                color_primaries,
85                transfer_characteristics,
86                matrix_coefficients,
87                full_range_flag,
88            } => {
89                writer.write_all(b"nclx")?;
90                writer.write_u16::<BigEndian>(*color_primaries)?;
91                writer.write_u16::<BigEndian>(*transfer_characteristics)?;
92                writer.write_u16::<BigEndian>(*matrix_coefficients)?;
93                writer.write_u8(if *full_range_flag { 0x80 } else { 0x00 })?;
94            }
95            ColorType::Unknown((color_type, data)) => {
96                writer.write_all(color_type)?;
97                writer.write_all(data)?;
98            }
99        }
100
101        Ok(())
102    }
103}