mirror of
https://github.com/mstorsjo/fdk-aac.git
synced 2025-03-03 01:47:37 +01:00
Bug: 148384920 Test: atest DecoderTestXheAac DecoderTestAacDrc Change-Id: I88cd6da0b18c73f7b521ea58ba8b8f364278b64f
2050 lines
73 KiB
C++
2050 lines
73 KiB
C++
/* -----------------------------------------------------------------------------
|
|
Software License for The Fraunhofer FDK AAC Codec Library for Android
|
|
|
|
© Copyright 1995 - 2019 Fraunhofer-Gesellschaft zur Förderung der angewandten
|
|
Forschung e.V. All rights reserved.
|
|
|
|
1. INTRODUCTION
|
|
The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
|
|
that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
|
|
scheme for digital audio. This FDK AAC Codec software is intended to be used on
|
|
a wide variety of Android devices.
|
|
|
|
AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
|
|
general perceptual audio codecs. AAC-ELD is considered the best-performing
|
|
full-bandwidth communications codec by independent studies and is widely
|
|
deployed. AAC has been standardized by ISO and IEC as part of the MPEG
|
|
specifications.
|
|
|
|
Patent licenses for necessary patent claims for the FDK AAC Codec (including
|
|
those of Fraunhofer) may be obtained through Via Licensing
|
|
(www.vialicensing.com) or through the respective patent owners individually for
|
|
the purpose of encoding or decoding bit streams in products that are compliant
|
|
with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
|
|
Android devices already license these patent claims through Via Licensing or
|
|
directly from the patent owners, and therefore FDK AAC Codec software may
|
|
already be covered under those patent licenses when it is used for those
|
|
licensed purposes only.
|
|
|
|
Commercially-licensed AAC software libraries, including floating-point versions
|
|
with enhanced sound quality, are also available from Fraunhofer. Users are
|
|
encouraged to check the Fraunhofer website for additional applications
|
|
information and documentation.
|
|
|
|
2. COPYRIGHT LICENSE
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted without payment of copyright license fees provided that you
|
|
satisfy the following conditions:
|
|
|
|
You must retain the complete text of this software license in redistributions of
|
|
the FDK AAC Codec or your modifications thereto in source code form.
|
|
|
|
You must retain the complete text of this software license in the documentation
|
|
and/or other materials provided with redistributions of the FDK AAC Codec or
|
|
your modifications thereto in binary form. You must make available free of
|
|
charge copies of the complete source code of the FDK AAC Codec and your
|
|
modifications thereto to recipients of copies in binary form.
|
|
|
|
The name of Fraunhofer may not be used to endorse or promote products derived
|
|
from this library without prior written permission.
|
|
|
|
You may not charge copyright license fees for anyone to use, copy or distribute
|
|
the FDK AAC Codec software or your modifications thereto.
|
|
|
|
Your modified versions of the FDK AAC Codec must carry prominent notices stating
|
|
that you changed the software and the date of any change. For modified versions
|
|
of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
|
|
must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
|
|
AAC Codec Library for Android."
|
|
|
|
3. NO PATENT LICENSE
|
|
|
|
NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
|
|
limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
|
|
Fraunhofer provides no warranty of patent non-infringement with respect to this
|
|
software.
|
|
|
|
You may use this FDK AAC Codec software or modifications thereto only for
|
|
purposes that are authorized by appropriate patent licenses.
|
|
|
|
4. DISCLAIMER
|
|
|
|
This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
|
|
holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
including but not limited to the implied warranties of merchantability and
|
|
fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
|
|
or consequential damages, including but not limited to procurement of substitute
|
|
goods or services; loss of use, data, or profits, or business interruption,
|
|
however caused and on any theory of liability, whether in contract, strict
|
|
liability, or tort (including negligence), arising in any way out of the use of
|
|
this software, even if advised of the possibility of such damage.
|
|
|
|
5. CONTACT INFORMATION
|
|
|
|
Fraunhofer Institute for Integrated Circuits IIS
|
|
Attention: Audio and Multimedia Departments - FDK AAC LL
|
|
Am Wolfsmantel 33
|
|
91058 Erlangen, Germany
|
|
|
|
www.iis.fraunhofer.de/amm
|
|
amm-info@iis.fraunhofer.de
|
|
----------------------------------------------------------------------------- */
|
|
|
|
/**************************** AAC decoder library ******************************
|
|
|
|
Author(s): Manuel Jander
|
|
|
|
Description:
|
|
|
|
*******************************************************************************/
|
|
|
|
#include "aacdecoder_lib.h"
|
|
|
|
#include "aac_ram.h"
|
|
#include "aacdecoder.h"
|
|
#include "tpdec_lib.h"
|
|
#include "FDK_core.h" /* FDK_tools version info */
|
|
|
|
#include "sbrdecoder.h"
|
|
|
|
#include "conceal.h"
|
|
|
|
#include "aacdec_drc.h"
|
|
|
|
#include "sac_dec_lib.h"
|
|
|
|
#include "pcm_utils.h"
|
|
|
|
/* Decoder library info */
|
|
#define AACDECODER_LIB_VL0 3
|
|
#define AACDECODER_LIB_VL1 1
|
|
#define AACDECODER_LIB_VL2 2
|
|
#define AACDECODER_LIB_TITLE "AAC Decoder Lib"
|
|
#ifdef __ANDROID__
|
|
#define AACDECODER_LIB_BUILD_DATE ""
|
|
#define AACDECODER_LIB_BUILD_TIME ""
|
|
#else
|
|
#define AACDECODER_LIB_BUILD_DATE __DATE__
|
|
#define AACDECODER_LIB_BUILD_TIME __TIME__
|
|
#endif
|
|
|
|
static AAC_DECODER_ERROR setConcealMethod(const HANDLE_AACDECODER self,
|
|
const INT method);
|
|
|
|
static void aacDecoder_setMetadataExpiry(const HANDLE_AACDECODER self,
|
|
const INT value) {
|
|
/* check decoder handle */
|
|
if (self != NULL) {
|
|
INT mdExpFrame = 0; /* default: disable */
|
|
|
|
if ((value > 0) &&
|
|
(self->streamInfo.aacSamplesPerFrame >
|
|
0)) { /* Determine the corresponding number of frames: */
|
|
FIXP_DBL frameTime = fDivNorm(self->streamInfo.aacSampleRate,
|
|
self->streamInfo.aacSamplesPerFrame * 1000);
|
|
mdExpFrame = fMultIceil(frameTime, value);
|
|
}
|
|
|
|
/* Configure DRC module */
|
|
aacDecoder_drcSetParam(self->hDrcInfo, DRC_DATA_EXPIRY_FRAME, mdExpFrame);
|
|
|
|
/* Configure PCM downmix module */
|
|
pcmDmx_SetParam(self->hPcmUtils, DMX_BS_DATA_EXPIRY_FRAME, mdExpFrame);
|
|
}
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR
|
|
aacDecoder_GetFreeBytes(const HANDLE_AACDECODER self, UINT *pFreeBytes) {
|
|
/* reset free bytes */
|
|
*pFreeBytes = 0;
|
|
|
|
/* check handle */
|
|
if (!self) return AAC_DEC_INVALID_HANDLE;
|
|
|
|
/* return nr of free bytes */
|
|
HANDLE_FDK_BITSTREAM hBs = transportDec_GetBitstream(self->hInput, 0);
|
|
*pFreeBytes = FDKgetFreeBits(hBs) >> 3;
|
|
|
|
/* success */
|
|
return AAC_DEC_OK;
|
|
}
|
|
|
|
/**
|
|
* Config Decoder using a CSAudioSpecificConfig struct.
|
|
*/
|
|
static LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_Config(
|
|
HANDLE_AACDECODER self, const CSAudioSpecificConfig *pAscStruct,
|
|
UCHAR configMode, UCHAR *configChanged) {
|
|
AAC_DECODER_ERROR err;
|
|
|
|
/* Initialize AAC core decoder, and update self->streaminfo */
|
|
err = CAacDecoder_Init(self, pAscStruct, configMode, configChanged);
|
|
|
|
if (!FDK_chMapDescr_isValid(&self->mapDescr)) {
|
|
return AAC_DEC_UNSUPPORTED_CHANNELCONFIG;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_ConfigRaw(HANDLE_AACDECODER self,
|
|
UCHAR *conf[],
|
|
const UINT length[]) {
|
|
AAC_DECODER_ERROR err = AAC_DEC_OK;
|
|
TRANSPORTDEC_ERROR errTp;
|
|
UINT layer, nrOfLayers = self->nrOfLayers;
|
|
|
|
for (layer = 0; layer < nrOfLayers; layer++) {
|
|
if (length[layer] > 0) {
|
|
errTp = transportDec_OutOfBandConfig(self->hInput, conf[layer],
|
|
length[layer], layer);
|
|
if (errTp != TRANSPORTDEC_OK) {
|
|
switch (errTp) {
|
|
case TRANSPORTDEC_NEED_TO_RESTART:
|
|
err = AAC_DEC_NEED_TO_RESTART;
|
|
break;
|
|
case TRANSPORTDEC_UNSUPPORTED_FORMAT:
|
|
err = AAC_DEC_UNSUPPORTED_FORMAT;
|
|
break;
|
|
default:
|
|
err = AAC_DEC_UNKNOWN;
|
|
break;
|
|
}
|
|
/* if baselayer is OK we continue decoding */
|
|
if (layer >= 1) {
|
|
self->nrOfLayers = layer;
|
|
err = AAC_DEC_OK;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_RawISOBMFFData(HANDLE_AACDECODER self,
|
|
UCHAR *buffer,
|
|
UINT length) {
|
|
FDK_BITSTREAM bs;
|
|
HANDLE_FDK_BITSTREAM hBs = &bs;
|
|
AAC_DECODER_ERROR err = AAC_DEC_OK;
|
|
|
|
if (length < 8) return AAC_DEC_UNKNOWN;
|
|
|
|
while (length >= 8) {
|
|
UINT size =
|
|
(buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
|
|
DRC_DEC_ERROR uniDrcErr = DRC_DEC_OK;
|
|
|
|
if (length < size) return AAC_DEC_UNKNOWN;
|
|
if (size <= 8) return AAC_DEC_UNKNOWN;
|
|
|
|
FDKinitBitStream(hBs, buffer + 8, 0x10000000, (size - 8) * 8);
|
|
|
|
if ((buffer[4] == 'l') && (buffer[5] == 'u') && (buffer[6] == 'd') &&
|
|
(buffer[7] == 't')) {
|
|
uniDrcErr = FDK_drcDec_ReadLoudnessBox(self->hUniDrcDecoder, hBs);
|
|
} else if ((buffer[4] == 'd') && (buffer[5] == 'm') && (buffer[6] == 'i') &&
|
|
(buffer[7] == 'x')) {
|
|
uniDrcErr =
|
|
FDK_drcDec_ReadDownmixInstructions_Box(self->hUniDrcDecoder, hBs);
|
|
} else if ((buffer[4] == 'u') && (buffer[5] == 'd') && (buffer[6] == 'i') &&
|
|
(buffer[7] == '2')) {
|
|
uniDrcErr =
|
|
FDK_drcDec_ReadUniDrcInstructions_Box(self->hUniDrcDecoder, hBs);
|
|
} else if ((buffer[4] == 'u') && (buffer[5] == 'd') && (buffer[6] == 'c') &&
|
|
(buffer[7] == '2')) {
|
|
uniDrcErr =
|
|
FDK_drcDec_ReadUniDrcCoefficients_Box(self->hUniDrcDecoder, hBs);
|
|
}
|
|
|
|
if (uniDrcErr != DRC_DEC_OK) err = AAC_DEC_UNKNOWN;
|
|
|
|
buffer += size;
|
|
length -= size;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static INT aacDecoder_ConfigCallback(void *handle,
|
|
const CSAudioSpecificConfig *pAscStruct,
|
|
UCHAR configMode, UCHAR *configChanged) {
|
|
HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle;
|
|
AAC_DECODER_ERROR err = AAC_DEC_OK;
|
|
TRANSPORTDEC_ERROR errTp;
|
|
|
|
FDK_ASSERT(self != NULL);
|
|
{
|
|
{ err = aacDecoder_Config(self, pAscStruct, configMode, configChanged); }
|
|
}
|
|
if (err == AAC_DEC_OK) {
|
|
/*
|
|
revert concealment method if either
|
|
- Interpolation concealment might not be meaningful
|
|
- Interpolation concealment is not implemented
|
|
*/
|
|
if ((self->flags[0] & (AC_LD | AC_ELD) &&
|
|
(self->concealMethodUser == ConcealMethodNone) &&
|
|
CConcealment_GetDelay(&self->concealCommonData) >
|
|
0) /* might not be meaningful but allow if user has set it
|
|
expicitly */
|
|
|| (self->flags[0] & (AC_USAC | AC_RSVD50 | AC_RSV603DA) &&
|
|
CConcealment_GetDelay(&self->concealCommonData) >
|
|
0) /* not implemented */
|
|
) {
|
|
/* Revert to error concealment method Noise Substitution.
|
|
Because interpolation is not implemented for USAC or
|
|
the additional delay is unwanted for low delay codecs. */
|
|
setConcealMethod(self, 1);
|
|
}
|
|
aacDecoder_setMetadataExpiry(self, self->metadataExpiry);
|
|
errTp = TRANSPORTDEC_OK;
|
|
} else {
|
|
if (err == AAC_DEC_NEED_TO_RESTART) {
|
|
errTp = TRANSPORTDEC_NEED_TO_RESTART;
|
|
} else if (IS_INIT_ERROR(err)) {
|
|
errTp = TRANSPORTDEC_UNSUPPORTED_FORMAT;
|
|
} /* Fatal errors */
|
|
else {
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
return errTp;
|
|
}
|
|
|
|
static INT aacDecoder_FreeMemCallback(void *handle,
|
|
const CSAudioSpecificConfig *pAscStruct) {
|
|
TRANSPORTDEC_ERROR errTp = TRANSPORTDEC_OK;
|
|
HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle;
|
|
|
|
const int subStreamIndex = 0;
|
|
|
|
FDK_ASSERT(self != NULL);
|
|
|
|
if (CAacDecoder_FreeMem(self, subStreamIndex) != AAC_DEC_OK) {
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
}
|
|
|
|
/* free Ram_SbrDecoder and Ram_SbrDecChannel */
|
|
if (self->hSbrDecoder != NULL) {
|
|
if (sbrDecoder_FreeMem(&self->hSbrDecoder) != SBRDEC_OK) {
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
/* free pSpatialDec and mpsData */
|
|
if (self->pMpegSurroundDecoder != NULL) {
|
|
if (mpegSurroundDecoder_FreeMem(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder) != MPS_OK) {
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
/* free persistent qmf domain buffer, QmfWorkBufferCore3, QmfWorkBufferCore4,
|
|
* QmfWorkBufferCore5 and configuration variables */
|
|
FDK_QmfDomain_FreeMem(&self->qmfDomain);
|
|
|
|
return errTp;
|
|
}
|
|
|
|
static INT aacDecoder_CtrlCFGChangeCallback(
|
|
void *handle, const CCtrlCFGChange *pCtrlCFGChangeStruct) {
|
|
TRANSPORTDEC_ERROR errTp = TRANSPORTDEC_OK;
|
|
HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle;
|
|
|
|
if (self != NULL) {
|
|
CAacDecoder_CtrlCFGChange(
|
|
self, pCtrlCFGChangeStruct->flushStatus, pCtrlCFGChangeStruct->flushCnt,
|
|
pCtrlCFGChangeStruct->buildUpStatus, pCtrlCFGChangeStruct->buildUpCnt);
|
|
} else {
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
}
|
|
|
|
return errTp;
|
|
}
|
|
|
|
static INT aacDecoder_SbrCallback(
|
|
void *handle, HANDLE_FDK_BITSTREAM hBs, const INT sampleRateIn,
|
|
const INT sampleRateOut, const INT samplesPerFrame,
|
|
const AUDIO_OBJECT_TYPE coreCodec, const MP4_ELEMENT_ID elementID,
|
|
const INT elementIndex, const UCHAR harmonicSBR,
|
|
const UCHAR stereoConfigIndex, const UCHAR configMode, UCHAR *configChanged,
|
|
const INT downscaleFactor) {
|
|
HANDLE_SBRDECODER self = (HANDLE_SBRDECODER)handle;
|
|
|
|
INT errTp = sbrDecoder_Header(self, hBs, sampleRateIn, sampleRateOut,
|
|
samplesPerFrame, coreCodec, elementID,
|
|
elementIndex, harmonicSBR, stereoConfigIndex,
|
|
configMode, configChanged, downscaleFactor);
|
|
|
|
return errTp;
|
|
}
|
|
|
|
static INT aacDecoder_SscCallback(void *handle, HANDLE_FDK_BITSTREAM hBs,
|
|
const AUDIO_OBJECT_TYPE coreCodec,
|
|
const INT samplingRate, const INT frameSize,
|
|
const INT stereoConfigIndex,
|
|
const INT coreSbrFrameLengthIndex,
|
|
const INT configBytes, const UCHAR configMode,
|
|
UCHAR *configChanged) {
|
|
SACDEC_ERROR err;
|
|
TRANSPORTDEC_ERROR errTp;
|
|
HANDLE_AACDECODER hAacDecoder = (HANDLE_AACDECODER)handle;
|
|
|
|
err = mpegSurroundDecoder_Config(
|
|
(CMpegSurroundDecoder *)hAacDecoder->pMpegSurroundDecoder, hBs, coreCodec,
|
|
samplingRate, frameSize, stereoConfigIndex, coreSbrFrameLengthIndex,
|
|
configBytes, configMode, configChanged);
|
|
|
|
switch (err) {
|
|
case MPS_UNSUPPORTED_CONFIG:
|
|
/* MPS found but invalid or not decodable by this instance */
|
|
/* We switch off MPS and keep going */
|
|
hAacDecoder->mpsEnableCurr = 0;
|
|
hAacDecoder->mpsApplicable = 0;
|
|
errTp = TRANSPORTDEC_OK;
|
|
break;
|
|
case MPS_PARSE_ERROR:
|
|
/* MPS found but invalid or not decodable by this instance */
|
|
hAacDecoder->mpsEnableCurr = 0;
|
|
hAacDecoder->mpsApplicable = 0;
|
|
if ((coreCodec == AOT_USAC) || (coreCodec == AOT_DRM_USAC) ||
|
|
IS_LOWDELAY(coreCodec)) {
|
|
errTp = TRANSPORTDEC_PARSE_ERROR;
|
|
} else {
|
|
errTp = TRANSPORTDEC_OK;
|
|
}
|
|
break;
|
|
case MPS_OK:
|
|
hAacDecoder->mpsApplicable = 1;
|
|
errTp = TRANSPORTDEC_OK;
|
|
break;
|
|
default:
|
|
/* especially Parsing error is critical for transport layer */
|
|
hAacDecoder->mpsApplicable = 0;
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
}
|
|
|
|
return (INT)errTp;
|
|
}
|
|
|
|
static INT aacDecoder_UniDrcCallback(void *handle, HANDLE_FDK_BITSTREAM hBs,
|
|
const INT fullPayloadLength,
|
|
const INT payloadType,
|
|
const INT subStreamIndex,
|
|
const INT payloadStart,
|
|
const AUDIO_OBJECT_TYPE aot) {
|
|
DRC_DEC_ERROR err = DRC_DEC_OK;
|
|
TRANSPORTDEC_ERROR errTp;
|
|
HANDLE_AACDECODER hAacDecoder = (HANDLE_AACDECODER)handle;
|
|
DRC_DEC_CODEC_MODE drcDecCodecMode = DRC_DEC_CODEC_MODE_UNDEFINED;
|
|
|
|
if (subStreamIndex != 0) {
|
|
return TRANSPORTDEC_OK;
|
|
}
|
|
|
|
else if (aot == AOT_USAC) {
|
|
drcDecCodecMode = DRC_DEC_MPEG_D_USAC;
|
|
}
|
|
|
|
err = FDK_drcDec_SetCodecMode(hAacDecoder->hUniDrcDecoder, drcDecCodecMode);
|
|
if (err) return (INT)TRANSPORTDEC_UNKOWN_ERROR;
|
|
|
|
if (payloadType == 0) /* uniDrcConfig */
|
|
{
|
|
err = FDK_drcDec_ReadUniDrcConfig(hAacDecoder->hUniDrcDecoder, hBs);
|
|
} else /* loudnessInfoSet */
|
|
{
|
|
err = FDK_drcDec_ReadLoudnessInfoSet(hAacDecoder->hUniDrcDecoder, hBs);
|
|
hAacDecoder->loudnessInfoSetPosition[1] = payloadStart;
|
|
hAacDecoder->loudnessInfoSetPosition[2] = fullPayloadLength;
|
|
}
|
|
|
|
if (err == DRC_DEC_OK)
|
|
errTp = TRANSPORTDEC_OK;
|
|
else
|
|
errTp = TRANSPORTDEC_UNKOWN_ERROR;
|
|
|
|
return (INT)errTp;
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_AncDataInit(HANDLE_AACDECODER self,
|
|
UCHAR *buffer, int size) {
|
|
CAncData *ancData = &self->ancData;
|
|
|
|
return CAacDecoder_AncDataInit(ancData, buffer, size);
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_AncDataGet(HANDLE_AACDECODER self,
|
|
int index, UCHAR **ptr,
|
|
int *size) {
|
|
CAncData *ancData = &self->ancData;
|
|
|
|
return CAacDecoder_AncDataGet(ancData, index, ptr, size);
|
|
}
|
|
|
|
/* If MPS is present in stream, but not supported by this instance, we'll
|
|
have to switch off MPS and use QMF synthesis in the SBR module if required */
|
|
static int isSupportedMpsConfig(AUDIO_OBJECT_TYPE aot,
|
|
unsigned int numInChannels,
|
|
unsigned int fMpsPresent) {
|
|
LIB_INFO libInfo[FDK_MODULE_LAST];
|
|
UINT mpsCaps;
|
|
int isSupportedCfg = 1;
|
|
|
|
FDKinitLibInfo(libInfo);
|
|
|
|
mpegSurroundDecoder_GetLibInfo(libInfo);
|
|
|
|
mpsCaps = FDKlibInfo_getCapabilities(libInfo, FDK_MPSDEC);
|
|
|
|
if (!(mpsCaps & CAPF_MPS_LD) && IS_LOWDELAY(aot)) {
|
|
/* We got an LD AOT but MPS decoder does not support LD. */
|
|
isSupportedCfg = 0;
|
|
}
|
|
if ((mpsCaps & CAPF_MPS_LD) && IS_LOWDELAY(aot) && !fMpsPresent) {
|
|
/* We got an LD AOT and the MPS decoder supports it.
|
|
* But LD-MPS is not explicitly signaled. */
|
|
isSupportedCfg = 0;
|
|
}
|
|
if (!(mpsCaps & CAPF_MPS_USAC) && IS_USAC(aot)) {
|
|
/* We got an USAC AOT but MPS decoder does not support USAC. */
|
|
isSupportedCfg = 0;
|
|
}
|
|
if (!(mpsCaps & CAPF_MPS_STD) && !IS_LOWDELAY(aot) && !IS_USAC(aot)) {
|
|
/* We got an GA AOT but MPS decoder does not support it. */
|
|
isSupportedCfg = 0;
|
|
}
|
|
/* Check whether the MPS modul supports the given number of input channels: */
|
|
switch (numInChannels) {
|
|
case 1:
|
|
if (!(mpsCaps & CAPF_MPS_1CH_IN)) {
|
|
/* We got a one channel input to MPS decoder but it does not support it.
|
|
*/
|
|
isSupportedCfg = 0;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!(mpsCaps & CAPF_MPS_2CH_IN)) {
|
|
/* We got a two channel input to MPS decoder but it does not support it.
|
|
*/
|
|
isSupportedCfg = 0;
|
|
}
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
if (!(mpsCaps & CAPF_MPS_6CH_IN)) {
|
|
/* We got a six channel input to MPS decoder but it does not support it.
|
|
*/
|
|
isSupportedCfg = 0;
|
|
}
|
|
break;
|
|
default:
|
|
isSupportedCfg = 0;
|
|
}
|
|
|
|
return (isSupportedCfg);
|
|
}
|
|
|
|
static AAC_DECODER_ERROR setConcealMethod(
|
|
const HANDLE_AACDECODER self, /*!< Handle of the decoder instance */
|
|
const INT method) {
|
|
AAC_DECODER_ERROR errorStatus = AAC_DEC_OK;
|
|
CConcealParams *pConcealData = NULL;
|
|
int method_revert = 0;
|
|
HANDLE_SBRDECODER hSbrDec = NULL;
|
|
HANDLE_AAC_DRC hDrcInfo = NULL;
|
|
HANDLE_PCM_DOWNMIX hPcmDmx = NULL;
|
|
CConcealmentMethod backupMethod = ConcealMethodNone;
|
|
int backupDelay = 0;
|
|
int bsDelay = 0;
|
|
|
|
/* check decoder handle */
|
|
if (self != NULL) {
|
|
pConcealData = &self->concealCommonData;
|
|
hSbrDec = self->hSbrDecoder;
|
|
hDrcInfo = self->hDrcInfo;
|
|
hPcmDmx = self->hPcmUtils;
|
|
if (self->flags[0] & (AC_USAC | AC_RSVD50 | AC_RSV603DA) && method >= 2) {
|
|
/* Interpolation concealment is not implemented for USAC/RSVD50 */
|
|
/* errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
goto bail; */
|
|
method_revert = 1;
|
|
}
|
|
if (self->flags[0] & (AC_USAC | AC_RSVD50 | AC_RSV603DA) && method >= 2) {
|
|
/* Interpolation concealment is not implemented for USAC/RSVD50 */
|
|
errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
/* Get current method/delay */
|
|
backupMethod = CConcealment_GetMethod(pConcealData);
|
|
backupDelay = CConcealment_GetDelay(pConcealData);
|
|
|
|
/* Be sure to set AAC and SBR concealment method simultaneously! */
|
|
errorStatus = CConcealment_SetParams(
|
|
pConcealData,
|
|
(method_revert == 0) ? (int)method : (int)1, // concealMethod
|
|
AACDEC_CONCEAL_PARAM_NOT_SPECIFIED, // concealFadeOutSlope
|
|
AACDEC_CONCEAL_PARAM_NOT_SPECIFIED, // concealFadeInSlope
|
|
AACDEC_CONCEAL_PARAM_NOT_SPECIFIED, // concealMuteRelease
|
|
AACDEC_CONCEAL_PARAM_NOT_SPECIFIED // concealComfNoiseLevel
|
|
);
|
|
if ((errorStatus != AAC_DEC_OK) && (errorStatus != AAC_DEC_INVALID_HANDLE)) {
|
|
goto bail;
|
|
}
|
|
|
|
/* Get new delay */
|
|
bsDelay = CConcealment_GetDelay(pConcealData);
|
|
|
|
{
|
|
SBR_ERROR sbrErr = SBRDEC_OK;
|
|
|
|
/* set SBR bitstream delay */
|
|
sbrErr = sbrDecoder_SetParam(hSbrDec, SBR_SYSTEM_BITSTREAM_DELAY, bsDelay);
|
|
|
|
switch (sbrErr) {
|
|
case SBRDEC_OK:
|
|
case SBRDEC_NOT_INITIALIZED:
|
|
if (self != NULL) {
|
|
/* save the param value and set later
|
|
(when SBR has been initialized) */
|
|
self->sbrParams.bsDelay = bsDelay;
|
|
}
|
|
break;
|
|
default:
|
|
errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
errorStatus = aacDecoder_drcSetParam(hDrcInfo, DRC_BS_DELAY, bsDelay);
|
|
if ((errorStatus != AAC_DEC_OK) && (errorStatus != AAC_DEC_INVALID_HANDLE)) {
|
|
goto bail;
|
|
}
|
|
|
|
if (errorStatus == AAC_DEC_OK) {
|
|
PCMDMX_ERROR err = pcmDmx_SetParam(hPcmDmx, DMX_BS_DATA_DELAY, bsDelay);
|
|
switch (err) {
|
|
case PCMDMX_INVALID_HANDLE:
|
|
errorStatus = AAC_DEC_INVALID_HANDLE;
|
|
break;
|
|
case PCMDMX_OK:
|
|
break;
|
|
default:
|
|
errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
bail:
|
|
if ((errorStatus != AAC_DEC_OK) && (errorStatus != AAC_DEC_INVALID_HANDLE)) {
|
|
/* Revert to the initial state */
|
|
CConcealment_SetParams(
|
|
pConcealData, (int)backupMethod, AACDEC_CONCEAL_PARAM_NOT_SPECIFIED,
|
|
AACDEC_CONCEAL_PARAM_NOT_SPECIFIED, AACDEC_CONCEAL_PARAM_NOT_SPECIFIED,
|
|
AACDEC_CONCEAL_PARAM_NOT_SPECIFIED);
|
|
/* Revert SBR bitstream delay */
|
|
sbrDecoder_SetParam(hSbrDec, SBR_SYSTEM_BITSTREAM_DELAY, backupDelay);
|
|
/* Revert DRC bitstream delay */
|
|
aacDecoder_drcSetParam(hDrcInfo, DRC_BS_DELAY, backupDelay);
|
|
/* Revert PCM mixdown bitstream delay */
|
|
pcmDmx_SetParam(hPcmDmx, DMX_BS_DATA_DELAY, backupDelay);
|
|
}
|
|
|
|
return errorStatus;
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_SetParam(
|
|
const HANDLE_AACDECODER self, /*!< Handle of the decoder instance */
|
|
const AACDEC_PARAM param, /*!< Parameter to set */
|
|
const INT value) /*!< Parameter valued */
|
|
{
|
|
AAC_DECODER_ERROR errorStatus = AAC_DEC_OK;
|
|
HANDLE_TRANSPORTDEC hTpDec = NULL;
|
|
TRANSPORTDEC_ERROR errTp = TRANSPORTDEC_OK;
|
|
HANDLE_AAC_DRC hDrcInfo = NULL;
|
|
HANDLE_PCM_DOWNMIX hPcmDmx = NULL;
|
|
PCMDMX_ERROR dmxErr = PCMDMX_OK;
|
|
TDLimiterPtr hPcmTdl = NULL;
|
|
DRC_DEC_ERROR uniDrcErr = DRC_DEC_OK;
|
|
|
|
/* check decoder handle */
|
|
if (self != NULL) {
|
|
hTpDec = self->hInput;
|
|
hDrcInfo = self->hDrcInfo;
|
|
hPcmDmx = self->hPcmUtils;
|
|
hPcmTdl = self->hLimiter;
|
|
} else {
|
|
errorStatus = AAC_DEC_INVALID_HANDLE;
|
|
goto bail;
|
|
}
|
|
|
|
/* configure the subsystems */
|
|
switch (param) {
|
|
case AAC_PCM_MIN_OUTPUT_CHANNELS:
|
|
if (value < -1 || value > (8)) {
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
dmxErr = pcmDmx_SetParam(hPcmDmx, MIN_NUMBER_OF_OUTPUT_CHANNELS, value);
|
|
break;
|
|
|
|
case AAC_PCM_MAX_OUTPUT_CHANNELS:
|
|
if (value < -1 || value > (8)) {
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
dmxErr = pcmDmx_SetParam(hPcmDmx, MAX_NUMBER_OF_OUTPUT_CHANNELS, value);
|
|
|
|
if (dmxErr != PCMDMX_OK) {
|
|
goto bail;
|
|
}
|
|
errorStatus =
|
|
aacDecoder_drcSetParam(hDrcInfo, MAX_OUTPUT_CHANNELS, value);
|
|
if (value > 0) {
|
|
uniDrcErr = FDK_drcDec_SetParam(self->hUniDrcDecoder,
|
|
DRC_DEC_TARGET_CHANNEL_COUNT_REQUESTED,
|
|
(FIXP_DBL)value);
|
|
}
|
|
break;
|
|
|
|
case AAC_PCM_DUAL_CHANNEL_OUTPUT_MODE:
|
|
dmxErr = pcmDmx_SetParam(hPcmDmx, DMX_DUAL_CHANNEL_MODE, value);
|
|
break;
|
|
|
|
case AAC_PCM_LIMITER_ENABLE:
|
|
if (value < -2 || value > 1) {
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
self->limiterEnableUser = value;
|
|
break;
|
|
|
|
case AAC_PCM_LIMITER_ATTACK_TIME:
|
|
if (value <= 0) { /* module function converts value to unsigned */
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
switch (pcmLimiter_SetAttack(hPcmTdl, value)) {
|
|
case TDLIMIT_OK:
|
|
break;
|
|
case TDLIMIT_INVALID_HANDLE:
|
|
return AAC_DEC_INVALID_HANDLE;
|
|
case TDLIMIT_INVALID_PARAMETER:
|
|
default:
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
break;
|
|
|
|
case AAC_PCM_LIMITER_RELEAS_TIME:
|
|
if (value <= 0) { /* module function converts value to unsigned */
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
switch (pcmLimiter_SetRelease(hPcmTdl, value)) {
|
|
case TDLIMIT_OK:
|
|
break;
|
|
case TDLIMIT_INVALID_HANDLE:
|
|
return AAC_DEC_INVALID_HANDLE;
|
|
case TDLIMIT_INVALID_PARAMETER:
|
|
default:
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
break;
|
|
|
|
case AAC_METADATA_PROFILE: {
|
|
DMX_PROFILE_TYPE dmxProfile;
|
|
INT mdExpiry = -1; /* in ms (-1: don't change) */
|
|
|
|
switch ((AAC_MD_PROFILE)value) {
|
|
case AAC_MD_PROFILE_MPEG_STANDARD:
|
|
dmxProfile = DMX_PRFL_STANDARD;
|
|
break;
|
|
case AAC_MD_PROFILE_MPEG_LEGACY:
|
|
dmxProfile = DMX_PRFL_MATRIX_MIX;
|
|
break;
|
|
case AAC_MD_PROFILE_MPEG_LEGACY_PRIO:
|
|
dmxProfile = DMX_PRFL_FORCE_MATRIX_MIX;
|
|
break;
|
|
case AAC_MD_PROFILE_ARIB_JAPAN:
|
|
dmxProfile = DMX_PRFL_ARIB_JAPAN;
|
|
mdExpiry = 550; /* ms */
|
|
break;
|
|
default:
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
dmxErr = pcmDmx_SetParam(hPcmDmx, DMX_PROFILE_SETTING, (INT)dmxProfile);
|
|
if (dmxErr != PCMDMX_OK) {
|
|
goto bail;
|
|
}
|
|
if ((self != NULL) && (mdExpiry >= 0)) {
|
|
self->metadataExpiry = mdExpiry;
|
|
/* Determine the corresponding number of frames and configure all
|
|
* related modules. */
|
|
aacDecoder_setMetadataExpiry(self, mdExpiry);
|
|
}
|
|
} break;
|
|
|
|
case AAC_METADATA_EXPIRY_TIME:
|
|
if (value < 0) {
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
if (self != NULL) {
|
|
self->metadataExpiry = value;
|
|
/* Determine the corresponding number of frames and configure all
|
|
* related modules. */
|
|
aacDecoder_setMetadataExpiry(self, value);
|
|
}
|
|
break;
|
|
|
|
case AAC_PCM_OUTPUT_CHANNEL_MAPPING:
|
|
if (value < 0 || value > 1) {
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
/* CAUTION: The given value must be inverted to match the logic! */
|
|
FDK_chMapDescr_setPassThrough(&self->mapDescr, !value);
|
|
break;
|
|
|
|
case AAC_QMF_LOWPOWER:
|
|
if (value < -1 || value > 1) {
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
|
|
/**
|
|
* Set QMF mode (might be overriden)
|
|
* 0:HQ (complex)
|
|
* 1:LP (partially complex)
|
|
*/
|
|
self->qmfModeUser = (QMF_MODE)value;
|
|
break;
|
|
|
|
case AAC_DRC_ATTENUATION_FACTOR:
|
|
/* DRC compression factor (where 0 is no and 127 is max compression) */
|
|
errorStatus = aacDecoder_drcSetParam(hDrcInfo, DRC_CUT_SCALE, value);
|
|
uniDrcErr = FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_COMPRESS,
|
|
value * (FL2FXCONST_DBL(0.5f / 127.0f)));
|
|
break;
|
|
|
|
case AAC_DRC_BOOST_FACTOR:
|
|
/* DRC boost factor (where 0 is no and 127 is max boost) */
|
|
errorStatus = aacDecoder_drcSetParam(hDrcInfo, DRC_BOOST_SCALE, value);
|
|
uniDrcErr = FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_BOOST,
|
|
value * (FL2FXCONST_DBL(0.5f / 127.0f)));
|
|
break;
|
|
|
|
case AAC_DRC_REFERENCE_LEVEL:
|
|
if ((value >= 0) &&
|
|
((value < 40) || (value > 127))) /* allowed range: -10 to -31.75 dB */
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
/* DRC target reference level quantized in 0.25dB steps using values
|
|
[40..127]. Negative values switch off loudness normalisation. Negative
|
|
values also switch off MPEG-4 DRC, while MPEG-D DRC can be separately
|
|
switched on/off with AAC_UNIDRC_SET_EFFECT */
|
|
errorStatus = aacDecoder_drcSetParam(hDrcInfo, TARGET_REF_LEVEL, value);
|
|
uniDrcErr = FDK_drcDec_SetParam(self->hUniDrcDecoder,
|
|
DRC_DEC_LOUDNESS_NORMALIZATION_ON,
|
|
(FIXP_DBL)(value >= 0));
|
|
/* set target loudness also for MPEG-D DRC */
|
|
self->defaultTargetLoudness = (SCHAR)value;
|
|
break;
|
|
|
|
case AAC_DRC_HEAVY_COMPRESSION:
|
|
/* Don't need to overwrite cut/boost values */
|
|
errorStatus =
|
|
aacDecoder_drcSetParam(hDrcInfo, APPLY_HEAVY_COMPRESSION, value);
|
|
break;
|
|
|
|
case AAC_DRC_DEFAULT_PRESENTATION_MODE:
|
|
/* DRC default presentation mode */
|
|
errorStatus =
|
|
aacDecoder_drcSetParam(hDrcInfo, DEFAULT_PRESENTATION_MODE, value);
|
|
break;
|
|
|
|
case AAC_DRC_ENC_TARGET_LEVEL:
|
|
/* Encoder target level for light (i.e. not heavy) compression:
|
|
Target reference level assumed at encoder for deriving limiting gains
|
|
*/
|
|
errorStatus =
|
|
aacDecoder_drcSetParam(hDrcInfo, ENCODER_TARGET_LEVEL, value);
|
|
break;
|
|
|
|
case AAC_UNIDRC_SET_EFFECT:
|
|
if ((value < -1) || (value > 6)) return AAC_DEC_SET_PARAM_FAIL;
|
|
uniDrcErr = FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_EFFECT_TYPE,
|
|
(FIXP_DBL)value);
|
|
break;
|
|
case AAC_UNIDRC_ALBUM_MODE:
|
|
uniDrcErr = FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_ALBUM_MODE,
|
|
(FIXP_DBL)value);
|
|
break;
|
|
|
|
case AAC_TPDEC_CLEAR_BUFFER:
|
|
errTp = transportDec_SetParam(hTpDec, TPDEC_PARAM_RESET, 1);
|
|
self->streamInfo.numLostAccessUnits = 0;
|
|
self->streamInfo.numBadBytes = 0;
|
|
self->streamInfo.numTotalBytes = 0;
|
|
/* aacDecoder_SignalInterruption(self); */
|
|
break;
|
|
case AAC_CONCEAL_METHOD:
|
|
/* Changing the concealment method can introduce additional bitstream
|
|
delay. And that in turn affects sub libraries and modules which makes
|
|
the whole thing quite complex. So the complete changing routine is
|
|
packed into a helper function which keeps all modules and libs in a
|
|
consistent state even in the case an error occures. */
|
|
errorStatus = setConcealMethod(self, value);
|
|
if (errorStatus == AAC_DEC_OK) {
|
|
self->concealMethodUser = (CConcealmentMethod)value;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return AAC_DEC_SET_PARAM_FAIL;
|
|
} /* switch(param) */
|
|
|
|
bail:
|
|
|
|
if (errorStatus == AAC_DEC_OK) {
|
|
/* Check error code returned by DMX module library: */
|
|
switch (dmxErr) {
|
|
case PCMDMX_OK:
|
|
break;
|
|
case PCMDMX_INVALID_HANDLE:
|
|
errorStatus = AAC_DEC_INVALID_HANDLE;
|
|
break;
|
|
default:
|
|
errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
}
|
|
|
|
if (errTp != TRANSPORTDEC_OK && errorStatus == AAC_DEC_OK) {
|
|
errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
}
|
|
|
|
if (errorStatus == AAC_DEC_OK) {
|
|
/* Check error code returned by MPEG-D DRC decoder library: */
|
|
switch (uniDrcErr) {
|
|
case 0:
|
|
break;
|
|
case -9998:
|
|
errorStatus = AAC_DEC_INVALID_HANDLE;
|
|
break;
|
|
default:
|
|
errorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (errorStatus);
|
|
}
|
|
LINKSPEC_CPP HANDLE_AACDECODER aacDecoder_Open(TRANSPORT_TYPE transportFmt,
|
|
UINT nrOfLayers) {
|
|
AAC_DECODER_INSTANCE *aacDec = NULL;
|
|
HANDLE_TRANSPORTDEC pIn;
|
|
int err = 0;
|
|
int stereoConfigIndex = -1;
|
|
|
|
UINT nrOfLayers_min = fMin(nrOfLayers, (UINT)1);
|
|
|
|
/* Allocate transport layer struct. */
|
|
pIn = transportDec_Open(transportFmt, TP_FLAG_MPEG4, nrOfLayers_min);
|
|
if (pIn == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate AAC decoder core struct. */
|
|
aacDec = CAacDecoder_Open(transportFmt);
|
|
|
|
if (aacDec == NULL) {
|
|
transportDec_Close(&pIn);
|
|
goto bail;
|
|
}
|
|
aacDec->hInput = pIn;
|
|
|
|
aacDec->nrOfLayers = nrOfLayers_min;
|
|
|
|
/* Setup channel mapping descriptor. */
|
|
FDK_chMapDescr_init(&aacDec->mapDescr, NULL, 0, 0);
|
|
|
|
/* Register Config Update callback. */
|
|
transportDec_RegisterAscCallback(pIn, aacDecoder_ConfigCallback,
|
|
(void *)aacDec);
|
|
|
|
/* Register Free Memory callback. */
|
|
transportDec_RegisterFreeMemCallback(pIn, aacDecoder_FreeMemCallback,
|
|
(void *)aacDec);
|
|
|
|
/* Register config switch control callback. */
|
|
transportDec_RegisterCtrlCFGChangeCallback(
|
|
pIn, aacDecoder_CtrlCFGChangeCallback, (void *)aacDec);
|
|
|
|
FDKmemclear(&aacDec->qmfDomain, sizeof(FDK_QMF_DOMAIN));
|
|
/* open SBR decoder */
|
|
if (SBRDEC_OK != sbrDecoder_Open(&aacDec->hSbrDecoder, &aacDec->qmfDomain)) {
|
|
err = -1;
|
|
goto bail;
|
|
}
|
|
aacDec->qmfModeUser = NOT_DEFINED;
|
|
transportDec_RegisterSbrCallback(aacDec->hInput, aacDecoder_SbrCallback,
|
|
(void *)aacDec->hSbrDecoder);
|
|
|
|
if (mpegSurroundDecoder_Open(
|
|
(CMpegSurroundDecoder **)&aacDec->pMpegSurroundDecoder,
|
|
stereoConfigIndex, &aacDec->qmfDomain)) {
|
|
err = -1;
|
|
goto bail;
|
|
}
|
|
/* Set MPEG Surround defaults */
|
|
aacDec->mpsEnableUser = 0;
|
|
aacDec->mpsEnableCurr = 0;
|
|
aacDec->mpsApplicable = 0;
|
|
aacDec->mpsOutputMode = (SCHAR)SACDEC_OUT_MODE_NORMAL;
|
|
transportDec_RegisterSscCallback(pIn, aacDecoder_SscCallback, (void *)aacDec);
|
|
|
|
{
|
|
if (FDK_drcDec_Open(&(aacDec->hUniDrcDecoder), DRC_DEC_ALL) != 0) {
|
|
err = -1;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
transportDec_RegisterUniDrcConfigCallback(pIn, aacDecoder_UniDrcCallback,
|
|
(void *)aacDec,
|
|
aacDec->loudnessInfoSetPosition);
|
|
aacDec->defaultTargetLoudness = (SCHAR)96;
|
|
|
|
pcmDmx_Open(&aacDec->hPcmUtils);
|
|
if (aacDec->hPcmUtils == NULL) {
|
|
err = -1;
|
|
goto bail;
|
|
}
|
|
|
|
aacDec->hLimiter =
|
|
pcmLimiter_Create(TDL_ATTACK_DEFAULT_MS, TDL_RELEASE_DEFAULT_MS,
|
|
(FIXP_DBL)MAXVAL_DBL, (8), 96000);
|
|
if (NULL == aacDec->hLimiter) {
|
|
err = -1;
|
|
goto bail;
|
|
}
|
|
aacDec->limiterEnableUser = (UCHAR)-1;
|
|
aacDec->limiterEnableCurr = 0;
|
|
|
|
/* Assure that all modules have same delay */
|
|
if (setConcealMethod(aacDec,
|
|
CConcealment_GetMethod(&aacDec->concealCommonData))) {
|
|
err = -1;
|
|
goto bail;
|
|
}
|
|
|
|
bail:
|
|
if (err == -1) {
|
|
aacDecoder_Close(aacDec);
|
|
aacDec = NULL;
|
|
}
|
|
return aacDec;
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_Fill(HANDLE_AACDECODER self,
|
|
UCHAR *pBuffer[],
|
|
const UINT bufferSize[],
|
|
UINT *pBytesValid) {
|
|
TRANSPORTDEC_ERROR tpErr;
|
|
/* loop counter for layers; if not TT_MP4_RAWPACKETS used as index for only
|
|
available layer */
|
|
INT layer = 0;
|
|
INT nrOfLayers = self->nrOfLayers;
|
|
|
|
{
|
|
for (layer = 0; layer < nrOfLayers; layer++) {
|
|
{
|
|
tpErr = transportDec_FillData(self->hInput, pBuffer[layer],
|
|
bufferSize[layer], &pBytesValid[layer],
|
|
layer);
|
|
if (tpErr != TRANSPORTDEC_OK) {
|
|
return AAC_DEC_UNKNOWN; /* Must be an internal error */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return AAC_DEC_OK;
|
|
}
|
|
|
|
static void aacDecoder_SignalInterruption(HANDLE_AACDECODER self) {
|
|
CAacDecoder_SignalInterruption(self);
|
|
|
|
if (self->hSbrDecoder != NULL) {
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_BS_INTERRUPTION, 1);
|
|
}
|
|
if (self->mpsEnableUser) {
|
|
mpegSurroundDecoder_SetParam(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder,
|
|
SACDEC_BS_INTERRUPTION, 1);
|
|
}
|
|
}
|
|
|
|
static void aacDecoder_UpdateBitStreamCounters(CStreamInfo *pSi,
|
|
HANDLE_FDK_BITSTREAM hBs,
|
|
INT nBits,
|
|
AAC_DECODER_ERROR ErrorStatus) {
|
|
/* calculate bit difference (amount of bits moved forward) */
|
|
nBits = nBits - (INT)FDKgetValidBits(hBs);
|
|
|
|
/* Note: The amount of bits consumed might become negative when parsing a
|
|
bit stream with several sub frames, and we find out at the last sub frame
|
|
that the total frame length does not match the sum of sub frame length.
|
|
If this happens, the transport decoder might want to rewind to the supposed
|
|
ending of the transport frame, and this position might be before the last
|
|
access unit beginning. */
|
|
|
|
/* Calc bitrate. */
|
|
if (pSi->frameSize > 0) {
|
|
/* bitRate = nBits * sampleRate / frameSize */
|
|
int ratio_e = 0;
|
|
FIXP_DBL ratio_m = fDivNorm(pSi->sampleRate, pSi->frameSize, &ratio_e);
|
|
pSi->bitRate = (INT)fMultNorm(nBits, DFRACT_BITS - 1, ratio_m, ratio_e,
|
|
DFRACT_BITS - 1);
|
|
}
|
|
|
|
/* bit/byte counters */
|
|
{
|
|
INT nBytes;
|
|
|
|
nBytes = nBits >> 3;
|
|
pSi->numTotalBytes += nBytes;
|
|
if (IS_OUTPUT_VALID(ErrorStatus)) {
|
|
pSi->numTotalAccessUnits++;
|
|
}
|
|
if (IS_DECODE_ERROR(ErrorStatus)) {
|
|
pSi->numBadBytes += nBytes;
|
|
pSi->numBadAccessUnits++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static INT aacDecoder_EstimateNumberOfLostFrames(HANDLE_AACDECODER self) {
|
|
INT n;
|
|
|
|
transportDec_GetMissingAccessUnitCount(&n, self->hInput);
|
|
|
|
return n;
|
|
}
|
|
|
|
LINKSPEC_CPP AAC_DECODER_ERROR
|
|
aacDecoder_DecodeFrame(HANDLE_AACDECODER self, INT_PCM *pTimeData_extern,
|
|
const INT timeDataSize_extern, const UINT flags) {
|
|
AAC_DECODER_ERROR ErrorStatus;
|
|
INT layer;
|
|
INT nBits;
|
|
HANDLE_FDK_BITSTREAM hBs;
|
|
int fTpInterruption = 0; /* Transport originated interruption detection. */
|
|
int fTpConceal = 0; /* Transport originated concealment. */
|
|
INT_PCM *pTimeData = NULL;
|
|
INT timeDataSize = 0;
|
|
UINT accessUnit = 0;
|
|
UINT numAccessUnits = 1;
|
|
UINT numPrerollAU = 0;
|
|
int fEndAuNotAdjusted = 0; /* The end of the access unit was not adjusted */
|
|
int applyCrossfade = 1; /* flag indicates if flushing was possible */
|
|
FIXP_PCM *pTimeDataFixpPcm; /* Signal buffer for decoding process before PCM
|
|
processing */
|
|
INT timeDataFixpPcmSize;
|
|
PCM_DEC *pTimeDataPcmPost; /* Signal buffer for PCM post-processing */
|
|
INT timeDataPcmPostSize;
|
|
|
|
if (self == NULL) {
|
|
return AAC_DEC_INVALID_HANDLE;
|
|
}
|
|
|
|
pTimeData = self->pcmOutputBuffer;
|
|
timeDataSize = sizeof(self->pcmOutputBuffer) / sizeof(*self->pcmOutputBuffer);
|
|
|
|
if (flags & AACDEC_INTR) {
|
|
self->streamInfo.numLostAccessUnits = 0;
|
|
}
|
|
hBs = transportDec_GetBitstream(self->hInput, 0);
|
|
|
|
/* Get current bits position for bitrate calculation. */
|
|
nBits = FDKgetValidBits(hBs);
|
|
|
|
if (flags & AACDEC_CLRHIST) {
|
|
if (self->flags[0] & AC_USAC) {
|
|
/* 1) store AudioSpecificConfig always in AudioSpecificConfig_Parse() */
|
|
/* 2) free memory of dynamic allocated data */
|
|
CSAudioSpecificConfig asc;
|
|
transportDec_GetAsc(self->hInput, 0, &asc);
|
|
aacDecoder_FreeMemCallback(self, &asc);
|
|
self->streamInfo.numChannels = 0;
|
|
/* 3) restore AudioSpecificConfig */
|
|
transportDec_OutOfBandConfig(self->hInput, asc.config,
|
|
(asc.configBits + 7) >> 3, 0);
|
|
}
|
|
}
|
|
|
|
if (!((flags & (AACDEC_CONCEAL | AACDEC_FLUSH)) ||
|
|
(self->flushStatus == AACDEC_RSV60_DASH_IPF_ATSC_FLUSH_ON) ||
|
|
(self->flushStatus == AACDEC_USAC_DASH_IPF_FLUSH_ON) ||
|
|
(self->buildUpStatus == AACDEC_RSV60_BUILD_UP_IDLE_IN_BAND))) {
|
|
TRANSPORTDEC_ERROR err;
|
|
|
|
for (layer = 0; layer < self->nrOfLayers; layer++) {
|
|
err = transportDec_ReadAccessUnit(self->hInput, layer);
|
|
if (err != TRANSPORTDEC_OK) {
|
|
switch (err) {
|
|
case TRANSPORTDEC_NOT_ENOUGH_BITS:
|
|
ErrorStatus = AAC_DEC_NOT_ENOUGH_BITS;
|
|
goto bail;
|
|
case TRANSPORTDEC_SYNC_ERROR:
|
|
self->streamInfo.numLostAccessUnits =
|
|
aacDecoder_EstimateNumberOfLostFrames(self);
|
|
fTpInterruption = 1;
|
|
break;
|
|
case TRANSPORTDEC_NEED_TO_RESTART:
|
|
ErrorStatus = AAC_DEC_NEED_TO_RESTART;
|
|
goto bail;
|
|
case TRANSPORTDEC_CRC_ERROR:
|
|
fTpConceal = 1;
|
|
break;
|
|
case TRANSPORTDEC_UNSUPPORTED_FORMAT:
|
|
ErrorStatus = AAC_DEC_UNSUPPORTED_FORMAT;
|
|
goto bail;
|
|
default:
|
|
ErrorStatus = AAC_DEC_UNKNOWN;
|
|
goto bail;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (self->streamInfo.numLostAccessUnits > 0) {
|
|
self->streamInfo.numLostAccessUnits--;
|
|
}
|
|
}
|
|
|
|
self->frameOK = 1;
|
|
|
|
UINT prerollAUOffset[AACDEC_MAX_NUM_PREROLL_AU];
|
|
UINT prerollAULength[AACDEC_MAX_NUM_PREROLL_AU];
|
|
for (int i = 0; i < AACDEC_MAX_NUM_PREROLL_AU + 1; i++)
|
|
self->prerollAULength[i] = 0;
|
|
|
|
INT auStartAnchor;
|
|
HANDLE_FDK_BITSTREAM hBsAu;
|
|
|
|
/* Process preroll frames and current frame */
|
|
do {
|
|
if (!(flags & (AACDEC_CONCEAL | AACDEC_FLUSH)) &&
|
|
(self->flushStatus != AACDEC_RSV60_CFG_CHANGE_ATSC_FLUSH_ON) &&
|
|
(accessUnit == 0) &&
|
|
(self->hasAudioPreRoll ||
|
|
(self->buildUpStatus == AACDEC_RSV60_BUILD_UP_IDLE_IN_BAND)) &&
|
|
!fTpInterruption &&
|
|
!fTpConceal /* Bit stream pointer needs to be at the beginning of a
|
|
(valid) AU. */
|
|
) {
|
|
ErrorStatus = CAacDecoder_PreRollExtensionPayloadParse(
|
|
self, &numPrerollAU, prerollAUOffset, prerollAULength);
|
|
|
|
if (ErrorStatus != AAC_DEC_OK) {
|
|
switch (ErrorStatus) {
|
|
case AAC_DEC_NOT_ENOUGH_BITS:
|
|
goto bail;
|
|
case AAC_DEC_PARSE_ERROR:
|
|
self->frameOK = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
numAccessUnits += numPrerollAU;
|
|
}
|
|
|
|
hBsAu = transportDec_GetBitstream(self->hInput, 0);
|
|
auStartAnchor = (INT)FDKgetValidBits(hBsAu);
|
|
|
|
self->accessUnit = accessUnit;
|
|
if (accessUnit < numPrerollAU) {
|
|
FDKpushFor(hBsAu, prerollAUOffset[accessUnit]);
|
|
}
|
|
|
|
/* Signal bit stream interruption to other modules if required. */
|
|
if (fTpInterruption || ((flags & AACDEC_INTR) && (accessUnit == 0))) {
|
|
aacDecoder_SignalInterruption(self);
|
|
if (!((flags & AACDEC_INTR) && (accessUnit == 0))) {
|
|
ErrorStatus = AAC_DEC_TRANSPORT_SYNC_ERROR;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
/* Clearing core data will be done in CAacDecoder_DecodeFrame() below.
|
|
Tell other modules to clear states if required. */
|
|
if (flags & AACDEC_CLRHIST) {
|
|
if (!(self->flags[0] & AC_USAC)) {
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_CLEAR_HISTORY, 1);
|
|
mpegSurroundDecoder_SetParam(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder,
|
|
SACDEC_CLEAR_HISTORY, 1);
|
|
if (FDK_QmfDomain_ClearPersistentMemory(&self->qmfDomain) != 0) {
|
|
ErrorStatus = AAC_DEC_UNKNOWN;
|
|
goto bail;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Empty bit buffer in case of flush request. */
|
|
if (flags & AACDEC_FLUSH && !(flags & AACDEC_CONCEAL)) {
|
|
if (!self->flushStatus) {
|
|
transportDec_SetParam(self->hInput, TPDEC_PARAM_RESET, 1);
|
|
self->streamInfo.numLostAccessUnits = 0;
|
|
self->streamInfo.numBadBytes = 0;
|
|
self->streamInfo.numTotalBytes = 0;
|
|
}
|
|
}
|
|
/* Reset the output delay field. The modules will add their figures one
|
|
* after another. */
|
|
self->streamInfo.outputDelay = 0;
|
|
|
|
if (self->limiterEnableUser == (UCHAR)-2) {
|
|
/* Enable limiter only for RSVD60. */
|
|
self->limiterEnableCurr = (self->flags[0] & AC_RSV603DA) ? 1 : 0;
|
|
} else if (self->limiterEnableUser == (UCHAR)-1) {
|
|
/* Enable limiter for all non-lowdelay AOT's. */
|
|
self->limiterEnableCurr = (self->flags[0] & (AC_LD | AC_ELD)) ? 0 : 1;
|
|
} else {
|
|
/* Use limiter configuration as requested. */
|
|
self->limiterEnableCurr = self->limiterEnableUser;
|
|
}
|
|
/* reset limiter gain on a per frame basis */
|
|
self->extGain[0] = FL2FXCONST_DBL(1.0f / (float)(1 << TDL_GAIN_SCALING));
|
|
|
|
pTimeDataFixpPcm = pTimeData;
|
|
timeDataFixpPcmSize = timeDataSize;
|
|
|
|
ErrorStatus = CAacDecoder_DecodeFrame(
|
|
self,
|
|
flags | (fTpConceal ? AACDEC_CONCEAL : 0) |
|
|
((self->flushStatus && !(flags & AACDEC_CONCEAL)) ? AACDEC_FLUSH
|
|
: 0),
|
|
pTimeDataFixpPcm + 0, timeDataFixpPcmSize,
|
|
self->streamInfo.aacSamplesPerFrame + 0);
|
|
|
|
/* if flushing for USAC DASH IPF was not possible go on with decoding
|
|
* preroll */
|
|
if ((self->flags[0] & AC_USAC) &&
|
|
(self->flushStatus == AACDEC_USAC_DASH_IPF_FLUSH_ON) &&
|
|
!(flags & AACDEC_CONCEAL) && (ErrorStatus != AAC_DEC_OK)) {
|
|
applyCrossfade = 0;
|
|
} else /* USAC DASH IPF flushing possible begin */
|
|
{
|
|
if (!((flags & (AACDEC_CONCEAL | AACDEC_FLUSH)) || fTpConceal ||
|
|
self->flushStatus) &&
|
|
(!(IS_OUTPUT_VALID(ErrorStatus)) || !(accessUnit < numPrerollAU))) {
|
|
TRANSPORTDEC_ERROR tpErr;
|
|
tpErr = transportDec_EndAccessUnit(self->hInput);
|
|
if (tpErr != TRANSPORTDEC_OK) {
|
|
self->frameOK = 0;
|
|
}
|
|
} else { /* while preroll processing later possibly an error in the
|
|
renderer part occurrs */
|
|
if (IS_OUTPUT_VALID(ErrorStatus)) {
|
|
fEndAuNotAdjusted = 1;
|
|
}
|
|
}
|
|
|
|
/* If the current pTimeDataFixpPcm does not contain a valid signal, there
|
|
* nothing else we can do, so bail. */
|
|
if (!IS_OUTPUT_VALID(ErrorStatus)) {
|
|
goto bail;
|
|
}
|
|
|
|
{
|
|
self->streamInfo.sampleRate = self->streamInfo.aacSampleRate;
|
|
self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame;
|
|
}
|
|
|
|
self->streamInfo.numChannels = self->streamInfo.aacNumChannels;
|
|
|
|
{
|
|
FDK_Delay_Apply(&self->usacResidualDelay,
|
|
pTimeDataFixpPcm +
|
|
1 * (self->streamInfo.aacSamplesPerFrame + 0) + 0,
|
|
self->streamInfo.frameSize, 0);
|
|
}
|
|
|
|
/* Setting of internal MPS state; may be reset in CAacDecoder_SyncQmfMode
|
|
if decoder is unable to decode with user defined qmfMode */
|
|
if (!(self->flags[0] & (AC_USAC | AC_RSVD50 | AC_RSV603DA | AC_ELD))) {
|
|
self->mpsEnableCurr =
|
|
(self->mpsEnableUser &&
|
|
isSupportedMpsConfig(self->streamInfo.aot,
|
|
self->streamInfo.numChannels,
|
|
(self->flags[0] & AC_MPS_PRESENT) ? 1 : 0));
|
|
}
|
|
|
|
if (!self->qmfDomain.globalConf.qmfDomainExplicitConfig &&
|
|
self->mpsEnableCurr) {
|
|
/* if not done yet, allocate full MPEG Surround decoder instance */
|
|
if (mpegSurroundDecoder_IsFullMpegSurroundDecoderInstanceAvailable(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder) ==
|
|
SAC_INSTANCE_NOT_FULL_AVAILABLE) {
|
|
if (mpegSurroundDecoder_Open(
|
|
(CMpegSurroundDecoder **)&self->pMpegSurroundDecoder, -1,
|
|
&self->qmfDomain)) {
|
|
return AAC_DEC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
CAacDecoder_SyncQmfMode(self);
|
|
|
|
if (!self->qmfDomain.globalConf.qmfDomainExplicitConfig &&
|
|
self->mpsEnableCurr) {
|
|
SAC_INPUT_CONFIG sac_interface = (self->sbrEnabled && self->hSbrDecoder)
|
|
? SAC_INTERFACE_QMF
|
|
: SAC_INTERFACE_TIME;
|
|
/* needs to be done before first SBR apply. */
|
|
mpegSurroundDecoder_ConfigureQmfDomain(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder, sac_interface,
|
|
(UINT)self->streamInfo.aacSampleRate, self->streamInfo.aot);
|
|
if (self->qmfDomain.globalConf.nBandsAnalysis_requested > 0) {
|
|
self->qmfDomain.globalConf.nQmfTimeSlots_requested =
|
|
self->streamInfo.aacSamplesPerFrame /
|
|
self->qmfDomain.globalConf.nBandsAnalysis_requested;
|
|
} else {
|
|
self->qmfDomain.globalConf.nQmfTimeSlots_requested = 0;
|
|
}
|
|
}
|
|
|
|
self->qmfDomain.globalConf.TDinput = pTimeData;
|
|
|
|
switch (FDK_QmfDomain_Configure(&self->qmfDomain)) {
|
|
default:
|
|
case QMF_DOMAIN_INIT_ERROR:
|
|
ErrorStatus = AAC_DEC_UNKNOWN;
|
|
goto bail;
|
|
case QMF_DOMAIN_OUT_OF_MEMORY:
|
|
ErrorStatus = AAC_DEC_OUT_OF_MEMORY;
|
|
goto bail;
|
|
case QMF_DOMAIN_OK:
|
|
break;
|
|
}
|
|
|
|
/* sbr decoder */
|
|
|
|
if ((ErrorStatus != AAC_DEC_OK) || (flags & AACDEC_CONCEAL) ||
|
|
self->pAacDecoderStaticChannelInfo[0]->concealmentInfo.concealState >
|
|
ConcealState_FadeIn) {
|
|
self->frameOK = 0; /* if an error has occured do concealment in the SBR
|
|
decoder too */
|
|
}
|
|
|
|
if (self->sbrEnabled && (!(self->flags[0] & AC_USAC_SCFGI3))) {
|
|
SBR_ERROR sbrError = SBRDEC_OK;
|
|
int chIdx, numCoreChannel = self->streamInfo.numChannels;
|
|
|
|
/* set params */
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_SYSTEM_BITSTREAM_DELAY,
|
|
self->sbrParams.bsDelay);
|
|
sbrDecoder_SetParam(
|
|
self->hSbrDecoder, SBR_FLUSH_DATA,
|
|
(flags & AACDEC_FLUSH) |
|
|
((self->flushStatus && !(flags & AACDEC_CONCEAL)) ? AACDEC_FLUSH
|
|
: 0));
|
|
|
|
if (self->streamInfo.aot == AOT_ER_AAC_ELD) {
|
|
/* Configure QMF */
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_LD_QMF_TIME_ALIGN,
|
|
(self->flags[0] & AC_MPS_PRESENT) ? 1 : 0);
|
|
}
|
|
|
|
{
|
|
PCMDMX_ERROR dmxErr;
|
|
INT maxOutCh = 0;
|
|
|
|
dmxErr = pcmDmx_GetParam(self->hPcmUtils,
|
|
MAX_NUMBER_OF_OUTPUT_CHANNELS, &maxOutCh);
|
|
if ((dmxErr == PCMDMX_OK) && (maxOutCh == 1)) {
|
|
/* Disable PS processing if we have to create a mono output signal.
|
|
*/
|
|
self->psPossible = 0;
|
|
}
|
|
}
|
|
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_SKIP_QMF,
|
|
(self->mpsEnableCurr) ? 2 : 0);
|
|
|
|
INT_PCM *input;
|
|
input = (INT_PCM *)self->workBufferCore2;
|
|
FDKmemcpy(input, pTimeData,
|
|
sizeof(INT_PCM) * (self->streamInfo.numChannels) *
|
|
(self->streamInfo.frameSize));
|
|
|
|
/* apply SBR processing */
|
|
sbrError = sbrDecoder_Apply(self->hSbrDecoder, input, pTimeData,
|
|
timeDataSize, &self->streamInfo.numChannels,
|
|
&self->streamInfo.sampleRate,
|
|
&self->mapDescr, self->chMapIndex,
|
|
self->frameOK, &self->psPossible);
|
|
|
|
if (sbrError == SBRDEC_OK) {
|
|
/* Update data in streaminfo structure. Assume that the SBR upsampling
|
|
factor is either 1, 2, 8/3 or 4. Maximum upsampling factor is 4
|
|
(CELP+SBR or USAC 4:1 SBR) */
|
|
self->flags[0] |= AC_SBR_PRESENT;
|
|
if (self->streamInfo.aacSampleRate != self->streamInfo.sampleRate) {
|
|
if (self->streamInfo.aacSampleRate >> 2 ==
|
|
self->streamInfo.sampleRate) {
|
|
self->streamInfo.frameSize =
|
|
self->streamInfo.aacSamplesPerFrame >> 2;
|
|
self->streamInfo.outputDelay = self->streamInfo.outputDelay >> 2;
|
|
} else if (self->streamInfo.aacSampleRate >> 1 ==
|
|
self->streamInfo.sampleRate) {
|
|
self->streamInfo.frameSize =
|
|
self->streamInfo.aacSamplesPerFrame >> 1;
|
|
self->streamInfo.outputDelay = self->streamInfo.outputDelay >> 1;
|
|
} else if (self->streamInfo.aacSampleRate << 1 ==
|
|
self->streamInfo.sampleRate) {
|
|
self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame
|
|
<< 1;
|
|
self->streamInfo.outputDelay = self->streamInfo.outputDelay << 1;
|
|
} else if (self->streamInfo.aacSampleRate << 2 ==
|
|
self->streamInfo.sampleRate) {
|
|
self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame
|
|
<< 2;
|
|
self->streamInfo.outputDelay = self->streamInfo.outputDelay << 2;
|
|
} else if (self->streamInfo.frameSize == 768) {
|
|
self->streamInfo.frameSize =
|
|
(self->streamInfo.aacSamplesPerFrame << 3) / 3;
|
|
self->streamInfo.outputDelay =
|
|
(self->streamInfo.outputDelay << 3) / 3;
|
|
} else {
|
|
ErrorStatus = AAC_DEC_SET_PARAM_FAIL;
|
|
goto bail;
|
|
}
|
|
} else {
|
|
self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame;
|
|
}
|
|
self->streamInfo.outputDelay +=
|
|
sbrDecoder_GetDelay(self->hSbrDecoder);
|
|
|
|
if (self->psPossible) {
|
|
self->flags[0] |= AC_PS_PRESENT;
|
|
}
|
|
for (chIdx = numCoreChannel; chIdx < self->streamInfo.numChannels;
|
|
chIdx += 1) {
|
|
self->channelType[chIdx] = ACT_FRONT;
|
|
self->channelIndices[chIdx] = chIdx;
|
|
}
|
|
}
|
|
if (sbrError == SBRDEC_OUTPUT_BUFFER_TOO_SMALL) {
|
|
ErrorStatus = AAC_DEC_OUTPUT_BUFFER_TOO_SMALL;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (self->mpsEnableCurr) {
|
|
int err, sac_interface, nChannels, frameSize;
|
|
|
|
nChannels = self->streamInfo.numChannels;
|
|
frameSize = self->streamInfo.frameSize;
|
|
sac_interface = SAC_INTERFACE_TIME;
|
|
|
|
if (self->sbrEnabled && self->hSbrDecoder)
|
|
sac_interface = SAC_INTERFACE_QMF;
|
|
if (self->streamInfo.aot == AOT_USAC) {
|
|
if (self->flags[0] & AC_USAC_SCFGI3) {
|
|
sac_interface = SAC_INTERFACE_TIME;
|
|
}
|
|
}
|
|
err = mpegSurroundDecoder_SetParam(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder,
|
|
SACDEC_INTERFACE, sac_interface);
|
|
|
|
if (err == 0) {
|
|
err = mpegSurroundDecoder_Apply(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder,
|
|
(INT_PCM *)self->workBufferCore2, pTimeData, timeDataSize,
|
|
self->streamInfo.aacSamplesPerFrame, &nChannels, &frameSize,
|
|
self->streamInfo.sampleRate, self->streamInfo.aot,
|
|
self->channelType, self->channelIndices, &self->mapDescr);
|
|
}
|
|
|
|
if (err == MPS_OUTPUT_BUFFER_TOO_SMALL) {
|
|
ErrorStatus = AAC_DEC_OUTPUT_BUFFER_TOO_SMALL;
|
|
goto bail;
|
|
}
|
|
if (err == 0) {
|
|
/* Update output parameter */
|
|
self->streamInfo.numChannels = nChannels;
|
|
self->streamInfo.frameSize = frameSize;
|
|
self->streamInfo.outputDelay += mpegSurroundDecoder_GetDelay(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder);
|
|
/* Save current parameter for possible concealment of next frame */
|
|
self->mpsOutChannelsLast = nChannels;
|
|
self->mpsFrameSizeLast = frameSize;
|
|
} else if ((self->mpsOutChannelsLast > 0) &&
|
|
(self->mpsFrameSizeLast > 0)) {
|
|
/* Restore parameters of last frame ... */
|
|
self->streamInfo.numChannels = self->mpsOutChannelsLast;
|
|
self->streamInfo.frameSize = self->mpsFrameSizeLast;
|
|
/* ... and clear output buffer so that potentially corrupted data does
|
|
* not reach the framework. */
|
|
FDKmemclear(pTimeData, self->mpsOutChannelsLast *
|
|
self->mpsFrameSizeLast * sizeof(INT_PCM));
|
|
/* Additionally proclaim that this frame had errors during decoding.
|
|
*/
|
|
ErrorStatus = AAC_DEC_DECODE_FRAME_ERROR;
|
|
} else {
|
|
ErrorStatus = AAC_DEC_UNKNOWN; /* no output */
|
|
}
|
|
}
|
|
|
|
/* SBR decoder for Unified Stereo Config (stereoConfigIndex == 3) */
|
|
|
|
if (self->sbrEnabled && (self->flags[0] & AC_USAC_SCFGI3)) {
|
|
SBR_ERROR sbrError = SBRDEC_OK;
|
|
|
|
/* set params */
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_SYSTEM_BITSTREAM_DELAY,
|
|
self->sbrParams.bsDelay);
|
|
|
|
sbrDecoder_SetParam(self->hSbrDecoder, SBR_SKIP_QMF, 1);
|
|
|
|
/* apply SBR processing */
|
|
sbrError = sbrDecoder_Apply(self->hSbrDecoder, pTimeData, pTimeData,
|
|
timeDataSize, &self->streamInfo.numChannels,
|
|
&self->streamInfo.sampleRate,
|
|
&self->mapDescr, self->chMapIndex,
|
|
self->frameOK, &self->psPossible);
|
|
|
|
if (sbrError == SBRDEC_OK) {
|
|
/* Update data in streaminfo structure. Assume that the SBR upsampling
|
|
* factor is either 1,2 or 4 */
|
|
self->flags[0] |= AC_SBR_PRESENT;
|
|
if (self->streamInfo.aacSampleRate != self->streamInfo.sampleRate) {
|
|
if (self->streamInfo.frameSize == 768) {
|
|
self->streamInfo.frameSize =
|
|
(self->streamInfo.aacSamplesPerFrame * 8) / 3;
|
|
} else if (self->streamInfo.aacSampleRate << 2 ==
|
|
self->streamInfo.sampleRate) {
|
|
self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame
|
|
<< 2;
|
|
} else {
|
|
self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame
|
|
<< 1;
|
|
}
|
|
}
|
|
|
|
self->flags[0] &= ~AC_PS_PRESENT;
|
|
}
|
|
if (sbrError == SBRDEC_OUTPUT_BUFFER_TOO_SMALL) {
|
|
ErrorStatus = AAC_DEC_OUTPUT_BUFFER_TOO_SMALL;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
/* Use dedicated memory for PCM postprocessing */
|
|
pTimeDataPcmPost = self->pTimeData2;
|
|
timeDataPcmPostSize = self->timeData2Size;
|
|
|
|
{
|
|
const int size =
|
|
self->streamInfo.frameSize * self->streamInfo.numChannels;
|
|
FDK_ASSERT(timeDataPcmPostSize >= size);
|
|
for (int i = 0; i < size; i++) {
|
|
pTimeDataPcmPost[i] =
|
|
(PCM_DEC)FX_PCM2PCM_DEC(pTimeData[i]) >> PCM_OUT_HEADROOM;
|
|
}
|
|
}
|
|
|
|
{
|
|
if ((FDK_drcDec_GetParam(self->hUniDrcDecoder, DRC_DEC_IS_ACTIVE)) &&
|
|
!(self->flags[0] & AC_RSV603DA)) {
|
|
/* Apply DRC gains*/
|
|
int ch, drcDelay = 0;
|
|
int needsDeinterleaving = 0;
|
|
FIXP_DBL *drcWorkBuffer = NULL;
|
|
FIXP_DBL channelGain[(8)];
|
|
int reverseInChannelMap[(8)];
|
|
int reverseOutChannelMap[(8)];
|
|
int numDrcOutChannels = FDK_drcDec_GetParam(
|
|
self->hUniDrcDecoder, DRC_DEC_TARGET_CHANNEL_COUNT_SELECTED);
|
|
FDKmemclear(channelGain, sizeof(channelGain));
|
|
for (ch = 0; ch < (8); ch++) {
|
|
reverseInChannelMap[ch] = ch;
|
|
reverseOutChannelMap[ch] = ch;
|
|
}
|
|
|
|
/* Update sampleRate and frameSize. This may be necessary in case of
|
|
* implicit SBR signaling */
|
|
FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_SAMPLE_RATE,
|
|
self->streamInfo.sampleRate);
|
|
FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_FRAME_SIZE,
|
|
self->streamInfo.frameSize);
|
|
|
|
/* If SBR and/or MPS is active, the DRC gains are aligned to the QMF
|
|
domain signal before the QMF synthesis. Therefore the DRC gains
|
|
need to be delayed by the QMF synthesis delay. */
|
|
if (self->sbrEnabled) drcDelay = 257;
|
|
if (self->mpsEnableCurr) drcDelay = 257;
|
|
/* Take into account concealment delay */
|
|
drcDelay += CConcealment_GetDelay(&self->concealCommonData) *
|
|
self->streamInfo.frameSize;
|
|
|
|
for (ch = 0; ch < self->streamInfo.numChannels; ch++) {
|
|
UCHAR mapValue = FDK_chMapDescr_getMapValue(
|
|
&self->mapDescr, (UCHAR)ch, self->chMapIndex);
|
|
if (mapValue < (8)) reverseInChannelMap[mapValue] = ch;
|
|
}
|
|
for (ch = 0; ch < (int)numDrcOutChannels; ch++) {
|
|
UCHAR mapValue = FDK_chMapDescr_getMapValue(
|
|
&self->mapDescr, (UCHAR)ch, numDrcOutChannels);
|
|
if (mapValue < (8)) reverseOutChannelMap[mapValue] = ch;
|
|
}
|
|
|
|
/* The output of SBR and MPS is interleaved. Deinterleaving may be
|
|
* necessary for FDK_drcDec_ProcessTime, which accepts deinterleaved
|
|
* audio only. */
|
|
if ((self->streamInfo.numChannels > 1) &&
|
|
(0 || (self->sbrEnabled) || (self->mpsEnableCurr))) {
|
|
/* interleaving/deinterleaving is performed on upper part of
|
|
* pTimeDataPcmPost. Check if this buffer is large enough. */
|
|
if (timeDataPcmPostSize <
|
|
(INT)(2 * self->streamInfo.numChannels *
|
|
self->streamInfo.frameSize * sizeof(PCM_DEC))) {
|
|
ErrorStatus = AAC_DEC_UNKNOWN;
|
|
goto bail;
|
|
}
|
|
needsDeinterleaving = 1;
|
|
drcWorkBuffer =
|
|
(FIXP_DBL *)pTimeDataPcmPost +
|
|
self->streamInfo.numChannels * self->streamInfo.frameSize;
|
|
FDK_deinterleave(
|
|
pTimeDataPcmPost, drcWorkBuffer, self->streamInfo.numChannels,
|
|
self->streamInfo.frameSize, self->streamInfo.frameSize);
|
|
} else {
|
|
drcWorkBuffer = (FIXP_DBL *)pTimeDataPcmPost;
|
|
}
|
|
|
|
/* prepare Loudness Normalisation gain */
|
|
FDK_drcDec_SetParam(self->hUniDrcDecoder, DRC_DEC_TARGET_LOUDNESS,
|
|
(INT)-self->defaultTargetLoudness *
|
|
FL2FXCONST_DBL(1.0f / (float)(1 << 9)));
|
|
FDK_drcDec_SetChannelGains(self->hUniDrcDecoder,
|
|
self->streamInfo.numChannels,
|
|
self->streamInfo.frameSize, channelGain,
|
|
drcWorkBuffer, self->streamInfo.frameSize);
|
|
FDK_drcDec_Preprocess(self->hUniDrcDecoder);
|
|
|
|
/* apply DRC1 gain sequence */
|
|
for (ch = 0; ch < self->streamInfo.numChannels; ch++) {
|
|
FDK_drcDec_ProcessTime(self->hUniDrcDecoder, drcDelay, DRC_DEC_DRC1,
|
|
ch, reverseInChannelMap[ch] - ch, 1,
|
|
drcWorkBuffer, self->streamInfo.frameSize);
|
|
}
|
|
/* apply downmix */
|
|
FDK_drcDec_ApplyDownmix(
|
|
self->hUniDrcDecoder, reverseInChannelMap, reverseOutChannelMap,
|
|
drcWorkBuffer,
|
|
&self->streamInfo.numChannels); /* self->streamInfo.numChannels
|
|
may change here */
|
|
/* apply DRC2/3 gain sequence */
|
|
for (ch = 0; ch < self->streamInfo.numChannels; ch++) {
|
|
FDK_drcDec_ProcessTime(self->hUniDrcDecoder, drcDelay,
|
|
DRC_DEC_DRC2_DRC3, ch,
|
|
reverseOutChannelMap[ch] - ch, 1,
|
|
drcWorkBuffer, self->streamInfo.frameSize);
|
|
}
|
|
|
|
if (needsDeinterleaving) {
|
|
FDK_interleave(
|
|
drcWorkBuffer, pTimeDataPcmPost, self->streamInfo.numChannels,
|
|
self->streamInfo.frameSize, self->streamInfo.frameSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (self->streamInfo.extAot != AOT_AAC_SLS) {
|
|
INT pcmLimiterScale = 0;
|
|
PCMDMX_ERROR dmxErr = PCMDMX_OK;
|
|
if ((flags & AACDEC_INTR) && (accessUnit == 0)) {
|
|
/* delete data from the past (e.g. mixdown coeficients) */
|
|
pcmDmx_Reset(self->hPcmUtils, PCMDMX_RESET_BS_DATA);
|
|
}
|
|
if (flags & (AACDEC_CLRHIST)) {
|
|
if (!(self->flags[0] & AC_USAC)) {
|
|
/* delete data from the past (e.g. mixdown coeficients) */
|
|
pcmDmx_Reset(self->hPcmUtils, PCMDMX_RESET_BS_DATA);
|
|
}
|
|
}
|
|
|
|
INT interleaved = 0;
|
|
interleaved |= (self->sbrEnabled) ? 1 : 0;
|
|
interleaved |= (self->mpsEnableCurr) ? 1 : 0;
|
|
|
|
/* do PCM post processing */
|
|
dmxErr = pcmDmx_ApplyFrame(
|
|
self->hPcmUtils, pTimeDataPcmPost, timeDataFixpPcmSize,
|
|
self->streamInfo.frameSize, &self->streamInfo.numChannels,
|
|
interleaved, self->channelType, self->channelIndices,
|
|
&self->mapDescr,
|
|
(self->limiterEnableCurr) ? &pcmLimiterScale : NULL);
|
|
if (dmxErr == PCMDMX_OUTPUT_BUFFER_TOO_SMALL) {
|
|
ErrorStatus = AAC_DEC_OUTPUT_BUFFER_TOO_SMALL;
|
|
goto bail;
|
|
}
|
|
if ((ErrorStatus == AAC_DEC_OK) && (dmxErr == PCMDMX_INVALID_MODE)) {
|
|
/* Announce the framework that the current combination of channel
|
|
* configuration and downmix settings are not know to produce a
|
|
* predictable behavior and thus maybe produce strange output. */
|
|
ErrorStatus = AAC_DEC_DECODE_FRAME_ERROR;
|
|
}
|
|
|
|
if (flags & AACDEC_CLRHIST) {
|
|
if (!(self->flags[0] & AC_USAC)) {
|
|
/* Delete the delayed signal. */
|
|
pcmLimiter_Reset(self->hLimiter);
|
|
}
|
|
}
|
|
|
|
if (self->limiterEnableCurr) {
|
|
/* use workBufferCore2 buffer for interleaving */
|
|
PCM_LIM *pInterleaveBuffer;
|
|
int blockLength = self->streamInfo.frameSize;
|
|
|
|
/* Set actual signal parameters */
|
|
pcmLimiter_SetNChannels(self->hLimiter, self->streamInfo.numChannels);
|
|
pcmLimiter_SetSampleRate(self->hLimiter, self->streamInfo.sampleRate);
|
|
pcmLimiterScale += PCM_OUT_HEADROOM;
|
|
|
|
if ((self->streamInfo.numChannels == 1) || (self->sbrEnabled) ||
|
|
(self->mpsEnableCurr)) {
|
|
pInterleaveBuffer = (PCM_LIM *)pTimeDataPcmPost;
|
|
} else {
|
|
pInterleaveBuffer = (PCM_LIM *)pTimeData;
|
|
/* applyLimiter requests for interleaved data */
|
|
/* Interleave ouput buffer */
|
|
FDK_interleave(pTimeDataPcmPost, pInterleaveBuffer,
|
|
self->streamInfo.numChannels, blockLength,
|
|
self->streamInfo.frameSize);
|
|
}
|
|
|
|
pcmLimiter_Apply(self->hLimiter, pInterleaveBuffer, pTimeData,
|
|
self->extGain, &pcmLimiterScale, 1,
|
|
self->extGainDelay, self->streamInfo.frameSize);
|
|
|
|
{
|
|
/* Announce the additional limiter output delay */
|
|
self->streamInfo.outputDelay += pcmLimiter_GetDelay(self->hLimiter);
|
|
}
|
|
} else {
|
|
/* If numChannels = 1 we do not need interleaving. The same applies if
|
|
SBR or MPS are used, since their output is interleaved already
|
|
(resampled or not) */
|
|
if ((self->streamInfo.numChannels == 1) || (self->sbrEnabled) ||
|
|
(self->mpsEnableCurr)) {
|
|
scaleValuesSaturate(
|
|
pTimeData, pTimeDataPcmPost,
|
|
self->streamInfo.frameSize * self->streamInfo.numChannels,
|
|
PCM_OUT_HEADROOM);
|
|
|
|
} else {
|
|
scaleValuesSaturate(
|
|
(INT_PCM *)self->workBufferCore2, pTimeDataPcmPost,
|
|
self->streamInfo.frameSize * self->streamInfo.numChannels,
|
|
PCM_OUT_HEADROOM);
|
|
/* Interleave ouput buffer */
|
|
FDK_interleave((INT_PCM *)self->workBufferCore2, pTimeData,
|
|
self->streamInfo.numChannels,
|
|
self->streamInfo.frameSize,
|
|
self->streamInfo.frameSize);
|
|
}
|
|
}
|
|
} /* if (self->streamInfo.extAot != AOT_AAC_SLS)*/
|
|
|
|
if (self->flags[0] & AC_USAC) {
|
|
if (self->flushStatus == AACDEC_USAC_DASH_IPF_FLUSH_ON &&
|
|
!(flags & AACDEC_CONCEAL)) {
|
|
CAacDecoder_PrepareCrossFade(pTimeData, self->pTimeDataFlush,
|
|
self->streamInfo.numChannels,
|
|
self->streamInfo.frameSize, 1);
|
|
}
|
|
|
|
/* prepare crossfade buffer for fade in */
|
|
if (!applyCrossfade && self->applyCrossfade &&
|
|
!(flags & AACDEC_CONCEAL)) {
|
|
for (int ch = 0; ch < self->streamInfo.numChannels; ch++) {
|
|
for (int i = 0; i < TIME_DATA_FLUSH_SIZE; i++) {
|
|
self->pTimeDataFlush[ch][i] = 0;
|
|
}
|
|
}
|
|
applyCrossfade = 1;
|
|
}
|
|
|
|
if (applyCrossfade && self->applyCrossfade &&
|
|
!(accessUnit < numPrerollAU) &&
|
|
(self->buildUpStatus == AACDEC_USAC_BUILD_UP_ON)) {
|
|
CAacDecoder_ApplyCrossFade(pTimeData, self->pTimeDataFlush,
|
|
self->streamInfo.numChannels,
|
|
self->streamInfo.frameSize, 1);
|
|
self->applyCrossfade = 0;
|
|
}
|
|
}
|
|
|
|
/* Signal interruption to take effect in next frame. */
|
|
if ((flags & AACDEC_FLUSH || self->flushStatus) &&
|
|
!(flags & AACDEC_CONCEAL)) {
|
|
aacDecoder_SignalInterruption(self);
|
|
}
|
|
|
|
/* Update externally visible copy of flags */
|
|
self->streamInfo.flags = self->flags[0];
|
|
|
|
} /* USAC DASH IPF flushing possible end */
|
|
if (accessUnit < numPrerollAU) {
|
|
FDKpushBack(hBsAu, auStartAnchor - (INT)FDKgetValidBits(hBsAu));
|
|
} else {
|
|
if ((self->buildUpStatus == AACDEC_RSV60_BUILD_UP_ON) ||
|
|
(self->buildUpStatus == AACDEC_RSV60_BUILD_UP_ON_IN_BAND) ||
|
|
(self->buildUpStatus == AACDEC_USAC_BUILD_UP_ON)) {
|
|
self->buildUpCnt--;
|
|
|
|
if (self->buildUpCnt < 0) {
|
|
self->buildUpStatus = 0;
|
|
}
|
|
}
|
|
|
|
if (self->flags[0] & AC_USAC) {
|
|
if (self->flushStatus == AACDEC_USAC_DASH_IPF_FLUSH_ON &&
|
|
!(flags & AACDEC_CONCEAL)) {
|
|
self->streamInfo.frameSize = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (self->flushStatus != AACDEC_USAC_DASH_IPF_FLUSH_ON) {
|
|
accessUnit++;
|
|
}
|
|
} while ((accessUnit < numAccessUnits) ||
|
|
((self->flushStatus == AACDEC_USAC_DASH_IPF_FLUSH_ON) &&
|
|
!(flags & AACDEC_CONCEAL)));
|
|
|
|
bail:
|
|
|
|
/* error in renderer part occurred, ErrorStatus was set to invalid output */
|
|
if (fEndAuNotAdjusted && !IS_OUTPUT_VALID(ErrorStatus) &&
|
|
(accessUnit < numPrerollAU)) {
|
|
transportDec_EndAccessUnit(self->hInput);
|
|
}
|
|
|
|
/* Update Statistics */
|
|
aacDecoder_UpdateBitStreamCounters(&self->streamInfo, hBs, nBits,
|
|
ErrorStatus);
|
|
if (((self->streamInfo.numChannels <= 0) ||
|
|
(self->streamInfo.frameSize <= 0) ||
|
|
(self->streamInfo.sampleRate <= 0)) &&
|
|
IS_OUTPUT_VALID(ErrorStatus)) {
|
|
/* Ensure consistency of IS_OUTPUT_VALID() macro. */
|
|
ErrorStatus = AAC_DEC_UNKNOWN;
|
|
}
|
|
|
|
/* Check whether external output buffer is large enough. */
|
|
if (timeDataSize_extern <
|
|
self->streamInfo.numChannels * self->streamInfo.frameSize) {
|
|
ErrorStatus = AAC_DEC_OUTPUT_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Update external output buffer. */
|
|
if (IS_OUTPUT_VALID(ErrorStatus)) {
|
|
FDKmemcpy(pTimeData_extern, pTimeData,
|
|
self->streamInfo.numChannels * self->streamInfo.frameSize *
|
|
sizeof(*pTimeData));
|
|
} else {
|
|
FDKmemclear(pTimeData_extern,
|
|
timeDataSize_extern * sizeof(*pTimeData_extern));
|
|
}
|
|
|
|
return ErrorStatus;
|
|
}
|
|
|
|
LINKSPEC_CPP void aacDecoder_Close(HANDLE_AACDECODER self) {
|
|
if (self == NULL) return;
|
|
|
|
if (self->hLimiter != NULL) {
|
|
pcmLimiter_Destroy(self->hLimiter);
|
|
}
|
|
|
|
if (self->hPcmUtils != NULL) {
|
|
pcmDmx_Close(&self->hPcmUtils);
|
|
}
|
|
|
|
FDK_drcDec_Close(&self->hUniDrcDecoder);
|
|
|
|
if (self->pMpegSurroundDecoder != NULL) {
|
|
mpegSurroundDecoder_Close(
|
|
(CMpegSurroundDecoder *)self->pMpegSurroundDecoder);
|
|
}
|
|
|
|
if (self->hSbrDecoder != NULL) {
|
|
sbrDecoder_Close(&self->hSbrDecoder);
|
|
}
|
|
|
|
if (self->hInput != NULL) {
|
|
transportDec_Close(&self->hInput);
|
|
}
|
|
|
|
CAacDecoder_Close(self);
|
|
}
|
|
|
|
LINKSPEC_CPP CStreamInfo *aacDecoder_GetStreamInfo(HANDLE_AACDECODER self) {
|
|
return CAacDecoder_GetStreamInfo(self);
|
|
}
|
|
|
|
LINKSPEC_CPP INT aacDecoder_GetLibInfo(LIB_INFO *info) {
|
|
int i;
|
|
|
|
if (info == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
sbrDecoder_GetLibInfo(info);
|
|
mpegSurroundDecoder_GetLibInfo(info);
|
|
transportDec_GetLibInfo(info);
|
|
FDK_toolsGetLibInfo(info);
|
|
pcmDmx_GetLibInfo(info);
|
|
pcmLimiter_GetLibInfo(info);
|
|
FDK_drcDec_GetLibInfo(info);
|
|
|
|
/* search for next free tab */
|
|
for (i = 0; i < FDK_MODULE_LAST; i++) {
|
|
if (info[i].module_id == FDK_NONE) break;
|
|
}
|
|
if (i == FDK_MODULE_LAST) {
|
|
return -1;
|
|
}
|
|
info += i;
|
|
|
|
info->module_id = FDK_AACDEC;
|
|
/* build own library info */
|
|
info->version =
|
|
LIB_VERSION(AACDECODER_LIB_VL0, AACDECODER_LIB_VL1, AACDECODER_LIB_VL2);
|
|
LIB_VERSION_STRING(info);
|
|
info->build_date = AACDECODER_LIB_BUILD_DATE;
|
|
info->build_time = AACDECODER_LIB_BUILD_TIME;
|
|
info->title = AACDECODER_LIB_TITLE;
|
|
|
|
/* Set flags */
|
|
info->flags = 0 | CAPF_AAC_LC | CAPF_ER_AAC_LC | CAPF_ER_AAC_SCAL |
|
|
CAPF_AAC_VCB11 | CAPF_AAC_HCR | CAPF_AAC_RVLC | CAPF_ER_AAC_LD |
|
|
CAPF_ER_AAC_ELD | CAPF_AAC_CONCEALMENT | CAPF_AAC_DRC |
|
|
CAPF_AAC_MPEG4 | CAPF_AAC_DRM_BSFORMAT | CAPF_AAC_1024 |
|
|
CAPF_AAC_960 | CAPF_AAC_512 | CAPF_AAC_480 |
|
|
CAPF_AAC_ELD_DOWNSCALE
|
|
|
|
| CAPF_AAC_USAC | CAPF_ER_AAC_ELDV2 | CAPF_AAC_UNIDRC;
|
|
/* End of flags */
|
|
|
|
return 0;
|
|
}
|