use std::ffi::c_void;
use std::ptr;
use crate::error::{Error, Result};
use crate::ffi::{check_decoder, decoder_version};
use crate::util::{AlignedBuffer, VersionInfo};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecoderTransport {
Auto,
Raw,
Mp4Raw,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DrcEffectType {
None,
Night,
Noisy,
Limited,
LowLevel,
Dialog,
GeneralCompression,
Expanded,
Articulated,
Headphone,
PortableSpeaker,
StereoDownmix,
}
impl DrcEffectType {
fn raw(self) -> i32 {
match self {
Self::None => 0,
Self::Night => 1,
Self::Noisy => 2,
Self::Limited => 3,
Self::LowLevel => 4,
Self::Dialog => 5,
Self::GeneralCompression => 6,
Self::Expanded => 7,
Self::Articulated => 8,
Self::Headphone => 9,
Self::PortableSpeaker => 10,
Self::StereoDownmix => 11,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SbrMode {
Unknown,
Disabled,
Enabled,
Downsampled,
Esbr,
}
impl From<i32> for SbrMode {
fn from(value: i32) -> Self {
match value {
0 => Self::Disabled,
1 => Self::Enabled,
3 => Self::Esbr,
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RawStreamConfig {
pub sample_rate: u32,
pub audio_object_type: ProfileHint,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProfileHint {
AacLc,
HeAacV1,
AacLd,
HeAacV2,
AacEld,
Usac,
}
impl ProfileHint {
fn aot(self) -> i32 {
match self {
Self::AacLc => libxaac_sys::AOT_AAC_LC as i32,
Self::HeAacV1 => libxaac_sys::AOT_SBR as i32,
Self::AacLd => libxaac_sys::AOT_AAC_LD as i32,
Self::HeAacV2 => libxaac_sys::AOT_PS as i32,
Self::AacEld => libxaac_sys::AOT_AAC_ELD as i32,
Self::Usac => libxaac_sys::AOT_USAC as i32,
}
}
}
#[derive(Debug, Clone)]
pub struct DecoderConfig {
pub transport: DecoderTransport,
pub pcm_word_size: u16,
pub raw: Option<RawStreamConfig>,
pub downmix_to_mono: bool,
pub to_stereo: bool,
pub downsample_sbr: bool,
pub max_channels: u8,
pub coupling_channels: u8,
pub downmix_to_stereo: bool,
pub disable_sync: bool,
pub auto_sbr_upsample: bool,
pub drc_cut: f32,
pub drc_boost: f32,
pub drc_target_level: u8,
pub drc_heavy_compression: bool,
pub drc_effect_type: DrcEffectType,
pub drc_target_loudness: Option<i32>,
pub ld_frame_480: bool,
pub hq_esbr: bool,
pub ps_enable: bool,
pub peak_limiter: bool,
pub frame_length_960: bool,
pub error_concealment: bool,
pub enable_esbr: bool,
}
impl Default for DecoderConfig {
fn default() -> Self {
Self {
transport: DecoderTransport::Auto,
pcm_word_size: 16,
raw: None,
downmix_to_mono: false,
to_stereo: false,
downsample_sbr: false,
max_channels: 2,
coupling_channels: 0,
downmix_to_stereo: false,
disable_sync: false,
auto_sbr_upsample: true,
drc_cut: 1.0,
drc_boost: 1.0,
drc_target_level: 108,
drc_heavy_compression: false,
drc_effect_type: DrcEffectType::None,
drc_target_loudness: None,
ld_frame_480: false,
hq_esbr: false,
ps_enable: false,
peak_limiter: true,
frame_length_960: false,
error_concealment: true,
enable_esbr: false,
}
}
}
impl DecoderConfig {
fn validate(&self) -> Result<()> {
if !matches!(self.pcm_word_size, 16 | 24) {
return Err(Error::InvalidConfig("pcm_word_size must be 16 or 24"));
}
if self.max_channels == 0 {
return Err(Error::InvalidConfig(
"max_channels must be greater than zero",
));
}
if matches!(
self.transport,
DecoderTransport::Raw | DecoderTransport::Mp4Raw
) && self.raw.is_none()
{
return Err(Error::InvalidConfig(
"raw transport requires RawStreamConfig",
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamInfo {
pub sample_rate: u32,
pub channels: u16,
pub channel_mask: u32,
pub pcm_word_size: u16,
pub sbr_mode: SbrMode,
pub audio_object_type: i32,
}
#[derive(Debug, Clone)]
pub struct DecodedFrame {
pub pcm: Vec<u8>,
pub bytes_consumed: usize,
pub stream_info: StreamInfo,
}
impl DecodedFrame {
pub fn pcm_i16(&self) -> Option<Vec<i16>> {
if self.stream_info.pcm_word_size != 16 || self.pcm.len() % 2 != 0 {
return None;
}
Some(
self.pcm
.chunks_exact(2)
.map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]]))
.collect(),
)
}
}
#[derive(Debug)]
pub struct Decoder {
config: DecoderConfig,
version: VersionInfo,
api: AlignedBuffer,
memtabs: AlignedBuffer,
memblocks: Vec<AlignedBuffer>,
input_index: usize,
output_index: usize,
input_capacity: usize,
initialized: bool,
}
impl Decoder {
pub fn new(config: DecoderConfig) -> Result<Self> {
config.validate()?;
let version = decoder_version();
let mut api_size = 0u32;
check_decoder(unsafe {
libxaac_sys::ixheaacd_dec_api(
ptr::null_mut(),
libxaac_sys::IA_API_CMD_GET_API_SIZE as i32,
0,
(&mut api_size as *mut _) as *mut c_void,
)
})?;
let api = AlignedBuffer::new(api_size as usize, 4)?;
check_decoder(unsafe {
libxaac_sys::ixheaacd_dec_api(
api.as_ptr().cast::<c_void>(),
libxaac_sys::IA_API_CMD_INIT as i32,
libxaac_sys::IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS as i32,
ptr::null_mut(),
)
})?;
let mut decoder = Self {
config,
version,
api,
memtabs: AlignedBuffer::new(4, 4)?,
memblocks: Vec::new(),
input_index: 0,
output_index: 0,
input_capacity: 0,
initialized: false,
};
decoder.apply_config()?;
decoder.allocate_memory()?;
Ok(decoder)
}
pub fn config(&self) -> &DecoderConfig {
&self.config
}
pub fn version(&self) -> &VersionInfo {
&self.version
}
pub fn input_capacity(&self) -> usize {
self.input_capacity
}
pub fn probe_stream_info(&mut self, data: &[u8]) -> Result<StreamInfo> {
if self.initialized {
return self.stream_info();
}
let input_ptr = self.memblocks[self.input_index].as_ptr();
let mut source_offset = 0usize;
let mut buffered = 0usize;
let mut input_over = false;
loop {
if buffered < self.input_capacity && source_offset < data.len() {
let to_copy = (self.input_capacity - buffered).min(data.len() - source_offset);
unsafe {
ptr::copy_nonoverlapping(
data.as_ptr().add(source_offset),
input_ptr.add(buffered),
to_copy,
);
}
buffered += to_copy;
source_offset += to_copy;
}
if buffered == 0 && source_offset == data.len() && !input_over {
self.signal_end_of_input()?;
input_over = true;
}
if buffered == 0 && input_over {
return Err(Error::DecoderError(libxaac_sys::IA_FATAL_ERROR as i32));
}
let mut bytes = buffered as i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_INPUT_BYTES as i32,
0,
(&mut bytes as *mut _) as *mut c_void,
))?;
let status = self.call(
libxaac_sys::IA_API_CMD_INIT as i32,
libxaac_sys::IA_CMD_TYPE_INIT_PROCESS as i32,
ptr::null_mut(),
);
if is_fatal(status) {
return Err(Error::DecoderError(status));
}
let mut init_done = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_INIT as i32,
libxaac_sys::IA_CMD_TYPE_INIT_DONE_QUERY as i32,
(&mut init_done as *mut _) as *mut c_void,
))?;
let mut consumed = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_CURIDX_INPUT_BUF as i32,
0,
(&mut consumed as *mut _) as *mut c_void,
))?;
let consumed = consumed.max(0) as usize;
if consumed > buffered {
return Err(Error::DecoderError(libxaac_sys::IA_FATAL_ERROR as i32));
}
buffered -= consumed;
if buffered != 0 {
unsafe {
ptr::copy(input_ptr.add(consumed), input_ptr, buffered);
}
}
if buffered < self.input_capacity {
unsafe {
ptr::write_bytes(input_ptr.add(buffered), 0, self.input_capacity - buffered);
}
}
if init_done != 0 {
self.initialized = true;
return self.stream_info();
}
if source_offset == data.len() && !input_over {
self.signal_end_of_input()?;
input_over = true;
}
}
}
pub fn decode(&mut self, packet: &[u8]) -> Result<DecodedFrame> {
if packet.len() > self.input_capacity {
return Err(Error::InputTooLarge {
capacity: self.input_capacity,
actual: packet.len(),
});
}
self.copy_input(packet);
let mut input_len = packet.len();
if !self.initialized {
let consumed = self.initialize(packet.len())?;
if consumed > input_len {
return Err(Error::DecoderError(libxaac_sys::IA_FATAL_ERROR as i32));
}
if consumed != 0 {
let remaining = input_len - consumed;
let input_ptr = self.memblocks[self.input_index].as_ptr();
unsafe {
ptr::copy(input_ptr.add(consumed), input_ptr, remaining);
ptr::write_bytes(input_ptr.add(remaining), 0, self.input_capacity - remaining);
}
input_len = remaining;
}
}
let mut bytes = input_len as i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_INPUT_BYTES as i32,
0,
(&mut bytes as *mut _) as *mut c_void,
))?;
check_decoder(self.call(
libxaac_sys::IA_API_CMD_EXECUTE as i32,
libxaac_sys::IA_CMD_TYPE_DO_EXECUTE as i32,
ptr::null_mut(),
))?;
let mut consumed = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_CURIDX_INPUT_BUF as i32,
0,
(&mut consumed as *mut _) as *mut c_void,
))?;
let mut out_bytes = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_OUTPUT_BYTES as i32,
0,
(&mut out_bytes as *mut _) as *mut c_void,
))?;
let info = self.stream_info()?;
let out_ptr = self.memblocks[self.output_index].as_ptr();
let pcm =
unsafe { std::slice::from_raw_parts(out_ptr, out_bytes.max(0) as usize) }.to_vec();
Ok(DecodedFrame {
pcm,
bytes_consumed: consumed.max(0) as usize,
stream_info: info,
})
}
pub fn signal_end_of_input(&mut self) -> Result<()> {
check_decoder(self.call(
libxaac_sys::IA_API_CMD_INPUT_OVER as i32,
0,
ptr::null_mut(),
))
}
fn apply_config(&mut self) -> Result<()> {
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ as i32,
self.config.pcm_word_size as i32,
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DOWNMIX as i32,
i32::from(self.config.downmix_to_mono),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_TOSTEREO as i32,
i32::from(self.config.to_stereo),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DSAMPLE as i32,
i32::from(self.config.downsample_sbr),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4 as i32,
i32::from(matches!(self.config.transport, DecoderTransport::Mp4Raw)),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_MAX_CHANNEL as i32,
i32::from(self.config.max_channels),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_COUP_CHANNEL as i32,
i32::from(self.config.coupling_channels),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DOWNMIX_STEREO as i32,
i32::from(self.config.downmix_to_stereo),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_DISABLE_SYNC as i32,
i32::from(
self.config.disable_sync
|| matches!(
self.config.transport,
DecoderTransport::Raw | DecoderTransport::Mp4Raw
),
),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_AUTO_SBR_UPSAMPLE as i32,
i32::from(self.config.auto_sbr_upsample),
)?;
self.set_config_f32(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT as i32,
self.config.drc_cut,
)?;
self.set_config_f32(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST as i32,
self.config.drc_boost,
)?;
self.set_config(
libxaac_sys::IA_XHEAAC_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL as i32,
i32::from(self.config.drc_target_level),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP as i32,
i32::from(self.config.drc_heavy_compression),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE as i32,
self.config.drc_effect_type.raw(),
)?;
if let Some(target_loudness) = self.config.drc_target_loudness {
let target_loudness = -(target_loudness.clamp(-63, 0) << 2);
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS as i32,
target_loudness,
)?;
}
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_FRAMESIZE as i32,
i32::from(self.config.ld_frame_480),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_HQ_ESBR as i32,
i32::from(self.config.hq_esbr),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_PS_ENABLE as i32,
i32::from(self.config.ps_enable),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_PEAK_LIMITER as i32,
i32::from(!self.config.peak_limiter),
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_FRAMELENGTH_FLAG as i32,
i32::from(self.config.frame_length_960),
)?;
self.set_config(
libxaac_sys::IA_XHEAAC_DEC_CONFIG_ERROR_CONCEALMENT as i32,
i32::from(self.config.error_concealment),
)?;
self.set_config(
libxaac_sys::IA_XHEAAC_DEC_CONFIG_PARAM_ESBR as i32,
i32::from(self.config.enable_esbr),
)?;
if let Some(raw) = self.config.raw {
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ as i32,
raw.sample_rate as i32,
)?;
self.set_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_AOT as i32,
raw.audio_object_type.aot(),
)?;
}
Ok(())
}
fn allocate_memory(&mut self) -> Result<()> {
let mut memtabs_size = 0u32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_MEMTABS_SIZE as i32,
0,
(&mut memtabs_size as *mut _) as *mut c_void,
))?;
self.memtabs = AlignedBuffer::new(memtabs_size as usize, 4)?;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_MEMTABS_PTR as i32,
0,
self.memtabs.as_ptr().cast::<c_void>(),
))?;
check_decoder(self.call(
libxaac_sys::IA_API_CMD_INIT as i32,
libxaac_sys::IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS as i32,
ptr::null_mut(),
))?;
let mut mem_count = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_N_MEMTABS as i32,
0,
(&mut mem_count as *mut _) as *mut c_void,
))?;
for index in 0..mem_count {
let mut size = 0u32;
let mut alignment = 0u32;
let mut mem_type = 0u32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_MEM_INFO_SIZE as i32,
index,
(&mut size as *mut _) as *mut c_void,
))?;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_MEM_INFO_ALIGNMENT as i32,
index,
(&mut alignment as *mut _) as *mut c_void,
))?;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_MEM_INFO_TYPE as i32,
index,
(&mut mem_type as *mut _) as *mut c_void,
))?;
let block = AlignedBuffer::new(size as usize, alignment as usize)?;
let ptr = block.as_ptr().cast::<c_void>();
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_MEM_PTR as i32,
index,
ptr,
))?;
if mem_type == libxaac_sys::IA_MEMTYPE_INPUT {
self.input_index = self.memblocks.len();
self.input_capacity = size as usize;
} else if mem_type == libxaac_sys::IA_MEMTYPE_OUTPUT {
self.output_index = self.memblocks.len();
}
self.memblocks.push(block);
}
Ok(())
}
fn initialize(&mut self, input_len: usize) -> Result<usize> {
let mut bytes = input_len as i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_INPUT_BYTES as i32,
0,
(&mut bytes as *mut _) as *mut c_void,
))?;
check_decoder(self.call(
libxaac_sys::IA_API_CMD_INIT as i32,
libxaac_sys::IA_CMD_TYPE_INIT_PROCESS as i32,
ptr::null_mut(),
))?;
let mut init_done = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_INIT as i32,
libxaac_sys::IA_CMD_TYPE_INIT_DONE_QUERY as i32,
(&mut init_done as *mut _) as *mut c_void,
))?;
let mut consumed = 0i32;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_GET_CURIDX_INPUT_BUF as i32,
0,
(&mut consumed as *mut _) as *mut c_void,
))?;
self.initialized = init_done != 0;
Ok(consumed.max(0) as usize)
}
fn stream_info(&self) -> Result<StreamInfo> {
let mut sample_rate = 0i32;
let mut channels = 0i32;
let mut channel_mask = 0i32;
let mut sbr_mode = 0i32;
let mut aot = 0i32;
check_decoder(self.get_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ as i32,
&mut sample_rate,
))?;
check_decoder(self.get_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS as i32,
&mut channels,
))?;
check_decoder(self.get_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK as i32,
&mut channel_mask,
))?;
check_decoder(self.get_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE as i32,
&mut sbr_mode,
))?;
check_decoder(self.get_config(
libxaac_sys::IA_ENHAACPLUS_DEC_CONFIG_PARAM_AOT as i32,
&mut aot,
))?;
Ok(StreamInfo {
sample_rate: sample_rate.max(0) as u32,
channels: channels.max(0) as u16,
channel_mask: channel_mask.max(0) as u32,
pcm_word_size: self.config.pcm_word_size,
sbr_mode: SbrMode::from(sbr_mode),
audio_object_type: aot,
})
}
fn copy_input(&mut self, packet: &[u8]) {
let input_ptr = self.memblocks[self.input_index].as_ptr();
unsafe {
ptr::copy_nonoverlapping(packet.as_ptr(), input_ptr, packet.len());
}
if packet.len() < self.input_capacity {
unsafe {
ptr::write_bytes(
input_ptr.add(packet.len()),
0,
self.input_capacity - packet.len(),
);
}
}
}
fn set_config(&mut self, index: i32, value: i32) -> Result<()> {
let mut value = value;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_CONFIG_PARAM as i32,
index,
(&mut value as *mut _) as *mut c_void,
))
}
fn set_config_f32(&mut self, index: i32, value: f32) -> Result<()> {
let mut value = value;
check_decoder(self.call_with_value(
libxaac_sys::IA_API_CMD_SET_CONFIG_PARAM as i32,
index,
(&mut value as *mut _) as *mut c_void,
))
}
fn get_config(&self, index: i32, value: &mut i32) -> i32 {
unsafe {
libxaac_sys::ixheaacd_dec_api(
self.api.as_ptr().cast::<c_void>(),
libxaac_sys::IA_API_CMD_GET_CONFIG_PARAM as i32,
index,
(value as *mut _) as *mut c_void,
)
}
}
fn call(&self, cmd: i32, idx: i32, value: *mut c_void) -> i32 {
unsafe {
libxaac_sys::ixheaacd_dec_api(self.api.as_ptr().cast::<c_void>(), cmd, idx, value)
}
}
fn call_with_value(&self, cmd: i32, idx: i32, value: *mut c_void) -> i32 {
self.call(cmd, idx, value)
}
}
fn is_fatal(status: i32) -> bool {
(status as u32 & libxaac_sys::IA_FATAL_ERROR) != 0
}