mp4_atom/moov/udta/
chpl.rs1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct ChplEntry {
7 pub start: u64,
9 pub title: String,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq, Default)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Chpl {
26 pub entries: Vec<ChplEntry>,
27}
28
29impl Atom for Chpl {
30 const KIND: FourCC = FourCC::new(b"chpl");
31
32 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
33 let version = u8::decode(buf)?;
34 <[u8; 3]>::decode(buf)?; if version != 0 {
36 <[u8; 4]>::decode(buf)?; }
38
39 let count = u8::decode(buf)?;
40 let mut entries = Vec::with_capacity(count as usize);
41 for _ in 0..count {
42 let start = u64::decode(buf)?;
43
44 let title_len = u8::decode(buf)? as usize;
45 if title_len > buf.remaining() {
46 return Err(Error::OutOfBounds);
47 }
48 let title_bytes = buf.slice(title_len).to_vec();
49 buf.advance(title_len);
50 let title = String::from_utf8(title_bytes)
51 .map_err(|err| Error::InvalidString(err.to_string()))?;
52
53 entries.push(ChplEntry { start, title });
54 }
55
56 Ok(Self { entries })
57 }
58
59 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
60 1u8.encode(buf)?; [0u8; 3].encode(buf)?; [0u8; 4].encode(buf)?; let count: u8 = self
65 .entries
66 .len()
67 .try_into()
68 .map_err(|_| Error::TooLarge(Self::KIND))?;
69 count.encode(buf)?;
70
71 for entry in &self.entries {
72 entry.start.encode(buf)?;
73
74 let title_bytes = entry.title.as_bytes();
75 let title_len: u8 = title_bytes
76 .len()
77 .try_into()
78 .map_err(|_| Error::TooLarge(Self::KIND))?;
79 title_len.encode(buf)?;
80 buf.append_slice(title_bytes);
81 }
82
83 Ok(())
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_chpl_roundtrip() {
93 let expected = Chpl {
94 entries: vec![
95 ChplEntry {
96 start: 0,
97 title: "Intro".into(),
98 },
99 ChplEntry {
100 start: 30_000_000,
101 title: "Chapter 2".into(),
102 },
103 ],
104 };
105
106 let mut buf = Vec::new();
107 expected.encode(&mut buf).unwrap();
108
109 let decoded = Chpl::decode(&mut buf.as_slice()).expect("failed to decode chpl");
110 assert_eq!(decoded, expected);
111 }
112
113 #[test]
114 fn test_chpl_empty() {
115 let expected = Chpl { entries: vec![] };
116
117 let mut buf = Vec::new();
118 expected.encode(&mut buf).unwrap();
119
120 let decoded = Chpl::decode(&mut buf.as_slice()).expect("failed to decode chpl");
121 assert_eq!(decoded, expected);
122 }
123
124 #[test]
125 fn test_chpl_version0_no_reserved_field() {
126 let expected = Chpl {
129 entries: vec![
130 ChplEntry {
131 start: 0,
132 title: "Intro".into(),
133 },
134 ChplEntry {
135 start: 30_000_000,
136 title: "Chapter 2".into(),
137 },
138 ],
139 };
140
141 let mut buf = Vec::new();
142 0u8.encode(&mut buf).unwrap(); [0u8; 3].encode(&mut buf).unwrap(); let count: u8 = expected.entries.len().try_into().unwrap();
145 count.encode(&mut buf).unwrap();
146 for entry in &expected.entries {
147 entry.start.encode(&mut buf).unwrap();
148 let title_bytes = entry.title.as_bytes();
149 let title_len: u8 = title_bytes.len().try_into().unwrap();
150 title_len.encode(&mut buf).unwrap();
151 buf.extend_from_slice(title_bytes);
152 }
153
154 let decoded = Chpl::decode_body(&mut buf.as_slice()).expect("failed to decode chpl");
155 assert_eq!(decoded, expected);
156 }
157}