/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 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.
 * -------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
; INCLUDES
----------------------------------------------------------------------------*/

#include "amr_enc.h"

#if PROFILING_ON
#include "oscl_tickcount.h"
#endif

OmxAmrEncoder::OmxAmrEncoder()
{
    // Codec and encoder setting structure
    ipGsmEncoder = NULL;
    ipEncProps   = NULL;
//    iNextStartTime = 0;

    iOutputFormat             = PVMF_MIME_AMR_IETF;
    ipSizeArrayForOutputFrames = NULL;
//    iNextStartTime            = 0;

    iMaxNumOutputFramesPerBuffer = MAX_NUM_OUTPUT_FRAMES_PER_BUFFER;
    iOneInputFrameLength         = 320;
    iMaxInputSize                = 0;

    iAmrInitFlag = 0;

#if PROFILING_ON
    oscl_memset(&iProfileStats, 0, sizeof(PVEncNodeStats));
#endif
}


/* Decoder Initialization function */
OMX_BOOL OmxAmrEncoder::AmrEncInit(OMX_AUDIO_PARAM_PCMMODETYPE aPcmMode,
                                   OMX_AUDIO_PARAM_AMRTYPE aAmrParam,
                                   OMX_U32* aInputFrameLength,
                                   OMX_U32* aMaxNumberOutputFrames)
{
    OMX_U32 MaxOutputBufferSize;


    iAmrInitFlag = 0;

    ipGsmEncoder = OSCL_NEW(CPvGsmAmrEncoder, ());
    if (!ipGsmEncoder)
    {
        return OMX_FALSE;
    }

    // for encoder properties
    ipEncProps = OSCL_NEW(TEncodeProperties, ());
    if (!ipEncProps)
    {
        return OMX_FALSE;
    }

    //Extracting the output format information
    if (OMX_AUDIO_AMRFrameFormatFSF == aAmrParam.eAMRFrameFormat)
    {
        iOutputFormat = PVMF_MIME_AMR_IETF;
    }
    else if (OMX_AUDIO_AMRFrameFormatIF2 == aAmrParam.eAMRFrameFormat)
    {
        iOutputFormat = PVMF_MIME_AMR_IF2;
    }
    else
    {
        //unsupported format
        return OMX_FALSE;
    }


    if (OMX_AUDIO_AMRBandModeNB0 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_4_75;
    }
    else if (OMX_AUDIO_AMRBandModeNB1 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_5_15;
    }
    else if (OMX_AUDIO_AMRBandModeNB2 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_5_90;
    }
    else if (OMX_AUDIO_AMRBandModeNB3 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_6_70;
    }
    else if (OMX_AUDIO_AMRBandModeNB4 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_7_40;
    }
    else if (OMX_AUDIO_AMRBandModeNB5 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_7_95;
    }
    else if (OMX_AUDIO_AMRBandModeNB6 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_10_2;
    }
    else if (OMX_AUDIO_AMRBandModeNB7 == aAmrParam.eAMRBandMode)
    {
        ipEncProps->iMode = ipGsmEncoder->GSM_AMR_12_2;
    }
    else
    {
        //unsupported mode
        return OMX_FALSE;
    }

    //Adding all the param verification here before allocating them into ipEncProps
    if ((16 != aPcmMode.nBitPerSample) ||
            (8000 != aPcmMode.nSamplingRate) ||
            (1 != aPcmMode.nChannels))
    {
        return OMX_FALSE;
    }


    ipEncProps->iInBitsPerSample  = aPcmMode.nBitPerSample;
    ipEncProps->iInSamplingRate   = aPcmMode.nSamplingRate;
    ipEncProps->iInClockRate      = 1000;
    ipEncProps->iInNumChannels    = (uint8)aPcmMode.nChannels;
    ipEncProps->iInInterleaveMode = TEncodeProperties::EINTERLEAVE_LR;

    ipEncProps->iBitStreamFormat = (iOutputFormat == PVMF_MIME_AMR_IF2);
    ipEncProps->iAudioObjectType    = 0; // only for AAC encoder
    ipEncProps->iOutSamplingRate    = aPcmMode.nSamplingRate;
    ipEncProps->iOutNumChannels     = (uint8)aPcmMode.nChannels;
    ipEncProps->iOutClockRate       = ipEncProps->iInClockRate;

    // initialize the amr encoder
    MaxOutputBufferSize = iMaxNumOutputFramesPerBuffer * MAX_AMR_FRAME_SIZE;
    if (ipGsmEncoder->InitializeEncoder(MaxOutputBufferSize, ipEncProps) < 0)
    {
        return OMX_FALSE;
    }

    ipSizeArrayForOutputFrames = (int32*) oscl_malloc(iMaxNumOutputFramesPerBuffer * sizeof(int32));
    oscl_memset(ipSizeArrayForOutputFrames, 0, iMaxNumOutputFramesPerBuffer * sizeof(int32));

    iOneInputFrameLength = AMR_FRAME_LENGTH_IN_TIMESTAMP * ipEncProps->iInSamplingRate * ipEncProps->iInBitsPerSample / 8000000;
    iMaxInputSize = iMaxNumOutputFramesPerBuffer * iOneInputFrameLength;

    *aInputFrameLength = iOneInputFrameLength;

    *aMaxNumberOutputFrames = iMaxNumOutputFramesPerBuffer;

    return OMX_TRUE;
}


/* Decoder De-Initialization function */
void OmxAmrEncoder::AmrEncDeinit()
{
    if (ipGsmEncoder)
    {
        ipGsmEncoder->CleanupEncoder();
        OSCL_DELETE(ipGsmEncoder);
        ipGsmEncoder = NULL;
    }

    if (ipEncProps)
    {
        OSCL_DELETE(ipEncProps);
        ipEncProps = NULL;
    }

    if (ipSizeArrayForOutputFrames)
    {
        oscl_free(ipSizeArrayForOutputFrames);
        ipSizeArrayForOutputFrames = NULL;
    }
}


/* Decode function for all the input formats */
OMX_BOOL OmxAmrEncoder::AmrEncodeFrame(OMX_U8*    aOutputBuffer,
                                       OMX_U32*   aOutputLength,
                                       OMX_U8*    aInBuffer,
                                       OMX_U32    aInBufSize,
                                       OMX_TICKS  aInTimeStamp,
                                       OMX_TICKS* aOutTimeStamp)
{

    TInputAudioStream StreamInput;
    TOutputAudioStream StreamOutput;
    int32 InputFrameNum;


    StreamOutput.iBitStreamBuffer = (uint8*) aOutputBuffer;
    StreamOutput.iNumSampleFrames = 0;
    StreamOutput.iSampleFrameSize = ipSizeArrayForOutputFrames;

    //Calculate the number of input frames to be encoded
    InputFrameNum = aInBufSize / iOneInputFrameLength;


    StreamInput.iSampleBuffer = (uint8*) aInBuffer;
    StreamInput.iSampleLength = (int32) aInBufSize;
    StreamInput.iMode         = ipEncProps->iMode;
//    StreamInput.iStartTime    = (iNextStartTime >= aInTimeStamp  ? iNextStartTime : aInTimeStamp);
    StreamInput.iStartTime    = aInTimeStamp; // totally rely on IL Client to provide correct timestamp.
    StreamInput.iStopTime     = StreamInput.iStartTime + AMR_FRAME_LENGTH_IN_TIMESTAMP * InputFrameNum;
//    iNextStartTime            = StreamInput.iStopTime; // for the next encoding, not necessary

#if PROFILING_ON
    OMX_U32 Start = OsclTickCount::TickCount();
#endif
    // Do encoding at one time for multiple frame input
    if (ipGsmEncoder->Encode(StreamInput, StreamOutput) < 0 || StreamOutput.iNumSampleFrames != InputFrameNum)
    {
#if PROFILING_ON
        OMX_U32 Stop = OsclTickCount::TickCount();
        iProfileStats.iTotalEncTime += (Stop - Start);
#endif
        return OMX_FALSE;
    }

    // For IETF, make a conversion from WMF
    uint8* TmpBuffer = StreamOutput.iBitStreamBuffer;
    uint32 ii;

    for (ii = 0; ii < (uint32)StreamOutput.iNumSampleFrames; ii++)
    {
        // for IETF format, we need to make change
        if (!ipEncProps->iBitStreamFormat)
        {
            // non-IF2 => IETF format, not WMF format
            TmpBuffer[0] = (uint8)(((TmpBuffer[0] << 3) | 0x4) & 0x7C); // IETF frame header: P(1) + FT(4) + Q(1) + P(2) , Q=1 for good frame, P=padding bit, 0
            TmpBuffer += StreamOutput.iSampleFrameSize[ii];
        }

        // Set fragment length
        *aOutputLength += StreamOutput.iSampleFrameSize[ii];
    }

#if PROFILING_ON
    OMX_U32 Stop = OsclTickCount::TickCount();
    iProfileStats.iTotalEncTime += (Stop - Start);
    iProfileStats.iNumPCMSamplesEncoded += (StreamInput.iSampleLength >> 1);
#endif

    *aOutTimeStamp = StreamInput.iStartTime;

    return OMX_TRUE;
}



