mp4_atom/moov/trak/mdia/
mdhd.rs

1use crate::*;
2
3ext! {
4    name: Mdhd,
5    versions: [0, 1],
6    flags: {}
7}
8
9#[derive(Debug, Clone, PartialEq, Eq, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Mdhd {
12    pub creation_time: u64,
13    pub modification_time: u64,
14    pub timescale: u32,
15    pub duration: u64,
16    pub language: String,
17}
18
19impl AtomExt for Mdhd {
20    type Ext = MdhdExt;
21
22    const KIND_EXT: FourCC = FourCC::new(b"mdhd");
23
24    fn decode_body_ext<B: Buf>(buf: &mut B, ext: MdhdExt) -> Result<Self> {
25        let (creation_time, modification_time, timescale, duration) = match ext.version {
26            MdhdVersion::V1 => (
27                u64::decode(buf)?,
28                u64::decode(buf)?,
29                u32::decode(buf)?,
30                u64::decode(buf)?,
31            ),
32            MdhdVersion::V0 => (
33                u32::decode(buf)? as u64,
34                u32::decode(buf)? as u64,
35                u32::decode(buf)?,
36                u32::decode(buf)? as u64,
37            ),
38        };
39
40        let language_code = u16::decode(buf)?;
41        let language = language_string(language_code);
42
43        u16::decode(buf)?; // pre-defined
44
45        Ok(Mdhd {
46            creation_time,
47            modification_time,
48            timescale,
49            duration,
50            language,
51        })
52    }
53
54    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<MdhdExt> {
55        self.creation_time.encode(buf)?;
56        self.modification_time.encode(buf)?;
57        self.timescale.encode(buf)?;
58        self.duration.encode(buf)?;
59
60        let language_code = language_code(&self.language);
61        (language_code).encode(buf)?;
62        0u16.encode(buf)?; // pre-defined
63
64        Ok(MdhdVersion::V1.into())
65    }
66}
67
68fn language_string(language: u16) -> String {
69    let mut lang: [u16; 3] = [0; 3];
70
71    lang[0] = ((language >> 10) & 0x1F) + 0x60;
72    lang[1] = ((language >> 5) & 0x1F) + 0x60;
73    lang[2] = ((language) & 0x1F) + 0x60;
74
75    // Decode utf-16 encoded bytes into a string.
76    String::from_utf16_lossy(&lang)
77}
78
79fn language_code(language: &str) -> u16 {
80    let mut lang = language.encode_utf16();
81    let mut code = (lang.next().unwrap_or(0) & 0x1F) << 10;
82    code += (lang.next().unwrap_or(0) & 0x1F) << 5;
83    code += lang.next().unwrap_or(0) & 0x1F;
84    code
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    fn test_language_code(lang: &str) {
92        let code = language_code(lang);
93        let lang2 = language_string(code);
94        assert_eq!(lang, lang2);
95    }
96
97    #[test]
98    fn test_language_codes() {
99        test_language_code("und");
100        test_language_code("eng");
101        test_language_code("kor");
102    }
103
104    #[test]
105    fn test_mdhd32() {
106        let expected = Mdhd {
107            creation_time: 100,
108            modification_time: 200,
109            timescale: 48000,
110            duration: 30439936,
111            language: String::from("und"),
112        };
113        let mut buf = Vec::new();
114        expected.encode(&mut buf).unwrap();
115
116        let mut buf = buf.as_ref();
117        let decoded = Mdhd::decode(&mut buf).unwrap();
118        assert_eq!(decoded, expected);
119    }
120
121    #[test]
122    fn test_mdhd64() {
123        let expected = Mdhd {
124            creation_time: 100,
125            modification_time: 200,
126            timescale: 48000,
127            duration: 30439936,
128            language: String::from("eng"),
129        };
130        let mut buf = Vec::new();
131        expected.encode(&mut buf).unwrap();
132
133        let mut buf = buf.as_ref();
134        let decoded = Mdhd::decode(&mut buf).unwrap();
135        assert_eq!(decoded, expected);
136    }
137}