/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
/*

 Filename: get_audio_specific_config.cpp

------------------------------------------------------------------------------
 INPUT AND OUTPUT DEFINITIONS

 Inputs:
    pVars = pointer to the structure that holds all information for
            this instance of the library. pVars->prog_config is directly
            used, and pVars->mc_info, pVars->prog_config,
            pVars->pWinSeqInfo, pVars->SFBWidth128 are needed indirectly
            for calling set_mc_info. Data type pointer to tDec_Int_File

 Local Stores/Buffers/Pointers Needed:
    None

 Global Stores/Buffers/Pointers Needed:
    None

 Outputs:
    status = 0 if successfully decoded AudioSpecificConfig
             1 if un-supported config is used for this release

 Pointers and Buffers Modified:
    pVars->prog_config contents are updated with the information read in.
    pVars->mc_info contents are updated with channel information.
    pVars->pWinSeqInfo contents are updated with window information.
    pVars->SFBWidth128 contents are updated with scale factor band width data.

 Local Stores Modified:
    None

 Global Stores Modified:
    None

------------------------------------------------------------------------------
 FUNCTION DESCRIPTION

 This function reads the bitstream for the structure "AudioSpecificConfig",
 and sets the decoder configuration that is needed by the decoder to be able
 to decode the media properly.

------------------------------------------------------------------------------
 REQUIREMENTS

 This function shall not use global variables

------------------------------------------------------------------------------
 REFERENCES

 (1) ISO/IEC 14496-3: 1999(E)
 Part 3
 Subpart 1  p18     1.6   Interface to MPEG-4 Systems
 Subpart 4  p13     4.4.1 GA Specific Configuration
 Amendment  p10     6.2.1 AudioSpecificInfo
 Amendment  p78     8.2   Decoder configuration (GASpecificConfig)

 (2) AAC DecoderSpecificInfo Information
   PacketVideo descriptions - San Diego

------------------------------------------------------------------------------
 PSEUDO-CODE

    status = SUCCESS;

    pInputStream = &(pVars->inputStream);

    temp = CALL getbits(
                    neededBits = LEN_OBJ_TYPE + LEN_SAMP_RATE_IDX,
                    pInputStream = pInputStream)
           MODIFYING (pInputStream)
           RETURNING (temp)

    audioObjectType = (temp & 0x1f0) >> 4;

    pVars->prog_config.profile = audioObjectType;

    pVars->prog_config.sampling_rate_idx = temp & 0xf;

    IF (pVars->prog_config.sampling_rate_idx == 0xf)
    THEN
        sampling_rate = CALL getbits(
                            neededBits = LEN_SAMP_RATE,
                            pInputStream = pInputStream);
                        MODIFYING (pInputStream)
                        RETURNING (sampling_rate)
    ENDIF

    channel_config = CALL getbits(
                            neededBits = LEN_CHAN_CONFIG,
                            pInputStream = pInputStream);
                        MODIFYING (pInputStream)
                        RETURNING (channel_config)

    IF (channel_config > 2)
    THEN
        status = 1;
    ENDIF

    IF (((audioObjectType == MP4AUDIO_AAC_MAIN)     OR
        (audioObjectType == MP4AUDIO_AAC_LC)        OR
        (audioObjectType == MP4AUDIO_AAC_SSR)       OR
        (audioObjectType == MP4AUDIO_LTP)           OR
        (audioObjectType == MP4AUDIO_AAC_SCALABLE)  OR
        (audioObjectType == MP4AUDIO_TWINVQ)) AND (status == -1))
    THEN
        status = CALL get_GA_specific_config(
                            pVars = pVars,
                            channel_config = channel_config,
                            audioObjectType = audioObjectType,
                            pInputStream = pInputStream);
                      MODIFYING (pVars->mc_info,channel_config,pInputStream)
                      RETURNING (status)

    ENDIF

    IF (audioObjectType == MP4AUDIO_CELP)
    THEN
        status = 1;
    ENDIF

    IF (audioObjectType == MP4AUDIO_HVXC)
    THEN
        status = 1;
    ENDIF

    IF (audioObjectType == MP4AUDIO_TTSI)
    THEN
        status = 1;
    ENDIF

    IF ((audioObjectType == 13) OR (audioObjectType == 14) OR
        (audioObjectType == 15) OR (audioObjectType == 16))
    THEN
        status = 1;
    ENDIF

    IF (((audioObjectType == MP4AUDIO_ER_AAC_LC)       OR
         (audioObjectType == MP4AUDIO_ER_AAC_LTP)      OR
         (audioObjectType == MP4AUDIO_ER_AAC_SCALABLE) OR
         (audioObjectType == MP4AUDIO_ER_TWINVQ)       OR
         (audioObjectType == MP4AUDIO_ER_BSAC)         OR
         (audioObjectType == MP4AUDIO_ER_AAC_LD)) AND (status == -1))
    THEN
        status = 1;
    ENDIF

    IF (audioObjectType == MP4AUDIO_ER_CELP)
    THEN
        status = 1;
    ENDIF

    IF (audioObjectType == MP4AUDIO_ER_HVXC)
    THEN
        status = 1;
    ENDIF

    IF ((audioObjectType == MP4AUDIO_ER_HILN) OR
        (audioObjectType == MP4AUDIO_PARAMETRIC))
    THEN
        status = 1;
    ENDIF

    IF ((audioObjectType == MP4AUDIO_ER_AAC_LC)       OR
        (audioObjectType == MP4AUDIO_ER_AAC_LTP)      OR
        (audioObjectType == MP4AUDIO_ER_AAC_SCALABLE) OR
        (audioObjectType == MP4AUDIO_ER_TWINVQ)       OR
        (audioObjectType == MP4AUDIO_ER_BSAC)         OR
        (audioObjectType == MP4AUDIO_ER_AAC_LD)       OR
        (audioObjectType == MP4AUDIO_ER_CELP)         OR
        (audioObjectType == MP4AUDIO_ER_HVXC)         OR
        (audioObjectType == MP4AUDIO_ER_HILN)         OR
        (audioObjectType == MP4AUDIO_PARAMETRIC))
    THEN
        epConfig = CALL getbits(
                            neededBits = LEN_EP_CONFIG,
                            pInputStream = pInputStream);
                      MODIFYING (pInputStream)
                      RETURNING (epConfig)

        IF (epConfig == 2)
        THEN
            status = 1;
        ENDIF

    ENDIF

    RETURN status;

------------------------------------------------------------------------------
*/


/*----------------------------------------------------------------------------
; INCLUDES
----------------------------------------------------------------------------*/
#include    "pv_audio_type_defs.h"
#include    "e_mp4ff_const.h"
#include    "e_tmp4audioobjecttype.h"
#include    "get_audio_specific_config.h"
#include    "get_ga_specific_config.h"
#include    "ibstream.h"
#include    "sfb.h"                   /* Where samp_rate_info[] is declared */

/*----------------------------------------------------------------------------
; MACROS
; Define module specific macros here
----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------
; DEFINES
; Include all pre-processor statements here. Include conditional
; compile variables also.
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL FUNCTION DEFINITIONS
; Function Prototype declaration
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL FUNCTION REFERENCES
; Declare functions defined elsewhere and referenced in this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL GLOBAL STORE/BUFFER/POINTER REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
Int get_audio_specific_config(tDec_Int_File   * const pVars)
{

    UInt    temp;
    tMP4AudioObjectType     audioObjectType;
    //UInt32  sampling_rate;
    UInt    channel_config;
    UInt    syncExtensionType;
    UInt    extensionAudioObjectType = 0;
    UInt    extensionSamplingFrequencyIndex = 0;
    BITS   *pInputStream;
    Int     status;

    status = SUCCESS;

    pInputStream = &(pVars->inputStream);

    pVars->mc_info.upsamplingFactor = 1;   /*  default to regular AAC */

    temp =  get9_n_lessbits(LEN_OBJ_TYPE + LEN_SAMP_RATE_IDX,
                            pInputStream);

    /*
     * The following code can directly set the values of elements in
     * MC_Info, rather than first setting the values in pVars->prog_config
     * and then copy these values to MC_Info by calling set_mc_info.
     * In order to keep consistent with get_prog_config (ADIF) and
     * get_adts_header (ADTS), the code here is still copying
     * the info, and set the pVars->current_program = 0
     */

    /* AudioObjectType */
    audioObjectType = (tMP4AudioObjectType)((temp & 0x1f0) >> 4);

    pVars->mc_info.ExtendedAudioObjectType =  audioObjectType;   /* default */
    /* saving an audioObjectType into a profile field */
    /* pVars->prog_config.profile = audioObjectType; */

    /* sampling rate index */
    pVars->prog_config.sampling_rate_idx = temp & 0xf;

    if (pVars->prog_config.sampling_rate_idx > 0xb)
    {
        /*
         *  Only support 12 sampling frequencies from array samp_rate_info ( see sfb.cpp)
         *  7350 Hz (index 0xc) is not supported, the other indexes are reserved or escape
         */
        if (pVars->prog_config.sampling_rate_idx == 0xf) /* escape sequence */
        {
            /*
             * sampling rate not listed in Table 1.6.2,
             * this release does not support this
             */
            /*sampling_rate =  getbits( LEN_SAMP_RATE,
                                      pInputStream);*/
            getbits(LEN_SAMP_RATE, pInputStream); /* future use */
        }

        status = 1;
    }

    channel_config =  get9_n_lessbits(LEN_CHAN_CONFIG,
                                      pInputStream);

    if ((channel_config > 2) && (!pVars->aacConfigUtilityEnabled))
    {
        /*
         * AAC lib does not support more than two channels
         * signal error when in decoder mode
         * do not test when in utility mode
         */
        status = 1;

    }

    if (audioObjectType == MP4AUDIO_SBR || audioObjectType == MP4AUDIO_PS)
    {
        /* to disable explicit backward compatiblity check */
        pVars->mc_info.ExtendedAudioObjectType = MP4AUDIO_SBR;
        pVars->mc_info.sbrPresentFlag = 1;

        if (audioObjectType == MP4AUDIO_PS)
        {
            pVars->mc_info.psPresentFlag = 1;
            pVars->mc_info.ExtendedAudioObjectType = MP4AUDIO_PS;
        }

        extensionSamplingFrequencyIndex = /* extensionSamplingFrequencyIndex */
            get9_n_lessbits(LEN_SAMP_RATE_IDX,
                            pInputStream);
        if (extensionSamplingFrequencyIndex == 0x0f)
        {
            /*
             * sampling rate not listed in Table 1.6.2,
             * this release does not support this
                */
            /*sampling_rate = getbits( LEN_SAMP_RATE,
                                     pInputStream);*/
            getbits(LEN_SAMP_RATE, pInputStream);
        }

        audioObjectType = (tMP4AudioObjectType) get9_n_lessbits(LEN_OBJ_TYPE ,
                          pInputStream);
    }


    if ((/*(audioObjectType == MP4AUDIO_AAC_MAIN)     ||*/
                (audioObjectType == MP4AUDIO_AAC_LC)        ||
                /*(audioObjectType == MP4AUDIO_AAC_SSR)       ||*/
                (audioObjectType == MP4AUDIO_LTP)           /*||*/
                /*(audioObjectType == MP4AUDIO_AAC_SCALABLE)  ||*/
                /*(audioObjectType == MP4AUDIO_TWINVQ)*/) && (status == SUCCESS))
    {
        status = get_GA_specific_config(pVars,
                                        pInputStream,
                                        channel_config,
                                        audioObjectType);

        /*
         *  verify that Program config returned a supported audio object type
         */

        if ((pVars->mc_info.audioObjectType != MP4AUDIO_AAC_LC) &&
                (pVars->mc_info.audioObjectType != MP4AUDIO_LTP))
        {
            return 1;   /* status != SUCCESS invalid aot */
        }
    }
    else
    {
        return 1;   /* status != SUCCESS invalid aot or invalid parameter */
    }

    /*
     *  SBR tool explicit signaling ( backward compatible )
     */
    if (extensionAudioObjectType != MP4AUDIO_SBR)
    {
        syncExtensionType = (UInt)get17_n_lessbits(LEN_SYNC_EXTENSION_TYPE,
                            pInputStream);

        if (syncExtensionType == 0x2b7)
        {
            extensionAudioObjectType = get9_n_lessbits( /* extensionAudioObjectType */
                                           LEN_OBJ_TYPE,
                                           pInputStream);

            if (extensionAudioObjectType == MP4AUDIO_SBR)
            {
                pVars->mc_info.sbrPresentFlag = get1bits(pInputStream);  /* sbrPresentFlag */
                if (pVars->mc_info.sbrPresentFlag == 1)
                {
                    extensionSamplingFrequencyIndex =
                        get9_n_lessbits( /* extensionSamplingFrequencyIndex */
                            LEN_SAMP_RATE_IDX,
                            pInputStream);
                    if (pVars->aacPlusEnabled == true)
                    {
#ifdef AAC_PLUS
                        if (extensionSamplingFrequencyIndex < 3)
                        {
                            /*
                             *  Disable SBR/PS for any sampling freq. > 48 KHz
                             *  3GPP request support up to Level 2, == max AAC/SBR present
                             *  24/48 KHz
                             */
                            pVars->aacPlusEnabled = false;
                        }
                        else
                        {
                            pVars->mc_info.upsamplingFactor = (samp_rate_info[extensionSamplingFrequencyIndex].samp_rate >> 1) ==
                                                              samp_rate_info[pVars->prog_config.sampling_rate_idx].samp_rate ? 2 : 1;

                            if ((Int)extensionSamplingFrequencyIndex == pVars->prog_config.sampling_rate_idx)
                            {
                                /*
                                 *  Disable SBR decoding for any sbr-downsampled file whose SF is >= 24 KHz
                                 */
                                if (pVars->prog_config.sampling_rate_idx < 6)
                                {
                                    pVars->aacPlusEnabled = false;
                                }

                                pVars->mc_info.bDownSampledSbr = true;
                            }

                            pVars->prog_config.sampling_rate_idx = extensionSamplingFrequencyIndex;
                        }

#endif
                    }

                    if (extensionSamplingFrequencyIndex == 0x0f)
                    {
                        /*
                         * sampling rate not listed in Table 1.6.2,
                         * this release does not support this
                         */
                        /*sampling_rate = getbits( LEN_SAMP_RATE,
                                                 pInputStream);*/
                        getbits(LEN_SAMP_RATE, pInputStream);
                    }
                    /* syncExtensionType */
                    syncExtensionType = (UInt)get17_n_lessbits(LEN_SYNC_EXTENSION_TYPE,
                                        pInputStream);
                    if (syncExtensionType == 0x548)
                    {
                        pVars->mc_info.psPresentFlag = get1bits(pInputStream);  /* psPresentFlag */
                        if (pVars->mc_info.psPresentFlag)
                        {
                            extensionAudioObjectType = MP4AUDIO_PS;
                        }
                    }
                    else
                    {
                        /*
                        * Rewind bitstream pointer so that the syncExtensionType reading has no
                        * effect when decoding raw bitstream
                            */
                        pVars->inputStream.usedBits -= LEN_SYNC_EXTENSION_TYPE;
                    }

                    pVars->mc_info.ExtendedAudioObjectType = (eMP4AudioObjectType)extensionAudioObjectType;
                }
            }
        }
        else if (!status)
        {
            /*
             * Rewind bitstream pointer so that the syncExtensionType reading has no
             * effect when decoding raw bitstream
             */
            pVars->inputStream.usedBits -= LEN_SYNC_EXTENSION_TYPE;

#ifdef AAC_PLUS

            /*
             *  For implicit signalling, no hint that sbr or ps is used, so we need to
             *  check the sampling frequency of the aac content, if lesser or equal to
             *  24 KHz, by defualt upsample, otherwise, do nothing
             */
            if ((pVars->prog_config.sampling_rate_idx >= 6) && (pVars->aacPlusEnabled == true) &&
                    audioObjectType == MP4AUDIO_AAC_LC)
            {
                pVars->mc_info.upsamplingFactor = 2;
                pVars->prog_config.sampling_rate_idx -= 3;
                pVars->mc_info.sbrPresentFlag = 1;
                pVars->sbrDecoderData.SbrChannel[0].syncState = SBR_NOT_INITIALIZED;
                pVars->sbrDecoderData.SbrChannel[1].syncState = SBR_NOT_INITIALIZED;

            }
            else
            {
                /*
                 *  Disable SBR decoding for any implicit-signalig clip whose SF is > 24 KHz
                 *  sbr-downsampling has to be implicit
                 */
                pVars->aacPlusEnabled = false;
            }
#endif

        }
    }
    else    /*  MP4AUDIO_SBR was detected  */
    {
        /*
         *  Set the real output frequency use by the SBR tool, define tentative upsample ratio
         */
        if (pVars->aacPlusEnabled == true)
        {
#ifdef AAC_PLUS
            pVars->mc_info.upsamplingFactor = (samp_rate_info[extensionSamplingFrequencyIndex].samp_rate >> 1) ==
                                              samp_rate_info[pVars->prog_config.sampling_rate_idx].samp_rate ? 2 : 1;

            if ((Int)extensionSamplingFrequencyIndex == pVars->prog_config.sampling_rate_idx)
            {
                /*
                 *  Disable SBR decoding for any sbr-downsampled file whose SF is >= 24 KHz
                 */
                if (pVars->prog_config.sampling_rate_idx < 6)
                {
                    pVars->aacPlusEnabled = false;
                }
                pVars->mc_info.bDownSampledSbr = true;
            }
            pVars->prog_config.sampling_rate_idx = extensionSamplingFrequencyIndex;



#endif




        }

    }  /*  if ( extensionAudioObjectType != MP4AUDIO_SBR ) */

    /*
     * The following object types are not supported in this release,
     * however, keep these interfaces for future implementation
     */

    /*
     *if (audioObjectType == MP4AUDIO_CELP)
     *{
     *    status = 1;
     *}
     */

    /*
     *if (audioObjectType == MP4AUDIO_HVXC)
     *{
     *    status = 1;
     *}
     */

    /*
     *if (audioObjectType == MP4AUDIO_TTSI)
     *{
     *    status = 1;
     *}
     */

    /*
     *if ((audioObjectType == 13) || (audioObjectType == 14) ||
     *   (audioObjectType == 15) || (audioObjectType == 16))
     *{
     *    status = 1;
     *}
     */

    /* The following objects are Amendment 1 objects */
    /*
     *if (((audioObjectType == MP4AUDIO_ER_AAC_LC)       ||
     *    (audioObjectType == MP4AUDIO_ER_AAC_LTP)      ||
     *    (audioObjectType == MP4AUDIO_ER_AAC_SCALABLE) ||
     *    (audioObjectType == MP4AUDIO_ER_TWINVQ)       ||
     *    (audioObjectType == MP4AUDIO_ER_BSAC)         ||
     *    (audioObjectType == MP4AUDIO_ER_AAC_LD)) && (status == -1))
     *{
     */
    /*
     * should call get_GA_specific_config
     * for this release, do not support Error Resilience
     * temporary solution is set status flag and exit decoding
     */
    /*    status = 1;
    *}
    */

    /*
     *if (audioObjectType == MP4AUDIO_ER_CELP)
     * {
     *    status = 1;
     *}
     */

    /*
     *if (audioObjectType == MP4AUDIO_ER_HVXC)
     *{
     *    status = 1;
     *}
     */

    /*
     *if ((audioObjectType == MP4AUDIO_ER_HILN) ||
     *    (audioObjectType == MP4AUDIO_PARAMETRIC))
     *{
     *    status = 1;
     *}
     */

    /*
     *if ((audioObjectType == MP4AUDIO_ER_AAC_LC)       ||
     *    (audioObjectType == MP4AUDIO_ER_AAC_LTP)      ||
     *    (audioObjectType == MP4AUDIO_ER_AAC_SCALABLE) ||
     *    (audioObjectType == MP4AUDIO_ER_TWINVQ)       ||
     *    (audioObjectType == MP4AUDIO_ER_BSAC)         ||
     *    (audioObjectType == MP4AUDIO_ER_AAC_LD)       ||
     *    (audioObjectType == MP4AUDIO_ER_CELP)         ||
     *    (audioObjectType == MP4AUDIO_ER_HVXC)         ||
     *    (audioObjectType == MP4AUDIO_ER_HILN)         ||
     *    (audioObjectType == MP4AUDIO_PARAMETRIC))
     *{
     */
    /* error protection config */
    /*
     *     epConfig =
     *       getbits(
     *           LEN_EP_CONFIG,
     *           pInputStream);
     *
     *   if (epConfig == 2)
     *   {
     */
    /* should call ErrorProtectionSpecificConfig() */
    /*
     *       status = 1;
     *   }
     *
     *}
     */

    return status;

}
