extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
use crate::error::{Error, Result};
use crate::local_set::{LocalSet, StructuralSetKind};
use crate::sets::{
InterchangeObjectFields, LocalSetOwnedItem, collect_dark, finish_owned_set, get_optional_fixed,
get_optional_raw, get_required_fixed, get_required_raw, owned_set_serialized_len,
serialize_owned_set,
};
use crate::types::{RATIONAL_LEN, Rational, StrongRef, decode_utf16_be, encode_utf16_be};
pub const TAG_TRACK_ID: u16 = 0x4801;
pub const TAG_TRACK_NAME: u16 = 0x4802;
pub const TAG_SEQUENCE: u16 = 0x4803;
pub const TAG_TRACK_NUMBER: u16 = 0x4804;
pub const TAG_EDIT_RATE: u16 = 0x4B01;
pub const TAG_ORIGIN: u16 = 0x4B02;
pub const TAG_EVENT_EDIT_RATE: u16 = 0x4901;
pub const TAG_EVENT_ORIGIN: u16 = 0x4902;
const TIMELINE_KNOWN_TAGS: [u16; 9] = [
crate::sets::TAG_INSTANCE_UID,
crate::sets::TAG_GENERATION_UID,
crate::sets::TAG_OBJECT_CLASS,
TAG_TRACK_ID,
TAG_TRACK_NAME,
TAG_SEQUENCE,
TAG_TRACK_NUMBER,
TAG_EDIT_RATE,
TAG_ORIGIN,
];
const EVENT_KNOWN_TAGS: [u16; 9] = [
crate::sets::TAG_INSTANCE_UID,
crate::sets::TAG_GENERATION_UID,
crate::sets::TAG_OBJECT_CLASS,
TAG_TRACK_ID,
TAG_TRACK_NAME,
TAG_SEQUENCE,
TAG_TRACK_NUMBER,
TAG_EVENT_EDIT_RATE,
TAG_EVENT_ORIGIN,
];
const STATIC_KNOWN_TAGS: [u16; 7] = [
crate::sets::TAG_INSTANCE_UID,
crate::sets::TAG_GENERATION_UID,
crate::sets::TAG_OBJECT_CLASS,
TAG_TRACK_ID,
TAG_TRACK_NAME,
TAG_SEQUENCE,
TAG_TRACK_NUMBER,
];
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimelineTrack {
pub interchange: InterchangeObjectFields,
pub track_id: u32,
pub track_number: u32,
pub track_name: Option<String>,
pub sequence: StrongRef,
pub edit_rate: Rational,
pub origin: i64,
pub dark: Vec<(u16, Vec<u8>)>,
}
impl<'a> Parse<'a> for TimelineTrack {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let set = LocalSet::parse(bytes)?;
if set.kind() != StructuralSetKind::TimelineTrack {
return Err(Error::KeyPrefixMismatch {
what: "Timeline Track (Table 17)",
});
}
let items = &set.items;
let interchange = InterchangeObjectFields::decode(items, "Timeline Track")?;
let track_id = u32::from_be_bytes(get_required_fixed::<4>(
items,
TAG_TRACK_ID,
"Track ID",
"Timeline Track",
)?);
let track_number = u32::from_be_bytes(get_required_fixed::<4>(
items,
TAG_TRACK_NUMBER,
"Track Number",
"Timeline Track",
)?);
let track_name = get_optional_raw(items, TAG_TRACK_NAME)
.map(decode_utf16_be)
.transpose()
.map_err(|_| Error::InvalidUtf16 {
tag: TAG_TRACK_NAME,
name: "Track Name",
})?;
let sequence = get_required_fixed::<16>(items, TAG_SEQUENCE, "Sequence", "Timeline Track")?;
let edit_rate = Rational::parse(get_required_raw(
items,
TAG_EDIT_RATE,
"Edit Rate",
"Timeline Track",
)?)?;
let origin = i64::from_be_bytes(get_required_fixed::<8>(
items,
TAG_ORIGIN,
"Origin",
"Timeline Track",
)?);
let dark = collect_dark(items, &TIMELINE_KNOWN_TAGS);
Ok(TimelineTrack {
interchange,
track_id,
track_number,
track_name,
sequence,
edit_rate,
origin,
dark,
})
}
}
impl TimelineTrack {
fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
let mut out = Vec::new();
self.interchange.encode_into(&mut out);
out.push(LocalSetOwnedItem::fixed(
TAG_TRACK_ID,
self.track_id.to_be_bytes(),
));
out.push(LocalSetOwnedItem::fixed(
TAG_TRACK_NUMBER,
self.track_number.to_be_bytes(),
));
if let Some(n) = &self.track_name {
out.push(LocalSetOwnedItem::owned(TAG_TRACK_NAME, encode_utf16_be(n)));
}
out.push(LocalSetOwnedItem::fixed(TAG_SEQUENCE, self.sequence));
{
let mut buf = [0u8; RATIONAL_LEN];
self.edit_rate
.serialize_into(&mut buf)
.expect("fixed-size buffer");
out.push(LocalSetOwnedItem::owned(TAG_EDIT_RATE, buf.to_vec()));
}
out.push(LocalSetOwnedItem::fixed(
TAG_ORIGIN,
self.origin.to_be_bytes(),
));
out
}
}
impl Serialize for TimelineTrack {
type Error = Error;
fn serialized_len(&self) -> usize {
let (key, items) = finish_owned_set(
StructuralSetKind::TimelineTrack,
self.owned_items(),
&self.dark,
);
owned_set_serialized_len(key, &items)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let (key, items) = finish_owned_set(
StructuralSetKind::TimelineTrack,
self.owned_items(),
&self.dark,
);
serialize_owned_set(key, &items, buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventTrack {
pub interchange: InterchangeObjectFields,
pub track_id: u32,
pub track_number: u32,
pub track_name: Option<String>,
pub sequence: StrongRef,
pub event_edit_rate: Rational,
pub event_origin: Option<i64>,
pub dark: Vec<(u16, Vec<u8>)>,
}
impl<'a> Parse<'a> for EventTrack {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let set = LocalSet::parse(bytes)?;
if set.kind() != StructuralSetKind::EventTrackDm {
return Err(Error::KeyPrefixMismatch {
what: "Event Track (Table 17)",
});
}
let items = &set.items;
let interchange = InterchangeObjectFields::decode(items, "Event Track")?;
let track_id = u32::from_be_bytes(get_required_fixed::<4>(
items,
TAG_TRACK_ID,
"Track ID",
"Event Track",
)?);
let track_number = u32::from_be_bytes(get_required_fixed::<4>(
items,
TAG_TRACK_NUMBER,
"Track Number",
"Event Track",
)?);
let track_name = get_optional_raw(items, TAG_TRACK_NAME)
.map(decode_utf16_be)
.transpose()
.map_err(|_| Error::InvalidUtf16 {
tag: TAG_TRACK_NAME,
name: "Track Name",
})?;
let sequence = get_required_fixed::<16>(items, TAG_SEQUENCE, "Sequence", "Event Track")?;
let event_edit_rate = Rational::parse(get_required_raw(
items,
TAG_EVENT_EDIT_RATE,
"Event Edit Rate",
"Event Track",
)?)?;
let event_origin = get_optional_fixed::<8>(items, TAG_EVENT_ORIGIN, "Event Origin")?
.map(i64::from_be_bytes);
let dark = collect_dark(items, &EVENT_KNOWN_TAGS);
Ok(EventTrack {
interchange,
track_id,
track_number,
track_name,
sequence,
event_edit_rate,
event_origin,
dark,
})
}
}
impl EventTrack {
fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
let mut out = Vec::new();
self.interchange.encode_into(&mut out);
out.push(LocalSetOwnedItem::fixed(
TAG_TRACK_ID,
self.track_id.to_be_bytes(),
));
out.push(LocalSetOwnedItem::fixed(
TAG_TRACK_NUMBER,
self.track_number.to_be_bytes(),
));
if let Some(n) = &self.track_name {
out.push(LocalSetOwnedItem::owned(TAG_TRACK_NAME, encode_utf16_be(n)));
}
out.push(LocalSetOwnedItem::fixed(TAG_SEQUENCE, self.sequence));
{
let mut buf = [0u8; RATIONAL_LEN];
self.event_edit_rate
.serialize_into(&mut buf)
.expect("fixed-size buffer");
out.push(LocalSetOwnedItem::owned(TAG_EVENT_EDIT_RATE, buf.to_vec()));
}
if let Some(o) = self.event_origin {
out.push(LocalSetOwnedItem::fixed(TAG_EVENT_ORIGIN, o.to_be_bytes()));
}
out
}
}
impl Serialize for EventTrack {
type Error = Error;
fn serialized_len(&self) -> usize {
let (key, items) = finish_owned_set(
StructuralSetKind::EventTrackDm,
self.owned_items(),
&self.dark,
);
owned_set_serialized_len(key, &items)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let (key, items) = finish_owned_set(
StructuralSetKind::EventTrackDm,
self.owned_items(),
&self.dark,
);
serialize_owned_set(key, &items, buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StaticTrack {
pub interchange: InterchangeObjectFields,
pub track_id: u32,
pub track_number: u32,
pub track_name: Option<String>,
pub sequence: StrongRef,
pub dark: Vec<(u16, Vec<u8>)>,
}
impl<'a> Parse<'a> for StaticTrack {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let set = LocalSet::parse(bytes)?;
if set.kind() != StructuralSetKind::StaticTrackDm {
return Err(Error::KeyPrefixMismatch {
what: "Static Track (Table 17)",
});
}
let items = &set.items;
let interchange = InterchangeObjectFields::decode(items, "Static Track")?;
let track_id = u32::from_be_bytes(get_required_fixed::<4>(
items,
TAG_TRACK_ID,
"Track ID",
"Static Track",
)?);
let track_number = u32::from_be_bytes(get_required_fixed::<4>(
items,
TAG_TRACK_NUMBER,
"Track Number",
"Static Track",
)?);
let track_name = get_optional_raw(items, TAG_TRACK_NAME)
.map(decode_utf16_be)
.transpose()
.map_err(|_| Error::InvalidUtf16 {
tag: TAG_TRACK_NAME,
name: "Track Name",
})?;
let sequence = get_required_fixed::<16>(items, TAG_SEQUENCE, "Sequence", "Static Track")?;
let dark = collect_dark(items, &STATIC_KNOWN_TAGS);
Ok(StaticTrack {
interchange,
track_id,
track_number,
track_name,
sequence,
dark,
})
}
}
impl StaticTrack {
fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
let mut out = Vec::new();
self.interchange.encode_into(&mut out);
out.push(LocalSetOwnedItem::fixed(
TAG_TRACK_ID,
self.track_id.to_be_bytes(),
));
out.push(LocalSetOwnedItem::fixed(
TAG_TRACK_NUMBER,
self.track_number.to_be_bytes(),
));
if let Some(n) = &self.track_name {
out.push(LocalSetOwnedItem::owned(TAG_TRACK_NAME, encode_utf16_be(n)));
}
out.push(LocalSetOwnedItem::fixed(TAG_SEQUENCE, self.sequence));
out
}
}
impl Serialize for StaticTrack {
type Error = Error;
fn serialized_len(&self) -> usize {
let (key, items) = finish_owned_set(
StructuralSetKind::StaticTrackDm,
self.owned_items(),
&self.dark,
);
owned_set_serialized_len(key, &items)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let (key, items) = finish_owned_set(
StructuralSetKind::StaticTrackDm,
self.owned_items(),
&self.dark,
);
serialize_owned_set(key, &items, buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_timeline_track() -> TimelineTrack {
TimelineTrack {
interchange: InterchangeObjectFields {
instance_uid: [0x01; 16],
generation_uid: None,
object_class: None,
},
track_id: 1,
track_number: 0x15010100,
track_name: Some(String::from("Video")),
sequence: [0x02; 16],
edit_rate: Rational {
numerator: 25,
denominator: 1,
},
origin: 0,
dark: Vec::new(),
}
}
fn sample_event_track() -> EventTrack {
EventTrack {
interchange: InterchangeObjectFields {
instance_uid: [0x10; 16],
generation_uid: None,
object_class: None,
},
track_id: 3,
track_number: 0,
track_name: None,
sequence: [0x20; 16],
event_edit_rate: Rational {
numerator: 25,
denominator: 1,
},
event_origin: Some(100),
dark: Vec::new(),
}
}
fn sample_static_track() -> StaticTrack {
StaticTrack {
interchange: InterchangeObjectFields {
instance_uid: [0x30; 16],
generation_uid: None,
object_class: None,
},
track_id: 4,
track_number: 0,
track_name: None,
sequence: [0x40; 16],
dark: Vec::new(),
}
}
#[test]
fn timeline_track_round_trip() {
let tt = sample_timeline_track();
let bytes = tt.to_bytes();
let parsed = TimelineTrack::parse(&bytes).unwrap();
assert_eq!(parsed, tt);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn timeline_track_no_name_round_trip() {
let mut tt = sample_timeline_track();
tt.track_name = None;
let bytes = tt.to_bytes();
let parsed = TimelineTrack::parse(&bytes).unwrap();
assert_eq!(parsed.track_name, None);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn timeline_track_dark_preserved() {
let mut tt = sample_timeline_track();
tt.dark = alloc::vec![(0x8001, alloc::vec![0xAB])];
let bytes = tt.to_bytes();
let parsed = TimelineTrack::parse(&bytes).unwrap();
assert_eq!(parsed.dark, tt.dark);
}
#[test]
fn event_track_round_trip() {
let et = sample_event_track();
let bytes = et.to_bytes();
let parsed = EventTrack::parse(&bytes).unwrap();
assert_eq!(parsed, et);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn event_track_no_origin_round_trip() {
let mut et = sample_event_track();
et.event_origin = None;
let bytes = et.to_bytes();
let parsed = EventTrack::parse(&bytes).unwrap();
assert_eq!(parsed.event_origin, None);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn static_track_round_trip() {
let st = sample_static_track();
let bytes = st.to_bytes();
let parsed = StaticTrack::parse(&bytes).unwrap();
assert_eq!(parsed, st);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn wrong_kind_rejected_timeline() {
let st = sample_static_track();
let bytes = st.to_bytes();
assert!(matches!(
TimelineTrack::parse(&bytes),
Err(Error::KeyPrefixMismatch { .. })
));
}
#[test]
fn wrong_kind_rejected_event() {
let tt = sample_timeline_track();
let bytes = tt.to_bytes();
assert!(matches!(
EventTrack::parse(&bytes),
Err(Error::KeyPrefixMismatch { .. })
));
}
#[test]
fn wrong_kind_rejected_static() {
let et = sample_event_track();
let bytes = et.to_bytes();
assert!(matches!(
StaticTrack::parse(&bytes),
Err(Error::KeyPrefixMismatch { .. })
));
}
#[test]
fn mutation_changes_serialized_bytes() {
let mut tt = sample_timeline_track();
let before = tt.to_bytes();
tt.origin = 42;
let after = tt.to_bytes();
assert_ne!(before, after);
assert_eq!(TimelineTrack::parse(&after).unwrap().origin, 42);
}
}