memedb_core/formats/
isobmff.rs1pub(crate) const MAGIC: &[u8] = b"ftyp";
23pub(crate) const OFFSET: usize = 4;
24
25use crate::{
26 utils::{decode_tags, encode_tags, or_eof, passthrough, read_stack, skip},
27 Error,
28};
29use std::io::{Read, Seek, Write};
30
31const MEMEDB_UUID: [u8; 16] = *b"\x12\xeb\xc6\x4d\xea\x62\x47\xa0\x8e\x92\xb9\xfb\x3b\x51\x8c\x28";
32
33#[derive(Debug)]
34enum Size {
35 Short(u32),
36 Long(u64),
37}
38
39#[derive(Debug)]
40enum Type {
41 Short([u8; 4]),
42 Long([u8; 16]),
43}
44
45#[derive(Debug)]
46struct Box {
47 size: Size,
48 r#type: Type,
49}
50
51impl Box {
52 fn new(r#type: Type, data_size: u64) -> Self {
53 let type_size = match r#type {
54 Type::Short(_) => 4,
55 Type::Long(_) => 4 + 16,
56 };
57 let total_size = 4 + type_size + data_size;
58 let size = if total_size > u32::MAX.into() {
59 Size::Long(total_size + 8)
60 } else {
61 Size::Short(total_size as u32)
62 };
63 Self { size, r#type }
64 }
65
66 fn read(src: &mut impl Read) -> Result<Box, std::io::Error> {
67 let short_size = u32::from_be_bytes(read_stack::<4>(src)?);
68 let short_type = read_stack::<4>(src)?;
69 let r#box = Box {
70 size: match short_size {
71 1 => Size::Long(u64::from_be_bytes(read_stack::<8>(src)?)),
72 _ => Size::Short(short_size),
73 },
74 r#type: match &short_type {
75 b"uuid" => Type::Long(read_stack::<16>(src)?),
76 _ => Type::Short(short_type),
77 },
78 };
79 Ok(r#box)
80 }
81
82 fn write(&self, dest: &mut impl Write) -> Result<(), std::io::Error> {
83 match self.size {
84 Size::Short(s) => dest.write_all(&s.to_be_bytes())?,
85 Size::Long(_) => dest.write_all(&[0, 0, 0, 1])?,
86 }
87 match self.r#type {
88 Type::Short(t) => dest.write_all(&t)?,
89 Type::Long(_) => dest.write_all(b"uuid")?,
90 };
91 if let Size::Long(s) = self.size {
92 dest.write_all(&s.to_be_bytes())?;
93 }
94 if let Type::Long(t) = self.r#type {
95 dest.write_all(&t)?;
96 }
97 Ok(())
98 }
99
100 fn data_size(&self) -> u64 {
101 let type_size = match self.r#type {
102 Type::Short(_) => 4,
103 Type::Long(_) => 20,
104 };
105 match self.size {
107 Size::Short(s) => (s as u64).saturating_sub(4 + type_size),
108 Size::Long(s) => s.saturating_sub(12 + type_size),
109 }
110 }
111}
112
113pub fn read_tags(src: &mut (impl Read + Seek)) -> Result<Vec<String>, Error> {
115 while let Some(r#box) = or_eof(Box::read(src))? {
116 if let Size::Short(0) = r#box.size {
117 return Ok(Vec::new());
118 }
119 if let Type::Long(MEMEDB_UUID) = r#box.r#type {
120 return decode_tags(src);
121 }
122 let size = r#box.data_size();
123 if passthrough(src, &mut std::io::sink(), size)? != size {
125 Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?;
126 };
127 }
128 Ok(Vec::new())
129}
130
131pub fn write_tags(
135 src: &mut (impl Read + Seek),
136 dest: &mut impl Write,
137 tags: impl IntoIterator<Item = impl AsRef<str>>,
138) -> Result<(), Error> {
139 while let Some(r#box) = or_eof(Box::read(src))? {
140 if let Size::Short(0) = r#box.size {
141 let pos = src.stream_position()?;
142 let len = src.seek(std::io::SeekFrom::End(0))?;
143 if pos != len {
144 src.seek(std::io::SeekFrom::Start(pos))?;
145 }
146 Box::new(r#box.r#type, len - pos).write(dest)?;
147 std::io::copy(src, dest)?;
148 break;
149 }
150 if let Type::Long(MEMEDB_UUID) = r#box.r#type {
151 skip(src, r#box.data_size() as i64)?;
152 } else {
153 r#box.write(dest)?;
154 passthrough(src, dest, r#box.data_size())?;
155 };
156 }
157
158 let mut tag_bytes = Vec::new();
159 encode_tags(tags, &mut tag_bytes)?;
160 let r#box = Box::new(Type::Long(MEMEDB_UUID), tag_bytes.len() as u64);
161 r#box.write(dest)?;
162 dest.write_all(&tag_bytes)?;
163 Ok(())
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use std::io::Cursor;
170
171 const ZERO_BOX: &[&[u8]] = &[&0u32.to_be_bytes(), &[0; 8]];
172 const SIZED_BOX: &[&[u8]] = &[&12u32.to_be_bytes(), &[0; 8]];
173 const TAGS: &[&[u8]] = &[&26u32.to_be_bytes(), b"uuid", &MEMEDB_UUID, &[0x80, 0x00]];
174
175 #[test]
176 fn size_zero_box() {
177 let src = &ZERO_BOX.concat();
178 assert_eq!(read_tags(&mut Cursor::new(src)).unwrap(), Vec::<String>::new());
179 let mut dest = Vec::new();
180 write_tags(&mut Cursor::new(src), &mut dest, vec![""]).unwrap();
181 let expected = &[SIZED_BOX.concat(), TAGS.concat()].concat();
182 assert_eq!(&dest, expected);
183 }
184}
185
186crate::utils::standard_tests!("mp4");