/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * 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.
 */
/**
 ******************************************************************************
 * @file    M4VSS3GPP_AudioMixing.c
 * @brief    Video Studio Service 3GPP audio mixing implementation.
 * @note
 ******************************************************************************
 */

/****************/
/*** Includes ***/
/****************/

#include "NXPSW_CompilerSwitches.h"
/**
 * Our headers */
#include "M4VSS3GPP_API.h"
#include "M4VSS3GPP_InternalTypes.h"
#include "M4VSS3GPP_InternalFunctions.h"
#include "M4VSS3GPP_ErrorCodes.h"

/* Put the definition of silence frames here */
#define M4VSS3GPP_SILENCE_FRAMES
#include "M4VSS3GPP_InternalConfig.h"

/**
 * OSAL headers */
#include "M4OSA_Memory.h" /**< OSAL memory management */
#include "M4OSA_Debug.h"  /**< OSAL debug management */


#include "VideoEditorResampler.h"
/**
 ******************************************************************************
 * @brief    Static functions
 ******************************************************************************
 */
static M4OSA_ERR
M4VSS3GPP_intAudioMixingOpen( M4VSS3GPP_InternalAudioMixingContext *pC,
                             M4VSS3GPP_AudioMixingSettings *pSettings );
static M4OSA_ERR M4VSS3GPP_intAudioMixingStepVideo(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingStepAudioMix(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingStepAudioReplace(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingCopyOrig(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingCopyAdded(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingConvert(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingDoMixing(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingWriteSilence(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingTransition(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingCreateVideoEncoder(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_ERR M4VSS3GPP_intAudioMixingDestroyVideoEncoder(
    M4VSS3GPP_InternalAudioMixingContext *pC );
static M4OSA_Bool M4VSS3GPP_isThresholdBreached( M4OSA_Int32 *averageValue,
                                                M4OSA_Int32 storeCount,
                                                M4OSA_Int32 thresholdValue );
/**
 *    Internal warning */
#define M4VSS3GPP_WAR_END_OF_ADDED_AUDIO    M4OSA_ERR_CREATE( M4_WAR, M4VSS3GPP, 0x0030)

/* A define used with SSRC 1.04 and above to avoid taking
blocks smaller that the minimal block size */
#define M4VSS_SSRC_MINBLOCKSIZE        600

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_audioMixingInit(M4VSS3GPP_AudioMixingContext* pContext,
 *                                     M4VSS3GPP_AudioMixingSettings* pSettings)
 * @brief    Initializes the VSS audio mixing operation (allocates an execution context).
 * @note
 * @param    pContext        (OUT) Pointer on the VSS audio mixing context to allocate
 * @param    pSettings        (IN) Pointer to valid audio mixing settings
 * @param    pFileReadPtrFct        (IN) Pointer to OSAL file reader functions
 * @param   pFileWritePtrFct    (IN) Pointer to OSAL file writer functions
 * @return    M4NO_ERROR:            No error
 * @return    M4ERR_PARAMETER:    At least one parameter is M4OSA_NULL (debug only)
 * @return    M4ERR_ALLOC:        There is no more available memory
 ******************************************************************************
 */

M4OSA_ERR M4VSS3GPP_audioMixingInit( M4VSS3GPP_AudioMixingContext *pContext,
                                    M4VSS3GPP_AudioMixingSettings *pSettings,
                                    M4OSA_FileReadPointer *pFileReadPtrFct,
                                    M4OSA_FileWriterPointer *pFileWritePtrFct )
{
    M4VSS3GPP_InternalAudioMixingContext *pC;
    M4OSA_ERR err;

    M4OSA_TRACE3_2(
        "M4VSS3GPP_audioMixingInit called with pContext=0x%x, pSettings=0x%x",
        pContext, pSettings);

    /**
    * Check input parameters */
    M4OSA_DEBUG_IF2((M4OSA_NULL == pContext), M4ERR_PARAMETER,
        "M4VSS3GPP_audioMixingInit: pContext is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == pSettings), M4ERR_PARAMETER,
        "M4VSS3GPP_audioMixingInit: pSettings is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == pFileReadPtrFct), M4ERR_PARAMETER,
        "M4VSS3GPP_audioMixingInit: pFileReadPtrFct is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == pFileWritePtrFct), M4ERR_PARAMETER,
        "M4VSS3GPP_audioMixingInit: pFileWritePtrFct is M4OSA_NULL");

    if( pSettings->uiBeginLoop > pSettings->uiEndLoop )
    {
        M4OSA_TRACE1_0(
            "M4VSS3GPP_audioMixingInit: Begin loop time is higher than end loop time!");
        return M4VSS3GPP_ERR_BEGINLOOP_HIGHER_ENDLOOP;
    }

    /**
    * Allocate the VSS audio mixing context and return it to the user */
    pC = (M4VSS3GPP_InternalAudioMixingContext
        *)M4OSA_32bitAlignedMalloc(sizeof(M4VSS3GPP_InternalAudioMixingContext),
        M4VSS3GPP,(M4OSA_Char *)"M4VSS3GPP_InternalAudioMixingContext");
    *pContext = pC;

    if( M4OSA_NULL == pC )
    {
        M4OSA_TRACE1_0(
            "M4VSS3GPP_audioMixingInit(): unable to allocate \
            M4VSS3GPP_InternalAudioMixingContext,returning M4ERR_ALLOC");
        return M4ERR_ALLOC;
    }

    /* Initialization of context Variables */
    memset((void *)pC ,0,
                 sizeof(M4VSS3GPP_InternalAudioMixingContext));
    /**
    * Copy this setting in context */
    pC->iAddCts = pSettings->uiAddCts;
    pC->bRemoveOriginal = pSettings->bRemoveOriginal;
    pC->b_DuckingNeedeed = pSettings->b_DuckingNeedeed;
    pC->InDucking_threshold = pSettings->InDucking_threshold;
    pC->fBTVolLevel = pSettings->fBTVolLevel;
    pC->fPTVolLevel = pSettings->fPTVolLevel;
    pC->InDucking_lowVolume = pSettings->InDucking_lowVolume;
    pC->bDoDucking = M4OSA_FALSE;
    pC->bLoop = pSettings->bLoop;
    pC->bNoLooping = M4OSA_FALSE;
    pC->bjumpflag = M4OSA_TRUE;
    /**
    * Init some context variables */

    pC->pInputClipCtxt = M4OSA_NULL;
    pC->pAddedClipCtxt = M4OSA_NULL;
    pC->fOrigFactor = 1.0F;
    pC->fAddedFactor = 0.0F;
    pC->bSupportSilence = M4OSA_FALSE;
    pC->bHasAudio = M4OSA_FALSE;
    pC->bAudioMixingIsNeeded = M4OSA_FALSE;

    /* Init PC->ewc members */
    // Decorrelate input and output encoding timestamp to handle encoder prefetch
    pC->ewc.VideoStreamType = M4SYS_kVideoUnknown;
    pC->ewc.bVideoDataPartitioning = M4OSA_FALSE;
    pC->ewc.pVideoOutputDsi = M4OSA_NULL;
    pC->ewc.AudioStreamType = M4SYS_kAudioUnknown;
    pC->ewc.uiNbChannels = 1;
    pC->ewc.pAudioOutputDsi = M4OSA_NULL;
    pC->ewc.pAudioEncCtxt = M4OSA_NULL;
    pC->ewc.pAudioEncDSI.pInfo = M4OSA_NULL;
    pC->ewc.pSilenceFrameData = M4OSA_NULL;
    pC->ewc.pEncContext = M4OSA_NULL;
    pC->ewc.pDummyAuBuffer = M4OSA_NULL;
    pC->ewc.p3gpWriterContext = M4OSA_NULL;
    pC->pLVAudioResampler = M4OSA_NULL;
    /**
    * Set the OSAL filesystem function set */
    pC->pOsaFileReadPtr = pFileReadPtrFct;
    pC->pOsaFileWritPtr = pFileWritePtrFct;

    /**
    * Ssrc stuff */
    pC->b_SSRCneeded = M4OSA_FALSE;
    pC->pSsrcBufferIn = M4OSA_NULL;
    pC->pSsrcBufferOut = M4OSA_NULL;
    pC->pTempBuffer = M4OSA_NULL;
    pC->pPosInTempBuffer = M4OSA_NULL;
    pC->pPosInSsrcBufferIn = M4OSA_NULL;
    pC->pPosInSsrcBufferOut = M4OSA_NULL;
    pC->SsrcScratch = M4OSA_NULL;
    pC->uiBeginLoop = pSettings->uiBeginLoop;
    pC->uiEndLoop = pSettings->uiEndLoop;

    /*
    * Reset pointers for media and codecs interfaces */
    err = M4VSS3GPP_clearInterfaceTables(&pC->ShellAPI);
    M4ERR_CHECK_RETURN(err);

    /*  Call the media and codecs subscription module */
    err = M4VSS3GPP_subscribeMediaAndCodec(&pC->ShellAPI);
    M4ERR_CHECK_RETURN(err);

    /**
    * Open input clip, added clip and output clip and proceed with the settings */
    err = M4VSS3GPP_intAudioMixingOpen(pC, pSettings);
    M4ERR_CHECK_RETURN(err);

    /**
    * Update main state automaton */
    if( M4OSA_NULL != pC->pInputClipCtxt->pVideoStream )
        pC->State = M4VSS3GPP_kAudioMixingState_VIDEO;
    else
        pC->State = M4VSS3GPP_kAudioMixingState_AUDIO_FIRST_SEGMENT;

    pC->ewc.iOutputDuration = (M4OSA_Int32)pC->pInputClipCtxt->pSettings->
        ClipProperties.uiClipDuration;
    /*gInputParams.lvBTChannelCount*/
    pC->pLVAudioResampler = LVAudioResamplerCreate(16,
        pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels,
        /* gInputParams.lvOutSampleRate*/(M4OSA_Int32)pSettings->outputASF, 1);
     if( M4OSA_NULL == pC->pLVAudioResampler )
     {
         return M4ERR_ALLOC;
     }
        LVAudiosetSampleRate(pC->pLVAudioResampler,
        /*gInputParams.lvInSampleRate*/
        pC->pAddedClipCtxt->pSettings->ClipProperties.uiSamplingFrequency);

    LVAudiosetVolume(pC->pLVAudioResampler,
                    (M4OSA_Int16)(0x1000 ),
                    (M4OSA_Int16)(0x1000 ));

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_audioMixingInit(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_audioMixingStep(M4VSS3GPP_AudioMixingContext pContext)
 * @brief    Perform one step of audio mixing.
 * @note
 * @param     pContext          (IN) VSS audio mixing context
 * @return    M4NO_ERROR:       No error
 * @return    M4ERR_PARAMETER:  pContext is M4OSA_NULL (debug only)
 * @param     pProgress         (OUT) Progress percentage (0 to 100) of the finalization operation
 * @return    M4ERR_STATE:      VSS is not in an appropriate state for this function to be called
 * @return    M4VSS3GPP_WAR_END_OF_AUDIO_MIXING: Audio mixing is over, user should now call
 *                                               M4VSS3GPP_audioMixingCleanUp()
 ******************************************************************************
 */
M4OSA_ERR M4VSS3GPP_audioMixingStep( M4VSS3GPP_AudioMixingContext pContext,
                                    M4OSA_UInt8 *pProgress )
{
    M4OSA_ERR err;
    M4VSS3GPP_InternalAudioMixingContext *pC =
        (M4VSS3GPP_InternalAudioMixingContext *)pContext;

    M4OSA_TRACE3_1("M4VSS3GPP_audioMixingStep called with pContext=0x%x",
        pContext);

    /**
    * Check input parameters */
    M4OSA_DEBUG_IF2((M4OSA_NULL == pContext), M4ERR_PARAMETER,
        "M4VSS3GPP_audioMixingStep: pContext is M4OSA_NULL");

    /**
    * State automaton */
    switch( pC->State )
    {
        case M4VSS3GPP_kAudioMixingState_VIDEO:
            err = M4VSS3GPP_intAudioMixingStepVideo(pC);

            /**
            * Compute the progress percentage
            * Note: audio and video CTS are not initialized before
            * the call of M4VSS3GPP_intAudioMixingStepVideo */

            /* P4ME00003276: First 0-50% segment is dedicated to state :
               M4VSS3GPP_kAudioMixingState_VIDEO */
            *pProgress = (M4OSA_UInt8)(50 * (pC->ewc.WriterVideoAU.CTS)
                / pC->pInputClipCtxt->pVideoStream->
                m_basicProperties.m_duration);

            /**
            * There may be no audio track (Remove audio track feature).
            * In that case we double the current percentage */
            if( M4SYS_kAudioUnknown == pC->ewc.WriterAudioStream.streamType )
            {
                ( *pProgress) <<= 1; /**< x2 */
            }
            else if( *pProgress >= 50 )
            {
                *pProgress =
                    49; /**< Video processing is not greater than 50% */
            }

            if( M4WAR_NO_MORE_AU == err )
            {
                if( pC->bHasAudio )
                {
                    /**
                    * Video is over, state transition to audio and return OK */
                    if( pC->iAddCts > 0 )
                        pC->State =
                        M4VSS3GPP_kAudioMixingState_AUDIO_FIRST_SEGMENT;
                    else
                        pC->State =
                        M4VSS3GPP_kAudioMixingState_AUDIO_SECOND_SEGMENT;
                }
                else
                {
                    /**
                    * No audio, state transition to FINISHED */
                    pC->State = M4VSS3GPP_kAudioMixingState_FINISHED;
                }

                return M4NO_ERROR;
            }
            else if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_audioMixingStep: M4VSS3GPP_intAudioMixingStepVideo returns 0x%x!",
                    err);
                return err;
            }
            else
            {
                return M4NO_ERROR;
            }
            break;

        case M4VSS3GPP_kAudioMixingState_AUDIO_FIRST_SEGMENT:
        case M4VSS3GPP_kAudioMixingState_AUDIO_SECOND_SEGMENT:
        case M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT:
            if( pC->pAddedClipCtxt->iAudioFrameCts
                != -pC->pAddedClipCtxt->iSilenceFrameDuration
                && (pC->pAddedClipCtxt->iAudioFrameCts - 0.5)
                / pC->pAddedClipCtxt->scale_audio > pC->uiEndLoop
                && pC->uiEndLoop > 0 )
            {
            if(pC->bLoop == M4OSA_FALSE)
            {
                pC->bNoLooping = M4OSA_TRUE;
            }
            else
            {
                M4OSA_Int32 jumpCTS = (M4OSA_Int32)(pC->uiBeginLoop);

                err = pC->pAddedClipCtxt->ShellAPI.m_pReader->m_pFctJump(
                    pC->pAddedClipCtxt->pReaderContext,
                    (M4_StreamHandler *)pC->pAddedClipCtxt->
                    pAudioStream, &jumpCTS);

                if( err != M4NO_ERROR )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_audioMixingStep: error when jumping in added audio clip: 0x%x",
                        err);
                    return err;
                }
                /**
                * Use offset to give a correct CTS ... */
                pC->pAddedClipCtxt->iAoffset =
                    (M4OSA_Int32)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5);
            }

            }

            if( M4OSA_FALSE == pC->bRemoveOriginal )
            {
                err = M4VSS3GPP_intAudioMixingStepAudioMix(pC);
            }
            else
            {
                err = M4VSS3GPP_intAudioMixingStepAudioReplace(pC);
            }

            /**
            * Compute the progress percentage
            * Note: audio and video CTS are not initialized before
            * the call of M4VSS3GPP_intAudioMixingStepAudio */
            if( 0 != pC->ewc.iOutputDuration )
            {
                /* P4ME00003276: Second 50-100% segment is dedicated to states :
                M4VSS3GPP_kAudioMixingState_AUDIO... */
                /* For Audio the progress computation is based on dAto and offset,
                   it is more accurate */
                *pProgress = (M4OSA_UInt8)(50
                    + (50 * pC->ewc.dATo - pC->pInputClipCtxt->iVoffset)
                    / (pC->ewc.iOutputDuration)); /**< 50 for 100/2 **/

                if( *pProgress >= 100 )
                {
                    *pProgress =
                        99; /**< It's not really finished, I prefer to return less than 100% */
                }
            }
            else
            {
                *pProgress = 99;
            }

            if( M4WAR_NO_MORE_AU == err )
            {
                /**
                * Audio is over, state transition to FINISHED */
                pC->State = M4VSS3GPP_kAudioMixingState_FINISHED;
                return M4NO_ERROR;
            }
            else if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_audioMixingStep: M4VSS3GPP_intAudioMixingStepAudio returns 0x%x!",
                    err);
                return err;
            }
            else
            {
                return M4NO_ERROR;
            }
            break;

        case M4VSS3GPP_kAudioMixingState_FINISHED:

            /**
            * Progress percentage: finalize finished -> 100% */
            *pProgress = 100;

            /**
            * Audio mixing is finished, return correct warning */
            return M4VSS3GPP_WAR_END_OF_AUDIO_MIXING;

        default:
            M4OSA_TRACE1_1(
                "M4VSS3GPP_audioMixingStep: State error (0x%x)! Returning M4ERR_STATE",
                pC->State);
            return M4ERR_STATE;
    }
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_audioMixingCleanUp(M4VSS3GPP_AudioMixingContext pContext)
 * @brief    Free all resources used by the VSS audio mixing operation.
 * @note    The context is no more valid after this call
 * @param    pContext            (IN) VSS audio mixing context
 * @return    M4NO_ERROR:            No error
 * @return    M4ERR_PARAMETER:    pContext is M4OSA_NULL (debug only)
 ******************************************************************************
 */
M4OSA_ERR M4VSS3GPP_audioMixingCleanUp( M4VSS3GPP_AudioMixingContext pContext )
{
    M4VSS3GPP_InternalAudioMixingContext *pC =
        (M4VSS3GPP_InternalAudioMixingContext *)pContext;
    M4OSA_ERR err;
    M4OSA_UInt32 lastCTS;

    M4OSA_TRACE3_1("M4VSS3GPP_audioMixingCleanUp called with pContext=0x%x",
        pContext);

    /**
    * Check input parameters */
    M4OSA_DEBUG_IF2((M4OSA_NULL == pContext), M4ERR_PARAMETER,
        "M4VSS3GPP_audioMixingCleanUp: pContext is M4OSA_NULL");

    /**
    * Check input parameter */
    if( M4OSA_NULL == pContext )
    {
        M4OSA_TRACE1_0(
            "M4VSS3GPP_audioMixingCleanUp(): M4VSS3GPP_audioMixingCleanUp: pContext is\
             M4OSA_NULL, returning M4ERR_PARAMETER");
        return M4ERR_PARAMETER;
    }

    /**
    * Close Input 3GPP file */
    if( M4OSA_NULL != pC->pInputClipCtxt )
    {
        M4VSS3GPP_intClipCleanUp(pC->pInputClipCtxt);
        pC->pInputClipCtxt = M4OSA_NULL;
    }

    /**
    * Close Added 3GPP file */
    if( M4OSA_NULL != pC->pAddedClipCtxt )
    {
        M4VSS3GPP_intClipCleanUp(pC->pAddedClipCtxt);
        pC->pAddedClipCtxt = M4OSA_NULL;
    }

    /**
    * Close the 3GP writer. In normal use case it has already been closed,
      but not in abort use case */
    if( M4OSA_NULL != pC->ewc.p3gpWriterContext )
    {
        /* Update last Video CTS */
        lastCTS = pC->ewc.iOutputDuration;

        err = pC->ShellAPI.pWriterGlobalFcts->pFctSetOption(
            pC->ewc.p3gpWriterContext,
            (M4OSA_UInt32)M4WRITER_kMaxFileDuration, &lastCTS);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_audioMixingCleanUp: SetOption(M4WRITER_kMaxFileDuration) returns 0x%x",
                err);
        }

        err = pC->ShellAPI.pWriterGlobalFcts->pFctCloseWrite(
            pC->ewc.p3gpWriterContext);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_audioMixingCleanUp: pWriterGlobalFcts->pFctCloseWrite returns 0x%x!",
                err);
            /**< don't return the error because we have other things to free! */
        }
        pC->ewc.p3gpWriterContext = M4OSA_NULL;
    }

    /**
    * Free the Audio encoder context */
    if( M4OSA_NULL != pC->ewc.pAudioEncCtxt )
    {
        err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctClose(
            pC->ewc.pAudioEncCtxt);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_audioMixingCleanUp: pAudioEncoderGlobalFcts->pFctClose returns 0x%x",
                err);
            /**< don't return, we still have stuff to free */
        }

        err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctCleanUp(
            pC->ewc.pAudioEncCtxt);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_audioMixingCleanUp: pAudioEncoderGlobalFcts->pFctCleanUp returns 0x%x",
                err);
            /**< don't return, we still have stuff to free */
        }

        pC->ewc.pAudioEncCtxt = M4OSA_NULL;
    }

    /**
    * Free the ssrc stuff */

    if( M4OSA_NULL != pC->SsrcScratch )
    {
        free(pC->SsrcScratch);
        pC->SsrcScratch = M4OSA_NULL;
    }

    if( M4OSA_NULL != pC->pSsrcBufferIn )
    {
        free(pC->pSsrcBufferIn);
        pC->pSsrcBufferIn = M4OSA_NULL;
    }

    if( M4OSA_NULL != pC->pSsrcBufferOut
        && (M4OSA_TRUE == pC->b_SSRCneeded || pC->ChannelConversion > 0) )
    {
        free(pC->pSsrcBufferOut);
        pC->pSsrcBufferOut = M4OSA_NULL;
    }

    if( M4OSA_NULL != pC->pTempBuffer )
    {
        free(pC->pTempBuffer);
        pC->pTempBuffer = M4OSA_NULL;
    }

    if (pC->pLVAudioResampler != M4OSA_NULL)
    {
        LVDestroy(pC->pLVAudioResampler);
        pC->pLVAudioResampler = M4OSA_NULL;
    }

    /**
    * Free the shells interfaces */
    M4VSS3GPP_unRegisterAllWriters(&pC->ShellAPI);
    M4VSS3GPP_unRegisterAllEncoders(&pC->ShellAPI);
    M4VSS3GPP_unRegisterAllReaders(&pC->ShellAPI);
    M4VSS3GPP_unRegisterAllDecoders(&pC->ShellAPI);

    /**
    * Free the context */
    free(pContext);
    pContext = M4OSA_NULL;

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_audioMixingCleanUp(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/******************************************************************************/
/******************************************************************************/
/*********                  STATIC FUNCTIONS                         **********/
/******************************************************************************/
/******************************************************************************/

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingOpen()
 * @brief    Initializes the VSS audio mixing operation (allocates an execution context).
 * @note
 * @param    pContext        (OUT) Pointer on the VSS audio mixing context to allocate
 * @param    pSettings        (IN) Pointer to valid audio mixing settings
 * @return    M4NO_ERROR:            No error
 * @return    M4ERR_PARAMETER:    At least one parameter is M4OSA_NULL (debug only)
 * @return    M4ERR_ALLOC:        There is no more available memory
 ******************************************************************************
 */
static M4OSA_ERR
M4VSS3GPP_intAudioMixingOpen( M4VSS3GPP_InternalAudioMixingContext *pC,
                             M4VSS3GPP_AudioMixingSettings *pSettings )
{
    M4OSA_ERR err;
    M4OSA_UInt32 outputASF = 0;
    M4ENCODER_Header *encHeader;

    M4OSA_TRACE3_2(
        "M4VSS3GPP_intAudioMixingOpen called with pContext=0x%x, pSettings=0x%x",
        pC, pSettings);

    /**
    * The Add Volume must be (strictly) superior than zero */
    if( pSettings->uiAddVolume == 0 )
    {
        M4OSA_TRACE1_0(
            "M4VSS3GPP_intAudioMixingOpen(): AddVolume is zero,\
            returning M4VSS3GPP_ERR_ADDVOLUME_EQUALS_ZERO");
        return M4VSS3GPP_ERR_ADDVOLUME_EQUALS_ZERO;
    }
    /*
    else if(pSettings->uiAddVolume >= 100) // If volume is set to 100, no more original audio ...
    {
    pC->bRemoveOriginal = M4OSA_TRUE;
    }
    */
    /**
    * Build the input clip settings */
    pC->InputClipSettings.pFile =
        pSettings->pOriginalClipFile; /**< Input 3GPP file descriptor */
    pC->InputClipSettings.FileType = M4VIDEOEDITING_kFileType_3GPP;
    pC->InputClipSettings.uiBeginCutTime =
        0; /**< No notion of cut for the audio mixing feature */
    pC->InputClipSettings.uiEndCutTime =
        0; /**< No notion of cut for the audio mixing feature */

    /**
    * Open the original Audio/Video 3GPP clip */
    err = M4VSS3GPP_intClipInit(&pC->pInputClipCtxt, pC->pOsaFileReadPtr);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intClipInit(orig) returns 0x%x",
            err);
        return err;
    }

    err = M4VSS3GPP_intClipOpen(pC->pInputClipCtxt, &pC->InputClipSettings,
        M4OSA_FALSE, M4OSA_FALSE, M4OSA_TRUE);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intClipOpen(orig) returns 0x%x",
            err);
        return err;
    }

    if( M4OSA_NULL == pC->pInputClipCtxt->pAudioStream )
        {
        pC->bRemoveOriginal = M4OSA_TRUE;
        }
    /**
    * If there is no video, it's an error */
    if( M4OSA_NULL == pC->pInputClipCtxt->pVideoStream )
    {
        M4OSA_TRACE1_0(
            "M4VSS3GPP_intAudioMixingOpen(): no video stream in clip,\
            returning M4VSS3GPP_ERR_NO_SUPPORTED_STREAM_IN_FILE");
        return M4VSS3GPP_ERR_NO_SUPPORTED_STREAM_IN_FILE;
    }

    /**
    * Compute clip properties */
    err = M4VSS3GPP_intBuildAnalysis(pC->pInputClipCtxt,
        &pC->pInputClipCtxt->pSettings->ClipProperties);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intBuildAnalysis(orig) returns 0x%x",
            err);
        return err;
    }

    /**
    * Build the added clip settings */
    pC->AddedClipSettings.pFile =
        pSettings->pAddedAudioTrackFile; /**< Added file descriptor */
    pC->AddedClipSettings.FileType = pSettings->AddedAudioFileType;
    pC->AddedClipSettings.uiBeginCutTime =
        0; /**< No notion of cut for the audio mixing feature */
    pC->AddedClipSettings.uiEndCutTime   = 0;/**< No notion of cut for the audio mixing feature */
    pC->AddedClipSettings.ClipProperties.uiNbChannels=
        pSettings->uiNumChannels;
    pC->AddedClipSettings.ClipProperties.uiSamplingFrequency=    pSettings->uiSamplingFrequency;

    if( M4OSA_NULL != pC->AddedClipSettings.pFile )
    {
        /**
        * Open the added Audio clip */
        err = M4VSS3GPP_intClipInit(&pC->pAddedClipCtxt, pC->pOsaFileReadPtr);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intClipInit(added) returns 0x%x",
                err);
            return err;
        }

        err = M4VSS3GPP_intClipOpen(pC->pAddedClipCtxt, &pC->AddedClipSettings,
            M4OSA_FALSE, M4OSA_FALSE, M4OSA_TRUE);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intClipOpen(added) returns 0x%x",
                err);
            return err;
        }

        /**
        * If there is no audio, it's an error */
        if( M4OSA_NULL == pC->pAddedClipCtxt->pAudioStream )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen(): no audio nor video stream in clip,\
                returning M4VSS3GPP_ERR_NO_SUPPORTED_STREAM_IN_FILE");
            return M4VSS3GPP_ERR_NO_SUPPORTED_STREAM_IN_FILE;
        }

        /**
        * Compute clip properties */
        err = M4VSS3GPP_intBuildAnalysis(pC->pAddedClipCtxt,
            &pC->pAddedClipCtxt->pSettings->ClipProperties);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intBuildAnalysis(added) returns 0x%x",
                err);
            return err;
        }

        switch( pSettings->outputASF )
        {
            case M4VIDEOEDITING_k8000_ASF:
                outputASF = 8000;
                break;

            case M4VIDEOEDITING_k16000_ASF:
                outputASF = 16000;
                break;

            case M4VIDEOEDITING_k22050_ASF:
                outputASF = 22050;
                break;

            case M4VIDEOEDITING_k24000_ASF:
                outputASF = 24000;
                break;

            case M4VIDEOEDITING_k32000_ASF:
                outputASF = 32000;
                break;

            case M4VIDEOEDITING_k44100_ASF:
                outputASF = 44100;
                break;

            case M4VIDEOEDITING_k48000_ASF:
                outputASF = 48000;
                break;

            default:
                M4OSA_TRACE1_0("Bad parameter in output ASF ");
                return M4ERR_PARAMETER;
                break;
        }

        if( pC->bRemoveOriginal == M4OSA_TRUE
            && (pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
            == M4VIDEOEDITING_kMP3 || pC->pAddedClipCtxt->pSettings->
            ClipProperties.AudioStreamType == M4VIDEOEDITING_kPCM
            || pC->pAddedClipCtxt->pSettings->
            ClipProperties.AudioStreamType
            != pSettings->outputAudioFormat
            || pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiSamplingFrequency != outputASF
            || pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiNbChannels
            != pSettings->outputNBChannels) )
        {

            if( pSettings->outputAudioFormat == M4VIDEOEDITING_kAMR_NB )
            {
                pSettings->outputASF = M4VIDEOEDITING_k8000_ASF;
                pSettings->outputNBChannels = 1;
                pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize = 320;
            }
            else if( pSettings->outputAudioFormat == M4VIDEOEDITING_kAAC )
            {
                pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize =
                    2048 * pSettings->outputNBChannels;
            }

            pC->pInputClipCtxt->pSettings->ClipProperties.uiSamplingFrequency =
                outputASF;

            if( outputASF != pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiSamplingFrequency )
            {
                /* We need to call SSRC in order to align ASF and/or nb of channels */
                /* Moreover, audio encoder may be needed in case of audio replacing... */
                pC->b_SSRCneeded = M4OSA_TRUE;
            }

            if( pSettings->outputNBChannels
                < pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels )
            {
                /* Stereo to Mono */
                pC->ChannelConversion = 1;
            }
            else if( pSettings->outputNBChannels
                > pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels )
            {
                /* Mono to Stereo */
                pC->ChannelConversion = 2;
            }

            pC->pInputClipCtxt->pSettings->ClipProperties.uiNbChannels =
                pSettings->outputNBChannels;
        }

        /**
        * Check compatibility chart */
        err = M4VSS3GPP_intAudioMixingCompatibility(pC,
            &pC->pInputClipCtxt->pSettings->ClipProperties,
            &pC->pAddedClipCtxt->pSettings->ClipProperties);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingOpen():\
                M4VSS3GPP_intAudioMixingCompatibility returns 0x%x",
                err);
            return err;
        }

        /**
        * Check loop parameters */
        if( pC->uiBeginLoop > pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiClipAudioDuration )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen():\
                begin loop time is higher than added clip audio duration");
            return M4VSS3GPP_ERR_BEGINLOOP_HIGHER_ENDLOOP;
        }

        /**
        * Ok, let's go with this audio track */
        pC->bHasAudio = M4OSA_TRUE;
    }
    else
    {
        /* No added file, force remove original */
        pC->AddedClipSettings.FileType = M4VIDEOEDITING_kFileType_Unsupported;
        pC->bRemoveOriginal = M4OSA_TRUE;
        pC->bHasAudio = M4OSA_FALSE;
    }

    /**
    * Copy the video properties of the input clip to the output properties */
    pC->ewc.uiVideoBitrate =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoBitrate;
    pC->ewc.uiVideoWidth =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoWidth;
    pC->ewc.uiVideoHeight =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoHeight;
    pC->ewc.uiVideoTimeScale =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoTimeScale;
    pC->ewc.bVideoDataPartitioning =
        pC->pInputClipCtxt->pSettings->ClipProperties.bMPEG4dataPartition;
    pC->ewc.outputVideoProfile =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoProfile;
    pC->ewc.outputVideoLevel =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoLevel;
    switch( pC->pInputClipCtxt->pSettings->ClipProperties.VideoStreamType )
    {
        case M4VIDEOEDITING_kH263:
            pC->ewc.VideoStreamType = M4SYS_kH263;
            break;

        case M4VIDEOEDITING_kMPEG4:
            pC->ewc.VideoStreamType = M4SYS_kMPEG_4;
            break;

        case M4VIDEOEDITING_kH264:
            pC->ewc.VideoStreamType = M4SYS_kH264;
            break;

        default:
            pC->ewc.VideoStreamType = M4SYS_kVideoUnknown;
            break;
    }

    /* Add a link to video dsi */
    if( M4SYS_kH264 == pC->ewc.VideoStreamType )
    {

        /* For H.264 encoder case
        * Fetch the DSI from the shell video encoder, and feed it to the writer */

        M4OSA_TRACE3_0("M4VSS3GPP_intAudioMixingOpen: get DSI for H264 stream");

        if( M4OSA_NULL == pC->ewc.pEncContext )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen: pC->ewc.pEncContext is NULL");
            err = M4VSS3GPP_intAudioMixingCreateVideoEncoder(pC);

            if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingOpen:\
                    M4VSS3GPP_intAudioMixingCreateVideoEncoder returned error 0x%x",
                    err);
            }
        }

        if( M4OSA_NULL != pC->ewc.pEncContext )
        {
            err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctGetOption(
                pC->ewc.pEncContext, M4ENCODER_kOptionID_EncoderHeader,
                (M4OSA_DataOption) &encHeader);

            if( ( M4NO_ERROR != err) || (M4OSA_NULL == encHeader->pBuf) )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingOpen: failed to get the encoder header (err 0x%x)",
                    err);
                M4OSA_TRACE1_2(
                    "M4VSS3GPP_intAudioMixingOpen: encHeader->pBuf=0x%x, size=0x%x",
                    encHeader->pBuf, encHeader->Size);
            }
            else
            {
                M4OSA_TRACE1_0(
                    "M4VSS3GPP_intAudioMixingOpen: send DSI for H264 stream to 3GP writer");

                /**
                * Allocate and copy the new DSI */
                pC->ewc.pVideoOutputDsi =
                    (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(encHeader->Size, M4VSS3GPP,
                    (M4OSA_Char *)"pC->ewc.pVideoOutputDsi (H264)");

                if( M4OSA_NULL == pC->ewc.pVideoOutputDsi )
                {
                    M4OSA_TRACE1_0(
                        "M4VSS3GPP_intAudioMixingOpen():\
                        unable to allocate pVideoOutputDsi (H264), returning M4ERR_ALLOC");
                    return M4ERR_ALLOC;
                }
                pC->ewc.uiVideoOutputDsiSize = (M4OSA_UInt16)encHeader->Size;
                memcpy((void *)pC->ewc.pVideoOutputDsi, (void *)encHeader->pBuf,
                    encHeader->Size);
            }

            err = M4VSS3GPP_intAudioMixingDestroyVideoEncoder(pC);

            if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingOpen:\
                    M4VSS3GPP_intAudioMixingDestroyVideoEncoder returned error 0x%x",
                    err);
            }
        }
        else
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen: pC->ewc.pEncContext is NULL, cannot get the DSI");
        }
    }
    else
    {
        M4OSA_TRACE3_1(
            "M4VSS3GPP_intAudioMixingOpen: input clip video stream type = 0x%x",
            pC->ewc.VideoStreamType);
        pC->ewc.uiVideoOutputDsiSize =
            (M4OSA_UInt16)pC->pInputClipCtxt->pVideoStream->
            m_basicProperties.m_decoderSpecificInfoSize;
        pC->ewc.pVideoOutputDsi = (M4OSA_MemAddr8)pC->pInputClipCtxt->pVideoStream->
            m_basicProperties.m_pDecoderSpecificInfo;
    }

    /**
    * Copy the audio properties of the added clip to the output properties */
    if( pC->bHasAudio )
    {
        if( pC->bRemoveOriginal == M4OSA_TRUE )
        {
            pC->ewc.uiNbChannels =
                pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels;
            pC->ewc.uiAudioBitrate =
                pC->pAddedClipCtxt->pSettings->ClipProperties.uiAudioBitrate;
            pC->ewc.uiSamplingFrequency = pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiSamplingFrequency;
            pC->ewc.uiSilencePcmSize =
                pC->pAddedClipCtxt->pSettings->ClipProperties.uiDecodedPcmSize;
            pC->ewc.scale_audio = pC->ewc.uiSamplingFrequency / 1000.0;

            /* if output settings are differents from added clip settings,
            we need to reencode BGM */
            if( pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
                != pSettings->outputAudioFormat
                || pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiSamplingFrequency != outputASF
                || pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiNbChannels
                != pSettings->outputNBChannels
                || pC->pAddedClipCtxt->pSettings->
                ClipProperties.AudioStreamType == M4VIDEOEDITING_kMP3 )
            {
                /* Set reader DSI to NULL (unknown), we will use encoder DSI later */
                if( pC->pAddedClipCtxt->pAudioStream->
                    m_basicProperties.m_pDecoderSpecificInfo != M4OSA_NULL )
                {

                    /*
                     free(pC->pAddedClipCtxt->pAudioStream->\
                       m_basicProperties.m_pDecoderSpecificInfo);
                       */
                    pC->pAddedClipCtxt->pAudioStream->
                        m_basicProperties.m_decoderSpecificInfoSize = 0;
                    pC->pAddedClipCtxt->pAudioStream->
                        m_basicProperties.m_pDecoderSpecificInfo = M4OSA_NULL;
                }

                pC->ewc.uiNbChannels =
                    pC->pInputClipCtxt->pSettings->ClipProperties.uiNbChannels;
                pC->ewc.uiSamplingFrequency = pC->pInputClipCtxt->pSettings->
                    ClipProperties.uiSamplingFrequency;
                pC->ewc.scale_audio = pC->ewc.uiSamplingFrequency / 1000.0;

                if( pSettings->outputAudioFormat == M4VIDEOEDITING_kAMR_NB )
                {
                    pC->ewc.AudioStreamType = M4SYS_kAMR;
                    pC->ewc.pSilenceFrameData =
                        (M4OSA_UInt8 *)M4VSS3GPP_AMR_AU_SILENCE_FRAME_048;
                    pC->ewc.uiSilenceFrameSize =
                        M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_SIZE;
                    pC->ewc.iSilenceFrameDuration =
                        M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_DURATION;
                    pC->ewc.uiAudioBitrate = 12200;
                    pC->ewc.uiSamplingFrequency = 8000;
                    pC->ewc.uiSilencePcmSize = 320;
                    pC->ewc.scale_audio = pC->ewc.uiSamplingFrequency / 1000.0;
                }
                else if( pSettings->outputAudioFormat == M4VIDEOEDITING_kAAC )
                {
                    pC->ewc.AudioStreamType = M4SYS_kAAC;

                    if( pSettings->outputAudioBitrate
                        == M4VIDEOEDITING_kUndefinedBitrate )
                    {
                        switch( pC->ewc.uiSamplingFrequency )
                        {
                            case 16000:
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k24_KBPS;
                                break;

                            case 22050:
                            case 24000:
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k32_KBPS;
                                break;

                            case 32000:
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k48_KBPS;
                                break;

                            case 44100:
                            case 48000:
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k64_KBPS;
                                break;

                            default:
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k64_KBPS;
                                break;
                        }

                        if( pC->ewc.uiNbChannels == 2 )
                        {
                            /* Output bitrate have to be doubled */
                            pC->ewc.uiAudioBitrate += pC->ewc.uiAudioBitrate;
                        }
                    }
                    else
                    {
                        pC->ewc.uiAudioBitrate = pSettings->outputAudioBitrate;
                    }

                    if( pC->ewc.uiNbChannels == 1 )
                    {
                        pC->ewc.pSilenceFrameData =
                            (M4OSA_UInt8 *)M4VSS3GPP_AAC_AU_SILENCE_MONO;
                        pC->ewc.uiSilenceFrameSize =
                            M4VSS3GPP_AAC_AU_SILENCE_MONO_SIZE;
                    }
                    else
                    {
                        pC->ewc.pSilenceFrameData =
                            (M4OSA_UInt8 *)M4VSS3GPP_AAC_AU_SILENCE_STEREO;
                        pC->ewc.uiSilenceFrameSize =
                            M4VSS3GPP_AAC_AU_SILENCE_STEREO_SIZE;
                    }
                    pC->ewc.iSilenceFrameDuration =
                        1024; /* AAC is always 1024/Freq sample duration */
                }
            }
            else
            {
                switch( pC->pAddedClipCtxt->pSettings->
                    ClipProperties.AudioStreamType )
                {
                    case M4VIDEOEDITING_kAMR_NB:
                        pC->ewc.AudioStreamType = M4SYS_kAMR;
                        pC->ewc.pSilenceFrameData =
                            (M4OSA_UInt8 *)M4VSS3GPP_AMR_AU_SILENCE_FRAME_048;
                        pC->ewc.uiSilenceFrameSize =
                            M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_SIZE;
                        pC->ewc.iSilenceFrameDuration =
                            M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_DURATION;
                        break;

                    case M4VIDEOEDITING_kAAC:
                    case M4VIDEOEDITING_kAACplus:
                    case M4VIDEOEDITING_keAACplus:
                        pC->ewc.AudioStreamType = M4SYS_kAAC;

                        if( pC->ewc.uiNbChannels == 1 )
                        {
                            pC->ewc.pSilenceFrameData =
                                (M4OSA_UInt8 *)M4VSS3GPP_AAC_AU_SILENCE_MONO;
                            pC->ewc.uiSilenceFrameSize =
                                M4VSS3GPP_AAC_AU_SILENCE_MONO_SIZE;
                        }
                        else
                        {
                            pC->ewc.pSilenceFrameData =
                                (M4OSA_UInt8 *)M4VSS3GPP_AAC_AU_SILENCE_STEREO;
                            pC->ewc.uiSilenceFrameSize =
                                M4VSS3GPP_AAC_AU_SILENCE_STEREO_SIZE;
                        }
                        pC->ewc.iSilenceFrameDuration =
                            1024; /* AAC is always 1024/Freq sample duration */
                        break;

                    case M4VIDEOEDITING_kEVRC:
                        pC->ewc.AudioStreamType = M4SYS_kEVRC;
                        pC->ewc.pSilenceFrameData = M4OSA_NULL;
                        pC->ewc.uiSilenceFrameSize = 0;
                        pC->ewc.iSilenceFrameDuration = 160; /* EVRC frames are 20 ms at 8000 Hz
                                            (makes it easier to factorize amr and evrc code) */
                        break;

                    case M4VIDEOEDITING_kPCM:
                        /* Set reader DSI to NULL (unknown), we will use encoder DSI later */
                        pC->pAddedClipCtxt->pAudioStream->
                            m_basicProperties.m_decoderSpecificInfoSize = 0;
                        pC->pAddedClipCtxt->pAudioStream->
                            m_basicProperties.m_pDecoderSpecificInfo =
                            M4OSA_NULL;

                        if( pC->pAddedClipCtxt->pSettings->
                            ClipProperties.uiSamplingFrequency == 8000 )
                        {
                            pC->ewc.AudioStreamType = M4SYS_kAMR;
                            pC->ewc.pSilenceFrameData = (M4OSA_UInt8
                                *)M4VSS3GPP_AMR_AU_SILENCE_FRAME_048;
                            pC->ewc.uiSilenceFrameSize =
                                M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_SIZE;
                            pC->ewc.iSilenceFrameDuration =
                                M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_DURATION;
                            pC->ewc.uiAudioBitrate = M4VIDEOEDITING_k12_2_KBPS;
                        }
                        else if( pC->pAddedClipCtxt->pSettings->
                            ClipProperties.uiSamplingFrequency == 16000 )
                        {
                            if( pC->ewc.uiNbChannels == 1 )
                            {
                                pC->ewc.AudioStreamType = M4SYS_kAAC;
                                pC->ewc.pSilenceFrameData = (M4OSA_UInt8
                                    *)M4VSS3GPP_AAC_AU_SILENCE_MONO;
                                pC->ewc.uiSilenceFrameSize =
                                    M4VSS3GPP_AAC_AU_SILENCE_MONO_SIZE;
                                pC->ewc.iSilenceFrameDuration =
                                    1024; /* AAC is always 1024/Freq sample duration */
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k32_KBPS;
                            }
                            else
                            {
                                pC->ewc.AudioStreamType = M4SYS_kAAC;
                                pC->ewc.pSilenceFrameData = (M4OSA_UInt8
                                    *)M4VSS3GPP_AAC_AU_SILENCE_STEREO;
                                pC->ewc.uiSilenceFrameSize =
                                    M4VSS3GPP_AAC_AU_SILENCE_STEREO_SIZE;
                                pC->ewc.iSilenceFrameDuration =
                                    1024; /* AAC is always 1024/Freq sample duration */
                                pC->ewc.uiAudioBitrate =
                                    M4VIDEOEDITING_k64_KBPS;
                            }
                        }
                        else
                        {
                            pC->ewc.AudioStreamType = M4SYS_kAudioUnknown;
                        }
                        break;

                    default:
                        pC->ewc.AudioStreamType = M4SYS_kAudioUnknown;
                        break;
                }
            }

            /* Add a link to audio dsi */
            pC->ewc.uiAudioOutputDsiSize =
                (M4OSA_UInt16)pC->pAddedClipCtxt->pAudioStream->
                m_basicProperties.m_decoderSpecificInfoSize;
            pC->ewc.pAudioOutputDsi = (M4OSA_MemAddr8)pC->pAddedClipCtxt->pAudioStream->
                m_basicProperties.m_pDecoderSpecificInfo;
        }
        else
        {
            pC->ewc.uiNbChannels =
                pC->pInputClipCtxt->pSettings->ClipProperties.uiNbChannels;
            pC->ewc.uiAudioBitrate =
                pC->pInputClipCtxt->pSettings->ClipProperties.uiAudioBitrate;
            pC->ewc.uiSamplingFrequency = pC->pInputClipCtxt->pSettings->
                ClipProperties.uiSamplingFrequency;
            pC->ewc.uiSilencePcmSize =
                pC->pInputClipCtxt->pSettings->ClipProperties.uiDecodedPcmSize;
            pC->ewc.scale_audio = pC->ewc.uiSamplingFrequency / 1000.0;

            switch( pC->pInputClipCtxt->pSettings->
                ClipProperties.AudioStreamType )
            {
                case M4VIDEOEDITING_kAMR_NB:
                    pC->ewc.AudioStreamType = M4SYS_kAMR;
                    pC->ewc.pSilenceFrameData =
                        (M4OSA_UInt8 *)M4VSS3GPP_AMR_AU_SILENCE_FRAME_048;
                    pC->ewc.uiSilenceFrameSize =
                        M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_SIZE;
                    pC->ewc.iSilenceFrameDuration =
                        M4VSS3GPP_AMR_AU_SILENCE_FRAME_048_DURATION;
                    break;

                case M4VIDEOEDITING_kAAC:
                case M4VIDEOEDITING_kAACplus:
                case M4VIDEOEDITING_keAACplus:
                    pC->ewc.AudioStreamType = M4SYS_kAAC;

                    if( pC->ewc.uiNbChannels == 1 )
                    {
                        pC->ewc.pSilenceFrameData =
                            (M4OSA_UInt8 *)M4VSS3GPP_AAC_AU_SILENCE_MONO;
                        pC->ewc.uiSilenceFrameSize =
                            M4VSS3GPP_AAC_AU_SILENCE_MONO_SIZE;
                    }
                    else
                    {
                        pC->ewc.pSilenceFrameData =
                            (M4OSA_UInt8 *)M4VSS3GPP_AAC_AU_SILENCE_STEREO;
                        pC->ewc.uiSilenceFrameSize =
                            M4VSS3GPP_AAC_AU_SILENCE_STEREO_SIZE;
                    }
                    pC->ewc.iSilenceFrameDuration =
                        1024; /* AAC is always 1024/Freq sample duration */
                    break;

                default:
                    pC->ewc.AudioStreamType = M4SYS_kAudioUnknown;
                    M4OSA_TRACE1_0(
                        "M4VSS3GPP_intAudioMixingOpen: No audio track in input file.");
                    return M4VSS3GPP_ERR_AUDIO_CANNOT_BE_MIXED;
                    break;
            }

            /* Add a link to audio dsi */
            pC->ewc.uiAudioOutputDsiSize =
                (M4OSA_UInt16)pC->pInputClipCtxt->pAudioStream->
                m_basicProperties.m_decoderSpecificInfoSize;
            pC->ewc.pAudioOutputDsi = (M4OSA_MemAddr8)pC->pInputClipCtxt->pAudioStream->
                m_basicProperties.m_pDecoderSpecificInfo;
        }
    }

    /**
    * Copy common 'silence frame stuff' to ClipContext */
    pC->pInputClipCtxt->uiSilencePcmSize = pC->ewc.uiSilencePcmSize;
    pC->pInputClipCtxt->pSilenceFrameData = pC->ewc.pSilenceFrameData;
    pC->pInputClipCtxt->uiSilenceFrameSize = pC->ewc.uiSilenceFrameSize;
    pC->pInputClipCtxt->iSilenceFrameDuration = pC->ewc.iSilenceFrameDuration;
    pC->pInputClipCtxt->scale_audio = pC->ewc.scale_audio;

    pC->pInputClipCtxt->iAudioFrameCts =
        -pC->pInputClipCtxt->iSilenceFrameDuration; /* Reset time */

    /**
    * Copy common 'silence frame stuff' to ClipContext */
    if( pC->bHasAudio )
    {
        pC->pAddedClipCtxt->uiSilencePcmSize = pC->ewc.uiSilencePcmSize;
        pC->pAddedClipCtxt->pSilenceFrameData = pC->ewc.pSilenceFrameData;
        pC->pAddedClipCtxt->uiSilenceFrameSize = pC->ewc.uiSilenceFrameSize;
        pC->pAddedClipCtxt->iSilenceFrameDuration =
            pC->ewc.iSilenceFrameDuration;
        pC->pAddedClipCtxt->scale_audio = pC->ewc.scale_audio;

        pC->pAddedClipCtxt->iAudioFrameCts =
            -pC->pAddedClipCtxt->iSilenceFrameDuration; /* Reset time */
    }

    /**
    * Check AddCts is lower than original clip duration */
    if( ( M4OSA_NULL != pC->pInputClipCtxt->pVideoStream)
        && (pC->iAddCts > (M4OSA_Int32)pC->pInputClipCtxt->pVideoStream->
        m_basicProperties.m_duration) )
    {
        M4OSA_TRACE1_0(
            "M4VSS3GPP_intAudioMixingOpen(): uiAddCts is larger than video duration,\
            returning M4VSS3GPP_ERR_ADDCTS_HIGHER_THAN_VIDEO_DURATION");
        return M4VSS3GPP_ERR_ADDCTS_HIGHER_THAN_VIDEO_DURATION;
    }

    /**
    * If the audio tracks are not compatible, replace input track by silence */
    if( M4OSA_FALSE == pC->pInputClipCtxt->pSettings->
        ClipProperties.bAudioIsCompatibleWithMasterClip )
    {
        M4VSS3GPP_intClipDeleteAudioTrack(pC->pInputClipCtxt);
    }

    /**
    * Check if audio mixing is required */
    if( ( ( pC->bHasAudio) && (M4OSA_FALSE
        == pC->pAddedClipCtxt->pSettings->ClipProperties.bAudioIsEditable))
        || (M4OSA_TRUE == pC->bRemoveOriginal) ) /*||
                                                 (pSettings->uiAddVolume >= 100)) */
    {
        pC->bAudioMixingIsNeeded = M4OSA_FALSE;
    }
    else
    {
        pC->bAudioMixingIsNeeded = M4OSA_TRUE;
    }

    /**
    * Check if output audio can support silence frames
    Trick i use bAudioIsCompatibleWithMasterClip filed to store that  */
    if( pC->bHasAudio )
    {
        pC->bSupportSilence = pC->pAddedClipCtxt->pSettings->
            ClipProperties.bAudioIsCompatibleWithMasterClip;

        if( M4OSA_FALSE == pC->bSupportSilence )
        {
            if( pC->iAddCts > 0 )
            {
                M4OSA_TRACE1_0(
                    "M4VSS3GPP_intAudioMixingOpen():\
                    iAddCts should be set to 0 with this audio track !");
                return M4VSS3GPP_ERR_FEATURE_UNSUPPORTED_WITH_AUDIO_TRACK;
            }

            if( 0 < pC->uiEndLoop )
            {
                M4OSA_TRACE1_0(
                    "M4VSS3GPP_intAudioMixingOpen():\
                    uiEndLoop should be set to 0 with this audio track !");
                return M4VSS3GPP_ERR_FEATURE_UNSUPPORTED_WITH_AUDIO_TRACK;
            }
        }
    }
    if( pC->b_DuckingNeedeed == M4OSA_FALSE)
    {
        /**
        * Compute the factor to apply to sample to do the mixing */
        pC->fAddedFactor = 0.50F;
        pC->fOrigFactor = 0.50F;
    }


    /**
    * Check if SSRC is needed */
    if( M4OSA_TRUE == pC->b_SSRCneeded )
    {
        M4OSA_UInt32 numerator, denominator, ratio, ratioBuffer;

        /**
        * Init the SSRC module */
        SSRC_ReturnStatus_en
            ReturnStatus; /* Function return status                       */
        LVM_INT16 NrSamplesMin =
            0; /* Minimal number of samples on the input or on the output */
        LVM_INT32
            ScratchSize; /* The size of the scratch memory               */
        LVM_INT16
            *pInputInScratch; /* Pointer to input in the scratch buffer       */
        LVM_INT16
            *
            pOutputInScratch; /* Pointer to the output in the scratch buffer  */
        SSRC_Params_t ssrcParams;          /* Memory for init parameters                    */

        switch( pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiSamplingFrequency )
        {
            case 8000:
                ssrcParams.SSRC_Fs_In = LVM_FS_8000;
                break;

            case 11025:
                ssrcParams.SSRC_Fs_In = LVM_FS_11025;
                break;

            case 12000:
                ssrcParams.SSRC_Fs_In = LVM_FS_12000;
                break;

            case 16000:
                ssrcParams.SSRC_Fs_In = LVM_FS_16000;
                break;

            case 22050:
                ssrcParams.SSRC_Fs_In = LVM_FS_22050;
                break;

            case 24000:
                ssrcParams.SSRC_Fs_In = LVM_FS_24000;
                break;

            case 32000:
                ssrcParams.SSRC_Fs_In = LVM_FS_32000;
                break;

            case 44100:
                ssrcParams.SSRC_Fs_In = LVM_FS_44100;
                break;

            case 48000:
                ssrcParams.SSRC_Fs_In = LVM_FS_48000;
                break;

            default:
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingOpen: invalid added clip sampling frequency (%d Hz),\
                    returning M4VSS3GPP_ERR_UNSUPPORTED_ADDED_AUDIO_STREAM",
                    pC->pAddedClipCtxt->pSettings->
                    ClipProperties.uiSamplingFrequency);
                return M4VSS3GPP_ERR_UNSUPPORTED_ADDED_AUDIO_STREAM;
        }

        if( 1 == pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels )
        {
            ssrcParams.SSRC_NrOfChannels = LVM_MONO;
        }
        else
        {
            ssrcParams.SSRC_NrOfChannels = LVM_STEREO;
        }

        switch( pC->ewc.uiSamplingFrequency )
        {
            case 8000:
                ssrcParams.SSRC_Fs_Out = LVM_FS_8000;
                break;

            case 16000:
                ssrcParams.SSRC_Fs_Out = LVM_FS_16000;
                break;

            case 22050:
                ssrcParams.SSRC_Fs_Out = LVM_FS_22050;
                break;

            case 24000:
                ssrcParams.SSRC_Fs_Out = LVM_FS_24000;
                break;

            case 32000:
                ssrcParams.SSRC_Fs_Out = LVM_FS_32000;
                break;

            case 44100:
                ssrcParams.SSRC_Fs_Out = LVM_FS_44100;
                break;

            case 48000:
                ssrcParams.SSRC_Fs_Out = LVM_FS_48000;
                break;

            default:
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingOpen: invalid output sampling frequency (%d Hz),\
                    returning M4VSS3GPP_ERR_AUDIO_CANNOT_BE_MIXED",
                    pC->ewc.uiSamplingFrequency);
                return M4VSS3GPP_ERR_AUDIO_CANNOT_BE_MIXED;
                break;
        }
        ReturnStatus = 0;

        switch (ssrcParams.SSRC_Fs_In){
        case LVM_FS_8000:
            ssrcParams.NrSamplesIn = 320;
            break;
        case LVM_FS_11025:
            ssrcParams.NrSamplesIn =441;
            break;
        case LVM_FS_12000:
            ssrcParams.NrSamplesIn =    480;
            break;
        case LVM_FS_16000:
            ssrcParams.NrSamplesIn =    640;
            break;
        case LVM_FS_22050:
            ssrcParams.NrSamplesIn =    882;
            break;
        case LVM_FS_24000:
            ssrcParams.NrSamplesIn =    960;
            break;
        case LVM_FS_32000:
            ssrcParams.NrSamplesIn = 1280;
            break;
        case LVM_FS_44100:
            ssrcParams.NrSamplesIn = 1764;
            break;
        case LVM_FS_48000:
            ssrcParams.NrSamplesIn = 1920;
            break;
        default:
            ReturnStatus = -1;
            break;
        }

        switch (ssrcParams.SSRC_Fs_Out){
        case LVM_FS_8000:
            ssrcParams.NrSamplesOut= 320;
            break;
        case LVM_FS_11025:
            ssrcParams.NrSamplesOut =441;
            break;
        case LVM_FS_12000:
            ssrcParams.NrSamplesOut=    480;
            break;
        case LVM_FS_16000:
            ssrcParams.NrSamplesOut=    640;
            break;
        case LVM_FS_22050:
            ssrcParams.NrSamplesOut=    882;
            break;
        case LVM_FS_24000:
            ssrcParams.NrSamplesOut=    960;
            break;
        case LVM_FS_32000:
            ssrcParams.NrSamplesOut = 1280;
            break;
        case LVM_FS_44100:
            ssrcParams.NrSamplesOut= 1764;
            break;
        case LVM_FS_48000:
            ssrcParams.NrSamplesOut = 1920;
            break;
        default:
            ReturnStatus = -1;
            break;
        }
        if( ReturnStatus != SSRC_OK )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingOpen:\
                Error code %d returned by the SSRC_GetNrSamples function",
                ReturnStatus);
            return M4VSS3GPP_ERR_AUDIO_CANNOT_BE_MIXED;
        }

        NrSamplesMin =
            (LVM_INT16)((ssrcParams.NrSamplesIn > ssrcParams.NrSamplesOut)
            ? ssrcParams.NrSamplesOut : ssrcParams.NrSamplesIn);

        while( NrSamplesMin < M4VSS_SSRC_MINBLOCKSIZE )
        { /* Don't take blocks smaller that the minimal block size */
            ssrcParams.NrSamplesIn = (LVM_INT16)(ssrcParams.NrSamplesIn << 1);
            ssrcParams.NrSamplesOut = (LVM_INT16)(ssrcParams.NrSamplesOut << 1);
            NrSamplesMin = (LVM_INT16)(NrSamplesMin << 1);
        }
        pC->iSsrcNbSamplIn = (LVM_INT16)(
            ssrcParams.
            NrSamplesIn); /* multiplication by NrOfChannels is done below */
        pC->iSsrcNbSamplOut = (LVM_INT16)(ssrcParams.NrSamplesOut);

        numerator =
            pC->pAddedClipCtxt->pSettings->ClipProperties.uiSamplingFrequency
            * pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels;
        denominator =
            pC->pInputClipCtxt->pSettings->ClipProperties.uiSamplingFrequency
            * pC->pInputClipCtxt->pSettings->ClipProperties.uiNbChannels;

        if( numerator % denominator == 0 )
        {
            ratioBuffer = (M4OSA_UInt32)(numerator / denominator);
        }
        else
        {
            ratioBuffer = (M4OSA_UInt32)(numerator / denominator) + 1;
        }

        ratio =
            (M4OSA_UInt32)(( pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize
            * ratioBuffer) / (pC->iSsrcNbSamplIn * sizeof(short)
            * pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiNbChannels));

        if( ratio == 0 )
        {
            /* It means that the input size of SSRC bufferIn is bigger than the asked buffer */
            pC->minimumBufferIn = pC->iSsrcNbSamplIn * sizeof(short)
                * pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiNbChannels;
        }
        else
        {
            ratio++; /* We use the immediate superior integer */
            pC->minimumBufferIn = ratio * (pC->iSsrcNbSamplIn * sizeof(short)
                * pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiNbChannels);
        }

        /**
        * Allocate buffer for the input of the SSRC */
        pC->pSsrcBufferIn =
            (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(pC->minimumBufferIn
            + pC->pAddedClipCtxt->
            AudioDecBufferOut.
            m_bufferSize,
            M4VSS3GPP, (M4OSA_Char *)"pSsrcBufferIn");

        if( M4OSA_NULL == pC->pSsrcBufferIn )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen():\
                unable to allocate pSsrcBufferIn, returning M4ERR_ALLOC");
            return M4ERR_ALLOC;
        }
        pC->pPosInSsrcBufferIn = (M4OSA_MemAddr8)pC->pSsrcBufferIn;

        /**
        * Allocate buffer for the output of the SSRC */
        /* The "3" value below should be optimized ... one day ... */
        pC->pSsrcBufferOut =
            (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(3 * pC->iSsrcNbSamplOut * sizeof(short)
            * pC->ewc.uiNbChannels, M4VSS3GPP, (M4OSA_Char *)"pSsrcBufferOut");

        if( M4OSA_NULL == pC->pSsrcBufferOut )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen():\
                unable to allocate pSsrcBufferOut, returning M4ERR_ALLOC");
            return M4ERR_ALLOC;
        }
        pC->pPosInSsrcBufferOut = pC->pSsrcBufferOut;

        /**
        * Allocate temporary buffer needed in case of channel conversion */
        if( pC->ChannelConversion > 0 )
        {
            /* The "3" value below should be optimized ... one day ... */
            pC->pTempBuffer =
                (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(3 * pC->iSsrcNbSamplOut
                * sizeof(short) * pC->pAddedClipCtxt->pSettings->
                ClipProperties.uiNbChannels, M4VSS3GPP, (M4OSA_Char *)"pSsrcBufferOut");

            if( M4OSA_NULL == pC->pTempBuffer )
            {
                M4OSA_TRACE1_0(
                    "M4VSS3GPP_intAudioMixingOpen():\
                    unable to allocate pTempBuffer, returning M4ERR_ALLOC");
                return M4ERR_ALLOC;
            }
            pC->pPosInTempBuffer = pC->pTempBuffer;
        }
    }
    else if( pC->ChannelConversion > 0 )
    {
        pC->minimumBufferIn =
            pC->pAddedClipCtxt->AudioDecBufferOut.m_bufferSize;

        /**
        * Allocate buffer for the input of the SSRC */
        pC->pSsrcBufferIn =
            (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(pC->minimumBufferIn
            + pC->pAddedClipCtxt->
            AudioDecBufferOut.
            m_bufferSize,
            M4VSS3GPP, (M4OSA_Char *)"pSsrcBufferIn");

        if( M4OSA_NULL == pC->pSsrcBufferIn )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen(): \
                unable to allocate pSsrcBufferIn, returning M4ERR_ALLOC");
            return M4ERR_ALLOC;
        }
        pC->pPosInSsrcBufferIn = (M4OSA_MemAddr8)pC->pSsrcBufferIn;

        /**
        * Allocate buffer for the output of the SSRC */
        /* The "3" value below should be optimized ... one day ... */
        pC->pSsrcBufferOut = (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(
            pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize,
            M4VSS3GPP, (M4OSA_Char *)"pSsrcBufferOut");

        if( M4OSA_NULL == pC->pSsrcBufferOut )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen():\
                unable to allocate pSsrcBufferOut, returning M4ERR_ALLOC");
            return M4ERR_ALLOC;
        }
        pC->pPosInSsrcBufferOut = pC->pSsrcBufferOut;
    }
    else if( (pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType == M4VIDEOEDITING_kMP3)||
         (pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType == M4VIDEOEDITING_kPCM))
    {
        M4OSA_UInt32 minbuffer = 0;

        if( pSettings->outputAudioFormat == M4VIDEOEDITING_kAAC )
        {
            pC->minimumBufferIn = 2048 * pC->ewc.uiNbChannels;
            minbuffer = pC->minimumBufferIn;
        }
        else if( pSettings->outputAudioFormat == M4VIDEOEDITING_kAMR_NB )
        {
            pC->minimumBufferIn = 320;

            if( pC->pAddedClipCtxt->AudioDecBufferOut.m_bufferSize > 320 )
            {
                minbuffer = pC->pAddedClipCtxt->AudioDecBufferOut.m_bufferSize;
            }
            else
            {
                minbuffer = pC->minimumBufferIn; /* Not really possible ...*/
            }
        }
        else
        {
            M4OSA_TRACE1_0("Bad output audio format, in case of MP3 replacing");
            return M4ERR_PARAMETER;
        }

        /**
        * Allocate buffer for the input of the SSRC */
        pC->pSsrcBufferIn =
            (M4OSA_MemAddr8)M4OSA_32bitAlignedMalloc(2 * minbuffer, M4VSS3GPP,
            (M4OSA_Char *)"pSsrcBufferIn");

        if( M4OSA_NULL == pC->pSsrcBufferIn )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingOpen(): unable to allocate pSsrcBufferIn,\
                returning M4ERR_ALLOC");
            return M4ERR_ALLOC;
        }
        pC->pPosInSsrcBufferIn = (M4OSA_MemAddr8)pC->pSsrcBufferIn;

        pC->pPosInSsrcBufferOut = pC->pPosInSsrcBufferIn;
        pC->pSsrcBufferOut = pC->pSsrcBufferIn;
    }

    /**
    * Check if audio encoder is needed to do audio mixing or audio resampling */
    if( M4OSA_TRUE == pC->bAudioMixingIsNeeded || M4VIDEOEDITING_kPCM
        == pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
        || M4VIDEOEDITING_kMP3
        == pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
        || pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
        != pSettings->outputAudioFormat
        || pC->pAddedClipCtxt->pSettings->ClipProperties.uiSamplingFrequency
        != outputASF
        || pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels
        != pSettings->outputNBChannels )
    {
        /**
        * Init the audio encoder */
        err = M4VSS3GPP_intCreateAudioEncoder(&pC->ewc, &pC->ShellAPI,
            pC->ewc.uiAudioBitrate);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intCreateAudioEncoder() returns 0x%x",
                err);
            return err;
        }

        /* In case of PCM, MP3 or audio replace with reencoding, use encoder DSI */
        if( pC->ewc.uiAudioOutputDsiSize == 0 && (M4VIDEOEDITING_kPCM
            == pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
            || M4VIDEOEDITING_kMP3 == pC->pAddedClipCtxt->pSettings->
            ClipProperties.AudioStreamType
            || pC->pAddedClipCtxt->pSettings->
            ClipProperties.AudioStreamType
            != pSettings->outputAudioFormat
            || pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiSamplingFrequency != outputASF
            || pC->pAddedClipCtxt->pSettings->
            ClipProperties.uiNbChannels
            != pSettings->outputNBChannels) )
        {
            pC->ewc.uiAudioOutputDsiSize =
                (M4OSA_UInt16)pC->ewc.pAudioEncDSI.infoSize;
            pC->ewc.pAudioOutputDsi = pC->ewc.pAudioEncDSI.pInfo;
        }
    }

    /**
    * Init the output 3GPP file */
    /*11/12/2008 CR3283 add the max output file size for the MMS use case in VideoArtist*/
    err = M4VSS3GPP_intCreate3GPPOutputFile(&pC->ewc, &pC->ShellAPI,
        pC->pOsaFileWritPtr, pSettings->pOutputClipFile,
        pC->pOsaFileReadPtr, pSettings->pTemporaryFile, 0);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingOpen(): M4VSS3GPP_intCreate3GPPOutputFile() returns 0x%x",
            err);
        return err;
    }

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_intAudioMixingOpen(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingWriteSilence()
 * @brief    Write an audio silence frame into the writer
 * @note    Mainly used when padding with silence
 * @param    pC            (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingWriteSilence(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;

    err = pC->ShellAPI.pWriterDataFcts->pStartAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1("M4VSS3GPP_intAudioMixingWriteSilence:\
         pWriterDataFcts->pStartAU(audio) returns 0x%x!", err);
        return err;
    }

    M4OSA_TRACE2_0("A #### silence AU");

    memcpy((void *)pC->ewc.WriterAudioAU.dataAddress,
        (void *)pC->ewc.pSilenceFrameData, pC->ewc.uiSilenceFrameSize);

    pC->ewc.WriterAudioAU.size = pC->ewc.uiSilenceFrameSize;
    pC->ewc.WriterAudioAU.CTS =
        (M4OSA_Time)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5);

    M4OSA_TRACE2_2("B ---- write : cts  = %ld [ 0x%x ]",
        (M4OSA_Int32)(pC->ewc.dATo), pC->ewc.WriterAudioAU.size);

    err = pC->ShellAPI.pWriterDataFcts->pProcessAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingWriteSilence:\
            pWriterDataFcts->pProcessAU(silence) returns 0x%x!",
            err);
        return err;
    }

    pC->ewc.dATo += pC->ewc.iSilenceFrameDuration / pC->ewc.scale_audio;

    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingStepVideo(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Perform one step of video.
 * @note
 * @param    pC            (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingStepVideo(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;
    M4OSA_UInt16 offset;

    M4OSA_TRACE2_3("  VIDEO step : dVTo = %f  state = %d  offset = %ld",
        pC->ewc.dOutputVidCts, pC->State, pC->pInputClipCtxt->iVoffset);

    /**
    * Read the input video AU */
    err = pC->pInputClipCtxt->ShellAPI.m_pReaderDataIt->m_pFctGetNextAu(
        pC->pInputClipCtxt->pReaderContext,
        (M4_StreamHandler *)pC->pInputClipCtxt->pVideoStream,
        &pC->pInputClipCtxt->VideoAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE3_1(
            "M4VSS3GPP_intAudioMixingStepVideo(): m_pFctGetNextAu(video) returns 0x%x",
            err);
        return err;
    }

    M4OSA_TRACE2_3("C .... read  : cts  = %.0f + %ld [ 0x%x ]",
        pC->pInputClipCtxt->VideoAU.m_CTS, pC->pInputClipCtxt->iVoffset,
        pC->pInputClipCtxt->VideoAU.m_size);

    /**
    * Get the output AU to write into */
    err = pC->ShellAPI.pWriterDataFcts->pStartAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_VIDEO_STREAM_ID, &pC->ewc.WriterVideoAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingStepVideo: pWriterDataFcts->pStartAU(Video) returns 0x%x!",
            err);
        return err;
    }

    offset = 0;
    /* for h.264 stream do not read the 1st 4 bytes as they are header indicators */
    if( pC->pInputClipCtxt->pVideoStream->m_basicProperties.m_streamType
        == M4DA_StreamTypeVideoMpeg4Avc )
    {
        M4OSA_TRACE3_0(
            "M4VSS3GPP_intAudioMixingStepVideo(): input stream type H264");
        offset = 4;
    }
    pC->pInputClipCtxt->VideoAU.m_size  -=  offset;
    /**
    * Check that the video AU is not larger than expected */
    if( pC->pInputClipCtxt->VideoAU.m_size > pC->ewc.uiVideoMaxAuSize )
    {
        M4OSA_TRACE1_2(
            "M4VSS3GPP_intAudioMixingStepVideo: AU size greater than MaxAuSize (%d>%d)!\
            returning M4VSS3GPP_ERR_INPUT_VIDEO_AU_TOO_LARGE",
            pC->pInputClipCtxt->VideoAU.m_size, pC->ewc.uiVideoMaxAuSize);
        return M4VSS3GPP_ERR_INPUT_VIDEO_AU_TOO_LARGE;
    }

    /**
    * Copy the input AU payload to the output AU */
    memcpy((void *)pC->ewc.WriterVideoAU.dataAddress,
        (void *)(pC->pInputClipCtxt->VideoAU.m_dataAddress + offset),
        (pC->pInputClipCtxt->VideoAU.m_size));

    /**
    * Copy the input AU parameters to the output AU */
    pC->ewc.WriterVideoAU.size = pC->pInputClipCtxt->VideoAU.m_size;
    pC->ewc.WriterVideoAU.CTS =
        (M4OSA_UInt32)(pC->pInputClipCtxt->VideoAU.m_CTS + 0.5);
    pC->ewc.WriterVideoAU.attribute = pC->pInputClipCtxt->VideoAU.m_attribute;

    /**
    * Write the AU */
    M4OSA_TRACE2_2("D ---- write : cts  = %lu [ 0x%x ]",
        pC->ewc.WriterVideoAU.CTS, pC->ewc.WriterVideoAU.size);

    err = pC->ShellAPI.pWriterDataFcts->pProcessAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_VIDEO_STREAM_ID, &pC->ewc.WriterVideoAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingStepVideo: pWriterDataFcts->pProcessAU(Video) returns 0x%x!",
            err);
        return err;
    }

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_intAudioMixingStepVideo(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingStepAudioMix(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Perform one step of audio.
 * @note
 * @param    pC            (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingStepAudioMix(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;

    M4OSA_TRACE2_3("  AUDIO mix  : dATo = %f  state = %d  offset = %ld",
        pC->ewc.dATo, pC->State, pC->pInputClipCtxt->iAoffset);

    switch( pC->State )
    {
        /**********************************************************/
        case M4VSS3GPP_kAudioMixingState_AUDIO_FIRST_SEGMENT:
            {
                err = M4VSS3GPP_intAudioMixingCopyOrig(pC);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingStepAudioMix:\
                        M4VSS3GPP_intAudioMixingCopyOrig(1) returns 0x%x!",
                        err);
                    return err;
                }

                /**
                * Check if we reached the AddCts */
                if( pC->ewc.dATo >= pC->iAddCts )
                {
                    /**
                    * First segment is over, state transition to second and return OK */
                    pC->State = M4VSS3GPP_kAudioMixingState_AUDIO_SECOND_SEGMENT;

                    /* Transition from reading state to encoding state */
                    err = M4VSS3GPP_intAudioMixingTransition(pC);

                    if( M4NO_ERROR != err )
                    {
                        M4OSA_TRACE1_1(
                            "M4VSS3GPP_intAudioMixingStepAudioMix(): pre-encode fails err = 0x%x",
                            err);
                        return err;
                    }

                    /**
                    * Return with no error so the step function will be called again */
                    pC->pAddedClipCtxt->iAoffset =
                        (M4OSA_Int32)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5);

                    M4OSA_TRACE2_0(
                        "M4VSS3GPP_intAudioMixingStepAudioMix(): returning M4NO_ERROR (1->2)");

                    return M4NO_ERROR;
                }
            }
            break;

            /**********************************************************/
        case M4VSS3GPP_kAudioMixingState_AUDIO_SECOND_SEGMENT:
            {
                if( M4OSA_TRUE == pC->bAudioMixingIsNeeded ) /**< Mix */
                {
                    /**
                    * Read the added audio AU */
                    if( pC->ChannelConversion > 0 || pC->b_SSRCneeded == M4OSA_TRUE
                        || pC->pAddedClipCtxt->pSettings->
                        ClipProperties.AudioStreamType == M4VIDEOEDITING_kMP3 )
                    {
                        /* In case of sampling freq conversion and/or channel conversion,
                           the read next AU will be    called by the
                           M4VSS3GPP_intAudioMixingDoMixing function */
                    }
                    else
                    {
                        err =
                            M4VSS3GPP_intClipReadNextAudioFrame(pC->pAddedClipCtxt);

                        M4OSA_TRACE2_3("E .... read  : cts  = %.0f + %.0f [ 0x%x ]",
                            pC->pAddedClipCtxt->iAudioFrameCts
                            / pC->pAddedClipCtxt->scale_audio,
                            pC->pAddedClipCtxt->iAoffset
                            / pC->pAddedClipCtxt->scale_audio,
                            pC->pAddedClipCtxt->uiAudioFrameSize);

                        if( M4WAR_NO_MORE_AU == err )
                        {
                            /**
                            * Decide what to do when audio is over */
                            if( pC->uiEndLoop > 0 )
                            {
                                /**
                                * Jump at the Begin loop time */
                                M4OSA_Int32 time = (M4OSA_Int32)(pC->uiBeginLoop);

                                err = pC->pAddedClipCtxt->ShellAPI.m_pReader->
                                    m_pFctJump(
                                    pC->pAddedClipCtxt->pReaderContext,
                                    (M4_StreamHandler
                                    *)pC->pAddedClipCtxt->pAudioStream,
                                    &time);

                                if( M4NO_ERROR != err )
                                {
                                    M4OSA_TRACE1_1(
                                        "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                        m_pReader->m_pFctJump(audio returns 0x%x",
                                        err);
                                    return err;
                                }
                            }
                            else
                            {
                                /* Transition from encoding state to reading state */
                                err = M4VSS3GPP_intAudioMixingTransition(pC);

                                if( M4NO_ERROR != err )
                                {
                                    M4OSA_TRACE1_1(
                                        "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                        pre-encode fails err = 0x%x",
                                        err);
                                    return err;
                                }

                                /**
                                * Second segment is over, state transition to third and
                                 return OK */
                                pC->State =
                                    M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT;

                                /**
                                * Return with no error so the step function will be
                                 called again */
                                M4OSA_TRACE2_0(
                                    "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                    returning M4NO_ERROR (2->3) a");
                                return M4NO_ERROR;
                            }
                        }
                        else if( M4NO_ERROR != err )
                        {
                            M4OSA_TRACE1_1(
                                "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                m_pFctGetNextAu(audio) returns 0x%x",
                                err);
                            return err;
                        }
                    }

                    /**
                    * Read the original audio AU */
                    err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pInputClipCtxt);

                    M4OSA_TRACE2_3("F .... read  : cts  = %.0f + %.0f [ 0x%x ]",
                        pC->pInputClipCtxt->iAudioFrameCts
                        / pC->pInputClipCtxt->scale_audio,
                        pC->pInputClipCtxt->iAoffset
                        / pC->pInputClipCtxt->scale_audio,
                        pC->pInputClipCtxt->uiAudioFrameSize);

                    if( M4NO_ERROR != err )
                    {
                        M4OSA_TRACE3_1(
                            "M4VSS3GPP_intAudioMixingStepAudioMix():\
                            m_pFctGetNextAu(audio) returns 0x%x",
                            err);
                        return err;
                    }

                    if( pC->ChannelConversion == 0
                        && pC->b_SSRCneeded == M4OSA_FALSE
                        && pC->pAddedClipCtxt->pSettings->
                        ClipProperties.AudioStreamType != M4VIDEOEDITING_kMP3 )
                    {
                        /**
                        * Get the output AU to write into */
                        err = pC->ShellAPI.pWriterDataFcts->pStartAU(
                            pC->ewc.p3gpWriterContext,
                            M4VSS3GPP_WRITER_AUDIO_STREAM_ID,
                            &pC->ewc.WriterAudioAU);

                        if( M4NO_ERROR != err )
                        {
                            M4OSA_TRACE1_1(
                                "M4VSS3GPP_intAudioMixingStepAudioMix:\
                                pWriterDataFcts->pStartAU(audio) returns 0x%x!",
                                err);
                            return err;
                        }
                    }

                    /**
                    * Perform the audio mixing */
                    err = M4VSS3GPP_intAudioMixingDoMixing(pC);

                    if( err == M4VSS3GPP_WAR_END_OF_ADDED_AUDIO )
                    {
                        return M4NO_ERROR;
                    }

                    if( M4NO_ERROR != err )
                    {
                        M4OSA_TRACE1_1(
                            "M4VSS3GPP_intAudioMixingStepAudioMix:\
                            M4VSS3GPP_intAudioMixingDoMixing returns 0x%x!",
                            err);
                        return err;
                    }
                }
                else /**< No mix, just copy added audio */
                {
                    err = M4VSS3GPP_intAudioMixingCopyAdded(pC);

                    if( M4WAR_NO_MORE_AU == err )
                    {
                        /**
                        * Decide what to do when audio is over */
                        if( pC->uiEndLoop > 0 )
                        {
                            /**
                            * Jump at the Begin loop time */
                            M4OSA_Int32 time = (M4OSA_Int32)(pC->uiBeginLoop);

                            err =
                                pC->pAddedClipCtxt->ShellAPI.m_pReader->m_pFctJump(
                                pC->pAddedClipCtxt->pReaderContext,
                                (M4_StreamHandler
                                *)pC->pAddedClipCtxt->pAudioStream,
                                &time);

                            if( M4NO_ERROR != err )
                            {
                                M4OSA_TRACE1_1(
                                    "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                    m_pReader->m_pFctJump(audio returns 0x%x",
                                    err);
                                return err;
                            }

                            /**
                            * 'BZZZ' bug fix:
                            * add a silence frame */
                            err = M4VSS3GPP_intAudioMixingWriteSilence(pC);

                            if( M4NO_ERROR != err )
                            {
                                M4OSA_TRACE1_1(
                                    "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                    M4VSS3GPP_intAudioMixingWriteSilence returns 0x%x",
                                    err);
                                return err;
                            }

                            /**
                            * Return with no error so the step function will be called again to
                              read audio data */
                            pC->pAddedClipCtxt->iAoffset =
                                (M4OSA_Int32)(pC->ewc.dATo * pC->ewc.scale_audio
                                + 0.5);

                            M4OSA_TRACE2_0(
                                "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                    returning M4NO_ERROR (loop)");
                            return M4NO_ERROR;
                        }
                        else
                        {
                            /* Transition to begin cut */
                            err = M4VSS3GPP_intAudioMixingTransition(pC);

                            if( M4NO_ERROR != err )
                            {
                                M4OSA_TRACE1_1(
                                    "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                    pre-encode fails err = 0x%x",
                                    err);
                                return err;
                            }

                            /**
                            * Second segment is over, state transition to third */
                            pC->State =
                                M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT;

                            /**
                            * Return with no error so the step function will be called again */
                            M4OSA_TRACE2_0(
                                "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                returning M4NO_ERROR (2->3) b");
                            return M4NO_ERROR;
                        }
                    }
                    else if( M4NO_ERROR != err )
                    {
                        M4OSA_TRACE1_1(
                            "M4VSS3GPP_intAudioMixingStepAudioMix():\
                            M4VSS3GPP_intAudioMixingCopyOrig(2) returns 0x%x",
                            err);
                        return err;
                    }
                }

                /**
                * Check if we reached the end of the video */
                if( pC->ewc.dATo >= pC->ewc.iOutputDuration )
                {
                    M4OSA_TRACE3_0(
                        "M4VSS3GPP_intAudioMixingStepAudioMix(): Video duration reached,\
                        returning M4WAR_NO_MORE_AU");
                    return M4WAR_NO_MORE_AU; /**< Simulate end of file error */
                }
            }
            break;

            /**********************************************************/
        case M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT:
            {
                err = M4VSS3GPP_intAudioMixingCopyOrig(pC);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingStepAudioMix:\
                        M4VSS3GPP_intAudioMixingCopyOrig(3) returns 0x%x!",
                        err);
                    return err;
                }

                /**
                * Check if we reached the end of the video */
                if( pC->ewc.dATo >= pC->ewc.iOutputDuration )
                {
                    M4OSA_TRACE3_0(
                        "M4VSS3GPP_intAudioMixingStepAudioMix():\
                        Video duration reached, returning M4WAR_NO_MORE_AU");
                    return M4WAR_NO_MORE_AU; /**< Simulate end of file error */
                }
            }
            break;
       default:
            break;
    }

    /**
    * Return with no error */
    M4OSA_TRACE3_0(
        "M4VSS3GPP_intAudioMixingStepAudioMix(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingStepAudioReplace(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Perform one step of audio.
 * @note
 * @param    pC            (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingStepAudioReplace(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;

    M4OSA_TRACE2_3("  AUDIO repl : dATo = %f  state = %d  offset = %ld",
        pC->ewc.dATo, pC->State, pC->pInputClipCtxt->iAoffset);

    switch( pC->State )
    {
        /**********************************************************/
        case M4VSS3GPP_kAudioMixingState_AUDIO_FIRST_SEGMENT:
            {
                /**
                * Replace the SID (silence) payload in the writer AU */
                err = M4VSS3GPP_intAudioMixingWriteSilence(pC);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingStepAudioMix():\
                        M4VSS3GPP_intAudioMixingWriteSilence returns 0x%x",
                        err);
                    return err;
                }

                /**
                * Check if we reached the AddCts */
                if( pC->ewc.dATo >= pC->iAddCts )
                {
                    /**
                    * First segment is over, state transition to second and return OK */
                    pC->State = M4VSS3GPP_kAudioMixingState_AUDIO_SECOND_SEGMENT;

                    /**
                    * Return with no error so the step function will be called again */
                    pC->pAddedClipCtxt->iAoffset =
                        (M4OSA_Int32)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5);

                    M4OSA_TRACE2_0("M4VSS3GPP_intAudioMixingStepAudioReplace():\
                         returning M4NO_ERROR (1->2)");
                    return M4NO_ERROR;
                }
            }
            break;

            /**********************************************************/
        case M4VSS3GPP_kAudioMixingState_AUDIO_SECOND_SEGMENT:
            {
                err = M4VSS3GPP_intAudioMixingCopyAdded(pC);

                if( M4WAR_NO_MORE_AU == err )
                {
                    /**
                    * Decide what to do when audio is over */

                    if( pC->uiEndLoop > 0 )
                    {
                        /**
                        * Jump at the Begin loop time */
                        M4OSA_Int32 time = (M4OSA_Int32)(pC->uiBeginLoop);

                        err = pC->pAddedClipCtxt->ShellAPI.m_pReader->m_pFctJump(
                            pC->pAddedClipCtxt->pReaderContext,
                            (M4_StreamHandler
                            *)pC->pAddedClipCtxt->pAudioStream, &time);

                        if( M4NO_ERROR != err )
                        {
                            M4OSA_TRACE1_1(
                                "M4VSS3GPP_intAudioMixingStepAudioReplace():\
                                m_pReader->m_pFctJump(audio returns 0x%x",
                                err);
                            return err;
                        }

                        /**
                        * 'BZZZ' bug fix:
                        * add a silence frame */
                        err = M4VSS3GPP_intAudioMixingWriteSilence(pC);

                        if( M4NO_ERROR != err )
                        {
                            M4OSA_TRACE1_1(
                                "M4VSS3GPP_intAudioMixingStepAudioMix():\
                                M4VSS3GPP_intAudioMixingWriteSilence returns 0x%x",
                                err);
                            return err;
                        }

                        /**
                        * Return with no error so the step function will be called again to
                          read audio data */
                        pC->pAddedClipCtxt->iAoffset =
                            (M4OSA_Int32)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5);

                        M4OSA_TRACE2_0(
                            "M4VSS3GPP_intAudioMixingStepAudioReplace():\
                            returning M4NO_ERROR (loop)");

                        return M4NO_ERROR;
                    }
                    else if( M4OSA_TRUE == pC->bSupportSilence )
                    {
                        /**
                        * Second segment is over, state transition to third and return OK */
                        pC->State = M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT;

                        /**
                        * Return with no error so the step function will be called again */
                        M4OSA_TRACE2_0(
                            "M4VSS3GPP_intAudioMixingStepAudioReplace():\
                                 returning M4NO_ERROR (2->3)");
                        return M4NO_ERROR;
                    }
                    else
                    {
                        /**
                        * The third segment (silence) is only done if supported.
                        * In other case, we finish here. */
                        pC->State = M4VSS3GPP_kAudioMixingState_FINISHED;

                        /**
                        * Return with no error so the step function will be called again */
                        M4OSA_TRACE2_0(
                            "M4VSS3GPP_intAudioMixingStepAudioReplace():\
                                 returning M4NO_ERROR (2->F)");
                        return M4NO_ERROR;
                    }
                }
                else if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingStepAudioReplace():\
                        M4VSS3GPP_intAudioMixingCopyOrig(2) returns 0x%x",
                        err);
                    return err;
                }

                /**
                * Check if we reached the end of the clip */
                if( pC->ewc.dATo >= pC->ewc.iOutputDuration )
                {
                    M4OSA_TRACE3_0(
                        "M4VSS3GPP_intAudioMixingStepAudioReplace(): Clip duration reached,\
                        returning M4WAR_NO_MORE_AU");
                    return M4WAR_NO_MORE_AU; /**< Simulate end of file error */
                }
            }
            break;

            /**********************************************************/
        case M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT:
            {
                /**
                * Replace the SID (silence) payload in the writer AU */
                err = M4VSS3GPP_intAudioMixingWriteSilence(pC);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingStepAudioMix():\
                        M4VSS3GPP_intAudioMixingWriteSilence returns 0x%x",
                        err);
                    return err;
                }

                /**
                * Check if we reached the end of the video */
                if( pC->ewc.dATo >= pC->ewc.iOutputDuration )
                {
                    M4OSA_TRACE3_0(
                        "M4VSS3GPP_intAudioMixingStepAudioReplace():\
                        Video duration reached, returning M4WAR_NO_MORE_AU");
                    return M4WAR_NO_MORE_AU; /**< Simulate end of file error */
                }
            }
            break;
        default:
            break;
    }

    /**
    * Return with no error */
    M4OSA_TRACE3_0(
        "M4VSS3GPP_intAudioMixingStepAudioReplace(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingCopyOrig(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Read one AU from the original audio file and write it to the output
 * @note
 * @param    pC    (IN) VSS audio mixing internal context
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingCopyOrig(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;

    /**
    * Read the input original audio AU */
    err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pInputClipCtxt);

    M4OSA_TRACE2_3("G .... read  : cts  = %.0f + %.0f [ 0x%x ]",
        pC->pInputClipCtxt->iAudioFrameCts / pC->pInputClipCtxt->scale_audio,
        pC->pInputClipCtxt->iAoffset / pC->pInputClipCtxt->scale_audio,
        pC->pInputClipCtxt->uiAudioFrameSize);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE3_1(
            "M4VSS3GPP_intAudioMixingCopyOrig(): m_pFctGetNextAu(audio) returns 0x%x",
            err);
        return err;
    }

    /**
    * Get the output AU to write into */
    err = pC->ShellAPI.pWriterDataFcts->pStartAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingCopyOrig: pWriterDataFcts->pStartAU(audio) returns 0x%x!",
            err);
        return err;
    }

    /**
    * Copy the input AU properties to the output AU */
    pC->ewc.WriterAudioAU.size = pC->pInputClipCtxt->uiAudioFrameSize;
    pC->ewc.WriterAudioAU.CTS =
        pC->pInputClipCtxt->iAudioFrameCts + pC->pInputClipCtxt->iAoffset;

    /**
    * Copy the AU itself */
    memcpy((void *)pC->ewc.WriterAudioAU.dataAddress,
        (void *)pC->pInputClipCtxt->pAudioFramePtr, pC->ewc.WriterAudioAU.size);

    /**
    * Write the mixed AU */
    M4OSA_TRACE2_2("H ---- write : cts  = %ld [ 0x%x ]",
        (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio),
        pC->ewc.WriterAudioAU.size);

    err = pC->ShellAPI.pWriterDataFcts->pProcessAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingCopyOrig: pWriterDataFcts->pProcessAU(audio) returns 0x%x!",
            err);
        return err;
    }

    /**
    * Increment the audio CTS for the next step */
    pC->ewc.dATo += pC->ewc.iSilenceFrameDuration / pC->ewc.scale_audio;

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_intAudioMixingCopyOrig(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingCopyAdded(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Read one AU from the added audio file and write it to the output
 * @note
 * @param    pC    (IN) VSS audio mixing internal context
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingCopyAdded(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;

    if(pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType == M4VIDEOEDITING_kMP3 ||
        pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType == M4VIDEOEDITING_kPCM ||
        pC->b_SSRCneeded == M4OSA_TRUE ||
        pC->ChannelConversion > 0)
    {
        M4ENCODER_AudioBuffer pEncInBuffer; /**< Encoder input buffer for api */
        M4ENCODER_AudioBuffer
            pEncOutBuffer; /**< Encoder output buffer for api */
        M4OSA_Time
            frameTimeDelta; /**< Duration of the encoded (then written) data */
        M4OSA_MemAddr8 tempPosBuffer;

        err = M4VSS3GPP_intAudioMixingConvert(pC);

        if( err == M4VSS3GPP_WAR_END_OF_ADDED_AUDIO )
        {
            M4OSA_TRACE2_0(
                "M4VSS3GPP_intAudioMixingCopyAdded:\
                M4VSS3GPP_intAudioMixingConvert end of added file");
            return M4NO_ERROR;
        }
        else if( err != M4NO_ERROR )
        {
            M4OSA_TRACE1_1("M4VSS3GPP_intAudioMixingCopyAdded:\
                M4VSS3GPP_intAudioMixingConvert returned 0x%x", err);
            return err;
        }

        /**
        * Get the output AU to write into */
        err = pC->ShellAPI.pWriterDataFcts->pStartAU(pC->ewc.p3gpWriterContext,
            M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingStepAudioMix:\
                pWriterDataFcts->pStartAU(audio) returns 0x%x!",
                err);
            return err;
        }

        /* [Mono] or [Stereo interleaved] : all is in one buffer */
        pEncInBuffer.pTableBuffer[0] = pC->pSsrcBufferOut;
        pEncInBuffer.pTableBufferSize[0] =
            pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
        pEncInBuffer.pTableBuffer[1] = M4OSA_NULL;
        pEncInBuffer.pTableBufferSize[1] = 0;

        /* Time in ms from data size, because it is PCM16 samples */
        frameTimeDelta = pEncInBuffer.pTableBufferSize[0] / sizeof(short)
            / pC->ewc.uiNbChannels;

        /**
        * Prepare output buffer */
        pEncOutBuffer.pTableBuffer[0] =
            (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress;
        pEncOutBuffer.pTableBufferSize[0] = 0;

        M4OSA_TRACE2_0("K **** blend AUs");
        /**
        * Encode the PCM audio */

        err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep(
            pC->ewc.pAudioEncCtxt, &pEncInBuffer, &pEncOutBuffer);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingDoMixing():\
                pAudioEncoderGlobalFcts->pFctStep returns 0x%x",
                err);
            return err;
        }

        /**
        * Set AU cts and size */
        pC->ewc.WriterAudioAU.size =
            pEncOutBuffer.
            pTableBufferSize[0]; /**< Get the size of encoded data */
        pC->ewc.WriterAudioAU.CTS += frameTimeDelta;

        /* Update decoded buffer here */
        if( M4OSA_TRUE == pC->b_SSRCneeded || pC->ChannelConversion > 0 )
        {
            tempPosBuffer = pC->pSsrcBufferOut
                + pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
            memmove((void *)pC->pSsrcBufferOut, (void *)tempPosBuffer,
                pC->pPosInSsrcBufferOut - tempPosBuffer);
            pC->pPosInSsrcBufferOut -=
                pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
        }
        else
        {
            tempPosBuffer = pC->pSsrcBufferIn + pC->minimumBufferIn;
            memmove((void *)pC->pSsrcBufferIn, (void *)tempPosBuffer,
                pC->pPosInSsrcBufferIn - tempPosBuffer);
            pC->pPosInSsrcBufferIn -= pC->minimumBufferIn;
        }

        /**
        * Write the mixed AU */
        M4OSA_TRACE2_2("J ---- write : cts  = %ld [ 0x%x ]",
            (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio),
            pC->ewc.WriterAudioAU.size);

        err =
            pC->ShellAPI.pWriterDataFcts->pProcessAU(pC->ewc.p3gpWriterContext,
            M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingCopyAdded:\
                pWriterDataFcts->pProcessAU(audio) returns 0x%x!",
                err);
            return err;
        }

        /**
        * Increment the audio CTS for the next step */
        pC->ewc.dATo += frameTimeDelta / pC->ewc.scale_audio;
    }
    else
    {
        /**
        * Read the added audio AU */
        err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pAddedClipCtxt);

        M4OSA_TRACE2_3("I .... read  : cts  = %.0f + %.0f [ 0x%x ]",
            pC->pAddedClipCtxt->iAudioFrameCts
            / pC->pAddedClipCtxt->scale_audio,
            pC->pAddedClipCtxt->iAoffset / pC->pAddedClipCtxt->scale_audio,
            pC->pAddedClipCtxt->uiAudioFrameSize);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE3_1(
                "M4VSS3GPP_intAudioMixingCopyAdded(): m_pFctGetNextAu(audio) returns 0x%x",
                err);
            return err;
        }

        /**
        * Get the output AU to write into */
        err = pC->ShellAPI.pWriterDataFcts->pStartAU(pC->ewc.p3gpWriterContext,
            M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingCopyAdded:\
                pWriterDataFcts->pStartAU(audio) returns 0x%x!",
                err);
            return err;
        }

        /**
        * Copy the input AU properties to the output AU */

        /** THE CHECK BELOW IS ADDED TO PREVENT ISSUES LINKED TO PRE-ALLOCATED MAX AU SIZE
        max AU size is set based on M4VSS3GPP_AUDIO_MAX_AU_SIZE defined in file
        M4VSS3GPP_InternalConfig.h, If this error occurs increase the limit set in this file
        */
        if( pC->pAddedClipCtxt->uiAudioFrameSize > pC->ewc.WriterAudioAU.size )
        {
            M4OSA_TRACE1_2(
                "ERROR: audio AU size (%d) to copy larger than allocated one (%d) => abort",
                pC->pAddedClipCtxt->uiAudioFrameSize,
                pC->ewc.WriterAudioAU.size);
            M4OSA_TRACE1_0(
                "PLEASE CONTACT SUPPORT TO EXTEND MAX AU SIZE IN THE PRODUCT LIBRARY");
            err = M4ERR_UNSUPPORTED_MEDIA_TYPE;
            return err;
        }
        pC->ewc.WriterAudioAU.size = pC->pAddedClipCtxt->uiAudioFrameSize;
        pC->ewc.WriterAudioAU.CTS =
            pC->pAddedClipCtxt->iAudioFrameCts + pC->pAddedClipCtxt->iAoffset;

        /**
        * Copy the AU itself */
        memcpy((void *)pC->ewc.WriterAudioAU.dataAddress,
            (void *)pC->pAddedClipCtxt->pAudioFramePtr, pC->ewc.WriterAudioAU.size);

        /**
        * Write the mixed AU */
        M4OSA_TRACE2_2("J ---- write : cts  = %ld [ 0x%x ]",
            (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio),
            pC->ewc.WriterAudioAU.size);

        err =
            pC->ShellAPI.pWriterDataFcts->pProcessAU(pC->ewc.p3gpWriterContext,
            M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingCopyAdded:\
                pWriterDataFcts->pProcessAU(audio) returns 0x%x!",
                err);
            return err;
        }

        /**
        * Increment the audio CTS for the next step */
        pC->ewc.dATo += pC->ewc.iSilenceFrameDuration / pC->ewc.scale_audio;
    }

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_intAudioMixingCopyAdded(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR  M4VSS3GPP_intAudioMixingConvert(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Convert PCM of added track to the right ASF / nb of Channels
 * @note
 * @param    pC    (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingConvert(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;
    int ssrcErr; /**< Error while ssrc processing */
    M4OSA_UInt32 uiChannelConvertorNbSamples =
        pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize / sizeof(short)
        / pC->pInputClipCtxt->pSettings->ClipProperties.uiNbChannels;
    M4OSA_MemAddr8 tempPosBuffer;

    M4OSA_UInt32 outFrameCount = uiChannelConvertorNbSamples;
    /* Do we need to feed SSRC buffer In ? */
    /**
    * RC: This is not really optimum (memmove). We should handle this with linked list. */
    while( pC->pPosInSsrcBufferIn - pC->pSsrcBufferIn < (M4OSA_Int32)pC->minimumBufferIn )
    {
        /* We need to get more PCM data */
        if (pC->bNoLooping == M4OSA_TRUE)
        {
            err = M4WAR_NO_MORE_AU;
        }
        else
        {
        err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pAddedClipCtxt);
        }
        if(pC->bjumpflag)
        {
        /**
            * Jump at the Begin loop time */
            M4OSA_Int32 time = (M4OSA_Int32)(pC->uiBeginLoop);

            err =
                pC->pAddedClipCtxt->ShellAPI.m_pReader->m_pFctJump\
                    (pC->pAddedClipCtxt->pReaderContext,
                     (M4_StreamHandler*)pC->pAddedClipCtxt->pAudioStream, &time);
            if (M4NO_ERROR != err)
            {
                M4OSA_TRACE1_1("M4VSS3GPP_intAudioMixingConvert():\
                     m_pReader->m_pFctJump(audio returns 0x%x", err);
                return err;
            }
            pC->bjumpflag = M4OSA_FALSE;
        }
        M4OSA_TRACE2_3("E .... read  : cts  = %.0f + %.0f [ 0x%x ]",
             pC->pAddedClipCtxt->iAudioFrameCts / pC->pAddedClipCtxt->scale_audio,
                 pC->pAddedClipCtxt->iAoffset / pC->pAddedClipCtxt->scale_audio,
                     pC->pAddedClipCtxt->uiAudioFrameSize);
        if( M4WAR_NO_MORE_AU == err )
        {
            if(pC->bNoLooping == M4OSA_TRUE)
            {
                pC->uiEndLoop =0; /* Value 0 means no looping is required */
            }
            /**
            * Decide what to do when audio is over */
            if( pC->uiEndLoop > 0 )
            {
                /**
                * Jump at the Begin loop time */
                M4OSA_Int32 time = (M4OSA_Int32)(pC->uiBeginLoop);

                err = pC->pAddedClipCtxt->ShellAPI.m_pReader->m_pFctJump(
                    pC->pAddedClipCtxt->pReaderContext,
                    (M4_StreamHandler *)pC->pAddedClipCtxt->
                    pAudioStream, &time);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingConvert():\
                        m_pReader->m_pFctJump(audio returns 0x%x",
                        err);
                    return err;
                }
            }
            else
            {
                /* Transition from encoding state to reading state */
                err = M4VSS3GPP_intAudioMixingTransition(pC);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingStepAudioMix(): pre-encode fails err = 0x%x",
                        err);
                    return err;
                }

                /**
                * Second segment is over, state transition to third and return OK */
                pC->State = M4VSS3GPP_kAudioMixingState_AUDIO_THIRD_SEGMENT;

                /**
                * Return with no error so the step function will be called again */
                M4OSA_TRACE2_0(
                    "M4VSS3GPP_intAudioMixingConvert():\
                    returning M4VSS3GPP_WAR_END_OF_ADDED_AUDIO (2->3) a");
                return M4VSS3GPP_WAR_END_OF_ADDED_AUDIO;
            }
        }
        else if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingConvert(): m_pFctGetNextAu(audio) returns 0x%x",
                err);
            return err;
        }

        err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pAddedClipCtxt);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingDoMixing:\
                M4VSS3GPP_intClipDecodeCurrentAudioFrame(added) returns 0x%x",
                err);
            return M4VSS3GPP_ERR_INPUT_AUDIO_CORRUPTED_AU;
        }

        /* Copy decoded data into SSRC buffer in */
        memcpy((void *)pC->pPosInSsrcBufferIn,
            (void *)pC->pAddedClipCtxt->AudioDecBufferOut.m_dataAddress,
            pC->pAddedClipCtxt->AudioDecBufferOut.m_bufferSize);
        /* Update position pointer into SSRC buffer In */

        pC->pPosInSsrcBufferIn +=
            pC->pAddedClipCtxt->AudioDecBufferOut.m_bufferSize;
    }

    /* Do the resampling / channel conversion if needed (=feed buffer out) */
    if( pC->b_SSRCneeded == M4OSA_TRUE )
    {
        pC->ChannelConversion = 0;
        if( pC->ChannelConversion > 0 )
        {
            while( pC->pPosInTempBuffer - pC->pTempBuffer
                < (M4OSA_Int32)(pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize
                *pC->pAddedClipCtxt->pSettings->ClipProperties.uiNbChannels)
                / pC->ChannelConversion )
                /* We use ChannelConversion variable because in case 2, we need twice less data */
            {
                ssrcErr = 0;
                memset((void *)pC->pPosInTempBuffer,0,
                    (pC->iSsrcNbSamplOut * sizeof(short) * pC->ewc.uiNbChannels));

                LVAudioresample_LowQuality((short*)pC->pPosInTempBuffer,
                    (short*)pC->pSsrcBufferIn,
                    pC->iSsrcNbSamplOut,
                    pC->pLVAudioResampler);
                if( 0 != ssrcErr )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingConvert: SSRC_Process returns 0x%x, returning ",
                        ssrcErr);
                    return ssrcErr;
                }

                pC->pPosInTempBuffer += pC->iSsrcNbSamplOut * sizeof(short)
                    * pC->pAddedClipCtxt->pSettings->
                    ClipProperties.uiNbChannels;

                /* Update SSRC bufferIn */
                tempPosBuffer =
                    pC->pSsrcBufferIn + (pC->iSsrcNbSamplIn * sizeof(short)
                    * pC->pAddedClipCtxt->pSettings->
                    ClipProperties.uiNbChannels);
                memmove((void *)pC->pSsrcBufferIn, (void *)tempPosBuffer,
                    pC->pPosInSsrcBufferIn - tempPosBuffer);
                pC->pPosInSsrcBufferIn -= pC->iSsrcNbSamplIn * sizeof(short)
                    * pC->pAddedClipCtxt->pSettings->
                    ClipProperties.uiNbChannels;
            }
        }
        else
        {
            while( pC->pPosInSsrcBufferOut - pC->pSsrcBufferOut
                < (M4OSA_Int32)pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize )
            {
                ssrcErr = 0;
                memset((void *)pC->pPosInSsrcBufferOut,0,
                    (pC->iSsrcNbSamplOut * sizeof(short) * pC->ewc.uiNbChannels));

                LVAudioresample_LowQuality((short*)pC->pPosInSsrcBufferOut,
                    (short*)pC->pSsrcBufferIn,
                    pC->iSsrcNbSamplOut,
                    pC->pLVAudioResampler);
                if( 0 != ssrcErr )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingConvert: SSRC_Process returns 0x%x, returning ",
                        ssrcErr);
                    return ssrcErr;
                }
                pC->pPosInSsrcBufferOut +=
                    pC->iSsrcNbSamplOut * sizeof(short) * pC->ewc.uiNbChannels;

                /* Update SSRC bufferIn */
                tempPosBuffer =
                    pC->pSsrcBufferIn + (pC->iSsrcNbSamplIn * sizeof(short)
                    * pC->pAddedClipCtxt->pSettings->
                    ClipProperties.uiNbChannels);
                memmove((void *)pC->pSsrcBufferIn, (void *)tempPosBuffer,
                    pC->pPosInSsrcBufferIn - tempPosBuffer);
                pC->pPosInSsrcBufferIn -= pC->iSsrcNbSamplIn * sizeof(short)
                    * pC->pAddedClipCtxt->pSettings->
                    ClipProperties.uiNbChannels;
            }
        }

        /* Convert Stereo<->Mono */
        switch( pC->ChannelConversion )
        {
            case 0: /* No channel conversion */
                break;

            case 1: /* stereo to mono */
                if( pC->pPosInSsrcBufferOut - pC->pSsrcBufferOut
                    < (M4OSA_Int32)pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize )
                {
                    From2iToMono_16((short *)pC->pTempBuffer,
                        (short *)pC->pSsrcBufferOut,
                        (short)(uiChannelConvertorNbSamples));
                    /* Update pTempBuffer */
                    tempPosBuffer = pC->pTempBuffer
                        + (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.
                        uiNbChannels); /* Buffer is in bytes */
                    memmove((void *)pC->pTempBuffer, (void *)tempPosBuffer,
                        pC->pPosInTempBuffer - tempPosBuffer);
                    pC->pPosInTempBuffer -=
                        (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.uiNbChannels);
                    pC->pPosInSsrcBufferOut +=
                        pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
                }
                break;

            case 2: /* mono to stereo */
                if( pC->pPosInSsrcBufferOut - pC->pSsrcBufferOut
                    < (M4OSA_Int32)pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize )
                {
                    MonoTo2I_16((short *)pC->pTempBuffer,
                        (short *)pC->pSsrcBufferOut,
                        (short)uiChannelConvertorNbSamples);
                    tempPosBuffer = pC->pTempBuffer
                        + (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.uiNbChannels);
                    memmove((void *)pC->pTempBuffer, (void *)tempPosBuffer,
                        pC->pPosInTempBuffer - tempPosBuffer);
                    pC->pPosInTempBuffer -=
                        (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.uiNbChannels);
                    pC->pPosInSsrcBufferOut +=
                        pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
                }
                break;
        }
    }
    else if( pC->ChannelConversion > 0 )
    {
        //M4OSA_UInt32 uiChannelConvertorNbSamples =
        // pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize / sizeof(short) /
        // pC->pInputClipCtxt->pSettings->ClipProperties.uiNbChannels;
        /* Convert Stereo<->Mono */
        switch( pC->ChannelConversion )
        {
            case 0: /* No channel conversion */
                break;

            case 1: /* stereo to mono */
                if( pC->pPosInSsrcBufferOut - pC->pSsrcBufferOut
                    < (M4OSA_Int32)pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize )
                {
                    From2iToMono_16((short *)pC->pSsrcBufferIn,
                        (short *)pC->pSsrcBufferOut,
                        (short)(uiChannelConvertorNbSamples));
                    /* Update pTempBuffer */
                    tempPosBuffer = pC->pSsrcBufferIn
                        + (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.
                        uiNbChannels); /* Buffer is in bytes */
                    memmove((void *)pC->pSsrcBufferIn, (void *)tempPosBuffer,
                        pC->pPosInSsrcBufferIn - tempPosBuffer);
                    pC->pPosInSsrcBufferIn -=
                        (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.uiNbChannels);
                    pC->pPosInSsrcBufferOut +=
                        pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
                }
                break;

            case 2: /* mono to stereo */
                if( pC->pPosInSsrcBufferOut - pC->pSsrcBufferOut
                    < (M4OSA_Int32)pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize )
                {
                    MonoTo2I_16((short *)pC->pSsrcBufferIn,
                        (short *)pC->pSsrcBufferOut,
                        (short)uiChannelConvertorNbSamples);
                    tempPosBuffer = pC->pSsrcBufferIn
                        + (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.uiNbChannels);
                    memmove((void *)pC->pSsrcBufferIn, (void *)tempPosBuffer,
                        pC->pPosInSsrcBufferIn - tempPosBuffer);
                    pC->pPosInSsrcBufferIn -=
                        (uiChannelConvertorNbSamples * sizeof(short)
                        * pC->pAddedClipCtxt->pSettings->
                        ClipProperties.uiNbChannels);
                    pC->pPosInSsrcBufferOut +=
                        pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
                }
                break;
        }
    }
    else
    {
        /* No channel conversion nor sampl. freq. conversion needed, just buffer management */
        pC->pPosInSsrcBufferOut = pC->pPosInSsrcBufferIn;
    }

    return M4NO_ERROR;
}

M4OSA_Int32 M4VSS3GPP_getDecibelSound( M4OSA_UInt32 value )
    {
    int dbSound = 1;

    if( value == 0 )
        return 0;

    if( value > 0x4000 && value <= 0x8000 )      // 32768
        dbSound = 90;

    else if( value > 0x2000 && value <= 0x4000 ) // 16384
        dbSound = 84;

    else if( value > 0x1000 && value <= 0x2000 ) // 8192
        dbSound = 78;

    else if( value > 0x0800 && value <= 0x1000 ) // 4028
        dbSound = 72;

    else if( value > 0x0400 && value <= 0x0800 ) // 2048
        dbSound = 66;

    else if( value > 0x0200 && value <= 0x0400 ) // 1024
        dbSound = 60;

    else if( value > 0x0100 && value <= 0x0200 ) // 512
        dbSound = 54;

    else if( value > 0x0080 && value <= 0x0100 ) // 256
        dbSound = 48;

    else if( value > 0x0040 && value <= 0x0080 ) // 128
        dbSound = 42;

    else if( value > 0x0020 && value <= 0x0040 ) // 64
        dbSound = 36;

    else if( value > 0x0010 && value <= 0x0020 ) // 32
        dbSound = 30;

    else if( value > 0x0008 && value <= 0x0010 ) //16
        dbSound = 24;

    else if( value > 0x0007 && value <= 0x0008 ) //8
        dbSound = 24;

    else if( value > 0x0003 && value <= 0x0007 ) // 4
        dbSound = 18;

    else if( value > 0x0001 && value <= 0x0003 ) //2
        dbSound = 12;

    else if( value > 0x000 && value <= 0x0001 )  // 1
        dbSound = 6;

    else
        dbSound = 0;

    return dbSound;
    }
/**
 ******************************************************************************
 * M4OSA_ERR  M4VSS3GPP_intAudioMixingDoMixing(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Mix the current audio AUs (decoder, mix, encode)
 * @note
 * @param    pC    (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingDoMixing(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;
    M4OSA_Int16 *pPCMdata1;
    M4OSA_Int16 *pPCMdata2;
    M4OSA_UInt32 uiPCMsize;

    M4ENCODER_AudioBuffer pEncInBuffer;  /**< Encoder input buffer for api */
    M4ENCODER_AudioBuffer pEncOutBuffer; /**< Encoder output buffer for api */
    M4OSA_Time
        frameTimeDelta; /**< Duration of the encoded (then written) data */
    M4OSA_MemAddr8 tempPosBuffer;
    /* ducking variable */
    M4OSA_UInt16 loopIndex = 0;
    M4OSA_Int16 *pPCM16Sample = M4OSA_NULL;
    M4OSA_Int32 peakDbValue = 0;
    M4OSA_Int32 previousDbValue = 0;
    M4OSA_UInt32 i;

    /**
    * Decode original audio track AU */

    err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pInputClipCtxt);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingDoMixing:\
            M4VSS3GPP_intClipDecodeCurrentAudioFrame(orig) returns 0x%x",
            err);
        return M4VSS3GPP_ERR_INPUT_AUDIO_CORRUPTED_AU;
    }

    if( M4OSA_TRUE == pC->b_SSRCneeded || pC->ChannelConversion > 0
        || pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
        == M4VIDEOEDITING_kMP3 )
    {
        err = M4VSS3GPP_intAudioMixingConvert(pC);

        if( err == M4VSS3GPP_WAR_END_OF_ADDED_AUDIO )
        {
            return err;
        }

        if( err != M4NO_ERROR )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingDoMixing: M4VSS3GPP_intAudioMixingConvert returned 0x%x",
                err);
            return M4VSS3GPP_ERR_AUDIO_DECODED_PCM_SIZE_ISSUE;
        }

        /**
        * Get the output AU to write into */
        err = pC->ShellAPI.pWriterDataFcts->pStartAU(pC->ewc.p3gpWriterContext,
            M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingStepAudioMix:\
                pWriterDataFcts->pStartAU(audio) returns 0x%x!",
                err);
            return err;
        }

        pPCMdata2 = (M4OSA_Int16 *)pC->pSsrcBufferOut;
    }
    else
    {
        /**
        * Decode added audio track AU */
        err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pAddedClipCtxt);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingDoMixing:\
                M4VSS3GPP_intClipDecodeCurrentAudioFrame(added) returns 0x%x",
                err);
            return M4VSS3GPP_ERR_INPUT_AUDIO_CORRUPTED_AU;
        }

        /**
        * Check both clips decoded the same amount of PCM samples */
        if( pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize
            != pC->pAddedClipCtxt->AudioDecBufferOut.m_bufferSize )
        {
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingDoMixing:\
                both clips AU must have the same decoded PCM size!");
            return M4VSS3GPP_ERR_AUDIO_DECODED_PCM_SIZE_ISSUE;
        }
        pPCMdata2 = (M4OSA_Int16 *)pC->pAddedClipCtxt->AudioDecBufferOut.m_dataAddress;
    }

    /**
    * Mix the two decoded PCM audios */
    pPCMdata1 =
        (M4OSA_Int16 *)pC->pInputClipCtxt->AudioDecBufferOut.m_dataAddress;
    uiPCMsize = pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize
        / 2; /*buffer size (bytes) to number of sample (int16)*/

    if( pC->b_DuckingNeedeed )
    {
        loopIndex = 0;
        peakDbValue = 0;
        previousDbValue = peakDbValue;

        pPCM16Sample = (M4OSA_Int16 *)pC->pInputClipCtxt->
            AudioDecBufferOut.m_dataAddress;

        //Calculate the peak value
         while( loopIndex
             < pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize
            / sizeof(M4OSA_Int16) )
        {
            if( pPCM16Sample[loopIndex] >= 0 )
            {
                peakDbValue = previousDbValue > pPCM16Sample[loopIndex]
                ? previousDbValue : pPCM16Sample[loopIndex];
                previousDbValue = peakDbValue;
            }
            else
            {
                peakDbValue = previousDbValue > -pPCM16Sample[loopIndex]
                ? previousDbValue : -pPCM16Sample[loopIndex];
                previousDbValue = peakDbValue;
            }
            loopIndex++;
        }

        pC->audioVolumeArray[pC->audVolArrIndex] =
            M4VSS3GPP_getDecibelSound(peakDbValue);

        /* WINDOW_SIZE is 10 by default and check for threshold is done after 10 cycles */
        if( pC->audVolArrIndex >= WINDOW_SIZE - 1 )
        {
            pC->bDoDucking =
                M4VSS3GPP_isThresholdBreached((M4OSA_Int32 *)&(pC->audioVolumeArray),
                pC->audVolArrIndex, pC->InDucking_threshold);

            pC->audVolArrIndex = 0;
        }
        else
        {
            pC->audVolArrIndex++;
        }

        /*
        *Below logic controls the mixing weightage for Background Track and Primary Track
        *for the duration of window under analysis to give fade-out for Background and fade-in
        *for primary
        *
        *Current fading factor is distributed in equal range over the defined window size.
        *
        *For a window size = 25 (500 ms (window under analysis) / 20 ms (sample duration))
        *
        */

        if( pC->bDoDucking )
        {
            if( pC->duckingFactor
                > pC->InDucking_lowVolume ) // FADE OUT BG Track
            {
                    // decrement ducking factor in total steps in factor of low volume steps to reach
                    // low volume level
                pC->duckingFactor -= (pC->InDucking_lowVolume);
            }
            else
            {
                pC->duckingFactor = pC->InDucking_lowVolume;
            }
        }
        else
        {
            if( pC->duckingFactor < 1.0 ) // FADE IN BG Track
            {
                // increment ducking factor in total steps of low volume factor to reach
                // orig.volume level
                pC->duckingFactor += (pC->InDucking_lowVolume);
            }
        else
           {
                pC->duckingFactor = 1.0;
            }
        }
        /* endif - ducking_enable */

        /* Mixing Logic */

        while( uiPCMsize-- > 0 )
        {
            M4OSA_Int32 temp;

           /* set vol factor for BT and PT */
            *pPCMdata2 = (M4OSA_Int16)(*pPCMdata2 * pC->fBTVolLevel);

            *pPCMdata1 = (M4OSA_Int16)(*pPCMdata1 * pC->fPTVolLevel);

            /* mix the two samples */

            *pPCMdata2 = (M4OSA_Int16)(( *pPCMdata2) * (pC->duckingFactor));
            *pPCMdata1 = (M4OSA_Int16)(*pPCMdata2 / 2 + *pPCMdata1 / 2);


            if( *pPCMdata1 < 0 )
            {
                temp = -( *pPCMdata1)
                    * 2; // bring to same Amplitude level as it was original

                if( temp > 32767 )
                {
                    *pPCMdata1 = -32766; // less then max allowed value
                }
                else
                {
                    *pPCMdata1 = (M4OSA_Int16)(-temp);
               }
        }
        else
        {
            temp = ( *pPCMdata1)
                * 2; // bring to same Amplitude level as it was original

            if( temp > 32768 )
            {
                *pPCMdata1 = 32767; // less than max allowed value
            }
            else
            {
                *pPCMdata1 = (M4OSA_Int16)temp;
            }
        }

            pPCMdata2++;
            pPCMdata1++;
        }
    }
    else
    {
        while( uiPCMsize-- > 0 )
       {
        /* mix the two samples */
            *pPCMdata1 = (M4OSA_Int16)(*pPCMdata1 * pC->fOrigFactor * pC->fPTVolLevel
               + *pPCMdata2 * pC->fAddedFactor * pC->fBTVolLevel );

            pPCMdata1++;
            pPCMdata2++;
        }
    }

    /* Update pC->pSsrcBufferOut buffer */

    if( M4OSA_TRUE == pC->b_SSRCneeded || pC->ChannelConversion > 0 )
    {
        tempPosBuffer = pC->pSsrcBufferOut
            + pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
        memmove((void *)pC->pSsrcBufferOut, (void *)tempPosBuffer,
            pC->pPosInSsrcBufferOut - tempPosBuffer);
        pC->pPosInSsrcBufferOut -=
            pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
    }
    else if( pC->pAddedClipCtxt->pSettings->ClipProperties.AudioStreamType
        == M4VIDEOEDITING_kMP3 )
    {
        tempPosBuffer = pC->pSsrcBufferIn
            + pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
        memmove((void *)pC->pSsrcBufferIn, (void *)tempPosBuffer,
            pC->pPosInSsrcBufferIn - tempPosBuffer);
        pC->pPosInSsrcBufferIn -=
            pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
    }

    /* [Mono] or [Stereo interleaved] : all is in one buffer */
    pEncInBuffer.pTableBuffer[0] =
        pC->pInputClipCtxt->AudioDecBufferOut.m_dataAddress;
    pEncInBuffer.pTableBufferSize[0] =
        pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
    pEncInBuffer.pTableBuffer[1] = M4OSA_NULL;
    pEncInBuffer.pTableBufferSize[1] = 0;

    /* Time in ms from data size, because it is PCM16 samples */
    frameTimeDelta =
        pEncInBuffer.pTableBufferSize[0] / sizeof(short) / pC->ewc.uiNbChannels;

    /**
    * Prepare output buffer */
    pEncOutBuffer.pTableBuffer[0] =
        (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress;
    pEncOutBuffer.pTableBufferSize[0] = 0;

    M4OSA_TRACE2_0("K **** blend AUs");

    /**
    * Encode the PCM audio */
    err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep(pC->ewc.pAudioEncCtxt,
        &pEncInBuffer, &pEncOutBuffer);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingDoMixing(): pAudioEncoderGlobalFcts->pFctStep returns 0x%x",
            err);
        return err;
    }

    /**
    * Set AU cts and size */
    pC->ewc.WriterAudioAU.size =
        pEncOutBuffer.pTableBufferSize[0]; /**< Get the size of encoded data */
    pC->ewc.WriterAudioAU.CTS += frameTimeDelta;

    /**
    * Write the AU */
    M4OSA_TRACE2_2("L ---- write : cts  = %ld [ 0x%x ]",
        (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio),
        pC->ewc.WriterAudioAU.size);

    err = pC->ShellAPI.pWriterDataFcts->pProcessAU(pC->ewc.p3gpWriterContext,
        M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingDoMixing: pWriterDataFcts->pProcessAU returns 0x%x!",
            err);
        return err;
    }

    /**
    * Increment the audio CTS for the next step */
    pC->ewc.dATo += frameTimeDelta / pC->ewc.scale_audio;

    /**
    * Return with no error */
    M4OSA_TRACE3_0("M4VSS3GPP_intAudioMixingDoMixing(): returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR  M4VSS3GPP_intAudioMixingTransition(M4VSS3GPP_InternalAudioMixingContext *pC)
 * @brief    Decode/encode a few AU backward to initiate the encoder for later Mix segment.
 * @note
 * @param    pC    (IN) VSS audio mixing internal context
 * @return    M4NO_ERROR:    No error
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingTransition(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;

    M4ENCODER_AudioBuffer pEncInBuffer;  /**< Encoder input buffer for api */
    M4ENCODER_AudioBuffer pEncOutBuffer; /**< Encoder output buffer for api */
    M4OSA_Time
        frameTimeDelta = 0; /**< Duration of the encoded (then written) data */

    M4OSA_Int32 iTargetCts, iCurrentCts;

    /**
    * 'BZZZ' bug fix:
    * add a silence frame */
    err = M4VSS3GPP_intAudioMixingWriteSilence(pC);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingTransition():\
            M4VSS3GPP_intAudioMixingWriteSilence returns 0x%x",
            err);
        return err;
    }

    iCurrentCts = (M4OSA_Int32)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5);

    /* Do not do pre-encode step if there is no mixing (remove, 100 %, or not editable) */
    if( M4OSA_FALSE == pC->bAudioMixingIsNeeded )
    {
        /**
        * Advance in the original audio stream to reach the current time
        * (We don't want iAudioCTS to be modified by the jump function,
        * so we have to use a local variable). */
        err = M4VSS3GPP_intClipJumpAudioAt(pC->pInputClipCtxt, &iCurrentCts);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1("M4VSS3GPP_intAudioMixingTransition:\
             M4VSS3GPP_intClipJumpAudioAt() returns 0x%x!", err);
            return err;
        }
    }
    else
    {
        /**< don't try to pre-decode if clip is at its beginning... */
        if( iCurrentCts > 0 )
        {
            /**
            * Get the output AU to write into */
            err = pC->ShellAPI.pWriterDataFcts->pStartAU(
                pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID,
                &pC->ewc.WriterAudioAU);

            if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingTransition:\
                    pWriterDataFcts->pStartAU(audio) returns 0x%x!",
                    err);
                return err;
            }

            /**
            * Jump a few AUs backward */
            iTargetCts = iCurrentCts - M4VSS3GPP_NB_AU_PREFETCH
                * pC->ewc.iSilenceFrameDuration;

            if( iTargetCts < 0 )
            {
                iTargetCts = 0; /**< Sanity check */
            }

            err = M4VSS3GPP_intClipJumpAudioAt(pC->pInputClipCtxt, &iTargetCts);

            if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingTransition: DECODE_ENCODE-prefetch:\
                    M4VSS3GPP_intClipJumpAudioAt returns 0x%x!",
                    err);
                return err;
            }

            /**
            * Decode/encode up to the wanted position */
            while( pC->pInputClipCtxt->iAudioFrameCts < iCurrentCts )
            {
                err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pInputClipCtxt);

                M4OSA_TRACE2_3("M .... read  : cts  = %.0f + %.0f [ 0x%x ]",
                    pC->pInputClipCtxt->iAudioFrameCts
                    / pC->pInputClipCtxt->scale_audio,
                    pC->pInputClipCtxt->iAoffset
                    / pC->pInputClipCtxt->scale_audio,
                    pC->pInputClipCtxt->uiAudioFrameSize);

                if( M4OSA_ERR_IS_ERROR(err) )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingTransition: DECODE_ENCODE-prefetch:\
                        M4VSS3GPP_intClipReadNextAudioFrame(b) returns 0x%x!",
                        err);
                    return err;
                }

                err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(
                    pC->pInputClipCtxt);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingTransition: DECODE_ENCODE-prefetch:\
                        M4VSS3GPP_intClipDecodeCurrentAudioFrame returns 0x%x!",
                        err);
                    return err;
                }

                /* [Mono] or [Stereo interleaved] : all is in one buffer */
                pEncInBuffer.pTableBuffer[0] =
                    pC->pInputClipCtxt->AudioDecBufferOut.m_dataAddress;
                pEncInBuffer.pTableBufferSize[0] =
                    pC->pInputClipCtxt->AudioDecBufferOut.m_bufferSize;
                pEncInBuffer.pTableBuffer[1] = M4OSA_NULL;
                pEncInBuffer.pTableBufferSize[1] = 0;

                /* Time in ms from data size, because it is PCM16 samples */
                frameTimeDelta =
                    pEncInBuffer.pTableBufferSize[0] / sizeof(short)
                    / pC->ewc.uiNbChannels;

                /**
                * Prepare output buffer */
                pEncOutBuffer.pTableBuffer[0] =
                    (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress;
                pEncOutBuffer.pTableBufferSize[0] = 0;

                M4OSA_TRACE2_0("N **** pre-encode");

                /**
                * Encode the PCM audio */
                err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep(
                    pC->ewc.pAudioEncCtxt, &pEncInBuffer, &pEncOutBuffer);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingTransition():\
                        pAudioEncoderGlobalFcts->pFctStep returns 0x%x",
                        err);
                    return err;
                }
            }

            /**
            * Set AU cts and size */
            pC->ewc.WriterAudioAU.size = pEncOutBuffer.pTableBufferSize[
                0]; /**< Get the size of encoded data */
                pC->ewc.WriterAudioAU.CTS += frameTimeDelta;

                /**
                * Write the AU */
                M4OSA_TRACE2_2("O ---- write : cts  = %ld [ 0x%x ]",
                    (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio),
                    pC->ewc.WriterAudioAU.size);

                err = pC->ShellAPI.pWriterDataFcts->pProcessAU(
                    pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID,
                    &pC->ewc.WriterAudioAU);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingTransition:\
                        pWriterDataFcts->pProcessAU returns 0x%x!",    err);
                    return err;
                }

                /**
                * Increment the audio CTS for the next step */
                pC->ewc.dATo += pC->ewc.iSilenceFrameDuration / pC->ewc.scale_audio;
        }
    }

    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingCreateVideoEncoder()
 * @brief    Creates the video encoder
 * @note
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingCreateVideoEncoder(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err;
    M4ENCODER_AdvancedParams EncParams;

    /**
    * Simulate a writer interface with our specific function */
    pC->ewc.OurWriterDataInterface.pProcessAU =
        M4VSS3GPP_intProcessAU; /**< This function is VSS 3GPP specific,
                                but it follow the writer interface */
    pC->ewc.OurWriterDataInterface.pStartAU =
        M4VSS3GPP_intStartAU; /**< This function is VSS 3GPP specific,
                              but it follow the writer interface */
    pC->ewc.OurWriterDataInterface.pWriterContext =
        (M4WRITER_Context)
        pC; /**< We give the internal context as writer context */

    /**
    * Get the encoder interface, if not already done */
    if( M4OSA_NULL == pC->ShellAPI.pVideoEncoderGlobalFcts )
    {
        err = M4VSS3GPP_setCurrentVideoEncoder(&pC->ShellAPI,
            pC->ewc.VideoStreamType);
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingCreateVideoEncoder: setCurrentEncoder returns 0x%x",
            err);
        M4ERR_CHECK_RETURN(err);
    }

    /**
    * Set encoder shell parameters according to VSS settings */

    /* Common parameters */
    EncParams.InputFormat = M4ENCODER_kIYUV420;
    EncParams.FrameWidth = pC->ewc.uiVideoWidth;
    EncParams.FrameHeight = pC->ewc.uiVideoHeight;
    EncParams.uiTimeScale = pC->ewc.uiVideoTimeScale;
    EncParams.videoProfile = pC->ewc.outputVideoProfile;
    EncParams.videoLevel = pC->ewc.outputVideoLevel;

    /* No strict regulation in video editor */
    /* Because of the effects and transitions we should allow more flexibility */
    /* Also it prevents to drop important frames
      (with a bad result on sheduling and block effetcs) */
    EncParams.bInternalRegulation = M4OSA_FALSE;
    EncParams.FrameRate = M4ENCODER_kVARIABLE_FPS;

    /**
    * Other encoder settings (defaults) */
    EncParams.uiHorizontalSearchRange = 0;     /* use default */
    EncParams.uiVerticalSearchRange = 0;       /* use default */
    EncParams.bErrorResilience = M4OSA_FALSE;  /* no error resilience */
    EncParams.uiIVopPeriod = 0;                /* use default */
    EncParams.uiMotionEstimationTools = 0;     /* M4V_MOTION_EST_TOOLS_ALL */
    EncParams.bAcPrediction = M4OSA_TRUE;      /* use AC prediction */
    EncParams.uiStartingQuantizerValue = 10;   /* initial QP = 10 */
    EncParams.bDataPartitioning = M4OSA_FALSE; /* no data partitioning */

    switch( pC->ewc.VideoStreamType )
    {
        case M4SYS_kH263:

            EncParams.Format = M4ENCODER_kH263;

            EncParams.uiStartingQuantizerValue = 10;
            EncParams.uiRateFactor = 1; /* default */

            EncParams.bErrorResilience = M4OSA_FALSE;
            EncParams.bDataPartitioning = M4OSA_FALSE;
            break;

        case M4SYS_kMPEG_4:

            EncParams.Format = M4ENCODER_kMPEG4;

            EncParams.uiStartingQuantizerValue = 8;
            EncParams.uiRateFactor = 1;

            if( M4OSA_FALSE == pC->ewc.bVideoDataPartitioning )
            {
                EncParams.bErrorResilience = M4OSA_FALSE;
                EncParams.bDataPartitioning = M4OSA_FALSE;
            }
            else
            {
                EncParams.bErrorResilience = M4OSA_TRUE;
                EncParams.bDataPartitioning = M4OSA_TRUE;
            }
            break;

        case M4SYS_kH264:
            M4OSA_TRACE1_0(
                "M4VSS3GPP_intAudioMixingCreateVideoEncoder: M4SYS_H264");

            EncParams.Format = M4ENCODER_kH264;

            EncParams.uiStartingQuantizerValue = 10;
            EncParams.uiRateFactor = 1; /* default */

            EncParams.bErrorResilience = M4OSA_FALSE;
            EncParams.bDataPartitioning = M4OSA_FALSE;
            break;

        default:
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingCreateVideoEncoder: Unknown videoStreamType 0x%x",
                pC->ewc.VideoStreamType);
            return M4VSS3GPP_ERR_EDITING_UNSUPPORTED_VIDEO_FORMAT;
    }

    EncParams.Bitrate =
        pC->pInputClipCtxt->pSettings->ClipProperties.uiVideoBitrate;

    M4OSA_TRACE1_0(
        "M4VSS3GPP_intAudioMixingCreateVideoEncoder: calling encoder pFctInit");
    /**
    * Init the video encoder (advanced settings version of the encoder Open function) */
    err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctInit(&pC->ewc.pEncContext,
        &pC->ewc.OurWriterDataInterface, M4VSS3GPP_intVPP, pC,
        pC->ShellAPI.pCurrentVideoEncoderExternalAPI,
        pC->ShellAPI.pCurrentVideoEncoderUserData);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingCreateVideoEncoder:\
            pVideoEncoderGlobalFcts->pFctInit returns 0x%x",
            err);
        return err;
    }

    pC->ewc.encoderState = M4VSS3GPP_kEncoderClosed;
    M4OSA_TRACE1_0(
        "M4VSS3GPP_intAudioMixingCreateVideoEncoder: calling encoder pFctOpen");
    M4OSA_TRACE1_2("vss: audio mix encoder open profile :%d, level %d",
        EncParams.videoProfile, EncParams.videoLevel);
    err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctOpen(pC->ewc.pEncContext,
        &pC->ewc.WriterVideoAU, &EncParams);

    if( M4NO_ERROR != err )
    {
        M4OSA_TRACE1_1(
            "M4VSS3GPP_intAudioMixingCreateVideoEncoder:\
            pVideoEncoderGlobalFcts->pFctOpen returns 0x%x",
            err);
        return err;
    }

    pC->ewc.encoderState = M4VSS3GPP_kEncoderStopped;
    M4OSA_TRACE1_0(
        "M4VSS3GPP_intAudioMixingCreateVideoEncoder: calling encoder pFctStart");

    if( M4OSA_NULL != pC->ShellAPI.pVideoEncoderGlobalFcts->pFctStart )
    {
        err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctStart(
            pC->ewc.pEncContext);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingCreateVideoEncoder:\
                pVideoEncoderGlobalFcts->pFctStart returns 0x%x",
                err);
            return err;
        }
    }

    pC->ewc.encoderState = M4VSS3GPP_kEncoderRunning;

    /**
    *    Return */
    M4OSA_TRACE3_0(
        "M4VSS3GPP_intAudioMixingCreateVideoEncoder: returning M4NO_ERROR");
    return M4NO_ERROR;
}

/**
 ******************************************************************************
 * M4OSA_ERR M4VSS3GPP_intAudioMixingDestroyVideoEncoder()
 * @brief    Destroy the video encoder
 * @note
 ******************************************************************************
 */
static M4OSA_ERR M4VSS3GPP_intAudioMixingDestroyVideoEncoder(
    M4VSS3GPP_InternalAudioMixingContext *pC )
{
    M4OSA_ERR err = M4NO_ERROR;

    if( M4OSA_NULL != pC->ewc.pEncContext )
    {
        if( M4VSS3GPP_kEncoderRunning == pC->ewc.encoderState )
        {
            if( pC->ShellAPI.pVideoEncoderGlobalFcts->pFctStop != M4OSA_NULL )
            {
                err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctStop(
                    pC->ewc.pEncContext);

                if( M4NO_ERROR != err )
                {
                    M4OSA_TRACE1_1(
                        "M4VSS3GPP_intAudioMixingDestroyVideoEncoder:\
                        pVideoEncoderGlobalFcts->pFctStop returns 0x%x",
                        err);
                }
            }

            pC->ewc.encoderState = M4VSS3GPP_kEncoderStopped;
        }

        /* Has the encoder actually been opened? Don't close it if that's not the case. */
        if( M4VSS3GPP_kEncoderStopped == pC->ewc.encoderState )
        {
            err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctClose(
                pC->ewc.pEncContext);

            if( M4NO_ERROR != err )
            {
                M4OSA_TRACE1_1(
                    "M4VSS3GPP_intAudioMixingDestroyVideoEncoder:\
                    pVideoEncoderGlobalFcts->pFctClose returns 0x%x",
                    err);
            }

            pC->ewc.encoderState = M4VSS3GPP_kEncoderClosed;
        }

        err = pC->ShellAPI.pVideoEncoderGlobalFcts->pFctCleanup(
            pC->ewc.pEncContext);

        if( M4NO_ERROR != err )
        {
            M4OSA_TRACE1_1(
                "M4VSS3GPP_intAudioMixingDestroyVideoEncoder:\
                pVideoEncoderGlobalFcts->pFctCleanup returns 0x%x!",
                err);
            /**< We do not return the error here because we still have stuff to free */
        }

        pC->ewc.encoderState = M4VSS3GPP_kNoEncoder;
        /**
        * Reset variable */
        pC->ewc.pEncContext = M4OSA_NULL;
    }

    M4OSA_TRACE3_1(
        "M4VSS3GPP_intAudioMixingDestroyVideoEncoder: returning 0x%x", err);
    return err;
}

M4OSA_Bool M4VSS3GPP_isThresholdBreached( M4OSA_Int32 *averageValue,
                                         M4OSA_Int32 storeCount, M4OSA_Int32 thresholdValue )
{
    M4OSA_Bool result = 0;
    int i;
    int finalValue = 0;

    for ( i = 0; i < storeCount; i++ )
        finalValue += averageValue[i];

    finalValue = finalValue / storeCount;


    if( finalValue > thresholdValue )
        result = M4OSA_TRUE;
    else
        result = M4OSA_FALSE;

    return result;
}