flowly_mp4/mp4box/
mdhd.rs1use byteorder::{BigEndian, WriteBytesExt};
2use serde::Serialize;
3use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
4use std::io::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 const TYPE: BoxType = BoxType::MdhdBox;
53
54 fn box_size(&self) -> u64 {
55 self.get_size()
56 }
57
58 fn to_json(&self) -> Result<String, Error> {
59 Ok(serde_json::to_string(&self).unwrap())
60 }
61
62 fn summary(&self) -> Result<String, Error> {
63 let s = format!(
64 "creation_time={} timescale={} duration={} language={}",
65 self.creation_time, self.timescale, self.duration, self.language
66 );
67 Ok(s)
68 }
69}
70
71impl BlockReader for MdhdBox {
72 fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self, Error> {
73 let (version, flags) = read_box_header_ext(reader);
74
75 let (creation_time, modification_time, timescale, duration) = if version == 1 {
76 (
77 reader.get_u64(),
78 reader.get_u64(),
79 reader.get_u32(),
80 reader.get_u64(),
81 )
82 } else if version == 0 {
83 (
84 reader.get_u32() as u64,
85 reader.get_u32() as u64,
86 reader.get_u32(),
87 reader.get_u32() as u64,
88 )
89 } else {
90 return Err(Error::InvalidData("version must be 0 or 1"));
91 };
92
93 let language_code = reader.get_u16();
94 let language = language_string(language_code);
95
96 Ok(MdhdBox {
97 version,
98 flags,
99 creation_time,
100 modification_time,
101 timescale,
102 duration,
103 language,
104 })
105 }
106
107 fn size_hint() -> usize {
108 22
109 }
110}
111
112impl<W: Write> WriteBox<&mut W> for MdhdBox {
113 fn write_box(&self, writer: &mut W) -> Result<u64, Error> {
114 let size = self.box_size();
115 BoxHeader::new(Self::TYPE, size).write(writer)?;
116
117 write_box_header_ext(writer, self.version, self.flags)?;
118
119 if self.version == 1 {
120 writer.write_u64::<BigEndian>(self.creation_time)?;
121 writer.write_u64::<BigEndian>(self.modification_time)?;
122 writer.write_u32::<BigEndian>(self.timescale)?;
123 writer.write_u64::<BigEndian>(self.duration)?;
124 } else if self.version == 0 {
125 writer.write_u32::<BigEndian>(self.creation_time as u32)?;
126 writer.write_u32::<BigEndian>(self.modification_time as u32)?;
127 writer.write_u32::<BigEndian>(self.timescale)?;
128 writer.write_u32::<BigEndian>(self.duration as u32)?;
129 } else {
130 return Err(Error::InvalidData("version must be 0 or 1"));
131 }
132
133 let language_code = language_code(&self.language);
134 writer.write_u16::<BigEndian>(language_code)?;
135 writer.write_u16::<BigEndian>(0)?; Ok(size)
138 }
139}
140
141fn language_string(language: u16) -> String {
142 let mut lang: [u16; 3] = [0; 3];
143
144 lang[0] = ((language >> 10) & 0x1F) + 0x60;
145 lang[1] = ((language >> 5) & 0x1F) + 0x60;
146 lang[2] = ((language) & 0x1F) + 0x60;
147
148 let lang_str = decode_utf16(lang.iter().cloned())
150 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
151 .collect::<String>();
152
153 lang_str
154}
155
156fn language_code(language: &str) -> u16 {
157 let mut lang = language.encode_utf16();
158 let mut code = (lang.next().unwrap_or(0) & 0x1F) << 10;
159 code += (lang.next().unwrap_or(0) & 0x1F) << 5;
160 code += lang.next().unwrap_or(0) & 0x1F;
161 code
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::mp4box::BoxHeader;
168
169 fn test_language_code(lang: &str) {
170 let code = language_code(lang);
171 let lang2 = language_string(code);
172 assert_eq!(lang, lang2);
173 }
174
175 #[test]
176 fn test_language_codes() {
177 test_language_code("und");
178 test_language_code("eng");
179 test_language_code("kor");
180 }
181
182 #[tokio::test]
183 async fn test_mdhd32() {
184 let src_box = MdhdBox {
185 version: 0,
186 flags: 0,
187 creation_time: 100,
188 modification_time: 200,
189 timescale: 48000,
190 duration: 30439936,
191 language: String::from("und"),
192 };
193 let mut buf = Vec::new();
194 src_box.write_box(&mut buf).unwrap();
195 assert_eq!(buf.len(), src_box.box_size() as usize);
196
197 let mut reader = buf.as_slice();
198 let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
199 assert_eq!(header.kind, BoxType::MdhdBox);
200 assert_eq!(src_box.box_size(), header.size);
201
202 let dst_box = MdhdBox::read_block(&mut reader).unwrap();
203 assert_eq!(src_box, dst_box);
204 }
205
206 #[tokio::test]
207 async fn test_mdhd64() {
208 let src_box = MdhdBox {
209 version: 0,
210 flags: 0,
211 creation_time: 100,
212 modification_time: 200,
213 timescale: 48000,
214 duration: 30439936,
215 language: String::from("eng"),
216 };
217 let mut buf = Vec::new();
218 src_box.write_box(&mut buf).unwrap();
219 assert_eq!(buf.len(), src_box.box_size() as usize);
220
221 let mut reader = buf.as_slice();
222 let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
223 assert_eq!(header.kind, BoxType::MdhdBox);
224 assert_eq!(src_box.box_size(), header.size);
225
226 let dst_box = MdhdBox::read_block(&mut reader).unwrap();
227 assert_eq!(src_box, dst_box);
228 }
229}