Fix MP4 audio bitrate

This commit is contained in:
Serial 2022-01-24 11:05:12 -05:00
parent b4d56ab841
commit d9174697d3
6 changed files with 55 additions and 46 deletions

View file

@ -15,7 +15,7 @@ macro_rules! test_read_path {
test_read_path!(read_aiff_path, "tests/files/assets/a.aiff");
test_read_path!(read_ape_path, "tests/files/assets/a.ape");
test_read_path!(read_flac_path, "tests/files/assets/a.flac");
test_read_path!(read_m4a_path, "tests/files/assets/a.m4a");
test_read_path!(read_m4a_path, "tests/files/assets/m4a_codec_aac.m4a");
test_read_path!(read_mp3_path, "tests/files/assets/a.mp3");
test_read_path!(read_vorbis_path, "tests/files/assets/a.ogg");
test_read_path!(read_opus_path, "tests/files/assets/a.opus");
@ -50,7 +50,11 @@ macro_rules! test_read_file {
test_read_file!(read_aiff_file, AIFF, "../tests/files/assets/a.aiff");
test_read_file!(read_ape_file, APE, "../tests/files/assets/a.ape");
test_read_file!(read_flac_file, FLAC, "../tests/files/assets/a.flac");
test_read_file!(read_m4a_file, MP4, "../tests/files/assets/a.m4a");
test_read_file!(
read_m4a_file,
MP4,
"../tests/files/assets/m4a_codec_aac.m4a"
);
test_read_file!(read_mp3_file, MP3, "../tests/files/assets/a.mp3");
test_read_file!(read_vorbis_file, VORBIS, "../tests/files/assets/a.ogg");
test_read_file!(read_opus_file, OPUS, "../tests/files/assets/a.opus");

View file

@ -171,37 +171,37 @@ where
return Err(LoftyError::Mp4("File contains no audio tracks"));
}
let duration = match mdhd {
Some(mdhd) => {
data.seek(SeekFrom::Start(mdhd.start + 8))?;
let version = data.read_u8()?;
let _flags = data.read_uint::<BigEndian>(3)?;
let (timescale, duration) = if version == 1 {
// We don't care about these two values
let _creation_time = data.read_u64::<BigEndian>()?;
let _modification_time = data.read_u64::<BigEndian>()?;
let timescale = data.read_u32::<BigEndian>()?;
let duration = data.read_u64::<BigEndian>()?;
(timescale, duration)
} else {
let _creation_time = data.read_u32::<BigEndian>()?;
let _modification_time = data.read_u32::<BigEndian>()?;
let timescale = data.read_u32::<BigEndian>()?;
let duration = data.read_u32::<BigEndian>()?;
(timescale, u64::from(duration))
};
Duration::from_millis(duration * 1000 / u64::from(timescale))
},
let mdhd = match mdhd {
Some(mdhd) => mdhd,
None => return Err(LoftyError::BadAtom("Expected atom \"trak.mdia.mdhd\"")),
};
data.seek(SeekFrom::Start(mdhd.start + 8))?;
let version = data.read_u8()?;
let _flags = data.read_uint::<BigEndian>(3)?;
let (timescale, duration) = if version == 1 {
// We don't care about these two values
let _creation_time = data.read_u64::<BigEndian>()?;
let _modification_time = data.read_u64::<BigEndian>()?;
let timescale = data.read_u32::<BigEndian>()?;
let duration = data.read_u64::<BigEndian>()?;
(timescale, duration)
} else {
let _creation_time = data.read_u32::<BigEndian>()?;
let _modification_time = data.read_u32::<BigEndian>()?;
let timescale = data.read_u32::<BigEndian>()?;
let duration = data.read_u32::<BigEndian>()?;
(timescale, u64::from(duration))
};
let duration = Duration::from_millis(duration * 1000 / u64::from(timescale));
// We create the properties here, since it is possible the other information isn't available
let mut properties = Mp4Properties {
codec: Mp4Codec::Unknown(String::new()),
@ -234,7 +234,7 @@ where
if let AtomIdent::Fourcc(ref fourcc) = atom.ident {
match fourcc {
b"mp4a" => mp4a_properties(&mut stsd_reader, &mut properties, file_length)?,
b"alac" => alac_properties(&mut stsd_reader, &mut properties)?,
b"alac" => alac_properties(&mut stsd_reader, &mut properties, file_length)?,
unknown => {
if let Ok(codec) = std::str::from_utf8(unknown) {
properties.codec = Mp4Codec::Unknown(codec.to_string())
@ -323,7 +323,7 @@ where
Ok(())
}
fn alac_properties<R>(data: &mut R, properties: &mut Mp4Properties) -> Result<()>
fn alac_properties<R>(data: &mut R, properties: &mut Mp4Properties, file_length: u64) -> Result<()>
where
R: Read + Seek,
{
@ -352,7 +352,8 @@ where
data.seek(SeekFrom::Current(9))?;
// Sample size (1)
properties.bit_depth = Some(data.read_u8()?);
let sample_size = data.read_u8()?;
properties.bit_depth = Some(sample_size);
// Skipping 3 bytes
// Rice history mult (1)
@ -367,7 +368,11 @@ where
// Max frame size (4)
data.seek(SeekFrom::Current(6))?;
properties.audio_bitrate = data.read_u32::<BigEndian>()?;
let overall_bitrate = u128::from(file_length * 8) / properties.duration.as_millis();
properties.overall_bitrate = overall_bitrate as u32;
// TODO: Determine bitrate from mdat
properties.audio_bitrate = data.read_u32::<BigEndian>()? / 1000;
properties.sample_rate = data.read_u32::<BigEndian>()?;
}
}

Binary file not shown.

Binary file not shown.

View file

@ -5,7 +5,7 @@ use std::io::{Seek, SeekFrom, Write};
#[test]
fn read() {
// This file contains an ilst atom
let file = lofty::read_from_path("tests/files/assets/a.m4a", false).unwrap();
let file = lofty::read_from_path("tests/files/assets/m4a_codec_aac.m4a", false).unwrap();
assert_eq!(file.file_type(), &FileType::MP4);
@ -15,7 +15,7 @@ fn read() {
#[test]
fn write() {
let mut file = temp_file!("tests/files/assets/a.m4a");
let mut file = temp_file!("tests/files/assets/m4a_codec_aac.m4a");
let mut tagged_file = lofty::read_from(&mut file, false).unwrap();
@ -33,5 +33,5 @@ fn write() {
#[test]
fn remove() {
crate::remove_tag!("tests/files/assets/a.m4a", TagType::Mp4Ilst);
crate::remove_tag!("tests/files/assets/m4a_codec_aac.m4a", TagType::Mp4Ilst);
}

View file

@ -40,7 +40,7 @@ const MP3_PROPERTIES: Mp3Properties = Mp3Properties::new(
2,
);
const MP4_PROPERTIES: Mp4Properties = Mp4Properties::new(
const MP4_AAC_PROPERTIES: Mp4Properties = Mp4Properties::new(
Mp4Codec::AAC,
Duration::from_millis(1449),
135,
@ -50,11 +50,11 @@ const MP4_PROPERTIES: Mp4Properties = Mp4Properties::new(
2,
);
const ALAC_PROPERTIES: Mp4Properties = Mp4Properties::new(
const MP4_ALAC_PROPERTIES: Mp4Properties = Mp4Properties::new(
Mp4Codec::ALAC,
Duration::from_millis(1428),
331,
124,
1536,
48000,
Some(16),
2,
@ -130,18 +130,18 @@ fn mp3_properties() {
}
#[test]
fn mp4_properties() {
fn mp4_aac_properties() {
assert_eq!(
get_properties::<Mp4File>("tests/files/assets/a.m4a"),
MP4_PROPERTIES
get_properties::<Mp4File>("tests/files/assets/m4a_codec_aac.m4a"),
MP4_AAC_PROPERTIES
)
}
#[test]
fn alac_properties() {
fn mp4_alac_properties() {
assert_eq!(
get_properties::<Mp4File>("tests/files/assets/b.m4a").bit_depth(),
ALAC_PROPERTIES.bit_depth()
get_properties::<Mp4File>("tests/files/assets/m4a_codec_alac.m4a"),
MP4_ALAC_PROPERTIES
)
}