1extern crate alloc;
15
16use alloc::string::String;
17use alloc::vec::Vec;
18
19use broadcast_common::{Parse, Serialize};
20
21use crate::error::{Error, Result};
22use crate::local_set::{LocalSet, StructuralSetKind};
23use crate::sets::{
24 InterchangeObjectFields, LocalSetOwnedItem, collect_dark, finish_owned_set, get_optional_fixed,
25 get_optional_raw, get_required_fixed, get_required_raw, owned_set_serialized_len,
26 serialize_owned_set,
27};
28use crate::types::{RATIONAL_LEN, Rational, StrongRef, decode_utf16_be, encode_utf16_be};
29
30pub const TAG_TRACK_ID: u16 = 0x4801;
34pub const TAG_TRACK_NAME: u16 = 0x4802;
36pub const TAG_SEQUENCE: u16 = 0x4803;
38pub const TAG_TRACK_NUMBER: u16 = 0x4804;
40
41pub const TAG_EDIT_RATE: u16 = 0x4B01;
45pub const TAG_ORIGIN: u16 = 0x4B02;
47
48pub const TAG_EVENT_EDIT_RATE: u16 = 0x4901;
52pub const TAG_EVENT_ORIGIN: u16 = 0x4902;
54
55const TIMELINE_KNOWN_TAGS: [u16; 9] = [
58 crate::sets::TAG_INSTANCE_UID,
59 crate::sets::TAG_GENERATION_UID,
60 crate::sets::TAG_OBJECT_CLASS,
61 TAG_TRACK_ID,
62 TAG_TRACK_NAME,
63 TAG_SEQUENCE,
64 TAG_TRACK_NUMBER,
65 TAG_EDIT_RATE,
66 TAG_ORIGIN,
67];
68
69const EVENT_KNOWN_TAGS: [u16; 9] = [
70 crate::sets::TAG_INSTANCE_UID,
71 crate::sets::TAG_GENERATION_UID,
72 crate::sets::TAG_OBJECT_CLASS,
73 TAG_TRACK_ID,
74 TAG_TRACK_NAME,
75 TAG_SEQUENCE,
76 TAG_TRACK_NUMBER,
77 TAG_EVENT_EDIT_RATE,
78 TAG_EVENT_ORIGIN,
79];
80
81const STATIC_KNOWN_TAGS: [u16; 7] = [
82 crate::sets::TAG_INSTANCE_UID,
83 crate::sets::TAG_GENERATION_UID,
84 crate::sets::TAG_OBJECT_CLASS,
85 TAG_TRACK_ID,
86 TAG_TRACK_NAME,
87 TAG_SEQUENCE,
88 TAG_TRACK_NUMBER,
89];
90
91#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct TimelineTrack {
100 pub interchange: InterchangeObjectFields,
102 pub track_id: u32,
104 pub track_number: u32,
106 pub track_name: Option<String>,
108 pub sequence: StrongRef,
110 pub edit_rate: Rational,
112 pub origin: i64,
114 pub dark: Vec<(u16, Vec<u8>)>,
116}
117
118impl<'a> Parse<'a> for TimelineTrack {
119 type Error = Error;
120
121 fn parse(bytes: &'a [u8]) -> Result<Self> {
122 let set = LocalSet::parse(bytes)?;
123 if set.kind() != StructuralSetKind::TimelineTrack {
124 return Err(Error::KeyPrefixMismatch {
125 what: "Timeline Track (Table 17)",
126 });
127 }
128 let items = &set.items;
129 let interchange = InterchangeObjectFields::decode(items, "Timeline Track")?;
130 let track_id = u32::from_be_bytes(get_required_fixed::<4>(
131 items,
132 TAG_TRACK_ID,
133 "Track ID",
134 "Timeline Track",
135 )?);
136 let track_number = u32::from_be_bytes(get_required_fixed::<4>(
137 items,
138 TAG_TRACK_NUMBER,
139 "Track Number",
140 "Timeline Track",
141 )?);
142 let track_name = get_optional_raw(items, TAG_TRACK_NAME)
143 .map(decode_utf16_be)
144 .transpose()
145 .map_err(|_| Error::InvalidUtf16 {
146 tag: TAG_TRACK_NAME,
147 name: "Track Name",
148 })?;
149 let sequence = get_required_fixed::<16>(items, TAG_SEQUENCE, "Sequence", "Timeline Track")?;
150 let edit_rate = Rational::parse(get_required_raw(
151 items,
152 TAG_EDIT_RATE,
153 "Edit Rate",
154 "Timeline Track",
155 )?)?;
156 let origin = i64::from_be_bytes(get_required_fixed::<8>(
157 items,
158 TAG_ORIGIN,
159 "Origin",
160 "Timeline Track",
161 )?);
162 let dark = collect_dark(items, &TIMELINE_KNOWN_TAGS);
163
164 Ok(TimelineTrack {
165 interchange,
166 track_id,
167 track_number,
168 track_name,
169 sequence,
170 edit_rate,
171 origin,
172 dark,
173 })
174 }
175}
176
177impl TimelineTrack {
178 fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
179 let mut out = Vec::new();
180 self.interchange.encode_into(&mut out);
181 out.push(LocalSetOwnedItem::fixed(
182 TAG_TRACK_ID,
183 self.track_id.to_be_bytes(),
184 ));
185 out.push(LocalSetOwnedItem::fixed(
186 TAG_TRACK_NUMBER,
187 self.track_number.to_be_bytes(),
188 ));
189 if let Some(n) = &self.track_name {
190 out.push(LocalSetOwnedItem::owned(TAG_TRACK_NAME, encode_utf16_be(n)));
191 }
192 out.push(LocalSetOwnedItem::fixed(TAG_SEQUENCE, self.sequence));
193 {
194 let mut buf = [0u8; RATIONAL_LEN];
195 self.edit_rate
196 .serialize_into(&mut buf)
197 .expect("fixed-size buffer");
198 out.push(LocalSetOwnedItem::owned(TAG_EDIT_RATE, buf.to_vec()));
199 }
200 out.push(LocalSetOwnedItem::fixed(
201 TAG_ORIGIN,
202 self.origin.to_be_bytes(),
203 ));
204 out
205 }
206}
207
208impl Serialize for TimelineTrack {
209 type Error = Error;
210
211 fn serialized_len(&self) -> usize {
212 let (key, items) = finish_owned_set(
213 StructuralSetKind::TimelineTrack,
214 self.owned_items(),
215 &self.dark,
216 );
217 owned_set_serialized_len(key, &items)
218 }
219
220 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
221 let (key, items) = finish_owned_set(
222 StructuralSetKind::TimelineTrack,
223 self.owned_items(),
224 &self.dark,
225 );
226 serialize_owned_set(key, &items, buf)
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Eq)]
238pub struct EventTrack {
239 pub interchange: InterchangeObjectFields,
241 pub track_id: u32,
243 pub track_number: u32,
245 pub track_name: Option<String>,
247 pub sequence: StrongRef,
249 pub event_edit_rate: Rational,
251 pub event_origin: Option<i64>,
253 pub dark: Vec<(u16, Vec<u8>)>,
255}
256
257impl<'a> Parse<'a> for EventTrack {
258 type Error = Error;
259
260 fn parse(bytes: &'a [u8]) -> Result<Self> {
261 let set = LocalSet::parse(bytes)?;
262 if set.kind() != StructuralSetKind::EventTrackDm {
263 return Err(Error::KeyPrefixMismatch {
264 what: "Event Track (Table 17)",
265 });
266 }
267 let items = &set.items;
268 let interchange = InterchangeObjectFields::decode(items, "Event Track")?;
269 let track_id = u32::from_be_bytes(get_required_fixed::<4>(
270 items,
271 TAG_TRACK_ID,
272 "Track ID",
273 "Event Track",
274 )?);
275 let track_number = u32::from_be_bytes(get_required_fixed::<4>(
276 items,
277 TAG_TRACK_NUMBER,
278 "Track Number",
279 "Event Track",
280 )?);
281 let track_name = get_optional_raw(items, TAG_TRACK_NAME)
282 .map(decode_utf16_be)
283 .transpose()
284 .map_err(|_| Error::InvalidUtf16 {
285 tag: TAG_TRACK_NAME,
286 name: "Track Name",
287 })?;
288 let sequence = get_required_fixed::<16>(items, TAG_SEQUENCE, "Sequence", "Event Track")?;
289 let event_edit_rate = Rational::parse(get_required_raw(
290 items,
291 TAG_EVENT_EDIT_RATE,
292 "Event Edit Rate",
293 "Event Track",
294 )?)?;
295 let event_origin = get_optional_fixed::<8>(items, TAG_EVENT_ORIGIN, "Event Origin")?
296 .map(i64::from_be_bytes);
297 let dark = collect_dark(items, &EVENT_KNOWN_TAGS);
298
299 Ok(EventTrack {
300 interchange,
301 track_id,
302 track_number,
303 track_name,
304 sequence,
305 event_edit_rate,
306 event_origin,
307 dark,
308 })
309 }
310}
311
312impl EventTrack {
313 fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
314 let mut out = Vec::new();
315 self.interchange.encode_into(&mut out);
316 out.push(LocalSetOwnedItem::fixed(
317 TAG_TRACK_ID,
318 self.track_id.to_be_bytes(),
319 ));
320 out.push(LocalSetOwnedItem::fixed(
321 TAG_TRACK_NUMBER,
322 self.track_number.to_be_bytes(),
323 ));
324 if let Some(n) = &self.track_name {
325 out.push(LocalSetOwnedItem::owned(TAG_TRACK_NAME, encode_utf16_be(n)));
326 }
327 out.push(LocalSetOwnedItem::fixed(TAG_SEQUENCE, self.sequence));
328 {
329 let mut buf = [0u8; RATIONAL_LEN];
330 self.event_edit_rate
331 .serialize_into(&mut buf)
332 .expect("fixed-size buffer");
333 out.push(LocalSetOwnedItem::owned(TAG_EVENT_EDIT_RATE, buf.to_vec()));
334 }
335 if let Some(o) = self.event_origin {
336 out.push(LocalSetOwnedItem::fixed(TAG_EVENT_ORIGIN, o.to_be_bytes()));
337 }
338 out
339 }
340}
341
342impl Serialize for EventTrack {
343 type Error = Error;
344
345 fn serialized_len(&self) -> usize {
346 let (key, items) = finish_owned_set(
347 StructuralSetKind::EventTrackDm,
348 self.owned_items(),
349 &self.dark,
350 );
351 owned_set_serialized_len(key, &items)
352 }
353
354 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
355 let (key, items) = finish_owned_set(
356 StructuralSetKind::EventTrackDm,
357 self.owned_items(),
358 &self.dark,
359 );
360 serialize_owned_set(key, &items, buf)
361 }
362}
363
364#[derive(Debug, Clone, PartialEq, Eq)]
372pub struct StaticTrack {
373 pub interchange: InterchangeObjectFields,
375 pub track_id: u32,
377 pub track_number: u32,
379 pub track_name: Option<String>,
381 pub sequence: StrongRef,
383 pub dark: Vec<(u16, Vec<u8>)>,
385}
386
387impl<'a> Parse<'a> for StaticTrack {
388 type Error = Error;
389
390 fn parse(bytes: &'a [u8]) -> Result<Self> {
391 let set = LocalSet::parse(bytes)?;
392 if set.kind() != StructuralSetKind::StaticTrackDm {
393 return Err(Error::KeyPrefixMismatch {
394 what: "Static Track (Table 17)",
395 });
396 }
397 let items = &set.items;
398 let interchange = InterchangeObjectFields::decode(items, "Static Track")?;
399 let track_id = u32::from_be_bytes(get_required_fixed::<4>(
400 items,
401 TAG_TRACK_ID,
402 "Track ID",
403 "Static Track",
404 )?);
405 let track_number = u32::from_be_bytes(get_required_fixed::<4>(
406 items,
407 TAG_TRACK_NUMBER,
408 "Track Number",
409 "Static Track",
410 )?);
411 let track_name = get_optional_raw(items, TAG_TRACK_NAME)
412 .map(decode_utf16_be)
413 .transpose()
414 .map_err(|_| Error::InvalidUtf16 {
415 tag: TAG_TRACK_NAME,
416 name: "Track Name",
417 })?;
418 let sequence = get_required_fixed::<16>(items, TAG_SEQUENCE, "Sequence", "Static Track")?;
419 let dark = collect_dark(items, &STATIC_KNOWN_TAGS);
420
421 Ok(StaticTrack {
422 interchange,
423 track_id,
424 track_number,
425 track_name,
426 sequence,
427 dark,
428 })
429 }
430}
431
432impl StaticTrack {
433 fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
434 let mut out = Vec::new();
435 self.interchange.encode_into(&mut out);
436 out.push(LocalSetOwnedItem::fixed(
437 TAG_TRACK_ID,
438 self.track_id.to_be_bytes(),
439 ));
440 out.push(LocalSetOwnedItem::fixed(
441 TAG_TRACK_NUMBER,
442 self.track_number.to_be_bytes(),
443 ));
444 if let Some(n) = &self.track_name {
445 out.push(LocalSetOwnedItem::owned(TAG_TRACK_NAME, encode_utf16_be(n)));
446 }
447 out.push(LocalSetOwnedItem::fixed(TAG_SEQUENCE, self.sequence));
448 out
449 }
450}
451
452impl Serialize for StaticTrack {
453 type Error = Error;
454
455 fn serialized_len(&self) -> usize {
456 let (key, items) = finish_owned_set(
457 StructuralSetKind::StaticTrackDm,
458 self.owned_items(),
459 &self.dark,
460 );
461 owned_set_serialized_len(key, &items)
462 }
463
464 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
465 let (key, items) = finish_owned_set(
466 StructuralSetKind::StaticTrackDm,
467 self.owned_items(),
468 &self.dark,
469 );
470 serialize_owned_set(key, &items, buf)
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477
478 fn sample_timeline_track() -> TimelineTrack {
479 TimelineTrack {
480 interchange: InterchangeObjectFields {
481 instance_uid: [0x01; 16],
482 generation_uid: None,
483 object_class: None,
484 },
485 track_id: 1,
486 track_number: 0x15010100,
487 track_name: Some(String::from("Video")),
488 sequence: [0x02; 16],
489 edit_rate: Rational {
490 numerator: 25,
491 denominator: 1,
492 },
493 origin: 0,
494 dark: Vec::new(),
495 }
496 }
497
498 fn sample_event_track() -> EventTrack {
499 EventTrack {
500 interchange: InterchangeObjectFields {
501 instance_uid: [0x10; 16],
502 generation_uid: None,
503 object_class: None,
504 },
505 track_id: 3,
506 track_number: 0,
507 track_name: None,
508 sequence: [0x20; 16],
509 event_edit_rate: Rational {
510 numerator: 25,
511 denominator: 1,
512 },
513 event_origin: Some(100),
514 dark: Vec::new(),
515 }
516 }
517
518 fn sample_static_track() -> StaticTrack {
519 StaticTrack {
520 interchange: InterchangeObjectFields {
521 instance_uid: [0x30; 16],
522 generation_uid: None,
523 object_class: None,
524 },
525 track_id: 4,
526 track_number: 0,
527 track_name: None,
528 sequence: [0x40; 16],
529 dark: Vec::new(),
530 }
531 }
532
533 #[test]
534 fn timeline_track_round_trip() {
535 let tt = sample_timeline_track();
536 let bytes = tt.to_bytes();
537 let parsed = TimelineTrack::parse(&bytes).unwrap();
538 assert_eq!(parsed, tt);
539 assert_eq!(parsed.to_bytes(), bytes);
540 }
541
542 #[test]
543 fn timeline_track_no_name_round_trip() {
544 let mut tt = sample_timeline_track();
545 tt.track_name = None;
546 let bytes = tt.to_bytes();
547 let parsed = TimelineTrack::parse(&bytes).unwrap();
548 assert_eq!(parsed.track_name, None);
549 assert_eq!(parsed.to_bytes(), bytes);
550 }
551
552 #[test]
553 fn timeline_track_dark_preserved() {
554 let mut tt = sample_timeline_track();
555 tt.dark = alloc::vec![(0x8001, alloc::vec![0xAB])];
556 let bytes = tt.to_bytes();
557 let parsed = TimelineTrack::parse(&bytes).unwrap();
558 assert_eq!(parsed.dark, tt.dark);
559 }
560
561 #[test]
562 fn event_track_round_trip() {
563 let et = sample_event_track();
564 let bytes = et.to_bytes();
565 let parsed = EventTrack::parse(&bytes).unwrap();
566 assert_eq!(parsed, et);
567 assert_eq!(parsed.to_bytes(), bytes);
568 }
569
570 #[test]
571 fn event_track_no_origin_round_trip() {
572 let mut et = sample_event_track();
573 et.event_origin = None;
574 let bytes = et.to_bytes();
575 let parsed = EventTrack::parse(&bytes).unwrap();
576 assert_eq!(parsed.event_origin, None);
577 assert_eq!(parsed.to_bytes(), bytes);
578 }
579
580 #[test]
581 fn static_track_round_trip() {
582 let st = sample_static_track();
583 let bytes = st.to_bytes();
584 let parsed = StaticTrack::parse(&bytes).unwrap();
585 assert_eq!(parsed, st);
586 assert_eq!(parsed.to_bytes(), bytes);
587 }
588
589 #[test]
590 fn wrong_kind_rejected_timeline() {
591 let st = sample_static_track();
592 let bytes = st.to_bytes();
593 assert!(matches!(
594 TimelineTrack::parse(&bytes),
595 Err(Error::KeyPrefixMismatch { .. })
596 ));
597 }
598
599 #[test]
600 fn wrong_kind_rejected_event() {
601 let tt = sample_timeline_track();
602 let bytes = tt.to_bytes();
603 assert!(matches!(
604 EventTrack::parse(&bytes),
605 Err(Error::KeyPrefixMismatch { .. })
606 ));
607 }
608
609 #[test]
610 fn wrong_kind_rejected_static() {
611 let et = sample_event_track();
612 let bytes = et.to_bytes();
613 assert!(matches!(
614 StaticTrack::parse(&bytes),
615 Err(Error::KeyPrefixMismatch { .. })
616 ));
617 }
618
619 #[test]
620 fn mutation_changes_serialized_bytes() {
621 let mut tt = sample_timeline_track();
622 let before = tt.to_bytes();
623 tt.origin = 42;
624 let after = tt.to_bytes();
625 assert_ne!(before, after);
626 assert_eq!(TimelineTrack::parse(&after).unwrap().origin, 42);
627 }
628}