Skip to main content

dvb_si/descriptors/
metadata.rs

1//! Metadata Descriptor — ISO/IEC 13818-1 §2.6.60, Table 2-89 (tag 0x26).
2//!
3//! Carries metadata for a program or elementary stream. The DSM-CC block is
4//! conditional on `DSM-CC_flag`; the decoder-config block is one of several
5//! variants selected by `decoder_config_flags` (001/011/100/101|110).
6
7use super::descriptor_body;
8use crate::descriptors::decoder_config_flags::DecoderConfigFlags;
9use crate::descriptors::metadata_format::MetadataFormat;
10use crate::error::{Error, Result};
11use broadcast_common::{Parse, Serialize};
12
13/// Descriptor tag for metadata_descriptor.
14pub const TAG: u8 = 0x26;
15const HEADER_LEN: usize = 2;
16
17/// Decoder configuration carried in this descriptor (decoder_config_flags == 0b001).
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct DecoderConfigInDescriptor<'a> {
21    /// Opaque decoder config bytes.
22    #[cfg_attr(feature = "serde", serde(borrow))]
23    pub decoder_config: &'a [u8],
24}
25
26/// Decoder configuration carried in a DSM-CC carousel (decoder_config_flags == 0b011).
27#[derive(Debug, Clone, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize))]
29pub struct DecoderConfigDsmcc<'a> {
30    /// Opaque dec_config_identification_record bytes.
31    #[cfg_attr(feature = "serde", serde(borrow))]
32    pub dec_config_identification: &'a [u8],
33}
34
35/// Decoder configuration carried in another metadata service (decoder_config_flags == 0b100).
36#[derive(Debug, Clone, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize))]
38pub struct DecoderConfigOtherService {
39    /// Decoder config metadata service ID.
40    pub decoder_config_metadata_service_id: u8,
41}
42
43/// Reserved data block (decoder_config_flags == 0b101 or 0b110).
44#[derive(Debug, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize))]
46pub struct ReservedData<'a> {
47    /// Opaque reserved data bytes.
48    #[cfg_attr(feature = "serde", serde(borrow))]
49    pub data: &'a [u8],
50}
51
52/// All decoder-config variants, selected by decoder_config_flags.
53#[derive(Debug, Clone, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55#[non_exhaustive]
56pub enum DecoderConfig<'a> {
57    /// No decoder config needed (0b000).
58    None,
59    /// Carried in this descriptor (0b001).
60    InDescriptor(DecoderConfigInDescriptor<'a>),
61    /// Carried in same metadata service (0b010, no data).
62    SameService,
63    /// Carried in DSM-CC carousel (0b011).
64    DsmccCarousel(DecoderConfigDsmcc<'a>),
65    /// Carried in another metadata service (0b100).
66    OtherService(DecoderConfigOtherService),
67    /// Reserved data (0b101|0b110).
68    ReservedData(ReservedData<'a>),
69    /// Privately defined (0b111, no data).
70    Private,
71}
72
73/// Metadata Descriptor.
74#[derive(Debug, Clone, PartialEq, Eq)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize))]
76#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
77pub struct MetadataDescriptor<'a> {
78    /// Metadata application format (u16; Table 2-84).
79    pub metadata_application_format: u16,
80    /// Metadata application format identifier — present when `metadata_application_format == 0xFFFF`.
81    pub metadata_application_format_identifier: Option<u32>,
82    /// Metadata format (Table 2-87).
83    pub metadata_format: MetadataFormat,
84    /// Metadata format identifier — present when `metadata_format == 0xFF`.
85    pub metadata_format_identifier: Option<u32>,
86    /// Metadata service ID.
87    pub metadata_service_id: u8,
88    /// Decoder config flags (Table 2-90, 3-bit).
89    pub decoder_config_flags: DecoderConfigFlags,
90    /// DSM-CC service identification flag.
91    pub dsmcc_flag: bool,
92    /// DSM-CC service identification — present when `dsmcc_flag` is true.
93    #[cfg_attr(feature = "serde", serde(borrow))]
94    pub service_identification: Option<&'a [u8]>,
95    /// Decoder configuration block.
96    pub decoder_config: DecoderConfig<'a>,
97    /// Trailing private data bytes.
98    #[cfg_attr(feature = "serde", serde(borrow))]
99    pub private_data: &'a [u8],
100}
101
102impl<'a> Parse<'a> for MetadataDescriptor<'a> {
103    type Error = crate::error::Error;
104
105    fn parse(bytes: &'a [u8]) -> Result<Self> {
106        let body = descriptor_body(
107            bytes,
108            TAG,
109            "MetadataDescriptor",
110            "unexpected tag for metadata_descriptor",
111        )?;
112
113        // Minimum: metadata_application_format(2) + metadata_format(1) + metadata_service_id(1) + flags(1) = 5
114        if body.len() < 5 {
115            return Err(Error::InvalidDescriptor {
116                tag: TAG,
117                reason: "metadata_descriptor too short (< 5 body bytes)",
118            });
119        }
120
121        let metadata_application_format = u16::from_be_bytes([body[0], body[1]]);
122        let mut pos = 2;
123
124        let metadata_application_format_identifier = if metadata_application_format == 0xFFFF {
125            if body.len() < pos + 4 {
126                return Err(Error::InvalidDescriptor {
127                    tag: TAG,
128                    reason: "metadata_descriptor too short for metadata_application_format_identifier",
129                });
130            }
131            let id = u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
132            pos += 4;
133            Some(id)
134        } else {
135            None
136        };
137
138        if body.len() < pos + 1 {
139            return Err(Error::InvalidDescriptor {
140                tag: TAG,
141                reason: "metadata_descriptor too short for metadata_format",
142            });
143        }
144        let metadata_format = MetadataFormat::from_u8(body[pos]);
145        pos += 1;
146
147        let metadata_format_identifier = if body[pos - 1] == 0xFF {
148            if body.len() < pos + 4 {
149                return Err(Error::InvalidDescriptor {
150                    tag: TAG,
151                    reason: "metadata_descriptor too short for metadata_format_identifier",
152                });
153            }
154            let id = u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
155            pos += 4;
156            Some(id)
157        } else {
158            None
159        };
160
161        if body.len() < pos + 2 {
162            return Err(Error::InvalidDescriptor {
163                tag: TAG,
164                reason: "metadata_descriptor too short for service_id + flags",
165            });
166        }
167        let metadata_service_id = body[pos];
168        pos += 1;
169
170        let flags = body[pos];
171        let decoder_config_raw = (flags & 0xE0) >> 5;
172        let decoder_config_flags = DecoderConfigFlags::from_u8(decoder_config_raw);
173        let dsmcc_flag = (flags & 0x10) != 0;
174        let _reserved = flags & 0x0F;
175        pos += 1;
176
177        let (service_identification, mut pos) = if dsmcc_flag {
178            if body.len() < pos + 1 {
179                return Err(Error::InvalidDescriptor {
180                    tag: TAG,
181                    reason: "metadata_descriptor too short for service_identification_length",
182                });
183            }
184            let si_len = body[pos] as usize;
185            pos += 1;
186            if body.len() < pos + si_len {
187                return Err(Error::InvalidDescriptor {
188                    tag: TAG,
189                    reason: "metadata_descriptor too short for service_identification_record",
190                });
191            }
192            let si = &body[pos..pos + si_len];
193            pos += si_len;
194            (Some(si), pos)
195        } else {
196            (None, pos)
197        };
198
199        let decoder_config = match decoder_config_raw {
200            0 => DecoderConfig::None,
201            1 => {
202                if body.len() < pos + 1 {
203                    return Err(Error::InvalidDescriptor {
204                        tag: TAG,
205                        reason: "metadata_descriptor too short for decoder_config_length",
206                    });
207                }
208                let dc_len = body[pos] as usize;
209                pos += 1;
210                if body.len() < pos + dc_len {
211                    return Err(Error::InvalidDescriptor {
212                        tag: TAG,
213                        reason: "metadata_descriptor too short for decoder_config bytes",
214                    });
215                }
216                let dc = &body[pos..pos + dc_len];
217                pos += dc_len;
218                DecoderConfig::InDescriptor(DecoderConfigInDescriptor { decoder_config: dc })
219            }
220            2 => DecoderConfig::SameService,
221            3 => {
222                if body.len() < pos + 1 {
223                    return Err(Error::InvalidDescriptor {
224                        tag: TAG,
225                        reason: "metadata_descriptor too short for dec_config_identification_record_length",
226                    });
227                }
228                let dci_len = body[pos] as usize;
229                pos += 1;
230                if body.len() < pos + dci_len {
231                    return Err(Error::InvalidDescriptor {
232                        tag: TAG,
233                        reason: "metadata_descriptor too short for dec_config_identification_record",
234                    });
235                }
236                let dci = &body[pos..pos + dci_len];
237                pos += dci_len;
238                DecoderConfig::DsmccCarousel(DecoderConfigDsmcc {
239                    dec_config_identification: dci,
240                })
241            }
242            4 => {
243                if body.len() < pos + 1 {
244                    return Err(Error::InvalidDescriptor {
245                        tag: TAG,
246                        reason: "metadata_descriptor too short for decoder_config_metadata_service_id",
247                    });
248                }
249                let dcm = body[pos];
250                pos += 1;
251                DecoderConfig::OtherService(DecoderConfigOtherService {
252                    decoder_config_metadata_service_id: dcm,
253                })
254            }
255            5 | 6 => {
256                if body.len() < pos + 1 {
257                    return Err(Error::InvalidDescriptor {
258                        tag: TAG,
259                        reason: "metadata_descriptor too short for reserved_data_length",
260                    });
261                }
262                let rd_len = body[pos] as usize;
263                pos += 1;
264                if body.len() < pos + rd_len {
265                    return Err(Error::InvalidDescriptor {
266                        tag: TAG,
267                        reason: "metadata_descriptor too short for reserved_data",
268                    });
269                }
270                let rd = &body[pos..pos + rd_len];
271                pos += rd_len;
272                DecoderConfig::ReservedData(ReservedData { data: rd })
273            }
274            _ => DecoderConfig::Private,
275        };
276
277        let private_data = &body[pos..];
278
279        Ok(Self {
280            metadata_application_format,
281            metadata_application_format_identifier,
282            metadata_format,
283            metadata_format_identifier,
284            metadata_service_id,
285            decoder_config_flags,
286            dsmcc_flag,
287            service_identification,
288            decoder_config,
289            private_data,
290        })
291    }
292}
293
294impl Serialize for MetadataDescriptor<'_> {
295    type Error = crate::error::Error;
296
297    fn serialized_len(&self) -> usize {
298        let mut len: usize = HEADER_LEN + 5; // metadata_application_format(2) + metadata_format(1) + service_id(1) + flags(1)
299        if self.metadata_application_format_identifier.is_some() {
300            len += 4;
301        }
302        if self.metadata_format_identifier.is_some() {
303            len += 4;
304        }
305        if let Some(si) = self.service_identification {
306            len += 1 + si.len();
307        }
308        match &self.decoder_config {
309            DecoderConfig::InDescriptor(d) => len += 1 + d.decoder_config.len(),
310            DecoderConfig::DsmccCarousel(d) => len += 1 + d.dec_config_identification.len(),
311            DecoderConfig::OtherService(_) => len += 1,
312            DecoderConfig::ReservedData(d) => len += 1 + d.data.len(),
313            DecoderConfig::None | DecoderConfig::SameService | DecoderConfig::Private => {}
314        }
315        len += self.private_data.len();
316        len
317    }
318
319    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
320        let len = self.serialized_len();
321        if buf.len() < len {
322            return Err(Error::OutputBufferTooSmall {
323                need: len,
324                have: buf.len(),
325            });
326        }
327        buf[0] = TAG;
328        buf[1] = (len - HEADER_LEN) as u8;
329
330        buf[HEADER_LEN] = (self.metadata_application_format >> 8) as u8;
331        buf[HEADER_LEN + 1] = self.metadata_application_format as u8;
332        let mut pos = HEADER_LEN + 2;
333
334        if let Some(id) = self.metadata_application_format_identifier {
335            buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
336            pos += 4;
337        }
338
339        buf[pos] = self.metadata_format.to_u8();
340        pos += 1;
341
342        if let Some(id) = self.metadata_format_identifier {
343            buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
344            pos += 4;
345        }
346
347        buf[pos] = self.metadata_service_id;
348        pos += 1;
349
350        let mut flags = (self.decoder_config_flags.to_u8() & 0x07) << 5;
351        if self.dsmcc_flag {
352            flags |= 0x10;
353        }
354        buf[pos] = flags;
355        pos += 1;
356
357        if let Some(si) = self.service_identification {
358            buf[pos] = si.len() as u8;
359            pos += 1;
360            buf[pos..pos + si.len()].copy_from_slice(si);
361            pos += si.len();
362        }
363
364        match &self.decoder_config {
365            DecoderConfig::InDescriptor(d) => {
366                buf[pos] = d.decoder_config.len() as u8;
367                pos += 1;
368                buf[pos..pos + d.decoder_config.len()].copy_from_slice(d.decoder_config);
369                pos += d.decoder_config.len();
370            }
371            DecoderConfig::DsmccCarousel(d) => {
372                buf[pos] = d.dec_config_identification.len() as u8;
373                pos += 1;
374                buf[pos..pos + d.dec_config_identification.len()]
375                    .copy_from_slice(d.dec_config_identification);
376                pos += d.dec_config_identification.len();
377            }
378            DecoderConfig::OtherService(d) => {
379                buf[pos] = d.decoder_config_metadata_service_id;
380                pos += 1;
381            }
382            DecoderConfig::ReservedData(d) => {
383                buf[pos] = d.data.len() as u8;
384                pos += 1;
385                buf[pos..pos + d.data.len()].copy_from_slice(d.data);
386                pos += d.data.len();
387            }
388            DecoderConfig::None | DecoderConfig::SameService | DecoderConfig::Private => {}
389        }
390
391        buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
392        Ok(len)
393    }
394}
395
396impl<'a> crate::traits::DescriptorDef<'a> for MetadataDescriptor<'a> {
397    const TAG: u8 = TAG;
398    const NAME: &'static str = "METADATA";
399}
400
401#[cfg(test)]
402mod tests {
403    use super::*;
404
405    fn serialize_round_trip(d: &MetadataDescriptor<'_>) {
406        let mut buf = vec![0u8; d.serialized_len()];
407        let written = d.serialize_into(&mut buf).unwrap();
408        assert_eq!(written, d.serialized_len());
409        let reparsed = MetadataDescriptor::parse(&buf).unwrap();
410        assert_eq!(*d, reparsed, "round-trip mismatch");
411    }
412
413    #[test]
414    fn round_trip_decoder_config_none() {
415        let d = MetadataDescriptor {
416            metadata_application_format: 0x0010,
417            metadata_application_format_identifier: None,
418            metadata_format: MetadataFormat::TeM,
419            metadata_format_identifier: None,
420            metadata_service_id: 5,
421            decoder_config_flags: DecoderConfigFlags::None,
422            dsmcc_flag: false,
423            service_identification: None,
424            decoder_config: DecoderConfig::None,
425            private_data: &[],
426        };
427        serialize_round_trip(&d);
428    }
429
430    #[test]
431    fn round_trip_decoder_config_in_descriptor() {
432        let d = MetadataDescriptor {
433            metadata_application_format: 0x0011,
434            metadata_application_format_identifier: None,
435            metadata_format: MetadataFormat::BiM,
436            metadata_format_identifier: None,
437            metadata_service_id: 3,
438            decoder_config_flags: DecoderConfigFlags::InDescriptor,
439            dsmcc_flag: false,
440            service_identification: None,
441            decoder_config: DecoderConfig::InDescriptor(DecoderConfigInDescriptor {
442                decoder_config: &[0x01, 0x02, 0x03],
443            }),
444            private_data: &[0xFF],
445        };
446        serialize_round_trip(&d);
447    }
448
449    #[test]
450    fn round_trip_decoder_config_same_service() {
451        let d = MetadataDescriptor {
452            metadata_application_format: 0x0100,
453            metadata_application_format_identifier: None,
454            metadata_format: MetadataFormat::AppFormat,
455            metadata_format_identifier: None,
456            metadata_service_id: 1,
457            decoder_config_flags: DecoderConfigFlags::SameService,
458            dsmcc_flag: true,
459            service_identification: Some(&[0xAA, 0xBB]),
460            decoder_config: DecoderConfig::SameService,
461            private_data: &[],
462        };
463        serialize_round_trip(&d);
464    }
465
466    #[test]
467    fn round_trip_decoder_config_dsmcc() {
468        let d = MetadataDescriptor {
469            metadata_application_format: 0x0010,
470            metadata_application_format_identifier: None,
471            metadata_format: MetadataFormat::Reserved1(0x30),
472            metadata_format_identifier: None,
473            metadata_service_id: 7,
474            decoder_config_flags: DecoderConfigFlags::DsmccCarousel,
475            dsmcc_flag: false,
476            service_identification: None,
477            decoder_config: DecoderConfig::DsmccCarousel(DecoderConfigDsmcc {
478                dec_config_identification: &[0x11, 0x22, 0x33, 0x44],
479            }),
480            private_data: &[],
481        };
482        serialize_round_trip(&d);
483    }
484
485    #[test]
486    fn round_trip_decoder_config_other_service() {
487        let d = MetadataDescriptor {
488            metadata_application_format: 0x0010,
489            metadata_application_format_identifier: None,
490            metadata_format: MetadataFormat::Private(0x50),
491            metadata_format_identifier: None,
492            metadata_service_id: 2,
493            decoder_config_flags: DecoderConfigFlags::OtherService,
494            dsmcc_flag: false,
495            service_identification: None,
496            decoder_config: DecoderConfig::OtherService(DecoderConfigOtherService {
497                decoder_config_metadata_service_id: 99,
498            }),
499            private_data: &[],
500        };
501        serialize_round_trip(&d);
502    }
503
504    #[test]
505    fn round_trip_decoder_config_reserved_data() {
506        let d = MetadataDescriptor {
507            metadata_application_format: 0x0010,
508            metadata_application_format_identifier: None,
509            metadata_format: MetadataFormat::Identifier,
510            metadata_format_identifier: Some(0xDEADBEEF),
511            metadata_service_id: 4,
512            decoder_config_flags: DecoderConfigFlags::Reserved(5),
513            dsmcc_flag: false,
514            service_identification: None,
515            decoder_config: DecoderConfig::ReservedData(ReservedData {
516                data: &[0x99, 0x88, 0x77],
517            }),
518            private_data: &[],
519        };
520        serialize_round_trip(&d);
521    }
522
523    #[test]
524    fn round_trip_decoder_config_private() {
525        let d = MetadataDescriptor {
526            metadata_application_format: 0x0010,
527            metadata_application_format_identifier: None,
528            metadata_format: MetadataFormat::TeM,
529            metadata_format_identifier: None,
530            metadata_service_id: 6,
531            decoder_config_flags: DecoderConfigFlags::Private,
532            dsmcc_flag: false,
533            service_identification: None,
534            decoder_config: DecoderConfig::Private,
535            private_data: &[0x01],
536        };
537        serialize_round_trip(&d);
538    }
539
540    #[test]
541    fn round_trip_with_ffff_and_ff() {
542        let d = MetadataDescriptor {
543            metadata_application_format: 0xFFFF,
544            metadata_application_format_identifier: Some(0x11223344),
545            metadata_format: MetadataFormat::Identifier,
546            metadata_format_identifier: Some(0x55667788),
547            metadata_service_id: 8,
548            decoder_config_flags: DecoderConfigFlags::None,
549            dsmcc_flag: true,
550            service_identification: Some(&[0xCA, 0xFE]),
551            decoder_config: DecoderConfig::None,
552            private_data: &[],
553        };
554        serialize_round_trip(&d);
555    }
556
557    #[test]
558    fn parse_rejects_wrong_tag() {
559        let err = MetadataDescriptor::parse(&[0x02, 5, 0, 0, 0, 0, 0]).unwrap_err();
560        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
561    }
562
563    #[test]
564    fn parse_rejects_too_short() {
565        let err = MetadataDescriptor::parse(&[TAG, 0]).unwrap_err();
566        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
567    }
568}