/*
 * 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   M4READER_Amr.c
 * @brief  Generic encapsulation of the core amr reader
 * @note   This file implements the generic M4READER interface
 *         on top of the AMR reader
 ************************************************************************
*/
#include "M4OSA_Types.h"
#include "M4OSA_Error.h"
#include "M4OSA_Memory.h"
#include "M4OSA_Debug.h"
#include "M4OSA_CoreID.h"

#include "M4_Utils.h"

#include "M4AMRR_CoreReader.h"
#include "M4READER_Amr.h"

/**
 ************************************************************************
 * structure    M4READER_AMR_Context
 * @brief       This structure defines the internal context of a amr reader instance
 * @note        The context is allocated and de-allocated by the reader
 ************************************************************************
*/
typedef struct _M4READER_AMR_Context
{
    M4OSA_Context           m_pCoreContext;     /**< core amr reader context */
    M4_AudioStreamHandler*  m_pAudioStream;     /**< pointer on the audio stream
                                                 description returned by the core */
    M4SYS_AccessUnit        m_audioAu;          /**< audio access unit to be filled by the core */
    M4OSA_Time              m_maxDuration;      /**< duration of the audio stream */
    M4OSA_FileReadPointer*    m_pOsaFileReaderFcts;    /**< OSAL file read functions */

} M4READER_AMR_Context;


/**
 ************************************************************************
 * @brief    create an instance of the reader
 * @note     allocates the context
 * @param    pContext:        (OUT)    pointer on a reader context
 * @return    M4NO_ERROR                 there is no error
 * @return    M4ERR_ALLOC                a memory allocation has failed
 * @return    M4ERR_PARAMETER            at least one parameter is not properly set (in DEBUG only)
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_create(M4OSA_Context *pContext)
{
    M4READER_AMR_Context* pReaderContext;

    /* Check function parameters */
    M4OSA_DEBUG_IF1((pContext == 0), M4ERR_PARAMETER,
         "M4READER_AMR_create: invalid context pointer");

    pReaderContext = (M4READER_AMR_Context*)M4OSA_32bitAlignedMalloc(sizeof(M4READER_AMR_Context),
         M4READER_AMR, (M4OSA_Char *)"M4READER_AMR_Context");
    if (pReaderContext == M4OSA_NULL)
    {
        return M4ERR_ALLOC;
    }

    pReaderContext->m_pAudioStream  = M4OSA_NULL;
    pReaderContext->m_audioAu.dataAddress = M4OSA_NULL;
    pReaderContext->m_maxDuration = 0;
    pReaderContext->m_pCoreContext = M4OSA_NULL;
    pReaderContext->m_pOsaFileReaderFcts = M4OSA_NULL;

    *pContext = pReaderContext;

    return M4NO_ERROR;
}

/**
 ************************************************************************
 * @brief    destroy the instance of the reader
 * @note     after this call the context is invalid
 *
 * @param    context:        (IN)    Context of the reader
 *
 * @return    M4NO_ERROR                 there is no error
 * @return    M4ERR_PARAMETER            at least one parameter is not properly set
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_destroy(M4OSA_Context context)
{
    M4READER_AMR_Context*   pC=(M4READER_AMR_Context*)context;

    /* Check function parameters*/
    M4OSA_DEBUG_IF1((M4OSA_NULL == pC), M4ERR_PARAMETER,
         "M4READER_AMR_destroy: invalid context pointer");

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

    free(pC);

    return M4NO_ERROR;
}


/**
 ************************************************************************
 * @brief    open the reader and initializes its created instance
 * @note     this function opens the AMR file
 * @param    context:            (IN)    Context of the reader
 * @param    pFileDescriptor:    (IN)    Pointer to proprietary data identifying the media to open
 * @return    M4NO_ERROR                     there is no error
 * @return    M4ERR_PARAMETER                the context is NULL
 * @return    M4ERR_BAD_CONTEXT            provided context is not a valid one
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_open(M4OSA_Context context, M4OSA_Void* pFileDescriptor)
{
    M4READER_AMR_Context*    pC = (M4READER_AMR_Context*)context;
    M4OSA_ERR                err;

    /* Check function parameters*/
    M4OSA_DEBUG_IF1((M4OSA_NULL == pC),              M4ERR_PARAMETER,
         "M4READER_AMR_open: invalid context pointer");
    M4OSA_DEBUG_IF1((M4OSA_NULL == pFileDescriptor), M4ERR_PARAMETER,
         "M4READER_AMR_open: invalid pointer pFileDescriptor");

    err = M4AMRR_openRead( &pC->m_pCoreContext, pFileDescriptor, pC->m_pOsaFileReaderFcts);

    return err;
}



/**
 ************************************************************************
 * @brief    close the reader
 * @note
 * @param    context:        (IN)    Context of the reader
 * @return    M4NO_ERROR                 there is no error
 * @return    M4ERR_PARAMETER            the context is NULL
 * @return    M4ERR_BAD_CONTEXT        provided context is not a valid one
 ************************************************************************
*/
M4OSA_ERR   M4READER_AMR_close(M4OSA_Context context)
{
    M4READER_AMR_Context*    pC = (M4READER_AMR_Context*)context;
    M4OSA_ERR                err;
    M4AMRR_State State;

    /* Check function parameters*/
    M4OSA_DEBUG_IF1((M4OSA_NULL == pC), M4ERR_PARAMETER,
         "M4READER_AMR_close: invalid context pointer");

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

    if (M4OSA_NULL != pC->m_pAudioStream)
    {
        err = M4AMRR_getState(pC->m_pCoreContext, &State,
                ((M4_StreamHandler*)pC->m_pAudioStream)->m_streamId);
        if(M4NO_ERROR != err)
        {
            M4OSA_TRACE1_0("M4READER_AMR_close: error when calling M4AMRR_getState\n");
            return err;
        }

        if (M4AMRR_kReading_nextAU == State)
        {
            err = M4AMRR_freeAU(pC->m_pCoreContext,
                ((M4_StreamHandler*)pC->m_pAudioStream)->m_streamId,  &pC->m_audioAu);
            if (err != M4NO_ERROR)
            {
                M4OSA_TRACE1_0("M4READER_AMR_close: error when freeing access unit\n");
                return err;
            }
        }

        /* Delete the DSI if needed */
        if(M4OSA_NULL != pC->m_pAudioStream->m_basicProperties.m_pDecoderSpecificInfo)
        {
            free(\
                pC->m_pAudioStream->m_basicProperties.m_pDecoderSpecificInfo);

            pC->m_pAudioStream->m_basicProperties.m_decoderSpecificInfoSize = 0;
            pC->m_pAudioStream->m_basicProperties.m_pDecoderSpecificInfo = M4OSA_NULL;
        }

        /* Finally destroy the stream handler */
        free(pC->m_pAudioStream);
        pC->m_pAudioStream = M4OSA_NULL;
    }

    if (M4OSA_NULL != pC->m_pCoreContext)
    {
        err = M4AMRR_closeRead(pC->m_pCoreContext);
        pC->m_pCoreContext = M4OSA_NULL;
    }

    return err;
}

/**
 ************************************************************************
 * @brief    Get the next stream found in the media
 * @note    current version needs to translate M4SYS_Stream to M4_StreamHandler
 *
 * @param    context:        (IN)   Context of the reader
 * @param    pMediaFamily:   (OUT)  pointer to a user allocated M4READER_MediaFamily
 *                                  that will be filled with the media family of the found stream
 * @param    pStreamHandler: (OUT)  pointer to a stream handler that will be
 *                                  allocated and filled with the found stream description
 *
 * @return    M4NO_ERROR            there is no error
 * @return    M4WAR_NO_MORE_STREAM  no more available stream in the media (all streams found)
 * @return    M4ERR_PARAMETER       at least one parameter is not properly set (in DEBUG mode only)
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_getNextStream(M4OSA_Context context, M4READER_MediaFamily *pMediaFamily,
                                     M4_StreamHandler **pStreamHandlerParam)
{
    M4READER_AMR_Context*   pC=(M4READER_AMR_Context*)context;
    M4OSA_ERR               err;
    M4SYS_StreamID          streamIdArray[2];
    M4SYS_StreamDescription streamDesc;
    M4_AudioStreamHandler*  pAudioStreamHandler;
    M4_StreamHandler*       pStreamHandler;

    M4OSA_DEBUG_IF1((pC == 0),                  M4ERR_PARAMETER,
                "M4READER_AMR_getNextStream: invalid context");
    M4OSA_DEBUG_IF1((pMediaFamily == 0),        M4ERR_PARAMETER,
                "M4READER_AMR_getNextStream: invalid pointer to MediaFamily");
    M4OSA_DEBUG_IF1((pStreamHandlerParam == 0), M4ERR_PARAMETER,
                "M4READER_AMR_getNextStream: invalid pointer to StreamHandler");

    err = M4AMRR_getNextStream( pC->m_pCoreContext, &streamDesc);
    if (err == M4WAR_NO_MORE_STREAM)
    {
        streamIdArray[0] = 0;
        streamIdArray[1] = 0;
        err = M4AMRR_startReading(pC->m_pCoreContext, streamIdArray);
        if ((M4OSA_UInt32)M4ERR_ALLOC == err)
        {
            M4OSA_TRACE2_0("M4READER_AMR_getNextStream: M4AMRR_startReading returns M4ERR_ALLOC!");
            return err;
        }
        return M4WAR_NO_MORE_STREAM;
    }
    else if (err != M4NO_ERROR)
    {
        return err;
    }

    *pMediaFamily = M4READER_kMediaFamilyAudio;

    pAudioStreamHandler = (M4_AudioStreamHandler*)M4OSA_32bitAlignedMalloc(sizeof(M4_AudioStreamHandler),
                        M4READER_AMR, (M4OSA_Char *)"M4_AudioStreamHandler");
    if (pAudioStreamHandler == M4OSA_NULL)
    {
        return M4ERR_ALLOC;
    }
    pStreamHandler =(M4_StreamHandler*)(pAudioStreamHandler);
    *pStreamHandlerParam = pStreamHandler;
    pC->m_pAudioStream = pAudioStreamHandler;

    pAudioStreamHandler->m_structSize = sizeof(M4_AudioStreamHandler);

    /*
     * Audio stream handler fields are initialised with 0 value.
     * They will be properly set by the AMR decoder
     */
    pAudioStreamHandler->m_samplingFrequency = 0;
    pAudioStreamHandler->m_byteFrameLength   = 0;
    pAudioStreamHandler->m_byteSampleSize    = 0;
    pAudioStreamHandler->m_nbChannels        = 0;

    pStreamHandler->m_pDecoderSpecificInfo    = (M4OSA_UInt8*)(streamDesc.decoderSpecificInfo);
    pStreamHandler->m_decoderSpecificInfoSize = streamDesc.decoderSpecificInfoSize;
    pStreamHandler->m_streamId                = streamDesc.streamID;
    pStreamHandler->m_duration                = streamDesc.duration;
    pStreamHandler->m_pUserData               = (void*)streamDesc.timeScale; /*trick to change*/

    if (streamDesc.duration > pC->m_maxDuration)
    {
        pC->m_maxDuration = streamDesc.duration;
    }
    pStreamHandler->m_averageBitRate          = streamDesc.averageBitrate;

    M4AMRR_getmaxAUsize(pC->m_pCoreContext, &pStreamHandler->m_maxAUSize);

    switch (streamDesc.streamType)
    {
    case M4SYS_kAMR:
        pStreamHandler->m_streamType = M4DA_StreamTypeAudioAmrNarrowBand;
        break;
    case M4SYS_kAMR_WB:
        pStreamHandler->m_streamType = M4DA_StreamTypeAudioAmrWideBand;
        break;
    default:
        break;
    }

    return err;
}

/**
 ************************************************************************
 * @brief    fill the access unit structure with initialization values
 * @note
 * @param    context:        (IN)     Context of the reader
 * @param    pStreamHandler: (IN)     pointer to the stream handler to
 *                                    which the access unit will be associated
 * @param    pAccessUnit:    (IN/OUT) pointer to the access unit (allocated by the caller)
 *                                      to initialize
 *
 * @return    M4NO_ERROR              there is no error
 * @return    M4ERR_PARAMETER         at least one parameter is not properly set
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_fillAuStruct(M4OSA_Context context, M4_StreamHandler *pStreamHandler,
                                     M4_AccessUnit *pAccessUnit)
{
    M4READER_AMR_Context*   pC = (M4READER_AMR_Context*)context;
    M4SYS_AccessUnit*       pAu;

    M4OSA_DEBUG_IF1((pC == 0),             M4ERR_PARAMETER,
         "M4READER_AMR_fillAuStruct: invalid context");
    M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER,
         "M4READER_AMR_fillAuStruct: invalid pointer to M4_StreamHandler");
    M4OSA_DEBUG_IF1((pAccessUnit == 0),    M4ERR_PARAMETER,
         "M4READER_AMR_fillAuStruct: invalid pointer to M4_AccessUnit");

    if (pStreamHandler == (M4_StreamHandler*)pC->m_pAudioStream)
    {
        pAu = &pC->m_audioAu;
    }
    else
    {
        M4OSA_TRACE1_0("M4READER_AMR_fillAuStruct: passed StreamHandler is not known\n");
        return M4ERR_PARAMETER;
    }

    pAu->dataAddress = M4OSA_NULL;
    pAu->size        = 0;
    /* JC: bug fix 1197 (set CTS to -20 in order the first AU CTS is 0) */
    pAu->CTS         = -20;
    pAu->DTS         = -20;
    pAu->attribute   = 0;
    pAu->nbFrag      = 0;

    pAccessUnit->m_size         = 0;
    /* JC: bug fix 1197 (set CTS to -20 in order the first AU CTS is 0) */
    pAccessUnit->m_CTS          = -20;
    pAccessUnit->m_DTS          = -20;
    pAccessUnit->m_attribute    = 0;
    pAccessUnit->m_dataAddress  = M4OSA_NULL;/*pBuffer;*/
    pAccessUnit->m_maxsize      = pStreamHandler->m_maxAUSize;
    pAccessUnit->m_streamID     = pStreamHandler->m_streamId;
    pAccessUnit->m_structSize   = sizeof(M4_AccessUnit);

    return M4NO_ERROR;
}

/**
 ************************************************************************
 * @brief    get an option value from the reader
 * @note    this function follows the set/get option mechanism described in OSAL 3.0
 *          it allows the caller to retrieve a property value:
 *          - the duration of the longest stream of the media
 *          - the version number of the reader (not implemented yet)
 *
 * @param    context:        (IN)    Context of the reader
 * @param    optionId:        (IN)    indicates the option to get
 * @param    pValue:            (OUT)    pointer to structure or value (allocated by user)
 *                                       where option is stored
 *
 * @return    M4NO_ERROR                 there is no error
 * @return    M4ERR_PARAMETER            at least one parameter is not properly set
 * @return    M4ERR_BAD_OPTION_ID        when the option ID is not a valid one
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_getOption(M4OSA_Context context, M4OSA_OptionID optionId,
                                 M4OSA_DataOption pValue)

{
    M4READER_AMR_Context* pC = (M4READER_AMR_Context*)context;
    M4OSA_ERR err = M4NO_ERROR;

    /* Check function parameters */
    M4OSA_DEBUG_IF1((M4OSA_NULL == pC),     M4ERR_PARAMETER, "invalid context pointer");
    M4OSA_DEBUG_IF1((M4OSA_NULL == pValue), M4ERR_PARAMETER, "invalid value pointer");

    switch(optionId)
    {
    case M4READER_kOptionID_Duration :
        {
            *(M4OSA_Time*)pValue = pC->m_maxDuration;
        }
        break;

    case M4READER_kOptionID_Bitrate:
        {
            M4OSA_UInt32* pBitrate = (M4OSA_UInt32*)pValue;
            if (M4OSA_NULL != pC->m_pAudioStream)
            {
                *pBitrate = pC->m_pAudioStream->m_basicProperties.m_averageBitRate;
            }
            else
            {
                pBitrate = 0;
                err = M4ERR_PARAMETER;
            }

        }
        break;
    case M4READER_kOptionID_Version:
        {
            err = M4AMRR_getVersion((M4_VersionInfo*)pValue);
        }
        break;

    default :
        {
            err = M4ERR_PARAMETER;
        }
    }

    return err;
}

/**
 ************************************************************************
 * @brief   set en option value of the readder
 * @note    this function follows the set/get option mechanism described in OSAL 3.0
 *          it allows the caller to set a property value:
 *          - the OSAL file read functions
 *
 * @param   context:    (IN)        Context of the decoder
 * @param   optionId:   (IN)        Identifier indicating the option to set
 * @param   pValue:     (IN)        Pointer to structure or value (allocated by user)
 *                                  where option is stored
 *
 * @return  M4NO_ERROR              There is no error
 * @return  M4ERR_BAD_OPTION_ID     The option ID is not a valid one
 * @return  M4ERR_STATE             State automaton is not applied
 * @return  M4ERR_PARAMETER         The option parameter is invalid
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_setOption(M4OSA_Context context, M4OSA_OptionID optionId,
                                 M4OSA_DataOption pValue)
{
    M4READER_AMR_Context* pC = (M4READER_AMR_Context*)context;
    M4OSA_ERR err = M4NO_ERROR;

    /* Check function parameters */
    M4OSA_DEBUG_IF1((M4OSA_NULL == pC),     M4ERR_PARAMETER, "invalid context pointer");
    M4OSA_DEBUG_IF1((M4OSA_NULL == pValue), M4ERR_PARAMETER, "invalid value pointer");

    switch(optionId)
    {
    case M4READER_kOptionID_SetOsaFileReaderFctsPtr :
        {
            pC->m_pOsaFileReaderFcts = (M4OSA_FileReadPointer*)pValue;
        }
        break;
    default :
        {
            err = M4ERR_PARAMETER;
        }
    }

    return err;
}

/**
 ************************************************************************
 * @brief    reset the stream, that is seek it to beginning and make it ready to be read
 * @note    this function is to be deprecated in next versions
 *
 * @param    context:        (IN)    Context of the reader
 * @param    pStreamHandler    (IN)    The stream handler of the stream to reset
 *
 * @return    M4NO_ERROR                 there is no error
 * @return    M4ERR_PARAMETER            at least one parameter is not properly set
 * @return    M4ERR_ALLOC                there is no more memory available
 * @return    M4ERR_BAD_STREAM_ID        the streamID does not exist
 * @return    M4ERR_STATE    this function cannot be called now
 * @return    M4ERR_BAD_CONTEXT        provided context is not a valid one
 * @return    M4WAR_INVALID_TIME        beginning of the stream can not be reached
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_reset(M4OSA_Context context, M4_StreamHandler *pStreamHandler)
{
    M4READER_AMR_Context*   pC = (M4READER_AMR_Context*)context;
    M4SYS_StreamID          streamIdArray[2];
    M4OSA_ERR               err;
    M4SYS_AccessUnit*       pAu;
    M4OSA_Time              time64 = 0;
    M4AMRR_State            State;

    M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, "M4READER_AMR_reset: invalid context");
    M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER,
         "M4READER_AMR_reset: invalid pointer to M4_StreamHandler");

    if (pStreamHandler == (M4_StreamHandler*)pC->m_pAudioStream)
    {
        pAu = &pC->m_audioAu;
    }
    else
    {
        M4OSA_TRACE1_0("M4READER_AMR_reset: passed StreamHandler is not known\n");
        return M4ERR_PARAMETER;
    }

    err = M4AMRR_getState(pC->m_pCoreContext, &State, pStreamHandler->m_streamId);
    if (M4AMRR_kReading_nextAU == State)
    {
        err = M4AMRR_freeAU(pC->m_pCoreContext, pStreamHandler->m_streamId, pAu);
        if (err != M4NO_ERROR)
        {
            M4OSA_TRACE1_0("M4READER_AMR_reset: error when freeing access unit\n");
            return err;
        }
        pAu->dataAddress = M4OSA_NULL;
    }

    streamIdArray[0] = pStreamHandler->m_streamId;
    streamIdArray[1] = 0;

    err = M4NO_ERROR;

    /* for reset during playback */
    /* (set CTS to -20 in order the first AU CTS is 0) */
    pAu->CTS = -20;
    pAu->DTS = -20;

    err = M4AMRR_seek(pC->m_pCoreContext, streamIdArray, time64, M4SYS_kBeginning, &time64);
    if (err != M4NO_ERROR)
    {
        M4OSA_TRACE1_0("M4READER_AMR_reset: error when calling M4AMRR_seek()\n");
        return err;
    }

    return err;
}

/**
 ************************************************************************
 * @brief    jump into the stream at the specified time
 * @note
 * @param    context:        (IN)     Context of the reader
 * @param    pStreamHandler    (IN)     the stream description of the stream to make jump
 * @param    pTime            (IN/OUT) IN:  the time to jump to (in ms)
 *                                     OUT: the time to which the stream really jumped
 * @return    M4NO_ERROR                 there is no error
 * @return    M4ERR_BAD_CONTEXT        provided context is not a valid one
 * @return    M4ERR_PARAMETER            at least one parameter is not properly set
 * @return    M4ERR_ALLOC                there is no more memory available
 * @return    M4WAR_INVALID_TIME        the time can not be reached
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_jump(M4OSA_Context context, M4_StreamHandler *pStreamHandler,
                             M4OSA_Int32* pTime)
{
    M4READER_AMR_Context*   pC = (M4READER_AMR_Context*)context;
    M4SYS_StreamID          streamIdArray[2];
    M4OSA_ERR               err;
    M4SYS_AccessUnit*       pAu;
    M4OSA_Time              time64 = (M4OSA_Time)*pTime;
    M4AMRR_State            State;

    M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, "M4READER_AMR_reset: invalid context");
    M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER,
         "M4READER_AMR_reset: invalid pointer to M4_StreamHandler");
    M4OSA_DEBUG_IF1((pTime == 0), M4ERR_PARAMETER, "M4READER_3GP_jump: invalid time pointer");

    if (pStreamHandler == (M4_StreamHandler*)pC->m_pAudioStream)
    {
        pAu = &pC->m_audioAu;
    }
    else
    {
        M4OSA_TRACE1_0("M4READER_AMR_jump: passed StreamHandler is not known\n");
        return M4ERR_PARAMETER;
    }

    err = M4AMRR_getState(pC->m_pCoreContext, &State, pStreamHandler->m_streamId);
    if (M4AMRR_kReading_nextAU == State)
    {
        err = M4AMRR_freeAU(pC->m_pCoreContext, pStreamHandler->m_streamId, pAu);
        if (err != M4NO_ERROR)
        {
            M4OSA_TRACE1_0("M4READER_AMR_jump: error when freeing access unit\n");
            return err;
        }
        pAu->dataAddress = M4OSA_NULL;
    }

    streamIdArray[0] = pStreamHandler->m_streamId;
    streamIdArray[1] = 0;

    pAu->CTS = time64;
    pAu->DTS = time64;
    err = M4AMRR_seek(pC->m_pCoreContext, streamIdArray, time64, M4SYS_kNoRAPprevious, &time64);
    if (err != M4NO_ERROR)
    {
        M4OSA_TRACE1_0("M4READER_AMR_jump: error when calling M4AMRR_seek()\n");
        return err;
    }

    *pTime = (M4OSA_Int32)time64;

    return err;
}

/**
 ************************************************************************
 * @brief   Gets an access unit (AU) from the stream handler source.
 * @note    An AU is the smallest possible amount of data to be decoded by a decoder (audio/video).
 *          In the current version, we need to translate M4OSA_AccessUnit to M4_AccessUnit
 *
 * @param    context:        (IN)        Context of the reader
 * @param    pStreamHandler  (IN)        The stream handler of the stream to make jump
 * @param    pAccessUnit     (IN/OUT)    Pointer to an access unit to fill with read data (the au
                                         structure is allocated by the user, and must be
                                         initialized by calling M4READER_fillAuStruct_fct after
                                         creation)
 * @return    M4NO_ERROR              there is no error
 * @return    M4ERR_BAD_CONTEXT       provided context is not a valid one
 * @return    M4ERR_PARAMETER         at least one parameter is not properly set
 * @return    M4ERR_ALLOC             memory allocation failed
 * @return    M4ERR_BAD_STREAM_ID     at least one of the stream Id. does not exist.
 * @return    M4WAR_NO_MORE_AU        there are no more access unit in the stream (end of stream)
 ************************************************************************
*/
M4OSA_ERR M4READER_AMR_getNextAu(M4OSA_Context context, M4_StreamHandler *pStreamHandler,
                                M4_AccessUnit *pAccessUnit)
{
    M4READER_AMR_Context*   pC = (M4READER_AMR_Context*)context;
    M4OSA_ERR               err = M4NO_ERROR;
    M4SYS_AccessUnit*       pAu;
    M4_MediaTime            timeScale;
    M4AMRR_State            State;

    M4OSA_DEBUG_IF1((pC == 0),             M4ERR_PARAMETER,
         "M4READER_AMR_getNextAu: invalid context");
    M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER,
         "M4READER_AMR_getNextAu: invalid pointer to M4_StreamHandler");
    M4OSA_DEBUG_IF1((pAccessUnit == 0),    M4ERR_PARAMETER,
         "M4READER_AMR_getNextAu: invalid pointer to M4_AccessUnit");

    /* keep trace of the allocated buffers in AU to be able to free them at destroy()
       but be aware that system is risky and would need upgrade if more than
       one video and one audio AU is needed */
    if (pStreamHandler == (M4_StreamHandler*)pC->m_pAudioStream)
    {
        pAu = &pC->m_audioAu;
    }
    else
    {
        M4OSA_TRACE1_0("M4READER_AMR_getNextAu: passed StreamHandler is not known\n");
        return M4ERR_PARAMETER;
    }

    err = M4AMRR_getState(pC->m_pCoreContext, &State, pStreamHandler->m_streamId);
    if (M4AMRR_kReading_nextAU == State)
    {
        err = M4AMRR_freeAU(pC->m_pCoreContext, pStreamHandler->m_streamId, pAu);
        if (err != M4NO_ERROR)
        {
            M4OSA_TRACE1_0("M4READER_AVI_getNextAu: error when freeing access unit\n");
            return err;
        }
        pAu->dataAddress = M4OSA_NULL;
    }

    pAu->nbFrag = 0;
    err = M4AMRR_nextAU(pC->m_pCoreContext, pStreamHandler->m_streamId, pAu);

    if (err == M4NO_ERROR)
    {
        timeScale = (M4OSA_Float)(M4OSA_Int32)(pStreamHandler->m_pUserData)/1000;
        pAccessUnit->m_dataAddress = (M4OSA_MemAddr8)pAu->dataAddress;
        pAccessUnit->m_size = pAu->size;
        pAccessUnit->m_CTS  = (M4_MediaTime)pAu->CTS/*/timeScale*/;
        pAccessUnit->m_DTS  = (M4_MediaTime)pAu->DTS/*/timeScale*/;
        pAccessUnit->m_attribute = pAu->attribute;
    }
    else
    {
        pAccessUnit->m_size=0;
    }

    return err;
}

/**
*************************************************************************
* @brief Retrieves the generic interfaces implemented by the reader
*
* @param pMediaType          : Pointer on a M4READER_MediaType (allocated by the caller)
*                              that will be filled with the media type supported by this reader
* @param pRdrGlobalInterface : Address of a pointer that will be set to the global interface implemented
*                              by this reader. The interface is a structure allocated by the function and must
*                              be un-allocated by the caller.
* @param pRdrDataInterface   : Address of a pointer that will be set to the data interface implemented
*                              by this reader. The interface is a structure allocated by the function and must
*                              be un-allocated by the caller.
*
* @returns : M4NO_ERROR     if OK
*            ERR_ALLOC      if an allocation failed
*            ERR_PARAMETER  at least one parameter is not properly set (in DEBUG only)
*************************************************************************
*/
M4OSA_ERR   M4READER_AMR_getInterfaces(M4READER_MediaType *pMediaType,
                                         M4READER_GlobalInterface **pRdrGlobalInterface,
                                         M4READER_DataInterface **pRdrDataInterface)
{
    M4OSA_DEBUG_IF1((pMediaType == 0),          M4ERR_PARAMETER,
         "M4READER_AMR_getInterfaces: invalid pointer to MediaType");
    M4OSA_DEBUG_IF1((pRdrGlobalInterface == 0), M4ERR_PARAMETER,
         "M4READER_AMR_getInterfaces: invalid pointer to M4READER_GlobalInterface");
    M4OSA_DEBUG_IF1((pRdrDataInterface == 0),   M4ERR_PARAMETER,
         "M4READER_AMR_getInterfaces: invalid pointer to M4READER_DataInterface");

    *pRdrGlobalInterface =
         (M4READER_GlobalInterface*)M4OSA_32bitAlignedMalloc( sizeof(M4READER_GlobalInterface),
             M4READER_AMR, (M4OSA_Char *)"M4READER_GlobalInterface" );
    if (M4OSA_NULL == *pRdrGlobalInterface)
    {
        *pRdrDataInterface = M4OSA_NULL;
        return M4ERR_ALLOC;
    }
    *pRdrDataInterface = (M4READER_DataInterface*)M4OSA_32bitAlignedMalloc( sizeof(M4READER_DataInterface),
         M4READER_AMR, (M4OSA_Char *)"M4READER_DataInterface");
    if (M4OSA_NULL == *pRdrDataInterface)
    {
        free(*pRdrGlobalInterface);
        *pRdrGlobalInterface = M4OSA_NULL;
        return M4ERR_ALLOC;
    }

    *pMediaType = M4READER_kMediaTypeAMR;

    (*pRdrGlobalInterface)->m_pFctCreate           = M4READER_AMR_create;
    (*pRdrGlobalInterface)->m_pFctDestroy          = M4READER_AMR_destroy;
    (*pRdrGlobalInterface)->m_pFctOpen             = M4READER_AMR_open;
    (*pRdrGlobalInterface)->m_pFctClose            = M4READER_AMR_close;
    (*pRdrGlobalInterface)->m_pFctGetOption        = M4READER_AMR_getOption;
    (*pRdrGlobalInterface)->m_pFctSetOption        = M4READER_AMR_setOption;
    (*pRdrGlobalInterface)->m_pFctGetNextStream    = M4READER_AMR_getNextStream;
    (*pRdrGlobalInterface)->m_pFctFillAuStruct     = M4READER_AMR_fillAuStruct;
    (*pRdrGlobalInterface)->m_pFctStart            = M4OSA_NULL;
    (*pRdrGlobalInterface)->m_pFctStop             = M4OSA_NULL;
    (*pRdrGlobalInterface)->m_pFctJump             = M4READER_AMR_jump;
    (*pRdrGlobalInterface)->m_pFctReset            = M4READER_AMR_reset;
    (*pRdrGlobalInterface)->m_pFctGetPrevRapTime   = M4OSA_NULL; /*all AUs are RAP*/

    (*pRdrDataInterface)->m_pFctGetNextAu          = M4READER_AMR_getNextAu;

    (*pRdrDataInterface)->m_readerContext = M4OSA_NULL;

    return M4NO_ERROR;
}