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_bytes, _) = bytes
665 .get(pos..)
666 .and_then(|s| s.split_first_chunk::<2>())
667 .ok_or(Error::InvalidDescriptor {
668 tag: TAG,
669 reason: "mobile hand-over info with gated network_id needs at least 3 bytes",
670 })?;
671 let nid = u16::from_be_bytes(*nid_bytes);
672 pos += 2;
673 Some(nid)
674 } else {
675 None
676 };
677 let initial_service_id = if !origin_type {
678 if pos + 2 > end {
679 return Err(Error::InvalidDescriptor {
680 tag: TAG,
681 reason: "mobile hand-over info with origin_type=NIT needs initial_service_id",
682 });
683 }
684 let (sid_bytes, _) = bytes
685 .get(pos..)
686 .and_then(|s| s.split_first_chunk::<2>())
687 .ok_or(Error::InvalidDescriptor {
688 tag: TAG,
689 reason: "mobile hand-over info with origin_type=NIT needs initial_service_id",
690 })?;
691 Some(u16::from_be_bytes(*sid_bytes))
692 } else {
693 None
694 };
695 Ok(MobileHandOverInfo {
696 hand_over_type,
697 origin_type,
698 network_id,
699 initial_service_id,
700 })
701}
702
703fn parse_event_linkage(bytes: &[u8]) -> Result<EventLinkageInfo> {
704 let (hdr, _) = bytes
705 .split_first_chunk::<3>()
706 .ok_or(Error::InvalidDescriptor {
707 tag: TAG,
708 reason: "event linkage info needs 3 bytes",
709 })?;
710 let target_event_id = u16::from_be_bytes([hdr[0], hdr[1]]);
711 let target_listed = (hdr[2] & TARGET_LISTED_MASK) != 0;
712 let event_simulcast = (hdr[2] & EVENT_SIMULCAST_MASK) != 0;
713 Ok(EventLinkageInfo {
714 target_event_id,
715 target_listed,
716 event_simulcast,
717 })
718}
719
720fn parse_extended_event_linkage(bytes: &[u8]) -> Result<ExtendedEventLinkageInfo> {
721 if bytes.is_empty() {
722 return Err(Error::InvalidDescriptor {
723 tag: TAG,
724 reason: "extended event linkage info needs at least loop_length byte",
725 });
726 }
727 let loop_length = bytes[0] as usize;
728 let loop_end = 1 + loop_length;
729 if bytes.len() < loop_end {
730 return Err(Error::BufferTooShort {
731 need: loop_end,
732 have: bytes.len(),
733 what: "extended event linkage info loop",
734 });
735 }
736 let mut entries = Vec::new();
737 let mut pos = 1;
738 let read_u16 = |p: &mut usize| -> Result<u16> {
739 if *p + 2 > loop_end {
740 return Err(Error::InvalidDescriptor {
741 tag: TAG,
742 reason: "extended event linkage entry truncated (need u16)",
743 });
744 }
745 let (b, _) = bytes[*p..]
746 .split_first_chunk::<2>()
747 .ok_or(Error::InvalidDescriptor {
748 tag: TAG,
749 reason: "extended event linkage entry truncated (need u16)",
750 })?;
751 let v = u16::from_be_bytes(*b);
752 *p += 2;
753 Ok(v)
754 };
755 while pos < loop_end {
756 let target_event_id = read_u16(&mut pos)?;
757 if pos >= loop_end {
758 return Err(Error::InvalidDescriptor {
759 tag: TAG,
760 reason: "extended event linkage entry truncated (need flags byte)",
761 });
762 }
763 let fb = bytes[pos];
764 pos += 1;
765 let target_listed = (fb & EXT_TARGET_LISTED_MASK) != 0;
766 let event_simulcast = (fb & EXT_EVENT_SIMULCAST_MASK) != 0;
767 let link_type = LinkType::from_u8((fb & EXT_LINK_TYPE_MASK) >> 4);
768 let target_id_type = TargetIdType::from_u8((fb & EXT_TARGET_ID_TYPE_MASK) >> 2);
769 let original_network_id_flag = (fb & EXT_ONID_FLAG_MASK) != 0;
770 let service_id_flag = (fb & EXT_SID_FLAG_MASK) != 0;
771
772 let target_id = if target_id_type == TargetIdType::UseUserDefinedId {
773 let user_defined_id = read_u16(&mut pos)?;
774 TargetId::UserDefined { user_defined_id }
775 } else {
776 let target_transport_stream_id =
777 if target_id_type == TargetIdType::UseTargetTransportStreamId {
778 Some(read_u16(&mut pos)?)
779 } else {
780 None
781 };
782 let target_original_network_id = if original_network_id_flag {
783 Some(read_u16(&mut pos)?)
784 } else {
785 None
786 };
787 let target_service_id = if service_id_flag {
788 Some(read_u16(&mut pos)?)
789 } else {
790 None
791 };
792 TargetId::Dvb {
793 target_id_type,
794 target_transport_stream_id,
795 target_original_network_id,
796 target_service_id,
797 }
798 };
799 entries.push(ExtendedEventLinkageEntry {
800 target_event_id,
801 target_listed,
802 event_simulcast,
803 link_type,
804 target_id_type,
805 original_network_id_flag,
806 service_id_flag,
807 target_id,
808 });
809 }
810 Ok(ExtendedEventLinkageInfo { entries })
811}
812
813#[derive(Debug, Clone, PartialEq, Eq)]
815#[cfg_attr(feature = "serde", derive(serde::Serialize))]
816#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
817pub struct LinkageDescriptor<'a> {
818 pub transport_stream_id: u16,
820 pub original_network_id: u16,
822 pub service_id: u16,
825 pub linkage_type: LinkageType,
827 pub linkage_data: LinkageData<'a>,
829 pub private_data: &'a [u8],
832}
833
834const LINKAGE_TYPES_WITH_OTHER: &[u8] = &[0x09, 0x0A, 0x0B, 0x0C, 0x20, 0x21];
835
836impl<'a> Parse<'a> for LinkageDescriptor<'a> {
837 type Error = crate::error::Error;
838 fn parse(bytes: &'a [u8]) -> Result<Self> {
839 let body = descriptor_body(
840 bytes,
841 TAG,
842 "LinkageDescriptor",
843 "unexpected tag for linkage_descriptor",
844 )?;
845 let (fixed, tail) =
846 body.split_first_chunk::<FIXED_FIELDS_LEN>()
847 .ok_or(Error::InvalidDescriptor {
848 tag: TAG,
849 reason: "linkage_descriptor body shorter than minimum 7 bytes",
850 })?;
851 let transport_stream_id = u16::from_be_bytes([fixed[0], fixed[1]]);
852 let original_network_id = u16::from_be_bytes([fixed[2], fixed[3]]);
853 let service_id = u16::from_be_bytes([fixed[4], fixed[5]]);
854 let linkage_type_raw = fixed[6];
855 let linkage_type = LinkageType::from_u8(linkage_type_raw);
856 let tail_len = tail.len();
857
858 let (linkage_data, private_data) = match linkage_type_raw {
859 0x08 => {
860 let info = parse_mobile_handover(tail, tail_len)?;
861 let consumed = info.serialized_len();
862 (LinkageData::MobileHandOver(info), &tail[consumed..])
863 }
864 0x0D => {
865 let info = parse_event_linkage(tail)?;
866 let consumed = EventLinkageInfo::SERIALIZED_LEN;
867 (LinkageData::EventLinkage(info), &tail[consumed..])
868 }
869 0x0E..=0x1F => {
870 let info = parse_extended_event_linkage(tail)?;
871 let consumed = info.serialized_len();
872 (LinkageData::ExtendedEventLinkage(info), &tail[consumed..])
873 }
874 lt if LINKAGE_TYPES_WITH_OTHER.contains(<) || (0x80..=0xFE).contains(<) => {
875 (LinkageData::Other(tail), &[] as &[u8])
876 }
877 _ => (LinkageData::None, tail),
878 };
879 Ok(Self {
880 transport_stream_id,
881 original_network_id,
882 service_id,
883 linkage_type,
884 linkage_data,
885 private_data,
886 })
887 }
888}
889
890impl Serialize for LinkageDescriptor<'_> {
891 type Error = crate::error::Error;
892 fn serialized_len(&self) -> usize {
893 HEADER_LEN + FIXED_FIELDS_LEN + self.linkage_data.serialized_len() + self.private_data.len()
894 }
895
896 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
897 let len = self.serialized_len();
898 if buf.len() < len {
899 return Err(Error::OutputBufferTooSmall {
900 need: len,
901 have: buf.len(),
902 });
903 }
904 buf[0] = TAG;
905 buf[1] = (len - HEADER_LEN) as u8;
906 let bs = HEADER_LEN;
907 buf[bs..bs + 2].copy_from_slice(&self.transport_stream_id.to_be_bytes());
908 buf[bs + 2..bs + 4].copy_from_slice(&self.original_network_id.to_be_bytes());
909 buf[bs + 4..bs + 6].copy_from_slice(&self.service_id.to_be_bytes());
910 buf[bs + 6] = self.linkage_type.to_u8();
911 let ld_start = bs + FIXED_FIELDS_LEN;
912 let ld_written = self.linkage_data.serialize_into(&mut buf[ld_start..])?;
913 let pd_start = ld_start + ld_written;
914 if !self.private_data.is_empty() {
915 buf[pd_start..pd_start + self.private_data.len()].copy_from_slice(self.private_data);
916 }
917 Ok(len)
918 }
919}
920impl<'a> crate::traits::DescriptorDef<'a> for LinkageDescriptor<'a> {
921 const TAG: u8 = TAG;
922 const NAME: &'static str = "LINKAGE";
923}
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928
929 #[test]
930 fn parse_extracts_tsid_onid_sid() {
931 let bytes = [
932 TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB,
933 ];
934 let d = LinkageDescriptor::parse(&bytes).unwrap();
935 assert_eq!(d.transport_stream_id, 0x0001);
936 assert_eq!(d.original_network_id, 0x0002);
937 assert_eq!(d.service_id, 0x0003);
938 }
939
940 #[test]
941 fn parse_extracts_linkage_type() {
942 let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x06];
943 let d = LinkageDescriptor::parse(&bytes).unwrap();
944 assert_eq!(d.linkage_type, LinkageType::DataBroadcastService);
945 }
946
947 #[test]
948 fn parse_none_type_preserves_private_data() {
949 let bytes = [
950 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB, 0xCC,
951 ];
952 let d = LinkageDescriptor::parse(&bytes).unwrap();
953 assert!(matches!(d.linkage_data, LinkageData::None));
954 assert_eq!(d.private_data, &[0xAA, 0xBB, 0xCC]);
955 }
956
957 #[test]
958 fn parse_accepts_empty_private_data() {
959 let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05];
960 let d = LinkageDescriptor::parse(&bytes).unwrap();
961 assert!(d.private_data.is_empty());
962 }
963
964 #[test]
965 fn parse_mobile_handover_with_initial_sid() {
966 let bytes = [
967 TAG, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x12, 0x00, 0x10, 0x00, 0x20, 0xDE, 0xAD, ];
977 let d = LinkageDescriptor::parse(&bytes).unwrap();
978 assert_eq!(d.linkage_type, LinkageType::MobileHandOver);
979 match &d.linkage_data {
980 LinkageData::MobileHandOver(m) => {
981 assert_eq!(
982 m.hand_over_type,
983 HandOverType::DvbIdenticalNeighbouringCountry
984 );
985 assert!(!m.origin_type);
986 assert_eq!(m.network_id, Some(0x0010));
987 assert_eq!(m.initial_service_id, Some(0x0020));
988 }
989 other => panic!("expected MobileHandOver, got {other:?}"),
990 }
991 assert_eq!(d.private_data, &[0xDE, 0xAD]);
992 }
993
994 #[test]
995 fn parse_mobile_handover_sdt_no_initial_sid() {
996 let bytes = [
997 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x2F, 0x00, 0x10, 0xCA, 0xFE, ];
1006 let d = LinkageDescriptor::parse(&bytes).unwrap();
1007 match &d.linkage_data {
1008 LinkageData::MobileHandOver(m) => {
1009 assert_eq!(m.hand_over_type, HandOverType::DvbLocalVariation);
1010 assert!(m.origin_type);
1011 assert_eq!(m.network_id, Some(0x0010));
1012 assert_eq!(m.initial_service_id, None);
1013 }
1014 other => panic!("expected MobileHandOver, got {other:?}"),
1015 }
1016 assert_eq!(d.private_data, &[0xCA, 0xFE]);
1017 }
1018
1019 #[test]
1020 fn parse_event_linkage() {
1021 let bytes = [
1022 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0D, 0xAB, 0xCD, 0xC0, 0xBE, 0xEF, ];
1031 let d = LinkageDescriptor::parse(&bytes).unwrap();
1032 match &d.linkage_data {
1033 LinkageData::EventLinkage(e) => {
1034 assert_eq!(e.target_event_id, 0xABCD);
1035 assert!(e.target_listed);
1036 assert!(e.event_simulcast);
1037 }
1038 other => panic!("expected EventLinkage, got {other:?}"),
1039 }
1040 assert_eq!(d.private_data, &[0xBE, 0xEF]);
1041 }
1042
1043 #[test]
1044 fn parse_extended_event_linkage_user_defined() {
1045 let bytes = [
1046 TAG, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0E, 0x05, 0x12, 0x34, 0xCC, 0x56, 0x78, 0xCC, ];
1057 let d = LinkageDescriptor::parse(&bytes).unwrap();
1058 match &d.linkage_data {
1059 LinkageData::ExtendedEventLinkage(x) => {
1060 assert_eq!(x.entries.len(), 1);
1061 let e = &x.entries[0];
1062 assert_eq!(e.target_event_id, 0x1234);
1063 assert!(e.target_listed);
1064 assert!(e.event_simulcast);
1065 assert_eq!(e.link_type, LinkType::SdOrUhd);
1066 assert_eq!(e.target_id_type, TargetIdType::UseUserDefinedId);
1067 assert_eq!(
1068 e.target_id,
1069 TargetId::UserDefined {
1070 user_defined_id: 0x5678
1071 }
1072 );
1073 }
1074 other => panic!("expected ExtendedEventLinkage, got {other:?}"),
1075 }
1076 assert_eq!(d.private_data, &[0xCC]);
1077 }
1078
1079 #[test]
1080 fn parse_extended_event_linkage_dvb_target() {
1081 let bytes = [
1082 TAG, 0x0F, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0F, 0x07, 0xAA, 0xBB, 0x26, 0x00, 0x11, 0x00, 0x22, ];
1093 let d = LinkageDescriptor::parse(&bytes).unwrap();
1094 match &d.linkage_data {
1095 LinkageData::ExtendedEventLinkage(x) => {
1096 assert_eq!(x.entries.len(), 1);
1097 let e = &x.entries[0];
1098 assert_eq!(e.target_id_type, TargetIdType::UseTargetTransportStreamId);
1099 assert!(e.original_network_id_flag);
1100 assert!(!e.service_id_flag);
1101 assert_eq!(
1102 e.target_id,
1103 TargetId::Dvb {
1104 target_id_type: TargetIdType::UseTargetTransportStreamId,
1105 target_transport_stream_id: Some(0x0011),
1106 target_original_network_id: Some(0x0022),
1107 target_service_id: None,
1108 }
1109 );
1110 }
1111 other => panic!("expected ExtendedEventLinkage, got {other:?}"),
1112 }
1113 assert_eq!(d.private_data, &[] as &[u8]);
1114 }
1115
1116 #[test]
1117 fn parse_other_type_captures_raw_tail() {
1118 let bytes = [
1119 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0B, 0xAA, 0xBB, 0xCC, ];
1126 let d = LinkageDescriptor::parse(&bytes).unwrap();
1127 match &d.linkage_data {
1128 LinkageData::Other(b) => assert_eq!(*b, &[0xAA, 0xBB, 0xCC]),
1129 other => panic!("expected Other, got {other:?}"),
1130 }
1131 assert!(d.private_data.is_empty());
1132 }
1133
1134 #[test]
1135 fn parse_user_defined_type_is_other() {
1136 let bytes = [
1137 TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x90, 0xFF, 0xFE, ];
1144 let d = LinkageDescriptor::parse(&bytes).unwrap();
1145 assert!(matches!(d.linkage_data, LinkageData::Other(_)));
1146 }
1147
1148 #[test]
1149 fn parse_rejects_wrong_tag() {
1150 let err = LinkageDescriptor::parse(&[0x4B, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05])
1151 .unwrap_err();
1152 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x4B, .. }));
1153 }
1154
1155 #[test]
1156 fn parse_rejects_body_shorter_than_seven() {
1157 let bytes = [TAG, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00];
1158 let err = LinkageDescriptor::parse(&bytes).unwrap_err();
1159 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
1160 }
1161
1162 #[test]
1163 fn parse_rejects_truncated_buffer() {
1164 let err = LinkageDescriptor::parse(&[TAG]).unwrap_err();
1165 assert!(matches!(err, Error::BufferTooShort { .. }));
1166 }
1167
1168 #[test]
1169 fn parse_rejects_truncated_mobile_handover() {
1170 let bytes = [
1171 TAG, 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x10, 0x00, 0x10, ];
1176 let err = LinkageDescriptor::parse(&bytes).unwrap_err();
1177 assert!(
1178 matches!(err, Error::InvalidDescriptor { .. }),
1179 "expected InvalidDescriptor for truncated mobile hand-over, got {err:?}"
1180 );
1181 }
1182
1183 #[test]
1184 fn serialize_round_trip_no_linkage_data() {
1185 let d = LinkageDescriptor {
1186 transport_stream_id: 0x1234,
1187 original_network_id: 0x5678,
1188 service_id: 0xABCD,
1189 linkage_type: LinkageType::EpgService,
1190 linkage_data: LinkageData::None,
1191 private_data: &[],
1192 };
1193 let mut buf = vec![0u8; d.serialized_len()];
1194 d.serialize_into(&mut buf).unwrap();
1195 let re = LinkageDescriptor::parse(&buf).unwrap();
1196 assert_eq!(d, re);
1197 }
1198
1199 #[test]
1200 fn serialize_round_trip_with_private_data() {
1201 let d = LinkageDescriptor {
1202 transport_stream_id: 0x0001,
1203 original_network_id: 0x0002,
1204 service_id: 0x0003,
1205 linkage_type: LinkageType::ServiceReplacementService,
1206 linkage_data: LinkageData::None,
1207 private_data: &[0xDE, 0xAD, 0xBE, 0xEF],
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_mobile_handover() {
1217 let d = LinkageDescriptor {
1218 transport_stream_id: 0x0001,
1219 original_network_id: 0x0002,
1220 service_id: 0x0003,
1221 linkage_type: LinkageType::MobileHandOver,
1222 linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1223 hand_over_type: HandOverType::DvbAssociatedService,
1224 origin_type: false,
1225 network_id: Some(0x0044),
1226 initial_service_id: Some(0x0055),
1227 }),
1228 private_data: &[0xFF],
1229 };
1230 let mut buf = vec![0u8; d.serialized_len()];
1231 d.serialize_into(&mut buf).unwrap();
1232 let re = LinkageDescriptor::parse(&buf).unwrap();
1233 assert_eq!(d, re);
1234 }
1235
1236 #[test]
1237 fn serialize_round_trip_event_linkage() {
1238 let d = LinkageDescriptor {
1239 transport_stream_id: 0x0001,
1240 original_network_id: 0x0002,
1241 service_id: 0x0003,
1242 linkage_type: LinkageType::EventLinkage,
1243 linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1244 target_event_id: 0x1234,
1245 target_listed: true,
1246 event_simulcast: false,
1247 }),
1248 private_data: &[],
1249 };
1250 let mut buf = vec![0u8; d.serialized_len()];
1251 d.serialize_into(&mut buf).unwrap();
1252 let re = LinkageDescriptor::parse(&buf).unwrap();
1253 assert_eq!(d, re);
1254 }
1255
1256 #[test]
1257 fn serialize_round_trip_extended_event_linkage() {
1258 let d = LinkageDescriptor {
1259 transport_stream_id: 0x0001,
1260 original_network_id: 0x0002,
1261 service_id: 0x0003,
1262 linkage_type: LinkageType::ExtendedEventLinkage(0x0E),
1263 linkage_data: LinkageData::ExtendedEventLinkage(ExtendedEventLinkageInfo {
1264 entries: vec![ExtendedEventLinkageEntry {
1265 target_event_id: 0xAAAA,
1266 target_listed: true,
1267 event_simulcast: true,
1268 link_type: LinkType::HdOrServiceFrameCompatible,
1269 target_id_type: TargetIdType::UseTargetTransportStreamId,
1270 original_network_id_flag: true,
1271 service_id_flag: true,
1272 target_id: TargetId::Dvb {
1273 target_id_type: TargetIdType::UseTargetTransportStreamId,
1274 target_transport_stream_id: Some(0x1111),
1275 target_original_network_id: Some(0x2222),
1276 target_service_id: Some(0x3333),
1277 },
1278 }],
1279 }),
1280 private_data: &[0xCC],
1281 };
1282 let mut buf = vec![0u8; d.serialized_len()];
1283 d.serialize_into(&mut buf).unwrap();
1284 let re = LinkageDescriptor::parse(&buf).unwrap();
1285 assert_eq!(d, re);
1286 }
1287
1288 #[test]
1289 fn serialize_round_trip_other() {
1290 let raw = [0xAA, 0xBB, 0xCC];
1291 let d = LinkageDescriptor {
1292 transport_stream_id: 0x0001,
1293 original_network_id: 0x0002,
1294 service_id: 0x0003,
1295 linkage_type: LinkageType::IpMacNotificationService,
1296 linkage_data: LinkageData::Other(&raw),
1297 private_data: &[],
1298 };
1299 let mut buf = vec![0u8; d.serialized_len()];
1300 d.serialize_into(&mut buf).unwrap();
1301 let re = LinkageDescriptor::parse(&buf).unwrap();
1302 assert_eq!(d, re);
1303 }
1304
1305 #[test]
1306 fn serialize_reserved_bits_are_set() {
1307 let d = LinkageDescriptor {
1308 transport_stream_id: 0x0001,
1309 original_network_id: 0x0002,
1310 service_id: 0x0003,
1311 linkage_type: LinkageType::EventLinkage,
1312 linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1313 target_event_id: 0x0000,
1314 target_listed: false,
1315 event_simulcast: false,
1316 }),
1317 private_data: &[],
1318 };
1319 let mut buf = vec![0u8; d.serialized_len()];
1320 d.serialize_into(&mut buf).unwrap();
1321 assert_eq!(buf[11] & RESERVED_EVENT_MASK, RESERVED_EVENT_MASK);
1322
1323 let d2 = LinkageDescriptor {
1324 transport_stream_id: 0x0001,
1325 original_network_id: 0x0002,
1326 service_id: 0x0003,
1327 linkage_type: LinkageType::MobileHandOver,
1328 linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1329 hand_over_type: HandOverType::Reserved(0),
1330 origin_type: true,
1331 network_id: None,
1332 initial_service_id: None,
1333 }),
1334 private_data: &[],
1335 };
1336 let mut buf2 = vec![0u8; d2.serialized_len()];
1337 d2.serialize_into(&mut buf2).unwrap();
1338 assert_eq!(buf2[9] & RESERVED_HANDOVER_MASK, RESERVED_HANDOVER_MASK);
1339 }
1340
1341 #[test]
1342 fn parse_mobile_handover_type4_no_network_id() {
1343 let bytes = [
1344 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x4E, 0x00, 0x20, ];
1352 let d = LinkageDescriptor::parse(&bytes).unwrap();
1353 match &d.linkage_data {
1354 LinkageData::MobileHandOver(m) => {
1355 assert_eq!(m.hand_over_type, HandOverType::Reserved(4));
1356 assert!(!m.origin_type);
1357 assert_eq!(m.network_id, None);
1358 assert_eq!(m.initial_service_id, Some(0x0020));
1359 }
1360 other => panic!("expected MobileHandOver, got {other:?}"),
1361 }
1362 }
1363
1364 #[test]
1365 fn parse_mobile_handover_type1_network_id_present() {
1366 let bytes = [
1367 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x1F, 0x00, 0x10, 0xCA, 0xFE, ];
1376 let d = LinkageDescriptor::parse(&bytes).unwrap();
1377 match &d.linkage_data {
1378 LinkageData::MobileHandOver(m) => {
1379 assert_eq!(
1380 m.hand_over_type,
1381 HandOverType::DvbIdenticalNeighbouringCountry
1382 );
1383 assert!(m.origin_type);
1384 assert_eq!(m.network_id, Some(0x0010));
1385 assert_eq!(m.initial_service_id, None);
1386 }
1387 other => panic!("expected MobileHandOver, got {other:?}"),
1388 }
1389 assert_eq!(d.private_data, &[0xCA, 0xFE]);
1390 }
1391
1392 #[test]
1393 fn linkage_type_full_range_round_trip() {
1394 for b in 0..=0xFF_u8 {
1395 let lt = LinkageType::from_u8(b);
1396 assert_eq!(lt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1397 }
1398 }
1399
1400 #[test]
1401 fn hand_over_type_full_range_round_trip() {
1402 for b in 0..=0xFF_u8 {
1403 let ht = HandOverType::from_u8(b);
1404 assert_eq!(ht.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1405 }
1406 }
1407
1408 #[test]
1409 fn link_type_full_range_round_trip() {
1410 for b in 0..=0xFF_u8 {
1411 let lt = LinkType::from_u8(b);
1412 assert_eq!(lt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1413 }
1414 }
1415
1416 #[test]
1417 fn target_id_type_full_range_round_trip() {
1418 for b in 0..=0xFF_u8 {
1419 let tt = TargetIdType::from_u8(b);
1420 assert_eq!(tt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1421 }
1422 }
1423}