1extern crate alloc;
10
11use alloc::vec::Vec;
12
13use broadcast_common::{Parse, Serialize};
14
15use crate::error::{Error, Result};
16use crate::local_set::{LocalSet, StructuralSetKind};
17use crate::sets::{
18 InterchangeObjectFields, LocalSetOwnedItem, collect_dark, finish_owned_set, get_optional_fixed,
19 get_required_fixed, owned_set_serialized_len, serialize_owned_set,
20};
21use crate::types::UlBytes;
22
23pub const TAG_DATA_DEFINITION: u16 = 0x0201;
27pub const TAG_DURATION: u16 = 0x0202;
29
30pub const TAG_START_TIMECODE: u16 = 0x1501;
34pub const TAG_ROUNDED_TIMECODE_BASE: u16 = 0x1502;
36pub const TAG_DROP_FRAME: u16 = 0x1503;
38
39const KNOWN_TAGS: [u16; 8] = [
40 crate::sets::TAG_INSTANCE_UID,
41 crate::sets::TAG_GENERATION_UID,
42 crate::sets::TAG_OBJECT_CLASS,
43 TAG_DATA_DEFINITION,
44 TAG_DURATION,
45 TAG_START_TIMECODE,
46 TAG_ROUNDED_TIMECODE_BASE,
47 TAG_DROP_FRAME,
48];
49
50#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct TimecodeComponent {
55 pub interchange: InterchangeObjectFields,
57 pub data_definition: UlBytes,
60 pub duration: Option<i64>,
62 pub start_timecode: i64,
65 pub rounded_timecode_base: u16,
68 pub drop_frame: bool,
71 pub dark: Vec<(u16, Vec<u8>)>,
73}
74
75impl<'a> Parse<'a> for TimecodeComponent {
76 type Error = Error;
77
78 fn parse(bytes: &'a [u8]) -> Result<Self> {
79 let set = LocalSet::parse(bytes)?;
80 if set.kind() != StructuralSetKind::TimecodeComponent {
81 return Err(Error::KeyPrefixMismatch {
82 what: "Timecode Component (Table 17)",
83 });
84 }
85 let items = &set.items;
86 let interchange = InterchangeObjectFields::decode(items, "Timecode Component")?;
87 let data_definition = get_required_fixed::<16>(
88 items,
89 TAG_DATA_DEFINITION,
90 "Data Definition",
91 "Timecode Component",
92 )?;
93 let duration =
94 get_optional_fixed::<8>(items, TAG_DURATION, "Duration")?.map(i64::from_be_bytes);
95 let start_timecode = i64::from_be_bytes(get_required_fixed::<8>(
96 items,
97 TAG_START_TIMECODE,
98 "Start Timecode",
99 "Timecode Component",
100 )?);
101 let rounded_timecode_base = u16::from_be_bytes(get_required_fixed::<2>(
102 items,
103 TAG_ROUNDED_TIMECODE_BASE,
104 "Rounded Timecode Base",
105 "Timecode Component",
106 )?);
107 let drop_frame =
108 get_required_fixed::<1>(items, TAG_DROP_FRAME, "Drop Frame", "Timecode Component")?[0]
109 != 0;
110 let dark = collect_dark(items, &KNOWN_TAGS);
111
112 Ok(TimecodeComponent {
113 interchange,
114 data_definition,
115 duration,
116 start_timecode,
117 rounded_timecode_base,
118 drop_frame,
119 dark,
120 })
121 }
122}
123
124impl TimecodeComponent {
125 fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
126 let mut out = Vec::new();
127 self.interchange.encode_into(&mut out);
128 out.push(LocalSetOwnedItem::fixed(
129 TAG_DATA_DEFINITION,
130 self.data_definition,
131 ));
132 if let Some(d) = self.duration {
133 out.push(LocalSetOwnedItem::fixed(TAG_DURATION, d.to_be_bytes()));
134 }
135 out.push(LocalSetOwnedItem::fixed(
136 TAG_START_TIMECODE,
137 self.start_timecode.to_be_bytes(),
138 ));
139 out.push(LocalSetOwnedItem::fixed(
140 TAG_ROUNDED_TIMECODE_BASE,
141 self.rounded_timecode_base.to_be_bytes(),
142 ));
143 out.push(LocalSetOwnedItem::fixed(
144 TAG_DROP_FRAME,
145 [u8::from(self.drop_frame)],
146 ));
147 out
148 }
149}
150
151impl Serialize for TimecodeComponent {
152 type Error = Error;
153
154 fn serialized_len(&self) -> usize {
155 let (key, items) = finish_owned_set(
156 StructuralSetKind::TimecodeComponent,
157 self.owned_items(),
158 &self.dark,
159 );
160 owned_set_serialized_len(key, &items)
161 }
162
163 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
164 let (key, items) = finish_owned_set(
165 StructuralSetKind::TimecodeComponent,
166 self.owned_items(),
167 &self.dark,
168 );
169 serialize_owned_set(key, &items, buf)
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 const TIMECODE_DD: UlBytes = [
179 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00,
180 0x00,
181 ];
182
183 fn sample() -> TimecodeComponent {
184 TimecodeComponent {
185 interchange: InterchangeObjectFields {
186 instance_uid: [0x01; 16],
187 generation_uid: None,
188 object_class: None,
189 },
190 data_definition: TIMECODE_DD,
191 duration: Some(250),
192 start_timecode: 90000,
193 rounded_timecode_base: 25,
194 drop_frame: false,
195 dark: Vec::new(),
196 }
197 }
198
199 #[test]
200 fn round_trip() {
201 let tc = sample();
202 let bytes = tc.to_bytes();
203 let parsed = TimecodeComponent::parse(&bytes).unwrap();
204 assert_eq!(parsed, tc);
205 assert_eq!(parsed.to_bytes(), bytes);
206 }
207
208 #[test]
209 fn drop_frame_true_round_trips() {
210 let mut tc = sample();
211 tc.drop_frame = true;
212 tc.rounded_timecode_base = 30;
213 let bytes = tc.to_bytes();
214 let parsed = TimecodeComponent::parse(&bytes).unwrap();
215 assert!(parsed.drop_frame);
216 assert_eq!(parsed.rounded_timecode_base, 30);
217 assert_eq!(parsed.to_bytes(), bytes);
218 }
219
220 #[test]
221 fn no_duration_round_trip() {
222 let mut tc = sample();
223 tc.duration = None;
224 let bytes = tc.to_bytes();
225 let parsed = TimecodeComponent::parse(&bytes).unwrap();
226 assert_eq!(parsed.duration, None);
227 assert_eq!(parsed.to_bytes(), bytes);
228 }
229
230 #[test]
231 fn dark_preserved() {
232 let mut tc = sample();
233 tc.dark = alloc::vec![(0x9004, alloc::vec![0xCA, 0xFE])];
234 let bytes = tc.to_bytes();
235 let parsed = TimecodeComponent::parse(&bytes).unwrap();
236 assert_eq!(parsed.dark, tc.dark);
237 }
238
239 #[test]
240 fn wrong_kind_rejected() {
241 let key = LocalSet::build_key(
242 StructuralSetKind::SourceClip,
243 crate::local_set::ItemLengthMode::TwoByte,
244 );
245 let set = LocalSet {
246 key,
247 items: Vec::new(),
248 };
249 let bytes = set.to_bytes();
250 assert!(matches!(
251 TimecodeComponent::parse(&bytes),
252 Err(Error::KeyPrefixMismatch { .. })
253 ));
254 }
255
256 #[test]
257 fn mutation_changes_serialized_bytes() {
258 let mut tc = sample();
259 let before = tc.to_bytes();
260 tc.start_timecode = 1800000;
261 let after = tc.to_bytes();
262 assert_ne!(before, after);
263 assert_eq!(
264 TimecodeComponent::parse(&after).unwrap().start_timecode,
265 1800000
266 );
267 }
268}