blob: cfe5b9197631c1bdf9cac1cf845d563882cb2d2e [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 2008 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 pvmi_io_interface_node.cpp
* @brief
*/
#include "oscl_base.h"
#include "pv_media_output_node_factory.h"
#include "pv_media_output_node.h"
#include "pv_media_output_node_inport.h"
#include "oscl_dll.h"
#include "pvmf_basic_errorinfomessage.h"
#include "pv_media_output_node_events.h"
// Define entry point for this DLL
OSCL_DLL_ENTRY_POINT_DEFAULT()
/**
//Macros for calling PVLogger
*/
#define LOGREPOS(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iReposLogger,PVLOGMSG_INFO,m);
#define LOGERROR(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m);
#define LOGINFOHI(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger,PVLOGMSG_INFO,m);
#define LOGINFOMED(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG,iLogger,PVLOGMSG_INFO,m);
#define LOGINFOLOW(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iLogger,PVLOGMSG_INFO,m);
#define LOGINFO(m) LOGINFOMED(m)
//this should always be 1. set this to zero if
//you want to bypass avsync (typically used in
//case one wants to decode and render ASAP)
#define PVMF_MEDIA_OUTPUT_NODE_ENABLE_AV_SYNC 1
////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNode::Assert(bool condition)
{
if (!condition)
{
LOGERROR((0, "PVMediaOutputNode::Assert Failed!"));
OSCL_ASSERT(0);
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFNodeInterface* PVMediaOutputNodeFactory::CreateMediaOutputNode(
PvmiMIOControl* aMIOControl)
{
return PVMediaOutputNode::Create(aMIOControl);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNodeFactory::DeleteMediaOutputNode(PVMFNodeInterface* aNode)
{
PVMediaOutputNode::Release(aNode);
}
////////////////////////////////////////////////////////////////////////////
PVMFNodeInterface* PVMediaOutputNode::Create(PvmiMIOControl* aIOInterfacePtr)
{
PVMediaOutputNode* node = OSCL_NEW(PVMediaOutputNode, ());
if (node)
{
OSCL_TRAPSTACK_PUSH(node);
node->ConstructL(aIOInterfacePtr);
OSCL_TRAPSTACK_POP();
}
return (PVMFNodeInterface*)node;
}
////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNode::Release(PVMFNodeInterface* aNode)
{
OSCL_DELETE(((PVMediaOutputNode*)aNode));
}
////////////////////////////////////////////////////////////////////////////
PVMediaOutputNode::~PVMediaOutputNode()
{
Cancel();
if (IsAdded())
RemoveFromScheduler();
iLogger = NULL;
if (iMIOControl)
{
//call disconnect to make sure that there are no
//callback once the object has been destroyed
iMIOControl->disconnect(iMIOSession);
//ignore any returned errors.
iMIOControl->ThreadLogoff();
}
//if any MIO commands are outstanding, there will be
//a crash when they callback-- so panic here instead.
if (!iCancelCommand.empty()
|| iMediaIORequest != ENone)
OsclError::Panic("PVMOUT", PVMoutPanic_OutstandingMIO_Command);
//Cleanup allocated ports
while (!iInPortVector.empty())
iInPortVector.Erase(&iInPortVector.front());
//Cleanup commands
//The command queues are self-deleting, but we want to
//notify the observer of unprocessed commands.
while (!iCurrentCommand.empty())
{
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFFailure);
}
while (!iInputCommands.empty())
{
CommandComplete(iInputCommands, iInputCommands.front(), PVMFFailure);
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::ThreadLogon()
{
if (iInterfaceState != EPVMFNodeCreated)
return PVMFErrInvalidState;
iLogger = PVLogger::GetLoggerObject("PVMediaOutputNode");
iReposLogger = PVLogger::GetLoggerObject("pvplayerrepos.mionode");
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ThreadLogon"));
if (!IsAdded())
AddToScheduler();
if (iMIOControl)
{
iMIOControl->ThreadLogon();
iMediaIOState = STATE_LOGGED_ON;
}
SetState(EPVMFNodeIdle);
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::ThreadLogoff()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ThreadLogoff"));
if (iInterfaceState != EPVMFNodeIdle)
return PVMFErrInvalidState;
if (IsAdded())
RemoveFromScheduler();
iLogger = NULL;
if (iMIOControl)
{
// Currently we do not distinguish between these states
// in how we drive the MIO. In the future, we will be
// able to independently reset/disconnect MIOs.
//
// The MIO reset is called here instead of the internal
// reset because there is currently no processing there.
// This is to reduce risk to existing MIOs.
//
// It can be moved to the internal node reset in the future.
PVMFStatus status = iMIOControl->Reset(iMIOSession);
status = iMIOControl->disconnect(iMIOSession);
//ignore any returned errors.
iMIOControl->ThreadLogoff();
iMediaIOState = STATE_IDLE;
}
SetState(EPVMFNodeCreated);
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::GetCapability(PVMFNodeCapability& aNodeCapability)
{
if (!iMIOConfig)
return PVMFFailure;
aNodeCapability.iCanSupportMultipleInputPorts = false;
aNodeCapability.iCanSupportMultipleOutputPorts = false;
aNodeCapability.iHasMaxNumberOfPorts = true;
aNodeCapability.iMaxNumberOfPorts = 1;
PvmiKvp* kvp ;
int numParams ;
int32 err ;
int32 i ;
PVMFStatus status;
// Get input formats capability from media IO
kvp = NULL;
numParams = 0;
status = iMIOConfig->getParametersSync(NULL, INPUT_FORMATS_CAP_QUERY, kvp, numParams, NULL);
if (status == PVMFSuccess)
{
OSCL_TRY(err,
for (i = 0; i < numParams; i++)
aNodeCapability.iInputFormatCapability.push_back(kvp[i].value.uint32_value);
);
if (kvp)
iMIOConfig->releaseParameters(0, kvp, numParams);
}
//else ignore errors.
// Get output formats capability from media IO
kvp = NULL;
numParams = 0;
status = iMIOConfig->getParametersSync(NULL, OUTPUT_FORMATS_CAP_QUERY, kvp, numParams, NULL);
if (status == PVMFSuccess)
{
OSCL_TRY(err,
for (i = 0; i < numParams; i++)
aNodeCapability.iOutputFormatCapability.push_back(kvp[i].value.uint32_value);
);
if (kvp)
iMIOConfig->releaseParameters(0, kvp, numParams);
}
//else ignore errors.
if (aNodeCapability.iInputFormatCapability.empty() && aNodeCapability.iOutputFormatCapability.empty())
return PVMFFailure;
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFPortIter* PVMediaOutputNode::GetPorts(const PVMFPortFilter* aFilter)
{
OSCL_UNUSED_ARG(aFilter);//port filter is not implemented.
iInPortVector.Reset();
return &iInPortVector;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::QueryUUID(PVMFSessionId s, const PvmfMimeString& aMimeType,
Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids,
bool aExactUuidsOnly,
const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::QueryUUID() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_QUERYUUID, aMimeType, aUuids, aExactUuidsOnly, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::QueryInterface(PVMFSessionId s, const PVUuid& aUuid,
PVInterface*& aInterfacePtr,
const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::QueryInterface() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::RequestPort(PVMFSessionId s, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::RequestPort() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_REQUESTPORT, aPortTag, aPortConfig, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::ReleasePort(PVMFSessionId s, PVMFPortInterface& aPort, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ReleasePort() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_RELEASEPORT, aPort, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Init(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Init() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_INIT, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Prepare(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Prepare() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_PREPARE, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Start(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Start() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_START, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Stop(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Stop() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_STOP, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Flush(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Flush() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_FLUSH, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Pause(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Pause() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_PAUSE, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::Reset(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::Reset() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_RESET, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::CancelAllCommands(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::CancelAllCommands() called"));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_CANCELALLCOMMANDS, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::CancelCommand(PVMFSessionId s, PVMFCommandId aCmdId, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::CancelCommands() called cmdId=%d", aCmdId));
PVMediaOutputNodeCmd cmd;
cmd.PVMediaOutputNodeCmdBase::Construct(s, PVMF_GENERIC_NODE_CANCELCOMMAND, aCmdId, aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNode::addRef()
{
++iExtensionRefCount;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNode::removeRef()
{
if (iExtensionRefCount > 0)
--iExtensionRefCount;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF bool PVMediaOutputNode::queryInterface(const PVUuid& uuid, PVInterface*& iface)
{
if (uuid == PvmfNodesSyncControlUuid)
{
PvmfNodesSyncControlInterface* myInterface = OSCL_STATIC_CAST(PvmfNodesSyncControlInterface*, this);
iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
++iExtensionRefCount;
}
else if (uuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID)
{
PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this);
iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
++iExtensionRefCount;
}
else
{
iface = NULL;
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::SetClock(OsclClock* aClock)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::SetClock: aClock=0x%x", aClock));
//remove any old clock
if (iClock)
{
if (iMIOClockExtension)
iMIOClockExtension->SetClock(NULL);
for (uint32 i = 0; i < iInPortVector.size(); i++)
iInPortVector[i]->SetClock(NULL);
}
iClock = aClock;
for (uint32 i = 0; i < iInPortVector.size(); i++)
{
iInPortVector[i]->SetClock(aClock);
iInPortVector[i]->ChangeClockRate(iClockRate);
}
//pass the clock to the optional MIO clock interface
if (iMIOClockExtension)
{
iMIOClockExtension->SetClock(aClock);
}
else
{
#if (PVMF_MEDIA_OUTPUT_NODE_ENABLE_AV_SYNC)
//turn on sync params
for (uint32 i = 0; i < iInPortVector.size(); i++)
{
iInPortVector[i]->EnableMediaSync();
iInPortVector[i]->SetMargins(iEarlyMargin, iLateMargin);
}
#endif
}
return PVMFSuccess;
}
///////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::ChangeClockRate(int32 aRate)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ChangeClockRate: aRate=%d", aRate));
iClockRate = aRate;
for (uint32 i = 0; i < iInPortVector.size() ; i++)
iInPortVector[i]->ChangeClockRate(aRate);
// For now support all rates.
// In future, need to check with underlying media IO component
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::SetMargins(int32 aEarlyMargin, int32 aLateMargin)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::SetMargins: aEarlyMargin=%d, aLateMargin=%d", aEarlyMargin, aLateMargin));
//save the margins
iEarlyMargin = aEarlyMargin;
iLateMargin = aLateMargin;
//pass the margins to the ports
for (uint32 i = 0; i < iInPortVector.size() ; i++)
iInPortVector[i]->SetMargins(aEarlyMargin, aLateMargin);
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNode::ClockStarted()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ClockStarted"));
//notify the ports
for (uint32 i = 0; i < iInPortVector.size() ; i++)
iInPortVector[i]->ClockStarted();
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNode::ClockStopped()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ClockStopped"));
//notify the ports
for (uint32 i = 0; i < iInPortVector.size() ; i++)
iInPortVector[i]->ClockStopped();
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMediaOutputNode::SkipMediaData(PVMFSessionId s,
PVMFTimestamp aStartingTimestamp,
PVMFTimestamp aResumeTimestamp,
uint32 aStreamID,
bool aRenderSkippedData ,
bool aPlayBackPositionContinuous,
OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::SkipMediaData() called "));
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::SkipMediaData() called - Mime=%s", iSinkFormatString.get_str()));
PVMediaOutputNodeCmd cmd;
cmd.Construct(s,
PVMF_MEDIAOUTPUTNODE_SKIPMEDIADATA,
aStartingTimestamp,
aResumeTimestamp,
aRenderSkippedData,
aStreamID,
aPlayBackPositionContinuous,
aContext);
return QueueCommandL(cmd);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMediaOutputNode::RequestCompleted(const PVMFCmdResp& aResponse)
//callback from the MIO module.
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::RequestCompleted: Cmd ID=%d", aResponse.GetCmdId()));
//look for cancel completion.
if (iMediaIOCancelPending
&& aResponse.GetCmdId() == iMediaIOCancelCmdId)
{
iMediaIOCancelPending = false;
Assert(!iCancelCommand.empty());
//Current cancel command is now complete.
CommandComplete(iCancelCommand, iCancelCommand.front(), PVMFSuccess);
}
//look for non-cancel completion
else if (iMediaIORequest != ENone
&& aResponse.GetCmdId() == iMediaIOCmdId)
{
Assert(!iCurrentCommand.empty());
PVMediaOutputNodeCmd& cmd = iCurrentCommand.front();
switch (iMediaIORequest)
{
case EQueryCapability:
if (aResponse.GetCmdStatus() != PVMFSuccess)
cmd.iEventCode = PVMFMoutNodeErr_MediaIOQueryCapConfigInterface;
CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
break;
case EQueryClockExtension:
//ignore any error from this query since the interface is optional.
iMediaIORequest = ENone;
//re-do the clock setting call since iMIOClockExtension may have changed.
if (aResponse.GetCmdStatus() == PVMFSuccess
&& iMIOClockExtension)
{
SetClock(iClock);
}
//To continue the Node Init, query for the
//capability & config interface
{
PVMFStatus status = SendMioRequest(iCurrentCommand[0], EQueryCapability);
if (status == PVMFPending)
return;//wait on response
else
CommandComplete(iCurrentCommand, iCurrentCommand.front(), status);
}
break;
case EInit:
if (aResponse.GetCmdStatus() != PVMFSuccess)
cmd.iEventCode = PVMFMoutNodeErr_MediaIOInit;
else
iMediaIOState = STATE_INITIALIZED;
CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
break;
case EStart:
if (aResponse.GetCmdStatus() != PVMFSuccess)
cmd.iEventCode = PVMFMoutNodeErr_MediaIOStart;
else
iMediaIOState = STATE_STARTED;
CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
break;
case EPause:
if (aResponse.GetCmdStatus() != PVMFSuccess)
cmd.iEventCode = PVMFMoutNodeErr_MediaIOPause;
else
iMediaIOState = STATE_PAUSED;
CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
break;
case EStop:
if (aResponse.GetCmdStatus() != PVMFSuccess)
cmd.iEventCode = PVMFMoutNodeErr_MediaIOStop;
else
iMediaIOState = STATE_INITIALIZED;
CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
break;
case EDiscard:
{
if (aResponse.GetCmdStatus() != PVMFSuccess)
{
cmd.iEventCode = PVMFMoutNodeErr_MediaIODiscardData;
CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
}
else
{
iMediaIORequest = ENone;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::ResuestCompleted - EDiscard success - Mime=%s",
iSinkFormatString.get_str()));
//attempt to complete skip media data
CompleteSkipMediaData();
}
}
break;
default:
Assert(false);
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFFailure);
break;
}
}
else
{
//unexpected response.
LOGERROR((0, "PVMediaOutputNode:RequestComplete Warning! Unexpected command ID %d"
, aResponse.GetCmdId()));
}
}
////////////////////////////////////////////////////////////////////////////
PVMediaOutputNode::PVMediaOutputNode()
: OsclActiveObject(OsclActiveObject::EPriorityNominal, "PVMediaOutputNode")
, iEventUuid(PVMFMediaOutputNodeEventTypesUUID)
, iMIOControl(NULL)
, iMIOSession(NULL)
, iMIOConfig(NULL)
, iClock(NULL)
, iEarlyMargin(DEFAULT_EARLY_MARGIN)
, iLateMargin(DEFAULT_LATE_MARGIN)
, iExtensionRefCount(0)
, iLogger(NULL)
, iMediaIOState(STATE_IDLE)
{
}
////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNode::ConstructL(PvmiMIOControl* aIOInterfacePtr)
{
iLogger = NULL;
iMIOControl = aIOInterfacePtr;
iInputCommands.Construct(1, 10);//reserve 10
iCurrentCommand.Construct(1, 1);//reserve 1.
iCancelCommand.Construct(1, 1);//reserve 1.
iInPortVector.Construct(0);//reserve zero
iMediaIORequest = ENone;
iMediaIOCancelPending = false;
iMIOClockExtension = NULL;
iClockRate = 1;
}
////////////////////////////////////////////////////////////////////////////
/**
//This routine is called by various command APIs to queue an
//asynchronous command for processing by the command handler AO.
//This function may leave if the command can't be queued due to
//memory allocation failure.
*/
PVMFCommandId PVMediaOutputNode::QueueCommandL(PVMediaOutputNodeCmd& aCmd)
{
PVMFCommandId id;
id = iInputCommands.AddL(aCmd);
//wakeup the AO
RunIfNotReady();
return id;
}
/**
//The various command handlers call this when a command is complete.
*/
void PVMediaOutputNode::CommandComplete(PVMediaOutputNodeCmdQ& aCmdQ, PVMediaOutputNodeCmd& aCmd, PVMFStatus aStatus, OsclAny*aEventData)
{
if (aStatus == PVMFSuccess || aCmd.iCmd == PVMF_GENERIC_NODE_QUERYINTERFACE) //(mg) treat QueryIF failures as info, not errors
{
LOGINFO((0, "PVMediaOutputNode:CommandComplete Id %d Cmd %d Status %d Context %d EVData %d EVCode %d"
, aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData, aCmd.iEventCode));
}
else
{
LOGERROR((0, "PVMediaOutputNode:CommandComplete Error! Id %d Cmd %d Status %d Context %d EVData %d EVCode %d"
, aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData, aCmd.iEventCode));
}
//do state transitions and any final command completion.
if (aStatus == PVMFSuccess)
{
switch (aCmd.iCmd)
{
case PVMF_GENERIC_NODE_INIT:
SetState(EPVMFNodeInitialized);
break;
case PVMF_GENERIC_NODE_PREPARE:
SetState(EPVMFNodePrepared);
break;
case PVMF_GENERIC_NODE_START:
SetState(EPVMFNodeStarted);
{
for (uint32 i = 0; i < iInPortVector.size(); i++)
iInPortVector[i]->NodeStarted();
}
break;
case PVMF_GENERIC_NODE_PAUSE:
SetState(EPVMFNodePaused);
break;
case PVMF_GENERIC_NODE_STOP:
SetState(EPVMFNodePrepared);
break;
case PVMF_GENERIC_NODE_FLUSH:
SetState(EPVMFNodePrepared);
//resume port input so the ports can be re-started.
{
for (uint32 i = 0;i < iInPortVector.size();i++)
iInPortVector[i]->ResumeInput();
}
break;
default:
break;
}
}
//Reset the media I/O request
iMediaIORequest = ENone;
{
//Extract parameters needed for command response.
PVMFCommandId cmdId = aCmd.iId;
const OsclAny* cmdContext = aCmd.iContext;
PVMFSessionId cmdSess = aCmd.iSession;
PVMFStatus eventCode = aCmd.iEventCode;
//Erase the command from the queue.
aCmdQ.Erase(&aCmd);
if (eventCode != PVMFMoutNodeErr_First)
{
//create extended response.
PVMFBasicErrorInfoMessage*eventmsg = OSCL_NEW(PVMFBasicErrorInfoMessage, (eventCode, iEventUuid, NULL));
PVMFCmdResp resp(cmdId
, cmdContext
, aStatus
, OSCL_STATIC_CAST(PVInterface*, eventmsg)
, aEventData);
//report to the session observers.
PVMFNodeInterface::ReportCmdCompleteEvent(cmdSess, resp);
//remove the ref to the extended response
if (eventmsg)
eventmsg->removeRef();
}
else
{
//create basic response
PVMFCmdResp resp(cmdId, cmdContext, aStatus, aEventData);
//report to the session observers.
PVMFNodeInterface::ReportCmdCompleteEvent(cmdSess, resp);
}
}
//re-schedule if there are more commands and node isn't logged off
if (!iInputCommands.empty()
&& IsAdded())
RunIfNotReady();
}
////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNode::Run()
{
//Process async node commands.
if (!iInputCommands.empty())
{
ProcessCommand();
}
//Check for completion of a flush command...
if (iCurrentCommand.size() > 0
&& iCurrentCommand.front().iCmd == PVMF_GENERIC_NODE_FLUSH
&& PortQueuesEmpty())
{
//Flush is complete.
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFSuccess);
}
}
bool PVMediaOutputNode::PortQueuesEmpty()
{
uint32 i;
for (i = 0;i < iInPortVector.size();i++)
{
if (iInPortVector[i]->IncomingMsgQueueSize() > 0 ||
iInPortVector[i]->OutgoingMsgQueueSize())
{
return false;
}
}
return true;
}
/**
//Called by the command handler AO to process a command from
//the input queue.
*/
void PVMediaOutputNode::ProcessCommand()
{
//Can't do anything when an asynchronous cancel is in progress-- just
//need to wait on completion.
if (!iCancelCommand.empty())
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ProcessCommand Cancel pending so return"));
return ; //keep waiting.
}
//If a command is in progress, only certain commands can interrupt it.
if (!iCurrentCommand.empty()
&& !iInputCommands.front().caninterrupt())
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::ProcessCommand Command pending so return"));
return ; //keep waiting
}
//The newest or highest pri command is in the front of the queue.
Assert(!iInputCommands.empty());
PVMediaOutputNodeCmd& aCmd = iInputCommands.front();
PVMFStatus cmdstatus;
OsclAny* aEventData = NULL;
if (aCmd.caninterrupt())
{
//save input command in cancel command
int32 err;
OSCL_TRY(err, iCancelCommand.StoreL(aCmd););
if (err != OsclErrNone)
{
cmdstatus = PVMFErrNoMemory;
}
else
{
//Process the interrupt commands.
switch (aCmd.iCmd)
{
case PVMF_GENERIC_NODE_CANCELALLCOMMANDS:
cmdstatus = DoCancelAllCommands(aCmd);
break;
case PVMF_GENERIC_NODE_CANCELCOMMAND:
cmdstatus = DoCancelCommand(aCmd);
break;
default:
Assert(false);
cmdstatus = PVMFFailure;
break;
}
}
//erase the input command.
if (cmdstatus != PVMFPending)
{
//Request already failed or completed successfully -- erase from Cancel Command.
//Node command remains in Input Commands to be completed at end of this function
if (iCancelCommand.size() > 0)
iCancelCommand.Erase(&iCancelCommand.front());
}
else
{
//Cancel is still pending.
//Node command is now stored in Cancel Command, so erase from Input Commands and wait
iInputCommands.Erase(&aCmd);
}
}
else
{
//save input command in current command
int32 err;
OSCL_TRY(err, iCurrentCommand.StoreL(aCmd););
if (err != OsclErrNone)
{
cmdstatus = PVMFErrNoMemory;
}
else
{
//Process the normal pri commands.
switch (aCmd.iCmd)
{
case PVMF_GENERIC_NODE_QUERYUUID:
cmdstatus = DoQueryUuid(aCmd);
break;
case PVMF_GENERIC_NODE_QUERYINTERFACE:
cmdstatus = DoQueryInterface(aCmd);
break;
case PVMF_GENERIC_NODE_REQUESTPORT:
cmdstatus = DoRequestPort(aCmd, aEventData);
break;
case PVMF_GENERIC_NODE_RELEASEPORT:
cmdstatus = DoReleasePort(aCmd);
break;
case PVMF_GENERIC_NODE_INIT:
cmdstatus = DoInit(aCmd);
break;
case PVMF_GENERIC_NODE_PREPARE:
cmdstatus = DoPrepare(aCmd);
break;
case PVMF_GENERIC_NODE_START:
cmdstatus = DoStart(aCmd);
break;
case PVMF_GENERIC_NODE_STOP:
cmdstatus = DoStop(aCmd);
break;
case PVMF_GENERIC_NODE_FLUSH:
cmdstatus = DoFlush(aCmd);
break;
case PVMF_GENERIC_NODE_PAUSE:
cmdstatus = DoPause(aCmd);
break;
case PVMF_GENERIC_NODE_RESET:
cmdstatus = DoReset(aCmd);
break;
case PVMF_MEDIAOUTPUTNODE_SKIPMEDIADATA:
cmdstatus = DoSkipMediaData(aCmd);
break;
default://unknown command type
Assert(false);
cmdstatus = PVMFFailure;
break;
}
}
//erase the input command.
if (cmdstatus != PVMFPending)
{
//Request already failed-- erase from Current Command.
//Node command remains in Input Commands.
iCurrentCommand.Erase(&iCurrentCommand.front());
}
else
{
//Node command is now stored in Current Command, so erase from Input Commands.
iInputCommands.Erase(&aCmd);
}
}
if (cmdstatus != PVMFPending)
{
CommandComplete(iInputCommands, aCmd, cmdstatus, aEventData);
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoQueryUuid(PVMediaOutputNodeCmd& aCmd)
{
//This node supports Query UUID from any state
OSCL_String* mimetype;
Oscl_Vector<PVUuid, OsclMemAllocator> *uuidvec;
bool exactmatch;
aCmd.PVMediaOutputNodeCmdBase::Parse(mimetype, uuidvec, exactmatch);
uuidvec->push_back(PvmfNodesSyncControlUuid);
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoQueryInterface(PVMediaOutputNodeCmd& aCmd)
{
PVUuid* uuid;
PVInterface** ptr;
aCmd.PVMediaOutputNodeCmdBase::Parse(uuid, ptr);
if (uuid && ptr)
{
if (queryInterface(*uuid, *ptr))
return PVMFSuccess;
}
return PVMFFailure;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoRequestPort(PVMediaOutputNodeCmd& aCmd, OsclAny*&aEventData)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoRequestPort"));
//This node supports port request from any state
//retrieve port tag & mimetype
int32 tag;
OSCL_String* mimetype;
aCmd.PVMediaOutputNodeCmdBase::Parse(tag, mimetype);
switch (tag)
{
case PVMF_MEDIAIO_NODE_INPUT_PORT_TAG:
{
//Allocate a new port
OsclAny *ptr = NULL;
int32 err;
OSCL_TRY(err, ptr = iInPortVector.Allocate(););
if (err != OsclErrNone || !ptr)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoRequestPort: Error - iInPortVector Out of memory"));
return PVMFErrNoMemory;
}
PVMediaOutputNodePort *port = OSCL_PLACEMENT_NEW(ptr, PVMediaOutputNodePort(this));
//set the format from the mimestring, if provided
if (mimetype)
{
GetFormatIndex(mimetype->get_str());
PVMFStatus status = port->Configure(*mimetype);
if (status != PVMFSuccess)
{
//bad format!
iInPortVector.DestructAndDealloc(port);
return PVMFErrArgument;
}
}
iSinkFormatString = *mimetype;
//Add the port to the port vector.
OSCL_TRY(err, iInPortVector.AddL(port););
if (err != OsclErrNone)
{
iInPortVector.DestructAndDealloc(port);
return PVMFErrNoMemory;
}
//pass the current clock settings to the port.
SetClock(iClock);
//cast the port to the generic interface before returning.
PVMFPortInterface*retval = port;
aEventData = (OsclAny*)retval;
return PVMFSuccess;
}
break;
default:
{
//bad port tag
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoRequestPort: Error - Invalid port tag"));
return PVMFFailure;
}
break;
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoInit(PVMediaOutputNodeCmd& aCmd)
{
OSCL_UNUSED_ARG(aCmd);
if (iInterfaceState != EPVMFNodeIdle)
return PVMFErrInvalidState;
if (!iMIOControl)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl is NULL"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIONotExist;
return PVMFFailure;
}
if (iMIOControl->connect(iMIOSession, this) != PVMFSuccess)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl->connect failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOConnect;
return PVMFFailure;
}
//Query for MIO interfaces.
return SendMioRequest(aCmd, EQueryClockExtension);
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoPrepare(PVMediaOutputNodeCmd& aCmd)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoPrepare"));
if (iInterfaceState != EPVMFNodeInitialized)
return PVMFErrInvalidState;
return SendMioRequest(aCmd, EInit);
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoStart(PVMediaOutputNodeCmd& aCmd)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoStart"));
if (iInterfaceState != EPVMFNodePrepared
&& iInterfaceState != EPVMFNodePaused)
return PVMFErrInvalidState;
//see whether to delay the MIO start command until config info
//is received on the port. When the config is received the
//port will call MioConfigured. Media output node start will
//not complete till MioConfigured is called.
if (iInPortVector.size() > 0 && iInPortVector[0]->iWaitForConfig)
{
return PVMFPending;
}
//Start the MIO
return SendMioRequest(aCmd, EStart);
}
////////////////////////////////////////////////////////////////////////////
void PVMediaOutputNode::MioConfigured()
//called by the port when the MIO was just configured.
//only called for ports that need to wait on config before starting
//the MIO.
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::MioConfigured() called"));
if (iCurrentCommand.front().iCmd == PVMF_GENERIC_NODE_START)
{
//implies that media output node start is waiting for
//media output comp to be configured
//Now that media output comp has been configured,
//start it
SendMioRequest(iCurrentCommand.front(), EStart);
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoStop(PVMediaOutputNodeCmd& aCmd)
{
//clear the message queues of any unprocessed data now.
uint32 i;
for (i = 0; i < iInPortVector.size(); i++)
{
iInPortVector[i]->ClearMsgQueues();
}
if (iInterfaceState != EPVMFNodeStarted
&& iInterfaceState != EPVMFNodePaused)
return PVMFErrInvalidState;
if (iMediaIOState == STATE_STARTED || iMediaIOState == STATE_PAUSED)
{
//Stop the MIO component only if MIOs are in Paused or Started states.
return SendMioRequest(aCmd, EStop);
}
else
{
// no stop needed if MIOs are not in started or paused states. return success.
return PVMFSuccess;
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoFlush(PVMediaOutputNodeCmd& aCmd)
{
OSCL_UNUSED_ARG(aCmd);
if (iInterfaceState != EPVMFNodeStarted
&& iInterfaceState != EPVMFNodePaused)
return PVMFErrInvalidState;
//Disable the input.
if (iInPortVector.size() > 0)
{
for (uint32 i = 0;i < iInPortVector.size();i++)
iInPortVector[i]->SuspendInput();
}
//wait for all data to be consumed
return PVMFPending;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoPause(PVMediaOutputNodeCmd& aCmd)
{
if (iInterfaceState != EPVMFNodeStarted)
return PVMFErrInvalidState;
return SendMioRequest(aCmd, EPause);
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoSkipMediaData(PVMediaOutputNodeCmd& aCmd)
{
//Here is what we do during skip:
//1) We pass the skiptimestamp to ports. This is to make sure that
//they can start tossing out data ASAP
//2) We send DiscardData to media output components. Failure to
//queue discarddata on mediaoutput comps is considered fatal failure
//and will result in skip failure on media output node
//3) Then we wait for all discard data to complete and all ports to
//report BOS. Ports call "ReportBOS" when they recv BOS.
PVMFTimestamp startingTimestamp, resumeTimestamp;
bool renderSkippedData ;
bool playbackpositioncontinuous;
uint32 streamID;
aCmd.Parse(startingTimestamp,
resumeTimestamp,
renderSkippedData,
playbackpositioncontinuous,
streamID);
iRecentBOSStreamID = streamID;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::DoSkipMediaData - Mime=%s, SkipTS=%d, StreamID=%d, SFR=%d",
iSinkFormatString.get_str(),
resumeTimestamp,
iRecentBOSStreamID,
playbackpositioncontinuous));
//although we treat inport as a vector, we still
//assume just one media output comp per node
if (iInPortVector.size() > 0)
{
iInPortVector[0]->SetSkipTimeStamp(resumeTimestamp, iRecentBOSStreamID);
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoSkipMediaData - No Input Ports - Mime=%s", iSinkFormatString.get_str()));
return PVMFErrInvalidState;
}
PVMFStatus status = PVMFFailure;
if (playbackpositioncontinuous == true)
{
//source node has not be involved in determining this skip
//boundaries. in this case if an EOS has been sent to the mio
//component, then do not call discard data
//assume that there is only one port per mio node and one
//mio comp per node.
//also do NOT ever call discard data in case of compressed mio
if (iInPortVector[0]->isUnCompressedMIO == true)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::DoSkipMediaData - SFR - Calling DiscardData - Mime=%s", iSinkFormatString.get_str()));
status = SendMioRequest(aCmd, EDiscard);
if (status != PVMFPending)
{
iMediaIORequest = ENone;
return status;
}
}
else
{
// For Compressed MIO, we need not call discard data and should report Skip Media Complete
// right away.
return PVMFSuccess;
}
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::DoSkipMediaData - Calling DiscardData - Mime=%s", iSinkFormatString.get_str()));
status = SendMioRequest(aCmd, EDiscard);
if (status != PVMFPending)
{
iMediaIORequest = ENone;
return status;
}
}
//wait on discard
return status;
}
void PVMediaOutputNode::CompleteSkipMediaData()
{
if ((!iCurrentCommand.empty()) &&
((iCurrentCommand.front().iCmd == PVMF_MEDIAOUTPUTNODE_SKIPMEDIADATA)))
{
if (iMediaIORequest == ENone)
{
//implies that discard is complete
if (CheckForBOS() == PVMFSuccess)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::CompleteSkipMediaData - SkipComplete - Mime=%s", iSinkFormatString.get_str()));
CommandComplete(iCurrentCommand, iCurrentCommand[0], PVMFSuccess);
for (uint32 i = 0; i < iInPortVector.size(); i++)
{
//clear out the bos stream id vec on all ports so that they is no confusion later on
iInPortVector[i]->ClearPreviousBOSStreamIDs(iRecentBOSStreamID);
}
return;
}
}
//else continue waiting on the discard to complete.
}
else
{
//this means that either skipmediadata cmd is yet to be issued or processed
//this should be a problem since ports record the BOS stream ids which would
//be checked later
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::CompleteSkipMediaData - Waiting On SkipCmd To Be Issued/Processed - Mime=%s", iSinkFormatString.get_str()));
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::CompleteSkipMediaData - Waiting On SkipCmd To Be Issued/Processed - Mime=%s", iSinkFormatString.get_str()));
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoCancelAllCommands(PVMediaOutputNodeCmd& aCmd)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoCancelAllCommands In"));
if (!iCurrentCommand.empty())
{
if (iCurrentCommand.front().iCmd == PVMF_MEDIAOUTPUTNODE_SKIPMEDIADATA)
{
for (uint32 i = 0;i < iInPortVector.size();i++)
{
iInPortVector[i]->CancelSkip();
}
}
}
//First cancel any MIO commmand in progress.
bool cancelmiopending = false;
if (iMediaIORequest != ENone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoCancelAllCommands Cancelling any pending MIO request"));
if (CancelMioRequest(aCmd) == PVMFPending)
{
cancelmiopending = true;
}
}
//Then cancel the current command if any
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoCancelAllCommands Cancelling current command"));
while (!iCurrentCommand.empty())
{
CommandComplete(iCurrentCommand, iCurrentCommand[0], PVMFErrCancelled);
}
}
//next cancel all queued commands
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::DoCancelAllCommands Cancelling all queued commands"));
//start at element 1 since this cancel command is element 0.
while (iInputCommands.size() > 1)
{
CommandComplete(iInputCommands, iInputCommands[1], PVMFErrCancelled);
}
}
if (cancelmiopending)
{
// Need to wait for MIO cancel to complete before completing
// the cancelall command
return PVMFPending;
}
else
{
//Cancel is complete
return PVMFSuccess;
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoCancelCommand(PVMediaOutputNodeCmd& aCmd)
{
//extract the command ID from the parameters.
PVMFCommandId id;
aCmd.PVMediaOutputNodeCmdBase::Parse(id);
//first check "current" command if any
{
PVMediaOutputNodeCmd* cmd = iCurrentCommand.FindById(id);
if (cmd)
{
if (cmd->iCmd == PVMF_MEDIAOUTPUTNODE_SKIPMEDIADATA)
{
//cancel any SkipMediaData in progress on the ports.
for (uint32 i = 0;i < iInPortVector.size();i++)
{
iInPortVector[i]->CancelSkip();
}
}
//Check if MIO request needs to be cancelled
bool pendingmiocancel = false;
if (iMediaIORequest != ENone)
{
if (CancelMioRequest(aCmd) == PVMFPending)
{
pendingmiocancel = true;
}
}
//Cancel the queued command
CommandComplete(iCurrentCommand, *cmd, PVMFErrCancelled);
if (pendingmiocancel)
{
// Wait for MIO cancel to complete
// before completing CancelCommand
return PVMFPending;
}
else
{
//report cancel success
return PVMFSuccess;
}
}
}
//next check input queue.
{
//start at element 1 since this cancel command is element 0.
PVMediaOutputNodeCmd* cmd = iInputCommands.FindById(id, 1);
if (cmd)
{
//cancel the queued command
CommandComplete(iInputCommands, *cmd, PVMFErrCancelled);
//report cancel success
return PVMFSuccess;
}
}
//if we get here the command isn't queued so the cancel fails.
aCmd.iEventCode = PVMFMoutNodeErr_CmdNotQueued;
return PVMFFailure;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoReleasePort(PVMediaOutputNodeCmd& aCmd)
{
//This node supports release port from any state
PVMFPortInterface* p;
aCmd.PVMediaOutputNodeCmdBase::Parse(p);
//search the input port vector
{
PVMediaOutputNodePort* port = (PVMediaOutputNodePort*)p;
PVMediaOutputNodePort** portPtr = iInPortVector.FindByValue(port);
if (portPtr)
{
(*portPtr)->Disconnect();
iInPortVector.Erase(portPtr);
return PVMFSuccess;
}
}
aCmd.iEventCode = PVMFMoutNodeErr_PortNotExist;
return PVMFFailure;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::DoReset(PVMediaOutputNodeCmd& aCmd)
{
OSCL_UNUSED_ARG(aCmd);
//delete all ports and notify observer.
while (!iInPortVector.empty())
iInPortVector.Erase(&iInPortVector.front());
//restore original port vector reserve.
iInPortVector.Reconstruct();
//logoff & go back to Created state.
SetState(EPVMFNodeIdle);
PVMFStatus status = ThreadLogoff();
//currently thread logoff can't fail.
//if any error returns are added, then a node error event should
//be added also.
Assert(status == PVMFSuccess);
return status;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::CancelMioRequest(PVMediaOutputNodeCmd& aCmd)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMediaOutputNode::CancelMioRequest In"));
Assert(iMediaIORequest != ENone);
Assert(!iMediaIOCancelPending);
//Issue the cancel to the MIO.
iMediaIOCancelPending = true;
int32 err;
OSCL_TRY(err, iMediaIOCancelCmdId = iMIOControl->CancelCommand(iMediaIOCmdId););
if (err != OsclErrNone)
{
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOCancelCommand;
iMediaIOCancelPending = false;
LOGINFOHI((0, "PVMediaOutputNode::CancelMioRequest: CancelCommand on MIO failed"));
return PVMFFailure;
}
LOGINFOHI((0, "PVMediaOutputNode::CancelMioRequest: Cancel Command Issued to MIO component, waiting on response..."));
return PVMFPending;//wait on request to cancel.
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMediaOutputNode::SendMioRequest(PVMediaOutputNodeCmd& aCmd, EMioRequest aRequest)
{
//Make an asynchronous request to MIO component
//and save the request in iMediaIORequest.
//there should not be a MIO command in progress..
Assert(iMediaIORequest == ENone);
//save media io request.
iMediaIORequest = aRequest;
PVMFStatus status;
//Issue the command to the MIO.
switch (aRequest)
{
case EQueryCapability:
{
int32 err ;
OSCL_TRY(err,
iMediaIOCmdId = iMIOControl->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID,
(PVInterface*&)iMIOConfig, NULL);
);
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl->QueryInterface(cap & config) failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOQueryCapConfigInterface;
status = PVMFFailure;
}
else
{
status = PVMFPending;
}
}
break;
case EQueryClockExtension:
{
int32 err ;
OSCL_TRY(err,
iMediaIOCmdId = iMIOControl->QueryInterface(PvmiClockExtensionInterfaceUuid,
(PVInterface*&)iMIOClockExtension, NULL);
);
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error iMIOControl->QueryInterface(clock ext) failed"));
//this interface is optional so ignore the error
status = PVMFSuccess;
}
else
{
status = PVMFPending;
}
}
break;
case EInit:
{
int32 err ;
OSCL_TRY(err, iMediaIOCmdId = iMIOControl->Init(););
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl->Init failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOInit;
status = PVMFFailure;
}
else
{
status = PVMFPending;
}
}
break;
case EStart:
{
int32 err ;
OSCL_TRY(err, iMediaIOCmdId = iMIOControl->Start(););
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl->Start failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOStart;
status = PVMFFailure;
}
else
{
status = PVMFPending;
}
}
break;
case EPause:
{
int32 err ;
OSCL_TRY(err, iMediaIOCmdId = iMIOControl->Pause(););
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl->Pause failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOPause;
status = PVMFFailure;
}
else
{
status = PVMFPending;
}
}
break;
case EStop:
{
int32 err ;
OSCL_TRY(err, iMediaIOCmdId = iMIOControl->Stop(););
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::DoInit: Error - iMIOControl->Stop failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIOStop;
status = PVMFFailure;
}
else
{
status = PVMFPending;
}
}
break;
case EDiscard:
{
int32 err;
PVMFTimestamp startingTimestamp, resumeTimestamp;
bool renderSkippedData;
bool playbackpositioncontinuous;
uint32 streamId;
aCmd.Parse(startingTimestamp, resumeTimestamp, renderSkippedData, playbackpositioncontinuous, streamId);
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::SendMioRequest(EDiscard): skipTimestamp=%d", resumeTimestamp));
OSCL_TRY(err, iMediaIOCmdId = iMIOControl->DiscardData(resumeTimestamp););
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMediaOutputNode::SendMioRequest: Error - iMIOControl->DiscardData failed"));
aCmd.iEventCode = PVMFMoutNodeErr_MediaIODiscardData;
status = PVMFFailure;
}
else
{
status = PVMFPending;
}
}
break;
default:
Assert(false);//unrecognized command.
status = PVMFFailure;
break;
}
if (status == PVMFPending)
{
LOGINFOHI((0, "PVMediaOutputNode:SendMIORequest: Command Issued to MIO component, waiting on response..."));
}
return status;
}
/////////////////////////////////////////////////////
// Event reporting routines.
/////////////////////////////////////////////////////
void PVMediaOutputNode::SetState(TPVMFNodeInterfaceState s)
{
LOGINFO((0, "PVMediaOutputNode:SetState %d", s));
PVMFNodeInterface::SetState(s);
}
void PVMediaOutputNode::ReportErrorEvent(PVMFEventType aEventType, OsclAny* aEventData, PVMFStatus aEventCode)
{
LOGERROR((0, "PVMediaOutputNode:NodeErrorEvent Type %d EVData %d EVCode %d"
, aEventType, aEventData, aEventCode));
//create the extension message if any.
if (aEventCode != PVMFMoutNodeErr_First)
{
PVMFBasicErrorInfoMessage* eventmsg = OSCL_NEW(PVMFBasicErrorInfoMessage, (aEventCode, iEventUuid, NULL));
PVMFAsyncEvent asyncevent(PVMFErrorEvent, aEventType, NULL, OSCL_STATIC_CAST(PVInterface*, eventmsg), aEventData, NULL, 0);
PVMFNodeInterface::ReportErrorEvent(asyncevent);
eventmsg->removeRef();
}
else
PVMFNodeInterface::ReportErrorEvent(aEventType, aEventData);
}
void PVMediaOutputNode::ReportInfoEvent(PVMFEventType aEventType, OsclAny* aEventData, PVMFStatus aEventCode)
{
LOGINFO((0, "PVMediaOutputNode:NodeInfoEvent Type %d EVData %d EVCode %d"
, aEventType, aEventData, aEventCode));
//create the extension message if any.
if (aEventCode != PVMFMoutNodeErr_First)
{
PVMFBasicErrorInfoMessage* eventmsg = OSCL_NEW(PVMFBasicErrorInfoMessage, (aEventCode, iEventUuid, NULL));
PVMFAsyncEvent asyncevent(PVMFErrorEvent, aEventType, NULL, OSCL_STATIC_CAST(PVInterface*, eventmsg), aEventData, NULL, 0);
PVMFNodeInterface::ReportInfoEvent(asyncevent);
eventmsg->removeRef();
}
else
PVMFNodeInterface::ReportInfoEvent(aEventType, aEventData);
}
OSCL_EXPORT_REF PVMFStatus PVMediaOutputNode::verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements)
{
OSCL_UNUSED_ARG(aSession);
return (iMIOConfig->verifyParametersSync(iMIOSession, aParameters, num_elements));
}
void PVMediaOutputNode::ReportBOS()
{
CompleteSkipMediaData();
}
PVMFStatus PVMediaOutputNode::CheckForBOS()
{
Oscl_Vector<uint32, OsclMemAllocator>::iterator it;
for (it = iInPortVector[0]->iBOSStreamIDVec.begin();
it != iInPortVector[0]->iBOSStreamIDVec.end();
it++)
{
if (*it == iRecentBOSStreamID)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iReposLogger, PVLOGMSG_INFO,
(0, "PVMediaOutputNode::CheckForBOS - BOS Found - Mime=%s, BOSStreamID=%d",
iSinkFormatString.get_str(), iRecentBOSStreamID));
//we have recvd BOS
return PVMFSuccess;
}
}
return PVMFPending;
}