Skip to main content

dvb_si/descriptors/extension/
dts_hd.rs

1//! DTS-HD Descriptor — ETSI EN 300 468 Annex G.3, Tables G.6–G.10 (tag_extension 0x0E).
2use super::*;
3use alloc::vec::Vec;
4
5impl<'a> ExtensionBodyDef<'a> for DtsHd<'a> {
6    const TAG_EXTENSION: u8 = 0x0E;
7    const NAME: &'static str = "DTS_HD";
8}
9
10/// DTS-HD descriptor body (Table G.6, Annex G.3).
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
14pub struct DtsHd<'a> {
15    /// `substream_core_flag`(1).
16    pub substream_core_flag: bool,
17    /// `substream_0_flag`(1).
18    pub substream_0_flag: bool,
19    /// `substream_1_flag`(1).
20    pub substream_1_flag: bool,
21    /// `substream_2_flag`(1).
22    pub substream_2_flag: bool,
23    /// `substream_3_flag`(1).
24    pub substream_3_flag: bool,
25    /// `reserved_future_use`(3) — preserved for byte-exact round-trip.
26    pub reserved: u8,
27    /// Substream info blocks, one per set flag (core first, then 0..3 in order).
28    pub substreams: Vec<SubstreamInfo>,
29    /// Optional `additional_info_byte` run.
30    #[cfg_attr(feature = "serde", serde(borrow))]
31    pub additional_info: &'a [u8],
32}
33
34/// `substream_info()` block (Table G.7).
35#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize))]
37pub struct SubstreamInfo {
38    /// `channel_count`(5).
39    pub channel_count: u8,
40    /// `lfe_flag`(1).
41    pub lfe_flag: bool,
42    /// `sampling_frequency`(4) — coded per Table G.8.
43    pub sampling_frequency: SamplingFrequency,
44    /// `sample_resolution`(1) — '1' if decoded resolution > 16 bit.
45    pub sample_resolution: bool,
46    /// `reserved_future_use`(2) — preserved for byte-exact round-trip.
47    pub reserved: u8,
48    /// `asset_info()` entries (num_assets + 1).
49    pub assets: Vec<AssetInfo>,
50}
51
52/// `asset_info()` block (Tables G.9, G.10).
53#[derive(Debug, Clone, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55pub struct AssetInfo {
56    /// `asset_construction`(5) — interpreted per Table G.10.
57    pub asset_construction: u8,
58    /// `vbr_flag`(1).
59    pub vbr_flag: bool,
60    /// `post_encode_br_scaling_flag`(1).
61    pub post_encode_br_scaling_flag: bool,
62    /// `component_type_flag`(1).
63    pub component_type_flag: bool,
64    /// `language_code_flag`(1).
65    pub language_code_flag: bool,
66    /// `bit_rate`(13) or `bit_rate_scaled`(13), selected by `post_encode_br_scaling_flag`.
67    pub bit_rate_or_scaled: u16,
68    /// `reserved_future_use`(2) — preserved for byte-exact round-trip.
69    pub reserved: u8,
70    /// `component_type`(8) — present iff `component_type_flag`.
71    pub component_type: Option<u8>,
72    /// `ISO_639_language_code`(24) — present iff `language_code_flag`.
73    pub iso_639_language_code: Option<LangCode>,
74}
75
76/// `sampling_frequency`(4) — Table G.8.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize))]
79#[non_exhaustive]
80#[repr(u8)]
81pub enum SamplingFrequency {
82    /// 8 kHz.
83    Khz8 = 0,
84    /// 16 kHz.
85    Khz16 = 1,
86    /// 32 kHz.
87    Khz32 = 2,
88    /// 64 kHz.
89    Khz64 = 3,
90    /// 128 kHz (not for core).
91    Khz128 = 4,
92    /// 22,05 kHz.
93    Khz22_05 = 5,
94    /// 44,1 kHz.
95    Khz44_1 = 6,
96    /// 88,2 kHz.
97    Khz88_2 = 7,
98    /// 176,4 kHz (not for core).
99    Khz176_4 = 8,
100    /// 352,8 kHz (not for core).
101    Khz352_8 = 9,
102    /// 12 kHz.
103    Khz12 = 10,
104    /// 24 kHz.
105    Khz24 = 11,
106    /// 48 kHz.
107    Khz48 = 12,
108    /// 96 kHz.
109    Khz96 = 13,
110    /// 192 kHz (not for core).
111    Khz192 = 14,
112    /// 348 kHz (not for core).
113    Khz348 = 15,
114}
115
116impl SamplingFrequency {
117    /// Construct from a raw `u8`; total, lossless (4-bit field).
118    #[must_use]
119    pub fn from_u8(v: u8) -> Self {
120        match v {
121            0 => SamplingFrequency::Khz8,
122            1 => SamplingFrequency::Khz16,
123            2 => SamplingFrequency::Khz32,
124            3 => SamplingFrequency::Khz64,
125            4 => SamplingFrequency::Khz128,
126            5 => SamplingFrequency::Khz22_05,
127            6 => SamplingFrequency::Khz44_1,
128            7 => SamplingFrequency::Khz88_2,
129            8 => SamplingFrequency::Khz176_4,
130            9 => SamplingFrequency::Khz352_8,
131            10 => SamplingFrequency::Khz12,
132            11 => SamplingFrequency::Khz24,
133            12 => SamplingFrequency::Khz48,
134            13 => SamplingFrequency::Khz96,
135            14 => SamplingFrequency::Khz192,
136            15 => SamplingFrequency::Khz348,
137            _ => SamplingFrequency::Khz48, // unreachable for 4-bit
138        }
139    }
140
141    /// Inverse of `from_u8`.
142    #[must_use]
143    pub fn to_u8(self) -> u8 {
144        self as u8
145    }
146
147    /// Human-readable spec name per Table G.8.
148    #[must_use]
149    pub fn name(self) -> &'static str {
150        match self {
151            SamplingFrequency::Khz8 => "8 kHz",
152            SamplingFrequency::Khz16 => "16 kHz",
153            SamplingFrequency::Khz32 => "32 kHz",
154            SamplingFrequency::Khz64 => "64 kHz",
155            SamplingFrequency::Khz128 => "128 kHz",
156            SamplingFrequency::Khz22_05 => "22,05 kHz",
157            SamplingFrequency::Khz44_1 => "44,1 kHz",
158            SamplingFrequency::Khz88_2 => "88,2 kHz",
159            SamplingFrequency::Khz176_4 => "176,4 kHz",
160            SamplingFrequency::Khz352_8 => "352,8 kHz",
161            SamplingFrequency::Khz12 => "12 kHz",
162            SamplingFrequency::Khz24 => "24 kHz",
163            SamplingFrequency::Khz48 => "48 kHz",
164            SamplingFrequency::Khz96 => "96 kHz",
165            SamplingFrequency::Khz192 => "192 kHz",
166            SamplingFrequency::Khz348 => "348 kHz",
167        }
168    }
169}
170dvb_common::impl_spec_display!(SamplingFrequency);
171
172/// Maximum `channel_count` (5-bit field).
173const MAX_CHANNEL_COUNT: u8 = 0x1F;
174/// Maximum bit-rate value (13-bit field).
175const MAX_BIT_RATE: u16 = 0x1FFF;
176
177/// Substream info header size (excluding `asset_info()` blocks):
178/// `substream_length`(1) + packed(1) + packed(1) = 3 bytes.
179const SUBSTREAM_HEADER_LEN: usize = 3;
180/// Asset info fixed length (excluding optional `component_type` and
181/// `ISO_639_language_code`): `asset_construction`+flags(1) + br(2) = 3 bytes.
182const ASSET_FIXED_LEN: usize = 3;
183
184impl Serialize for SubstreamInfo {
185    type Error = crate::error::Error;
186    fn serialized_len(&self) -> usize {
187        let payload_len: usize = self
188            .assets
189            .iter()
190            .map(|a| {
191                ASSET_FIXED_LEN
192                    + usize::from(a.component_type_flag)
193                    + usize::from(a.language_code_flag) * ISO_639_LEN
194            })
195            .sum();
196        SUBSTREAM_HEADER_LEN + payload_len
197    }
198    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
199        let payload_len = self.serialized_len() - SUBSTREAM_HEADER_LEN;
200        let substream_length = payload_len;
201        if substream_length > 0xFF {
202            return Err(Error::ValueOutOfRange {
203                field: "substream_length",
204                reason: "substream payload exceeds 255 bytes",
205            });
206        }
207        if self.channel_count > MAX_CHANNEL_COUNT {
208            return Err(Error::ValueOutOfRange {
209                field: "channel_count",
210                reason: "exceeds 5-bit field",
211            });
212        }
213        buf[0] = substream_length as u8;
214        let num_assets = self.assets.len();
215        if num_assets == 0 {
216            return Err(Error::ValueOutOfRange {
217                field: "num_assets",
218                reason: "substream must have at least 1 asset",
219            });
220        }
221        let na = (num_assets - 1) as u8;
222        buf[1] = (na << 5) | (self.channel_count & MAX_CHANNEL_COUNT);
223        buf[2] = ((u8::from(self.lfe_flag)) << 7)
224            | ((self.sampling_frequency.to_u8() & 0x0F) << 3)
225            | ((u8::from(self.sample_resolution)) << 2)
226            | (self.reserved & 0x03);
227        let mut pos = SUBSTREAM_HEADER_LEN;
228        for a in &self.assets {
229            if a.bit_rate_or_scaled > MAX_BIT_RATE {
230                return Err(Error::ValueOutOfRange {
231                    field: "bit_rate_or_scaled",
232                    reason: "exceeds 13-bit field",
233                });
234            }
235            buf[pos] = (a.asset_construction << 3)
236                | ((u8::from(a.vbr_flag)) << 2)
237                | ((u8::from(a.post_encode_br_scaling_flag)) << 1)
238                | u8::from(a.component_type_flag);
239            buf[pos + 1] =
240                (u8::from(a.language_code_flag) << 7) | ((a.bit_rate_or_scaled >> 6) as u8 & 0x7F);
241            buf[pos + 2] = ((a.bit_rate_or_scaled as u8 & 0x3F) << 2) | (a.reserved & 0x03);
242            pos += ASSET_FIXED_LEN;
243            if a.component_type_flag {
244                buf[pos] = a.component_type.unwrap_or(0);
245                pos += 1;
246            }
247            if a.language_code_flag {
248                buf[pos..pos + ISO_639_LEN]
249                    .copy_from_slice(&a.iso_639_language_code.unwrap_or(LangCode(*b"   ")).0);
250                pos += ISO_639_LEN;
251            }
252        }
253        Ok(pos)
254    }
255}
256
257impl<'a> Parse<'a> for DtsHd<'a> {
258    type Error = crate::error::Error;
259    fn parse(sel: &'a [u8]) -> Result<Self> {
260        let first = *sel.first().ok_or(Error::BufferTooShort {
261            need: 1,
262            have: 0,
263            what: "DTS-HD descriptor body",
264        })?;
265        let substream_core_flag = (first & 0x80) != 0;
266        let substream_0_flag = (first & 0x40) != 0;
267        let substream_1_flag = (first & 0x20) != 0;
268        let substream_2_flag = (first & 0x10) != 0;
269        let substream_3_flag = (first & 0x08) != 0;
270        let reserved = first & 0x07;
271        let flags = [
272            substream_core_flag,
273            substream_0_flag,
274            substream_1_flag,
275            substream_2_flag,
276            substream_3_flag,
277        ];
278        let num_substreams = flags.iter().filter(|&&f| f).count();
279        let mut pos = 1;
280        let mut substreams = Vec::with_capacity(num_substreams);
281
282        for &present in &flags {
283            if !present {
284                continue;
285            }
286            let si = parse_substream_info(sel, &mut pos)?;
287            substreams.push(si);
288        }
289
290        Ok(DtsHd {
291            substream_core_flag,
292            substream_0_flag,
293            substream_1_flag,
294            substream_2_flag,
295            substream_3_flag,
296            reserved,
297            substreams,
298            additional_info: &sel[pos..],
299        })
300    }
301}
302
303fn parse_substream_info(sel: &[u8], pos: &mut usize) -> Result<SubstreamInfo> {
304    let need = *pos + SUBSTREAM_HEADER_LEN;
305    if sel.len() < need {
306        return Err(Error::BufferTooShort {
307            need,
308            have: sel.len(),
309            what: "substream_info header",
310        });
311    }
312    let substream_length = sel[*pos] as usize;
313    *pos += 1;
314    let end = *pos + substream_length;
315    if sel.len() < end {
316        return Err(Error::BufferTooShort {
317            need: end,
318            have: sel.len(),
319            what: "substream_info body",
320        });
321    }
322    let num_assets = ((sel[*pos] >> 5) & 0x07) as usize + 1;
323    let channel_count = sel[*pos] & 0x1F;
324    *pos += 1;
325    let lfe_flag = (sel[*pos] & 0x80) != 0;
326    let sampling_frequency = SamplingFrequency::from_u8((sel[*pos] >> 3) & 0x0F);
327    let sample_resolution = (sel[*pos] & 0x04) != 0;
328    let s_reserved = sel[*pos] & 0x03;
329    *pos += 1;
330
331    let mut assets = Vec::with_capacity(num_assets);
332    for _ in 0..num_assets {
333        let a = parse_asset_info(sel, pos)?;
334        assets.push(a);
335    }
336
337    Ok(SubstreamInfo {
338        channel_count,
339        lfe_flag,
340        sampling_frequency,
341        sample_resolution,
342        reserved: s_reserved,
343        assets,
344    })
345}
346
347fn parse_asset_info(sel: &[u8], pos: &mut usize) -> Result<AssetInfo> {
348    let need = *pos + ASSET_FIXED_LEN;
349    if sel.len() < need {
350        return Err(Error::BufferTooShort {
351            need,
352            have: sel.len(),
353            what: "asset_info",
354        });
355    }
356    let asset_construction = (sel[*pos] >> 3) & 0x1F;
357    let vbr_flag = (sel[*pos] & 0x04) != 0;
358    let post_encode_br_scaling_flag = (sel[*pos] & 0x02) != 0;
359    let component_type_flag = (sel[*pos] & 0x01) != 0;
360    *pos += 1;
361    let language_code_flag = (sel[*pos] & 0x80) != 0;
362    let bit_rate_or_scaled = ((u16::from(sel[*pos] & 0x7F)) << 6) | (u16::from(sel[*pos + 1]) >> 2);
363    *pos += 1;
364    let a_reserved = sel[*pos] & 0x03;
365    *pos += 1;
366
367    let component_type = if component_type_flag {
368        let ct = *sel.get(*pos).ok_or(Error::BufferTooShort {
369            need: *pos + 1,
370            have: sel.len(),
371            what: "asset_info component_type",
372        })?;
373        *pos += 1;
374        Some(ct)
375    } else {
376        None
377    };
378
379    let iso_639_language_code = if language_code_flag {
380        let need_lang = *pos + ISO_639_LEN;
381        let lang_bytes = sel.get(*pos..need_lang).ok_or(Error::BufferTooShort {
382            need: need_lang,
383            have: sel.len(),
384            what: "asset_info ISO_639_language_code",
385        })?;
386        *pos += ISO_639_LEN;
387        Some(LangCode([lang_bytes[0], lang_bytes[1], lang_bytes[2]]))
388    } else {
389        None
390    };
391
392    Ok(AssetInfo {
393        asset_construction,
394        vbr_flag,
395        post_encode_br_scaling_flag,
396        component_type_flag,
397        language_code_flag,
398        bit_rate_or_scaled,
399        reserved: a_reserved,
400        component_type,
401        iso_639_language_code,
402    })
403}
404
405impl Serialize for DtsHd<'_> {
406    type Error = crate::error::Error;
407    fn serialized_len(&self) -> usize {
408        1 + self
409            .substreams
410            .iter()
411            .map(|s| s.serialized_len())
412            .sum::<usize>()
413            + self.additional_info.len()
414    }
415    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
416        let len = self.serialized_len();
417        if buf.len() < len {
418            return Err(Error::OutputBufferTooSmall {
419                need: len,
420                have: buf.len(),
421            });
422        }
423        buf[0] = ((u8::from(self.substream_core_flag)) << 7)
424            | ((u8::from(self.substream_0_flag)) << 6)
425            | ((u8::from(self.substream_1_flag)) << 5)
426            | ((u8::from(self.substream_2_flag)) << 4)
427            | ((u8::from(self.substream_3_flag)) << 3)
428            | (self.reserved & 0x07);
429
430        let flags = [
431            self.substream_core_flag,
432            self.substream_0_flag,
433            self.substream_1_flag,
434            self.substream_2_flag,
435            self.substream_3_flag,
436        ];
437        let mut pos = 1;
438        let mut si = 0;
439        for &present in &flags {
440            if !present {
441                continue;
442            }
443            if si >= self.substreams.len() {
444                return Err(Error::ValueOutOfRange {
445                    field: "DTS-HD substreams",
446                    reason: "fewer substreams than flags indicate",
447                });
448            }
449            let written = self.substreams[si].serialize_into(&mut buf[pos..])?;
450            pos += written;
451            si += 1;
452        }
453        if si != self.substreams.len() {
454            return Err(Error::ValueOutOfRange {
455                field: "DTS-HD substreams",
456                reason: "more substreams than flags indicate",
457            });
458        }
459        buf[pos..len].copy_from_slice(self.additional_info);
460        Ok(len)
461    }
462}
463
464#[cfg(test)]
465mod tests {
466    use super::*;
467    use crate::descriptors::extension::test_support::*;
468    use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
469
470    #[test]
471    fn decodes_sampling_frequency() {
472        assert_eq!(SamplingFrequency::from_u8(12).name(), "48 kHz");
473        assert_eq!(SamplingFrequency::from_u8(0).name(), "8 kHz");
474    }
475
476    #[test]
477    fn parse_dts_hd_core_only() {
478        // flags: core=1, others=0, reserved=7
479        // substream: length=6, num_assets=0, channel_count=6, lfe=1, sf=12,
480        //   sample_res=1, rsv=3
481        // asset: ac=1, vbr=0, pe=0, ct=1, lang=1, br=755, rsv=3
482        //   component_type=0x42, lang=eng
483        let sel = [
484            0x87, // flags
485            0x06, // substream_length
486            0x06, // num_assets=0, channel_count=6
487            0xE7, // lfe=1, sf=12, sample_res=1, rsv=3
488            0x09, 0x8B, 0xCF, // asset: ac=1, vbr=0, pe=0, ct=1, lang=1, br=755, rsv=3
489            0x42, // component_type
490            b'e', b'n', b'g', // lang
491        ];
492        let bytes = wrap(0x0E, &sel);
493        let d = ExtensionDescriptor::parse(&bytes).unwrap();
494        match &d.body {
495            ExtensionBody::DtsHd(b) => {
496                assert!(b.substream_core_flag);
497                assert!(!b.substream_0_flag);
498                assert!(!b.substream_1_flag);
499                assert!(!b.substream_2_flag);
500                assert!(!b.substream_3_flag);
501                assert_eq!(b.reserved, 7);
502                assert_eq!(b.substreams.len(), 1);
503                let s = &b.substreams[0];
504                assert_eq!(s.channel_count, 6);
505                assert!(s.lfe_flag);
506                assert_eq!(s.sampling_frequency, SamplingFrequency::Khz48);
507                assert!(s.sample_resolution);
508                assert_eq!(s.reserved, 3);
509                assert_eq!(s.assets.len(), 1);
510                let a = &s.assets[0];
511                assert_eq!(a.asset_construction, 1);
512                assert!(!a.vbr_flag);
513                assert!(!a.post_encode_br_scaling_flag);
514                assert!(a.component_type_flag);
515                assert!(a.language_code_flag);
516                assert_eq!(a.bit_rate_or_scaled, 755);
517                assert_eq!(a.reserved, 3);
518                assert_eq!(a.component_type, Some(0x42));
519                assert_eq!(a.iso_639_language_code, Some(LangCode(*b"eng")));
520                assert!(b.additional_info.is_empty());
521            }
522            other => panic!("expected DtsHd, got {other:?}"),
523        }
524        round_trip(&d);
525    }
526
527    #[test]
528    fn parse_dts_hd_flags_only() {
529        // No substreams set — all flags zero, reserved=0
530        let sel = [0x00];
531        let bytes = wrap(0x0E, &sel);
532        let d = ExtensionDescriptor::parse(&bytes).unwrap();
533        match &d.body {
534            ExtensionBody::DtsHd(b) => {
535                assert!(!b.substream_core_flag);
536                assert!(b.substreams.is_empty());
537            }
538            other => panic!("expected DtsHd, got {other:?}"),
539        }
540        round_trip(&d);
541    }
542}