blob: 61aa7b082fb5476b17fafe50ab78b6776582a82b [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 1998-2009 PacketVideo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
* -------------------------------------------------------------------
*/
#include "pvmf_omx_enc_node.h"
#include "pvlogger.h"
#include "oscl_error_codes.h"
#include "pvmf_omx_enc_port.h"
#include "pv_mime_string_utils.h"
#include "oscl_snprintf.h"
#include "pvmf_media_cmd.h"
#include "pvmf_media_msg_format_ids.h"
#include "pvmi_kvp_util.h"
#ifdef _DEBUG
#include <stdio.h>
#endif
#include "OMX_Core.h"
#include "pvmf_omx_enc_callbacks.h" //used for thin AO in encoder's callbacks
#include "pv_omxcore.h"
#define CONFIG_SIZE_AND_VERSION(param) \
param.nSize=sizeof(param); \
param.nVersion.s.nVersionMajor = SPECVERSIONMAJOR; \
param.nVersion.s.nVersionMinor = SPECVERSIONMINOR; \
param.nVersion.s.nRevision = SPECREVISION; \
param.nVersion.s.nStep = SPECSTEP;
#define CHECK_OMX_ERR_AND_RETURN(Err, str) \
if (Err != OMX_ErrorNone) \
{ \
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, str)); \
}
#define PVOMXENC_EXTRA_YUVBUFFER_POOLNUM 3
#define PVOMXENC_MEDIADATA_POOLNUM (PVOMXENCMAXNUMDPBFRAMESPLUS1 + PVOMXENC_EXTRA_YUVBUFFER_POOLNUM)
#define PVOMXENC_MEDIADATA_CHUNKSIZE 128
#include "utils/Log.h"
#undef LOG_TAG
#define LOG_TAG "PVOMXEncNode"
const uint32 DEFAULT_VOL_HEADER_LENGTH = 28;
const uint8 DEFAULT_VOL_HEADER[DEFAULT_VOL_HEADER_LENGTH] =
{
0x00, 0x00, 0x01, 0xB0, 0x08, 0x00, 0x00, 0x01,
0xB5, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x20, 0x00, 0x84, 0x40, 0xFA, 0x28, 0x2C,
0x20, 0x90, 0xA2, 0x1F
};
#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 PVMF_OMXENC_NUM_METADATA_VALUES 6
// Constant character strings for metadata keys
static const char PVOMXENCMETADATA_CODECINFO_VIDEO_FORMAT_KEY[] = "codec-info/video/format";
static const char PVOMXENCMETADATA_CODECINFO_VIDEO_WIDTH_KEY[] = "codec-info/video/width";
static const char PVOMXENCMETADATA_CODECINFO_VIDEO_HEIGHT_KEY[] = "codec-info/video/height";
static const char PVOMXENCMETADATA_CODECINFO_VIDEO_PROFILE_KEY[] = "codec-info/video/profile";
static const char PVOMXENCMETADATA_CODECINFO_VIDEO_LEVEL_KEY[] = "codec-info/video/level";
static const char PVOMXENCMETADATA_CODECINFO_VIDEO_AVGBITRATE_KEY[] = "codec-info/video/avgbitrate";//(bits per sec)
static const char PVOMXENCMETADATA_SEMICOLON[] = ";";
static const char LOG_ID_AUDIO_AMRNB[] = "Audio_AMRNB";
static const char LOG_ID_AUDIO_AMRWB[] = "Audio_AMRWB";
static const char LOG_ID_AUDIO_AAC[] = "Audio_AAC";
static const char LOG_ID_VIDEO_H263[] = "Video_H263";
static const char LOG_ID_VIDEO_M4V[] = "Video_M4V";
static const char LOG_ID_VIDEO_AVC[] = "Video_AVC";
static const char LOG_ID_UNKNOWN[] = "TypeNotSetYet";
// OMX CALLBACKS
// 1) AO OMX component running in the same thread as the OMX node
// In this case, the callbacks can be called directly from the component
// The callback: OMX Component->CallbackEventHandler->EventHandlerProcessing
// The callback can perform do RunIfNotReady
// 2) Multithreaded component
// In this case, the callback is made using the threadsafe callback (TSCB) AO
// Component thread : OMX Component->CallbackEventHandler->TSCB(ReceiveEvent) => event is queued
// Node thread : dequeue event => TSCB(ProcessEvent)->ProcessCallbackEventHandler->EventHandlerProcessing
// callback for Event Handler - in multithreaded case, event is queued to be processed later
// in AO case, event is processed immediately by calling EventHandlerProcessing
OMX_ERRORTYPE CallbackEventHandlerEnc(OMX_OUT OMX_HANDLETYPE aComponent,
OMX_OUT OMX_PTR aAppData,
OMX_OUT OMX_EVENTTYPE aEvent,
OMX_OUT OMX_U32 aData1,
OMX_OUT OMX_U32 aData2,
OMX_OUT OMX_PTR aEventData)
{
PVMFOMXEncNode *Node = (PVMFOMXEncNode *) aAppData;
if (Node->IsComponentMultiThreaded())
{
// allocate the memory for the callback event specific data
//EventHandlerSpecificData* ED = (EventHandlerSpecificData*) oscl_malloc(sizeof (EventHandlerSpecificData));
EventHandlerSpecificData* ED = (EventHandlerSpecificData*) Node->iThreadSafeHandlerEventHandler->iMemoryPool->allocate(sizeof(EventHandlerSpecificData));
// pack the relevant data into the structure
ED->hComponent = aComponent;
ED->pAppData = aAppData;
ED->eEvent = aEvent;
ED->nData1 = aData1;
ED->nData2 = aData2;
ED->pEventData = aEventData;
// convert the pointer into OsclAny ptr
OsclAny* P = (OsclAny*) ED;
// CALL the generic callback AO API:
Node->iThreadSafeHandlerEventHandler->ReceiveEvent(P);
return OMX_ErrorNone;
}
else
{
OMX_ERRORTYPE status;
status = Node->EventHandlerProcessing(aComponent, aAppData, aEvent, aData1, aData2, aEventData);
return status;
}
}
// callback for EmptyBufferDone - in multithreaded case, event is queued to be processed later
// in AO case, event is processed immediately by calling EmptyBufferDoneProcessing
OMX_ERRORTYPE CallbackEmptyBufferDoneEnc(OMX_OUT OMX_HANDLETYPE aComponent,
OMX_OUT OMX_PTR aAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* aBuffer)
{
PVMFOMXEncNode *Node = (PVMFOMXEncNode *) aAppData;
if (Node->IsComponentMultiThreaded())
{
// allocate the memory for the callback event specific data
//EmptyBufferDoneSpecificData* ED = (EmptyBufferDoneSpecificData*) oscl_malloc(sizeof (EmptyBufferDoneSpecificData));
EmptyBufferDoneSpecificData* ED = (EmptyBufferDoneSpecificData*) Node->iThreadSafeHandlerEmptyBufferDone->iMemoryPool->allocate(sizeof(EmptyBufferDoneSpecificData));
// pack the relevant data into the structure
ED->hComponent = aComponent;
ED->pAppData = aAppData;
ED->pBuffer = aBuffer;
// convert the pointer into OsclAny ptr
OsclAny* P = (OsclAny*) ED;
// CALL the generic callback AO API:
Node->iThreadSafeHandlerEmptyBufferDone->ReceiveEvent(P);
return OMX_ErrorNone;
}
else
{
OMX_ERRORTYPE status;
status = Node->EmptyBufferDoneProcessing(aComponent, aAppData, aBuffer);
return status;
}
}
// callback for FillBufferDone - in multithreaded case, event is queued to be processed later
// in AO case, event is processed immediately by calling FillBufferDoneProcessing
OMX_ERRORTYPE CallbackFillBufferDoneEnc(OMX_OUT OMX_HANDLETYPE aComponent,
OMX_OUT OMX_PTR aAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* aBuffer)
{
PVMFOMXEncNode *Node = (PVMFOMXEncNode *) aAppData;
if (Node->IsComponentMultiThreaded())
{
// allocate the memory for the callback event specific data
//FillBufferDoneSpecificData* ED = (FillBufferDoneSpecificData*) oscl_malloc(sizeof (FillBufferDoneSpecificData));
FillBufferDoneSpecificData* ED = (FillBufferDoneSpecificData*) Node->iThreadSafeHandlerFillBufferDone->iMemoryPool->allocate(sizeof(FillBufferDoneSpecificData));
// pack the relevant data into the structure
ED->hComponent = aComponent;
ED->pAppData = aAppData;
ED->pBuffer = aBuffer;
// convert the pointer into OsclAny ptr
OsclAny* P = (OsclAny*) ED;
// CALL the generic callback AO API:
Node->iThreadSafeHandlerFillBufferDone->ReceiveEvent(P);
return OMX_ErrorNone;
}
else
{
OMX_ERRORTYPE status;
status = Node->FillBufferDoneProcessing(aComponent, aAppData, aBuffer);
return status;
}
}
// Callback processing in multithreaded case - dequeued event - call EventHandlerProcessing
OsclReturnCode PVMFOMXEncNode::ProcessCallbackEventHandler_MultiThreaded(OsclAny* P)
{
// re-cast the pointer
EventHandlerSpecificData* ED = (EventHandlerSpecificData*) P;
OMX_HANDLETYPE aComponent = ED->hComponent;
OMX_PTR aAppData = ED->pAppData;
OMX_EVENTTYPE aEvent = ED->eEvent;
OMX_U32 aData1 = ED->nData1;
OMX_U32 aData2 = ED->nData2;
OMX_PTR aEventData = ED->pEventData;
EventHandlerProcessing(aComponent, aAppData, aEvent, aData1, aData2, aEventData);
// release the allocated memory when no longer needed
iThreadSafeHandlerEventHandler->iMemoryPool->deallocate(ED);
ED = NULL;
return OsclSuccess;
}
// Callback processing in multithreaded case - dequeued event - call EmptyBufferDoneProcessing
OsclReturnCode PVMFOMXEncNode::ProcessCallbackEmptyBufferDone_MultiThreaded(OsclAny* P)
{
// re-cast the pointer
EmptyBufferDoneSpecificData* ED = (EmptyBufferDoneSpecificData*) P;
OMX_HANDLETYPE aComponent = ED->hComponent;
OMX_PTR aAppData = ED->pAppData;
OMX_BUFFERHEADERTYPE* aBuffer = ED->pBuffer;
EmptyBufferDoneProcessing(aComponent, aAppData, aBuffer);
// release the allocated memory when no longer needed
iThreadSafeHandlerEmptyBufferDone->iMemoryPool->deallocate(ED);
ED = NULL;
return OsclSuccess;
}
// Callback processing in multithreaded case - dequeued event - call FillBufferDoneProcessing
OsclReturnCode PVMFOMXEncNode::ProcessCallbackFillBufferDone_MultiThreaded(OsclAny* P)
{
// re-cast the pointer
FillBufferDoneSpecificData* ED = (FillBufferDoneSpecificData*) P;
OMX_HANDLETYPE aComponent = ED->hComponent;
OMX_PTR aAppData = ED->pAppData;
OMX_BUFFERHEADERTYPE* aBuffer = ED->pBuffer;
FillBufferDoneProcessing(aComponent, aAppData, aBuffer);
// release the allocated memory when no longer needed
iThreadSafeHandlerFillBufferDone->iMemoryPool->deallocate(ED);
ED = NULL;
return OsclSuccess;
}
/////////////////////////////////////////////////////////////////////////////
// Class Destructor
/////////////////////////////////////////////////////////////////////////////
PVMFOMXEncNode::~PVMFOMXEncNode()
{
LogDiagnostics();
//Clearup encoder
DeleteOMXEncoder();
// Cleanup callback AOs and Mempools
if (iThreadSafeHandlerEventHandler)
{
OSCL_DELETE(iThreadSafeHandlerEventHandler);
iThreadSafeHandlerEventHandler = NULL;
}
if (iThreadSafeHandlerEmptyBufferDone)
{
OSCL_DELETE(iThreadSafeHandlerEmptyBufferDone);
iThreadSafeHandlerEmptyBufferDone = NULL;
}
if (iThreadSafeHandlerFillBufferDone)
{
OSCL_DELETE(iThreadSafeHandlerFillBufferDone);
iThreadSafeHandlerFillBufferDone = NULL;
}
if (iMediaDataMemPool)
{
iMediaDataMemPool->removeRef();
iMediaDataMemPool = NULL;
}
if (iOutBufMemoryPool)
{
iOutBufMemoryPool->removeRef();
iOutBufMemoryPool = NULL;
}
ipFixedSizeBufferAlloc = NULL;
if(ipExternalInputBufferAllocatorInterface)
{
ipExternalInputBufferAllocatorInterface->removeRef();
ipExternalInputBufferAllocatorInterface = NULL;
}
if (iInBufMemoryPool)
{
iInBufMemoryPool->removeRef();
iInBufMemoryPool = NULL;
}
if (in_ctrl_struct_ptr)
{
oscl_free(in_ctrl_struct_ptr);
in_ctrl_struct_ptr = NULL;
}
if (in_buff_hdr_ptr)
{
oscl_free(in_buff_hdr_ptr);
in_buff_hdr_ptr = NULL;
}
if (out_ctrl_struct_ptr)
{
oscl_free(out_ctrl_struct_ptr);
out_ctrl_struct_ptr = NULL;
}
if (out_buff_hdr_ptr)
{
oscl_free(out_buff_hdr_ptr);
out_buff_hdr_ptr = NULL;
}
//Thread logoff
if (IsAdded())
{
RemoveFromScheduler();
iIsAdded = false;
}
//Cleanup allocated interfaces
//Cleanup allocated ports
ReleaseAllPorts();
//Cleanup commands
//The command queues are self-deleting, but we want to
//notify the observer of unprocessed commands.
while (!iCurrentCommand.empty())
{
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFFailure);
}
while (!iInputCommands.empty())
{
CommandComplete(iInputCommands, iInputCommands.front(), PVMFFailure);
}
if (iNALSizeArray != NULL)
{
oscl_free(iNALSizeArray);
}
if (iNALPtrArray != NULL)
{
oscl_free(iNALPtrArray);
}
//Release Input buffer
iDataIn.Unbind();
}
/////////////////////////////////////////////////////////////////////////////
// Add AO to the scheduler
/////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFOMXEncNode::ThreadLogon()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFOMXEncNode:ThreadLogon"));
switch (iInterfaceState)
{
case EPVMFNodeCreated:
if (!IsAdded())
{
AddToScheduler();
iIsAdded = true;
}
SetState(EPVMFNodeIdle);
return PVMFSuccess;
// break; This break statement was removed to avoid compiler warning for Unreachable Code
default:
return PVMFErrInvalidState;
// break; This break statement was removed to avoid compiler warning for Unreachable Code
}
}
/////////////////////////////////////////////////////////////////////////////
// Remove AO from the scheduler
/////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFOMXEncNode::ThreadLogoff()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFOMXEncNode-%s::ThreadLogoff", iNodeTypeId));
switch (iInterfaceState)
{
case EPVMFNodeIdle:
if (IsAdded())
{
RemoveFromScheduler();
iIsAdded = false;
}
iLogger = NULL;
SetState(EPVMFNodeCreated);
return PVMFSuccess;
// break; This break statement was removed to avoid compiler warning for Unreachable Code
default:
return PVMFErrInvalidState;
// break; This break statement was removed to avoid compiler warning for Unreachable Code
}
}
/////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFOMXEncNode::GetCapability(PVMFNodeCapability& aNodeCapability)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::GetCapability() called", iNodeTypeId));
aNodeCapability = iCapability;
return PVMFSuccess;
}
/////////////////////////////////////////////////////////////////////////////
PVMFPortIter* PVMFOMXEncNode::GetPorts(const PVMFPortFilter* aFilter)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::GetPorts() %s called", iNodeTypeId));
OSCL_UNUSED_ARG(aFilter);
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::QueueCommandL(PVMFOMXEncNodeCommand& aCmd)
{
PVMFCommandId id;
id = iInputCommands.AddL(aCmd);
if (iInputCommands.size() == 1)
{
//wakeup the AO all the rest of input commands will reschedule the AO in Run
RunIfNotReady();
}
return id;
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::QueryUUID(PVMFSessionId s, const PvmfMimeString& aMimeType,
Oscl_Vector<PVUuid, PVMFOMXEncNodeAllocator>& aUuids,
bool aExactUuidsOnly,
const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::QueryUUID() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_QUERYUUID, aMimeType, aUuids, aExactUuidsOnly, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::QueryInterface(PVMFSessionId s, const PVUuid& aUuid,
PVInterface*& aInterfacePtr,
const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::QueryInterface() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_QUERYINTERFACE, aUuid, aInterfacePtr, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::RequestPort(PVMFSessionId s, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::RequestPort() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
// IMPORTANT NOTE - ENGINE IS SENDING THE MIME TYPE FOR THE ENCODER INPUT/OUTPUT FORMAT THRU THE PORT PARAMETER
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_REQUESTPORT, aPortTag, aPortConfig, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFOMXEncNode::ReleasePort(PVMFSessionId s, PVMFPortInterface& aPort, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::ReleasePort() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_RELEASEPORT, aPort, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Init(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Init() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_INIT, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Prepare(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Prepare() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_PREPARE, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Start(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Start() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_START, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Stop(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Stop() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_STOP, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Flush(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Flush() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_FLUSH, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Pause(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Pause() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_PAUSE, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::Reset(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Reset() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_RESET, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::CancelAllCommands(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::CancelAllCommands() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_CANCELALL, aContext);
return QueueCommandL(cmd);
}
/////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFOMXEncNode::CancelCommand(PVMFSessionId s, PVMFCommandId aCmdId, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::CancelCommand() called", iNodeTypeId));
PVMFOMXEncNodeCommand cmd;
cmd.PVMFOMXEncNodeCommandBase::Construct(s, PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_CANCELCMD, aCmdId, aContext);
return QueueCommandL(cmd);
}
/////////////////////
// Private Section //
/////////////////////
/////////////////////////////////////////////////////////////////////////////
// Class Constructor
/////////////////////////////////////////////////////////////////////////////
PVMFOMXEncNode::PVMFOMXEncNode(int32 aPriority) :
OsclActiveObject(aPriority, "PVMFOMXEncNode"),
iInPort(NULL),
iOutPort(NULL),
iOutBufMemoryPool(NULL),
iMediaDataMemPool(NULL),
iOMXComponentOutputBufferSize(0),
iOutputAllocSize(0),
iNumOutstandingOutputBuffers(0),
iNumOutstandingInputBuffers(0),
iProcessingState(EPVMFOMXEncNodeProcessingState_Idle),
iOMXEncoder(NULL),
iSendBOS(false),
iStreamID(0),
iBOSTimestamp(0),
iSeqNum(0),
iSeqNum_In(0),
iIsAdded(true),
iLogger(NULL),
iDataPathLogger(NULL),
iClockLogger(NULL),
iExtensionRefCount(0),
iEndOfDataReached(false),
iEndOfDataTimestamp(0),
iDiagnosticsLogger(NULL),
iDiagnosticsLogged(false),
iAvgBitrateValue(0),
iResetInProgress(false),
iResetMsgSent(false),
iStopInResetMsgSent(false),
ipExternalInputBufferAllocatorInterface(NULL),
ipFixedSizeBufferAlloc(NULL)
{
iInterfaceState = EPVMFNodeCreated;
// Allocate memory for VOL header
uint refCounterSize = oscl_mem_aligned_size(sizeof(OsclRefCounterDA));
uint VolHdrSize = refCounterSize + DEFAULT_VOL_HEADER_LENGTH;
uint8 *memBufferVOLHeader = NULL;
uint ParamSetSize = refCounterSize + DEFAULT_PARAMS_SET_LENGTH;
uint8 *memBufferParamSet = NULL;
int32 err;
OSCL_TRY(err,
//Create the input command queue. Use a reserve to avoid lots of
//dynamic memory allocation.
iInputCommands.Construct(PVMF_OMXENC_NODE_COMMAND_ID_START, PVMF_OMXENC_NODE_COMMAND_VECTOR_RESERVE);
//Create the "current command" queue. It will only contain one
//command at a time, so use a reserve of 1.
iCurrentCommand.Construct(0, 1);
//Set the node capability data.
//This node can support an unlimited number of ports.
iCapability.iCanSupportMultipleInputPorts = false;
iCapability.iCanSupportMultipleOutputPorts = false;
iCapability.iHasMaxNumberOfPorts = true;
iCapability.iMaxNumberOfPorts = 2;
// video output
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H264_VIDEO_MP4);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H264_VIDEO_RAW);
//iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H264_VIDEO);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_M4V);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H2631998);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_H2632000);
//iCapability.iOutputFormatCapability.push_back(PVMF_MIME_WMV);
// audio output
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_AMR_IETF);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_AMRWB_IETF);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_AMR_IF2);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_ADTS);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_ADIF);
iCapability.iOutputFormatCapability.push_back(PVMF_MIME_MPEG4_AUDIO);
// video input
iCapability.iInputFormatCapability.push_back(PVMF_MIME_YUV420);
iCapability.iInputFormatCapability.push_back(PVMF_MIME_YUV422);
iCapability.iInputFormatCapability.push_back(PVMF_MIME_YUV422_INTERLEAVED_UYVY);
iCapability.iInputFormatCapability.push_back(PVMF_MIME_YUV422_INTERLEAVED_YUYV);
iCapability.iInputFormatCapability.push_back(PVMF_MIME_RGB24);
iCapability.iInputFormatCapability.push_back(PVMF_MIME_RGB12);
// audio input
iCapability.iInputFormatCapability.push_back(PVMF_MIME_PCM16);
iAvailableMetadataKeys.reserve(PVMF_OMXENC_NUM_METADATA_VALUES);
iAvailableMetadataKeys.clear();
// for VOL header
memBufferVOLHeader = (uint8*)iAlloc.allocate(VolHdrSize);
if (!memBufferVOLHeader)
{
OSCL_LEAVE(PVMFErrNoMemory);
}
memBufferParamSet = (uint8*) iAlloc.allocate(ParamSetSize);
if (!memBufferParamSet)
{
OSCL_LEAVE(PVMFErrNoMemory);
}
);
// Save default VOL header
oscl_memset(memBufferVOLHeader, 0, DEFAULT_VOL_HEADER_LENGTH);
OsclMemoryFragment volHeader;
OsclRefCounter* refCounterVOLHeader = new(memBufferVOLHeader) OsclRefCounterDA(memBufferVOLHeader,
(OsclDestructDealloc*)&iAlloc);
memBufferVOLHeader += refCounterSize;
volHeader.ptr = memBufferVOLHeader;
oscl_memcpy(volHeader.ptr, (OsclAny*)DEFAULT_VOL_HEADER, DEFAULT_VOL_HEADER_LENGTH);
volHeader.len = DEFAULT_VOL_HEADER_LENGTH;
iVolHeader = OsclRefCounterMemFrag(volHeader, refCounterVOLHeader, DEFAULT_VOL_HEADER_LENGTH);
// construct SPS&PPS placeholder
oscl_memset(memBufferParamSet, 0, DEFAULT_PARAMS_SET_LENGTH);
OsclMemoryFragment paramSet;
OsclRefCounter* refCounterParamSet = new(memBufferParamSet) OsclRefCounterDA(memBufferParamSet,
(OsclDestructDealloc*)&iAlloc);
memBufferParamSet += refCounterSize;
paramSet.ptr = memBufferParamSet;
paramSet.len = DEFAULT_PARAMS_SET_LENGTH;
iParamSet = OsclRefCounterMemFrag(paramSet, refCounterParamSet, DEFAULT_PARAMS_SET_LENGTH);
// initialize length and number of sps/ppss
iParamSet.getMemFrag().len = 0;
iNumPPSs = 0;
iNumSPSs = 0;
iSpsPpsSequenceOver = false;
iFirstNAL = false; //set this to false so that mp4 can proceed without a problem.
// in case of AVC, this flag will be set after spspps
iNALSizeArray = NULL;
iNALPtrArray = NULL;
iNALSizeArrayMaxElems = 0;
iNumNALs = 0;
iFirstNALStartCodeSize = 0;
iThreadSafeHandlerEventHandler = NULL;
iThreadSafeHandlerEmptyBufferDone = NULL;
iThreadSafeHandlerFillBufferDone = NULL;
iInBufMemoryPool = NULL;
iOutBufMemoryPool = NULL;
in_ctrl_struct_ptr = NULL;
in_buff_hdr_ptr = NULL;
out_ctrl_struct_ptr = NULL;
out_buff_hdr_ptr = NULL;
// init to some value
iOMXComponentOutputBufferSize = 0;
iNumOutputBuffers = 0;
iOMXComponentInputBufferSize = 0;
iNumInputBuffers = 0;
iDoNotSendOutputBuffersDownstreamFlag = false;
iDoNotSaveInputBuffersFlag = false;
iOutputBuffersFreed = true;// buffers have not been created yet, so they can be considered freed
iInputBuffersFreed = true;
// dynamic port reconfig init vars
iSecondPortReportedChange = false;
iDynamicReconfigInProgress = false;
// EOS flag init
iIsEOSSentToComponent = false;
iIsEOSReceivedFromComponent = false;
// init state of component
iCurrentEncoderState = OMX_StateInvalid;
iTimeStampOut = 0;
iTimeStampPrevious = 0;
iBufferLenOut = 0;
iBufferLenPrevious = 0;
iEndOfFrameFlagPrevious = 0;
iKeyFrameFlagPrevious = 0;
iEndOfNALFlagPrevious = 0;
iEndOfFrameFlagOut = 0;
iKeyFrameFlagOut = 0;
iEndOfNALFlagOut = 0;
//if timescale value is 1 000 000 - it means that
//timestamp is expressed in units of 1/10^6 (i.e. microseconds)
iTimeScale = 1000000;
iInTimeScale = 1000;
iOutTimeScale = 1000;
iInputTimestampClock.set_timescale(iInTimeScale); // keep the timescale set to input timestamp
// counts output frames (for logging)
iFrameCounter = 0;
iInFormat = PVMF_MIME_FORMAT_UNKNOWN;
iOutFormat = PVMF_MIME_FORMAT_UNKNOWN;
// zero out encoder param structure
oscl_memset(&iVideoInputFormat, 0, sizeof(iVideoInputFormat));
// set default values
iVideoInputFormat.iFrameWidth = DEFAULT_FRAME_WIDTH;
iVideoInputFormat.iFrameHeight = DEFAULT_FRAME_HEIGHT;
iVideoInputFormat.iFrameRate = (float)DEFAULT_FRAME_RATE;
iVideoInputFormat.iFrameOrientation = 0;
oscl_memset(&iVideoEncodeParam, 0, sizeof(iVideoEncodeParam));
iVideoEncodeParam.iEncodeID = 0;
iVideoEncodeParam.iNumLayer = 1;
iVideoEncodeParam.iFrameWidth[0] = DEFAULT_FRAME_WIDTH;
iVideoEncodeParam.iFrameHeight[0] = DEFAULT_FRAME_HEIGHT;
iVideoEncodeParam.iBitRate[0] = DEFAULT_BITRATE;
iVideoEncodeParam.iFrameRate[0] = (float)DEFAULT_FRAME_RATE;
iVideoEncodeParam.iFrameQuality = 10;
iVideoEncodeParam.iSceneDetection = false;
iVideoEncodeParam.iRVLCEnable = false;
iVideoEncodeParam.iIFrameInterval = DEFAULT_I_FRAME_INTERVAL;
iVideoEncodeParam.iBufferDelay = (float)0.2;
iVideoEncodeParam.iShortHeader = false;
iVideoEncodeParam.iDataPartitioning = false;
iVideoEncodeParam.iResyncMarker = true;
// set the default rate control type to variable bit rate control
// since it has better performance
iVideoEncodeParam.iRateControlType = PVMFVEN_RATE_CONTROL_VBR;
iVideoEncodeParam.iIquant[0] = 15;
iVideoEncodeParam.iPquant[0] = 12;
iVideoEncodeParam.iBquant[0] = 12;
iVideoEncodeParam.iSearchRange = 16;
iVideoEncodeParam.iMV8x8 = false;
iVideoEncodeParam.iMVHalfPel = true;
iVideoEncodeParam.iPacketSize = 256;
iVideoEncodeParam.iNoCurrentSkip = false;
iVideoEncodeParam.iNoFrameSkip = false;
iVideoEncodeParam.iClipDuration = 0;
iVideoEncodeParam.iProfileLevel = EI_CORE_LEVEL2;
/////////////////AVC SPECIFIC///////////////////////////
iVideoEncodeParam.iEncMode = EI_ENCMODE_RECORDER;
iVideoEncodeParam.iAVCProfile = EI_PROFILE_BASELINE;
iVideoEncodeParam.iAVCLevel = EI_LEVEL_11;
oscl_memset(&iAudioInputFormat, 0, sizeof(iAudioInputFormat));
// Currently, set according to AMR values
iAudioInputFormat.iInputInterleaveMode = EINTERLEAVE_LR;
iAudioInputFormat.iInputBitsPerSample = 16;
iAudioInputFormat.iInputNumChannels = 1;
iAudioInputFormat.iInputSamplingRate = 8000;
oscl_memset(&iAudioEncodeParam, 0, sizeof(iAudioEncodeParam));
iAudioEncodeParam.iMaxNumOutputFramesPerBuffer = MAX_NUM_AMR_FRAMES_PER_BUFFER;
iAudioEncodeParam.iAMRBitrate = GSM_AMR_12_2;
iAudioEncodeParam.iOutputBitrate = 24000;
iAudioEncodeParam.iOutputNumChannels = iAudioInputFormat.iInputNumChannels;
iAudioEncodeParam.iOutputSamplingRate = iAudioInputFormat.iInputSamplingRate;
#ifdef _TEST_AE_ERROR_HANDLING
iErrorHandlingInit = false;
iErrorHandlingEncodeCount = 0;
iCountFrames = 0;
iErrorDataPathStall = 0;
iErrorNodeCmd = 0;
iErrorConfigHeader = false;
iErrorEncodeFlag = 0;
#endif
iInputTimestampClock.set_clock(iBOSTimestamp, 0);
iOMXTicksTimestamp = ConvertTimestampIntoOMXTicks(iInputTimestampClock);
sendYuvFsi = true;
iNodeTypeId = LOG_ID_UNKNOWN;
iLogger = PVLogger::GetLoggerObject("PVMFOMXEncNode");
iRunlLogger = PVLogger::GetLoggerObject("Run.PVMFOMXEncNode");
iDataPathLogger = PVLogger::GetLoggerObject("datapath");
iClockLogger = PVLogger::GetLoggerObject("clock");
iDiagnosticsLogger = PVLogger::GetLoggerObject("pvplayerdiagnostics.encnode.OMXEncnode");
}
/////////////////////////////////////////////////////////////////////////////
// Local Run Routine
/////////////////////////////////////////////////////////////////////////////
void PVMFOMXEncNode::Run()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Run() In", iNodeTypeId));
// if reset is in progress, call DoReset again until Reset Msg is sent
if ((iResetInProgress == true) &&
(iResetMsgSent == false) &&
(iCurrentCommand.size() > 0) &&
(iCurrentCommand.front().iCmd == PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_RESET)
)
{
DoReset(iCurrentCommand.front());
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - Calling DoReset", iNodeTypeId));
return; // don't do anything else
}
//Check for NODE commands...
if (!iInputCommands.empty())
{
if (ProcessCommand(iInputCommands.front()))
{
if (iInterfaceState != EPVMFNodeCreated
&& (!iInputCommands.empty() || (iInPort && (iInPort->IncomingMsgQueueSize() > 0)) ||
(iDataIn.GetRep() != NULL)))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - rescheduling after process command", iNodeTypeId));
RunIfNotReady();
}
return;
}
if (!iInputCommands.empty())
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - rescheduling to process more commands", iNodeTypeId));
RunIfNotReady();
}
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - Input commands empty", iNodeTypeId));
}
if (((iCurrentCommand.size() == 0) && (iInterfaceState != EPVMFNodeStarted)) ||
((iCurrentCommand.size() > 0) && (iCurrentCommand.front().iCmd == PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_START) && (iInterfaceState != EPVMFNodeStarted)))
{
// rescheduling because of input data will be handled in Command Processing Part
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - Node not in Started state yet", iNodeTypeId));
return;
}
// Process port activity, push out all outgoing messages
if (iOutPort)
{
while (iOutPort->OutgoingMsgQueueSize())
{
// if port is busy it is going to wakeup from port ready event
if (!ProcessOutgoingMsg(iOutPort))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - Outgoing Port Busy, cannot send more msgs", iNodeTypeId));
break;
}
}
}
int loopCount = 0;
#if (PVLOGGER_INST_LEVEL >= PVLOGMSG_INST_REL)
uint32 startticks = OsclTickCount::TickCount();
uint32 starttime = OsclTickCount::TicksToMsec(startticks);
#endif
do // Try to consume all the data from the Input port
{
// Process port activity if there is no input data that is being processed
// Do not accept any input if EOS needs to be sent out
if (iInPort && (iInPort->IncomingMsgQueueSize() > 0) && (iDataIn.GetRep() == NULL) && !iEndOfDataReached)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - Getting more input", iNodeTypeId));
if (!ProcessIncomingMsg(iInPort))
{
//Re-schedule to come back.
RunIfNotReady();
return;
}
}
if (iSendBOS)
{
SendBeginOfMediaStreamCommand();
}
// If in init or ready to encode state, process data in the input port if there is input available and input buffers are present
// (note: at EOS, iDataIn will not be available)
if ((iDataIn.GetRep() != NULL) ||
((iNumOutstandingOutputBuffers < iNumOutputBuffers) &&
(iProcessingState == EPVMFOMXEncNodeProcessingState_ReadyToEncode) &&
(iResetMsgSent == false)) ||
((iDynamicReconfigInProgress == true) && (iResetMsgSent == false))
)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::Run() - Calling HandleProcessingState", iNodeTypeId));
// input data is available, that means there is data to be encoded
if (HandleProcessingState() != PVMFSuccess)
{
// If HandleProcessingState does not return Success, we must wait for an event
// no point in rescheduling
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::Run() - HandleProcessingState did not return Success", iNodeTypeId));
return;
}
}
loopCount++;
}
while (iInPort &&
(((iInPort->IncomingMsgQueueSize() > 0) || (iDataIn.GetRep() != NULL)) && (iNumOutstandingInputBuffers < iNumInputBuffers))
&& (!iEndOfDataReached)
&& (iResetMsgSent == false)
);
#if (PVLOGGER_INST_LEVEL >= PVLOGMSG_INST_REL)
uint32 endticks = OsclTickCount::TickCount();
uint32 endtime = OsclTickCount::TicksToMsec(endticks);
uint32 timeinloop;
timeinloop = (endtime - starttime);
PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iRunlLogger, PVLOGMSG_INFO,
(0, "PVMFOMXEncNode-%s::Run() - LoopCount = %d, Time spent in loop(in ms) = %d, iNumOutstandingInputBuffers = %d, iNumOutstandingOutputBuffers = %d ", iNodeTypeId,
loopCount, timeinloop, iNumOutstandingInputBuffers, iNumOutstandingOutputBuffers));
#endif
// EOS processing:
// first send an empty buffer to OMX component and mark the EOS flag
// wait for the OMX component to send async event to indicate that it has reached this EOS buffer
// then, create and send the EOS message downstream
if (iEndOfDataReached && !iDynamicReconfigInProgress)
{
// if EOS was not sent yet and we have an available input buffer, send EOS buffer to component
if (!iIsEOSSentToComponent && (iNumOutstandingInputBuffers < iNumInputBuffers))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::Run() - Sending EOS marked buffer To Component ", iNodeTypeId));
iIsEOSSentToComponent = true;
// if the component is not yet initialized or if it's in the middle of port reconfig,
// don't send EOS buffer to component. It does not care. Just set the flag as if we received
// EOS from the component to enable sending EOS downstream
if (iProcessingState != EPVMFOMXEncNodeProcessingState_ReadyToEncode)
{
iIsEOSReceivedFromComponent = true;
}
else if (!SendEOSBufferToOMXComponent())
{
// for some reason, Component can't receive the EOS buffer
// it could be that it is not initialized yet (because EOS could be the first msg). In this case,
// send the EOS downstream anyway
iIsEOSReceivedFromComponent = true;
}
}
// DV: we must wait for event (acknowledgment from component)
// before sending EOS downstream. This is because OMX Component will send
// the EOS event only after processing remaining buffers
if (iIsEOSReceivedFromComponent)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::Run() - Received EOS from component, Sending EOS msg downstream ", iNodeTypeId));
if (iOutPort && iOutPort->IsOutgoingQueueBusy())
{
// note: we already tried to empty the outgoing q. If it's still busy,
// it means that output port is busy. Just return and wait for the port to become free.
// this will wake up the node and it will send out a msg from the q etc.
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::Run() - - EOS cannot be sent downstream, outgoing queue busy - wait", iNodeTypeId));
return;
}
if (SendEndOfTrackCommand()) // this will only q the EOS
{
// EOS send downstream OK, so reset the flag
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::Run() - EOS was queued to be sent downstream", iNodeTypeId));
iEndOfDataReached = false; // to resume normal processing, reset the flags
iIsEOSSentToComponent = false;
iIsEOSReceivedFromComponent = false;
RunIfNotReady(); // Run again to send out the EOS msg from the outgoing q, and resume
// normal processing
// END OF STREAM EVENT reported by the downstream node.
//ReportInfoEvent(PVMFInfoEndOfData);
}
}
else
{
// keep sending output buffers, it's possible that the component needs to flush output
// data at the end
while (iNumOutstandingOutputBuffers < iNumOutputBuffers)
{
if (!SendOutputBufferToOMXComponent())
break;
}
}
}
//Check for flash command complition...
if (iInPort && iOutPort && (iCurrentCommand.size() > 0) &&
(iCurrentCommand.front().iCmd == PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_FLUSH) &&
(iInPort->IncomingMsgQueueSize() == 0) &&
(iOutPort->OutgoingMsgQueueSize() == 0) &&
(iDataIn.GetRep() == NULL) &&
(iNumOutstandingOutputBuffers == iNumOutputBuffers) && // all output buffers are with the component or outstanding
(iNumOutstandingInputBuffers == 0) // all input buffers were processed and returned. These 2 conditions mean the component is idle
)
{
//flush command is almost completed
//Debug check-- all the port queues should be empty at this point.
OMX_ERRORTYPE err = OMX_ErrorNone;
OMX_STATETYPE sState = OMX_StateInvalid;
OSCL_ASSERT(iInPort->IncomingMsgQueueSize() == 0 && iOutPort->OutgoingMsgQueueSize() == 0);
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::Run() - Flush pending", iNodeTypeId));
// now, drive the state of the omx component to IDLE - wait for the callback from the component and send cmd complete from there
iDataIn.Unbind();
iPreviousMediaData.Unbind(); // make sure nothing is holding this data
if ((iOutFormat == PVMF_MIME_H264_VIDEO_MP4) || (iOutFormat == PVMF_MIME_H264_VIDEO_RAW))
{
// prepare for next start
iFirstNAL = true;
}
// Clear the data flags
iEndOfDataReached = false;
iIsEOSSentToComponent = false;
iIsEOSReceivedFromComponent = false;
iDoNotSendOutputBuffersDownstreamFlag = true; // stop sending output buffers downstream
iDoNotSaveInputBuffersFlag = true;
//Get state of OpenMAX encoder
err = OMX_GetState(iOMXEncoder, &sState);
if (err != OMX_ErrorNone)
{
//Error condition report
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::Run(): Flush pending - Can't get State of encoder!", iNodeTypeId));
sState = OMX_StateInvalid;
}
if ((sState == OMX_StateExecuting) || (sState == OMX_StatePause))
{
/* Change state to OMX_StateIdle from OMX_StateExecuting or OMX_StatePause. */
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::Run(): Flush pending: Changing Component State Executing->Idle or Pause->Idle", iNodeTypeId));
if (iOutPort) iOutPort->ClearMsgQueues();
err = OMX_SendCommand(iOMXEncoder, OMX_CommandStateSet, OMX_StateIdle, NULL);
if (err != OMX_ErrorNone)
{
//Error condition report
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::Run(): Flush pending : Can't send StateSet command to encoder!", iNodeTypeId));
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFErrInvalidState);
return;
}
// prevent the node from sending more buffers etc.
// if port reconfiguration is in process, let the state remain one of the port config states
// if there is a start command, we can do it seemlessly (by continuing the port reconfig)
if (iProcessingState == EPVMFOMXEncNodeProcessingState_ReadyToEncode)
iProcessingState = EPVMFOMXEncNodeProcessingState_Stopping;
}
else
{
//Error condition report
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::Run(): Flush pending : Encoder is not in the Executing or Pause state!", iNodeTypeId));
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFErrInvalidState);
return;
}
}
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::Run() Out", iNodeTypeId));
}
/////////////////////////////////////////////////////////////////////////////
// This routine will dispatch recived commands
/////////////////////////////////////////////////////////////////////////////
bool PVMFOMXEncNode::ProcessCommand(PVMFOMXEncNodeCommand& 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 (!iCurrentCommand.empty() && !aCmd.hipri())
return false;
switch (aCmd.iCmd)
{
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_QUERYUUID:
DoQueryUuid(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_QUERYINTERFACE:
DoQueryInterface(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_REQUESTPORT:
DoRequestPort(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_RELEASEPORT:
DoReleasePort(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_INIT:
DoInit(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_PREPARE:
DoPrepare(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_START:
DoStart(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_STOP:
DoStop(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_FLUSH:
DoFlush(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_PAUSE:
DoPause(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_RESET:
DoReset(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_CANCELCMD:
DoCancelCommand(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_CANCELALL:
DoCancelAllCommands(aCmd);
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_GETNODEMETADATAKEY:
{
PVMFStatus retval = DoGetNodeMetadataKey(aCmd);
CommandComplete(iInputCommands, aCmd, retval);
}
break;
case PVMFOMXEncNodeCommand::PVOMXENC_NODE_CMD_GETNODEMETADATAVALUE:
{
PVMFStatus retval = DoGetNodeMetadataValue(aCmd);
CommandComplete(iInputCommands, aCmd, retval);
}
break;
default://unknown command type
CommandComplete(iInputCommands, aCmd, PVMFFailure);
break;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
// This routine will process incomming message from the port
/////////////////////////////////////////////////////////////////////////////
bool PVMFOMXEncNode::ProcessIncomingMsg(PVMFPortInterface* aPort)
{
//Called by the AO to process one buffer off the port's
//incoming data queue. This routine will dequeue and
//dispatch the data.
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "0x%x PVMFOMXEncNode-%s::ProcessIncomingMsg: aPort=0x%x", this, iNodeTypeId, aPort));
PVMFStatus status = PVMFFailure;
//#define SIMULATE_BOS
#ifdef SIMULATE_BOS
if (((PVMFOMXEncPort*)aPort)->iNumFramesConsumed == 6))
{
PVMFSharedMediaCmdPtr BOSCmdPtr = PVMFMediaCmd::createMediaCmd();
// Set the format ID to BOS
BOSCmdPtr->setFormatID(PVMF_MEDIA_CMD_BOS_FORMAT_ID);
// Set the timestamp
BOSCmdPtr->setTimestamp(201);
BOSCmdPtr->setStreamID(0);
// Convert to media message and send it out
PVMFSharedMediaMsgPtr mediaMsgOut;
convertToPVMFMediaCmdMsg(mediaMsgOut, BOSCmdPtr);
//store the stream id and time stamp of bos message
iStreamID = mediaMsgOut->getStreamID();
iBOSTimestamp = mediaMsgOut->getTimestamp();
iInputTimestampClock.set_clock(iBOSTimestamp, 0);
iOMXTicksTimestamp = ConvertTimestampIntoOMXTicks(iInputTimestampClock);
iSendBOS = true;
#ifdef _DEBUG
printf("PVMFOMXEncNode-%s::ProcessIncomingMsg() SIMULATED BOS\n", iNodeTypeId);
#endif
((PVMFOMXEncPort*)aPort)->iNumFramesConsumed++;
return true;
}
#endif
//#define SIMULATE_PREMATURE_EOS
#ifdef SIMULATE_PREMATURE_EOS
if (((PVMFOMXEncPort*)aPort)->iNumFramesConsumed == 5)
{
PVMFSharedMediaCmdPtr EOSCmdPtr = PVMFMediaCmd::createMediaCmd();
// Set the format ID to EOS
EOSCmdPtr->setFormatID(PVMF_MEDIA_CMD_EOS_FORMAT_ID);
// Set the timestamp
EOSCmdPtr->setTimestamp(200);
// Convert to media message and send it out
PVMFSharedMediaMsgPtr mediaMsgOut;
convertToPVMFMediaCmdMsg(mediaMsgOut, EOSCmdPtr);
((PVMFOMXEncPort*)aPort)->iNumFramesConsumed++;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::ProcessIncomingMsg: SIMULATED EOS", iNodeTypeId));
#ifdef _DEBUG
printf("PVMFOMXEncNode-%s::ProcessIncomingMsg() SIMULATED EOS\n", iNodeTypeId);
#endif
// Set EOS flag
iEndOfDataReached = true;
// Save the timestamp for the EOS cmd
iEndOfDataTimestamp = mediaMsgOut->getTimestamp();
return true;
}
#endif
PVMFSharedMediaMsgPtr msg;
status = aPort->DequeueIncomingMsg(msg);
if (status != PVMFSuccess)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "0x%x PVMFOMXEncNode-%s::ProcessIncomingMsg: Error - DequeueIncomingMsg failed", this, iNodeTypeId));
return false;
}
if (msg->getFormatID() == PVMF_MEDIA_CMD_BOS_FORMAT_ID)
{
//store the stream id and time stamp of bos message
iStreamID = msg->getStreamID();
iBOSTimestamp = msg->getTimestamp();
// we need to initialize the timestamp here - so that updates later on are accurate (in terms of rollover etc)
iInputTimestampClock.set_clock(iBOSTimestamp, 0);
iOMXTicksTimestamp = ConvertTimestampIntoOMXTicks(iInputTimestampClock);
iSendBOS = true;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::ProcessIncomingMsg: Received BOS stream %d, timestamp %d", iNodeTypeId, iStreamID, iBOSTimestamp));
((PVMFOMXEncPort*)aPort)->iNumFramesConsumed++;
return true;
}
else if (msg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
{
// Set EOS flag
iEndOfDataReached = true;
// Save the timestamp for the EOS cmd
iEndOfDataTimestamp = msg->getTimestamp();
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::ProcessIncomingMsg: Received EOS", iNodeTypeId));
((PVMFOMXEncPort*)aPort)->iNumFramesConsumed++;
return true; // do not do conversion into media data, just set the flag and leave
}
convertToPVMFMediaData(iDataIn, msg);
iCurrFragNum = 0; // for new message, reset the fragment counter
((PVMFOMXEncPort*)aPort)->iNumFramesConsumed++;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::ProcessIncomingMsg() Received %d frames", iNodeTypeId, ((PVMFOMXEncPort*)aPort)->iNumFramesConsumed));
//return true if we processed an activity...
return true;
}
/////////////////////////////////////////////////////////////////////////////
// This routine will process outgoing message by sending it into output the port
/////////////////////////////////////////////////////////////////////////////
bool PVMFOMXEncNode::ProcessOutgoingMsg(PVMFPortInterface* aPort)
{
//Called by the AO to process one message off the outgoing
//message queue for the given port. This routine will
//try to send the data to the connected port.
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "0x%x PVMFOMXEncNode-%s::ProcessOutgoingMsg: aPort=0x%x", this, iNodeTypeId, aPort));
PVMFStatus status = aPort->Send();
if (status == PVMFErrBusy)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "0x%x PVMFOMXEncNode-%s::ProcessOutgoingMsg: Connected port goes into busy state", this, iNodeTypeId));
}
//Report any unexpected failure in port processing...
//(the InvalidState error happens when port input is suspended,
//so don't report it.)
if (status != PVMFErrBusy
&& status != PVMFSuccess
&& status != PVMFErrInvalidState)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "0x%x PVMFOMXEncNode-%s::ProcessOutgoingMsg: Error - ProcessPortActivity failed. port=0x%x, type=%d",
this, iNodeTypeId, iOutPort, PVMF_PORT_ACTIVITY_OUTGOING_MSG));
ReportErrorEvent(PVMFErrPortProcessing);
}
//return true if we processed an activity...
return (status != PVMFErrBusy);
}
/////////////////////////////////////////////////////////////////////////////
// This routine will process received data usign State Machine
/////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFOMXEncNode::HandleProcessingState()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::HandleProcessingState() In", iNodeTypeId));
PVMFStatus status = PVMFSuccess;
switch (iProcessingState)
{
// The FOLLOWING 4 states handle Dynamic Port Reconfiguration
case EPVMFOMXEncNodeProcessingState_PortReconfig:
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Sending Port Disable Command", iNodeTypeId));
// port reconfiguration is required. Only one port at a time is disabled and then re-enabled after buffer resizing
OMX_SendCommand(iOMXEncoder, OMX_CommandPortDisable, iPortIndexForDynamicReconfig, NULL);
// the port will now start returning outstanding buffers
// set the flag to prevent output from going downstream (in case of output port being reconfigd)
// set the flag to prevent input from being saved and returned to component (in case of input port being reconfigd)
// set the state to wait for port saying it is disabled
if (iPortIndexForDynamicReconfig == iOutputPortIndex)
{
iDoNotSendOutputBuffersDownstreamFlag = true;
}
else if (iPortIndexForDynamicReconfig == iInputPortIndex)
{
iDoNotSaveInputBuffersFlag = true;
}
iProcessingState = EPVMFOMXEncNodeProcessingState_WaitForBufferReturn;
// fall through to the next case to check if all buffers are already back
}
case EPVMFOMXEncNodeProcessingState_WaitForBufferReturn:
{
// as buffers are coming back, Run may be called, wait until all buffers are back, then Free them all
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> WaitForBufferReturn ", iNodeTypeId));
// check if it's output port being reconfigured
if (iPortIndexForDynamicReconfig == iOutputPortIndex)
{
// if all buffers have returned, free them
if (iNumOutstandingOutputBuffers == 0)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> all output buffers are back, free them", iNodeTypeId));
if (false == iOutputBuffersFreed)
{
if (!FreeBuffersFromComponent(iOutBufMemoryPool, // allocator
iOutputAllocSize, // size to allocate from pool (hdr only or hdr+ buffer)
iNumOutputBuffers, // number of buffers
iOutputPortIndex, // port idx
false // this is not input
))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Cannot free output buffers ", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return PVMFErrNoMemory;
}
}
// if the callback (that port is disabled) has not arrived yet, wait for it
// if it has arrived, it will set the state to PortReEnable
if (iProcessingState != EPVMFOMXEncNodeProcessingState_PortReEnable)
iProcessingState = EPVMFOMXEncNodeProcessingState_WaitForPortDisable;
status = PVMFSuccess; // allow rescheduling of the node potentially
}
else
status = PVMFErrNoMemory; // must wait for buffers to come back. No point in automatic rescheduling
// but each buffer will reschedule the node when it comes in
}
else
{
// this is input port
// if all buffers have returned, free them
if (iNumOutstandingInputBuffers == 0)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> all input buffers are back, free them", iNodeTypeId));
if (false == iInputBuffersFreed)
{
if (!FreeBuffersFromComponent(iInBufMemoryPool, // allocator
iInputAllocSize, // size to allocate from pool (hdr only or hdr+ buffer)
iNumInputBuffers, // number of buffers
iInputPortIndex, // port idx
true // this is input
))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Cannot free input buffers ", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return PVMFErrNoMemory;
}
}
// if the callback (that port is disabled) has not arrived yet, wait for it
// if it has arrived, it will set the state to PortReEnable
if (iProcessingState != EPVMFOMXEncNodeProcessingState_PortReEnable)
iProcessingState = EPVMFOMXEncNodeProcessingState_WaitForPortDisable;
status = PVMFSuccess; // allow rescheduling of the node
}
else
status = PVMFErrNoMemory; // must wait for buffers to come back. No point in automatic
// rescheduling. Each buffer will reschedule the node
// when it comes in
}
// the state will be changed to PortReEnable once we get confirmation that Port was actually disabled
break;
}
case EPVMFOMXEncNodeProcessingState_WaitForPortDisable:
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> wait for port disable callback", iNodeTypeId));
// do nothing. Just wait for the port to become disabled (we'll get event from component, which will
// transition the state to PortReEnable
status = PVMFErrNoMemory; // prevent Rescheduling the node
break;
}
case EPVMFOMXEncNodeProcessingState_PortReEnable:
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Sending reenable port command", iNodeTypeId));
// set the port index so that we get parameters for the proper port
iParamPort.nPortIndex = iPortIndexForDynamicReconfig;
// iParamPort.nVersion = OMX_VERSION;
// get new parameters of the port
OMX_GetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
// send command for port re-enabling (for this to happen, we must first recreate the buffers)
OMX_SendCommand(iOMXEncoder, OMX_CommandPortEnable, iPortIndexForDynamicReconfig, NULL);
// is this output port?
if (iPortIndexForDynamicReconfig == iOutputPortIndex)
{
iOMXComponentOutputBufferSize = ((iParamPort.format.video.nFrameWidth + 15) & (~15)) * ((iParamPort.format.video.nFrameHeight + 15) & (~15)) * 3 / 2;
// check the new buffer size
if (iInPort)
{
if (((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_H264_VIDEO ||
((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_H264_VIDEO_MP4 ||
((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_H264_VIDEO_RAW ||
((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_M4V ||
((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_H2631998 ||
((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_H2632000)
{
iOMXComponentOutputBufferSize = ((iParamPort.format.video.nFrameWidth + 15) & (~15)) * ((iParamPort.format.video.nFrameHeight + 15) & (~15)) * 3 / 2;
}
else if (((PVMFOMXEncPort*)iInPort)->iFormat == PVMF_MIME_WMV)
{
// This is a requirement for the WMV encoder that we have currently
iOMXComponentOutputBufferSize = ((iParamPort.format.video.nFrameWidth + 3) & (~3)) * (iParamPort.format.video.nFrameHeight) * 3 / 2;
}
else
{
OSCL_ASSERT(false);
}
}
// set the new width / height
iYUVWidth = iParamPort.format.video.nFrameWidth;
iYUVHeight = iParamPort.format.video.nFrameHeight;
if (iOMXComponentOutputBufferSize < iParamPort.nBufferSize)
iOMXComponentOutputBufferSize = iParamPort.nBufferSize;
// do we need to increase the number of buffers?
if (iNumOutputBuffers < iParamPort.nBufferCountMin)
iNumOutputBuffers = iParamPort.nBufferCountMin;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() new output buffers %d, size %d", iNodeTypeId, iNumOutputBuffers, iOMXComponentOutputBufferSize));
/* Allocate output buffers */
if (!CreateOutMemPool(iNumOutputBuffers))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Cannot allocate output buffers ", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return PVMFErrNoMemory;
}
if (!ProvideBuffersToComponent(iOutBufMemoryPool, // allocator
iOutputAllocSize, // size to allocate from pool (hdr only or hdr+ buffer)
iNumOutputBuffers, // number of buffers
iOMXComponentOutputBufferSize, // actual buffer size
iOutputPortIndex, // port idx
iOMXComponentSupportsExternalOutputBufferAlloc, // can component use OMX_UseBuffer
false // this is not input
))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Cannot provide output buffers to component", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return PVMFErrNoMemory;
}
// do not drop output any more, i.e. enable output to be sent downstream
iDoNotSendOutputBuffersDownstreamFlag = false;
}
else
{
// this is input port
iOMXComponentInputBufferSize = iParamPort.nBufferSize;
// do we need to increase the number of buffers?
if (iNumInputBuffers < iParamPort.nBufferCountMin)
iNumInputBuffers = iParamPort.nBufferCountMin;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() new buffers %d, size %d", iNodeTypeId, iNumInputBuffers, iOMXComponentInputBufferSize));
/* Allocate input buffers */
if (!CreateInputMemPool(iNumInputBuffers))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Cannot allocate new input buffers to component", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return PVMFErrNoMemory;
}
if (!ProvideBuffersToComponent(iInBufMemoryPool, // allocator
iInputAllocSize, // size to allocate from pool (hdr only or hdr+ buffer)
iNumInputBuffers, // number of buffers
iOMXComponentInputBufferSize, // actual buffer size
iInputPortIndex, // port idx
iOMXComponentSupportsExternalInputBufferAlloc, // can component use OMX_UseBuffer
true // this is input
))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> Cannot provide new input buffers to component", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return PVMFErrNoMemory;
}
// do not drop partially consumed input
iDoNotSaveInputBuffersFlag = false;
}
// if the callback that the port was re-enabled has not arrived yet, wait for it
// if it has arrived, it will set the state to either PortReconfig or to ReadyToEncode
if (iProcessingState != EPVMFOMXEncNodeProcessingState_PortReconfig &&
iProcessingState != EPVMFOMXEncNodeProcessingState_ReadyToEncode)
iProcessingState = EPVMFOMXEncNodeProcessingState_WaitForPortEnable;
status = PVMFSuccess; // allow rescheduling of the node
break;
}
case EPVMFOMXEncNodeProcessingState_WaitForPortEnable:
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Port Reconfiguration -> wait for port enable callback", iNodeTypeId));
// do nothing. Just wait for the port to become enabled (we'll get event from component, which will
// transition the state to ReadyToEncode
status = PVMFErrNoMemory; // prevent ReScheduling
break;
}
// NORMAL DATA FLOW STATE:
case EPVMFOMXEncNodeProcessingState_ReadyToEncode:
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Ready To Encode start", iNodeTypeId));
// In normal data flow and decoding state
// Send all available output buffers to the encoder
while (iNumOutstandingOutputBuffers < iNumOutputBuffers)
{
// grab buffer header from the mempool if possible, and send to component
if (!SendOutputBufferToOMXComponent())
break;
}
// next, see if partially consumed input buffer needs to be resent back to OMX component
// NOTE: it is not allowed that the component returns more than 1 partially consumed input buffers
// i.e. if a partially consumed input buffer is returned, it is assumed that the OMX component
// will be waiting to get data
if (iInputBufferToResendToComponent != NULL)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Sending previous - partially consumed input back to the OMX component", iNodeTypeId));
OMX_EmptyThisBuffer(iOMXEncoder, iInputBufferToResendToComponent);
iInputBufferToResendToComponent = NULL; // do this only once
}
else if ((iNumOutstandingInputBuffers < iNumInputBuffers) && (iDataIn.GetRep() != NULL))
{
// try to get an input buffer header
// and send the input data over to the component
SendInputBufferToOMXComponent();
}
status = PVMFSuccess;
break;
}
case EPVMFOMXEncNodeProcessingState_Stopping:
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::HandleProcessingState() Stopping -> wait for Component to move from Executing->Idle", iNodeTypeId));
status = PVMFErrNoMemory; // prevent rescheduling
break;
}
case EPVMFOMXEncNodeProcessingState_WaitForOutgoingQueue:
status = PVMFErrNoMemory;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::HandleProcessingState() Do nothing since waiting for output port queue to become available", iNodeTypeId));
break;
default:
break;
}
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFOMXEncNode-%s::HandleProcessingState() Out", iNodeTypeId));
return status;
}
/////////////////////////////////////////////////////////////////////////////
bool PVMFOMXEncNode::SendOutputBufferToOMXComponent()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SendOutputBufferToOMXComponent() In", iNodeTypeId));
OutputBufCtrlStruct *output_buf = NULL;
int32 errcode = 0;
uint32 ii;
// try to get output buffer header
OSCL_TRY(errcode, output_buf = (OutputBufCtrlStruct *) iOutBufMemoryPool->allocate(iOutputAllocSize));
if (errcode != 0)
{
if (errcode == OsclErrNoResources)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger,
PVLOGMSG_DEBUG, (0, "PVMFOMXEncNode-%s::SendOutputBufferToOMXComponent() No more output buffers in the mempool", iNodeTypeId));
iOutBufMemoryPool->notifyfreechunkavailable(*this, (OsclAny *) iOutBufMemoryPool); // To signal when next deallocate() is called on mempool
return false;
}
else
{
// Memory allocation for the pool failed
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::SendOutputBufferToOMXComponent() Output mempool error", iNodeTypeId));
SetState(EPVMFNodeError);
ReportErrorEvent(PVMFErrNoMemory);
return false;
}
}
//for every allocated buffer, make sure you notify when buffer is released. Keep track of allocated buffers
// use mempool as context to recognize which buffer (input or output) was returned
iOutBufMemoryPool->notifyfreechunkavailable(*this, (OsclAny *)iOutBufMemoryPool);
iNumOutstandingOutputBuffers++;
for (ii = 0; ii < iNumOutputBuffers; ii++)
{
if (output_buf == out_ctrl_struct_ptr[ii])
{
break;
}
}
if (ii == iNumOutputBuffers)
return false;
output_buf->pBufHdr = (OMX_BUFFERHEADERTYPE *)out_buff_hdr_ptr[ii];
output_buf->pBufHdr->nFilledLen = 0; // make sure you tell OMX component buffer is empty
output_buf->pBufHdr->nOffset = 0;
output_buf->pBufHdr->pAppPrivate = output_buf; // set pAppPrivate to be pointer to output_buf
// (this is context for future release of this buffer to the mempool)
// this was done during buffer creation, but still repeat just in case
output_buf->pBufHdr->nFlags = 0; // zero out the flags
OMX_FillThisBuffer(iOMXEncoder, output_buf->pBufHdr);
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SendOutputBufferToOMXComponent() Out", iNodeTypeId));
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool PVMFOMXEncNode::NegotiateVideoComponentParameters()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoParameters() In", iNodeTypeId));
OMX_ERRORTYPE Err;
OMX_CONFIG_ROTATIONTYPE InputRotationType;
// first get the number of ports and port indices
OMX_PORT_PARAM_TYPE VideoPortParameters;
uint32 NumPorts;
uint32 ii;
// get starting number
CONFIG_SIZE_AND_VERSION(VideoPortParameters);
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoInit, &VideoPortParameters);
NumPorts = VideoPortParameters.nPorts; // must be at least 2 of them (in&out)
if (Err != OMX_ErrorNone || NumPorts < 2)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() There is insuffucient (%d) ports", iNodeTypeId, NumPorts));
return false;
}
// loop through video ports starting from the starting index to find index of the first input port
for (ii = VideoPortParameters.nStartPortNumber ; ii < VideoPortParameters.nStartPortNumber + NumPorts; ii++)
{
// get port parameters, and determine if it is input or output
// if there are more than 2 ports, the first one we encounter that has input direction is picked
CONFIG_SIZE_AND_VERSION(iParamPort);
//port
iParamPort.nPortIndex = ii; // iInputPortIndex; //OMF_MC_H264D_PORT_INDEX_OF_STREAM;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem negotiating with port %d ", iNodeTypeId, ii));
return false;
}
if (iParamPort.eDir == OMX_DirInput)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Found Input port index %d ", iNodeTypeId, ii));
iInputPortIndex = ii;
break;
}
}
if (ii == VideoPortParameters.nStartPortNumber + NumPorts)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Cannot find any input port ", iNodeTypeId));
return false;
}
// loop through video ports starting from the starting index to find index of the first output port
for (ii = VideoPortParameters.nStartPortNumber ; ii < VideoPortParameters.nStartPortNumber + NumPorts; ii++)
{
// get port parameters, and determine if it is input or output
// if there are more than 2 ports, the first one we encounter that has output direction is picked
CONFIG_SIZE_AND_VERSION(iParamPort);
//port
iParamPort.nPortIndex = ii;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem negotiating with port %d ", iNodeTypeId, ii));
return false;
}
if (iParamPort.eDir == OMX_DirOutput)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Found Output port index %d ", iNodeTypeId, ii));
iOutputPortIndex = ii;
break;
}
}
if (ii == VideoPortParameters.nStartPortNumber + NumPorts)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Cannot find any output port ", iNodeTypeId));
return false;
}
// now get input parameters
//INPUT PORT
// first check if encode parameters have been set correctly
if ((0 == iVideoEncodeParam.iFrameWidth[0]) ||
(0 == iVideoEncodeParam.iFrameHeight[0]) ||
(0 == iVideoEncodeParam.iFrameRate[0]) ||
(0 == iVideoEncodeParam.iBitRate[0]) ||
(0 == iVideoInputFormat.iFrameWidth) ||
(0 == iVideoInputFormat.iFrameHeight) ||
(0 == iVideoInputFormat.iFrameRate)
)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Encode parameters not set correctly", iNodeTypeId));
return false;
}
// first of all, check if the port supports the adequate port format
OMX_VIDEO_PARAM_PORTFORMATTYPE Video_port_format;
OMX_COLOR_FORMATTYPE DesiredPortColorFormat;
if (iInFormat == PVMF_MIME_RGB24)
{
DesiredPortColorFormat = OMX_COLOR_Format24bitRGB888;
}
else if (iInFormat == PVMF_MIME_RGB12)
{
DesiredPortColorFormat = OMX_COLOR_Format12bitRGB444;
}
else if (iInFormat == PVMF_MIME_YUV420)
{
//TODO: get color format from MIO. JJ 03/09/09
DesiredPortColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
//DesiredPortColorFormat = OMX_COLOR_FormatYUV420Planar;
}
else if (iInFormat == PVMF_MIME_YUV422_INTERLEAVED_UYVY)
{
DesiredPortColorFormat = OMX_COLOR_FormatCbYCrY;
}
else if (iInFormat == PVMF_MIME_YUV422_INTERLEAVED_YUYV)
{
DesiredPortColorFormat = OMX_COLOR_FormatYCbYCr;
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem with input port %d color format", iNodeTypeId, iInputPortIndex));
return false;
}
CONFIG_SIZE_AND_VERSION(Video_port_format);
Video_port_format.nPortIndex = iInputPortIndex; // set input port as target
// loop over supported formats until we hit the one we want
// or until the component has no more supported formats (in which case it returns OMX_ErrorNoMore
Err = OMX_ErrorNone;
Video_port_format.nIndex = 0; //init the format counter
while (OMX_ErrorNone == Err)
{
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoPortFormat, &Video_port_format);
if ((OMX_ErrorNone != Err))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem getting port format for input port %d or no desired port format found", iNodeTypeId, iInputPortIndex));
return false;
}
if ((Video_port_format.eColorFormat == DesiredPortColorFormat))
break;
Video_port_format.nIndex ++;
}
// OK, we've found the desired format, set it as the one used
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoPortFormat, &Video_port_format);
if ((OMX_ErrorNone != Err))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem setting port format for input port %d ", iNodeTypeId, iInputPortIndex));
return false;
}
CONFIG_SIZE_AND_VERSION(iParamPort);
iParamPort.nPortIndex = iInputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem negotiating with input port %d ", iNodeTypeId, iInputPortIndex));
return false;
}
if (iInFormat == PVMF_MIME_RGB24)
{
iOMXComponentInputBufferSize = (iVideoInputFormat.iFrameWidth * iVideoInputFormat.iFrameHeight * 3);
iParamPort.format.video.eColorFormat = OMX_COLOR_Format24bitRGB888;
}
else if (iInFormat == PVMF_MIME_RGB12)
{
iOMXComponentInputBufferSize = (iVideoInputFormat.iFrameWidth * iVideoInputFormat.iFrameHeight * 2);
iParamPort.format.video.eColorFormat = OMX_COLOR_Format12bitRGB444;
}
else if (iInFormat == PVMF_MIME_YUV420)
{
iOMXComponentInputBufferSize = (iVideoInputFormat.iFrameWidth * iVideoInputFormat.iFrameHeight * 3) >> 1;
//TODO: get color format from MIO. JJ 03/09/09
iParamPort.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
//iParamPort.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
}
else if (iInFormat == PVMF_MIME_YUV422_INTERLEAVED_UYVY)
{
iOMXComponentInputBufferSize = iVideoInputFormat.iFrameWidth * iVideoInputFormat.iFrameHeight * 2;
iParamPort.format.video.eColorFormat = OMX_COLOR_FormatCbYCrY;
}
else if (iInFormat == PVMF_MIME_YUV422_INTERLEAVED_YUYV)
{
iOMXComponentInputBufferSize = iVideoInputFormat.iFrameWidth * iVideoInputFormat.iFrameHeight * 2;
iParamPort.format.video.eColorFormat = OMX_COLOR_FormatYCbYCr;
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem with input port %d color format", iNodeTypeId, iInputPortIndex));
return false;
}
//The input buffer size is a read-only parameter and the encoder component needs to adjust it.
// We (the client) determine the size of the buffers based on width/height -
// the component doesn't know width/height yet
//iParamPort.nBufferSize = iOMXComponentInputBufferSize;
// set the width and height of video frame and input framerate
iParamPort.format.video.nFrameWidth = iVideoInputFormat.iFrameWidth;
iParamPort.format.video.nFrameHeight = iVideoInputFormat.iFrameHeight;
// This is Q16 value, so shift by 16 first and cast to preserve accuracy
iParamPort.format.video.xFramerate = (uint32)(iVideoInputFormat.iFrameRate * (1 << 16));
// indicate that input is uncompressed so that color format is valid
iParamPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
// let the component decide about the number of input buffers
iNumInputBuffers = iParamPort.nBufferCountActual;
// do we need to increase the number of buffers?
if (iNumInputBuffers < iParamPort.nBufferCountMin)
iNumInputBuffers = iParamPort.nBufferCountMin;
/* OMX_UseBuffer, ie. if OMX client instead of OMX component is to allocate buffers,
* if the buffers are allocated in MIO, check MIO allocator the max number of buffers;
* else (the buffer is allocated by OMX client itself) floor the number of buffers to NUMBER_INPUT_BUFFER
* validate with OMX component whether it is ok with the number decided above.
* Note: the spec says in OMX_UseBuffer, the OMX client can decide number of input buffers.
*/
if(iOMXComponentSupportsExternalInputBufferAlloc)
{
ipExternalInputBufferAllocatorInterface = NULL;
PvmiKvp* kvp = NULL;
int numKvp = 0;
PvmiKeyType aIdentifier = (PvmiKeyType)PVMF_BUFFER_ALLOCATOR_KEY;
int32 err, err1;
OSCL_TRY(err, ((PVMFOMXEncPort*)iInPort)->pvmiGetBufferAllocatorSpecificInfoSync(aIdentifier, kvp, numKvp););
if ((err == OsclErrNone) && (NULL != kvp))
{
ipExternalInputBufferAllocatorInterface = (PVInterface*) kvp->value.key_specific_value;
if (ipExternalInputBufferAllocatorInterface)
{
PVInterface* pTempPVInterfacePtr = NULL;
OSCL_TRY(err, ipExternalInputBufferAllocatorInterface->queryInterface(PVMFFixedSizeBufferAllocUUID, pTempPVInterfacePtr););
OSCL_TRY(err1, ((PVMFOMXEncPort*)iInPort)->releaseParametersSync(kvp, numKvp););
OSCL_FIRST_CATCH_ANY(err1,
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s::NegotiateComponentParameters() - Unable to Release Parameters", iNodeTypeId));
);
if ((err == OsclErrNone) && (NULL != pTempPVInterfacePtr))
{
ipFixedSizeBufferAlloc = OSCL_STATIC_CAST(PVMFFixedSizeBufferAlloc*, pTempPVInterfacePtr);
uint32 iNumBuffers = ipFixedSizeBufferAlloc->getNumBuffers();
uint32 iBufferSize = ipFixedSizeBufferAlloc->getBufferSize();
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFOMXEncNode-%s iNumBuffers %d iBufferSize %d iOMXComponentInputBufferSize %d iParamPort.nBufferCountMin %d", iNodeTypeId, iNumBuffers , iBufferSize , iOMXComponentInputBufferSize, iParamPort.nBufferCountMin ) );
//TODO should let camera decide number of buffers.
if ((iNumBuffers < iParamPort.nBufferCountMin) || (iBufferSize < iOMXComponentInputBufferSize ))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFOMXEncNode-%s::NegotiateComponentParameters() - not enough buffer. Got %d x %d. Require %d x %d", iNodeTypeId, iNumBuffers , iBufferSize , iOMXComponentInputBufferSize, iParamPort.nBufferCountMin));
//ipFixedSizeBufferAlloc = NULL;
ipExternalInputBufferAllocatorInterface->removeRef();
ipExternalInputBufferAllocatorInterface = NULL;
}
else
{
iNumInputBuffers = iNumBuffers;
iOMXComponentInputBufferSize = iBufferSize;
}
}
else
{
ipExternalInputBufferAllocatorInterface->removeRef();
ipExternalInputBufferAllocatorInterface = NULL;
}
}
}
}
// if component allows us to allocate buffers, we'll decide how many to allocate
if (iOMXComponentSupportsExternalInputBufferAlloc && (iParamPort.nBufferCountMin < NUMBER_INPUT_BUFFER))
{
if(NULL == ipFixedSizeBufferAlloc)
{
// preset the number of input buffers
iNumInputBuffers = NUMBER_INPUT_BUFFER;
}
}
// set the number of input buffer
iParamPort.nBufferCountActual = iNumInputBuffers;
// set the number of actual input buffers
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Inport buffers %d,size %d", iNodeTypeId, iNumInputBuffers, iOMXComponentInputBufferSize));
// lock in the input port parameters
CONFIG_SIZE_AND_VERSION(iParamPort);
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem setting parameters in input port %d ", iNodeTypeId, iInputPortIndex));
return false;
}
//////////////////// OUTPUT PORT //////////////////////////////////////////////
CONFIG_SIZE_AND_VERSION(Video_port_format);
Video_port_format.nPortIndex = iOutputPortIndex; // set output port as target
OMX_VIDEO_CODINGTYPE DesiredPortFormat = OMX_VIDEO_CodingUnused;
if (iOutFormat == PVMF_MIME_M4V)
{
DesiredPortFormat = OMX_VIDEO_CodingMPEG4;
}
else if (iOutFormat == PVMF_MIME_H2631998 ||
iOutFormat == PVMF_MIME_H2632000)
{
DesiredPortFormat = OMX_VIDEO_CodingH263;
}
else if (iOutFormat == PVMF_MIME_H264_VIDEO_RAW ||
iOutFormat == PVMF_MIME_H264_VIDEO_MP4)
{
DesiredPortFormat = OMX_VIDEO_CodingAVC;
}
else
{
DesiredPortFormat = OMX_VIDEO_CodingUnused;
}
if (DesiredPortFormat == OMX_VIDEO_CodingUnused)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem with output port %d format", iNodeTypeId, iOutputPortIndex));
return false;
}
// loop over supported formats until we hit the one we want
// or until the component has no more supported formats (in which case it returns OMX_ErrorNoMore
Err = OMX_ErrorNone;
Video_port_format.nIndex = 0; //init the format counter
while (OMX_ErrorNone == Err)
{
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoPortFormat, &Video_port_format);
if ((OMX_ErrorNone != Err))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem getting port format for output port %d or no desired port format found", iNodeTypeId, iOutputPortIndex));
return false;
}
if ((Video_port_format.eCompressionFormat == DesiredPortFormat))
break;
Video_port_format.nIndex ++;
}
// OK, we've found the desired format, set it as the one used
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoPortFormat, &Video_port_format);
if ((OMX_ErrorNone != Err))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem setting port format for output port %d ", iNodeTypeId, iOutputPortIndex));
return false;
}
//Port 1 for output port
CONFIG_SIZE_AND_VERSION(iParamPort);
iParamPort.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem negotiating with output port %d ", iNodeTypeId, iOutputPortIndex));
return false;
}
// let the component decide num output buffers
iNumOutputBuffers = iParamPort.nBufferCountActual;
//check the number
if (iNumOutputBuffers < iParamPort.nBufferCountMin)
iNumOutputBuffers = iParamPort.nBufferCountMin;
// set the number ourselves
if (iOMXComponentSupportsExternalOutputBufferAlloc && (iParamPort.nBufferCountMin < NUMBER_OUTPUT_BUFFER))
{
iNumOutputBuffers = NUMBER_OUTPUT_BUFFER;
}
iParamPort.nBufferCountActual = iNumOutputBuffers;
// set the output (target) bitrate, framerate, width/height etc.
iParamPort.format.video.nFrameWidth = iVideoEncodeParam.iFrameWidth[0];
iParamPort.format.video.nFrameHeight = iVideoEncodeParam.iFrameHeight[0];
// Q16 value, cast after the shift to preserve the accuracy.
iParamPort.format.video.xFramerate = (uint32)(iVideoEncodeParam.iFrameRate[0] * (1 << 16));
iParamPort.format.video.nBitrate = iVideoEncodeParam.iBitRate[0];
iParamPort.format.video.eColorFormat = OMX_COLOR_FormatUnused;
if (iOutFormat == PVMF_MIME_M4V)
{
iParamPort.format.video.eCompressionFormat = OMX_VIDEO_CodingMPEG4;
}
else if (iOutFormat == PVMF_MIME_H2631998 ||
iOutFormat == PVMF_MIME_H2632000)
{
iParamPort.format.video.eCompressionFormat = OMX_VIDEO_CodingH263;
}
else if (iOutFormat == PVMF_MIME_H264_VIDEO_RAW ||
iOutFormat == PVMF_MIME_H264_VIDEO_MP4)
{
iParamPort.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
}
else
{
iParamPort.format.video.eCompressionFormat = OMX_VIDEO_CodingAutoDetect;
}
CONFIG_SIZE_AND_VERSION(iParamPort);
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem setting parameters in output port %d ", iNodeTypeId, iOutputPortIndex));
return false;
}
//ask for the calculated buffer size
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamPortDefinition, &iParamPort);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem negotiating with output port %d ", iNodeTypeId, iOutputPortIndex));
return false;
}
iOMXComponentOutputBufferSize = iParamPort.nBufferSize;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Outport buffers %d size %d", iNodeTypeId, iNumOutputBuffers, iOMXComponentOutputBufferSize));
CONFIG_SIZE_AND_VERSION(InputRotationType);
InputRotationType.nPortIndex = iInputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexConfigCommonRotate, &InputRotationType);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem getting OMX_IndexConfigCommonRotate param ", iNodeTypeId));
}
//Set the OMX_CONFIG_ROTATIONTYPE parameters
InputRotationType.nRotation = ((iVideoInputFormat.iFrameOrientation == 1) ? 180 : 0);
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexConfigCommonRotate, &InputRotationType);
if (Err != OMX_ErrorNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::NegotiateVideoComponentParameters() Problem setting OMX_IndexConfigCommonRotate param ", iNodeTypeId));
}
// now call codec specific parameter setting
bool status = true;
if (iOutFormat == PVMF_MIME_M4V)
{
status = SetMP4EncoderParameters();
}
else if (iOutFormat == PVMF_MIME_H2631998 ||
iOutFormat == PVMF_MIME_H2632000)
{
status = SetH263EncoderParameters();
}
else if (iOutFormat == PVMF_MIME_H264_VIDEO_RAW ||
iOutFormat == PVMF_MIME_H264_VIDEO_MP4)
{
status = SetH264EncoderParameters();
}
return status;
}
bool PVMFOMXEncNode::SetMP4EncoderParameters()
{
OMX_ERRORTYPE Err = OMX_ErrorNone;
OMX_VIDEO_PARAM_MPEG4TYPE Mpeg4Type;
OMX_VIDEO_PARAM_BITRATETYPE BitRateType;
OMX_VIDEO_PARAM_QUANTIZATIONTYPE QuantParam;
OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE ErrCorrType;
// DV: VALUES HERE ARE FOR THE MOST PART HARDCODED BASED ON PV DEFAULTS
OMX_VIDEO_PARAM_MOTIONVECTORTYPE MotionVector;
OMX_VIDEO_PARAM_INTRAREFRESHTYPE RefreshParam;
CONFIG_SIZE_AND_VERSION(Mpeg4Type);
Mpeg4Type.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoMpeg4, &Mpeg4Type);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//Set the OMX_VIDEO_PARAM_MPEG4TYPE parameters
Mpeg4Type.nPortIndex = iOutputPortIndex;
// extra parameters - hardcoded
Mpeg4Type.nSliceHeaderSpacing = 0;
Mpeg4Type.bSVH = OMX_FALSE; //((iEncoderParam.iContentType == EI_H263)? true: false);
Mpeg4Type.bGov = OMX_FALSE; // disable or enable GOV header
// extra parameters - hardcoded
Mpeg4Type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
// params based on iFrame interval
if (iVideoEncodeParam.iIFrameInterval == -1) // encode only one frame
{
Mpeg4Type.nPFrames = 0xFFFFFFFF;
}
else if (iVideoEncodeParam.iIFrameInterval == 0) // no P frames
{
Mpeg4Type.nPFrames = 0;
Mpeg4Type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; // maps to only supporting I-frames
}
else
{
Mpeg4Type.nPFrames = (OMX_U32)(iVideoEncodeParam.iIFrameInterval * iVideoEncodeParam.iFrameRate[0] - 1);
}
// extra parameters - hardcoded
Mpeg4Type.nBFrames = 0;
Mpeg4Type.nIDCVLCThreshold = 0;
Mpeg4Type.bACPred = OMX_TRUE;
Mpeg4Type.nMaxPacketSize = iVideoEncodeParam.iPacketSize;
Mpeg4Type.nTimeIncRes = 1000; // (in relation to (should be higher than) frame rate )
Mpeg4Type.nHeaderExtension = 0;
Mpeg4Type.bReversibleVLC = ((iVideoEncodeParam.iRVLCEnable == true) ? OMX_TRUE : OMX_FALSE);
switch (iVideoEncodeParam.iProfileLevel)
{
case EI_SIMPLE_LEVEL0:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level0;
break;
case EI_SIMPLE_LEVEL1:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level1;
break;
case EI_SIMPLE_LEVEL2:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level2;
break;
case EI_SIMPLE_LEVEL3:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level3;
break;
case EI_CORE_LEVEL1:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileCore;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level1;
break;
case EI_CORE_LEVEL2:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileCore;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level2;
break;
case EI_SIMPLE_SCALABLE_LEVEL0:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimpleScalable;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level0;
break;
case EI_SIMPLE_SCALABLE_LEVEL1:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimpleScalable;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level1;
break;
case EI_SIMPLE_SCALABLE_LEVEL2:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileSimpleScalable;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level2;
break;
case EI_CORE_SCALABLE_LEVEL1:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileCoreScalable;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level1;
break;
case EI_CORE_SCALABLE_LEVEL2:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileCoreScalable;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level2;
break;
case EI_CORE_SCALABLE_LEVEL3:
Mpeg4Type.eProfile = OMX_VIDEO_MPEG4ProfileCoreScalable;
Mpeg4Type.eLevel = OMX_VIDEO_MPEG4Level3;
break;
}
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoMpeg4, &Mpeg4Type);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//OMX_VIDEO_PARAM_BITRATETYPE Settings
CONFIG_SIZE_AND_VERSION(BitRateType);
BitRateType.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoBitrate, &BitRateType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//Set the parameters now
BitRateType.nPortIndex = iOutputPortIndex;
BitRateType.eControlRate = static_cast<OMX_VIDEO_CONTROLRATETYPE>(iVideoEncodeParam.iRateControlType);
BitRateType.nTargetBitrate = iVideoEncodeParam.iBitRate[0];
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoBitrate, &BitRateType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//OMX_VIDEO_PARAM_QUANTIZATIONTYPE Settings
if (BitRateType.eControlRate == OMX_Video_ControlRateDisable)
{
CONFIG_SIZE_AND_VERSION(QuantParam);
QuantParam.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoQuantization, &QuantParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//Set the parameters now
QuantParam.nPortIndex = iOutputPortIndex;
QuantParam.nQpI = DEFAULT_OMX_MP4ENC_QPI;
QuantParam.nQpP = DEFAULT_OMX_MP4ENC_QPP;
QuantParam.nQpB = DEFAULT_OMX_MP4ENC_QPB;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoQuantization, &QuantParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
}
//OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE Settings (For streaming/2-way)
CONFIG_SIZE_AND_VERSION(ErrCorrType);
ErrCorrType.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoErrorCorrection, &ErrCorrType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//Set the parameters now
ErrCorrType.nPortIndex = iOutputPortIndex;
ErrCorrType.bEnableDataPartitioning = ((iVideoEncodeParam.iDataPartitioning == true) ? OMX_TRUE : OMX_FALSE);
ErrCorrType.bEnableResync = ((iVideoEncodeParam.iResyncMarker == true) ? OMX_TRUE : OMX_FALSE);
// extra parameters - hardcoded
ErrCorrType.bEnableHEC = OMX_FALSE;
ErrCorrType.nResynchMarkerSpacing = iVideoEncodeParam.iPacketSize;
ErrCorrType.bEnableRVLC = ((iVideoEncodeParam.iRVLCEnable == true) ? OMX_TRUE : OMX_FALSE); // corresponds to encode param rvlcEnable
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoErrorCorrection, &ErrCorrType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//OMX_VIDEO_PARAM_MOTIONVECTORTYPE Settings
CONFIG_SIZE_AND_VERSION(MotionVector);
MotionVector.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoMotionVector, &MotionVector);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
MotionVector.nPortIndex = iOutputPortIndex;
// extra parameters - hardcoded
MotionVector.sXSearchRange = iVideoEncodeParam.iSearchRange;
MotionVector.sYSearchRange = iVideoEncodeParam.iSearchRange;
MotionVector.bFourMV = ((iVideoEncodeParam.iMV8x8 == true) ? OMX_TRUE : OMX_FALSE);
MotionVector.eAccuracy = ((iVideoEncodeParam.iMVHalfPel == true) ? OMX_Video_MotionVectorHalfPel : OMX_Video_MotionVectorPixel);
MotionVector.bUnrestrictedMVs = OMX_TRUE;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoMotionVector, &MotionVector);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
//OMX_VIDEO_PARAM_INTRAREFRESHTYPE Settings
CONFIG_SIZE_AND_VERSION(RefreshParam);
RefreshParam.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoIntraRefresh, &RefreshParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
// extra parameters - hardcoded based on PV defaults
RefreshParam.nPortIndex = iOutputPortIndex;
RefreshParam.eRefreshMode = OMX_VIDEO_IntraRefreshBoth;
RefreshParam.nCirMBs = iVideoEncodeParam.iNumIntraMBRefresh;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoIntraRefresh, &RefreshParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetMP4EncoderParameters Parameter Invalid", iNodeTypeId));
}
return true;
}
bool PVMFOMXEncNode::SetH263EncoderParameters()
{
OMX_ERRORTYPE Err = OMX_ErrorNone;
OMX_VIDEO_PARAM_H263TYPE H263Type;
OMX_VIDEO_PARAM_BITRATETYPE BitRateType;
OMX_VIDEO_PARAM_QUANTIZATIONTYPE QuantParam;
OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE ErrCorrType;
// DV: VALUES HERE ARE FOR THE MOST PART HARDCODED BASED ON PV DEFAULTS
OMX_VIDEO_PARAM_MOTIONVECTORTYPE MotionVector;
OMX_VIDEO_PARAM_INTRAREFRESHTYPE RefreshParam;
CONFIG_SIZE_AND_VERSION(H263Type);
H263Type.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoH263, &H263Type);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_GetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoH263));
}
//Set the OMX_VIDEO_PARAM_H263TYPE parameters
//DV: Here, we only set the nPFrames and AllowedFrameTypes, i.e. iIFrameInterval related variables
H263Type.nPortIndex = iOutputPortIndex;
H263Type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
if (iVideoEncodeParam.iIFrameInterval == -1) // encode only one I frame followed by P frames
{
H263Type.nPFrames = 0xFFFFFFFF;
}
else if (iVideoEncodeParam.iIFrameInterval == 0) // no P frames
{
H263Type.nPFrames = 0;
H263Type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; // maps to only supporting I-frames
}
else
{
H263Type.nPFrames = (OMX_U32)(iVideoEncodeParam.iIFrameInterval * iVideoEncodeParam.iFrameRate[0] - 1);
}
// extra parameters - hardcoded
H263Type.nBFrames = 0;
H263Type.eProfile = OMX_VIDEO_H263ProfileBaseline;
// the supported level is up to OMX_VIDEO_H263Level45
if (H263Type.eLevel > OMX_VIDEO_H263Level45)
{
H263Type.eLevel = OMX_VIDEO_H263Level45;
}
H263Type.bPLUSPTYPEAllowed = OMX_FALSE;
H263Type.bForceRoundingTypeToZero = OMX_FALSE;
H263Type.nPictureHeaderRepetition = 0;
H263Type.nGOBHeaderInterval = 0;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoH263, &H263Type);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_SetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoH263));
}
//OMX_VIDEO_PARAM_BITRATETYPE Settings
CONFIG_SIZE_AND_VERSION(BitRateType);
BitRateType.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoBitrate, &BitRateType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_GetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoBitrate));
}
//Set the parameters now
BitRateType.nPortIndex = iOutputPortIndex;
BitRateType.eControlRate = static_cast<OMX_VIDEO_CONTROLRATETYPE>(iVideoEncodeParam.iRateControlType);
BitRateType.nTargetBitrate = iVideoEncodeParam.iBitRate[0];
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoBitrate, &BitRateType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_SetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoBitrate));
}
//OMX_VIDEO_PARAM_QUANTIZATIONTYPE Settings
if (BitRateType.eControlRate == OMX_Video_ControlRateDisable)
{
CONFIG_SIZE_AND_VERSION(QuantParam);
QuantParam.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoQuantization, &QuantParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_GetParameter() failed for index(0x%x", iNodeTypeId, OMX_IndexParamVideoQuantization));
}
//Set the parameters now
QuantParam.nPortIndex = iOutputPortIndex;
QuantParam.nQpI = DEFAULT_OMX_MP4ENC_QPI;
QuantParam.nQpP = DEFAULT_OMX_MP4ENC_QPP;
QuantParam.nQpB = DEFAULT_OMX_MP4ENC_QPB;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoQuantization, &QuantParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_SetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoQuantization));
}
}
//OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE Settings (For streaming/2-way)
CONFIG_SIZE_AND_VERSION(ErrCorrType);
ErrCorrType.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoErrorCorrection, &ErrCorrType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_GetParameter() faile for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoErrorCorrection));
}
//Set the parameters now
ErrCorrType.nPortIndex = iOutputPortIndex;
//if (iVideoEncodeParam.iContentType == EI_M4V_STREAMING)
//{
// ErrCorrType.bEnableDataPartitioning = OMX_TRUE;
//}
//else
//{
// ErrCorrType.bEnableDataPartitioning = OMX_FALSE;
//}
ErrCorrType.bEnableHEC = OMX_FALSE;
ErrCorrType.bEnableResync = OMX_FALSE;
ErrCorrType.nResynchMarkerSpacing = 0;
ErrCorrType.bEnableRVLC = OMX_FALSE; // corresponds to encode param rvlcEnable
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoErrorCorrection, &ErrCorrType);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_SetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoErrorCorrection));
}
//OMX_VIDEO_PARAM_MOTIONVECTORTYPE Settings
CONFIG_SIZE_AND_VERSION(MotionVector);
MotionVector.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoMotionVector, &MotionVector);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_GetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoMotionVector));
}
// extra parameters - hardcoded
MotionVector.sXSearchRange = iVideoEncodeParam.iSearchRange;
MotionVector.sYSearchRange = iVideoEncodeParam.iSearchRange;
MotionVector.bFourMV = OMX_FALSE;
MotionVector.eAccuracy = ((iVideoEncodeParam.iMVHalfPel == true) ? OMX_Video_MotionVectorHalfPel : OMX_Video_MotionVectorPixel);
MotionVector.bUnrestrictedMVs = OMX_FALSE;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoMotionVector, &MotionVector);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_SetParameter() failed for index(0x%x", iNodeTypeId, OMX_IndexParamVideoMotionVector));
}
//OMX_VIDEO_PARAM_INTRAREFRESHTYPE Settings
CONFIG_SIZE_AND_VERSION(RefreshParam);
RefreshParam.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoIntraRefresh, &RefreshParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_GetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoIntraRefresh));
}
// extra parameters - hardcoded based on PV defaults
RefreshParam.nPortIndex = iOutputPortIndex;
RefreshParam.eRefreshMode = OMX_VIDEO_IntraRefreshBoth;
RefreshParam.nCirMBs = iVideoEncodeParam.iNumIntraMBRefresh;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoIntraRefresh, &RefreshParam);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH263EncoderParameters OMX_SetParameter() failed for index(0x%x)", iNodeTypeId, OMX_IndexParamVideoIntraRefresh));
}
return true;
}
bool PVMFOMXEncNode::SetH264EncoderParameters()
{
OMX_ERRORTYPE Err = OMX_ErrorNone;
OMX_VIDEO_PARAM_AVCTYPE H264Type;
OMX_VIDEO_PARAM_BITRATETYPE BitRateType;
OMX_VIDEO_PARAM_QUANTIZATIONTYPE QuantParam;
// to be refined
OMX_VIDEO_PARAM_MOTIONVECTORTYPE MotionVector;
OMX_VIDEO_PARAM_INTRAREFRESHTYPE RefreshParam;
OMX_VIDEO_PARAM_VBSMCTYPE VbsmcType;
CONFIG_SIZE_AND_VERSION(H264Type);
H264Type.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoAvc, &H264Type);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH264EncoderParameters Parameter Invalid", iNodeTypeId));
}
H264Type.nPortIndex = iOutputPortIndex;
H264Type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
if (iVideoEncodeParam.iIFrameInterval == -1) // encode only one I frame followed by P frames
{
H264Type.nPFrames = 0xFFFFFFFF;
}
else if (iVideoEncodeParam.iIFrameInterval == 0) // no P frames
{
H264Type.nPFrames = 0;
H264Type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; // maps to only supporting I-frames
}
else
{
H264Type.nPFrames = (OMX_U32)(iVideoEncodeParam.iIFrameInterval * iVideoEncodeParam.iFrameRate[0] - 1);
}
// extra parameters -hardcoded
H264Type.nSliceHeaderSpacing = 0;
H264Type.nBFrames = 0;
H264Type.bUseHadamard = OMX_TRUE;
H264Type.nRefFrames = 1;
H264Type.nRefIdx10ActiveMinus1 = 0;
H264Type.nRefIdx11ActiveMinus1 = 0;
H264Type.bEnableUEP = OMX_FALSE;
H264Type.bEnableFMO = OMX_FALSE;
H264Type.bEnableASO = OMX_FALSE;
H264Type.bEnableRS = OMX_FALSE;
//H264Type.eProfile = OMX_VIDEO_AVCProfileBaseline;
//H264Type.eLevel = OMX_VIDEO_AVCLevel1b;
H264Type.bFrameMBsOnly = OMX_TRUE;
H264Type.bMBAFF = OMX_FALSE;
H264Type.bEntropyCodingCABAC = OMX_FALSE;
H264Type.bWeightedPPrediction = OMX_FALSE;
H264Type.bconstIpred = OMX_FALSE;
H264Type.bDirect8x8Inference = OMX_FALSE;
H264Type.bDirectSpatialTemporal = OMX_FALSE;
H264Type.nCabacInitIdc = 0;
H264Type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
Err = OMX_SetParameter(iOMXEncoder, OMX_IndexParamVideoAvc, &H264Type);
if (OMX_ErrorNone != Err)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVMFOMXEncNode-%s::SetH264EncoderParameters Parameter Invalid", iNodeTypeId));
}
//OMX_VIDEO_PARAM_BITRATETYPE Settings
CONFIG_SIZE_AND_VERSION(BitRateType);
BitRateType.nPortIndex = iOutputPortIndex;
Err = OMX_GetParameter(iOMXEncoder, OMX_IndexParamVideoBitrate, &BitRateType);