mpeg2ts_reader/descriptor/
iso_639_language.rs

1//! Support for [ISO-639](https://en.wikipedia.org/wiki/ISO_639) language code metadata, and
2//! audio-type metadata.
3
4use super::DescriptorError;
5use std::borrow::Cow;
6use std::fmt;
7
8/// Descriptor which may be attached to Transport Stream metadata to indicate the language of the
9/// content.
10pub struct Iso639LanguageDescriptor<'buf> {
11    buf: &'buf [u8],
12}
13impl<'buf> Iso639LanguageDescriptor<'buf> {
14    /// The descriptor tag value which identifies the descriptor as an `Iso639LanguageDescriptor`.
15    pub const TAG: u8 = 10;
16    /// Construct a `Iso639LanguageDescriptor` instance that will parse the data from the given
17    /// slice.
18    pub fn new(
19        _tag: u8,
20        buf: &'buf [u8],
21    ) -> Result<Iso639LanguageDescriptor<'buf>, DescriptorError> {
22        Ok(Iso639LanguageDescriptor { buf })
23    }
24
25    /// Produce an iterator over the `Language` items in the provided buffer.
26    pub fn languages(&self) -> impl Iterator<Item = Result<Language<'buf>, LangError>> {
27        LanguageIterator::new(self.buf)
28    }
29}
30
31struct LanguageIterator<'buf> {
32    remaining_data: &'buf [u8],
33}
34impl<'buf> LanguageIterator<'buf> {
35    pub fn new(data: &'buf [u8]) -> LanguageIterator<'buf> {
36        LanguageIterator {
37            remaining_data: data,
38        }
39    }
40}
41impl<'buf> Iterator for LanguageIterator<'buf> {
42    type Item = Result<Language<'buf>, LangError>;
43
44    fn next(&mut self) -> Option<Self::Item> {
45        if self.remaining_data.is_empty() {
46            None
47        } else if self.remaining_data.len() < 4 {
48            let actual = self.remaining_data.len();
49            self.remaining_data = &self.remaining_data[0..0];
50            Some(Err(LangError::TooShort { actual }))
51        } else {
52            let (head, tail) = self.remaining_data.split_at(4);
53            self.remaining_data = tail;
54            Some(Ok(Language::new(head)))
55        }
56    }
57}
58
59/// An error reading a language code out of the descriptor
60#[derive(Debug)]
61pub enum LangError {
62    /// there is not sufficient data in the descriptor to read out another complete language code
63    TooShort {
64        /// the actual buffer size in bytes (expected size is 4 bytes)
65        actual: usize,
66    },
67}
68
69/// Metadata about the role of the audio elementary stream to which this descriptor is attached.
70#[derive(Debug, PartialEq, Eq)]
71pub enum AudioType {
72    /// The audio has no particular role define
73    Undefined,
74    /// There is no language-specific content within the audio
75    CleanEffects,
76    /// The audio is prepared for the heading impaired
77    HearingImpaired,
78    /// The audio is prepared for the visually impaired
79    VisualImpairedCommentary,
80    /// Values `0x80` to `0xFF` are reserved for use in future versions of _ISO/IEC 13818-1_
81    Reserved(u8),
82}
83impl From<u8> for AudioType {
84    fn from(v: u8) -> Self {
85        match v {
86            0 => AudioType::Undefined,
87            1 => AudioType::CleanEffects,
88            2 => AudioType::HearingImpaired,
89            3 => AudioType::VisualImpairedCommentary,
90            _ => AudioType::Reserved(v),
91        }
92    }
93}
94/// One of potentially many pieces of language metadata within an `Iso639LanguageDescriptor`.
95pub struct Language<'buf> {
96    buf: &'buf [u8],
97}
98impl<'buf> Language<'buf> {
99    fn new(buf: &'buf [u8]) -> Language<'buf> {
100        assert_eq!(buf.len(), 4);
101        Language { buf }
102    }
103    /// Returns a string containing the ISO-639 language code of the elementary stream to which
104    /// this descriptor is attached.
105    pub fn code(&self) -> Cow<'_, str> {
106        encoding_rs::mem::decode_latin1(&self.buf[0..3])
107    }
108    /// Returns an `AudioType` variant indicating what role this audio track plays within the
109    /// program.
110    pub fn audio_type(&self) -> AudioType {
111        AudioType::from(self.buf[3])
112    }
113}
114impl<'buf> fmt::Debug for Language<'buf> {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
116        f.debug_struct("Language")
117            .field("code", &self.code())
118            .field("audio_type", &self.audio_type())
119            .finish()
120    }
121}
122
123struct LangsDebug<'buf>(&'buf Iso639LanguageDescriptor<'buf>);
124impl<'buf> fmt::Debug for LangsDebug<'buf> {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
126        f.debug_list().entries(self.0.languages()).finish()
127    }
128}
129impl<'buf> fmt::Debug for Iso639LanguageDescriptor<'buf> {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
131        f.debug_struct("Iso639LanguageDescriptor")
132            .field("languages", &LangsDebug(self))
133            .finish()
134    }
135}
136
137#[cfg(test)]
138mod test {
139    use super::super::{CoreDescriptors, Descriptor};
140    use super::*;
141    use assert_matches::assert_matches;
142    use hex_literal::*;
143
144    #[test]
145    fn descriptor() {
146        let data = hex!("0a04656e6700");
147        let desc = CoreDescriptors::from_bytes(&data).unwrap();
148        assert_matches!(desc, CoreDescriptors::ISO639Language(iso_639_language) => {
149            let mut langs = iso_639_language.languages();
150            let first = langs.next().unwrap().unwrap();
151            assert_eq!("eng", first.code());
152            assert_eq!(AudioType::Undefined, first.audio_type());
153            assert_matches!(langs.next(), None);
154        });
155    }
156
157    #[test]
158    fn debug() {
159        let data = hex!("0a04656e6700");
160        let desc = CoreDescriptors::from_bytes(&data).unwrap();
161        assert_matches!(desc, CoreDescriptors::ISO639Language(iso_639_language) => {
162            assert!(!format!("{:?}", iso_639_language).is_empty());
163        });
164    }
165
166    #[test]
167    fn too_short() {
168        let data = hex!("0a03656e67");
169        let desc = CoreDescriptors::from_bytes(&data).unwrap();
170        if let CoreDescriptors::ISO639Language(iso_639_language) = desc {
171            let mut langs = iso_639_language.languages();
172            langs.next();
173        }
174    }
175}