Skip to main content

dvb_si/descriptors/
partial_transport_stream.rs

1//! Partial Transport Stream Descriptor — ETSI EN 300 468 §7.2.1 (tag 0x63).
2//!
3//! Table 165 (PDF p. 155). Carried in the SIT (Selection Information Table) of a
4//! partial TS to describe its bit-rate / smoothing-buffer characteristics. The
5//! body is a FIXED 8-byte structure (NOT a loop): three reserved-prefixed
6//! fields. The published markdown rendering of Table 165 is faithful to the PDF
7//! (verified against PDF p. 155, §7.2.1):
8//!
9//! - reserved_future_use (2) + peak_rate (22)
10//! - reserved_future_use (2) + minimum_overall_smoothing_rate (22)
11//! - reserved_future_use (2) + maximum_overall_smoothing_buffer (14)
12//!
13//! Reserved bits are ignored on parse and emitted as 1s on serialize (matching
14//! the `reserved_future_use` convention used elsewhere in this crate).
15
16use crate::error::{Error, Result};
17use crate::traits::Descriptor;
18use dvb_common::{Parse, Serialize};
19
20/// Descriptor tag for partial_transport_stream_descriptor.
21pub const TAG: u8 = 0x63;
22const HEADER_LEN: usize = 2;
23/// Fixed payload length: 3×24-bit reserved-prefixed words (EN 300 468 Table 165).
24const BODY_LEN: u8 = 8;
25/// Maximum value for a 22-bit field (peak_rate / minimum_overall_smoothing_rate).
26const MAX_22: u32 = (1 << 22) - 1;
27/// Maximum value for the 14-bit maximum_overall_smoothing_buffer field.
28const MAX_14: u16 = (1 << 14) - 1;
29
30/// Partial Transport Stream Descriptor (tag 0x63).
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct PartialTransportStreamDescriptor {
34    /// 22-bit peak_rate (188-byte packets per unit interval, PDF p. 155).
35    pub peak_rate: u32,
36    /// 22-bit minimum_overall_smoothing_rate (0x3FFFFF = undefined, PDF p. 155).
37    pub minimum_overall_smoothing_rate: u32,
38    /// 14-bit maximum_overall_smoothing_buffer (0x3FFF = undefined, PDF p. 155).
39    pub maximum_overall_smoothing_buffer: u16,
40}
41
42impl<'a> Parse<'a> for PartialTransportStreamDescriptor {
43    type Error = crate::error::Error;
44    fn parse(bytes: &'a [u8]) -> Result<Self> {
45        if bytes.len() < HEADER_LEN {
46            return Err(Error::BufferTooShort {
47                need: HEADER_LEN,
48                have: bytes.len(),
49                what: "PartialTransportStreamDescriptor header",
50            });
51        }
52        if bytes[0] != TAG {
53            return Err(Error::InvalidDescriptor {
54                tag: bytes[0],
55                reason: "unexpected tag for partial_transport_stream_descriptor",
56            });
57        }
58        let length = bytes[1] as usize;
59        let total = HEADER_LEN + length;
60        if bytes.len() < total {
61            return Err(Error::BufferTooShort {
62                need: total,
63                have: bytes.len(),
64                what: "PartialTransportStreamDescriptor body",
65            });
66        }
67        if length != BODY_LEN as usize {
68            return Err(Error::InvalidDescriptor {
69                tag: TAG,
70                reason: "partial_transport_stream_descriptor length must equal 8",
71            });
72        }
73        let b = HEADER_LEN;
74        // reserved(2) + peak_rate(22): top 2 bits of byte 0 are reserved.
75        let peak_rate = (u32::from(bytes[b] & 0x3F) << 16)
76            | (u32::from(bytes[b + 1]) << 8)
77            | u32::from(bytes[b + 2]);
78        let minimum_overall_smoothing_rate = (u32::from(bytes[b + 3] & 0x3F) << 16)
79            | (u32::from(bytes[b + 4]) << 8)
80            | u32::from(bytes[b + 5]);
81        // reserved(2) + maximum_overall_smoothing_buffer(14).
82        let maximum_overall_smoothing_buffer =
83            (u16::from(bytes[b + 6] & 0x3F) << 8) | u16::from(bytes[b + 7]);
84        Ok(Self {
85            peak_rate,
86            minimum_overall_smoothing_rate,
87            maximum_overall_smoothing_buffer,
88        })
89    }
90}
91
92impl Serialize for PartialTransportStreamDescriptor {
93    type Error = crate::error::Error;
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        if self.peak_rate > MAX_22 || self.minimum_overall_smoothing_rate > MAX_22 {
100            return Err(Error::InvalidDescriptor {
101                tag: TAG,
102                reason: "peak_rate / minimum_overall_smoothing_rate exceed 22 bits",
103            });
104        }
105        if self.maximum_overall_smoothing_buffer > MAX_14 {
106            return Err(Error::InvalidDescriptor {
107                tag: TAG,
108                reason: "maximum_overall_smoothing_buffer exceeds 14 bits",
109            });
110        }
111        let len = self.serialized_len();
112        if buf.len() < len {
113            return Err(Error::OutputBufferTooSmall {
114                need: len,
115                have: buf.len(),
116            });
117        }
118        buf[0] = TAG;
119        buf[1] = BODY_LEN;
120        let b = HEADER_LEN;
121        // reserved_future_use bits emitted as 1s (0xC0 mask on the high byte).
122        buf[b] = 0xC0 | ((self.peak_rate >> 16) as u8 & 0x3F);
123        buf[b + 1] = (self.peak_rate >> 8) as u8;
124        buf[b + 2] = self.peak_rate as u8;
125        buf[b + 3] = 0xC0 | ((self.minimum_overall_smoothing_rate >> 16) as u8 & 0x3F);
126        buf[b + 4] = (self.minimum_overall_smoothing_rate >> 8) as u8;
127        buf[b + 5] = self.minimum_overall_smoothing_rate as u8;
128        buf[b + 6] = 0xC0 | ((self.maximum_overall_smoothing_buffer >> 8) as u8 & 0x3F);
129        buf[b + 7] = self.maximum_overall_smoothing_buffer as u8;
130        Ok(len)
131    }
132}
133
134impl<'a> Descriptor<'a> for PartialTransportStreamDescriptor {
135    const TAG: u8 = TAG;
136    fn descriptor_length(&self) -> u8 {
137        BODY_LEN
138    }
139}
140
141impl<'a> crate::traits::DescriptorDef<'a> for PartialTransportStreamDescriptor {
142    const TAG: u8 = TAG;
143    const NAME: &'static str = "PARTIAL_TRANSPORT_STREAM";
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn parse_extracts_fields_and_ignores_reserved() {
152        // peak_rate=0x012345, min=0x0ABCDE, max=0x1234. Reserved bits set to 1.
153        let bytes = [TAG, 8, 0xC1, 0x23, 0x45, 0xCA, 0xBC, 0xDE, 0xD2, 0x34];
154        let d = PartialTransportStreamDescriptor::parse(&bytes).unwrap();
155        assert_eq!(d.peak_rate, 0x01_2345);
156        assert_eq!(d.minimum_overall_smoothing_rate, 0x0A_BCDE);
157        assert_eq!(d.maximum_overall_smoothing_buffer, 0x1234);
158    }
159
160    #[test]
161    fn parse_rejects_wrong_tag() {
162        let err = PartialTransportStreamDescriptor::parse(&[0x64, 8, 0, 0, 0, 0, 0, 0, 0, 0])
163            .unwrap_err();
164        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x64, .. }));
165    }
166
167    #[test]
168    fn parse_rejects_short_buffer() {
169        let err = PartialTransportStreamDescriptor::parse(&[TAG]).unwrap_err();
170        assert!(matches!(err, Error::BufferTooShort { .. }));
171    }
172
173    #[test]
174    fn parse_rejects_truncated_body() {
175        // length=8 but only 4 payload bytes present.
176        let err = PartialTransportStreamDescriptor::parse(&[TAG, 8, 0, 0, 0, 0]).unwrap_err();
177        assert!(matches!(err, Error::BufferTooShort { .. }));
178    }
179
180    #[test]
181    fn parse_rejects_wrong_length() {
182        let err =
183            PartialTransportStreamDescriptor::parse(&[TAG, 7, 0, 0, 0, 0, 0, 0, 0]).unwrap_err();
184        assert!(matches!(err, Error::InvalidDescriptor { .. }));
185    }
186
187    #[test]
188    fn serialize_round_trip() {
189        let d = PartialTransportStreamDescriptor {
190            peak_rate: 0x0F_FFFF,
191            minimum_overall_smoothing_rate: 0x00_0001,
192            maximum_overall_smoothing_buffer: 0x2ABC,
193        };
194        let mut buf = vec![0u8; d.serialized_len()];
195        d.serialize_into(&mut buf).unwrap();
196        let re = PartialTransportStreamDescriptor::parse(&buf).unwrap();
197        assert_eq!(d, re);
198    }
199
200    #[test]
201    fn serialize_emits_reserved_ones() {
202        let d = PartialTransportStreamDescriptor {
203            peak_rate: 0,
204            minimum_overall_smoothing_rate: 0,
205            maximum_overall_smoothing_buffer: 0,
206        };
207        let mut buf = vec![0u8; d.serialized_len()];
208        d.serialize_into(&mut buf).unwrap();
209        // The three reserved-prefixed bytes have their top two bits set.
210        assert_eq!(buf[2] & 0xC0, 0xC0);
211        assert_eq!(buf[5] & 0xC0, 0xC0);
212        assert_eq!(buf[8] & 0xC0, 0xC0);
213    }
214
215    #[test]
216    fn serialize_rejects_too_small_buffer() {
217        let d = PartialTransportStreamDescriptor {
218            peak_rate: 1,
219            minimum_overall_smoothing_rate: 1,
220            maximum_overall_smoothing_buffer: 1,
221        };
222        let mut tiny = [0u8; 5];
223        let err = d.serialize_into(&mut tiny).unwrap_err();
224        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
225    }
226
227    #[test]
228    fn serialize_rejects_over_range_22bit() {
229        let d = PartialTransportStreamDescriptor {
230            peak_rate: 1 << 22, // one past 22-bit max
231            minimum_overall_smoothing_rate: 0,
232            maximum_overall_smoothing_buffer: 0,
233        };
234        let mut buf = vec![0u8; d.serialized_len()];
235        let err = d.serialize_into(&mut buf).unwrap_err();
236        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
237    }
238
239    #[test]
240    fn serialize_rejects_over_range_14bit() {
241        let d = PartialTransportStreamDescriptor {
242            peak_rate: 0,
243            minimum_overall_smoothing_rate: 0,
244            maximum_overall_smoothing_buffer: 1 << 14, // one past 14-bit max
245        };
246        let mut buf = vec![0u8; d.serialized_len()];
247        let err = d.serialize_into(&mut buf).unwrap_err();
248        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
249    }
250
251    #[test]
252    fn descriptor_length_matches_payload() {
253        let d = PartialTransportStreamDescriptor {
254            peak_rate: 0,
255            minimum_overall_smoothing_rate: 0,
256            maximum_overall_smoothing_buffer: 0,
257        };
258        assert_eq!(d.descriptor_length(), 8);
259    }
260
261    #[cfg(feature = "serde")]
262    #[test]
263    fn serde_round_trip() {
264        let d = PartialTransportStreamDescriptor {
265            peak_rate: 0x00_1234,
266            minimum_overall_smoothing_rate: 0x00_5678,
267            maximum_overall_smoothing_buffer: 0x09AB,
268        };
269        let json = serde_json::to_string(&d).unwrap();
270        let back: PartialTransportStreamDescriptor = serde_json::from_str(&json).unwrap();
271        assert_eq!(d, back);
272    }
273}