blob: 4bd929e80275305022af7e2eb9febdd1cb6ff970 [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 pvmi_mio_fileinput.cpp
* @brief PV Media IO interface implementation using file input
*/
#ifndef OSCL_BASE_H_INCLUDED
#include "oscl_base.h"
#endif
#ifndef PVMI_MIO_COMM_LOOPBACK_H_INCLUDED
#include "pvmi_mio_comm_loopback.h"
#endif
#ifndef OSCL_MIME_STRING_UTILS_H
#include "pv_mime_string_utils.h"
#endif
#ifndef OSCL_DLL_H_INCLUDED
#include "oscl_dll.h"
#endif
#ifndef PVMF_MEDIA_MSG_FORMAT_IDS_H_INCLUDED
#include "pvmf_media_msg_format_ids.h"
#endif
// Define entry point for this DLL
OSCL_DLL_ENTRY_POINT_DEFAULT()
#define PVMIO_MEDIADATA_POOLNUM 8
const uint32 AMR_FRAME_DELAY = 20; // 20ms
#define INPUT_TRANSFER_MODEL_VAL ".../input/transfer_model;valtype=uint32"
#define OUTPUT_TRANSFER_MODEL_VAL ".../output/transfer_model;valtype=uin32"
// Logging macros
#define LOG_STACK_TRACE(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, m)
#define LOG_DEBUG(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG, m)
#define LOG_ERR(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m)
OSCL_EXPORT_REF PvmiMIOControl* PvmiMIOCommLoopbackFactory::Create(const PvmiMIOCommLoopbackSettings& aSettings)
{
PvmiMIOControl *mioFilein = OSCL_STATIC_CAST(PvmiMIOControl*, OSCL_NEW(PvmiMIOCommLoopback, (aSettings)));
return mioFilein;
}
OSCL_EXPORT_REF bool PvmiMIOCommLoopbackFactory::Delete(PvmiMIOControl* aMio)
{
PvmiMIOCommLoopback *mioFilein = OSCL_STATIC_CAST(PvmiMIOCommLoopback*, aMio);
if (!mioFilein)
return false;
OSCL_DELETE(mioFilein);
mioFilein = NULL;
return true;
}
////////////////////////////////////////////////////////////////////////////
PvmiMIOCommLoopback::~PvmiMIOCommLoopback()
{
while (!iObservers.empty())
{
iObservers.pop_back();
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PvmiMIOCommLoopback::connect(PvmiMIOSession& aSession, PvmiMIOObserver* aObserver)
{
if (!aObserver)
{
return PVMFFailure;
}
int32 err = 0;
OSCL_TRY(err, iObservers.push_back(aObserver));
OSCL_FIRST_CATCH_ANY(err, return PVMFErrNoMemory);
aSession = (PvmiMIOSession)(iObservers.size() - 1); // Session ID is the index of observer in the vector
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PvmiMIOCommLoopback::disconnect(PvmiMIOSession aSession)
{
uint32 index = (uint32)aSession;
if (index >= iObservers.size())
{
// Invalid session ID
return PVMFFailure;
}
iObservers.erase(iObservers.begin() + index);
iObservers[index] = 0;
iObservers.destroy();
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PvmiMediaTransfer* PvmiMIOCommLoopback::createMediaTransfer(PvmiMIOSession& aSession,
PvmiKvp* read_formats,
int32 read_flags,
PvmiKvp* write_formats,
int32 write_flags)
{
OSCL_UNUSED_ARG(read_formats);
OSCL_UNUSED_ARG(read_flags);
OSCL_UNUSED_ARG(write_formats);
OSCL_UNUSED_ARG(write_flags);
uint32 index = (uint32)aSession;
if (index >= iObservers.size())
{
// Invalid session ID
OSCL_LEAVE(OsclErrArgument);
return NULL;
}
return (PvmiMediaTransfer*)this;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::deleteMediaTransfer(PvmiMIOSession& aSession,
PvmiMediaTransfer* media_transfer)
{
uint32 index = (uint32)aSession;
if (!media_transfer || index >= iObservers.size())
{
// Invalid session ID
OSCL_LEAVE(OsclErrArgument);
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::QueryUUID(const PvmfMimeString& aMimeType,
Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids,
bool aExactUuidsOnly,
const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aMimeType);
OSCL_UNUSED_ARG(aExactUuidsOnly);
int32 err = 0;
OSCL_TRY(err, aUuids.push_back(PVMI_CAPABILITY_AND_CONFIG_PVUUID););
OSCL_FIRST_CATCH_ANY(err, OSCL_LEAVE(OsclErrNoMemory););
return AddCmdToQueue(CMD_QUERY_UUID, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::QueryInterface(const PVUuid& aUuid,
PVInterface*& aInterfacePtr,
const OsclAny* aContext)
{
if (aUuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID)
{
PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this);
aInterfacePtr = OSCL_STATIC_CAST(PVInterface*, myInterface);
}
else
{
aInterfacePtr = NULL;
}
return AddCmdToQueue(CMD_QUERY_INTERFACE, aContext, (OsclAny*)&aInterfacePtr);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback:: Init(const OsclAny* aContext)
{
if (iState != STATE_IDLE)
{
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_INIT, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::Start(const OsclAny* aContext)
{
if (iState != STATE_INITIALIZED && iState != STATE_PAUSED)
{
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_START, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::Pause(const OsclAny* aContext)
{
if (iState != STATE_STARTED)
{
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_PAUSE, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::Flush(const OsclAny* aContext)
{
if (iState != STATE_STARTED || iState != STATE_PAUSED)
{
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_FLUSH, aContext);
}
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::Reset(const OsclAny* aContext)
{
return AddCmdToQueue(CMD_RESET, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::DiscardData(const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::DiscardData(PVMFTimestamp aTimestamp, const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aTimestamp);
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::Stop(const OsclAny* aContext)
{
if (iState != STATE_STARTED && iState != STATE_PAUSED)
{
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_STOP, aContext);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::ThreadLogon()
{
if (!iThreadLoggedOn)
{
AddToScheduler();
iLogger = PVLogger::GetLoggerObject("PvmiMIOCommLoopback");
iThreadLoggedOn = true;
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::ThreadLogoff()
{
if (iThreadLoggedOn)
{
RemoveFromScheduler();
iLogger = NULL;
iThreadLoggedOn = false;
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::CancelAllCommands(const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::CancelCommand(PVMFCommandId aCmdId, const OsclAny* aContext)
{
OSCL_UNUSED_ARG(aCmdId);
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::setPeer(PvmiMediaTransfer* aPeer)
{
iPeer = aPeer;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::useMemoryAllocators(OsclMemAllocator* write_alloc)
{
OSCL_UNUSED_ARG(write_alloc);
OSCL_LEAVE(OsclErrNotSupported);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::writeAsync(uint8 aFormatType, int32 aFormatIndex,
uint8* aData, uint32 aDataLen,
const PvmiMediaXferHeader& data_header_info,
OsclAny* aContext)
{
OSCL_UNUSED_ARG(aFormatType);
OSCL_UNUSED_ARG(aFormatIndex);
uint32 aSeqNum = data_header_info.seq_num;
PVMFTimestamp aTimestamp = data_header_info.timestamp;
// writeAsync will copy the input data, and then make
// arrangements to send a writeComplete callback, and also
// echo back the receieved data to its peer
PVMFSharedMediaDataPtr mediaData;
OsclRefCounterMemFrag frag;
OsclSharedPtr<PVMFMediaDataImpl> mediaDataImpl;
int32 err = 0, cmdId;
if (iCommandCounter == 0x7FFFFFFF)
iCommandCounter = 0;
cmdId = iCommandCounter++;
mediaDataImpl = iMediaDataAlloc.allocate(aDataLen);
mediaDataImpl->getMediaFragment(0, frag);
oscl_memcpy(frag.getMemFragPtr(), aData, aDataLen);
OSCL_TRY(err, mediaData = PVMFMediaData::createMediaData(mediaDataImpl, &iMediaDataMemPool));
OSCL_FIRST_CATCH_ANY(err, mediaDataImpl.Unbind();
OSCL_LEAVE(err));
// Set timestamp
mediaData->setTimestamp(aTimestamp);
mediaData->setSeqNum(aSeqNum);
mediaData->setMediaFragFilledLen(0, aDataLen);
// Convert media data to MediaMsg, and store for loopback echo
PVMFSharedMediaMsgPtr mediaMsg;
convertToPVMFMediaMsg(mediaMsg, mediaData);
iOutgoingQueue.push_back(mediaMsg);
// Queue a writeComplete response for async sending
cmdId = iCommandCounter++;
WriteResponse resp(PVMFSuccess, cmdId, aContext, aTimestamp);
iWriteResponseQueue.push_back(resp);
RunIfNotReady();
return cmdId;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::writeComplete(PVMFStatus aStatus, PVMFCommandId aCmdId,
OsclAny* aContext)
{
OSCL_UNUSED_ARG(aStatus);
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
(0, "PvmiMIOCommLoopback::writeComplete status %d cmdId %d context 0x%x", aStatus, aCmdId, aContext));
//detect cases where the current call is completing synchronously.
if (iWriteState == EWriteBusy
&& (uint32)aContext == iWriteAsyncContext)
{
//synchronous completion
iWriteState = EWriteOK;
}
else
{
//asynchronous completion
//do any memory cleanup
uint32 i;
for (i = 0;i < iCleanupQueue.size();i++)
{
if (iCleanupQueue[i].iCmdId == aCmdId)
{
PVMFSharedMediaDataPtr mediaData = iCleanupQueue[i].iData;
if (iSettings.iTestObserver)
{
PVMFTimestamp ts = mediaData->getTimestamp();
iSettings.iTestObserver->LoopbackPos(ts);
}
iCleanupQueue.erase(&iCleanupQueue[i]);
break;
}
}
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::readAsync(uint8* data, uint32 max_data_len,
OsclAny* aContext, int32* formats, uint16 num_formats)
{
OSCL_UNUSED_ARG(data);
OSCL_UNUSED_ARG(max_data_len);
OSCL_UNUSED_ARG(aContext);
OSCL_UNUSED_ARG(formats);
OSCL_UNUSED_ARG(num_formats);
// This is an active data source. readAsync is not supported.
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::readComplete(PVMFStatus aStatus, PVMFCommandId read_cmd_id,
int32 format_index, const PvmiMediaXferHeader& data_header_info,
OsclAny* aContext)
{
OSCL_UNUSED_ARG(aStatus);
OSCL_UNUSED_ARG(read_cmd_id);
OSCL_UNUSED_ARG(format_index);
OSCL_UNUSED_ARG(data_header_info);
OSCL_UNUSED_ARG(aContext);
// This is an active data source. readComplete is not supported.
OSCL_LEAVE(OsclErrNotSupported);
return;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::statusUpdate(uint32 status_flags)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
(0, "PvmiMIOCommLoopback::statusUpdate flags %d", status_flags));
if (status_flags & PVMI_MEDIAXFER_STATUS_WRITE)
{
//recover from a previous async write error.
if (iWriteState == EWriteWait)
{
iWriteState = EWriteOK;
RunIfNotReady();
}
}
else
{
//disable write
iWriteState = EWriteWait;
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::cancelCommand(PVMFCommandId aCmdId)
{
OSCL_UNUSED_ARG(aCmdId);
// This cancel command ( with a small "c" in cancel ) is for the media transfer interface.
// implementation is similar to the cancel command of the media I/O interface.
OSCL_LEAVE(OsclErrNotSupported);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::cancelAllCommands()
{
OSCL_LEAVE(OsclErrNotSupported);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::setObserver(PvmiConfigAndCapabilityCmdObserver* aObserver)
{
OSCL_UNUSED_ARG(aObserver);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PvmiMIOCommLoopback::getParametersSync(PvmiMIOSession session,
PvmiKeyType identifier,
PvmiKvp*& parameters,
int& num_parameter_elements,
PvmiCapabilityContext context)
{
LOG_STACK_TRACE((0, "PvmiMIOCommLoopback::getParametersSync"));
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
parameters = NULL;
num_parameter_elements = 0;
PVMFStatus status = PVMFFailure;
if (pv_mime_strcmp(identifier, OUTPUT_FORMATS_CAP_QUERY) == 0 ||
pv_mime_strcmp(identifier, OUTPUT_FORMATS_CUR_QUERY) == 0)
{
num_parameter_elements = 1;
status = AllocateKvp(parameters,
OSCL_CONST_CAST(char*, OUTPUT_FORMATS_VALTYPE), num_parameter_elements);
if (status != PVMFSuccess)
{
LOG_ERR((0, "PvmiMIOCommLoopback::GetOutputParametersSync: Error - AllocateKvp failed. status=%d", status));
}
else
{
parameters[0].value.pChar_value = OSCL_STATIC_CAST(mbchar*, iSettings.iMediaFormat.getMIMEStrPtr());
}
}
else if (pv_mime_strcmp(identifier, INPUT_FORMATS_CAP_QUERY) == 0)
{
num_parameter_elements = 1;
status = AllocateKvp(parameters,
OSCL_CONST_CAST(char*, INPUT_FORMATS_VALTYPE), num_parameter_elements);
if (status != PVMFSuccess)
{
LOG_ERR((0, "PvmiMIOCommLoopback::GetOutputParametersSync: Error - AllocateKvp failed. status=%d", status));
}
else
{
parameters[0].value.pChar_value = OSCL_STATIC_CAST(mbchar*, iSettings.iMediaFormat.getMIMEStrPtr());
}
}
else if (pv_mime_strcmp(identifier, INPUT_TRANSFER_MODEL_VAL) == 0)
{
num_parameter_elements = 1;
status = AllocateKvp(parameters,
OSCL_CONST_CAST(char*, INPUT_TRANSFER_MODEL_VAL), num_parameter_elements);
if (status != PVMFSuccess)
{
LOG_ERR((0, "PvmiMIOCommLoopback::GetInputModelParametersSync: Error - AllocateKvp failed. status=%d", status));
}
else
{
parameters[0].value.uint32_value = 1; // push model for media input
}
}
else if (pv_mime_strcmp(identifier, OUTPUT_TRANSFER_MODEL_VAL) == 0)
{
num_parameter_elements = 1;
status = AllocateKvp(parameters,
OSCL_CONST_CAST(char*, OUTPUT_TRANSFER_MODEL_VAL), num_parameter_elements);
if (status != PVMFSuccess)
{
LOG_ERR((0, "PvmiMIOCommLoopback::GetOutputModelParametersSync: Error - AllocateKvp failed. status=%d", status));
}
else
{
parameters[0].value.uint32_value = 1; // push model for media output
}
}
return status;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PvmiMIOCommLoopback::releaseParameters(PvmiMIOSession session,
PvmiKvp* parameters,
int num_elements)
{
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(num_elements);
if (parameters)
{
iAlloc.deallocate((OsclAny*)parameters);
return PVMFSuccess;
}
else
{
return PVMFFailure;
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::createContext(PvmiMIOSession session, PvmiCapabilityContext& context)
{
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::setContextParameters(PvmiMIOSession session,
PvmiCapabilityContext& context,
PvmiKvp* parameters, int num_parameter_elements)
{
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
OSCL_UNUSED_ARG(parameters);
OSCL_UNUSED_ARG(num_parameter_elements);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::DeleteContext(PvmiMIOSession session, PvmiCapabilityContext& context)
{
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PvmiMIOCommLoopback::setParametersSync(PvmiMIOSession session, PvmiKvp* parameters,
int num_elements, PvmiKvp*& ret_kvp)
{
OSCL_UNUSED_ARG(session);
PVMFStatus status = PVMFSuccess;
ret_kvp = NULL;
for (int32 i = 0; i < num_elements; i++)
{
status = VerifyAndSetParameter(&(parameters[i]), true);
if (status != PVMFSuccess)
{
LOG_ERR((0, "PvmiMIOCommLoopback::setParametersSync: Error - VerifiyAndSetParameter failed on parameter #%d", i));
ret_kvp = &(parameters[i]);
}
}
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PvmiMIOCommLoopback::setParametersAsync(PvmiMIOSession session,
PvmiKvp* parameters,
int num_elements,
PvmiKvp*& ret_kvp,
OsclAny* context)
{
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(parameters);
OSCL_UNUSED_ARG(num_elements);
OSCL_UNUSED_ARG(ret_kvp);
OSCL_UNUSED_ARG(context);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF uint32 PvmiMIOCommLoopback::getCapabilityMetric(PvmiMIOSession session)
{
OSCL_UNUSED_ARG(session);
return 0;
}
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PvmiMIOCommLoopback::verifyParametersSync(PvmiMIOSession session,
PvmiKvp* parameters, int num_elements)
{
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(parameters);
OSCL_UNUSED_ARG(num_elements);
return PVMFErrNotSupported;
}
////////////////////////////////////////////////////////////////////////////
// Private methods
////////////////////////////////////////////////////////////////////////////
PvmiMIOCommLoopback::PvmiMIOCommLoopback(const PvmiMIOCommLoopbackSettings& aSettings)
: OsclTimerObject(OsclActiveObject::EPriorityNominal, "PvmiMIOCommLoopback"),
iCommandCounter(1000),
iPeer(NULL),
iThreadLoggedOn(false),
iSettings(aSettings),
iMediaDataAlloc(&iAlloc),
iMediaDataMemPool(PVMIO_MEDIADATA_POOLNUM),
iLogger(NULL),
iState(STATE_IDLE),
iWriteState(EWriteOK),
iResend(false),
iWriteAsyncContext(0)
{
ConstructL(aSettings);
}
////////////////////////////////////////////////////////////////////////////
void PvmiMIOCommLoopback::ConstructL(const PvmiMIOCommLoopbackSettings& aSettings)
{
iSettings.iMediaFormat = aSettings.iMediaFormat;
}
////////////////////////////////////////////////////////////////////////////
void PvmiMIOCommLoopback::Run()
{
if (!iCmdQueue.empty())
{
PvmiMIOCommLoopbackCmd cmd = iCmdQueue[0];
iCmdQueue.erase(iCmdQueue.begin());
switch (cmd.iType)
{
case CMD_INIT:
DoRequestCompleted(cmd, DoInit());
break;
case CMD_START:
DoRequestCompleted(cmd, DoStart());
break;
case CMD_PAUSE:
DoRequestCompleted(cmd, DoPause());
break;
case CMD_FLUSH:
DoRequestCompleted(cmd, DoFlush());
break;
case CMD_RESET:
DoRequestCompleted(cmd, DoReset());
break;
case CMD_STOP:
DoRequestCompleted(cmd, DoStop());
break;
case CMD_QUERY_UUID:
case CMD_QUERY_INTERFACE:
DoRequestCompleted(cmd, PVMFSuccess);
break;
case CMD_CANCEL_ALL_COMMANDS:
case CMD_CANCEL_COMMAND:
DoRequestCompleted(cmd, PVMFFailure);
break;
default:
break;
}
}
if (!iCmdQueue.empty())
{
// Run again if there are more things to process
RunIfNotReady();
}
if (!iOutgoingQueue.empty() && iWriteState == EWriteOK)
{
DoLoopback();
}
while (!iWriteResponseQueue.empty())
{
//report write complete
if (iPeer)
{
iPeer->writeComplete(iWriteResponseQueue[0].iStatus, iWriteResponseQueue[0].iCmdId, (OsclAny*)iWriteResponseQueue[0].iContext);
iWriteResponseQueue.erase(iWriteResponseQueue.begin());
}
}
}
////////////////////////////////////////////////////////////////////////////
PVMFCommandId PvmiMIOCommLoopback::AddCmdToQueue(PvmiMIOCommLoopbackCmdType aType,
const OsclAny* aContext, OsclAny* aData1)
{
if (aType == DATA_EVENT)
OSCL_LEAVE(OsclErrArgument);
PvmiMIOCommLoopbackCmd cmd;
cmd.iType = aType;
cmd.iContext = OSCL_STATIC_CAST(OsclAny*, aContext);
cmd.iData1 = aData1;
cmd.iId = iCommandCounter++;
iCmdQueue.push_back(cmd);
RunIfNotReady();
return cmd.iId;
}
////////////////////////////////////////////////////////////////////////////
void PvmiMIOCommLoopback::DoRequestCompleted(const PvmiMIOCommLoopbackCmd& aCmd, PVMFStatus aStatus, OsclAny* aEventData)
{
PVMFCmdResp response(aCmd.iId, aCmd.iContext, aStatus, aEventData);
for (uint32 i = 0; i < iObservers.size(); i++)
iObservers[i]->RequestCompleted(response);
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::DoInit()
{
iState = STATE_INITIALIZED;
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::DoStart()
{
iState = STATE_STARTED;
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::DoPause()
{
iState = STATE_PAUSED;
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::DoFlush()
{
// This method should stop capturing media data but continue to send captured
// media data that is already in buffer and then go to stopped state.
// However, in this case of file input we do not have such a buffer for
// captured data, so this behaves the same way as stop.
return DoStop();
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::DoStop()
{
iState = STATE_STOPPED;
return PVMFSuccess;
}
PVMFStatus PvmiMIOCommLoopback::DoReset()
{
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::AllocateKvp(PvmiKvp*& aKvp, PvmiKeyType aKey, int32 aNumParams)
{
LOG_STACK_TRACE((0, "PvmiMIOCommLoopback::AllocateKvp"));
uint8* buf = NULL;
uint32 keyLen = oscl_strlen(aKey) + 1;
int32 err = 0;
OSCL_TRY(err,
buf = (uint8*)iAlloc.allocate(aNumParams * (sizeof(PvmiKvp) + keyLen));
if (!buf)
OSCL_LEAVE(OsclErrNoMemory);
);
OSCL_FIRST_CATCH_ANY(err,
LOG_ERR((0, "PvmiMIOCommLoopback::AllocateKvp: Error - kvp allocation failed"));
return PVMFErrNoMemory;
);
int32 i = 0;
PvmiKvp* curKvp = aKvp = new(buf) PvmiKvp;
buf += sizeof(PvmiKvp);
for (i = 1; i < aNumParams; i++)
{
curKvp += i;
curKvp = new(buf) PvmiKvp;
buf += sizeof(PvmiKvp);
}
for (i = 0; i < aNumParams; i++)
{
aKvp[i].key = (char*)buf;
oscl_strncpy(aKvp[i].key, aKey, keyLen);
buf += keyLen;
}
return PVMFSuccess;
}
////////////////////////////////////////////////////////////////////////////
PVMFStatus PvmiMIOCommLoopback::VerifyAndSetParameter(PvmiKvp* aKvp, bool aSetParam)
{
OSCL_UNUSED_ARG(aSetParam);
LOG_STACK_TRACE((0, "PvmiMIOCommLoopback::VerifyAndSetParameter: aKvp=0x%x, aSetParam=%d", aKvp, aSetParam));
if (!aKvp)
{
LOG_ERR((0, "PvmiMIOCommLoopback::VerifyAndSetParameter: Error - Invalid key-value pair"));
return PVMFFailure;
}
if (pv_mime_strcmp(aKvp->key, OUTPUT_FORMATS_VALTYPE) == 0)
{
if (pv_mime_strcmp(iSettings.iMediaFormat.getMIMEStrPtr(), aKvp->value.pChar_value) == 0)
{
return PVMFSuccess;
}
else
{
LOG_ERR((0, "PvmiMIOCommLoopback::VerifyAndSetParameter: Error - Unsupported format %s",
aKvp->value.pChar_value));
return PVMFFailure;
}
}
if (pv_mime_strcmp(aKvp->key, INPUT_FORMATS_VALTYPE) == 0)
{
if (pv_mime_strcmp(iSettings.iMediaFormat.getMIMEStrPtr(), aKvp->value.pChar_value) == 0)
{
return PVMFSuccess;
}
else
{
LOG_ERR((0, "PvmiMIOCommLoopback::VerifyAndSetParameter: Error - Unsupported format %s",
aKvp->value.pChar_value));
return PVMFFailure;
}
}
LOG_ERR((0, "PvmiMIOCommLoopback::VerifyAndSetParameter: Error - Unsupported parameter"));
return PVMFFailure;
}
void PvmiMIOCommLoopback::DoLoopback()
{
PVMFSharedMediaMsgPtr aMsg;
PVMFSharedMediaDataPtr aMediaData;
uint32 fragment = 0;
uint32 fragindex;
while (!iOutgoingQueue.empty())
{
aMsg = iOutgoingQueue[0];
convertToPVMFMediaData(aMediaData, aMsg);
if (aMsg->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID)
{
return;
}
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVCommsIOPort::SendCommsData Seq %d TS %d Rep 0x%x Count %d"
, aMediaData->getSeqNum()
, aMediaData->getTimestamp()
, aMediaData.GetRep()
, aMediaData.get_count()));
OSCL_ASSERT(iWriteState == EWriteOK);
if (iResend
&& aMediaData->getSeqNum() == iResendSeqNum)
{
fragment = iResendFragment;
iResend = false;
}
for (fragindex = fragment; fragindex < aMediaData->getNumFragments();)
{
OsclRefCounterMemFrag frag;
aMediaData->getMediaFragment(fragindex, frag);
++iWriteAsyncContext;
iWriteState = EWriteBusy;
int32 err;
int32 cmdId = 0;
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVCommsIONodePort::SendCommsData Calling WriteAsync context 0x%x", iWriteAsyncContext));
PvmiMediaXferHeader data_hdr;
data_hdr.seq_num = aMediaData->getSeqNum();
data_hdr.timestamp = aMediaData->getTimestamp();
data_hdr.flags = 0;
data_hdr.duration = 0;
data_hdr.stream_id = 0;
err = WriteAsync(cmdId, frag, data_hdr);
if (err != OsclErrNone)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVCommsIONodePort::SendData WriteAsync Leave code %d", err));
//if a leave occurs in the writeAsync call, we suspend data
//transfer until a statusUpdate call from the MIO component
//tells us to resume.
//this is not an error-- it's the normal flow control mechanism.
iWriteState = EWriteWait;
//save the data to re-send later.
iResend = true;
iResendFragment = fragindex;
iResendSeqNum = aMediaData->getSeqNum();
return ;//wait on statusUpdate call from the MIO component.
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
(0, "PVCommsIONodePort::SendData WriteAsync returned cmdId %d", cmdId));
fragindex++;
if (fragindex == aMediaData->getNumFragments())
{
//all fragments have been sent. see whether completion
//is synchronous or asynchronous.
if (iWriteState == EWriteBusy)
{
//asynchronous completion.
//push the data onto the cleanup stack so it won't get cleaned
//up until the component consumes it.
iCleanupQueue.push_back(CleanupQueueElement(aMediaData, cmdId));
}
//else the write already completed synchronously.
iOutgoingQueue.erase(iOutgoingQueue.begin());
}
iWriteState = EWriteOK;
}
}
}
}
int32 PvmiMIOCommLoopback::WriteAsync(int32& cmdId,
OsclRefCounterMemFrag frag,
PvmiMediaXferHeader data_hdr)
{
int32 err;
OSCL_TRY(err,
cmdId = iPeer->writeAsync(PVMI_MEDIAXFER_FMT_TYPE_DATA, /*format_type*/
PVMI_MEDIAXFER_FMT_INDEX_DATA, /*format_index*/
(uint8*)frag.getMemFragPtr(),
frag.getMemFragSize(),
data_hdr,
(OsclAny*)iWriteAsyncContext);
);
return err;
}