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::{PackageId, UlBytes};
22
23pub const TAG_DATA_DEFINITION: u16 = 0x0201;
27pub const TAG_DURATION: u16 = 0x0202;
29
30pub const TAG_SOURCE_PACKAGE_ID: u16 = 0x1101;
34pub const TAG_SOURCE_TRACK_ID: u16 = 0x1102;
36pub const TAG_START_POSITION: u16 = 0x1201;
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_SOURCE_PACKAGE_ID,
46 TAG_SOURCE_TRACK_ID,
47 TAG_START_POSITION,
48];
49
50#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct SourceClip {
56 pub interchange: InterchangeObjectFields,
58 pub data_definition: UlBytes,
60 pub duration: Option<i64>,
62 pub start_position: i64,
65 pub source_package_id: PackageId,
68 pub source_track_id: u32,
71 pub dark: Vec<(u16, Vec<u8>)>,
73}
74
75impl<'a> Parse<'a> for SourceClip {
76 type Error = Error;
77
78 fn parse(bytes: &'a [u8]) -> Result<Self> {
79 let set = LocalSet::parse(bytes)?;
80 if set.kind() != StructuralSetKind::SourceClip {
81 return Err(Error::KeyPrefixMismatch {
82 what: "Source Clip (Table 17)",
83 });
84 }
85 let items = &set.items;
86 let interchange = InterchangeObjectFields::decode(items, "Source Clip")?;
87 let data_definition =
88 get_required_fixed::<16>(items, TAG_DATA_DEFINITION, "Data Definition", "Source Clip")?;
89 let duration =
90 get_optional_fixed::<8>(items, TAG_DURATION, "Duration")?.map(i64::from_be_bytes);
91 let start_position = i64::from_be_bytes(get_required_fixed::<8>(
92 items,
93 TAG_START_POSITION,
94 "Start Position",
95 "Source Clip",
96 )?);
97 let source_package_id = PackageId(get_required_fixed::<32>(
98 items,
99 TAG_SOURCE_PACKAGE_ID,
100 "Source Package ID",
101 "Source Clip",
102 )?);
103 let source_track_id = u32::from_be_bytes(get_required_fixed::<4>(
104 items,
105 TAG_SOURCE_TRACK_ID,
106 "Source Track ID",
107 "Source Clip",
108 )?);
109 let dark = collect_dark(items, &KNOWN_TAGS);
110
111 Ok(SourceClip {
112 interchange,
113 data_definition,
114 duration,
115 start_position,
116 source_package_id,
117 source_track_id,
118 dark,
119 })
120 }
121}
122
123impl SourceClip {
124 fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
125 let mut out = Vec::new();
126 self.interchange.encode_into(&mut out);
127 out.push(LocalSetOwnedItem::fixed(
128 TAG_DATA_DEFINITION,
129 self.data_definition,
130 ));
131 if let Some(d) = self.duration {
132 out.push(LocalSetOwnedItem::fixed(TAG_DURATION, d.to_be_bytes()));
133 }
134 out.push(LocalSetOwnedItem::fixed(
135 TAG_START_POSITION,
136 self.start_position.to_be_bytes(),
137 ));
138 out.push(LocalSetOwnedItem::fixed(
139 TAG_SOURCE_PACKAGE_ID,
140 self.source_package_id.0,
141 ));
142 out.push(LocalSetOwnedItem::fixed(
143 TAG_SOURCE_TRACK_ID,
144 self.source_track_id.to_be_bytes(),
145 ));
146 out
147 }
148}
149
150impl Serialize for SourceClip {
151 type Error = Error;
152
153 fn serialized_len(&self) -> usize {
154 let (key, items) = finish_owned_set(
155 StructuralSetKind::SourceClip,
156 self.owned_items(),
157 &self.dark,
158 );
159 owned_set_serialized_len(key, &items)
160 }
161
162 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
163 let (key, items) = finish_owned_set(
164 StructuralSetKind::SourceClip,
165 self.owned_items(),
166 &self.dark,
167 );
168 serialize_owned_set(key, &items, buf)
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 const PICTURE_DD: UlBytes = [
178 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00,
179 0x00,
180 ];
181
182 fn sample() -> SourceClip {
183 SourceClip {
184 interchange: InterchangeObjectFields {
185 instance_uid: [0x01; 16],
186 generation_uid: None,
187 object_class: None,
188 },
189 data_definition: PICTURE_DD,
190 duration: Some(250),
191 start_position: 0,
192 source_package_id: PackageId([0x02; 32]),
193 source_track_id: 1,
194 dark: Vec::new(),
195 }
196 }
197
198 #[test]
199 fn round_trip() {
200 let sc = sample();
201 let bytes = sc.to_bytes();
202 let parsed = SourceClip::parse(&bytes).unwrap();
203 assert_eq!(parsed, sc);
204 assert_eq!(parsed.to_bytes(), bytes);
205 }
206
207 #[test]
208 fn no_duration_round_trip() {
209 let mut sc = sample();
210 sc.duration = None;
211 let bytes = sc.to_bytes();
212 let parsed = SourceClip::parse(&bytes).unwrap();
213 assert_eq!(parsed.duration, None);
214 assert_eq!(parsed.to_bytes(), bytes);
215 }
216
217 #[test]
218 fn null_source_terminates_chain() {
219 let mut sc = sample();
220 sc.source_package_id = PackageId::NULL;
221 sc.source_track_id = 0;
222 let bytes = sc.to_bytes();
223 let parsed = SourceClip::parse(&bytes).unwrap();
224 assert!(parsed.source_package_id.is_null());
225 }
226
227 #[test]
228 fn dark_preserved() {
229 let mut sc = sample();
230 sc.dark = alloc::vec![(0x9003, alloc::vec![0xDE, 0xAD])];
231 let bytes = sc.to_bytes();
232 let parsed = SourceClip::parse(&bytes).unwrap();
233 assert_eq!(parsed.dark, sc.dark);
234 }
235
236 #[test]
237 fn wrong_kind_rejected() {
238 let key = LocalSet::build_key(
239 StructuralSetKind::Sequence,
240 crate::local_set::ItemLengthMode::TwoByte,
241 );
242 let set = LocalSet {
243 key,
244 items: Vec::new(),
245 };
246 let bytes = set.to_bytes();
247 assert!(matches!(
248 SourceClip::parse(&bytes),
249 Err(Error::KeyPrefixMismatch { .. })
250 ));
251 }
252
253 #[test]
254 fn mutation_changes_serialized_bytes() {
255 let mut sc = sample();
256 let before = sc.to_bytes();
257 sc.start_position = 100;
258 let after = sc.to_bytes();
259 assert_ne!(before, after);
260 assert_eq!(SourceClip::parse(&after).unwrap().start_position, 100);
261 }
262}