mp4_atom/moov/trak/mdia/minf/stbl/
cslg.rs

1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, Default)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Cslg {
6    pub composition_to_dts_shift: i64,
7    pub least_decode_to_display_shift: i64,
8    pub greatest_decode_to_display_delta: i64,
9    pub composition_start_time: i64,
10    pub composition_end_time: i64,
11}
12
13ext! {
14    name: Cslg,
15    versions: [0, 1],
16    flags: {}
17}
18
19impl AtomExt for Cslg {
20    type Ext = CslgExt;
21
22    const KIND_EXT: FourCC = FourCC::new(b"cslg");
23
24    fn decode_body_ext<B: Buf>(buf: &mut B, ext: CslgExt) -> Result<Self> {
25        if ext.version == CslgVersion::V0 {
26            Ok(Cslg {
27                composition_to_dts_shift: i32::decode(buf)?.into(),
28                least_decode_to_display_shift: i32::decode(buf)?.into(),
29                greatest_decode_to_display_delta: i32::decode(buf)?.into(),
30                composition_start_time: i32::decode(buf)?.into(),
31                composition_end_time: i32::decode(buf)?.into(),
32            })
33        } else {
34            Ok(Cslg {
35                composition_to_dts_shift: i64::decode(buf)?,
36                least_decode_to_display_shift: i64::decode(buf)?,
37                greatest_decode_to_display_delta: i64::decode(buf)?,
38                composition_start_time: i64::decode(buf)?,
39                composition_end_time: i64::decode(buf)?,
40            })
41        }
42    }
43
44    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<CslgExt> {
45        match (
46            i32::try_from(self.composition_to_dts_shift),
47            i32::try_from(self.least_decode_to_display_shift),
48            i32::try_from(self.greatest_decode_to_display_delta),
49            i32::try_from(self.composition_start_time),
50            i32::try_from(self.composition_end_time),
51        ) {
52            (
53                Ok(composition_to_dts_shift),
54                Ok(least_decode_to_display_shift),
55                Ok(greatest_decode_to_display_delta),
56                Ok(composition_start_time),
57                Ok(composition_end_time),
58            ) => {
59                composition_to_dts_shift.encode(buf)?;
60                least_decode_to_display_shift.encode(buf)?;
61                greatest_decode_to_display_delta.encode(buf)?;
62                composition_start_time.encode(buf)?;
63                composition_end_time.encode(buf)?;
64                Ok(CslgExt {
65                    version: CslgVersion::V0,
66                })
67            }
68            _ => {
69                self.composition_to_dts_shift.encode(buf)?;
70                self.least_decode_to_display_shift.encode(buf)?;
71                self.greatest_decode_to_display_delta.encode(buf)?;
72                self.composition_start_time.encode(buf)?;
73                self.composition_end_time.encode(buf)?;
74                Ok(CslgExt {
75                    version: CslgVersion::V1,
76                })
77            }
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    // From MPEG file format conformance suite, 17_negative_ctso.mp4
87    const ENCODED_CSLG_V0: &[u8] = &[
88        0x00, 0x00, 0x00, 0x20, 0x63, 0x73, 0x6c, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89        0x08, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90        0x13, 0x88,
91    ];
92
93    // Self generated, no V1 test data
94    const ENCODED_CSLG_V1: &[u8] = &[
95        0x00, 0x00, 0x00, 0x34, b'c', b's', b'l', b'g', 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96        0x00, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
97        0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
98        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
99    ];
100
101    // Self generated, no V1 test data
102    const ENCODED_CSLG_V1_VARIANT: &[u8] = &[
103        0x00, 0x00, 0x00, 0x34, b'c', b's', b'l', b'g', 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104        0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
105        0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
106        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
107    ];
108
109    #[test]
110    fn test_cslg_v0() {
111        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_CSLG_V0);
112
113        let cslg = Cslg::decode(buf).expect("failed to decode cslg");
114
115        assert_eq!(
116            cslg,
117            Cslg {
118                composition_to_dts_shift: 8,
119                least_decode_to_display_shift: -8,
120                greatest_decode_to_display_delta: 8,
121                composition_start_time: 0,
122                composition_end_time: 5000
123            },
124        );
125
126        let mut buf = Vec::new();
127        cslg.encode(&mut buf).unwrap();
128
129        assert_eq!(buf.as_slice(), ENCODED_CSLG_V0);
130    }
131
132    #[test]
133    fn test_cslg_v1_encode() {
134        let cslg = Cslg {
135            composition_to_dts_shift: 2147483648,
136            least_decode_to_display_shift: -2147483649,
137            greatest_decode_to_display_delta: 8,
138            composition_start_time: i64::MIN,
139            composition_end_time: i64::MAX,
140        };
141
142        let mut buf = Vec::new();
143        cslg.encode(&mut buf).unwrap();
144
145        assert_eq!(buf.as_slice(), ENCODED_CSLG_V1);
146    }
147
148    #[test]
149    fn test_cslg_v1_decode() {
150        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_CSLG_V1);
151
152        let cslg = Cslg::decode(buf).expect("failed to decode cslg");
153
154        assert_eq!(
155            cslg,
156            Cslg {
157                composition_to_dts_shift: 2147483648,
158                least_decode_to_display_shift: -2147483649,
159                greatest_decode_to_display_delta: 8,
160                composition_start_time: i64::MIN,
161                composition_end_time: i64::MAX,
162            },
163        );
164    }
165
166    #[test]
167    fn test_cslg_v1_encode_variant() {
168        let cslg = Cslg {
169            composition_to_dts_shift: 2147483647,
170            least_decode_to_display_shift: -2147483649,
171            greatest_decode_to_display_delta: 8,
172            composition_start_time: i64::MIN,
173            composition_end_time: i64::MAX,
174        };
175
176        let mut buf = Vec::new();
177        cslg.encode(&mut buf).unwrap();
178
179        assert_eq!(buf.as_slice(), ENCODED_CSLG_V1_VARIANT);
180    }
181}