Skip to content

Commit 19e4eaa

Browse files
emkman99alfg
andauthored
Extract esds box from wave box (alfg#96)
* Extract esds from wave box * Allow empty, multi-byte, and arbitrary NUL terminated strings * Skip unsupported avc1 sub-boxes * Fixed non-integer framerates * Fixed bitrate calculation * Fixed format issue * Public read sample offset * Fix lint warning. --------- Co-authored-by: Alfred Gutierrez <[email protected]>
1 parent b4fca8a commit 19e4eaa

File tree

8 files changed

+142
-70
lines changed

8 files changed

+142
-70
lines changed

src/mp4box/avc1.rs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -101,30 +101,37 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
101101
let depth = reader.read_u16::<BigEndian>()?;
102102
reader.read_i16::<BigEndian>()?; // pre-defined
103103

104-
let header = BoxHeader::read(reader)?;
105-
let BoxHeader { name, size: s } = header;
106-
if s > size {
107-
return Err(Error::InvalidData(
108-
"avc1 box contains a box with a larger size than it",
109-
));
110-
}
111-
if name == BoxType::AvcCBox {
112-
let avcc = AvcCBox::read_box(reader, s)?;
113-
114-
skip_bytes_to(reader, start + size)?;
115-
116-
Ok(Avc1Box {
117-
data_reference_index,
118-
width,
119-
height,
120-
horizresolution,
121-
vertresolution,
122-
frame_count,
123-
depth,
124-
avcc,
125-
})
126-
} else {
127-
Err(Error::InvalidData("avcc not found"))
104+
let end = start + size;
105+
loop {
106+
let current = reader.stream_position()?;
107+
if current >= end {
108+
return Err(Error::InvalidData("avcc not found"));
109+
}
110+
let header = BoxHeader::read(reader)?;
111+
let BoxHeader { name, size: s } = header;
112+
if s > size {
113+
return Err(Error::InvalidData(
114+
"avc1 box contains a box with a larger size than it",
115+
));
116+
}
117+
if name == BoxType::AvcCBox {
118+
let avcc = AvcCBox::read_box(reader, s)?;
119+
120+
skip_bytes_to(reader, start + size)?;
121+
122+
return Ok(Avc1Box {
123+
data_reference_index,
124+
width,
125+
height,
126+
horizresolution,
127+
vertresolution,
128+
frame_count,
129+
depth,
130+
avcc,
131+
});
132+
} else {
133+
skip_bytes_to(reader, current + s)?;
134+
}
128135
}
129136
}
130137
}

src/mp4box/dinf.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -264,22 +264,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for UrlBox {
264264

265265
let (version, flags) = read_box_header_ext(reader)?;
266266

267-
let location = if size.saturating_sub(HEADER_SIZE + HEADER_EXT_SIZE) > 0 {
268-
let buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 1;
269-
let mut buf = vec![0u8; buf_size as usize];
270-
reader.read_exact(&mut buf)?;
271-
match String::from_utf8(buf) {
272-
Ok(t) => {
273-
if t.len() != buf_size as usize {
274-
return Err(Error::InvalidData("string too small"));
275-
}
276-
t
277-
}
278-
_ => String::default(),
279-
}
280-
} else {
281-
String::default()
282-
};
267+
let buf_size = size
268+
.checked_sub(HEADER_SIZE + HEADER_EXT_SIZE)
269+
.ok_or(Error::InvalidData("url size too small"))?;
270+
271+
let mut buf = vec![0u8; buf_size as usize];
272+
reader.read_exact(&mut buf)?;
273+
if let Some(end) = buf.iter().position(|&b| b == b'\0') {
274+
buf.truncate(end);
275+
}
276+
let location = String::from_utf8(buf).unwrap_or_default();
283277

284278
skip_bytes_to(reader, start + size)?;
285279

src/mp4box/hdlr.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,15 @@ impl<R: Read + Seek> ReadBox<&mut R> for HdlrBox {
5353
skip_bytes(reader, 12)?; // reserved
5454

5555
let buf_size = size
56-
.checked_sub(HEADER_SIZE + HEADER_EXT_SIZE + 20 + 1)
56+
.checked_sub(HEADER_SIZE + HEADER_EXT_SIZE + 20)
5757
.ok_or(Error::InvalidData("hdlr size too small"))?;
58+
5859
let mut buf = vec![0u8; buf_size as usize];
5960
reader.read_exact(&mut buf)?;
60-
61-
let handler_string = match String::from_utf8(buf) {
62-
Ok(t) => {
63-
if t.len() != buf_size as usize {
64-
return Err(Error::InvalidData("string too small"));
65-
}
66-
t
67-
}
68-
_ => String::from("null"),
69-
};
61+
if let Some(end) = buf.iter().position(|&b| b == b'\0') {
62+
buf.truncate(end);
63+
}
64+
let handler_string = String::from_utf8(buf).unwrap_or_default();
7065

7166
skip_bytes_to(reader, start + size)?;
7267

@@ -127,4 +122,52 @@ mod tests {
127122
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
128123
assert_eq!(src_box, dst_box);
129124
}
125+
126+
#[test]
127+
fn test_hdlr_empty() {
128+
let src_box = HdlrBox {
129+
version: 0,
130+
flags: 0,
131+
handler_type: str::parse::<FourCC>("vide").unwrap(),
132+
name: String::new(),
133+
};
134+
let mut buf = Vec::new();
135+
src_box.write_box(&mut buf).unwrap();
136+
assert_eq!(buf.len(), src_box.box_size() as usize);
137+
138+
let mut reader = Cursor::new(&buf);
139+
let header = BoxHeader::read(&mut reader).unwrap();
140+
assert_eq!(header.name, BoxType::HdlrBox);
141+
assert_eq!(src_box.box_size(), header.size);
142+
143+
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
144+
assert_eq!(src_box, dst_box);
145+
}
146+
147+
#[test]
148+
fn test_hdlr_extra() {
149+
let real_src_box = HdlrBox {
150+
version: 0,
151+
flags: 0,
152+
handler_type: str::parse::<FourCC>("vide").unwrap(),
153+
name: String::from("Good"),
154+
};
155+
let src_box = HdlrBox {
156+
version: 0,
157+
flags: 0,
158+
handler_type: str::parse::<FourCC>("vide").unwrap(),
159+
name: String::from_utf8(b"Good\0Bad".to_vec()).unwrap(),
160+
};
161+
let mut buf = Vec::new();
162+
src_box.write_box(&mut buf).unwrap();
163+
assert_eq!(buf.len(), src_box.box_size() as usize);
164+
165+
let mut reader = Cursor::new(&buf);
166+
let header = BoxHeader::read(&mut reader).unwrap();
167+
assert_eq!(header.name, BoxType::HdlrBox);
168+
assert_eq!(src_box.box_size(), header.size);
169+
170+
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
171+
assert_eq!(real_src_box, dst_box);
172+
}
130173
}

src/mp4box/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ boxtype! {
237237
DayBox => 0xa9646179,
238238
CovrBox => 0x636f7672,
239239
DescBox => 0x64657363,
240-
WideBox => 0x77696465
240+
WideBox => 0x77696465,
241+
WaveBox => 0x77617665
241242
}
242243

243244
pub trait Mp4Box: Sized {

src/mp4box/mp4a.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,49 @@ impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
8282
reader.read_u32::<BigEndian>()?; // reserved
8383
reader.read_u16::<BigEndian>()?; // reserved
8484
let data_reference_index = reader.read_u16::<BigEndian>()?;
85-
86-
reader.read_u64::<BigEndian>()?; // reserved
85+
let version = reader.read_u16::<BigEndian>()?;
86+
reader.read_u16::<BigEndian>()?; // reserved
87+
reader.read_u32::<BigEndian>()?; // reserved
8788
let channelcount = reader.read_u16::<BigEndian>()?;
8889
let samplesize = reader.read_u16::<BigEndian>()?;
8990
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
9091
let samplerate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
9192

93+
if version == 1 {
94+
// Skip QTFF
95+
reader.read_u64::<BigEndian>()?;
96+
reader.read_u64::<BigEndian>()?;
97+
}
98+
99+
// Find esds in mp4a or wave
92100
let mut esds = None;
93-
let current = reader.stream_position()?;
94-
if current < start + size {
101+
let end = start + size;
102+
loop {
103+
let current = reader.stream_position()?;
104+
if current >= end {
105+
break;
106+
}
95107
let header = BoxHeader::read(reader)?;
96108
let BoxHeader { name, size: s } = header;
97109
if s > size {
98110
return Err(Error::InvalidData(
99111
"mp4a box contains a box with a larger size than it",
100112
));
101113
}
102-
103114
if name == BoxType::EsdsBox {
104115
esds = Some(EsdsBox::read_box(reader, s)?);
116+
break;
117+
} else if name == BoxType::WaveBox {
118+
// Typically contains frma, mp4a, esds, and a terminator atom
119+
} else {
120+
// Skip boxes
121+
let skip_to = current + s;
122+
skip_bytes_to(reader, skip_to)?;
105123
}
106-
skip_bytes_to(reader, start + size)?;
107124
}
108125

126+
skip_bytes_to(reader, end)?;
127+
109128
Ok(Mp4aBox {
110129
data_reference_index,
111130
channelcount,

src/reader.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,14 @@ impl<R: Read + Seek> Mp4Reader<R> {
262262
Err(Error::TrakNotFound(track_id))
263263
}
264264
}
265+
266+
pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result<u64> {
267+
if let Some(track) = self.tracks.get(&track_id) {
268+
track.sample_offset(sample_id)
269+
} else {
270+
Err(Error::TrakNotFound(track_id))
271+
}
272+
}
265273
}
266274

267275
impl<R> Mp4Reader<R> {

src/track.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ impl Mp4Track {
167167
}
168168

169169
pub fn frame_rate(&self) -> f64 {
170-
let dur_msec = self.duration().as_millis() as u64;
171-
if dur_msec > 0 {
172-
((self.sample_count() as u64 * 1000) / dur_msec) as f64
173-
} else {
170+
let dur = self.duration();
171+
if dur.is_zero() {
174172
0.0
173+
} else {
174+
self.sample_count() as f64 / dur.as_secs_f64()
175175
}
176176
}
177177

@@ -222,12 +222,12 @@ impl Mp4Track {
222222
}
223223
// mp4a.esds.es_desc.dec_config.avg_bitrate
224224
} else {
225-
let dur_sec = self.duration().as_secs();
226-
if dur_sec > 0 {
227-
let bitrate = self.total_sample_size() * 8 / dur_sec;
228-
bitrate as u32
229-
} else {
225+
let dur = self.duration();
226+
if dur.is_zero() {
230227
0
228+
} else {
229+
let bitrate = self.total_sample_size() as f64 * 8.0 / dur.as_secs_f64();
230+
bitrate as u32
231231
}
232232
}
233233
}
@@ -437,7 +437,7 @@ impl Mp4Track {
437437
}
438438
}
439439

440-
fn sample_offset(&self, sample_id: u32) -> Result<u64> {
440+
pub fn sample_offset(&self, sample_id: u32) -> Result<u64> {
441441
if !self.trafs.is_empty() {
442442
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
443443
let mut sample_offset = self.trafs[traf_idx]

tests/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ fn test_read_mp4() {
9999
assert_eq!(track1.video_profile().unwrap(), AvcProfile::AvcHigh);
100100
assert_eq!(track1.width(), 320);
101101
assert_eq!(track1.height(), 240);
102-
assert_eq!(track1.bitrate(), 0); // XXX
103-
assert_eq!(track1.frame_rate(), 25.00); // XXX
102+
assert_eq!(track1.bitrate(), 150200);
103+
assert_eq!(track1.frame_rate(), 25.00);
104104

105105
// track #2
106106
let track2 = mp4.tracks().get(&2).unwrap();

0 commit comments

Comments
 (0)