Skip to main content

dvb_si/tables/
metadata_section.rs

1//! `Metadata_section()` — Rec. ITU-T H.222.0 (08/2023) | ISO/IEC 13818-1:2023
2//! §2.12.6, Tables 2-158/2-159 (table_id 0x06); syntax in
3//! `docs/tables/iso_13818_1/2-153-2-158-2-162-sections-14496-metadata-green.md`.
4//!
5//! ```text
6//! table_id(8)=0x06 section_syntax_indicator(1) private_indicator(1)
7//! random_access_indicator(1) decoder_config_flag(1) metadata_section_length(12)
8//! metadata_service_id(8) reserved(8) section_fragment_indication(2)
9//! version_number(5) current_next_indicator(1) section_number(8)
10//! last_section_number(8) { metadata_byte(8) }* CRC_32(32)
11//! ```
12//!
13//! The `metadata_byte`s are the contiguous bytes of a metadata Access Unit
14//! whose format is out of scope here: carried as one opaque byte run.
15
16use crate::error::{Error, Result};
17use broadcast_common::{Parse, Serialize};
18
19/// `table_id` of `Metadata_section()`.
20pub const TABLE_ID: u8 = 0x06;
21const MIN_HEADER_LEN: usize = 3;
22const EXTENSION_HEADER_LEN: usize = 5;
23use super::CRC_LEN;
24const MIN_SECTION_LEN: usize = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + CRC_LEN;
25/// Default of the 8-bit `reserved` (all ones, H.222.0 clause 2.1).
26pub const RESERVED_DEFAULT: u8 = 0xFF;
27
28const B1_SSI: u8 = 0x80;
29const B1_PRIVATE: u8 = 0x40;
30const B1_RANDOM_ACCESS: u8 = 0x20;
31const B1_DECODER_CONFIG: u8 = 0x10;
32const FRAGMENT_SHIFT: u32 = 6;
33const FRAGMENT_MASK: u8 = 0x03;
34
35/// `section_fragment_indication` (Table 2-159).
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize))]
38#[non_exhaustive]
39pub enum SectionFragmentIndication {
40    /// `'11'` — a single section carrying a complete metadata Access Unit.
41    Complete,
42    /// `'10'` — the first section of a series for one metadata Access Unit.
43    First,
44    /// `'01'` — the last section of such a series.
45    Last,
46    /// `'00'` — neither the first nor the last section of such a series.
47    Middle,
48}
49
50impl SectionFragmentIndication {
51    /// Decode the 2-bit field (all four values are defined).
52    #[must_use]
53    pub const fn from_u8(v: u8) -> Self {
54        match v & FRAGMENT_MASK {
55            0b11 => Self::Complete,
56            0b10 => Self::First,
57            0b01 => Self::Last,
58            _ => Self::Middle,
59        }
60    }
61
62    /// The wire value.
63    #[must_use]
64    pub const fn to_u8(self) -> u8 {
65        match self {
66            Self::Complete => 0b11,
67            Self::First => 0b10,
68            Self::Last => 0b01,
69            Self::Middle => 0b00,
70        }
71    }
72
73    /// Spec label (Table 2-159).
74    #[must_use]
75    pub fn name(&self) -> &'static str {
76        match self {
77            Self::Complete => "complete metadata Access Unit",
78            Self::First => "first section",
79            Self::Last => "last section",
80            Self::Middle => "neither first nor last section",
81        }
82    }
83}
84broadcast_common::impl_spec_display!(SectionFragmentIndication);
85
86/// `Metadata_section()` — Table 2-158.
87#[derive(Debug, Clone, PartialEq, Eq)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize))]
89#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
90#[non_exhaustive]
91pub struct MetadataSection<'a> {
92    /// `section_syntax_indicator` (shall be 1; a received value is kept).
93    pub section_syntax_indicator: bool,
94    /// `private_indicator` (not specified by H.222.0).
95    pub private_indicator: bool,
96    /// `random_access_indicator`.
97    pub random_access_indicator: bool,
98    /// `decoder_config_flag`.
99    pub decoder_config_flag: bool,
100    /// `metadata_service_id`.
101    pub metadata_service_id: u8,
102    /// `reserved(8)`; re-emitted verbatim, default `0xFF`.
103    pub reserved: u8,
104    /// `section_fragment_indication` (Table 2-159).
105    pub section_fragment_indication: SectionFragmentIndication,
106    /// `version_number` (5 bits).
107    pub version_number: u8,
108    /// `current_next_indicator`.
109    pub current_next_indicator: bool,
110    /// `section_number`.
111    pub section_number: u8,
112    /// `last_section_number`.
113    pub last_section_number: u8,
114    /// `metadata_byte`s (opaque: the metadata Access Unit format is not part of
115    /// this section syntax).
116    pub metadata_bytes: &'a [u8],
117}
118
119impl<'a> MetadataSection<'a> {
120    /// Construct with the spec fills (`section_syntax_indicator` 1, `reserved` all ones).
121    #[must_use]
122    pub fn new(
123        metadata_service_id: u8,
124        section_fragment_indication: SectionFragmentIndication,
125        version_number: u8,
126        section_number: u8,
127        last_section_number: u8,
128        metadata_bytes: &'a [u8],
129    ) -> Self {
130        Self {
131            section_syntax_indicator: true,
132            private_indicator: false,
133            random_access_indicator: false,
134            decoder_config_flag: false,
135            metadata_service_id,
136            reserved: RESERVED_DEFAULT,
137            section_fragment_indication,
138            version_number,
139            current_next_indicator: true,
140            section_number,
141            last_section_number,
142            metadata_bytes,
143        }
144    }
145}
146
147impl<'a> Parse<'a> for MetadataSection<'a> {
148    type Error = Error;
149
150    fn parse(bytes: &'a [u8]) -> Result<Self> {
151        if bytes.len() < MIN_SECTION_LEN {
152            return Err(Error::BufferTooShort {
153                need: MIN_SECTION_LEN,
154                have: bytes.len(),
155                what: "MetadataSection",
156            });
157        }
158        if bytes[0] != TABLE_ID {
159            return Err(Error::UnexpectedTableId {
160                table_id: bytes[0],
161                what: "MetadataSection",
162                expected: &[TABLE_ID],
163            });
164        }
165        let total = super::check_section_length(
166            bytes.len(),
167            MIN_HEADER_LEN,
168            super::section_length_of(bytes),
169            MIN_SECTION_LEN,
170        )?;
171        let b1 = bytes[1];
172        Ok(Self {
173            section_syntax_indicator: b1 & B1_SSI != 0,
174            private_indicator: b1 & B1_PRIVATE != 0,
175            random_access_indicator: b1 & B1_RANDOM_ACCESS != 0,
176            decoder_config_flag: b1 & B1_DECODER_CONFIG != 0,
177            metadata_service_id: bytes[3],
178            reserved: bytes[4],
179            section_fragment_indication: SectionFragmentIndication::from_u8(
180                bytes[5] >> FRAGMENT_SHIFT,
181            ),
182            version_number: super::version_number_of(bytes[5]),
183            current_next_indicator: super::current_next_of(bytes[5]),
184            section_number: bytes[6],
185            last_section_number: bytes[7],
186            metadata_bytes: &bytes[MIN_HEADER_LEN + EXTENSION_HEADER_LEN..total - CRC_LEN],
187        })
188    }
189}
190
191impl Serialize for MetadataSection<'_> {
192    type Error = Error;
193
194    fn serialized_len(&self) -> usize {
195        MIN_SECTION_LEN + self.metadata_bytes.len()
196    }
197
198    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
199        let len = self.serialized_len();
200        if buf.len() < len {
201            return Err(Error::OutputBufferTooSmall {
202                need: len,
203                have: buf.len(),
204            });
205        }
206        buf[0] = TABLE_ID;
207        buf[1] = (u8::from(self.section_syntax_indicator) * B1_SSI)
208            | (u8::from(self.private_indicator) * B1_PRIVATE)
209            | (u8::from(self.random_access_indicator) * B1_RANDOM_ACCESS)
210            | (u8::from(self.decoder_config_flag) * B1_DECODER_CONFIG);
211        super::write_section_length(buf, len - MIN_HEADER_LEN)?;
212        buf[3] = self.metadata_service_id;
213        buf[4] = self.reserved;
214        // version_number(5) | current_next_indicator(1), with the 2-bit
215        // fragment indication in the reserved(2) position of the shared helper.
216        buf[5] = super::b5_of(
217            self.section_fragment_indication.to_u8(),
218            self.version_number,
219            self.current_next_indicator,
220        )?;
221        buf[6] = self.section_number;
222        buf[7] = self.last_section_number;
223        let start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN;
224        buf[start..start + self.metadata_bytes.len()].copy_from_slice(self.metadata_bytes);
225        super::write_crc(buf, len - CRC_LEN);
226        Ok(len)
227    }
228}
229
230impl<'a> crate::traits::TableDef<'a> for MetadataSection<'a> {
231    const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
232    const NAME: &'static str = "METADATA";
233}
234
235#[cfg(test)]
236#[allow(clippy::unusual_byte_groupings)] // bit-field-wise literals mirror the syntax tables
237mod tests {
238    use super::*;
239
240    fn rt(s: &MetadataSection<'_>) -> Vec<u8> {
241        let mut out = vec![0u8; s.serialized_len()];
242        s.serialize_into(&mut out).unwrap();
243        let p = MetadataSection::parse(&out).unwrap();
244        assert_eq!(&p, s);
245        out
246    }
247
248    #[test]
249    fn spec_vector_table_2_158() {
250        // table_id 06, ssi=1 private=0 ra=1 dc=0 len=12-bit, service 0x21, reserved FF,
251        // fragment '10' version 5 cni 1, section 0 of 2, 3 metadata bytes, CRC.
252        let s = MetadataSection {
253            random_access_indicator: true,
254            ..MetadataSection::new(0x21, SectionFragmentIndication::First, 5, 0, 2, &[1, 2, 3])
255        };
256        let b = rt(&s);
257        // 1 0 1 0 | length hi 0 -> 0xA0; length = 5 + 3 + 4 = 12
258        assert_eq!(&b[..8], &[0x06, 0xA0, 0x0C, 0x21, 0xFF, 0b10_00101_1, 0, 2]);
259        assert_eq!(&b[8..11], &[1, 2, 3]);
260        assert_eq!(broadcast_common::crc32_mpeg2::compute(&b), 0);
261    }
262
263    #[test]
264    fn all_flags_reserved_and_fragments_preserved() {
265        for f in [
266            SectionFragmentIndication::Complete,
267            SectionFragmentIndication::First,
268            SectionFragmentIndication::Last,
269            SectionFragmentIndication::Middle,
270        ] {
271            let mut s = MetadataSection::new(1, f, 31, 4, 9, &[]);
272            s.private_indicator = true;
273            s.decoder_config_flag = true;
274            s.reserved = 0x00;
275            s.current_next_indicator = false;
276            let b = rt(&s);
277            assert_eq!(MetadataSection::parse(&b).unwrap().reserved, 0);
278        }
279    }
280
281    #[test]
282    fn rejects_bad_input() {
283        assert!(MetadataSection::parse(&[0x06, 0xB0, 0x00]).is_err());
284        let mut b = vec![0u8; 12];
285        b[0] = 0x05;
286        assert!(matches!(
287            MetadataSection::parse(&b),
288            Err(Error::UnexpectedTableId { .. })
289        ));
290        let s = MetadataSection::new(1, SectionFragmentIndication::Complete, 32, 0, 0, &[]);
291        assert!(s.serialize_into(&mut [0u8; 16]).is_err());
292        let big = vec![0u8; 4090];
293        let s = MetadataSection::new(1, SectionFragmentIndication::Complete, 0, 0, 0, &big);
294        assert!(s.serialize_into(&mut vec![0u8; 5000]).is_err());
295    }
296}