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