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}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize))]
129#[non_exhaustive]
130pub enum ReferenceType {
131 UsualAudioStream,
133 SeparateAudioStream,
135 DifferentServiceSameTs,
138 DifferentServiceDifferentTs,
141 Reserved(u8),
143}
144
145impl ReferenceType {
146 #[must_use]
147 pub fn from_u8(v: u8) -> Self {
150 match v {
151 0x00 => Self::UsualAudioStream,
152 0x01 => Self::SeparateAudioStream,
153 0x02 => Self::DifferentServiceSameTs,
154 0x03 => Self::DifferentServiceDifferentTs,
155 v => Self::Reserved(v),
156 }
157 }
158
159 #[must_use]
160 pub fn to_u8(self) -> u8 {
162 match self {
163 Self::UsualAudioStream => 0x00,
164 Self::SeparateAudioStream => 0x01,
165 Self::DifferentServiceSameTs => 0x02,
166 Self::DifferentServiceDifferentTs => 0x03,
167 Self::Reserved(v) => v,
168 }
169 }
170
171 #[must_use]
172 pub fn name(self) -> &'static str {
174 match self {
175 Self::UsualAudioStream => "broadcast in the usual audio stream of the service",
176 Self::SeparateAudioStream => {
177 "broadcast in a separate audio stream that is part of the service"
178 }
179 Self::DifferentServiceSameTs => {
180 "broadcast by a different service within the same DVB transport stream"
181 }
182 Self::DifferentServiceDifferentTs => {
183 "broadcast by a different service within a different DVB transport stream"
184 }
185 Self::Reserved(_) => "reserved",
186 }
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192#[cfg_attr(feature = "serde", derive(serde::Serialize))]
193pub struct AnnouncementReference {
194 pub original_network_id: u16,
196 pub transport_stream_id: u16,
198 pub service_id: u16,
200 pub component_tag: u8,
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206#[cfg_attr(feature = "serde", derive(serde::Serialize))]
207pub struct AnnouncementEntry {
208 pub announcement_type: AnnouncementType,
210 pub reference_type: ReferenceType,
212 pub reference: Option<AnnouncementReference>,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
218#[cfg_attr(feature = "serde", derive(serde::Serialize))]
219pub struct AnnouncementSupportDescriptor {
220 pub announcement_support_indicator: u16,
222 pub entries: Vec<AnnouncementEntry>,
224}
225
226#[inline]
227fn reference_present(reference_type: u8) -> bool {
228 matches!(reference_type, 0x01..=0x03)
229}
230
231impl<'a> Parse<'a> for AnnouncementSupportDescriptor {
232 type Error = crate::error::Error;
233 fn parse(bytes: &'a [u8]) -> Result<Self> {
234 let body = descriptor_body(
235 bytes,
236 TAG,
237 "AnnouncementSupportDescriptor",
238 "unexpected tag for announcement_support_descriptor",
239 )?;
240 if body.len() < INDICATOR_LEN {
241 return Err(Error::InvalidDescriptor {
242 tag: TAG,
243 reason: "body too short (need announcement_support_indicator)",
244 });
245 }
246 let announcement_support_indicator = u16::from_be_bytes([body[0], body[1]]);
247 let mut entries = Vec::new();
248 let mut pos = INDICATOR_LEN;
249 while pos < body.len() {
250 let flags = body[pos];
251 pos += TYPE_BYTE_LEN;
252 let announcement_type = AnnouncementType::from_u8(
254 (flags & ANNOUNCEMENT_TYPE_MASK) >> ANNOUNCEMENT_TYPE_SHIFT,
255 );
256 let reference_type = ReferenceType::from_u8(flags & REFERENCE_TYPE_MASK);
257 let reference = if reference_present(reference_type.to_u8()) {
258 if pos + REFERENCE_LEN > body.len() {
259 return Err(Error::InvalidDescriptor {
260 tag: TAG,
261 reason: "announcement reference block truncated",
262 });
263 }
264 let r = AnnouncementReference {
265 original_network_id: u16::from_be_bytes([body[pos], body[pos + 1]]),
266 transport_stream_id: u16::from_be_bytes([body[pos + 2], body[pos + 3]]),
267 service_id: u16::from_be_bytes([body[pos + 4], body[pos + 5]]),
268 component_tag: body[pos + 6],
269 };
270 pos += REFERENCE_LEN;
271 Some(r)
272 } else {
273 None
274 };
275 entries.push(AnnouncementEntry {
276 announcement_type,
277 reference_type,
278 reference,
279 });
280 }
281 Ok(Self {
282 announcement_support_indicator,
283 entries,
284 })
285 }
286}
287
288impl AnnouncementSupportDescriptor {
289 fn body_len(&self) -> usize {
290 INDICATOR_LEN
291 + self
292 .entries
293 .iter()
294 .map(|e| {
295 TYPE_BYTE_LEN
296 + if reference_present(e.reference_type.to_u8()) {
297 REFERENCE_LEN
298 } else {
299 0
300 }
301 })
302 .sum::<usize>()
303 }
304}
305
306impl Serialize for AnnouncementSupportDescriptor {
307 type Error = crate::error::Error;
308 fn serialized_len(&self) -> usize {
309 HEADER_LEN + self.body_len()
310 }
311
312 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
313 for e in &self.entries {
314 if e.announcement_type.to_u8() > ANNOUNCEMENT_TYPE_MAX {
315 return Err(Error::InvalidDescriptor {
316 tag: TAG,
317 reason: "announcement_type exceeds 4 bits",
318 });
319 }
320 if e.reference_type.to_u8() > REFERENCE_TYPE_MAX {
321 return Err(Error::InvalidDescriptor {
322 tag: TAG,
323 reason: "reference_type exceeds 3 bits",
324 });
325 }
326 if reference_present(e.reference_type.to_u8()) != e.reference.is_some() {
328 return Err(Error::InvalidDescriptor {
329 tag: TAG,
330 reason: "reference presence does not match reference_type",
331 });
332 }
333 }
334 let body_len = self.body_len();
335 if body_len > u8::MAX as usize {
336 return Err(Error::InvalidDescriptor {
337 tag: TAG,
338 reason: "announcement_support_descriptor body exceeds 255 bytes",
339 });
340 }
341 let len = self.serialized_len();
342 if buf.len() < len {
343 return Err(Error::OutputBufferTooSmall {
344 need: len,
345 have: buf.len(),
346 });
347 }
348 buf[0] = TAG;
349 buf[1] = body_len as u8;
350 buf[HEADER_LEN..HEADER_LEN + INDICATOR_LEN]
351 .copy_from_slice(&self.announcement_support_indicator.to_be_bytes());
352 let mut pos = HEADER_LEN + INDICATOR_LEN;
353 for e in &self.entries {
354 let flags = ((e.announcement_type.to_u8() << ANNOUNCEMENT_TYPE_SHIFT)
356 & ANNOUNCEMENT_TYPE_MASK)
357 | RESERVED_BIT_MASK
358 | (e.reference_type.to_u8() & REFERENCE_TYPE_MASK);
359 buf[pos] = flags;
360 pos += TYPE_BYTE_LEN;
361 if let Some(r) = &e.reference {
362 buf[pos..pos + 2].copy_from_slice(&r.original_network_id.to_be_bytes());
363 buf[pos + 2..pos + 4].copy_from_slice(&r.transport_stream_id.to_be_bytes());
364 buf[pos + 4..pos + 6].copy_from_slice(&r.service_id.to_be_bytes());
365 buf[pos + 6] = r.component_tag;
366 pos += REFERENCE_LEN;
367 }
368 }
369 Ok(len)
370 }
371}
372impl<'a> crate::traits::DescriptorDef<'a> for AnnouncementSupportDescriptor {
373 const TAG: u8 = TAG;
374 const NAME: &'static str = "ANNOUNCEMENT_SUPPORT";
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn parse_entry_without_reference() {
383 let bytes = [TAG, 3, 0x00, 0x40, 0x68];
386 let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
387 assert_eq!(d.announcement_support_indicator, 0x0040);
388 assert_eq!(d.entries.len(), 1);
389 assert_eq!(
390 d.entries[0].announcement_type,
391 AnnouncementType::EventAnnouncement
392 );
393 assert_eq!(d.entries[0].reference_type, ReferenceType::UsualAudioStream);
394 assert!(d.entries[0].reference.is_none());
395 }
396
397 #[test]
398 fn parse_entry_with_reference() {
399 let bytes = [
402 TAG, 10, 0x00, 0x01,
403 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x09,
405 ];
406 let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
407 assert_eq!(d.entries.len(), 1);
408 assert_eq!(
409 d.entries[0].announcement_type,
410 AnnouncementType::RoadTrafficFlash
411 );
412 assert_eq!(
413 d.entries[0].reference_type,
414 ReferenceType::DifferentServiceSameTs
415 );
416 let r = d.entries[0].reference.unwrap();
417 assert_eq!(r.original_network_id, 0xAABB);
418 assert_eq!(r.transport_stream_id, 0xCCDD);
419 assert_eq!(r.service_id, 0xEEFF);
420 assert_eq!(r.component_tag, 0x09);
421 }
422
423 #[test]
424 fn parse_mixed_entries() {
425 let bytes = [
427 TAG, 11, 0x12, 0x34, 0x40, 0x53, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
431 ];
432 let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
433 assert_eq!(d.entries.len(), 2);
434 assert!(d.entries[0].reference.is_none());
435 assert_eq!(
436 d.entries[1].reference_type,
437 ReferenceType::DifferentServiceDifferentTs
438 );
439 assert!(d.entries[1].reference.is_some());
440 }
441
442 #[test]
443 fn parse_ignores_reserved_bit() {
444 let bytes = [TAG, 3, 0x00, 0x00, 0x60];
446 let d = AnnouncementSupportDescriptor::parse(&bytes).unwrap();
447 assert_eq!(
448 d.entries[0].announcement_type,
449 AnnouncementType::EventAnnouncement
450 );
451 assert_eq!(d.entries[0].reference_type, ReferenceType::UsualAudioStream);
452 }
453
454 #[test]
455 fn parse_rejects_wrong_tag() {
456 assert!(matches!(
457 AnnouncementSupportDescriptor::parse(&[0x6F, 2, 0, 0]).unwrap_err(),
458 Error::InvalidDescriptor { tag: 0x6F, .. }
459 ));
460 }
461
462 #[test]
463 fn parse_rejects_body_too_short_for_indicator() {
464 assert!(matches!(
465 AnnouncementSupportDescriptor::parse(&[TAG, 1, 0]).unwrap_err(),
466 Error::InvalidDescriptor { tag: TAG, .. }
467 ));
468 }
469
470 #[test]
471 fn parse_rejects_reference_truncated() {
472 let bytes = [TAG, 3, 0x00, 0x00, 0x09]; assert!(matches!(
475 AnnouncementSupportDescriptor::parse(&bytes).unwrap_err(),
476 Error::InvalidDescriptor { tag: TAG, .. }
477 ));
478 }
479
480 #[test]
481 fn parse_rejects_buffer_shorter_than_length() {
482 let bytes = [TAG, 5, 0x00, 0x00];
483 assert!(matches!(
484 AnnouncementSupportDescriptor::parse(&bytes).unwrap_err(),
485 Error::BufferTooShort { .. }
486 ));
487 }
488
489 #[test]
490 fn serialize_round_trip_mixed() {
491 let d = AnnouncementSupportDescriptor {
492 announcement_support_indicator: 0xBEEF,
493 entries: vec![
494 AnnouncementEntry {
495 announcement_type: AnnouncementType::NewsFlash,
496 reference_type: ReferenceType::UsualAudioStream,
497 reference: None,
498 },
499 AnnouncementEntry {
500 announcement_type: AnnouncementType::RoadTrafficFlash,
501 reference_type: ReferenceType::DifferentServiceSameTs,
502 reference: Some(AnnouncementReference {
503 original_network_id: 0xAABB,
504 transport_stream_id: 0xCCDD,
505 service_id: 0xEEFF,
506 component_tag: 0x09,
507 }),
508 },
509 ],
510 };
511 let mut buf = vec![0u8; d.serialized_len()];
512 d.serialize_into(&mut buf).unwrap();
513 assert_eq!(AnnouncementSupportDescriptor::parse(&buf).unwrap(), d);
514 }
515
516 #[test]
517 fn serialize_rejects_too_small_buffer() {
518 let d = AnnouncementSupportDescriptor {
519 announcement_support_indicator: 0,
520 entries: vec![],
521 };
522 let mut buf = vec![0u8; 3];
523 assert!(matches!(
524 d.serialize_into(&mut buf).unwrap_err(),
525 Error::OutputBufferTooSmall { .. }
526 ));
527 }
528
529 #[test]
530 fn serialize_rejects_over_range_announcement_type() {
531 let d = AnnouncementSupportDescriptor {
532 announcement_support_indicator: 0,
533 entries: vec![AnnouncementEntry {
534 announcement_type: AnnouncementType::Reserved(0x10), reference_type: ReferenceType::UsualAudioStream,
536 reference: None,
537 }],
538 };
539 let mut buf = vec![0u8; d.serialized_len()];
540 assert!(matches!(
541 d.serialize_into(&mut buf).unwrap_err(),
542 Error::InvalidDescriptor { tag: TAG, .. }
543 ));
544 }
545
546 #[test]
547 fn serialize_rejects_reference_mismatch() {
548 let d = AnnouncementSupportDescriptor {
550 announcement_support_indicator: 0,
551 entries: vec![AnnouncementEntry {
552 announcement_type: AnnouncementType::Reserved(0),
553 reference_type: ReferenceType::DifferentServiceSameTs,
554 reference: None,
555 }],
556 };
557 let mut buf = vec![0u8; d.serialized_len()];
558 assert!(matches!(
559 d.serialize_into(&mut buf).unwrap_err(),
560 Error::InvalidDescriptor { tag: TAG, .. }
561 ));
562 }
563
564 #[cfg(feature = "serde")]
565 #[test]
566 fn serde_round_trip() {
567 let d = AnnouncementSupportDescriptor {
568 announcement_support_indicator: 0xBEEF,
569 entries: vec![AnnouncementEntry {
570 announcement_type: AnnouncementType::RoadTrafficFlash,
571 reference_type: ReferenceType::DifferentServiceSameTs,
572 reference: Some(AnnouncementReference {
573 original_network_id: 0xAABB,
574 transport_stream_id: 0xCCDD,
575 service_id: 0xEEFF,
576 component_tag: 0x09,
577 }),
578 }],
579 };
580 let json = serde_json::to_string(&d).unwrap();
581 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
583 }
584
585 #[test]
586 fn announcement_type_full_range_round_trip() {
587 for b in 0..=0xFF_u8 {
588 let at = AnnouncementType::from_u8(b);
589 assert_eq!(at.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
590 }
591 }
592
593 #[test]
594 fn reference_type_full_range_round_trip() {
595 for b in 0..=0xFF_u8 {
596 let rt = ReferenceType::from_u8(b);
597 assert_eq!(rt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
598 }
599 }
600}