1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
pub struct Mp4BoxHeader {
pub name: u32,
pub size: u64,
pub offset: u64,
}
pub struct Mp4FileTypeBox {
name: u32,
size: u64,
major_brand: u32,
minor_version: u32,
compatible_brands: Vec<u32>,
}
extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Result, Seek, SeekFrom};
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Option<Mp4BoxHeader> {
let tmp_size = src.read_u32::<BigEndian>().unwrap();
let name = src.read_u32::<BigEndian>().unwrap();
let size = match tmp_size {
1 => src.read_u64::<BigEndian>().unwrap(),
_ => tmp_size as u64,
};
assert!(size >= 8);
if tmp_size == 1 {
assert!(size >= 16);
}
let offset = match tmp_size {
1 => 4 + 4 + 8,
_ => 4 + 4,
};
assert!(offset <= size);
Some(Mp4BoxHeader{
name: name,
size: size,
offset: offset,
})
}
pub fn skip_box_content<T: ReadBytesExt + std::io::Seek>
(src: &mut T, header: &Mp4BoxHeader)
-> std::io::Result<u64>
{
src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T) -> Option<Mp4FileTypeBox> {
let head = read_box_header(src).unwrap();
let major = src.read_u32::<BigEndian>().unwrap();
let minor = src.read_u32::<BigEndian>().unwrap();
let brand_count = (head.size - 8 - 8) /4;
let mut brands = Vec::new();
for _ in 0..brand_count {
brands.push(src.read_u32::<BigEndian>().unwrap());
}
Some(Mp4FileTypeBox{
name: head.name,
size: head.size,
major_brand: major,
minor_version: minor,
compatible_brands: brands,
})
}
fn u32_to_vec(x: u32) -> Vec<u8> {
vec!((x >> 24 & 0xffu32) as u8,
(x >> 16 & 0xffu32) as u8,
(x >> 8 & 0xffu32) as u8,
(x & 0xffu32) as u8)
}
use std::fmt;
impl fmt::Display for Mp4BoxHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name_bytes = u32_to_vec(self.name);
let name = String::from_utf8_lossy(&name_bytes);
write!(f, "'{}' {} bytes", name, self.size)
}
}
impl fmt::Display for Mp4FileTypeBox {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name_bytes = u32_to_vec(self.name);
let name = String::from_utf8_lossy(&name_bytes);
let brand_bytes = u32_to_vec(self.major_brand);
let brand = String::from_utf8_lossy(&brand_bytes);
write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
brand, self.minor_version)
}
}
#[test]
fn test_read_box_header() {
use std::io::Cursor;
use std::io::Write;
let mut test: Vec<u8> = vec![0, 0, 0, 8];
write!(&mut test, "test").unwrap();
let mut stream = Cursor::new(test);
let parsed = read_box_header(&mut stream).unwrap();
assert_eq!(parsed.name, 1952805748);
assert_eq!(parsed.size, 8);
println!("box {}", parsed);
}
#[test]
fn test_read_box_header_long() {
use std::io::Cursor;
let mut test: Vec<u8> = vec![0, 0, 0, 1];
test.extend("long".to_string().into_bytes());
test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]);
let mut stream = Cursor::new(test);
let parsed = read_box_header(&mut stream).unwrap();
assert_eq!(parsed.name, 1819242087);
assert_eq!(parsed.size, 4096);
println!("box {}", parsed);
}
#[test]
fn test_read_ftyp() {
use std::io::Cursor;
use std::io::Write;
let mut test: Vec<u8> = vec![0, 0, 0, 24];
write!(&mut test, "ftyp").unwrap();
write!(&mut test, "mp42").unwrap();
test.extend(vec![0, 0, 0, 0]);
write!(&mut test, "isom").unwrap();
write!(&mut test, "mp42").unwrap();
assert_eq!(test.len(), 24);
let mut stream = Cursor::new(test);
let parsed = read_ftyp(&mut stream).unwrap();
assert_eq!(parsed.name, 1718909296);
assert_eq!(parsed.size, 24);
assert_eq!(parsed.major_brand, 1836069938);
assert_eq!(parsed.minor_version, 0);
assert_eq!(parsed.compatible_brands.len(), 2);
println!("box {}", parsed);
}