/***************************** MPEG-4 AAC Decoder ************************** (C) Copyright Fraunhofer IIS 1999-2008 All Rights Reserved Please be advised that this software and/or program delivery is Confidential Information of Fraunhofer and subject to and covered by the Fraunhofer IIS Software Evaluation Agreement between Google Inc. and Fraunhofer effective and in full force since March 1, 2012. You may use this software and/or program only under the terms and conditions described in the above mentioned Fraunhofer IIS Software Evaluation Agreement. Any other and/or further use requires a separate agreement. $Id$ Author(s): Manuel Jander Description: This software and/or program is protected by copyright law and international treaties. Any reproduction or distribution of this software and/or program, or any portion of it, may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under law. ******************************************************************************/ #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" /* Decoder library info */ #define AACDECODER_LIB_VL0 2 #define AACDECODER_LIB_VL1 4 #define AACDECODER_LIB_VL2 0 #define AACDECODER_LIB_TITLE "AAC Decoder Lib" #define AACDECODER_LIB_BUILD_DATE __DATE__ #define AACDECODER_LIB_BUILD_TIME __TIME__ static AAC_DECODER_ERROR setConcealMethod ( const HANDLE_AACDECODER self, const INT method ); 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) { AAC_DECODER_ERROR err; /* Initialize AAC core decoder, and update self->streaminfo */ err = CAacDecoder_Init(self, pAscStruct); 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; } break; } } } return err; } static INT aacDecoder_ConfigCallback(void *handle, const CSAudioSpecificConfig *pAscStruct) { HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle; AAC_DECODER_ERROR err = AAC_DEC_OK; TRANSPORTDEC_ERROR errTp; { { err = aacDecoder_Config(self, pAscStruct); } } if (err == AAC_DEC_OK) { if ( self->flags & (AC_USAC|AC_RSVD50|AC_LD|AC_ELD) && CConcealment_GetDelay(&self->concealCommonData) > 0 ) { /* Revert to error concealment method Noise Substitution. Because interpolation is not implemented for USAC/RSVD50 or the additional delay is unwanted for low delay codecs. */ setConcealMethod(self, 1); #ifdef DEBUG FDKprintf(" Concealment method was reverted to 1 !\n"); #endif } errTp = TRANSPORTDEC_OK; } else { if (IS_INIT_ERROR(err)) { errTp = TRANSPORTDEC_UNSUPPORTED_FORMAT; } /* Fatal errors */ else if (err == AAC_DEC_NEED_TO_RESTART) { errTp = TRANSPORTDEC_NEED_TO_RESTART; } else { errTp = TRANSPORTDEC_UNKOWN_ERROR; } } return 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); } 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; HANDLE_SBRDECODER hSbrDec = NULL; HANDLE_AAC_DRC hDrcInfo = NULL; HANDLE_PCM_DOWNMIX hPcmDmx = NULL; CConcealmentMethod backupMethod; int backupDelay = 0; int bsDelay = 0; /* check decoder handle */ if (self != NULL) { pConcealData = &self->concealCommonData; hSbrDec = self->hSbrDecoder; hDrcInfo = self->hDrcInfo; hPcmDmx = self->hPcmUtils; } /* 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, (int)method, // 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; 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; CConcealParams *pConcealData = NULL; HANDLE_AAC_DRC hDrcInfo = NULL; /* check decoder handle */ if (self != NULL) { pConcealData = &self->concealCommonData; hDrcInfo = self->hDrcInfo; } /* configure the subsystems */ switch (param) { case AAC_PCM_OUTPUT_INTERLEAVED: if (value < 0 || value > 1) { return AAC_DEC_SET_PARAM_FAIL; } if (self == NULL) { return AAC_DEC_INVALID_HANDLE; } self->outputInterleaved = value; break; case AAC_PCM_OUTPUT_CHANNELS: { PCMDMX_ERROR err; err = pcmDmx_SetParam ( self->hPcmUtils, NUMBER_OF_OUTPUT_CHANNELS, value ); switch (err) { case PCMDMX_OK: break; case PCMDMX_INVALID_HANDLE: return AAC_DEC_INVALID_HANDLE; default: return AAC_DEC_SET_PARAM_FAIL; } } break; case AAC_PCM_DUAL_CHANNEL_OUTPUT_MODE: { PCMDMX_ERROR err; err = pcmDmx_SetParam ( self->hPcmUtils, DUAL_CHANNEL_DOWNMIX_MODE, value ); switch (err) { case PCMDMX_OK: break; case PCMDMX_INVALID_HANDLE: return AAC_DEC_INVALID_HANDLE; default: return AAC_DEC_SET_PARAM_FAIL; } } break; case AAC_PCM_OUTPUT_CHANNEL_MAPPING: switch (value) { case 0: self->channelOutputMapping = channelMappingTablePassthrough; break; case 1: self->channelOutputMapping = channelMappingTableWAV; break; default: errorStatus = AAC_DEC_SET_PARAM_FAIL; break; } break; case AAC_QMF_LOWPOWER: if (self == NULL) { return AAC_DEC_INVALID_HANDLE; } /** * 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 ); 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 ); break; case AAC_DRC_REFERENCE_LEVEL: /* DRC reference level quantized in 0.25dB steps using values [0..127] it is '-' for analog scaling */ errorStatus = aacDecoder_drcSetParam ( hDrcInfo, TARGET_REF_LEVEL, 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_TPDEC_CLEAR_BUFFER: transportDec_SetParam(self->hInput, 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 ); break; default: return AAC_DEC_SET_PARAM_FAIL; } /* switch(param) */ return (errorStatus); } LINKSPEC_CPP HANDLE_AACDECODER aacDecoder_Open(TRANSPORT_TYPE transportFmt, UINT nrOfLayers) { AAC_DECODER_INSTANCE *aacDec = NULL; HANDLE_TRANSPORTDEC pIn; int err = 0; /* Allocate transport layer struct. */ pIn = transportDec_Open(transportFmt, TP_FLAG_MPEG4); 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; aacDec->channelOutputMapping = channelMappingTableWAV; /* Register Config Update callback. */ transportDec_RegisterAscCallback(pIn, aacDecoder_ConfigCallback, (void*)aacDec); /* open SBR decoder */ if ( SBRDEC_OK != sbrDecoder_Open ( &aacDec->hSbrDecoder )) { err = -1; goto bail; } aacDec->qmfModeUser = NOT_DEFINED; transportDec_RegisterSbrCallback(aacDec->hInput, (cbSbr_t)sbrDecoder_Header, (void*)aacDec->hSbrDecoder); pcmDmx_Open( &aacDec->hPcmUtils ); if (aacDec->hPcmUtils == NULL) { err = -1; goto bail; } /* 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, 0); } } 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 - 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) { pSi->bitRate = (nBits * pSi->sampleRate)/pSi->frameSize; } /* 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, const INT timeDataSize, const UINT flags) { AAC_DECODER_ERROR ErrorStatus; INT layer; INT nBits; INT interleaved = self->outputInterleaved; HANDLE_FDK_BITSTREAM hBs; int fTpInterruption = 0; /* Transport originated interruption detection. */ int fTpConceal = 0; /* Transport originated concealment. */ if (self == NULL) { return AAC_DEC_INVALID_HANDLE; } 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_CONCEAL | AACDEC_FLUSH) ) ) { 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; default: ErrorStatus = AAC_DEC_UNKNOWN; goto bail; } } } } else { if (self->streamInfo.numLostAccessUnits > 0) { self->streamInfo.numLostAccessUnits--; } } /* Signal bit stream interruption to other modules if required. */ if ( fTpInterruption || (flags & (AACDEC_INTR|AACDEC_CLRHIST)) ) { aacDecoder_SignalInterruption(self); if ( ! (flags & AACDEC_INTR) ) { ErrorStatus = AAC_DEC_TRANSPORT_SYNC_ERROR; goto bail; } } /* Empty bit buffer in case of flush request. */ if (flags & AACDEC_FLUSH) { transportDec_SetParam(self->hInput, TPDEC_PARAM_RESET, 1); self->streamInfo.numLostAccessUnits = 0; self->streamInfo.numBadBytes = 0; self->streamInfo.numTotalBytes = 0; } ErrorStatus = CAacDecoder_DecodeFrame(self, flags | (fTpConceal ? AACDEC_CONCEAL : 0), pTimeData, timeDataSize, interleaved); if (!(flags & (AACDEC_CONCEAL|AACDEC_FLUSH))) { TRANSPORTDEC_ERROR tpErr; tpErr = transportDec_EndAccessUnit(self->hInput); if (tpErr != TRANSPORTDEC_OK) { self->frameOK = 0; } } /* If the current pTimeData does not contain a valid signal, there nothing else we can do, so bail. */ if ( ! IS_OUTPUT_VALID(ErrorStatus) ) { goto bail; } { /* Export data into streaminfo structure */ self->streamInfo.sampleRate = self->streamInfo.aacSampleRate; self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame; self->streamInfo.numChannels = self->aacChannels; } CAacDecoder_SyncQmfMode(self); /* sbr decoder */ if (ErrorStatus || (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) { SBR_ERROR sbrError = SBRDEC_OK; /* set params */ sbrDecoder_SetParam ( self->hSbrDecoder, SBR_SYSTEM_BITSTREAM_DELAY, self->sbrParams.bsDelay); if ( self->streamInfo.aot == AOT_ER_AAC_ELD ) { /* Configure QMF */ sbrDecoder_SetParam ( self->hSbrDecoder, SBR_LD_QMF_TIME_ALIGN, (self->flags & AC_LD_MPS) ? 1 : 0 ); } /* apply SBR processing */ sbrError = sbrDecoder_Apply ( self->hSbrDecoder, pTimeData, &self->streamInfo.numChannels, &self->streamInfo.sampleRate, self->channelOutputMapping[self->aacChannels-1], interleaved, self->frameOK, &self->psPossible); if (sbrError == SBRDEC_OK) { /* Update data in streaminfo structure. Assume that the SBR upsampling factor is either 1 or 2 */ self->flags |= AC_SBR_PRESENT; if (self->streamInfo.aacSampleRate != self->streamInfo.sampleRate) { if (self->streamInfo.frameSize == 768) { self->streamInfo.frameSize = (self->streamInfo.aacSamplesPerFrame * 8) / 3; } else { self->streamInfo.frameSize = self->streamInfo.aacSamplesPerFrame << 1; } } if (self->psPossible) { self->flags |= AC_PS_PRESENT; self->channelType[0] = ACT_FRONT; self->channelType[1] = ACT_FRONT; self->channelIndices[0] = 0; self->channelIndices[1] = 1; } else { self->flags &= ~AC_PS_PRESENT; } } } if ( flags & (AACDEC_INTR | AACDEC_CLRHIST) ) { /* delete data from the past (e.g. mixdown coeficients) */ pcmDmx_Reset( self->hPcmUtils, PCMDMX_RESET_BS_DATA ); } /* do PCM post processing */ pcmDmx_ApplyFrame ( self->hPcmUtils, pTimeData, self->streamInfo.frameSize, &self->streamInfo.numChannels, interleaved, self->channelType, self->channelIndices, self->channelOutputMapping ); /* Signal interruption to take effect in next frame. */ if ( flags & AACDEC_FLUSH ) { aacDecoder_SignalInterruption(self); } /* Update externally visible copy of flags */ self->streamInfo.flags = self->flags; bail: /* Update Statistics */ aacDecoder_UpdateBitStreamCounters(&self->streamInfo, hBs, nBits, ErrorStatus); return ErrorStatus; } LINKSPEC_CPP void aacDecoder_Close ( HANDLE_AACDECODER self ) { if (self == NULL) return; if (self->hPcmUtils != NULL) { pcmDmx_Close( &self->hPcmUtils ); } 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 ); transportDec_GetLibInfo( info ); FDK_toolsGetLibInfo( info ); pcmDmx_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_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_1024 | CAPF_AAC_960 | CAPF_AAC_512 | CAPF_AAC_480 ; /* End of flags */ return 0; }