1use super::descriptor_body;
27use crate::error::{Error, Result};
28use dvb_common::{Parse, Serialize};
29
30pub const TAG: u8 = 0x6E;
32pub const HEADER_LEN: usize = 2;
34pub const INDICATOR_LEN: usize = 2;
36pub const TYPE_BYTE_LEN: usize = 1;
38pub 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;
45pub const ANNOUNCEMENT_TYPE_MAX: u8 = 0x0F;
47pub const REFERENCE_TYPE_MAX: u8 = 0x07;
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53#[non_exhaustive]
54pub enum AnnouncementType {
55 EmergencyAlarm,
57 RoadTrafficFlash,
59 PublicTransportFlash,
61 WarningMessage,
63 NewsFlash,
65 WeatherFlash,
67 EventAnnouncement,
69 PersonalCall,
71 Reserved(u8),
73}
74
75impl AnnouncementType {
76 #[must_use]
77 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize))]
130#[non_exhaustive]
131pub enum ReferenceType {
132 UsualAudioStream,
134 SeparateAudioStream,
136 DifferentServiceSameTs,
139 DifferentServiceDifferentTs,
142 Reserved(u8),
144}
145
146impl ReferenceType {
147 #[must_use]
148 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize))]
195pub struct AnnouncementReference {
196 pub original_network_id: u16,
198 pub transport_stream_id: u16,
200 pub service_id: u16,
202 pub component_tag: u8,
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208#[cfg_attr(feature = "serde", derive(serde::Serialize))]
209pub struct AnnouncementEntry {
210 pub announcement_type: AnnouncementType,
212 pub reference_type: ReferenceType,
214 pub reference: Option<AnnouncementReference>,
216}
217
218#[derive(Debug, Clone, PartialEq, Eq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize))]
221pub struct AnnouncementSupportDescriptor {
222 pub announcement_support_indicator: u16,
224 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 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 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 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 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 let bytes = [
404 TAG, 10, 0x00, 0x01,
405 0x1A, 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 let bytes = [
429 TAG, 11, 0x12, 0x34, 0x40, 0x53, 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 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 let bytes = [TAG, 3, 0x00, 0x00, 0x09]; 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), 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 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 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}