/* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #include "pvmf_downloadmanager_node.h" #include "pvmf_download_data_source.h" #include "pvmf_local_data_source.h" #include "pvmf_protocol_engine_factory.h" #include "pvmf_socket_factory.h" #include "pvmf_socket_node.h" #include "pvlogger.h" #include "oscl_error_codes.h" #include "oscl_str_ptr_len.h" // for OSCL_ASCII_CASE_MAGIC_BIT #include "pvmi_datastreamuser_interface.h" #include "pvpvxparser.h" #include "pv_mime_string_utils.h" #include "pvmi_kvp_util.h" #include "pvmf_source_context_data.h" //Log levels for node commands #define CMD_LOG_LEVEL PVLOGMSG_INFO //Log levels for subnode commands. #define SUB_CMD_LOG_LEVEL PVLOGMSG_INFO /////////////////////////////////////////////////////////////////////////////// // // Capability and config interface related constants and definitions // - based on pv_player_engine.h // /////////////////////////////////////////////////////////////////////////////// static const DownloadManagerKeyStringData DownloadManagerConfig_BaseKeys[] = { {"user-agent", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_WCHARPTR}, {"http-version", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, {"http-timeout", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, {"download-progress-info", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_CHARPTR}, {"protocol-extension-header", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_CHARPTR}, {"num-redirect-attempts", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, {"http-header-request-disabled", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_BOOL}, {"max-tcp-recv-buffer-size-download", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, {"max-tcp-recv-buffer-count-download", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32} }; static const uint DownloadManagerConfig_NumBaseKeys = (sizeof(DownloadManagerConfig_BaseKeys) / sizeof(DownloadManagerKeyStringData)); enum BaseKeys_IndexMapType { BASEKEY_SESSION_CONTROLLER_USER_AGENT = 0, BASEKEY_SESSION_CONTROLLER_HTTP_VERSION, BASEKEY_SESSION_CONTROLLER_HTTP_TIMEOUT, BASEKEY_SESSION_CONTROLLER_DOWNLOAD_PROGRESS_INFO, BASEKEY_SESSION_CONTROLLER_PROTOCOL_EXTENSION_HEADER, BASEKEY_SESSION_CONTROLLER_NUM_REDIRECT_ATTEMPTS, BASEKEY_SESSION_CONTROLLER_NUM_HTTP_HEADER_REQUEST_DISABLED, BASEKEY_MAX_TCP_RECV_BUFFER_SIZE, BASEKEY_MAX_TCP_RECV_BUFFER_COUNT }; PVMFDownloadManagerNode::PVMFDownloadManagerNode(int32 aPriority) : OsclActiveObject(aPriority, "PVMFDownloadManagerNode") { int32 err; OSCL_TRY(err, ConstructL();); if (err != OsclErrNone) { //if a leave happened, cleanup and re-throw the error iInputCommands.clear(); iCurrentCommand.clear(); iCancelCommand.clear(); iCapability.iInputFormatCapability.clear(); iCapability.iOutputFormatCapability.clear(); OSCL_CLEANUP_BASE_CLASS(PVMFNodeInterface); OSCL_CLEANUP_BASE_CLASS(OsclActiveObject); OSCL_LEAVE(err); } iDNodeUuids.clear(); iDNodeUuidCount = 0; } void PVMFDownloadManagerNode::ConstructL() { iDebugMode = false; iLogger = NULL; iExtensionRefCount = 0; iSourceFormat = PVMF_MIME_FORMAT_UNKNOWN; iMimeType = PVMF_MIME_FORMAT_UNKNOWN; iSourceData = NULL; iPlayBackClock = NULL; iClockNotificationsInf = NULL; iNoPETrackSelect = false; iMovieAtomComplete = false; iParserInitAfterMovieAtom = false; iParserPrepareAfterMovieAtom = false; iParserInit = false; iDataReady = false; iDownloadComplete = false; iRecognizerError = false; iInitFailedLicenseRequired = false; iProtocolEngineNodePort = NULL; iSocketNodePort = NULL; iPlayerNodeRegistry = NULL; //create the sub-node command queue. Use a reserve to avoid dynamic memory failure later. //Max depth is the max number of sub-node commands for any one node command. Init command may take up to 15 iSubNodeCmdVec.reserve(15); //Create the input command queue. Max depth is undetermined -- just reserve 10. iInputCommands.Construct(1000 //start cmd id , 10);//reserve. //Create the "current command" queue. Max depth is 1 for each of these. iCurrentCommand.Construct(0, 1); iCancelCommand.Construct(0, 1); //create node containers. //@TODO this will create unused node containers. think about //optimizing it. iFormatParserNode.Construct(PVMFDownloadManagerSubNodeContainerBase::EFormatParser, this); iProtocolEngineNode.Construct(PVMFDownloadManagerSubNodeContainerBase::EProtocolEngine, this); iSocketNode.Construct(PVMFDownloadManagerSubNodeContainerBase::ESocket, this); iRecognizerNode.Construct(PVMFDownloadManagerSubNodeContainerBase::ERecognizer, this); //Set the node capability data. iCapability.iCanSupportMultipleInputPorts = false; iCapability.iCanSupportMultipleOutputPorts = true; iCapability.iHasMaxNumberOfPorts = true; iCapability.iMaxNumberOfPorts = 6; iCapability.iInputFormatCapability.push_back(PVMF_MIME_MPEG4FF); iCapability.iInputFormatCapability.push_back(PVMF_MIME_ASFFF); iCapability.iInputFormatCapability.push_back(PVMF_MIME_RMFF); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_AMR_IETF); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_MPEG4_AUDIO); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_M4V); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H2631998); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H2632000); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_REAL_VIDEO); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_WMV); iCapability.iOutputFormatCapability.push_back(PVMF_MIME_DIVXFF); iFileBufferDatastreamFactory = NULL; #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) iMemoryBufferDatastreamFactory = NULL; #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB iDownloadFileName = NULL; iContentTypeMIMEString = NULL; iProtocolEngineNode.iNode = PVMFProtocolEngineNodeFactory::CreatePVMFProtocolEngineNode(OsclActiveObject::EPriorityNominal); OsclError::LeaveIfNull(iProtocolEngineNode.iNode); iProtocolEngineNode.Connect(); iSocketNode.iNode = PVMFSocketNodeFactory::CreatePVMFSocketNode(OsclActiveObject::EPriorityNominal); OsclError::LeaveIfNull(iSocketNode.iNode); iSocketNode.Connect(); } PVMFDownloadManagerNode::~PVMFDownloadManagerNode() { if (iPlayBackClock != NULL) { if (iClockNotificationsInf != NULL) { iClockNotificationsInf->RemoveClockStateObserver(*this); iPlayBackClock->DestroyMediaClockNotificationsInterface(iClockNotificationsInf); } } Cancel(); if (IsAdded()) RemoveFromScheduler(); //if any sub-node commands are outstanding, there will be //a crash when they callback-- so panic here instead. if (iFormatParserNode.CmdPending() || iProtocolEngineNode.CmdPending() || iSocketNode.CmdPending() || iRecognizerNode.CmdPending() ) { OSCL_ASSERT(0); } //this is to ensure that there are no more callbacks from PE node to parser node, //in case parser node had some outstanding request resume notifications if (iProtocolEngineNode.DownloadProgress() != NULL) { (iProtocolEngineNode.DownloadProgress())->setFormatDownloadSupportInterface(NULL); } //make sure the subnodes got cleaned up iFormatParserNode.Cleanup(); iProtocolEngineNode.Cleanup(); iSocketNode.Cleanup(); iRecognizerNode.Cleanup(); //delete the subnodes if (iFormatParserNode.iNode) { iDNodeUuidCount--; bool release_status = false; int32 leavecode = 0; OSCL_TRY(leavecode, release_status = iPlayerNodeRegistry->ReleaseNode(iDNodeUuids[iDNodeUuidCount], iFormatParserNode.iNode)); //ignore errors. iDNodeUuids.clear(); } if (iProtocolEngineNode.iNode) PVMFProtocolEngineNodeFactory::DeletePVMFProtocolEngineNode(iProtocolEngineNode.iNode); if (iSocketNode.iNode) PVMFSocketNodeFactory::DeletePVMFSocketNode(iSocketNode.iNode); // delete the data stream factory (This has to come after deleting anybody who uses it, like the protocol engine node or the parser node.) if (iFileBufferDatastreamFactory) { OSCL_DELETE(iFileBufferDatastreamFactory); iFileBufferDatastreamFactory = NULL; } #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) if (iMemoryBufferDatastreamFactory) { OSCL_DELETE(iMemoryBufferDatastreamFactory); iMemoryBufferDatastreamFactory = NULL; } #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB //The command queues are self-deleting, but we want to notify the observer of unprocessed commands. while (!iCurrentCommand.empty()) CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFFailure, NULL, NULL); while (!iCancelCommand.empty()) CommandComplete(iCancelCommand, iCancelCommand.front(), PVMFFailure, NULL, NULL); while (!iInputCommands.empty()) CommandComplete(iInputCommands, iInputCommands.front(), PVMFFailure, NULL, NULL); } //Public API From node interface. PVMFStatus PVMFDownloadManagerNode::ThreadLogon() { if (iInterfaceState != EPVMFNodeCreated) return PVMFErrInvalidState; //logon this node. if (!IsAdded()) AddToScheduler(); iLogger = PVLogger::GetLoggerObject("pvdownloadmanagernode"); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::ThreadLogon() called")); //logon the sub-nodes. if (iProtocolEngineNode.iNode) iProtocolEngineNode.iNode->ThreadLogon(); if (iSocketNode.iNode) iSocketNode.iNode->ThreadLogon(); ChangeNodeState(EPVMFNodeIdle); return PVMFSuccess; } //Public API From node interface. PVMFStatus PVMFDownloadManagerNode::ThreadLogoff() { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::ThreadLogoff() called")); if (iInterfaceState != EPVMFNodeIdle) return PVMFErrInvalidState; //logoff this node. if (IsAdded()) RemoveFromScheduler(); iLogger = NULL; //logoff the sub-nodes. if (iFormatParserNode.iNode) iFormatParserNode.iNode->ThreadLogoff(); if (iProtocolEngineNode.iNode) iProtocolEngineNode.iNode->ThreadLogoff(); if (iSocketNode.iNode) iSocketNode.iNode->ThreadLogoff(); ChangeNodeState(EPVMFNodeCreated); return PVMFSuccess; } //Public API From node interface. PVMFStatus PVMFDownloadManagerNode::GetCapability(PVMFNodeCapability& aNodeCapability) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::GetCapability() called")); aNodeCapability = iCapability; return PVMFSuccess; } //Public API From node interface. PVMFPortIter* PVMFDownloadManagerNode::GetPorts(const PVMFPortFilter* aFilter) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::GetPorts() called")); if (iFormatParserNode.iNode) return iFormatParserNode.iNode->GetPorts(aFilter); return NULL; } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::QueryUUID(PVMFSessionId aSessionId, const PvmfMimeString& aMimeType, Oscl_Vector& aUuids, bool aExactUuidsOnly, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::QueryUUID() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_QUERYUUID, aMimeType, aUuids, aExactUuidsOnly, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::QueryInterface(PVMFSessionId aSessionId, const PVUuid& aUuid, PVInterface*& aInterfacePtr, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::QueryInterface() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::RequestPort(PVMFSessionId aSessionId, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::RequestPort() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_REQUESTPORT, aPortTag, aPortConfig, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFStatus PVMFDownloadManagerNode::ReleasePort(PVMFSessionId aSessionId, PVMFPortInterface& aPort, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::ReleasePort() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_RELEASEPORT, aPort, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Init(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Init() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_INIT, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Prepare(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Prepare() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_PREPARE, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Start(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Start() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_START, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Stop(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Stop() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_STOP, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Flush(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Flush() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_FLUSH, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Pause(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Pause() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_PAUSE, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::Reset(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::Reset() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_RESET, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::CancelAllCommands(PVMFSessionId aSessionId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::CancelAllCommands() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_CANCELALLCOMMANDS, aContext); return QueueCommandL(cmd); } //Public API From node interface. PVMFCommandId PVMFDownloadManagerNode::CancelCommand(PVMFSessionId aSessionId, PVMFCommandId aCmdId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::CancelCommand() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_CANCELCOMMAND, aCmdId, aContext); return QueueCommandL(cmd); } //public API from PVInterface void PVMFDownloadManagerNode::addRef() { ++iExtensionRefCount; } //public API from PVInterface void PVMFDownloadManagerNode::removeRef() { --iExtensionRefCount; } //public API from PVInterface bool PVMFDownloadManagerNode::queryInterface(const PVUuid& uuid, PVInterface*& iface) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::queryInterface() In")); if (uuid == PVMF_TRACK_SELECTION_INTERFACE_UUID) { PVMFTrackSelectionExtensionInterface* myInterface = OSCL_STATIC_CAST(PVMFTrackSelectionExtensionInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else if (uuid == PVMF_DATA_SOURCE_INIT_INTERFACE_UUID) { PVMFDataSourceInitializationExtensionInterface* myInterface = OSCL_STATIC_CAST(PVMFDataSourceInitializationExtensionInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else if (uuid == KPVMFMetadataExtensionUuid) { PVMFMetadataExtensionInterface* myInterface = OSCL_STATIC_CAST(PVMFMetadataExtensionInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else if (uuid == PVMF_DATA_SOURCE_NODE_REGISRTY_INIT_INTERFACE_UUID) { PVMFDataSourceNodeRegistryInitInterface* myInterface = OSCL_STATIC_CAST(PVMFDataSourceNodeRegistryInitInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else if (uuid == PvmfDataSourcePlaybackControlUuid) { PvmfDataSourcePlaybackControlInterface* myInterface = OSCL_STATIC_CAST(PvmfDataSourcePlaybackControlInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else if (uuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID) { PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else if (uuid == PVMFCPMPluginLicenseInterfaceUuid) { PVMFCPMPluginLicenseInterface* myInterface = OSCL_STATIC_CAST(PVMFCPMPluginLicenseInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); } else { return false; } ++iExtensionRefCount; return true; } //public API from data source initialization interface PVMFStatus PVMFDownloadManagerNode::SetSourceInitializationData(OSCL_wString& aSourceURL, PVMFFormatType& aSourceFormat, OsclAny* aSourceData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::SetSourceInitializationData() called")); //this method must be called before the Init command. if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeCreated) return PVMFErrInvalidState; // Pass the source info directly to the protocol engine node. if (!iProtocolEngineNode.DataSourceInit()) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Can't find datasourceinit interface in protocol engine subnode container.")); return PVMFFailure; //no source init interface. } PVMFStatus status = (iProtocolEngineNode.DataSourceInit())->SetSourceInitializationData(aSourceURL, aSourceFormat, aSourceData); if (status != PVMFSuccess) return status; if (!iProtocolEngineNode.ProtocolEngineExtension()) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Can't get ProtocolEngineExtension interface from protocol subnode container.")); return PVMFFailure; //no ProtocolNodeExtension interface. } bool socketConfigOK = (iProtocolEngineNode.ProtocolEngineExtension())->GetSocketConfig(iServerAddr); if (!socketConfigOK) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode: SetSourceInitializationData() Call to GetSocketConfig() on protocol engine node returned failure.")); return PVMFErrProcessing; } if (aSourceFormat == PVMF_MIME_DATA_SOURCE_HTTP_URL) { if (!aSourceData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Missing source data")); return PVMFErrArgument; } PVInterface* pvinterface = (PVInterface*)aSourceData; PVUuid uuid(PVMF_DOWNLOAD_DATASOURCE_HTTP_UUID); PVInterface* temp = NULL; if (pvinterface->queryInterface(uuid, temp)) { PVMFDownloadDataSourceHTTP* data = OSCL_STATIC_CAST(PVMFDownloadDataSourceHTTP*, temp); //extract the download file name from the opaque data. iDownloadFileName = data->iDownloadFileName; //extract the playback mode switch (data->iPlaybackControl) { case PVMFDownloadDataSourceHTTP::ENoPlayback: iPlaybackMode = EDownloadOnly; break; case PVMFDownloadDataSourceHTTP::EAfterDownload: iPlaybackMode = EDownloadThenPlay; break; case PVMFDownloadDataSourceHTTP::EAsap: iPlaybackMode = EPlayAsap; break; case PVMFDownloadDataSourceHTTP::ENoSaveToFile: #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) iPlaybackMode = EPlaybackOnly; break; #else PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() NoSaveToFile is not supported!")); return PVMFErrArgument;//unsupported mode. #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB default: iPlaybackMode = EPlayAsap; break; } } else { PVUuid uuid(PVMF_SOURCE_CONTEXT_DATA_DOWNLOAD_HTTP_UUID); temp = NULL; if (pvinterface->queryInterface(uuid, temp)) { PVMFSourceContextDataDownloadHTTP* data = OSCL_STATIC_CAST(PVMFSourceContextDataDownloadHTTP*, temp); //extract the download file name from the opaque data. iDownloadFileName = data->iDownloadFileName; //extract the playback mode switch (data->iPlaybackControl) { case PVMFSourceContextDataDownloadHTTP::ENoPlayback: iPlaybackMode = EDownloadOnly; break; case PVMFSourceContextDataDownloadHTTP::EAfterDownload: iPlaybackMode = EDownloadThenPlay; break; case PVMFSourceContextDataDownloadHTTP::EAsap: iPlaybackMode = EPlayAsap; break; case PVMFSourceContextDataDownloadHTTP::ENoSaveToFile: #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) iPlaybackMode = EPlaybackOnly; break; #else PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() NoSaveToFile is not supported!")); return PVMFErrArgument;//unsupported mode. #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB default: iPlaybackMode = EPlayAsap; break; } } else {//invalid source data return PVMFErrArgument; } } } else if (aSourceFormat == PVMF_MIME_DATA_SOURCE_PVX_FILE) { if (!aSourceData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Missing source data")); return PVMFErrArgument; } PVInterface* pvinterface = (PVInterface*)aSourceData; PVUuid uuid(PVMF_DOWNLOAD_DATASOURCE_PVX_UUID); PVInterface* temp = NULL; if (pvinterface->queryInterface(uuid, temp)) { PVMFDownloadDataSourcePVX* data = OSCL_STATIC_CAST(PVMFDownloadDataSourcePVX*, temp); iDownloadFileName = data->iDownloadFileName; //get the playback mode from the PVX info switch (data->iPvxInfo.iPlaybackControl) { case CPVXInfo::ENoPlayback: iPlaybackMode = EDownloadOnly; break; case CPVXInfo::EAfterDownload: iPlaybackMode = EDownloadThenPlay; break; case CPVXInfo::EAsap: iPlaybackMode = EPlayAsap; break; default: iPlaybackMode = EPlayAsap; break; } } else { PVUuid uuid(PVMF_SOURCE_CONTEXT_DATA_DOWNLOAD_PVX_UUID); temp = NULL; if (pvinterface->queryInterface(uuid, temp)) { PVMFSourceContextDataDownloadPVX* data = OSCL_STATIC_CAST(PVMFSourceContextDataDownloadPVX*, temp); iDownloadFileName = data->iDownloadFileName; if (!data->iPvxInfo) {//invalid source data return PVMFErrArgument; } //get the playback mode from the PVX info switch (data->iPvxInfo->iPlaybackControl) { case CPVXInfo::ENoPlayback: iPlaybackMode = EDownloadOnly; break; case CPVXInfo::EAfterDownload: iPlaybackMode = EDownloadThenPlay; break; case CPVXInfo::EAsap: iPlaybackMode = EPlayAsap; break; default: iPlaybackMode = EPlayAsap; break; } } else {//invalid source data return PVMFErrArgument; } } } else if (aSourceFormat == PVMF_MIME_DATA_SOURCE_SHOUTCAST_URL) { if (!aSourceData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Missing source data")); return PVMFErrArgument; } PVInterface* pvinterface = (PVInterface*)aSourceData; PVUuid uuid(PVMF_DOWNLOAD_DATASOURCE_HTTP_UUID); PVInterface* temp = NULL; if (pvinterface->queryInterface(uuid, temp)) { PVMFDownloadDataSourceHTTP* data = OSCL_STATIC_CAST(PVMFDownloadDataSourceHTTP*, temp); //extract the download file name from the opaque data. iDownloadFileName = data->iDownloadFileName; //extract the playback mode switch (data->iPlaybackControl) { case PVMFDownloadDataSourceHTTP::ENoSaveToFile: #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) iPlaybackMode = EPlaybackOnly; break; #else PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() NoSaveToFile is not supported!")); return PVMFErrArgument;//unsupported mode. #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB default: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Only NoSaveToFile mode is supported for PVMF_MIME_DATA_SOURCE_SHOUTCAST_URL!")); return PVMFErrArgument;//unsupported mode. break; } } else { PVUuid uuid(PVMF_SOURCE_CONTEXT_DATA_DOWNLOAD_HTTP_UUID); if (pvinterface->queryInterface(uuid, temp)) { PVMFSourceContextDataDownloadHTTP* data = OSCL_STATIC_CAST(PVMFSourceContextDataDownloadHTTP*, temp); //extract the download file name from the opaque data. iDownloadFileName = data->iDownloadFileName; //extract the playback mode switch (data->iPlaybackControl) { case PVMFSourceContextDataDownloadHTTP::ENoSaveToFile: #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) iPlaybackMode = EPlaybackOnly; break; #else PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() NoSaveToFile is not supported!")); return PVMFErrArgument;//unsupported mode. #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB default: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Only NoSaveToFile mode is supported for PVMF_MIME_DATA_SOURCE_SHOUTCAST_URL!")); return PVMFErrArgument;//unsupported mode. break; } } else {//invalid source data return PVMFErrArgument; } } } else { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode:SetSourceInitializationData() Unsupported source type")); return PVMFErrArgument; } #if(PVMF_DOWNLOADMANAGER_SUPPORT_PPB) //Configure the MBDS if (iPlaybackMode == EPlaybackOnly) { // make sure we have enough TCP buffers for PPB and shoutcast if (aSourceFormat == PVMF_MIME_DATA_SOURCE_SHOUTCAST_URL) { // calculate MBDS cache size in bytes // max bitrate in bytes per second * cache size in secs uint32 bitRate = PVMF_DOWNLOADMANAGER_MAX_BITRATE_FOR_SC * 1000 / 8; uint32 cacheSize = bitRate * PVMF_DOWNLOADMANAGER_CACHE_SIZE_FOR_SC_IN_SECONDS; if (iSocketNode.iNode) { // TCP buffer size for shoutcast is 1564 (1500 data + 64 overhead) // add 1 second margin ((PVMFSocketNode*)iSocketNode.iNode)->SetMaxTCPRecvBufferCount((cacheSize + bitRate) / PVMF_DOWNLOADMANAGER_TCP_BUFFER_SIZE_FOR_SC); ((PVMFSocketNode*)iSocketNode.iNode)->SetMaxTCPRecvBufferSize(PVMF_DOWNLOADMANAGER_TCP_BUFFER_SIZE_FOR_SC + PVMF_DOWNLOADMANAGER_TCP_BUFFER_OVERHEAD); } // Use Memory Buffer Data Stream for progressive playback and Shoutcast iMemoryBufferDatastreamFactory = OSCL_NEW(PVMFMemoryBufferDataStream, (aSourceFormat, cacheSize)); } else { uint32 bufSize = PVMF_DOWNLOADMANAGER_TCP_BUFFER_SIZE_FOR_PPB; if (iSocketNode.iNode) { ((PVMFSocketNode*)iSocketNode.iNode)->SetMaxTCPRecvBufferCount(PVMF_DOWNLOADMANAGER_MIN_TCP_BUFFERS_FOR_PPB); // get buffer size ((PVMFSocketNode*)iSocketNode.iNode)->GetMaxTCPRecvBufferSize(bufSize); } // MBDS cache size calculation // TCP buffer size is 64000 (the default), assume worst case that the average packet size is 250 bytes // Packet overhead is 64 bytes per packet // 8 buffers will yield a cache of 305500, 13 buffers will yield a cache of 560500 uint32 totalPoolSizeMinusTwoBuffers = (PVMF_DOWNLOADMANAGER_MIN_TCP_BUFFERS_FOR_PPB - PVMF_DOWNLOADMANAGER_TCP_BUFFER_NOT_AVAILABLE) * bufSize; uint32 numPacketsToFitInPool = totalPoolSizeMinusTwoBuffers / (PVMF_DOWNLOADMANAGER_TCP_AVG_SMALL_PACKET_SIZE + PVMF_DOWNLOADMANAGER_TCP_BUFFER_OVERHEAD); uint32 maxDataMinusOverheadInPool = numPacketsToFitInPool * PVMF_DOWNLOADMANAGER_TCP_AVG_SMALL_PACKET_SIZE; // Use Memory Buffer Data Stream for progressive playback and Shoutcast iMemoryBufferDatastreamFactory = OSCL_NEW(PVMFMemoryBufferDataStream, (aSourceFormat, maxDataMinusOverheadInPool)); } OSCL_ASSERT(iMemoryBufferDatastreamFactory != NULL); iReadFactory = iMemoryBufferDatastreamFactory->GetReadDataStreamFactoryPtr(); iWriteFactory = iMemoryBufferDatastreamFactory->GetWriteDataStreamFactoryPtr(); } else #endif//PVMF_DOWNLOADMANAGER_SUPPORT_PPB { // Now that we have the download file name, we can instantiate the file buffer data stream object // Create the filebuffer data stream factory iFileBufferDatastreamFactory = OSCL_NEW(PVMFFileBufferDataStream, (iDownloadFileName)); OSCL_ASSERT(iFileBufferDatastreamFactory != NULL); iReadFactory = iFileBufferDatastreamFactory->GetReadDataStreamFactoryPtr(); iWriteFactory = iFileBufferDatastreamFactory->GetWriteDataStreamFactoryPtr(); } //save the source info iSourceFormat = aSourceFormat; iSourceURL = aSourceURL; iSourceData = aSourceData; return PVMFSuccess; } //public API from data source initialization interface PVMFStatus PVMFDownloadManagerNode::SetClientPlayBackClock(PVMFMediaClock* aClientClock) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::SetClientPlayBackClock() called")); iPlayBackClock = aClientClock; if (iPlayBackClock) { iPlayBackClock->ConstructMediaClockNotificationsInterface(iClockNotificationsInf, *this); } if (iClockNotificationsInf != NULL) { iClockNotificationsInf->SetClockStateObserver(*this); } //pass the source info directly to the download node. if (NULL == iProtocolEngineNode.DataSourceInit()) return PVMFFailure;//no source init interface. PVMFStatus status = (iProtocolEngineNode.DataSourceInit())->SetClientPlayBackClock(aClientClock); return status; } //public API from data source initialization interface PVMFStatus PVMFDownloadManagerNode::SetEstimatedServerClock(PVMFMediaClock*) { //not needed for download. return PVMFErrNotSupported; } PVMFDownloadManagerSubNodeContainer& PVMFDownloadManagerNode::TrackSelectNode() { //Decide which sub-node is supporting track selection. if (iSourceFormat == PVMF_MIME_DATA_SOURCE_PVX_FILE) { //for pvx file, the PE node may or may not do track selection. //the final decision isn't available until PE node prepare is done //and we've queried for the TS interface, at which point the //iNoPETrackSelect may be set. if (iNoPETrackSelect) return iFormatParserNode; //if download is already complete, such as after a stop, then //the parser node will do track selection. if (iDownloadComplete && iPlaybackMode != EDownloadOnly) return iFormatParserNode; return iProtocolEngineNode; } else { //for 3gpp & shoutcast, parser does track selection. return iFormatParserNode; } } //Public API From track selection interface. PVMFStatus PVMFDownloadManagerNode::GetMediaPresentationInfo(PVMFMediaPresentationInfo& aInfo) { //this is assumed to happen only after node initialization. if (iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; if (TrackSelectNode().TrackSelection()) return (TrackSelectNode().TrackSelection())->GetMediaPresentationInfo(aInfo); else return PVMFFailure; //no track selection interface! } //Public API From track selection interface. PVMFStatus PVMFDownloadManagerNode::SelectTracks(PVMFMediaPresentationInfo& aInfo) { //this needs to happen after initialization. if (iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; if (TrackSelectNode().TrackSelection()) return (TrackSelectNode().TrackSelection())->SelectTracks(aInfo); else return PVMFFailure;//no track selection interface! } uint32 PVMFDownloadManagerNode::GetNumMetadataKeys(char* query_key) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetNumMetadataKeys() called")); if (iFormatParserNode.Metadata()) { return (iFormatParserNode.Metadata())->GetNumMetadataKeys(query_key); } return 0; } uint32 PVMFDownloadManagerNode::GetNumMetadataValues(PVMFMetadataList& aKeyList) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetNumMetadataValues() called")); if (iFormatParserNode.Metadata()) { return (iFormatParserNode.Metadata())->GetNumMetadataValues(aKeyList); } return 0; } PVMFCommandId PVMFDownloadManagerNode::GetNodeMetadataKeys(PVMFSessionId aSessionId, PVMFMetadataList& aKeyList, uint32 starting_index, int32 max_entries, char* query_key, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetNodeMetadataKeys() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_GETNODEMETADATAKEY, aKeyList, starting_index, max_entries, query_key, aContext); return QueueCommandL(cmd); } PVMFCommandId PVMFDownloadManagerNode::GetNodeMetadataValues(PVMFSessionId aSessionId, PVMFMetadataList& aKeyList, Oscl_Vector& aValueList, uint32 starting_index, int32 max_entries, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetNodeMetadataValue() called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_GETNODEMETADATAVALUE, aKeyList, aValueList, starting_index, max_entries, aContext); return QueueCommandL(cmd); } // From PVMFMetadataExtensionInterface PVMFStatus PVMFDownloadManagerNode::ReleaseNodeMetadataKeys(PVMFMetadataList& keys, uint32 start , uint32 end) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ReleaseNodeMetadataKeys() called")); if (iFormatParserNode.Metadata()) { return iFormatParserNode.Metadata()->ReleaseNodeMetadataKeys(keys, start, end); } return PVMFFailure; } // From PVMFMetadataExtensionInterface PVMFStatus PVMFDownloadManagerNode::ReleaseNodeMetadataValues(Oscl_Vector& aValueList, uint32 start, uint32 end) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ReleaseNodeMetadataValues() called")); if (iFormatParserNode.Metadata()) { return iFormatParserNode.Metadata()->ReleaseNodeMetadataValues(aValueList, start, end); } return PVMFFailure; } //public API from data source playback interface PVMFCommandId PVMFDownloadManagerNode::SetDataSourcePosition(PVMFSessionId aSessionId, PVMFTimestamp aTargetNPT, PVMFTimestamp& aActualNPT, PVMFTimestamp& aActualMediaDataTS, bool aSeekToSyncPoint, uint32 aStreamID, OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::SetDataSourcePosition: aTargetNPT=%d, aSeekToSyncPoint=%d, aContext=0x%x", aTargetNPT, aSeekToSyncPoint, aContext)); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_SETDATASOURCEPOSITION, aTargetNPT, aActualNPT, aActualMediaDataTS, aSeekToSyncPoint, aStreamID, aContext); return QueueCommandL(cmd); } PVMFCommandId PVMFDownloadManagerNode::QueryDataSourcePosition(PVMFSessionId aSessionId, PVMFTimestamp aTargetNPT, PVMFTimestamp& aSeekPointBeforeTargetNPT, PVMFTimestamp& aSeekPointAfterTargetNPT, OsclAny* aContextData, bool aSeekToSyncPoint) { OSCL_UNUSED_ARG(aSeekPointAfterTargetNPT); // Implemented to complete interface file definition // Not tested on logical plane PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::QueryDataSourcePosition: aTargetNPT=%d, aSeekToSyncPoint=%d, aContext=0x%x", aTargetNPT, aContextData, aSeekToSyncPoint)); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_QUERYDATASOURCEPOSITION, aTargetNPT, aSeekPointBeforeTargetNPT, aSeekToSyncPoint, aContextData); return QueueCommandL(cmd); } PVMFCommandId PVMFDownloadManagerNode::QueryDataSourcePosition(PVMFSessionId aSessionId, PVMFTimestamp aTargetNPT, PVMFTimestamp& aActualNPT, bool aSeekToSyncPoint, OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::QueryDataSourcePosition: aTargetNPT=%d, aSeekToSyncPoint=%d, aContext=0x%x", aTargetNPT, aSeekToSyncPoint, aContext)); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_QUERYDATASOURCEPOSITION, aTargetNPT, aActualNPT, aSeekToSyncPoint, aContext); return QueueCommandL(cmd); } PVMFCommandId PVMFDownloadManagerNode::SetDataSourceRate(PVMFSessionId aSessionId, int32 aRate, PVMFTimebase* aTimebase, OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::SetDataSourceRate: aRate=%d", aRate)); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_SETDATASOURCERATE, aRate, aTimebase, aContext); return QueueCommandL(cmd); } PVMFStatus PVMFDownloadManagerNode::SetPlayerNodeRegistry(PVPlayerNodeRegistryInterface* aRegistry) { iPlayerNodeRegistry = aRegistry; return PVMFSuccess; } void PVMFDownloadManagerNode::Run() { //Process async node commands. if (!iInputCommands.empty()) ProcessCommand(); //Issue commands to the sub-nodes. if (!iProtocolEngineNode.CmdPending() && !iFormatParserNode.CmdPending() && !iSocketNode.CmdPending() && !iRecognizerNode.CmdPending() && !iSubNodeCmdVec.empty()) { PVMFStatus status = iSubNodeCmdVec.front().iNC->IssueCommand(iSubNodeCmdVec.front().iCmd); if (status != PVMFPending) iSubNodeCmdVec.front().iNC->CommandDone(status, NULL, NULL); } } PVMFCommandId PVMFDownloadManagerNode::QueueCommandL(PVMFDownloadManagerNodeCommand& aCmd) { //add a command to the async node command queue and return command ID PVMFCommandId id = iInputCommands.AddL(aCmd); // Wakeup the AO RunIfNotReady(); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::QueueCommandL() returning %d", id)); return id; } void PVMFDownloadManagerNode::ProcessCommand() { //This call will process the first node command in the input queue. //Can't do anything when an asynchronous cancel is in progress -- just need to wait on completion. if (!iCancelCommand.empty()) return; //keep waiting. //If a command is in progress, only a hi-pri command can interrupt it. if (!iCurrentCommand.empty() && !iInputCommands.front().hipri() ) { return; //keep waiting } //The newest or highest pri command is in the front of the queue. OSCL_ASSERT(!iInputCommands.empty()); PVMFDownloadManagerNodeCommand& aCmd = iInputCommands.front(); PVMFStatus cmdstatus; if (aCmd.hipri()) { //Process the Hi-Pri commands. switch (aCmd.iCmd) { case PVMF_GENERIC_NODE_CANCELALLCOMMANDS: cmdstatus = DoCancelAllCommands(aCmd); break; case PVMF_GENERIC_NODE_CANCELCOMMAND: cmdstatus = DoCancelCommand(aCmd); break; case PVDLM_NODE_CMD_CANCEL_GET_LICENSE: cmdstatus = DoCancelGetLicense(aCmd); break; default: cmdstatus = PVMFErrNotSupported; break; } //If completion is pending, move the command from //the input queue to the cancel queue. //This is necessary since the input queue could get //rearranged by new commands coming in. if (cmdstatus == PVMFPending) { iCancelCommand.StoreL(aCmd); iInputCommands.Erase(&aCmd); } } 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); break; case PVMF_GENERIC_NODE_RELEASEPORT: cmdstatus = DoReleasePort(aCmd); break; case PVMF_GENERIC_NODE_INIT: cmdstatus = DoInitNode(aCmd); break; case PVMF_GENERIC_NODE_PREPARE: cmdstatus = DoPrepareNode(aCmd); break; case PVMF_GENERIC_NODE_START: cmdstatus = DoStartNode(aCmd); break; case PVMF_GENERIC_NODE_STOP: cmdstatus = DoStopNode(aCmd); break; case PVMF_GENERIC_NODE_FLUSH: cmdstatus = DoFlushNode(aCmd); break; case PVMF_GENERIC_NODE_PAUSE: cmdstatus = DoPauseNode(aCmd); break; case PVMF_GENERIC_NODE_RESET: cmdstatus = DoResetNode(aCmd); break; case PVDLM_NODE_CMD_GETNODEMETADATAKEY: cmdstatus = DoGetNodeMetadataKey(aCmd); break; case PVDLM_NODE_CMD_GETNODEMETADATAVALUE: cmdstatus = DoGetNodeMetadataValue(aCmd); break; case PVDLM_NODE_CMD_SETDATASOURCEPOSITION: cmdstatus = DoSetDataSourcePosition(aCmd); break; case PVDLM_NODE_CMD_QUERYDATASOURCEPOSITION: cmdstatus = DoQueryDataSourcePosition(aCmd); break; case PVDLM_NODE_CMD_SETDATASOURCERATE: // Rate change not supported for download cmdstatus = PVMFErrNotSupported; break; case PVDLM_NODE_CMD_GET_LICENSE_W: cmdstatus = DoGetLicense(aCmd, true); break; case PVDLM_NODE_CMD_GET_LICENSE: cmdstatus = DoGetLicense(aCmd); break; default: OSCL_ASSERT(false); cmdstatus = PVMFFailure; break; } //If completion is pending, move the command from the input queue to the current command. //This is necessary since the input queue could get rearranged by new commands coming in. if (cmdstatus == PVMFPending) { iCurrentCommand.StoreL(aCmd); iInputCommands.Erase(&aCmd); } } if (cmdstatus != PVMFPending) CommandComplete(iInputCommands, aCmd, cmdstatus, NULL, NULL); } void PVMFDownloadManagerNode::CommandComplete(PVMFDownloadManagerNodeCmdQueue& aCmdQ, PVMFDownloadManagerNodeCommand& aCmd, PVMFStatus aStatus, PVInterface*aExtMsg, OsclAny* aEventData) { //Complete a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::CommandComplete() In Id %d Cmd %d Status %d Context %d Data %d", aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData)); if (aStatus != PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, CMD_LOG_LEVEL, (0, "PVMFDownloadManagerNode::CommandComplete() Failure!")); } //if the command failed or was cancelled there may be un-processed sub-node commands, so clear the vector now. if (!iSubNodeCmdVec.empty()) iSubNodeCmdVec.clear(); //We may need to wait on the movie atom before the node cmd can complete. //This is a good place to catch that condition and suppress the node //cmd completion. if (iParserInitAfterMovieAtom || iParserPrepareAfterMovieAtom) { if (aStatus == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::CommandComplete() Blocking Command Completion until Movie Atom Downloaded.")); return;//keep waiting on movie atom complete. } else { //if command failed or was cancelled then clear any movie atom wait //flags. iParserInitAfterMovieAtom = false; iParserPrepareAfterMovieAtom = false; } } //Do the post-command state changes and anything else. if (aStatus == PVMFSuccess) { switch (aCmd.iCmd) { case PVMF_GENERIC_NODE_INIT: ChangeNodeState(EPVMFNodeInitialized); break; case PVMF_GENERIC_NODE_PREPARE: ChangeNodeState(EPVMFNodePrepared); break; case PVMF_GENERIC_NODE_START: ChangeNodeState(EPVMFNodeStarted); break; case PVMF_GENERIC_NODE_PAUSE: ChangeNodeState(EPVMFNodePaused); break; case PVMF_GENERIC_NODE_STOP: ChangeNodeState(EPVMFNodePrepared); break; case PVMF_GENERIC_NODE_FLUSH: ChangeNodeState(EPVMFNodePrepared); break; case PVMF_GENERIC_NODE_RESET: //drive this node back to Created state. ChangeNodeState(EPVMFNodeIdle); break; } } //create response PVMFCmdResp resp(aCmd.iId, aCmd.iContext, aStatus, aExtMsg, aEventData); PVMFSessionId session = aCmd.iSession; //Erase the command from the queue. aCmdQ.Erase(&aCmd); //Report completion to the session observer. ReportCmdCompleteEvent(session, resp); //re-schedule if there are more commands and node isn't logged off if (!iInputCommands.empty() && IsAdded()) RunIfNotReady(); } void PVMFDownloadManagerNode::ReportErrorEvent(PVMFEventType aEventType, PVInterface*aExtMsg, OsclAny* aEventData) { //Report a node error event PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ReportErrorEvent() In Type %d Data %d ExtMsg %d", aEventType, aEventData, aExtMsg)); PVMFNodeInterface::ReportErrorEvent(aEventType, aEventData, aExtMsg); } void PVMFDownloadManagerNode::ReportInfoEvent(PVMFAsyncEvent &aEvent) { //Report a node info event PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ReportInfoEvent() In Type %d Data %d ExtMsg %d", aEvent.GetEventType(), aEvent.GetEventData(), aEvent.GetEventExtensionInterface())); PVMFNodeInterface::ReportInfoEvent(aEvent); //For download-then-play mode, generate data ready event when buffering //is complete. We will have suppressed the real initial data ready //event from PE node in this case. if (aEvent.GetEventType() == PVMFInfoBufferingComplete && iPlaybackMode == PVMFDownloadManagerNode::EDownloadThenPlay && !iDataReady) { GenerateDataReadyEvent(); } else if (aEvent.GetEventType() == PVMFInfoContentType) { // copy and save MIME string for recognizer to use as hint iContentTypeMIMEString = (char *)aEvent.GetEventData(); } } void PVMFDownloadManagerNode::GenerateDataReadyEvent() { PVMFAsyncEvent info(PVMFInfoEvent, PVMFInfoDataReady, NULL, NULL); ReportInfoEvent(info); iDataReady = true; } bool PVMFDownloadManagerNode::FilterPlaybackEventsFromSubNodes(const PVMFAsyncEvent& aEvent) { switch (aEvent.GetEventType()) { case PVMFInfoUnderflow: //filter any underflow that happens before data ready if (!iDataReady) return true; else iDataReady = false; break; case PVMFInfoDataReady: //filter any data ready that happens before download complete //in dl-then-play mode if (iPlaybackMode == EDownloadThenPlay && !iDownloadComplete) { return true; } //filter any data ready in dl-only mode, though I don't //think it's possible. if (iPlaybackMode == EDownloadOnly) return true; iDataReady = true; break; case PVMFInfoRemoteSourceNotification: //we get this event for "not pseudostreamable" for both PVX //and 3gpp. Only pass it up for 3gpp. if (iSourceFormat != PVMF_MIME_DATA_SOURCE_HTTP_URL) return true; break; default: break; } return false; } void PVMFDownloadManagerNode::ChangeNodeState(TPVMFNodeInterfaceState aNewState) { //Update the node state PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ChangeNodeState() Old %d New %d", iInterfaceState, aNewState)); PVMFNodeInterface::SetState(aNewState); } PVMFStatus PVMFDownloadManagerNode::DoQueryUuid(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoQueryUuid() In")); OSCL_String* mimetype; Oscl_Vector *uuidvec; bool exactmatch; aCmd.PVMFDownloadManagerNodeCommandBase::Parse(mimetype, uuidvec, exactmatch); // @TODO Add MIME string matching // For now just return all available extension interface UUID uuidvec->push_back(PVMF_TRACK_SELECTION_INTERFACE_UUID); uuidvec->push_back(PVMF_DATA_SOURCE_INIT_INTERFACE_UUID); uuidvec->push_back(KPVMFMetadataExtensionUuid); uuidvec->push_back(PvmfDataSourcePlaybackControlUuid); uuidvec->push_back(PVMI_CAPABILITY_AND_CONFIG_PVUUID); return PVMFSuccess; } PVMFStatus PVMFDownloadManagerNode::DoQueryInterface(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoQueryInterface() In")); PVUuid* uuid; PVInterface** ptr; aCmd.PVMFDownloadManagerNodeCommandBase::Parse(uuid, ptr); if (queryInterface(*uuid, *ptr)) { //Schedule further queries on sub-nodes... return ScheduleSubNodeCommands(aCmd); } else { //interface not supported *ptr = NULL; return PVMFFailure; } } PVMFStatus PVMFDownloadManagerNode::DoRequestPort(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoRequestPort() In")); if (iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoReleasePort(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoReleasePort() In")); return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoInitNode(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoInitNode() In")); if (iInterfaceState != EPVMFNodeIdle) return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoPrepareNode(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoPrepareNode() In")); if (iInterfaceState != EPVMFNodeInitialized) return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoStartNode(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoStartNode() In")); if (iInterfaceState != EPVMFNodePrepared && iInterfaceState != EPVMFNodePaused) return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoStopNode(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoStopNode() In")); if (iInterfaceState != EPVMFNodeStarted && iInterfaceState != EPVMFNodePaused && iInterfaceState != EPVMFNodeError)//allow a stop in error state. return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoFlushNode(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoFlushNode() In")); if (iInterfaceState != EPVMFNodeStarted && iInterfaceState != EPVMFNodePaused) return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoPauseNode(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoPauseNode() In")); if (iInterfaceState != EPVMFNodeStarted) return PVMFErrInvalidState; return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoResetNode(PVMFDownloadManagerNodeCommand& aCmd) { //remove the clock observer if (iPlayBackClock != NULL) { if (iClockNotificationsInf != NULL) { iClockNotificationsInf->RemoveClockStateObserver(*this); iPlayBackClock->DestroyMediaClockNotificationsInterface(iClockNotificationsInf); iClockNotificationsInf = NULL; } } //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoResetNode() In")); //Reset the sub-nodes first. return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoCancelAllCommands(PVMFDownloadManagerNodeCommand& aCmd) { OSCL_UNUSED_ARG(aCmd); //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoCancelAllCommands() In")); //first cancel the current command if any while (!iCurrentCommand.empty()) { if (iFormatParserNode.CancelPendingCommand() || iProtocolEngineNode.CancelPendingCommand() || iSocketNode.CancelPendingCommand() || iRecognizerNode.CancelPendingCommand() ) { return PVMFPending;//wait on sub-node cancel to complete. } CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFErrCancelled, NULL, NULL); } //next cancel all queued commands //start at element 1 since this cancel command is element 0. while (iInputCommands.size() > 1) { CommandComplete(iInputCommands, iInputCommands[1], PVMFErrCancelled, NULL, NULL); } return PVMFSuccess; } PVMFStatus PVMFDownloadManagerNode::DoCancelCommand(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoCancelCommand() In")); //extract the command ID from the parameters. PVMFCommandId id; aCmd.PVMFDownloadManagerNodeCommandBase::Parse(id); //first check "current" command if any PVMFDownloadManagerNodeCommand* cmd = iCurrentCommand.FindById(id); if (cmd) { if (iFormatParserNode.CancelPendingCommand() || iProtocolEngineNode.CancelPendingCommand() || iRecognizerNode.CancelPendingCommand() ) { return PVMFPending;//wait on sub-node cancel to complete. } CommandComplete(iCurrentCommand, *cmd, PVMFErrCancelled, NULL, NULL); return PVMFSuccess; } //next check input queue. //start at element 1 since this cancel command is element 0. cmd = iInputCommands.FindById(id, 1); if (cmd) { //cancel the queued command CommandComplete(iInputCommands, *cmd, PVMFErrCancelled, NULL, NULL); //report cancel success return PVMFSuccess; } //if we get here the command isn't queued so the cancel fails. return PVMFFailure; } PVMFStatus PVMFDownloadManagerNode::DoGetNodeMetadataKey(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoGetNodeMetadataKey() In")); return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoGetNodeMetadataValue(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoGetNodeMetadataValue() In")); return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoSetDataSourcePosition(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoSetDataSourcePosition() In")); return ScheduleSubNodeCommands(aCmd); } PVMFStatus PVMFDownloadManagerNode::DoQueryDataSourcePosition(PVMFDownloadManagerNodeCommand& aCmd) { //Start executing a node command PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoQueryDataSourcePosition() In")); return ScheduleSubNodeCommands(aCmd); } void PVMFDownloadManagerNode::ContinueInitAfterTrackSelectDecision() { //this is called during the Init sequence, once we have enough information //to make a definite track select decision. //See whether we need to stop to allow track selection on the download server. //If it's download-only, we don't offer this option, since the download must //be started in the Init. if (iPlaybackMode != EDownloadOnly && TrackSelectNode().iType == PVMFDownloadManagerSubNodeContainerBase::EProtocolEngine) { //stop the Init sequence here so we can do track selection on the download //server. ; } else { //else download-only, or there's no track selection available from the //PE node. Continue the Init or Prepare sequence. ContinueFromDownloadTrackSelectionPoint(); } } void PVMFDownloadManagerNode::ContinueFromDownloadTrackSelectionPoint() { //Continue the Init or Prepare sequence, stopping at parser init. //start the download. Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EStart); //initiate file recognize & parse, unless this is download-only mode. if (iPlaybackMode != EDownloadOnly) { //do recognizer sequence if needed. if (iSourceFormat == PVMF_MIME_DATA_SOURCE_PVX_FILE) { //PXV is always assumed to be MP4 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ContinueFromDownloadTrackSelectionPoint Setting format to MP4")); iMimeType = PVMF_MIME_MPEG4FF; } else { //for other source formats, use the recognizer to determine the format. Push(iRecognizerNode, PVMFDownloadManagerSubNodeContainerBase::ERecognizerStart); Push(iRecognizerNode, PVMFDownloadManagerSubNodeContainerBase::ERecognizerClose); } //create parser Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EParserCreate); // Send commands to the parser node to query these extension interfaces. Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDataSourceInit); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryTrackSelection); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryMetadata); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDatastreamUser); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDataSourcePlayback); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryFFProgDownload); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::ESetFFProgDownloadSupport); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::ECPMQueryLicenseInterface); //if this is PVX, we need to wait on movie atom before we can //init parser. if (iSourceFormat == PVMF_MIME_DATA_SOURCE_PVX_FILE) { if (iMovieAtomComplete || iDownloadComplete) { iParserInit = true; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EInit); } else { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ContinueFromDownloadTrackSelectionPoint Setting flag to Init Parser after Movie Atom Downloaded")); //set this flag to trigger parser init when movie atom is done. iParserInitAfterMovieAtom = true; } } else { //for other formats, go ahead and init parser. Init will block until //receiving movie atom. iParserInit = true; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EInit); } } } //Called when movie atom is received, or when download is complete //but movie atom was never received. void PVMFDownloadManagerNode::ContinueAfterMovieAtom() { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ContinueAfterMovieAtom() ")); if (!iMovieAtomComplete) { iMovieAtomComplete = true; //see whether we need to continue with parser init if (iParserInitAfterMovieAtom) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ContinueAfterMovieAtom() Continuing to Parser Init")); iParserInitAfterMovieAtom = false; iParserInit = true; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EInit); RunIfNotReady(); } //see whether we need to continue with parser prepare if (iParserPrepareAfterMovieAtom) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ContinueAfterMovieAtom() Continuing to Parser Prepare")); iParserPrepareAfterMovieAtom = false; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EPrepare); RunIfNotReady(); } } } PVMFNodeInterface* PVMFDownloadManagerNode::CreateParser() { if (!(iMimeType == PVMF_MIME_FORMAT_UNKNOWN)) { PVMFNodeInterface *iSourceNode = NULL; PVMFFormatType outputFormatType = PVMF_MIME_FORMAT_UNKNOWN; iFmt = iMimeType.get_str(); PVMFStatus status = iPlayerNodeRegistry->QueryRegistry(iFmt, outputFormatType, iDNodeUuids); if ((status == PVMFSuccess) && (iDNodeUuids.size() > 0)) { int32 leavecode = 0; OSCL_TRY(leavecode, iSourceNode = iPlayerNodeRegistry->CreateNode(iDNodeUuids[iDNodeUuidCount])); OSCL_FIRST_CATCH_ANY(leavecode, return NULL); iDNodeUuidCount++; return iSourceNode; } } return NULL; } PVMFStatus PVMFDownloadManagerNode::ScheduleSubNodeCommands(PVMFDownloadManagerNodeCommand& aCmd) { //given the node command ID, create the sub-node command vector, initiate the processing and return the node command status. OSCL_ASSERT(iSubNodeCmdVec.empty()); //Create the vector of all the commands in the sequence. switch (aCmd.iCmd) { case PVMF_GENERIC_NODE_QUERYINTERFACE: { //When we get here we've already called queryInterface on this node //for the interface. This code schedules any additional sub-node commands //that are needed to support the interface. //extract uuid from Node command... PVUuid*aUuid; PVInterface**aInterface; aCmd.PVMFDownloadManagerNodeCommandBase::Parse(aUuid, aInterface); OSCL_ASSERT(aUuid != NULL); if (*aUuid == PVMF_DATA_SOURCE_INIT_INTERFACE_UUID) { //To support data source init interface we need a bunch of sub-node interfaces. Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EQueryProtocolEngine); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDatastreamUser); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDataSourceInit); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDownloadProgress); } //else nothing else needed for other interfaces. } break; case PVMF_GENERIC_NODE_INIT: //check for second "Init" command after a license acquire. if (iInitFailedLicenseRequired) { iParserInit = true; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EInit); } else { //reset any prior download/playback event iDownloadComplete = false; iParserInit = false; iDataReady = false; iMovieAtomComplete = false; iParserInitAfterMovieAtom = false; iParserPrepareAfterMovieAtom = false; iRecognizerError = false; iInitFailedLicenseRequired = false; //reset any prior track select decisions. iFormatParserNode.iTrackSelection = NULL; iProtocolEngineNode.iTrackSelection = NULL; iNoPETrackSelect = false; //reset any prior recognizer decisions. iMimeType = PVMF_MIME_FORMAT_UNKNOWN; // Send the INIT command to the protocol engine node, followed by the Socket Node. Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EInit); Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EInit); // Issue the port request to the Protocol Engine Node and the socket node // NOTE: The request for the socket node's port must come first, followed by the protocol node, // because the code to connect the two ports in CommandDone() will do so when the protocol node's port is returned. Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::ERequestPort); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::ERequestPort); // The two ports will be connected in CommandDone, when the 2nd port request completes. // After the ports are connected, the datastream factory is passed to the protocol engine node. Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EPrepare); Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EStart); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EPrepare); if (TrackSelectNode().iType == PVMFDownloadManagerSubNodeContainerBase::EFormatParser) { //parser is doing track selection, there's no question ContinueInitAfterTrackSelectDecision(); } else { //PE node may be doing track selection, but to be sure, we need //to wait until it is prepared, then request the track selection interface. iProtocolEngineNode.iTrackSelection = NULL; Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EQueryTrackSelection); //once this command is complete, we will call ContinueInitAfterTrackSelectDecision() } } break; case PVMF_GENERIC_NODE_PREPARE: //if protocol engine node did track selection, then we need to continue //to the file parse stage here. Otherwise it was already done in the Init. if (TrackSelectNode().iType == PVMFDownloadManagerSubNodeContainerBase::EProtocolEngine) { ContinueFromDownloadTrackSelectionPoint(); } //if we initiated file parse sequence already, then go ahead and prepare //the parser node. if (iParserInit) { Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EPrepare); } //if we're waiting on movie atom to init parser, then set a flag so we'll //also do the parser prepare when it arrives. else if (iParserInitAfterMovieAtom) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::ScheduleSubNodeCommands Setting flag to Prepare Parser after Movie Atom Downloaded")); iParserPrepareAfterMovieAtom = true; } break; case PVMF_GENERIC_NODE_REQUESTPORT: //if file isn't parsed (as in download-only), then fail command if (!iFormatParserNode.iNode) return PVMFErrNotSupported; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::ERequestPort); break; case PVMF_GENERIC_NODE_RELEASEPORT: //if file isn't parsed (as in download-only), then fail command if (!iFormatParserNode.iNode) return PVMFErrNotSupported; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EReleasePort); break; case PVMF_GENERIC_NODE_START: //Re-start socket node & PE node in case they were stopped by a prior //stop command. if (iSocketNode.iNode->GetState() == EPVMFNodePrepared) Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EStart); if (iProtocolEngineNode.iNode->GetState() == EPVMFNodePrepared) Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EStart); //Start or re-start parser node (unless download-only) if (iFormatParserNode.iNode) Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EStart); break; case PVMF_GENERIC_NODE_STOP: iDataReady = false; //Stop parser (unless download-only) if (iFormatParserNode.iNode) Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EStop); //Stop PE node & socket node. Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EStop); Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EStop); break; case PVMF_GENERIC_NODE_FLUSH: if (iFormatParserNode.iNode) Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EFlush); break; case PVMF_GENERIC_NODE_PAUSE: //note: pause/resume download is not supported. if (iFormatParserNode.iNode) Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EPause); break; case PVMF_GENERIC_NODE_RESET: //Stop socket node if needed. if (iSocketNode.iNode->GetState() == EPVMFNodeStarted || iSocketNode.iNode->GetState() == EPVMFNodePaused) { Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EStop); } //Stop PE node if needed. if (iProtocolEngineNode.iNode->GetState() == EPVMFNodeStarted || iProtocolEngineNode.iNode->GetState() == EPVMFNodePaused) { Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EStop); } //Reset & cleanup all nodes. if (iFormatParserNode.iNode) Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EReset); Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::EReset); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::EReset); Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::ECleanup); Push(iProtocolEngineNode, PVMFDownloadManagerSubNodeContainerBase::ECleanup); Push(iSocketNode, PVMFDownloadManagerSubNodeContainerBase::ECleanup); break; case PVDLM_NODE_CMD_SETDATASOURCEPOSITION: //if file isn't parsed (as in download-only), then fail command if (!iFormatParserNode.iNode) return PVMFErrNotSupported; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::ESetDataSourcePosition); break; case PVDLM_NODE_CMD_QUERYDATASOURCEPOSITION: //if file isn't parsed (as in download-only), then fail command if (!iFormatParserNode.iNode) return PVMFErrNotSupported; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EQueryDataSourcePosition); break; case PVDLM_NODE_CMD_GETNODEMETADATAKEY: //if file isn't parsed (as in download-only), then fail command if (!iFormatParserNode.iNode) return PVMFErrNotSupported; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EGetMetadataKey); break; case PVDLM_NODE_CMD_GETNODEMETADATAVALUE: //if file isn't parsed (as in download-only), then fail command if (!iFormatParserNode.iNode) return PVMFErrNotSupported; Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::EGetMetadataValue); break; default: OSCL_ASSERT(false); break; } if (iSubNodeCmdVec.empty()) { //in a few cases there's nothing needed and no new commands //were issued-- so succeed here. return PVMFSuccess; } else { //Wakeup the node to start issuing the sub-node commands. RunIfNotReady(); //the node command is pending. return PVMFPending; } } void PVMFDownloadManagerNode::Push(PVMFDownloadManagerSubNodeContainerBase& n, PVMFDownloadManagerSubNodeContainerBase::CmdType c) { //push a sub-node command onto the cmd vector CmdElem elem; elem.iCmd = c; elem.iNC = &n; iSubNodeCmdVec.push_back(elem); } PVMFCommandId PVMFDownloadManagerNode::GetLicense(PVMFSessionId aSessionId, OSCL_wString& aContentName, OsclAny* aData, uint32 aDataSize, int32 aTimeoutMsec, OsclAny* aContextData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetLicense - Wide called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_GET_LICENSE_W, aContentName, aData, aDataSize, aTimeoutMsec, aContextData); return QueueCommandL(cmd); } PVMFCommandId PVMFDownloadManagerNode::GetLicense(PVMFSessionId aSessionId, OSCL_String& aContentName, OsclAny* aData, uint32 aDataSize, int32 aTimeoutMsec, OsclAny* aContextData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetLicense - Non-Wide called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommand::Construct(aSessionId, PVDLM_NODE_CMD_GET_LICENSE, aContentName, aData, aDataSize, aTimeoutMsec, aContextData); return QueueCommandL(cmd); } PVMFCommandId PVMFDownloadManagerNode::CancelGetLicense(PVMFSessionId aSessionId , PVMFCommandId aCmdId , OsclAny* aContextData) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::GetLicense - Non-Wide called")); PVMFDownloadManagerNodeCommand cmd; cmd.PVMFDownloadManagerNodeCommandBase::Construct(aSessionId, PVDLM_NODE_CMD_CANCEL_GET_LICENSE, aCmdId, aContextData); return QueueCommandL(cmd); } PVMFStatus PVMFDownloadManagerNode::DoGetLicense(PVMFDownloadManagerNodeCommand& aCmd, bool aWideCharVersion) { OSCL_UNUSED_ARG(aCmd); if (iFormatParserNode.LicenseInterface() == NULL) { return PVMFErrNotSupported; } if (aWideCharVersion == true) { Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::ECPMGetLicenseW); } else { Push(iFormatParserNode, PVMFDownloadManagerSubNodeContainerBase::ECPMGetLicense); } RunIfNotReady(); return PVMFPending; } void PVMFDownloadManagerNode::CompleteGetLicense() { CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFSuccess, NULL, NULL); } PVMFStatus PVMFDownloadManagerNode::DoCancelGetLicense(PVMFDownloadManagerNodeCommand& aCmd) { OSCL_UNUSED_ARG(aCmd); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::DoCancelGetLicense called")); if (iFormatParserNode.LicenseInterface() == NULL) { return PVMFErrNotSupported; } else { iFormatParserNode.iCancelCmdState = PVMFDownloadManagerSubNodeContainerBase::EBusy; iFormatParserNode.iCPMCancelGetLicenseCmdId = iFormatParserNode.LicenseInterface()->CancelGetLicense(iFormatParserNode.iSessionId, iFormatParserNode.iCPMGetLicenseCmdId); RunIfNotReady(); } return PVMFPending; } // // PVMFDownloadManagerSubNodeContainer Implementation. // PVMFDownloadManagerSubNodeContainerBase::PVMFDownloadManagerSubNodeContainerBase() { iCmdState = EIdle; iCancelCmdState = EIdle; } void PVMFDownloadManagerSubNodeContainerBase::Construct(NodeType t, PVMFDownloadManagerNode* c) { iContainer = c; iType = t; } void PVMFDownloadManagerSubNodeContainer::Cleanup() { //release all the queried interfaces. if (iDataSourceInit) { iDataSourceInit->removeRef(); iDataSourceInit = NULL; } if (iProtocolEngineExtensionInt) { iProtocolEngineExtensionInt->removeRef(); iProtocolEngineExtensionInt = NULL; } if (iDatastreamUser) { iDatastreamUser->removeRef(); iDatastreamUser = NULL; } if (iTrackSelection) { iTrackSelection->removeRef(); iTrackSelection = NULL; } if (iMetadata) { iMetadata->removeRef(); iMetadata = NULL; } if (iDataSourcePlayback) { iDataSourcePlayback->removeRef(); iDataSourcePlayback = NULL; } if (iFormatProgDownloadSupport) { iFormatProgDownloadSupport->removeRef(); iFormatProgDownloadSupport = NULL; } if (iDownloadProgress) { iDownloadProgress->removeRef(); iDownloadProgress = NULL; } if (iLicenseInterface) { iLicenseInterface->removeRef(); iLicenseInterface = NULL; } //the node instance is cleaned up elsewhere. } void PVMFDownloadManagerRecognizerContainer::Cleanup() { // Nothing to do here 'til recognizer is integrated } void PVMFDownloadManagerSubNodeContainer::Connect() { //Issue connect command to the sub-node. //This container class is the observer. PVMFNodeSessionInfo info(this //cmd , this, NULL //info , this, NULL); //err if (iNode) iSessionId = iNode->Connect(info); } #define LOGSUBCMD(x) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, SUB_CMD_LOG_LEVEL, x) #define GETNODESTR (iType==EFormatParser)?"Parser":((iType==EProtocolEngine)?"ProtEngine":"SockNode") PVMFStatus PVMFDownloadManagerSubNodeContainer::IssueCommand(int32 aCmd) { //Issue a command to the sub-node. //Return the sub-node completion status-- either pending, success, or failure. LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s () In", GETNODESTR)); OSCL_ASSERT(!CmdPending()); //find the current node command since we may need its parameters. OSCL_ASSERT(!iContainer->iCurrentCommand.empty()); PVMFDownloadManagerNodeCommand* nodeCmd = &iContainer->iCurrentCommand.front(); //save the sub-node command code iCmd = aCmd; switch (aCmd) { case ECleanup: LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Cleanup", GETNODESTR)); Cleanup(); return PVMFSuccess; case EParserCreate: iNode = iContainer->CreateParser(); if (iNode) { Connect(); iNode->ThreadLogon(); return PVMFSuccess; } return PVMFErrCorrupt; case EQueryDataSourceInit: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(data source init)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PVMF_DATA_SOURCE_INIT_INTERFACE_UUID, iDataSourceInit); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EQueryProtocolEngine: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(ProtocolEngine)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, KPVMFProtocolEngineNodeExtensionUuid, iProtocolEngineExtensionInt); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EQueryDatastreamUser: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(DatastreamUser)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PVMIDatastreamuserInterfaceUuid, iDatastreamUser); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EQueryTrackSelection: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(track selection)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PVMF_TRACK_SELECTION_INTERFACE_UUID, iTrackSelection); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EQueryMetadata: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(metadata)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, KPVMFMetadataExtensionUuid, iMetadata); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case ECPMQueryLicenseInterface: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(License)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PVMFCPMPluginLicenseInterfaceUuid, iLicenseInterface); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EQueryDataSourcePlayback: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface(datasourcePB)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PvmfDataSourcePlaybackControlUuid, iDataSourcePlayback); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EInit: OSCL_ASSERT(iNode != NULL); if (iType == EFormatParser) { // For this command, which gets pushed to the format parser node, we set the source init and also // set the datstream factory LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling SetSourceInitializationData", GETNODESTR)); if (!DataSourceInit()) return PVMFFailure; //no source init interface? if (!DatastreamUser()) return PVMFFailure; //no datastreamuser interface? //Pass data to the parser node. if (iContainer->iInitFailedLicenseRequired) { ;//do nothing-- data was already set on the first init call. } else { //Pass source data if (iContainer->iSourceFormat == PVMF_MIME_DATA_SOURCE_PVX_FILE) { // let the parser know this is PVX format. PVMFFormatType fmt = PVMF_MIME_DATA_SOURCE_PVX_FILE; (DataSourceInit())->SetSourceInitializationData(iContainer->iDownloadFileName , fmt , (OsclAny*)&iContainer->iLocalDataSource); } else if (iContainer->iSourceFormat == PVMF_MIME_DATA_SOURCE_SHOUTCAST_URL) { // let the parser node know that it is playing from a shoutcast stream (DataSourceInit())->SetSourceInitializationData(iContainer->iDownloadFileName , iContainer->iSourceFormat , (OsclAny*)iContainer->iSourceData); } else { // pass the recognized format to the parser. (DataSourceInit())->SetSourceInitializationData(iContainer->iDownloadFileName , iContainer->iFmt , (OsclAny*)iContainer->iSourceData); } //Pass datastream data. (DatastreamUser())->PassDatastreamFactory(*(iContainer->iReadFactory), (int32)0); PVMFFileBufferDataStreamWriteDataStreamFactoryImpl* wdsfactory = OSCL_STATIC_CAST(PVMFFileBufferDataStreamWriteDataStreamFactoryImpl*, iContainer->iWriteFactory); int32 leavecode = 0; OSCL_TRY(leavecode, PVMFDataStreamReadCapacityObserver* obs = OSCL_STATIC_CAST(PVMFDataStreamReadCapacityObserver*, wdsfactory); (DatastreamUser())->PassDatastreamReadCapacityObserver(obs)); OSCL_FIRST_CATCH_ANY(leavecode, LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s PassDatastreamReadCapacityObserver not supported", GETNODESTR)); ); } LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Init ", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Init(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } else { LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Init ", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Init(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case ECPMGetLicenseW: { OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling ECPMGetLicenseW", GETNODESTR)); iCmdState = EBusy; OSCL_wString* contentName = NULL; OsclAny* data = NULL; uint32 dataSize = 0; int32 timeoutMsec = 0; nodeCmd->Parse(contentName, data, dataSize, timeoutMsec); iCmdId = LicenseInterface()->GetLicense(iSessionId, *contentName, data, dataSize, timeoutMsec); iCPMGetLicenseCmdId = iCmdId; LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case ECPMGetLicense: { OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling ECPMGetLicense", GETNODESTR)); iCmdState = EBusy; OSCL_String* contentName = NULL; OsclAny* data = NULL; uint32 dataSize = 0; int32 timeoutMsec = 0; nodeCmd->Parse(contentName, data, dataSize, timeoutMsec); iCmdId = LicenseInterface()->GetLicense(iSessionId, *contentName, data, dataSize, timeoutMsec); iCPMGetLicenseCmdId = iCmdId; LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case ERequestPort: OSCL_ASSERT(iNode != NULL); // The parameters to RequestPort vary depending on which node we're getting a port from, so we switch on it. switch (iType) { case EProtocolEngine: LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling RequestPort ", GETNODESTR)); iCmdState = EBusy; // For protocol engine port request, we don't need port tag or config info because it's the only port we ask it for. iCmdId = iNode->RequestPort(iSessionId, (int32)0); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case ESocket: LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling RequestPort with port config %s", GETNODESTR, iContainer->iServerAddr.get_cstr())); iCmdState = EBusy; //append a mimestring to the port for socket node logging iContainer->iServerAddr += ";mime=download"; iCmdId = iNode->RequestPort(iSessionId, PVMF_SOCKET_NODE_PORT_TYPE_PASSTHRU, &iContainer->iServerAddr); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EFormatParser: //extract params from current Node command. OSCL_ASSERT(nodeCmd->iCmd == PVMF_GENERIC_NODE_REQUESTPORT); { int32 aPortTag; OSCL_String*aMimetype; nodeCmd->PVMFDownloadManagerNodeCommandBase::Parse(aPortTag, aMimetype); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling RequestPort ", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->RequestPort(iSessionId, aPortTag, aMimetype); } LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; default: OSCL_ASSERT(false); return PVMFFailure; } case EReleasePort: OSCL_ASSERT(iNode != NULL); { //extract params from current Node command. OSCL_ASSERT(nodeCmd->iCmd == PVMF_GENERIC_NODE_RELEASEPORT); PVMFPortInterface *port; nodeCmd->PVMFDownloadManagerNodeCommandBase::Parse(port); OSCL_ASSERT(port != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling ReleasePort", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->ReleasePort(iSessionId, *port); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case EPrepare: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Prepare", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Prepare(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EStop: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Stop", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Stop(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EStart: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Start", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Start(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EPause: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Pause", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Pause(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EFlush: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Flush", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Flush(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EReset: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling Reset", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->Reset(iSessionId); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EGetMetadataKey: OSCL_ASSERT(iNode != NULL); { if (!Metadata()) return PVMFErrNotSupported;//no interface! //extract params from current Node command. OSCL_ASSERT(nodeCmd->iCmd == PVDLM_NODE_CMD_GETNODEMETADATAKEY); PVMFMetadataList* aKeyList; uint32 starting_index; int32 max_entries; char* query_key; nodeCmd->Parse(aKeyList, starting_index, max_entries, query_key); OSCL_ASSERT(aKeyList != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling GetNodeMetadataKeys", GETNODESTR)); iCmdState = EBusy; iCmdId = (Metadata())->GetNodeMetadataKeys(iSessionId, *aKeyList, starting_index, max_entries, query_key, NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case EGetMetadataValue: OSCL_ASSERT(iNode != NULL); { if (!Metadata()) return PVMFErrNotSupported;//no interface! //extract params from current Node command. OSCL_ASSERT(nodeCmd->iCmd == PVDLM_NODE_CMD_GETNODEMETADATAVALUE); PVMFMetadataList* aKeyList; Oscl_Vector* aValueList; uint32 starting_index; int32 max_entries; nodeCmd->Parse(aKeyList, aValueList, starting_index, max_entries); OSCL_ASSERT(aKeyList != NULL); OSCL_ASSERT(aValueList != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling GetNodeMetadataValues", GETNODESTR)); iCmdState = EBusy; iCmdId = (Metadata())->GetNodeMetadataValues(iSessionId, *aKeyList, *aValueList, starting_index, max_entries, NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case EQueryFFProgDownload: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface (format prog dl)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PVMF_FF_PROGDOWNLOAD_SUPPORT_INTERFACE_UUID, iFormatProgDownloadSupport); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case EQueryDownloadProgress: OSCL_ASSERT(iNode != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryInterface (dl prog)", GETNODESTR)); iCmdState = EBusy; iCmdId = iNode->QueryInterface(iSessionId, PVMF_DOWNLOAD_PROGRESS_INTERFACE_UUID, iDownloadProgress); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; case ESetFFProgDownloadSupport: OSCL_ASSERT(iNode != NULL); if (!DownloadProgress() || !iContainer->iFormatParserNode.FormatProgDownloadSupport()) return PVMFErrNotSupported;//no interface! //pass parser node format prog download interface to the protocol node. LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling setFormatDownloadSupportInterface", GETNODESTR)); (DownloadProgress())->setFormatDownloadSupportInterface(iContainer->iFormatParserNode.FormatProgDownloadSupport()); return PVMFSuccess; case ESetDataSourcePosition: OSCL_ASSERT(iNode != NULL); { if (!DataSourcePlayback()) return PVMFErrNotSupported;//no interface! //extract params from current Node command. OSCL_ASSERT(nodeCmd->iCmd == PVDLM_NODE_CMD_SETDATASOURCEPOSITION); PVMFTimestamp aTargetNPT; PVMFTimestamp* aActualNPT; PVMFTimestamp* aActualMediaDataTS; uint32 streamID = 0; bool aJump; nodeCmd->Parse(aTargetNPT, aActualNPT, aActualMediaDataTS, aJump, streamID); OSCL_ASSERT(aActualNPT != NULL); OSCL_ASSERT(aActualMediaDataTS != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling SetDataSourcePosition", GETNODESTR)); iCmdState = EBusy; iCmdId = (DataSourcePlayback())->SetDataSourcePosition(iSessionId, aTargetNPT, *aActualNPT, *aActualMediaDataTS, aJump, streamID); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } case EQueryDataSourcePosition: OSCL_ASSERT(iNode != NULL); { if (!DataSourcePlayback()) return PVMFErrNotSupported;//no interface! //extract params from current Node command. OSCL_ASSERT(nodeCmd->iCmd == PVDLM_NODE_CMD_QUERYDATASOURCEPOSITION); PVMFTimestamp aTargetNPT; PVMFTimestamp* aActualNPT; bool aJump; nodeCmd->Parse(aTargetNPT, aActualNPT, aJump); OSCL_ASSERT(aActualNPT != NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s Calling QueryDataSourcePosition", GETNODESTR)); iCmdState = EBusy; iCmdId = (DataSourcePlayback())->QueryDataSourcePosition(iSessionId, aTargetNPT, *aActualNPT, aJump); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::IssueCommand %s CmdId %d ", GETNODESTR, iCmdId)); return PVMFPending; } default: OSCL_ASSERT(false); return PVMFFailure; } } PVMFStatus PVMFDownloadManagerRecognizerContainer::IssueCommand(int32 aCmd) { //Issue a command to the Recognizer. //Return the completion status-- either pending, success, or failure. LOGSUBCMD((0, "PVMFDownloadManagerRecognizerContainer::IssueCommand In")); OSCL_ASSERT(!CmdPending()); //save the sub-node command code iCmd = aCmd; switch (aCmd) { case ERecognizerStart: { PVMFStatus status = PVMFRecognizerRegistry::OpenSession(iRecognizerSessionId, (*this)); if (status == PVMFSuccess) { //Issue the asynchronous command to the recognizer. iCmdState = EBusy; iCmdId = PVMFRecognizerRegistry::Recognize(iRecognizerSessionId, *(iContainer->iReadFactory), NULL, iRecognizerResultVec); LOGSUBCMD((0, "PVMFDownloadManagerRecognizerContainer::IssueCommand Recognize Pending Cmd ID %d", iCmdId)); return PVMFPending; //wait on the RecognizerCommandCompleted callback. } else { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerRecognizerContainer::IssueCommand Open Session Failed, status %d", status)); } return status; } // break; This statement was removed to avoid compiler warning for Unreachable Code case ERecognizerClose: //close the recognizer session. { PVMFStatus status = PVMFRecognizerRegistry::CloseSession(iRecognizerSessionId); if (status != PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerRecognizerContainer::IssueCommand CloseSession status %d", status)); } return status; } default: LOGSUBCMD((0, "PVMFDownloadManagerRecognizerContainer::IssueCommand Error, Unknown Recognizer Command!")); OSCL_ASSERT(false);//unknown command type for recognizer. return PVMFFailure; } } //this is the callback from the Recognizer::Recognize command. void PVMFDownloadManagerRecognizerContainer::RecognizerCommandCompleted(const PVMFCmdResp& aResponse) { if (aResponse.GetCmdId() == iCmdId && iCmdState == EBusy) { //save the result. if (aResponse.GetCmdStatus() == PVMFSuccess && iRecognizerResultVec.size() > 0) { // if there is only 1, use it // if more than 1, check the confidence level if (1 == iRecognizerResultVec.size()) { iContainer->iMimeType = iRecognizerResultVec[0].iRecognizedFormat; } else { // if certain, use it // if possible, keep looking bool found = false; for (uint32 i = 0; i < iRecognizerResultVec.size(); i++) { if (PVMFRecognizerConfidenceCertain == iRecognizerResultVec[i].iRecognitionConfidence) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; break; } } // if Content-Type may not be known, just use the first result if (!found && (0 != iContainer->iContentTypeMIMEString.get_size())) { // no certain, all possibles // compare with the Content-Type hint, which is in IANA MIME string format // these are file formats, does not include streaming formats // @TODO: need to add the following to "pvmi/pvmf/include/pvmf_format_type.h" // // MP4 + 3GPP = "video/3gpp", "video/mp4", "audio/3gpp", "audio/mp4", "video/3gpp-tt" // AMR = "audio/amr", "audio/amr-wb" // AAC = "audio/aac", "audio/x-aac", "audio/aacp" // MP3 = "audio/mpeg" // WM + ASF = "video/x-ms-wmv", "video/x-ms-wm", "video/x-ms-asf","audio/x-ms-wma", // RM = "video/vnd.rn-realvideo", "audio/vnd.rn-realaudio" // WAV = "audio/wav", "audio/x-wav", "audio/wave" // // the recognizer plugins may return PV proprietary format, X-... // in "pvmi/pvmf/include/pvmf_format_type.h" // #define PVMF_MIME_MPEG4FF "video/MP4" // #define PVMF_MIME_AMRFF "X-AMR-FF" // #define PVMF_MIME_AACFF "X-AAC-FF" // #define PVMF_MIME_MP3FF "X-MP3-FF" // #define PVMF_MIME_WAVFF "X-WAV-FF" // #define PVMF_MIME_ASFFF "x-pvmf/mux/asf" // #define PVMF_MIME_RMFF "x-pvmf/mux/rm" // need case insensitive compares const char* mimeStr = iContainer->iContentTypeMIMEString.get_cstr(); for (uint32 i = 0; !found && i < iRecognizerResultVec.size(); i++) { const char* recognizedStr = iRecognizerResultVec[i].iRecognizedFormat.get_cstr(); if (0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_MPEG4FF)) { if ((0 == oscl_CIstrcmp(mimeStr, "video/3gpp")) || (0 == oscl_CIstrcmp(mimeStr, "video/mp4")) || (0 == oscl_CIstrcmp(mimeStr, "audio/3gpp")) || (0 == oscl_CIstrcmp(mimeStr, "audio/mp4")) || (0 == oscl_CIstrcmp(mimeStr, "video/3gpp-tt"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else if (0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_MP3FF)) { if ((0 == oscl_CIstrcmp(mimeStr, "audio/mpeg"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else if (0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_AACFF)) { if ((0 == oscl_CIstrcmp(mimeStr, "audio/aac")) || (0 == oscl_CIstrcmp(mimeStr, "audio/x-aac")) || (0 == oscl_CIstrcmp(mimeStr, "audio/aacp"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else if (0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_AMRFF)) { if ((0 == oscl_CIstrcmp(mimeStr, "audio/amr")) || (0 == oscl_CIstrcmp(mimeStr, "audio/amr-wb"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else if (0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_ASFFF)) { if ((0 == oscl_CIstrcmp(mimeStr, "video/x-ms-wmv")) || (0 == oscl_CIstrcmp(mimeStr, "video/x-ms-wm")) || (0 == oscl_CIstrcmp(mimeStr, "video/x-ms-asf")) || (0 == oscl_CIstrcmp(mimeStr, "audio/x-ms-wma"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else if (0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_RMFF)) { if ((0 == oscl_CIstrcmp(mimeStr, "video/vnd.rn-realvideo")) || (0 == oscl_CIstrcmp(mimeStr, "audio/vnd.rn-realaudio"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else if ((0 == oscl_CIstrcmp(recognizedStr, PVMF_MIME_WAVFF))) { if ((0 == oscl_CIstrcmp(mimeStr, "audio/wav")) || (0 == oscl_CIstrcmp(mimeStr, "audio/wave")) || (0 == oscl_CIstrcmp(mimeStr, "audio/x-wav"))) { found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } else { // some new format that this component does not know about // we'll use it found = true; iContainer->iMimeType = iRecognizerResultVec[i].iRecognizedFormat; } } } // if still no match found // need to wait for more data and run the recognizer again, will implement this later // just use the first one for now if (!found) { // @TODO - implement the recognizer loop later iContainer->iMimeType = iRecognizerResultVec[0].iRecognizedFormat; } } } CommandDone(aResponse.GetCmdStatus(), aResponse.GetEventExtensionInterface(), aResponse.GetEventData()); //catch completion of cancel for recognizer commands //since there's no cancel to the recognizer module, the cancel //is done whenever the current recognizer command is done. if (iCancelCmdState != EIdle) { CancelCommandDone(PVMFSuccess, NULL, NULL); } } else { OSCL_ASSERT(false);//unexpected response. } } //from PVMFNodeErrorEventObserver void PVMFDownloadManagerSubNodeContainer::HandleNodeErrorEvent(const PVMFAsyncEvent& aEvent) { //A sub-node is reporting an event. //print events switch (iType) { case EFormatParser: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerSubNodeContainer::HandleNodeErrorEvent Parser Node Error Event %d", aEvent.GetEventType())); break; case EProtocolEngine: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerSubNodeContainer::HandleNodeErrorEvent ProtocolEngine Node Error Event %d", aEvent.GetEventType())); break; case ESocket: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerSubNodeContainer::HandleNodeErrorEvent Socket Node Error Event %d", aEvent.GetEventType())); if (iContainer->iDownloadComplete) return; // Suppress socket node error, if the download is already complete. break; default: OSCL_ASSERT(false); break; } //duplicate any PVMF Error events from either node. if (IsPVMFErrCode(aEvent.GetEventType())) iContainer->ReportErrorEvent(aEvent.GetEventType(), aEvent.GetEventExtensionInterface(), aEvent.GetEventData()); } #include "pvmf_protocol_engine_node_events.h" //from PVMFNodeInfoEventObserver void PVMFDownloadManagerSubNodeContainer::HandleNodeInformationalEvent(const PVMFAsyncEvent& aEvent) { //A sub-node is reporting an event. //detect sub-node error states. if (aEvent.GetEventType() == PVMFInfoStateChanged && iNode->GetState() == EPVMFNodeError) { iContainer->SetState(EPVMFNodeError); } //detect important status events. if (iType == EProtocolEngine) { switch (aEvent.GetEventType()) { case PVMFInfoBufferingComplete: iContainer->iDownloadComplete = true; iContainer->NotifyDownloadComplete(); //not sure whether this is possible, but just in case download //completes before movie atom notice, go ahead and do anything //that was waiting on movie atom. if (!iContainer->iMovieAtomComplete) iContainer->ContinueAfterMovieAtom(); break; case PVMFPROTOCOLENGINE_INFO_MovieAtomCompleted: //we may be waiting on this event to continue Parser init. if (!iContainer->iMovieAtomComplete) iContainer->ContinueAfterMovieAtom(); if (iContainer->iDebugMode) { iContainer->ReportInfoEvent((PVMFAsyncEvent&)aEvent); } break; default: break; } } //filter out events that we don't want to pass up to observer bool filter = false; if (iType == ESocket) { switch (aEvent.GetEventType()) { case PVMFInfoRemoteSourceNotification: //To let the socket node events propagate to pvengine filter = false; break; default: filter = true; } } else { switch (aEvent.GetEventType()) { case PVMFInfoStateChanged: filter = true;//always ignore break; case PVMFInfoPortDeleted: case PVMFInfoPortCreated: case PVMFInfoPortConnected: case PVMFInfoPortDisconnected: if (iType != EFormatParser) filter = true;//ignore port events unless from format parser break; case PVMFInfoUnderflow: case PVMFInfoDataReady: case PVMFInfoRemoteSourceNotification: //apply some filtering to these if (iContainer->FilterPlaybackEventsFromSubNodes(aEvent)) filter = true; break; default: break; } } //duplicate all remaining PVMFInfo events. if (!filter && IsPVMFInfoCode(aEvent.GetEventType())) { iContainer->ReportInfoEvent((PVMFAsyncEvent&)aEvent); } //just print and ignore other events switch (iType) { case EFormatParser: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::HandleNodeInfoEvent Parser Node Info Event %d", aEvent.GetEventType())); break; case EProtocolEngine: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::HandleNodeInfoEvent ProtocolEngine Node Info Event %d", aEvent.GetEventType())); break; case ESocket: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::HandleNodeInfoEvent Socket Node Info Event %d", aEvent.GetEventType())); break; default: OSCL_ASSERT(false); break; } } bool PVMFDownloadManagerSubNodeContainer::CancelPendingCommand() { //initiate sub-node command cancel, return True if cancel initiated. if (iCmdState != EBusy) return false;//nothing to cancel iCancelCmdState = EBusy; if (iNode) { LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::CancelPendingCommand Calling Cancel")); iCancelCmdId = iNode->CancelCommand(iSessionId, iCmdId, NULL); LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::CancelPendingCommand CmdId %d", iCancelCmdId)); } return true;//cancel initiated } bool PVMFDownloadManagerRecognizerContainer::CancelPendingCommand() { //initiate sub-node command cancel, return True if cancel initiated. if (iCmdState != EBusy) return false;//nothing to cancel iCancelCmdState = EBusy; LOGSUBCMD((0, "PVMFDownloadManagerRecognizerContainer::CancelPendingCommand Calling Cancel")); iCancelCmdId = PVMFRecognizerRegistry::CancelCommand(iRecognizerSessionId, iCmdId, NULL); LOGSUBCMD((0, "PVMFDownloadManagerRecognizerContainer::CancelPendingCommand CmdId %d", iCancelCmdId)); return true;//cancel initiated } void PVMFDownloadManagerSubNodeContainerBase::CommandDone(PVMFStatus aStatus, PVInterface*aExtMsg, OsclAny*aEventData) { //a sub-node command is done-- process the result. OSCL_ASSERT(aStatus != PVMFPending); //pop the sub-node command vector. OSCL_ASSERT(!iContainer->iSubNodeCmdVec.empty()); iContainer->iSubNodeCmdVec.erase(&iContainer->iSubNodeCmdVec.front()); iCmdState = EIdle; PVMFStatus status = aStatus; // Set "Init Failed License Required" flag with the results of parser Init. if (iType == EFormatParser && iCmd == EInit) { iContainer->iInitFailedLicenseRequired = (status == PVMFErrLicenseRequired); } // Watch for the request port command completion from the protocol node, because we need to save the port pointer if (iType == EProtocolEngine && iCmd == ERequestPort && status == PVMFSuccess) { iContainer->iProtocolEngineNodePort = (PVMFPortInterface*)aEventData; // If both ports are non-null, connect them. if (iContainer->iSocketNodePort && iContainer->iProtocolEngineNodePort) { iContainer->iSocketNodePort->Connect(iContainer->iProtocolEngineNodePort); // The ports are connected, so now we pass the datastream factory to the protocol node via the extension interface, if it's available. if (iContainer->iProtocolEngineNode.iDatastreamUser) { ((PVMIDatastreamuserInterface*)iContainer->iProtocolEngineNode.iDatastreamUser)->PassDatastreamFactory(*(iContainer->iWriteFactory), (int32)0); } } } // Watch for the request port command completion from the socket node, because we need to save the port pointer if (iType == ESocket && iCmd == ERequestPort && status == PVMFSuccess) { iContainer->iSocketNodePort = (PVMFPortInterface*)aEventData; } // Watch for the query track selection interface completion from the protocol engine node. if (iType == EProtocolEngine && iCmd == EQueryTrackSelection) { //see whether we got the TS interface from PE node. iContainer->iNoPETrackSelect = (status != PVMFSuccess || iContainer->iProtocolEngineNode.iTrackSelection == NULL); //ignore cmd failure so it won't terminate the Init sequence if (status != PVMFSuccess) status = PVMFSuccess; //Continue the Init sequence now that we have the track select decision. iContainer->ContinueInitAfterTrackSelectDecision(); } // Watch for recognizer start failure if (iType == ERecognizer && iCmd == ERecognizerStart && aStatus != PVMFSuccess) { iContainer->iRecognizerError = true; //save the error code to report after recognizer close. iContainer->iRecognizerStartStatus = status; //purge everything from the subnode command vector except the recognizer //close command iContainer->iSubNodeCmdVec.clear(); iContainer->Push(iContainer->iRecognizerNode, PVMFDownloadManagerSubNodeContainerBase::ERecognizerClose); //set status to "success" so that we'll continue with processing status = PVMFSuccess; } // Watch for recognizer close completion after a start failure else if (iContainer->iRecognizerError) { OSCL_ASSERT(iCmd == ERecognizerClose); iContainer->iRecognizerError = false; //restore the original error code from the recognizer start. status = iContainer->iRecognizerStartStatus; } //Check whether the node command is being cancelled. if (iCancelCmdState != EIdle) { if (!iContainer->iSubNodeCmdVec.empty()) { //even if this command succeeded, we want to report //the node command status as cancelled since some sub-node //commands were not yet issued. status = PVMFErrCancelled; //go into an error state since it's not clear //how to recover from a partially completed command. iContainer->SetState(EPVMFNodeError); } } //figure out the next step in the sequence... //A node command is done when either all sub-node commands are //done or when one fails. if (status == PVMFSuccess && !iContainer->iSubNodeCmdVec.empty()) { //The node needs to issue the next sub-node command. iContainer->RunIfNotReady(); } else { //node command is done. OSCL_ASSERT(!iContainer->iCurrentCommand.empty()); iContainer->CommandComplete(iContainer->iCurrentCommand, iContainer->iCurrentCommand.front(), status, aExtMsg, aEventData); } } void PVMFDownloadManagerSubNodeContainerBase::CancelCommandDone(PVMFStatus aStatus, PVInterface*aExtMsg, OsclAny*aEventData) { OSCL_UNUSED_ARG(aExtMsg); OSCL_UNUSED_ARG(aEventData); //a sub-node cancel command is done-- process the result. OSCL_ASSERT(aStatus != PVMFPending); iCancelCmdState = EIdle; //print and ignore any failed sub-node cancel commands. if (aStatus != PVMFSuccess) { switch (iType) { case EFormatParser: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::CancelCommandDone Parser Node Cancel failed")); break; case EProtocolEngine: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::CancelCommandDone ProtocolEngine Node Cancel failed")); break; case ESocket: PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::CancelCommandDone Socket Node Cancel failed")); break; default: OSCL_ASSERT(false); break; } } //Node cancel command is now done. OSCL_ASSERT(!iContainer->iCancelCommand.empty()); iContainer->CommandComplete(iContainer->iCancelCommand, iContainer->iCancelCommand.front(), aStatus, NULL, NULL); } //from PVMFNodeCmdStatusObserver void PVMFDownloadManagerSubNodeContainer::NodeCommandCompleted(const PVMFCmdResp& aResponse) { //A command to a sub-node is complete LOGSUBCMD((0, "PVMFDownloadManagerSubNodeContainer::NodeCommandCompleted %s () In CmdId %d Status %d", GETNODESTR, aResponse.GetCmdId(), aResponse.GetCmdStatus())); if (aResponse.GetCmdStatus() != PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iContainer->iLogger, PVLOGMSG_DEBUG, (0, "PVMFDownloadManagerSubNodeContainer::NodeCommandCompleted Failure! %d", aResponse.GetCmdStatus())); } if (aResponse.GetCmdId() == iCmdId && iCmdState == EBusy) { //Process normal command response. CommandDone(aResponse.GetCmdStatus(), aResponse.GetEventExtensionInterface(), aResponse.GetEventData()); } else if (aResponse.GetCmdId() == iCancelCmdId && iCancelCmdState == EBusy) { //Process node cancel command response CancelCommandDone(aResponse.GetCmdStatus(), aResponse.GetEventExtensionInterface(), aResponse.GetEventData()); } //Process Get License cancel command response. else if (aResponse.GetCmdId() == iCPMCancelGetLicenseCmdId && iCancelCmdState == EBusy) { CancelCommandDone(aResponse.GetCmdStatus(), aResponse.GetEventExtensionInterface(), aResponse.GetEventData()); } else { OSCL_ASSERT(false);//unexpected response. } } //From capability and config interface PVMFStatus PVMFDownloadManagerNode::getParametersSync(PvmiMIOSession aSession, PvmiKeyType aIdentifier, PvmiKvp*& aParameters, int& aNumParamElements, PvmiCapabilityContext aContext) { OSCL_UNUSED_ARG(aSession); OSCL_UNUSED_ARG(aContext); // Initialize the output parameters aNumParamElements = 0; aParameters = NULL; // Count the number of components and parameters in the key int compcount = pv_mime_string_compcnt(aIdentifier); // Retrieve the first component from the key string char* compstr = NULL; pv_mime_string_extract_type(0, aIdentifier, compstr); if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) < 0) || compcount < 2) { // First component should be "x-pvmf" and there must // be at least two components to go past x-pvmf PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::getParametersSync() Invalid key string")); return PVMFErrArgument; } // Retrieve the second component from the key string pv_mime_string_extract_type(1, aIdentifier, compstr); // Check if it is key string for Download manager if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) < 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::getParametersSync() Unsupported key")); return PVMFFailure; } if (compcount == 2) { // Since key is "x-pvmf/net" return all // nodes available at this level. Ignore attribute // since capability is only allowed // Allocate memory for the KVP list aParameters = (PvmiKvp*)oscl_malloc(DownloadManagerConfig_NumBaseKeys * sizeof(PvmiKvp)); if (aParameters == NULL) { PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::getParametersSync() Memory allocation for KVP failed")); return PVMFErrNoMemory; } oscl_memset(aParameters, 0, DownloadManagerConfig_NumBaseKeys*sizeof(PvmiKvp)); // Allocate memory for the key strings in each KVP PvmiKeyType memblock = (PvmiKeyType)oscl_malloc(DownloadManagerConfig_NumBaseKeys * DLMCONFIG_KEYSTRING_SIZE * sizeof(char)); if (memblock == NULL) { oscl_free(aParameters); PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::getParametersSync() Memory allocation for key string failed")); return PVMFErrNoMemory; } oscl_strset(memblock, 0, DownloadManagerConfig_NumBaseKeys*DLMCONFIG_KEYSTRING_SIZE*sizeof(char)); // Assign the key string buffer to each KVP uint32 j; for (j = 0; j < DownloadManagerConfig_NumBaseKeys; ++j) { aParameters[j].key = memblock + (j * DLMCONFIG_KEYSTRING_SIZE); } // Copy the requested info for (j = 0; j < DownloadManagerConfig_NumBaseKeys; ++j) { oscl_strncat(aParameters[j].key, _STRLIT_CHAR("x-pvmf/net/"), 17); oscl_strncat(aParameters[j].key, DownloadManagerConfig_BaseKeys[j].iString, oscl_strlen(DownloadManagerConfig_BaseKeys[j].iString)); oscl_strncat(aParameters[j].key, _STRLIT_CHAR(";type="), 6); switch (DownloadManagerConfig_BaseKeys[j].iType) { case PVMI_KVPTYPE_AGGREGATE: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPTYPE_AGGREGATE_STRING), oscl_strlen(PVMI_KVPTYPE_AGGREGATE_STRING)); break; case PVMI_KVPTYPE_POINTER: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPTYPE_POINTER_STRING), oscl_strlen(PVMI_KVPTYPE_POINTER_STRING)); break; case PVMI_KVPTYPE_VALUE: default: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPTYPE_VALUE_STRING), oscl_strlen(PVMI_KVPTYPE_VALUE_STRING)); break; } oscl_strncat(aParameters[j].key, _STRLIT_CHAR(";valtype="), 9); switch (DownloadManagerConfig_BaseKeys[j].iValueType) { case PVMI_KVPVALTYPE_RANGE_INT32: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_RANGE_INT32_STRING), oscl_strlen(PVMI_KVPVALTYPE_RANGE_INT32_STRING)); break; case PVMI_KVPVALTYPE_KSV: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_KSV_STRING), oscl_strlen(PVMI_KVPVALTYPE_KSV_STRING)); break; case PVMI_KVPVALTYPE_CHARPTR: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_CHARPTR_STRING), oscl_strlen(PVMI_KVPVALTYPE_CHARPTR_STRING)); break; case PVMI_KVPVALTYPE_WCHARPTR: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_WCHARPTR_STRING), oscl_strlen(PVMI_KVPVALTYPE_WCHARPTR_STRING)); break; case PVMI_KVPVALTYPE_BOOL: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_BOOL_STRING), oscl_strlen(PVMI_KVPVALTYPE_BOOL_STRING)); break; case PVMI_KVPVALTYPE_UINT32: default: oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_UINT32_STRING), oscl_strlen(PVMI_KVPVALTYPE_UINT32_STRING)); break; } aParameters[j].key[DLMCONFIG_KEYSTRING_SIZE-1] = 0; } aNumParamElements = DownloadManagerConfig_NumBaseKeys; } else if (compcount == 3) { pv_mime_string_extract_type(2, aIdentifier, compstr); // Determine what is requested PvmiKvpAttr reqattr = GetAttrTypeFromKeyString(aIdentifier); if (reqattr == PVMI_KVPATTR_UNKNOWN) { reqattr = PVMI_KVPATTR_CUR; } uint i; for (i = 0; i < DownloadManagerConfig_NumBaseKeys; i++) { if (pv_mime_strcmp(compstr, (char*)(DownloadManagerConfig_BaseKeys[i].iString)) >= 0) { break; } } if (i == DownloadManagerConfig_NumBaseKeys) { // no match found PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::getParametersSync() Unsupported key")); return PVMFErrNoMemory; } } else { oscl_free(aParameters); PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::getParametersSync() Unsupported key")); return PVMFErrNoMemory; } return PVMFSuccess; } PVMFStatus PVMFDownloadManagerNode::releaseParameters(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements) { OSCL_UNUSED_ARG(aSession); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::releaseParameters() In")); if (aParameters == NULL || num_elements < 1) { PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::releaseParameters() KVP list is NULL or number of elements is 0")); return PVMFErrArgument; } // Count the number of components and parameters in the key int compcount = pv_mime_string_compcnt(aParameters[0].key); // Retrieve the first component from the key string char* compstr = NULL; pv_mime_string_extract_type(0, aParameters[0].key, compstr); if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) < 0) || compcount < 2) { // First component should be "x-pvmf" and there must // be at least two components to go past x-pvmf PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::releaseParameters() Unsupported key")); return PVMFErrArgument; } // Retrieve the second component from the key string pv_mime_string_extract_type(1, aParameters[0].key, compstr); // Assume all the parameters come from the same component so the base components are the same if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) >= 0) { // Go through each KVP and release memory for value if allocated from heap for (int32 i = 0; i < num_elements; ++i) { // Next check if it is a value type that allocated memory PvmiKvpType kvptype = GetTypeFromKeyString(aParameters[i].key); if (kvptype == PVMI_KVPTYPE_VALUE || kvptype == PVMI_KVPTYPE_UNKNOWN) { PvmiKvpValueType keyvaltype = GetValTypeFromKeyString(aParameters[i].key); if (keyvaltype == PVMI_KVPVALTYPE_UNKNOWN) { PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::releaseParameters() Valtype not specified in key string")); return PVMFErrArgument; } if (keyvaltype == PVMI_KVPVALTYPE_CHARPTR && aParameters[i].value.pChar_value != NULL) { oscl_free(aParameters[i].value.pChar_value); aParameters[i].value.pChar_value = NULL; } else if (keyvaltype == PVMI_KVPVALTYPE_WCHARPTR && aParameters[i].value.pWChar_value != NULL) { oscl_free(aParameters[i].value.pWChar_value); aParameters[i].value.pWChar_value = NULL; } else if (keyvaltype == PVMI_KVPVALTYPE_CHARPTR && aParameters[i].value.pChar_value != NULL) { oscl_free(aParameters[i].value.pChar_value); aParameters[i].value.pChar_value = NULL; } else if (keyvaltype == PVMI_KVPVALTYPE_KSV && aParameters[i].value.key_specific_value != NULL) { oscl_free(aParameters[i].value.key_specific_value); aParameters[i].value.key_specific_value = NULL; } else if (keyvaltype == PVMI_KVPVALTYPE_RANGE_INT32 && aParameters[i].value.key_specific_value != NULL) { range_int32* ri32 = (range_int32*)aParameters[i].value.key_specific_value; aParameters[i].value.key_specific_value = NULL; oscl_free(ri32); } else if (keyvaltype == PVMI_KVPVALTYPE_RANGE_UINT32 && aParameters[i].value.key_specific_value != NULL) { range_uint32* rui32 = (range_uint32*)aParameters[i].value.key_specific_value; aParameters[i].value.key_specific_value = NULL; oscl_free(rui32); } } } oscl_free(aParameters[0].key); // Free memory for the parameter list oscl_free(aParameters); aParameters = NULL; } else { // Unknown key string return PVMFErrArgument; } PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::releaseParameters() Out")); return PVMFSuccess; } void PVMFDownloadManagerNode::createContext(PvmiMIOSession aSession, PvmiCapabilityContext& aContext) { OSCL_UNUSED_ARG(aSession); OSCL_UNUSED_ARG(aContext); // not supported OSCL_LEAVE(PVMFErrNotSupported); } void PVMFDownloadManagerNode::setContextParameters(PvmiMIOSession aSession, PvmiCapabilityContext& aContext, PvmiKvp* aParameters, int num_parameter_elements) { OSCL_UNUSED_ARG(aSession); OSCL_UNUSED_ARG(aContext); OSCL_UNUSED_ARG(aParameters); OSCL_UNUSED_ARG(num_parameter_elements); // not supported OSCL_LEAVE(PVMFErrNotSupported); } void PVMFDownloadManagerNode::DeleteContext(PvmiMIOSession aSession, PvmiCapabilityContext& aContext) { OSCL_UNUSED_ARG(aSession); OSCL_UNUSED_ARG(aContext); // not supported OSCL_LEAVE(PVMFErrNotSupported); } void PVMFDownloadManagerNode::setParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements, PvmiKvp * & aRet_kvp) { OSCL_UNUSED_ARG(aSession); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::setParametersSync() In")); aRet_kvp = NULL; // Go through each parameter for (int paramind = 0; paramind < num_elements; ++paramind) { // Count the number of components and parameters in the key int compcount = pv_mime_string_compcnt(aParameters[paramind].key); // Retrieve the first component from the key string char* compstr = NULL; pv_mime_string_extract_type(0, aParameters[paramind].key, compstr); if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) < 0) || compcount < 2) { // First component should be "x-pvmf" and there must // be at least two components to go past x-pvmf aRet_kvp = &aParameters[paramind]; PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::setParametersSync() Unsupported key")); return; } // Retrieve the second component from the key string pv_mime_string_extract_type(1, aParameters[paramind].key, compstr); // First check if it is key string for the Download manager if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) >= 0) { if (compcount == 3) { pv_mime_string_extract_type(2, aParameters[paramind].key, compstr); uint i; for (i = 0; i < DownloadManagerConfig_NumBaseKeys; i++) { if (pv_mime_strcmp(compstr, (char*)(DownloadManagerConfig_BaseKeys[i].iString)) >= 0) { break; } } if (DownloadManagerConfig_NumBaseKeys == i) { // invalid third component aRet_kvp = &aParameters[paramind]; PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::setParametersSync() Unsupported key")); return; } // Verify and set the passed-in setting switch (i) { case BASEKEY_SESSION_CONTROLLER_USER_AGENT: { if (IsDownloadExtensionHeaderValid(*aParameters)) { //setting KVP string for download when mode applied for download or not applied at all. OSCL_wHeapString userAgent; userAgent = aParameters[paramind].value.pWChar_value; (iProtocolEngineNode.ProtocolEngineExtension())->SetUserAgent(userAgent, true); } } break; case BASEKEY_SESSION_CONTROLLER_HTTP_VERSION: { uint32 httpVersion; httpVersion = aParameters[paramind].value.uint32_value; (iProtocolEngineNode.ProtocolEngineExtension())->SetHttpVersion(httpVersion); } break; case BASEKEY_SESSION_CONTROLLER_HTTP_TIMEOUT: { uint32 httpTimeout; httpTimeout = aParameters[paramind].value.uint32_value; (iProtocolEngineNode.ProtocolEngineExtension())->SetNetworkTimeout(httpTimeout); } break; case BASEKEY_SESSION_CONTROLLER_DOWNLOAD_PROGRESS_INFO: { OSCL_HeapString downloadProgressInfo; downloadProgressInfo = aParameters[paramind].value.pChar_value; DownloadProgressMode aMode = DownloadProgressMode_TimeBased; if (IsByteBasedDownloadProgress(downloadProgressInfo)) aMode = DownloadProgressMode_ByteBased; (iProtocolEngineNode.ProtocolEngineExtension())->SetDownloadProgressMode(aMode); } break; case BASEKEY_SESSION_CONTROLLER_PROTOCOL_EXTENSION_HEADER: { if (IsDownloadExtensionHeaderValid(aParameters[paramind])) { OSCL_HeapString extensionHeaderKey; OSCL_HeapString extensionHeaderValue; HttpMethod httpMethod = HTTP_GET; bool aPurgeOnRedirect = false; if (GetHttpExtensionHeaderParams(aParameters[paramind], extensionHeaderKey, extensionHeaderValue, httpMethod, aPurgeOnRedirect)) { (iProtocolEngineNode.ProtocolEngineExtension())->SetHttpExtensionHeaderField(extensionHeaderKey, extensionHeaderValue, httpMethod, aPurgeOnRedirect); } } } break; case BASEKEY_SESSION_CONTROLLER_NUM_REDIRECT_ATTEMPTS: { if (IsDownloadExtensionHeaderValid(*aParameters)) { //setting KVP string for download when mode applied for download or not applied at all. uint32 numRedirects = aParameters[paramind].value.uint32_value; (iProtocolEngineNode.ProtocolEngineExtension())->SetNumRedirectTrials(numRedirects); } } break; case BASEKEY_SESSION_CONTROLLER_NUM_HTTP_HEADER_REQUEST_DISABLED: { bool httpHeaderRequestDisabled = aParameters[paramind].value.bool_value; (iProtocolEngineNode.ProtocolEngineExtension())->DisableHttpHeadRequest(httpHeaderRequestDisabled); } break; case BASEKEY_MAX_TCP_RECV_BUFFER_SIZE: { uint32 size = aParameters[paramind].value.uint32_value; PVMFSocketNode* socketNode = (PVMFSocketNode*)(iSocketNode.iNode); if (socketNode != NULL) { socketNode->SetMaxTCPRecvBufferSize(size); } } break; case BASEKEY_MAX_TCP_RECV_BUFFER_COUNT: { uint32 size = aParameters[paramind].value.uint32_value; PVMFSocketNode* socketNode = (PVMFSocketNode*)(iSocketNode.iNode); if (socketNode != NULL) { socketNode->SetMaxTCPRecvBufferCount(size); } } break; default: { aRet_kvp = &aParameters[paramind]; PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::setParametersSync() Setting " "parameter %d failed", paramind)); } break; } } else { // Do not support more than 3 components right now aRet_kvp = &aParameters[paramind]; PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::setParametersSync() Unsupported key")); return; } } else { // Unknown key string aRet_kvp = &aParameters[paramind]; PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFDownloadManagerNode::setParametersSync() Unsupported key")); return; } } PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFDownloadManagerNode::setParametersSync() Out")); } bool PVMFDownloadManagerNode::IsByteBasedDownloadProgress(OSCL_String &aDownloadProgressInfo) { if (aDownloadProgressInfo.get_size() < 4) return false; // 4 => byte char *ptr = (char*)aDownloadProgressInfo.get_cstr(); uint32 len = aDownloadProgressInfo.get_size(); while (!(((ptr[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'b') && ((ptr[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'y') && ((ptr[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') && ((ptr[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'e')) && len >= 4) { ptr++; len--; } if (len < 4) return false; return true; // find case-insentive string "byte" } bool PVMFDownloadManagerNode::GetHttpExtensionHeaderParams(PvmiKvp &aParameter, OSCL_String &extensionHeaderKey, OSCL_String &extensionHeaderValue, HttpMethod &httpMethod, bool &aPurgeOnRedirect) { // check if the extension header is meant for download if (!IsHttpExtensionHeaderValid(aParameter)) return false; // get aPurgeOnRedirect aPurgeOnRedirect = false; OSCL_StackString<32> purgeOnRedirect(_STRLIT_CHAR("purge-on-redirect")); if (oscl_strstr(aParameter.key, purgeOnRedirect.get_cstr()) != NULL) { aPurgeOnRedirect = true; } // get key, value and http method of protocol extension header // the string value needs to be structured as follows: "key=app-feature-tag;value=xyz" char* extensionHeader = aParameter.value.pChar_value; if (!extensionHeader) return false; // (1) extract the key OSCL_StackString<8> keyTag(_STRLIT_CHAR("key=")); OSCL_StackString<8> valueTag(_STRLIT_CHAR("value=")); char *keyStart = OSCL_CONST_CAST(char*, oscl_strstr(extensionHeader, keyTag.get_cstr())); if (!keyStart) return false; keyStart += keyTag.get_size(); char *keyEnd = OSCL_CONST_CAST(char*, oscl_strstr(extensionHeader, valueTag.get_cstr())); if (!keyEnd) return false; uint32 keyLen = getItemLen(keyStart, keyEnd); if (keyLen == 0) return false; extensionHeaderKey = OSCL_HeapString (keyStart, keyLen); // (2) extract the value char* valueStart = keyEnd; valueStart += valueTag.get_size(); OSCL_StackString<8> methodTag(_STRLIT_CHAR("method=")); char* valueEnd = OSCL_CONST_CAST(char*, oscl_strstr(valueStart, methodTag.get_cstr())); if (!valueEnd) valueEnd = extensionHeader + aParameter.capacity; uint32 valueLen = getItemLen(valueStart, valueEnd); extensionHeaderValue = OSCL_HeapString (valueStart, valueLen); // (3) check for optional method const char *methodStart = oscl_strstr(extensionHeader, methodTag.get_cstr()); if (!methodStart) { httpMethod = HTTP_GET; return true; } methodStart += methodTag.get_size(); OSCL_StackString<8> methodHttpGet(_STRLIT_CHAR("GET")); OSCL_StackString<8> methodHttpHead(_STRLIT_CHAR("HEAD")); OSCL_StackString<8> methodHttpPost(_STRLIT_CHAR("POST")); const char* methodGet = oscl_strstr(methodStart, methodHttpGet.get_cstr()); const char* methodHead = oscl_strstr(methodStart, methodHttpHead.get_cstr()); const char* methodPost = oscl_strstr(methodStart, methodHttpPost.get_cstr()); httpMethod = HTTP_GET; if (methodPost != NULL) httpMethod = HTTP_POST; if (methodGet != NULL) httpMethod = HTTP_GET; if (methodHead != NULL) httpMethod = HTTP_HEAD; if ((methodGet != NULL) && (methodHead != NULL)) httpMethod = HTTP_ALLMETHOD; return true; } bool PVMFDownloadManagerNode::IsHttpExtensionHeaderValid(PvmiKvp &aParameter) { OSCL_StackString<32> downloadMode(_STRLIT_CHAR("mode=download")); OSCL_StackString<32> streamingMode(_STRLIT_CHAR("mode=streaming")); bool isDownloadMode = (oscl_strstr(aParameter.key, downloadMode.get_cstr()) != NULL); bool isStreamingMode = (oscl_strstr(aParameter.key, streamingMode.get_cstr()) != NULL); // streaming mode only would fail, download mode specified or not specified will be viewed as true if (isStreamingMode && !isDownloadMode) return false; return true; } // remove the ending ';', ',' or ' ' and calulate value length uint32 PVMFDownloadManagerNode::getItemLen(char *ptrItemStart, char *ptrItemEnd) { char *ptr = ptrItemEnd - 1; uint32 itemLen = ptr - ptrItemStart; for (uint32 i = 0; i < itemLen; i++) { if (*ptr == ';' || *ptr == ',' || *ptr == ' ') --ptr; else break; } itemLen = ptr - ptrItemStart + 1; return itemLen; } PVMFCommandId PVMFDownloadManagerNode::setParametersAsync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements, PvmiKvp*& aRet_kvp, OsclAny* context) { OSCL_UNUSED_ARG(aSession); OSCL_UNUSED_ARG(aParameters); OSCL_UNUSED_ARG(num_elements); OSCL_UNUSED_ARG(aRet_kvp); OSCL_UNUSED_ARG(context); // not supported OSCL_LEAVE(PVMFErrNotSupported); return 0; } uint32 PVMFDownloadManagerNode::getCapabilityMetric(PvmiMIOSession aSession) { OSCL_UNUSED_ARG(aSession); return 0; } PVMFStatus PVMFDownloadManagerNode::verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements) { OSCL_UNUSED_ARG(aSession); OSCL_UNUSED_ARG(aParameters); OSCL_UNUSED_ARG(num_elements); // not supported OSCL_LEAVE(PVMFErrNotSupported); return 0; } bool PVMFDownloadManagerNode::IsDownloadExtensionHeaderValid(PvmiKvp &aParameter) { OSCL_StackString<32> downloadMode(_STRLIT_CHAR("mode=download")); OSCL_StackString<32> streamingMode(_STRLIT_CHAR("mode=streaming")); OSCL_StackString<32> dlaMode(_STRLIT_CHAR("mode=dla")); bool isDownloadMode = (oscl_strstr(aParameter.key, downloadMode.get_cstr()) != NULL); bool isStreamingMode = (oscl_strstr(aParameter.key, streamingMode.get_cstr()) != NULL); bool isDlaMode = (oscl_strstr(aParameter.key, dlaMode.get_cstr()) != NULL); // streaming mode only would fail, download mode specified or not specified will be viewed as true if (isStreamingMode && !isDownloadMode) return false; // dla mode only would fail, download mode specified or not specified will be viewed as true if (isDlaMode && !isDownloadMode) return false; return true; } void PVMFDownloadManagerNode::NotificationsInterfaceDestroyed() { iClockNotificationsInf = NULL; } void PVMFDownloadManagerNode::ClockStateUpdated() { if (!iDataReady) { // Don't let anyone start the clock while the source node is in underflow if (iPlayBackClock != NULL) { if (iPlayBackClock->GetState() == PVMFMediaClock::RUNNING) { iPlayBackClock->Pause(); } } } }