mirror of https://github.com/mstorsjo/fdk-aac.git
1185 lines
44 KiB
C++
1185 lines
44 KiB
C++
/* -----------------------------------------------------------------------------
|
|
Software License for The Fraunhofer FDK AAC Codec Library for Android
|
|
|
|
© Copyright 1995 - 2018 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 encoder library ******************************
|
|
|
|
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_FRAMELEN (2 * 1024)
|
|
#define MAX_DELAY_FRAMES (3)
|
|
|
|
/*--------------- 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;
|
|
|
|
UCHAR extAncDataStatus;
|
|
|
|
struct {
|
|
UCHAR ext_downmix_lvl_status;
|
|
UCHAR ext_downmix_gain_status;
|
|
UCHAR ext_lfe_downmix_status;
|
|
UCHAR
|
|
ext_dmix_a_idx; /* extended downmix level (0..7, according to table)
|
|
*/
|
|
UCHAR
|
|
ext_dmix_b_idx; /* extended downmix level (0..7, according to table)
|
|
*/
|
|
UCHAR dmx_gain_5_sgn;
|
|
UCHAR dmx_gain_5_idx;
|
|
UCHAR dmx_gain_2_sgn;
|
|
UCHAR dmx_gain_2_idx;
|
|
UCHAR ext_dmix_lfe_idx; /* extended downmix level for lfe (0..15,
|
|
according to table) */
|
|
|
|
} extAncData;
|
|
|
|
} 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 drcPresentationMode;
|
|
|
|
UCHAR
|
|
metadataMode; /* indicate meta data mode in current frame (delay line) */
|
|
|
|
} AAC_METADATA;
|
|
|
|
typedef struct FDK_METADATA_ENCODER {
|
|
INT metadataMode;
|
|
HDRC_COMP hDrcComp;
|
|
AACENC_MetaData submittedMetaData;
|
|
|
|
INT nAudioDataDelay; /* Additional delay to round up to next frame border (in
|
|
samples) */
|
|
INT nMetaDataDelay; /* Meta data delay (in frames) */
|
|
INT nChannels;
|
|
CHANNEL_MODE channelMode;
|
|
|
|
INT_PCM* pAudioDelayBuffer;
|
|
|
|
AAC_METADATA metaDataBuffer[MAX_DELAY_FRAMES];
|
|
INT metaDataDelayIdx;
|
|
|
|
UCHAR drcInfoPayload[12];
|
|
UCHAR drcDsePayload[8];
|
|
|
|
INT matrix_mixdown_idx;
|
|
|
|
AACENC_EXT_PAYLOAD exPayload[2];
|
|
INT nExtensions;
|
|
|
|
UINT maxChannels; /* Maximum number of audio channels to be supported. */
|
|
|
|
INT finalizeMetaData; /* Delay switch off by one frame and write default
|
|
configuration to finalize the metadata setup. */
|
|
INT initializeMetaData; /* Fill up delay line with first meta data info. This
|
|
is required to have meta data already in first
|
|
frame. */
|
|
} FDK_METADATA_ENCODER;
|
|
|
|
/*---------------- constants -----------------------*/
|
|
static const AACENC_MetaData defaultMetaDataSetup = {
|
|
AACENC_METADATA_DRC_NONE, /* drc_profile */
|
|
AACENC_METADATA_DRC_NOT_PRESENT, /* comp_profile */
|
|
-(31 << 16), /* drc_TargetRefLevel */
|
|
-(23 << 16), /* comp_TargetRefLevel */
|
|
0, /* prog_ref_level_present */
|
|
-(23 << 16), /* prog_ref_level */
|
|
0, /* PCE_mixdown_idx_present */
|
|
0, /* ETSI_DmxLvl_present */
|
|
0, /* centerMixLevel */
|
|
0, /* surroundMixLevel */
|
|
0, /* dolbySurroundMode */
|
|
0, /* drcPresentationMode */
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0} /* ExtMetaData */
|
|
};
|
|
|
|
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)};
|
|
|
|
#define FL2DMXLFE(a) FL2FXCONST_DBL((a) / (1 << LFE_LEV_SCALE))
|
|
static const FIXP_DBL dmxLfeTable[16] = {
|
|
FL2DMXLFE(3.162f), FL2DMXLFE(2.000f), FL2DMXLFE(1.679f), FL2DMXLFE(1.413f),
|
|
FL2DMXLFE(1.189f), FL2DMXLFE(1.000f), FL2DMXLFE(0.841f), FL2DMXLFE(0.707f),
|
|
FL2DMXLFE(0.596f), FL2DMXLFE(0.500f), FL2DMXLFE(0.316f), FL2DMXLFE(0.178f),
|
|
FL2DMXLFE(0.100f), FL2DMXLFE(0.032f), FL2DMXLFE(0.010f), FL2DMXLFE(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 UINT audioSamplesBufSize, 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 UINT samplesBufSize,
|
|
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;
|
|
case AACENC_METADATA_DRC_NOT_PRESENT:
|
|
drcProfile = DRC_NOT_PRESENT;
|
|
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)fMax(0, fMin((-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 = fMin(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((FIXP_DBL)(((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,
|
|
const UINT maxChannels) {
|
|
FDK_METADATA_ERROR err = METADATA_OK;
|
|
HANDLE_FDK_METADATA_ENCODER hMetaData = NULL;
|
|
|
|
if (phMetaData == NULL) {
|
|
err = METADATA_INVALID_HANDLE;
|
|
goto bail;
|
|
}
|
|
|
|
/* allocate memory */
|
|
if (NULL == (hMetaData = (HANDLE_FDK_METADATA_ENCODER)FDKcalloc(
|
|
1, sizeof(FDK_METADATA_ENCODER)))) {
|
|
err = METADATA_MEMORY_ERROR;
|
|
goto bail;
|
|
}
|
|
FDKmemclear(hMetaData, sizeof(FDK_METADATA_ENCODER));
|
|
|
|
if (NULL == (hMetaData->pAudioDelayBuffer = (INT_PCM*)FDKcalloc(
|
|
maxChannels * MAX_DRC_FRAMELEN, sizeof(INT_PCM)))) {
|
|
err = METADATA_MEMORY_ERROR;
|
|
goto bail;
|
|
}
|
|
FDKmemclear(hMetaData->pAudioDelayBuffer,
|
|
maxChannels * MAX_DRC_FRAMELEN * sizeof(INT_PCM));
|
|
hMetaData->maxChannels = maxChannels;
|
|
|
|
/* Allocate DRC Compressor. */
|
|
if (FDK_DRC_Generator_Open(&hMetaData->hDrcComp) != 0) {
|
|
err = METADATA_MEMORY_ERROR;
|
|
goto bail;
|
|
}
|
|
hMetaData->channelMode = MODE_UNKNOWN;
|
|
|
|
/* 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)->pAudioDelayBuffer);
|
|
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 - (INT)frameLength; delay > 0;
|
|
delay -= (INT)frameLength, nFrames++)
|
|
;
|
|
|
|
if ((nChannels > (8)) || (nChannels > hMetaData->maxChannels) ||
|
|
((-delay) > MAX_DRC_FRAMELEN) || nFrames >= MAX_DELAY_FRAMES) {
|
|
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 */
|
|
hMetaData->initializeMetaData =
|
|
0; /* fill up meta data delay line only at a reset otherwise disabled */
|
|
|
|
/* Reset delay lines. */
|
|
if (resetStates || (hMetaData->nAudioDataDelay != -delay) ||
|
|
(hMetaData->channelMode != channelMode)) {
|
|
if (resetStates || (hMetaData->channelMode == MODE_UNKNOWN)) {
|
|
/* clear delay buffer */
|
|
FDKmemclear(hMetaData->pAudioDelayBuffer,
|
|
hMetaData->maxChannels * MAX_DRC_FRAMELEN * sizeof(INT_PCM));
|
|
} else {
|
|
/* if possible, keep static audio channels for seamless channel
|
|
* reconfiguration */
|
|
FDK_channelMapDescr mapDescrPrev, mapDescr;
|
|
int c, src[2] = {-1, -1}, dst[2] = {-1, -1};
|
|
|
|
FDK_chMapDescr_init(&mapDescrPrev, NULL, 0,
|
|
(channelOrder == CH_ORDER_MPEG) ? 1 : 0);
|
|
FDK_chMapDescr_init(&mapDescr, NULL, 0,
|
|
(channelOrder == CH_ORDER_MPEG) ? 1 : 0);
|
|
|
|
switch (channelMode) {
|
|
case MODE_1:
|
|
if ((INT)nChannels != 2) {
|
|
/* preserve center channel */
|
|
src[0] = FDK_chMapDescr_getMapValue(&mapDescrPrev, 0,
|
|
hMetaData->channelMode);
|
|
dst[0] = FDK_chMapDescr_getMapValue(&mapDescr, 0, channelMode);
|
|
}
|
|
break;
|
|
case MODE_2:
|
|
case MODE_1_2:
|
|
case MODE_1_2_1:
|
|
case MODE_1_2_2:
|
|
case MODE_1_2_2_1:
|
|
if (hMetaData->nChannels >= 2) {
|
|
/* preserve left/right channel */
|
|
src[0] = FDK_chMapDescr_getMapValue(
|
|
&mapDescrPrev, ((hMetaData->channelMode == 2) ? 0 : 1),
|
|
hMetaData->channelMode);
|
|
src[1] = FDK_chMapDescr_getMapValue(
|
|
&mapDescrPrev, ((hMetaData->channelMode == 2) ? 1 : 2),
|
|
hMetaData->channelMode);
|
|
dst[0] = FDK_chMapDescr_getMapValue(
|
|
&mapDescr, ((channelMode == 2) ? 0 : 1), channelMode);
|
|
dst[1] = FDK_chMapDescr_getMapValue(
|
|
&mapDescr, ((channelMode == 2) ? 1 : 2), channelMode);
|
|
}
|
|
break;
|
|
default:;
|
|
}
|
|
C_ALLOC_SCRATCH_START(scratch_audioDelayBuffer, INT_PCM, (8));
|
|
FDKmemclear(scratch_audioDelayBuffer, (8) * sizeof(INT_PCM));
|
|
|
|
i = (hMetaData->nChannels > (INT)nChannels)
|
|
? 0
|
|
: hMetaData->nAudioDataDelay - 1;
|
|
do {
|
|
for (c = 0; c < 2; c++) {
|
|
if (src[c] != -1 && dst[c] != -1) {
|
|
scratch_audioDelayBuffer[dst[c]] =
|
|
hMetaData->pAudioDelayBuffer[i * hMetaData->nChannels + src[c]];
|
|
}
|
|
}
|
|
FDKmemcpy(&hMetaData->pAudioDelayBuffer[i * nChannels],
|
|
scratch_audioDelayBuffer, nChannels * sizeof(INT_PCM));
|
|
i += (hMetaData->nChannels > (INT)nChannels) ? 1 : -1;
|
|
} while ((i < hMetaData->nAudioDataDelay) && (i >= 0));
|
|
|
|
C_ALLOC_SCRATCH_END(scratch_audioDelayBuffer, INT_PCM, (8));
|
|
}
|
|
FDKmemclear(hMetaData->metaDataBuffer, sizeof(hMetaData->metaDataBuffer));
|
|
hMetaData->metaDataDelayIdx = 0;
|
|
hMetaData->initializeMetaData =
|
|
1; /* fill up delay line with first meta data info */
|
|
} 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->channelMode = channelMode;
|
|
hMetaData->metadataMode = metadataMode;
|
|
|
|
/* Initialize compressor. */
|
|
if ((metadataMode == 1) || (metadataMode == 2)) {
|
|
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 UINT samplesBufSize,
|
|
const INT nSamples) {
|
|
FDK_METADATA_ERROR err = METADATA_OK;
|
|
|
|
INT dynrng, compr;
|
|
INT dmxGain5, dmxGain2;
|
|
DRC_PROFILE profileDrc;
|
|
DRC_PROFILE profileComp;
|
|
|
|
if ((pMetadata == NULL) || (hDrcComp == NULL)) {
|
|
err = METADATA_INVALID_HANDLE;
|
|
return err;
|
|
}
|
|
|
|
profileDrc = convertProfile(pMetadata->mpegDrc.drc_profile);
|
|
profileComp = convertProfile(pMetadata->etsiAncData.comp_profile);
|
|
|
|
/* 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);
|
|
|
|
dmxGain5 = decodeDynrng(pMetadata->etsiAncData.extAncData.dmx_gain_5_idx,
|
|
pMetadata->etsiAncData.extAncData.dmx_gain_5_sgn);
|
|
dmxGain2 = decodeDynrng(pMetadata->etsiAncData.extAncData.dmx_gain_2_idx,
|
|
pMetadata->etsiAncData.extAncData.dmx_gain_2_sgn);
|
|
|
|
/* Call compressor */
|
|
if (FDK_DRC_Generator_Calc(
|
|
hDrcComp, pSamples, samplesBufSize,
|
|
progreflvl2dialnorm(pMetadata->mpegDrc.prog_ref_level),
|
|
pMetadata->mpegDrc.drc_TargetRefLevel,
|
|
pMetadata->etsiAncData.comp_TargetRefLevel,
|
|
dmxTable[pMetadata->centerMixLevel],
|
|
dmxTable[pMetadata->surroundMixLevel],
|
|
dmxTable[pMetadata->etsiAncData.extAncData.ext_dmix_a_idx],
|
|
dmxTable[pMetadata->etsiAncData.extAncData.ext_dmix_b_idx],
|
|
pMetadata->etsiAncData.extAncData.ext_lfe_downmix_status
|
|
? dmxLfeTable[pMetadata->etsiAncData.extAncData.ext_dmix_lfe_idx]
|
|
: (FIXP_DBL)0,
|
|
dmxGain5, dmxGain2, &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 UINT audioSamplesBufSize, 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 == 1) ||
|
|
(hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx].metadataMode == 2)) {
|
|
if ((err = ProcessCompressor(
|
|
&hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx],
|
|
hMetaDataEnc->hDrcComp, pAudioSamples, audioSamplesBufSize,
|
|
nAudioSamples)) != METADATA_OK) {
|
|
/* Get last submitted data again. */
|
|
LoadSubmittedMetadata(
|
|
&hMetaDataEnc->submittedMetaData, hMetaDataEnc->nChannels,
|
|
metadataMode, &hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx]);
|
|
}
|
|
}
|
|
|
|
/* Fill up delay line with initial meta data info.*/
|
|
if ((hMetaDataEnc->initializeMetaData != 0) &&
|
|
(hMetaDataEnc->metadataMode != 0)) {
|
|
int i;
|
|
for (i = 0;
|
|
i < (int)(sizeof(hMetaDataEnc->metaDataBuffer) / sizeof(AAC_METADATA));
|
|
i++) {
|
|
if (i != metaDataDelayWriteIdx) {
|
|
FDKmemcpy(&hMetaDataEnc->metaDataBuffer[i],
|
|
&hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx],
|
|
sizeof(hMetaDataEnc->metaDataBuffer[metaDataDelayWriteIdx]));
|
|
}
|
|
}
|
|
hMetaDataEnc->initializeMetaData = 0;
|
|
}
|
|
|
|
/* Convert Meta Data side info to bitstream data. */
|
|
FDK_ASSERT(metaDataDelayReadIdx < MAX_DELAY_FRAMES);
|
|
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, audioSamplesBufSize,
|
|
nAudioSamples / hMetaDataEnc->nChannels);
|
|
|
|
return err;
|
|
}
|
|
|
|
static FDK_METADATA_ERROR CompensateAudioDelay(
|
|
HANDLE_FDK_METADATA_ENCODER hMetaDataEnc, INT_PCM* const pAudioSamples,
|
|
const UINT audioSamplesBufSize, const INT nAudioSamples) {
|
|
FDK_METADATA_ERROR err = METADATA_OK;
|
|
|
|
if (hMetaDataEnc->nAudioDataDelay) {
|
|
C_ALLOC_SCRATCH_START(scratch_audioDelayBuffer, INT_PCM, 1024);
|
|
|
|
for (int c = 0; c < hMetaDataEnc->nChannels; c++) {
|
|
int M = 1024;
|
|
INT_PCM* pAudioSamples2 = pAudioSamples + c * audioSamplesBufSize;
|
|
int delayIdx = hMetaDataEnc->nAudioDataDelay;
|
|
|
|
do {
|
|
M = fMin(M, delayIdx);
|
|
delayIdx -= M;
|
|
|
|
FDKmemcpy(&scratch_audioDelayBuffer[0],
|
|
&pAudioSamples2[(nAudioSamples - M)], sizeof(INT_PCM) * M);
|
|
FDKmemmove(&pAudioSamples2[M], &pAudioSamples2[0],
|
|
sizeof(INT_PCM) * (nAudioSamples - M));
|
|
FDKmemcpy(
|
|
&pAudioSamples2[0],
|
|
&hMetaDataEnc->pAudioDelayBuffer[delayIdx +
|
|
c * hMetaDataEnc->nAudioDataDelay],
|
|
sizeof(INT_PCM) * M);
|
|
FDKmemcpy(
|
|
&hMetaDataEnc->pAudioDelayBuffer[delayIdx +
|
|
c * hMetaDataEnc->nAudioDataDelay],
|
|
&scratch_audioDelayBuffer[0], sizeof(INT_PCM) * M);
|
|
|
|
} while (delayIdx > 0);
|
|
}
|
|
|
|
C_ALLOC_SCRATCH_END(scratch_audioDelayBuffer, INT_PCM, 1024);
|
|
}
|
|
|
|
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;
|
|
|
|
if (pMetadata->metadataMode != 0) {
|
|
/* AAC-DRC */
|
|
if ((pMetadata->metadataMode == 1) || (pMetadata->metadataMode == 2)) {
|
|
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++;
|
|
} /* pMetadata->metadataMode==1 || pMetadata->metadataMode==2 */
|
|
|
|
/* 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) ||
|
|
(pMetadata->metadataMode == 3)) /* 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==3 */
|
|
|
|
} /* 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, pMetadata->drcPresentationMode,
|
|
2); /* DRC presentation mode */
|
|
FDKwriteBits(&bsWriter, 0x0, 1); /* stereo_downmix_mode */
|
|
FDKwriteBits(&bsWriter, 0x0, 1); /* 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, pMetadata->etsiAncData.extAncDataStatus,
|
|
1); /* ext_anc_data_status */
|
|
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 */
|
|
}
|
|
|
|
/* extended ancillary data structure */
|
|
if (pMetadata->etsiAncData.extAncDataStatus) {
|
|
/* ext_ancillary_data_status */
|
|
FDKwriteBits(&bsWriter, 0, 1); /* Reserved, set to "0" */
|
|
FDKwriteBits(&bsWriter,
|
|
pMetadata->etsiAncData.extAncData.ext_downmix_lvl_status,
|
|
1); /* ext_downmixing_levels_status */
|
|
FDKwriteBits(&bsWriter,
|
|
pMetadata->etsiAncData.extAncData.ext_downmix_gain_status,
|
|
1); /* ext_downmixing_global_gains_status */
|
|
FDKwriteBits(&bsWriter,
|
|
pMetadata->etsiAncData.extAncData.ext_lfe_downmix_status,
|
|
1); /* ext_downmixing_lfe_level_status" */
|
|
FDKwriteBits(&bsWriter, 0, 4); /* Reserved, set to "0" */
|
|
|
|
/* ext_downmixing_levels */
|
|
if (pMetadata->etsiAncData.extAncData.ext_downmix_lvl_status) {
|
|
FDKwriteBits(&bsWriter, pMetadata->etsiAncData.extAncData.ext_dmix_a_idx,
|
|
3); /* dmix_a_idx */
|
|
FDKwriteBits(&bsWriter, pMetadata->etsiAncData.extAncData.ext_dmix_b_idx,
|
|
3); /* dmix_b_idx */
|
|
FDKwriteBits(&bsWriter, 0, 2); /* Reserved, set to "0" */
|
|
}
|
|
|
|
/* ext_downmixing_gains */
|
|
if (pMetadata->etsiAncData.extAncData.ext_downmix_gain_status) {
|
|
FDKwriteBits(&bsWriter, pMetadata->etsiAncData.extAncData.dmx_gain_5_sgn,
|
|
1); /* dmx_gain_5_sign */
|
|
FDKwriteBits(&bsWriter, pMetadata->etsiAncData.extAncData.dmx_gain_5_idx,
|
|
6); /* dmx_gain_5_idx */
|
|
FDKwriteBits(&bsWriter, 0, 1); /* Reserved, set to "0" */
|
|
FDKwriteBits(&bsWriter, pMetadata->etsiAncData.extAncData.dmx_gain_2_sgn,
|
|
1); /* dmx_gain_2_sign */
|
|
FDKwriteBits(&bsWriter, pMetadata->etsiAncData.extAncData.dmx_gain_2_idx,
|
|
6); /* dmx_gain_2_idx */
|
|
FDKwriteBits(&bsWriter, 0, 1); /* Reserved, set to "0" */
|
|
}
|
|
|
|
if (pMetadata->etsiAncData.extAncData.ext_lfe_downmix_status) {
|
|
FDKwriteBits(&bsWriter,
|
|
pMetadata->etsiAncData.extAncData.ext_dmix_lfe_idx,
|
|
4); /* dmix_lfe_idx */
|
|
FDKwriteBits(&bsWriter, 0, 4); /* Reserved, set to "0" */
|
|
}
|
|
}
|
|
|
|
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 =
|
|
(hMetadata->comp_profile == AACENC_METADATA_DRC_NOT_PRESENT ? 0 : 1);
|
|
|
|
if (pAacMetaData->mpegDrc.drc_profile ==
|
|
AACENC_METADATA_DRC_NOT_PRESENT) {
|
|
pAacMetaData->mpegDrc.drc_profile =
|
|
AACENC_METADATA_DRC_NONE; /* MPEG DRC gains are
|
|
always present in BS
|
|
syntax */
|
|
/* we should give a warning, but ErrorHandler does not support this */
|
|
}
|
|
|
|
if (nChannels == 2) {
|
|
pAacMetaData->dolbySurroundMode =
|
|
hMetadata->dolbySurroundMode; /* dolby_surround_mode */
|
|
} else {
|
|
pAacMetaData->dolbySurroundMode = 0;
|
|
}
|
|
|
|
pAacMetaData->drcPresentationMode = hMetadata->drcPresentationMode;
|
|
/* override external values if DVB DRC presentation mode is given */
|
|
if (pAacMetaData->drcPresentationMode == 1) {
|
|
pAacMetaData->mpegDrc.drc_TargetRefLevel =
|
|
fMax(-(31 << 16), pAacMetaData->mpegDrc.drc_TargetRefLevel);
|
|
pAacMetaData->etsiAncData.comp_TargetRefLevel = fMax(
|
|
-(20 << 16),
|
|
pAacMetaData->etsiAncData.comp_TargetRefLevel); /* implies -23dB */
|
|
}
|
|
if (pAacMetaData->drcPresentationMode == 2) {
|
|
pAacMetaData->mpegDrc.drc_TargetRefLevel =
|
|
fMax(-(23 << 16), pAacMetaData->mpegDrc.drc_TargetRefLevel);
|
|
pAacMetaData->etsiAncData.comp_TargetRefLevel =
|
|
fMax(-(23 << 16), pAacMetaData->etsiAncData.comp_TargetRefLevel);
|
|
}
|
|
if (pAacMetaData->etsiAncData.comp_profile ==
|
|
AACENC_METADATA_DRC_NOT_PRESENT) {
|
|
/* DVB defines to revert to Light DRC if heavy is not present */
|
|
if (pAacMetaData->drcPresentationMode != 0) {
|
|
/* we exclude the "not indicated" mode as this requires the user to
|
|
* define desired levels anyway */
|
|
pAacMetaData->mpegDrc.drc_TargetRefLevel =
|
|
fMax(pAacMetaData->etsiAncData.comp_TargetRefLevel,
|
|
pAacMetaData->mpegDrc.drc_TargetRefLevel);
|
|
}
|
|
}
|
|
|
|
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! */
|
|
|
|
/* extended ancillary data */
|
|
pAacMetaData->etsiAncData.extAncDataStatus =
|
|
((hMetadata->ExtMetaData.extAncDataEnable == 1) ? 1 : 0);
|
|
|
|
if (pAacMetaData->etsiAncData.extAncDataStatus) {
|
|
pAacMetaData->etsiAncData.extAncData.ext_downmix_lvl_status =
|
|
(hMetadata->ExtMetaData.extDownmixLevelEnable ? 1 : 0);
|
|
pAacMetaData->etsiAncData.extAncData.ext_downmix_gain_status =
|
|
(hMetadata->ExtMetaData.dmxGainEnable ? 1 : 0);
|
|
pAacMetaData->etsiAncData.extAncData.ext_lfe_downmix_status =
|
|
(hMetadata->ExtMetaData.lfeDmxEnable ? 1 : 0);
|
|
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_a_idx =
|
|
hMetadata->ExtMetaData.extDownmixLevel_A;
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_b_idx =
|
|
hMetadata->ExtMetaData.extDownmixLevel_B;
|
|
|
|
if (pAacMetaData->etsiAncData.extAncData.ext_downmix_gain_status) {
|
|
encodeDynrng(hMetadata->ExtMetaData.dmxGain5,
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_5_idx),
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_5_sgn));
|
|
encodeDynrng(hMetadata->ExtMetaData.dmxGain2,
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_2_idx),
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_2_sgn));
|
|
} else {
|
|
encodeDynrng(1 << 16,
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_5_idx),
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_5_sgn));
|
|
encodeDynrng(1 << 16,
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_2_idx),
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_2_sgn));
|
|
}
|
|
|
|
if (pAacMetaData->etsiAncData.extAncData.ext_lfe_downmix_status) {
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_lfe_idx =
|
|
hMetadata->ExtMetaData.lfeDmxLevel;
|
|
} else {
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_lfe_idx =
|
|
15; /* -inf dB */
|
|
}
|
|
} else {
|
|
pAacMetaData->etsiAncData.extAncData.ext_downmix_lvl_status = 0;
|
|
pAacMetaData->etsiAncData.extAncData.ext_downmix_gain_status = 0;
|
|
pAacMetaData->etsiAncData.extAncData.ext_lfe_downmix_status = 0;
|
|
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_a_idx = 7; /* -inf dB */
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_b_idx = 7; /* -inf dB */
|
|
|
|
encodeDynrng(1 << 16,
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_5_idx),
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_5_sgn));
|
|
encodeDynrng(1 << 16,
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_2_idx),
|
|
&(pAacMetaData->etsiAncData.extAncData.dmx_gain_2_sgn));
|
|
|
|
pAacMetaData->etsiAncData.extAncData.ext_dmix_lfe_idx =
|
|
15; /* -inf dB */
|
|
}
|
|
|
|
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;
|
|
}
|