1use super::descriptor_body;
9use crate::descriptors::metadata_format::MetadataFormat;
10use crate::descriptors::mpeg_carriage_flags::MpegCarriageFlags;
11use crate::error::{Error, Result};
12use broadcast_common::{Parse, Serialize};
13
14pub const TAG: u8 = 0x25;
16const HEADER_LEN: usize = 2;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
22pub struct MetadataPointerDescriptor<'a> {
23 pub metadata_application_format: u16,
25 pub metadata_application_format_identifier: Option<u32>,
27 pub metadata_format: MetadataFormat,
29 pub metadata_format_identifier: Option<u32>,
31 pub metadata_service_id: u8,
33 pub metadata_locator_record_flag: bool,
35 pub mpeg_carriage_flags: MpegCarriageFlags,
37 #[cfg_attr(feature = "serde", serde(borrow))]
39 pub metadata_locator_record: Option<&'a [u8]>,
40 pub program_number: Option<u16>,
42 pub transport_stream_location: Option<u16>,
44 pub transport_stream_id: Option<u16>,
46 #[cfg_attr(feature = "serde", serde(borrow))]
48 pub private_data: &'a [u8],
49}
50
51impl<'a> Parse<'a> for MetadataPointerDescriptor<'a> {
52 type Error = crate::error::Error;
53
54 fn parse(bytes: &'a [u8]) -> Result<Self> {
55 let body = descriptor_body(
56 bytes,
57 TAG,
58 "MetadataPointerDescriptor",
59 "unexpected tag for metadata_pointer_descriptor",
60 )?;
61
62 if body.len() < 5 {
64 return Err(Error::InvalidDescriptor {
65 tag: TAG,
66 reason: "metadata_pointer_descriptor too short (< 5 body bytes)",
67 });
68 }
69
70 let metadata_application_format = u16::from_be_bytes([body[0], body[1]]);
71 let mut pos = 2;
72
73 let metadata_application_format_identifier = if metadata_application_format == 0xFFFF {
74 if body.len() < pos + 4 {
75 return Err(Error::InvalidDescriptor {
76 tag: TAG,
77 reason: "metadata_pointer_descriptor too short for metadata_application_format_identifier",
78 });
79 }
80 let id = u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
81 pos += 4;
82 Some(id)
83 } else {
84 None
85 };
86
87 if body.len() < pos + 1 {
88 return Err(Error::InvalidDescriptor {
89 tag: TAG,
90 reason: "metadata_pointer_descriptor too short for metadata_format",
91 });
92 }
93 let metadata_format = MetadataFormat::from_u8(body[pos]);
94 pos += 1;
95
96 let metadata_format_identifier = if body[pos - 1] == 0xFF {
97 if body.len() < pos + 4 {
98 return Err(Error::InvalidDescriptor {
99 tag: TAG,
100 reason: "metadata_pointer_descriptor too short for metadata_format_identifier",
101 });
102 }
103 let id = u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
104 pos += 4;
105 Some(id)
106 } else {
107 None
108 };
109
110 if body.len() < pos + 2 {
111 return Err(Error::InvalidDescriptor {
112 tag: TAG,
113 reason: "metadata_pointer_descriptor too short for service_id + flags",
114 });
115 }
116 let metadata_service_id = body[pos];
117 pos += 1;
118
119 let flags = body[pos];
120 let metadata_locator_record_flag = (flags & 0x80) != 0;
121 let mpeg_carriage_flags = MpegCarriageFlags::from_u8((flags >> 5) & 0x03);
122 pos += 1;
123
124 let (metadata_locator_record, mut pos) = if metadata_locator_record_flag {
125 if body.len() < pos + 1 {
126 return Err(Error::InvalidDescriptor {
127 tag: TAG,
128 reason: "metadata_pointer_descriptor too short for metadata_locator_record_length",
129 });
130 }
131 let rec_len = body[pos] as usize;
132 pos += 1;
133 if body.len() < pos + rec_len {
134 return Err(Error::InvalidDescriptor {
135 tag: TAG,
136 reason: "metadata_pointer_descriptor too short for metadata_locator_record",
137 });
138 }
139 let rec = &body[pos..pos + rec_len];
140 pos += rec_len;
141 (Some(rec), pos)
142 } else {
143 (None, pos)
144 };
145
146 let carriage_val = mpeg_carriage_flags.to_u8();
147
148 let program_number = if carriage_val <= 2 {
149 if body.len() < pos + 2 {
150 return Err(Error::InvalidDescriptor {
151 tag: TAG,
152 reason: "metadata_pointer_descriptor too short for program_number",
153 });
154 }
155 let pn = u16::from_be_bytes([body[pos], body[pos + 1]]);
156 pos += 2;
157 Some(pn)
158 } else {
159 None
160 };
161
162 let (transport_stream_location, transport_stream_id, pos) = if carriage_val == 1 {
163 if body.len() < pos + 4 {
164 return Err(Error::InvalidDescriptor {
165 tag: TAG,
166 reason: "metadata_pointer_descriptor too short for transport_stream_location+id",
167 });
168 }
169 let tsl = u16::from_be_bytes([body[pos], body[pos + 1]]);
170 let tsi = u16::from_be_bytes([body[pos + 2], body[pos + 3]]);
171 pos += 4;
172 (Some(tsl), Some(tsi), pos)
173 } else {
174 (None, None, pos)
175 };
176
177 let private_data = &body[pos..];
178
179 Ok(Self {
180 metadata_application_format,
181 metadata_application_format_identifier,
182 metadata_format,
183 metadata_format_identifier,
184 metadata_service_id,
185 metadata_locator_record_flag,
186 mpeg_carriage_flags,
187 metadata_locator_record,
188 program_number,
189 transport_stream_location,
190 transport_stream_id,
191 private_data,
192 })
193 }
194}
195
196impl Serialize for MetadataPointerDescriptor<'_> {
197 type Error = crate::error::Error;
198
199 fn serialized_len(&self) -> usize {
200 let mut len: usize = HEADER_LEN + 5; if self.metadata_application_format_identifier.is_some() {
202 len += 4;
203 }
204 if self.metadata_format_identifier.is_some() {
205 len += 4;
206 }
207 if let Some(rec) = self.metadata_locator_record {
208 len += 1 + rec.len();
209 }
210 if self.program_number.is_some() {
211 len += 2;
212 }
213 if self.transport_stream_location.is_some() {
214 len += 4;
215 }
216 len += self.private_data.len();
217 len
218 }
219
220 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
221 let len = self.serialized_len();
222 if buf.len() < len {
223 return Err(Error::OutputBufferTooSmall {
224 need: len,
225 have: buf.len(),
226 });
227 }
228 buf[0] = TAG;
229 buf[1] = (len - HEADER_LEN) as u8;
230
231 buf[HEADER_LEN] = (self.metadata_application_format >> 8) as u8;
232 buf[HEADER_LEN + 1] = self.metadata_application_format as u8;
233 let mut pos = HEADER_LEN + 2;
234
235 if let Some(id) = self.metadata_application_format_identifier {
236 buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
237 pos += 4;
238 }
239
240 buf[pos] = self.metadata_format.to_u8();
241 pos += 1;
242
243 if let Some(id) = self.metadata_format_identifier {
244 buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
245 pos += 4;
246 }
247
248 buf[pos] = self.metadata_service_id;
249 pos += 1;
250
251 let mut flags = (self.mpeg_carriage_flags.to_u8() & 0x03) << 5;
252 if self.metadata_locator_record_flag {
253 flags |= 0x80;
254 }
255 buf[pos] = flags;
256 pos += 1;
257
258 if let Some(rec) = self.metadata_locator_record {
259 buf[pos] = rec.len() as u8;
260 pos += 1;
261 buf[pos..pos + rec.len()].copy_from_slice(rec);
262 pos += rec.len();
263 }
264
265 if let Some(pn) = self.program_number {
266 buf[pos..pos + 2].copy_from_slice(&pn.to_be_bytes());
267 pos += 2;
268 }
269 if let Some(tsl) = self.transport_stream_location {
270 buf[pos..pos + 2].copy_from_slice(&tsl.to_be_bytes());
271 buf[pos + 2..pos + 4]
272 .copy_from_slice(&self.transport_stream_id.unwrap_or(0).to_be_bytes());
273 pos += 4;
274 }
275
276 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
277 Ok(len)
278 }
279}
280
281impl<'a> crate::traits::DescriptorDef<'a> for MetadataPointerDescriptor<'a> {
282 const TAG: u8 = TAG;
283 const NAME: &'static str = "METADATA_POINTER";
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 fn serialize_round_trip(d: &MetadataPointerDescriptor<'_>) {
291 let mut buf = vec![0u8; d.serialized_len()];
292 let written = d.serialize_into(&mut buf).unwrap();
293 assert_eq!(written, d.serialized_len());
294 let reparsed = MetadataPointerDescriptor::parse(&buf).unwrap();
295 assert_eq!(*d, reparsed, "round-trip mismatch");
296 }
297
298 #[test]
299 fn round_trip_carriage_0_same_ts() {
300 let d = MetadataPointerDescriptor {
301 metadata_application_format: 0x0010,
302 metadata_application_format_identifier: None,
303 metadata_format: MetadataFormat::TeM,
304 metadata_format_identifier: None,
305 metadata_service_id: 5,
306 metadata_locator_record_flag: false,
307 mpeg_carriage_flags: MpegCarriageFlags::SameTs,
308 metadata_locator_record: None,
309 program_number: Some(0x0101),
310 transport_stream_location: None,
311 transport_stream_id: None,
312 private_data: &[],
313 };
314 serialize_round_trip(&d);
315 }
316
317 #[test]
318 fn round_trip_carriage_1_different_ts() {
319 let d = MetadataPointerDescriptor {
320 metadata_application_format: 0x0011,
321 metadata_application_format_identifier: None,
322 metadata_format: MetadataFormat::BiM,
323 metadata_format_identifier: None,
324 metadata_service_id: 7,
325 metadata_locator_record_flag: false,
326 mpeg_carriage_flags: MpegCarriageFlags::DifferentTs,
327 metadata_locator_record: None,
328 program_number: Some(0x0202),
329 transport_stream_location: Some(0x0303),
330 transport_stream_id: Some(0x0404),
331 private_data: &[],
332 };
333 serialize_round_trip(&d);
334 }
335
336 #[test]
337 fn round_trip_carriage_2_program_stream() {
338 let d = MetadataPointerDescriptor {
339 metadata_application_format: 0x0100,
340 metadata_application_format_identifier: None,
341 metadata_format: MetadataFormat::AppFormat,
342 metadata_format_identifier: None,
343 metadata_service_id: 3,
344 metadata_locator_record_flag: false,
345 mpeg_carriage_flags: MpegCarriageFlags::ProgramStream,
346 metadata_locator_record: None,
347 program_number: Some(0x0505),
348 transport_stream_location: None,
349 transport_stream_id: None,
350 private_data: &[],
351 };
352 serialize_round_trip(&d);
353 }
354
355 #[test]
356 fn round_trip_carriage_3_none() {
357 let d = MetadataPointerDescriptor {
358 metadata_application_format: 0x0200,
359 metadata_application_format_identifier: None,
360 metadata_format: MetadataFormat::Private(0x80),
361 metadata_format_identifier: None,
362 metadata_service_id: 1,
363 metadata_locator_record_flag: true,
364 mpeg_carriage_flags: MpegCarriageFlags::None,
365 metadata_locator_record: Some(&[0xAA, 0xBB]),
366 program_number: None,
367 transport_stream_location: None,
368 transport_stream_id: None,
369 private_data: &[0xCC],
370 };
371 serialize_round_trip(&d);
372 }
373
374 #[test]
375 fn round_trip_with_ffff_and_ff_identifiers() {
376 let d = MetadataPointerDescriptor {
377 metadata_application_format: 0xFFFF,
378 metadata_application_format_identifier: Some(0x12345678),
379 metadata_format: MetadataFormat::Identifier,
380 metadata_format_identifier: Some(0x9ABCDEF0),
381 metadata_service_id: 9,
382 metadata_locator_record_flag: false,
383 mpeg_carriage_flags: MpegCarriageFlags::SameTs,
384 metadata_locator_record: None,
385 program_number: Some(42),
386 transport_stream_location: None,
387 transport_stream_id: None,
388 private_data: &[0xFF],
389 };
390 serialize_round_trip(&d);
391 }
392
393 #[test]
394 fn round_trip_all_empty_private() {
395 let d = MetadataPointerDescriptor {
396 metadata_application_format: 0x0010,
397 metadata_application_format_identifier: None,
398 metadata_format: MetadataFormat::Reserved0(0x05),
399 metadata_format_identifier: None,
400 metadata_service_id: 0,
401 metadata_locator_record_flag: false,
402 mpeg_carriage_flags: MpegCarriageFlags::None,
403 metadata_locator_record: None,
404 program_number: None,
405 transport_stream_location: None,
406 transport_stream_id: None,
407 private_data: &[],
408 };
409 serialize_round_trip(&d);
410 }
411
412 #[test]
413 fn parse_rejects_wrong_tag() {
414 let err = MetadataPointerDescriptor::parse(&[0x02, 5, 0, 0, 0, 0, 0]).unwrap_err();
415 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
416 }
417
418 #[test]
419 fn parse_rejects_too_short() {
420 let err = MetadataPointerDescriptor::parse(&[TAG, 0]).unwrap_err();
421 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
422 }
423}