| |
| /* ----------------------------------------------------------------------------------------------------------- |
| Software License for The Fraunhofer FDK AAC Codec Library for Android |
| |
| © Copyright 1995 - 2013 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 |
| ----------------------------------------------------------------------------------------------------------- */ |
| |
| /********************** Fraunhofer IIS FDK AAC Encoder lib ****************** |
| |
| Author(s): V. Bacigalupo |
| Description: Metadata Encoder library interface functions |
| |
| ******************************************************************************/ |
| |
| |
| #include "metadata_main.h" |
| #include "metadata_compressor.h" |
| #include "FDK_bitstream.h" |
| #include "FDK_audio.h" |
| #include "genericStds.h" |
| |
| /*----------------- defines ----------------------*/ |
| #define MAX_DRC_BANDS (1<<4) |
| #define MAX_DRC_CHANNELS (8) |
| #define MAX_DRC_FRAMELEN (2*1024) |
| |
| /*--------------- structure definitions --------------------*/ |
| |
| typedef struct AAC_METADATA |
| { |
| /* MPEG: Dynamic Range Control */ |
| struct { |
| UCHAR prog_ref_level_present; |
| SCHAR prog_ref_level; |
| |
| UCHAR dyn_rng_sgn[MAX_DRC_BANDS]; |
| UCHAR dyn_rng_ctl[MAX_DRC_BANDS]; |
| |
| UCHAR drc_bands_present; |
| UCHAR drc_band_incr; |
| UCHAR drc_band_top[MAX_DRC_BANDS]; |
| UCHAR drc_interpolation_scheme; |
| AACENC_METADATA_DRC_PROFILE drc_profile; |
| INT drc_TargetRefLevel; /* used for Limiter */ |
| |
| /* excluded channels */ |
| UCHAR excluded_chns_present; |
| UCHAR exclude_mask[2]; /* MAX_NUMBER_CHANNELS/8 */ |
| } mpegDrc; |
| |
| /* ETSI: addtl ancillary data */ |
| struct { |
| /* Heavy Compression */ |
| UCHAR compression_on; /* flag, if compression value should be written */ |
| UCHAR compression_value; /* compression value */ |
| AACENC_METADATA_DRC_PROFILE comp_profile; |
| INT comp_TargetRefLevel; /* used for Limiter */ |
| INT timecode_coarse_status; |
| INT timecode_fine_status; |
| } etsiAncData; |
| |
| SCHAR centerMixLevel; /* center downmix level (0...7, according to table) */ |
| SCHAR surroundMixLevel; /* surround downmix level (0...7, according to table) */ |
| UCHAR WritePCEMixDwnIdx; /* flag */ |
| UCHAR DmxLvl_On; /* flag */ |
| |
| UCHAR dolbySurroundMode; |
| |
| UCHAR metadataMode; /* indicate meta data mode in current frame (delay line) */ |
| |
| } AAC_METADATA; |
| |
| struct FDK_METADATA_ENCODER |
| { |
| INT metadataMode; |
| HDRC_COMP hDrcComp; |
| AACENC_MetaData submittedMetaData; |
| |
| INT nAudioDataDelay; |
| INT nMetaDataDelay; |
| INT nChannels; |
| |
| INT_PCM audioDelayBuffer[MAX_DRC_CHANNELS*MAX_DRC_FRAMELEN]; |
| int audioDelayIdx; |
| |
| AAC_METADATA metaDataBuffer[3]; |
| int metaDataDelayIdx; |
| |
| UCHAR drcInfoPayload[12]; |
| UCHAR drcDsePayload[8]; |
| |
| INT matrix_mixdown_idx; |
| AACENC_EXT_PAYLOAD exPayload[2]; |
| INT nExtensions; |
| |
| INT finalizeMetaData; /* Delay switch off by one frame and write default configuration to |
| finalize the metadata setup. */ |
| }; |
| |
| |
| /*---------------- constants -----------------------*/ |
| static const AACENC_MetaData defaultMetaDataSetup = { |
| AACENC_METADATA_DRC_NONE, |
| AACENC_METADATA_DRC_NONE, |
| -(31<<16), |
| -(31<<16), |
| 0, |
| -(31<<16), |
| 0, |
| 0, |
| 0, |
| 0, |
| 0 |
| }; |
| |
| static const FIXP_DBL dmxTable[8] = { |
| ((FIXP_DBL)MAXVAL_DBL), FL2FXCONST_DBL(0.841f), FL2FXCONST_DBL(0.707f), FL2FXCONST_DBL(0.596f), |
| FL2FXCONST_DBL(0.500f), FL2FXCONST_DBL(0.422f), FL2FXCONST_DBL(0.355f), FL2FXCONST_DBL(0.000f) |
| }; |
| |
| static const UCHAR surmix2matrix_mixdown_idx[8] = { |
| 0, 0, 0, 1, 1, 2, 2, 3 |
| }; |
| |
| |
| /*--------------- function declarations --------------------*/ |
| static FDK_METADATA_ERROR WriteMetadataPayload( |
| const HANDLE_FDK_METADATA_ENCODER hMetaData, |
| const AAC_METADATA * const pMetadata |
| ); |
| |
| static INT WriteDynamicRangeInfoPayload( |
| const AAC_METADATA* const pMetadata, |
| UCHAR* const pExtensionPayload |
| ); |
| |
| static INT WriteEtsiAncillaryDataPayload( |
| const AAC_METADATA* const pMetadata, |
| UCHAR* const pExtensionPayload |
| ); |
| |
| static FDK_METADATA_ERROR CompensateAudioDelay( |
| HANDLE_FDK_METADATA_ENCODER hMetaDataEnc, |
| INT_PCM * const pAudioSamples, |
| const INT nAudioSamples |
| ); |
| |
| static FDK_METADATA_ERROR LoadSubmittedMetadata( |
| const AACENC_MetaData * const hMetadata, |
| const INT nChannels, |
| const INT metadataMode, |
| AAC_METADATA * const pAacMetaData |
| ); |
| |
| static FDK_METADATA_ERROR ProcessCompressor( |
| AAC_METADATA *pMetadata, |
| HDRC_COMP hDrcComp, |
| const INT_PCM * const pSamples, |
| const INT nSamples |
| ); |
| |
| /*------------- function definitions ----------------*/ |
| |
| static DRC_PROFILE convertProfile(AACENC_METADATA_DRC_PROFILE aacProfile) |
| { |
| DRC_PROFILE drcProfile = DRC_NONE; |
| |
| switch(aacProfile) { |
| case AACENC_METADATA_DRC_NONE: drcProfile = DRC_NONE; break; |
| case AACENC_METADATA_DRC_FILMSTANDARD: drcProfile = DRC_FILMSTANDARD; break; |
| case AACENC_METADATA_DRC_FILMLIGHT: drcProfile = DRC_FILMLIGHT; break; |
| case AACENC_METADATA_DRC_MUSICSTANDARD: drcProfile = DRC_MUSICSTANDARD; break; |
| case AACENC_METADATA_DRC_MUSICLIGHT: drcProfile = DRC_MUSICLIGHT; break; |
| case AACENC_METADATA_DRC_SPEECH: drcProfile = DRC_SPEECH; break; |
| default: drcProfile = DRC_NONE; break; |
| } |
| return drcProfile; |
| } |
| |
| |
| /* convert dialog normalization to program reference level */ |
| /* NOTE: this only is correct, if the decoder target level is set to -31dB for line mode / -20dB for RF mode */ |
| static UCHAR dialnorm2progreflvl(const INT d) |
| { |
| return ((UCHAR)FDKmax(0, FDKmin((-d + (1<<13)) >> 14, 127))); |
| } |
| |
| /* convert program reference level to dialog normalization */ |
| static INT progreflvl2dialnorm(const UCHAR p) |
| { |
| return -((INT)(p<<(16-2))); |
| } |
| |
| /* encode downmix levels to Downmixing_levels_MPEG4 */ |
| static SCHAR encodeDmxLvls(const SCHAR cmixlev, const SCHAR surmixlev) |
| { |
| SCHAR dmxLvls = 0; |
| dmxLvls |= 0x80 | (cmixlev << 4); /* center_mix_level_on */ |
| dmxLvls |= 0x08 | surmixlev; /* surround_mix_level_on */ |
| |
| return dmxLvls; |
| } |
| |
| /* encode AAC DRC gain (ISO/IEC 14496-3:2005 4.5.2.7) */ |
| static void encodeDynrng(INT gain, UCHAR* const dyn_rng_ctl, UCHAR* const dyn_rng_sgn ) |
| { |
| if(gain < 0) |
| { |
| *dyn_rng_sgn = 1; |
| gain = -gain; |
| } |
| else |
| { |
| *dyn_rng_sgn = 0; |
| } |
| gain = FDKmin(gain,(127<<14)); |
| |
| *dyn_rng_ctl = (UCHAR)((gain + (1<<13)) >> 14); |
| } |
| |
| /* decode AAC DRC gain (ISO/IEC 14496-3:2005 4.5.2.7) */ |
| static INT decodeDynrng(const UCHAR dyn_rng_ctl, const UCHAR dyn_rng_sgn) |
| { |
| INT tmp = ((INT)dyn_rng_ctl << (16-2)); |
| if (dyn_rng_sgn) tmp = -tmp; |
| |
| return tmp; |
| } |
| |
| /* encode AAC compression value (ETSI TS 101 154 page 99) */ |
| static UCHAR encodeCompr(INT gain) |
| { |
| UCHAR x, y; |
| INT tmp; |
| |
| /* tmp = (int)((48.164f - gain) / 6.0206f * 15 + 0.5f); */ |
| tmp = ((3156476 - gain) * 15 + 197283) / 394566; |
| |
| if (tmp >= 240) { |
| return 0xFF; |
| } |
| else if (tmp < 0) { |
| return 0; |
| } |
| else { |
| x = tmp / 15; |
| y = tmp % 15; |
| } |
| |
| return (x << 4) | y; |
| } |
| |
| /* decode AAC compression value (ETSI TS 101 154 page 99) */ |
| static INT decodeCompr(const UCHAR compr) |
| { |
| INT gain; |
| SCHAR x = compr >> 4; /* 4 MSB of compr */ |
| UCHAR y = (compr & 0x0F); /* 4 LSB of compr */ |
| |
| /* gain = (INT)((48.164f - 6.0206f * x - 0.4014f * y) ); */ |
| gain = (INT)( scaleValue(((LONG)FL2FXCONST_DBL(6.0206f/128.f)*(8-x) - (LONG)FL2FXCONST_DBL(0.4014f/128.f)*y), -(DFRACT_BITS-1-7-16)) ); |
| |
| return gain; |
| } |
| |
| |
| FDK_METADATA_ERROR FDK_MetadataEnc_Open( |
| HANDLE_FDK_METADATA_ENCODER *phMetaData |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| HANDLE_FDK_METADATA_ENCODER hMetaData = NULL; |
| |
| if (phMetaData == NULL) { |
| err = METADATA_INVALID_HANDLE; |
| goto bail; |
| } |
| |
| /* allocate memory */ |
| hMetaData = (HANDLE_FDK_METADATA_ENCODER) FDKcalloc(1, sizeof(FDK_METADATA_ENCODER) ); |
| |
| if (hMetaData == NULL) { |
| err = METADATA_MEMORY_ERROR; |
| goto bail; |
| } |
| |
| FDKmemclear(hMetaData, sizeof(FDK_METADATA_ENCODER)); |
| |
| /* Allocate DRC Compressor. */ |
| if (FDK_DRC_Generator_Open(&hMetaData->hDrcComp)!=0) { |
| err = METADATA_MEMORY_ERROR; |
| goto bail; |
| } |
| |
| /* Return metadata instance */ |
| *phMetaData = hMetaData; |
| |
| return err; |
| |
| bail: |
| FDK_MetadataEnc_Close(&hMetaData); |
| return err; |
| } |
| |
| FDK_METADATA_ERROR FDK_MetadataEnc_Close( |
| HANDLE_FDK_METADATA_ENCODER *phMetaData |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| |
| if (phMetaData == NULL) { |
| err = METADATA_INVALID_HANDLE; |
| goto bail; |
| } |
| |
| if (*phMetaData != NULL) { |
| FDK_DRC_Generator_Close(&(*phMetaData)->hDrcComp); |
| FDKfree(*phMetaData); |
| *phMetaData = NULL; |
| } |
| bail: |
| return err; |
| } |
| |
| FDK_METADATA_ERROR FDK_MetadataEnc_Init( |
| HANDLE_FDK_METADATA_ENCODER hMetaData, |
| const INT resetStates, |
| const INT metadataMode, |
| const INT audioDelay, |
| const UINT frameLength, |
| const UINT sampleRate, |
| const UINT nChannels, |
| const CHANNEL_MODE channelMode, |
| const CHANNEL_ORDER channelOrder |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| int i, nFrames, delay; |
| |
| if (hMetaData==NULL) { |
| err = METADATA_INVALID_HANDLE; |
| goto bail; |
| } |
| |
| /* Determine values for delay compensation. */ |
| for (nFrames=0, delay=audioDelay-frameLength; delay>0; delay-=frameLength, nFrames++); |
| |
| if ( (hMetaData->nChannels>MAX_DRC_CHANNELS) || ((-delay)>MAX_DRC_FRAMELEN) ) { |
| err = METADATA_INIT_ERROR; |
| goto bail; |
| } |
| |
| /* Initialize with default setup. */ |
| FDKmemcpy(&hMetaData->submittedMetaData, &defaultMetaDataSetup, sizeof(AACENC_MetaData)); |
| |
| hMetaData->finalizeMetaData = 0; /* finalize meta data only while on/off switching, else disabled */ |
| |
| /* Reset delay lines. */ |
| if ( resetStates || (hMetaData->nAudioDataDelay!=-delay) || (hMetaData->nChannels!=(INT)nChannels) ) |
| { |
| FDKmemclear(hMetaData->audioDelayBuffer, sizeof(hMetaData->audioDelayBuffer)); |
| FDKmemclear(hMetaData->metaDataBuffer, sizeof(hMetaData->metaDataBuffer)); |
| hMetaData->audioDelayIdx = 0; |
| hMetaData->metaDataDelayIdx = 0; |
| } |
| else { |
| /* Enable meta data. */ |
| if ( (hMetaData->metadataMode==0) && (metadataMode!=0) ) { |
| /* disable meta data in all delay lines */ |
| for (i=0; i<(int)(sizeof(hMetaData->metaDataBuffer)/sizeof(AAC_METADATA)); i++) { |
| LoadSubmittedMetadata(&hMetaData->submittedMetaData, nChannels, 0, &hMetaData->metaDataBuffer[i]); |
| } |
| } |
| |
| /* Disable meta data.*/ |
| if ( (hMetaData->metadataMode!=0) && (metadataMode==0) ) { |
| hMetaData->finalizeMetaData = hMetaData->metadataMode; |
| } |
| } |
| |
| /* Initialize delay. */ |
| hMetaData->nAudioDataDelay = -delay; |
| hMetaData->nMetaDataDelay = nFrames; |
| hMetaData->nChannels = nChannels; |
| hMetaData->metadataMode = metadataMode; |
| |
| /* Initialize compressor. */ |
| if (metadataMode != 0) { |
| if ( FDK_DRC_Generator_Initialize( |
| hMetaData->hDrcComp, |
| DRC_NONE, |
| DRC_NONE, |
| frameLength, |
| sampleRate, |
| channelMode, |
| channelOrder, |
| 1) != 0) |
| { |
| err = METADATA_INIT_ERROR; |
| } |
| } |
| bail: |
| return err; |
| } |
| |
| static FDK_METADATA_ERROR ProcessCompressor( |
| AAC_METADATA *pMetadata, |
| HDRC_COMP hDrcComp, |
| const INT_PCM * const pSamples, |
| const INT nSamples |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| |
| INT dynrng, compr; |
| DRC_PROFILE profileDrc = convertProfile(pMetadata->mpegDrc.drc_profile); |
| DRC_PROFILE profileComp = convertProfile(pMetadata->etsiAncData.comp_profile); |
| |
| if ( (pMetadata==NULL) || (hDrcComp==NULL) ) { |
| err = METADATA_INVALID_HANDLE; |
| return err; |
| } |
| |
| /* first, check if profile is same as last frame |
| * otherwise, update setup */ |
| if ( (profileDrc != FDK_DRC_Generator_getDrcProfile(hDrcComp)) |
| || (profileComp != FDK_DRC_Generator_getCompProfile(hDrcComp)) ) |
| { |
| FDK_DRC_Generator_setDrcProfile(hDrcComp, profileDrc, profileComp); |
| } |
| |
| /* Sanity check */ |
| if (profileComp == DRC_NONE) { |
| pMetadata->etsiAncData.compression_value = 0x80; /* to ensure no external values will be written if not configured */ |
| } |
| |
| /* in case of embedding external values, copy this now (limiter may overwrite them) */ |
| dynrng = decodeDynrng(pMetadata->mpegDrc.dyn_rng_ctl[0], pMetadata->mpegDrc.dyn_rng_sgn[0]); |
| compr = decodeCompr(pMetadata->etsiAncData.compression_value); |
| |
| /* Call compressor */ |
| if (FDK_DRC_Generator_Calc(hDrcComp, |
| pSamples, |
| progreflvl2dialnorm(pMetadata->mpegDrc.prog_ref_level), |
| pMetadata->mpegDrc.drc_TargetRefLevel, |
| pMetadata->etsiAncData.comp_TargetRefLevel, |
| dmxTable[pMetadata->centerMixLevel], |
| dmxTable[pMetadata->surroundMixLevel], |
| &dynrng, |
| &compr) != 0) |
| { |
| err = METADATA_ENCODE_ERROR; |
| goto bail; |
| } |
| |
| /* Write DRC values */ |
| pMetadata->mpegDrc.drc_band_incr = 0; |
| encodeDynrng(dynrng, pMetadata->mpegDrc.dyn_rng_ctl, pMetadata->mpegDrc.dyn_rng_sgn); |
| pMetadata->etsiAncData.compression_value = encodeCompr(compr); |
| |
| bail: |
| return err; |
| } |
| |
| FDK_METADATA_ERROR FDK_MetadataEnc_Process( |
| HANDLE_FDK_METADATA_ENCODER hMetaDataEnc, |
| INT_PCM * const pAudioSamples, |
| const INT nAudioSamples, |
| const AACENC_MetaData * const pMetadata, |
| AACENC_EXT_PAYLOAD ** ppMetaDataExtPayload, |
| UINT * nMetaDataExtensions, |
| INT * matrix_mixdown_idx |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| int metaDataDelayWriteIdx, metaDataDelayReadIdx, metadataMode; |
| |
| /* Where to write new meta data info */ |
| metaDataDelayWriteIdx = hMetaDataEnc->metaDataDelayIdx; |
| |
| /* How to write the data */ |
| metadataMode = hMetaDataEnc->metadataMode; |
| |
| /* Compensate meta data delay. */ |
| hMetaDataEnc->metaDataDelayIdx++; |
| if (hMetaDataEnc->metaDataDelayIdx > hMetaDataEnc->nMetaDataDelay) hMetaDataEnc->metaDataDelayIdx = 0; |
| |
| /* Where to read pending meta data info from. */ |
| metaDataDelayReadIdx = hMetaDataEnc->metaDataDelayIdx; |
| |
| /* Submit new data if available. */ |
| if (pMetadata!=NULL) { |
| FDKmemcpy(&hMetaDataEnc->submittedMetaData, pMetadata, sizeof(AACENC_MetaData)); |
| } |
| |
| /* Write one additional frame with default configuration of meta data. Ensure defined behaviour on decoder side. */ |
| if ( (hMetaDataEnc->finalizeMetaData!=0) && (hMetaDataEnc->metadataMode==0)) { |
| FDKmemcpy(&hMetaDataEnc->submittedMetaData, &defaultMetaDataSetup, sizeof(AACENC_MetaData)); |
| metadataMode = hMetaDataEnc->finalizeMetaData; |
| hMetaDataEnc->finalizeMetaData = 0; |
| } |
| |
| /* Get last submitted data. */ |
| if ( (err = LoadSubmittedMetadata( |
| &hMetaDataEnc->submittedMetaData, |
| hMetaDataEnc->nChannels, |
| metadataMode, |
| &hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx])) != METADATA_OK ) |
| { |
| goto bail; |
| } |
| |
| /* Calculate compressor if necessary and updata meta data info */ |
| if (hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx].metadataMode != 0) { |
| if ( (err = ProcessCompressor( |
| &hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx], |
| hMetaDataEnc->hDrcComp, |
| pAudioSamples, |
| nAudioSamples)) != METADATA_OK) |
| { |
| /* Get last submitted data again. */ |
| LoadSubmittedMetadata( |
| &hMetaDataEnc->submittedMetaData, |
| hMetaDataEnc->nChannels, |
| metadataMode, |
| &hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx]); |
| } |
| } |
| |
| /* Convert Meta Data side info to bitstream data. */ |
| if ( (err = WriteMetadataPayload(hMetaDataEnc, &hMetaDataEnc->metaDataBuffer[metaDataDelayReadIdx])) != METADATA_OK ) { |
| goto bail; |
| } |
| |
| /* Assign meta data to output */ |
| *ppMetaDataExtPayload = hMetaDataEnc->exPayload; |
| *nMetaDataExtensions = hMetaDataEnc->nExtensions; |
| *matrix_mixdown_idx = hMetaDataEnc->matrix_mixdown_idx; |
| |
| bail: |
| /* Compensate audio delay, reset err status. */ |
| err = CompensateAudioDelay(hMetaDataEnc, pAudioSamples, nAudioSamples); |
| |
| return err; |
| } |
| |
| |
| static FDK_METADATA_ERROR CompensateAudioDelay( |
| HANDLE_FDK_METADATA_ENCODER hMetaDataEnc, |
| INT_PCM * const pAudioSamples, |
| const INT nAudioSamples |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| |
| if (hMetaDataEnc->nAudioDataDelay) { |
| int i, delaySamples = hMetaDataEnc->nAudioDataDelay*hMetaDataEnc->nChannels; |
| |
| for (i = 0; i < nAudioSamples; i++) { |
| INT_PCM tmp = pAudioSamples[i]; |
| pAudioSamples[i] = hMetaDataEnc->audioDelayBuffer[hMetaDataEnc->audioDelayIdx]; |
| hMetaDataEnc->audioDelayBuffer[hMetaDataEnc->audioDelayIdx] = tmp; |
| |
| hMetaDataEnc->audioDelayIdx++; |
| if (hMetaDataEnc->audioDelayIdx >= delaySamples) hMetaDataEnc->audioDelayIdx = 0; |
| } |
| } |
| |
| return err; |
| } |
| |
| /*----------------------------------------------------------------------------- |
| |
| functionname: WriteMetadataPayload |
| description: fills anc data and extension payload |
| returns: Error status |
| |
| ------------------------------------------------------------------------------*/ |
| static FDK_METADATA_ERROR WriteMetadataPayload( |
| const HANDLE_FDK_METADATA_ENCODER hMetaData, |
| const AAC_METADATA * const pMetadata |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| |
| if ( (hMetaData==NULL) || (pMetadata==NULL) ) { |
| err = METADATA_INVALID_HANDLE; |
| goto bail; |
| } |
| |
| hMetaData->nExtensions = 0; |
| hMetaData->matrix_mixdown_idx = -1; |
| |
| /* AAC-DRC */ |
| if (pMetadata->metadataMode != 0) |
| { |
| hMetaData->exPayload[hMetaData->nExtensions].pData = hMetaData->drcInfoPayload; |
| hMetaData->exPayload[hMetaData->nExtensions].dataType = EXT_DYNAMIC_RANGE; |
| hMetaData->exPayload[hMetaData->nExtensions].associatedChElement = -1; |
| |
| hMetaData->exPayload[hMetaData->nExtensions].dataSize = |
| WriteDynamicRangeInfoPayload(pMetadata, hMetaData->exPayload[hMetaData->nExtensions].pData); |
| |
| hMetaData->nExtensions++; |
| |
| /* Matrix Mixdown Coefficient in PCE */ |
| if (pMetadata->WritePCEMixDwnIdx) { |
| hMetaData->matrix_mixdown_idx = surmix2matrix_mixdown_idx[pMetadata->surroundMixLevel]; |
| } |
| |
| /* ETSI TS 101 154 (DVB) - MPEG4 ancillary_data() */ |
| if (pMetadata->metadataMode == 2) /* MP4_METADATA_MPEG_ETSI */ |
| { |
| hMetaData->exPayload[hMetaData->nExtensions].pData = hMetaData->drcDsePayload; |
| hMetaData->exPayload[hMetaData->nExtensions].dataType = EXT_DATA_ELEMENT; |
| hMetaData->exPayload[hMetaData->nExtensions].associatedChElement = -1; |
| |
| hMetaData->exPayload[hMetaData->nExtensions].dataSize = |
| WriteEtsiAncillaryDataPayload(pMetadata,hMetaData->exPayload[hMetaData->nExtensions].pData); |
| |
| hMetaData->nExtensions++; |
| } /* metadataMode == 2 */ |
| |
| } /* metadataMode != 0 */ |
| |
| bail: |
| return err; |
| } |
| |
| static INT WriteDynamicRangeInfoPayload( |
| const AAC_METADATA* const pMetadata, |
| UCHAR* const pExtensionPayload |
| ) |
| { |
| const INT pce_tag_present = 0; /* yet fixed setting! */ |
| const INT prog_ref_lev_res_bits = 0; |
| INT i, drc_num_bands = 1; |
| |
| FDK_BITSTREAM bsWriter; |
| FDKinitBitStream(&bsWriter, pExtensionPayload, 16, 0, BS_WRITER); |
| |
| /* dynamic_range_info() */ |
| FDKwriteBits(&bsWriter, pce_tag_present, 1); /* pce_tag_present */ |
| if (pce_tag_present) { |
| FDKwriteBits(&bsWriter, 0x0, 4); /* pce_instance_tag */ |
| FDKwriteBits(&bsWriter, 0x0, 4); /* drc_tag_reserved_bits */ |
| } |
| |
| /* Exclude channels */ |
| FDKwriteBits(&bsWriter, (pMetadata->mpegDrc.excluded_chns_present) ? 1 : 0, 1); /* excluded_chns_present*/ |
| |
| /* Multiband DRC */ |
| FDKwriteBits(&bsWriter, (pMetadata->mpegDrc.drc_bands_present) ? 1 : 0, 1); /* drc_bands_present */ |
| if (pMetadata->mpegDrc.drc_bands_present) |
| { |
| FDKwriteBits(&bsWriter, pMetadata->mpegDrc.drc_band_incr, 4); /* drc_band_incr */ |
| FDKwriteBits(&bsWriter, pMetadata->mpegDrc.drc_interpolation_scheme, 4); /* drc_interpolation_scheme */ |
| drc_num_bands += pMetadata->mpegDrc.drc_band_incr; |
| for (i=0; i<drc_num_bands; i++) { |
| FDKwriteBits(&bsWriter, pMetadata->mpegDrc.drc_band_top[i], 8); /* drc_band_top */ |
| } |
| } |
| |
| /* Program Reference Level */ |
| FDKwriteBits(&bsWriter, pMetadata->mpegDrc.prog_ref_level_present, 1); /* prog_ref_level_present */ |
| if (pMetadata->mpegDrc.prog_ref_level_present) |
| { |
| FDKwriteBits(&bsWriter, pMetadata->mpegDrc.prog_ref_level, 7); /* prog_ref_level */ |
| FDKwriteBits(&bsWriter, prog_ref_lev_res_bits, 1); /* prog_ref_level_reserved_bits */ |
| } |
| |
| /* DRC Values */ |
| for (i=0; i<drc_num_bands; i++) { |
| FDKwriteBits(&bsWriter, (pMetadata->mpegDrc.dyn_rng_sgn[i]) ? 1 : 0, 1); /* dyn_rng_sgn[ */ |
| FDKwriteBits(&bsWriter, pMetadata->mpegDrc.dyn_rng_ctl[i], 7); /* dyn_rng_ctl */ |
| } |
| |
| /* return number of valid bits in extension payload. */ |
| return FDKgetValidBits(&bsWriter); |
| } |
| |
| static INT WriteEtsiAncillaryDataPayload( |
| const AAC_METADATA* const pMetadata, |
| UCHAR* const pExtensionPayload |
| ) |
| { |
| FDK_BITSTREAM bsWriter; |
| FDKinitBitStream(&bsWriter, pExtensionPayload, 16, 0, BS_WRITER); |
| |
| /* ancillary_data_sync */ |
| FDKwriteBits(&bsWriter, 0xBC, 8); |
| |
| /* bs_info */ |
| FDKwriteBits(&bsWriter, 0x3, 2); /* mpeg_audio_type */ |
| FDKwriteBits(&bsWriter, pMetadata->dolbySurroundMode, 2); /* dolby_surround_mode */ |
| FDKwriteBits(&bsWriter, 0x0, 4); /* reserved */ |
| |
| /* ancillary_data_status */ |
| FDKwriteBits(&bsWriter, 0, 3); /* 3 bit Reserved, set to "0" */ |
| FDKwriteBits(&bsWriter, (pMetadata->DmxLvl_On) ? 1 : 0, 1); /* downmixing_levels_MPEG4_status */ |
| FDKwriteBits(&bsWriter, 0, 1); /* Reserved, set to "0" */ |
| FDKwriteBits(&bsWriter, (pMetadata->etsiAncData.compression_on) ? 1 : 0, 1); /* audio_coding_mode_and_compression status */ |
| FDKwriteBits(&bsWriter, (pMetadata->etsiAncData.timecode_coarse_status) ? 1 : 0, 1); /* coarse_grain_timecode_status */ |
| FDKwriteBits(&bsWriter, (pMetadata->etsiAncData.timecode_fine_status) ? 1 : 0, 1); /* fine_grain_timecode_status */ |
| |
| /* downmixing_levels_MPEG4_status */ |
| if (pMetadata->DmxLvl_On) { |
| FDKwriteBits(&bsWriter, encodeDmxLvls(pMetadata->centerMixLevel, pMetadata->surroundMixLevel), 8); |
| } |
| |
| /* audio_coding_mode_and_compression_status */ |
| if (pMetadata->etsiAncData.compression_on) { |
| FDKwriteBits(&bsWriter, 0x01, 8); /* audio coding mode */ |
| FDKwriteBits(&bsWriter, pMetadata->etsiAncData.compression_value, 8); /* compression value */ |
| } |
| |
| /* grain-timecode coarse/fine */ |
| if (pMetadata->etsiAncData.timecode_coarse_status) { |
| FDKwriteBits(&bsWriter, 0x0, 16); /* not yet supported */ |
| } |
| |
| if (pMetadata->etsiAncData.timecode_fine_status) { |
| FDKwriteBits(&bsWriter, 0x0, 16); /* not yet supported */ |
| } |
| |
| return FDKgetValidBits(&bsWriter); |
| } |
| |
| |
| static FDK_METADATA_ERROR LoadSubmittedMetadata( |
| const AACENC_MetaData * const hMetadata, |
| const INT nChannels, |
| const INT metadataMode, |
| AAC_METADATA * const pAacMetaData |
| ) |
| { |
| FDK_METADATA_ERROR err = METADATA_OK; |
| |
| if (pAacMetaData==NULL) { |
| err = METADATA_INVALID_HANDLE; |
| } |
| else { |
| /* init struct */ |
| FDKmemclear(pAacMetaData, sizeof(AAC_METADATA)); |
| |
| if (hMetadata!=NULL) { |
| /* convert data */ |
| pAacMetaData->mpegDrc.drc_profile = hMetadata->drc_profile; |
| pAacMetaData->etsiAncData.comp_profile = hMetadata->comp_profile; |
| pAacMetaData->mpegDrc.drc_TargetRefLevel = hMetadata->drc_TargetRefLevel; |
| pAacMetaData->etsiAncData.comp_TargetRefLevel= hMetadata->comp_TargetRefLevel; |
| pAacMetaData->mpegDrc.prog_ref_level_present = hMetadata->prog_ref_level_present; |
| pAacMetaData->mpegDrc.prog_ref_level = dialnorm2progreflvl(hMetadata->prog_ref_level); |
| |
| pAacMetaData->centerMixLevel = hMetadata->centerMixLevel; |
| pAacMetaData->surroundMixLevel = hMetadata->surroundMixLevel; |
| pAacMetaData->WritePCEMixDwnIdx = hMetadata->PCE_mixdown_idx_present; |
| pAacMetaData->DmxLvl_On = hMetadata->ETSI_DmxLvl_present; |
| |
| pAacMetaData->etsiAncData.compression_on = 1; |
| |
| |
| if (nChannels == 2) { |
| pAacMetaData->dolbySurroundMode = hMetadata->dolbySurroundMode; /* dolby_surround_mode */ |
| } else { |
| pAacMetaData->dolbySurroundMode = 0; |
| } |
| |
| pAacMetaData->etsiAncData.timecode_coarse_status = 0; /* not yet supported - attention: Update GetEstMetadataBytesPerFrame() if enable this! */ |
| pAacMetaData->etsiAncData.timecode_fine_status = 0; /* not yet supported - attention: Update GetEstMetadataBytesPerFrame() if enable this! */ |
| |
| pAacMetaData->metadataMode = metadataMode; |
| } |
| else { |
| pAacMetaData->metadataMode = 0; /* there is no configuration available */ |
| } |
| } |
| |
| return err; |
| } |
| |
| INT FDK_MetadataEnc_GetDelay( |
| HANDLE_FDK_METADATA_ENCODER hMetadataEnc |
| ) |
| { |
| INT delay = 0; |
| |
| if (hMetadataEnc!=NULL) { |
| delay = hMetadataEnc->nAudioDataDelay; |
| } |
| |
| return delay; |
| } |
| |
| |