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 super::descriptor_body;
8use crate::error::{Error, Result};
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))]
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))]
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        let body = descriptor_body(
42            bytes,
43            TAG,
44            "NvodReferenceDescriptor",
45            "unexpected tag for NVOD_reference_descriptor",
46        )?;
47        if body.len() % ENTRY_LEN != 0 {
48            return Err(Error::InvalidDescriptor {
49                tag: TAG,
50                reason: "descriptor_length must be a multiple of 6",
51            });
52        }
53        let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
54        for chunk in body.chunks_exact(ENTRY_LEN) {
55            entries.push(NvodReferenceEntry {
56                transport_stream_id: u16::from_be_bytes([chunk[0], chunk[1]]),
57                original_network_id: u16::from_be_bytes([chunk[2], chunk[3]]),
58                service_id: u16::from_be_bytes([chunk[4], chunk[5]]),
59            });
60        }
61        Ok(Self { entries })
62    }
63}
64
65impl Serialize for NvodReferenceDescriptor {
66    type Error = crate::error::Error;
67    fn serialized_len(&self) -> usize {
68        HEADER_LEN + ENTRY_LEN * self.entries.len()
69    }
70
71    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
72        let len = self.serialized_len();
73        if buf.len() < len {
74            return Err(Error::OutputBufferTooSmall {
75                need: len,
76                have: buf.len(),
77            });
78        }
79        let body_len = ENTRY_LEN * self.entries.len();
80        // 8-bit descriptor_length field: error rather than silently truncate.
81        if body_len > MAX_BODY_LEN {
82            return Err(Error::InvalidDescriptor {
83                tag: TAG,
84                reason: "NVOD_reference_descriptor body exceeds 255 bytes",
85            });
86        }
87        buf[0] = TAG;
88        buf[1] = body_len as u8;
89        let mut pos = HEADER_LEN;
90        for e in &self.entries {
91            buf[pos..pos + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
92            buf[pos + 2..pos + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
93            buf[pos + 4..pos + 6].copy_from_slice(&e.service_id.to_be_bytes());
94            pos += ENTRY_LEN;
95        }
96        Ok(len)
97    }
98}
99impl<'a> crate::traits::DescriptorDef<'a> for NvodReferenceDescriptor {
100    const TAG: u8 = TAG;
101    const NAME: &'static str = "NVOD_REFERENCE";
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn parse_single_triple() {
110        let bytes = [TAG, 6, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
111        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
112        assert_eq!(d.entries.len(), 1);
113        assert_eq!(d.entries[0].transport_stream_id, 1);
114        assert_eq!(d.entries[0].original_network_id, 2);
115        assert_eq!(d.entries[0].service_id, 3);
116    }
117
118    #[test]
119    fn parse_multiple_triples_preserves_order() {
120        let bytes = [
121            TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
122        ];
123        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
124        assert_eq!(d.entries.len(), 2);
125        assert_eq!(d.entries[1].transport_stream_id, 4);
126        assert_eq!(d.entries[1].original_network_id, 5);
127        assert_eq!(d.entries[1].service_id, 6);
128    }
129
130    #[test]
131    fn parse_rejects_wrong_tag() {
132        assert!(matches!(
133            NvodReferenceDescriptor::parse(&[0x4C, 6, 0, 0, 0, 0, 0, 0]).unwrap_err(),
134            Error::InvalidDescriptor { tag: 0x4C, .. }
135        ));
136    }
137
138    #[test]
139    fn parse_rejects_short_buffer() {
140        let bytes = [TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
141        assert!(matches!(
142            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
143            Error::BufferTooShort { .. }
144        ));
145    }
146
147    #[test]
148    fn parse_rejects_length_not_multiple_of_6() {
149        let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x02, 0x00];
150        assert!(matches!(
151            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
152            Error::InvalidDescriptor { tag: TAG, .. }
153        ));
154    }
155
156    #[test]
157    fn empty_descriptor_valid() {
158        let d = NvodReferenceDescriptor::parse(&[TAG, 0]).unwrap();
159        assert!(d.entries.is_empty());
160    }
161
162    #[test]
163    fn serialize_round_trip() {
164        let d = NvodReferenceDescriptor {
165            entries: vec![
166                NvodReferenceEntry {
167                    transport_stream_id: 0x1234,
168                    original_network_id: 0x5678,
169                    service_id: 0x9ABC,
170                },
171                NvodReferenceEntry {
172                    transport_stream_id: 0x0001,
173                    original_network_id: 0x0002,
174                    service_id: 0x0003,
175                },
176            ],
177        };
178        let mut buf = vec![0u8; d.serialized_len()];
179        d.serialize_into(&mut buf).unwrap();
180        assert_eq!(NvodReferenceDescriptor::parse(&buf).unwrap(), d);
181    }
182
183    #[test]
184    fn serialize_rejects_over_range_body() {
185        // 43 triples = 258 body bytes, past the u8 length field.
186        let d = NvodReferenceDescriptor {
187            entries: vec![
188                NvodReferenceEntry {
189                    transport_stream_id: 1,
190                    original_network_id: 2,
191                    service_id: 3,
192                };
193                43
194            ],
195        };
196        let mut buf = vec![0u8; d.serialized_len()];
197        assert!(matches!(
198            d.serialize_into(&mut buf).unwrap_err(),
199            Error::InvalidDescriptor { tag: TAG, .. }
200        ));
201    }
202
203    #[cfg(feature = "serde")]
204    #[test]
205    fn serde_round_trip() {
206        let d = NvodReferenceDescriptor {
207            entries: vec![NvodReferenceEntry {
208                transport_stream_id: 1,
209                original_network_id: 2,
210                service_id: 3,
211            }],
212        };
213        let json = serde_json::to_string(&d).unwrap();
214        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
215        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
216    }
217}