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