mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-12-22 12:06:33 +00:00
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.
This commit is contained in:
parent
0ac9986c7f
commit
c4ff8627b0
3 changed files with 116 additions and 42 deletions
|
@ -4,7 +4,7 @@ use std::io::prelude::*;
|
|||
use std::io::{self, BufReader};
|
||||
use std::path::Path;
|
||||
|
||||
use mp4::{Mp4Track, Result, TrackType};
|
||||
use mp4::{Mp4Track, Result, TrackType, Error};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -26,17 +26,23 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
|
|||
|
||||
let mp4 = mp4::Mp4Reader::read_header(reader, size)?;
|
||||
|
||||
println!("Metadata:");
|
||||
println!(" size : {}", mp4.size());
|
||||
println!(" major_brand : {}", mp4.major_brand());
|
||||
println!("File:");
|
||||
println!(" file size: {}", mp4.size());
|
||||
println!(" major_brand: {}", mp4.major_brand());
|
||||
let mut compatible_brands = String::new();
|
||||
for brand in mp4.compatible_brands().iter() {
|
||||
compatible_brands.push_str(&brand.to_string());
|
||||
compatible_brands.push_str(",");
|
||||
compatible_brands.push_str(" ");
|
||||
}
|
||||
println!(" compatible_brands: {}", compatible_brands);
|
||||
println!("Duration: {:?}", mp4.duration());
|
||||
println!(" compatible_brands: {}\n", compatible_brands);
|
||||
|
||||
println!("Movie:");
|
||||
println!(" version: {}", mp4.moov.mvhd.version);
|
||||
println!(" creation time: {}", creation_time(mp4.moov.mvhd.creation_time));
|
||||
println!(" duration: {:?}", mp4.duration());
|
||||
println!(" timescale: {:?}\n", mp4.timescale());
|
||||
|
||||
println!("Found {} Tracks", mp4.tracks().len());
|
||||
for track in mp4.tracks().iter() {
|
||||
let media_info = match track.track_type()? {
|
||||
TrackType::Video => video_info(track)?,
|
||||
|
@ -68,13 +74,35 @@ fn video_info(track: &Mp4Track) -> Result<String> {
|
|||
}
|
||||
|
||||
fn audio_info(track: &Mp4Track) -> Result<String> {
|
||||
Ok(format!(
|
||||
"{} ({}) ({:?}), {} Hz, {}, {} kb/s",
|
||||
track.media_type()?,
|
||||
track.audio_profile()?,
|
||||
track.box_type()?,
|
||||
track.sample_freq_index()?.freq(),
|
||||
track.channel_config()?,
|
||||
track.bitrate() / 1000
|
||||
))
|
||||
if let Some(ref mp4a) = track.trak.mdia.minf.stbl.stsd.mp4a {
|
||||
if mp4a.esds.is_some() {
|
||||
Ok(format!(
|
||||
"{} ({}) ({:?}), {} Hz, {}, {} kb/s",
|
||||
track.media_type()?,
|
||||
track.audio_profile()?,
|
||||
track.box_type()?,
|
||||
track.sample_freq_index()?.freq(),
|
||||
track.channel_config()?,
|
||||
track.bitrate() / 1000
|
||||
))
|
||||
} else {
|
||||
Ok(format!(
|
||||
"{} ({:?}), {} kb/s",
|
||||
track.media_type()?,
|
||||
track.box_type()?,
|
||||
track.bitrate() / 1000
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidData("mp4a box not found"))
|
||||
}
|
||||
}
|
||||
|
||||
fn creation_time(creation_time: u64) -> u64 {
|
||||
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
|
||||
if creation_time >= 2082844800 {
|
||||
creation_time - 2082844800
|
||||
} else {
|
||||
creation_time
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ pub struct Mp4aBox {
|
|||
pub channelcount: u16,
|
||||
pub samplesize: u16,
|
||||
pub samplerate: FixedPointU16,
|
||||
pub esds: EsdsBox,
|
||||
pub esds: Option<EsdsBox>,
|
||||
}
|
||||
|
||||
impl Default for Mp4aBox {
|
||||
|
@ -19,7 +19,7 @@ impl Default for Mp4aBox {
|
|||
channelcount: 2,
|
||||
samplesize: 16,
|
||||
samplerate: FixedPointU16::new(48000),
|
||||
esds: EsdsBox::default(),
|
||||
esds: Some(EsdsBox::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl Mp4aBox {
|
|||
channelcount: config.chan_conf as u16,
|
||||
samplesize: 16,
|
||||
samplerate: FixedPointU16::new(config.freq_index.freq() as u16),
|
||||
esds: EsdsBox::new(config),
|
||||
esds: Some(EsdsBox::new(config)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,11 @@ impl Mp4Box for Mp4aBox {
|
|||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
HEADER_SIZE + 8 + 20 + self.esds.box_size()
|
||||
let mut size = HEADER_SIZE + 8 + 20;
|
||||
if let Some(ref esds) = self.esds {
|
||||
size += esds.box_size();
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,21 +66,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
|
|||
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
|
||||
let mut esds = None;
|
||||
if name == BoxType::EsdsBox {
|
||||
let esds = EsdsBox::read_box(reader, s)?;
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(Mp4aBox {
|
||||
data_reference_index,
|
||||
channelcount,
|
||||
samplesize,
|
||||
samplerate,
|
||||
esds,
|
||||
})
|
||||
} else {
|
||||
Err(Error::InvalidData("esds not found"))
|
||||
esds = Some(EsdsBox::read_box(reader, s)?);
|
||||
}
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(Mp4aBox {
|
||||
data_reference_index,
|
||||
channelcount,
|
||||
samplesize,
|
||||
samplerate,
|
||||
esds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +98,9 @@ impl<W: Write> WriteBox<&mut W> for Mp4aBox {
|
|||
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||
writer.write_u32::<BigEndian>(self.samplerate.raw_value())?;
|
||||
|
||||
self.esds.write_box(writer)?;
|
||||
if let Some(ref esds) = self.esds {
|
||||
esds.write_box(writer)?;
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
@ -524,7 +529,7 @@ mod tests {
|
|||
channelcount: 2,
|
||||
samplesize: 16,
|
||||
samplerate: FixedPointU16::new(48000),
|
||||
esds: EsdsBox {
|
||||
esds: Some(EsdsBox {
|
||||
version: 0,
|
||||
flags: 0,
|
||||
es_desc: ESDescriptor {
|
||||
|
@ -544,7 +549,29 @@ mod tests {
|
|||
},
|
||||
sl_config: SLConfigDescriptor::default(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
src_box.write_box(&mut buf).unwrap();
|
||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||
|
||||
let mut reader = Cursor::new(&buf);
|
||||
let header = BoxHeader::read(&mut reader).unwrap();
|
||||
assert_eq!(header.name, BoxType::Mp4aBox);
|
||||
assert_eq!(src_box.box_size(), header.size);
|
||||
|
||||
let dst_box = Mp4aBox::read_box(&mut reader, header.size).unwrap();
|
||||
assert_eq!(src_box, dst_box);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mp4a_no_esds() {
|
||||
let src_box = Mp4aBox {
|
||||
data_reference_index: 1,
|
||||
channelcount: 2,
|
||||
samplesize: 16,
|
||||
samplerate: FixedPointU16::new(48000),
|
||||
esds: None,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
src_box.write_box(&mut buf).unwrap();
|
||||
|
|
31
src/track.rs
31
src/track.rs
|
@ -53,7 +53,7 @@ impl From<AacConfig> for TrackConfig {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Mp4Track {
|
||||
trak: TrakBox,
|
||||
pub trak: TrakBox,
|
||||
}
|
||||
|
||||
impl Mp4Track {
|
||||
|
@ -126,7 +126,11 @@ impl Mp4Track {
|
|||
|
||||
pub fn sample_freq_index(&self) -> Result<SampleFreqIndex> {
|
||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||
SampleFreqIndex::try_from(mp4a.esds.es_desc.dec_config.dec_specific.freq_index)
|
||||
if let Some(ref esds) = mp4a.esds {
|
||||
SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
}
|
||||
|
@ -134,7 +138,11 @@ impl Mp4Track {
|
|||
|
||||
pub fn channel_config(&self) -> Result<ChannelConfig> {
|
||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||
ChannelConfig::try_from(mp4a.esds.es_desc.dec_config.dec_specific.chan_conf)
|
||||
if let Some(ref esds) = mp4a.esds {
|
||||
ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
}
|
||||
|
@ -156,7 +164,12 @@ impl Mp4Track {
|
|||
|
||||
pub fn bitrate(&self) -> u32 {
|
||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||
mp4a.esds.es_desc.dec_config.avg_bitrate
|
||||
if let Some(ref esds) = mp4a.esds {
|
||||
esds.es_desc.dec_config.avg_bitrate
|
||||
} else {
|
||||
0
|
||||
}
|
||||
// mp4a.esds.es_desc.dec_config.avg_bitrate
|
||||
} else {
|
||||
let dur_sec = self.duration().as_secs();
|
||||
if dur_sec > 0 {
|
||||
|
@ -215,7 +228,11 @@ impl Mp4Track {
|
|||
|
||||
pub fn audio_profile(&self) -> Result<AudioObjectType> {
|
||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||
AudioObjectType::try_from(mp4a.esds.es_desc.dec_config.dec_specific.profile)
|
||||
if let Some(ref esds) = mp4a.esds {
|
||||
AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
}
|
||||
|
@ -660,7 +677,9 @@ impl Mp4TrackWriter {
|
|||
|
||||
let max_sample_size = self.max_sample_size();
|
||||
if let Some(ref mut mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||
mp4a.esds.es_desc.dec_config.buffer_size_db = max_sample_size;
|
||||
if let Some(ref mut esds) = mp4a.esds {
|
||||
esds.es_desc.dec_config.buffer_size_db = max_sample_size;
|
||||
}
|
||||
// TODO
|
||||
// mp4a.esds.es_desc.dec_config.max_bitrate
|
||||
// mp4a.esds.es_desc.dec_config.avg_bitrate
|
||||
|
|
Loading…
Reference in a new issue