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 super::descriptor_body;
27use crate::error::{Error, Result};
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/// Announcement type — ETSI EN 300 468 Table 19.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53#[non_exhaustive]
54pub enum AnnouncementType {
55    /// 0x00 — Emergency alarm.
56    EmergencyAlarm,
57    /// 0x01 — Road Traffic flash.
58    RoadTrafficFlash,
59    /// 0x02 — Public Transport flash.
60    PublicTransportFlash,
61    /// 0x03 — Warning message.
62    WarningMessage,
63    /// 0x04 — News flash.
64    NewsFlash,
65    /// 0x05 — Weather flash.
66    WeatherFlash,
67    /// 0x06 — Event announcement.
68    EventAnnouncement,
69    /// 0x07 — Personal call.
70    PersonalCall,
71    /// Reserved/unallocated wire value, preserved verbatim for round-trip.
72    Reserved(u8),
73}
74
75impl AnnouncementType {
76    #[must_use]
77    /// Creates a value from a wire byte, preserving every possible
78    /// byte value for lossless round-trip.
79    pub fn from_u8(v: u8) -> Self {
80        match v {
81            0x00 => Self::EmergencyAlarm,
82            0x01 => Self::RoadTrafficFlash,
83            0x02 => Self::PublicTransportFlash,
84            0x03 => Self::WarningMessage,
85            0x04 => Self::NewsFlash,
86            0x05 => Self::WeatherFlash,
87            0x06 => Self::EventAnnouncement,
88            0x07 => Self::PersonalCall,
89            v => Self::Reserved(v),
90        }
91    }
92
93    #[must_use]
94    /// Returns the wire byte for this value.
95    pub fn to_u8(self) -> u8 {
96        match self {
97            Self::EmergencyAlarm => 0x00,
98            Self::RoadTrafficFlash => 0x01,
99            Self::PublicTransportFlash => 0x02,
100            Self::WarningMessage => 0x03,
101            Self::NewsFlash => 0x04,
102            Self::WeatherFlash => 0x05,
103            Self::EventAnnouncement => 0x06,
104            Self::PersonalCall => 0x07,
105            Self::Reserved(v) => v,
106        }
107    }
108
109    #[must_use]
110    /// Returns a human-readable spec name for this value.
111    pub fn name(self) -> &'static str {
112        match self {
113            Self::EmergencyAlarm => "Emergency alarm",
114            Self::RoadTrafficFlash => "Road Traffic flash",
115            Self::PublicTransportFlash => "Public Transport flash",
116            Self::WarningMessage => "Warning message",
117            Self::NewsFlash => "News flash",
118            Self::WeatherFlash => "Weather flash",
119            Self::EventAnnouncement => "Event announcement",
120            Self::PersonalCall => "Personal call",
121            Self::Reserved(_) => "reserved",
122        }
123    }
124}
125dvb_common::impl_spec_display!(AnnouncementType, Reserved);
126
127/// Reference type — ETSI EN 300 468 Table 20.
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize))]
130#[non_exhaustive]
131pub enum ReferenceType {
132    /// 0x00 — broadcast in the usual audio stream of the service.
133    UsualAudioStream,
134    /// 0x01 — broadcast in a separate audio stream that is part of the service.
135    SeparateAudioStream,
136    /// 0x02 — broadcast by a different service within the same DVB transport
137    /// stream.
138    DifferentServiceSameTs,
139    /// 0x03 — broadcast by a different service within a different DVB transport
140    /// stream.
141    DifferentServiceDifferentTs,
142    /// Reserved/unallocated wire value, preserved verbatim for round-trip.
143    Reserved(u8),
144}
145
146impl ReferenceType {
147    #[must_use]
148    /// Creates a value from a wire byte, preserving every possible
149    /// byte value for lossless round-trip.
150    pub fn from_u8(v: u8) -> Self {
151        match v {
152            0x00 => Self::UsualAudioStream,
153            0x01 => Self::SeparateAudioStream,
154            0x02 => Self::DifferentServiceSameTs,
155            0x03 => Self::DifferentServiceDifferentTs,
156            v => Self::Reserved(v),
157        }
158    }
159
160    #[must_use]
161    /// Returns the wire byte for this value.
162    pub fn to_u8(self) -> u8 {
163        match self {
164            Self::UsualAudioStream => 0x00,
165            Self::SeparateAudioStream => 0x01,
166            Self::DifferentServiceSameTs => 0x02,
167            Self::DifferentServiceDifferentTs => 0x03,
168            Self::Reserved(v) => v,
169        }
170    }
171
172    #[must_use]
173    /// Returns a human-readable spec name for this value.
174    pub fn name(self) -> &'static str {
175        match self {
176            Self::UsualAudioStream => "broadcast in the usual audio stream of the service",
177            Self::SeparateAudioStream => {
178                "broadcast in a separate audio stream that is part of the service"
179            }
180            Self::DifferentServiceSameTs => {
181                "broadcast by a different service within the same DVB transport stream"
182            }
183            Self::DifferentServiceDifferentTs => {
184                "broadcast by a different service within a different DVB transport stream"
185            }
186            Self::Reserved(_) => "reserved",
187        }
188    }
189}
190dvb_common::impl_spec_display!(ReferenceType, Reserved);
191
192/// Conditional reference block, present for reference_type 1, 2, 3.
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize))]
195pub struct AnnouncementReference {
196    /// original_network_id of the carrying TS.
197    pub original_network_id: u16,
198    /// transport_stream_id of the carrying TS.
199    pub transport_stream_id: u16,
200    /// service_id carrying the announcement.
201    pub service_id: u16,
202    /// component_tag identifying the elementary stream.
203    pub component_tag: u8,
204}
205
206/// One announcement entry.
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208#[cfg_attr(feature = "serde", derive(serde::Serialize))]
209pub struct AnnouncementEntry {
210    /// 4-bit announcement_type (Table 19).
211    pub announcement_type: AnnouncementType,
212    /// 3-bit reference_type (Table 20).
213    pub reference_type: ReferenceType,
214    /// Reference block, present iff reference_type ∈ {1,2,3}.
215    pub reference: Option<AnnouncementReference>,
216}
217
218/// Announcement Support Descriptor.
219#[derive(Debug, Clone, PartialEq, Eq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize))]
221pub struct AnnouncementSupportDescriptor {
222    /// 16-bit announcement_support_indicator flag field (TS 101 154 C.4.3).
223    pub announcement_support_indicator: u16,
224    /// Announcement entries in wire order.
225    pub entries: Vec<AnnouncementEntry>,
226}
227
228#[inline]
229fn reference_present(reference_type: u8) -> bool {
230    matches!(reference_type, 0x01..=0x03)
231}
232
233impl<'a> Parse<'a> for AnnouncementSupportDescriptor {
234    type Error = crate::error::Error;
235    fn parse(bytes: &'a [u8]) -> Result<Self> {
236        let body = descriptor_body(
237            bytes,
238            TAG,
239            "AnnouncementSupportDescriptor",
240            "unexpected tag for announcement_support_descriptor",
241        )?;
242        if body.len() < INDICATOR_LEN {
243            return Err(Error::InvalidDescriptor {
244                tag: TAG,
245                reason: "body too short (need announcement_support_indicator)",
246            });
247        }
248        let announcement_support_indicator = u16::from_be_bytes([body[0], body[1]]);
249        let mut entries = Vec::new();
250        let mut pos = INDICATOR_LEN;
251        while pos < body.len() {
252            let flags = body[pos];
253            pos += TYPE_BYTE_LEN;
254            // reserved_future_use bit ignored on parse (§5.1).
255            let announcement_type = AnnouncementType::from_u8(
256                (flags & ANNOUNCEMENT_TYPE_MASK) >> ANNOUNCEMENT_TYPE_SHIFT,
257            );
258            let reference_type = ReferenceType::from_u8(flags & REFERENCE_TYPE_MASK);
259            let reference = if reference_present(reference_type.to_u8()) {
260                if pos + REFERENCE_LEN > body.len() {
261                    return Err(Error::InvalidDescriptor {
262                        tag: TAG,
263                        reason: "announcement reference block truncated",
264                    });
265                }
266                let r = AnnouncementReference {
267                    original_network_id: u16::from_be_bytes([body[pos], body[pos + 1]]),
268                    transport_stream_id: u16::from_be_bytes([body[pos + 2], body[pos + 3]]),
269                    service_id: u16::from_be_bytes([body[pos + 4], body[pos + 5]]),
270                    component_tag: body[pos + 6],
271                };
272                pos += REFERENCE_LEN;
273                Some(r)
274            } else {
275                None
276            };
277            entries.push(AnnouncementEntry {
278                announcement_type,
279                reference_type,
280                reference,
281            });
282        }
283        Ok(Self {
284            announcement_support_indicator,
285            entries,
286        })
287    }
288}
289
290impl AnnouncementSupportDescriptor {
291    fn body_len(&self) -> usize {
292        INDICATOR_LEN
293            + self
294                .entries
295                .iter()
296                .map(|e| {
297                    TYPE_BYTE_LEN
298                        + if reference_present(e.reference_type.to_u8()) {
299                            REFERENCE_LEN
300                        } else {
301                            0
302                        }
303                })
304                .sum::<usize>()
305    }
306}
307
308impl Serialize for AnnouncementSupportDescriptor {
309    type Error = crate::error::Error;
310    fn serialized_len(&self) -> usize {
311        HEADER_LEN + self.body_len()
312    }
313
314    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
315        for e in &self.entries {
316            if e.announcement_type.to_u8() > ANNOUNCEMENT_TYPE_MAX {
317                return Err(Error::InvalidDescriptor {
318                    tag: TAG,
319                    reason: "announcement_type exceeds 4 bits",
320                });
321            }
322            if e.reference_type.to_u8() > REFERENCE_TYPE_MAX {
323                return Err(Error::InvalidDescriptor {
324                    tag: TAG,
325                    reason: "reference_type exceeds 3 bits",
326                });
327            }
328            // A reference must be present exactly when reference_type ∈ {1,2,3}.
329            if reference_present(e.reference_type.to_u8()) != e.reference.is_some() {
330                return Err(Error::InvalidDescriptor {
331                    tag: TAG,
332                    reason: "reference presence does not match reference_type",
333                });
334            }
335        }
336        let body_len = self.body_len();
337        if body_len > u8::MAX as usize {
338            return Err(Error::InvalidDescriptor {
339                tag: TAG,
340                reason: "announcement_support_descriptor body exceeds 255 bytes",
341            });
342        }
343        let len = self.serialized_len();
344        if buf.len() < len {
345            return Err(Error::OutputBufferTooSmall {
346                need: len,
347                have: buf.len(),
348            });
349        }
350        buf[0] = TAG;
351        buf[1] = body_len as u8;
352        buf[HEADER_LEN..HEADER_LEN + INDICATOR_LEN]
353            .copy_from_slice(&self.announcement_support_indicator.to_be_bytes());
354        let mut pos = HEADER_LEN + INDICATOR_LEN;
355        for e in &self.entries {
356            // reserved_future_use bit emitted as 1 (§5.1).
357            let flags = ((e.announcement_type.to_u8() << ANNOUNCEMENT_TYPE_SHIFT)
358                & ANNOUNCEMENT_TYPE_MASK)
359                | RESERVED_BIT_MASK
360                | (e.reference_type.to_u8() & REFERENCE_TYPE_MASK);
361            buf[pos] = flags;
362            pos += TYPE_BYTE_LEN;
363            if let Some(r) = &e.reference {
364                buf[pos..pos + 2].copy_from_slice(&r.original_network_id.to_be_bytes());
365                buf[pos + 2..pos + 4].copy_from_slice(&r.transport_stream_id.to_be_bytes());
366                buf[pos + 4..pos + 6].copy_from_slice(&r.service_id.to_be_bytes());
367                buf[pos + 6] = r.component_tag;
368                pos += REFERENCE_LEN;
369            }
370        }
371        Ok(len)
372    }
373}
374impl<'a> crate::traits::DescriptorDef<'a> for AnnouncementSupportDescriptor {
375    const TAG: u8 = TAG;
376    const NAME: &'static str = "ANNOUNCEMENT_SUPPORT";
377}
378
379#[cfg(test)]
380mod tests {
381    use super::*;
382
383    #[test]
384    fn parse_entry_without_reference() {
385        // indicator=0x0040, one entry: type=0x06 (event), reserved=1, ref_type=0x00 (usual audio)
386        // flags = 0110 1 000 = 0x68
387        let bytes = [TAG, 3, 0x00, 0x40, 0x68];
388        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
389        assert_eq!(d.announcement_support_indicator, 0x0040);
390        assert_eq!(d.entries.len(), 1);
391        assert_eq!(
392            d.entries[0].announcement_type,
393            AnnouncementType::EventAnnouncement
394        );
395        assert_eq!(d.entries[0].reference_type, ReferenceType::UsualAudioStream);
396        assert!(d.entries[0].reference.is_none());
397    }
398
399    #[test]
400    fn parse_entry_with_reference() {
401        // indicator=0x0001, entry: type=0x01, ref_type=0x02 → reference present.
402        // flags = 0001 1 010 = 0x1A
403        let bytes = [
404            TAG, 10, 0x00, 0x01,
405            0x1A, // onid=0xAABB, tsid=0xCCDD, sid=0xEEFF, component_tag=0x09
406            0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x09,
407        ];
408        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
409        assert_eq!(d.entries.len(), 1);
410        assert_eq!(
411            d.entries[0].announcement_type,
412            AnnouncementType::RoadTrafficFlash
413        );
414        assert_eq!(
415            d.entries[0].reference_type,
416            ReferenceType::DifferentServiceSameTs
417        );
418        let r = d.entries[0].reference.unwrap();
419        assert_eq!(r.original_network_id, 0xAABB);
420        assert_eq!(r.transport_stream_id, 0xCCDD);
421        assert_eq!(r.service_id, 0xEEFF);
422        assert_eq!(r.component_tag, 0x09);
423    }
424
425    #[test]
426    fn parse_mixed_entries() {
427        // entry1 ref_type=0 (no ref), entry2 ref_type=3 (ref present)
428        let bytes = [
429            TAG, 11, 0x12, 0x34, // indicator
430            0x40, // type=4 ref_type=0
431            0x53, // type=5 reserved=0 ref_type=3 -> 0101 0 011
432            0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
433        ];
434        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
435        assert_eq!(d.entries.len(), 2);
436        assert!(d.entries[0].reference.is_none());
437        assert_eq!(
438            d.entries[1].reference_type,
439            ReferenceType::DifferentServiceDifferentTs
440        );
441        assert!(d.entries[1].reference.is_some());
442    }
443
444    #[test]
445    fn parse_ignores_reserved_bit() {
446        // reserved bit clear (0x60 vs 0x68): still parses.
447        let bytes = [TAG, 3, 0x00, 0x00, 0x60];
448        let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
449        assert_eq!(
450            d.entries[0].announcement_type,
451            AnnouncementType::EventAnnouncement
452        );
453        assert_eq!(d.entries[0].reference_type, ReferenceType::UsualAudioStream);
454    }
455
456    #[test]
457    fn parse_rejects_wrong_tag() {
458        assert!(matches!(
459            AnnouncementSupportDescriptor::parse(&[0x6F, 2, 0, 0]).unwrap_err(),
460            Error::InvalidDescriptor { tag: 0x6F, .. }
461        ));
462    }
463
464    #[test]
465    fn parse_rejects_body_too_short_for_indicator() {
466        assert!(matches!(
467            AnnouncementSupportDescriptor::parse(&[TAG, 1, 0]).unwrap_err(),
468            Error::InvalidDescriptor { tag: TAG, .. }
469        ));
470    }
471
472    #[test]
473    fn parse_rejects_reference_truncated() {
474        // ref_type=1 but no reference bytes.
475        let bytes = [TAG, 3, 0x00, 0x00, 0x09]; // 0000 1 001
476        assert!(matches!(
477            AnnouncementSupportDescriptor::parse(&bytes).unwrap_err(),
478            Error::InvalidDescriptor { tag: TAG, .. }
479        ));
480    }
481
482    #[test]
483    fn parse_rejects_buffer_shorter_than_length() {
484        let bytes = [TAG, 5, 0x00, 0x00];
485        assert!(matches!(
486            AnnouncementSupportDescriptor::parse(&bytes).unwrap_err(),
487            Error::BufferTooShort { .. }
488        ));
489    }
490
491    #[test]
492    fn serialize_round_trip_mixed() {
493        let d = AnnouncementSupportDescriptor {
494            announcement_support_indicator: 0xBEEF,
495            entries: vec![
496                AnnouncementEntry {
497                    announcement_type: AnnouncementType::NewsFlash,
498                    reference_type: ReferenceType::UsualAudioStream,
499                    reference: None,
500                },
501                AnnouncementEntry {
502                    announcement_type: AnnouncementType::RoadTrafficFlash,
503                    reference_type: ReferenceType::DifferentServiceSameTs,
504                    reference: Some(AnnouncementReference {
505                        original_network_id: 0xAABB,
506                        transport_stream_id: 0xCCDD,
507                        service_id: 0xEEFF,
508                        component_tag: 0x09,
509                    }),
510                },
511            ],
512        };
513        let mut buf = vec![0u8; d.serialized_len()];
514        d.serialize_into(&mut buf).unwrap();
515        assert_eq!(AnnouncementSupportDescriptor::parse(&buf).unwrap(), d);
516    }
517
518    #[test]
519    fn serialize_rejects_too_small_buffer() {
520        let d = AnnouncementSupportDescriptor {
521            announcement_support_indicator: 0,
522            entries: vec![],
523        };
524        let mut buf = vec![0u8; 3];
525        assert!(matches!(
526            d.serialize_into(&mut buf).unwrap_err(),
527            Error::OutputBufferTooSmall { .. }
528        ));
529    }
530
531    #[test]
532    fn serialize_rejects_over_range_announcement_type() {
533        let d = AnnouncementSupportDescriptor {
534            announcement_support_indicator: 0,
535            entries: vec![AnnouncementEntry {
536                announcement_type: AnnouncementType::Reserved(0x10), // 5 bits
537                reference_type: ReferenceType::UsualAudioStream,
538                reference: None,
539            }],
540        };
541        let mut buf = vec![0u8; d.serialized_len()];
542        assert!(matches!(
543            d.serialize_into(&mut buf).unwrap_err(),
544            Error::InvalidDescriptor { tag: TAG, .. }
545        ));
546    }
547
548    #[test]
549    fn serialize_rejects_reference_mismatch() {
550        // ref_type=2 demands a reference, but None supplied.
551        let d = AnnouncementSupportDescriptor {
552            announcement_support_indicator: 0,
553            entries: vec![AnnouncementEntry {
554                announcement_type: AnnouncementType::Reserved(0),
555                reference_type: ReferenceType::DifferentServiceSameTs,
556                reference: None,
557            }],
558        };
559        let mut buf = vec![0u8; d.serialized_len()];
560        assert!(matches!(
561            d.serialize_into(&mut buf).unwrap_err(),
562            Error::InvalidDescriptor { tag: TAG, .. }
563        ));
564    }
565
566    #[cfg(feature = "serde")]
567    #[test]
568    fn serde_round_trip() {
569        let d = AnnouncementSupportDescriptor {
570            announcement_support_indicator: 0xBEEF,
571            entries: vec![AnnouncementEntry {
572                announcement_type: AnnouncementType::RoadTrafficFlash,
573                reference_type: ReferenceType::DifferentServiceSameTs,
574                reference: Some(AnnouncementReference {
575                    original_network_id: 0xAABB,
576                    transport_stream_id: 0xCCDD,
577                    service_id: 0xEEFF,
578                    component_tag: 0x09,
579                }),
580            }],
581        };
582        let json = serde_json::to_string(&d).unwrap();
583        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
584        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
585    }
586
587    #[test]
588    fn announcement_type_full_range_round_trip() {
589        for b in 0..=0xFF_u8 {
590            let at = AnnouncementType::from_u8(b);
591            assert_eq!(at.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
592        }
593    }
594
595    #[test]
596    fn reference_type_full_range_round_trip() {
597        for b in 0..=0xFF_u8 {
598            let rt = ReferenceType::from_u8(b);
599            assert_eq!(rt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
600        }
601    }
602}