/*
 * 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    M4MP4W_Interface.c
 * @brief    3GPP file writer interface
 * @note    This implementation follows the common interface defined
 *          in "M4WRITER_common.h".
 ******************************************************************************
*/

#include "NXPSW_CompilerSwitches.h"

/**
 * OSAL includes */
#include "M4OSA_Types.h"            /**< OSAL basic types definiton */
#include "M4OSA_FileWriter.h"        /**< Include for OSAL file accesses implementation */
#include "M4OSA_Memory.h"            /**< Include for OSAL memory accesses implementation */
#include "M4OSA_Debug.h"            /**< OSAL debug tools */

/**
 * Writer includes */
#include "M4WRITER_common.h"        /**< Definition of the writer common interface that
                                          this module follows */

#ifdef _M4MP4W_USE_CST_MEMORY_WRITER
#include "M4MP4W_Types_CstMem.h"    /**< MP4/3GP core writer types */
#include "M4MP4W_Writer_CstMem.h"    /**< MP4/3GP core writer functions */
#else
#include "M4MP4W_Types.h"            /**< MP4/3GP core writer types */
#include "M4MP4W_Writer.h"            /**< MP4/3GP core writer functions */
#endif /* _M4MP4W_USE_CST_MEMORY_WRITER */

/**
 * Specific errors for this module */
#define M4WRITER_3GP_ERR_UNSUPPORTED_STREAM_TYPE \
                M4OSA_ERR_CREATE(M4_ERR, M4WRITER_3GP, 0x000001)


/**
 ******************************************************************************
 * structure    M4WRITER_3GP_InternalContext
 * @brief        This structure defines the writer context (private)
 * @note        This structure is used for all writer calls to store the context
 ******************************************************************************
*/
typedef struct
{
    M4OSA_Context    pMP4Context;    /**< MP4 writer context */
    M4OSA_UInt32    maxAUsizes;        /**< the maximum AU size possible */
} M4WRITER_3GP_InternalContext;


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_openWrite(M4WRITER_Context* pContext, void* pWhat,
 *                                   M4OSA_FileWriterPointer* pFileWriterPointer)
 * @brief    Open a writer session.
 * @note
 * @param    pContext:     (OUT) Execution context of the 3GP writer, allocated by this function.
 * @param    outputFileDescriptor (IN)  Descriptor of the output file to create.
 * @param    fileWriterFunction     (IN)  Pointer to structure containing the set of OSAL
 *                                       file write functions.
 * @param    tempFileDescriptor     (IN)  Descriptor of the temporary file to open
 *                                        (NULL if not used)
 * @param    fileReaderFunction     (IN)  Pointer to structure containing the set of OSAL file read
 *                                      functions (NULL if not used)
 * @return    M4NO_ERROR:  there is no error
 * @return    M4ERR_ALLOC: there is no more available memory
 * @return    M4ERR_PARAMETER: pContext or pFilePtrFct is M4OSA_NULL (debug only)
 * @return    any error returned by the MP4 core writer openWrite (Its coreID is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_openWrite( M4WRITER_Context* pContext,
                                  void* outputFileDescriptor,
                                  M4OSA_FileWriterPointer* pFileWriterPointer,
                                  void* tempFileDescriptor,
                                  M4OSA_FileReadPointer* pFileReaderPointer )
{
    M4WRITER_3GP_InternalContext* apContext;
    M4OSA_ERR err;

    M4OSA_TRACE1_0("M4WRITER_3GP_openWrite");

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

    /**
     *    Allocate memory for the context */
    *pContext=M4OSA_NULL;
    apContext = (M4WRITER_3GP_InternalContext*)M4OSA_32bitAlignedMalloc(
                    sizeof(M4WRITER_3GP_InternalContext),
                    M4WRITER_3GP,
                    (M4OSA_Char *)"M4WRITER_3GP_InternalContext");

    if (M4OSA_NULL == apContext)
    {
        M4OSA_TRACE1_0("M4WRITER_3GP_openWrite:\
             unable to allocate context, returning M4ERR_ALLOC");
        return (M4OSA_ERR)M4ERR_ALLOC;
    }

    /**
     *    Reset context variables */
    apContext->pMP4Context = M4OSA_NULL;
    apContext->maxAUsizes = 0;

    /**
     *    Return the writer context */
    *pContext = (M4WRITER_Context *)apContext;

    /**
     *    Launch the openWrite of the MP4 writer */
    M4OSA_TRACE3_0("M4WRITER_3GP_openWrite: calling M4MP4W_openWrite()");

    err = M4MP4W_openWrite(&apContext->pMP4Context, outputFileDescriptor,
            pFileWriterPointer, tempFileDescriptor, pFileReaderPointer );

    if (M4OSA_ERR_IS_ERROR(err))
    {
        M4OSA_TRACE1_1("M4WRITER_3GP_openWrite: "
                       "M4MP4W_openWrite returns error 0x%x", err);
    }

    M4OSA_TRACE2_1("M4WRITER_3GP_openWrite: returning 0x%x", err);

    return err;
}


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_startWriting(M4WRITER_Context pContext)
 * @brief    Indicates to the writer that the setup session is ended and that
 *          we will start to write.
 * @note
 * @param     pContext:   (IN) Execution context of the 3GP writer,
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext is M4OSA_NULL (debug only)
 * @return    any error returned by the MP4 core writer startWriting (Its
 *            coreID is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_startWriting(M4WRITER_Context pContext)
{
    M4WRITER_3GP_InternalContext* apContext =
                (M4WRITER_3GP_InternalContext*)pContext;

    M4OSA_ERR err;

    M4OSA_TRACE1_1("M4WRITER_3GP_startWriting: pContext=0x%x", pContext);

    /**
     *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL == apContext),M4ERR_PARAMETER,
         "M4WRITER_3GP_startWriting: pContext is M4OSA_NULL");

    /**
     *    Call the MP4 core writer */
    M4OSA_TRACE3_0("M4WRITER_3GP_startWriting: calling M4MP4W_startWriting()");
    err = M4MP4W_startWriting(apContext->pMP4Context);
    if (M4OSA_ERR_IS_ERROR(err))
    {
        M4OSA_TRACE1_1("M4MP4W_startWriting returns error 0x%x", err);
    }

    M4OSA_TRACE2_1("M4WRITER_3GP_startWriting: returning 0x%x", err);
    return err;
}


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_addStream(
 *     M4WRITER_Context pContext,
 *     M4SYS_StreamDescription *pStreamDescription)
 * @brief     Add a stream (audio or video).
 * @note      Decoder specific info properties are correctly set before calling
 *            the core writer add function
 * @param     pContext:   (IN) Execution context of the 3GP writer,
 * @param     streamDescription:    (IN) stream description.
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext or pStreamDescription is M4OSA_NULL
 *            (debug only)
 * @return    any error returned by the MP4 core writer addStream
 *            (Its coreID is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_addStream(M4WRITER_Context pContext,
                                 M4SYS_StreamDescription* pStreamDescription)
{
    M4WRITER_3GP_InternalContext *apContext =
        (M4WRITER_3GP_InternalContext *)pContext;

    M4OSA_ERR err;
    M4WRITER_StreamVideoInfos *pVideoInfo = M4OSA_NULL;
    M4WRITER_StreamAudioInfos *pAudioInfo = M4OSA_NULL;
    M4MP4W_StreamIDsize sizeValue;

    M4OSA_TRACE1_2("M4WRITER_3GP_addStream: pContext=0x%x, "
                   "pStreamDescription=0x%x",
                   pContext, pStreamDescription);

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

    /**
     *    Adapt audio/video stream infos */
    switch (pStreamDescription->streamType)
    {
        case M4SYS_kMPEG_4:
        case M4SYS_kH264:
        case M4SYS_kH263:
            M4OSA_TRACE3_1("M4WRITER_3GP_addStream: "
                    "adding a Video stream (streamType=0x%x)",
                    pStreamDescription->streamType);
            /**
             *    Common descriptions */
            pStreamDescription->streamID = VideoStreamID;    /**< The only values checked by our
                                                                  core writer are streamID */
            pStreamDescription->timeScale = 1000;            /**< and timeScale */

/* Not recommended for video editing -> write explicitely the 'bitr' box into 'd263' */
/* Rem : it is REL 5 of 3gpp documentation */
//            /**
//             * Average bit-rate must not be set in H263 to be compatible with Platform4 */
//            if (M4SYS_kH263 == pStreamDescription->streamType)
//            {
//                pStreamDescription->averageBitrate = -1;
//            }

            /**
             *    Decoder specific info */
            pVideoInfo = (M4WRITER_StreamVideoInfos *)pStreamDescription->decoderSpecificInfo;
            pStreamDescription->decoderSpecificInfoSize = pVideoInfo->Header.Size;
            pStreamDescription->decoderSpecificInfo = (M4OSA_MemAddr32)pVideoInfo->Header.pBuf;
            M4OSA_TRACE3_2("M4WRITER_3GP_addStream: Video: DSI=0x%x, DSIsize=%d",
                 pVideoInfo->Header.pBuf, pVideoInfo->Header.Size);
            break;

        case M4SYS_kAMR:
        case M4SYS_kAMR_WB:
        case M4SYS_kAAC:
        case M4SYS_kEVRC:
            M4OSA_TRACE3_1("M4WRITER_3GP_addStream: adding an Audio stream (streamType=0x%x)",
                 pStreamDescription->streamType);
            /**
             *    Common descriptions */
            pStreamDescription->streamID = AudioStreamID;    /**< The only value checked by our
                                                                 core writer is streamID */

            /**
             *    Decoder specific info */
            pAudioInfo = (M4WRITER_StreamAudioInfos *)pStreamDescription->decoderSpecificInfo;
            pStreamDescription->decoderSpecificInfoSize = pAudioInfo->Header.Size;
            pStreamDescription->decoderSpecificInfo = (M4OSA_MemAddr32)pAudioInfo->Header.pBuf;
            M4OSA_TRACE3_2("M4WRITER_3GP_addStream: Audio: DSI=0x%x, DSIsize=%d",
                 pAudioInfo->Header.pBuf, pAudioInfo->Header.Size);
            break;

        default:
            M4OSA_TRACE1_1("M4WRITER_3GP_addStream:\
                 returning M4WRITER_3GP_ERR_UNSUPPORTED_STREAM_TYPE (streamType=0x%x)",
                     pStreamDescription->streamType);
            return (M4OSA_ERR)M4WRITER_3GP_ERR_UNSUPPORTED_STREAM_TYPE;
            break;
    }

    /**
     *    Call the MP4 core writer */
    M4OSA_TRACE3_0("M4WRITER_3GP_addStream: calling M4MP4W_addStream()");
    err = M4MP4W_addStream(apContext->pMP4Context,pStreamDescription);
    if (M4OSA_ERR_IS_ERROR(err))
    {
        M4OSA_TRACE1_1("M4WRITER_3GP_addStream: M4MP4W_addStream returns error 0x%x", err);
        M4OSA_TRACE1_1("M4WRITER_3GP_addStream: returning 0x%x", err);
        return (err);
    }

    /**
     *    For Video, set the M4MP4W_trackSize Option */
    switch (pStreamDescription->streamType)
    {
        case M4SYS_kMPEG_4:
        case M4SYS_kH264:
        case M4SYS_kH263:
            sizeValue.streamID = VideoStreamID;
            sizeValue.height = (M4OSA_UInt16)(pVideoInfo->height);
            sizeValue.width  = (M4OSA_UInt16)(pVideoInfo->width);
            M4OSA_TRACE3_2("M4WRITER_3GP_addStream: Video: height=%d, width=%d",
                 sizeValue.height, sizeValue.width);

            M4OSA_TRACE3_0("M4WRITER_3GP_addStream: calling M4MP4W_setOption(M4MP4W_trackSize)");
            err = M4MP4W_setOption( apContext->pMP4Context, M4MP4W_trackSize,
                 (M4OSA_DataOption)&sizeValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4WRITER_3GP_addStream: M4MP4W_setOption returns error 0x%x",
                     err);
            }
            break;
        default:
            break;
    }

    M4OSA_TRACE2_1("M4WRITER_3GP_addStream: returning 0x%x", err);
    return err;
}


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_closeWrite(M4WRITER_Context pContext)
 * @brief    Close the writer. The context is freed here.
 * @note
 * @param     pContext:   (IN) Execution context of the 3GP writer,
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext is M4OSA_NULL (debug only)
 * @return    any error returned by the MP4 core writer closeWrite (Its coreID
 *            is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_closeWrite(M4WRITER_Context pContext)
{
    M4WRITER_3GP_InternalContext* apContext=(M4WRITER_3GP_InternalContext*)pContext;
    M4OSA_ERR err = M4NO_ERROR;

    M4OSA_TRACE1_1("M4WRITER_3GP_closeWrite called with pContext=0x%x", pContext);

    /**
    *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL == apContext),M4ERR_PARAMETER,
         "M4WRITER_3GP_closeWrite: pContext is M4OSA_NULL");

    /**
     *    Call the MP4 core writer */
    if (M4OSA_NULL != apContext->pMP4Context)
    {
        M4OSA_TRACE3_0("M4WRITER_3GP_closeWrite: calling M4MP4W_closeWrite()");
        err = M4MP4W_closeWrite(apContext->pMP4Context);
        if (M4OSA_ERR_IS_ERROR(err))
        {
            M4OSA_TRACE1_1("M4WRITER_3GP_closeWrite: M4MP4W_closeWrite returns error 0x%x", err);
        }
    }

    /**
     *    Deallocate our own context */
    free(apContext);

    M4OSA_TRACE2_1("M4WRITER_3GP_closeWrite: returning 0x%x", err);
    return err;
}


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_setOption(
 *        M4WRITER_Context pContext, M4OSA_UInt32 optionID,
 *        M4OSA_DataOption optionValue)
 * @brief     This function asks the writer to set the value associated with
 *            the optionID. The caller is responsible for allocating/
 *            de-allocating the memory of the value field.
 * @note      The options handled by the component depend on the implementation
 *            of the component.
 * @param     pContext:     (IN) Execution context of the 3GP writer,
 * @param     pptionId:     (IN) ID of the option to set.
 * @param     OptionValue : (IN) Value of the option to set.
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext is M4OSA_NULL (debug only)
 * @return    M4ERR_BAD_OPTION_ID: the ID of the option is not valid.
 * @return    any error returned by the MP4 core writer setOption (Its coreID
 *            is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_setOption(
        M4WRITER_Context pContext, M4OSA_UInt32 optionID,
        M4OSA_DataOption optionValue)
{
    M4WRITER_3GP_InternalContext* apContext =
            (M4WRITER_3GP_InternalContext*)pContext;

    M4OSA_ERR err = M4NO_ERROR;
    M4MP4W_memAddr memval;
    M4SYS_StreamIDValue optval;

    M4OSA_TRACE2_3("M4WRITER_3GP_setOption: pContext=0x%x, optionID=0x%x,\
         optionValue=0x%x", pContext, optionID, optionValue);

    /**
     *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL==apContext),M4ERR_PARAMETER,
         "M4WRITER_3GP_setOption: pContext is M4OSA_NULL");

    switch (optionID)
    {
        /**
         *    Maximum Access Unit size */
        case M4WRITER_kMaxAUSize:
            M4OSA_TRACE2_0("setting M4WRITER_kMaxAUSize option");
            err = M4MP4W_setOption(
                    apContext->pMP4Context,M4MP4W_maxAUsize, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_maxAUsize) "
                               "returns error 0x%x", err);
            }
            break;
        /**
         *    Maximum chunck size */
        case M4WRITER_kMaxChunckSize:
            M4OSA_TRACE2_0("setting M4WRITER_kMaxChunckSize option");
            err = M4MP4W_setOption(
                apContext->pMP4Context,M4MP4W_maxChunkSize, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_maxChunkSize)\
                     returns error 0x%x", err);
            }
            break;
        /**
         *    File string signature */
        case M4WRITER_kEmbeddedString:
            M4OSA_TRACE2_0("setting M4WRITER_kEmbeddedString option");
            /* The given M4OSA_DataOption must actually
               be a text string */
            memval.addr = (M4OSA_MemAddr32)optionValue;
            /**< this is max string size copied by the core */
            memval.size = 16;
            err = M4MP4W_setOption(
                apContext->pMP4Context,M4MP4W_embeddedString, &memval);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_embeddedString)\
                     returns error 0x%x", err);
            }
            break;
        /**
         *    File integration tag */
        case M4WRITER_kIntegrationTag:
            M4OSA_TRACE2_0("setting M4WRITER_kIntegrationTag option");
            /* The given M4OSA_DataOption must actually
               be a text string */
            memval.addr = (M4OSA_MemAddr32)optionValue;
            /**< this is max string size copied by the core */
            memval.size = strlen((const char *)optionValue);
            err = M4MP4W_setOption(
                apContext->pMP4Context,M4MP4W_integrationTag, &memval);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_integrationTag)"
                               " returns error 0x%x", err);
            }
            break;
        /**
         *    File version signature */
        case M4WRITER_kEmbeddedVersion:
            M4OSA_TRACE2_0("setting M4WRITER_kEmbeddedVersion option");
            /* The given M4OSA_DataOption must actually
               be a version number */

            /**< Here 0 means both streams */
            optval.streamID = 0;
            /**< version number */
            optval.value = *(M4OSA_UInt32*)optionValue;
            err = M4MP4W_setOption(
                apContext->pMP4Context,M4MP4W_CamcoderVersion, &optval);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_CamcoderVersion)"
                               " returns error 0x%x", err);
            }
            break;
        /**
         *    Some options are read-only */
        case M4WRITER_kFileSize:
        case M4WRITER_kFileSizeAudioEstimated:
            M4OSA_TRACE2_1("trying to set a read-only option! (ID=0x%x)",
                    optionID);
            return (M4OSA_ERR)M4ERR_READ_ONLY;
            break;
        /**
         *    Maximum filesize limitation */
        case M4WRITER_kMaxFileSize:
            M4OSA_TRACE2_0("setting M4WRITER_kMaxFileSize option");
            err = M4MP4W_setOption(
                apContext->pMP4Context,M4MP4W_maxFileSize, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_maxFileSize)\
                     returns error 0x%x", err);
            }
            break;

        /**
         *    Maximum file duration limitation */
        case M4WRITER_kMaxFileDuration:
            M4OSA_TRACE2_0("setting M4WRITER_kMaxFileDuration option");
            err = M4MP4W_setOption(
                apContext->pMP4Context,M4MP4W_maxFileDuration, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4WRITER_kMaxFileDuration)"
                               " returns error 0x%x", err);
            }
            break;

        /**
         *    Set 'ftyp' atom */
        case M4WRITER_kSetFtypBox:
            M4OSA_TRACE2_0("setting M4WRITER_kSetFtypBox option");
            err = M4MP4W_setOption(
                apContext->pMP4Context, M4MP4W_setFtypBox, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_setFtypBox)\
                     returns error 0x%x", err);
            }
            break;

        /**
         *    Decoder Specific Info */
        case M4WRITER_kDSI:
            M4OSA_TRACE2_0("setting M4WRITER_kDSI option");
            err = M4MP4W_setOption(
                apContext->pMP4Context, M4MP4W_DSI, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_DSI)\
                     returns error 0x%x", err);
            }
            break;
        /*+ H.264 Trimming  */
        case M4WRITER_kMUL_PPS_SPS:
            M4OSA_TRACE2_0("setting M4WRITER_kMUL_PPS_SPS option");
            err = M4MP4W_setOption(
                apContext->pMP4Context, M4MP4W_MUL_PPS_SPS, optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_setOption(M4MP4W_DSI)\
                     returns error 0x%x", err);
            }
            break;
        /*- H.264 Trimming  */

        /**
         *    Unknown option */
        default:
            M4OSA_TRACE2_1("trying to set an unknown option!\
                 (optionID=0x%x)", optionID);
            return (M4OSA_ERR)M4ERR_BAD_OPTION_ID;
            break;
    }

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


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_getOption(
 *     M4WRITER_Context pContext, M4OSA_UInt32 optionID,
 *     M4OSA_DataOption optionValue)
 * @brief     This function asks the writer to return the value associated with
 *            the optionID. The caller is responsible for allocating/
 *            de-allocating the memory of the value field.
 * @note      The options handled by the component depend on the implementation
 *            of the component.
 * @param     pContext:     (IN) Execution context of the 3GP writer,
 * @param     OptionId:      (IN) Id of the option to get.
 * @param     pOptionValue: (OUT) Value of the option to get.
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext is M4OSA_NULL (debug only)
 * @return    M4ERR_BAD_OPTION_ID: the ID of the option is not valid.
 * @return    M4ERR_NOT_IMPLEMENTED: This option is not implemented yet.
 * @return    any error returned by the MP4 core writer getOption (Its coreID
 *            is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_getOption(
        M4WRITER_Context pContext, M4OSA_UInt32 optionID,
        M4OSA_DataOption optionValue)
{
    M4WRITER_3GP_InternalContext* apContext =
            (M4WRITER_3GP_InternalContext*)pContext;

    M4OSA_ERR err;

    M4OSA_TRACE2_3("M4WRITER_3GP_getOption: pContext=0x%x, optionID=0x%x,\
         optionValue=0x%x", pContext, optionID, optionValue);

    /**
    *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL == apContext),M4ERR_PARAMETER,
         "M4WRITER_3GP_getOption: pContext is M4OSA_NULL");

    switch (optionID)
    {
        /**
         *    Maximum Access Unit size */
        case M4WRITER_kMaxAUSize:
            M4OSA_TRACE2_0("getting M4WRITER_kMaxAUSize option");
            err = M4MP4W_getOption(apContext->pMP4Context,M4MP4W_maxAUsize,
                (M4OSA_DataOption*)&optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_getOption(M4MP4W_maxAUsize)"
                               " returns error 0x%x", err);
            }
            break;
        /**
         *    Maximum chunck size */
        case M4WRITER_kMaxChunckSize:
            M4OSA_TRACE2_0("getting M4WRITER_kMaxChunckSize option");
            err = M4MP4W_getOption(apContext->pMP4Context,M4MP4W_maxChunkSize,
                (M4OSA_DataOption*)&optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_getOption(M4MP4W_maxChunkSize)\
                     returns error 0x%x", err);
            }
            break;
        /**
         *    The file size option */
        case M4WRITER_kFileSize:
            M4OSA_TRACE2_0("getting M4WRITER_kFileSize option");
            /* get the current file size */
            err = M4MP4W_getCurrentFileSize(
                apContext->pMP4Context, (M4OSA_UInt32*)optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_getCurrentFileSize"
                               " returns error 0x%x", err);
            }
            break;
        /**
         *    The file size with audio option has its own function call
              in the MP4 core writer */
        case M4WRITER_kFileSizeAudioEstimated:
            M4OSA_TRACE2_0("getting M4WRITER_kFileSizeAudioEstimated option");
            /* get the current file size ... */
            err = M4MP4W_getCurrentFileSize(
                apContext->pMP4Context, (M4OSA_UInt32*)optionValue);
            if (M4OSA_ERR_IS_ERROR(err))
            {
                M4OSA_TRACE1_1("M4MP4W_getCurrentFileSize"
                               " returns error 0x%x", err);
            }
            //no more needed 3gp writer has its own mecanism
            ///* ... add the estimated next max AU size */
            //*((M4OSA_UInt32*)optionValue) += apContext->maxAUsizes;
            break;
        /**
         *    Unknown option */
        default:
            M4OSA_TRACE2_1("trying to get an unknown option!\
                 (optionID=0x%x)", optionID);
            return    (M4OSA_ERR)M4ERR_BAD_OPTION_ID;
            break;
    }

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


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_startAU(
 *          M4WRITER_Context pContext, M4SYS_StreamID streamID,
 *          M4SYS_AccessUnit* pAU)
 * @brief     Prepare an Access Unit to be ready to store data
 * @note
 * @param     pContext: (IN) Execution context of the 3GP writer,
 * @param     streamID: (IN) Id of the stream to which the Access Unit
 *            is related.
 * @param     pAU:      (IN/OUT) Access Unit to be prepared.
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext or pAU is M4OSA_NULL (debug only)
 * @return    M4ERR_BAD_STREAM_ID: streamID is not VideoStreamID nor
 *            AudioStreamID (debug only)
 * @return    any error returned by the MP4 core writer startAU (Its coreID
 *            is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_startAU(
        M4WRITER_Context pContext, M4SYS_StreamID streamID,
        M4SYS_AccessUnit* pAU)
{
    M4WRITER_3GP_InternalContext* apContext =
            (M4WRITER_3GP_InternalContext*)pContext;

    M4OSA_ERR err;

    M4OSA_TRACE2_3("M4WRITER_3GP_startAU: pContext=0x%x, streamID=%d, pAU=0x%x",
         pContext, streamID, pAU);

    /**
     *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL == apContext), M4ERR_PARAMETER,
         "M4WRITER_3GP_startAU: pContext is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == pAU), M4ERR_PARAMETER,
         "M4WRITER_3GP_startAU: pAU is M4OSA_NULL");
    M4OSA_DEBUG_IF2(
         ((VideoStreamID != streamID) && (AudioStreamID != streamID)),
         M4ERR_BAD_STREAM_ID,
         "M4WRITER_3GP_processAU: Wrong streamID");

    /**
     * Call the MP4 writer */
    M4OSA_TRACE3_0("M4WRITER_3GP_startAU: calling M4MP4W_startAU()");
    err = M4MP4W_startAU(apContext->pMP4Context, streamID, pAU);
    if (M4OSA_ERR_IS_ERROR(err))
    {
        M4OSA_TRACE1_1("M4MP4W_startAU returns error 0x%x", err);
    }

    M4OSA_TRACE3_2("AU: dataAddress=0x%x, size=%d",
         pAU->dataAddress, pAU->size);

    /* Convert oversize to a request toward VES automaton */
    if (M4WAR_MP4W_OVERSIZE == err)
    {
        err = M4WAR_WRITER_STOP_REQ;
    }

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


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_processAU(
 *          M4WRITER_Context pContext, M4SYS_StreamID streamID,
 *          M4SYS_AccessUnit* pAU)
 * @brief     Write an Access Unit
 * @note
 * @param     pContext: (IN) Execution context of the 3GP writer,
 * @param     streamID: (IN) Id of the stream to which the Access Unit
 *            is related.
 * @param     pAU:      (IN/OUT) Access Unit to be written
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_PARAMETER: pContext or pAU is M4OSA_NULL (debug only)
 * @return    M4ERR_BAD_STREAM_ID: streamID is not VideoStreamID nor
 *            AudioStreamID (debug only)
 * @return    any error returned by the MP4 core writer processAU
 *            (Its coreID is M4MP4_WRITER)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_processAU(
        M4WRITER_Context pContext, M4SYS_StreamID streamID,
        M4SYS_AccessUnit* pAU)
{
    M4WRITER_3GP_InternalContext* apContext =
        (M4WRITER_3GP_InternalContext*)pContext;

    M4OSA_ERR err;

    M4OSA_TRACE2_3("M4WRITER_3GP_processAU: "
                   "pContext=0x%x, streamID=%d, pAU=0x%x",
                    pContext, streamID, pAU);

    /**
     *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL == apContext), M4ERR_PARAMETER,
         "M4WRITER_3GP_processAU: pContext is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == pAU), M4ERR_PARAMETER,
         "M4WRITER_3GP_processAU: pAU is M4OSA_NULL");
    M4OSA_DEBUG_IF2(
         ((VideoStreamID != streamID) && (AudioStreamID != streamID)),
         M4ERR_BAD_STREAM_ID,
         "M4WRITER_3GP_processAU: Wrong streamID");

    M4OSA_TRACE3_4("M4WRITER_3GP_processAU: AU: "
         "dataAddress=0x%x, size=%d, CTS=%d, nbFrag=%d",
         pAU->dataAddress, pAU->size, (M4OSA_UInt32)pAU->CTS, pAU->nbFrag);

    if(pAU->size > apContext->maxAUsizes)
    {
        apContext->maxAUsizes = pAU->size;
    }
    /**
     * Call the MP4 writer */
    M4OSA_TRACE3_0("M4WRITER_3GP_processAU: calling M4MP4W_processAU()");
    err = M4MP4W_processAU(apContext->pMP4Context, streamID, pAU);
    if (M4OSA_ERR_IS_ERROR(err))
    {
        M4OSA_TRACE1_1("M4MP4W_processAU returns error 0x%x", err);
    }

    /* Convert oversize to a request toward VES automaton */
    if(M4WAR_MP4W_OVERSIZE == err)
    {
        err = M4WAR_WRITER_STOP_REQ;
    }

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


/******************************************************************************
 * M4OSA_ERR M4WRITER_3GP_getInterfaces(
 *      M4WRITER_OutputFileType* Type,
 *      M4WRITER_GlobalInterface** SrcGlobalInterface,
 *      M4WRITER_DataInterface** SrcDataInterface)
 * @brief     Get the 3GPP writer common interface
 * @note      Retrieves the set of functions needed to use the 3GPP writer.
 *            It follows the common writer interface.
 * @param     Type: (OUT) return the type of this writer. Will always be
 *            M4WRITER_k3GPP.
 * @param     SrcGlobalInterface: (OUT) Main set of function to use this
 *            3GPP writer
 * @param     SrcDataInterface:   (OUT) Set of function related to datas
 *            to use this 3GPP writer
 * @return    M4NO_ERROR: there is no error
 * @return    M4ERR_ALLOC: there is no more available memory
 * @return    M4ERR_PARAMETER: At least one of the parameters is M4OSA_NULL
 *            (debug only)
 ******************************************************************************
*/
M4OSA_ERR M4WRITER_3GP_getInterfaces(
        M4WRITER_OutputFileType* Type,
        M4WRITER_GlobalInterface** SrcGlobalInterface,
        M4WRITER_DataInterface** SrcDataInterface)
{
    M4WRITER_GlobalInterface *pGlobal;
    M4WRITER_DataInterface *pData;

    M4OSA_TRACE2_3("M4WRITER_3GP_getInterfaces: "
         "Type=0x%x, SrcGlobalInterface=0x%x,\
         SrcDataInterface=0x%x", Type, SrcGlobalInterface, SrcDataInterface);

    /**
     *    Check input parameter */
    M4OSA_DEBUG_IF2((M4OSA_NULL == Type), M4ERR_PARAMETER,
         "M4WRITER_3GP_getInterfaces: Type is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == SrcGlobalInterface), M4ERR_PARAMETER,
         "M4WRITER_3GP_getInterfaces: SrcGlobalInterface is M4OSA_NULL");
    M4OSA_DEBUG_IF2((M4OSA_NULL == SrcDataInterface), M4ERR_PARAMETER,
         "M4WRITER_3GP_getInterfaces: SrcDataInterface is M4OSA_NULL");

    /**
     *    Set the output type */
    *Type = M4WRITER_k3GPP;

    /**
     *    Allocate the global interface structure */
    pGlobal = (M4WRITER_GlobalInterface*)M4OSA_32bitAlignedMalloc(
                sizeof(M4WRITER_GlobalInterface),
                M4WRITER_3GP, (M4OSA_Char *)"M4WRITER_GlobalInterface");
    if (M4OSA_NULL == pGlobal)
    {
        M4OSA_TRACE1_0("unable to allocate M4WRITER_GlobalInterface,\
             returning M4ERR_ALLOC");
        *SrcGlobalInterface = M4OSA_NULL;
        *SrcDataInterface = M4OSA_NULL;
        return (M4OSA_ERR)M4ERR_ALLOC;
    }

    /**
     *    Allocate the data interface structure */
    pData =
        (M4WRITER_DataInterface *)M4OSA_32bitAlignedMalloc(sizeof(M4WRITER_DataInterface),
        M4WRITER_3GP, (M4OSA_Char *)"M4WRITER_DataInterface");
    if (M4OSA_NULL == pData)
    {
        M4OSA_TRACE1_0("unable to allocate M4WRITER_DataInterface,\
             returning M4ERR_ALLOC");
        free(pGlobal);
        *SrcGlobalInterface = M4OSA_NULL;
        *SrcDataInterface = M4OSA_NULL;
        return (M4OSA_ERR)M4ERR_ALLOC;
    }

    /**
     *    Fill the global interface structure */
    pGlobal->pFctOpen = M4WRITER_3GP_openWrite;
    pGlobal->pFctAddStream = M4WRITER_3GP_addStream;
    pGlobal->pFctStartWriting = M4WRITER_3GP_startWriting;
    pGlobal->pFctCloseWrite = M4WRITER_3GP_closeWrite;
    pGlobal->pFctSetOption = M4WRITER_3GP_setOption;
    pGlobal->pFctGetOption = M4WRITER_3GP_getOption;

    /**
     *    Fill the data interface structure */
    pData->pStartAU = M4WRITER_3GP_startAU;
    pData->pProcessAU = M4WRITER_3GP_processAU;

    /**
     *    Set the return values */
    *SrcGlobalInterface = pGlobal;
    *SrcDataInterface = pData;

    M4OSA_TRACE2_0("M4WRITER_3GP_getInterfaces: returning M4NO_ERROR");
    return M4NO_ERROR;
}