/* ------------------------------------------------------------------ * 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. * ------------------------------------------------------------------- */ /** * @file pvmp4ffcn_node.cpp * @brief Node for PV MPEG4 file format composer */ #ifdef ANDROID // #define LOG_NDEBUG 0 #define LOG_TAG "PvMp4Composer" #include #include #include #endif // ANDROID #ifndef PVMP4FFCN_NODE_H_INCLUDED #include "pvmp4ffcn_node.h" #endif #ifndef PVMP4FFCN_FACTORY_H_INCLUDED #include "pvmp4ffcn_factory.h" #endif #ifndef PVMP4FFCN_PORT_H_INCLUDED #include "pvmp4ffcn_port.h" #endif #ifndef OSCL_DLL_H_INCLUDED #include "oscl_dll.h" #endif #ifndef OSCL_MEM_BASIC_FUNCTIONS_H #include "oscl_mem_basic_functions.h" #endif #ifdef ANDROID namespace android { // FragmentWriter is a queue of media fragment to be written in the // media file. The caller enqueues the next fragment and returns // immediately. An separate thread dequeues the fragment and writes it. // // This class is friend with the composer node it belongs to, in order // to be able to call the original AddMemFragToTrack which does all // the work. #define FRAME_QUEUE_DEFAULT_SIZE 20 class FragmentWriter: public Thread { public: FragmentWriter(PVMp4FFComposerNode *composer) : Thread(kThreadCallJava), mComposer(composer), mPrevWriteStatus(PVMFSuccess), mTid(NULL), mExitRequested(false) { mQueue.reserve(FRAME_QUEUE_DEFAULT_SIZE); } virtual ~FragmentWriter() { Mutex::Autolock l(mRequestMutex); LOG_ASSERT(mExitRequested, "Deleting an active instance."); LOGD_IF(!mQueue.empty(), "Releasing %d fragments in dtor", mQueue.size()); while (!mQueue.empty()) // make sure we are flushed { releaseQueuedFrame(mQueue.begin()); } } // Mark the thread as exiting and kick it so it can see the // exitPending state. virtual void requestExit() { mExitRequested = true; Thread::requestExit(); mRequestMutex.lock(); mRequestCv.signal(); mRequestMutex.unlock(); } // Wait for all the fragment to be written. virtual void flush() { LOG_ASSERT(androidGetThreadId() != mTid, "Reentrant call"); bool done = false; size_t iter = 0; while (!done) { mRequestMutex.lock(); done = mQueue.empty(); if (!done) mRequestCv.signal(); mRequestMutex.unlock(); if (!done) { usleep(kFlushSleepMicros); if ((++iter % kMaxFlushAttemptsWarning) == 0) { if (iter >= kMaxFlushAttemptsCrashing) { LOGE("Fragment flush takes way too long!"); // Crash media server! *((char *) 0) = 0x01; } else { LOGW("Fragement writer flush takes %d us", iter * kFlushSleepMicros); } } } } } // Called by the ProcessIncomingMsg method from the // PVMp4FFComposerNode to append the fragment to the track. // @return The result of the *previous* fragment written. Since the call // is asynch we cannot wait. PVMFStatus enqueueMemFragToTrack(Oscl_Vector aFrame, OsclRefCounterMemFrag& aMemFrag, PVMFFormatType aFormat, uint32& aTimestamp, int32 aTrackId, PVMp4FFComposerPort *aPort) { if (mExitRequested) { LOGW("Enqueue fragment after exit request!"); aFrame.clear(); // Release the frame return PVMFErrCancelled; } Mutex::Autolock lock(mRequestMutex); Request frame = {aFrame, aMemFrag, aFormat, aTimestamp, aTrackId, aPort}; mQueue.push_back(frame); mRequestCv.signal(); return mPrevWriteStatus; } private: static const bool kThreadCallJava = false; static const OsclRefCounterMemFrag kEmptyFrag; static const int kFlushSleepMicros = 200 * 1000; // 200 ms static const size_t kMaxFlushAttemptsWarning = 10; // 2 seconds static const size_t kMaxFlushAttemptsCrashing = 30; // 6 seconds struct Request { Oscl_Vector mFrame; OsclRefCounterMemFrag mFrag; PVMFFormatType mFormat; uint32 mTimestamp; uint32 mTrackId; PVMp4FFComposerPort *mPort; }; void releaseQueuedFrame(Request *frame) { if (!frame) { LOGE("Frame not valid"); return; } frame->mFrame.clear(); // Release the memory fragment tracked using a refcount // class. Need to assign an empty frag to release the memory // fragment. We cannot wait for the array to wrap around. frame->mFrag = kEmptyFrag; // FIXME: This assignement to decr the ref count is ugly. mQueue.erase(frame); } // Called by the base class Thread. // @return true if there more work to do. false when done. // @Override Thread virtual bool threadLoop() { if (!mTid) mTid = androidGetThreadId(); LOG_ASSERT(androidGetThreadId() == mTid, "Thread id has changed!: %p != %p", mTid, androidGetThreadId()); if (mExitRequested) return false; mRequestMutex.lock(); if (mQueue.empty()) mRequestCv.wait(mRequestMutex); if (!mQueue.empty()) { // Hold the lock while writing the fragment Request frame = mQueue[0]; // Make a local copy mPrevWriteStatus = mComposer->AddMemFragToTrack( frame.mFrame, frame.mFrag, frame.mFormat, frame.mTimestamp, frame.mTrackId, frame.mPort); if (!mQueue.empty()) { releaseQueuedFrame(mQueue.begin()); } } mRequestMutex.unlock(); return true; } Mutex mRequestMutex; // Protects mRequestCv, mQueue Condition mRequestCv; Oscl_Vector mQueue; // mComposer with the real implementation of the AddMemFragToTrack method. PVMp4FFComposerNode *mComposer; // TODO: lock needed for mPrevWriteStatus? Are int assignement atomic on arm? PVMFStatus mPrevWriteStatus; android_thread_id_t mTid; // Unlike exitPending(), stays to true once exit has been called. bool mExitRequested; }; const OsclRefCounterMemFrag FragmentWriter::kEmptyFrag; } #endif // ANDROID #define LOG_STACK_TRACE(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, m); #define LOG_DEBUG(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, m); #define LOG_ERR(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m); #define LOGDATATRAFFIC(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iDataPathLogger,PVLOGMSG_INFO,m); #ifdef _TEST_AE_ERROR_HANDLING const uint32 FAIL_NODE_CMD_START = 2; const uint32 FAIL_NODE_CMD_STOP = 3; const uint32 FAIL_NODE_CMD_FLUSH = 4; const uint32 FAIL_NODE_CMD_PAUSE = 5; const uint32 FAIL_NODE_CMD_RELEASE_PORT = 7; #endif #define SLASH '/' #define LANG_CODE_SIZE 3 // Define entry point for this DLL OSCL_DLL_ENTRY_POINT_DEFAULT() //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFNodeInterface* PVMp4FFComposerNodeFactory::CreateMp4FFComposer(int32 aPriority) { int32 err = 0; PVMFNodeInterface* node = NULL; OSCL_TRY(err, node = (PVMFNodeInterface*)OSCL_NEW(PVMp4FFComposerNode, (aPriority)); if (!node) OSCL_LEAVE(OsclErrNoMemory); ); OSCL_FIRST_CATCH_ANY(err, return NULL;); return node; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF bool PVMp4FFComposerNodeFactory::DeleteMp4FFComposer(PVMFNodeInterface* aComposer) { if (aComposer) { PVMp4FFComposerNode* node = (PVMp4FFComposerNode*)aComposer; OSCL_DELETE(node); return true; } return false; } //////////////////////////////////////////////////////////////////////////// PVMp4FFComposerNode::PVMp4FFComposerNode(int32 aPriority) : OsclActiveObject(aPriority, "PVMp4FFComposerNode") , iMpeg4File(NULL) , iFileType(0) , iAuthoringMode(PVMP4FF_3GPP_DOWNLOAD_MODE) , iPresentationTimescale(1000) , iMovieFragmentDuration(2000) , iRecordingYear(0) , iClockConverter(8000) , iExtensionRefCount(0) , iRealTimeTS(false) , iInitTSOffset(false) , iTSOffset(0) , iMaxFileSizeEnabled(false) , iMaxDurationEnabled(false) , iMaxFileSize(0) , iMaxTimeDuration(0) , iFileSizeReportEnabled(false) , iDurationReportEnabled(false) , iFileSizeReportFreq(0) , iDurationReportFreq(0) , iNextDurationReport(0) , iNextFileSizeReport(0) , iCacheSize(0) , iConfigSize(0) , pConfig(NULL) , iTrackId_H264(0) , iTrackId_Text(0) , iSyncSample(0) , iformat_h264(PVMF_MIME_FORMAT_UNKNOWN) , iformat_text(PVMF_MIME_FORMAT_UNKNOWN) , iNodeEndOfDataReached(false) , iSampleInTrack(false) , iFileRendered(false) { iInterfaceState = EPVMFNodeCreated; iNum_PPS_Set = 0; iNum_SPS_Set = 0; iText_sdIndex = 0; iFileObject = NULL; #if PROFILING_ON iMaxSampleAddTime = 0; iMinSampleAddTime = 0; iMinSampleSize = 0; iMaxSampleSize = 0; iNumSamplesAdded = 0; oDiagnosticsLogged = false; iDiagnosticsLogger = PVLogger::GetLoggerObject("pvauthordiagnostics.composer.mp4"); // Statistics for (uint32 i = 0; i < 3; i++) oscl_memset(&(iStats[i]), 0, sizeof(PVMp4FFCNStats)); #endif iLogger = PVLogger::GetLoggerObject("PVMp4FFComposerNode"); iDataPathLogger = PVLogger::GetLoggerObject("datapath.sinknode.mp4composer"); int32 err; OSCL_TRY(err, //Create the input command queue. Use a reserve to avoid lots of //dynamic memory allocation. iCmdQueue.Construct(PVMF_MP4FFCN_COMMAND_ID_START, PVMF_MP4FFCN_COMMAND_VECTOR_RESERVE); iCurrentCmd.Construct(0, 1); // There's only 1 current command //Create the port vector. iInPorts.Construct(PVMF_MP4FFCN_PORT_VECTOR_RESERVE); ); OSCL_FIRST_CATCH_ANY(err, //if a leave happened, cleanup and re-throw the error iCmdQueue.clear(); iCurrentCmd.clear(); iInPorts.clear(); memvector_sps.clear(); memvector_pps.clear(); OSCL_CLEANUP_BASE_CLASS(PVMFNodeInterface); OSCL_CLEANUP_BASE_CLASS(OsclActiveObject); OSCL_LEAVE(err); ); #ifdef ANDROID iMaxReachedEvent = 0; iMaxReachedReported = false; iFragmentWriter = new android::FragmentWriter(this); iFragmentWriter->run(LOG_TAG); #endif #ifdef _TEST_AE_ERROR_HANDLING iErrorHandlingAddMemFrag = false; iErrorHandlingAddTrack = false; iErrorCreateComposer = false; iErrorRenderToFile = false; iErrorAddTrack = PVMF_MIME_FORMAT_UNKNOWN; iErrorNodeCmd = 0; iTestFileSize = 0; iTestTimeStamp = 0; iErrorAddSample = 0; iFileSize = 0; iFileDuration = 0; iErrorDataPathStall = 0; #endif } //////////////////////////////////////////////////////////////////////////// PVMp4FFComposerNode::~PVMp4FFComposerNode() { #if PROFILING_ON if (!oDiagnosticsLogged) { LogDiagnostics(); } #endif #ifdef ANDROID if (iFragmentWriter != NULL) { iFragmentWriter->requestExit(); // kick the thread iFragmentWriter->requestExitAndWait(); } #endif if (iMpeg4File) { PVA_FF_IMpeg4File::DestroyMP4FileObject(iMpeg4File); if (!iFileRendered) { iFs.Connect(); iFs.Oscl_DeleteFile(iFileName.get_cstr()); iFs.Close(); } } if (iFileObject) { iFileObject->Close(); OSCL_DELETE(iFileObject); iFileObject = NULL; } for (uint32 i = 0; i < iKeyWordVector.size() ; i++) { if (iKeyWordVector[i] != NULL) { OSCL_DELETE(iKeyWordVector[i]); iKeyWordVector[i] = NULL; } } if (pConfig != NULL) { OSCL_FREE(pConfig); iConfigSize = 0; } if (iLocationInfo._location_name != NULL) { OSCL_FREE(iLocationInfo._location_name); } if (iLocationInfo._astronomical_body != NULL) { OSCL_FREE(iLocationInfo._astronomical_body); } if (iLocationInfo._additional_notes != NULL) { OSCL_FREE(iLocationInfo._additional_notes); } // Cleanup allocated ports while (!iInPorts.empty()) { iInPorts.Erase(&iInPorts.front()); } //Cleanup commands //The command queues are self-deleting, but we want to //notify the observer of unprocessed commands. while (!iCmdQueue.empty()) { CommandComplete(iCmdQueue, iCmdQueue[0], PVMFFailure); } while (!iCurrentCmd.empty()) { CommandComplete(iCurrentCmd, iCurrentCmd[0], PVMFFailure); } iNodeEndOfDataReached = false; Cancel(); if (iInterfaceState != EPVMFNodeCreated) iInterfaceState = EPVMFNodeIdle; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::ThreadLogon() { switch (iInterfaceState) { case EPVMFNodeCreated: if (!IsAdded()) AddToScheduler(); SetState(EPVMFNodeIdle); return PVMFSuccess; default: return PVMFErrInvalidState; } } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::ThreadLogoff() { PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMp4FFComposerNode:ThreadLogoff")); switch (iInterfaceState) { case EPVMFNodeIdle: if (IsAdded()) RemoveFromScheduler(); iLogger = NULL; iDataPathLogger = NULL; SetState(EPVMFNodeCreated); return PVMFSuccess; default: return PVMFErrInvalidState; } } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::GetCapability(PVMFNodeCapability& aNodeCapability) { aNodeCapability.iCanSupportMultipleInputPorts = true; aNodeCapability.iCanSupportMultipleOutputPorts = false; aNodeCapability.iHasMaxNumberOfPorts = true; aNodeCapability.iMaxNumberOfPorts = PVMF_MP4FFCN_MAX_INPUT_PORT + PVMF_MP4FFCN_MAX_OUTPUT_PORT; aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_M4V); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_H264_VIDEO_MP4); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_H2631998); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_H2632000); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_AMR_IETF); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_AMRWB_IETF); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_3GPP_TIMEDTEXT); aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_MPEG4_AUDIO); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFPortIter* PVMp4FFComposerNode::GetPorts(const PVMFPortFilter* aFilter) { OSCL_UNUSED_ARG(aFilter); iInPorts.Reset(); return &iInPorts; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::QueryUUID(PVMFSessionId aSession, const PvmfMimeString& aMimeType, Oscl_Vector& aUuids, bool aExactUuidsOnly, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::QueryUUID")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_QUERYUUID, aMimeType, aUuids, aExactUuidsOnly, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::QueryInterface(PVMFSessionId aSession, const PVUuid& aUuid, PVInterface*& aInterfacePtr, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::QueryInterface")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Init(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Init")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_INIT, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Prepare(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Prepare")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_PREPARE, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::RequestPort(PVMFSessionId aSession, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::RequestPort")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_REQUESTPORT, aPortTag, aPortConfig, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::ReleasePort(PVMFSessionId aSession, PVMFPortInterface& aPort, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::ReleasePort")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_RELEASEPORT, aPort, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Start(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Start")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_START, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Stop(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Stop")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_STOP, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Pause(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Pause")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_PAUSE, aContext); return QueueCommandL(cmd); } OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Flush(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Flush")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_FLUSH, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::Reset(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::Reset")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_RESET, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::CancelAllCommands(PVMFSessionId aSession, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::CancelAllCommands")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_CANCELALLCOMMANDS, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFCommandId PVMp4FFComposerNode::CancelCommand(PVMFSessionId aSession, PVMFCommandId aCmdId, const OsclAny* aContext) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::CancelCommand")); PVMp4FFCNCmd cmd; cmd.Construct(aSession, PVMF_GENERIC_NODE_CANCELCOMMAND, aCmdId, aContext); return QueueCommandL(cmd); } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF void PVMp4FFComposerNode::addRef() { ++iExtensionRefCount; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF void PVMp4FFComposerNode::removeRef() { if (iExtensionRefCount > 0) --iExtensionRefCount; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF bool PVMp4FFComposerNode::queryInterface(const PVUuid& uuid, PVInterface*& iface) { if (uuid == KPVMp4FFCNClipConfigUuid) { PVMp4FFCNClipConfigInterface* myInterface = OSCL_STATIC_CAST(PVMp4FFCNClipConfigInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); ++iExtensionRefCount; } else if (uuid == KPVMp4FFCNTrackConfigUuid) { PVMp4FFCNTrackConfigInterface* myInterface = OSCL_STATIC_CAST(PVMp4FFCNTrackConfigInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); ++iExtensionRefCount; } else if (uuid == PvmfComposerSizeAndDurationUuid) { PvmfComposerSizeAndDurationInterface* myInterface = OSCL_STATIC_CAST(PvmfComposerSizeAndDurationInterface*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); ++iExtensionRefCount; } else if (uuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID) { PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this); iface = OSCL_STATIC_CAST(PVInterface*, myInterface); ++iExtensionRefCount; } else { iface = NULL; return false; } return true; } //////////////////////////////////////////////////////////////////////////// // PVMp4FFCNClipConfigInterface routines //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF uint16 PVMp4FFComposerNode::ConvertLangCode(const OSCL_String & aLang) { int i = 0; char lang[LANG_CODE_SIZE] = {0}; oscl_strncpy(lang, aLang.get_cstr(), LANG_CODE_SIZE); uint16 lang_code = ((((uint16)lang[i] - 0x60) << 10) | (((uint16)lang[i+1] - 0x60) << 5) | ((uint16)lang[i+2] - 0x60)); return lang_code; } ///////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetOutputFileName(const OSCL_wString& aFileName) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized) return false; iFileName = aFileName; return PVMFSuccess; } ////////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetOutputFileDescriptor(const OsclFileHandle* aFileHandle) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized) return false; iFileObject = OSCL_NEW(Oscl_File, (0, (OsclFileHandle *)aFileHandle)); iFileObject->SetPVCacheSize(0); iFileObject->SetAsyncReadBufferSize(0); iFileObject->SetNativeBufferSize(0); iFileObject->SetLoggingEnable(false); iFileObject->SetSummaryStatsLoggingEnable(false); iFileObject->SetFileHandle((OsclFileHandle*)aFileHandle); //call open int32 retval = iFileObject->Open(_STRLIT_CHAR("dummy"), Oscl_File::MODE_READWRITE | Oscl_File::MODE_BINARY, iFs); if (retval == 0) { return PVMFSuccess; } return PVMFFailure; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetAuthoringMode(PVMp4FFCN_AuthoringMode aAuthoringMode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized) return PVMFErrInvalidState; iAuthoringMode = aAuthoringMode; return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetPresentationTimescale(uint32 aTimescale) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iPresentationTimescale = aTimescale; return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetVersion(const OSCL_wString& aVersion, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iVersion.iDataString = aVersion; iVersion.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetTitle(const OSCL_wString& aTitle, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iTitle.iDataString = aTitle; iTitle.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetAuthor(const OSCL_wString& aAuthor, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iAuthor.iDataString = aAuthor; iAuthor.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetCopyright(const OSCL_wString& aCopyright, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iCopyright.iDataString = aCopyright; iCopyright.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetDescription(const OSCL_wString& aDescription, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iDescription.iDataString = aDescription; iDescription.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetRating(const OSCL_wString& aRating, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iRating.iDataString = aRating; iRating.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetCreationDate(const OSCL_wString& aCreationDate) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iCreationDate = aCreationDate; return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::SetRealTimeAuthoring(const bool aRealTime) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized) return PVMFErrInvalidState; iRealTimeTS = aRealTime; return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetAlbumInfo(const OSCL_wString& aAlbumTitle, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iAlbumTitle.iDataString = aAlbumTitle; iAlbumTitle.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetRecordingYear(uint16 aRecordingYear) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iRecordingYear = aRecordingYear; return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetPerformer(const OSCL_wString& aPerformer, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iPerformer.iDataString = aPerformer; iPerformer.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetGenre(const OSCL_wString& aGenre, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iGenre.iDataString = aGenre; iGenre.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetClassification(const OSCL_wString& aClassificationInfo, uint32 aClassificationEntity, uint16 aClassificationTable, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iClassification.iDataString = aClassificationInfo; iClassification.iClassificationEntity = aClassificationEntity; iClassification.iClassificationTable = aClassificationTable; iClassification.iLangCode = ConvertLangCode(aLangCode); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetKeyWord(const OSCL_wString& aKeyWordInfo, const OSCL_String& aLangCode) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; PVMP4FFCN_KeyWord *KeyWord = NULL; uint16 langCode = ConvertLangCode(aLangCode); KeyWord = OSCL_NEW(PVMP4FFCN_KeyWord, (aKeyWordInfo, aKeyWordInfo.get_size(), langCode)); iKeyWordVector.push_back(KeyWord); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetLocationInfo(PvmfAssetInfo3GPPLocationStruct& aLocation_info) { if (iInterfaceState != EPVMFNodeIdle && iInterfaceState != EPVMFNodeInitialized && iInterfaceState != EPVMFNodePrepared) return PVMFErrInvalidState; iLocationInfo._location_name = NULL; uint32 size = oscl_strlen(aLocation_info._location_name); iLocationInfo._location_name = (oscl_wchar*)oscl_malloc(sizeof(oscl_wchar) * size + 10); oscl_strncpy(iLocationInfo._location_name, aLocation_info._location_name, size); iLocationInfo._location_name[size+1] = 0; iLocationInfo._astronomical_body = NULL; size = oscl_strlen(aLocation_info._astronomical_body); iLocationInfo._astronomical_body = (oscl_wchar*)oscl_malloc(sizeof(oscl_wchar) * size + 10); oscl_strncpy(iLocationInfo._astronomical_body, aLocation_info._astronomical_body, size); iLocationInfo._astronomical_body[size+1] = 0; iLocationInfo._additional_notes = NULL; size = oscl_strlen(aLocation_info._additional_notes); iLocationInfo._additional_notes = (oscl_wchar*)oscl_malloc(sizeof(oscl_wchar) * size + 10); oscl_strncpy(iLocationInfo._additional_notes, aLocation_info._additional_notes, size); iLocationInfo._additional_notes[size+1] = 0; iLocationInfo._role = aLocation_info._role; iLocationInfo._longitude = aLocation_info._longitude; iLocationInfo._latitude = aLocation_info._latitude; iLocationInfo._altitude = aLocation_info._altitude; iLocationInfo._langCode = ConvertLangCode(aLocation_info.Lang_code); return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// // PVMp4FFCNTrackConfigInterface routines //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetTrackReference(const PVMFPortInterface& aPort, const PVMFPortInterface& aReferencePort) { if (iInterfaceState != EPVMFNodeInitialized) return PVMFErrInvalidState; int32 portIndex = -1; int32 refPortIndex = -1; PVMp4FFComposerPort* port = OSCL_REINTERPRET_CAST(PVMp4FFComposerPort*, &aPort); PVMp4FFComposerPort* refPort = OSCL_REINTERPRET_CAST(PVMp4FFComposerPort*, &aReferencePort); for (uint32 i = 0; i < iInPorts.size(); i++) { if (iInPorts[i] == port) portIndex = i; if (iInPorts[i] == refPort) refPortIndex = i; } if (portIndex > 0 && refPortIndex > 0) { iInPorts[portIndex]->SetReferencePort(iInPorts[refPortIndex]); return PVMFSuccess; } else return PVMFFailure; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetCodecSpecificInfo(const PVMFPortInterface& aPort, uint8* aInfo, int32 aSize) { PVMFStatus status = PVMFFailure; if ((status == PVMFSuccess) && (iInterfaceState == EPVMFNodeStarted)) { PVMp4FFComposerPort* port = OSCL_STATIC_CAST(PVMp4FFComposerPort*, &aPort); iMpeg4File->setDecoderSpecificInfo(aInfo, aSize, port->GetTrackId()); } return status; } //////////////////////////////////////////////////////////////////////////// // PvmfComposerSizeAndDurationInterface routines //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetMaxFileSize(bool aEnable, uint32 aMaxFileSizeBytes) { iMaxFileSizeEnabled = aEnable; if (iMaxFileSizeEnabled) { iMaxFileSize = aMaxFileSizeBytes; } else { iMaxFileSize = 0; } return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF void PVMp4FFComposerNode::GetMaxFileSizeConfig(bool& aEnable, uint32& aMaxFileSizeBytes) { aEnable = iMaxFileSizeEnabled; aMaxFileSizeBytes = iMaxFileSize; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetMaxDuration(bool aEnable, uint32 aMaxDurationMilliseconds) { iMaxDurationEnabled = aEnable; if (iMaxDurationEnabled) { iMaxTimeDuration = aMaxDurationMilliseconds; } else { iMaxTimeDuration = 0; } return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF void PVMp4FFComposerNode::GetMaxDurationConfig(bool& aEnable, uint32& aMaxDurationMilliseconds) { aEnable = iMaxDurationEnabled; aMaxDurationMilliseconds = iMaxTimeDuration; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetFileSizeProgressReport(bool aEnable, uint32 aReportFrequency) { iFileSizeReportEnabled = aEnable; if (iFileSizeReportEnabled) { iFileSizeReportFreq = aReportFrequency; } return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF void PVMp4FFComposerNode::GetFileSizeProgressReportConfig(bool& aEnable, uint32& aReportFrequency) { aEnable = iFileSizeReportEnabled; aReportFrequency = iFileSizeReportFreq; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF PVMFStatus PVMp4FFComposerNode::SetDurationProgressReport(bool aEnable, uint32 aReportFrequency) { iDurationReportEnabled = aEnable; if (iDurationReportEnabled) { iDurationReportFreq = aReportFrequency; } return PVMFSuccess; } //////////////////////////////////////////////////////////////////////////// OSCL_EXPORT_REF void PVMp4FFComposerNode::GetDurationProgressReportConfig(bool& aEnable, uint32& aReportFrequency) { aEnable = iDurationReportEnabled; aReportFrequency = iDurationReportFreq; } //////////////////////////////////////////////////////////////////////////// // PVMFPortActivityHandler routines //////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::HandlePortActivity(const PVMFPortActivity& aActivity) { OSCL_UNUSED_ARG(aActivity); // Scheduling to process port activities are handled in the port itself } //////////////////////////////////////////////////////////////////////////// // OsclActiveObject routines //////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::Run() { LOG_STACK_TRACE((0, "PVMp4FFComposerNode::Run: iInterfaceState=%d", iInterfaceState)); if (!iCmdQueue.empty()) { if (ProcessCommand(iCmdQueue.front())) { //note: need to check the state before re-scheduling //since the node could have been reset in the ProcessCommand //call. if (iInterfaceState != EPVMFNodeCreated) RunIfNotReady(); return; } } LOG_STACK_TRACE((0, "PVMp4FFComposerNode::Run: Out. iInterfaceState=%d", iInterfaceState)); } //////////////////////////////////////////////////////////////////////////// // Command Processing routines //////////////////////////////////////////////////////////////////////////// PVMFCommandId PVMp4FFComposerNode::QueueCommandL(PVMp4FFCNCmd& aCmd) { int32 err = 0; PVMFCommandId id = 0; OSCL_TRY(err, id = iCmdQueue.AddL(aCmd);); OSCL_FIRST_CATCH_ANY(err, OSCL_LEAVE(err); return 0; ); // Wakeup the AO RunIfNotReady(); return id; } //////////////////////////////////////////////////////////////////////////// bool PVMp4FFComposerNode::ProcessCommand(PVMp4FFCNCmd& aCmd) { //normally this node will not start processing one command //until the prior one is finished. However, a hi priority //command such as Cancel must be able to interrupt a command //in progress. if (!iCurrentCmd.empty() && !aCmd.hipri()) return false; switch (aCmd.iCmd) { case PVMF_GENERIC_NODE_QUERYUUID: DoQueryUuid(aCmd); break; case PVMF_GENERIC_NODE_QUERYINTERFACE: DoQueryInterface(aCmd); break; case PVMF_GENERIC_NODE_REQUESTPORT: DoRequestPort(aCmd); break; case PVMF_GENERIC_NODE_RELEASEPORT: DoReleasePort(aCmd); break; case PVMF_GENERIC_NODE_INIT: DoInit(aCmd); break; case PVMF_GENERIC_NODE_PREPARE: DoPrepare(aCmd); break; case PVMF_GENERIC_NODE_START: DoStart(aCmd); break; case PVMF_GENERIC_NODE_STOP: DoStop(aCmd); break; case PVMF_GENERIC_NODE_FLUSH: DoFlush(aCmd); break; case PVMF_GENERIC_NODE_PAUSE: DoPause(aCmd); break; case PVMF_GENERIC_NODE_RESET: DoReset(aCmd); break; case PVMF_GENERIC_NODE_CANCELALLCOMMANDS: DoCancelAllCommands(aCmd); break; case PVMF_GENERIC_NODE_CANCELCOMMAND: DoCancelCommand(aCmd); break; default://unknown command type CommandComplete(iCmdQueue, aCmd, PVMFFailure); break; } return true; } //////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::CommandComplete(PVMp4FFCNCmdQueue& aCmdQueue, PVMp4FFCNCmd& aCmd, PVMFStatus aStatus, OsclAny* aEventData) { LOG_STACK_TRACE((0, "PVMp4FFComposerNode:CommandComplete: Id %d Cmd %d Status %d Context %d Data %d" , aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData)); //create response PVMFCmdResp resp(aCmd.iId, aCmd.iContext, aStatus, aEventData); PVMFSessionId session = aCmd.iSession; //Erase the command from the queue. aCmdQueue.Erase(&aCmd); //Report completion to the session observer. ReportCmdCompleteEvent(session, resp); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoQueryUuid(PVMp4FFCNCmd& aCmd) { OSCL_String* mimetype; Oscl_Vector *uuidvec; bool exactmatch; aCmd.Parse(mimetype, uuidvec, exactmatch); uuidvec->push_back(KPVMp4FFCNClipConfigUuid); uuidvec->push_back(KPVMp4FFCNTrackConfigUuid); uuidvec->push_back(PvmfComposerSizeAndDurationUuid); CommandComplete(iCmdQueue, aCmd, PVMFSuccess); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoQueryInterface(PVMp4FFCNCmd& aCmd) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::DoQueryInterface")); PVUuid* uuid; PVInterface** ptr; aCmd.Parse(uuid, ptr); if (queryInterface(*uuid, *ptr)) { CommandComplete(iCmdQueue, aCmd, PVMFSuccess); } else { CommandComplete(iCmdQueue, aCmd, PVMFFailure); } } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoRequestPort(PVMp4FFCNCmd& aCmd) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::DoRequestPort() In")); int32 tag; OSCL_String* portconfig; aCmd.Parse(tag, portconfig); //validate the tag... switch (tag) { case PVMF_MP4FFCN_PORT_TYPE_SINK: if (iInPorts.size() >= PVMF_MP4FFCN_MAX_INPUT_PORT) { LOG_ERR((0, "PVMp4FFComposerNode::DoRequestPort: Error - Max number of input port already allocated")); CommandComplete(iCmdQueue, aCmd, PVMFFailure); return; } break; default: //bad port tag PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::DoRequestPort: Error - Invalid port tag")); CommandComplete(iCmdQueue, aCmd, PVMFFailure); return; } //Allocate a new port OsclAny *ptr = NULL; int32 err; OSCL_TRY(err, ptr = iInPorts.Allocate(); if (!ptr) OSCL_LEAVE(OsclErrNoMemory); ); OSCL_FIRST_CATCH_ANY(err, PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::DoRequestPort: Error - iInPorts Out of memory")); CommandComplete(iCmdQueue, aCmd, PVMFErrNoMemory); return; ); OSCL_StackString<20> portname; portname = "PVMP4ComposerIn"; PVMp4FFComposerPort* port = OSCL_PLACEMENT_NEW(ptr, PVMp4FFComposerPort(tag, this, Priority(), portname.get_cstr())); // if format was provided in mimestring, set it now. if (portconfig) { PVMFFormatType format = portconfig->get_str(); if (format == PVMF_MIME_3GPP_TIMEDTEXT || format == PVMF_MIME_H264_VIDEO_MP4 || format == PVMF_MIME_M4V || format == PVMF_MIME_H2631998 || format == PVMF_MIME_H2632000 || format == PVMF_MIME_AMR_IETF || format == PVMF_MIME_AMRWB_IETF || format == PVMF_MIME_ADIF || format == PVMF_MIME_ADTS || format == PVMF_MIME_MPEG4_AUDIO) { port->SetFormat(format); } else { CommandComplete(iCmdQueue, aCmd, PVMFErrNotSupported); return; } } //Add the port to the port vector. OSCL_TRY(err, iInPorts.AddL(port);); OSCL_FIRST_CATCH_ANY(err, iInPorts.DestructAndDealloc(port); CommandComplete(iCmdQueue, aCmd, PVMFErrNoMemory); return; ); // Return the port pointer to the caller. CommandComplete(iCmdQueue, aCmd, PVMFSuccess, (OsclAny*)port); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoReleasePort(PVMp4FFCNCmd& aCmd) { //Find the port in the port vector PVMFPortInterface* p = NULL; for (uint32 i = 0; i < iInPorts.size(); i++) { aCmd.Parse(p); PVMp4FFComposerPort* port = (PVMp4FFComposerPort*)p; PVMp4FFComposerPort** portPtr = iInPorts.FindByValue(port); if (portPtr) { //delete the port. iInPorts.Erase(portPtr); #ifdef _TEST_AE_ERROR_HANDLING if (FAIL_NODE_CMD_RELEASE_PORT == iErrorNodeCmd) { CommandComplete(iCmdQueue, aCmd, PVMFFailure); } else { CommandComplete(iCmdQueue, aCmd, PVMFSuccess); } #else CommandComplete(iCmdQueue, aCmd, PVMFSuccess); #endif } else { //port not found. CommandComplete(iCmdQueue, aCmd, PVMFFailure); } } } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoInit(PVMp4FFCNCmd& aCmd) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::DoInitNode() In")); switch (iInterfaceState) { case EPVMFNodeIdle: // Creation of file format library is done in DoStart. Nothing to do here. SetState(EPVMFNodeInitialized); CommandComplete(iCmdQueue, aCmd, PVMFSuccess); break; case EPVMFNodeInitialized: CommandComplete(iCmdQueue, aCmd, PVMFSuccess); break; default: CommandComplete(iCmdQueue, aCmd, PVMFErrInvalidState); break; } } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoPrepare(PVMp4FFCNCmd& aCmd) { switch (iInterfaceState) { case EPVMFNodeInitialized: // Creation of file format library is done in DoStart. Nothing to do here. SetState(EPVMFNodePrepared); CommandComplete(iCmdQueue, aCmd, PVMFSuccess); break; case EPVMFNodePrepared: CommandComplete(iCmdQueue, aCmd, PVMFSuccess); break; default: CommandComplete(iCmdQueue, aCmd, PVMFErrInvalidState); break; } } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoStart(PVMp4FFCNCmd& aCmd) { PVMFStatus status = PVMFSuccess; uint32 i = 0; #ifdef _TEST_AE_ERROR_HANDLING if (FAIL_NODE_CMD_START == iErrorNodeCmd) { iInterfaceState = EPVMFNodeError; } #endif switch (iInterfaceState) { case EPVMFNodePrepared: { iPostfix = _STRLIT("00"); iOutputPath = _STRLIT(""); int32 pos = 0; for (pos = iFileName.get_size() - 1; pos >= 0; pos--) { if (iFileName[pos] == SLASH) break; } if (pos == -1) { iOutputPath = _STRLIT("."); } else { for (i = 0; i <= (uint32) pos; i++) iOutputPath += iFileName[i]; } iFileType = 0; for (i = 0; i < iInPorts.size(); i++) { if (iInPorts[i]->GetFormat() == PVMF_MIME_H264_VIDEO_MP4 || iInPorts[i]->GetFormat() == PVMF_MIME_M4V || iInPorts[i]->GetFormat() == PVMF_MIME_H2631998 || iInPorts[i]->GetFormat() == PVMF_MIME_H2632000) { iFileType |= FILE_TYPE_VIDEO; } else if (iInPorts[i]->GetFormat() == PVMF_MIME_AMR_IETF || iInPorts[i]->GetFormat() == PVMF_MIME_AMRWB_IETF || iInPorts[i]->GetFormat() == PVMF_MIME_MPEG4_AUDIO) { iFileType |= FILE_TYPE_AUDIO; } else if (iInPorts[i]->GetFormat() == PVMF_MIME_3GPP_TIMEDTEXT) { iFileType |= FILE_TYPE_TIMED_TEXT; } else { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::DoStart: Error - Unsupported format")); return; } } if (iMpeg4File) { LOG_ERR((0, "PVMp4FFComposerNode::DoStart: Error - File format library already exists")); CommandComplete(iCmdQueue, aCmd, PVMFFailure); return; } PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::DoStart: Calling PVA_FF_IMpeg4File::createMP4File(%d,0x%x,%d)", iFileType, &iFs, iAuthoringMode)); #ifdef _TEST_AE_ERROR_HANDLING //test case to fail mp4 file parser if (iErrorCreateComposer) { //to fail createMP4File() OSCL_wHeapString ErrFileName; iMpeg4File = PVA_FF_IMpeg4File::createMP4File(iFileType, iOutputPath, iPostfix, (void*) & iFs, iAuthoringMode, ErrFileName, iCacheSize); } else { if (iFileObject != NULL) { iMpeg4File = PVA_FF_IMpeg4File::createMP4File(iFileType, iAuthoringMode, iFileObject, iCacheSize); } else { iMpeg4File = PVA_FF_IMpeg4File::createMP4File(iFileType, iOutputPath, iPostfix, (void*) & iFs, iAuthoringMode, iFileName, iCacheSize); } } #else if (iFileObject != NULL) { iMpeg4File = PVA_FF_IMpeg4File::createMP4File(iFileType, iAuthoringMode, iFileObject, iCacheSize); } else { iMpeg4File = PVA_FF_IMpeg4File::createMP4File(iFileType, iOutputPath, iPostfix, (void*) & iFs, iAuthoringMode, iFileName, iCacheSize); } #endif if (!iMpeg4File) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::DoStart: Error - PVA_FF_IMpeg4File::createMP4File failed")); CommandComplete(iCmdQueue, aCmd, PVMFFailure); return; } iMpeg4File->setPresentationTimescale(iPresentationTimescale); iMpeg4File->setVersion(iVersion.iDataString, iVersion.iLangCode); iMpeg4File->setTitle(iTitle.iDataString, iTitle.iLangCode); iMpeg4File->setAuthor(iAuthor.iDataString, iAuthor.iLangCode); iMpeg4File->setCopyright(iCopyright.iDataString, iCopyright.iLangCode); iMpeg4File->setDescription(iDescription.iDataString, iDescription.iLangCode); iMpeg4File->setRating(iRating.iDataString, iRating.iLangCode); if(iCreationDate.get_size() > 0) { iMpeg4File->setCreationDate(iCreationDate); } iMpeg4File->setMovieFragmentDuration(iMovieFragmentDuration); iMpeg4File->setAlbumInfo(iAlbumTitle.iDataString, iAlbumTitle.iLangCode); iMpeg4File->setRecordingYear(iRecordingYear); iMpeg4File->setPerformer(iPerformer.iDataString, iPerformer.iLangCode); iMpeg4File->setGenre(iGenre.iDataString, iGenre.iLangCode); iMpeg4File->setClassification(iClassification.iDataString, iClassification.iClassificationEntity, iClassification.iClassificationTable, iClassification.iLangCode); for (i = 0; i < iKeyWordVector.size() ; i++) { iMpeg4File->setKeyWord(iKeyWordVector[i]->iKeyWordSize, iKeyWordVector[i]->iData_String, iKeyWordVector[i]->iLang_Code); } iMpeg4File->setLocationInfo(&iLocationInfo); for (i = 0; i < iInPorts.size(); i++) { status = AddTrack(iInPorts[i]); if (status != PVMFSuccess) { CommandComplete(iCmdQueue, aCmd, status); return; } } // Check for and set reference tracks after track IDs are assigned PVMp4FFComposerPort* refPort = NULL; for (i = 0; i < iInPorts.size(); i++) { refPort = OSCL_STATIC_CAST(PVMp4FFComposerPort*, iInPorts[i]->GetReferencePort()); if (refPort) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::DoStart: Calling addTrackReference(%d, %d)", iInPorts[i]->GetTrackId(), refPort->GetTrackId())); iMpeg4File->addTrackReference(iInPorts[i]->GetTrackId(), refPort->GetTrackId()); } } iMpeg4File->prepareToEncode(); iInitTSOffset = true; iTSOffset = 0; SetState(EPVMFNodeStarted); break; } case EPVMFNodePaused: SetState(EPVMFNodeStarted); for (i = 0; i < iInPorts.size(); i++) ((PVMp4FFComposerPort*)iInPorts[i])->ProcessIncomingMsgReady(); break; case EPVMFNodeStarted: status = PVMFSuccess; break; default: status = PVMFErrInvalidState; break; } CommandComplete(iCmdQueue, aCmd, status); } ////////////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::AddTrack(PVMp4FFComposerPort *aPort) { int32 codecType = 0; int32 mediaType = 0; int32 trackId = 0; PVMP4FFCNFormatSpecificConfig* config = aPort->GetFormatSpecificConfig(); if (!config) { LOG_ERR((0, "PVMp4FFComposerNode::AddTrack: Error - GetFormatSpecificConfig failed")); return PVMFFailure; } if (aPort->GetFormat() == PVMF_MIME_3GPP_TIMEDTEXT) { codecType = CODEC_TYPE_TIMED_TEXT; mediaType = MEDIA_TYPE_TEXT; } else if (aPort->GetFormat() == PVMF_MIME_H264_VIDEO_MP4) { codecType = CODEC_TYPE_AVC_VIDEO; mediaType = MEDIA_TYPE_VISUAL; } else if (aPort->GetFormat() == PVMF_MIME_M4V) { codecType = CODEC_TYPE_MPEG4_VIDEO; mediaType = MEDIA_TYPE_VISUAL; } else if (aPort->GetFormat() == PVMF_MIME_H2631998 || aPort->GetFormat() == PVMF_MIME_H2632000) { codecType = CODEC_TYPE_BASELINE_H263_VIDEO; mediaType = MEDIA_TYPE_VISUAL; } else if (aPort->GetFormat() == PVMF_MIME_AMR_IETF) { codecType = CODEC_TYPE_AMR_AUDIO; mediaType = MEDIA_TYPE_AUDIO; } else if (aPort->GetFormat() == PVMF_MIME_AMRWB_IETF) { codecType = CODEC_TYPE_AMR_WB_AUDIO; mediaType = MEDIA_TYPE_AUDIO; } else if (aPort->GetFormat() == PVMF_MIME_MPEG4_AUDIO) { codecType = CODEC_TYPE_AAC_AUDIO; mediaType = MEDIA_TYPE_AUDIO; } else { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddTrack: Error - Unsupported format")); return PVMFFailure; } aPort->SetCodecType(codecType); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddTrack: Calling PVA_FF_IMpeg4File::addTrack(0x%x,0x%x)", mediaType, codecType)); #ifdef _TEST_AE_ERROR_HANDLING if (aPort->GetFormat() == iErrorAddTrack) { return PVMFFailure; } #endif trackId = iMpeg4File->addTrack(mediaType, codecType); #ifdef _TEST_AE_ERROR_HANDLING if (iErrorHandlingAddTrack) { trackId = 0; } #endif if (trackId == 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddTrack: Error - PVA_FF_IMpeg4File::addTrack failed")); return PVMFFailure; } aPort->SetTrackId(trackId); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddTrack: PVA_FF_IMpeg4File::addTrack success. trackID=%d", trackId)); #if PROFILING_ON for (uint32 k = 0; k < 3; k++) { if (iStats[k].iTrackId == 0) { iStats[k].iTrackId = trackId; break; } } #endif switch (mediaType) { case MEDIA_TYPE_AUDIO: { iMpeg4File->setTargetBitrate(trackId, config->iBitrate); iMpeg4File->setTimeScale(trackId, config->iTimescale); PVMP4FFComposerAudioEncodeParams audioParams; audioParams.numberOfChannels = config->iNumberOfChannels; audioParams.samplingRate = config->iSamplingRate; audioParams.bitsPerSample = config->iBitsPerSample; iMpeg4File->setAudioEncodeParams(trackId, audioParams); break; } case MEDIA_TYPE_VISUAL: switch (codecType) { case CODEC_TYPE_BASELINE_H263_VIDEO: iMpeg4File->setH263ProfileLevel(trackId, config->iH263Profile, config->iH263Level); // Don't break here. Continue to set other video properties case CODEC_TYPE_AVC_VIDEO: case CODEC_TYPE_MPEG4_VIDEO: iMpeg4File->setTargetBitrate(trackId, config->iBitrate, config->iBitrate, 0); iMpeg4File->setTimeScale(trackId, config->iTimescale); iMpeg4File->setVideoParams(trackId, (float)config->iFrameRate, (uint16)config->iIFrameInterval, config->iWidth, config->iHeight); break; } break; case MEDIA_TYPE_TEXT: iMpeg4File->setTargetBitrate(trackId, config->iBitrate); iMpeg4File->setTimeScale(trackId, config->iTimescale); break; } return PVMFSuccess; } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoStop(PVMp4FFCNCmd& aCmd) { PVMFStatus status = PVMFSuccess; #if PROFILING_ON if (!oDiagnosticsLogged) { LogDiagnostics(); } #endif #ifdef _TEST_AE_ERROR_HANDLING if (FAIL_NODE_CMD_STOP == iErrorNodeCmd) { iInterfaceState = EPVMFNodeError; } #endif switch (iInterfaceState) { case EPVMFNodeStarted: case EPVMFNodePaused: { #ifdef ANDROID iFragmentWriter->flush(); #endif if (!iNodeEndOfDataReached) { WriteDecoderSpecificInfo(); if (iSampleInTrack) { status = RenderToFile(); } iSampleInTrack = false; } iNodeEndOfDataReached = false; for (uint32 ii = 0; ii < iInPorts.size(); ii++) { iInPorts[ii]->iEndOfDataReached = false; } } SetState(EPVMFNodePrepared); break; case EPVMFNodePrepared: status = PVMFSuccess; break; default: status = PVMFErrInvalidState; break; } CommandComplete(iCmdQueue, aCmd, status); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::WriteDecoderSpecificInfo() { uint32 i; uint32 offset = 0; iConfigSize = 0; int32 trackId; if (iformat_h264 == PVMF_MIME_H264_VIDEO_MP4) { trackId = iTrackId_H264; for (i = 0; i < memvector_sps.size(); i++) { iConfigSize += 2;//2 bytes for SPS_len iConfigSize += memvector_sps[i]->len; } for (i = 0; i < memvector_pps.size(); i++) { iConfigSize += 2;//2 bytes for PPS_len iConfigSize += memvector_pps[i]->len; } iConfigSize = iConfigSize + 2;//extra two bytes for nunSPS and NumPPS pConfig = (uint8*)(OSCL_MALLOC(sizeof(uint8) * iConfigSize)); //currently we are ignoring NAL Length information oscl_memcpy((void*)(pConfig + offset), (const void*)&iNum_SPS_Set, 1);//Writing Number of SPS sets offset += 1; for (i = 0; i < memvector_sps.size(); i++) { oscl_memcpy((void*)(pConfig + offset), (const void*)&memvector_sps[i]->len, 2);//Writing length of SPS offset += 2; oscl_memcpy((void*)(pConfig + offset), memvector_sps[i]->ptr, memvector_sps[i]->len); offset = offset + memvector_sps[i]->len; } oscl_memcpy((void*)(pConfig + offset), (const void*)&iNum_PPS_Set, 1);//Writing Number of PPS sets offset += 1; for (i = 0; i < memvector_pps.size(); i++) { oscl_memcpy((void*)(pConfig + offset), (const void*)&memvector_pps[i]->len, 2);//Writing length of PPS offset += 2;//2 bytes for PPS Length oscl_memcpy((void*)(pConfig + offset), memvector_pps[i]->ptr, memvector_pps[i]->len); offset = offset + memvector_pps[i]->len; } iMpeg4File->setDecoderSpecificInfo(pConfig, iConfigSize, trackId); } if (iformat_text == PVMF_MIME_3GPP_TIMEDTEXT) { for (uint32 ii = 0; ii < textdecodervector.size(); ii++) { trackId = iTrackId_Text; iMpeg4File->setTextDecoderSpecificInfo(textdecodervector[ii], trackId); } } } ////////////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::RenderToFile() { PVMFStatus status = PVMFSuccess; // Clear queued messages in ports uint32 i; for (i = 0; i < iInPorts.size(); i++) iInPorts[i]->ClearMsgQueues(); #ifdef _TEST_AE_ERROR_HANDLING //to fail renderToFile if (iErrorRenderToFile) { if (iMpeg4File) { PVA_FF_IMpeg4File::DestroyMP4FileObject(iMpeg4File); iMpeg4File = NULL; } } #endif #ifdef ANDROID iFragmentWriter->flush(); #endif if (!iMpeg4File || !iMpeg4File->renderToFile(iFileName)) { LOG_ERR((0, "PVMp4FFComposerNode::RenderToFile: Error - renderToFile failed")); ReportErrorEvent(PVMF_MP4FFCN_ERROR_FINALIZE_OUTPUT_FILE_FAILED); status = PVMFFailure; } else { #if PROFILING_ON // Statistics for (i = 0; i < 3; i++) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode Stats: TrackId=%d, NumFrame=%d, Duration=%d", iStats[i].iTrackId, iStats[i].iNumFrames, iStats[i].iDuration)); oscl_memset(&(iStats[i]), 0, sizeof(PVMp4FFCNStats)); } #endif LOGDATATRAFFIC((0, "PVMp4FFComposerNode::RenderToFile() Done")); // Delete file format library if (iMpeg4File) { PVA_FF_IMpeg4File::DestroyMP4FileObject(iMpeg4File); iMpeg4File = NULL; } // Change state SetState(EPVMFNodePrepared); status = PVMFSuccess; } if (PVMFSuccess == status) { iFileRendered = true; } return status; } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoFlush(PVMp4FFCNCmd& aCmd) { LOG_STACK_TRACE((0, "PVMp4FFComposerNode::DoFlush() iInterfaceState:%d", iInterfaceState)); #ifdef _TEST_AE_ERROR_HANDLING if (FAIL_NODE_CMD_FLUSH == iErrorNodeCmd) { iInterfaceState = EPVMFNodeError; } #endif switch (iInterfaceState) { case EPVMFNodeStarted: case EPVMFNodePaused: int32 err; uint32 i; bool msgPending; msgPending = false; for (i = 0; i < iInPorts.size(); i++) { if (iInPorts[i]->IncomingMsgQueueSize() > 0) msgPending = true; iInPorts[i]->SuspendInput(); if (iInterfaceState != EPVMFNodeStarted) { // Port is in idle if node state is not started. Call ProcessIncomingMsgReady // to wake up port AO ((PVMp4FFComposerPort*)iInPorts[i])->ProcessIncomingMsgReady(); } } // Move the command from the input command queue to the current command, where // it will remain until the flush completes. err = StoreCurrentCommand(iCurrentCmd, aCmd, iCmdQueue); if (0 != err) return; iCmdQueue.Erase(&aCmd); if (!msgPending) { FlushComplete(); return; } break; default: CommandComplete(iCmdQueue, aCmd, PVMFFailure); break; } } //////////////////////////////////////////////////////////////////////////// bool PVMp4FFComposerNode::IsFlushPending() { return (iCurrentCmd.size() > 0 && iCurrentCmd.front().iCmd == PVMF_GENERIC_NODE_FLUSH); } //////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::FlushComplete() { LOG_STACK_TRACE((0, "PVMp4FFComposerNode::FlushComplete")); uint32 i = 0; PVMFStatus status = PVMFSuccess; // Flush is complete only when all queues of all ports are clear. // Other wise, just return from this method and wait for FlushComplete // from the remaining ports. for (i = 0; i < iInPorts.size(); i++) { if (iInPorts[i]->IncomingMsgQueueSize() > 0 || iInPorts[i]->OutgoingMsgQueueSize() > 0) { return; } } #ifdef ANDROID iFragmentWriter->flush(); #endif if (!iNodeEndOfDataReached) { WriteDecoderSpecificInfo(); // Finalize output file if (iSampleInTrack) { status = RenderToFile(); } iSampleInTrack = false; if (status != PVMFSuccess) LOG_ERR((0, "PVMp4FFComposerNode::FlushComplete: Error - RenderToFile failed")); } // Resume port input so the ports can be re-started. for (i = 0; i < iInPorts.size(); i++) iInPorts[i]->ResumeInput(); SetState(EPVMFNodePrepared); if (!iCurrentCmd.empty()) { CommandComplete(iCurrentCmd, iCurrentCmd[0], status); } if (!iCmdQueue.empty()) { // If command queue is not empty, schedule to process the next command RunIfNotReady(); } } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoPause(PVMp4FFCNCmd& aCmd) { PVMFStatus status = PVMFSuccess; #ifdef _TEST_AE_ERROR_HANDLING if (FAIL_NODE_CMD_PAUSE == iErrorNodeCmd) { iInterfaceState = EPVMFNodeError; } #endif switch (iInterfaceState) { case EPVMFNodeStarted: SetState(EPVMFNodePaused); break; case EPVMFNodePaused: break; default: status = PVMFErrInvalidState; break; } CommandComplete(iCmdQueue, aCmd, status); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoReset(PVMp4FFCNCmd& aCmd) { PVMFStatus status = PVMFSuccess; #if PROFILING_ON if (!oDiagnosticsLogged) { LogDiagnostics(); } #endif if (IsAdded()) { if (iSampleInTrack) { WriteDecoderSpecificInfo(); status = RenderToFile(); iSampleInTrack = false; } //delete all ports and notify observer. while (!iInPorts.empty()) iInPorts.Erase(&iInPorts.front()); //restore original port vector reserve. iInPorts.Reconstruct(); iNodeEndOfDataReached = false; //logoff & go back to Created state. SetState(EPVMFNodeIdle); status = PVMFSuccess; } else { OSCL_LEAVE(OsclErrInvalidState); } CommandComplete(iCmdQueue, aCmd, status); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoCancelAllCommands(PVMp4FFCNCmd& aCmd) { //first cancel the current command if any while (!iCurrentCmd.empty()) CommandComplete(iCurrentCmd, iCurrentCmd[0], PVMFErrCancelled); //next cancel all queued commands //start at element 1 since this cancel command is element 0. while (iCmdQueue.size() > 1) CommandComplete(iCmdQueue, iCmdQueue[1], PVMFErrCancelled); //finally, report cancel complete. CommandComplete(iCmdQueue, aCmd, PVMFSuccess); } ////////////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::DoCancelCommand(PVMp4FFCNCmd& aCmd) { //extract the command ID from the parameters. PVMFCommandId id; aCmd.Parse(id); //first check "current" command if any PVMp4FFCNCmd* cmd = iCurrentCmd.FindById(id); if (cmd) { //cancel the queued command CommandComplete(iCurrentCmd, *cmd, PVMFErrCancelled); //report cancel success CommandComplete(iCmdQueue, aCmd, PVMFSuccess); return; } //next check input queue. //start at element 1 since this cancel command is element 0. cmd = iCmdQueue.FindById(id, 1); if (cmd) { //cancel the queued command CommandComplete(iCmdQueue, *cmd, PVMFErrCancelled); //report cancel success CommandComplete(iCmdQueue, aCmd, PVMFSuccess); return; } //if we get here the command isn't queued so the cancel fails. CommandComplete(iCmdQueue, aCmd, PVMFFailure); } ////////////////////////////////////////////////////////////////////////////////// // Port activity processing routines ////////////////////////////////////////////////////////////////////////////////// bool PVMp4FFComposerNode::IsProcessIncomingMsgReady() { if (iInterfaceState == EPVMFNodeStarted || IsFlushPending()) return true; else return false; } //////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::ProcessIncomingMsg(PVMFPortInterface* aPort) { LOG_STACK_TRACE((0, "PVMp4FFComposerNode::ProcessIncomingMsg: aPort=0x%x", aPort)); PVMFStatus status = PVMFSuccess; switch (aPort->GetPortTag()) { case PVMF_MP4FFCN_PORT_TYPE_SINK: { PVMp4FFComposerPort* port = OSCL_REINTERPRET_CAST(PVMp4FFComposerPort*, aPort); if (!IsProcessIncomingMsgReady()) { LOG_ERR((0, "PVMp4FFComposerNode::ProcessIncomingMsg: Error - Not ready.")); return PVMFErrBusy; } PVMFSharedMediaMsgPtr msg; status = port->DequeueIncomingMsg(msg); if (status != PVMFSuccess) { LOG_ERR((0, "PVMp4FFComposerNode::ProcessIncomingMsg: Error - DequeueIncomingMsg failed")); return status; } if (msg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID) { LOGDATATRAFFIC((0, "PVMp4FFComposerNode::ProcessIncomingMsg: EOS Recvd - TrackID=%d, StreamID=%d, TS=%d, Mime=%s", port->GetTrackId(), msg->getStreamID(), msg->getTimestamp(), port->GetMimeType().get_cstr())); port->iEndOfDataReached = true; //check if EOS has been received on all connected ports. uint32 ii = 0; iNodeEndOfDataReached = true; for (ii = 0; ii < iInPorts.size(); ii++) { if (!iInPorts[ii]->iEndOfDataReached) { iNodeEndOfDataReached = false; } } if (iNodeEndOfDataReached) { //Close the file since EOS is received on every connected port WriteDecoderSpecificInfo(); if (iSampleInTrack) { status = RenderToFile(); iSampleInTrack = false; } //report EOS info to engine ReportInfoEvent(PVMF_COMPOSER_EOS_REACHED); } //since we do not have data to process, we can safely break here. break; } PVMFSharedMediaDataPtr mediaDataPtr; convertToPVMFMediaData(mediaDataPtr, msg); int32 trackId = port->GetTrackId(); if ((mediaDataPtr->getSeqNum() == 0) && (port->GetFormat() == PVMF_MIME_M4V)) { // Set VOL Header OsclRefCounterMemFrag volHeader; if (mediaDataPtr->getFormatSpecificInfo(volHeader) == false || volHeader.getMemFragSize() == 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::ProcessIncomingMsg: Error - VOL Header not available")); return PVMFFailure; } iMpeg4File->setDecoderSpecificInfo((uint8*)volHeader.getMemFragPtr(), (int32)volHeader.getMemFragSize(), trackId); } if ((mediaDataPtr->getSeqNum() == 0) && (port->GetFormat() == PVMF_MIME_H264_VIDEO_MP4)) { iTrackId_H264 = port->GetTrackId(); iformat_h264 = port->GetFormat(); } if (port->GetFormat() == PVMF_MIME_3GPP_TIMEDTEXT) { iTrackId_Text = port->GetTrackId(); iformat_text = port->GetFormat(); GetTextSDIndex(mediaDataPtr->getSeqNum(), iText_sdIndex); } if (((port->GetFormat() == PVMF_MIME_AMR_IETF) || (port->GetFormat() == PVMF_MIME_AMRWB_IETF)) && mediaDataPtr->getErrorsFlag()) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_NOTICE, (0, "PVMp4FFComposerNode::ProcessIncomingMsg: Error flag set for AMR!")); return PVMFSuccess; } if ((mediaDataPtr->getSeqNum() == 0) && (port->GetFormat() == PVMF_MIME_MPEG4_AUDIO)) { // Set AAC Config OsclRefCounterMemFrag decSpecInfo; if (mediaDataPtr->getFormatSpecificInfo(decSpecInfo) == false || decSpecInfo.getMemFragSize() == 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::ProcessIncomingMsg: Error - Decoder Specific not available")); return PVMFFailure; } iMpeg4File->setDecoderSpecificInfo((uint8*)decSpecInfo.getMemFragPtr(), (int32)decSpecInfo.getMemFragSize(), trackId); } // Retrieve data from incoming queue OsclRefCounterMemFrag memFrag; uint32 numFrags = mediaDataPtr->getNumFragments(); uint32 timestamp = mediaDataPtr->getTimestamp(); iSyncSample = 0; if (mediaDataPtr->getMarkerInfo()&PVMF_MEDIA_DATA_MARKER_INFO_RANDOM_ACCESS_POINT_BIT) { iSyncSample = 1; } Oscl_Vector pFrame; //vector to store the nals in the particular case of AVC for (uint32 i = 0; (i < numFrags) && status == PVMFSuccess; i++) { if (!mediaDataPtr->getMediaFragment(i, memFrag)) { status = PVMFFailure; } else { OsclMemoryFragment memfragment; memfragment.len = memFrag.getMemFragSize(); memfragment.ptr = memFrag.getMemFragPtr(); pFrame.push_back(memfragment); } } #ifdef ANDROID if (!iMaxReachedEvent) { // TODO: We are passing port and port->GetFormat(), should pass port only. status = iFragmentWriter->enqueueMemFragToTrack( pFrame, memFrag, port->GetFormat(), timestamp, trackId, (PVMp4FFComposerPort*)aPort); } else if (!iMaxReachedReported) { iMaxReachedReported = true; ReportInfoEvent(static_cast(iMaxReachedEvent), NULL); status = PVMFSuccess; } #else status = AddMemFragToTrack(pFrame, memFrag, port->GetFormat(), timestamp, trackId, (PVMp4FFComposerPort*)aPort); #endif if (status == PVMFFailure) ReportErrorEvent(PVMF_MP4FFCN_ERROR_ADD_SAMPLE_TO_TRACK_FAILED, (OsclAny*)aPort); } break; default: LOG_ERR((0, "PVMp4FFComposerNode::ProcessIncomingMsg: Error - Invalid port tag")); ReportErrorEvent(PVMF_MP4FFCN_ERROR_ADD_SAMPLE_TO_TRACK_FAILED, (OsclAny*)aPort); status = PVMFFailure; break; } return status; } ////////////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::AddMemFragToTrack(Oscl_Vector aFrame, OsclRefCounterMemFrag& aMemFrag, PVMFFormatType aFormat, uint32& aTimestamp, int32 aTrackId, PVMp4FFComposerPort *aPort) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::AddMemFragToTrack: aFormat=%s, aTimestamp=%d, aTrackId=%d", aFormat.getMIMEStrPtr(), aTimestamp, aTrackId)); if (iRealTimeTS) { if (iInitTSOffset && (aMemFrag.getMemFragSize() > 0)) { iTSOffset = aTimestamp; iInitTSOffset = false; } aTimestamp = aTimestamp - iTSOffset; } uint32 timeScale = 0; PVMP4FFCNFormatSpecificConfig* config = aPort->GetFormatSpecificConfig(); if (config) { timeScale = config->iTimescale; } uint32 i = 0; #if PROFILING_ON PVMp4FFCNStats* stats = NULL; for (i = 0; i < 3; i++) { if (aTrackId == iStats[i].iTrackId) { stats = &(iStats[i]); break; } } #endif PVMFStatus status = PVMFSuccess; uint8 flags = 0; uint32 size = 0; uint8* data = NULL; for (i = 0; i < aFrame.size(); i++) { size = aFrame[i].len; data = OSCL_REINTERPRET_CAST(uint8*, aFrame[i].ptr); if (!data || size == 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - Invalid data or data size")); return PVMFFailure; } } if (aFormat == PVMF_MIME_3GPP_TIMEDTEXT || aFormat == PVMF_MIME_H264_VIDEO_MP4 || aFormat == PVMF_MIME_M4V || aFormat == PVMF_MIME_H2631998 || aFormat == PVMF_MIME_H2632000) { status = CheckMaxDuration(aTimestamp); if (status == PVMFFailure) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - CheckMaxDuration failed")); return status; } else if (status == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Maxmimum duration reached")); return status; } for (i = 0; i < aFrame.size(); i++) { size = aFrame[i].len; status = CheckMaxFileSize(size); if (status == PVMFFailure) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - CheckMaxFileSize failed")); return status; } else if (status == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Maxmimum file size reached")); return status; } //No data for some reason. if (size == 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_NOTICE, (0, "PVMp4FFComposerNode::AddMemFragToTrack: no data in frag!")); return PVMFSuccess; } } uint8 codingType = CODING_TYPE_P; if (iRealTimeTS) { if (aTimestamp <= aPort->GetLastTS()) { aTimestamp = aPort->GetLastTS() + 1; } aPort->SetLastTS(aTimestamp); } //iSyncSample is obtained from the marker info //to identify the I Frame if (iSyncSample) { codingType = CODING_TYPE_I; } // Format: mtb (1) | layer_id (3) | coding_type (2) | ref_select_code (2) // flags |= ((stream->iHintTrack.MTB & 0x01) << 7); // flags |= ((stream->iHintTrack.LayerID & 0x07) << 4); flags |= ((codingType & 0x03) << 2); // flags |= (stream->iHintTrack.RefSelCode & 0x03); PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Calling addSampleToTrack(%d, 0x%x, %d, %d, %d)", aTrackId, data, size, aTimestamp, flags)); LOGDATATRAFFIC((0, "PVMp4FFComposerNode::AddMemFragToTrack: TrackID=%d, Size=%d, TS=%d, Flags=%d, Mime=%s", aTrackId, size, aTimestamp, flags, aPort->GetMimeType().get_cstr())); if (aFormat == PVMF_MIME_3GPP_TIMEDTEXT) { PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Calling addtextSampleToTrack(%d, 0x%x, %d, %d, %d)", aTrackId, data, size, aTimestamp, flags)); int32 index = iText_sdIndex; if (index >= 0) { #if PROFILING_ON uint32 start = OsclTickCount::TickCount(); #endif if (!iMpeg4File->addTextSampleToTrack(aTrackId, aFrame, aTimestamp, flags, index, NULL)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addTextSampleToTrack for Timed Text failed")); return PVMFFailure; } iSampleInTrack = true; #if PROFILING_ON uint32 stop = OsclTickCount::TickCount(); uint32 comptime = OsclTickCount::TicksToMsec(stop - start); uint32 dataSize = 0; for (uint32 ii = 0; ii < aFrame.size(); ii++) { dataSize += aFrame[ii].len; } GenerateDiagnostics(comptime, dataSize); #endif } } else { #if PROFILING_ON uint32 start = OsclTickCount::TickCount(); #endif #ifdef _TEST_AE_ERROR_HANDLING if (1 == iErrorAddSample) { if (iTestFileSize <= iFileSize) //iTestFileSize set in sendProgressReport() { if (!iMpeg4File->addSampleToTrack(aTrackId, aFrame, aTimestamp, flags)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addSampleToTrack failed")); return PVMFFailure; } } } else if (2 == iErrorAddSample) { if (aTimestamp <= iFileDuration) { if (!iMpeg4File->addSampleToTrack(aTrackId, aFrame, aTimestamp, flags)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addSampleToTrack failed")); return PVMFFailure; } } } else { if (!iMpeg4File->addSampleToTrack(aTrackId, aFrame, aTimestamp, flags)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addSampleToTrack failed")); return PVMFFailure; } } #else if (!iMpeg4File->addSampleToTrack(aTrackId, aFrame, aTimestamp, flags)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addSampleToTrack failed")); return PVMFFailure; } #endif iSampleInTrack = true; #ifdef _TEST_AE_ERROR_HANDLING if (iErrorHandlingAddMemFrag == true) { return PVMFFailure; //Just to trigger error handling } #endif #if PROFILING_ON uint32 stop = OsclTickCount::TickCount(); uint32 comptime = OsclTickCount::TicksToMsec(stop - start); uint32 dataSize = 0; for (uint32 ii = 0; ii < aFrame.size(); ii++) { dataSize += aFrame[ii].len; } GenerateDiagnostics(comptime, dataSize); #endif } // Send progress report after sample is successfully added SendProgressReport(aTimestamp); #if PROFILING_ON ++(stats->iNumFrames); stats->iDuration = aTimestamp; #endif } else if ((aFormat == PVMF_MIME_AMR_IETF) || (aFormat == PVMF_MIME_AMRWB_IETF)) { if (iRealTimeTS) { if (((int32) aTimestamp - (int32) aPort->GetLastTS()) < 20) { aTimestamp = aPort->GetLastTS() + 20; } aPort->SetLastTS(aTimestamp); } uint32 bytesProcessed = 0; uint32 frameSize = 0; Oscl_Vector amrfrags; for (i = 0; i < aFrame.size(); i++) { bytesProcessed = 0; size = aFrame[i].len; data = OSCL_REINTERPRET_CAST(uint8*, aFrame[i].ptr); // Parse audio data and add one 20ms frame to track at a time while (bytesProcessed < size) { // Check for max duration status = CheckMaxDuration(aTimestamp); if (status == PVMFFailure) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - CheckMaxDuration failed")); return status; } else if (status == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Maxmimum duration reached")); return status; } // Update clock converter iClockConverter.set_timescale(timeScale); iClockConverter.set_clock_other_timescale(aTimestamp, 1000); // Check max file size int32 frSize = GetIETFFrameSize(data[0], aPort->GetCodecType()); if (frSize == -1) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - Frame Type Not Supported - Skipping")); LOGDATATRAFFIC((0, "PVMp4FFComposerNode::AddMemFragToTrack - Invalid Frame: TrackID=%d, Byte=0x%x, Mime=%s", aTrackId, data[0], aPort->GetMimeType().get_cstr())); return PVMFFailure; } frameSize = (uint32)frSize; status = CheckMaxFileSize(frameSize); if (status == PVMFFailure) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - CheckMaxFileSize failed")); return status; } else if (status == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Maxmimum file size reached")); return status; } PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Calling addSampleToTrack(%d, 0x%x, %d, %d, %d)", aTrackId, data, frameSize, iClockConverter.get_current_timestamp(), flags)); OsclMemoryFragment amr_memfrag; amr_memfrag.len = frameSize; amr_memfrag.ptr = data; amrfrags.push_back(amr_memfrag); #if PROFILING_ON uint32 start = OsclTickCount::TickCount(); #endif uint32 amrts = iClockConverter.get_current_timestamp(); LOGDATATRAFFIC((0, "PVMp4FFComposerNode::AddMemFragToTrack: TrackID=%d, Size=%d, TS=%d, Flags=%d, Mime=%s", aTrackId, frameSize, amrts, flags, aPort->GetMimeType().get_cstr())); if (!iMpeg4File->addSampleToTrack(aTrackId, amrfrags, amrts, flags)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addSampleToTrack failed")); return PVMFFailure; } iSampleInTrack = true; #if PROFILING_ON uint32 stop = OsclTickCount::TickCount(); uint32 comptime = OsclTickCount::TicksToMsec(stop - start); uint32 dataSize = 0; for (uint32 ii = 0; ii < amrfrags.size(); ii++) { dataSize += amrfrags[ii].len; } GenerateDiagnostics(comptime, dataSize); #endif // Send progress report after sample is successfully added SendProgressReport(aTimestamp); #if PROFILING_ON ++(stats->iNumFrames); stats->iDuration = aTimestamp; #endif data += frameSize; bytesProcessed += frameSize; aTimestamp += 20; amrfrags.clear(); } } if (iRealTimeTS) { aPort->SetLastTS(aTimestamp - 20); } } else if (aFormat == PVMF_MIME_MPEG4_AUDIO) { status = CheckMaxDuration(aTimestamp); if (status == PVMFFailure) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - CheckMaxDuration failed")); return status; } else if (status == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Maxmimum duration reached")); return status; } for (i = 0; i < aFrame.size(); i++) { size = aFrame[i].len; status = CheckMaxFileSize(size); if (status == PVMFFailure) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - CheckMaxFileSize failed")); return status; } else if (status == PVMFSuccess) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Maxmimum file size reached")); return status; } //No data for some reason. if (size == 0) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_NOTICE, (0, "PVMp4FFComposerNode::AddMemFragToTrack: no data in frag!")); return PVMFSuccess; } } if (iRealTimeTS) { if (aTimestamp <= aPort->GetLastTS()) { aTimestamp = aPort->GetLastTS() + 1; } aPort->SetLastTS(aTimestamp); } iClockConverter.set_timescale(timeScale); iClockConverter.set_clock_other_timescale(aTimestamp, 1000); uint32 aacTS = iClockConverter.get_current_timestamp(); if (!iMpeg4File->addSampleToTrack(aTrackId, aFrame, aacTS, flags)) { PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, (0, "PVMp4FFComposerNode::AddMemFragToTrack: Error - addSampleToTrack failed")); return PVMFFailure; } iSampleInTrack = true; // Send progress report after sample is successfully added SendProgressReport(aTimestamp); #if PROFILING_ON ++(stats->iNumFrames); stats->iDuration = aTimestamp; #endif } return PVMFSuccess; } void PVMp4FFComposerNode::GenerateDiagnostics(uint32 aTime, uint32 aSize) { #if PROFILING_ON if ((iMinSampleAddTime > aTime) || (0 == iMinSampleAddTime)) { iMinSampleAddTime = aTime; } if (iMaxSampleAddTime < aTime) { iMaxSampleAddTime = aTime; } if ((iMinSampleSize > aSize) || (0 == iMinSampleSize)) { iMinSampleSize = aSize; } if (iMaxSampleSize < aSize) { iMaxSampleSize = aSize; } iNumSamplesAdded++; #endif OSCL_UNUSED_ARG(aTime); OSCL_UNUSED_ARG(aSize); } ////////////////////////////////////////////////////////////////////////////////// int32 PVMp4FFComposerNode::GetIETFFrameSize(uint8 aFrameType, int32 aCodecType) { uint8 frameType = (uint8)(aFrameType >> 3) & 0x0f; if (aCodecType == CODEC_TYPE_AMR_AUDIO) { // Find frame size for each frame type switch (frameType) { case 0: // AMR 4.75 Kbps return 13; case 1: // AMR 5.15 Kbps return 14; case 2: // AMR 5.90 Kbps return 16; case 3: // AMR 6.70 Kbps return 18; case 4: // AMR 7.40 Kbps return 20; case 5: // AMR 7.95 Kbps return 21; case 6: // AMR 10.2 Kbps return 27; case 7: // AMR 12.2 Kbps return 32; case 8: // AMR Frame SID return 6; case 9: // AMR Frame GSM EFR SID return 7; case 10:// AMR Frame TDMA EFR SID case 11:// AMR Frame PDC EFR SID return 6; case 15: // AMR Frame No Data return 1; default: // Error - For Future Use return -1; } } else if (aCodecType == CODEC_TYPE_AMR_WB_AUDIO) { // Find frame size for each frame type switch (frameType) { case 0: // AMR-WB 6.60 Kbps return 18; case 1: // AMR-WB 8.85 Kbps return 24; case 2: // AMR-WB 12.65 Kbps return 33; case 3: // AMR-WB 14.25 Kbps return 37; case 4: // AMR-WB 15.85 Kbps return 41; case 5: // AMR-WB 18.25 Kbps return 47; case 6: // AMR-WB 19.85 Kbps return 51; case 7: // AMR-WB 23.05 Kbps return 59; case 8: // AMR-WB 23.85 Kbps return 61; case 9: // AMR-WB SID Frame return 6; case 10: //Reserved case 11: //Reserved case 12: //Reserved case 13: //Reserved return -1; case 14: // AMR-WB Frame Lost case 15: // AMR-WB Frame No Data return 1; default: // Error - For Future Use return -1; } } return -1; } ////////////////////////////////////////////////////////////////////////////////// // Progress and max size / duration routines ////////////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::SendProgressReport(uint32 aTimestamp) { if (iDurationReportEnabled && aTimestamp >= iNextDurationReport) { iNextDurationReport = aTimestamp - (aTimestamp % iDurationReportFreq) + iDurationReportFreq; ReportInfoEvent(PVMF_COMPOSER_DURATION_PROGRESS, (OsclAny*)aTimestamp); } else if (iFileSizeReportEnabled) { uint32 metaDataSize = 0; uint32 mediaDataSize = 0; uint32 fileSize = 0; iMpeg4File->getTargetFileSize(metaDataSize, mediaDataSize); fileSize = metaDataSize + mediaDataSize; if (fileSize >= iNextFileSizeReport) { iNextFileSizeReport = fileSize - (fileSize % iFileSizeReportFreq) + iFileSizeReportFreq; ReportInfoEvent(PVMF_COMPOSER_FILESIZE_PROGRESS, (OsclAny*)fileSize); } #ifdef _TEST_AE_ERROR_HANDLING iTestFileSize = fileSize; //iTestTimeStamp to fail the addSampleTrack() once a particulare time duration is reached as specified in testapp. #endif } return PVMFSuccess; } ////////////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::CheckMaxFileSize(uint32 aFrameSize) { if (iMaxFileSizeEnabled) { uint32 metaDataSize = 0; uint32 mediaDataSize = 0; iMpeg4File->getTargetFileSize(metaDataSize, mediaDataSize); if ((metaDataSize + mediaDataSize + aFrameSize) >= iMaxFileSize) { #ifdef ANDROID // This code is executed on the fragment writer thread, we // don't want to call RenderToFile since it will call // flush() on the writer from this very same // thread. Instead, we use a marker to report an event to // the author node next time a new fragment is processed. iMaxReachedEvent = PVMF_COMPOSER_MAXFILESIZE_REACHED; #else // Finalized output file if (iSampleInTrack) { WriteDecoderSpecificInfo(); iSampleInTrack = false; if (RenderToFile() != PVMFSuccess) return PVMFFailure; } ReportInfoEvent(PVMF_COMPOSER_MAXFILESIZE_REACHED, NULL); return PVMFSuccess; #endif } return PVMFPending; } return PVMFErrNotSupported; } ////////////////////////////////////////////////////////////////////////////////// PVMFStatus PVMp4FFComposerNode::CheckMaxDuration(uint32 aTimestamp) { //if(!iInfoObserver) // return PVMFFailure; if (iMaxDurationEnabled) { if (aTimestamp >= iMaxTimeDuration) { #ifdef ANDROID // This code is executed on the fragment writer thread, we // don't want to call RenderToFile since it will call // flush() on the writer from this very same // thread. Instead, we use a marker to report an event to // the author node next time a new fragment is processed. iMaxReachedEvent = PVMF_COMPOSER_MAXDURATION_REACHED; #else // Finalize output file if (iSampleInTrack) { WriteDecoderSpecificInfo(); iSampleInTrack = false; if (RenderToFile() != PVMFSuccess) return PVMFFailure; } ReportInfoEvent(PVMF_COMPOSER_MAXDURATION_REACHED, NULL); return PVMFSuccess; #endif } return PVMFPending; } return PVMFErrNotSupported; } //////////////////////////////////////////////////////////////////////////// // Event reporting routines. //////////////////////////////////////////////////////////////////////////// void PVMp4FFComposerNode::SetState(TPVMFNodeInterfaceState aState) { LOG_STACK_TRACE((0, "PVMp4FFComposerNode::SetState: aState=%d", aState)); PVMFNodeInterface::SetState(aState); } void PVMp4FFComposerNode::ReportErrorEvent(PvmfMp4FFCNError aErrorEvent, OsclAny* aEventData) { LOG_ERR((0, "PVMp4FFComposerNode:ReportErrorEvent: aEventType=%d, aEventData=0x%x", aErrorEvent, aEventData)); switch (aErrorEvent) { case PVMF_MP4FFCN_ERROR_FINALIZE_OUTPUT_FILE_FAILED: case PVMF_MP4FFCN_ERROR_ADD_SAMPLE_TO_TRACK_FAILED: PVMFNodeInterface::ReportErrorEvent(PVMFErrResourceConfiguration, aEventData); break; default: PVMFNodeInterface::ReportErrorEvent(PVMFFailure, aEventData); break; } } void PVMp4FFComposerNode::ReportInfoEvent(PVMFEventType aEventType, OsclAny* aEventData) { LOG_STACK_TRACE((0, "PVMp4FFComposerNode:ReportInfoEvent: aEventType=%d, aEventData=0x%x", aEventType, aEventData)); PVMFNodeInterface::ReportInfoEvent(aEventType, aEventData); } void PVMp4FFComposerNode::LogDiagnostics() { #if PROFILING_ON oDiagnosticsLogged = true; PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iDiagnosticsLogger, PVLOGMSG_DEBUG, (0, "PVMp4FFComposerNode Stats:Sample Add time (Min:%d, Max:%d), Sample Size(Min:%d, Max:%d), number of samples added:%d\n", iMinSampleAddTime, iMaxSampleAddTime, iMinSampleSize, iMaxSampleSize, iNumSamplesAdded)); #endif } int32 PVMp4FFComposerNode::StoreCurrentCommand(PVMp4FFCNCmdQueue& aCurrentCmd, PVMp4FFCNCmd& aCmd, PVMp4FFCNCmdQueue& aCmdQueue) { int32 err = 0; OSCL_TRY(err, aCurrentCmd.StoreL(aCmd);); OSCL_FIRST_CATCH_ANY(err, CommandComplete(aCmdQueue, aCmd, PVMFErrNoMemory); return err; ); return err; } void PVMp4FFComposerNode::GetTextSDIndex(uint32 aSampleNum, int32& aIndex) { //default index is zero aIndex = 0; Oscl_Vector::iterator it; for (it = textdecodervector.begin(); it != textdecodervector.end(); it++) { if ((aSampleNum >= (*it)->start_sample_num) && (aSampleNum <= (*it)->end_sample_num)) { aIndex = (*it)->sdindex; break; } } }