Skip to main content

dvb_si/descriptors/
metadata_pointer.rs

1//! Metadata Pointer Descriptor — ISO/IEC 13818-1 §2.6.58, Table 2-86 (tag 0x25).
2//!
3//! Points to a metadata service that carries metadata for the associated
4//! program or elementary stream. The program_number block is conditional on
5//! `MPEG_carriage_flags <= 2`; the transport_stream_location + transport_stream_id
6//! block only when `MPEG_carriage_flags == 1`.
7
8use 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
14/// Descriptor tag for metadata_pointer_descriptor.
15pub const TAG: u8 = 0x25;
16const HEADER_LEN: usize = 2;
17
18/// Metadata Pointer Descriptor.
19#[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    /// Metadata application format (u16; Table 2-84).
24    pub metadata_application_format: u16,
25    /// Metadata application format identifier — present when `metadata_application_format == 0xFFFF`.
26    pub metadata_application_format_identifier: Option<u32>,
27    /// Metadata format (Table 2-87).
28    pub metadata_format: MetadataFormat,
29    /// Metadata format identifier — present when `metadata_format == 0xFF`.
30    pub metadata_format_identifier: Option<u32>,
31    /// Metadata service ID.
32    pub metadata_service_id: u8,
33    /// Metadata locator record present flag.
34    pub metadata_locator_record_flag: bool,
35    /// MPEG carriage flags (Table 2-88).
36    pub mpeg_carriage_flags: MpegCarriageFlags,
37    /// Metadata locator record — present when `metadata_locator_record_flag` is true.
38    #[cfg_attr(feature = "serde", serde(borrow))]
39    pub metadata_locator_record: Option<&'a [u8]>,
40    /// Program number — present when `MPEG_carriage_flags <= 2`.
41    pub program_number: Option<u16>,
42    /// Transport stream location — present when `MPEG_carriage_flags == 1`.
43    pub transport_stream_location: Option<u16>,
44    /// Transport stream ID — present when `MPEG_carriage_flags == 1`.
45    pub transport_stream_id: Option<u16>,
46    /// Trailing private data bytes.
47    #[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        // Minimum: metadata_application_format(2) + metadata_format(1) + metadata_service_id(1) + flags(1) = 5
63        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; // metadata_application_format(2) + metadata_format(1) + service_id(1) + flags(1)
203        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}