Skip to main content

dvb_si/descriptors/
svc_extension.rs

1//! SVC Extension Descriptor — ISO/IEC 13818-1 §2.6.76, Table 2-99 (tag 0x30).
2//!
3//! Carries SVC (Scalable Video Coding, H.264 Annex G) extension parameters
4//! for a video elementary stream.
5
6use super::descriptor_body;
7use crate::error::{Error, Result};
8use dvb_common::{Parse, Serialize};
9
10/// Descriptor tag for SVC_extension_descriptor.
11pub const TAG: u8 = 0x30;
12const HEADER_LEN: usize = 2;
13const BODY_LEN: u8 = 13;
14
15/// SVC Extension Descriptor.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct SvcExtensionDescriptor {
19    /// Width of the SVC stream in pixels.
20    pub width: u16,
21    /// Height of the SVC stream in pixels.
22    pub height: u16,
23    /// Frame rate in frames/256 seconds.
24    pub frame_rate: u16,
25    /// Average bitrate in kbit/s (`0` means unspecified).
26    pub average_bitrate: u16,
27    /// Maximum bitrate in kbit/s (`0` means unspecified).
28    pub maximum_bitrate: u16,
29    /// Dependency ID (SVC spatial/quality dependency layer).
30    pub dependency_id: u8,
31    /// Quality ID start (inclusive).
32    pub quality_id_start: u8,
33    /// Quality ID end (inclusive).
34    pub quality_id_end: u8,
35    /// Temporal ID start (inclusive).
36    pub temporal_id_start: u8,
37    /// Temporal ID end (inclusive).
38    pub temporal_id_end: u8,
39    /// No SEI NAL unit present flag.
40    pub no_sei_nal_unit_present: bool,
41}
42
43impl<'a> Parse<'a> for SvcExtensionDescriptor {
44    type Error = crate::error::Error;
45
46    fn parse(bytes: &'a [u8]) -> Result<Self> {
47        let body = descriptor_body(
48            bytes,
49            TAG,
50            "SvcExtensionDescriptor",
51            "unexpected tag for SVC_extension_descriptor",
52        )?;
53        if body.len() != BODY_LEN as usize {
54            return Err(Error::InvalidDescriptor {
55                tag: TAG,
56                reason: "SVC_extension_descriptor length must equal 13",
57            });
58        }
59        let width = u16::from_be_bytes([body[0], body[1]]);
60        let height = u16::from_be_bytes([body[2], body[3]]);
61        let frame_rate = u16::from_be_bytes([body[4], body[5]]);
62        let average_bitrate = u16::from_be_bytes([body[6], body[7]]);
63        let maximum_bitrate = u16::from_be_bytes([body[8], body[9]]);
64        let b10 = body[10];
65        let dependency_id = (b10 >> 5) & 0x07;
66        // reserved: top 5 bits are reserved — we accept any value for round-trip
67        let b11 = body[11];
68        let quality_id_start = (b11 >> 4) & 0x0F;
69        let quality_id_end = b11 & 0x0F;
70        let b12 = body[12];
71        let temporal_id_start = (b12 >> 5) & 0x07;
72        let temporal_id_end = (b12 >> 2) & 0x07;
73        let no_sei_nal_unit_present = (b12 & 0x02) != 0;
74        // reserved: bit 0 is reserved
75        Ok(Self {
76            width,
77            height,
78            frame_rate,
79            average_bitrate,
80            maximum_bitrate,
81            dependency_id,
82            quality_id_start,
83            quality_id_end,
84            temporal_id_start,
85            temporal_id_end,
86            no_sei_nal_unit_present,
87        })
88    }
89}
90
91impl Serialize for SvcExtensionDescriptor {
92    type Error = crate::error::Error;
93
94    fn serialized_len(&self) -> usize {
95        HEADER_LEN + BODY_LEN as usize
96    }
97
98    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
99        let len = self.serialized_len();
100        if buf.len() < len {
101            return Err(Error::OutputBufferTooSmall {
102                need: len,
103                have: buf.len(),
104            });
105        }
106        buf[0] = TAG;
107        buf[1] = BODY_LEN;
108        buf[HEADER_LEN..HEADER_LEN + 2].copy_from_slice(&self.width.to_be_bytes());
109        buf[HEADER_LEN + 2..HEADER_LEN + 4].copy_from_slice(&self.height.to_be_bytes());
110        buf[HEADER_LEN + 4..HEADER_LEN + 6].copy_from_slice(&self.frame_rate.to_be_bytes());
111        buf[HEADER_LEN + 6..HEADER_LEN + 8].copy_from_slice(&self.average_bitrate.to_be_bytes());
112        buf[HEADER_LEN + 8..HEADER_LEN + 10].copy_from_slice(&self.maximum_bitrate.to_be_bytes());
113        buf[HEADER_LEN + 10] = (self.dependency_id & 0x07) << 5;
114        buf[HEADER_LEN + 11] = ((self.quality_id_start & 0x0F) << 4) | (self.quality_id_end & 0x0F);
115        buf[HEADER_LEN + 12] = ((self.temporal_id_start & 0x07) << 5)
116            | ((self.temporal_id_end & 0x07) << 2)
117            | ((self.no_sei_nal_unit_present as u8) << 1);
118        Ok(len)
119    }
120}
121impl<'a> crate::traits::DescriptorDef<'a> for SvcExtensionDescriptor {
122    const TAG: u8 = TAG;
123    const NAME: &'static str = "SVC_EXTENSION";
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn parse_extracts_fields() {
132        let bytes = [
133            TAG, 13, 0x07, 0x80, // width = 1920
134            0x04, 0x38, // height = 1080
135            0x00, 0x3C, // frame_rate = 60
136            0x00, 0x64, // average_bitrate = 100
137            0x00, 0xC8, // maximum_bitrate = 200
138            0x20, // dependency_id=1, reserved=0
139            0x01, // quality_id_start=0, quality_id_end=1
140            0x06, // temporal_id_start=0, temporal_id_end=1, no_sei=1, reserved=0
141        ];
142        let d = SvcExtensionDescriptor::parse(&bytes).unwrap();
143        assert_eq!(d.width, 1920);
144        assert_eq!(d.height, 1080);
145        assert_eq!(d.frame_rate, 60);
146        assert_eq!(d.average_bitrate, 100);
147        assert_eq!(d.maximum_bitrate, 200);
148        assert_eq!(d.dependency_id, 1);
149        assert_eq!(d.quality_id_start, 0);
150        assert_eq!(d.quality_id_end, 1);
151        assert_eq!(d.temporal_id_start, 0);
152        assert_eq!(d.temporal_id_end, 1);
153        assert!(d.no_sei_nal_unit_present);
154    }
155
156    #[test]
157    fn parse_max_bit_fields() {
158        let bytes = [
159            TAG, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xE0, // dependency_id=7
160            0xFF, // quality_id_start=15, quality_id_end=15
161            0xFE, // temporal_id_start=7, temporal_id_end=7, no_sei=1
162        ];
163        let d = SvcExtensionDescriptor::parse(&bytes).unwrap();
164        assert_eq!(d.dependency_id, 7);
165        assert_eq!(d.quality_id_start, 15);
166        assert_eq!(d.quality_id_end, 15);
167        assert_eq!(d.temporal_id_start, 7);
168        assert_eq!(d.temporal_id_end, 7);
169        assert!(d.no_sei_nal_unit_present);
170    }
171
172    #[test]
173    fn parse_rejects_wrong_tag() {
174        let err = SvcExtensionDescriptor::parse(&[0x02, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
175            .unwrap_err();
176        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
177    }
178
179    #[test]
180    fn parse_rejects_wrong_length() {
181        let err = SvcExtensionDescriptor::parse(&[TAG, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
182            .unwrap_err();
183        assert!(matches!(err, Error::InvalidDescriptor { .. }));
184    }
185
186    #[test]
187    fn serialize_round_trip() {
188        let d = SvcExtensionDescriptor {
189            width: 1280,
190            height: 720,
191            frame_rate: 50,
192            average_bitrate: 5000,
193            maximum_bitrate: 10000,
194            dependency_id: 2,
195            quality_id_start: 3,
196            quality_id_end: 5,
197            temporal_id_start: 1,
198            temporal_id_end: 3,
199            no_sei_nal_unit_present: false,
200        };
201        let mut buf = vec![0u8; d.serialized_len()];
202        d.serialize_into(&mut buf).unwrap();
203        let reparsed = SvcExtensionDescriptor::parse(&buf).unwrap();
204        assert_eq!(d, reparsed);
205    }
206
207    #[test]
208    fn serialize_round_trip_reserved_preserved() {
209        // Reserved bits are not part of any field that the struct can
210        // round-trip; the serializer writes zero for those bits.
211        // This test confirms we parse and re-serialize deterministically
212        // with the reserved bits wiped.
213        let bytes_with_reserved = [
214            TAG, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1F, // dependency_id=0, reserved non-zero
215            0x00, 0x01, // temporal_id_start=0, temporal_id_end=0, no_sei=0, reserved=1
216        ];
217        let d = SvcExtensionDescriptor::parse(&bytes_with_reserved).unwrap();
218        let mut buf = vec![0u8; d.serialized_len()];
219        d.serialize_into(&mut buf).unwrap();
220        // Re-parse the serialized output and verify it's stable
221        let reparsed = SvcExtensionDescriptor::parse(&buf).unwrap();
222        let mut buf2 = vec![0u8; reparsed.serialized_len()];
223        reparsed.serialize_into(&mut buf2).unwrap();
224        assert_eq!(buf, buf2);
225    }
226
227    #[test]
228    fn serialize_rejects_small_buffer() {
229        let d = SvcExtensionDescriptor {
230            width: 0,
231            height: 0,
232            frame_rate: 0,
233            average_bitrate: 0,
234            maximum_bitrate: 0,
235            dependency_id: 0,
236            quality_id_start: 0,
237            quality_id_end: 0,
238            temporal_id_start: 0,
239            temporal_id_end: 0,
240            no_sei_nal_unit_present: false,
241        };
242        let mut tiny = vec![0u8; 5];
243        let err = d.serialize_into(&mut tiny).unwrap_err();
244        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
245    }
246}