/* ------------------------------------------------------------------
* 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
*/
/*
* FIXME:
* The current implementation of the file writer is NOT
* thread-safe.
*
* A separate delete queue should be used to
* pass back the fragments from the file writer thread
* to the composer node and to let the composer node
* handle the deallocation of any fragments; otherwise,
* race condition may occur. Some lost free chunk
* notification has been found due to this race
* condition.
*
* The reason why there is race condition is that the
* rest of the OpenCore assumes a single-thread model.
* In other words, everything is scheduled to
* run within a single omx thread. A separate file writer
* thread breaks this model, and deallocate fragments
* here may cause deallocate() be called within two
* separate thread, and thus the free chunk available
* flag can be corrupted.
*
* Don't remove the following #undef line unless you
* fix the above issue.
*/
#undef ANDROID
#ifdef ANDROID
// #define LOG_NDEBUG 0
#define LOG_TAG "PvMp4Composer"
#include <utils/Log.h>
#include <utils/Errors.h>
#include <utils/threads.h>
#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<OsclMemoryFragment, OsclMemAllocator> 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<OsclMemoryFragment, OsclMemAllocator> 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<Request, OsclMemAllocator> 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<PVUuid, OsclMemAllocator>& 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<PVUuid, OsclMemAllocator> *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<OsclMemAllocator> 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<OsclMemoryFragment, OsclMemAllocator> 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<PVMFComposerSizeAndDurationEvent>(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<OsclMemoryFragment, OsclMemAllocator> 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<OsclMemoryFragment, OsclMemAllocator> 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<PVA_FF_TextSampleDescInfo*, OsclMemAllocator>::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;
}
}
}