MP4: Support property reading for files with FLAC audio

This commit is contained in:
Serial 2022-07-05 13:14:09 -04:00
parent 22bd6a7513
commit e1c10bee66
No known key found for this signature in database
GPG key ID: DA95198DC17C4568
7 changed files with 88 additions and 8 deletions

View file

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- **Vorbis Comments**: `VorbisComments::{pictures, set_picture, remove_picture}`
- **Tag**: `Tag::{set_picture, remove_picture}`
- **MP4**: Support property reading for files with FLAC audio
### Changed
- **ID3v2**: `ID3v2Tag` now derives `Eq`

View file

@ -5,11 +5,11 @@ use std::io::{Read, Seek};
use byteorder::{BigEndian, ReadBytesExt};
pub(super) struct Block {
pub(crate) struct Block {
pub(super) byte: u8,
pub(super) ty: u8,
pub(super) last: bool,
pub(super) content: Vec<u8>,
pub(crate) content: Vec<u8>,
pub(super) start: u64,
pub(super) end: u64,
}

View file

@ -4,8 +4,8 @@
//!
//! * See [`FlacFile`]
mod block;
mod properties;
pub(crate) mod block;
pub(crate) mod properties;
mod read;
#[cfg(feature = "vorbis_comments")]
pub(crate) mod write;

View file

@ -47,8 +47,9 @@ where
if sample_rate > 0 && total_samples > 0 {
let length = (u64::from(total_samples) * 1000) / u64::from(sample_rate);
if length > 0 {
properties.duration = Duration::from_millis(length);
properties.duration = Duration::from_millis(length);
if length > 0 && file_length > 0 && stream_length > 0 {
properties.overall_bitrate = Some(((file_length * 8) / length) as u32);
properties.audio_bitrate = Some(((stream_length * 8) / length) as u32);
}

View file

@ -20,6 +20,7 @@ pub enum Mp4Codec {
AAC,
ALAC,
MP3,
FLAC,
}
impl Default for Mp4Codec {
@ -341,9 +342,9 @@ where
match fourcc {
b"mp4a" => mp4a_properties(&mut stsd_reader, &mut properties)?,
b"alac" => alac_properties(&mut stsd_reader, &mut properties)?,
b"fLaC" => flac_properties(&mut stsd_reader, &mut properties)?,
// Maybe do these?
// TODO: dfla (https://github.com/xiph/flac/blob/master/doc/isoflac.txt)
// TODO: dops
// TODO: dops (opus)
// TODO: wave (https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-134202)
_ => {},
}
@ -585,6 +586,64 @@ where
Ok(())
}
fn flac_properties<R>(stsd: &mut R, properties: &mut Mp4Properties) -> Result<()>
where
R: Read + Seek,
{
properties.codec = Mp4Codec::FLAC;
// Skipping 16 bytes
//
// Reserved (6)
// Data reference index (2)
// Version (2)
// Revision level (2)
// Vendor (4)
stsd.seek(SeekFrom::Current(16))?;
properties.channels = stsd.read_u16::<BigEndian>()? as u8;
properties.bit_depth = Some(stsd.read_u16::<BigEndian>()? as u8);
// Skipping 4 bytes
//
// Compression ID (2)
// Packet size (2)
stsd.seek(SeekFrom::Current(4))?;
properties.sample_rate = u32::from(stsd.read_u16::<BigEndian>()?);
let _reserved = stsd.read_u16::<BigEndian>()?;
let dfla_atom = AtomInfo::read(stsd)?;
match dfla_atom.ident {
// There should be a dfla atom, but it's not worth erroring if absent.
AtomIdent::Fourcc(ref fourcc) if fourcc == b"dfla" => {},
_ => return Ok(()),
}
// Skipping 4 bytes
//
// Version (1)
// Flags (3)
stsd.seek(SeekFrom::Current(4))?;
if dfla_atom.len - 12 < 18 {
// The atom isn't long enough to hold a STREAMINFO block, also not worth an error.
return Ok(());
}
let stream_info_block = crate::flac::block::Block::read(stsd)?;
let flac_properties =
crate::flac::properties::read_properties(&mut &stream_info_block.content[..], 0, 0)?;
// Safe to unwrap, since these fields are guaranteed to be present
properties.sample_rate = flac_properties.sample_rate.unwrap();
properties.bit_depth = flac_properties.bit_depth;
properties.channels = flac_properties.channels.unwrap();
Ok(())
}
// Used to calculate the bitrate, when it isn't readily available to us
fn mdat_length<R>(data: &mut R) -> Result<u64>
where

View file

@ -153,6 +153,17 @@ mod tests {
channels: 2,
};
const MP4_FLAC_PROPERTIES: Mp4Properties = Mp4Properties {
codec: Mp4Codec::FLAC,
extended_audio_object_type: None,
duration: Duration::from_millis(1428),
overall_bitrate: 280, // TODO: FFmpeg reports 279
audio_bitrate: 275,
sample_rate: 48000,
bit_depth: Some(16),
channels: 2,
};
const OPUS_PROPERTIES: OpusProperties = OpusProperties {
duration: Duration::from_millis(1428),
overall_bitrate: 120,
@ -275,6 +286,14 @@ mod tests {
)
}
#[test]
fn mp4_flac_properties() {
assert_eq!(
get_properties::<Mp4File>("tests/files/assets/minimal/mp4_codec_flac.mp4"),
MP4_FLAC_PROPERTIES
)
}
#[test]
fn opus_properties() {
assert_eq!(

Binary file not shown.