Skip to main content

dvb_si/tables/
ait.rs

1//! Application Information Table — ETSI TS 102 809 §5.3.4.
2//!
3//! AIT carries application metadata for HbbTV / interactive-TV services.
4//! Carried on a per-service PID with table_id 0x74.
5
6use crate::error::{Error, Result};
7use crate::traits::Table;
8use dvb_common::{Parse, Serialize};
9
10/// AIT table_id (ETSI TS 102 809 §5.3.4).
11pub const TABLE_ID: u8 = 0x74;
12/// AIT has no well-known PID — it is service-specific.
13pub const PID: u16 = 0x0000;
14
15const MIN_HEADER_LEN: usize = 3;
16const EXTENSION_HEADER_LEN: usize = 5;
17const COMMON_DESC_LEN_BYTES: usize = 2;
18const APP_LOOP_LEN_BYTES: usize = 2;
19const CRC_LEN: usize = 4;
20const APP_HEADER_LEN: usize = 9;
21
22/// 48-bit application identifier: organisation_id + application_id.
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct ApplicationIdentifier {
26    /// 32-bit organisation_id.
27    pub organisation_id: u32,
28    /// 16-bit application_id.
29    pub application_id: u16,
30}
31
32/// One application entry in the AIT application loop.
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35pub struct AitApplication<'a> {
36    /// Application identifier.
37    pub identifier: ApplicationIdentifier,
38    /// Application control code (1 = autostart, etc.).
39    pub control_code: u8,
40    /// Raw descriptor bytes for this application.
41    #[cfg_attr(feature = "serde", serde(borrow))]
42    pub descriptors: &'a [u8],
43}
44
45/// Application Information Table.
46#[derive(Debug, Clone, PartialEq, Eq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct Ait<'a> {
49    /// 15-bit application_type (e.g. 0x0010 for HbbTV).
50    pub application_type: u16,
51    /// Test application flag (bit 15 of the extension field).
52    pub test_application_flag: bool,
53    /// 5-bit version_number.
54    pub version_number: u8,
55    /// current_next_indicator bit.
56    pub current_next_indicator: bool,
57    /// section_number in the sub-table sequence.
58    pub section_number: u8,
59    /// last_section_number in the sub-table sequence.
60    pub last_section_number: u8,
61    /// Raw common descriptor bytes.
62    #[cfg_attr(feature = "serde", serde(borrow))]
63    pub common_descriptors: &'a [u8],
64    /// Applications in wire order.
65    #[cfg_attr(feature = "serde", serde(borrow))]
66    pub applications: Vec<AitApplication<'a>>,
67}
68
69impl<'a> Parse<'a> for Ait<'a> {
70    type Error = crate::error::Error;
71    fn parse(bytes: &'a [u8]) -> Result<Self> {
72        let min_len = MIN_HEADER_LEN
73            + EXTENSION_HEADER_LEN
74            + COMMON_DESC_LEN_BYTES
75            + APP_LOOP_LEN_BYTES
76            + CRC_LEN;
77        if bytes.len() < min_len {
78            return Err(Error::BufferTooShort {
79                need: min_len,
80                have: bytes.len(),
81                what: "Ait",
82            });
83        }
84
85        if bytes[0] != TABLE_ID {
86            return Err(Error::UnexpectedTableId {
87                table_id: bytes[0],
88                what: "Ait",
89                expected: &[TABLE_ID],
90            });
91        }
92
93        let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
94        let total = MIN_HEADER_LEN + section_length as usize;
95        if bytes.len() < total {
96            return Err(Error::SectionLengthOverflow {
97                declared: section_length as usize,
98                available: bytes.len() - MIN_HEADER_LEN,
99            });
100        }
101
102        let test_application_flag = (bytes[3] & 0x80) != 0;
103        let application_type = (((bytes[3] & 0x7F) as u16) << 8) | (bytes[4] as u16);
104        let version_number = (bytes[5] >> 1) & 0x1F;
105        let current_next_indicator = (bytes[5] & 0x01) != 0;
106        let section_number = bytes[6];
107        let last_section_number = bytes[7];
108
109        let common_descriptors_length = (((bytes[8] & 0x0F) as usize) << 8) | bytes[9] as usize;
110        let common_desc_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + COMMON_DESC_LEN_BYTES;
111        let common_desc_end = common_desc_start + common_descriptors_length;
112        let app_loop_end = total - CRC_LEN;
113        if common_desc_end > app_loop_end {
114            return Err(Error::SectionLengthOverflow {
115                declared: common_descriptors_length,
116                available: app_loop_end - common_desc_start,
117            });
118        }
119        let common_descriptors = &bytes[common_desc_start..common_desc_end];
120
121        let app_loop_length =
122            (((bytes[common_desc_end] & 0x0F) as usize) << 8) | bytes[common_desc_end + 1] as usize;
123        let app_loop_start = common_desc_end + APP_LOOP_LEN_BYTES;
124        let app_loop_actual_end = app_loop_start + app_loop_length;
125        if app_loop_actual_end > app_loop_end {
126            return Err(Error::SectionLengthOverflow {
127                declared: app_loop_length,
128                available: app_loop_end - app_loop_start,
129            });
130        }
131
132        let mut applications = Vec::new();
133        let mut pos = app_loop_start;
134        while pos + APP_HEADER_LEN <= app_loop_actual_end {
135            let organisation_id = ((bytes[pos] as u32) << 24)
136                | ((bytes[pos + 1] as u32) << 16)
137                | ((bytes[pos + 2] as u32) << 8)
138                | (bytes[pos + 3] as u32);
139            let application_id = u16::from_be_bytes([bytes[pos + 4], bytes[pos + 5]]);
140            let control_code = bytes[pos + 6];
141            let app_desc_length =
142                (((bytes[pos + 7] & 0x0F) as usize) << 8) | bytes[pos + 8] as usize;
143            let app_desc_start = pos + APP_HEADER_LEN;
144            let app_desc_end = app_desc_start + app_desc_length;
145            if app_desc_end > app_loop_actual_end {
146                return Err(Error::SectionLengthOverflow {
147                    declared: app_desc_length,
148                    available: app_loop_actual_end - app_desc_start,
149                });
150            }
151            applications.push(AitApplication {
152                identifier: ApplicationIdentifier {
153                    organisation_id,
154                    application_id,
155                },
156                control_code,
157                descriptors: &bytes[app_desc_start..app_desc_end],
158            });
159            pos = app_desc_end;
160        }
161
162        Ok(Ait {
163            application_type,
164            test_application_flag,
165            version_number,
166            current_next_indicator,
167            section_number,
168            last_section_number,
169            common_descriptors,
170            applications,
171        })
172    }
173}
174
175impl Serialize for Ait<'_> {
176    type Error = crate::error::Error;
177    fn serialized_len(&self) -> usize {
178        let app_bytes: usize = self
179            .applications
180            .iter()
181            .map(|a| APP_HEADER_LEN + a.descriptors.len())
182            .sum();
183        MIN_HEADER_LEN
184            + EXTENSION_HEADER_LEN
185            + COMMON_DESC_LEN_BYTES
186            + self.common_descriptors.len()
187            + APP_LOOP_LEN_BYTES
188            + app_bytes
189            + CRC_LEN
190    }
191
192    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
193        let len = self.serialized_len();
194        if buf.len() < len {
195            return Err(Error::OutputBufferTooSmall {
196                need: len,
197                have: buf.len(),
198            });
199        }
200
201        let section_length: u16 = (len - MIN_HEADER_LEN) as u16;
202        buf[0] = TABLE_ID;
203        buf[1] = 0xB0 | ((section_length >> 8) as u8 & 0x0F);
204        buf[2] = (section_length & 0xFF) as u8;
205        buf[3] = (u8::from(self.test_application_flag) << 7)
206            | ((self.application_type >> 8) as u8 & 0x7F);
207        buf[4] = (self.application_type & 0xFF) as u8;
208        buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
209        buf[6] = self.section_number;
210        buf[7] = self.last_section_number;
211
212        let cdl = self.common_descriptors.len() as u16;
213        buf[8] = 0xF0 | ((cdl >> 8) as u8 & 0x0F);
214        buf[9] = (cdl & 0xFF) as u8;
215
216        let common_desc_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + COMMON_DESC_LEN_BYTES;
217        buf[common_desc_start..common_desc_start + self.common_descriptors.len()]
218            .copy_from_slice(self.common_descriptors);
219
220        let app_loop_start = common_desc_start + self.common_descriptors.len();
221        let app_bytes: usize = self
222            .applications
223            .iter()
224            .map(|a| APP_HEADER_LEN + a.descriptors.len())
225            .sum();
226        let apl = app_bytes as u16;
227        buf[app_loop_start] = 0xF0 | ((apl >> 8) as u8 & 0x0F);
228        buf[app_loop_start + 1] = (apl & 0xFF) as u8;
229
230        let mut pos = app_loop_start + APP_LOOP_LEN_BYTES;
231        for app in &self.applications {
232            buf[pos..pos + 4].copy_from_slice(&app.identifier.organisation_id.to_be_bytes());
233            buf[pos + 4..pos + 6].copy_from_slice(&app.identifier.application_id.to_be_bytes());
234            buf[pos + 6] = app.control_code;
235            let adl = app.descriptors.len() as u16;
236            buf[pos + 7] = 0xF0 | ((adl >> 8) as u8 & 0x0F);
237            buf[pos + 8] = (adl & 0xFF) as u8;
238            let desc_start = pos + APP_HEADER_LEN;
239            buf[desc_start..desc_start + app.descriptors.len()].copy_from_slice(app.descriptors);
240            pos = desc_start + app.descriptors.len();
241        }
242
243        let crc_pos = len - CRC_LEN;
244        let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
245        buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
246        Ok(len)
247    }
248}
249
250impl<'a> Table<'a> for Ait<'a> {
251    const TABLE_ID: u8 = TABLE_ID;
252    const PID: u16 = PID;
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    fn build_ait(
260        application_type: u16,
261        test_flag: bool,
262        version: u8,
263        common_descriptors: &[u8],
264        applications: &[(u32, u16, u8, Vec<u8>)],
265    ) -> Vec<u8> {
266        let app_bytes: usize = applications
267            .iter()
268            .map(|(_, _, _, d)| APP_HEADER_LEN + d.len())
269            .sum();
270        let section_length: u16 = (EXTENSION_HEADER_LEN
271            + COMMON_DESC_LEN_BYTES
272            + common_descriptors.len()
273            + APP_LOOP_LEN_BYTES
274            + app_bytes
275            + CRC_LEN) as u16;
276        let mut v = vec![
277            TABLE_ID,
278            0xB0 | ((section_length >> 8) as u8 & 0x0F),
279            (section_length & 0xFF) as u8,
280            (u8::from(test_flag) << 7) | ((application_type >> 8) as u8 & 0x7F),
281            (application_type & 0xFF) as u8,
282            0xC0 | ((version & 0x1F) << 1) | 0x01,
283            0,
284            0,
285        ];
286        let cdl = common_descriptors.len() as u16;
287        v.push(0xF0 | ((cdl >> 8) as u8 & 0x0F));
288        v.push((cdl & 0xFF) as u8);
289        v.extend_from_slice(common_descriptors);
290        let apl = app_bytes as u16;
291        v.push(0xF0 | ((apl >> 8) as u8 & 0x0F));
292        v.push((apl & 0xFF) as u8);
293        for &(org_id, app_id, cc, ref desc) in applications {
294            v.extend_from_slice(&org_id.to_be_bytes());
295            v.extend_from_slice(&app_id.to_be_bytes());
296            v.push(cc);
297            let adl = desc.len() as u16;
298            v.push(0xF0 | ((adl >> 8) as u8 & 0x0F));
299            v.push((adl & 0xFF) as u8);
300            v.extend_from_slice(desc);
301        }
302        v.extend_from_slice(&[0, 0, 0, 0]);
303        v
304    }
305
306    #[test]
307    fn parse_rejects_wrong_table_id() {
308        let mut bytes = build_ait(0x0010, false, 0, &[], &[]);
309        bytes[0] = 0x00;
310        let err = Ait::parse(&bytes).unwrap_err();
311        assert!(matches!(
312            err,
313            Error::UnexpectedTableId { table_id: 0x00, .. }
314        ));
315    }
316
317    #[test]
318    fn parse_rejects_short_buffer() {
319        let err = Ait::parse(&[0x74, 0x00]).unwrap_err();
320        assert!(matches!(err, Error::BufferTooShort { .. }));
321    }
322
323    #[test]
324    fn parse_empty_ait_no_applications() {
325        let bytes = build_ait(0x0010, false, 5, &[], &[]);
326        let ait = Ait::parse(&bytes).expect("parse");
327        assert_eq!(ait.application_type, 0x0010);
328        assert!(!ait.test_application_flag);
329        assert_eq!(ait.version_number, 5);
330        assert!(ait.current_next_indicator);
331        assert_eq!(ait.section_number, 0);
332        assert_eq!(ait.last_section_number, 0);
333        assert_eq!(ait.common_descriptors.len(), 0);
334        assert_eq!(ait.applications.len(), 0);
335    }
336
337    #[test]
338    fn parse_test_application_flag_extracted() {
339        let bytes = build_ait(0x0010, true, 0, &[], &[]);
340        let ait = Ait::parse(&bytes).unwrap();
341        assert!(ait.test_application_flag);
342    }
343
344    #[test]
345    fn parse_common_descriptors_preserved() {
346        let desc = vec![0x00, 0x02, 0xAA, 0xBB];
347        let bytes = build_ait(0x0010, false, 0, &desc, &[]);
348        let ait = Ait::parse(&bytes).unwrap();
349        assert_eq!(ait.common_descriptors, &desc[..]);
350    }
351
352    #[test]
353    fn parse_single_application() {
354        let desc = vec![0x02, 0x03, 0xCC, 0xDD, 0xEE];
355        let bytes = build_ait(
356            0x0010,
357            false,
358            0,
359            &[],
360            &[(0x12345678, 0xABCD, 0x01, desc.clone())],
361        );
362        let ait = Ait::parse(&bytes).unwrap();
363        assert_eq!(ait.applications.len(), 1);
364        assert_eq!(ait.applications[0].identifier.organisation_id, 0x12345678);
365        assert_eq!(ait.applications[0].identifier.application_id, 0xABCD);
366        assert_eq!(ait.applications[0].control_code, 0x01);
367        assert_eq!(ait.applications[0].descriptors, &desc[..]);
368    }
369
370    #[test]
371    fn parse_multiple_applications_preserve_order() {
372        let bytes = build_ait(
373            0x0010,
374            false,
375            0,
376            &[],
377            &[
378                (0x00000001, 0x0001, 0x01, vec![]),
379                (0x00000002, 0x0002, 0x02, vec![0x01]),
380                (0x00000003, 0x0003, 0x03, vec![0x02, 0x03]),
381            ],
382        );
383        let ait = Ait::parse(&bytes).unwrap();
384        assert_eq!(ait.applications.len(), 3);
385        assert_eq!(ait.applications[0].identifier.organisation_id, 1);
386        assert_eq!(ait.applications[1].identifier.organisation_id, 2);
387        assert_eq!(ait.applications[2].identifier.organisation_id, 3);
388    }
389
390    #[test]
391    fn serialize_round_trip_empty() {
392        let ait = Ait {
393            application_type: 0x0010,
394            test_application_flag: false,
395            version_number: 3,
396            current_next_indicator: true,
397            section_number: 0,
398            last_section_number: 0,
399            common_descriptors: &[],
400            applications: vec![],
401        };
402        let mut buf = vec![0u8; ait.serialized_len()];
403        ait.serialize_into(&mut buf).unwrap();
404        let reparsed = Ait::parse(&buf).unwrap();
405        assert_eq!(ait, reparsed);
406    }
407
408    #[test]
409    fn serialize_round_trip_with_applications() {
410        let desc1: [u8; 2] = [0xAA, 0xBB];
411        let ait = Ait {
412            application_type: 0x0010,
413            test_application_flag: true,
414            version_number: 7,
415            current_next_indicator: true,
416            section_number: 1,
417            last_section_number: 2,
418            common_descriptors: &[0x01, 0x00],
419            applications: vec![
420                AitApplication {
421                    identifier: ApplicationIdentifier {
422                        organisation_id: 0x12345678,
423                        application_id: 0xABCD,
424                    },
425                    control_code: 0x01,
426                    descriptors: &desc1,
427                },
428                AitApplication {
429                    identifier: ApplicationIdentifier {
430                        organisation_id: 0x87654321,
431                        application_id: 0x00EF,
432                    },
433                    control_code: 0x02,
434                    descriptors: &[],
435                },
436            ],
437        };
438        let mut buf = vec![0u8; ait.serialized_len()];
439        ait.serialize_into(&mut buf).unwrap();
440        let reparsed = Ait::parse(&buf).unwrap();
441        assert_eq!(ait, reparsed);
442    }
443}