Skip to content

Parse codec specific descriptor to retrieve audio sample rate #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 60 additions & 20 deletions mp4parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ pub enum SampleEntry {
#[derive(Debug, Clone)]
pub struct ES_Descriptor {
pub audio_codec: CodecType,
pub audio_sample_rate: Option<u32>,
pub codec_specific_config: Vec<u8>,
}

Expand Down Expand Up @@ -1116,8 +1117,15 @@ fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock

fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
// Tags for elementary stream description
const ESDESCR_TAG: u8 = 0x03;
const DECODER_CONFIG_TAG: u8 = 0x04;
const ESDESCR_TAG: u8 = 0x03;
const DECODER_CONFIG_TAG: u8 = 0x04;
const DECODER_SPECIFIC_TAG: u8 = 0x05;

let frequency_table =
vec![(0x1, 96000), (0x1, 88200), (0x2, 64000), (0x3, 48000),
(0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050),
(0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000),
(0xc, 7350)];

let (_, _) = try!(read_fullbox_extra(src));

Expand All @@ -1128,24 +1136,29 @@ fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
let esds_array = try!(read_buf(src, esds_size as usize));

// Parsing DecoderConfig descriptor to get the object_profile_indicator
// for correct codec type.
let object_profile_indicator = {
// for correct codec type and audio sample rate.
let (object_profile_indicator, sample_frequency) = {
let mut object_profile: u8 = 0;
let mut sample_frequency = None;

// clone a esds cursor for parsing.
let esds = &mut Cursor::new(&esds_array);
let esds_tag = try!(esds.read_u8());
let next_tag = try!(esds.read_u8());

if esds_tag != ESDESCR_TAG {
if next_tag != ESDESCR_TAG {
return Err(Error::Unsupported("fail to parse ES descriptor"));
}

let esds_extend = try!(esds.read_u8());
// extension tag start from 0x80.
if esds_extend >= 0x80 {
// skip remaining extension and length.
try!(skip(esds, 5));
} else {
let esds_end = if esds_extend >= 0x80 {
// skip remaining extension.
try!(skip(esds, 2));
}
esds.position() + try!(esds.read_u8()) as u64
} else {
esds.position() + esds_extend as u64
};
try!(skip(esds, 2));

let esds_flags = try!(esds.read_u8());

Expand All @@ -1163,19 +1176,45 @@ fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
}

// find DecoderConfig descriptor (tag = DECODER_CONFIG_TAG)
let dcds = try!(esds.read_u8());
if dcds != DECODER_CONFIG_TAG {
return Err(Error::Unsupported("fail to parse decoder config descriptor"));
if esds_end > esds.position() {
let next_tag = try!(esds.read_u8());
if next_tag == DECODER_CONFIG_TAG {
let dcds_extend = try!(esds.read_u8());
// extension tag start from 0x80.
if dcds_extend >= 0x80 {
// skip remains extension and length.
try!(skip(esds, 3));
}

object_profile = try!(esds.read_u8());

// Skip uninteresting fields.
try!(skip(esds, 12));
}
}

let dcds_extend = try!(esds.read_u8());
// extension tag start from 0x80.
if dcds_extend >= 0x80 {
// skip remains extension and length.
try!(skip(esds, 3));

// find DecoderSpecific descriptor (tag = DECODER_SPECIFIC_TAG)
if esds_end > esds.position() {
let next_tag = try!(esds.read_u8());
if next_tag == DECODER_SPECIFIC_TAG {
let dsds_extend = try!(esds.read_u8());
// extension tag start from 0x80.
if dsds_extend >= 0x80 {
// skip remains extension and length.
try!(skip(esds, 3));
}

let audio_specific_config = try!(be_u16(esds));

let sample_index = (audio_specific_config & 0x07FF) >> 7;

sample_frequency =
frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1);
}
}

try!(esds.read_u8())
(object_profile, sample_frequency)
};

let codec = match object_profile_indicator {
Expand All @@ -1190,6 +1229,7 @@ fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {

Ok(ES_Descriptor {
audio_codec: codec,
audio_sample_rate: sample_frequency,
codec_specific_config: esds_array,
})
}
Expand Down
4 changes: 2 additions & 2 deletions mp4parse/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,9 +831,9 @@ fn read_qt_wave_atom() {
.append_repeated(0, 2)
.B8(0x00) // flags
.B8(0x04) // decoder config descriptor tag
.B8(0x06) // dcds length
.B8(0x0d) // dcds length
.B8(0x6b) // mp3
.append_repeated(0, 5)
.append_repeated(0, 12)
}).into_inner();
let wave = make_box(BoxSize::Auto, b"wave", |s| {
s.append_bytes(esds.as_slice())
Expand Down
1 change: 1 addition & 0 deletions mp4parse/tests/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ fn public_api() {
assert_eq!(match a.codec_specific {
mp4::AudioCodecSpecific::ES_Descriptor(esds) => {
assert_eq!(esds.audio_codec, mp4::CodecType::AAC);
assert_eq!(esds.audio_sample_rate.unwrap(), 48000);
"ES"
}
mp4::AudioCodecSpecific::FLACSpecificBox(_) => {
Expand Down
3 changes: 3 additions & 0 deletions mp4parse_capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,9 @@ pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser,
}
(*info).codec_specific_config.length = v.codec_specific_config.len() as u32;
(*info).codec_specific_config.data = v.codec_specific_config.as_ptr();
if let Some(rate) = v.audio_sample_rate {
(*info).sample_rate = rate;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match is good to show handling of all branches, or when yielding a value for assignment. For something like this if let is shorter, since the _ => {} clause is equivalent to an omitted empty else clause.

// An ESDS value overrides the track sample rate.
if let Some(rate) = v.audio_sample_rate {
    (*info)sample_rate = rate;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for suggestion. Fixed.

}
AudioCodecSpecific::FLACSpecificBox(_) => {
return MP4PARSE_ERROR_UNSUPPORTED;
Expand Down