Skip to main content

dvb_si/descriptors/
teletext.rs

1//! Teletext Descriptor — ETSI EN 300 468 §6.2.44 (tag 0x56).
2//!
3//! Carried inside PMT's ES_info loop. Enumerates teletext components: one
4//! entry per 3-char language code + type/magazine/page triple (5 bytes).
5
6use super::descriptor_body;
7use crate::error::{Error, Result};
8use crate::text::LangCode;
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for teletext_descriptor.
12pub const TAG: u8 = 0x56;
13const HEADER_LEN: usize = 2;
14const ENTRY_LEN: usize = 5;
15const LANG_LEN: usize = 3;
16
17/// One teletext component.
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct TeletextEntry {
21    /// ISO 639-2 language code of this teletext service.
22    pub language_code: LangCode,
23    /// 5-bit teletext_type (ETSI Table 102): 1 = initial page, 2 = subtitle, etc.
24    pub teletext_type: u8,
25    /// 3-bit teletext_magazine_number.
26    pub magazine_number: u8,
27    /// 8-bit BCD teletext_page_number.
28    pub page_number: u8,
29}
30
31/// Teletext Descriptor.
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34pub struct TeletextDescriptor {
35    /// Teletext components listed in wire order.
36    pub entries: Vec<TeletextEntry>,
37}
38
39impl<'a> Parse<'a> for TeletextDescriptor {
40    type Error = crate::error::Error;
41    fn parse(bytes: &'a [u8]) -> Result<Self> {
42        let body = descriptor_body(
43            bytes,
44            TAG,
45            "TeletextDescriptor",
46            "unexpected tag for teletext_descriptor",
47        )?;
48        if body.len() % ENTRY_LEN != 0 {
49            return Err(Error::InvalidDescriptor {
50                tag: TAG,
51                reason: "teletext_descriptor length must be a multiple of 5",
52            });
53        }
54        let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
55        for chunk in body.chunks_exact(ENTRY_LEN) {
56            let language_code = LangCode([chunk[0], chunk[1], chunk[2]]);
57            let type_and_mag = chunk[LANG_LEN];
58            let teletext_type = (type_and_mag >> 3) & 0x1F;
59            let magazine_number = type_and_mag & 0x07;
60            let page_number = chunk[LANG_LEN + 1];
61            entries.push(TeletextEntry {
62                language_code,
63                teletext_type,
64                magazine_number,
65                page_number,
66            });
67        }
68        Ok(Self { entries })
69    }
70}
71
72impl Serialize for TeletextDescriptor {
73    type Error = crate::error::Error;
74    fn serialized_len(&self) -> usize {
75        HEADER_LEN + self.entries.len() * ENTRY_LEN
76    }
77
78    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
79        let len = self.serialized_len();
80        if buf.len() < len {
81            return Err(Error::OutputBufferTooSmall {
82                need: len,
83                have: buf.len(),
84            });
85        }
86        buf[0] = TAG;
87        buf[1] = (self.entries.len() * ENTRY_LEN) as u8;
88        let mut pos = HEADER_LEN;
89        for e in &self.entries {
90            buf[pos..pos + LANG_LEN].copy_from_slice(&e.language_code.0);
91            buf[pos + LANG_LEN] = ((e.teletext_type & 0x1F) << 3) | (e.magazine_number & 0x07);
92            buf[pos + LANG_LEN + 1] = e.page_number;
93            pos += ENTRY_LEN;
94        }
95        Ok(len)
96    }
97}
98impl<'a> crate::traits::DescriptorDef<'a> for TeletextDescriptor {
99    const TAG: u8 = TAG;
100    const NAME: &'static str = "TELETEXT";
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn parse_single_entry() {
109        // lang=eng, type=1, mag=2, page=0x10
110        let bytes = [TAG, 5, b'e', b'n', b'g', (1 << 3) | 2, 0x10];
111        let d = TeletextDescriptor::parse(&bytes).unwrap();
112        assert_eq!(d.entries.len(), 1);
113        assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
114        assert_eq!(d.entries[0].teletext_type, 1);
115        assert_eq!(d.entries[0].magazine_number, 2);
116        assert_eq!(d.entries[0].page_number, 0x10);
117    }
118
119    #[test]
120    fn parse_multiple_entries() {
121        let bytes = [
122            TAG,
123            10,
124            b'e',
125            b'n',
126            b'g',
127            (1 << 3) | 1,
128            0x10,
129            b'f',
130            b'r',
131            b'a',
132            (2 << 3) | 1,
133            0x20,
134        ];
135        let d = TeletextDescriptor::parse(&bytes).unwrap();
136        assert_eq!(d.entries.len(), 2);
137        assert_eq!(d.entries[1].teletext_type, 2);
138    }
139
140    #[test]
141    fn parse_rejects_wrong_tag() {
142        assert!(matches!(
143            TeletextDescriptor::parse(&[0x57, 0]).unwrap_err(),
144            Error::InvalidDescriptor { tag: 0x57, .. }
145        ));
146    }
147
148    #[test]
149    fn parse_rejects_length_not_multiple_of_5() {
150        let bytes = [TAG, 4, 0, 0, 0, 0];
151        assert!(matches!(
152            TeletextDescriptor::parse(&bytes).unwrap_err(),
153            Error::InvalidDescriptor { .. }
154        ));
155    }
156
157    #[test]
158    fn serialize_round_trip() {
159        let d = TeletextDescriptor {
160            entries: vec![TeletextEntry {
161                language_code: LangCode(*b"fra"),
162                teletext_type: 2,
163                magazine_number: 8 & 0x07,
164                page_number: 0x88,
165            }],
166        };
167        let mut buf = vec![0u8; d.serialized_len()];
168        d.serialize_into(&mut buf).unwrap();
169        assert_eq!(TeletextDescriptor::parse(&buf).unwrap(), d);
170    }
171
172    #[test]
173    fn empty_descriptor_valid() {
174        let bytes = [TAG, 0];
175        let d = TeletextDescriptor::parse(&bytes).unwrap();
176        assert_eq!(d.entries.len(), 0);
177    }
178}