1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
4use std::io::{Read, Seek, Write};
5
6use crate::mp4box::*;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
9pub struct MdhdBox {
10 pub version: u8,
11 pub flags: u32,
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 MdhdBox {
20 pub fn get_type(&self) -> BoxType {
21 BoxType::MdhdBox
22 }
23
24 pub fn get_size(&self) -> u64 {
25 let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
26
27 if self.version == 1 {
28 size += 28;
29 } else if self.version == 0 {
30 size += 16;
31 }
32 size += 4;
33 size
34 }
35}
36
37impl Default for MdhdBox {
38 fn default() -> Self {
39 MdhdBox {
40 version: 0,
41 flags: 0,
42 creation_time: 0,
43 modification_time: 0,
44 timescale: 1000,
45 duration: 0,
46 language: String::from("und"),
47 }
48 }
49}
50
51impl Mp4Box for MdhdBox {
52 fn box_type(&self) -> BoxType {
53 self.get_type()
54 }
55
56 fn box_size(&self) -> u64 {
57 self.get_size()
58 }
59
60 fn to_json(&self) -> Result<String> {
61 Ok(serde_json::to_string(&self).unwrap())
62 }
63
64 fn summary(&self) -> Result<String> {
65 let s = format!(
66 "creation_time={} timescale={} duration={} language={}",
67 self.creation_time, self.timescale, self.duration, self.language
68 );
69 Ok(s)
70 }
71}
72
73impl<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
74 fn read_box(reader: &mut R, size: u64) -> Result<Self> {
75 let start = box_start(reader)?;
76
77 let (version, flags) = read_box_header_ext(reader)?;
78
79 let (creation_time, modification_time, timescale, duration) = if version == 1 {
80 (
81 reader.read_u64::<BigEndian>()?,
82 reader.read_u64::<BigEndian>()?,
83 reader.read_u32::<BigEndian>()?,
84 reader.read_u64::<BigEndian>()?,
85 )
86 } else if version == 0 {
87 (
88 reader.read_u32::<BigEndian>()? as u64,
89 reader.read_u32::<BigEndian>()? as u64,
90 reader.read_u32::<BigEndian>()?,
91 reader.read_u32::<BigEndian>()? as u64,
92 )
93 } else {
94 return Err(Error::InvalidData("version must be 0 or 1"));
95 };
96 let language_code = reader.read_u16::<BigEndian>()?;
97 let language = language_string(language_code);
98
99 skip_bytes_to(reader, start + size)?;
100
101 Ok(MdhdBox {
102 version,
103 flags,
104 creation_time,
105 modification_time,
106 timescale,
107 duration,
108 language,
109 })
110 }
111}
112
113impl<W: Write> WriteBox<&mut W> for MdhdBox {
114 fn write_box(&self, writer: &mut W) -> Result<u64> {
115 let size = self.box_size();
116 BoxHeader::new(self.box_type(), size).write(writer)?;
117
118 write_box_header_ext(writer, self.version, self.flags)?;
119
120 if self.version == 1 {
121 writer.write_u64::<BigEndian>(self.creation_time)?;
122 writer.write_u64::<BigEndian>(self.modification_time)?;
123 writer.write_u32::<BigEndian>(self.timescale)?;
124 writer.write_u64::<BigEndian>(self.duration)?;
125 } else if self.version == 0 {
126 writer.write_u32::<BigEndian>(self.creation_time as u32)?;
127 writer.write_u32::<BigEndian>(self.modification_time as u32)?;
128 writer.write_u32::<BigEndian>(self.timescale)?;
129 writer.write_u32::<BigEndian>(self.duration as u32)?;
130 } else {
131 return Err(Error::InvalidData("version must be 0 or 1"));
132 }
133
134 let language_code = language_code(&self.language);
135 writer.write_u16::<BigEndian>(language_code)?;
136 writer.write_u16::<BigEndian>(0)?; Ok(size)
139 }
140}
141
142fn language_string(language: u16) -> String {
143 let mut lang: [u16; 3] = [0; 3];
144
145 lang[0] = ((language >> 10) & 0x1F) + 0x60;
146 lang[1] = ((language >> 5) & 0x1F) + 0x60;
147 lang[2] = ((language) & 0x1F) + 0x60;
148
149 let lang_str = decode_utf16(lang.iter().cloned())
151 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
152 .collect::<String>();
153
154 lang_str
155}
156
157fn language_code(language: &str) -> u16 {
158 let mut lang = language.encode_utf16();
159 let mut code = (lang.next().unwrap_or(0) & 0x1F) << 10;
160 code += (lang.next().unwrap_or(0) & 0x1F) << 5;
161 code += lang.next().unwrap_or(0) & 0x1F;
162 code
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::mp4box::BoxHeader;
169 use std::io::Cursor;
170
171 fn test_language_code(lang: &str) {
172 let code = language_code(lang);
173 let lang2 = language_string(code);
174 assert_eq!(lang, lang2);
175 }
176
177 #[test]
178 fn test_language_codes() {
179 test_language_code("und");
180 test_language_code("eng");
181 test_language_code("kor");
182 }
183
184 #[test]
185 fn test_mdhd32() {
186 let src_box = MdhdBox {
187 version: 0,
188 flags: 0,
189 creation_time: 100,
190 modification_time: 200,
191 timescale: 48000,
192 duration: 30439936,
193 language: String::from("und"),
194 };
195 let mut buf = Vec::new();
196 src_box.write_box(&mut buf).unwrap();
197 assert_eq!(buf.len(), src_box.box_size() as usize);
198
199 let mut reader = Cursor::new(&buf);
200 let header = BoxHeader::read(&mut reader).unwrap();
201 assert_eq!(header.name, BoxType::MdhdBox);
202 assert_eq!(src_box.box_size(), header.size);
203
204 let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
205 assert_eq!(src_box, dst_box);
206 }
207
208 #[test]
209 fn test_mdhd64() {
210 let src_box = MdhdBox {
211 version: 0,
212 flags: 0,
213 creation_time: 100,
214 modification_time: 200,
215 timescale: 48000,
216 duration: 30439936,
217 language: String::from("eng"),
218 };
219 let mut buf = Vec::new();
220 src_box.write_box(&mut buf).unwrap();
221 assert_eq!(buf.len(), src_box.box_size() as usize);
222
223 let mut reader = Cursor::new(&buf);
224 let header = BoxHeader::read(&mut reader).unwrap();
225 assert_eq!(header.name, BoxType::MdhdBox);
226 assert_eq!(src_box.box_size(), header.size);
227
228 let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
229 assert_eq!(src_box, dst_box);
230 }
231}