1use super::descriptor_body;
27use crate::error::{Error, Result};
28use alloc::vec::Vec;
29use dvb_common::{Parse, Serialize};
30
31pub const TAG: u8 = 0x6E;
33pub const HEADER_LEN: usize = 2;
35pub const INDICATOR_LEN: usize = 2;
37pub const TYPE_BYTE_LEN: usize = 1;
39pub 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;
46pub const ANNOUNCEMENT_TYPE_MAX: u8 = 0x0F;
48pub const REFERENCE_TYPE_MAX: u8 = 0x07;
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize))]
54#[non_exhaustive]
55pub enum AnnouncementType {
56 EmergencyAlarm,
58 RoadTrafficFlash,
60 PublicTransportFlash,
62 WarningMessage,
64 NewsFlash,
66 WeatherFlash,
68 EventAnnouncement,
70 PersonalCall,
72 Reserved(u8),
74}
75
76impl AnnouncementType {
77 #[must_use]
78 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 pub 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130#[cfg_attr(feature = "serde", derive(serde::Serialize))]
131#[non_exhaustive]
132pub enum ReferenceType {
133 UsualAudioStream,
135 SeparateAudioStream,
137 DifferentServiceSameTs,
140 DifferentServiceDifferentTs,
143 Reserved(u8),
145}
146
147impl ReferenceType {
148 #[must_use]
149 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 pub 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195#[cfg_attr(feature = "serde", derive(serde::Serialize))]
196pub struct AnnouncementReference {
197 pub original_network_id: u16,
199 pub transport_stream_id: u16,
201 pub service_id: u16,
203 pub component_tag: u8,
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize))]
210pub struct AnnouncementEntry {
211 pub announcement_type: AnnouncementType,
213 pub reference_type: ReferenceType,
215 pub reference: Option<AnnouncementReference>,
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
221#[cfg_attr(feature = "serde", derive(serde::Serialize))]
222pub struct AnnouncementSupportDescriptor {
223 pub announcement_support_indicator: u16,
225 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 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 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 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 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 let bytes = [
409 TAG, 10, 0x00, 0x01,
410 0x1A, 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 let bytes = [
434 TAG, 11, 0x12, 0x34, 0x40, 0x53, 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 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 let bytes = [TAG, 3, 0x00, 0x00, 0x09]; 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), 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 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 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}