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