1use super::descriptor_body;
9use crate::descriptors::metadata_format::MetadataFormat;
10use crate::descriptors::mpeg_carriage_flags::MpegCarriageFlags;
11use crate::error::{Error, Result};
12use dvb_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:
129 "metadata_pointer_descriptor too short for metadata_locator_record_length",
130 });
131 }
132 let rec_len = body[pos] as usize;
133 pos += 1;
134 if body.len() < pos + rec_len {
135 return Err(Error::InvalidDescriptor {
136 tag: TAG,
137 reason: "metadata_pointer_descriptor too short for metadata_locator_record",
138 });
139 }
140 let rec = &body[pos..pos + rec_len];
141 pos += rec_len;
142 (Some(rec), pos)
143 } else {
144 (None, pos)
145 };
146
147 let carriage_val = mpeg_carriage_flags.to_u8();
148
149 let program_number = if carriage_val <= 2 {
150 if body.len() < pos + 2 {
151 return Err(Error::InvalidDescriptor {
152 tag: TAG,
153 reason: "metadata_pointer_descriptor too short for program_number",
154 });
155 }
156 let pn = u16::from_be_bytes([body[pos], body[pos + 1]]);
157 pos += 2;
158 Some(pn)
159 } else {
160 None
161 };
162
163 let (transport_stream_location, transport_stream_id, pos) = if carriage_val == 1 {
164 if body.len() < pos + 4 {
165 return Err(Error::InvalidDescriptor {
166 tag: TAG,
167 reason:
168 "metadata_pointer_descriptor too short for transport_stream_location+id",
169 });
170 }
171 let tsl = u16::from_be_bytes([body[pos], body[pos + 1]]);
172 let tsi = u16::from_be_bytes([body[pos + 2], body[pos + 3]]);
173 pos += 4;
174 (Some(tsl), Some(tsi), pos)
175 } else {
176 (None, None, pos)
177 };
178
179 let private_data = &body[pos..];
180
181 Ok(Self {
182 metadata_application_format,
183 metadata_application_format_identifier,
184 metadata_format,
185 metadata_format_identifier,
186 metadata_service_id,
187 metadata_locator_record_flag,
188 mpeg_carriage_flags,
189 metadata_locator_record,
190 program_number,
191 transport_stream_location,
192 transport_stream_id,
193 private_data,
194 })
195 }
196}
197
198impl Serialize for MetadataPointerDescriptor<'_> {
199 type Error = crate::error::Error;
200
201 fn serialized_len(&self) -> usize {
202 let mut len: usize = HEADER_LEN + 5; if self.metadata_application_format_identifier.is_some() {
204 len += 4;
205 }
206 if self.metadata_format_identifier.is_some() {
207 len += 4;
208 }
209 if let Some(rec) = self.metadata_locator_record {
210 len += 1 + rec.len();
211 }
212 if self.program_number.is_some() {
213 len += 2;
214 }
215 if self.transport_stream_location.is_some() {
216 len += 4;
217 }
218 len += self.private_data.len();
219 len
220 }
221
222 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
223 let len = self.serialized_len();
224 if buf.len() < len {
225 return Err(Error::OutputBufferTooSmall {
226 need: len,
227 have: buf.len(),
228 });
229 }
230 buf[0] = TAG;
231 buf[1] = (len - HEADER_LEN) as u8;
232
233 buf[HEADER_LEN] = (self.metadata_application_format >> 8) as u8;
234 buf[HEADER_LEN + 1] = self.metadata_application_format as u8;
235 let mut pos = HEADER_LEN + 2;
236
237 if let Some(id) = self.metadata_application_format_identifier {
238 buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
239 pos += 4;
240 }
241
242 buf[pos] = self.metadata_format.to_u8();
243 pos += 1;
244
245 if let Some(id) = self.metadata_format_identifier {
246 buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
247 pos += 4;
248 }
249
250 buf[pos] = self.metadata_service_id;
251 pos += 1;
252
253 let mut flags = (self.mpeg_carriage_flags.to_u8() & 0x03) << 5;
254 if self.metadata_locator_record_flag {
255 flags |= 0x80;
256 }
257 buf[pos] = flags;
258 pos += 1;
259
260 if let Some(rec) = self.metadata_locator_record {
261 buf[pos] = rec.len() as u8;
262 pos += 1;
263 buf[pos..pos + rec.len()].copy_from_slice(rec);
264 pos += rec.len();
265 }
266
267 if let Some(pn) = self.program_number {
268 buf[pos..pos + 2].copy_from_slice(&pn.to_be_bytes());
269 pos += 2;
270 }
271 if let Some(tsl) = self.transport_stream_location {
272 buf[pos..pos + 2].copy_from_slice(&tsl.to_be_bytes());
273 buf[pos + 2..pos + 4]
274 .copy_from_slice(&self.transport_stream_id.unwrap_or(0).to_be_bytes());
275 pos += 4;
276 }
277
278 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
279 Ok(len)
280 }
281}
282
283impl<'a> crate::traits::DescriptorDef<'a> for MetadataPointerDescriptor<'a> {
284 const TAG: u8 = TAG;
285 const NAME: &'static str = "METADATA_POINTER";
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 fn serialize_round_trip(d: &MetadataPointerDescriptor<'_>) {
293 let mut buf = vec![0u8; d.serialized_len()];
294 let written = d.serialize_into(&mut buf).unwrap();
295 assert_eq!(written, d.serialized_len());
296 let reparsed = MetadataPointerDescriptor::parse(&buf).unwrap();
297 assert_eq!(*d, reparsed, "round-trip mismatch");
298 }
299
300 #[test]
301 fn round_trip_carriage_0_same_ts() {
302 let d = MetadataPointerDescriptor {
303 metadata_application_format: 0x0010,
304 metadata_application_format_identifier: None,
305 metadata_format: MetadataFormat::TeM,
306 metadata_format_identifier: None,
307 metadata_service_id: 5,
308 metadata_locator_record_flag: false,
309 mpeg_carriage_flags: MpegCarriageFlags::SameTs,
310 metadata_locator_record: None,
311 program_number: Some(0x0101),
312 transport_stream_location: None,
313 transport_stream_id: None,
314 private_data: &[],
315 };
316 serialize_round_trip(&d);
317 }
318
319 #[test]
320 fn round_trip_carriage_1_different_ts() {
321 let d = MetadataPointerDescriptor {
322 metadata_application_format: 0x0011,
323 metadata_application_format_identifier: None,
324 metadata_format: MetadataFormat::BiM,
325 metadata_format_identifier: None,
326 metadata_service_id: 7,
327 metadata_locator_record_flag: false,
328 mpeg_carriage_flags: MpegCarriageFlags::DifferentTs,
329 metadata_locator_record: None,
330 program_number: Some(0x0202),
331 transport_stream_location: Some(0x0303),
332 transport_stream_id: Some(0x0404),
333 private_data: &[],
334 };
335 serialize_round_trip(&d);
336 }
337
338 #[test]
339 fn round_trip_carriage_2_program_stream() {
340 let d = MetadataPointerDescriptor {
341 metadata_application_format: 0x0100,
342 metadata_application_format_identifier: None,
343 metadata_format: MetadataFormat::AppFormat,
344 metadata_format_identifier: None,
345 metadata_service_id: 3,
346 metadata_locator_record_flag: false,
347 mpeg_carriage_flags: MpegCarriageFlags::ProgramStream,
348 metadata_locator_record: None,
349 program_number: Some(0x0505),
350 transport_stream_location: None,
351 transport_stream_id: None,
352 private_data: &[],
353 };
354 serialize_round_trip(&d);
355 }
356
357 #[test]
358 fn round_trip_carriage_3_none() {
359 let d = MetadataPointerDescriptor {
360 metadata_application_format: 0x0200,
361 metadata_application_format_identifier: None,
362 metadata_format: MetadataFormat::Private(0x80),
363 metadata_format_identifier: None,
364 metadata_service_id: 1,
365 metadata_locator_record_flag: true,
366 mpeg_carriage_flags: MpegCarriageFlags::None,
367 metadata_locator_record: Some(&[0xAA, 0xBB]),
368 program_number: None,
369 transport_stream_location: None,
370 transport_stream_id: None,
371 private_data: &[0xCC],
372 };
373 serialize_round_trip(&d);
374 }
375
376 #[test]
377 fn round_trip_with_ffff_and_ff_identifiers() {
378 let d = MetadataPointerDescriptor {
379 metadata_application_format: 0xFFFF,
380 metadata_application_format_identifier: Some(0x12345678),
381 metadata_format: MetadataFormat::Identifier,
382 metadata_format_identifier: Some(0x9ABCDEF0),
383 metadata_service_id: 9,
384 metadata_locator_record_flag: false,
385 mpeg_carriage_flags: MpegCarriageFlags::SameTs,
386 metadata_locator_record: None,
387 program_number: Some(42),
388 transport_stream_location: None,
389 transport_stream_id: None,
390 private_data: &[0xFF],
391 };
392 serialize_round_trip(&d);
393 }
394
395 #[test]
396 fn round_trip_all_empty_private() {
397 let d = MetadataPointerDescriptor {
398 metadata_application_format: 0x0010,
399 metadata_application_format_identifier: None,
400 metadata_format: MetadataFormat::Reserved0(0x05),
401 metadata_format_identifier: None,
402 metadata_service_id: 0,
403 metadata_locator_record_flag: false,
404 mpeg_carriage_flags: MpegCarriageFlags::None,
405 metadata_locator_record: None,
406 program_number: None,
407 transport_stream_location: None,
408 transport_stream_id: None,
409 private_data: &[],
410 };
411 serialize_round_trip(&d);
412 }
413
414 #[test]
415 fn parse_rejects_wrong_tag() {
416 let err = MetadataPointerDescriptor::parse(&[0x02, 5, 0, 0, 0, 0, 0]).unwrap_err();
417 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
418 }
419
420 #[test]
421 fn parse_rejects_too_short() {
422 let err = MetadataPointerDescriptor::parse(&[TAG, 0]).unwrap_err();
423 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
424 }
425}