Skip to main content

dvb_si/descriptors/
nvod_reference.rs

1//! NVOD Reference Descriptor — ETSI EN 300 468 §6.2.28 (tag 0x4B).
2//!
3//! Table 80 (PDF p. 96). Carried in the SDT for an NVOD reference service.
4//! Body is a loop of (transport_stream_id, original_network_id, service_id)
5//! triples, each identifying one of the time-shifted NVOD services.
6
7use crate::error::{Error, Result};
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for NVOD_reference_descriptor.
12pub const TAG: u8 = 0x4B;
13const HEADER_LEN: usize = 2;
14const ENTRY_LEN: usize = 6;
15/// Maximum body length expressible in the 8-bit `descriptor_length` field.
16const MAX_BODY_LEN: usize = u8::MAX as usize;
17
18/// One NVOD reference triple.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct NvodReferenceEntry {
22    /// transport_stream_id carrying the referenced service.
23    pub transport_stream_id: u16,
24    /// original_network_id of the referenced service.
25    pub original_network_id: u16,
26    /// service_id of the referenced NVOD time-shifted service.
27    pub service_id: u16,
28}
29
30/// NVOD Reference Descriptor.
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct NvodReferenceDescriptor {
34    /// Reference triples in wire order.
35    pub entries: Vec<NvodReferenceEntry>,
36}
37
38impl<'a> Parse<'a> for NvodReferenceDescriptor {
39    type Error = crate::error::Error;
40    fn parse(bytes: &'a [u8]) -> Result<Self> {
41        if bytes.len() < HEADER_LEN {
42            return Err(Error::BufferTooShort {
43                need: HEADER_LEN,
44                have: bytes.len(),
45                what: "NvodReferenceDescriptor header",
46            });
47        }
48        if bytes[0] != TAG {
49            return Err(Error::InvalidDescriptor {
50                tag: bytes[0],
51                reason: "unexpected tag for NVOD_reference_descriptor",
52            });
53        }
54        let length = bytes[1] as usize;
55        if length % ENTRY_LEN != 0 {
56            return Err(Error::InvalidDescriptor {
57                tag: TAG,
58                reason: "descriptor_length must be a multiple of 6",
59            });
60        }
61        let end = HEADER_LEN + length;
62        if bytes.len() < end {
63            return Err(Error::BufferTooShort {
64                need: end,
65                have: bytes.len(),
66                what: "NvodReferenceDescriptor body",
67            });
68        }
69        let body = &bytes[HEADER_LEN..end];
70        let mut entries = Vec::with_capacity(length / ENTRY_LEN);
71        for chunk in body.chunks_exact(ENTRY_LEN) {
72            entries.push(NvodReferenceEntry {
73                transport_stream_id: u16::from_be_bytes([chunk[0], chunk[1]]),
74                original_network_id: u16::from_be_bytes([chunk[2], chunk[3]]),
75                service_id: u16::from_be_bytes([chunk[4], chunk[5]]),
76            });
77        }
78        Ok(Self { entries })
79    }
80}
81
82impl Serialize for NvodReferenceDescriptor {
83    type Error = crate::error::Error;
84    fn serialized_len(&self) -> usize {
85        HEADER_LEN + ENTRY_LEN * self.entries.len()
86    }
87
88    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
89        let len = self.serialized_len();
90        if buf.len() < len {
91            return Err(Error::OutputBufferTooSmall {
92                need: len,
93                have: buf.len(),
94            });
95        }
96        let body_len = ENTRY_LEN * self.entries.len();
97        // 8-bit descriptor_length field: error rather than silently truncate.
98        if body_len > MAX_BODY_LEN {
99            return Err(Error::SectionLengthOverflow {
100                declared: body_len,
101                available: MAX_BODY_LEN,
102            });
103        }
104        buf[0] = TAG;
105        buf[1] = body_len as u8;
106        let mut pos = HEADER_LEN;
107        for e in &self.entries {
108            buf[pos..pos + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
109            buf[pos + 2..pos + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
110            buf[pos + 4..pos + 6].copy_from_slice(&e.service_id.to_be_bytes());
111            pos += ENTRY_LEN;
112        }
113        Ok(len)
114    }
115}
116
117impl<'a> Descriptor<'a> for NvodReferenceDescriptor {
118    const TAG: u8 = TAG;
119    fn descriptor_length(&self) -> u8 {
120        (ENTRY_LEN * self.entries.len()) as u8
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn parse_single_triple() {
130        let bytes = [TAG, 6, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
131        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
132        assert_eq!(d.entries.len(), 1);
133        assert_eq!(d.entries[0].transport_stream_id, 1);
134        assert_eq!(d.entries[0].original_network_id, 2);
135        assert_eq!(d.entries[0].service_id, 3);
136    }
137
138    #[test]
139    fn parse_multiple_triples_preserves_order() {
140        let bytes = [
141            TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
142        ];
143        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
144        assert_eq!(d.entries.len(), 2);
145        assert_eq!(d.entries[1].transport_stream_id, 4);
146        assert_eq!(d.entries[1].original_network_id, 5);
147        assert_eq!(d.entries[1].service_id, 6);
148    }
149
150    #[test]
151    fn parse_rejects_wrong_tag() {
152        assert!(matches!(
153            NvodReferenceDescriptor::parse(&[0x4C, 6, 0, 0, 0, 0, 0, 0]).unwrap_err(),
154            Error::InvalidDescriptor { tag: 0x4C, .. }
155        ));
156    }
157
158    #[test]
159    fn parse_rejects_short_buffer() {
160        let bytes = [TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
161        assert!(matches!(
162            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
163            Error::BufferTooShort { .. }
164        ));
165    }
166
167    #[test]
168    fn parse_rejects_length_not_multiple_of_6() {
169        let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x02, 0x00];
170        assert!(matches!(
171            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
172            Error::InvalidDescriptor { tag: TAG, .. }
173        ));
174    }
175
176    #[test]
177    fn empty_descriptor_valid() {
178        let d = NvodReferenceDescriptor::parse(&[TAG, 0]).unwrap();
179        assert!(d.entries.is_empty());
180    }
181
182    #[test]
183    fn serialize_round_trip() {
184        let d = NvodReferenceDescriptor {
185            entries: vec![
186                NvodReferenceEntry {
187                    transport_stream_id: 0x1234,
188                    original_network_id: 0x5678,
189                    service_id: 0x9ABC,
190                },
191                NvodReferenceEntry {
192                    transport_stream_id: 0x0001,
193                    original_network_id: 0x0002,
194                    service_id: 0x0003,
195                },
196            ],
197        };
198        let mut buf = vec![0u8; d.serialized_len()];
199        d.serialize_into(&mut buf).unwrap();
200        assert_eq!(NvodReferenceDescriptor::parse(&buf).unwrap(), d);
201    }
202
203    #[test]
204    fn serialize_rejects_over_range_body() {
205        // 43 triples = 258 body bytes, past the u8 length field.
206        let d = NvodReferenceDescriptor {
207            entries: vec![
208                NvodReferenceEntry {
209                    transport_stream_id: 1,
210                    original_network_id: 2,
211                    service_id: 3,
212                };
213                43
214            ],
215        };
216        let mut buf = vec![0u8; d.serialized_len()];
217        assert!(matches!(
218            d.serialize_into(&mut buf).unwrap_err(),
219            Error::SectionLengthOverflow { .. }
220        ));
221    }
222
223    #[cfg(feature = "serde")]
224    #[test]
225    fn serde_round_trip() {
226        let d = NvodReferenceDescriptor {
227            entries: vec![NvodReferenceEntry {
228                transport_stream_id: 1,
229                original_network_id: 2,
230                service_id: 3,
231            }],
232        };
233        let json = serde_json::to_string(&d).unwrap();
234        let back: NvodReferenceDescriptor = serde_json::from_str(&json).unwrap();
235        assert_eq!(d, back);
236    }
237}