use std::ffi::c_void;
use std::ptr;
use crate::aac_encoder::audiotoolbox::{
AudioBufferList, AudioBufferStruct, AudioConverterDispose, AudioConverterFillComplexBuffer,
AudioConverterNew, AudioConverterRef, AudioConverterReset, AudioConverterSetProperty,
AudioStreamBasicDescription, AudioStreamPacketDescription, K_AUDIO_FORMAT_FLAG_IS_FLOAT,
K_AUDIO_FORMAT_FLAG_IS_PACKED, K_AUDIO_FORMAT_LINEAR_PCM, K_AUDIO_FORMAT_MPEG4_AAC,
NO_MORE_INPUT, OSStatus, fourcc, status_error,
};
use crate::{AacTrackConfig, Error, ErrorKind, Result};
const K_AUDIO_CONVERTER_DECOMPRESSION_MAGIC_COOKIE: u32 = fourcc(b"dmgc");
const FRAME_LENGTH: u32 = 1024;
pub(super) struct Decoder {
converter: AudioConverterRef,
channels: u32,
}
unsafe impl Send for Decoder {}
impl Drop for Decoder {
fn drop(&mut self) {
unsafe {
AudioConverterDispose(self.converter);
}
}
}
impl Decoder {
pub(super) fn new(config: &AacTrackConfig) -> Result<Self> {
let channels = u32::from(config.channels);
let source_format = AudioStreamBasicDescription {
sample_rate: f64::from(config.sample_rate),
format_id: K_AUDIO_FORMAT_MPEG4_AAC,
format_flags: 0,
bytes_per_packet: 0,
frames_per_packet: FRAME_LENGTH,
bytes_per_frame: 0,
channels_per_frame: channels,
bits_per_channel: 0,
reserved: 0,
};
let destination_format = AudioStreamBasicDescription {
sample_rate: f64::from(config.sample_rate),
format_id: K_AUDIO_FORMAT_LINEAR_PCM,
format_flags: K_AUDIO_FORMAT_FLAG_IS_FLOAT | K_AUDIO_FORMAT_FLAG_IS_PACKED,
bytes_per_packet: 4 * channels,
frames_per_packet: 1,
bytes_per_frame: 4 * channels,
channels_per_frame: channels,
bits_per_channel: 32,
reserved: 0,
};
let mut converter: AudioConverterRef = ptr::null_mut();
let status =
unsafe { AudioConverterNew(&source_format, &destination_format, &mut converter) };
if status != 0 || converter.is_null() {
return Err(Error::new(
ErrorKind::Unsupported,
format!("AudioConverterNew could not create an AAC decoder (OSStatus {status})"),
));
}
let decoder = Self {
converter,
channels,
};
let cookie = magic_cookie(&config.audio_specific_config);
let status = unsafe {
AudioConverterSetProperty(
decoder.converter,
K_AUDIO_CONVERTER_DECOMPRESSION_MAGIC_COOKIE,
u32::try_from(cookie.len()).unwrap_or(u32::MAX),
cookie.as_ptr().cast(),
)
};
if status != 0 {
return Err(Error::new(
ErrorKind::Unsupported,
format!("AudioToolbox rejected the AAC configuration (OSStatus {status})"),
));
}
Ok(decoder)
}
pub(super) fn decode(&mut self, data: &[u8], _position: u64, out: &mut Vec<f32>) -> Result<()> {
let mut input = PendingPacket {
data: data.as_ptr(),
description: AudioStreamPacketDescription {
start_offset: 0,
variable_frames_in_packet: 0,
data_byte_size: u32::try_from(data.len()).map_err(|_| {
Error::new(ErrorKind::ResourceLimit, "AAC access unit is too large")
})?,
},
channels: self.channels,
consumed: false,
};
let start = out.len();
let capacity = FRAME_LENGTH as usize * self.channels as usize;
out.resize(start + capacity, 0.0);
let mut frames = FRAME_LENGTH;
let mut buffer_list = AudioBufferList {
number_buffers: 1,
buffers: [AudioBufferStruct {
number_channels: self.channels,
data_byte_size: u32::try_from(capacity * std::mem::size_of::<f32>())
.unwrap_or(u32::MAX),
data: out[start..].as_mut_ptr().cast(),
}],
};
let status = unsafe {
AudioConverterFillComplexBuffer(
self.converter,
input_proc,
(&raw mut input).cast(),
&mut frames,
&mut buffer_list,
ptr::null_mut(),
)
};
out.truncate(start + frames as usize * self.channels as usize);
if status != 0 && status != NO_MORE_INPUT {
return Err(Error::new(
ErrorKind::Codec,
format!("malformed AAC access unit (OSStatus {status})"),
));
}
Ok(())
}
pub(super) fn reset(&mut self) -> Result<()> {
let status: OSStatus = unsafe { AudioConverterReset(self.converter) };
if status == 0 {
Ok(())
} else {
Err(status_error(
status,
"AudioConverterReset could not reset the AAC decoder",
))
}
}
}
struct PendingPacket {
data: *const u8,
description: AudioStreamPacketDescription,
channels: u32,
consumed: bool,
}
extern "C" fn input_proc(
_converter: AudioConverterRef,
io_number_data_packets: *mut u32,
io_data: *mut AudioBufferList,
out_data_packet_description: *mut *mut AudioStreamPacketDescription,
in_user_data: *mut c_void,
) -> OSStatus {
let state = unsafe { &mut *in_user_data.cast::<PendingPacket>() };
unsafe {
(*io_data).number_buffers = 1;
(*io_data).buffers[0].number_channels = state.channels;
}
if state.consumed {
unsafe {
*io_number_data_packets = 0;
(*io_data).buffers[0].data_byte_size = 0;
(*io_data).buffers[0].data = ptr::null_mut();
if !out_data_packet_description.is_null() {
*out_data_packet_description = ptr::null_mut();
}
}
return NO_MORE_INPUT;
}
state.consumed = true;
unsafe {
*io_number_data_packets = 1;
(*io_data).buffers[0].data_byte_size = state.description.data_byte_size;
(*io_data).buffers[0].data = state.data.cast_mut().cast();
if !out_data_packet_description.is_null() {
*out_data_packet_description = &raw mut state.description;
}
}
0
}
fn magic_cookie(audio_specific_config: &[u8]) -> Vec<u8> {
let specific = descriptor(0x05, audio_specific_config);
let mut decoder_config = vec![0x40, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
decoder_config.extend_from_slice(&specific);
let mut es = vec![0, 0, 0];
es.extend_from_slice(&descriptor(0x04, &decoder_config));
descriptor(0x03, &es)
}
fn descriptor(tag: u8, body: &[u8]) -> Vec<u8> {
let size = body.len() as u32;
let mut out = vec![
tag,
0x80 | ((size >> 21) & 0x7f) as u8,
0x80 | ((size >> 14) & 0x7f) as u8,
0x80 | ((size >> 7) & 0x7f) as u8,
(size & 0x7f) as u8,
];
out.extend_from_slice(body);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn magic_cookie_wraps_the_audio_specific_config_in_an_es_descriptor() {
assert_eq!(
magic_cookie(&[0x11, 0x90]),
[
0x03, 0x80, 0x80, 0x80, 0x1c, 0, 0, 0, 0x04, 0x80, 0x80, 0x80, 0x14, 0x40, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, 0x80, 0x80, 0x80, 0x02, 0x11, 0x90,
]
);
}
}