/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
/**
 *
 * @file pv_media_output_node_inport.cpp
 * @brief Input port for media io interface wrapper node.
 *
 */

#include "pv_media_output_node_inport.h"
#include "pv_media_output_node.h"
#include "pvmf_common_audio_decnode.h"
#include "pvmf_video.h"
#include "pv_mime_string_utils.h"
#include "pvmf_media_cmd.h"
#include "pvmf_media_msg_format_ids.h"
#include "media_clock_converter.h"
#include "time_comparison_utils.h"

#define LOGDATAPATH(x)  PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iDatapathLogger, PVLOGMSG_INFO, x);

#define PVMF_MOPORT_LOGDATAPATH(x)  PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iDatapathLogger, PVLOGMSG_INFO, x);
#define PVMF_MOPORT_LOGDATAPATH_IN(x)   PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iDatapathLoggerIn, PVLOGMSG_INFO, x);
#define PVMF_MOPORT_LOGDATAPATH_OUT(x)  PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iDatapathLoggerOut, PVLOGMSG_INFO, x);
#define PVMF_MOPORT_LOGREPOS(x) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO, x);
#define PVMF_MOPORT_LOGDEBUG(x) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, x);
#define PVMF_MOPORT_LOGERROR(x) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, x);


//for logging media data info
void PVMediaOutputNodePort::LogMediaDataInfo(const char* msg, PVMFSharedMediaDataPtr mediaData)
{
    if (!mediaData.GetRep())
    {
        OSCL_UNUSED_ARG(msg);
        return;
    }
    LOGDATAPATH(
        (0, "MOUT %s %s MediaData SeqNum %d, SId %d, ClipId %d, TS %d"
         , PortName()
         , msg
         , mediaData->getSeqNum()
         , mediaData->getStreamID()
         , mediaData->getClipID()
         , mediaData->getTimestamp()
        ));
}


//for logging media data info plus write ID and cleanup q depth
void PVMediaOutputNodePort::LogMediaDataInfo(const char* msg, PVMFSharedMediaDataPtr mediaData, int32 cmdid, int32 qdepth)
{
    if (!mediaData.GetRep())
    {
        OSCL_UNUSED_ARG(msg);
        OSCL_UNUSED_ARG(cmdid);
        OSCL_UNUSED_ARG(qdepth);
        return;
    }
    LOGDATAPATH(
        (0, "MOUT %s %s, Write Id %d, MediaData SeqNum %d, SId %d, ClipId %d, TS %d, Cleanup Q-depth %d"
         , PortName()
         , msg
         , cmdid
         , mediaData->getSeqNum()
         , mediaData->getStreamID()
         , mediaData->getClipID()
         , mediaData->getTimestamp()
         , qdepth
        ));
}


//for logging media xfer info
void PVMediaOutputNodePort::LogDatapath(const char*msg)
{

    if (!iDatapathLogger)
    {
        OSCL_UNUSED_ARG(msg);
        return; //unexpected call.
    }
    LOGDATAPATH(
        (0, "MOUT %s %s"
         , PortName()
         , msg
        ));
}


////////////////////////////////////////////////////////////////////////////
PVMediaOutputNodePort::PVMediaOutputNodePort(PVMediaOutputNode* aNode)
        : OsclTimerObject(OsclActiveObject::EPriorityNominal, "PVMediaOutputNodePort")
        , PvmfPortBaseImpl(PVMF_MEDIAIO_NODE_INPUT_PORT_TAG
                           //this port handles its own port activity
                           , this
                           , 20, 20, 90
                           //output queue isn't needed.
                           , 0, 0, 0, "MediaOut")
        , iNode(aNode)
{
    AddToScheduler();
    isUnCompressedMIO = false;

    iExtensionRefCount = 0;
    iPortFormat = PVMF_MIME_FORMAT_UNKNOWN;

    iMediaTransfer = NULL;
    iMioInfoErrorCmdId = 0;
    iMediaType = PVMF_MEDIA_UNKNOWN;
    iWriteState = EWriteOK;
    iCleanupQueue.reserve(1);
    iWriteAsyncContext = 0;
    iWriteAsyncEOSContext = 0;
    iWriteAsyncReConfigContext = 0;
    iClock = NULL;
    iClockNotificationsInf = NULL;
    oClockCallBackPending = false;
    iDelayEarlyFrameCallBkId = 0;
    iClockRate = 1;
    iEarlyMargin = 0;
    iLateMargin = 0;
    oActiveMediaOutputComp = true; // By default we treat the MIO's to be active.

    // MIO node waits for MIO component configuration to complete before sending data. Once configuration is
    // complete, MIO node waits for clock to start if mio component is passive. For active MIO's, it does not
    // wait for clock to start before sending data. It sends data as soon as MIO component configuration is
    // complete.

    oProcessIncomingMessage = false;


    oMIOComponentConfigured = false;

    iConsecutiveFramesDropped = 0;
    iLateFrameEventSent = false;

    iFragIndex = 0;

    iSkipTimestamp = 0;
    iRecentStreamID = 0;
    iRecentClipID = 0;
    iSendStartOfDataEvent = false;

    iSendOneFrameAfterSkip = false;

    iFrameStepMode = false;
    iClockFrameCount = 0;
    iSyncFrameCount = 0;
    iFramesDropped = 0;
    iTotalFrames = 0;

    iEosStreamIDVec.reserve(2);
    iClipIDVec.reserve(2);
    iBosTransitionVec.reserve(2);

    iOsclErrorTrapImp = OsclErrorTrap::GetErrorTrapImp();
    iLogger = PVLogger::GetLoggerObject("PVMediaOutputNodePort");
    iDatapathLogger = PVLogger::GetLoggerObject("datapath.sinknode");
    iDatapathLoggerIn = PVLogger::GetLoggerObject("datapath.sinknode.in");
    iDatapathLoggerOut = PVLogger::GetLoggerObject("datapath.sinknode.out");
    iReposLogger = PVLogger::GetLoggerObject("pvplayerrepos.mionode");
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::ClearCleanupQueue()
//clear the media transfer cleanup queue and log all messages.
{
    while (!iCleanupQueue.empty())
    {
        PVMFSharedMediaDataPtr mediaData = iCleanupQueue.begin()->iData;
        PVMFCommandId cmdId = iCleanupQueue.begin()->iCmdId;
        iCleanupQueue.erase(iCleanupQueue.begin());
        LogMediaDataInfo("Cleared"
                         , mediaData
                         , cmdId
                         , iCleanupQueue.size()
                        );
    }
}

////////////////////////////////////////////////////////////////////////////
PVMediaOutputNodePort::~PVMediaOutputNodePort()
{
    Disconnect();
    PvmfPortBaseImpl::ClearMsgQueues();
    //cancel any pending write operations
    if (!iCleanupQueue.empty())
    {
        int32 err;
        OSCL_TRY(err, iMediaTransfer->cancelAllCommands(););
        ClearCleanupQueue();
    }
    CleanupClock();

    // we need to clear the activity handler, since otherwise the PvmfPortBaseImpl destructor
    // ends up calling back onto our HandlePortActivity method, which no longer exists because
    // this objects's destructor has already been called.
    SetActivityHandler(NULL);
}


////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::Configure(OSCL_String& fmtstr)
{
    //This is called when the format is being set.
    if (iConnectedPort)
    {
        // Must disconnect before changing port properties, so return error
        return PVMFFailure;
    }
    PVMFFormatType fmt = fmtstr.get_cstr();

    if (IsFormatSupported(fmt))
    {
        iPortFormat = fmt;
        iSinkFormat = fmt;
        iSinkFormatString = fmtstr;
        FormatUpdated();
        return PVMFSuccess;
    }
    else
    {
        iPortFormat = PVMF_MIME_FORMAT_UNKNOWN;
        iSinkFormat = PVMF_MIME_FORMAT_UNKNOWN;
        iSinkFormatString = fmtstr;
        return PVMFFailure;
    }
}

////////////////////////////////////////////////////////////////////////////
// override the PvmfPortBaseImpl routine
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::Connect(PVMFPortInterface* aPort)
{
    PVMFStatus status = PvmfPortBaseImpl::Connect(aPort);
    if (status != PVMFSuccess)
    {
        return status;
    }

    if (iMediaTransfer == NULL)
    {
        iMediaTransfer = iNode->iMIOControl->createMediaTransfer(iNode->iMIOSession);
        if (iMediaTransfer)
        {
            iMediaTransfer->setPeer(this);
        }
        else
        {
            return PVMFFailure;
        }
    }

    iSkipTimestamp = 0;

    OsclAny* temp = NULL;
    aPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);
    PvmiCapabilityAndConfig *config = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, temp);

    if (config != NULL)
    {
        PvmiKvp* configKvp = NULL;
        PvmiKvp* aRet_kvp = NULL;
        int num_elements = 0;
        PVMFStatus status = config->getParametersSync(NULL, NULL, configKvp, num_elements, NULL);

        if (status == PVMFSuccess)
        {
            iNode->iMIOConfig->setParametersSync(iNode->iMIOSession,
                                                 configKvp,
                                                 num_elements,
                                                 aRet_kvp);
            config->releaseParameters(NULL, configKvp, num_elements);
        }
    }
    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
// override the PvmfPortBaseImpl routine
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::PeerConnect(PVMFPortInterface* aPort)
{
    PVMFStatus status = PvmfPortBaseImpl::PeerConnect(aPort);
    if (status != PVMFSuccess)
    {
        return status;
    }

    if (iMediaTransfer == NULL)
    {
        iMediaTransfer = iNode->iMIOControl->createMediaTransfer(iNode->iMIOSession);
        if (iMediaTransfer)
        {
            iMediaTransfer->setPeer(this);
        }
        else
        {
            return PVMFFailure;
        }
    }
    return PVMFSuccess;
}

void PVMediaOutputNodePort::CleanupMediaTransfer()
{
    int32 err;
    //Just in case the media transfer did not report all the async write
    //completes, cancel any pending write operations and clear up the cleanup queue
    if (!iCleanupQueue.empty())
    {
        err = 0;
        OSCL_TRY(err, iMediaTransfer->cancelAllCommands(););
        ClearCleanupQueue();
    }
    if (iNode && iNode->iMIOControl && iMediaTransfer)
    {
        iMediaTransfer->setPeer(NULL);
        err = 0;
        OSCL_TRY(err, iNode->iMIOControl->deleteMediaTransfer(iNode->iMIOSession, iMediaTransfer));
        OSCL_FIRST_CATCH_ANY(err,
                             PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
                                             (0, "PVMediaOutputNodePort::CleanupMediaTransfer Caught a leave when calling deleteMediaTransfer!"));
                            );
        iMediaTransfer = NULL;
    }
    CleanupClock();
    if (iCurrentMediaMsg.GetRep() != NULL)
    {
        iCurrentMediaMsg.Unbind();
    }
}

////////////////////////////////////////////////////////////////////////////
// override the PvmfPortBaseImpl routine
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::Disconnect()
{
    CleanupMediaTransfer();
    return PvmfPortBaseImpl::Disconnect();
}

////////////////////////////////////////////////////////////////////////////
// override the PvmfPortBaseImpl routine
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::PeerDisconnect()
{
    PVMFStatus status = PvmfPortBaseImpl::PeerDisconnect();
    if (status != PVMFSuccess)
    {
        return status;
    }
    CleanupMediaTransfer();
    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
// override the PvmfPortBaseImpl routine
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::ClearMsgQueues()
{
    if (iCurrentMediaMsg.GetRep() != NULL)
    {
        iCurrentMediaMsg.Unbind();
    }

    PvmfPortBaseImpl::ClearMsgQueues();
    //cancel any pending write operations
    if (!iCleanupQueue.empty())
    {
        int32 err;
        OSCL_TRY(err, iMediaTransfer->cancelAllCommands(););
        ClearCleanupQueue();
    }

    //may need to generate port flow control now
    PvmfPortBaseImpl::EvaluateIncomingBusy();

    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::addRef()
// for PVInterface
{
    ++iExtensionRefCount;
}

///////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::removeRef()
// for PVInterface
{
    if (iExtensionRefCount > 0)
    {
        --iExtensionRefCount;
    }
}

////////////////////////////////////////////////////////////////////////////
bool PVMediaOutputNodePort::queryInterface(const PVUuid& uuid, PVInterface*& iface)
// for PVInterface
{
    if (uuid == PvmfNodesSyncControlUuid)
    {
        PvmfNodesSyncControlInterface* myInterface = OSCL_STATIC_CAST(PvmfNodesSyncControlInterface*, this);
        iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
        addRef();
    }
    else
    {
        iface = NULL;
        return false;
    }
    return true;
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::NodeStarted()
{
    //it is possible that we attempted to call writeasync
    //before media output comp start (can happen for active components)
    //in those cases the comp can either accept or reject data
    //if it rejected it by doing a leave, attempt to send data again
    //now that we are done with start. Do not do the same for passive
    //comps since this method is called during a pause-resume, and we
    //do not want to attempt sending media data to passive comps till
    //clock has been started
    if ((iWriteState == EWriteWait) &&
            (true == oActiveMediaOutputComp))
    {
        iWriteState = EWriteOK;
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::NodeStarted - WriteAsync Enabled - Fmt=%s, iWriteState=%d",
                                 iSinkFormatString.get_str(),
                                 iWriteState));
        oProcessIncomingMessage = true;

        if (iCurrentMediaMsg.GetRep() != NULL)
        {
            //attempt to send data current media msg if any
            SendData();
        }
    }

    RunIfNotReady();
}

// Attempt to process incoming message only after the mio component is fully configured
// and the clock is set by the node.
void PVMediaOutputNodePort::ProcessIncomingMessageIfPossible()
{
    if (oMIOComponentConfigured && iClock)
    {
        PVMF_MOPORT_LOGDATAPATH(
            (0, "PVMediaOutputNodePort::ProcessIncomingMessageIfPossible: Fmt - Fmt=%s, MIOCompConfigured=%d, ActiveMIO=%d, ClockState=%d",
             iSinkFormatString.get_str(), oMIOComponentConfigured, oActiveMediaOutputComp, iClock->GetState()));

        PVMF_MOPORT_LOGDEBUG(
            (0, "PVMediaOutputNodePort::ProcessIncomingMessageIfPossible: Fmt - Fmt=%s, MIOCompConfigured=%d, ActiveMIO=%d, ClockState=%d",
             iSinkFormatString.get_str(), oMIOComponentConfigured, oActiveMediaOutputComp, iClock->GetState()));

        if (true == oActiveMediaOutputComp)
        {
            oProcessIncomingMessage = true;
        }
        else
        {
            if (iClock->GetState() == PVMFMediaClock::RUNNING)
            {
                oProcessIncomingMessage = true;
            }
        }
        RunIfNotReady();
    }
    else
    {
        PVMF_MOPORT_LOGDEBUG(
            (0, "PVMediaOutputNodePort::ProcessIncomingMessageIfPossible: Fmt - Fmt=%s, MIOCompConfigured=%d, iClock=0x%x",
             iSinkFormatString.get_str(), oMIOComponentConfigured, iClock));
    }
}

void PVMediaOutputNodePort::SetMIOComponentConfigStatus(bool aStatus)
{
    oMIOComponentConfigured = aStatus;

    ProcessIncomingMessageIfPossible();
}

void PVMediaOutputNodePort::CleanupClock()
{
    if (iClock != NULL)
    {
        if (iClockNotificationsInf != NULL)
        {
            iClockNotificationsInf->RemoveClockObserver(*this);
            iClockNotificationsInf->RemoveClockStateObserver(*this);
            iClock->DestroyMediaClockNotificationsInterface(iClockNotificationsInf);
            iClockNotificationsInf = NULL;
        }
    }
    iClock = NULL;
}

////////////////////////////////////////////////////////////////////////////
//for sync control interface
PVMFStatus PVMediaOutputNodePort::SetClock(PVMFMediaClock* aClock)
{
    if (NULL == aClock)
    {
        CleanupClock();
        return PVMFErrArgument;
    }
    iClock = aClock;

    ProcessIncomingMessageIfPossible();

    iClock->ConstructMediaClockNotificationsInterface(iClockNotificationsInf, *this);

    if (NULL == iClockNotificationsInf)
    {
        return PVMFErrNoMemory;
    }

    iClockNotificationsInf->SetClockObserver(*this);
    iClockNotificationsInf->SetClockStateObserver(*this);

    return PVMFSuccess;
}

void PVMediaOutputNodePort::EnableMediaSync()
{
    //wait on play clock
    oProcessIncomingMessage = false;
    oActiveMediaOutputComp = false;
}

////////////////////////////////////////////////////////////////////////////
//for sync control interface
PVMFStatus PVMediaOutputNodePort::ChangeClockRate(int32 aRate)
{
    if (0 == aRate)
    {
        // A 0 value of clockrate is not handled
        return PVMFFailure;
    }
    iClockRate = aRate;

    PVMFStatus status;
    status = SetMIOParameterInt32((char*)MOUT_MEDIAXFER_OUTPUT_RATE, aRate);

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0,
                    "PVMediaOutputNodePort::ChangeClockRate rate %d",
                    aRate));

    return status;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::SetMargins(int32 aEarlyMargin, int32 aLateMargin)
//for sync control interface
{
    iEarlyMargin = aEarlyMargin;
    iLateMargin = aLateMargin;
    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::ClockStarted()
//for sync control interface
{
    if (IsBusy())
    {
        // Cancel any long waits due to data synchronization done
        // before clock was set and started
        Cancel();
    }
    RunIfNotReady();
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::ClockStopped()
//for sync control interface
{
    ;//ignore
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::setPeer(PvmiMediaTransfer *aPeer)
//for PvmiMediaTransfer
{
    OSCL_UNUSED_ARG(aPeer);
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::useMemoryAllocators(OsclMemAllocator* write_alloc)
//for PvmiMediaTransfer
{
    OSCL_UNUSED_ARG(write_alloc);
    OSCL_LEAVE(OsclErrNotSupported);
}


////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMediaOutputNodePort::writeAsync(uint8 format_type, int32 format_index,
        uint8* data, uint32 data_len,
        const PvmiMediaXferHeader& data_header_info,
        OsclAny* context)
//for PvmiMediaTransfer
{
    OSCL_UNUSED_ARG(data_header_info);
    OSCL_UNUSED_ARG(context);

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVMediaOutputNodePort::writeAsync: format_type=%d, format_index=%d, data=0x%x, data_len=%d",
                     format_type, format_index, data, data_len));

    PVMFAsyncEvent* event = NULL;
    switch (format_type)
    {
        case PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION:
            switch (format_index)
            {
                case PVMI_MEDIAXFER_FMT_INDEX_INFO_EVENT:
                    OSCL_ASSERT(iNode);
                    OSCL_ASSERT(iMediaTransfer);

                    if (!data)
                    {
                        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                        (0, "PVMediaOutputNodePort::writeAsync: Error - data is NULL"));
                        OSCL_LEAVE(OsclErrArgument);
                        return -1;
                    }

                    if (data_len != sizeof(PVMFAsyncEvent))
                    {
                        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                        (0, "PVMediaOutputNodePort::writeAsync: Error - data length is not size of PVMFAsyncEvent"));
                        OSCL_LEAVE(OsclErrArgument);
                        return -1;
                    }

                    event = OSCL_STATIC_CAST(PVMFAsyncEvent*, data);
                    iNode->ReportInfoEvent((*event));

                    // Not really processing this asynchronously. Just call writeComplete
                    // synchronously
                    iMediaTransfer->writeComplete(PVMFSuccess, iMioInfoErrorCmdId, context);
                    return iMioInfoErrorCmdId++;

                case PVMI_MEDIAXFER_FMT_INDEX_ERROR_EVENT:
                    OSCL_ASSERT(iNode);
                    OSCL_ASSERT(iMediaTransfer);

                    if (!data)
                    {
                        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                        (0, "PVMediaOutputNodePort::writeAsync: Error - data is NULL"));
                        OSCL_LEAVE(OsclErrArgument);
                        return -1;
                    }

                    if (data_len != sizeof(PVMFAsyncEvent))
                    {
                        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                        (0, "PVMediaOutputNodePort::writeAsync: Error - data length is not size of PVMFAsyncEvent"));
                        OSCL_LEAVE(OsclErrArgument);
                        return -1;
                    }

                    event = OSCL_STATIC_CAST(PVMFAsyncEvent*, data);
                    iNode->ReportErrorEvent((*event));

                    // Not really processing this asynchronously. Just call writeComplete
                    // synchronously
                    iMediaTransfer->writeComplete(PVMFSuccess, iMioInfoErrorCmdId, context);
                    return iMioInfoErrorCmdId++;

                default:
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                    (0, "PVMediaOutputNodePort::writeAsync: Error - Unsupported format_index"));
                    OSCL_LEAVE(OsclErrNotSupported);
                    break;
            }

        default:
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "PVMediaOutputNodePort::writeAsync: Error - Unsupported format_type"));
            OSCL_LEAVE(OsclErrNotSupported);
            break;
    }

    return -1;
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::writeComplete(PVMFStatus status, PVMFCommandId aCmdId, OsclAny* aContext)
//for PvmiMediaTransfer
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                    (0, "PVMediaOutputNodePort::writeComplete status %d cmdId %d context 0x%x", status, aCmdId, aContext));

    // Check if the writeComplete is in response to EOS message
    if (&iWriteAsyncEOSContext == (uint32*)aContext)
    {
        if (iWriteState == EWriteBusy)
        {
            // Synchronous completion
            // Let EndOfData info event be sent out in EndOfData() function
            iWriteState = EWriteOK;
        }
        else
        {
            // Report End of Data to the user of media output node
            // only if the EOS media transfer completes successfully and
            // there is no pending MIO control request
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::writeComplete For EOS - Fmt=%s",
                                     iSinkFormatString.get_str()));
            if (iEosStreamIDVec.size() != 0 && iClipIDVec.size() != 0)
            {
                //iEosStreamIDVec is used as a FIFO to store the steamids of eos sent to mio comp.
                //streamid is pushed in at front when call writeasync(eos) to mio comp.
                //streamid is poped out from end when mio comp. calls writecomplete(eos),
                //we report PVMFInfoEndOfData with the poped streamid.
                //This logic depends on Mio comp. process data(at least eos msg) in a sequencial style.
                uint32 EosStreamID = iEosStreamIDVec.back();
                uint32 clipID = iClipIDVec.back();

                // Asynchronous completion
                if (status == PVMFSuccess)
                {
                    // Report EndofData to engine only if MIO comp sends success for End Of Stream.
                    // FOr other return codes just pop out the EOS from the vector.
                    uint8 localbuffer[8];
                    oscl_memcpy(localbuffer, &EosStreamID, sizeof(uint32));
                    oscl_memcpy(&localbuffer[4], &clipID, sizeof(uint32));
                    iNode->ReportInfoEvent(PVMFInfoEndOfData, (OsclAny*)&localbuffer);
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::writeComplete PVMFInfoEndOfData sent with StreamID %d ClipID %d", EosStreamID, clipID));
                }
                else
                {
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::writeComplete EOS media transfer completed but PVMFInfoEndOfData not sent"));
                }

                if (iBosTransitionVec.size() != 0)
                {
                    BosTransitionVec btv = iBosTransitionVec.back();
                    SendStartOfDataEvent(btv.streamId, btv.clipId);
                    iBosTransitionVec.pop_back();
                }
                iEosStreamIDVec.pop_back();
                iClipIDVec.pop_back();
            }
            else
            {
                PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::writeComplete - Invalid iEosStreamIDVec size=0"));
                OSCL_ASSERT(false);
            }

        }
    }
    // Check if the writeComplete is in response to Reconfig message
    else if (&iWriteAsyncReConfigContext == (uint32*)aContext)
    {
        if (iWriteState == EWriteBusy)
        {
            // Synchronous completion
            iWriteState = EWriteOK;
        }
        else
        {
            // Asynchronous completion
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::writeComplete For RECONFIG - Fmt=%s",
                                     iSinkFormatString.get_str()));
        }
    }
    //detect cases where the current SendData call is completing synchronously.
    else if (iWriteState == EWriteBusy)
    {
        //synchronous completion
        iWriteState = EWriteOK;
    }
    else
    {
        //asynchronous completion.
        //do any memory cleanup
        bool oCmdIdFound = false;
        uint32 i;
        for (i = 0; i < iCleanupQueue.size(); i++)
        {
            if (iCleanupQueue[i].iCmdId == aCmdId)
            {
                PVMFSharedMediaDataPtr mediaData = iCleanupQueue[i].iData;
                iCleanupQueue.erase(&iCleanupQueue[i]);
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::writeComplete - Fmt=%s, Seq=%d, TS=%d, FIdx=%d, ClnUpQSize=%d",
                                         iSinkFormatString.get_str(),
                                         mediaData->getSeqNum(),
                                         mediaData->getTimestamp(),
                                         iFragIndex,
                                         iCleanupQueue.size()));
                oCmdIdFound = true;
                break;
            }
        }
        if (oCmdIdFound == false)
        {
            PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::writeComplete - CmdId Not Found - Fmt=%s, FIdx=%d, ClnUpQSize=%d",
                                  iSinkFormatString.get_str(),
                                  iFragIndex,
                                  iCleanupQueue.size()));
        }
    }
}

////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMediaOutputNodePort::readAsync(uint8* data, uint32 max_data_len, OsclAny* context,
        int32* formats, uint16 num_formats)
//for PvmiMediaTransfer
{
    OSCL_UNUSED_ARG(data);
    OSCL_UNUSED_ARG(max_data_len);
    OSCL_UNUSED_ARG(context);
    OSCL_UNUSED_ARG(formats);
    OSCL_UNUSED_ARG(num_formats);
    OSCL_LEAVE(OsclErrNotSupported);
    return -1;
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::readComplete(PVMFStatus status, PVMFCommandId read_cmd_id,
        int32 format_index, const PvmiMediaXferHeader& data_header_info,
        OsclAny* context)
//for PvmiMediaTransfer
{
    OSCL_UNUSED_ARG(status);
    OSCL_UNUSED_ARG(read_cmd_id);
    OSCL_UNUSED_ARG(format_index);
    OSCL_UNUSED_ARG(data_header_info);
    OSCL_UNUSED_ARG(context);
    OSCL_LEAVE(OsclErrNotSupported);
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::statusUpdate(uint32 status_flags)
//for PvmiMediaTransfer
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                    (0, "PVMediaOutputNodePort::statusUpdate flags %d", status_flags));

    if (status_flags & PVMI_MEDIAXFER_STATUS_WRITE)
    {
        //recover from a previous async write error.
        if (iWriteState == EWriteWait)
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::statusUpdate - WriteAsync Enabled - Fmt=%s, iWriteState=%d",
                                     iSinkFormatString.get_str(),
                                     iWriteState));
            iWriteState = EWriteOK;

            // Allow data to be processed if possible
            ProcessIncomingMessageIfPossible();

            if (oProcessIncomingMessage || iSendOneFrameAfterSkip)
            {
                if (iCurrentMediaMsg.GetRep() != NULL)
                {
                    //attempt to send data current media msg if any
                    SendData();
                }
                //reschedule if there is more stuff waiting and
                //if we can process more data
                if (IncomingMsgQueueSize() > 0)
                {
                    RunIfNotReady();
                }
            }
        }
    }
    else
    {
        //disable write
        iWriteState = EWriteWait;
        oProcessIncomingMessage = false;
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::statusUpdate - WriteAsync Disabled - Fmt=%s, iWriteState=%d",
                                 iSinkFormatString.get_str(),
                                 iWriteState));
    }
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::cancelCommand(PVMFCommandId command_id)
//for PvmiMediaTransfer
{
    OSCL_UNUSED_ARG(command_id);
    OSCL_LEAVE(OsclErrNotSupported);
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::cancelAllCommands()
//for PvmiMediaTransfer
{
    OSCL_LEAVE(OsclErrNotSupported);
}


////////////////////////////////////////////////////////////////////////////
bool PVMediaOutputNodePort::IsFormatSupported(PVMFFormatType aFmt)
// for PvmiCapabilityAndConfigPortFormatImpl interface
{
    //Verify if the format is supported by the media I/O component.
    PvmiKvp kvpFormatType;

    OSCL_StackString<64> iKVPFormatType = _STRLIT_CHAR(PVMF_FORMAT_TYPE_VALUE_KEY);

    kvpFormatType.key = NULL;
    kvpFormatType.key = iKVPFormatType.get_str();
    kvpFormatType.value.pChar_value = (char*)aFmt.getMIMEStrPtr();

    PVMFStatus status = iNode->iMIOConfig->verifyParametersSync(NULL, &kvpFormatType, 1);
    if (status != PVMFSuccess)
    {
        return false;
    }
    return true;
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::FormatUpdated()
// for PvmiCapabilityAndConfigPortFormatImpl interface
{
    //called when format was just set, either through capability and config,
    //for by node during port request.
    if (iSinkFormat.isAudio())
    {
        //set port name for datapath logging.
        SetName("MediaOutIn(Audio)");

        //pass the selected format to the MIO component.
        //ignore any failure since not all MIO may support the feature.
        SetMIOParameterPchar((char*)MOUT_AUDIO_FORMAT_KEY, iSinkFormatString.get_str());

        //save the media type.
        if (iSinkFormat.isCompressed())
        {
            iMediaType = PVMF_MEDIA_COMPRESSED_AUDIO;
        }
        else
        {
            iMediaType = PVMF_MEDIA_UNCOMPRESSED_AUDIO;
            isUnCompressedMIO = true;
        }
    }
    else if (iSinkFormat.isVideo())
    {
        //set port name for datapath logging.
        SetName("MediaOutIn(Video)");

        //pass the selected format to the MIO component.
        //ignore any failure since not all MIO may support the feature.
        SetMIOParameterPchar((char*)MOUT_VIDEO_FORMAT_KEY, iSinkFormatString.get_str());

        //save the media type.
        if (iSinkFormat.isCompressed())
        {
            iMediaType = PVMF_MEDIA_COMPRESSED_VIDEO;
        }
        else
        {
            iMediaType = PVMF_MEDIA_UNCOMPRESSED_VIDEO;
            isUnCompressedMIO = true;
        }
    }
    else if (iSinkFormat.isText())
    {
        //set port name for datapath logging.
        SetName("MediaOutIn");

        //pass the selected format to the MIO component.
        //ignore any failure since not all MIO may support the feature.
        SetMIOParameterPchar((char*)MOUT_TEXT_FORMAT_KEY, iSinkFormatString.get_str());

        //save the media type.
        iMediaType = PVMF_MEDIA_TEXT;
    }
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::SendData()
//send data to the MIO componenent.
{
    const int32 toleranceWndForCallback = 0;

    if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
    {
        if (oActiveMediaOutputComp)
        {
            SendEndOfData();
        }
        else if (iFrameStepMode == false)
        {
            uint32 delta = 0;
            PVMFMediaOutputNodePortMediaTimeStatus status = CheckMediaTimeStamp(delta);
            if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME || status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_LATE)
            {
                SendEndOfData();
            }
            else if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_EARLY)
            {

                OSCL_ASSERT(false == oClockCallBackPending);
                //stop processing input, since we are not done with the current media msg
                oProcessIncomingMessage = false;
                oClockCallBackPending = false;

                if (NULL != iClockNotificationsInf)
                {
                    PVMFStatus status =
                        iClockNotificationsInf->SetCallbackDeltaTime(delta, //delta time in clock when callBack should be called
                                toleranceWndForCallback,
                                this, //observer object to be called on timeout
                                false, //no threadLock
                                NULL, //no context
                                iDelayEarlyFrameCallBkId); //ID used to identify the timer for cancellation
                    if (PVMFSuccess != status)
                    {
                        //If delta specified for the callback is too large, then callback to the Mediaclock notification interface does not succeed
                        //Possible reasons for huge difference between the playback clock and the timestamp could be
                        //Sample is intended to be played after a very long time from now (as indicated by the data source(streaming server/file)).
                        //Timestamp is corrupted in the source node.
                        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendData - Could not set callback notification for send data %d delta %u", status, delta));
                        iNode->ReportErrorEvent(PVMFErrCorrupt, NULL, PVMFMoutNodeErr_Unexpected);
                        return;
                    }
                    else
                    {
                        oClockCallBackPending = true;
                    }
                }
                else
                {
                    PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendData - Fmt=%s, No callback notification Intf ",
                                          iSinkFormatString.get_str()));
                    OSCL_ASSERT(false);
                }
            }
        }
        else if (iFrameStepMode == true)
        {
            PVMFMediaOutputNodePortMediaTimeStatus status = CheckMediaFrameStep();
            if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME || status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_LATE)
            {
                SendEndOfData();
            }
            else if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_EARLY)
            {
                //stop processing input, since we are not done with the current media msg
                oProcessIncomingMessage = false;
                //wait on ClockCountUpdated call back from oscl clock
            }
        }
    }
    else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_RE_CONFIG_FORMAT_ID)
    {
        SendReConfigNotification();
    }
    else if (iCurrentMediaMsg->getFormatID() < PVMF_MEDIA_CMD_FORMAT_IDS_START)
    {
        if (oActiveMediaOutputComp)
        {
            SendMediaData();
        }
        else if (iFrameStepMode == false)
        {
            uint32 delta = 0;
            PVMFMediaOutputNodePortMediaTimeStatus status = CheckMediaTimeStamp(delta);
            if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME)
            {
                SendMediaData();
            }
            else if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_LATE)
            {
                iCurrentMediaMsg.Unbind();
                iFragIndex = 0;
            }
            else if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_EARLY)
            {

                OSCL_ASSERT(false == oClockCallBackPending);
                //stop processing input, since we are not done with the current media msg
                oProcessIncomingMessage = false;
                oClockCallBackPending = false;

                if (NULL != iClockNotificationsInf)
                {
                    PVMFStatus status =
                        iClockNotificationsInf->SetCallbackDeltaTime(delta, //delta time in clock when callBack should be called
                                toleranceWndForCallback,
                                this, //observer object to be called on timeout
                                false, //no threadLock
                                NULL, //no context
                                iDelayEarlyFrameCallBkId); //ID used to identify the timer for cancellation
                    if (PVMFSuccess != status)
                    {
                        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendData - Could not set callback notification for early frame %d delta %u", status, delta));
                        iNode->ReportErrorEvent(PVMFErrCorrupt, NULL, PVMFMoutNodeErr_Unexpected);
                        return;
                    }
                    else
                    {
                        oClockCallBackPending = true;
                    }
                }
                else
                {
                    PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendData - Fmt=%s, No callback notification Intf ",
                                          iSinkFormatString.get_str()));
                    OSCL_ASSERT(false);
                }
            }
        }
        else if (iFrameStepMode == true)
        {
            PVMFMediaOutputNodePortMediaTimeStatus status = CheckMediaFrameStep();
            if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME)
            {
                SendMediaData();
            }
            else if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_LATE)
            {
                iCurrentMediaMsg.Unbind();
                iFragIndex = 0;
            }
            else if (status == PVMF_MEDIAOUTPUTNODEPORT_MEDIA_EARLY)
            {
                //stop processing input, since we are not done with the current media msg
                oProcessIncomingMessage = false;
                //wait on ClockCountUpdated call back from oscl clock
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::SendMediaData()
//send media data to the MIO componenent.
{
    if (iWriteState != EWriteOK)
    {
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendMediaData - Fmt=%s - Invalid WriteAsync State - State=%d",
                                 iSinkFormatString.get_str(),
                                 iWriteState));
        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendMediaData - Fmt=%s - Invalid WriteAsync State - State=%d",
                              iSinkFormatString.get_str(),
                              iWriteState));
        OSCL_ASSERT(false);
    }

    PVMFSharedMediaDataPtr mediaData;
    convertToPVMFMediaData(mediaData, iCurrentMediaMsg);

    uint32 duration = 0;
    if (mediaData->getMarkerInfo() & PVMF_MEDIA_DATA_MARKER_INFO_DURATION_AVAILABLE_BIT)
    {
        duration = mediaData->getDuration();
    }

    // extract private data (if it is in fsi)
    OsclAny *privatedataptr = NULL;
    uint32 privatedatalength = 0;

    OsclRefCounterMemFrag fsifrag;
    mediaData->getFormatSpecificInfo(fsifrag);

    uint32 *data = (uint32 *)fsifrag.getMemFragPtr();
    uint32 data_len = fsifrag.getMemFragSize();
    if (data && data_len > 0)
    {
        //data to extract
        privatedataptr = (OsclAny*)(*data);
        privatedatalength = data_len;
    }

    if (mediaData->getNumFragments() == 0)
    {
        iCurrentMediaMsg.Unbind();
        iFragIndex = 0;
        return;
    }
    for (uint32 fragindex = iFragIndex; fragindex < mediaData->getNumFragments();)
    {
        OsclRefCounterMemFrag frag;
        mediaData->getMediaFragment(fragindex, frag);

        iWriteState = EWriteBusy;
        int32 err = OsclErrNone;
        int32 cmdId = 0;

        uint32 flags = PVMI_MEDIAXFER_MEDIA_DATA_FLAG_NONE;

        // The marker bit should only be set for the final frag to allow MIO to reassemble data properly
        if ((mediaData->getMarkerInfo() & PVMF_MEDIA_DATA_MARKER_INFO_M_BIT) &&
                (fragindex == (mediaData->getNumFragments() - 1)))
        {
            flags |= PVMI_MEDIAXFER_MEDIA_DATA_FLAG_MARKER_BIT;
        }
        if (mediaData->getMarkerInfo() & PVMF_MEDIA_DATA_MARKER_INFO_NO_RENDER_BIT)
        {
            flags |= PVMI_MEDIAXFER_MEDIA_DATA_FLAG_NO_RENDER_BIT;
        }

        if (mediaData->getMarkerInfo() & PVMF_MEDIA_DATA_MARKER_INFO_END_OF_NAL_BIT)
        {
            flags |= PVMI_MEDIAXFER_MEDIA_DATA_FLAG_END_OF_NAL_BIT;
        }

        PvmiMediaXferHeader mediaxferhdr;
        mediaxferhdr.seq_num = mediaData->getSeqNum();
        mediaxferhdr.timestamp = mediaData->getTimestamp();
        mediaxferhdr.duration = duration;
        mediaxferhdr.flags = flags;
        mediaxferhdr.stream_id = mediaData->getStreamID();
        mediaxferhdr.private_data_length = privatedatalength;
        mediaxferhdr.private_data_ptr = privatedataptr;
        err = WriteDataToMIO(cmdId, mediaxferhdr, frag);

        if (err != OsclErrNone)
        {
            // A Busy or an InvalidState leave can occur in a writeAsync call. If a leave occurs in writeasync call,
            // suspend data transfer.
            // A Busy leave is not an error and is a normal flow control mechanism.
            // Some MIO components can do a InvalidState leave if writeasyncs are called before calling Start on MIOs.
            // Data transfer to MIOs will resume when
            // 1) In case of Busy Leave: A statusUpdate call from the MIO component tells us to resume.
            // 2) In case of InvalidState Leave: A statusUpdate call from the MIO component tells us to resume. OR
            //                                   MIO sends the command complete for Start.
            iWriteState = EWriteWait;

            //stop processing input, since we are not done with the current media msg
            oProcessIncomingMessage = false;

            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendMediaData - WriteAsyncLeave - Fmt=%s, LeaveCode=%d",
                                     iSinkFormatString.get_str(),
                                     err));

            return ;//wait on statusUpdate call or start complete from the MIO component.
        }
        else
        {
            fragindex++;
            iFragIndex++;
            if (fragindex == mediaData->getNumFragments())
            {
                //all fragments have been sent.  see whether completion
                //is synchronous or asynchronous.
                if (iWriteState == EWriteBusy)
                {
                    //asynchronous completion.
                    //push the data onto the cleanup stack so it won't get cleaned
                    //up until the component consumes it.
                    iCleanupQueue.push_back(CleanupQueueElement(mediaData, cmdId));
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendMediaData - AsyncWrite - Fmt=%s, Seq=%d, TS=%d, Dur=%d, FIdx=%d, ClnUpQSize=%d",
                                             iSinkFormatString.get_str(),
                                             mediaData->getSeqNum(),
                                             mediaData->getTimestamp(),
                                             duration,
                                             iFragIndex,
                                             iCleanupQueue.size()));
                }
                //else the write already completed synchronously.
                else
                {
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendMediaData - SyncWrite - Fmt=%s, Seq=%d, TS=%d, Dur=%d, FIdx=%d, ClnUpQSize=%d",
                                             iSinkFormatString.get_str(),
                                             mediaData->getSeqNum(),
                                             mediaData->getTimestamp(),
                                             duration,
                                             iFragIndex,
                                             iCleanupQueue.size()));
                }
                //we are done with current media msg - either we are truly done (sync write complete) or we are
                //waiting on async write complete in which case media msg has been pushed into the cleanup queue
                iCurrentMediaMsg.Unbind();
                iFragIndex = 0;
                iSendOneFrameAfterSkip = false;
            }
            iWriteState = EWriteOK;
        }
    }

    return;
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::SendEndOfData()
//send end of data notice to the MIO componenent.
{
    if (iWriteState != EWriteOK)
    {
        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendEndOfData - Invalid WriteAsync State - State=%d", iWriteState));
        OSCL_ASSERT(false);
    }

    uint32 eosseqnum = iCurrentMediaMsg->getSeqNum();
    uint32 eostimestamp = iCurrentMediaMsg->getTimestamp();
    uint32 clipId = iCurrentMediaMsg->getClipID();

    iWriteState = EWriteBusy;

    int32 err;
    int32 cmdId = 0;

    PvmiMediaXferHeader mediaxferhdr;
    mediaxferhdr.seq_num = eosseqnum;
    mediaxferhdr.timestamp = eostimestamp;
    mediaxferhdr.duration = 0;
    mediaxferhdr.flags = PVMI_MEDIAXFER_MEDIA_DATA_FLAG_NONE;
    mediaxferhdr.stream_id = iCurrentMediaMsg->getStreamID();
    uint32 EosStreamId = mediaxferhdr.stream_id;

    //iEosStreamIDVec is used as a FIFO to store the steamids of eos sent to mio comp.
    //streamid is pushed in at front when call writeasync(eos) to mio comp.
    //streamid is poped out from end when mio comp. calls writecomplete(eos),
    //we report PVMFInfoEndOfData with the poped streamid.
    //This logic depends on Mio comp. process data(at least eos msg) in a sequencial style.
    iEosStreamIDVec.push_front(EosStreamId);
    iClipIDVec.push_front(clipId);

    OSCL_TRY(err,
             cmdId = iMediaTransfer->writeAsync(PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION,  /*format_type*/
                                                PVMI_MEDIAXFER_FMT_INDEX_END_OF_STREAM, /*format_index*/
                                                NULL,
                                                0,
                                                mediaxferhdr,
                                                (OsclAny*) & iWriteAsyncEOSContext);
            );

    if (err != OsclErrNone)
    {
        // A Busy or an InvalidState leave can occur in a writeAsync call. If a leave occurs in writeasync call,
        // suspend data transfer.
        // A Busy leave is not an error and is a normal flow control mechanism.
        // Some MIO components can do a InvalidState leave if writeasyncs are called before calling Start on MIOs.
        // Data transfer to MIOs will resume when
        // 1) In case of Busy Leave: A statusUpdate call from the MIO component tells us to resume.
        // 2) In case of InvalidState Leave: A statusUpdate call from the MIO component tells us to resume. OR
        //                                   MIO sends the command complete for Start.
        iWriteState = EWriteWait;

        //stop processing input, since we are not done with the current media msg
        oProcessIncomingMessage = false;

        // StreamID popped from vector when leave occurs in the writeAsync call
        if (iEosStreamIDVec.size() != 0 && iClipIDVec.size() != 0)
        {
            iEosStreamIDVec.erase(iEosStreamIDVec.begin());
            iClipIDVec.erase(iClipIDVec.begin());
        }
        else
        {
            PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendEndOfData - Invalid iEosStreamIDVec size=0"));
            OSCL_ASSERT(false);
        }

        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendEndOfData - WriteAsyncLeave - Fmt=%s, LeaveCode=%d",
                                 iSinkFormatString.get_str(),
                                 err));

        return ;//wait on statusUpdate call or start complete from the MIO component.
    }
    else
    {
        if (iWriteState == EWriteBusy)
        {
            //asynchronous completion.
            //wait for writeComplete to be called for this request
            //log media data info.
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendEndOfData - AsyncWrite - Fmt=%s, Seq=%d, TS=%d, ClnUpQSize=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iCleanupQueue.size()));

            iWriteState = EWriteOK;

        }
        //else the write already completed synchronously.
        else
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendEndOfData - SyncWrite - Fmt=%s, Seq=%d, TS=%d, ClnUpQSize=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iCleanupQueue.size()));

            // Report End of Data to the user of media output node
            if (iEosStreamIDVec.size() != 0 && iClipIDVec.size() != 0)
            {
                //iEosStreamIDVec is used as a FIFO to store the steamids of eos sent to mio comp.
                //streamid is pushed in at front when call writeasync(eos) to mio comp.
                //streamid is poped out from end when mio comp. calls writecomplete(eos),
                //we report PVMFInfoEndOfData with the poped streamid.
                //This logic depends on Mio comp. process data(at least eos msg) in a sequencial style.
                uint32 EosStreamID = iEosStreamIDVec.back();
                uint32 msgClipId = iClipIDVec.back();
                uint8 localbuffer[8];
                oscl_memcpy(localbuffer, &EosStreamID, sizeof(uint32));
                oscl_memcpy(&localbuffer[4], &msgClipId, sizeof(uint32));
                iNode->ReportInfoEvent(PVMFInfoEndOfData, (OsclAny*)&localbuffer);
                iEosStreamIDVec.pop_back();
                iClipIDVec.pop_back();
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendEndOfData Send PVMFInfoEndOfData with StreamID=%d, ClipID=%d", EosStreamID, msgClipId));
            }
            else
            {
                PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendEndOfData - Invalid iEosStreamIDVec size=0"));
                OSCL_ASSERT(false);
            }
        }
        //we are done with current media msg - either we are truly done (sync write complete) or we are
        //waiting on async write complete. there is no need to push this msg into cleanup queue since it
        //is a notification as opossed to media data
        iCurrentMediaMsg.Unbind();
        iFragIndex = 0;
    }
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::SendReConfigNotification()
//send reconfig notice to the MIO componenent.
{
    if (iWriteState != EWriteOK)
    {
        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendReConfigNotification - Invalid WriteAsync State - State=%d", iWriteState));
        OSCL_ASSERT(false);
    }

    iWriteState = EWriteBusy;
    int32 err;
    int32 cmdId = 0;
    PvmiMediaXferHeader mediaxferhdr;
    mediaxferhdr.seq_num = iCurrentMediaMsg->getSeqNum();
    mediaxferhdr.timestamp = iCurrentMediaMsg->getTimestamp();
    mediaxferhdr.duration = 0;
    mediaxferhdr.flags = PVMI_MEDIAXFER_MEDIA_DATA_FLAG_NONE;
    mediaxferhdr.stream_id = iCurrentMediaMsg->getStreamID();

    OsclRefCounterMemFrag aFormatSpecificInfo;
    iCurrentMediaMsg->getFormatSpecificInfo(aFormatSpecificInfo);

    OSCL_TRY(err,
             cmdId = iMediaTransfer->writeAsync(PVMI_MEDIAXFER_FMT_TYPE_NOTIFICATION,  /*format_type*/
                                                PVMI_MEDIAXFER_FMT_INDEX_RE_CONFIG_NOTIFICATION, /*format_index*/
                                                (uint8 *)aFormatSpecificInfo.getMemFragPtr(), aFormatSpecificInfo.getMemFragSize(),/*ReConfig info*/
                                                mediaxferhdr,
                                                (OsclAny*) & iWriteAsyncReConfigContext);
            );

    if (err != OsclErrNone)
    {
        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::SendReConfigNotification SendReConfigNotification leave code %d", err));
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendReConfigNotification SendReConfigNotification leave code %d", err));
        //we don't handle a leave here-- it's an error
        //media output comps should be open to recving reconfig anytime
        //they are free to process it at a later point in time and they may
        //choose to accept the reconfig and reject it. but there should never
        //be a case where cannot even queue the reconfig request
        iNode->ReportErrorEvent(PVMFErrResource, NULL, PVMFMoutNodeErr_WriteAsync);
    }
    else
    {
        if (iWriteState == EWriteBusy)
        {
            //asynchronous completion.
            //wait for writeComplete to be called for this request
            //log media data info.
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendReConfigNotification - AsyncWrite - Fmt=%s, Seq=%d, TS=%d, ClnUpQSize=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iCleanupQueue.size()));

            iWriteState = EWriteOK;

        }
        //else the write already completed synchronously.
        else
        {
            //log media data info.
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendReConfigNotification - SyncWrite - Fmt=%s, Seq=%d, TS=%d, ClnUpQSize=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iCleanupQueue.size()));
        }
        //we are done with current media msg - either we are truly done (sync write complete) or we are
        //waiting on async write complete. there is no need to push this msg into cleanup queue since it
        //is a notification as opossed to media data
        iCurrentMediaMsg.Unbind();
        iFragIndex = 0;
    }
}

////////////////////////////////////////////////////////////////////////////
PVMFMediaOutputNodePortMediaTimeStatus
PVMediaOutputNodePort::CheckMediaTimeStamp(uint32& aDelta)
{
    PVMFTimestamp aTimeStamp = iCurrentMediaMsg->getTimestamp();
    aDelta = 0;
    //aTimeStamp is assumed to be in milliseconds
    if (iClock != NULL)
    {
        uint32 clock_msec32;
        bool overflowFlag = false;

        iClock->GetCurrentTime32(clock_msec32, overflowFlag, PVMF_MEDIA_CLOCK_MSEC);

        uint32 duration = iCurrentMediaMsg->getDuration();

        uint32 clock_adjforearlymargin = clock_msec32 + iEarlyMargin;
        uint32 ts_adjforlatemargin = aTimeStamp + iLateMargin + duration;

        if (iSendOneFrameAfterSkip)
        {
            PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - Fmt=%s, First frame after repositioning",
                                  iSinkFormatString.get_str()));
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - OnTime - Fmt=%s, Seq=%d, Ts=%d, Clock=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     aTimeStamp,
                                     clock_msec32));
            iConsecutiveFramesDropped = 0;
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME;
        }
        else if ((clock_adjforearlymargin - aTimeStamp) > WRAP_THRESHOLD)
        {
            // This condition indicates that the timestamp is ahead of the adjusted playback clock
            // Note that since the computation is based on 32-bit values, it has a limitation that
            // it will not work for durations exceeding 2^32 milliseconds = 49+ days which is an acceptable
            // limit for this application.
            // Say clock is 1000ms, early margin is 200ms, then any timestamp later than 1200ms should be
            // held back. If ts is greater than 1200ms, then (1200 - ts) will be larger than wrap threshold
            uint32 deltaInMS = (aTimeStamp - clock_msec32);
            deltaInMS -= iEarlyMargin;
            if (iClockRate > 1)
            {
                //In case of rate change, calculate delta to acct
                //for clock rate
                MediaClockConverter mcc;
                mcc.set_timescale(iClockRate);
                //delta is in milliseconds
                mcc.set_clock_other_timescale(deltaInMS, 1000);
                //get the value in milliseconds
                aDelta = mcc.get_converted_ts(1000);
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - Early - Fmt=%s, Seq=%d, Ts=%d, Clock=%d, DeltaInMS=%d, ClkRate=%d, DeltaInClkUnits=%d",
                                         iSinkFormatString.get_str(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         aTimeStamp,
                                         clock_msec32,
                                         deltaInMS,
                                         iClockRate,
                                         aDelta));
            }
            else
            {
                aDelta = deltaInMS;
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - Early - Fmt=%s, Seq=%d, Ts=%d, Clock=%d, Delta=%d",
                                         iSinkFormatString.get_str(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         aTimeStamp,
                                         clock_msec32,
                                         aDelta));
            }
            iConsecutiveFramesDropped = 0;
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_EARLY;
        }
        else if ((ts_adjforlatemargin - clock_msec32) > WRAP_THRESHOLD)
        {
            // This condition indicates that the clock is ahead of the adjusted timestamp
            // Note that since the computation is based on 32-bit values, it has a limitation that
            // it will not work for durations exceeding 2^32 milliseconds = 49+ days which is an acceptable
            // limit for this application.
            // Say clock is 1000ms, late margin is 200ms, then any timestamp earlier than 800ms should be
            // dropped, as late. If ts is less than 800ms, then (ts + 200 - 1000) will be larger than wrap threshold
            aDelta = (clock_msec32 - aTimeStamp);
            iFramesDropped++;
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - Late - Fmt=%s, Seq=%d, Ts=%d, Clock=%d, Delta=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     aTimeStamp,
                                     clock_msec32,
                                     aDelta));
            iConsecutiveFramesDropped++;
            if (iMediaType == PVMF_MEDIA_UNCOMPRESSED_VIDEO)
            {
                if ((iConsecutiveFramesDropped >= MEDIAOUTPUTNODE_CONFIG_THRESHOLD_FOR_DROPPED_VIDEO_FRAMES) && (iLateFrameEventSent == false))
                {
                    iLateFrameEventSent = true;
                    iNode->ReportInfoEvent(PVMFInfoVideoTrackFallingBehind, (OsclAny*)NULL);
                }
            }
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_LATE;
        }
        else
        {
            //ts falls in the interval (clock-latermargin, clock+earlymargin)
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - OnTime - Fmt=%s, Seq=%d, Ts=%d, Clock=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     aTimeStamp,
                                     clock_msec32));
            iConsecutiveFramesDropped = 0;
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME;
        }
    }
    else
    {

        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - Fmt=%s, No PlayBack Clock!!!",
                              iSinkFormatString.get_str()));
        OSCL_ASSERT(false);
        return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ERROR;
    }
}

////////////////////////////////////////////////////////////////////////////
PVMFMediaOutputNodePortMediaTimeStatus
PVMediaOutputNodePort::CheckMediaFrameStep()
{
    if (iClock != NULL)
    {
        if (iClockFrameCount > iSyncFrameCount)
        {
            //this condition implies that clock count has
            //gone past the sync frame count, late frame, drop it
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaFrameStep - Late - Fmt=%s, Seq=%d, Ts=%d, iClockFrameCount=%d, iSyncFrameCount=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iClockFrameCount,
                                     iSyncFrameCount));
            iSyncFrameCount++;
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_LATE;
        }
        else if (iClockFrameCount < iSyncFrameCount)
        {
            //this condition implies that clock count is less than
            //the sync frame count, early frame, hold it
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaFrameStep - Early - Fmt=%s, Seq=%d, Ts=%d, iClockFrameCount=%d, iSyncFrameCount=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iClockFrameCount,
                                     iSyncFrameCount));
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_EARLY;
        }
        else
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CheckMediaFrameStep - OnTime - Fmt=%s, Seq=%d, Ts=%d, iClockFrameCount=%d, iSyncFrameCount=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getSeqNum(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iClockFrameCount,
                                     iSyncFrameCount));
            iSyncFrameCount++;
            return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ON_TIME;
        }
    }
    else
    {

        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::CheckMediaTimeStamp - Fmt=%s, No PlayBack Clock!!!",
                              iSinkFormatString.get_str()));
        OSCL_ASSERT(false);
        return PVMF_MEDIAOUTPUTNODEPORT_MEDIA_ERROR;
    }
}

////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::Run()
{
    /*
     * Media Output node inport does not process any more incoming messages
     * under following circumstances:
     * 1) Playback clock is NOT in RUNNING state
     * 2) Media output comp is in a STARTED state
     * 3) When waiting on media output comp to go ready from a busy state
     * Boolean "oProcessIncomingMessage" will be false if any of the above
     * conditions is true.
     * This while loop is intentional. This port doesnt do much in terms of
     * data processing. So we attempt to send as much data as we can in a
     * single Run call. Since we could process multiple media msgs per Run
     * we need to account for BOS and Skip as well.
     */
    while ((IncomingMsgQueueSize() > 0) || (iCurrentMediaMsg.GetRep() != NULL))
    {
        bool oMsgDqd = false;
        if (iCurrentMediaMsg.GetRep() == NULL)
        {
            //we are starting to process a new media msg
            iFragIndex = 0;
            if (DequeueIncomingMsg(iCurrentMediaMsg) != PVMFSuccess)
            {
                PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::Run: DequeueIncomingMsg Failed - Fmt=%s",
                                      iSinkFormatString.get_str()));
                OSCL_ASSERT(0);
            }
            else
            {
                //this boolean avoids duplicate logs in case we are skipping data
                oMsgDqd = true;
            }
        }
        if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_BOS_FORMAT_ID)
        {
            uint32 msgStreamId = iCurrentMediaMsg->getStreamID();
            iBOSStreamIDVec.push_back(msgStreamId);
            iRecentClipID = iCurrentMediaMsg->getClipID();

            PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::Run: BOS Recvd - Fmt=%s, TS=%d, ClipID=%d, StreamID=%d, Qs=%d",
                                  iSinkFormatString.get_str(),
                                  iCurrentMediaMsg->getTimestamp(),
                                  iRecentClipID,
                                  msgStreamId,
                                  IncomingMsgQueueSize()));

            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run: BOS Recvd - Fmt=%s, TS=%d, ClipID=%d, StreamID=%d, Qs=%d",
                                     iSinkFormatString.get_str(),
                                     iCurrentMediaMsg->getTimestamp(),
                                     iRecentClipID,
                                     msgStreamId,
                                     IncomingMsgQueueSize()));

            if (iSendStartOfDataEvent != true)
            {
                // This means that the BOS is not part of repositioning,
                // but instead is from clip transition. So, send the
                // PVMFInfoStartOfData right away, or wait for any pending
                // EOS.
                if (iEosStreamIDVec.size() == 0)
                {
                    SendStartOfDataEvent(msgStreamId, iRecentClipID);
                }
                else
                {
                    BosTransitionVec btv;
                    btv.streamId = msgStreamId;
                    btv.clipId = iRecentClipID;
                    iBosTransitionVec.push_front(btv);
                }
            }

            iNode->ReportBOS();
            iCurrentMediaMsg.Unbind();
            iFragIndex = 0;
        }
        else if (DataToSkip(iCurrentMediaMsg) == true)
        {
            //this condition implies that a skip timestamp has been set
            //and we are looking for a mediamsg whole timestamp is equal or greater than
            //iSkipTimestamp. All media msgs whose timestamps are less than iSkipTimestamp
            //will be dropped. There is no need for any additional checks. Just dequeue and toss
            if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
            {
                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::Run: EOSSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                      iCurrentMediaMsg->getStreamID(),
                                      iCurrentMediaMsg->getClipID(),
                                      iCurrentMediaMsg->getSeqNum(),
                                      iCurrentMediaMsg->getTimestamp(),
                                      iSinkFormatString.get_str()));

                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run: EOSSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                         iCurrentMediaMsg->getStreamID(),
                                         iCurrentMediaMsg->getClipID(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         iCurrentMediaMsg->getTimestamp(),
                                         iSinkFormatString.get_str()));
            }
            else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOC_FORMAT_ID)
            {
                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::Run: EOCSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                      iCurrentMediaMsg->getStreamID(),
                                      iCurrentMediaMsg->getClipID(),
                                      iCurrentMediaMsg->getSeqNum(),
                                      iCurrentMediaMsg->getTimestamp(),
                                      iSinkFormatString.get_str()));

                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run: EOCSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                         iCurrentMediaMsg->getStreamID(),
                                         iCurrentMediaMsg->getClipID(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         iCurrentMediaMsg->getTimestamp(),
                                         iSinkFormatString.get_str()));
            }
            else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_BOC_FORMAT_ID)
            {
                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::Run: BOCSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                      iCurrentMediaMsg->getStreamID(),
                                      iCurrentMediaMsg->getClipID(),
                                      iCurrentMediaMsg->getSeqNum(),
                                      iCurrentMediaMsg->getTimestamp(),
                                      iSinkFormatString.get_str()));

                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run: BOCSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                         iCurrentMediaMsg->getStreamID(),
                                         iCurrentMediaMsg->getClipID(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         iCurrentMediaMsg->getTimestamp(),
                                         iSinkFormatString.get_str()));
            }
            else
            {
                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::Run: MsgSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                      iCurrentMediaMsg->getStreamID(),
                                      iCurrentMediaMsg->getClipID(),
                                      iCurrentMediaMsg->getSeqNum(),
                                      iCurrentMediaMsg->getTimestamp(),
                                      iSinkFormatString.get_str()));

                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run: MsgSkip - StreamId=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s",
                                         iCurrentMediaMsg->getStreamID(),
                                         iCurrentMediaMsg->getClipID(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         iCurrentMediaMsg->getTimestamp(),
                                         iSinkFormatString.get_str()));
            }

            iCurrentMediaMsg.Unbind();
            iFragIndex = 0;
        }
        else
        {
            //implies that this message needs to be consumed
            if (oMsgDqd == true)
            {
                //logging
                if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
                {
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run - EOS Recvd - StreamID=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                             iCurrentMediaMsg->getStreamID(),
                                             iCurrentMediaMsg->getClipID(),
                                             iCurrentMediaMsg->getSeqNum(),
                                             iCurrentMediaMsg->getTimestamp(),
                                             iSinkFormatString.get_str(),
                                             IncomingMsgQueueSize()));
                }
                else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOC_FORMAT_ID)
                {
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run - EOC Recvd - StreamID=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                             iCurrentMediaMsg->getStreamID(),
                                             iCurrentMediaMsg->getClipID(),
                                             iCurrentMediaMsg->getSeqNum(),
                                             iCurrentMediaMsg->getTimestamp(),
                                             iSinkFormatString.get_str(),
                                             IncomingMsgQueueSize()));
                }
                else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_BOC_FORMAT_ID)
                {
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run - BOC Recvd - StreamID=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                             iCurrentMediaMsg->getStreamID(),
                                             iCurrentMediaMsg->getClipID(),
                                             iCurrentMediaMsg->getSeqNum(),
                                             iCurrentMediaMsg->getTimestamp(),
                                             iSinkFormatString.get_str(),
                                             IncomingMsgQueueSize()));
                }
                else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_RE_CONFIG_FORMAT_ID)
                {
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run - RECONFIG Recvd - Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                             iCurrentMediaMsg->getSeqNum(),
                                             iCurrentMediaMsg->getTimestamp(),
                                             iSinkFormatString.get_str(),
                                             IncomingMsgQueueSize()));
                }
                else if (iCurrentMediaMsg->getFormatID() < PVMF_MEDIA_CMD_FORMAT_IDS_START)
                {
                    iTotalFrames++;
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::Run - MediaMsg Recvd - ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d, Dur=%d",
                                             iCurrentMediaMsg->getClipID(),
                                             iCurrentMediaMsg->getSeqNum(),
                                             iCurrentMediaMsg->getTimestamp(),
                                             iSinkFormatString.get_str(),
                                             IncomingMsgQueueSize(),
                                             iCurrentMediaMsg->getDuration()));
                }
            }

            if ((iSendStartOfDataEvent == true) && oMIOComponentConfigured && (iSendOneFrameAfterSkip == false))
            {
                // Implies that we are attempting to process the first media msg during
                // after skip, a mediamsg whose timestamp is equal to or greater than
                // iSkipTimestamp.
                // By checking against iSendOneFrameAfterSkip, we are making sure that for
                // video tracks, the PVMFInfoStartOfData event is sent "after" the first
                // frame is rendered.
                SendStartOfDataEvent(iCurrentMediaMsg->getStreamID(), iRecentClipID);
            }

            // We need to check for valid CurrentMediaMsg because it is possible that SendData has been
            // called from ClockStateUpdated when all tracks report InfoStartofData and Engine starts the clock
            // If dequeued data has already been sent, we cant send that data again, it will crash
            // so just return and AO will be scheduled again for next data.
            if ((oProcessIncomingMessage || (iSendOneFrameAfterSkip && (iWriteState == EWriteOK))) && (iCurrentMediaMsg.GetRep() != NULL))
            {
                SendData();
            }
            else
            {
                //implies that we cannot send out any more data
                //no point continuing, just return
                //Do not reschedule here. We need to wait for
                //oProcessIncomingMessage to be true again
                return;
            }
        }
    }
}


////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNodePort::HandlePortActivity(const PVMFPortActivity& aActivity)
//from PVMFPortActivityHandler.  this port handles its own activity.
{
    switch (aActivity.iType)
    {
        case PVMF_PORT_ACTIVITY_CREATED:
            iNode->ReportInfoEvent(PVMFInfoPortCreated, (OsclAny*)aActivity.iPort);
            break;

        case PVMF_PORT_ACTIVITY_DELETED:
            iNode->ReportInfoEvent(PVMFInfoPortDeleted, (OsclAny*)aActivity.iPort);
            break;

        case PVMF_PORT_ACTIVITY_CONNECT:
            iNode->ReportInfoEvent(PVMFInfoPortConnected, (OsclAny*)aActivity.iPort);
            break;

        case PVMF_PORT_ACTIVITY_DISCONNECT:
            iNode->ReportInfoEvent(PVMFInfoPortDisconnected, (OsclAny*)aActivity.iPort);
            break;

        case PVMF_PORT_ACTIVITY_INCOMING_MSG:
            if ((IncomingMsgQueueSize() > 0) && (iCurrentMediaMsg.GetRep() == NULL))
            {
                PVMFSharedMediaMsgPtr peekMsgPtr;
                bool oBos = false;
                bool oMIOAlreadyConfigured = false;
                oMIOAlreadyConfigured = oMIOComponentConfigured;
                if (peekHead(peekMsgPtr, oBos))
                {
                    /*
                     * Media Output node inport does not process any more incoming messages
                     * under following circumstances:
                     * 1) Playback clock is NOT in RUNNING state
                     * 2) Media output comp is in a STARTED state
                     * 3) When waiting on media output comp to go ready from a busy state
                     * Boolean "oProcessIncomingMessage" will be false if any of the above
                     * conditions is true.
                     * However BOS is an exception to all of these. BOS is not sent to media output comp,
                     * nor should we worry about playback clock state or media output comp state while
                     * processing BOS. When we get BOS we always send a notification to mediaoutput node
                     * via the BOSObserver with BOS stream-id.
                     */
                    if (oBos)
                    {
                        if (DequeueIncomingMsg(iCurrentMediaMsg) == PVMFSuccess)
                        {
                            uint32 msgStreamId = peekMsgPtr->getStreamID();
                            iBOSStreamIDVec.push_back(msgStreamId);
                            iRecentClipID = peekMsgPtr->getClipID();

                            PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::HPA: BOS Recvd - Fmt=%s, TS=%d, StreamID=%d, ClipID=%d, Qs=%d",
                                                  iSinkFormatString.get_str(),
                                                  peekMsgPtr->getTimestamp(),
                                                  msgStreamId,
                                                  iRecentClipID,
                                                  IncomingMsgQueueSize()));

                            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: BOS Recvd - Fmt=%s, TS=%d, StreamID=%d, ClipID=%d, Qs=%d",
                                                     iSinkFormatString.get_str(),
                                                     peekMsgPtr->getTimestamp(),
                                                     msgStreamId,
                                                     iRecentClipID,
                                                     IncomingMsgQueueSize()));

                            if (iSendStartOfDataEvent != true)
                            {
                                // This means that the BOS is not part of repositioning,
                                // but instead is from clip transition. So, send the
                                // PVMFInfoStartOfData right away, or wait for any pending
                                // EOS.
                                if (iEosStreamIDVec.size() == 0)
                                {
                                    SendStartOfDataEvent(msgStreamId, iRecentClipID);
                                }
                                else
                                {
                                    BosTransitionVec btv;
                                    btv.streamId = msgStreamId;
                                    btv.clipId = iRecentClipID;
                                    iBosTransitionVec.push_front(btv);
                                }
                            }

                            iNode->ReportBOS();
                            iCurrentMediaMsg.Unbind();
                            iFragIndex = 0;
                        }
                        else
                        {
                            PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::HPA: DequeueIncomingMsg Failed - Fmt=%s",
                                                  iSinkFormatString.get_str()));
                            OSCL_ASSERT(false);
                        }
                    }
                    else if (DataToSkip(peekMsgPtr) == true)
                    {
                        //this condition implies that a skip timestamp has been set
                        //and we are looking for a mediamsg whole timestamp is equal or greater than
                        //iSkipTimestamp. All media msgs whose timestamps are less than iSkipTimestamp
                        //will be dropped. There is no need for any additional checks. Just dequeue and toss
                        iCurrentMediaMsg.Unbind();
                        iFragIndex = 0;
                        if (DequeueIncomingMsg(iCurrentMediaMsg) == PVMFSuccess)
                        {
                            if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
                            {
                                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::HPA: EOSSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                                      iCurrentMediaMsg->getStreamID(),
                                                      iCurrentMediaMsg->getSeqNum(),
                                                      iCurrentMediaMsg->getTimestamp(),
                                                      iSinkFormatString.get_str()));

                                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: EOSSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                                         iCurrentMediaMsg->getStreamID(),
                                                         iCurrentMediaMsg->getSeqNum(),
                                                         iCurrentMediaMsg->getTimestamp(),
                                                         iSinkFormatString.get_str()));
                            }
                            else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOC_FORMAT_ID)
                            {
                                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::HPA: EOCSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                                      iCurrentMediaMsg->getStreamID(),
                                                      iCurrentMediaMsg->getSeqNum(),
                                                      iCurrentMediaMsg->getTimestamp(),
                                                      iSinkFormatString.get_str()));

                                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: EOCSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                                         iCurrentMediaMsg->getStreamID(),
                                                         iCurrentMediaMsg->getSeqNum(),
                                                         iCurrentMediaMsg->getTimestamp(),
                                                         iSinkFormatString.get_str()));
                            }
                            else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_BOC_FORMAT_ID)
                            {
                                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::HPA: BOCSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                                      iCurrentMediaMsg->getStreamID(),
                                                      iCurrentMediaMsg->getSeqNum(),
                                                      iCurrentMediaMsg->getTimestamp(),
                                                      iSinkFormatString.get_str()));

                                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: BOCSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                                         iCurrentMediaMsg->getStreamID(),
                                                         iCurrentMediaMsg->getSeqNum(),
                                                         iCurrentMediaMsg->getTimestamp(),
                                                         iSinkFormatString.get_str()));
                            }
                            else
                            {
                                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::HPA: MsgSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                      iCurrentMediaMsg->getStreamID(),
                                                      iCurrentMediaMsg->getSeqNum(),
                                                      iCurrentMediaMsg->getTimestamp(),
                                                      iSinkFormatString.get_str(),
                                                      IncomingMsgQueueSize()));

                                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: MsgSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                         iCurrentMediaMsg->getStreamID(),
                                                         iCurrentMediaMsg->getSeqNum(),
                                                         iCurrentMediaMsg->getTimestamp(),
                                                         iSinkFormatString.get_str(),
                                                         IncomingMsgQueueSize()));
                            }
                            iCurrentMediaMsg.Unbind();
                            iFragIndex = 0;
                        }
                        else
                        {
                            PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::HPA: DequeueIncomingMsg Failed - Fmt=%s",
                                                  iSinkFormatString.get_str()));
                            OSCL_ASSERT(false);
                        }
                    }
                    else
                    {
                        if ((iSendStartOfDataEvent == true) && oMIOComponentConfigured)
                        {
                            //implies that we are attempting to process the first media msg during
                            //after skip, a mediamsg whose timestamp is equal to or greater than
                            //iSkipTimestamp
                            if (iSendOneFrameAfterSkip == false)
                            {
                                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::HPA: PVMFInfoStartOfData - Fmt=%s",
                                                      iSinkFormatString.get_str()));
                                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: PVMFInfoStartOfData - Fmt=%s",
                                                         iSinkFormatString.get_str()));

                                SendStartOfDataEvent(iRecentStreamID, iRecentClipID);
                            }

                            // Now check if the media msg is EOS notification OR media data.
                            // If EOS then need to do writeComplete for EOS without sending it downstream
                            // This will happen in cases where the media output node is waiting on configuration,
                            // but for some reason the upstream node does not have a config to send.
                            // (Think of cases where we have empty tracks in a presentation OR reposition to EndofClip just after Prepare).
                            // In such cases, if we get EOS we want to notify the Engine with InfoEndofData for the track
                            // so that Engine does not wait for EndOfData from the track and be in a hang state.
                            if (peekMsgPtr->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
                            {
                                // There could be cases where MIO was already configured and then a reposition to End cmd
                                // was given to Engine. In this case again EOS will be the first msg after skip but in this
                                // case we want to send EOS downstream rather than sending InfoEndofData from here.
                                if (!oMIOAlreadyConfigured)
                                {
                                    // EOS is the first media msg received after Skip
                                    if (iCurrentMediaMsg.GetRep() != NULL)
                                    {
                                        // This should never happen. We cannot have a media msg being processed in this condition.
                                        // Just assert and return
                                        OSCL_ASSERT(false);
                                        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: Processing previous msg iCurrentMediaMsg: 0x%x - Fmt=%s \
                                                                 when 1st media msg after skip is EOS, wrong, asserting",
                                                                 iCurrentMediaMsg.GetRep(), iSinkFormatString.get_str()));
                                        return;
                                    }
                                    // Dequeue the Incoming msg and send EndofData
                                    if (DequeueIncomingMsg(iCurrentMediaMsg) == PVMFSuccess)
                                    {
                                        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA - EOS Recvd - StreamID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                                 iCurrentMediaMsg->getStreamID(),
                                                                 iCurrentMediaMsg->getSeqNum(),
                                                                 iCurrentMediaMsg->getTimestamp(),
                                                                 iSinkFormatString.get_str(),
                                                                 IncomingMsgQueueSize()));

                                        if (iSendStartOfDataEvent == true)
                                        {
                                            // We need to send this PVMFInfoStartOfData event for a usecase
                                            // when a EOS immediately follows a BOS for a video track so that
                                            // the engine is not kept waiting for the StartOfData event.
                                            SendStartOfDataEvent(iRecentStreamID, iRecentClipID);
                                        }
                                        uint32 eosStreamId = iCurrentMediaMsg->getStreamID();
                                        uint32 msgClipId = iCurrentMediaMsg->getClipID();
                                        uint8 localbuffer[8];
                                        oscl_memcpy(localbuffer, &eosStreamId, sizeof(uint32));
                                        oscl_memcpy(&localbuffer[4], &msgClipId, sizeof(uint32));
                                        iNode->ReportInfoEvent(PVMFInfoEndOfData, (OsclAny*)&localbuffer);
                                        iCurrentMediaMsg.Unbind();
                                        iFragIndex = 0;
                                    }
                                    else
                                    {
                                        PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::HPA: DequeueIncomingMsg Failed - Fmt=%s",
                                                              iSinkFormatString.get_str()));
                                        OSCL_ASSERT(false);
                                    }
                                    return;
                                }
                            }
                        }
                        if (oProcessIncomingMessage || (iSendOneFrameAfterSkip && (iWriteState == EWriteOK)))
                        {
                            //we are starting to process a new media msg
                            if ((iCurrentMediaMsg.GetRep() != NULL) ||
                                    (iFragIndex != 0))
                            {
                                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA: DequeueIncomingMsg ignoring msg. Still processing previous msg iCurrentMediaMsg: 0x%x  iFragIndex: %d- Fmt=%s",
                                                         iCurrentMediaMsg.GetRep(), iFragIndex, iSinkFormatString.get_str()));
                                return;
                            }
                            if (DequeueIncomingMsg(iCurrentMediaMsg) == PVMFSuccess)
                            {
                                if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
                                {
                                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA - EOS Recvd - StreamID=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                             iCurrentMediaMsg->getStreamID(),
                                                             iCurrentMediaMsg->getClipID(),
                                                             iCurrentMediaMsg->getSeqNum(),
                                                             iCurrentMediaMsg->getTimestamp(),
                                                             iSinkFormatString.get_str(),
                                                             IncomingMsgQueueSize()));
                                }
                                else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOC_FORMAT_ID)
                                {
                                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA - EOC Recvd - StreamID=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                             iCurrentMediaMsg->getStreamID(),
                                                             iCurrentMediaMsg->getClipID(),
                                                             iCurrentMediaMsg->getSeqNum(),
                                                             iCurrentMediaMsg->getTimestamp(),
                                                             iSinkFormatString.get_str(),
                                                             IncomingMsgQueueSize()));
                                }
                                else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_BOC_FORMAT_ID)
                                {
                                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA - BOC Recvd - StreamID=%d, ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                             iCurrentMediaMsg->getStreamID(),
                                                             iCurrentMediaMsg->getClipID(),
                                                             iCurrentMediaMsg->getSeqNum(),
                                                             iCurrentMediaMsg->getTimestamp(),
                                                             iSinkFormatString.get_str(),
                                                             IncomingMsgQueueSize()));
                                }
                                else if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_RE_CONFIG_FORMAT_ID)
                                {
                                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA - RECONFIG Recvd - Seq=%d, TS=%d, Fmt=%s, Qs=%d",
                                                             iCurrentMediaMsg->getSeqNum(),
                                                             iCurrentMediaMsg->getTimestamp(),
                                                             iSinkFormatString.get_str(),
                                                             IncomingMsgQueueSize()));
                                }
                                else if (iCurrentMediaMsg->getFormatID() < PVMF_MEDIA_CMD_FORMAT_IDS_START)
                                {
                                    iTotalFrames++;
                                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::HPA - MediaMsg Recvd - ClipID=%d, Seq=%d, TS=%d, Fmt=%s, Qs=%d, Dur=%d",
                                                             iCurrentMediaMsg->getClipID(),
                                                             iCurrentMediaMsg->getSeqNum(),
                                                             iCurrentMediaMsg->getTimestamp(),
                                                             iSinkFormatString.get_str(),
                                                             IncomingMsgQueueSize(),
                                                             iCurrentMediaMsg->getDuration()));
                                }

                                SendData();



                                if ((oProcessIncomingMessage == true) &&
                                        (IncomingMsgQueueSize() > 0))
                                {
                                    //implies that we can process more data, so reschedule
                                    //we could process all the messages in the incoming msg q
                                    //in a while loop here, but that might not be advisable
                                    //since this call is sort of an observer callback and
                                    //it might not be good idea to process more than one msg
                                    RunIfNotReady();
                                }
                            }
                            else
                            {
                                PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::HPA: DequeueIncomingMsg Failed - Fmt=%s",
                                                      iSinkFormatString.get_str()));
                                OSCL_ASSERT(false);
                            }
                        }
                    }
                }
            }
            break;

        default:
            break;
    }
}


////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::ConfigMIO(PvmiKvp* aParameters, PvmiKvp* &aRetParameters)
//send config info to media I/O component.
{

    if (iMediaTransfer == NULL)
    {
        iMediaTransfer = iNode->iMIOControl->createMediaTransfer(iNode->iMIOSession);
        if (iMediaTransfer)
        {
            iMediaTransfer->setPeer(this);
        }
        else
        {
            return PVMFFailure;
        }
    }

    if (0 == aParameters)
    {
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO: No format-specific info for this track."));
        return PVMFSuccess;//no format-specific info, so nothing needed.
    }
    uint8* data = (uint8*)aParameters->value.key_specific_value;
    PVMFStatus status = PVMFFailure;

    //formatted data gets sent to the MIO on first arrival
    switch (iMediaType)
    {
        case PVMF_MEDIA_UNCOMPRESSED_AUDIO:
        {
            //Send pcm16 info.

            if (pv_mime_strcmp(aParameters->key, PVMF_FORMAT_SPECIFIC_INFO_KEY) == 0)
            {
                //ignore any failures since not all MIO may suppport the parameters.
                //Note: send the parameters individually since some MIO may not know
                //how to process arrays.
                channelSampleInfo* pcm16Info = (channelSampleInfo*)data;
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO - SamplingRate=%d, NumChannels=%d",
                                         pcm16Info->samplingRate, pcm16Info->desiredChannels));

                status = SetMIOParameterUint32((char*)MOUT_AUDIO_SAMPLING_RATE_KEY,
                                               pcm16Info->samplingRate);
                if (status == PVMFSuccess)
                {
                    SetMIOParameterUint32((char*)MOUT_AUDIO_NUM_CHANNELS_KEY,
                                          pcm16Info->desiredChannels);
                    if (status == PVMFSuccess)
                    {
                        SetMIOParameterUint32((char*)MOUT_AUDIO_BITS_PER_SAMPLE,
                                              pcm16Info->bitsPerSample);
                    }
                }
            }
            else if (pv_mime_strcmp(aParameters->key, PVMF_FORMAT_SPECIFIC_INFO_KEY_PCM) == 0)
            {
                // this is
                //Do not send individual parameters to MIO component, send them all at once

                int32 err;
                OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, aParameters, 1, aRetParameters););

                if (err != OsclErrNone || aRetParameters)
                {
                    PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY_PCM failed "));
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY_PCM failed "));

                    // TO DO: when all MIOs are updated to support this key - the error event needs to be sent out
                    // for now - to maintain bw compatibility - don't send the error event
                    //iNode->ReportErrorEvent(PVMFErrPortProcessing, NULL, PVMFMoutNodeErr_MediaIOSetParameterSync);
                    return PVMFFailure;
                }

                status = PVMFSuccess;
            }
        }
        break;

        case PVMF_MEDIA_UNCOMPRESSED_VIDEO:
        {
            //Send yuv info.
            if (pv_mime_strcmp(aParameters->key, PVMF_FORMAT_SPECIFIC_INFO_KEY) == 0)
            {
                PVMFYuvFormatSpecificInfo0* yuvInfo = (PVMFYuvFormatSpecificInfo0*)data;
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO - Width=%d, Height=%d, DispWidth=%d, DispHeight=%d",
                                         yuvInfo->buffer_width, yuvInfo->buffer_height, yuvInfo->viewable_width, yuvInfo->viewable_height));
                //Send yuv info.
                //ignore any failures since not all MIO may suppport the parameters.
                //Note: send the parameters individually since some MIO may not know
                //how to process arrays.
                status = SetMIOParameterUint32((char*)MOUT_VIDEO_WIDTH_KEY,
                                               yuvInfo->buffer_width);
                if (status == PVMFSuccess)
                {
                    status = SetMIOParameterUint32((char*)MOUT_VIDEO_HEIGHT_KEY,
                                                   yuvInfo->buffer_height);
                }
                if (status == PVMFSuccess)
                {
                    status = SetMIOParameterUint32((char*)MOUT_VIDEO_DISPLAY_WIDTH_KEY,
                                                   yuvInfo->viewable_width);
                }
                if (status == PVMFSuccess)
                {
                    status = SetMIOParameterUint32((char*)MOUT_VIDEO_DISPLAY_HEIGHT_KEY,
                                                   yuvInfo->viewable_height);
                }
                if (status == PVMFSuccess)
                {
                    // ignore status here
                    SetMIOParameterFormat((char*)MOUT_VIDEO_SUBFORMAT_KEY,
                                          yuvInfo->video_format);
                }


            }
            else if (pv_mime_strcmp(aParameters->key, PVMF_FORMAT_SPECIFIC_INFO_KEY_YUV) == 0)
            {

                //Do not send individual parameters to MIO component, send them all at once

                int32 err;
                OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, aParameters, 1, aRetParameters););

                if (err != OsclErrNone || aRetParameters)
                {
                    PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY_YUV failed "));
                    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY_YUV failed "));
                    // TO DO: when all MIOs are updated to support this key - the error event needs to be sent out
                    // for now - to maintain bw compatibility - don't send the error event
                    //iNode->ReportErrorEvent(PVMFErrPortProcessing, NULL, PVMFMoutNodeErr_MediaIOSetParameterSync);
                    return PVMFFailure;
                }

                status = PVMFSuccess;
            }
        }
        break;
        case PVMF_MEDIA_COMPRESSED_AUDIO:
        case PVMF_MEDIA_COMPRESSED_VIDEO:
            //for compressed formats, the format-specific info is sent as kvp
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO Setting Codec Header - Len=%d, Fmt=%s",
                                     aParameters->capacity, iSinkFormatString.get_str()));

            // We will enable the following line after source node fix KVP issue
            //oscl_assert(aParameters->length == aParameters->capacity)

            // We will remove the following line after source node fix KVP issue
            aParameters->length = aParameters->capacity;

            int32 err;
            OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, aParameters, 1, aRetParameters););

            if (err != OsclErrNone || aRetParameters)
            {
                PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY failed "));
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY failed "));
                //this is the first call, we don't handle a leave here-- it's an error
                iNode->ReportErrorEvent(PVMFErrPortProcessing, NULL, PVMFMoutNodeErr_MediaIOSetParameterSync);
                return PVMFFailure;
            }
            status = PVMFSuccess;

        }
        break;

        case PVMF_MEDIA_TEXT:
            //for text formats, the format-specific info is sent as kvp
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO Setting Codec Header - Len=%d, Fmt=%s",
                                     aParameters->capacity, iSinkFormatString.get_str()));

            // We will enable the following line after source node fix KVP issue
            //oscl_assert(aParameters->length == aParameters->capacity)

            // We will remove the following line after source node fix KVP issue
            aParameters->length = aParameters->capacity;

            int32 err;
            OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, aParameters, 1, aRetParameters););

            if (err != OsclErrNone || aRetParameters)
            {
                PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY failed "));
                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ConfigMIO setParametersSync of PVMF_FORMAT_SPECIFIC_INFO_KEY failed "));
                //this is the first call, we don't handle a leave here-- it's an error
                iNode->ReportErrorEvent(PVMFErrPortProcessing, NULL, PVMFMoutNodeErr_MediaIOSetParameterSync);
                return PVMFFailure;
            }
            status = PVMFSuccess;
        }
        break;

        default:
            status = PVMFErrNotSupported;
            OSCL_ASSERT(false);
            break;
    }
    if (status != PVMFSuccess)
    {
        iNode->ReportErrorEvent(PVMFErrResource, NULL, PVMFMoutNodeErr_MediaIOSetParameterSync);
    }

    return status;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::SetMIOParameterInt32(PvmiKeyType aKey, int32 aValue)
//to set parameters to the MIO component through its config interface.
{
    OsclMemAllocator alloc;
    PvmiKvp kvp;
    PvmiKvp* retKvp = NULL; // for return value

    kvp.key = NULL;
    kvp.length = oscl_strlen(aKey) + 1; // +1 for \0
    kvp.capacity = kvp.length;

    kvp.key = (PvmiKeyType)alloc.ALLOCATE(kvp.length);
    if (!kvp.key)
        return PVMFErrNoMemory;

    oscl_strncpy(kvp.key, aKey, kvp.length);
    kvp.value.int32_value = aValue;

    int32 err;
    OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, &kvp, 1, retKvp););
    alloc.deallocate(kvp.key);

    if (err != OsclErrNone || retKvp)
        return PVMFFailure;

    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::SetMIOParameterUint32(PvmiKeyType aKey, uint32 aValue)
//to set parameters to the MIO component through its config interface.
{
    OsclMemAllocator alloc;
    PvmiKvp kvp;
    PvmiKvp* retKvp = NULL; // for return value

    kvp.key = NULL;
    kvp.length = oscl_strlen(aKey) + 1; // +1 for \0
    kvp.capacity = kvp.length;

    kvp.key = (PvmiKeyType)alloc.ALLOCATE(kvp.length);
    if (!kvp.key)
        return PVMFErrNoMemory;

    oscl_strncpy(kvp.key, aKey, kvp.length);
    kvp.value.uint32_value = aValue;

    int32 err;
    OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, &kvp, 1, retKvp););
    alloc.deallocate(kvp.key);

    if (err != OsclErrNone || retKvp)
        return PVMFFailure;

    return PVMFSuccess;
}


////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::SetMIOParameterPchar(PvmiKeyType aKey, char* aValue)
//to set parameters to the MIO component through its config interface.
{
    OsclMemAllocator alloc;
    PvmiKvp kvp;
    PvmiKvp* retKvp = NULL; // for return value

    kvp.key = NULL;
    kvp.length = oscl_strlen(aKey) + 1; // +1 for \0
    kvp.capacity = kvp.length;

    kvp.key = (PvmiKeyType)alloc.ALLOCATE(kvp.length);
    if (!kvp.key)
        return PVMFErrNoMemory;

    oscl_strncpy(kvp.key, aKey, kvp.length);
    kvp.value.pChar_value = aValue;

    int32 err;
    OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, &kvp, 1, retKvp););
    alloc.deallocate(kvp.key);

    if (err != OsclErrNone || retKvp)
        return PVMFFailure;

    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNodePort::SetMIOParameterFormat(PvmiKeyType aKey, PVMFFormatType aFormatType)
//to set parameters to the MIO component through its config interface.
{
    OsclMemAllocator alloc;
    PvmiKvp kvp;
    PvmiKvp* retKvp = NULL; // for return value

    kvp.key = NULL;
    kvp.length = oscl_strlen(aKey) + 1; // +1 for \0
    kvp.capacity = kvp.length;

    kvp.key = (PvmiKeyType)alloc.ALLOCATE(kvp.length);
    if (!kvp.key)
        return PVMFErrNoMemory;

    oscl_strncpy(kvp.key, aKey, kvp.length);
    kvp.value.pChar_value = (char*)aFormatType.getMIMEStrPtr();

    int32 err;
    OSCL_TRY(err, iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, &kvp, 1, retKvp););
    alloc.deallocate(kvp.key);

    if (err != OsclErrNone || retKvp)
        return PVMFFailure;

    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::getParametersSync(PvmiMIOSession aSession, PvmiKeyType aIdentifier,
        PvmiKvp*& aParameters, int& num_parameter_elements,
        PvmiCapabilityContext aContext)
{
    PVMFStatus status = iNode->iMIOConfig->getParametersSync(aSession, aIdentifier, aParameters, num_parameter_elements,
                        aContext);
    return status;
}


////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::releaseParameters(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements)
{
    OSCL_UNUSED_ARG(aSession);

    if (aParameters && iNode)
    {
        return iNode->iMIOConfig->releaseParameters(aSession, aParameters, num_elements);
    }
    return PVMFFailure;
}


////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNodePort::setParametersSync(PvmiMIOSession aSession,
        PvmiKvp* aParameters,
        int num_elements,
        PvmiKvp * &aRet_kvp)
{
    PVMFStatus status = PVMFSuccess;
    OSCL_UNUSED_ARG(aSession);
    for (int32 i = 0; i < num_elements; i++)
    {
        if (pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_SPECIFIC_INFO_KEY) == 0)
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::setParametersSync - FSI - Fmt=%s",
                                     iSinkFormatString.get_str()));
            ConfigMIO(&aParameters[i], aRet_kvp);
        }
        else if (pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_SPECIFIC_INFO_KEY_YUV) == 0)
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::setParametersSync - FSI - Fmt=%s",
                                     iSinkFormatString.get_str()));
            status = ConfigMIO(&aParameters[i], aRet_kvp);
            if (status != PVMFSuccess)
                OSCL_LEAVE(PVMFFailure);
        }
        else if (pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_SPECIFIC_INFO_KEY_PCM) == 0)
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::setParametersSync - FSI - Fmt=%s",
                                     iSinkFormatString.get_str()));
            status = ConfigMIO(&aParameters[i], aRet_kvp);
            if (status != PVMFSuccess)
                OSCL_LEAVE(PVMFFailure);
        }
        else if ((pv_mime_strcmp(aParameters[i].key, MOUT_AUDIO_SAMPLING_RATE_KEY) == 0) ||
                 (pv_mime_strcmp(aParameters[i].key, MOUT_AUDIO_NUM_CHANNELS_KEY) == 0) ||
                 (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_WIDTH_KEY) == 0) ||
                 (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_HEIGHT_KEY) == 0) ||
                 (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_DISPLAY_WIDTH_KEY) == 0) ||
                 (pv_mime_strcmp(aParameters[i].key, MOUT_VIDEO_DISPLAY_HEIGHT_KEY) == 0) ||
                 (pv_mime_strcmp(aParameters[i].key, PVMF_DATAPATH_PORT_MAX_NUM_MEDIA_MSGS_KEY) == 0))

        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::setParametersSync - FSI - Fmt=%s",
                                     iSinkFormatString.get_str()));
            //Null parameters, because we just notify that MIO has been configured.
            ConfigMIO(0, aRet_kvp);
            iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, &aParameters[i], 1, aRet_kvp);
        }
        else if ((pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_WMA_RCA_HEADER_KEY) == 0)  ||
                 (pv_mime_strcmp(aParameters[i].key, PVMF_FORMAT_WMV_RCV_HEADER_KEY) == 0))
        {
            PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::setParametersSync - RCA/RCV Header - Fmt=%s",
                                     iSinkFormatString.get_str()));
            ConfigMIO(&aParameters[i], aRet_kvp);
        }
        else
            iNode->iMIOConfig->setParametersSync(iNode->iMIOSession, &aParameters[i], 1, aRet_kvp);
    }
}


////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNodePort::verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements)
{
    OSCL_UNUSED_ARG(aSession);
    return (iNode->iMIOConfig->verifyParametersSync(iNode->iMIOSession, aParameters, num_elements));
}


////////////////////////////////////////////////////////////////////////////
bool PVMediaOutputNodePort::peekHead(PVMFSharedMediaMsgPtr& aMsgPtr,
                                     bool& bBos)
{
    if (iIncomingQueue.iQ.empty())
    {
        return false;
    }
    // get a pointer to the queue head
    aMsgPtr = iIncomingQueue.iQ.front();
    // check the format id first - treat BOS special
    if (aMsgPtr->getFormatID() == PVMF_MEDIA_CMD_BOS_FORMAT_ID)
    {
        bBos = true;
    }
    else
    {
        bBos = false;
    }

    // This check is needed to handle cases where the media output node is waiting on configuration,
    // but for some reason the upstream node does not have a config to send.
    // (Think of cases where we have empty tracks in a presentation OR Engine reposition to EndofClip just after Prepare).
    // In such cases, if we get EOS we want to notify the media output node that configuration is complete,
    // so that Engine does not have to wait for StartofData/EndofData notification for the track.
    // We can send the notifications immediately.
    if (aMsgPtr->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
    {
        if (false == oMIOComponentConfigured)
        {
            OSCL_ASSERT(oProcessIncomingMessage != true);
            oMIOComponentConfigured = true;
            oProcessIncomingMessage = true;
        }
    }
    return true;
}

void PVMediaOutputNodePort::ProcessCallBack(uint32 callBackID,
        PVTimeComparisonUtils::MediaTimeStatus aTimerAccuracy,
        uint32 aDelta,
        const OsclAny* aContextData,
        PVMFStatus aStatus)
{
    OSCL_UNUSED_ARG(aTimerAccuracy);
    OSCL_UNUSED_ARG(aDelta);
    OSCL_UNUSED_ARG(aContextData);
    PVMF_MOPORT_LOGDEBUG((0, "PVMediaOutputNodePort::ProcessCallBack In Callback id [%d] CallbackStatus [%d]", callBackID, aStatus));
    if (PVMFSuccess == aStatus)
    {
        if (iDelayEarlyFrameCallBkId == callBackID)
        {
            oClockCallBackPending = false;
            //timer expires, reset the boolean so that we can start processing more data if need be
            oProcessIncomingMessage = true;
            if (iCurrentMediaMsg.GetRep() != NULL)
            {
                //attempt to send data current media msg if any
                SendData();
            }
            //reschedule if there is more stuff waiting and
            //if we can process more data
            if ((oProcessIncomingMessage == true) &&
                    (IncomingMsgQueueSize() > 0))
            {
                RunIfNotReady();
            }
        }
        else
        {
            PVMF_MOPORT_LOGERROR((0, "PVMediaOutputNodePort::ProcessCallBack- Error stray callback from iClockNotificationsInf callBackID[%d]", callBackID));
            OSCL_ASSERT(false);
        }

    }
}

void PVMediaOutputNodePort::SetSkipTimeStamp(uint32 aSkipTS,
        uint32 aStreamID)
{
    PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::SetSkipTimeStamp: TS=%d, Fmt=%s",
                          aSkipTS,
                          iSinkFormatString.get_str()));
    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SetSkipTimeStamp: TS=%d, Fmt=%s",
                             aSkipTS,
                             iSinkFormatString.get_str()));
    iSkipTimestamp = aSkipTS;
    iRecentStreamID = aStreamID;
    iSendStartOfDataEvent = true;
    // Send one frame of video for display after a seek.
    iSendOneFrameAfterSkip = (iSinkFormat.isVideo());
    if (oClockCallBackPending)
    {
        iClockNotificationsInf->CancelCallback(iDelayEarlyFrameCallBkId, false);
    }
    oClockCallBackPending = false;
    iDelayEarlyFrameCallBkId = 0;

    //release the current media msg right here instead of
    //waiting on a reschedule. this is to avoid deadlocks
    //in case the upstream node is just operating with a
    //single media msg
    if (iCurrentMediaMsg.GetRep() != NULL)
    {
        if (DataToSkip(iCurrentMediaMsg) == true)
        {
            if (iCurrentMediaMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
            {
                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::SetSkipTimeStamp: EOSSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                      iCurrentMediaMsg->getStreamID(),
                                      iCurrentMediaMsg->getSeqNum(),
                                      iCurrentMediaMsg->getTimestamp(),
                                      iSinkFormatString.get_str()));

                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SetSkipTimeStamp: EOSSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                         iCurrentMediaMsg->getStreamID(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         iCurrentMediaMsg->getTimestamp(),
                                         iSinkFormatString.get_str()));
            }
            else
            {
                PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::SetSkipTimeStamp: MsgSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                      iCurrentMediaMsg->getStreamID(),
                                      iCurrentMediaMsg->getSeqNum(),
                                      iCurrentMediaMsg->getTimestamp(),
                                      iSinkFormatString.get_str()));

                PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SetSkipTimeStamp: MsgSkip - StreamId=%d, Seq=%d, TS=%d, Fmt=%s",
                                         iCurrentMediaMsg->getStreamID(),
                                         iCurrentMediaMsg->getSeqNum(),
                                         iCurrentMediaMsg->getTimestamp(),
                                         iSinkFormatString.get_str()));
            }
            iCurrentMediaMsg.Unbind();
            iFragIndex = 0;
        }
    }
    //wake up the AO to start processing messages
    RunIfNotReady();
}

void PVMediaOutputNodePort::CancelSkip()
{
    PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::CancelSkip: Fmt=%s",
                          iSinkFormatString.get_str()));
    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::CancelSkip: Fmt=%s",
                             iSinkFormatString.get_str()));
    iSkipTimestamp = 0;
    iRecentStreamID = 0;
    iSendStartOfDataEvent = false;
}

bool PVMediaOutputNodePort::DataToSkip(PVMFSharedMediaMsgPtr& aMsg)
{
    //iRecentStreamID reflects the latest stream
    //that is being played. Msg with a streamid less than
    //iRecentStreamID belongs to an older stream and should
    //be skipped. Do not skip Msg that belong to the current
    //stream or a future stream (future being the case where
    //msg arrives before a skip request. Pls note that we
    //assume that the stream ids are a montonically increasing
    //sequence


    //if it is ReConfig msg we should not skip it ,don't need to consider TS and StreamingID.
    //In current process, it doesn't set streaming ID for Reconfig Msg and uses the default one ,zero
    //No need to set streaming ID for Reconfig Msg,if we set,there is one problem,
    //If after BitStreamSwitch,APP call setplaybackrange,the Reconfig Msg may be skipped and can't be sent to the MIO
    //media output node should never drop a reconfig msg
    if (aMsg->getFormatID() == PVMF_MEDIA_CMD_RE_CONFIG_FORMAT_ID)
    {
        return false;
    }

    uint32 delta = 0;
    bool oOldStream = PVTimeComparisonUtils::IsEarlier(aMsg->getStreamID(), iRecentStreamID, delta);
    if (oOldStream && delta > 0)
    {
        //a zero delta could mean the stream ids are equal
        return true;
    }
    //we typically do not do timestamp checks on EOS.
    //old EOSes that needed to be discarded during a repositioning
    //would not pass the stream id check above. If we get this far
    //it means that the EOS belong to the current stream id and its
    //timestamp does not really matter.
    if (aMsg->getFormatID() != PVMF_MEDIA_CMD_EOS_FORMAT_ID &&
            aMsg->getFormatID() != PVMF_MEDIA_CMD_EOC_FORMAT_ID &&
            aMsg->getFormatID() != PVMF_MEDIA_CMD_BOC_FORMAT_ID)
    {
        //if we get here it means that the msg belong to current or a
        //future media stream. check against skip timestamp if we are
        //required to send PVMFInfoStartOfData. If we are not reqd to
        //send the event, we intentionally by pass any timestamp checks
        if (iSendStartOfDataEvent == true)
        {
            delta = 0;
            // get the marker info to check if duration is available
            PVMFSharedMediaDataPtr mediaData;
            convertToPVMFMediaData(mediaData, aMsg);

            uint32 duration = 0;
            if (mediaData->getMarkerInfo() & PVMF_MEDIA_DATA_MARKER_INFO_DURATION_AVAILABLE_BIT)
            {
                duration = mediaData->getDuration();
            }

            bool tsEarly = PVTimeComparisonUtils::IsEarlier((aMsg->getTimestamp() + duration), iSkipTimestamp, delta);
            if (tsEarly && delta > 0)
            {
                //a zero delta could mean the timestamps are equal
                return true;
            }
        }
    }
    return false;
}

void PVMediaOutputNodePort::ClockTimebaseUpdated()
{
    if (iClock == NULL)
        return;

    if (iClock->GetCountTimebase())
    {
        PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::ClockTimebaseUpdated: CountTimeBase Added - Fmt=%s",
                              iSinkFormatString.get_str()));
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ClockTimebaseUpdated: CountTimeBase Added - Fmt=%s",
                                 iSinkFormatString.get_str()));
        //Reset the frame step delta to zero.
        iFrameStepMode = true;
        iClock->GetCountTimebase()->GetCount(iClockFrameCount);
        iSyncFrameCount = iClockFrameCount;
    }
    else
    {
        PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::ClockTimebaseUpdated: CountTimeBase Removed - Fmt=%s",
                              iSinkFormatString.get_str()));
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ClockTimebaseUpdated: CountTimeBase Removed - Fmt=%s",
                                 iSinkFormatString.get_str()));
        //reset frame step variables.
        iFrameStepMode = false;
        iSyncFrameCount = 0;
        iClockFrameCount = 0;
    }
    if (oClockCallBackPending)
    {
        iClockNotificationsInf->CancelCallback(iDelayEarlyFrameCallBkId, false);
    }
    oClockCallBackPending = false;
    iDelayEarlyFrameCallBkId = 0;

    //reschedule if there is more stuff waiting and
    //if we can process more data
    if ((oProcessIncomingMessage == true) &&
            (IncomingMsgQueueSize() > 0))
    {
        RunIfNotReady();
    }
}

void PVMediaOutputNodePort::ClockCountUpdated()
{
    if (iClock && iClock->GetCountTimebase())
    {
        //read the new framecount
        iClock->GetCountTimebase()->GetCount(iClockFrameCount);
        //wake up the AO to process data
        oProcessIncomingMessage = true;

        if (iCurrentMediaMsg.GetRep() != NULL)
        {
            //attempt to send data current media msg if any
            SendData();
        }
        //reschedule if there is more stuff waiting and
        //if we can process more data
        if ((oProcessIncomingMessage == true) &&
                (IncomingMsgQueueSize() > 0))
        {
            RunIfNotReady();
        }
    }
}

void PVMediaOutputNodePort::ClockAdjusted()
{
}

void PVMediaOutputNodePort::NotificationsInterfaceDestroyed()
{
    iClockNotificationsInf = NULL;
}

void PVMediaOutputNodePort::ClockStateUpdated()
{
    if (iClock == NULL)
        return;

    if (iClock->GetState() == PVMFMediaClock::PAUSED)
    {
        PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::ClockStateUpdated: Clock Paused - Fmt=%s",
                              iSinkFormatString.get_str()));
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ClockStateUpdated: Clock Paused - Fmt=%s",
                                 iSinkFormatString.get_str()));
        //stop processing input msgs only for passive mediaoutput comps
        //for active ones continue to send, since the mediaoutput comp
        //is responsible for pacing of the data
        if (oActiveMediaOutputComp == false)
        {
            oProcessIncomingMessage = false;
            if (oClockCallBackPending)
            {
                iClockNotificationsInf->CancelCallback(iDelayEarlyFrameCallBkId, false);
            }
            oClockCallBackPending = false;
            iDelayEarlyFrameCallBkId = 0;
        }
    }
    else if (iClock->GetState() == PVMFMediaClock::RUNNING)
    {
        PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::ClockStateUpdated: Clock Running - Fmt=%s",
                              iSinkFormatString.get_str()));
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ClockStateUpdated: Clock Running - Fmt=%s",
                                 iSinkFormatString.get_str()));

        //If MIO component is configured, messages can be processed now.
        if (oMIOComponentConfigured)
        {
            oProcessIncomingMessage = true;
        }

        if (oClockCallBackPending)
        {
            iClockNotificationsInf->CancelCallback(iDelayEarlyFrameCallBkId, false);
        }
        oClockCallBackPending = false;
        iDelayEarlyFrameCallBkId = 0;


        //reset write state as well, in case mo comp is still busy
        //it will leave again, so that state will get reset
        iWriteState = EWriteOK;

        if (iCurrentMediaMsg.GetRep() != NULL)
        {
            //attempt to send data current media msg if any
            SendData();
        }

        //reschedule if there is more stuff waiting and
        //if we can process more data
        if ((oProcessIncomingMessage == true) &&
                (IncomingMsgQueueSize() > 0))
        {
            RunIfNotReady();
        }
    }
    else if (iClock->GetState() == PVMFMediaClock::STOPPED)
    {
        PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::ClockStateUpdated: Clock Stopped - Fmt=%s",
                              iSinkFormatString.get_str()));
        PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::ClockStateUpdated: Clock Stopped - Fmt=%s",
                                 iSinkFormatString.get_str()));
        //stop processing input msgs only for passive mediaoutput comps
        //for active ones continue to send, since the mediaoutput comp
        //is responsible for pacing of the data, this is to account for a case when clock is stopped
        //after repositioning.
        if (oActiveMediaOutputComp == false)
        {
            oProcessIncomingMessage = false;
            if (oClockCallBackPending)
            {
                iClockNotificationsInf->CancelCallback(iDelayEarlyFrameCallBkId, false);
            }
            oClockCallBackPending = false;
            iDelayEarlyFrameCallBkId = 0;
        }
    }
    RunIfNotReady();
}

void PVMediaOutputNodePort::ClearPreviousBOSStreamIDs(uint32 aID)
{
    //Pls note that we
    //assume that the stream ids are a montonically increasing
    //sequence
    Oscl_Vector<uint32, OsclMemAllocator>::iterator it;
    it = iBOSStreamIDVec.begin();
    while (it != iBOSStreamIDVec.end())
    {
        // iBOSStreamIDVec will contain the stream ID of the current stream being Played. On each skip call
        // all the previous streamid's will be removed from the vector leaving the current stream id. The
        // current stream id will be erased in the next skip media data call.
        if (*it < aID)
        {
            it = iBOSStreamIDVec.erase(it);
        }
        else
        {
            it++;
        }
    }
}

int32 PVMediaOutputNodePort::WriteDataToMIO(int32 &aCmdId, PvmiMediaXferHeader &aMediaxferhdr, OsclRefCounterMemFrag &aFrag)
{
    int32 leavecode = OsclErrNone;
    OSCL_TRY_NO_TLS(iOsclErrorTrapImp, leavecode,
                    aCmdId = iMediaTransfer->writeAsync(PVMI_MEDIAXFER_FMT_TYPE_DATA,  /*format_type*/
                                                        PVMI_MEDIAXFER_FMT_INDEX_DATA, /*format_index*/
                                                        (uint8*)aFrag.getMemFragPtr(),
                                                        aFrag.getMemFragSize(),
                                                        aMediaxferhdr,
                                                        (OsclAny*) & iWriteAsyncContext););
    return leavecode;
}

void PVMediaOutputNodePort::SendStartOfDataEvent(uint32 streamID, uint32 clipID)
{
    PVMF_MOPORT_LOGREPOS((0, "PVMediaOutputNodePort::SendStartOfDataEvent - PVMFInfoStartOfData Fmt=%s StreamID=%d clipID=%d",
                          iSinkFormatString.get_str(), streamID, clipID));
    PVMF_MOPORT_LOGDATAPATH((0, "PVMediaOutputNodePort::SendStartOfDataEvent - PVMFInfoStartOfData Fmt=%s StreamID=%d clipID=%d",
                             iSinkFormatString.get_str(), streamID, clipID));
    uint8 localbuffer[8];
    oscl_memcpy(localbuffer, &streamID, sizeof(uint32));
    oscl_memcpy(&localbuffer[4], &clipID, sizeof(uint32));
    iNode->ReportInfoEvent(PVMFInfoStartOfData, (OsclAny*)localbuffer);
    iSendStartOfDataEvent = false;
}

void PVMediaOutputNodePort::ResetConsecutiveFramesDropped()
{
    iConsecutiveFramesDropped = 0;
    iLateFrameEventSent = false;
}


