extern crate alloc;
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_required_fixed, owned_set_serialized_len, serialize_owned_set,
};
use crate::types::UlBytes;
pub const TAG_DATA_DEFINITION: u16 = 0x0201;
pub const TAG_DURATION: u16 = 0x0202;
pub const TAG_START_TIMECODE: u16 = 0x1501;
pub const TAG_ROUNDED_TIMECODE_BASE: u16 = 0x1502;
pub const TAG_DROP_FRAME: u16 = 0x1503;
const KNOWN_TAGS: [u16; 8] = [
crate::sets::TAG_INSTANCE_UID,
crate::sets::TAG_GENERATION_UID,
crate::sets::TAG_OBJECT_CLASS,
TAG_DATA_DEFINITION,
TAG_DURATION,
TAG_START_TIMECODE,
TAG_ROUNDED_TIMECODE_BASE,
TAG_DROP_FRAME,
];
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimecodeComponent {
pub interchange: InterchangeObjectFields,
pub data_definition: UlBytes,
pub duration: Option<i64>,
pub start_timecode: i64,
pub rounded_timecode_base: u16,
pub drop_frame: bool,
pub dark: Vec<(u16, Vec<u8>)>,
}
impl<'a> Parse<'a> for TimecodeComponent {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let set = LocalSet::parse(bytes)?;
if set.kind() != StructuralSetKind::TimecodeComponent {
return Err(Error::KeyPrefixMismatch {
what: "Timecode Component (Table 17)",
});
}
let items = &set.items;
let interchange = InterchangeObjectFields::decode(items, "Timecode Component")?;
let data_definition = get_required_fixed::<16>(
items,
TAG_DATA_DEFINITION,
"Data Definition",
"Timecode Component",
)?;
let duration =
get_optional_fixed::<8>(items, TAG_DURATION, "Duration")?.map(i64::from_be_bytes);
let start_timecode = i64::from_be_bytes(get_required_fixed::<8>(
items,
TAG_START_TIMECODE,
"Start Timecode",
"Timecode Component",
)?);
let rounded_timecode_base = u16::from_be_bytes(get_required_fixed::<2>(
items,
TAG_ROUNDED_TIMECODE_BASE,
"Rounded Timecode Base",
"Timecode Component",
)?);
let drop_frame =
get_required_fixed::<1>(items, TAG_DROP_FRAME, "Drop Frame", "Timecode Component")?[0]
!= 0;
let dark = collect_dark(items, &KNOWN_TAGS);
Ok(TimecodeComponent {
interchange,
data_definition,
duration,
start_timecode,
rounded_timecode_base,
drop_frame,
dark,
})
}
}
impl TimecodeComponent {
fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
let mut out = Vec::new();
self.interchange.encode_into(&mut out);
out.push(LocalSetOwnedItem::fixed(
TAG_DATA_DEFINITION,
self.data_definition,
));
if let Some(d) = self.duration {
out.push(LocalSetOwnedItem::fixed(TAG_DURATION, d.to_be_bytes()));
}
out.push(LocalSetOwnedItem::fixed(
TAG_START_TIMECODE,
self.start_timecode.to_be_bytes(),
));
out.push(LocalSetOwnedItem::fixed(
TAG_ROUNDED_TIMECODE_BASE,
self.rounded_timecode_base.to_be_bytes(),
));
out.push(LocalSetOwnedItem::fixed(
TAG_DROP_FRAME,
[u8::from(self.drop_frame)],
));
out
}
}
impl Serialize for TimecodeComponent {
type Error = Error;
fn serialized_len(&self) -> usize {
let (key, items) = finish_owned_set(
StructuralSetKind::TimecodeComponent,
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::TimecodeComponent,
self.owned_items(),
&self.dark,
);
serialize_owned_set(key, &items, buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
const TIMECODE_DD: UlBytes = [
0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00,
0x00,
];
fn sample() -> TimecodeComponent {
TimecodeComponent {
interchange: InterchangeObjectFields {
instance_uid: [0x01; 16],
generation_uid: None,
object_class: None,
},
data_definition: TIMECODE_DD,
duration: Some(250),
start_timecode: 90000,
rounded_timecode_base: 25,
drop_frame: false,
dark: Vec::new(),
}
}
#[test]
fn round_trip() {
let tc = sample();
let bytes = tc.to_bytes();
let parsed = TimecodeComponent::parse(&bytes).unwrap();
assert_eq!(parsed, tc);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn drop_frame_true_round_trips() {
let mut tc = sample();
tc.drop_frame = true;
tc.rounded_timecode_base = 30;
let bytes = tc.to_bytes();
let parsed = TimecodeComponent::parse(&bytes).unwrap();
assert!(parsed.drop_frame);
assert_eq!(parsed.rounded_timecode_base, 30);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn no_duration_round_trip() {
let mut tc = sample();
tc.duration = None;
let bytes = tc.to_bytes();
let parsed = TimecodeComponent::parse(&bytes).unwrap();
assert_eq!(parsed.duration, None);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn dark_preserved() {
let mut tc = sample();
tc.dark = alloc::vec![(0x9004, alloc::vec![0xCA, 0xFE])];
let bytes = tc.to_bytes();
let parsed = TimecodeComponent::parse(&bytes).unwrap();
assert_eq!(parsed.dark, tc.dark);
}
#[test]
fn wrong_kind_rejected() {
let key = LocalSet::build_key(
StructuralSetKind::SourceClip,
crate::local_set::ItemLengthMode::TwoByte,
);
let set = LocalSet {
key,
items: Vec::new(),
};
let bytes = set.to_bytes();
assert!(matches!(
TimecodeComponent::parse(&bytes),
Err(Error::KeyPrefixMismatch { .. })
));
}
#[test]
fn mutation_changes_serialized_bytes() {
let mut tc = sample();
let before = tc.to_bytes();
tc.start_timecode = 1800000;
let after = tc.to_bytes();
assert_ne!(before, after);
assert_eq!(
TimecodeComponent::parse(&after).unwrap().start_timecode,
1800000
);
}
}