1use super::descriptor_body;
17use crate::error::{Error, Result};
18use alloc::vec::Vec;
19use dvb_common::{Parse, Serialize};
20
21pub const TAG: u8 = 0x4A;
23const HEADER_LEN: usize = 2;
24const FIXED_FIELDS_LEN: usize = 7;
25
26const HANDOVER_TYPE_MASK: u8 = 0xF0;
27const ORIGIN_TYPE_MASK: u8 = 0x01;
28const RESERVED_HANDOVER_MASK: u8 = 0x0E;
29
30const TARGET_LISTED_MASK: u8 = 0x80;
31const EVENT_SIMULCAST_MASK: u8 = 0x40;
32const RESERVED_EVENT_MASK: u8 = 0x3F;
33
34const EXT_TARGET_LISTED_MASK: u8 = 0x80;
35const EXT_EVENT_SIMULCAST_MASK: u8 = 0x40;
36const EXT_LINK_TYPE_MASK: u8 = 0x30;
37const EXT_TARGET_ID_TYPE_MASK: u8 = 0x0C;
38const EXT_ONID_FLAG_MASK: u8 = 0x02;
39const EXT_SID_FLAG_MASK: u8 = 0x01;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize))]
44#[non_exhaustive]
45pub enum LinkageType {
46 InformationService,
48 EpgService,
50 CaReplacementService,
52 TsContainingCompleteSi,
54 ServiceReplacementService,
56 DataBroadcastService,
58 RcsMap,
60 MobileHandOver,
62 SsuService,
64 TsContainingSsuBatOrNit,
66 IpMacNotificationService,
68 TsContainingIntBatOrNit,
70 EventLinkage,
72 ExtendedEventLinkage(u8),
74 DownloadableFontInfoLinkage,
76 NativeIpBootstrapMpeStream,
78 Reserved(u8),
80}
81
82impl LinkageType {
83 #[must_use]
84 pub fn from_u8(v: u8) -> Self {
87 match v {
88 0x01 => Self::InformationService,
89 0x02 => Self::EpgService,
90 0x03 => Self::CaReplacementService,
91 0x04 => Self::TsContainingCompleteSi,
92 0x05 => Self::ServiceReplacementService,
93 0x06 => Self::DataBroadcastService,
94 0x07 => Self::RcsMap,
95 0x08 => Self::MobileHandOver,
96 0x09 => Self::SsuService,
97 0x0A => Self::TsContainingSsuBatOrNit,
98 0x0B => Self::IpMacNotificationService,
99 0x0C => Self::TsContainingIntBatOrNit,
100 0x0D => Self::EventLinkage,
101 0x0E..=0x1F => Self::ExtendedEventLinkage(v),
102 0x20 => Self::DownloadableFontInfoLinkage,
103 0x21 => Self::NativeIpBootstrapMpeStream,
104 v => Self::Reserved(v),
105 }
106 }
107
108 #[must_use]
109 pub fn to_u8(self) -> u8 {
111 match self {
112 Self::InformationService => 0x01,
113 Self::EpgService => 0x02,
114 Self::CaReplacementService => 0x03,
115 Self::TsContainingCompleteSi => 0x04,
116 Self::ServiceReplacementService => 0x05,
117 Self::DataBroadcastService => 0x06,
118 Self::RcsMap => 0x07,
119 Self::MobileHandOver => 0x08,
120 Self::SsuService => 0x09,
121 Self::TsContainingSsuBatOrNit => 0x0A,
122 Self::IpMacNotificationService => 0x0B,
123 Self::TsContainingIntBatOrNit => 0x0C,
124 Self::EventLinkage => 0x0D,
125 Self::ExtendedEventLinkage(v) => v,
126 Self::DownloadableFontInfoLinkage => 0x20,
127 Self::NativeIpBootstrapMpeStream => 0x21,
128 Self::Reserved(v) => v,
129 }
130 }
131
132 #[must_use]
133 pub fn name(self) -> &'static str {
135 match self {
136 Self::InformationService => "information service",
137 Self::EpgService => "EPG service",
138 Self::CaReplacementService => "CA replacement service",
139 Self::TsContainingCompleteSi => "TS containing complete network/bouquet SI",
140 Self::ServiceReplacementService => "service replacement service",
141 Self::DataBroadcastService => "data broadcast service",
142 Self::RcsMap => "RCS map",
143 Self::MobileHandOver => "mobile hand-over",
144 Self::SsuService => "SSU service",
145 Self::TsContainingSsuBatOrNit => "TS containing SSU BAT or NIT",
146 Self::IpMacNotificationService => "IP/MAC notification service",
147 Self::TsContainingIntBatOrNit => "TS containing INT BAT or NIT",
148 Self::EventLinkage => "event linkage",
149 Self::ExtendedEventLinkage(_) => "extended event linkage",
150 Self::DownloadableFontInfoLinkage => "downloadable font info linkage",
151 Self::NativeIpBootstrapMpeStream => "Native IP bootstrap MPE stream",
152 Self::Reserved(_) => "reserved",
153 }
154 }
155}
156dvb_common::impl_spec_display!(LinkageType, ExtendedEventLinkage, Reserved);
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize))]
161#[non_exhaustive]
162pub enum HandOverType {
163 DvbIdenticalNeighbouringCountry,
165 DvbLocalVariation,
167 DvbAssociatedService,
169 Reserved(u8),
171}
172
173impl HandOverType {
174 #[must_use]
175 pub fn from_u8(v: u8) -> Self {
178 match v {
179 0x1 => Self::DvbIdenticalNeighbouringCountry,
180 0x2 => Self::DvbLocalVariation,
181 0x3 => Self::DvbAssociatedService,
182 v => Self::Reserved(v),
183 }
184 }
185
186 #[must_use]
187 pub fn to_u8(self) -> u8 {
189 match self {
190 Self::DvbIdenticalNeighbouringCountry => 0x1,
191 Self::DvbLocalVariation => 0x2,
192 Self::DvbAssociatedService => 0x3,
193 Self::Reserved(v) => v,
194 }
195 }
196
197 #[must_use]
198 pub fn name(self) -> &'static str {
200 match self {
201 Self::DvbIdenticalNeighbouringCountry => {
202 "DVB hand-over to an identical service in a neighbouring country"
203 }
204 Self::DvbLocalVariation => "DVB hand-over to a local variation of the same service",
205 Self::DvbAssociatedService => "DVB hand-over to an associated service",
206 Self::Reserved(_) => "reserved",
207 }
208 }
209}
210dvb_common::impl_spec_display!(HandOverType, Reserved);
211
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214#[cfg_attr(feature = "serde", derive(serde::Serialize))]
215#[non_exhaustive]
216pub enum LinkType {
217 SdOrUhd,
219 HdOrServiceFrameCompatible,
222 FrameCompatiblePlanoStereoscopic,
224 ServiceCompatiblePlanoStereoscopicMvc,
226 Reserved(u8),
228}
229
230impl LinkType {
231 #[must_use]
232 pub fn from_u8(v: u8) -> Self {
235 match v {
236 0 => Self::SdOrUhd,
237 1 => Self::HdOrServiceFrameCompatible,
238 2 => Self::FrameCompatiblePlanoStereoscopic,
239 3 => Self::ServiceCompatiblePlanoStereoscopicMvc,
240 v => Self::Reserved(v),
241 }
242 }
243
244 #[must_use]
245 pub fn to_u8(self) -> u8 {
247 match self {
248 Self::SdOrUhd => 0,
249 Self::HdOrServiceFrameCompatible => 1,
250 Self::FrameCompatiblePlanoStereoscopic => 2,
251 Self::ServiceCompatiblePlanoStereoscopicMvc => 3,
252 Self::Reserved(v) => v,
253 }
254 }
255
256 #[must_use]
257 pub fn name(self) -> &'static str {
259 match self {
260 Self::SdOrUhd => "SD (0x0E) / UHD (0x0F)",
261 Self::HdOrServiceFrameCompatible => {
262 "HD (0x0E) / service frame compatible plano-stereoscopic (0x0F)"
263 }
264 Self::FrameCompatiblePlanoStereoscopic => {
265 "frame compatible plano-stereoscopic H.264/AVC"
266 }
267 Self::ServiceCompatiblePlanoStereoscopicMvc => {
268 "service compatible plano-stereoscopic MVC"
269 }
270 Self::Reserved(_) => "reserved",
271 }
272 }
273}
274dvb_common::impl_spec_display!(LinkType, Reserved);
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278#[cfg_attr(feature = "serde", derive(serde::Serialize))]
279#[non_exhaustive]
280pub enum TargetIdType {
281 UseTransportStreamId,
283 UseTargetTransportStreamId,
285 MatchAnyTransportStreamId,
287 UseUserDefinedId,
289 Reserved(u8),
291}
292
293impl TargetIdType {
294 #[must_use]
295 pub fn from_u8(v: u8) -> Self {
298 match v {
299 0 => Self::UseTransportStreamId,
300 1 => Self::UseTargetTransportStreamId,
301 2 => Self::MatchAnyTransportStreamId,
302 3 => Self::UseUserDefinedId,
303 v => Self::Reserved(v),
304 }
305 }
306
307 #[must_use]
308 pub fn to_u8(self) -> u8 {
310 match self {
311 Self::UseTransportStreamId => 0,
312 Self::UseTargetTransportStreamId => 1,
313 Self::MatchAnyTransportStreamId => 2,
314 Self::UseUserDefinedId => 3,
315 Self::Reserved(v) => v,
316 }
317 }
318
319 #[must_use]
320 pub fn name(self) -> &'static str {
322 match self {
323 Self::UseTransportStreamId => "use transport_stream_id",
324 Self::UseTargetTransportStreamId => "use target_transport_stream_id",
325 Self::MatchAnyTransportStreamId => "match any transport_stream_id (wildcard)",
326 Self::UseUserDefinedId => "use user_defined_id",
327 Self::Reserved(_) => "reserved",
328 }
329 }
330}
331dvb_common::impl_spec_display!(TargetIdType, Reserved);
332
333#[derive(Debug, Clone, PartialEq, Eq)]
335#[cfg_attr(feature = "serde", derive(serde::Serialize))]
336pub struct MobileHandOverInfo {
337 pub hand_over_type: HandOverType,
339 pub origin_type: bool,
341 pub network_id: Option<u16>,
343 pub initial_service_id: Option<u16>,
345}
346
347impl MobileHandOverInfo {
348 fn serialized_len(&self) -> usize {
349 1 + self.network_id.map_or(0, |_| 2) + self.initial_service_id.map_or(0, |_| 2)
350 }
351
352 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
353 let len = self.serialized_len();
354 if buf.len() < len {
355 return Err(Error::OutputBufferTooSmall {
356 need: len,
357 have: buf.len(),
358 });
359 }
360 let flags_byte = (self.hand_over_type.to_u8() << 4)
361 | RESERVED_HANDOVER_MASK
362 | u8::from(self.origin_type);
363 buf[0] = flags_byte;
364 let mut pos = 1;
365 if let Some(nid) = self.network_id {
366 buf[pos..pos + 2].copy_from_slice(&nid.to_be_bytes());
367 pos += 2;
368 }
369 if let Some(sid) = self.initial_service_id {
370 buf[pos..pos + 2].copy_from_slice(&sid.to_be_bytes());
371 }
372 Ok(len)
373 }
374}
375
376#[derive(Debug, Clone, PartialEq, Eq)]
378#[cfg_attr(feature = "serde", derive(serde::Serialize))]
379pub struct EventLinkageInfo {
380 pub target_event_id: u16,
382 pub target_listed: bool,
384 pub event_simulcast: bool,
386}
387
388impl EventLinkageInfo {
389 const SERIALIZED_LEN: usize = 3;
390
391 fn serialized_len(&self) -> usize {
392 Self::SERIALIZED_LEN
393 }
394
395 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
396 if buf.len() < Self::SERIALIZED_LEN {
397 return Err(Error::OutputBufferTooSmall {
398 need: Self::SERIALIZED_LEN,
399 have: buf.len(),
400 });
401 }
402 buf[0..2].copy_from_slice(&self.target_event_id.to_be_bytes());
403 let mut byte2: u8 = RESERVED_EVENT_MASK;
404 if self.target_listed {
405 byte2 |= TARGET_LISTED_MASK;
406 }
407 if self.event_simulcast {
408 byte2 |= EVENT_SIMULCAST_MASK;
409 }
410 buf[2] = byte2;
411 Ok(Self::SERIALIZED_LEN)
412 }
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
417#[cfg_attr(feature = "serde", derive(serde::Serialize))]
418#[non_exhaustive]
419pub enum TargetId {
420 UserDefined {
422 user_defined_id: u16,
424 },
425 Dvb {
427 target_id_type: TargetIdType,
429 target_transport_stream_id: Option<u16>,
431 target_original_network_id: Option<u16>,
433 target_service_id: Option<u16>,
435 },
436}
437
438impl TargetId {
439 fn serialized_len(&self) -> usize {
440 match self {
441 TargetId::UserDefined { .. } => 2,
442 TargetId::Dvb {
443 target_transport_stream_id,
444 target_original_network_id,
445 target_service_id,
446 ..
447 } => {
448 usize::from(target_transport_stream_id.is_some()) * 2
449 + usize::from(target_original_network_id.is_some()) * 2
450 + usize::from(target_service_id.is_some()) * 2
451 }
452 }
453 }
454
455 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
456 let len = self.serialized_len();
457 if buf.len() < len {
458 return Err(Error::OutputBufferTooSmall {
459 need: len,
460 have: buf.len(),
461 });
462 }
463 match self {
464 TargetId::UserDefined { user_defined_id } => {
465 buf[..2].copy_from_slice(&user_defined_id.to_be_bytes());
466 }
467 TargetId::Dvb {
468 target_transport_stream_id,
469 target_original_network_id,
470 target_service_id,
471 ..
472 } => {
473 let ts_len = target_transport_stream_id.map_or(0, |_| 2);
474 let onid_len = target_original_network_id.map_or(0, |_| 2);
475 if let Some(ts_id) = target_transport_stream_id {
476 buf[..2].copy_from_slice(&ts_id.to_be_bytes());
477 }
478 if let Some(onid) = target_original_network_id {
479 buf[ts_len..ts_len + 2].copy_from_slice(&onid.to_be_bytes());
480 }
481 if let Some(sid) = target_service_id {
482 let off = ts_len + onid_len;
483 buf[off..off + 2].copy_from_slice(&sid.to_be_bytes());
484 }
485 }
486 }
487 Ok(len)
488 }
489}
490
491#[derive(Debug, Clone, PartialEq, Eq)]
493#[cfg_attr(feature = "serde", derive(serde::Serialize))]
494pub struct ExtendedEventLinkageEntry {
495 pub target_event_id: u16,
497 pub target_listed: bool,
499 pub event_simulcast: bool,
501 pub link_type: LinkType,
503 pub target_id_type: TargetIdType,
505 pub original_network_id_flag: bool,
507 pub service_id_flag: bool,
509 pub target_id: TargetId,
511}
512
513impl ExtendedEventLinkageEntry {
514 fn serialized_len(&self) -> usize {
515 2 + 1 + self.target_id.serialized_len()
516 }
517
518 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
519 let flags_byte_len = 3;
520 if buf.len() < flags_byte_len {
521 return Err(Error::OutputBufferTooSmall {
522 need: flags_byte_len,
523 have: buf.len(),
524 });
525 }
526 buf[0..2].copy_from_slice(&self.target_event_id.to_be_bytes());
527 let mut byte2: u8 = 0;
528 if self.target_listed {
529 byte2 |= EXT_TARGET_LISTED_MASK;
530 }
531 if self.event_simulcast {
532 byte2 |= EXT_EVENT_SIMULCAST_MASK;
533 }
534 byte2 |= (self.link_type.to_u8() & 0x03) << 4;
535 byte2 |= (self.target_id_type.to_u8() & 0x03) << 2;
536 if self.original_network_id_flag {
537 byte2 |= EXT_ONID_FLAG_MASK;
538 }
539 if self.service_id_flag {
540 byte2 |= EXT_SID_FLAG_MASK;
541 }
542 buf[2] = byte2;
543 let tid_written = self.target_id.serialize_into(&mut buf[3..])?;
544 Ok(3 + tid_written)
545 }
546}
547
548#[derive(Debug, Clone, PartialEq, Eq)]
551#[cfg_attr(feature = "serde", derive(serde::Serialize))]
552pub struct ExtendedEventLinkageInfo {
553 pub entries: Vec<ExtendedEventLinkageEntry>,
555}
556
557impl ExtendedEventLinkageInfo {
558 fn serialized_len(&self) -> usize {
559 1 + self
560 .entries
561 .iter()
562 .map(|e| e.serialized_len())
563 .sum::<usize>()
564 }
565
566 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
567 let loop_len: usize = self.entries.iter().map(|e| e.serialized_len()).sum();
568 let total = 1 + loop_len;
569 if buf.len() < total {
570 return Err(Error::OutputBufferTooSmall {
571 need: total,
572 have: buf.len(),
573 });
574 }
575 buf[0] = loop_len as u8;
576 let mut pos = 1;
577 for entry in &self.entries {
578 let written = entry.serialize_into(&mut buf[pos..])?;
579 pos += written;
580 }
581 Ok(total)
582 }
583}
584
585#[derive(Debug, Clone, PartialEq, Eq)]
595#[non_exhaustive]
596#[cfg_attr(feature = "serde", derive(serde::Serialize))]
597pub enum LinkageData<'a> {
598 MobileHandOver(MobileHandOverInfo),
600 EventLinkage(EventLinkageInfo),
602 ExtendedEventLinkage(ExtendedEventLinkageInfo),
604 None,
607 #[cfg_attr(feature = "serde", serde(borrow))]
612 Other(&'a [u8]),
613}
614
615impl LinkageData<'_> {
616 fn serialized_len(&self) -> usize {
617 match self {
618 LinkageData::MobileHandOver(m) => m.serialized_len(),
619 LinkageData::EventLinkage(e) => e.serialized_len(),
620 LinkageData::ExtendedEventLinkage(x) => x.serialized_len(),
621 LinkageData::None => 0,
622 LinkageData::Other(b) => b.len(),
623 }
624 }
625
626 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
627 match self {
628 LinkageData::MobileHandOver(m) => m.serialize_into(buf),
629 LinkageData::EventLinkage(e) => e.serialize_into(buf),
630 LinkageData::ExtendedEventLinkage(x) => x.serialize_into(buf),
631 LinkageData::None => Ok(0),
632 LinkageData::Other(b) => {
633 if buf.len() < b.len() {
634 return Err(Error::OutputBufferTooSmall {
635 need: b.len(),
636 have: buf.len(),
637 });
638 }
639 buf[..b.len()].copy_from_slice(b);
640 Ok(b.len())
641 }
642 }
643 }
644}
645
646fn parse_mobile_handover(bytes: &[u8], end: usize) -> Result<MobileHandOverInfo> {
647 if end < 1 {
648 return Err(Error::InvalidDescriptor {
649 tag: TAG,
650 reason: "mobile hand-over info needs at least flags byte",
651 });
652 }
653 let flags_byte = bytes[0];
654 let hand_over_type = HandOverType::from_u8((flags_byte & HANDOVER_TYPE_MASK) >> 4);
655 let origin_type = (flags_byte & ORIGIN_TYPE_MASK) != 0;
656 let mut pos = 1;
657 let network_id = if matches!(hand_over_type.to_u8(), 0x01..=0x03) {
658 if pos + 2 > end {
659 return Err(Error::InvalidDescriptor {
660 tag: TAG,
661 reason: "mobile hand-over info with gated network_id needs at least 3 bytes",
662 });
663 }
664 let nid = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
665 pos += 2;
666 Some(nid)
667 } else {
668 None
669 };
670 let initial_service_id = if !origin_type {
671 if pos + 2 > end {
672 return Err(Error::InvalidDescriptor {
673 tag: TAG,
674 reason: "mobile hand-over info with origin_type=NIT needs initial_service_id",
675 });
676 }
677 Some(u16::from_be_bytes([bytes[pos], bytes[pos + 1]]))
678 } else {
679 None
680 };
681 Ok(MobileHandOverInfo {
682 hand_over_type,
683 origin_type,
684 network_id,
685 initial_service_id,
686 })
687}
688
689fn parse_event_linkage(bytes: &[u8]) -> Result<EventLinkageInfo> {
690 if bytes.len() < 3 {
691 return Err(Error::InvalidDescriptor {
692 tag: TAG,
693 reason: "event linkage info needs 3 bytes",
694 });
695 }
696 let target_event_id = u16::from_be_bytes([bytes[0], bytes[1]]);
697 let target_listed = (bytes[2] & TARGET_LISTED_MASK) != 0;
698 let event_simulcast = (bytes[2] & EVENT_SIMULCAST_MASK) != 0;
699 Ok(EventLinkageInfo {
700 target_event_id,
701 target_listed,
702 event_simulcast,
703 })
704}
705
706fn parse_extended_event_linkage(bytes: &[u8]) -> Result<ExtendedEventLinkageInfo> {
707 if bytes.is_empty() {
708 return Err(Error::InvalidDescriptor {
709 tag: TAG,
710 reason: "extended event linkage info needs at least loop_length byte",
711 });
712 }
713 let loop_length = bytes[0] as usize;
714 let loop_end = 1 + loop_length;
715 if bytes.len() < loop_end {
716 return Err(Error::BufferTooShort {
717 need: loop_end,
718 have: bytes.len(),
719 what: "extended event linkage info loop",
720 });
721 }
722 let mut entries = Vec::new();
723 let mut pos = 1;
724 let read_u16 = |p: &mut usize| -> Result<u16> {
725 if *p + 2 > loop_end {
726 return Err(Error::InvalidDescriptor {
727 tag: TAG,
728 reason: "extended event linkage entry truncated (need u16)",
729 });
730 }
731 let v = u16::from_be_bytes([bytes[*p], bytes[*p + 1]]);
732 *p += 2;
733 Ok(v)
734 };
735 while pos < loop_end {
736 let target_event_id = read_u16(&mut pos)?;
737 if pos >= loop_end {
738 return Err(Error::InvalidDescriptor {
739 tag: TAG,
740 reason: "extended event linkage entry truncated (need flags byte)",
741 });
742 }
743 let fb = bytes[pos];
744 pos += 1;
745 let target_listed = (fb & EXT_TARGET_LISTED_MASK) != 0;
746 let event_simulcast = (fb & EXT_EVENT_SIMULCAST_MASK) != 0;
747 let link_type = LinkType::from_u8((fb & EXT_LINK_TYPE_MASK) >> 4);
748 let target_id_type = TargetIdType::from_u8((fb & EXT_TARGET_ID_TYPE_MASK) >> 2);
749 let original_network_id_flag = (fb & EXT_ONID_FLAG_MASK) != 0;
750 let service_id_flag = (fb & EXT_SID_FLAG_MASK) != 0;
751
752 let target_id = if target_id_type == TargetIdType::UseUserDefinedId {
753 let user_defined_id = read_u16(&mut pos)?;
754 TargetId::UserDefined { user_defined_id }
755 } else {
756 let target_transport_stream_id =
757 if target_id_type == TargetIdType::UseTargetTransportStreamId {
758 Some(read_u16(&mut pos)?)
759 } else {
760 None
761 };
762 let target_original_network_id = if original_network_id_flag {
763 Some(read_u16(&mut pos)?)
764 } else {
765 None
766 };
767 let target_service_id = if service_id_flag {
768 Some(read_u16(&mut pos)?)
769 } else {
770 None
771 };
772 TargetId::Dvb {
773 target_id_type,
774 target_transport_stream_id,
775 target_original_network_id,
776 target_service_id,
777 }
778 };
779 entries.push(ExtendedEventLinkageEntry {
780 target_event_id,
781 target_listed,
782 event_simulcast,
783 link_type,
784 target_id_type,
785 original_network_id_flag,
786 service_id_flag,
787 target_id,
788 });
789 }
790 Ok(ExtendedEventLinkageInfo { entries })
791}
792
793#[derive(Debug, Clone, PartialEq, Eq)]
795#[cfg_attr(feature = "serde", derive(serde::Serialize))]
796#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
797pub struct LinkageDescriptor<'a> {
798 pub transport_stream_id: u16,
800 pub original_network_id: u16,
802 pub service_id: u16,
805 pub linkage_type: LinkageType,
807 pub linkage_data: LinkageData<'a>,
809 pub private_data: &'a [u8],
812}
813
814const LINKAGE_TYPES_WITH_OTHER: &[u8] = &[0x09, 0x0A, 0x0B, 0x0C, 0x20, 0x21];
815
816impl<'a> Parse<'a> for LinkageDescriptor<'a> {
817 type Error = crate::error::Error;
818 fn parse(bytes: &'a [u8]) -> Result<Self> {
819 let body = descriptor_body(
820 bytes,
821 TAG,
822 "LinkageDescriptor",
823 "unexpected tag for linkage_descriptor",
824 )?;
825 if body.len() < FIXED_FIELDS_LEN {
826 return Err(Error::InvalidDescriptor {
827 tag: TAG,
828 reason: "linkage_descriptor body shorter than minimum 7 bytes",
829 });
830 }
831 let transport_stream_id = u16::from_be_bytes([body[0], body[1]]);
832 let original_network_id = u16::from_be_bytes([body[2], body[3]]);
833 let service_id = u16::from_be_bytes([body[4], body[5]]);
834 let linkage_type_raw = body[6];
835 let linkage_type = LinkageType::from_u8(linkage_type_raw);
836 let tail = &body[FIXED_FIELDS_LEN..];
837 let tail_len = tail.len();
838
839 let (linkage_data, private_data) = match linkage_type_raw {
840 0x08 => {
841 let info = parse_mobile_handover(tail, tail_len)?;
842 let consumed = info.serialized_len();
843 (LinkageData::MobileHandOver(info), &tail[consumed..])
844 }
845 0x0D => {
846 let info = parse_event_linkage(tail)?;
847 let consumed = EventLinkageInfo::SERIALIZED_LEN;
848 (LinkageData::EventLinkage(info), &tail[consumed..])
849 }
850 0x0E..=0x1F => {
851 let info = parse_extended_event_linkage(tail)?;
852 let consumed = info.serialized_len();
853 (LinkageData::ExtendedEventLinkage(info), &tail[consumed..])
854 }
855 lt if LINKAGE_TYPES_WITH_OTHER.contains(<) || (0x80..=0xFE).contains(<) => {
856 (LinkageData::Other(tail), &[] as &[u8])
857 }
858 _ => (LinkageData::None, tail),
859 };
860 Ok(Self {
861 transport_stream_id,
862 original_network_id,
863 service_id,
864 linkage_type,
865 linkage_data,
866 private_data,
867 })
868 }
869}
870
871impl Serialize for LinkageDescriptor<'_> {
872 type Error = crate::error::Error;
873 fn serialized_len(&self) -> usize {
874 HEADER_LEN + FIXED_FIELDS_LEN + self.linkage_data.serialized_len() + self.private_data.len()
875 }
876
877 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
878 let len = self.serialized_len();
879 if buf.len() < len {
880 return Err(Error::OutputBufferTooSmall {
881 need: len,
882 have: buf.len(),
883 });
884 }
885 buf[0] = TAG;
886 buf[1] = (len - HEADER_LEN) as u8;
887 let bs = HEADER_LEN;
888 buf[bs..bs + 2].copy_from_slice(&self.transport_stream_id.to_be_bytes());
889 buf[bs + 2..bs + 4].copy_from_slice(&self.original_network_id.to_be_bytes());
890 buf[bs + 4..bs + 6].copy_from_slice(&self.service_id.to_be_bytes());
891 buf[bs + 6] = self.linkage_type.to_u8();
892 let ld_start = bs + FIXED_FIELDS_LEN;
893 let ld_written = self.linkage_data.serialize_into(&mut buf[ld_start..])?;
894 let pd_start = ld_start + ld_written;
895 if !self.private_data.is_empty() {
896 buf[pd_start..pd_start + self.private_data.len()].copy_from_slice(self.private_data);
897 }
898 Ok(len)
899 }
900}
901impl<'a> crate::traits::DescriptorDef<'a> for LinkageDescriptor<'a> {
902 const TAG: u8 = TAG;
903 const NAME: &'static str = "LINKAGE";
904}
905
906#[cfg(test)]
907mod tests {
908 use super::*;
909
910 #[test]
911 fn parse_extracts_tsid_onid_sid() {
912 let bytes = [
913 TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB,
914 ];
915 let d = LinkageDescriptor::parse(&bytes).unwrap();
916 assert_eq!(d.transport_stream_id, 0x0001);
917 assert_eq!(d.original_network_id, 0x0002);
918 assert_eq!(d.service_id, 0x0003);
919 }
920
921 #[test]
922 fn parse_extracts_linkage_type() {
923 let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x06];
924 let d = LinkageDescriptor::parse(&bytes).unwrap();
925 assert_eq!(d.linkage_type, LinkageType::DataBroadcastService);
926 }
927
928 #[test]
929 fn parse_none_type_preserves_private_data() {
930 let bytes = [
931 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB, 0xCC,
932 ];
933 let d = LinkageDescriptor::parse(&bytes).unwrap();
934 assert!(matches!(d.linkage_data, LinkageData::None));
935 assert_eq!(d.private_data, &[0xAA, 0xBB, 0xCC]);
936 }
937
938 #[test]
939 fn parse_accepts_empty_private_data() {
940 let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05];
941 let d = LinkageDescriptor::parse(&bytes).unwrap();
942 assert!(d.private_data.is_empty());
943 }
944
945 #[test]
946 fn parse_mobile_handover_with_initial_sid() {
947 let bytes = [
948 TAG, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x12, 0x00, 0x10, 0x00, 0x20, 0xDE, 0xAD, ];
958 let d = LinkageDescriptor::parse(&bytes).unwrap();
959 assert_eq!(d.linkage_type, LinkageType::MobileHandOver);
960 match &d.linkage_data {
961 LinkageData::MobileHandOver(m) => {
962 assert_eq!(
963 m.hand_over_type,
964 HandOverType::DvbIdenticalNeighbouringCountry
965 );
966 assert!(!m.origin_type);
967 assert_eq!(m.network_id, Some(0x0010));
968 assert_eq!(m.initial_service_id, Some(0x0020));
969 }
970 other => panic!("expected MobileHandOver, got {other:?}"),
971 }
972 assert_eq!(d.private_data, &[0xDE, 0xAD]);
973 }
974
975 #[test]
976 fn parse_mobile_handover_sdt_no_initial_sid() {
977 let bytes = [
978 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x2F, 0x00, 0x10, 0xCA, 0xFE, ];
987 let d = LinkageDescriptor::parse(&bytes).unwrap();
988 match &d.linkage_data {
989 LinkageData::MobileHandOver(m) => {
990 assert_eq!(m.hand_over_type, HandOverType::DvbLocalVariation);
991 assert!(m.origin_type);
992 assert_eq!(m.network_id, Some(0x0010));
993 assert_eq!(m.initial_service_id, None);
994 }
995 other => panic!("expected MobileHandOver, got {other:?}"),
996 }
997 assert_eq!(d.private_data, &[0xCA, 0xFE]);
998 }
999
1000 #[test]
1001 fn parse_event_linkage() {
1002 let bytes = [
1003 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0D, 0xAB, 0xCD, 0xC0, 0xBE, 0xEF, ];
1012 let d = LinkageDescriptor::parse(&bytes).unwrap();
1013 match &d.linkage_data {
1014 LinkageData::EventLinkage(e) => {
1015 assert_eq!(e.target_event_id, 0xABCD);
1016 assert!(e.target_listed);
1017 assert!(e.event_simulcast);
1018 }
1019 other => panic!("expected EventLinkage, got {other:?}"),
1020 }
1021 assert_eq!(d.private_data, &[0xBE, 0xEF]);
1022 }
1023
1024 #[test]
1025 fn parse_extended_event_linkage_user_defined() {
1026 let bytes = [
1027 TAG, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0E, 0x05, 0x12, 0x34, 0xCC, 0x56, 0x78, 0xCC, ];
1038 let d = LinkageDescriptor::parse(&bytes).unwrap();
1039 match &d.linkage_data {
1040 LinkageData::ExtendedEventLinkage(x) => {
1041 assert_eq!(x.entries.len(), 1);
1042 let e = &x.entries[0];
1043 assert_eq!(e.target_event_id, 0x1234);
1044 assert!(e.target_listed);
1045 assert!(e.event_simulcast);
1046 assert_eq!(e.link_type, LinkType::SdOrUhd);
1047 assert_eq!(e.target_id_type, TargetIdType::UseUserDefinedId);
1048 assert_eq!(
1049 e.target_id,
1050 TargetId::UserDefined {
1051 user_defined_id: 0x5678
1052 }
1053 );
1054 }
1055 other => panic!("expected ExtendedEventLinkage, got {other:?}"),
1056 }
1057 assert_eq!(d.private_data, &[0xCC]);
1058 }
1059
1060 #[test]
1061 fn parse_extended_event_linkage_dvb_target() {
1062 let bytes = [
1063 TAG, 0x0F, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0F, 0x07, 0xAA, 0xBB, 0x26, 0x00, 0x11, 0x00, 0x22, ];
1074 let d = LinkageDescriptor::parse(&bytes).unwrap();
1075 match &d.linkage_data {
1076 LinkageData::ExtendedEventLinkage(x) => {
1077 assert_eq!(x.entries.len(), 1);
1078 let e = &x.entries[0];
1079 assert_eq!(e.target_id_type, TargetIdType::UseTargetTransportStreamId);
1080 assert!(e.original_network_id_flag);
1081 assert!(!e.service_id_flag);
1082 assert_eq!(
1083 e.target_id,
1084 TargetId::Dvb {
1085 target_id_type: TargetIdType::UseTargetTransportStreamId,
1086 target_transport_stream_id: Some(0x0011),
1087 target_original_network_id: Some(0x0022),
1088 target_service_id: None,
1089 }
1090 );
1091 }
1092 other => panic!("expected ExtendedEventLinkage, got {other:?}"),
1093 }
1094 assert_eq!(d.private_data, &[] as &[u8]);
1095 }
1096
1097 #[test]
1098 fn parse_other_type_captures_raw_tail() {
1099 let bytes = [
1100 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0B, 0xAA, 0xBB, 0xCC, ];
1107 let d = LinkageDescriptor::parse(&bytes).unwrap();
1108 match &d.linkage_data {
1109 LinkageData::Other(b) => assert_eq!(*b, &[0xAA, 0xBB, 0xCC]),
1110 other => panic!("expected Other, got {other:?}"),
1111 }
1112 assert!(d.private_data.is_empty());
1113 }
1114
1115 #[test]
1116 fn parse_user_defined_type_is_other() {
1117 let bytes = [
1118 TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x90, 0xFF, 0xFE, ];
1125 let d = LinkageDescriptor::parse(&bytes).unwrap();
1126 assert!(matches!(d.linkage_data, LinkageData::Other(_)));
1127 }
1128
1129 #[test]
1130 fn parse_rejects_wrong_tag() {
1131 let err = LinkageDescriptor::parse(&[0x4B, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05])
1132 .unwrap_err();
1133 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x4B, .. }));
1134 }
1135
1136 #[test]
1137 fn parse_rejects_body_shorter_than_seven() {
1138 let bytes = [TAG, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00];
1139 let err = LinkageDescriptor::parse(&bytes).unwrap_err();
1140 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
1141 }
1142
1143 #[test]
1144 fn parse_rejects_truncated_buffer() {
1145 let err = LinkageDescriptor::parse(&[TAG]).unwrap_err();
1146 assert!(matches!(err, Error::BufferTooShort { .. }));
1147 }
1148
1149 #[test]
1150 fn parse_rejects_truncated_mobile_handover() {
1151 let bytes = [
1152 TAG, 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x10, 0x00, 0x10, ];
1157 let err = LinkageDescriptor::parse(&bytes).unwrap_err();
1158 assert!(
1159 matches!(err, Error::InvalidDescriptor { .. }),
1160 "expected InvalidDescriptor for truncated mobile hand-over, got {err:?}"
1161 );
1162 }
1163
1164 #[test]
1165 fn serialize_round_trip_no_linkage_data() {
1166 let d = LinkageDescriptor {
1167 transport_stream_id: 0x1234,
1168 original_network_id: 0x5678,
1169 service_id: 0xABCD,
1170 linkage_type: LinkageType::EpgService,
1171 linkage_data: LinkageData::None,
1172 private_data: &[],
1173 };
1174 let mut buf = vec![0u8; d.serialized_len()];
1175 d.serialize_into(&mut buf).unwrap();
1176 let re = LinkageDescriptor::parse(&buf).unwrap();
1177 assert_eq!(d, re);
1178 }
1179
1180 #[test]
1181 fn serialize_round_trip_with_private_data() {
1182 let d = LinkageDescriptor {
1183 transport_stream_id: 0x0001,
1184 original_network_id: 0x0002,
1185 service_id: 0x0003,
1186 linkage_type: LinkageType::ServiceReplacementService,
1187 linkage_data: LinkageData::None,
1188 private_data: &[0xDE, 0xAD, 0xBE, 0xEF],
1189 };
1190 let mut buf = vec![0u8; d.serialized_len()];
1191 d.serialize_into(&mut buf).unwrap();
1192 let re = LinkageDescriptor::parse(&buf).unwrap();
1193 assert_eq!(d, re);
1194 }
1195
1196 #[test]
1197 fn serialize_round_trip_mobile_handover() {
1198 let d = LinkageDescriptor {
1199 transport_stream_id: 0x0001,
1200 original_network_id: 0x0002,
1201 service_id: 0x0003,
1202 linkage_type: LinkageType::MobileHandOver,
1203 linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1204 hand_over_type: HandOverType::DvbAssociatedService,
1205 origin_type: false,
1206 network_id: Some(0x0044),
1207 initial_service_id: Some(0x0055),
1208 }),
1209 private_data: &[0xFF],
1210 };
1211 let mut buf = vec![0u8; d.serialized_len()];
1212 d.serialize_into(&mut buf).unwrap();
1213 let re = LinkageDescriptor::parse(&buf).unwrap();
1214 assert_eq!(d, re);
1215 }
1216
1217 #[test]
1218 fn serialize_round_trip_event_linkage() {
1219 let d = LinkageDescriptor {
1220 transport_stream_id: 0x0001,
1221 original_network_id: 0x0002,
1222 service_id: 0x0003,
1223 linkage_type: LinkageType::EventLinkage,
1224 linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1225 target_event_id: 0x1234,
1226 target_listed: true,
1227 event_simulcast: false,
1228 }),
1229 private_data: &[],
1230 };
1231 let mut buf = vec![0u8; d.serialized_len()];
1232 d.serialize_into(&mut buf).unwrap();
1233 let re = LinkageDescriptor::parse(&buf).unwrap();
1234 assert_eq!(d, re);
1235 }
1236
1237 #[test]
1238 fn serialize_round_trip_extended_event_linkage() {
1239 let d = LinkageDescriptor {
1240 transport_stream_id: 0x0001,
1241 original_network_id: 0x0002,
1242 service_id: 0x0003,
1243 linkage_type: LinkageType::ExtendedEventLinkage(0x0E),
1244 linkage_data: LinkageData::ExtendedEventLinkage(ExtendedEventLinkageInfo {
1245 entries: vec![ExtendedEventLinkageEntry {
1246 target_event_id: 0xAAAA,
1247 target_listed: true,
1248 event_simulcast: true,
1249 link_type: LinkType::HdOrServiceFrameCompatible,
1250 target_id_type: TargetIdType::UseTargetTransportStreamId,
1251 original_network_id_flag: true,
1252 service_id_flag: true,
1253 target_id: TargetId::Dvb {
1254 target_id_type: TargetIdType::UseTargetTransportStreamId,
1255 target_transport_stream_id: Some(0x1111),
1256 target_original_network_id: Some(0x2222),
1257 target_service_id: Some(0x3333),
1258 },
1259 }],
1260 }),
1261 private_data: &[0xCC],
1262 };
1263 let mut buf = vec![0u8; d.serialized_len()];
1264 d.serialize_into(&mut buf).unwrap();
1265 let re = LinkageDescriptor::parse(&buf).unwrap();
1266 assert_eq!(d, re);
1267 }
1268
1269 #[test]
1270 fn serialize_round_trip_other() {
1271 let raw = [0xAA, 0xBB, 0xCC];
1272 let d = LinkageDescriptor {
1273 transport_stream_id: 0x0001,
1274 original_network_id: 0x0002,
1275 service_id: 0x0003,
1276 linkage_type: LinkageType::IpMacNotificationService,
1277 linkage_data: LinkageData::Other(&raw),
1278 private_data: &[],
1279 };
1280 let mut buf = vec![0u8; d.serialized_len()];
1281 d.serialize_into(&mut buf).unwrap();
1282 let re = LinkageDescriptor::parse(&buf).unwrap();
1283 assert_eq!(d, re);
1284 }
1285
1286 #[test]
1287 fn serialize_reserved_bits_are_set() {
1288 let d = LinkageDescriptor {
1289 transport_stream_id: 0x0001,
1290 original_network_id: 0x0002,
1291 service_id: 0x0003,
1292 linkage_type: LinkageType::EventLinkage,
1293 linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1294 target_event_id: 0x0000,
1295 target_listed: false,
1296 event_simulcast: false,
1297 }),
1298 private_data: &[],
1299 };
1300 let mut buf = vec![0u8; d.serialized_len()];
1301 d.serialize_into(&mut buf).unwrap();
1302 assert_eq!(buf[11] & RESERVED_EVENT_MASK, RESERVED_EVENT_MASK);
1303
1304 let d2 = LinkageDescriptor {
1305 transport_stream_id: 0x0001,
1306 original_network_id: 0x0002,
1307 service_id: 0x0003,
1308 linkage_type: LinkageType::MobileHandOver,
1309 linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1310 hand_over_type: HandOverType::Reserved(0),
1311 origin_type: true,
1312 network_id: None,
1313 initial_service_id: None,
1314 }),
1315 private_data: &[],
1316 };
1317 let mut buf2 = vec![0u8; d2.serialized_len()];
1318 d2.serialize_into(&mut buf2).unwrap();
1319 assert_eq!(buf2[9] & RESERVED_HANDOVER_MASK, RESERVED_HANDOVER_MASK);
1320 }
1321
1322 #[test]
1323 fn parse_mobile_handover_type4_no_network_id() {
1324 let bytes = [
1325 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x4E, 0x00, 0x20, ];
1333 let d = LinkageDescriptor::parse(&bytes).unwrap();
1334 match &d.linkage_data {
1335 LinkageData::MobileHandOver(m) => {
1336 assert_eq!(m.hand_over_type, HandOverType::Reserved(4));
1337 assert!(!m.origin_type);
1338 assert_eq!(m.network_id, None);
1339 assert_eq!(m.initial_service_id, Some(0x0020));
1340 }
1341 other => panic!("expected MobileHandOver, got {other:?}"),
1342 }
1343 }
1344
1345 #[test]
1346 fn parse_mobile_handover_type1_network_id_present() {
1347 let bytes = [
1348 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x1F, 0x00, 0x10, 0xCA, 0xFE, ];
1357 let d = LinkageDescriptor::parse(&bytes).unwrap();
1358 match &d.linkage_data {
1359 LinkageData::MobileHandOver(m) => {
1360 assert_eq!(
1361 m.hand_over_type,
1362 HandOverType::DvbIdenticalNeighbouringCountry
1363 );
1364 assert!(m.origin_type);
1365 assert_eq!(m.network_id, Some(0x0010));
1366 assert_eq!(m.initial_service_id, None);
1367 }
1368 other => panic!("expected MobileHandOver, got {other:?}"),
1369 }
1370 assert_eq!(d.private_data, &[0xCA, 0xFE]);
1371 }
1372
1373 #[test]
1374 fn linkage_type_full_range_round_trip() {
1375 for b in 0..=0xFF_u8 {
1376 let lt = LinkageType::from_u8(b);
1377 assert_eq!(lt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1378 }
1379 }
1380
1381 #[test]
1382 fn hand_over_type_full_range_round_trip() {
1383 for b in 0..=0xFF_u8 {
1384 let ht = HandOverType::from_u8(b);
1385 assert_eq!(ht.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1386 }
1387 }
1388
1389 #[test]
1390 fn link_type_full_range_round_trip() {
1391 for b in 0..=0xFF_u8 {
1392 let lt = LinkType::from_u8(b);
1393 assert_eq!(lt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1394 }
1395 }
1396
1397 #[test]
1398 fn target_id_type_full_range_round_trip() {
1399 for b in 0..=0xFF_u8 {
1400 let tt = TargetIdType::from_u8(b);
1401 assert_eq!(tt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1402 }
1403 }
1404}