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