use crate::id3::ID3Tags;
use crate::util::BitReader;
use crate::{AudexError, FileType, Result, StreamInfo};
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use std::time::Duration;
#[cfg(feature = "async")]
use crate::util::loadfile_read_async;
#[cfg(feature = "async")]
use tokio::fs::File as TokioFile;
#[cfg(feature = "async")]
use tokio::io::{AsyncReadExt, AsyncSeekExt};
const FREQS: [u32; 13] = [
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
];
type AdtsFixedHeader = (u8, u8, u8, u8, u8, u8, u8, u8, u8);
struct ADTSStream<'a, R: Read + Seek> {
reader: &'a mut BitReader<R>,
parsed_frames: usize,
offset: i64,
fixed_header_key: Option<AdtsFixedHeader>,
samples: u64,
payload: u64,
start: i64,
last: i64,
}
impl<'a, R: Read + Seek> ADTSStream<'a, R> {
fn new(reader: &'a mut BitReader<R>) -> Self {
let start = reader.get_position() as i64 / 8;
ADTSStream {
reader,
parsed_frames: 0,
offset: -1,
fixed_header_key: None,
samples: 0,
payload: 0,
start,
last: start,
}
}
fn sync(&mut self, max_bytes: usize) -> bool {
let max_bytes = max_bytes.max(2);
self.reader.align();
let mut remaining = max_bytes;
while remaining > 0 {
let b = match self.reader.bytes(1) {
Ok(bytes) => bytes[0],
Err(_) => return false,
};
if b == 0xFF {
match self.reader.read_bits(4) {
Ok(0xF) => return true,
Ok(_) => {
self.reader.align();
remaining = remaining.saturating_sub(2);
}
Err(_) => return false,
}
} else {
remaining = remaining.saturating_sub(1);
}
}
false
}
fn find_stream(reader: &'a mut BitReader<R>, max_bytes: usize) -> Option<i64> {
let mut stream = Self::new(reader);
if stream.sync(max_bytes) {
let offset = ((stream.reader.get_position()) as i64 - 12) / 8;
Some(offset)
} else {
None
}
}
fn bitrate(&self) -> u32 {
if self.samples == 0 {
return 0;
}
((8 * self.payload * self.frequency() as u64) / self.samples).min(u32::MAX as u64) as u32
}
fn total_samples(&self) -> u64 {
self.samples
}
fn size(&self) -> i64 {
self.last - self.start
}
fn channels(&self) -> u16 {
if let Some(key) = &self.fixed_header_key {
let b_index = key.6;
match b_index {
0 => 1, 7 => 8,
8..=15 => 1, _ => b_index as u16,
}
} else {
0
}
}
fn frequency(&self) -> u32 {
if let Some(key) = &self.fixed_header_key {
let f_index = key.4 as usize;
FREQS.get(f_index).copied().unwrap_or(0)
} else {
0
}
}
fn parse_frame(&mut self) -> bool {
let start = self.reader.get_position() as i64 - 12;
let id = match self.reader.read_bits(1) {
Ok(v) => v as u8,
Err(_) => return false,
};
let layer = match self.reader.read_bits(2) {
Ok(v) => v as u8,
Err(_) => return false,
};
let protection_absent = match self.reader.read_bits(1) {
Ok(v) => v as u8,
Err(_) => return false,
};
let profile = match self.reader.read_bits(2) {
Ok(v) => v as u8,
Err(_) => return false,
};
let sampling_frequency_index = match self.reader.read_bits(4) {
Ok(v) => v as u8,
Err(_) => return false,
};
let private_bit = match self.reader.read_bits(1) {
Ok(v) => v as u8,
Err(_) => return false,
};
let channel_configuration = match self.reader.read_bits(3) {
Ok(v) => v as u8,
Err(_) => return false,
};
let original_copy = match self.reader.read_bits(1) {
Ok(v) => v as u8,
Err(_) => return false,
};
let home = match self.reader.read_bits(1) {
Ok(v) => v as u8,
Err(_) => return false,
};
let fixed_header_key = (
id,
layer,
protection_absent,
profile,
sampling_frequency_index,
private_bit,
channel_configuration,
original_copy,
home,
);
if let Some(existing_key) = &self.fixed_header_key {
if existing_key != &fixed_header_key {
return false;
}
} else {
self.fixed_header_key = Some(fixed_header_key);
}
if self.reader.skip(2).is_err() {
return false;
}
let frame_length = match self.reader.read_bits(13) {
Ok(v) => v,
Err(_) => return false,
};
if self.reader.skip(11).is_err() {
return false;
}
let nordbif = match self.reader.read_bits(2) {
Ok(v) => v,
Err(_) => return false,
};
let mut crc_overhead: u64 = 0;
if protection_absent == 0 {
crc_overhead += (nordbif + 1) * 16;
if nordbif != 0 {
crc_overhead *= 2;
}
}
let current_pos = self.reader.get_position() as i64;
let left = (frame_length as i64 * 8) - (current_pos - start);
if left < 0 {
return false;
}
if left >= i32::MAX as i64 {
return false;
}
if self.reader.skip(left as i32).is_err() {
return false;
}
self.payload = self
.payload
.saturating_add(((left as u64).saturating_sub(crc_overhead)) / 8);
self.samples = self.samples.saturating_add((nordbif + 1) * 1024);
self.last = self.reader.get_position() as i64 / 8;
self.parsed_frames += 1;
true
}
}
struct ProgramConfigElement {
sampling_frequency_index: u8,
channels: u16,
}
impl ProgramConfigElement {
fn parse<R: Read + Seek>(reader: &mut BitReader<R>) -> Result<Self> {
let _ = reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?; let _ = reader
.read_bits(2)
.map_err(|e| AudexError::ParseError(e.to_string()))?; let sampling_frequency_index = reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?
as u8;
let num_front_channel_elements = reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let num_side_channel_elements = reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let num_back_channel_elements = reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let num_lfe_channel_elements = reader
.read_bits(2)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let num_assoc_data_elements = reader
.read_bits(3)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let num_valid_cc_elements = reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let mono_mixdown_present = reader
.read_bits(1)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
if mono_mixdown_present == 1 {
reader
.skip(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
}
let stereo_mixdown_present = reader
.read_bits(1)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
if stereo_mixdown_present == 1 {
reader
.skip(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
}
let matrix_mixdown_idx_present = reader
.read_bits(1)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
if matrix_mixdown_idx_present == 1 {
reader
.skip(3)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
}
let elms =
num_front_channel_elements + num_side_channel_elements + num_back_channel_elements;
let mut channels = 0u16;
for _ in 0..elms {
channels += 1;
let element_is_cpe = reader
.read_bits(1)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
if element_is_cpe == 1 {
channels += 1;
}
reader
.skip(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
}
channels += num_lfe_channel_elements as u16;
reader
.skip((4 * num_lfe_channel_elements) as i32)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
reader
.skip((4 * num_assoc_data_elements) as i32)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
reader
.skip((5 * num_valid_cc_elements) as i32)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
reader.align();
let comment_field_bytes = reader
.read_bits(8)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let comment_bits = (8 * comment_field_bytes) as u32;
if comment_bits > 0 {
if !reader.can_read(comment_bits) {
return Err(AudexError::ParseError(format!(
"comment_field_bytes ({}) exceeds remaining data in program config element",
comment_field_bytes
)));
}
let skip_amount = i32::try_from(comment_bits).map_err(|_| {
AudexError::ParseError(format!(
"comment field bit count {} exceeds maximum skip range",
comment_bits
))
})?;
reader
.skip(skip_amount)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
}
Ok(ProgramConfigElement {
sampling_frequency_index,
channels,
})
}
}
#[derive(Debug, Clone, Default)]
pub struct AACInfo {
channels: u16,
length: Option<Duration>,
sample_rate: u32,
bitrate: u32,
stream_type: String,
}
impl AACInfo {
pub fn stream_type(&self) -> &str {
&self.stream_type
}
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
pub fn channels(&self) -> u16 {
self.channels
}
pub fn bitrate(&self) -> u32 {
self.bitrate
}
pub fn from_reader<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let mut header = [0u8; 10];
reader.read_exact(&mut header)?;
let start_offset = if &header[0..3] == b"ID3" {
let size = ((header[6] as u32 & 0x7F) << 21)
| ((header[7] as u32 & 0x7F) << 14)
| ((header[8] as u32 & 0x7F) << 7)
| (header[9] as u32 & 0x7F);
(size + 10) as u64
} else {
0
};
reader.seek(SeekFrom::Start(start_offset))?;
let mut format_header = [0u8; 4];
reader.read_exact(&mut format_header)?;
if &format_header == b"ADIF" {
Self::parse_adif(reader)
} else {
reader.seek(SeekFrom::Start(start_offset))?;
Self::parse_adts(reader, start_offset)
}
}
fn parse_adif<R: Read + Seek>(reader: &mut R) -> Result<Self> {
Self::parse_adif_with_file_size(reader, None)
}
fn parse_adif_with_file_size<R: Read + Seek>(
reader: &mut R,
known_file_size: Option<u64>,
) -> Result<Self> {
let mut bit_reader =
BitReader::new(reader).map_err(|e| AudexError::ParseError(e.to_string()))?;
let copyright_id_present = bit_reader
.read_bits(1)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
if copyright_id_present == 1 {
bit_reader
.skip(72)
.map_err(|e| AudexError::ParseError(e.to_string()))?; }
bit_reader
.skip(2)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let bitstream_type = bit_reader
.read_bits(1)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
let bitrate = bit_reader
.read_bits(23)
.map_err(|e| AudexError::ParseError(e.to_string()))? as u32;
let npce = bit_reader
.read_bits(4)
.map_err(|e| AudexError::ParseError(e.to_string()))?;
if bitstream_type == 0 {
bit_reader
.skip(20)
.map_err(|e| AudexError::ParseError(e.to_string()))?; }
let pce = ProgramConfigElement::parse(&mut bit_reader)?;
let sample_rate = FREQS
.get(pce.sampling_frequency_index as usize)
.copied()
.unwrap_or(0);
let channels = pce.channels;
for _ in 0..npce {
ProgramConfigElement::parse(&mut bit_reader)?;
}
bit_reader.align();
let inner_reader = bit_reader.into_inner();
let start = inner_reader.stream_position()?;
let end = match known_file_size {
Some(fs) => fs,
None => inner_reader.seek(SeekFrom::End(0))?,
};
let data_length = end.saturating_sub(start);
let length = if bitrate != 0 {
Some(Duration::from_secs_f64(
(8.0 * data_length as f64) / bitrate as f64,
))
} else {
Some(Duration::from_secs(0))
};
Ok(AACInfo {
channels,
length,
sample_rate,
bitrate,
stream_type: "ADIF".to_string(),
})
}
fn parse_adts<R: Read + Seek>(reader: &mut R, start_offset: u64) -> Result<Self> {
Self::parse_adts_with_file_size(reader, start_offset, None)
}
fn parse_adts_with_file_size<R: Read + Seek>(
reader: &mut R,
start_offset: u64,
known_file_size: Option<u64>,
) -> Result<Self> {
const MAX_INITIAL_READ: usize = 512;
const MAX_RESYNC_READ: usize = 10;
const MAX_SYNC_TRIES: usize = 10;
const FRAMES_MAX: usize = 100;
const FRAMES_NEEDED: usize = 3;
let mut offset = start_offset;
let mut final_stream: Option<(u32, u16, u32, u64, i64, i64)> = None;
for _ in 0..MAX_SYNC_TRIES {
reader.seek(SeekFrom::Start(offset))?;
let mut bit_reader =
BitReader::new(&mut *reader).map_err(|e| AudexError::ParseError(e.to_string()))?;
let stream_offset = match ADTSStream::find_stream(&mut bit_reader, MAX_INITIAL_READ) {
Some(off) => off,
None => return Err(AudexError::AACError("sync not found".to_string())),
};
if stream_offset < 0 {
return Err(AudexError::AACError("negative stream offset".to_string()));
}
offset = offset
.checked_add(stream_offset as u64)
.and_then(|v| v.checked_add(1))
.ok_or_else(|| AudexError::AACError("stream offset overflow".to_string()))?;
let mut stream = ADTSStream::new(&mut bit_reader);
stream.offset = stream_offset;
for _ in 0..FRAMES_MAX {
if !stream.parse_frame() {
break;
}
if !stream.sync(MAX_RESYNC_READ) {
break;
}
}
if stream.parsed_frames >= FRAMES_NEEDED {
final_stream = Some((
stream.frequency(),
stream.channels(),
stream.bitrate(),
stream.total_samples(),
stream.size(),
stream.offset,
));
break;
}
}
let (frequency, channels, bitrate, samples, size, stream_offset) = final_stream
.ok_or_else(|| AudexError::AACError("no valid stream found".to_string()))?;
let end_pos = match known_file_size {
Some(fs) => fs,
None => reader.seek(SeekFrom::End(0))?,
};
let stream_size = end_pos.saturating_sub(offset + stream_offset as u64);
let length = if frequency != 0 && size > 0 {
let seconds = (samples as f64 * stream_size as f64) / (size as f64 * frequency as f64);
Some(Duration::from_secs_f64(seconds))
} else {
Some(Duration::from_secs(0))
};
Ok(AACInfo {
channels,
length,
sample_rate: frequency,
bitrate,
stream_type: "ADTS".to_string(),
})
}
}
impl StreamInfo for AACInfo {
fn length(&self) -> Option<Duration> {
self.length
}
fn bitrate(&self) -> Option<u32> {
Some(self.bitrate)
}
fn sample_rate(&self) -> Option<u32> {
Some(self.sample_rate)
}
fn channels(&self) -> Option<u16> {
Some(self.channels)
}
fn bits_per_sample(&self) -> Option<u16> {
None
}
fn pprint(&self) -> String {
format!(
"AAC ({}), {} Hz, {:.2} seconds, {} channel(s), {} bps",
self.stream_type,
self.sample_rate,
self.length.map(|d| d.as_secs_f64()).unwrap_or(0.0),
self.channels,
self.bitrate
)
}
}
#[derive(Debug)]
pub struct AAC {
pub info: AACInfo,
pub tags: Option<ID3Tags>,
pub filename: Option<String>,
}
impl AAC {
pub fn new() -> Self {
AAC {
info: AACInfo::default(),
tags: None,
filename: None,
}
}
fn parse_file<R: Read + Seek>(&mut self, reader: &mut R) -> Result<()> {
self.info = AACInfo::from_reader(reader)?;
self.tags = None;
Ok(())
}
#[cfg(feature = "async")]
pub async fn load_async<P: AsRef<Path>>(path: P) -> Result<Self> {
let mut file = loadfile_read_async(&path).await?;
let mut aac = AAC::new();
aac.filename = Some(path.as_ref().to_string_lossy().to_string());
aac.info = Self::parse_info_async(&mut file).await?;
Ok(aac)
}
#[cfg(feature = "async")]
async fn parse_info_async(file: &mut TokioFile) -> Result<AACInfo> {
use std::io::Cursor;
let file_size = file.seek(SeekFrom::End(0)).await?;
const MAX_HEADER_READ: u64 = 256 * 1024;
let read_size = std::cmp::min(file_size, MAX_HEADER_READ) as usize;
file.seek(SeekFrom::Start(0)).await?;
let mut header_data = vec![0u8; read_size];
file.read_exact(&mut header_data).await?;
let mut cursor = Cursor::new(&header_data[..]);
let mut id3_header = [0u8; 10];
if std::io::Read::read_exact(&mut cursor, &mut id3_header).is_err() {
return Err(AudexError::AACError("file too small".to_string()));
}
let start_offset = if &id3_header[0..3] == b"ID3" {
let size = ((id3_header[6] as u32 & 0x7F) << 21)
| ((id3_header[7] as u32 & 0x7F) << 14)
| ((id3_header[8] as u32 & 0x7F) << 7)
| (id3_header[9] as u32 & 0x7F);
(size + 10) as u64
} else {
0
};
std::io::Seek::seek(&mut cursor, SeekFrom::Start(start_offset))?;
let mut format_header = [0u8; 4];
std::io::Read::read_exact(&mut cursor, &mut format_header)
.map_err(|_| AudexError::AACError("cannot read format header".to_string()))?;
if &format_header == b"ADIF" {
AACInfo::parse_adif_with_file_size(&mut cursor, Some(file_size))
} else {
std::io::Seek::seek(&mut cursor, SeekFrom::Start(start_offset))?;
AACInfo::parse_adts_with_file_size(&mut cursor, start_offset, Some(file_size))
}
}
}
impl Default for AAC {
fn default() -> Self {
Self::new()
}
}
impl FileType for AAC {
type Tags = ID3Tags;
type Info = AACInfo;
fn format_id() -> &'static str {
"AAC"
}
fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
debug_event!("parsing AAC stream info");
let mut file = std::fs::File::open(&path)?;
let mut aac = AAC::new();
aac.filename = Some(path.as_ref().to_string_lossy().to_string());
aac.parse_file(&mut file)?;
Ok(aac)
}
fn load_from_reader(reader: &mut dyn crate::ReadSeek) -> Result<Self> {
debug_event!("parsing AAC stream info from reader");
let mut instance = Self::new();
let mut reader = reader;
instance.parse_file(&mut reader)?;
Ok(instance)
}
fn save(&mut self) -> Result<()> {
Err(AudexError::TagOperationUnsupported(
"AAC doesn't support embedded tags".to_string(),
))
}
fn clear(&mut self) -> Result<()> {
Err(AudexError::TagOperationUnsupported(
"AAC doesn't support embedded tags".to_string(),
))
}
fn add_tags(&mut self) -> Result<()> {
Err(AudexError::TagOperationUnsupported(
"AAC doesn't support embedded tags".to_string(),
))
}
fn tags(&self) -> Option<&Self::Tags> {
self.tags.as_ref()
}
fn tags_mut(&mut self) -> Option<&mut Self::Tags> {
self.tags.as_mut()
}
fn info(&self) -> &Self::Info {
&self.info
}
fn score(filename: &str, header: &[u8]) -> i32 {
let filename_lower = filename.to_lowercase();
let mut score = 0i32;
if filename_lower.ends_with(".aac")
|| filename_lower.ends_with(".aacp")
|| filename_lower.ends_with(".adts")
|| filename_lower.ends_with(".adif")
{
score += 1;
}
if header.len() >= 4 && &header[..4] == b"ADIF" {
score += 1;
}
score
}
fn mime_types() -> &'static [&'static str] {
&["audio/x-aac", "audio/aac", "audio/aacp"]
}
}
pub fn clear<P: AsRef<Path>>(_path: P) -> Result<()> {
Err(AudexError::TagOperationUnsupported(
"AAC doesn't support embedded tags".to_string(),
))
}