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