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
255impl<'a> crate::traits::TableDef<'a> for Ait<'a> {
256    const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
257    const NAME: &'static str = "APPLICATION_INFORMATION";
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    fn build_ait(
265        application_type: u16,
266        test_flag: bool,
267        version: u8,
268        common_descriptors: &[u8],
269        applications: &[(u32, u16, u8, Vec<u8>)],
270    ) -> Vec<u8> {
271        let app_bytes: usize = applications
272            .iter()
273            .map(|(_, _, _, d)| APP_HEADER_LEN + d.len())
274            .sum();
275        let section_length: u16 = (EXTENSION_HEADER_LEN
276            + COMMON_DESC_LEN_BYTES
277            + common_descriptors.len()
278            + APP_LOOP_LEN_BYTES
279            + app_bytes
280            + CRC_LEN) as u16;
281        let mut v = vec![
282            TABLE_ID,
283            0xB0 | ((section_length >> 8) as u8 & 0x0F),
284            (section_length & 0xFF) as u8,
285            (u8::from(test_flag) << 7) | ((application_type >> 8) as u8 & 0x7F),
286            (application_type & 0xFF) as u8,
287            0xC0 | ((version & 0x1F) << 1) | 0x01,
288            0,
289            0,
290        ];
291        let cdl = common_descriptors.len() as u16;
292        v.push(0xF0 | ((cdl >> 8) as u8 & 0x0F));
293        v.push((cdl & 0xFF) as u8);
294        v.extend_from_slice(common_descriptors);
295        let apl = app_bytes as u16;
296        v.push(0xF0 | ((apl >> 8) as u8 & 0x0F));
297        v.push((apl & 0xFF) as u8);
298        for &(org_id, app_id, cc, ref desc) in applications {
299            v.extend_from_slice(&org_id.to_be_bytes());
300            v.extend_from_slice(&app_id.to_be_bytes());
301            v.push(cc);
302            let adl = desc.len() as u16;
303            v.push(0xF0 | ((adl >> 8) as u8 & 0x0F));
304            v.push((adl & 0xFF) as u8);
305            v.extend_from_slice(desc);
306        }
307        v.extend_from_slice(&[0, 0, 0, 0]);
308        v
309    }
310
311    #[test]
312    fn parse_rejects_wrong_table_id() {
313        let mut bytes = build_ait(0x0010, false, 0, &[], &[]);
314        bytes[0] = 0x00;
315        let err = Ait::parse(&bytes).unwrap_err();
316        assert!(matches!(
317            err,
318            Error::UnexpectedTableId { table_id: 0x00, .. }
319        ));
320    }
321
322    #[test]
323    fn parse_rejects_short_buffer() {
324        let err = Ait::parse(&[0x74, 0x00]).unwrap_err();
325        assert!(matches!(err, Error::BufferTooShort { .. }));
326    }
327
328    #[test]
329    fn parse_empty_ait_no_applications() {
330        let bytes = build_ait(0x0010, false, 5, &[], &[]);
331        let ait = Ait::parse(&bytes).expect("parse");
332        assert_eq!(ait.application_type, 0x0010);
333        assert!(!ait.test_application_flag);
334        assert_eq!(ait.version_number, 5);
335        assert!(ait.current_next_indicator);
336        assert_eq!(ait.section_number, 0);
337        assert_eq!(ait.last_section_number, 0);
338        assert_eq!(ait.common_descriptors.len(), 0);
339        assert_eq!(ait.applications.len(), 0);
340    }
341
342    #[test]
343    fn parse_test_application_flag_extracted() {
344        let bytes = build_ait(0x0010, true, 0, &[], &[]);
345        let ait = Ait::parse(&bytes).unwrap();
346        assert!(ait.test_application_flag);
347    }
348
349    #[test]
350    fn parse_common_descriptors_preserved() {
351        let desc = vec![0x00, 0x02, 0xAA, 0xBB];
352        let bytes = build_ait(0x0010, false, 0, &desc, &[]);
353        let ait = Ait::parse(&bytes).unwrap();
354        assert_eq!(ait.common_descriptors, &desc[..]);
355    }
356
357    #[test]
358    fn parse_single_application() {
359        let desc = vec![0x02, 0x03, 0xCC, 0xDD, 0xEE];
360        let bytes = build_ait(
361            0x0010,
362            false,
363            0,
364            &[],
365            &[(0x12345678, 0xABCD, 0x01, desc.clone())],
366        );
367        let ait = Ait::parse(&bytes).unwrap();
368        assert_eq!(ait.applications.len(), 1);
369        assert_eq!(ait.applications[0].identifier.organisation_id, 0x12345678);
370        assert_eq!(ait.applications[0].identifier.application_id, 0xABCD);
371        assert_eq!(ait.applications[0].control_code, 0x01);
372        assert_eq!(ait.applications[0].descriptors, &desc[..]);
373    }
374
375    #[test]
376    fn parse_multiple_applications_preserve_order() {
377        let bytes = build_ait(
378            0x0010,
379            false,
380            0,
381            &[],
382            &[
383                (0x00000001, 0x0001, 0x01, vec![]),
384                (0x00000002, 0x0002, 0x02, vec![0x01]),
385                (0x00000003, 0x0003, 0x03, vec![0x02, 0x03]),
386            ],
387        );
388        let ait = Ait::parse(&bytes).unwrap();
389        assert_eq!(ait.applications.len(), 3);
390        assert_eq!(ait.applications[0].identifier.organisation_id, 1);
391        assert_eq!(ait.applications[1].identifier.organisation_id, 2);
392        assert_eq!(ait.applications[2].identifier.organisation_id, 3);
393    }
394
395    #[test]
396    fn serialize_round_trip_empty() {
397        let ait = Ait {
398            application_type: 0x0010,
399            test_application_flag: false,
400            version_number: 3,
401            current_next_indicator: true,
402            section_number: 0,
403            last_section_number: 0,
404            common_descriptors: &[],
405            applications: vec![],
406        };
407        let mut buf = vec![0u8; ait.serialized_len()];
408        ait.serialize_into(&mut buf).unwrap();
409        let reparsed = Ait::parse(&buf).unwrap();
410        assert_eq!(ait, reparsed);
411    }
412
413    #[test]
414    fn serialize_round_trip_with_applications() {
415        let desc1: [u8; 2] = [0xAA, 0xBB];
416        let ait = Ait {
417            application_type: 0x0010,
418            test_application_flag: true,
419            version_number: 7,
420            current_next_indicator: true,
421            section_number: 1,
422            last_section_number: 2,
423            common_descriptors: &[0x01, 0x00],
424            applications: vec![
425                AitApplication {
426                    identifier: ApplicationIdentifier {
427                        organisation_id: 0x12345678,
428                        application_id: 0xABCD,
429                    },
430                    control_code: 0x01,
431                    descriptors: &desc1,
432                },
433                AitApplication {
434                    identifier: ApplicationIdentifier {
435                        organisation_id: 0x87654321,
436                        application_id: 0x00EF,
437                    },
438                    control_code: 0x02,
439                    descriptors: &[],
440                },
441            ],
442        };
443        let mut buf = vec![0u8; ait.serialized_len()];
444        ait.serialize_into(&mut buf).unwrap();
445        let reparsed = Ait::parse(&buf).unwrap();
446        assert_eq!(ait, reparsed);
447    }
448}