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