Skip to main content

dvb_si/descriptors/
announcement_support.rs

1//! Announcement Support Descriptor — ETSI EN 300 468 §6.2.3 (tag 0x6E, Table 17, PDF p. 56).
2//!
3//! Carried inside the SDT. Signals which announcement types (emergency,
4//! traffic, news, weather, …) a service supports and where each is carried.
5//! Body layout (Table 17):
6//!
7//! ```text
8//! announcement_support_indicator 16
9//! for (i=0;i<N;i++) {
10//!   announcement_type   4
11//!   reserved_future_use 1
12//!   reference_type      3
13//!   if (reference_type == 0x01 || 0x02 || 0x03) {
14//!     original_network_id 16
15//!     transport_stream_id 16
16//!     service_id          16
17//!     component_tag        8
18//!   }
19//! }
20//! ```
21//!
22//! The conditional reference fields (onid/tsid/sid/component_tag) are present
23//! only for reference_type 1, 2, 3 (verified against PDF p. 56). They are
24//! modelled as an `Option<AnnouncementReference>`.
25
26use crate::error::{Error, Result};
27use crate::traits::Descriptor;
28use dvb_common::{Parse, Serialize};
29
30/// Descriptor tag for announcement_support_descriptor.
31pub const TAG: u8 = 0x6E;
32/// Length of the header (tag byte + length byte).
33pub const HEADER_LEN: usize = 2;
34/// Length of the announcement_support_indicator field.
35pub const INDICATOR_LEN: usize = 2;
36/// Length of the announcement_type/reserved/reference_type byte.
37pub const TYPE_BYTE_LEN: usize = 1;
38/// Length of the conditional reference block: onid(2)+tsid(2)+sid(2)+component_tag(1).
39pub const REFERENCE_LEN: usize = 7;
40
41const ANNOUNCEMENT_TYPE_MASK: u8 = 0xF0;
42const ANNOUNCEMENT_TYPE_SHIFT: u8 = 4;
43const RESERVED_BIT_MASK: u8 = 0x08;
44const REFERENCE_TYPE_MASK: u8 = 0x07;
45/// Max value of the 4-bit announcement_type field.
46pub const ANNOUNCEMENT_TYPE_MAX: u8 = 0x0F;
47/// Max value of the 3-bit reference_type field.
48pub const REFERENCE_TYPE_MAX: u8 = 0x07;
49
50/// Conditional reference block, present for reference_type 1, 2, 3.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53pub struct AnnouncementReference {
54    /// original_network_id of the carrying TS.
55    pub original_network_id: u16,
56    /// transport_stream_id of the carrying TS.
57    pub transport_stream_id: u16,
58    /// service_id carrying the announcement.
59    pub service_id: u16,
60    /// component_tag identifying the elementary stream.
61    pub component_tag: u8,
62}
63
64/// One announcement entry.
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize))]
67pub struct AnnouncementEntry {
68    /// 4-bit announcement_type (Table 19).
69    pub announcement_type: u8,
70    /// 3-bit reference_type (Table 20).
71    pub reference_type: u8,
72    /// Reference block, present iff reference_type ∈ {1,2,3}.
73    pub reference: Option<AnnouncementReference>,
74}
75
76/// Announcement Support Descriptor.
77#[derive(Debug, Clone, PartialEq, Eq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize))]
79pub struct AnnouncementSupportDescriptor {
80    /// 16-bit announcement_support_indicator flag field (TS 101 154 C.4.3).
81    pub announcement_support_indicator: u16,
82    /// Announcement entries in wire order.
83    pub entries: Vec<AnnouncementEntry>,
84}
85
86#[inline]
87fn reference_present(reference_type: u8) -> bool {
88    matches!(reference_type, 0x01..=0x03)
89}
90
91impl<'a> Parse<'a> for AnnouncementSupportDescriptor {
92    type Error = crate::error::Error;
93    fn parse(bytes: &'a [u8]) -> Result<Self> {
94        if bytes.len() < HEADER_LEN {
95            return Err(Error::BufferTooShort {
96                need: HEADER_LEN,
97                have: bytes.len(),
98                what: "AnnouncementSupportDescriptor header",
99            });
100        }
101        if bytes[0] != TAG {
102            return Err(Error::InvalidDescriptor {
103                tag: bytes[0],
104                reason: "unexpected tag for announcement_support_descriptor",
105            });
106        }
107        let length = bytes[1] as usize;
108        let end = HEADER_LEN + length;
109        if bytes.len() < end {
110            return Err(Error::BufferTooShort {
111                need: end,
112                have: bytes.len(),
113                what: "AnnouncementSupportDescriptor body",
114            });
115        }
116        if length < INDICATOR_LEN {
117            return Err(Error::InvalidDescriptor {
118                tag: TAG,
119                reason: "body too short (need announcement_support_indicator)",
120            });
121        }
122        let body = &bytes[HEADER_LEN..end];
123        let announcement_support_indicator = u16::from_be_bytes([body[0], body[1]]);
124        let mut entries = Vec::new();
125        let mut pos = INDICATOR_LEN;
126        while pos < body.len() {
127            let flags = body[pos];
128            pos += TYPE_BYTE_LEN;
129            // reserved_future_use bit ignored on parse (§5.1).
130            let announcement_type = (flags & ANNOUNCEMENT_TYPE_MASK) >> ANNOUNCEMENT_TYPE_SHIFT;
131            let reference_type = flags & REFERENCE_TYPE_MASK;
132            let reference = if reference_present(reference_type) {
133                if pos + REFERENCE_LEN > body.len() {
134                    return Err(Error::InvalidDescriptor {
135                        tag: TAG,
136                        reason: "announcement reference block truncated",
137                    });
138                }
139                let r = AnnouncementReference {
140                    original_network_id: u16::from_be_bytes([body[pos], body[pos + 1]]),
141                    transport_stream_id: u16::from_be_bytes([body[pos + 2], body[pos + 3]]),
142                    service_id: u16::from_be_bytes([body[pos + 4], body[pos + 5]]),
143                    component_tag: body[pos + 6],
144                };
145                pos += REFERENCE_LEN;
146                Some(r)
147            } else {
148                None
149            };
150            entries.push(AnnouncementEntry {
151                announcement_type,
152                reference_type,
153                reference,
154            });
155        }
156        Ok(Self {
157            announcement_support_indicator,
158            entries,
159        })
160    }
161}
162
163impl AnnouncementSupportDescriptor {
164    fn body_len(&self) -> usize {
165        INDICATOR_LEN
166            + self
167                .entries
168                .iter()
169                .map(|e| {
170                    TYPE_BYTE_LEN
171                        + if reference_present(e.reference_type) {
172                            REFERENCE_LEN
173                        } else {
174                            0
175                        }
176                })
177                .sum::<usize>()
178    }
179}
180
181impl Serialize for AnnouncementSupportDescriptor {
182    type Error = crate::error::Error;
183    fn serialized_len(&self) -> usize {
184        HEADER_LEN + self.body_len()
185    }
186
187    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
188        for e in &self.entries {
189            if e.announcement_type > ANNOUNCEMENT_TYPE_MAX {
190                return Err(Error::InvalidDescriptor {
191                    tag: TAG,
192                    reason: "announcement_type exceeds 4 bits",
193                });
194            }
195            if e.reference_type > REFERENCE_TYPE_MAX {
196                return Err(Error::InvalidDescriptor {
197                    tag: TAG,
198                    reason: "reference_type exceeds 3 bits",
199                });
200            }
201            // A reference must be present exactly when reference_type ∈ {1,2,3}.
202            if reference_present(e.reference_type) != e.reference.is_some() {
203                return Err(Error::InvalidDescriptor {
204                    tag: TAG,
205                    reason: "reference presence does not match reference_type",
206                });
207            }
208        }
209        let body_len = self.body_len();
210        if body_len > u8::MAX as usize {
211            return Err(Error::InvalidDescriptor {
212                tag: TAG,
213                reason: "announcement_support_descriptor body exceeds 255 bytes",
214            });
215        }
216        let len = self.serialized_len();
217        if buf.len() < len {
218            return Err(Error::OutputBufferTooSmall {
219                need: len,
220                have: buf.len(),
221            });
222        }
223        buf[0] = TAG;
224        buf[1] = body_len as u8;
225        buf[HEADER_LEN..HEADER_LEN + INDICATOR_LEN]
226            .copy_from_slice(&self.announcement_support_indicator.to_be_bytes());
227        let mut pos = HEADER_LEN + INDICATOR_LEN;
228        for e in &self.entries {
229            // reserved_future_use bit emitted as 1 (§5.1).
230            let flags = ((e.announcement_type << ANNOUNCEMENT_TYPE_SHIFT) & ANNOUNCEMENT_TYPE_MASK)
231                | RESERVED_BIT_MASK
232                | (e.reference_type & REFERENCE_TYPE_MASK);
233            buf[pos] = flags;
234            pos += TYPE_BYTE_LEN;
235            if let Some(r) = &e.reference {
236                buf[pos..pos + 2].copy_from_slice(&r.original_network_id.to_be_bytes());
237                buf[pos + 2..pos + 4].copy_from_slice(&r.transport_stream_id.to_be_bytes());
238                buf[pos + 4..pos + 6].copy_from_slice(&r.service_id.to_be_bytes());
239                buf[pos + 6] = r.component_tag;
240                pos += REFERENCE_LEN;
241            }
242        }
243        Ok(len)
244    }
245}
246
247impl<'a> Descriptor<'a> for AnnouncementSupportDescriptor {
248    const TAG: u8 = TAG;
249    fn descriptor_length(&self) -> u8 {
250        self.body_len() as u8
251    }
252}
253
254impl<'a> crate::traits::DescriptorDef<'a> for AnnouncementSupportDescriptor {
255    const TAG: u8 = TAG;
256    const NAME: &'static str = "ANNOUNCEMENT_SUPPORT";
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn parse_entry_without_reference() {
265        // indicator=0x0040, one entry: type=0x06 (event), reserved=1, ref_type=0x00 (usual audio)
266        // flags = 0110 1 000 = 0x68
267        let bytes = [TAG, 3, 0x00, 0x40, 0x68];
268        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
269        assert_eq!(d.announcement_support_indicator, 0x0040);
270        assert_eq!(d.entries.len(), 1);
271        assert_eq!(d.entries[0].announcement_type, 0x06);
272        assert_eq!(d.entries[0].reference_type, 0x00);
273        assert!(d.entries[0].reference.is_none());
274    }
275
276    #[test]
277    fn parse_entry_with_reference() {
278        // indicator=0x0001, entry: type=0x01, ref_type=0x02 → reference present.
279        // flags = 0001 1 010 = 0x1A
280        let bytes = [
281            TAG, 10, 0x00, 0x01,
282            0x1A, // onid=0xAABB, tsid=0xCCDD, sid=0xEEFF, component_tag=0x09
283            0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x09,
284        ];
285        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
286        assert_eq!(d.entries.len(), 1);
287        assert_eq!(d.entries[0].announcement_type, 0x01);
288        assert_eq!(d.entries[0].reference_type, 0x02);
289        let r = d.entries[0].reference.unwrap();
290        assert_eq!(r.original_network_id, 0xAABB);
291        assert_eq!(r.transport_stream_id, 0xCCDD);
292        assert_eq!(r.service_id, 0xEEFF);
293        assert_eq!(r.component_tag, 0x09);
294    }
295
296    #[test]
297    fn parse_mixed_entries() {
298        // entry1 ref_type=0 (no ref), entry2 ref_type=3 (ref present)
299        let bytes = [
300            TAG, 11, 0x12, 0x34, // indicator
301            0x40, // type=4 ref_type=0
302            0x53, // type=5 reserved=0 ref_type=3 -> 0101 0 011
303            0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
304        ];
305        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
306        assert_eq!(d.entries.len(), 2);
307        assert!(d.entries[0].reference.is_none());
308        assert_eq!(d.entries[1].reference_type, 0x03);
309        assert!(d.entries[1].reference.is_some());
310    }
311
312    #[test]
313    fn parse_ignores_reserved_bit() {
314        // reserved bit clear (0x60 vs 0x68): still parses.
315        let bytes = [TAG, 3, 0x00, 0x00, 0x60];
316        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
317        assert_eq!(d.entries[0].announcement_type, 0x06);
318        assert_eq!(d.entries[0].reference_type, 0x00);
319    }
320
321    #[test]
322    fn parse_rejects_wrong_tag() {
323        assert!(matches!(
324            AnnouncementSupportDescriptor::parse(&[0x6F, 2, 0, 0]).unwrap_err(),
325            Error::InvalidDescriptor { tag: 0x6F, .. }
326        ));
327    }
328
329    #[test]
330    fn parse_rejects_body_too_short_for_indicator() {
331        assert!(matches!(
332            AnnouncementSupportDescriptor::parse(&[TAG, 1, 0]).unwrap_err(),
333            Error::InvalidDescriptor { tag: TAG, .. }
334        ));
335    }
336
337    #[test]
338    fn parse_rejects_reference_truncated() {
339        // ref_type=1 but no reference bytes.
340        let bytes = [TAG, 3, 0x00, 0x00, 0x09]; // 0000 1 001
341        assert!(matches!(
342            AnnouncementSupportDescriptor::parse(&bytes).unwrap_err(),
343            Error::InvalidDescriptor { tag: TAG, .. }
344        ));
345    }
346
347    #[test]
348    fn parse_rejects_buffer_shorter_than_length() {
349        let bytes = [TAG, 5, 0x00, 0x00];
350        assert!(matches!(
351            AnnouncementSupportDescriptor::parse(&bytes).unwrap_err(),
352            Error::BufferTooShort { .. }
353        ));
354    }
355
356    #[test]
357    fn serialize_round_trip_mixed() {
358        let d = AnnouncementSupportDescriptor {
359            announcement_support_indicator: 0xBEEF,
360            entries: vec![
361                AnnouncementEntry {
362                    announcement_type: 0x04,
363                    reference_type: 0x00,
364                    reference: None,
365                },
366                AnnouncementEntry {
367                    announcement_type: 0x01,
368                    reference_type: 0x02,
369                    reference: Some(AnnouncementReference {
370                        original_network_id: 0xAABB,
371                        transport_stream_id: 0xCCDD,
372                        service_id: 0xEEFF,
373                        component_tag: 0x09,
374                    }),
375                },
376            ],
377        };
378        let mut buf = vec![0u8; d.serialized_len()];
379        d.serialize_into(&mut buf).unwrap();
380        assert_eq!(AnnouncementSupportDescriptor::parse(&buf).unwrap(), d);
381    }
382
383    #[test]
384    fn serialize_rejects_too_small_buffer() {
385        let d = AnnouncementSupportDescriptor {
386            announcement_support_indicator: 0,
387            entries: vec![],
388        };
389        let mut buf = vec![0u8; 3];
390        assert!(matches!(
391            d.serialize_into(&mut buf).unwrap_err(),
392            Error::OutputBufferTooSmall { .. }
393        ));
394    }
395
396    #[test]
397    fn serialize_rejects_over_range_announcement_type() {
398        let d = AnnouncementSupportDescriptor {
399            announcement_support_indicator: 0,
400            entries: vec![AnnouncementEntry {
401                announcement_type: 0x10, // 5 bits
402                reference_type: 0,
403                reference: None,
404            }],
405        };
406        let mut buf = vec![0u8; d.serialized_len()];
407        assert!(matches!(
408            d.serialize_into(&mut buf).unwrap_err(),
409            Error::InvalidDescriptor { tag: TAG, .. }
410        ));
411    }
412
413    #[test]
414    fn serialize_rejects_reference_mismatch() {
415        // ref_type=2 demands a reference, but None supplied.
416        let d = AnnouncementSupportDescriptor {
417            announcement_support_indicator: 0,
418            entries: vec![AnnouncementEntry {
419                announcement_type: 0,
420                reference_type: 0x02,
421                reference: None,
422            }],
423        };
424        let mut buf = vec![0u8; d.serialized_len()];
425        assert!(matches!(
426            d.serialize_into(&mut buf).unwrap_err(),
427            Error::InvalidDescriptor { tag: TAG, .. }
428        ));
429    }
430
431    #[cfg(feature = "serde")]
432    #[test]
433    fn serde_round_trip() {
434        let d = AnnouncementSupportDescriptor {
435            announcement_support_indicator: 0xBEEF,
436            entries: vec![AnnouncementEntry {
437                announcement_type: 0x01,
438                reference_type: 0x02,
439                reference: Some(AnnouncementReference {
440                    original_network_id: 0xAABB,
441                    transport_stream_id: 0xCCDD,
442                    service_id: 0xEEFF,
443                    component_tag: 0x09,
444                }),
445            }],
446        };
447        let json = serde_json::to_string(&d).unwrap();
448        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
449        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
450    }
451}