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 =
110            (((bytes[8] & 0x0F) as usize) << 8) | bytes[9] as usize;
111        let common_desc_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + COMMON_DESC_LEN_BYTES;
112        let common_desc_end = common_desc_start + common_descriptors_length;
113        let app_loop_end = total - CRC_LEN;
114        if common_desc_end > app_loop_end {
115            return Err(Error::SectionLengthOverflow {
116                declared: common_descriptors_length,
117                available: app_loop_end - common_desc_start,
118            });
119        }
120        let common_descriptors = &bytes[common_desc_start..common_desc_end];
121
122        let app_loop_length =
123            (((bytes[common_desc_end] & 0x0F) as usize) << 8) | bytes[common_desc_end + 1] as usize;
124        let app_loop_start = common_desc_end + APP_LOOP_LEN_BYTES;
125        let app_loop_actual_end = app_loop_start + app_loop_length;
126        if app_loop_actual_end > app_loop_end {
127            return Err(Error::SectionLengthOverflow {
128                declared: app_loop_length,
129                available: app_loop_end - app_loop_start,
130            });
131        }
132
133        let mut applications = Vec::new();
134        let mut pos = app_loop_start;
135        while pos + APP_HEADER_LEN <= app_loop_actual_end {
136            let organisation_id = ((bytes[pos] as u32) << 24)
137                | ((bytes[pos + 1] as u32) << 16)
138                | ((bytes[pos + 2] as u32) << 8)
139                | (bytes[pos + 3] as u32);
140            let application_id = u16::from_be_bytes([bytes[pos + 4], bytes[pos + 5]]);
141            let control_code = bytes[pos + 6];
142            let app_desc_length =
143                (((bytes[pos + 7] & 0x0F) as usize) << 8) | bytes[pos + 8] as usize;
144            let app_desc_start = pos + APP_HEADER_LEN;
145            let app_desc_end = app_desc_start + app_desc_length;
146            if app_desc_end > app_loop_actual_end {
147                return Err(Error::SectionLengthOverflow {
148                    declared: app_desc_length,
149                    available: app_loop_actual_end - app_desc_start,
150                });
151            }
152            applications.push(AitApplication {
153                identifier: ApplicationIdentifier {
154                    organisation_id,
155                    application_id,
156                },
157                control_code,
158                descriptors: &bytes[app_desc_start..app_desc_end],
159            });
160            pos = app_desc_end;
161        }
162
163        Ok(Ait {
164            application_type,
165            test_application_flag,
166            version_number,
167            current_next_indicator,
168            section_number,
169            last_section_number,
170            common_descriptors,
171            applications,
172        })
173    }
174}
175
176impl Serialize for Ait<'_> {
177    type Error = crate::error::Error;
178    fn serialized_len(&self) -> usize {
179        let app_bytes: usize = self
180            .applications
181            .iter()
182            .map(|a| APP_HEADER_LEN + a.descriptors.len())
183            .sum();
184        MIN_HEADER_LEN
185            + EXTENSION_HEADER_LEN
186            + COMMON_DESC_LEN_BYTES
187            + self.common_descriptors.len()
188            + APP_LOOP_LEN_BYTES
189            + app_bytes
190            + CRC_LEN
191    }
192
193    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
194        let len = self.serialized_len();
195        if buf.len() < len {
196            return Err(Error::OutputBufferTooSmall {
197                need: len,
198                have: buf.len(),
199            });
200        }
201
202        let section_length: u16 = (len - MIN_HEADER_LEN) as u16;
203        buf[0] = TABLE_ID;
204        buf[1] = 0xB0 | ((section_length >> 8) as u8 & 0x0F);
205        buf[2] = (section_length & 0xFF) as u8;
206        buf[3] = (u8::from(self.test_application_flag) << 7)
207            | ((self.application_type >> 8) as u8 & 0x7F);
208        buf[4] = (self.application_type & 0xFF) as u8;
209        buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
210        buf[6] = self.section_number;
211        buf[7] = self.last_section_number;
212
213        let cdl = self.common_descriptors.len() as u16;
214        buf[8] = 0xF0 | ((cdl >> 8) as u8 & 0x0F);
215        buf[9] = (cdl & 0xFF) as u8;
216
217        let common_desc_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + COMMON_DESC_LEN_BYTES;
218        buf[common_desc_start..common_desc_start + self.common_descriptors.len()]
219            .copy_from_slice(self.common_descriptors);
220
221        let app_loop_start = common_desc_start + self.common_descriptors.len();
222        let app_bytes: usize = self
223            .applications
224            .iter()
225            .map(|a| APP_HEADER_LEN + a.descriptors.len())
226            .sum();
227        let apl = app_bytes as u16;
228        buf[app_loop_start] = 0xF0 | ((apl >> 8) as u8 & 0x0F);
229        buf[app_loop_start + 1] = (apl & 0xFF) as u8;
230
231        let mut pos = app_loop_start + APP_LOOP_LEN_BYTES;
232        for app in &self.applications {
233            buf[pos..pos + 4].copy_from_slice(&app.identifier.organisation_id.to_be_bytes());
234            buf[pos + 4..pos + 6].copy_from_slice(&app.identifier.application_id.to_be_bytes());
235            buf[pos + 6] = app.control_code;
236            let adl = app.descriptors.len() as u16;
237            buf[pos + 7] = 0xF0 | ((adl >> 8) as u8 & 0x0F);
238            buf[pos + 8] = (adl & 0xFF) as u8;
239            let desc_start = pos + APP_HEADER_LEN;
240            buf[desc_start..desc_start + app.descriptors.len()].copy_from_slice(app.descriptors);
241            pos = desc_start + app.descriptors.len();
242        }
243
244        let crc_pos = len - CRC_LEN;
245        let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
246        buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
247        Ok(len)
248    }
249}
250
251impl<'a> Table<'a> for Ait<'a> {
252    const TABLE_ID: u8 = TABLE_ID;
253    const PID: u16 = PID;
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259
260    fn build_ait(
261        application_type: u16,
262        test_flag: bool,
263        version: u8,
264        common_descriptors: &[u8],
265        applications: &[(u32, u16, u8, Vec<u8>)],
266    ) -> Vec<u8> {
267        let app_bytes: usize = applications
268            .iter()
269            .map(|(_, _, _, d)| APP_HEADER_LEN + d.len())
270            .sum();
271        let section_length: u16 = (EXTENSION_HEADER_LEN
272            + COMMON_DESC_LEN_BYTES
273            + common_descriptors.len()
274            + APP_LOOP_LEN_BYTES
275            + app_bytes
276            + CRC_LEN) as u16;
277        let mut v = vec![
278            TABLE_ID,
279            0xB0 | ((section_length >> 8) as u8 & 0x0F),
280            (section_length & 0xFF) as u8,
281            (u8::from(test_flag) << 7) | ((application_type >> 8) as u8 & 0x7F),
282            (application_type & 0xFF) as u8,
283            0xC0 | ((version & 0x1F) << 1) | 0x01,
284            0,
285            0,
286        ];
287        let cdl = common_descriptors.len() as u16;
288        v.push(0xF0 | ((cdl >> 8) as u8 & 0x0F));
289        v.push((cdl & 0xFF) as u8);
290        v.extend_from_slice(common_descriptors);
291        let apl = app_bytes as u16;
292        v.push(0xF0 | ((apl >> 8) as u8 & 0x0F));
293        v.push((apl & 0xFF) as u8);
294        for &(org_id, app_id, cc, ref desc) in applications {
295            v.extend_from_slice(&org_id.to_be_bytes());
296            v.extend_from_slice(&app_id.to_be_bytes());
297            v.push(cc);
298            let adl = desc.len() as u16;
299            v.push(0xF0 | ((adl >> 8) as u8 & 0x0F));
300            v.push((adl & 0xFF) as u8);
301            v.extend_from_slice(desc);
302        }
303        v.extend_from_slice(&[0, 0, 0, 0]);
304        v
305    }
306
307    #[test]
308    fn parse_rejects_wrong_table_id() {
309        let mut bytes = build_ait(0x0010, false, 0, &[], &[]);
310        bytes[0] = 0x00;
311        let err = Ait::parse(&bytes).unwrap_err();
312        assert!(
313            matches!(err, 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}