Skip to content

Commit c4ff862

Browse files
authored
Set optional EsdsBox in Mp4aBox. (#23)
* Make EsdsBox in Mp4aBox optional, instead of error when parsing. Also update mp4info example with some missing info that was removed. * Update Mp4aBox test to check for optional EsdsBox.
1 parent 0ac9986 commit c4ff862

File tree

3 files changed

+116
-42
lines changed

3 files changed

+116
-42
lines changed

examples/mp4info.rs

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::io::prelude::*;
44
use std::io::{self, BufReader};
55
use std::path::Path;
66

7-
use mp4::{Mp4Track, Result, TrackType};
7+
use mp4::{Mp4Track, Result, TrackType, Error};
88

99
fn main() {
1010
let args: Vec<String> = env::args().collect();
@@ -26,17 +26,23 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
2626

2727
let mp4 = mp4::Mp4Reader::read_header(reader, size)?;
2828

29-
println!("Metadata:");
30-
println!(" size : {}", mp4.size());
31-
println!(" major_brand : {}", mp4.major_brand());
29+
println!("File:");
30+
println!(" file size: {}", mp4.size());
31+
println!(" major_brand: {}", mp4.major_brand());
3232
let mut compatible_brands = String::new();
3333
for brand in mp4.compatible_brands().iter() {
3434
compatible_brands.push_str(&brand.to_string());
35-
compatible_brands.push_str(",");
35+
compatible_brands.push_str(" ");
3636
}
37-
println!(" compatible_brands: {}", compatible_brands);
38-
println!("Duration: {:?}", mp4.duration());
37+
println!(" compatible_brands: {}\n", compatible_brands);
3938

39+
println!("Movie:");
40+
println!(" version: {}", mp4.moov.mvhd.version);
41+
println!(" creation time: {}", creation_time(mp4.moov.mvhd.creation_time));
42+
println!(" duration: {:?}", mp4.duration());
43+
println!(" timescale: {:?}\n", mp4.timescale());
44+
45+
println!("Found {} Tracks", mp4.tracks().len());
4046
for track in mp4.tracks().iter() {
4147
let media_info = match track.track_type()? {
4248
TrackType::Video => video_info(track)?,
@@ -68,13 +74,35 @@ fn video_info(track: &Mp4Track) -> Result<String> {
6874
}
6975

7076
fn audio_info(track: &Mp4Track) -> Result<String> {
71-
Ok(format!(
72-
"{} ({}) ({:?}), {} Hz, {}, {} kb/s",
73-
track.media_type()?,
74-
track.audio_profile()?,
75-
track.box_type()?,
76-
track.sample_freq_index()?.freq(),
77-
track.channel_config()?,
78-
track.bitrate() / 1000
79-
))
77+
if let Some(ref mp4a) = track.trak.mdia.minf.stbl.stsd.mp4a {
78+
if mp4a.esds.is_some() {
79+
Ok(format!(
80+
"{} ({}) ({:?}), {} Hz, {}, {} kb/s",
81+
track.media_type()?,
82+
track.audio_profile()?,
83+
track.box_type()?,
84+
track.sample_freq_index()?.freq(),
85+
track.channel_config()?,
86+
track.bitrate() / 1000
87+
))
88+
} else {
89+
Ok(format!(
90+
"{} ({:?}), {} kb/s",
91+
track.media_type()?,
92+
track.box_type()?,
93+
track.bitrate() / 1000
94+
))
95+
}
96+
} else {
97+
Err(Error::InvalidData("mp4a box not found"))
98+
}
8099
}
100+
101+
fn creation_time(creation_time: u64) -> u64 {
102+
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
103+
if creation_time >= 2082844800 {
104+
creation_time - 2082844800
105+
} else {
106+
creation_time
107+
}
108+
}

src/mp4box/mp4a.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct Mp4aBox {
99
pub channelcount: u16,
1010
pub samplesize: u16,
1111
pub samplerate: FixedPointU16,
12-
pub esds: EsdsBox,
12+
pub esds: Option<EsdsBox>,
1313
}
1414

1515
impl Default for Mp4aBox {
@@ -19,7 +19,7 @@ impl Default for Mp4aBox {
1919
channelcount: 2,
2020
samplesize: 16,
2121
samplerate: FixedPointU16::new(48000),
22-
esds: EsdsBox::default(),
22+
esds: Some(EsdsBox::default()),
2323
}
2424
}
2525
}
@@ -31,7 +31,7 @@ impl Mp4aBox {
3131
channelcount: config.chan_conf as u16,
3232
samplesize: 16,
3333
samplerate: FixedPointU16::new(config.freq_index.freq() as u16),
34-
esds: EsdsBox::new(config),
34+
esds: Some(EsdsBox::new(config)),
3535
}
3636
}
3737
}
@@ -42,7 +42,11 @@ impl Mp4Box for Mp4aBox {
4242
}
4343

4444
fn box_size(&self) -> u64 {
45-
HEADER_SIZE + 8 + 20 + self.esds.box_size()
45+
let mut size = HEADER_SIZE + 8 + 20;
46+
if let Some(ref esds) = self.esds {
47+
size += esds.box_size();
48+
}
49+
size
4650
}
4751
}
4852

@@ -62,21 +66,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
6266

6367
let header = BoxHeader::read(reader)?;
6468
let BoxHeader { name, size: s } = header;
69+
70+
let mut esds = None;
6571
if name == BoxType::EsdsBox {
66-
let esds = EsdsBox::read_box(reader, s)?;
67-
68-
skip_bytes_to(reader, start + size)?;
69-
70-
Ok(Mp4aBox {
71-
data_reference_index,
72-
channelcount,
73-
samplesize,
74-
samplerate,
75-
esds,
76-
})
77-
} else {
78-
Err(Error::InvalidData("esds not found"))
72+
esds = Some(EsdsBox::read_box(reader, s)?);
7973
}
74+
skip_bytes_to(reader, start + size)?;
75+
76+
Ok(Mp4aBox {
77+
data_reference_index,
78+
channelcount,
79+
samplesize,
80+
samplerate,
81+
esds,
82+
})
8083
}
8184
}
8285

@@ -95,7 +98,9 @@ impl<W: Write> WriteBox<&mut W> for Mp4aBox {
9598
writer.write_u32::<BigEndian>(0)?; // reserved
9699
writer.write_u32::<BigEndian>(self.samplerate.raw_value())?;
97100

98-
self.esds.write_box(writer)?;
101+
if let Some(ref esds) = self.esds {
102+
esds.write_box(writer)?;
103+
}
99104

100105
Ok(size)
101106
}
@@ -524,7 +529,7 @@ mod tests {
524529
channelcount: 2,
525530
samplesize: 16,
526531
samplerate: FixedPointU16::new(48000),
527-
esds: EsdsBox {
532+
esds: Some(EsdsBox {
528533
version: 0,
529534
flags: 0,
530535
es_desc: ESDescriptor {
@@ -544,7 +549,29 @@ mod tests {
544549
},
545550
sl_config: SLConfigDescriptor::default(),
546551
},
547-
},
552+
}),
553+
};
554+
let mut buf = Vec::new();
555+
src_box.write_box(&mut buf).unwrap();
556+
assert_eq!(buf.len(), src_box.box_size() as usize);
557+
558+
let mut reader = Cursor::new(&buf);
559+
let header = BoxHeader::read(&mut reader).unwrap();
560+
assert_eq!(header.name, BoxType::Mp4aBox);
561+
assert_eq!(src_box.box_size(), header.size);
562+
563+
let dst_box = Mp4aBox::read_box(&mut reader, header.size).unwrap();
564+
assert_eq!(src_box, dst_box);
565+
}
566+
567+
#[test]
568+
fn test_mp4a_no_esds() {
569+
let src_box = Mp4aBox {
570+
data_reference_index: 1,
571+
channelcount: 2,
572+
samplesize: 16,
573+
samplerate: FixedPointU16::new(48000),
574+
esds: None,
548575
};
549576
let mut buf = Vec::new();
550577
src_box.write_box(&mut buf).unwrap();

src/track.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl From<AacConfig> for TrackConfig {
5353

5454
#[derive(Debug)]
5555
pub struct Mp4Track {
56-
trak: TrakBox,
56+
pub trak: TrakBox,
5757
}
5858

5959
impl Mp4Track {
@@ -126,15 +126,23 @@ impl Mp4Track {
126126

127127
pub fn sample_freq_index(&self) -> Result<SampleFreqIndex> {
128128
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
129-
SampleFreqIndex::try_from(mp4a.esds.es_desc.dec_config.dec_specific.freq_index)
129+
if let Some(ref esds) = mp4a.esds {
130+
SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index)
131+
} else {
132+
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
133+
}
130134
} else {
131135
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
132136
}
133137
}
134138

135139
pub fn channel_config(&self) -> Result<ChannelConfig> {
136140
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
137-
ChannelConfig::try_from(mp4a.esds.es_desc.dec_config.dec_specific.chan_conf)
141+
if let Some(ref esds) = mp4a.esds {
142+
ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf)
143+
} else {
144+
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
145+
}
138146
} else {
139147
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
140148
}
@@ -156,7 +164,12 @@ impl Mp4Track {
156164

157165
pub fn bitrate(&self) -> u32 {
158166
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
159-
mp4a.esds.es_desc.dec_config.avg_bitrate
167+
if let Some(ref esds) = mp4a.esds {
168+
esds.es_desc.dec_config.avg_bitrate
169+
} else {
170+
0
171+
}
172+
// mp4a.esds.es_desc.dec_config.avg_bitrate
160173
} else {
161174
let dur_sec = self.duration().as_secs();
162175
if dur_sec > 0 {
@@ -215,7 +228,11 @@ impl Mp4Track {
215228

216229
pub fn audio_profile(&self) -> Result<AudioObjectType> {
217230
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
218-
AudioObjectType::try_from(mp4a.esds.es_desc.dec_config.dec_specific.profile)
231+
if let Some(ref esds) = mp4a.esds {
232+
AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile)
233+
} else {
234+
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
235+
}
219236
} else {
220237
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
221238
}
@@ -660,7 +677,9 @@ impl Mp4TrackWriter {
660677

661678
let max_sample_size = self.max_sample_size();
662679
if let Some(ref mut mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
663-
mp4a.esds.es_desc.dec_config.buffer_size_db = max_sample_size;
680+
if let Some(ref mut esds) = mp4a.esds {
681+
esds.es_desc.dec_config.buffer_size_db = max_sample_size;
682+
}
664683
// TODO
665684
// mp4a.esds.es_desc.dec_config.max_bitrate
666685
// mp4a.esds.es_desc.dec_config.avg_bitrate

0 commit comments

Comments
 (0)