blob: 90a60c7539a6bc24776a046257242af2f0a0fe2b [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.
* -------------------------------------------------------------------
*/
/**
*
* @file pvmf_videoparser_node.cpp
* @brief Video parser node. Parses incoming video bitstream into frames.
*
*/
#include "pvmf_videoparser_port.h"
#include "pvmf_videoparser_node.h"
#include "pvlogger.h"
#include "pvmf_media_cmd.h"
#include "pvmf_media_msg_format_ids.h"
#define VOP_START_BYTE_1 0x00
#define VOP_START_BYTE_2 0x00
#define VOP_START_BYTE_3 0x01
#define VOP_START_BYTE_4 0xB6
#define GOV_START_BYTE_4 0xB3
#define H263_START_BYTE_1 0x00
#define H263_START_BYTE_2 0x00
#define H263_START_BYTE_3 0x80
#define H263_START_BYTE_3_MASK 0xFC
#define VOL_HEADER_START_BYTE_1 0x00
#define VOL_HEADER_START_BYTE_2 0x00
#define VOL_HEADER_START_BYTE_3 0x01
#define VOL_HEADER_START_BYTE_4 0xB0
static const uint8 M4vScLlookup[] =
{
0x18 /*00000=11000*/, 0x19/*00001=11001*/, 0x18/*00010=11000*/, 0x19/*00011=11001*/,
0x0C /*00100=01100*/, 0x0C/*00101=01100*/, 0x0C/*00110=01100*/, 0x0C/*00111=01100*/,
0x0C /*01000=01100*/, 0x19/*01001=11001*/, 0x18/*01010=11000*/, 0x19/*01011=11001*/,
0x0C /*01100=01100*/ , 0x0C/*01101=01100*/, 0x0C/*01110=01100*/, 0x0C/*01111=01100*/,
0x18 /*10000=11000*/, 0x19/*10001=11001*/, 0x18/*10010=11000*/, 0x19/*10011=11001*/,
0x18 /*10100=11000*/ , 0x19/*10101=11001*/, 0x18/*10110=11000*/, 0x19/*10111=11001*/,
0x18 /*11000=11000*/, 0x19/*11001=11001*/, 0x18/*11010=11000*/ , 0x19/*11011=11001*/,
0x18 /*11100=11000*/ , 0x19/*11101=11001*/, 0x18/*11110=11000*/, 0x19/*11111=11001*/
};
#include "oscl_dll.h"
// Define entry point for this DLL
OSCL_DLL_ENTRY_POINT_DEFAULT()
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFNodeInterface* PVMFVideoParserNode::Create(uint8* aFormatSpecificInfo, uint32 aFormatSpecificInfoLen)
{
PVMFVideoParserNode* aRet = OSCL_NEW(PVMFVideoParserNode, ());
if (aRet)
{
aRet->ConstructL(aFormatSpecificInfo, aFormatSpecificInfoLen);
aRet->AddToScheduler();
}
return aRet;
}
////////////////////////////////////////////////////////////////////////////
PVMFVideoParserNode::PVMFVideoParserNode() : OsclActiveObject(OsclActiveObject::EPriorityNominal, "VideoParserNode"),
iCmdIdCounter(0),
iInputPort(NULL),
iOutputPort(NULL),
iFormatType(PVMF_MIME_FORMAT_UNKNOWN),
iFormatTypeInteger(PV_CODEC_TYPE_NONE),
iFirstFrameFound(false),
iVidParserMemoryPool(NULL),
iMediaDataAlloc(NULL),
iMediaDataMemPool(MAX_VIDEO_FRAMES, PVVIDEOPARSER_MEDIADATA_SIZE),
iLogger(NULL),
iFormatSpecificInfo(NULL),
iFormatSpecificInfoLen(0)
{
SetState(EPVMFNodeCreated);
// Create a 256 element table with number of 1 bits in each index value
// Ex: 0->0, 1->1, 2->1, 3->2, 4->1, 5->2, 6->2
for (int n = 0; n <= 0xFF; n++)
{
int m = n;
int cnt = 0;
while (m)
{
if (m & 0x1)
{
cnt++;
}
m >>= 1;
}
iNumOnes[n] = (uint8)cnt;
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFVideoParserNode::~PVMFVideoParserNode()
{
if (iFormatSpecificInfo)
{
OSCL_FREE(iFormatSpecificInfo);
iFormatSpecificInfo = NULL;
}
if (iMediaDataAlloc)
{
OSCL_DELETE(iMediaDataAlloc);
iMediaDataAlloc = NULL;
}
if (iVidParserMemoryPool)
{
OSCL_DELETE(iVidParserMemoryPool);
iVidParserMemoryPool = NULL;
}
}
////////////////////////////////////////////////////////////////////////////
void PVMFVideoParserNode::ConstructL(uint8* aFormatSpecificInfo, uint32 aFormatSpecificInfoLen)
{
iVidParserMemoryPool = OSCL_NEW(OsclMemPoolFixedChunkAllocator, (MAX_VIDEO_FRAMES));
if (iVidParserMemoryPool == NULL)
{
OSCL_LEAVE(PVMFErrNoMemory);
}
iVidParserMemoryPool->enablenullpointerreturn();
iMediaDataAlloc = OSCL_NEW(PVMFSimpleMediaBufferCombinedAlloc, (iVidParserMemoryPool));
if (iMediaDataAlloc == NULL)
{
OSCL_LEAVE(PVMFErrNoMemory);
}
if (aFormatSpecificInfo && aFormatSpecificInfoLen)
{
iFormatSpecificInfo = (uint8*)OSCL_MALLOC(aFormatSpecificInfoLen);
if (iFormatSpecificInfo == NULL)
{
OSCL_LEAVE(PVMFErrNoMemory);
}
oscl_memcpy(iFormatSpecificInfo, aFormatSpecificInfo, aFormatSpecificInfoLen);
iFormatSpecificInfoLen = aFormatSpecificInfoLen;
}
}
////////////////////////////////////////////////////////////////////////////
void PVMFVideoParserNode::DoCancel()
{
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVMFVideoParserNode::GetCapability(PVMFNodeCapability& aNodeCapability)
{
aNodeCapability.iCanSupportMultipleInputPorts = false;
aNodeCapability.iCanSupportMultipleOutputPorts = false;
aNodeCapability.iHasMaxNumberOfPorts = true;
aNodeCapability.iMaxNumberOfPorts = 2;
aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_M4V);
aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_H2631998);
aNodeCapability.iInputFormatCapability.push_back(PVMF_MIME_H2632000);
aNodeCapability.iOutputFormatCapability.push_back(PVMF_MIME_M4V);
aNodeCapability.iOutputFormatCapability.push_back(PVMF_MIME_H2631998);
aNodeCapability.iOutputFormatCapability.push_back(PVMF_MIME_H2632000);
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFPortIter* PVMFVideoParserNode::GetPorts(const PVMFPortFilter* aFilter)
{
OSCL_UNUSED_ARG(aFilter);
return NULL;
}
PVMFStatus PVMFVideoParserNode::ThreadLogon()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFVideoParserNode:ThreadLogon"));
switch (iInterfaceState)
{
case EPVMFNodeCreated:
if (!IsAdded())
{
AddToScheduler();
}
iLogger = PVLogger::GetLoggerObject("PVMFVideoParserNode");
SetState(EPVMFNodeIdle);
return PVMFSuccess;
default:
return PVMFErrInvalidState;
}
}
PVMFStatus PVMFVideoParserNode::ThreadLogoff()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFVideoParserNode:ThreadLogoff"));
switch (iInterfaceState)
{
case EPVMFNodeIdle:
if (IsAdded())
{
RemoveFromScheduler();
}
iLogger = NULL;
SetState(EPVMFNodeCreated);
return PVMFSuccess;
default:
return PVMFErrInvalidState;
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::QueryUUID(PVMFSessionId s, const PvmfMimeString& aMimeType,
Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids,
bool aExactUuidsOnly,
const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aMimeType);
OSCL_UNUSED_ARG(aUuids);
OSCL_UNUSED_ARG(aExactUuidsOnly);
OSCL_UNUSED_ARG(aContext);
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_QUERY_UUID, s, NULL, aContext, NULL);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::QueryInterface(PVMFSessionId s, const PVUuid& aUuid,
PVInterface*& aInterfacePtr,
const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aUuid);
OSCL_UNUSED_ARG(aInterfacePtr);
OSCL_UNUSED_ARG(aContext);
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_QUERY_INTERFACE, s, NULL, aContext, NULL);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::RequestPort(PVMFSessionId s, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::RequestPort called, format %s, port tag %d\n", aPortConfig ? aPortConfig->get_cstr() : "\"\"", aPortTag));
// Copy property
VideoParserPortProperty* property = OSCL_NEW(VideoParserPortProperty, ());
if (property != NULL)
{
property->porttag = aPortTag;
property->format = aPortConfig->get_str();
//property->mimetype = *aPortConfig;
}
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_REQUESTPORT, s, NULL, aContext, property);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::ReleasePort(PVMFSessionId s, PVMFPortInterface& aPort, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::ReleasePort\n"));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_RELEASEPORT, s, &aPort, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Init(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Init, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_INIT, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Start(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Start, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_START, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Prepare(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Prepare, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_PREPARE, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Flush(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Flush, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_FLUSH, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Stop(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Stop, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_STOP, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Pause(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Pause, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_PAUSE, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::Reset(PVMFSessionId s, const OsclAny* aContext)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::Reset, state %d\n", iInterfaceState));
return AddCmdToQueue(PVMFVIDEOPARSER_NODE_CMD_RESET, s, NULL, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::CancelAllCommands(PVMFSessionId s, const OsclAny* aContextData)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::CancelAllCommands, state %d\n", iInterfaceState));
OSCL_UNUSED_ARG(s);
OSCL_UNUSED_ARG(aContextData);
OSCL_LEAVE(PVMFErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVMFVideoParserNode::CancelCommand(PVMFSessionId s, PVMFCommandId aCmdId, const OsclAny* aContextData)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::CancelCommand, state %d\n", iInterfaceState));
OSCL_UNUSED_ARG(s);
OSCL_UNUSED_ARG(aCmdId);
OSCL_UNUSED_ARG(aContextData);
OSCL_LEAVE(PVMFErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVMFVideoParserNode::AddCmdToQueue(PVMFVideoParserNodeCmdType aType, PVMFSessionId s, PVMFPortInterface* aPort, const OsclAny* aContext, OsclAny* aData)
{
PVMFVideoParserNodeCmd cmd;
cmd.iSession = s;
cmd.iType = aType;
cmd.iPort = aPort;
cmd.iContext = (OsclAny *) aContext;
cmd.iData = aData;
cmd.iId = iCmdIdCounter;
++iCmdIdCounter;
iCmdQueue.push(cmd);
RunIfNotReady();
return cmd.iId;
}
////////////////////////////////////////////////////////////////////////////
void PVMFVideoParserNode::Run()
{
PVMFPortInterface *port = NULL;
OsclAny *eventData = NULL;
if (!iCmdQueue.empty())
{
PVMFStatus status = PVMFSuccess;
PVMFVideoParserNodeCmd cmd(iCmdQueue.top());
iCmdQueue.pop();
switch (cmd.iType)
{
case PVMFVIDEOPARSER_NODE_CMD_INIT:
{
if (iInterfaceState == EPVMFNodeIdle)
{
OsclSharedPtr<PVMFMediaDataImpl> tempImpl;
tempImpl = iMediaDataAlloc->allocate(MAX_VIDEO_FRAME_PARSE_SIZE);
if (!tempImpl || !tempImpl->getCapacity())
{
SetState(EPVMFNodeIdle);
status = PVMFErrNoMemory;
}
else
{
tempImpl.Unbind();
SetState(EPVMFNodeInitialized);
}
tempImpl.Unbind();
}
else
{
status = PVMFErrInvalidState;
}
}
break;
case PVMFVIDEOPARSER_NODE_CMD_PREPARE:
if (iInterfaceState != EPVMFNodeInitialized)
{
status = PVMFErrInvalidState;
}
else
{
SetState(EPVMFNodePrepared);
}
break;
case PVMFVIDEOPARSER_NODE_CMD_START:
if (iInterfaceState == EPVMFNodePaused || iInterfaceState == EPVMFNodePrepared)
{
status = HandleCmdStart(&cmd);
}
else
{
status = PVMFErrInvalidState;
}
break;
case PVMFVIDEOPARSER_NODE_CMD_STOP:
if (iInterfaceState == EPVMFNodeStarted)
{
SetState(EPVMFNodePrepared);
}
else
{
status = PVMFErrInvalidState;
}
break;
case PVMFVIDEOPARSER_NODE_CMD_RESET:
switch (iInterfaceState)
{
case EPVMFNodeCreated:
case EPVMFNodeIdle:
case EPVMFNodeInitialized:
case EPVMFNodePrepared:
{
if (iInputPort)
{
OSCL_DELETE(iInputPort);
iInputPort = NULL;
}
if (iOutputPort)
{
OSCL_DELETE(iOutputPort);
iOutputPort = NULL;
}
iVideoFrame.Unbind();
while (iPortActivityQueue.size())
{
PVMFPortActivity activity(iPortActivityQueue.front());
iPortActivityQueue.erase(&iPortActivityQueue.front());
}
while (iCmdQueue.size())
{
PVMFVideoParserNodeCmd cmd(iCmdQueue.top());
iCmdQueue.pop();
PVMFCmdResp resp(cmd.iId, cmd.iContext, PVMFFailure, eventData);
ReportCmdCompleteEvent(cmd.iSession, resp);
}
SetState(EPVMFNodeIdle);
status = PVMFSuccess;
}
break;
default:
status = PVMFErrInvalidState;
break;
}
break;
case PVMFVIDEOPARSER_NODE_CMD_PAUSE:
if (iInterfaceState == EPVMFNodeStarted)
{
SetState(EPVMFNodePaused);
}
else
{
status = PVMFErrInvalidState;
}
break;
case PVMFVIDEOPARSER_NODE_CMD_REQUESTPORT:
switch (iInterfaceState)
{
case EPVMFNodeInitialized:
case EPVMFNodePrepared:
case EPVMFNodeStarted:
{
port = HandleCmdRequestPort(&cmd, status);
eventData = (OsclAny*)port;
if (cmd.iData != NULL)
{
OSCL_DELETE(((VideoParserPortProperty*)cmd.iData)); // Allocated dynamically in RequestPort()
}
}
break;
default:
status = PVMFErrInvalidState;
break;
}
break;
case PVMFVIDEOPARSER_NODE_CMD_RELEASEPORT:
{
status = HandleCmdReleasePort(&cmd);
}
break;
case PVMFVIDEOPARSER_NODE_CMD_FLUSH:
break;
case PVMFVIDEOPARSER_NODE_CMD_QUERY_INTERFACE:
case PVMFVIDEOPARSER_NODE_CMD_QUERY_UUID:
default:
{
status = PVMFErrNotSupported;
}
break;
}
PVMFCmdResp resp(cmd.iId, cmd.iContext, status, eventData);
ReportCmdCompleteEvent(cmd.iSession, resp);
}
if (!iPortActivityQueue.empty() && (iInterfaceState == EPVMFNodeStarted))
{
// If the port activity cannot be processed because a port is
// busy, discard the activity and continue to process the next
// activity in queue until getting to one that can be processed.
while (!iPortActivityQueue.empty())
{
if (ProcessPortActivity())
{
break; //processed a port
}
}
}
if (!iPortActivityQueue.empty() || !iCmdQueue.empty())
{
// Run again if there are more commands to process
RunIfNotReady();
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFVideoParserNode::HandleCmdStart(PVMFVideoParserNodeCmd* aCmd)
{
OSCL_UNUSED_ARG(aCmd);
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::HandleCmdStart, input port %x, output port %x\n", iInputPort, iOutputPort));
SetState(EPVMFNodeStarted);
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFPortInterface *PVMFVideoParserNode::HandleCmdRequestPort(PVMFVideoParserNodeCmd* aCmd, PVMFStatus &aStatus)
{
VideoParserPortProperty *property = (VideoParserPortProperty*)aCmd->iData;
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::HandleCmdRequestPort\n"));
aStatus = PVMFSuccess;
if (!property)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, no port property\n"));
aStatus = PVMFErrNoMemory;
return NULL;
}
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::HandleCmdRequestPort, port type %d, format %s\n", property->porttag, (property->format).getMIMEStrPtr()));
if (!((property->format == PVMF_MIME_M4V) || (property->format == PVMF_MIME_H2631998) || (property->format == PVMF_MIME_H2632000)))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, unknown format\n"));
aStatus = PVMFErrArgument;
return NULL;
}
switch (property->porttag)
{
case PVMF_VIDEOPARSER_NODE_PORT_TYPE_SINK:
if (iOutputPort && iFormatType != property->format)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, output format (%s) doesn't match input format (%s)\n", iFormatType.getMIMEStrPtr(), (property->format).getMIMEStrPtr()));
//Output format doesn't match output format.
aStatus = PVMFFailure;
return NULL;
}
if (iInputPort)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, input port already exists\n"));
// it's been taken for now, so reject this request
aStatus = PVMFFailure;
return NULL;
}
// Create and configure output port
iInputPort = OSCL_NEW(PVMFVideoParserPort, (property->porttag,
property->format,
this,
iFormatSpecificInfo, iFormatSpecificInfoLen,
OSCL_STATIC_CAST(char*, "PVMFVideoParserInputPort")));
if (!iInputPort)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, unable to allocate input port\n"));
aStatus = PVMFErrNoMemory;
return NULL;
}
iFormatType = property->format;
if ((iFormatType == PVMF_MIME_H2631998) || (iFormatType == PVMF_MIME_H2632000))
{
iFormatTypeInteger = PV_VID_TYPE_H263;
}
else if (iFormatType == PVMF_MIME_M4V)
{
iFormatTypeInteger = PV_VID_TYPE_MPEG4;
}
return iInputPort;
case PVMF_VIDEOPARSER_NODE_PORT_TYPE_SOURCE:
if (iInputPort)
{
if (iFormatType != property->format)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, input format (%s) doesn't match output format (%s)\n", iFormatType.getMIMEStrPtr(), (property->format).getMIMEStrPtr()));
//Input format doesn't match output format.
aStatus = PVMFFailure;
return NULL;
}
}
if (iOutputPort)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, output port already exists\n"));
// it's been taken for now, so reject this request
aStatus = PVMFFailure;
return NULL;
}
iOutputPort = OSCL_NEW(PVMFVideoParserPort, (property->porttag,
property->format,
this,
iFormatSpecificInfo, iFormatSpecificInfoLen,
OSCL_STATIC_CAST(char*, "PVMFVideoParserOutputPort")));
if (!iOutputPort)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, unable to allocate output port\n"));
aStatus = PVMFErrNoMemory;
return NULL;
}
iFormatType = property->format;
if ((iFormatType == PVMF_MIME_H2631998) || (iFormatType == PVMF_MIME_H2632000))
{
iFormatTypeInteger = PV_VID_TYPE_H263;
}
else if (iFormatType == PVMF_MIME_M4V)
{
iFormatTypeInteger = PV_VID_TYPE_MPEG4;
}
return iOutputPort;
default:
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdStart, unknown port type\n"));
aStatus = PVMFErrNotSupported;
return NULL;
}
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PVMFVideoParserNode::HandleCmdReleasePort(PVMFVideoParserNodeCmd* aCmd)
{
if (aCmd->iPort == iInputPort)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_NOTICE, (0, "PVMFVideoParserNode::HandleCmdReleasePort, release input port\n"));
iInputPort->Disconnect();
OSCL_DELETE(iInputPort);
iInputPort = NULL;
return PVMFSuccess;
}
else if (aCmd->iPort == iOutputPort)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_NOTICE, (0, "PVMFVideoParserNode::HandleCmdReleasePort, release output port\n"));
iOutputPort->Disconnect();
OSCL_DELETE(iOutputPort);
iOutputPort = NULL;
return PVMFSuccess;
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::HandleCmdReleasePort, unknown port to release\n"));
return PVMFFailure;
}
}
////////////////////////////////////////////////////////////////////////////
void PVMFVideoParserNode::DataReceived(OsclSharedPtr<PVMFMediaMsg>& aMsg)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::DataReceived, state %d\n", iInterfaceState));
if (iInterfaceState == EPVMFNodeStarted)
{
PVMFSharedMediaDataPtr mediaData;
OsclRefCounterMemFrag memFrag;
OsclRefCounterMemFrag curVideoFrag;
convertToPVMFMediaData(mediaData, aMsg);
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::DataReceived, Num fragments=%d, filled size=%d\n", mediaData->getNumFragments(), mediaData->getFilledSize()));
for (uint32 i = 0; i < mediaData->getNumFragments(); i++)
{
mediaData->getMediaFragment(i, memFrag);
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFVideoParserNode::DataReceived, frag size %d, idx %d\n", memFrag.getMemFragSize(), i));
//Make sure it can fit in the video buffer.
if (memFrag.getMemFragSize() > MAX_VIDEO_FRAME_PARSE_SIZE)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::DataReceived, frag cannot fit into video buffer\n"));
continue;
}
if (FrameMarkerExists((uint8 *)memFrag.getMemFragPtr(), memFrag.getMemFragSize(), mediaData->getErrorsFlag()))
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_DEBUG, (0, "PVMFVideoParserNode::DataReceived, frame found\n"));
//If buffer exists then send it.
if (iVideoFrame.GetRep())
{
SendFrame();
}
}
//If data cannot fit in remaining buffer space, send current buffer regardless
if (iVideoFrame.GetRep() && (iVideoFrame->getFilledSize() + memFrag.getMemFragSize()) > iVideoFrame->getCapacity())
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_NOTICE, (0, "PVMFVideoParserNode::DataReceived, frag data cannot fit into remaining buffer space\n"));
SendFrame();
}
if (!iVideoFrame.GetRep())
{
OsclRefCounterMemFrag formatSpecificInfo;
OsclSharedPtr<PVMFMediaDataImpl> tempImpl;
// Drop rest of data if unable to allocate next buffer
tempImpl = iMediaDataAlloc->allocate(MAX_VIDEO_FRAME_PARSE_SIZE)
;
if (!tempImpl)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFVideoParserNode::DataReceived, unable to allocate media impl\n"));
return;
}
iVideoFrame = PVMFMediaData::createMediaData(tempImpl, &iMediaDataMemPool);
if (!iVideoFrame)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR,
(0, "PVMFVideoParserNode::DataReceived, unable to allocate media data\n"));
tempImpl.Unbind();
return;
}
iVideoFrame->setTimestamp(mediaData->getTimestamp());
iVideoFrame->setSeqNum(1);
mediaData->getFormatSpecificInfo(formatSpecificInfo);
iVideoFrame->setFormatSpecificInfo(formatSpecificInfo);
}
if (memFrag.getMemFragSize() > 0)
{
iVideoFrame->getMediaFragment(0, curVideoFrag);
oscl_memcpy((uint8 *)curVideoFrag.getMemFragPtr() + iVideoFrame->getFilledSize(), memFrag.getMemFragPtr(), memFrag.getMemFragSize());
iVideoFrame->setMediaFragFilledLen(0, iVideoFrame->getFilledSize() + memFrag.getMemFragSize());
}
}
}
}
////////////////////////////////////////////////////////////////////////////
void PVMFVideoParserNode::SendFrame()
{
OsclSharedPtr<PVMFMediaMsg> mediaMsg;
OsclSharedPtr<PVMFMediaDataImpl> mediaDataImpl;
if (iVideoFrame->getMediaDataImpl(mediaDataImpl))
{
mediaDataImpl->setMarkerInfo(1);
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFVideoParserNode::SendFrame() cannot get media data impl\n"));
}
convertToPVMFMediaMsg(mediaMsg, iVideoFrame);
PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVMFVideoParserNode::SendFrame, size %d\n", iVideoFrame->getFilledSize()));
if (iOutputPort) iOutputPort->QueueOutgoingMsg(mediaMsg);
iVideoFrame.Unbind();
}
////////////////////////////////////////////////////////////////////////////
bool PVMFVideoParserNode::FrameMarkerExists(uint8* aDataPtr,
int32 aDataSize,
uint32 aCrcError)
{
uint16 match_cnt = 0;
if (iFormatTypeInteger == PV_VID_TYPE_H263)
{
//If start of new frame.
if ((aDataSize > 2) &&
(aDataPtr[0] == H263_START_BYTE_1) &&
(aDataPtr[1] == H263_START_BYTE_2) &&
((aDataPtr[2] & H263_START_BYTE_3_MASK) == H263_START_BYTE_3))
{
return true;
}
}
else if (iFormatTypeInteger == PV_VID_TYPE_MPEG4)
{
if (!aCrcError)
{
if (aDataSize >= 3)
{
if ((aDataPtr[0] == VOP_START_BYTE_1) &&
(aDataPtr[1] == VOP_START_BYTE_2) &&
(aDataPtr[2] == VOP_START_BYTE_3))
{
return true;
}
}
}
else
{
if (aDataSize > 4)
{
int16 nBitsCorrected = 0;
//If start of new frame.
match_cnt = (uint16)(iNumOnes[(aDataPtr[0] ^ VOP_START_BYTE_1)] +
iNumOnes[(aDataPtr[1] ^ VOP_START_BYTE_2)]);
if (match_cnt > 2)
{
return false;
}
nBitsCorrected = match_cnt;
if (aDataPtr[2] & 0x80)
{
return false;
}
// Match the next 12 bits
match_cnt = (uint16)(iNumOnes[(aDataPtr[2] ^ VOP_START_BYTE_3)] +
iNumOnes[((aDataPtr[3] & 0xF8) ^ 0xB0)]);
if (match_cnt > 1)
{
return false;
}
nBitsCorrected = (int16)(nBitsCorrected + match_cnt);
uint8 c3 = 0, c4 = 0;
uint8 c = (uint8)(aDataPtr[3] & 0x07);
c3 = VOP_START_BYTE_4 & 0xF0;
c = (uint8)(c << 2 | aDataPtr[4] >> 6);
c = M4vScLlookup[c];
c3 |= (c >> 2);
c4 = (uint8)(aDataPtr[4] & 0x3F);
c4 |= (c << 6);
nBitsCorrected = (int16)(nBitsCorrected + iNumOnes[((c3^aDataPtr[3]) + (c4 ^ aDataPtr[4]))]);
aDataPtr[0] = VOP_START_BYTE_1;
aDataPtr[1] = VOP_START_BYTE_2;
aDataPtr[2] = VOP_START_BYTE_3;
aDataPtr[3] = c3;
aDataPtr[4] = c4;
return true;
}
}
}
else
{
// Unhandled codec type. Error
return false;
}
return false;
}
void PVMFVideoParserNode::HandlePortActivity(const PVMFPortActivity& aActivity)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "0x%x PVMFVideoParserNode::PortActivity: port=0x%x, type=%d",
this, aActivity.iPort, aActivity.iType));
switch (aActivity.iType)
{
case PVMF_PORT_ACTIVITY_OUTGOING_MSG:
if (ProcessOutgoingMsg(aActivity.iPort) != PVMFSuccess)
{
if (aActivity.iPort->OutgoingMsgQueueSize() == 1)
QueuePortActivity(aActivity);
}
break;
case PVMF_PORT_ACTIVITY_INCOMING_MSG:
if (ProcessIncomingMsg(aActivity.iPort) != PVMFSuccess)
{
if (aActivity.iPort->IncomingMsgQueueSize() == 1)
QueuePortActivity(aActivity);
}
break;
case PVMF_PORT_ACTIVITY_OUTGOING_QUEUE_READY:
if (aActivity.iPort->OutgoingMsgQueueSize() > 0)
{
RunIfNotReady();
}
break;
case PVMF_PORT_ACTIVITY_CONNECT:
//nothing needed.
break;
case PVMF_PORT_ACTIVITY_DISCONNECT:
//clear the node input queue when either port is disconnected.
break;
default:
break;
}
}
int PVMFVideoParserNode::AddPortActivity(const PVMFPortActivity& activity)
{
int32 err = OsclErrNone;
OSCL_TRY(err, iPortActivityQueue.push_back(activity););
return err;
}
bool PVMFVideoParserNode::ProcessPortActivity()
{//called by the AO to process a port activity message
//Pop the queue...
PVMFPortActivity activity(iPortActivityQueue.front());
iPortActivityQueue.erase(&iPortActivityQueue.front());
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "0x%x PVMFVideoParserNode::ProcessPortActivity: port=0x%x, type=%d",
this, activity.iPort, activity.iType));
int32 err = OsclErrNone;
PVMFStatus status = PVMFSuccess;
switch (activity.iType)
{
case PVMF_PORT_ACTIVITY_OUTGOING_MSG:
status = ProcessOutgoingMsg(activity.iPort);
//Re-queue the port activity event as long as there's
//more data to process and it isn't in a Busy state.
if (status != PVMFErrBusy
&& activity.iPort->OutgoingMsgQueueSize() > 0)
{
err = AddPortActivity(activity);
}
break;
case PVMF_PORT_ACTIVITY_INCOMING_MSG:
status = ProcessIncomingMsg(activity.iPort);
//Re-queue the port activity event as long as there's
//more data to process and it isn't in a Busy state.
if (status != PVMFErrBusy
&& activity.iPort->IncomingMsgQueueSize() > 0)
{
err = AddPortActivity(activity);
}
break;
default:
break;
}
//report a failure in queueing new activity...
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "0x%x PVMFVideoParserNode::Run: Error - queue port activity failed. port=0x%x, type=%d", this, activity.iPort, activity.iType));
ReportErrorEvent(PVMFErrPortProcessing);
}
//return true if we processed an activity...
return (status != PVMFErrBusy);
}
void PVMFVideoParserNode::QueuePortActivity(const PVMFPortActivity &aActivity)
{
//queue a new port activity event
int32 err;
err = AddPortActivity(aActivity);
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
(0, "0x%x PVMFVideoParserNode::PortActivity: Error - iPortActivityQueue.push_back() failed", this));
ReportErrorEvent(PVMFErrPortProcessing, (OsclAny*)(aActivity.iPort));
}
else
{
//wake up the AO to process the port activity event.
RunIfNotReady();
}
}
PVMFStatus PVMFVideoParserNode::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 PVMFVideoParserNode::ProcessOutgoingMsg: aPort=0x%x", this, aPort));
PVMFStatus status = aPort->Send();
if (status == PVMFErrBusy)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "0x%x PVMFVideoParserNode::ProcessOutgoingMsg: Connected port goes into busy state", this));
}
return status;
}
PVMFStatus PVMFVideoParserNode::ProcessIncomingMsg(PVMFPortInterface* aPort)
{
PVMFSharedMediaMsgPtr msg;
if (iInterfaceState != EPVMFNodeStarted)
return PVMFFailure;
PVMFStatus status = aPort->DequeueIncomingMsg(msg);
if (status != PVMFSuccess)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "0x%x PVMFVideoParserNode::ProcessIncomingMsg: Error - DequeueIncomingMsg failed", this));
}
else if (msg->getFormatID() == PVMF_MEDIA_CMD_BOS_FORMAT_ID)
{
PVMFSharedMediaCmdPtr sharedMediaCmdPtr = PVMFMediaCmd::createMediaCmd();
// Set the formatID, timestamp, sequenceNumber and streamID for the media message
sharedMediaCmdPtr->setFormatID(PVMF_MEDIA_CMD_BOS_FORMAT_ID);
sharedMediaCmdPtr->setTimestamp(msg->getTimestamp());
sharedMediaCmdPtr->setSeqNum(msg->getSeqNum());
sharedMediaCmdPtr->setStreamID(msg->getStreamID());
PVMFSharedMediaMsgPtr mediaMsgOut;
convertToPVMFMediaCmdMsg(mediaMsgOut, sharedMediaCmdPtr);
PVMFStatus retVal = iOutputPort->QueueOutgoingMsg(mediaMsgOut);
if (retVal != PVMFSuccess)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "PVMFVideoParserNode::SendBeginOfMediaStreamCommand: Outgoing queue busy. "));
}
msg.Unbind();
return retVal;
}
else
{
DataReceived(msg);
}
return status;
}