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 broadcast_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: "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; // metadata_application_format(2) + metadata_format(1) + service_id(1) + flags(1)
201        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}