mirror of
https://github.com/mstorsjo/fdk-aac.git
synced 2025-02-17 19:50:35 +01:00
Bug: 176246647 Test: atest android.media.cts.DecoderTestAacFormat android.media.cts.DecoderTestXheAac android.media.cts.DecoderTestAacDrc Change-Id: Ib8b6973e9c29e13b8ef33d7736be2b977928face
997 lines
35 KiB
C++
997 lines
35 KiB
C++
/* -----------------------------------------------------------------------------
|
|
Software License for The Fraunhofer FDK AAC Codec Library for Android
|
|
|
|
© Copyright 1995 - 2020 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
|
|
----------------------------------------------------------------------------- */
|
|
|
|
/******************* MPEG transport format encoder library *********************
|
|
|
|
Author(s):
|
|
|
|
Description:
|
|
|
|
*******************************************************************************/
|
|
|
|
#include "tp_data.h"
|
|
|
|
#include "tpenc_lib.h"
|
|
#include "tpenc_asc.h"
|
|
#include "FDK_bitstream.h"
|
|
#include "genericStds.h"
|
|
|
|
#include "FDK_crc.h"
|
|
|
|
#define PCE_HEIGHT_EXT_SYNC (0xAC)
|
|
#define HEIGHT_NORMAL 0
|
|
#define HEIGHT_TOP 1
|
|
#define HEIGHT_BOTTOM 2
|
|
#define MAX_FRONT_ELEMENTS 8
|
|
#define MAX_SIDE_ELEMENTS 3
|
|
#define MAX_BACK_ELEMENTS 4
|
|
|
|
/**
|
|
* Describe additional PCE height information for front, side and back channel
|
|
* elements.
|
|
*/
|
|
typedef struct {
|
|
UCHAR
|
|
num_front_height_channel_elements[2]; /*!< Number of front channel
|
|
elements in top [0] and bottom
|
|
[1] plane. */
|
|
UCHAR num_side_height_channel_elements[2]; /*!< Number of side channel
|
|
elements in top [0] and bottom
|
|
[1] plane. */
|
|
UCHAR num_back_height_channel_elements[2]; /*!< Number of back channel
|
|
elements in top [0] and bottom
|
|
[1] plane. */
|
|
} PCE_HEIGHT_NUM;
|
|
|
|
/**
|
|
* Describe a PCE based on placed channel elements and element type sequence.
|
|
*/
|
|
typedef struct {
|
|
UCHAR num_front_channel_elements; /*!< Number of front channel elements. */
|
|
UCHAR num_side_channel_elements; /*!< Number of side channel elements. */
|
|
UCHAR num_back_channel_elements; /*!< Number of back channel elements. */
|
|
UCHAR num_lfe_channel_elements; /*!< Number of lfe channel elements. */
|
|
const MP4_ELEMENT_ID
|
|
*pEl_type; /*!< List contains sequence describing the elements
|
|
in present channel mode. (MPEG order) */
|
|
const PCE_HEIGHT_NUM *pHeight_num;
|
|
} PCE_CONFIGURATION;
|
|
|
|
/**
|
|
* Map an incoming channel mode to a existing PCE configuration entry.
|
|
*/
|
|
typedef struct {
|
|
CHANNEL_MODE channel_mode; /*!< Present channel mode. */
|
|
PCE_CONFIGURATION
|
|
pce_configuration; /*!< Program config element description. */
|
|
|
|
} CHANNEL_CONFIGURATION;
|
|
|
|
/**
|
|
* The following arrays provide the IDs of the consecutive elements for each
|
|
* mode.
|
|
*/
|
|
static const MP4_ELEMENT_ID elType_1[] = {ID_SCE};
|
|
static const MP4_ELEMENT_ID elType_2[] = {ID_CPE};
|
|
static const MP4_ELEMENT_ID elType_1_2[] = {ID_SCE, ID_CPE};
|
|
static const MP4_ELEMENT_ID elType_1_2_1[] = {ID_SCE, ID_CPE, ID_SCE};
|
|
static const MP4_ELEMENT_ID elType_1_2_2[] = {ID_SCE, ID_CPE, ID_CPE};
|
|
static const MP4_ELEMENT_ID elType_1_2_2_1[] = {ID_SCE, ID_CPE, ID_CPE, ID_LFE};
|
|
static const MP4_ELEMENT_ID elType_1_2_2_2_1[] = {ID_SCE, ID_CPE, ID_CPE,
|
|
ID_CPE, ID_LFE};
|
|
static const MP4_ELEMENT_ID elType_6_1[] = {ID_SCE, ID_CPE, ID_CPE, ID_SCE,
|
|
ID_LFE};
|
|
static const MP4_ELEMENT_ID elType_7_1_back[] = {ID_SCE, ID_CPE, ID_CPE, ID_CPE,
|
|
ID_LFE};
|
|
static const MP4_ELEMENT_ID elType_7_1_top_front[] = {ID_SCE, ID_CPE, ID_CPE,
|
|
ID_LFE, ID_CPE};
|
|
static const MP4_ELEMENT_ID elType_7_1_rear_surround[] = {
|
|
ID_SCE, ID_CPE, ID_CPE, ID_CPE, ID_LFE};
|
|
static const MP4_ELEMENT_ID elType_7_1_front_center[] = {ID_SCE, ID_CPE, ID_CPE,
|
|
ID_CPE, ID_LFE};
|
|
|
|
/**
|
|
* The following arrays provide information on how many front, side and back
|
|
* elements are assigned to the top or bottom plane for each mode that comprises
|
|
* height information.
|
|
*/
|
|
static const PCE_HEIGHT_NUM heightNum_7_1_top_front = {{1, 0}, {0, 0}, {0, 0}};
|
|
|
|
/**
|
|
* \brief Table contains all supported channel modes and according PCE
|
|
configuration description.
|
|
*
|
|
* The mode identifier is followed by the number of front, side, back, and LFE
|
|
elements.
|
|
* These are followed by a pointer to the IDs of the consecutive elements
|
|
(ID_SCE, ID_CPE, ID_LFE).
|
|
*
|
|
* For some modes (MODE_7_1_TOP_FRONT and MODE_22_2) additional height
|
|
information is transmitted.
|
|
* In this case the additional pointer provides information on how many front,
|
|
side and back elements
|
|
* are assigned to the top or bottom plane.The elements are arranged in the
|
|
following order: normal height (front, side, back, LFE), top height (front,
|
|
side, back), bottom height (front, side, back).
|
|
*
|
|
*
|
|
* E.g. MODE_7_1_TOP_FRONT means:
|
|
* - 3 elements are front channel elements.
|
|
* - 0 elements are side channel elements.
|
|
* - 1 element is back channel element.
|
|
* - 1 element is an LFE channel element.
|
|
* - the element order is ID_SCE, ID_CPE, ID_CPE,
|
|
ID_LFE, ID_CPE.
|
|
* - 1 of the front elements is in the top plane.
|
|
*
|
|
* This leads to the following mapping for the cconsecutive elements in the
|
|
MODE_7_1_TOP_FRONT bitstream:
|
|
* - ID_SCE -> normal height front,
|
|
- ID_CPE -> normal height front,
|
|
- ID_CPE -> normal height back,
|
|
- ID_LFE -> normal height LFE,
|
|
- ID_CPE -> top height front.
|
|
*/
|
|
static const CHANNEL_CONFIGURATION pceConfigTab[] = {
|
|
{MODE_1,
|
|
{1, 0, 0, 0, elType_1,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_2,
|
|
{1, 0, 0, 0, elType_2,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_1_2,
|
|
{2, 0, 0, 0, elType_1_2,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_1_2_1,
|
|
{2, 0, 1, 0, elType_1_2_1,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_1_2_2,
|
|
{2, 0, 1, 0, elType_1_2_2,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_1_2_2_1,
|
|
{2, 0, 1, 1, elType_1_2_2_1,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_1_2_2_2_1,
|
|
{3, 0, 1, 1, elType_1_2_2_2_1,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
|
|
{MODE_6_1,
|
|
{2, 0, 2, 1, elType_6_1,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_7_1_BACK,
|
|
{2, 0, 2, 1, elType_7_1_back,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_7_1_TOP_FRONT,
|
|
{3, 0, 1, 1, elType_7_1_top_front, &heightNum_7_1_top_front}},
|
|
|
|
{MODE_7_1_REAR_SURROUND,
|
|
{2, 0, 2, 1, elType_7_1_rear_surround,
|
|
NULL}}, /* don't transmit height information in this mode */
|
|
{MODE_7_1_FRONT_CENTER,
|
|
{3, 0, 1, 1, elType_7_1_front_center,
|
|
NULL}} /* don't transmit height information in this mode */
|
|
};
|
|
|
|
/**
|
|
* \brief Get program config element description for existing channel mode.
|
|
*
|
|
* \param channel_mode Current channel mode.
|
|
*
|
|
* \return
|
|
* - Pointer to PCE_CONFIGURATION entry, on success.
|
|
* - NULL, on failure.
|
|
*/
|
|
static const PCE_CONFIGURATION *getPceEntry(const CHANNEL_MODE channel_mode) {
|
|
UINT i;
|
|
const PCE_CONFIGURATION *pce_config = NULL;
|
|
|
|
for (i = 0; i < (sizeof(pceConfigTab) / sizeof(CHANNEL_CONFIGURATION)); i++) {
|
|
if (pceConfigTab[i].channel_mode == channel_mode) {
|
|
pce_config = &pceConfigTab[i].pce_configuration;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pce_config;
|
|
}
|
|
|
|
int getChannelConfig(const CHANNEL_MODE channel_mode,
|
|
const UCHAR channel_config_zero) {
|
|
INT chan_config = 0;
|
|
|
|
if (channel_config_zero != 0) {
|
|
chan_config = 0;
|
|
} else {
|
|
switch (channel_mode) {
|
|
case MODE_1:
|
|
chan_config = 1;
|
|
break;
|
|
case MODE_2:
|
|
chan_config = 2;
|
|
break;
|
|
case MODE_1_2:
|
|
chan_config = 3;
|
|
break;
|
|
case MODE_1_2_1:
|
|
chan_config = 4;
|
|
break;
|
|
case MODE_1_2_2:
|
|
chan_config = 5;
|
|
break;
|
|
case MODE_1_2_2_1:
|
|
chan_config = 6;
|
|
break;
|
|
case MODE_1_2_2_2_1:
|
|
chan_config = 7;
|
|
break;
|
|
case MODE_6_1:
|
|
chan_config = 11;
|
|
break;
|
|
case MODE_7_1_BACK:
|
|
chan_config = 12;
|
|
break;
|
|
case MODE_7_1_TOP_FRONT:
|
|
chan_config = 14;
|
|
break;
|
|
default:
|
|
chan_config = 0;
|
|
}
|
|
}
|
|
|
|
return chan_config;
|
|
}
|
|
|
|
CHANNEL_MODE transportEnc_GetChannelMode(int noChannels) {
|
|
CHANNEL_MODE chMode;
|
|
|
|
if (noChannels <= 8 && noChannels > 0)
|
|
chMode = (CHANNEL_MODE)(
|
|
(noChannels == 8) ? 7
|
|
: noChannels); /* see : iso/mpeg4 v1 audio subpart1*/
|
|
else
|
|
chMode = MODE_UNKNOWN;
|
|
|
|
return chMode;
|
|
}
|
|
|
|
int transportEnc_writePCE(HANDLE_FDK_BITSTREAM hBs, CHANNEL_MODE channelMode,
|
|
INT sampleRate, int instanceTagPCE, int profile,
|
|
int matrixMixdownA, int pseudoSurroundEnable,
|
|
UINT alignAnchor) {
|
|
int sampleRateIndex, i;
|
|
const PCE_CONFIGURATION *config = NULL;
|
|
const MP4_ELEMENT_ID *pEl_list = NULL;
|
|
UCHAR cpeCnt = 0, sceCnt = 0, lfeCnt = 0, frntCnt = 0, sdCnt = 0, bckCnt = 0,
|
|
isCpe = 0, tag = 0, normalFrontEnd = 0, normalSideEnd = 0,
|
|
normalBackEnd = 0, topFrontEnd = 0, topSideEnd = 0, topBackEnd = 0,
|
|
bottomFrontEnd = 0, bottomSideEnd = 0;
|
|
#ifdef FDK_ASSERT_ENABLE
|
|
UCHAR bottomBackEnd = 0;
|
|
#endif
|
|
enum elementDepth { FRONT, SIDE, BACK } elDepth;
|
|
|
|
sampleRateIndex = getSamplingRateIndex(sampleRate, 4);
|
|
if (sampleRateIndex == 15) {
|
|
return -1;
|
|
}
|
|
|
|
if ((config = getPceEntry(channelMode)) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
FDK_ASSERT(config->num_front_channel_elements <= MAX_FRONT_ELEMENTS);
|
|
FDK_ASSERT(config->num_side_channel_elements <= MAX_SIDE_ELEMENTS);
|
|
FDK_ASSERT(config->num_back_channel_elements <= MAX_BACK_ELEMENTS);
|
|
|
|
UCHAR frontIsCpe[MAX_FRONT_ELEMENTS] = {0},
|
|
frontTag[MAX_FRONT_ELEMENTS] = {0}, sideIsCpe[MAX_SIDE_ELEMENTS] = {0},
|
|
sideTag[MAX_SIDE_ELEMENTS] = {0}, backIsCpe[MAX_BACK_ELEMENTS] = {0},
|
|
backTag[MAX_BACK_ELEMENTS] = {0};
|
|
|
|
/* Write general information */
|
|
|
|
FDKwriteBits(hBs, instanceTagPCE, 4); /* Element instance tag */
|
|
FDKwriteBits(hBs, profile, 2); /* Object type */
|
|
FDKwriteBits(hBs, sampleRateIndex, 4); /* Sample rate index*/
|
|
|
|
FDKwriteBits(hBs, config->num_front_channel_elements,
|
|
4); /* Front channel Elements */
|
|
FDKwriteBits(hBs, config->num_side_channel_elements,
|
|
4); /* No Side Channel Elements */
|
|
FDKwriteBits(hBs, config->num_back_channel_elements,
|
|
4); /* No Back channel Elements */
|
|
FDKwriteBits(hBs, config->num_lfe_channel_elements,
|
|
2); /* No Lfe channel elements */
|
|
|
|
FDKwriteBits(hBs, 0, 3); /* No assoc data elements */
|
|
FDKwriteBits(hBs, 0, 4); /* No valid cc elements */
|
|
FDKwriteBits(hBs, 0, 1); /* Mono mixdown present */
|
|
FDKwriteBits(hBs, 0, 1); /* Stereo mixdown present */
|
|
|
|
if (matrixMixdownA != 0 &&
|
|
((channelMode == MODE_1_2_2) || (channelMode == MODE_1_2_2_1))) {
|
|
FDKwriteBits(hBs, 1, 1); /* Matrix mixdown present */
|
|
FDKwriteBits(hBs, (matrixMixdownA - 1) & 0x3, 2); /* matrix_mixdown_idx */
|
|
FDKwriteBits(hBs, (pseudoSurroundEnable) ? 1 : 0,
|
|
1); /* pseudo_surround_enable */
|
|
} else {
|
|
FDKwriteBits(hBs, 0, 1); /* Matrix mixdown not present */
|
|
}
|
|
|
|
if (config->pHeight_num != NULL) {
|
|
/* we have up to three different height levels, and in each height level we
|
|
* may have front, side and back channels. We need to know where each
|
|
* section ends to correctly count the tags */
|
|
normalFrontEnd = config->num_front_channel_elements -
|
|
config->pHeight_num->num_front_height_channel_elements[0] -
|
|
config->pHeight_num->num_front_height_channel_elements[1];
|
|
normalSideEnd = normalFrontEnd + config->num_side_channel_elements -
|
|
config->pHeight_num->num_side_height_channel_elements[0] -
|
|
config->pHeight_num->num_side_height_channel_elements[1];
|
|
normalBackEnd = normalSideEnd + config->num_back_channel_elements -
|
|
config->pHeight_num->num_back_height_channel_elements[0] -
|
|
config->pHeight_num->num_back_height_channel_elements[1];
|
|
|
|
topFrontEnd =
|
|
normalBackEnd + config->num_lfe_channel_elements +
|
|
config->pHeight_num->num_front_height_channel_elements[0]; /* only
|
|
normal
|
|
height
|
|
LFEs
|
|
assumed */
|
|
topSideEnd =
|
|
topFrontEnd + config->pHeight_num->num_side_height_channel_elements[0];
|
|
topBackEnd =
|
|
topSideEnd + config->pHeight_num->num_back_height_channel_elements[0];
|
|
|
|
bottomFrontEnd =
|
|
topBackEnd + config->pHeight_num->num_front_height_channel_elements[1];
|
|
bottomSideEnd = bottomFrontEnd +
|
|
config->pHeight_num->num_side_height_channel_elements[1];
|
|
#ifdef FDK_ASSERT_ENABLE
|
|
bottomBackEnd = bottomSideEnd +
|
|
config->pHeight_num->num_back_height_channel_elements[1];
|
|
#endif
|
|
|
|
} else {
|
|
/* we have only one height level, so we don't care about top or bottom */
|
|
normalFrontEnd = config->num_front_channel_elements;
|
|
normalSideEnd = normalFrontEnd + config->num_side_channel_elements;
|
|
normalBackEnd = normalSideEnd + config->num_back_channel_elements;
|
|
}
|
|
|
|
/* assign cpe and tag information to either front, side or back channels */
|
|
|
|
pEl_list = config->pEl_type;
|
|
|
|
for (i = 0; i < config->num_front_channel_elements +
|
|
config->num_side_channel_elements +
|
|
config->num_back_channel_elements +
|
|
config->num_lfe_channel_elements;
|
|
i++) {
|
|
if (*pEl_list == ID_LFE) {
|
|
pEl_list++;
|
|
continue;
|
|
}
|
|
isCpe = (*pEl_list++ == ID_CPE) ? 1 : 0;
|
|
tag = (isCpe) ? cpeCnt++ : sceCnt++;
|
|
|
|
if (i < normalFrontEnd)
|
|
elDepth = FRONT;
|
|
else if (i < normalSideEnd)
|
|
elDepth = SIDE;
|
|
else if (i < normalBackEnd)
|
|
elDepth = BACK;
|
|
else if (i < topFrontEnd)
|
|
elDepth = FRONT;
|
|
else if (i < topSideEnd)
|
|
elDepth = SIDE;
|
|
else if (i < topBackEnd)
|
|
elDepth = BACK;
|
|
else if (i < bottomFrontEnd)
|
|
elDepth = FRONT;
|
|
else if (i < bottomSideEnd)
|
|
elDepth = SIDE;
|
|
else {
|
|
elDepth = BACK;
|
|
FDK_ASSERT(i < bottomBackEnd); /* won't fail if implementation of pce
|
|
configuration table is correct */
|
|
}
|
|
|
|
switch (elDepth) {
|
|
case FRONT:
|
|
FDK_ASSERT(frntCnt < config->num_front_channel_elements);
|
|
frontIsCpe[frntCnt] = isCpe;
|
|
frontTag[frntCnt++] = tag;
|
|
break;
|
|
case SIDE:
|
|
FDK_ASSERT(sdCnt < config->num_side_channel_elements);
|
|
sideIsCpe[sdCnt] = isCpe;
|
|
sideTag[sdCnt++] = tag;
|
|
break;
|
|
case BACK:
|
|
FDK_ASSERT(bckCnt < config->num_back_channel_elements);
|
|
backIsCpe[bckCnt] = isCpe;
|
|
backTag[bckCnt++] = tag;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Write front channel isCpe and tags */
|
|
for (i = 0; i < config->num_front_channel_elements; i++) {
|
|
FDKwriteBits(hBs, frontIsCpe[i], 1);
|
|
FDKwriteBits(hBs, frontTag[i], 4);
|
|
}
|
|
/* Write side channel isCpe and tags */
|
|
for (i = 0; i < config->num_side_channel_elements; i++) {
|
|
FDKwriteBits(hBs, sideIsCpe[i], 1);
|
|
FDKwriteBits(hBs, sideTag[i], 4);
|
|
}
|
|
/* Write back channel isCpe and tags */
|
|
for (i = 0; i < config->num_back_channel_elements; i++) {
|
|
FDKwriteBits(hBs, backIsCpe[i], 1);
|
|
FDKwriteBits(hBs, backTag[i], 4);
|
|
}
|
|
/* Write LFE information */
|
|
for (i = 0; i < config->num_lfe_channel_elements; i++) {
|
|
FDKwriteBits(hBs, lfeCnt++, 4); /* LFE channel Instance Tag. */
|
|
}
|
|
|
|
/* - num_valid_cc_elements always 0.
|
|
- num_assoc_data_elements always 0. */
|
|
|
|
/* Byte alignment: relative to alignAnchor
|
|
ADTS: align with respect to the first bit of the raw_data_block()
|
|
ADIF: align with respect to the first bit of the header
|
|
LATM: align with respect to the first bit of the ASC */
|
|
FDKbyteAlign(hBs, alignAnchor); /* Alignment */
|
|
|
|
/* Write comment information */
|
|
|
|
if (config->pHeight_num != NULL) {
|
|
/* embed height information in comment field */
|
|
|
|
INT commentBytes =
|
|
1 /* PCE_HEIGHT_EXT_SYNC */
|
|
+ ((((config->num_front_channel_elements +
|
|
config->num_side_channel_elements +
|
|
config->num_back_channel_elements)
|
|
<< 1) +
|
|
7) >>
|
|
3) /* 2 bit height info per element, round up to full bytes */
|
|
+ 1; /* CRC */
|
|
|
|
FDKwriteBits(hBs, commentBytes, 8); /* comment size. */
|
|
|
|
FDK_CRCINFO crcInfo; /* CRC state info */
|
|
INT crcReg;
|
|
|
|
FDKcrcInit(&crcInfo, 0x07, 0xFF, 8);
|
|
crcReg = FDKcrcStartReg(&crcInfo, hBs, 0);
|
|
|
|
FDKwriteBits(hBs, PCE_HEIGHT_EXT_SYNC, 8); /* indicate height extension */
|
|
|
|
/* front channel height information */
|
|
for (i = 0;
|
|
i < config->num_front_channel_elements -
|
|
config->pHeight_num->num_front_height_channel_elements[0] -
|
|
config->pHeight_num->num_front_height_channel_elements[1];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_NORMAL, 2);
|
|
for (i = 0; i < config->pHeight_num->num_front_height_channel_elements[0];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_TOP, 2);
|
|
for (i = 0; i < config->pHeight_num->num_front_height_channel_elements[1];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_BOTTOM, 2);
|
|
|
|
/* side channel height information */
|
|
for (i = 0;
|
|
i < config->num_side_channel_elements -
|
|
config->pHeight_num->num_side_height_channel_elements[0] -
|
|
config->pHeight_num->num_side_height_channel_elements[1];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_NORMAL, 2);
|
|
for (i = 0; i < config->pHeight_num->num_side_height_channel_elements[0];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_TOP, 2);
|
|
for (i = 0; i < config->pHeight_num->num_side_height_channel_elements[1];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_BOTTOM, 2);
|
|
|
|
/* back channel height information */
|
|
for (i = 0;
|
|
i < config->num_back_channel_elements -
|
|
config->pHeight_num->num_back_height_channel_elements[0] -
|
|
config->pHeight_num->num_back_height_channel_elements[1];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_NORMAL, 2);
|
|
for (i = 0; i < config->pHeight_num->num_back_height_channel_elements[0];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_TOP, 2);
|
|
for (i = 0; i < config->pHeight_num->num_back_height_channel_elements[1];
|
|
i++)
|
|
FDKwriteBits(hBs, HEIGHT_BOTTOM, 2);
|
|
|
|
FDKbyteAlign(hBs, alignAnchor); /* Alignment */
|
|
|
|
FDKcrcEndReg(&crcInfo, hBs, crcReg);
|
|
FDKwriteBits(hBs, FDKcrcGetCRC(&crcInfo), 8);
|
|
|
|
} else {
|
|
FDKwriteBits(hBs, 0,
|
|
8); /* Do no write any comment or height information. */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int transportEnc_GetPCEBits(CHANNEL_MODE channelMode, int matrixMixdownA,
|
|
int bits) {
|
|
const PCE_CONFIGURATION *config = NULL;
|
|
|
|
if ((config = getPceEntry(channelMode)) == NULL) {
|
|
return -1; /* unsupported channelmapping */
|
|
}
|
|
|
|
bits +=
|
|
4 + 2 + 4; /* Element instance tag + Object type + Sample rate index */
|
|
bits += 4 + 4 + 4 + 2; /* No (front + side + back + lfe channel) elements */
|
|
bits += 3 + 4; /* No (assoc data + valid cc) elements */
|
|
bits += 1 + 1 + 1; /* Mono + Stereo + Matrix mixdown present */
|
|
|
|
if (matrixMixdownA != 0 &&
|
|
((channelMode == MODE_1_2_2) || (channelMode == MODE_1_2_2_1))) {
|
|
bits += 3; /* matrix_mixdown_idx + pseudo_surround_enable */
|
|
}
|
|
|
|
bits += (1 + 4) * (INT)config->num_front_channel_elements;
|
|
bits += (1 + 4) * (INT)config->num_side_channel_elements;
|
|
bits += (1 + 4) * (INT)config->num_back_channel_elements;
|
|
bits += (4) * (INT)config->num_lfe_channel_elements;
|
|
|
|
/* - num_valid_cc_elements always 0.
|
|
- num_assoc_data_elements always 0. */
|
|
|
|
if ((bits % 8) != 0) {
|
|
bits += (8 - (bits % 8)); /* Alignment */
|
|
}
|
|
|
|
bits += 8; /* Comment field bytes */
|
|
|
|
if (config->pHeight_num != NULL) {
|
|
/* Comment field (height extension) */
|
|
|
|
bits +=
|
|
8 /* PCE_HEIGHT_EXT_SYNC */
|
|
+
|
|
((config->num_front_channel_elements +
|
|
config->num_side_channel_elements + config->num_back_channel_elements)
|
|
<< 1) /* 2 bit height info per element */
|
|
+ 8; /* CRC */
|
|
|
|
if ((bits % 8) != 0) {
|
|
bits += (8 - (bits % 8)); /* Alignment */
|
|
}
|
|
}
|
|
|
|
return bits;
|
|
}
|
|
|
|
static void writeAot(HANDLE_FDK_BITSTREAM hBitstreamBuffer,
|
|
AUDIO_OBJECT_TYPE aot) {
|
|
int tmp = (int)aot;
|
|
|
|
if (tmp > 31) {
|
|
FDKwriteBits(hBitstreamBuffer, AOT_ESCAPE, 5);
|
|
FDKwriteBits(hBitstreamBuffer, tmp - 32, 6); /* AudioObjectType */
|
|
} else {
|
|
FDKwriteBits(hBitstreamBuffer, tmp, 5);
|
|
}
|
|
}
|
|
|
|
static void writeSampleRate(HANDLE_FDK_BITSTREAM hBs, int sampleRate,
|
|
int nBits) {
|
|
int srIdx = getSamplingRateIndex(sampleRate, nBits);
|
|
|
|
FDKwriteBits(hBs, srIdx, nBits);
|
|
if (srIdx == (1 << nBits) - 1) {
|
|
FDKwriteBits(hBs, sampleRate, 24);
|
|
}
|
|
}
|
|
|
|
static int transportEnc_writeGASpecificConfig(HANDLE_FDK_BITSTREAM asc,
|
|
CODER_CONFIG *config, int extFlg,
|
|
UINT alignAnchor) {
|
|
int aot = config->aot;
|
|
int samplesPerFrame = config->samplesPerFrame;
|
|
|
|
/* start of GASpecificConfig according to ISO/IEC 14496-3 Subpart 4, 4.4.1 */
|
|
FDKwriteBits(asc,
|
|
((samplesPerFrame == 960 || samplesPerFrame == 480) ? 1 : 0),
|
|
1); /* frameLengthFlag: 1 for a 960/480 (I)MDCT, 0 for a 1024/512
|
|
(I)MDCT*/
|
|
FDKwriteBits(asc, 0,
|
|
1); /* dependsOnCoreCoder: Sampling Rate Coder Specific, see in
|
|
ISO/IEC 14496-3 Subpart 4, 4.4.1 */
|
|
FDKwriteBits(asc, extFlg,
|
|
1); /* Extension Flag: Shall be 1 for aot = 17,19,20,21,22,23 */
|
|
|
|
/* Write PCE if channel config is not 1-7 */
|
|
if (getChannelConfig(config->channelMode, config->channelConfigZero) == 0) {
|
|
transportEnc_writePCE(asc, config->channelMode, config->samplingRate, 0, 1,
|
|
config->matrixMixdownA,
|
|
(config->flags & CC_PSEUDO_SURROUND) ? 1 : 0,
|
|
alignAnchor);
|
|
}
|
|
if ((aot == AOT_AAC_SCAL) || (aot == AOT_ER_AAC_SCAL)) {
|
|
FDKwriteBits(asc, 0, 3); /* layerNr */
|
|
}
|
|
if (extFlg) {
|
|
if (aot == AOT_ER_BSAC) {
|
|
FDKwriteBits(asc, config->BSACnumOfSubFrame, 5); /* numOfSubFrame */
|
|
FDKwriteBits(asc, config->BSAClayerLength, 11); /* layer_length */
|
|
}
|
|
if ((aot == AOT_ER_AAC_LC) || (aot == AOT_ER_AAC_LTP) ||
|
|
(aot == AOT_ER_AAC_SCAL) || (aot == AOT_ER_AAC_LD)) {
|
|
FDKwriteBits(asc, (config->flags & CC_VCB11) ? 1 : 0,
|
|
1); /* aacSectionDataResillienceFlag */
|
|
FDKwriteBits(asc, (config->flags & CC_RVLC) ? 1 : 0,
|
|
1); /* aacScaleFactorDataResillienceFlag */
|
|
FDKwriteBits(asc, (config->flags & CC_HCR) ? 1 : 0,
|
|
1); /* aacSpectralDataResillienceFlag */
|
|
}
|
|
FDKwriteBits(asc, 0, 1); /* extensionFlag3: reserved. Shall be '0' */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int transportEnc_writeELDSpecificConfig(HANDLE_FDK_BITSTREAM hBs,
|
|
CODER_CONFIG *config,
|
|
int epConfig,
|
|
CSTpCallBacks *cb) {
|
|
UINT frameLengthFlag = 0;
|
|
switch (config->samplesPerFrame) {
|
|
case 512:
|
|
case 256:
|
|
case 128:
|
|
case 64:
|
|
frameLengthFlag = 0;
|
|
break;
|
|
case 480:
|
|
case 240:
|
|
case 160:
|
|
case 120:
|
|
case 60:
|
|
frameLengthFlag = 1;
|
|
break;
|
|
}
|
|
|
|
FDKwriteBits(hBs, frameLengthFlag, 1);
|
|
|
|
FDKwriteBits(hBs, (config->flags & CC_VCB11) ? 1 : 0, 1);
|
|
FDKwriteBits(hBs, (config->flags & CC_RVLC) ? 1 : 0, 1);
|
|
FDKwriteBits(hBs, (config->flags & CC_HCR) ? 1 : 0, 1);
|
|
|
|
FDKwriteBits(hBs, (config->flags & CC_SBR) ? 1 : 0, 1); /* SBR header flag */
|
|
if ((config->flags & CC_SBR)) {
|
|
FDKwriteBits(hBs, (config->samplingRate == config->extSamplingRate) ? 0 : 1,
|
|
1); /* Samplerate Flag */
|
|
FDKwriteBits(hBs, (config->flags & CC_SBRCRC) ? 1 : 0, 1); /* SBR CRC flag*/
|
|
|
|
if (cb->cbSbr != NULL) {
|
|
const PCE_CONFIGURATION *pPce;
|
|
int e, sbrElementIndex = 0;
|
|
|
|
pPce = getPceEntry(config->channelMode);
|
|
|
|
for (e = 0; e < pPce->num_front_channel_elements +
|
|
pPce->num_side_channel_elements +
|
|
pPce->num_back_channel_elements +
|
|
pPce->num_lfe_channel_elements;
|
|
e++) {
|
|
if ((pPce->pEl_type[e] == ID_SCE) || (pPce->pEl_type[e] == ID_CPE)) {
|
|
cb->cbSbr(cb->cbSbrData, hBs, 0, 0, 0, config->aot, pPce->pEl_type[e],
|
|
sbrElementIndex, 0, 0, 0, NULL, 1);
|
|
sbrElementIndex++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((config->flags & CC_SAC) && (cb->cbSsc != NULL)) {
|
|
FDKwriteBits(hBs, ELDEXT_LDSAC, 4);
|
|
|
|
const INT eldExtLen =
|
|
(cb->cbSsc(cb->cbSscData, NULL, config->aot, config->extSamplingRate, 0,
|
|
0, 0, 0, 0, 0, NULL) +
|
|
7) >>
|
|
3;
|
|
INT cnt = eldExtLen;
|
|
|
|
if (cnt < 0xF) {
|
|
FDKwriteBits(hBs, cnt, 4);
|
|
} else {
|
|
FDKwriteBits(hBs, 0xF, 4);
|
|
cnt -= 0xF;
|
|
|
|
if (cnt < 0xFF) {
|
|
FDKwriteBits(hBs, cnt, 8);
|
|
} else {
|
|
FDKwriteBits(hBs, 0xFF, 8);
|
|
cnt -= 0xFF;
|
|
|
|
FDK_ASSERT(cnt <= 0xFFFF);
|
|
FDKwriteBits(hBs, cnt, 16);
|
|
}
|
|
}
|
|
|
|
cb->cbSsc(cb->cbSscData, hBs, config->aot, config->extSamplingRate, 0, 0, 0,
|
|
0, 0, 0, NULL);
|
|
}
|
|
|
|
if (config->downscaleSamplingRate != 0 &&
|
|
config->downscaleSamplingRate != config->extSamplingRate) {
|
|
/* downscale active */
|
|
|
|
/* eldExtLenDsc: Number of bytes for the ELD downscale extension (srIdx
|
|
needs 1 byte
|
|
+ downscaleSamplingRate needs additional 3 bytes) */
|
|
int eldExtLenDsc = 1;
|
|
int downscaleSamplingRate = config->downscaleSamplingRate;
|
|
FDKwriteBits(hBs, ELDEXT_DOWNSCALEINFO, 4); /* ELDEXT_DOWNSCALEINFO */
|
|
|
|
if ((downscaleSamplingRate != 96000) && (downscaleSamplingRate != 88200) &&
|
|
(downscaleSamplingRate != 64000) && (downscaleSamplingRate != 48000) &&
|
|
(downscaleSamplingRate != 44100) && (downscaleSamplingRate != 32000) &&
|
|
(downscaleSamplingRate != 24000) && (downscaleSamplingRate != 22050) &&
|
|
(downscaleSamplingRate != 16000) && (downscaleSamplingRate != 12000) &&
|
|
(downscaleSamplingRate != 11025) && (downscaleSamplingRate != 8000) &&
|
|
(downscaleSamplingRate != 7350)) {
|
|
eldExtLenDsc = 4; /* length extends to 4 if downscaleSamplingRate's value
|
|
is not one of the listed values */
|
|
}
|
|
|
|
FDKwriteBits(hBs, eldExtLenDsc, 4);
|
|
writeSampleRate(hBs, downscaleSamplingRate, 4);
|
|
FDKwriteBits(hBs, 0x0, 4); /* fill_nibble */
|
|
}
|
|
|
|
FDKwriteBits(hBs, ELDEXT_TERM, 4); /* ELDEXT_TERM */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int transportEnc_writeUsacSpecificConfig(HANDLE_FDK_BITSTREAM hBs,
|
|
int extFlag, CODER_CONFIG *cc,
|
|
CSTpCallBacks *cb) {
|
|
FDK_BITSTREAM usacConf;
|
|
int usacConfigBits = cc->rawConfigBits;
|
|
|
|
if ((usacConfigBits <= 0) ||
|
|
((usacConfigBits + 7) / 8 > (int)sizeof(cc->rawConfig))) {
|
|
return TRANSPORTENC_UNSUPPORTED_FORMAT;
|
|
}
|
|
FDKinitBitStream(&usacConf, cc->rawConfig, BUFSIZE_DUMMY_VALUE,
|
|
usacConfigBits, BS_READER);
|
|
|
|
for (; usacConfigBits > 0; usacConfigBits--) {
|
|
UINT tmp = FDKreadBit(&usacConf);
|
|
FDKwriteBits(hBs, tmp, 1);
|
|
}
|
|
FDKsyncCache(hBs);
|
|
|
|
return TRANSPORTENC_OK;
|
|
}
|
|
|
|
int transportEnc_writeASC(HANDLE_FDK_BITSTREAM asc, CODER_CONFIG *config,
|
|
CSTpCallBacks *cb) {
|
|
UINT extFlag = 0;
|
|
int err;
|
|
int epConfig = 0;
|
|
|
|
/* Required for the PCE. */
|
|
UINT alignAnchor = FDKgetValidBits(asc);
|
|
|
|
/* Extension Flag: Shall be 1 for aot = 17,19,20,21,22,23,39 */
|
|
switch (config->aot) {
|
|
case AOT_ER_AAC_LC:
|
|
case AOT_ER_AAC_LTP:
|
|
case AOT_ER_AAC_SCAL:
|
|
case AOT_ER_TWIN_VQ:
|
|
case AOT_ER_BSAC:
|
|
case AOT_ER_AAC_LD:
|
|
case AOT_ER_AAC_ELD:
|
|
case AOT_USAC:
|
|
extFlag = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (config->sbrSignaling == SIG_EXPLICIT_HIERARCHICAL && config->sbrPresent)
|
|
writeAot(asc, config->extAOT);
|
|
else
|
|
writeAot(asc, config->aot);
|
|
|
|
/* In case of USAC it is the output not the core sampling rate */
|
|
writeSampleRate(asc, config->samplingRate, 4);
|
|
|
|
/* Try to guess a reasonable channel mode if not given */
|
|
if (config->channelMode == MODE_INVALID) {
|
|
config->channelMode = transportEnc_GetChannelMode(config->noChannels);
|
|
if (config->channelMode == MODE_INVALID) return -1;
|
|
}
|
|
|
|
FDKwriteBits(
|
|
asc, getChannelConfig(config->channelMode, config->channelConfigZero), 4);
|
|
|
|
if (config->sbrSignaling == SIG_EXPLICIT_HIERARCHICAL && config->sbrPresent) {
|
|
writeSampleRate(asc, config->extSamplingRate, 4);
|
|
writeAot(asc, config->aot);
|
|
}
|
|
|
|
switch (config->aot) {
|
|
case AOT_AAC_MAIN:
|
|
case AOT_AAC_LC:
|
|
case AOT_AAC_SSR:
|
|
case AOT_AAC_LTP:
|
|
case AOT_AAC_SCAL:
|
|
case AOT_TWIN_VQ:
|
|
case AOT_ER_AAC_LC:
|
|
case AOT_ER_AAC_LTP:
|
|
case AOT_ER_AAC_SCAL:
|
|
case AOT_ER_TWIN_VQ:
|
|
case AOT_ER_BSAC:
|
|
case AOT_ER_AAC_LD:
|
|
err =
|
|
transportEnc_writeGASpecificConfig(asc, config, extFlag, alignAnchor);
|
|
if (err) return err;
|
|
break;
|
|
case AOT_ER_AAC_ELD:
|
|
err = transportEnc_writeELDSpecificConfig(asc, config, epConfig, cb);
|
|
if (err) return err;
|
|
break;
|
|
case AOT_USAC:
|
|
err = transportEnc_writeUsacSpecificConfig(asc, extFlag, config, cb);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
switch (config->aot) {
|
|
case AOT_ER_AAC_LC:
|
|
case AOT_ER_AAC_LTP:
|
|
case AOT_ER_AAC_SCAL:
|
|
case AOT_ER_TWIN_VQ:
|
|
case AOT_ER_BSAC:
|
|
case AOT_ER_AAC_LD:
|
|
case AOT_ER_CELP:
|
|
case AOT_ER_HVXC:
|
|
case AOT_ER_HILN:
|
|
case AOT_ER_PARA:
|
|
case AOT_ER_AAC_ELD:
|
|
FDKwriteBits(asc, 0, 2); /* epconfig 0 */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* backward compatible explicit signaling of extension AOT */
|
|
if (config->sbrSignaling == SIG_EXPLICIT_BW_COMPATIBLE) {
|
|
TP_ASC_EXTENSION_ID ascExtId = ASCEXT_UNKOWN;
|
|
|
|
if (config->sbrPresent) {
|
|
ascExtId = ASCEXT_SBR;
|
|
FDKwriteBits(asc, ascExtId, 11);
|
|
writeAot(asc, config->extAOT);
|
|
FDKwriteBits(asc, 1, 1); /* sbrPresentFlag=1 */
|
|
writeSampleRate(asc, config->extSamplingRate, 4);
|
|
if (config->psPresent) {
|
|
ascExtId = ASCEXT_PS;
|
|
FDKwriteBits(asc, ascExtId, 11);
|
|
FDKwriteBits(asc, 1, 1); /* psPresentFlag=1 */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make sure all bits are sync'ed */
|
|
FDKsyncCache(asc);
|
|
|
|
return 0;
|
|
}
|