mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-12 13:42:34 +00:00
Fix MP4 audio bitrate
This commit is contained in:
parent
b4d56ab841
commit
d9174697d3
6 changed files with 55 additions and 46 deletions
|
@ -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");
|
||||
|
|
|
@ -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.
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue