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

PVFMVideoMIO::PVFMVideoMIO() :
        OsclTimerObject(OsclActiveObject::EPriorityNominal, "PVFMVideoMIO")
{
    InitData();
}


void PVFMVideoMIO::InitData()
{
    iVideoFormat = PVMF_MIME_FORMAT_UNKNOWN;
    iVideoSubFormat = PVMF_MIME_FORMAT_UNKNOWN;
    iVideoHeightValid = false;
    iVideoWidthValid = false;
    iVideoDisplayHeightValid = false;
    iVideoDisplayWidthValid = false;
    iIsMIOConfigured = false;
    iWriteBusy = false;
    iVideoHeight = 0;
    iVideoWidth = 0;
    iVideoDisplayHeight = 0;
    iVideoDisplayWidth = 0;

    iThumbnailWidth = 320;
    iThumbnailHeight = 240;


    iColorConverter = NULL;
    iCCRGBFormatType = PVMF_MIME_FORMAT_UNKNOWN;

    iCommandCounter = 0;
    iLogger = NULL;
    iCommandResponseQueue.reserve(5);
    iWriteResponseQueue.reserve(5);
    iObserver = NULL;
    iLogger = NULL;
    iPeer = NULL;
    iState = STATE_IDLE;

    iFrameRetrievalInfo.iRetrievalRequested = false;
    iFrameRetrievalInfo.iGetFrameObserver = NULL;
    iFrameRetrievalInfo.iUseFrameIndex = false;
    iFrameRetrievalInfo.iUseTimeOffset = false;
    iFrameRetrievalInfo.iFrameIndex = 0;
    iFrameRetrievalInfo.iTimeOffset = 0;
    iFrameRetrievalInfo.iFrameBuffer = NULL;
    iFrameRetrievalInfo.iBufferSize = NULL;

    // Init the input format capabilities vector
    iInputFormatCapability.clear();
    iInputFormatCapability.push_back(PVMF_MIME_YUV420);
    iInputFormatCapability.push_back(PVMF_MIME_YUV422);
    iInputFormatCapability.push_back(PVMF_MIME_YUV422_INTERLEAVED_UYVY);
    iInputFormatCapability.push_back(PVMF_MIME_RGB8);
    iInputFormatCapability.push_back(PVMF_MIME_RGB12);
    iInputFormatCapability.push_back(PVMF_MIME_RGB16);
    iInputFormatCapability.push_back(PVMF_MIME_RGB24);

    iYUV422toYUV420ColorConvert = NULL;
}


void PVFMVideoMIO::ResetData()
{
    Cleanup();

    // Reset all the received media parameters.
    iVideoFormat = PVMF_MIME_FORMAT_UNKNOWN;
    iVideoSubFormat = PVMF_MIME_FORMAT_UNKNOWN;
    iVideoHeightValid = false;
    iVideoWidthValid = false;
    iVideoDisplayHeightValid = false;
    iVideoDisplayWidthValid = false;
    iVideoHeight = 0;
    iVideoWidth = 0;
    iVideoDisplayHeight = 0;
    iVideoDisplayWidth = 0;
    iIsMIOConfigured = false;
    iWriteBusy = false;
}


void PVFMVideoMIO::Cleanup()
{
    while (!iCommandResponseQueue.empty())
    {
        if (iObserver)
        {
            iObserver->RequestCompleted(PVMFCmdResp(iCommandResponseQueue[0].iCmdId, iCommandResponseQueue[0].iContext, iCommandResponseQueue[0].iStatus));
        }
        iCommandResponseQueue.erase(&iCommandResponseQueue[0]);
    }

    while (!iWriteResponseQueue.empty())
    {
        if (iPeer)
        {
            iPeer->writeComplete(iWriteResponseQueue[0].iStatus, iWriteResponseQueue[0].iCmdId, (OsclAny*)iWriteResponseQueue[0].iContext);
        }
        iWriteResponseQueue.erase(&iWriteResponseQueue[0]);
    }
}


PVFMVideoMIO::~PVFMVideoMIO()
{
    Cleanup();

    if (iColorConverter)
    {
        DestroyYUVToRGBColorConverter(iColorConverter, iCCRGBFormatType);
    }

    if (iYUV422toYUV420ColorConvert)
    {
        DestroyYUV422toYUV420ColorConvert();
    }
}

void PVFMVideoMIO::setThumbnailDimensions(uint32 aWidth, uint32 aHeight)
{
    iThumbnailWidth = aWidth;
    iThumbnailHeight = aHeight;
}

PVMFStatus PVFMVideoMIO::GetFrameByFrameNumber(uint32 aFrameIndex, uint8* aFrameBuffer, uint32& aBufferSize,
        PVMFFormatType aFormatType, PVFMVideoMIOGetFrameObserver& aObserver)
{
    if (iFrameRetrievalInfo.iRetrievalRequested)
    {
        // Get frame request is already pending so don't accept this request
        return PVMFErrBusy;
    }

    if (aFrameBuffer == NULL || aBufferSize == 0)
    {
        // Bad parameters
        return PVMFErrArgument;
    }

    // Signal for frame retrieval by frame number
    iFrameRetrievalInfo.iRetrievalRequested = true;
    iFrameRetrievalInfo.iGetFrameObserver = &aObserver;
    iFrameRetrievalInfo.iUseFrameIndex = true;
    iFrameRetrievalInfo.iUseTimeOffset = false;
    iFrameRetrievalInfo.iFrameIndex = aFrameIndex;
    iFrameRetrievalInfo.iFrameBuffer = aFrameBuffer;
    iFrameRetrievalInfo.iBufferSize = &aBufferSize;
    iFrameRetrievalInfo.iFrameFormatType = aFormatType;

    iFrameRetrievalInfo.iReceivedFrameCount = 0;
    iFrameRetrievalInfo.iStartingTSSet = false;
    iFrameRetrievalInfo.iStartingTS = 0;

    return PVMFPending;
}


PVMFStatus PVFMVideoMIO::GetFrameByTimeoffset(uint32 aTimeOffset, uint8* aFrameBuffer, uint32& aBufferSize,
        PVMFFormatType aFormatType, PVFMVideoMIOGetFrameObserver& aObserver)
{
    if (iFrameRetrievalInfo.iRetrievalRequested)
    {
        // Get frame request is already pending don't accept this request
        return PVMFErrBusy;
    }

    if (aFrameBuffer == NULL || aBufferSize == 0)
    {
        // Bad parameters
        return PVMFErrArgument;
    }

    // Signal for frame retrieval by time offset
    iFrameRetrievalInfo.iRetrievalRequested = true;
    iFrameRetrievalInfo.iGetFrameObserver = &aObserver;
    iFrameRetrievalInfo.iUseFrameIndex = false;
    iFrameRetrievalInfo.iUseTimeOffset = true;
    iFrameRetrievalInfo.iTimeOffset = aTimeOffset;
    iFrameRetrievalInfo.iFrameBuffer = aFrameBuffer;
    iFrameRetrievalInfo.iBufferSize = &aBufferSize;
    iFrameRetrievalInfo.iFrameFormatType = aFormatType;

    iFrameRetrievalInfo.iReceivedFrameCount = 0;
    iFrameRetrievalInfo.iStartingTSSet = false;
    iFrameRetrievalInfo.iStartingTS = 0;

    return PVMFPending;
}


PVMFStatus PVFMVideoMIO::CancelGetFrame(void)
{
    // Cancel any pending frame retrieval and reset variables
    iFrameRetrievalInfo.iRetrievalRequested = false;
    iFrameRetrievalInfo.iUseFrameIndex = false;
    iFrameRetrievalInfo.iUseTimeOffset = false;

    return PVMFSuccess;
}


PVMFStatus PVFMVideoMIO::GetFrameProperties(uint32& aFrameWidth, uint32& aFrameHeight, uint32& aDisplayWidth, uint32& aDisplayHeight)
{
    if (iVideoWidthValid == false || iVideoHeightValid == false ||
            iVideoDisplayWidthValid == false || iVideoDisplayHeightValid == false)
    {
        return PVMFErrNotReady;
    }

    aFrameWidth = iVideoWidth;
    aFrameHeight = iVideoHeight;
    aDisplayWidth = iVideoDisplayWidth;
    aDisplayHeight = iVideoDisplayHeight;

    return PVMFSuccess;
}


PVMFStatus PVFMVideoMIO::connect(PvmiMIOSession& aSession, PvmiMIOObserver* aObserver)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::connect() called"));

    // Currently supports only one session
    OSCL_UNUSED_ARG(aSession);

    if (iObserver)
    {
        return PVMFFailure;
    }

    iObserver = aObserver;
    return PVMFSuccess;
}


PVMFStatus PVFMVideoMIO::disconnect(PvmiMIOSession aSession)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::disconnect() called"));

    // Currently supports only one session
    OSCL_UNUSED_ARG(aSession);

    iObserver = NULL;
    return PVMFSuccess;
}


PvmiMediaTransfer* PVFMVideoMIO::createMediaTransfer(PvmiMIOSession& aSession, PvmiKvp* read_formats, int32 read_flags,
        PvmiKvp* write_formats, int32 write_flags)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::createMediaTransfer() called"));

    OSCL_UNUSED_ARG(aSession);
    OSCL_UNUSED_ARG(read_formats);
    OSCL_UNUSED_ARG(read_flags);
    OSCL_UNUSED_ARG(write_formats);
    OSCL_UNUSED_ARG(write_flags);

    return (PvmiMediaTransfer*)this;
}


void PVFMVideoMIO::QueueCommandResponse(CommandResponse& aResp)
{
    // Queue a command response and schedule processing
    iCommandResponseQueue.push_back(aResp);

    // Cancel any timer delay so the command response will happen ASAP.
    if (IsBusy())
    {
        Cancel();
    }

    RunIfNotReady();
}


PVMFCommandId PVFMVideoMIO::QueryUUID(const PvmfMimeString& aMimeType, Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids,
                                      bool aExactUuidsOnly, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::QueryUUID() called"));

    OSCL_UNUSED_ARG(aMimeType);
    OSCL_UNUSED_ARG(aExactUuidsOnly);

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;
    int32 err ;
    OSCL_TRY(err,
             aUuids.push_back(PVMI_CAPABILITY_AND_CONFIG_PVUUID);
             PVUuid uuid;
             iActiveTiming.queryUuid(uuid);
             aUuids.push_back(uuid);
            );

    if (err == OsclErrNone)
    {
        status = PVMFSuccess;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


PVMFCommandId PVFMVideoMIO::QueryInterface(const PVUuid& aUuid, PVInterface*& aInterfacePtr, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::QueryInterface() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;
    if (aUuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID)
    {
        PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this);
        aInterfacePtr = OSCL_STATIC_CAST(PVInterface*, myInterface);
        status = PVMFSuccess;
    }
    else if (aUuid == PvmiClockExtensionInterfaceUuid)
    {
        PvmiClockExtensionInterface* myInterface = OSCL_STATIC_CAST(PvmiClockExtensionInterface*, &iActiveTiming);
        aInterfacePtr = OSCL_STATIC_CAST(PVInterface*, myInterface);
        status = PVMFSuccess;
        iActiveTiming.addRef();
    }
    else
    {
        status = PVMFFailure;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


void PVFMVideoMIO::deleteMediaTransfer(PvmiMIOSession& aSession, PvmiMediaTransfer* media_transfer)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::deleteMediaTransfer() called"));

    OSCL_UNUSED_ARG(aSession);
    OSCL_UNUSED_ARG(media_transfer);

    // This class is implementing the media transfer, so no cleanup is needed
}


PVMFCommandId PVFMVideoMIO::Init(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::Init() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;

    switch (iState)
    {
        case STATE_LOGGED_ON:
            status = PVMFSuccess;
            iState = STATE_INITIALIZED;
            break;

        default:
            status = PVMFErrInvalidState;
            break;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}

PVMFCommandId PVFMVideoMIO::Reset(const OsclAny* aContext)
{
    ResetData();

    PVMFCommandId cmdid = iCommandCounter++;
    PVMFStatus status = PVMFSuccess;

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}

PVMFCommandId PVFMVideoMIO::Start(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::Start() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;

    switch (iState)
    {
        case STATE_INITIALIZED:
        case STATE_PAUSED:
            iState = STATE_STARTED;
            status = PVMFSuccess;
            if (iPeer && iWriteBusy)
            {
                iWriteBusy = false;
                iPeer->statusUpdate(PVMI_MEDIAXFER_STATUS_WRITE);
            }
            break;

        default:
            status = PVMFErrInvalidState;
            break;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


PVMFCommandId PVFMVideoMIO::Pause(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::Pause() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;

    switch (iState)
    {
        case STATE_STARTED:
            iState = STATE_PAUSED;
            status = PVMFSuccess;
            break;

        default:
            status = PVMFErrInvalidState;
            break;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


PVMFCommandId PVFMVideoMIO::Flush(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::Flush() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;

    switch (iState)
    {
        case STATE_STARTED:
            iState = STATE_INITIALIZED;
            status = PVMFSuccess;
            break;

        default:
            status = PVMFErrInvalidState;
            break;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


PVMFCommandId PVFMVideoMIO::DiscardData(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::DiscardData() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    // This component doesn't buffer data, so there's nothing needed here.
    PVMFStatus status = PVMFSuccess;

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}

//////////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVFMVideoMIO::DiscardData(PVMFTimestamp aTimestamp, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::DiscardData(timestamp,context) called"));

    OSCL_UNUSED_ARG(aTimestamp);
    return DiscardData(aContext);
}


PVMFCommandId PVFMVideoMIO::Stop(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::Stop() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    PVMFStatus status = PVMFFailure;

    switch (iState)
    {
        case STATE_STARTED:
        case STATE_PAUSED:
            iState = STATE_INITIALIZED;
            status = PVMFSuccess;
            break;

        default:
            status = PVMFErrInvalidState;
            break;
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


PVMFCommandId PVFMVideoMIO::CancelAllCommands(const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::CancelAllCommands() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    // Commands are executed immediately upon being received, so it isn't really possible to cancel them.

    PVMFStatus status = PVMFSuccess;

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


PVMFCommandId PVFMVideoMIO::CancelCommand(PVMFCommandId aCmdId, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::CancelCommand() called"));

    PVMFCommandId cmdid = iCommandCounter++;

    // Commands are executed immediately upon being received, so it isn't really possible to cancel them.

    // See if the response is still queued.
    PVMFStatus status = PVMFFailure;
    for (uint32 i = 0; i < iCommandResponseQueue.size(); i++)
    {
        if (iCommandResponseQueue[i].iCmdId == aCmdId)
        {
            status = PVMFSuccess;
            break;
        }
    }

    CommandResponse resp(status, cmdid, aContext);
    QueueCommandResponse(resp);
    return cmdid;
}


void PVFMVideoMIO::ThreadLogon()
{
    if (iState == STATE_IDLE)
    {
        iLogger = PVLogger::GetLoggerObject("PVFMVideoMIO");
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::ThreadLogon() called"));
        AddToScheduler();
        iState = STATE_LOGGED_ON;
    }
}


void PVFMVideoMIO::ThreadLogoff()
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::ThreadLogoff() called"));

    if (iState != STATE_IDLE)
    {
        RemoveFromScheduler();
        iLogger = NULL;
        iState = STATE_IDLE;
    }
}


void PVFMVideoMIO::setPeer(PvmiMediaTransfer* aPeer)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setPeer() called"));

    // Set the observer
    iPeer = aPeer;
}


void PVFMVideoMIO::useMemoryAllocators(OsclMemAllocator* write_alloc)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::useMemoryAllocators() called"));

    OSCL_UNUSED_ARG(write_alloc);

    // Not supported.
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::useMemoryAllocators() NOT SUPPORTED"));
}


PVMFCommandId PVFMVideoMIO::writeAsync(uint8 aFormatType, int32 aFormatIndex, uint8* aData, uint32 aDataLen,
                                       const PvmiMediaXferHeader& data_header_info, OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() seqnum %d ts %d context %d", data_header_info.seq_num, data_header_info.timestamp, aContext));

    PVMFStatus status = PVMFFailure;

    // Do a leave if MIO is not configured except when it is an EOS
    if (!iIsMIOConfigured &&
            !((PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION == aFormatType) &&
              (PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM == aFormatIndex)))
    {
        iWriteBusy = true;
        OSCL_LEAVE(OsclErrInvalidState);
        return -1;
    }

    switch (aFormatType)
    {
        case PVMI_MEDIAXFER_FMT_TYPE_COMMAND :
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() called with Command info."));
            // Ignore
            status = PVMFSuccess;
            break;

        case PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION :
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() called with Notification info."));
            switch (aFormatIndex)
            {
                case PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM:
                    // If waiting for frame then return errMaxReached
                    if (iFrameRetrievalInfo.iRetrievalRequested)
                    {
                        iFrameRetrievalInfo.iRetrievalRequested = false;
                        iFrameRetrievalInfo.iUseFrameIndex = false;
                        iFrameRetrievalInfo.iUseTimeOffset = false;
                        iFrameRetrievalInfo.iGetFrameObserver->HandleFrameReadyEvent(PVMFErrMaxReached);
                    }
                    break;

                default:
                    break;
            }
            // Ignore
            status = PVMFSuccess;
            break;

        case PVMI_MEDIAXFER_FMT_TYPE_DATA :
            switch (aFormatIndex)
            {
                case PVMI_MEDIAXFER_FMT_INDEX_FMT_SPECIFIC_INFO:
                    // Format-specific info contains codec headers.
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() called with format-specific info."));

                    if (iState < STATE_INITIALIZED)
                    {
                        PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::writeAsync: Error - Invalid state"));
                        iWriteBusy = true;
                        OSCL_LEAVE(OsclErrInvalidState);
                        return -1;
                    }
                    else
                    {
                        if (aDataLen > 0)
                        {
                            status = PVMFSuccess;
                        }
                        else
                        {
                            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::writeAsync() called aDataLen==0."));
                            status = PVMFSuccess;
                        }
                    }
                    break;

                case PVMI_MEDIAXFER_FMT_INDEX_DATA:
                    // Data contains the media bitstream.

                    // Verify the state
                    if (iState != STATE_STARTED)
                    {
                        PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::writeAsync: Error - Invalid state"));
                        iWriteBusy = true;
                        OSCL_LEAVE(OsclErrInvalidState);
                        return -1;
                    }
                    else
                    {
                        if (iFrameRetrievalInfo.iRetrievalRequested)
                        {
                            if (iFrameRetrievalInfo.iUseFrameIndex)
                            {
                                ++iFrameRetrievalInfo.iReceivedFrameCount;
                                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() Received frames %d", iFrameRetrievalInfo.iReceivedFrameCount));
                            }
                            else if (iFrameRetrievalInfo.iUseTimeOffset && iFrameRetrievalInfo.iStartingTSSet == false)
                            {
                                iFrameRetrievalInfo.iStartingTSSet = true;
                                iFrameRetrievalInfo.iStartingTS = data_header_info.timestamp;
                                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() Starting timestamp set %d", iFrameRetrievalInfo.iStartingTS));
                            }
                        }

                        if (aDataLen > 0)
                        {
                            // Check if a frame retrieval was requested
                            if (iFrameRetrievalInfo.iRetrievalRequested)
                            {

                                // scale down output proportionally if smaller thumbnail requested
                                if (iVideoDisplayWidth > iThumbnailWidth || iVideoDisplayHeight > iThumbnailHeight)
                                {
                                    float fScaleWidth = (float)iThumbnailWidth / iVideoDisplayWidth;
                                    float fScaleHeight = (float)iThumbnailHeight / iVideoDisplayHeight;
                                    float fScale = (fScaleWidth > fScaleHeight) ? fScaleHeight : fScaleWidth;
                                    iVideoDisplayWidth = (uint32)(iVideoDisplayWidth * fScale);
                                    iVideoDisplayHeight = (uint32)(iVideoDisplayHeight * fScale);
                                    // It is possible that width and height becomes odd numbers after
                                    // scaling them down. These values might be used by ColorConverter
                                    // for ColorConversion which expects even parameters. Make these
                                    // parameters multiple of 2.
                                    iVideoDisplayWidth = ((iVideoDisplayWidth + 1) & (~1));
                                    iVideoDisplayHeight = ((iVideoDisplayHeight + 1) & (~1));
                                }

                                if (iFrameRetrievalInfo.iUseFrameIndex == true &&
                                        iFrameRetrievalInfo.iReceivedFrameCount > iFrameRetrievalInfo.iFrameIndex)
                                {
                                    PVMFStatus evstatus = PVMFFailure;
                                    // Copy the frame data
                                    evstatus = CopyVideoFrameData(aData, aDataLen, iVideoFormat,
                                                                  iFrameRetrievalInfo.iFrameBuffer, *(iFrameRetrievalInfo.iBufferSize), iFrameRetrievalInfo.iFrameFormatType,
                                                                  iVideoWidth, iVideoHeight, iVideoDisplayWidth, iVideoDisplayHeight);

                                    iFrameRetrievalInfo.iRetrievalRequested = false;
                                    iFrameRetrievalInfo.iUseFrameIndex = false;
                                    iFrameRetrievalInfo.iUseTimeOffset = false;

                                    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() Retrieved requested frame by frame index(%d) Status %d", iFrameRetrievalInfo.iFrameIndex, evstatus));
                                    iFrameRetrievalInfo.iGetFrameObserver->HandleFrameReadyEvent(evstatus);
                                }
                                else if (iFrameRetrievalInfo.iUseTimeOffset == true &&
                                         iFrameRetrievalInfo.iStartingTSSet == true &&
                                         (data_header_info.timestamp - iFrameRetrievalInfo.iStartingTS) >= iFrameRetrievalInfo.iTimeOffset)
                                {
                                    PVMFStatus evstatus = PVMFFailure;

                                    // Copy the frame data
                                    evstatus = CopyVideoFrameData(aData, aDataLen, iVideoFormat,
                                                                  iFrameRetrievalInfo.iFrameBuffer, *(iFrameRetrievalInfo.iBufferSize), iFrameRetrievalInfo.iFrameFormatType,
                                                                  iVideoWidth, iVideoHeight, iVideoDisplayWidth, iVideoDisplayHeight);

                                    iFrameRetrievalInfo.iRetrievalRequested = false;
                                    iFrameRetrievalInfo.iUseFrameIndex = false;
                                    iFrameRetrievalInfo.iUseTimeOffset = false;

                                    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeAsync() Retrieved requested frame by time(%d) Actual TS %d Status %d", iFrameRetrievalInfo.iTimeOffset, data_header_info.timestamp, evstatus));
                                    iFrameRetrievalInfo.iGetFrameObserver->HandleFrameReadyEvent(evstatus);
                                }
                            }

                            status = PVMFSuccess;
                        }
                        else
                        {
                            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::writeAsync() called aDataLen==0."));
                            status = PVMFSuccess;
                        }
                    }
                    break;

                default:
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::writeAsync: Error - unrecognized format index"));
                    status = PVMFFailure;
                    break;
            }
            break;

        default:
            PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::writeAsync: Error - unrecognized format type"));
            status = PVMFFailure;
            break;
    }

    //Schedule asynchronous response
    PVMFCommandId cmdid = iCommandCounter++;
    WriteResponse resp(status, cmdid, aContext, data_header_info.timestamp);
    iWriteResponseQueue.push_back(resp);
    RunIfNotReady();
    return cmdid;

}


PVMFStatus PVFMVideoMIO::CopyVideoFrameData(uint8* aSrcBuffer, uint32 aSrcSize, PVMFFormatType aSrcFormat,
        uint8* aDestBuffer, uint32& aDestSize, PVMFFormatType aDestFormat,
        uint32 aSrcWidth, uint32 aSrcHeight, uint32 aDestWidth, uint32 aDestHeight)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::CopyVideoFrameData() In"));

    if (aSrcBuffer == NULL || aSrcSize == 0 || aSrcFormat == PVMF_MIME_FORMAT_UNKNOWN ||
            aDestBuffer == NULL || aDestSize == 0 || aDestFormat == PVMF_MIME_FORMAT_UNKNOWN)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() bad input arguments."));
        return PVMFErrArgument;
    }

    if ((iVideoSubFormat == PVMF_MIME_YUV422_INTERLEAVED_UYVY) &&
            (aDestFormat == PVMF_MIME_YUV420))
    {
        // Source is YUV 4:2:2 and dest is YUV 4:2:0

        PVMFStatus status;
        uint32 yuvbufsize;
        if (!iYUV422toYUV420ColorConvert)
        {
            status = CreateYUV422toYUV420ColorConvert();
            if (status != PVMFSuccess)
            {
                // Failed to create the CC!
                PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Failed to create iYUV422toYUV420ColorConvert."));
                return status;
            }
        }

        // Init CC
        status = InitYUV422toYUV420ColorConvert(aSrcWidth, aSrcHeight, aDestWidth, aDestHeight);
        if (status != PVMFSuccess)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() iYUV422toYUV420ColorConvert Init failed"));
            return status;
        }

        yuvbufsize = (uint32)(iYUV422toYUV420ColorConvert->GetOutputBufferSize());

        // Is the CC destination buffer smaller that the expected destination buffer size?
        if (yuvbufsize > aDestSize)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Specified output YUV buffer does not have enough space. Needed %d Available %d.", yuvbufsize, aDestSize));
            return PVMFErrResource;
        }

        // Convert
        if (iYUV422toYUV420ColorConvert->Convert(aSrcBuffer, aDestBuffer) == 0)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() YUV Color conversion failed"));
            return PVMFErrResource;
        }

        // Save the YUV frame size
        aDestSize = yuvbufsize;
    }
    else if (aSrcFormat == aDestFormat)
    {
        // Same format so direct copy
        if (aDestSize < aSrcSize)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() destination and source size differ"));
            return PVMFErrArgument;
        }

        if (iVideoSubFormat == PVMF_MIME_YUV420_SEMIPLANAR_YVU)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::CopyVideoFrameData() SubFormat is YUV SemiPlanar, Convert to YUV planar first"));
            convertYUV420SPtoYUV420(aSrcBuffer, aDestBuffer, aSrcSize);
        }
        else
        {
            oscl_memcpy(aDestBuffer, aSrcBuffer, aSrcSize);
        }
        aDestSize = aSrcSize;
    }
    else if (aSrcFormat == PVMF_MIME_YUV420 &&
             (aDestFormat == PVMF_MIME_RGB12 || aDestFormat == PVMF_MIME_RGB16 || aDestFormat == PVMF_MIME_RGB24))
    {
        // Source is YUV 4:2:0 and dest is RGB 12, 16, or 24 bit

        // Validate the source and dest dimensions
        if (aSrcWidth == 0 || aSrcHeight == 0 || aDestWidth == 0 || aDestHeight == 0)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Invalid frame dimensions Src(WxH): %dx%d Dest(WxH): %dx%d",
                            aSrcWidth, aSrcHeight, aDestWidth, aDestHeight));
            return PVMFErrArgument;
        }

        // Check if the proper color converter is available
        if (iColorConverter && iCCRGBFormatType != aDestFormat)
        {
            DestroyYUVToRGBColorConverter(iColorConverter, iCCRGBFormatType);
            iCCRGBFormatType = PVMF_MIME_FORMAT_UNKNOWN;
        }

        // Instantiate a new color converter if needed
        if (iColorConverter == NULL)
        {
            PVMFStatus retval = CreateYUVToRGBColorConverter(iColorConverter, aDestFormat);
            if (retval != PVMFSuccess)
            {
                // Color converter could not be instantiated
                PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Appropriate YUV to RGB color converter could not be instantiated"));
                return retval;
            }
            iCCRGBFormatType = aDestFormat;
        }

        if (!(iColorConverter->Init((aSrcWidth + 1)&(~1), (aSrcHeight + 1)&(~1), (aSrcWidth + 1)&(~1), aDestWidth, (aDestHeight + 1)&(~1), (aDestWidth + 1)&(~1), CCROTATE_NONE)))
        {
            // Color converter failed Init
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() iColorConverter failed init."));
            return PVMFFailure;
        }


        iColorConverter->SetMemHeight((iVideoHeight + 1)&(~1));
        iColorConverter->SetMode(1); // Do scaling if needed.

        uint32 rgbbufsize = (uint32)(iColorConverter->GetOutputBufferSize());
        if (rgbbufsize > aDestSize)
        {
            // Specified buffer does not have enough space
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Specified output RGB buffer does not have enough space. Needed %d Available %d", rgbbufsize, aDestSize));
            return PVMFErrResource;
        }

        // Do the color conversion
        if (iColorConverter->Convert(aSrcBuffer, aDestBuffer) == 0)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Color conversion failed"));
            return PVMFErrResource;
        }
        // Save the RGB frame size
        aDestSize = rgbbufsize;
    }
    else
    {
        // Other conversions not supported yet
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() Unsupported conversion mode."));
        return PVMFErrNotSupported;
    }

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::CopyVideoFrameData() Out"));
    return PVMFSuccess;
}


void PVFMVideoMIO::writeComplete(PVMFStatus aStatus, PVMFCommandId write_cmd_id, OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::writeComplete() called"));

    OSCL_UNUSED_ARG(aStatus);
    OSCL_UNUSED_ARG(write_cmd_id);
    OSCL_UNUSED_ARG(aContext);

    // Won't be called since this component is a sink.
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::writeComplete() Should not be called since this MIO is a sink"));
}


PVMFCommandId PVFMVideoMIO::readAsync(uint8* data, uint32 max_data_len, OsclAny* aContext, int32* formats, uint16 num_formats)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::readAsync() called"));

    OSCL_UNUSED_ARG(data);
    OSCL_UNUSED_ARG(max_data_len);
    OSCL_UNUSED_ARG(aContext);
    OSCL_UNUSED_ARG(formats);
    OSCL_UNUSED_ARG(num_formats);

    // Read not supported.
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::readAsync() NOT SUPPORTED"));
    OsclError::Leave(OsclErrNotSupported);
    return -1;
}


void PVFMVideoMIO::readComplete(PVMFStatus aStatus, PVMFCommandId read_cmd_id, int32 format_index,
                                const PvmiMediaXferHeader& data_header_info, OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::readComplete() called"));

    OSCL_UNUSED_ARG(aStatus);
    OSCL_UNUSED_ARG(read_cmd_id);
    OSCL_UNUSED_ARG(format_index);
    OSCL_UNUSED_ARG(data_header_info);
    OSCL_UNUSED_ARG(aContext);

    // Won't be called since this component is a sink.
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::readComplete() Should not be called since this MIO is a sink"));
}


void PVFMVideoMIO::statusUpdate(uint32 status_flags)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::statusUpdate() called"));

    OSCL_UNUSED_ARG(status_flags);

    // Won't be called since this component is a sink.
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO, (0, "PVFMVideoMIO::statusUpdate() Should not be called since this MIO is a sink"));
}


void PVFMVideoMIO::cancelCommand(PVMFCommandId  command_id)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::cancelCommand() called"));

    // The purpose of this API is to cancel a writeAsync command and report completion ASAP.

    // In this implementation, the write commands are executed immediately when received so it isn't
    // really possible to cancel. Just report completion immediately.

    for (uint32 i = 0; i < iWriteResponseQueue.size(); i++)
    {
        if (iWriteResponseQueue[i].iCmdId == command_id)
        {
            //report completion
            if (iPeer)
            {
                iPeer->writeComplete(iWriteResponseQueue[i].iStatus, iWriteResponseQueue[i].iCmdId, (OsclAny*)iWriteResponseQueue[i].iContext);
            }
            iWriteResponseQueue.erase(&iWriteResponseQueue[i]);
            break;
        }
    }
}


void PVFMVideoMIO::cancelAllCommands()
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::cancelAllCommands() called"));

    // The purpose of this API is to cancel all writeAsync commands and report completion ASAP.

    // In this implementaiton, the write commands are executed immediately when received so it isn't
    // really possible to cancel. Just report completion immediately.

    for (uint32 i = 0; i < iWriteResponseQueue.size(); i++)
    {
        //report completion
        if (iPeer)
        {
            iPeer->writeComplete(iWriteResponseQueue[i].iStatus, iWriteResponseQueue[i].iCmdId, (OsclAny*)iWriteResponseQueue[i].iContext);
        }
        iWriteResponseQueue.erase(&iWriteResponseQueue[i]);
    }
}


PVMFStatus PVFMVideoMIO::getParametersSync(PvmiMIOSession aSession, PvmiKeyType aIdentifier, PvmiKvp*& aParameters,
        int& num_parameter_elements, PvmiCapabilityContext aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::getParametersSync() called"));

    OSCL_UNUSED_ARG(aSession);
    OSCL_UNUSED_ARG(aContext);

    aParameters = NULL;
    num_parameter_elements = 0;

    if (pv_mime_strcmp(aIdentifier, MOUT_VIDEO_FORMAT_KEY) == 0)
    {
        //query for video format string
        aParameters = (PvmiKvp*)oscl_malloc(sizeof(PvmiKvp));
        aParameters->value.pChar_value = (char*)iVideoFormat.getMIMEStrPtr();
        //don't bother to set the key string.
        return PVMFSuccess;
    }

    else if (pv_mime_strcmp(aIdentifier, INPUT_FORMATS_CAP_QUERY) == 0)
    {
        // This is a query for the list of supported formats.
        // This component supports all uncompressed video format
        // Generate a list of all the PVMF video formats...
        int32 count = iInputFormatCapability.size();

        aParameters = (PvmiKvp*)oscl_malloc(count * sizeof(PvmiKvp));

        if (aParameters)
        {
            num_parameter_elements = 0;
            Oscl_Vector<PVMFFormatType, OsclMemAllocator>::iterator it;
            for (it = iInputFormatCapability.begin(); it != iInputFormatCapability.end(); it++)
            {
                aParameters[num_parameter_elements++].value.pChar_value = OSCL_STATIC_CAST(char*, it->getMIMEStrPtr());
            }
            return PVMFSuccess;
        }
        return PVMFErrNoMemory;
    }

    // Other queries are not currently supported so report as unrecognized key.
    return PVMFFailure;
}


PVMFStatus PVFMVideoMIO::releaseParameters(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::releaseParameters() called"));

    OSCL_UNUSED_ARG(aSession);
    OSCL_UNUSED_ARG(num_elements);

    // Release parameters that were allocated by this component.
    if (aParameters)
    {
        oscl_free(aParameters);
        return PVMFSuccess;
    }
    return PVMFFailure;
}


void PVFMVideoMIO::setParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements, PvmiKvp*& aRet_kvp)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() called"));

    OSCL_UNUSED_ARG(aSession);

    aRet_kvp = NULL;

    for (int32 i = 0; i < num_elements; i++)
    {
        //Check against known video parameter keys...
        if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_FORMAT_KEY) == 0)
        {
            iVideoFormat = aParameters[i].value.pChar_value;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() Video Format Key, Value %s", iVideoFormat.getMIMEStrPtr()));
        }
        else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_WIDTH_KEY) == 0)
        {
            iVideoWidth = (int32)aParameters[i].value.uint32_value;
            iVideoWidthValid = true;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() Video Width Key, Value %d", iVideoWidth));
        }
        else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_HEIGHT_KEY) == 0)
        {
            iVideoHeight = (int32)aParameters[i].value.uint32_value;
            iVideoHeightValid = true;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() Video Height Key, Value %d", iVideoHeight));
        }
        else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_DISPLAY_HEIGHT_KEY) == 0)
        {
            iVideoDisplayHeight = (int32)aParameters[i].value.uint32_value;
            iVideoDisplayHeightValid = true;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() Video Display Height Key, Value %d", iVideoDisplayHeight));
        }
        else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_DISPLAY_WIDTH_KEY) == 0)
        {
            iVideoDisplayWidth = (int32)aParameters[i].value.uint32_value;
            iVideoDisplayWidthValid = true;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() Video Display Width Key, Value %d", iVideoDisplayWidth));
        }
        else if (pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_SPECIFIC_INFO_KEY) == 0)
        {
            //  iOutputFile.Write(aParameters[i].value.pChar_value, sizeof(uint8), (int32)aParameters[i].capacity);
        }
        else if (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_SUBFORMAT_KEY) == 0)
        {
            iVideoSubFormat = aParameters[i].value.pChar_value;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Video SubFormat Key, Value %s", iVideoSubFormat.getMIMEStrPtr()));
        }
        //All FSI for video will be set here in one go
        else if (pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_SPECIFIC_INFO_KEY_YUV) == 0)
        {
            PVMFYuvFormatSpecificInfo0* yuvInfo = (PVMFYuvFormatSpecificInfo0*)aParameters->value.key_specific_value;

            iVideoWidth = (int32)yuvInfo->buffer_width;
            if (iVideoWidth)
                iVideoWidthValid = true;
            else
                iVideoWidthValid = false;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Video Width, Value %d", iVideoWidth));

            iVideoHeight = (int32)yuvInfo->buffer_height;
            if (iVideoHeight)
                iVideoHeightValid = true;
            else
                iVideoHeightValid = false;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Video Height, Value %d", iVideoHeight));

            iVideoDisplayHeight = (int32)yuvInfo->viewable_height;
            if (iVideoDisplayHeight)
                iVideoDisplayHeightValid = true;
            else
                iVideoDisplayHeightValid = false;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Video Display Height, Value %d", iVideoDisplayHeight));

            iVideoDisplayWidth = (int32)yuvInfo->viewable_width;
            if (iVideoDisplayWidth)
                iVideoDisplayWidthValid = true;
            else
                iVideoDisplayWidthValid = false;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Video Display Width, Value %d", iVideoDisplayWidth));

            iNumberOfBuffers = (int32)yuvInfo->num_buffers;
            iNumberOfBuffersValid = true;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Number of Buffer, Value %d", iNumberOfBuffers));

            iBufferSize = (int32)yuvInfo->buffer_size;
            iBufferSizeValid = true;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVFMVideoMIO::setParametersSync() Buffer Size, Value %d", iBufferSize));

        }
        else
        {
            if (iVideoWidthValid && iVideoHeightValid && iVideoDisplayHeightValid && iVideoDisplayHeightValid && !iIsMIOConfigured)
            {
                if (iObserver)
                {
                    iIsMIOConfigured = true;
                    iObserver->ReportInfoEvent(PVMFMIOConfigurationComplete);
                    if (iPeer && iWriteBusy)
                    {
                        iWriteBusy = false;
                        iPeer->statusUpdate(PVMI_MEDIAXFER_STATUS_WRITE);
                    }
                }
            }

            // If we get here the key is unrecognized.
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::setParametersSync() Error, unrecognized key "));

            // Set the return value to indicate the unrecognized key and return.
            aRet_kvp = &aParameters[i];
            return;
        }
    }
    if (iVideoWidthValid && iVideoHeightValid && iVideoDisplayHeightValid && iVideoDisplayHeightValid && !iIsMIOConfigured)
    {
        if (iObserver)
        {
            iIsMIOConfigured = true;
            iObserver->ReportInfoEvent(PVMFMIOConfigurationComplete);
            if (iPeer && iWriteBusy)
            {
                iWriteBusy = false;
                iPeer->statusUpdate(PVMI_MEDIAXFER_STATUS_WRITE);
            }
        }
    }
}


PVMFStatus PVFMVideoMIO::verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVFMVideoMIO::verifyParametersSync() called"));

    OSCL_UNUSED_ARG(aSession);

    // Go through each parameter
    for (int32 paramind = 0; paramind < num_elements; ++paramind)
    {
        // Retrieve the first component from the key string
        char* compstr = NULL;
        pv_mime_string_extract_type(0, aParameters[paramind].key, compstr);

        if (pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf/media/format-type")) == 0)
        {
            //This component supports only uncompressed formats
            Oscl_Vector<PVMFFormatType, OsclMemAllocator>::iterator it;
            for (it = iInputFormatCapability.begin(); it != iInputFormatCapability.end(); it++)
            {
                if (pv_mime_strcmp(aParameters[paramind].value.pChar_value, it->getMIMEStrPtr()) == 0)
                {
                    return PVMFSuccess;
                }
            }
            // Not found on the list of supported input formats
            return PVMFErrNotSupported;
        }
    }
    // For all other parameters return success.
    return PVMFSuccess;
}


//
// For active timing support
//
PVMFStatus PVFMVideoMIOActiveTimingSupport::SetClock(PVMFMediaClock *clockVal)
{
    iClock = clockVal;
    return PVMFSuccess;
}


void PVFMVideoMIOActiveTimingSupport::addRef()
{
}


void PVFMVideoMIOActiveTimingSupport::removeRef()
{
}


bool PVFMVideoMIOActiveTimingSupport::queryInterface(const PVUuid& aUuid, PVInterface*& aInterface)
{
    aInterface = NULL;
    PVUuid uuid;
    queryUuid(uuid);
    if (uuid == aUuid)
    {
        PvmiClockExtensionInterface* myInterface = OSCL_STATIC_CAST(PvmiClockExtensionInterface*, this);
        aInterface = OSCL_STATIC_CAST(PVInterface*, myInterface);
        return true;
    }
    return false;
}


void PVFMVideoMIOActiveTimingSupport::queryUuid(PVUuid& uuid)
{
    uuid = PvmiClockExtensionInterfaceUuid;
}


//
// Private section
//
void PVFMVideoMIO::Run()
{
    // Send async command responses
    while (!iCommandResponseQueue.empty())
    {
        if (iObserver)
        {
            iObserver->RequestCompleted(PVMFCmdResp(iCommandResponseQueue[0].iCmdId, iCommandResponseQueue[0].iContext, iCommandResponseQueue[0].iStatus));
        }
        iCommandResponseQueue.erase(&iCommandResponseQueue[0]);
    }

    // Send async write completion
    while (!iWriteResponseQueue.empty())
    {
        // Report write complete
        if (iPeer)
        {
            iPeer->writeComplete(iWriteResponseQueue[0].iStatus, iWriteResponseQueue[0].iCmdId, (OsclAny*)iWriteResponseQueue[0].iContext);
        }
        iWriteResponseQueue.erase(&iWriteResponseQueue[0]);
    }
}

static inline void* byteOffset(void* p, uint32 offset)
{
    return (void*)((uint8*)p + offset);
}

// convert a frame in YUV420 semiplanar format with VU ordering to YUV420 planar format
void PVFMVideoMIO::convertYUV420SPtoYUV420(void* src, void* dst, uint32 len)
{
    // copy the Y plane
    uint32 y_plane_size = iVideoWidth * iVideoHeight;
    memcpy(dst, src, y_plane_size + iVideoWidth);

    // re-arrange U's and V's
    uint32* p = (uint32*)byteOffset(src, y_plane_size);
    uint16* pu = (uint16*)byteOffset(dst, y_plane_size);
    uint16* pv = (uint16*)byteOffset(pu, y_plane_size / 4);

    int count = y_plane_size / 8;
    do
    {
        uint32 uvuv = *p++;
        *pu++ = (uint16)(((uvuv >> 8) & 0xff) | ((uvuv >> 16) & 0xff00));
        *pv++ = (uint16)((uvuv & 0xff) | ((uvuv >> 8) & 0xff00));
    }
    while (--count);
}

PVMFStatus PVFMVideoMIO::CreateYUV422toYUV420ColorConvert()
{
    int32 leavecode = 0;
    OSCL_TRY(leavecode, iYUV422toYUV420ColorConvert = (CCYUV422toYUV420*) CCYUV422toYUV420::New());

    OSCL_FIRST_CATCH_ANY(leavecode,
                         PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CreateYUV422toYUV420ColorConvert() YUV Color converter instantiation did a leave"));
                         return PVMFErrNoResources;
                        );
    return PVMFSuccess;
}

PVMFStatus PVFMVideoMIO::InitYUV422toYUV420ColorConvert(uint32 aSrcWidth, uint32 aSrcHeight, uint32 aDestWidth, uint32 aDestHeight)
{
    if (!(iYUV422toYUV420ColorConvert->Init((aSrcWidth + 1)&(~1), (aSrcHeight + 1)&(~1),
                                            (aSrcWidth + 1)&(~1), (aDestWidth + 1)&(~1), (aDestHeight + 1)&(~1),
                                            (aDestWidth + 1)&(~1), CCROTATE_NONE)))
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVFMVideoMIO::CopyVideoFrameData() YUV Color converter Init failed"));
        DestroyYUV422toYUV420ColorConvert();
        return PVMFFailure;
    }
    return PVMFSuccess;
}

void PVFMVideoMIO::DestroyYUV422toYUV420ColorConvert()
{
    OSCL_DELETE(iYUV422toYUV420ColorConvert);
    iYUV422toYUV420ColorConvert = NULL;
}

