Skip to main content

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

1use crate::*;
2
3ext! {
4    name: Ctts,
5    versions: [0, 1],
6    flags: {}
7}
8
9#[derive(Debug, Clone, PartialEq, Eq, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Ctts {
12    pub entries: Vec<CttsEntry>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct CttsEntry {
18    pub sample_count: u32,
19    /// Unsigned for version 0 boxes and signed for version 1 boxes.
20    pub sample_offset: i64,
21}
22
23impl AtomExt for Ctts {
24    type Ext = CttsExt;
25
26    const KIND_EXT: FourCC = FourCC::new(b"ctts");
27
28    fn decode_body_ext<B: Buf>(buf: &mut B, ext: CttsExt) -> Result<Self> {
29        let entry_count = u32::decode(buf)?;
30
31        let mut entries = Vec::new();
32        for _ in 0..entry_count {
33            let entry = CttsEntry {
34                sample_count: u32::decode(buf)?,
35                sample_offset: match ext.version {
36                    CttsVersion::V0 => u32::decode(buf)?.into(),
37                    CttsVersion::V1 => i32::decode(buf)?.into(),
38                },
39            };
40            entries.push(entry);
41        }
42
43        Ok(Ctts { entries })
44    }
45
46    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<CttsExt> {
47        let version = if self.entries.iter().any(|entry| entry.sample_offset < 0) {
48            CttsVersion::V1
49        } else {
50            CttsVersion::V0
51        };
52
53        (self.entries.len() as u32).encode(buf)?;
54        for entry in self.entries.iter() {
55            (entry.sample_count).encode(buf)?;
56            match version {
57                CttsVersion::V0 => u32::try_from(entry.sample_offset)
58                    .map_err(|_| Error::InvalidSize)?
59                    .encode(buf)?,
60                CttsVersion::V1 => i32::try_from(entry.sample_offset)
61                    .map_err(|_| Error::InvalidSize)?
62                    .encode(buf)?,
63            }
64        }
65
66        Ok(version.into())
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn test_ctts_v1() {
76        const ENCODED: &[u8] = &[
77            0x00, 0x00, 0x00, 0x20, b'c', b't', b't', b's', 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
78            0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x02,
79            0xff, 0xff, 0xff, 0x9c,
80        ];
81
82        let expected = Ctts {
83            entries: vec![
84                CttsEntry {
85                    sample_count: 1,
86                    sample_offset: 200,
87                },
88                CttsEntry {
89                    sample_count: 2,
90                    sample_offset: -100,
91                },
92            ],
93        };
94        let mut buf = Vec::new();
95        expected.encode(&mut buf).unwrap();
96        assert_eq!(buf, ENCODED);
97
98        let mut buf = buf.as_ref();
99        let decoded = Ctts::decode(&mut buf).unwrap();
100        assert_eq!(decoded, expected);
101    }
102
103    #[test]
104    fn test_ctts_v0_unsigned_offset() {
105        const ENCODED: &[u8] = &[
106            0x00, 0x00, 0x00, 0x18, b'c', b't', b't', b's', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107            0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00,
108        ];
109
110        let decoded = Ctts::decode(&mut &ENCODED[..]).unwrap();
111        assert_eq!(
112            decoded,
113            Ctts {
114                entries: vec![CttsEntry {
115                    sample_count: 2,
116                    sample_offset: 1 << 31,
117                }],
118            }
119        );
120
121        let mut encoded = Vec::new();
122        decoded.encode(&mut encoded).unwrap();
123        assert_eq!(encoded, ENCODED);
124    }
125}