Skip to main content

dvb_si/descriptors/
logical_channel.rs

1//! Logical Channel Descriptor — EACEM/NorDig/D-Book private (tag 0x83).
2//!
3//! Carried inside NIT transport_stream_loop second descriptor loop.
4//! Assigns an LCN (Logical Channel Number) to each service, plus a
5//! `visible_service` flag for hiding services from the channel list.
6
7use super::descriptor_body;
8use crate::error::{Error, Result};
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for logical_channel_descriptor.
12pub const TAG: u8 = 0x83;
13const HEADER_LEN: usize = 2;
14const ENTRY_LEN: usize = 4;
15const VISIBLE_MASK: u8 = 0x80;
16const RESERVED_BITS_MASK: u8 = 0x7C;
17const LCN_HI_MASK: u8 = 0x03;
18
19/// One LCN assignment.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct LogicalChannelEntry {
23    /// Service being numbered.
24    pub service_id: u16,
25    /// Visible in the viewer's channel list.
26    pub visible_service: bool,
27    /// 10-bit logical channel number (0..=1023).
28    pub logical_channel_number: u16,
29}
30
31/// Logical Channel Descriptor.
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34pub struct LogicalChannelDescriptor {
35    /// Entries in wire order.
36    pub entries: Vec<LogicalChannelEntry>,
37}
38
39impl<'a> Parse<'a> for LogicalChannelDescriptor {
40    type Error = crate::error::Error;
41    fn parse(bytes: &'a [u8]) -> Result<Self> {
42        let body = descriptor_body(
43            bytes,
44            TAG,
45            "LogicalChannelDescriptor",
46            "unexpected tag for logical_channel_descriptor",
47        )?;
48        if body.len() % ENTRY_LEN != 0 {
49            return Err(Error::InvalidDescriptor {
50                tag: TAG,
51                reason: "descriptor_length must be a multiple of 4",
52            });
53        }
54        let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
55        let mut offset = 0;
56        while offset < body.len() {
57            let service_id = u16::from_be_bytes([body[offset], body[offset + 1]]);
58            let flags = body[offset + 2];
59            let visible_service = flags & VISIBLE_MASK != 0;
60            let lcn = (u16::from(flags & LCN_HI_MASK) << 8) | u16::from(body[offset + 3]);
61            entries.push(LogicalChannelEntry {
62                service_id,
63                visible_service,
64                logical_channel_number: lcn,
65            });
66            offset += ENTRY_LEN;
67        }
68        Ok(Self { entries })
69    }
70}
71
72impl Serialize for LogicalChannelDescriptor {
73    type Error = crate::error::Error;
74    fn serialized_len(&self) -> usize {
75        HEADER_LEN + ENTRY_LEN * self.entries.len()
76    }
77
78    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
79        let len = self.serialized_len();
80        if buf.len() < len {
81            return Err(Error::OutputBufferTooSmall {
82                need: len,
83                have: buf.len(),
84            });
85        }
86        buf[0] = TAG;
87        buf[1] = (len - HEADER_LEN) as u8;
88        let mut offset = HEADER_LEN;
89        for entry in &self.entries {
90            buf[offset..offset + 2].copy_from_slice(&entry.service_id.to_be_bytes());
91            let visible_byte = if entry.visible_service {
92                VISIBLE_MASK
93            } else {
94                0
95            };
96            let flags = visible_byte
97                | RESERVED_BITS_MASK
98                | ((entry.logical_channel_number >> 8) as u8 & LCN_HI_MASK);
99            buf[offset + 2] = flags;
100            buf[offset + 3] = (entry.logical_channel_number & 0xFF) as u8;
101            offset += ENTRY_LEN;
102        }
103        Ok(len)
104    }
105}
106impl<'a> crate::traits::DescriptorDef<'a> for LogicalChannelDescriptor {
107    const TAG: u8 = TAG;
108    const NAME: &'static str = "LOGICAL_CHANNEL";
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn parse_single_entry() {
117        let bytes = [TAG, 4, 0x00, 0x01, 0xFC, 0x05];
118        let d = LogicalChannelDescriptor::parse(&bytes).unwrap();
119        assert_eq!(d.entries.len(), 1);
120        assert_eq!(d.entries[0].service_id, 1);
121        assert!(d.entries[0].visible_service);
122        assert_eq!(d.entries[0].logical_channel_number, 5);
123    }
124
125    #[test]
126    fn parse_extracts_visible_service_false() {
127        let bytes = [TAG, 4, 0x00, 0x02, 0x7C, 0x0A];
128        let d = LogicalChannelDescriptor::parse(&bytes).unwrap();
129        assert_eq!(d.entries.len(), 1);
130        assert!(!d.entries[0].visible_service);
131        assert_eq!(d.entries[0].service_id, 2);
132        assert_eq!(d.entries[0].logical_channel_number, 10);
133    }
134
135    #[test]
136    fn parse_extracts_logical_channel_number_full_10_bit_range() {
137        let bytes = [TAG, 4, 0x00, 0x03, 0xFF, 0xFF];
138        let d = LogicalChannelDescriptor::parse(&bytes).unwrap();
139        assert_eq!(d.entries.len(), 1);
140        assert_eq!(d.entries[0].logical_channel_number, 1023);
141        assert!(d.entries[0].visible_service);
142    }
143
144    #[test]
145    fn parse_multiple_entries_preserves_order() {
146        let bytes = [
147            TAG, 12, 0x00, 0x01, 0xFC, 0x01, 0x00, 0x02, 0xFC, 0x02, 0x00, 0x03, 0xFC, 0x03,
148        ];
149        let d = LogicalChannelDescriptor::parse(&bytes).unwrap();
150        assert_eq!(d.entries.len(), 3);
151        assert_eq!(d.entries[0].service_id, 1);
152        assert_eq!(d.entries[0].logical_channel_number, 1);
153        assert_eq!(d.entries[1].service_id, 2);
154        assert_eq!(d.entries[1].logical_channel_number, 2);
155        assert_eq!(d.entries[2].service_id, 3);
156        assert_eq!(d.entries[2].logical_channel_number, 3);
157    }
158
159    #[test]
160    fn parse_rejects_wrong_tag() {
161        let err = LogicalChannelDescriptor::parse(&[0x84, 4, 0x00, 0x01, 0xFC, 0x05]).unwrap_err();
162        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x84, .. }));
163    }
164
165    #[test]
166    fn parse_rejects_length_not_multiple_of_4() {
167        let bytes = [TAG, 5, 0x00, 0x01, 0xFC, 0x05, 0xFF];
168        let err = LogicalChannelDescriptor::parse(&bytes).unwrap_err();
169        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
170    }
171
172    #[test]
173    fn parse_tolerates_cleared_reserved_bits() {
174        // Real-world broadcasts (TNTSat / ORF Digital) emit
175        // logical_channel_descriptors where the reserved bits aren't all
176        // set. ETSI EN 300 468 §5.1 says "decoders shall ignore reserved
177        // bits" — so we MUST accept the descriptor and just decode the
178        // documented fields.
179        //
180        // The flags byte 0x00 here has visible_service=0, reserved=0,
181        // LCN-hi=0. Combined with byte 0x05 (LCN-lo) the LCN is 5.
182        let bytes = [TAG, 4, 0x00, 0x01, 0x00, 0x05];
183        let d = LogicalChannelDescriptor::parse(&bytes).expect("tolerant parse");
184        assert_eq!(d.entries.len(), 1);
185        assert_eq!(d.entries[0].service_id, 1);
186        assert_eq!(d.entries[0].logical_channel_number, 5);
187        assert!(!d.entries[0].visible_service);
188    }
189
190    #[test]
191    fn empty_descriptor_valid() {
192        let bytes = [TAG, 0];
193        let d = LogicalChannelDescriptor::parse(&bytes).unwrap();
194        assert!(d.entries.is_empty());
195    }
196
197    #[test]
198    fn serialize_round_trip() {
199        let d = LogicalChannelDescriptor {
200            entries: vec![
201                LogicalChannelEntry {
202                    service_id: 1,
203                    visible_service: true,
204                    logical_channel_number: 5,
205                },
206                LogicalChannelEntry {
207                    service_id: 0x0102,
208                    visible_service: false,
209                    logical_channel_number: 1023,
210                },
211            ],
212        };
213        let mut buf = vec![0u8; d.serialized_len()];
214        d.serialize_into(&mut buf).unwrap();
215        let re = LogicalChannelDescriptor::parse(&buf).unwrap();
216        assert_eq!(d, re);
217    }
218}