Skip to main content

dvb_si/descriptors/extension/
dts_uhd.rs

1//! DTS-UHD Descriptor — ETSI EN 300 468 Annex G.5, Table G.15 (tag_extension 0x21).
2use super::*;
3
4impl<'a> ExtensionBodyDef<'a> for DtsUhd<'a> {
5    const TAG_EXTENSION: u8 = 0x21;
6    const NAME: &'static str = "DTS_UHD";
7}
8
9/// DTS-UHD descriptor body (Table G.15, Annex G.5).
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
13pub struct DtsUhd<'a> {
14    /// `DecoderProfileCode`(6) — decoder profile = value + 2.
15    pub decoder_profile_code: u8,
16    /// `FrameDurationCode`(2) — PCM-sample frame duration at 48 kHz base (Table G.16).
17    pub frame_duration_code: FrameDurationCode,
18    /// `MaxPayloadCode`(3) — maximum audio payload size (Table G.17).
19    pub max_payload_code: MaxPayloadCode,
20    /// `DTS_reserved`(2) — preserved for byte-exact round-trip.
21    pub dts_reserved: u8,
22    /// `StreamIndex`(3) — stream priority (0 = main, 1..7 = aux).
23    pub stream_index: u8,
24    /// `codec_selector_byte` run — codec-defined selector field.
25    #[cfg_attr(feature = "serde", serde(borrow))]
26    pub codec_selector: &'a [u8],
27}
28
29impl DtsUhd<'_> {
30    /// `DecoderProfile = DecoderProfileCode + 2`.
31    #[must_use]
32    pub fn decoder_profile(&self) -> u8 {
33        self.decoder_profile_code + 2
34    }
35}
36
37/// `FrameDurationCode` — Table G.16.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize))]
40#[non_exhaustive]
41pub enum FrameDurationCode {
42    /// 512 samples.
43    Samples512,
44    /// 1 024 samples.
45    Samples1024,
46    /// 2 048 samples.
47    Samples2048,
48    /// 4 096 samples.
49    Samples4096,
50}
51
52impl FrameDurationCode {
53    /// Construct from a raw `u8`; total, lossless (2-bit field).
54    #[must_use]
55    pub fn from_u8(v: u8) -> Self {
56        match v {
57            0 => FrameDurationCode::Samples512,
58            1 => FrameDurationCode::Samples1024,
59            2 => FrameDurationCode::Samples2048,
60            3 => FrameDurationCode::Samples4096,
61            _ => FrameDurationCode::Samples4096, // fallback for invalid values; never reached (2-bit field)
62        }
63    }
64
65    /// Inverse of `from_u8`.
66    #[must_use]
67    pub fn to_u8(self) -> u8 {
68        match self {
69            FrameDurationCode::Samples512 => 0,
70            FrameDurationCode::Samples1024 => 1,
71            FrameDurationCode::Samples2048 => 2,
72            FrameDurationCode::Samples4096 => 3,
73        }
74    }
75
76    /// Human-readable spec name per Table G.16.
77    #[must_use]
78    pub fn name(self) -> &'static str {
79        match self {
80            FrameDurationCode::Samples512 => "512 samples",
81            FrameDurationCode::Samples1024 => "1 024 samples",
82            FrameDurationCode::Samples2048 => "2 048 samples",
83            FrameDurationCode::Samples4096 => "4 096 samples",
84        }
85    }
86}
87dvb_common::impl_spec_display!(FrameDurationCode);
88
89/// `MaxPayloadCode` — Table G.17.
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize))]
92#[non_exhaustive]
93pub enum MaxPayloadCode {
94    /// 2 048 byte.
95    Byte2048,
96    /// 4 096 byte.
97    Byte4096,
98    /// 8 192 byte.
99    Byte8192,
100    /// 16 384 byte.
101    Byte16384,
102    /// 32 768 byte.
103    Byte32768,
104    /// 65 536 byte.
105    Byte65536,
106    /// 131 072 byte.
107    Byte131072,
108    /// Reserved for future use.
109    Reserved(u8),
110}
111
112impl MaxPayloadCode {
113    /// Construct from a raw `u8`; total, lossless (3-bit field, reserved catch-all).
114    #[must_use]
115    pub fn from_u8(v: u8) -> Self {
116        match v {
117            0 => MaxPayloadCode::Byte2048,
118            1 => MaxPayloadCode::Byte4096,
119            2 => MaxPayloadCode::Byte8192,
120            3 => MaxPayloadCode::Byte16384,
121            4 => MaxPayloadCode::Byte32768,
122            5 => MaxPayloadCode::Byte65536,
123            6 => MaxPayloadCode::Byte131072,
124            other => MaxPayloadCode::Reserved(other),
125        }
126    }
127
128    /// Inverse of `from_u8`; `Self::Reserved` emits its stored value.
129    #[must_use]
130    pub fn to_u8(self) -> u8 {
131        match self {
132            MaxPayloadCode::Byte2048 => 0,
133            MaxPayloadCode::Byte4096 => 1,
134            MaxPayloadCode::Byte8192 => 2,
135            MaxPayloadCode::Byte16384 => 3,
136            MaxPayloadCode::Byte32768 => 4,
137            MaxPayloadCode::Byte65536 => 5,
138            MaxPayloadCode::Byte131072 => 6,
139            MaxPayloadCode::Reserved(v) => v,
140        }
141    }
142
143    /// Human-readable spec name per Table G.17.
144    #[must_use]
145    pub fn name(self) -> &'static str {
146        match self {
147            MaxPayloadCode::Byte2048 => "2 048 byte",
148            MaxPayloadCode::Byte4096 => "4 096 byte",
149            MaxPayloadCode::Byte8192 => "8 192 byte",
150            MaxPayloadCode::Byte16384 => "16 384 byte",
151            MaxPayloadCode::Byte32768 => "32 768 byte",
152            MaxPayloadCode::Byte65536 => "65 536 byte",
153            MaxPayloadCode::Byte131072 => "131 072 byte",
154            MaxPayloadCode::Reserved(_) => "reserved for future use",
155        }
156    }
157}
158dvb_common::impl_spec_display!(MaxPayloadCode, Reserved);
159
160/// Fixed length before the `codec_selector` bytes.
161const DTS_UHD_FIXED_LEN: usize = 2;
162
163impl<'a> Parse<'a> for DtsUhd<'a> {
164    type Error = crate::error::Error;
165    fn parse(sel: &'a [u8]) -> Result<Self> {
166        if sel.len() < DTS_UHD_FIXED_LEN {
167            return Err(Error::BufferTooShort {
168                need: DTS_UHD_FIXED_LEN,
169                have: sel.len(),
170                what: "DTS-UHD descriptor body",
171            });
172        }
173        let decoder_profile_code = (sel[0] >> 2) & 0x3F;
174        let frame_duration_code = FrameDurationCode::from_u8(sel[0] & 0x03);
175        let max_payload_code = MaxPayloadCode::from_u8((sel[1] >> 5) & 0x07);
176        let dts_reserved = (sel[1] >> 3) & 0x03;
177        let stream_index = sel[1] & 0x07;
178        Ok(DtsUhd {
179            decoder_profile_code,
180            frame_duration_code,
181            max_payload_code,
182            dts_reserved,
183            stream_index,
184            codec_selector: &sel[DTS_UHD_FIXED_LEN..],
185        })
186    }
187}
188
189impl Serialize for DtsUhd<'_> {
190    type Error = crate::error::Error;
191    fn serialized_len(&self) -> usize {
192        DTS_UHD_FIXED_LEN + self.codec_selector.len()
193    }
194    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
195        let len = self.serialized_len();
196        if buf.len() < len {
197            return Err(Error::OutputBufferTooSmall {
198                need: len,
199                have: buf.len(),
200            });
201        }
202        buf[0] = (self.decoder_profile_code << 2) | (self.frame_duration_code.to_u8() & 0x03);
203        buf[1] = (self.max_payload_code.to_u8() << 5)
204            | ((self.dts_reserved & 0x03) << 3)
205            | (self.stream_index & 0x07);
206        buf[DTS_UHD_FIXED_LEN..len].copy_from_slice(self.codec_selector);
207        Ok(len)
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use crate::descriptors::extension::test_support::*;
215    use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
216
217    #[test]
218    fn decodes_frame_duration_code() {
219        assert_eq!(FrameDurationCode::from_u8(0).name(), "512 samples");
220        assert_eq!(FrameDurationCode::from_u8(2).name(), "2 048 samples");
221    }
222
223    #[test]
224    fn decodes_max_payload_code() {
225        assert_eq!(MaxPayloadCode::from_u8(4).name(), "32 768 byte");
226    }
227
228    #[test]
229    fn parse_dts_uhd_structured() {
230        // DPC=1, FDC=2, MPC=2, DTS_rsv=0, SI=3, codec_selector=ABCD
231        let sel = [0x06, 0x43, 0xAB, 0xCD];
232        let bytes = wrap(0x21, &sel);
233        let d = ExtensionDescriptor::parse(&bytes).unwrap();
234        match &d.body {
235            ExtensionBody::DtsUhd(b) => {
236                assert_eq!(b.decoder_profile_code, 1);
237                assert_eq!(b.decoder_profile(), 3);
238                assert_eq!(b.frame_duration_code, FrameDurationCode::Samples2048);
239                assert_eq!(b.max_payload_code, MaxPayloadCode::Byte8192);
240                assert_eq!(b.dts_reserved, 0);
241                assert_eq!(b.stream_index, 3);
242                assert_eq!(b.codec_selector, &[0xAB, 0xCD]);
243            }
244            other => panic!("expected DtsUhd, got {other:?}"),
245        }
246        round_trip(&d);
247    }
248
249    #[test]
250    fn parse_dts_uhd_no_codec_selector() {
251        let sel = [0x06, 0x43];
252        let bytes = wrap(0x21, &sel);
253        let d = ExtensionDescriptor::parse(&bytes).unwrap();
254        match &d.body {
255            ExtensionBody::DtsUhd(b) => {
256                assert!(b.codec_selector.is_empty());
257            }
258            other => panic!("expected DtsUhd, got {other:?}"),
259        }
260        round_trip(&d);
261    }
262}