blob: 71ce81c049bb8d82afbfefd1c1fdc782a84b5cfe [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2008 HTC Inc.
*
* 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.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "CameraInput"
#include <utils/Log.h>
#include <camera/CameraParameters.h>
#include <utils/Errors.h>
#include <media/mediarecorder.h>
#include <surfaceflinger/ISurface.h>
#include <camera/ICamera.h>
#include <camera/Camera.h>
#include "pv_mime_string_utils.h"
#include "oscl_dll.h"
#include "oscl_tickcount.h"
#include "android_camera_input.h"
using namespace android;
static const int VIEW_FINDER_FREEZE_DETECTION_TOLERANCE = 250; // ms
// Define entry point for this DLL
OSCL_DLL_ENTRY_POINT_DEFAULT()
PVRefBufferAlloc::~PVRefBufferAlloc()
{
if(numAllocated != 0)
{
LOGE("Ln %d ERROR PVRefBufferAlloc numAllocated %d",__LINE__, numAllocated );
}
}
// camera MIO
AndroidCameraInput::AndroidCameraInput()
: OsclTimerObject(OsclActiveObject::EPriorityNominal, "AndroidCameraInput"),
iWriteState(EWriteOK),
iAuthorClock(NULL),
iClockNotificationsInf(NULL),
iAudioFirstFrameTs(0),
iAudioLossDuration(0),
pPmemInfo(NULL)
{
LOGV("constructor(%p)", this);
iCmdIdCounter = 0;
iPeer = NULL;
iThreadLoggedOn = false;
iDataEventCounter = 0;
iTimeStamp = 0;
iMilliSecondsPerDataEvent = 0;
iMicroSecondsPerDataEvent = 0;
iState = STATE_IDLE;
mFrameWidth = ANDROID_DEFAULT_FRAME_WIDTH;
mFrameHeight= ANDROID_DEFAULT_FRAME_HEIGHT;
mFrameRate = ANDROID_DEFAULT_FRAME_RATE;
mCamera = NULL;
mFlags = 0;
iFrameQueue.reserve(5);
iFrameQueueMutex.Create();
iAudioLossMutex.Create();
// setup callback listener
mListener = new AndroidCameraInputListener(this);
}
void AndroidCameraInput::ReleaseQueuedFrames()
{
LOGV("ReleaseQueuedFrames");
iFrameQueueMutex.Lock();
while (!iFrameQueue.empty()) {
AndroidCameraInputMediaData data = iFrameQueue[0];
iFrameQueue.erase(iFrameQueue.begin());
#if (LOG_NDEBUG == 0)
ssize_t offset = 0;
size_t size = 0;
sp<IMemoryHeap> heap = data.iFrameBuffer->getMemory(&offset, &size);
LOGD("writeComplete: ID = %d, base = %p, offset = %ld, size = %d ReleaseQueuedFrames", heap->getHeapID(), heap->base(), offset, size);
#endif
mCamera->releaseRecordingFrame(data.iFrameBuffer);
}
if(pPmemInfo)
{
delete pPmemInfo;
pPmemInfo = NULL;
}
iFrameQueueMutex.Unlock();
}
AndroidCameraInput::~AndroidCameraInput()
{
LOGV("destructor");
if (mCamera != NULL) {
mCamera->setListener(NULL);
ReleaseQueuedFrames();
if ((mFlags & FLAGS_HOT_CAMERA) == 0) {
LOGV("camera was cold when we started, stopping preview");
mCamera->stopPreview();
}
if (mFlags & FLAGS_SET_CAMERA) {
LOGV("unlocking camera to return to app");
mCamera->unlock();
} else {
LOGV("disconnect from camera");
mCamera->disconnect();
}
mFlags = 0;
mCamera.clear();
}
iFrameQueueMutex.Close();
iAudioLossMutex.Close();
mListener.clear();
}
PVMFStatus AndroidCameraInput::connect(PvmiMIOSession& aSession,
PvmiMIOObserver* aObserver)
{
LOGV("connect");
if (!aObserver) {
LOGE("observer is a NULL pointer");
return PVMFFailure;
}
int32 err = 0;
OSCL_TRY(err, iObservers.push_back(aObserver));
OSCL_FIRST_CATCH_ANY(err,
LOGE("Out of memory"); return PVMFErrNoMemory);
// Session ID is the index of observer in the vector
aSession = (PvmiMIOSession)(iObservers.size() - 1);
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::disconnect(PvmiMIOSession aSession)
{
LOGV("disconnect");
uint32 index = (uint32) aSession;
uint32 size = iObservers.size();
if (index >= size) {
LOGE("Invalid session ID %d. Valid range is [0, %d]", index, size - 1);
return PVMFFailure;
}
iObservers.erase(iObservers.begin() + index);
return PVMFSuccess;
}
PvmiMediaTransfer* AndroidCameraInput::createMediaTransfer(
PvmiMIOSession& aSession,
PvmiKvp* read_formats,
int32 read_flags,
PvmiKvp* write_formats,
int32 write_flags)
{
LOGV("createMediaTransfer");
OSCL_UNUSED_ARG(read_formats);
OSCL_UNUSED_ARG(read_flags);
OSCL_UNUSED_ARG(write_formats);
OSCL_UNUSED_ARG(write_flags);
uint32 index = (uint32) aSession;
uint32 size = iObservers.size();
if (index >= size) {
LOGE("Invalid session ID %d. Valid range is [0, %d]", index, size - 1);
OSCL_LEAVE(OsclErrArgument);
return NULL;
}
return (PvmiMediaTransfer*)this;
}
void AndroidCameraInput::deleteMediaTransfer(PvmiMIOSession& aSession,
PvmiMediaTransfer* media_transfer)
{
LOGV("deleteMediaTransfer");
uint32 index = (uint32) aSession;
uint32 size = iObservers.size();
if (index >= size) {
LOGE("Invalid session ID %d. Valid range is [0, %d]", index, size - 1);
OSCL_LEAVE(OsclErrArgument);
return;
}
if (!media_transfer) {
LOGE("media transfer is a NULL pointer");
OSCL_LEAVE(OsclErrArgument);
}
// TODO:
// 1. I did not see how the media transfer session has been terminated
// after this method call.
// 2. according to pvmi_mio_control.h, this should also check with there
// is any outstanding buffer?
}
PVMFCommandId AndroidCameraInput::QueryUUID(const PvmfMimeString& aMimeType,
Oscl_Vector<PVUuid,
OsclMemAllocator>& aUuids,
bool aExactUuidsOnly,
const OsclAny* aContext)
{
LOGV("QueryUUID");
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,
LOGE("Out of memory"); OSCL_LEAVE(OsclErrNoMemory));
return AddCmdToQueue(CMD_QUERY_UUID, aContext);
}
PVMFCommandId AndroidCameraInput::QueryInterface(const PVUuid& aUuid,
PVInterface*& aInterfacePtr,
const OsclAny* aContext)
{
LOGV("QueryInterface");
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);
}
PVMFCommandId AndroidCameraInput::Init(const OsclAny* aContext)
{
LOGV("Init");
if (iState != STATE_IDLE) {
LOGE("Init called in an invalid state(%d)", iState);
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_INIT, aContext);
}
PVMFCommandId AndroidCameraInput::Start(const OsclAny* aContext)
{
LOGV("Start");
if (iState != STATE_INITIALIZED && iState != STATE_PAUSED) {
LOGE("Start called in an invalid state(%d)", iState);
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_START, aContext);
}
PVMFCommandId AndroidCameraInput::Pause(const OsclAny* aContext)
{
LOGV("Pause");
if (iState != STATE_STARTED) {
LOGE("Pause called in an invalid state(%d)", iState);
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_PAUSE, aContext);
}
PVMFCommandId AndroidCameraInput::Flush(const OsclAny* aContext)
{
LOGV("Flush");
if (iState != STATE_STARTED && iState != STATE_PAUSED) {
LOGE("Flush called in an invalid state(%d)", iState);
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_FLUSH, aContext);
}
PVMFCommandId AndroidCameraInput::Reset(const OsclAny* aContext)
{
LOGV("Reset");
return AddCmdToQueue(CMD_RESET, aContext);
}
PVMFCommandId AndroidCameraInput::DiscardData(PVMFTimestamp aTimestamp,
const OsclAny* aContext)
{
LOGV("DiscardData with time stamp");
OSCL_UNUSED_ARG(aContext);
OSCL_UNUSED_ARG(aTimestamp);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
PVMFCommandId AndroidCameraInput::DiscardData(const OsclAny* aContext)
{
LOGV("DiscardData");
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
PVMFCommandId AndroidCameraInput::Stop(const OsclAny* aContext)
{
LOGV("Stop");
if (iState != STATE_STARTED && iState != STATE_PAUSED) {
LOGE("Stop called in an invalid state(%d)", iState);
OSCL_LEAVE(OsclErrInvalidState);
return -1;
}
return AddCmdToQueue(CMD_STOP, aContext);
}
void AndroidCameraInput::ThreadLogon()
{
LOGV("ThreadLogon");
if (!iThreadLoggedOn) {
AddToScheduler();
iThreadLoggedOn = true;
}
}
void AndroidCameraInput::ThreadLogoff()
{
LOGV("ThreadLogoff");
if (iThreadLoggedOn) {
RemoveFromScheduler();
iThreadLoggedOn = false;
}
}
PVMFCommandId AndroidCameraInput::CancelAllCommands(const OsclAny* aContext)
{
LOGV("CancelAllCommands");
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
PVMFCommandId AndroidCameraInput::CancelCommand(PVMFCommandId aCmdId,
const OsclAny* aContext)
{
LOGV("CancelCommand");
OSCL_UNUSED_ARG(aCmdId);
OSCL_UNUSED_ARG(aContext);
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
void AndroidCameraInput::setPeer(PvmiMediaTransfer* aPeer)
{
LOGV("setPeer iPeer %p aPeer %p", iPeer, aPeer);
if(iPeer && aPeer){
LOGE("setPeer iPeer %p aPeer %p", iPeer, aPeer);
OSCL_LEAVE(OsclErrGeneral);
return;
}
iPeer = aPeer;
}
void AndroidCameraInput::useMemoryAllocators(OsclMemAllocator* write_alloc)
{
LOGV("useMemoryAllocators");
OSCL_UNUSED_ARG(write_alloc);
OSCL_LEAVE(OsclErrNotSupported);
}
PVMFCommandId AndroidCameraInput::writeAsync(uint8 aFormatType,
int32 aFormatIndex,
uint8* aData,
uint32 aDataLen,
const PvmiMediaXferHeader& data_header_info,
OsclAny* aContext)
{
LOGV("writeAsync");
OSCL_UNUSED_ARG(aFormatType);
OSCL_UNUSED_ARG(aFormatIndex);
OSCL_UNUSED_ARG(aData);
OSCL_UNUSED_ARG(aDataLen);
OSCL_UNUSED_ARG(data_header_info);
OSCL_UNUSED_ARG(aContext);
// This is an active data source. writeAsync is not supported.
OSCL_LEAVE(OsclErrNotSupported);
return -1;
}
void AndroidCameraInput::writeComplete(PVMFStatus aStatus,
PVMFCommandId write_cmd_id,
OsclAny* aContext)
{
LOGV("writeComplete");
OSCL_UNUSED_ARG(aContext);
iFrameQueueMutex.Lock();
if (iSentMediaData.empty()) {
LOGE("Nothing to complete");
iFrameQueueMutex.Unlock();
return;
}
AndroidCameraInputMediaData data = iSentMediaData[0];
#if (LOG_NDEBUG == 0)
ssize_t offset = 0;
size_t size = 0;
sp<IMemoryHeap> heap = data.iFrameBuffer->getMemory(&offset, &size);
LOGD("writeComplete: ID = %d, base = %p, offset = %ld, size = %d", heap->getHeapID(), heap->base(), offset, size);
#endif
// View finder freeze detection
// Note for low frame rate, we don't bother to log view finder freezes
int processingTimeInMs = (systemTime()/1000000L - (iAudioFirstFrameTs + iAudioLossDuration)) - data.iXferHeader.timestamp;
if (processingTimeInMs >= VIEW_FINDER_FREEZE_DETECTION_TOLERANCE && mFrameRate >= 10.0) {
LOGW("Frame %p takes too long (%d ms) to process, staring at %d", data.iFrameBuffer.get(), processingTimeInMs, iAudioFirstFrameTs);
}
mCamera->releaseRecordingFrame(data.iFrameBuffer);
iSentMediaData.erase(iSentMediaData.begin());
iFrameQueueMutex.Unlock();
// reference count is always updated, even if the write fails
if (aStatus != PVMFSuccess) {
LOGE("writeAsync failed. aStatus=%d", aStatus);
}
}
PVMFCommandId AndroidCameraInput::readAsync(uint8* data,
uint32 max_data_len,
OsclAny* aContext,
int32* formats,
uint16 num_formats)
{
LOGV("readAsync");
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;
}
void AndroidCameraInput::readComplete(PVMFStatus aStatus,
PVMFCommandId read_cmd_id,
int32 format_index,
const PvmiMediaXferHeader& data_header_info,
OsclAny* aContext)
{
LOGV("readComplete");
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;
}
void AndroidCameraInput::statusUpdate(uint32 status_flags)
{
LOGV("statusUpdate");
if (status_flags != PVMI_MEDIAXFER_STATUS_WRITE)
{
OSCL_LEAVE(OsclErrNotSupported);
}
else
{
// Restart the flow of data
iWriteState = EWriteOK;
}
}
void AndroidCameraInput::cancelCommand(PVMFCommandId aCmdId)
{
LOGV("cancelCommand");
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);
}
void AndroidCameraInput::cancelAllCommands()
{
LOGV("cancelAllCommands");
OSCL_LEAVE(OsclErrNotSupported);
}
void AndroidCameraInput::setObserver(
PvmiConfigAndCapabilityCmdObserver* aObserver)
{
LOGV("setObserver");
OSCL_UNUSED_ARG(aObserver);
}
PVMFStatus AndroidCameraInput::getParametersSync(PvmiMIOSession session,
PvmiKeyType identifier,
PvmiKvp*& params,
int& num_params,
PvmiCapabilityContext context)
{
LOGV("getParametersSync");
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
params = NULL;
num_params = 0;
PVMFStatus status = PVMFFailure;
if (!pv_mime_strcmp(identifier, OUTPUT_FORMATS_CAP_QUERY) ||
!pv_mime_strcmp(identifier, OUTPUT_FORMATS_CUR_QUERY)) {
num_params = 1;
status = AllocateKvp(params, (PvmiKeyType)OUTPUT_FORMATS_VALTYPE, num_params);
if (status != PVMFSuccess) {
LOGE("AllocateKvp failed for OUTPUT_FORMATS_VALTYP");
return status;
}
params[0].value.pChar_value = (char*)ANDROID_VIDEO_FORMAT;
} else if (!pv_mime_strcmp(identifier, VIDEO_OUTPUT_WIDTH_CUR_QUERY)) {
num_params = 1;
status = AllocateKvp(params, (PvmiKeyType)VIDEO_OUTPUT_WIDTH_CUR_VALUE, num_params);
if (status != PVMFSuccess) {
LOGE("AllocateKvp failed for VIDEO_OUTPUT_WIDTH_CUR_VALUE");
return status;
}
params[0].value.uint32_value = mFrameWidth;
} else if (!pv_mime_strcmp(identifier, VIDEO_OUTPUT_HEIGHT_CUR_QUERY)) {
num_params = 1;
status = AllocateKvp(params, (PvmiKeyType)VIDEO_OUTPUT_HEIGHT_CUR_VALUE, num_params);
if (status != PVMFSuccess) {
LOGE("AllocateKvp failed for VIDEO_OUTPUT_HEIGHT_CUR_VALUE");
return status;
}
params[0].value.uint32_value = mFrameHeight;
} else if (!pv_mime_strcmp(identifier, VIDEO_OUTPUT_FRAME_RATE_CUR_QUERY)) {
num_params = 1;
status = AllocateKvp(params,
(PvmiKeyType)VIDEO_OUTPUT_FRAME_RATE_CUR_VALUE, num_params);
if (status != PVMFSuccess) {
LOGE("AllocateKvp failed for VIDEO_OUTPUT_FRAME_RATE_CUR_VALUE");
return status;
}
params[0].value.float_value = mFrameRate;
} else if (!pv_mime_strcmp(identifier, OUTPUT_TIMESCALE_CUR_QUERY)) {
num_params = 1;
status = AllocateKvp(params, (PvmiKeyType)OUTPUT_TIMESCALE_CUR_VALUE, num_params);
if (status != PVMFSuccess) {
LOGE("AllocateKvp failed for OUTPUT_TIMESCALE_CUR_VALUE");
return status;
}
// TODO:
// is it okay to hardcode this as the timescale?
params[0].value.uint32_value = 1000;
} else if (!pv_mime_strcmp(identifier, PVMF_BUFFER_ALLOCATOR_KEY)) {
/*
* if( camera MIO does NOT allocate YUV buffers )
* {
* OSCL_LEAVE(OsclErrNotSupported);
* return PVMFErrNotSupported;
* }
*/
params = (PvmiKvp*)oscl_malloc(sizeof(PvmiKvp));
if (!params )
{
OSCL_LEAVE(OsclErrNoMemory);
return PVMFErrNoMemory;
}
params [0].value.key_specific_value = (PVInterface*)&mbufferAlloc;
status = PVMFSuccess;
}
return status;
}
PVMFStatus AndroidCameraInput::releaseParameters(PvmiMIOSession session,
PvmiKvp* parameters,
int num_elements)
{
LOGV("releaseParameters");
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(num_elements);
if (!parameters) {
LOGE("parameters is a NULL pointer");
return PVMFFailure;
}
iAlloc.deallocate((OsclAny*)parameters);
return PVMFSuccess;
}
void AndroidCameraInput::createContext(PvmiMIOSession session,
PvmiCapabilityContext& context)
{
LOGV("createContext");
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
}
void AndroidCameraInput::setContextParameters(PvmiMIOSession session,
PvmiCapabilityContext& context,
PvmiKvp* parameters,
int num_parameter_elements)
{
LOGV("setContextParameters");
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
OSCL_UNUSED_ARG(parameters);
OSCL_UNUSED_ARG(num_parameter_elements);
}
void AndroidCameraInput::DeleteContext(PvmiMIOSession session,
PvmiCapabilityContext& context)
{
LOGV("DeleteContext");
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(context);
}
void AndroidCameraInput::setParametersSync(PvmiMIOSession session,
PvmiKvp* parameters,
int num_elements,
PvmiKvp*& ret_kvp)
{
LOGV("setParametersSync");
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) {
LOGE("VerifiyAndSetParameter failed on parameter #%d", i);
ret_kvp = &(parameters[i]);
OSCL_LEAVE(OsclErrArgument);
return;
}
}
}
PVMFCommandId AndroidCameraInput::setParametersAsync(PvmiMIOSession session,
PvmiKvp* parameters,
int num_elements,
PvmiKvp*& ret_kvp,
OsclAny* context)
{
LOGV("setParametersAsync");
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;
}
uint32 AndroidCameraInput::getCapabilityMetric (PvmiMIOSession session)
{
LOGV("getCapabilityMetric");
OSCL_UNUSED_ARG(session);
return 0;
}
PVMFStatus AndroidCameraInput::verifyParametersSync(PvmiMIOSession session,
PvmiKvp* parameters,
int num_elements)
{
LOGV("verifyParametersSync");
OSCL_UNUSED_ARG(session);
OSCL_UNUSED_ARG(parameters);
OSCL_UNUSED_ARG(num_elements);
return PVMFErrNotSupported;
}
void AndroidCameraInput::SetFrameSize(int w, int h)
{
LOGV("SetFrameSize");
if (iState != STATE_IDLE) {
LOGE("SetFrameSize called in an invalid state(%d)", iState);
return;
}
mFrameWidth = w;
mFrameHeight = h;
FrameSizeChanged();
}
void AndroidCameraInput::SetFrameRate(int fps)
{
LOGV("SetFrameRate");
if (iState != STATE_IDLE) {
LOGE("SetFrameRate called in an invalid state(%d)", iState);
return;
}
mFrameRate = (float)fps;
}
void AndroidCameraInput::FrameSizeChanged()
{
LOGV("FrameSizeChanged");
if (iState != STATE_IDLE) {
LOGE("FrameSizeChanged called in an invalid state(%d)", iState);
return;
}
// Reinitialize the preview surface in case it was set up before now
if (mSurface != NULL) {
SetPreviewSurface(mSurface);
}
}
void AndroidCameraInput::Run()
{
LOGV("Run");
// dequeue frame buffers and write to peer
if (NULL != iPeer) {
if (iState != STATE_STARTED) {
ReleaseQueuedFrames();
}
iFrameQueueMutex.Lock();
while (!iFrameQueue.empty()) {
AndroidCameraInputMediaData data = iFrameQueue[0];
uint32 writeAsyncID = 0;
OsclLeaveCode error = OsclErrNone;
uint8 *ptr = (uint8*) (data.iFrameBuffer->pointer());
if (ptr) {
OSCL_TRY(error,writeAsyncID = iPeer->writeAsync(PVMI_MEDIAXFER_FMT_TYPE_DATA, 0, ptr,
data.iFrameSize, data.iXferHeader););
} else {
//FIXME Check why camera sends NULL frames
LOGE("Ln %d ERROR null pointer", __LINE__);
error = OsclErrBadHandle;
}
if (OsclErrNone == error) {
iFrameQueue.erase(iFrameQueue.begin());
data.iId = writeAsyncID;
iSentMediaData.push_back(data);
LOGV("Ln %d Run writeAsync writeAsyncID %d", __LINE__, writeAsyncID);
} else {
//FIXME resend the frame later if ( OsclErrBusy == error)
LOGE("Ln %d Run writeAsync error %d", __LINE__, error);
//release buffer immediately if write fails
mCamera->releaseRecordingFrame(data.iFrameBuffer);
iFrameQueue.erase(iFrameQueue.begin());
iWriteState = EWriteBusy;
break;
}
}
iFrameQueueMutex.Unlock();
}
PVMFStatus status = PVMFFailure;
if (!iCmdQueue.empty()) {
AndroidCameraInputCmd cmd = iCmdQueue[0];
iCmdQueue.erase(iCmdQueue.begin());
switch(cmd.iType) {
case CMD_INIT:
status = DoInit();
break;
case CMD_START:
status = DoStart();
break;
case CMD_PAUSE:
status = DoPause();
break;
case CMD_FLUSH:
status = DoFlush(cmd);
break;
case CMD_RESET:
status = DoReset();
break;
case CMD_STOP:
status = DoStop(cmd);
break;
case DATA_EVENT:
// this is internal only - don't send RequestCompleted
DoRead();
status = PVMFPending;
break;
case CMD_QUERY_UUID:
case CMD_QUERY_INTERFACE:
status = PVMFSuccess;
break;
// these commands all fail
case CMD_CANCEL_ALL_COMMANDS:
case CMD_CANCEL_COMMAND:
default:
break;
}
// do RequestCompleted unless command is still pending
if (status != PVMFPending) {
DoRequestCompleted(cmd, status);
}
}
if (!iCmdQueue.empty()) {
// Run again if there are more things to process
RunIfNotReady();
}
}
PVMFCommandId AndroidCameraInput::AddCmdToQueue(AndroidCameraInputCmdType aType,
const OsclAny* aContext,
OsclAny* aData)
{
LOGV("AddCmdToQueue");
if (aType == DATA_EVENT) {
LOGE("Invalid argument");
OSCL_LEAVE(OsclErrArgument);
return -1;
}
AndroidCameraInputCmd cmd;
cmd.iType = aType;
cmd.iContext = OSCL_STATIC_CAST(OsclAny*, aContext);
cmd.iData = aData;
cmd.iId = iCmdIdCounter;
++iCmdIdCounter;
// TODO:
// Check against out of memory failure
int err = 0;
OSCL_TRY(err, iCmdQueue.push_back(cmd));
OSCL_FIRST_CATCH_ANY(err, LOGE("Out of memory"); return -1;);
RunIfNotReady();
return cmd.iId;
}
void AndroidCameraInput::AddDataEventToQueue(uint32 aMicroSecondsToEvent)
{
LOGV("AddDataEventToQueue");
AndroidCameraInputCmd cmd;
cmd.iType = DATA_EVENT;
int err = 0;
OSCL_TRY(err, iCmdQueue.push_back(cmd));
OSCL_FIRST_CATCH_ANY(err, LOGE("Out of memory"); return;);
RunIfNotReady(aMicroSecondsToEvent);
}
void AndroidCameraInput::DoRequestCompleted(const AndroidCameraInputCmd& aCmd, PVMFStatus aStatus, OsclAny* aEventData)
{
LOGV("DoRequestCompleted");
PVMFCmdResp response(aCmd.iId, aCmd.iContext, aStatus, aEventData);
for (uint32 i = 0; i < iObservers.size(); i++) {
iObservers[i]->RequestCompleted(response);
}
}
PVMFStatus AndroidCameraInput::DoInit()
{
LOGV("DoInit()");
iState = STATE_INITIALIZED;
iMilliSecondsPerDataEvent = (int32)(1000 / mFrameRate);
iMicroSecondsPerDataEvent = (int32)(1000000 / mFrameRate);
iDataEventCounter = 0;
// create a camera if the app didn't supply one
if (mCamera == 0) {
mCamera = Camera::connect();
}
// always call setPreviewDisplay() regardless whether mCamera is just created or not
// return failure if no display surface is available
if (mCamera != NULL && mSurface != NULL) {
mCamera->setPreviewDisplay(mSurface);
} else {
if (mCamera == NULL) {
LOGE("Camera is not available");
} else if (mSurface == NULL) {
LOGE("No surface is available for display");
}
return PVMFFailure;
}
LOGD("Intended mFrameWidth=%d, mFrameHeight=%d ",mFrameWidth, mFrameHeight);
String8 s = mCamera->getParameters();
if (s.length() == 0) {
LOGE("Failed to get camera(%p) parameters", mCamera.get());
return PVMFFailure;
}
CameraParameters p(s);
p.setPreviewSize(mFrameWidth, mFrameHeight);
p.setPreviewFrameRate(mFrameRate);
s = p.flatten();
if (mCamera->setParameters(s) != NO_ERROR) {
LOGE("Failed to set camera(%p) parameters", mCamera.get());
return PVMFFailure;
}
// Since we may not honor the preview size that app has requested
// It is a good idea to get the actual preview size and used it
// for video recording.
CameraParameters newCameraParam(mCamera->getParameters());
int32 width, height;
newCameraParam.getPreviewSize(&width, &height);
if (width < 0 || height < 0) {
LOGE("Failed to get camera(%p) preview size", mCamera.get());
return PVMFFailure;
}
if (width != mFrameWidth || height != mFrameHeight) {
LOGE("Mismatch between the intended frame size (%dx%d) and the available frame size (%dx%d)", mFrameWidth, mFrameHeight, width, height);
return PVMFFailure;
}
LOGD("Actual mFrameWidth=%d, mFrameHeight=%d ",mFrameWidth, mFrameHeight);
if (mCamera->startPreview() != NO_ERROR) {
LOGE("Failed to start camera(%p) preview", mCamera.get());
return PVMFFailure;
}
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::DoStart()
{
LOGV("DoStart");
iAudioFirstFrameTs = 0;
// Set the clock state observer
if (iAuthorClock) {
iAuthorClock->ConstructMediaClockNotificationsInterface(iClockNotificationsInf, *this);
if (iClockNotificationsInf == NULL) {
return PVMFErrNoMemory;
}
iClockNotificationsInf->SetClockStateObserver(*this);
}
PVMFStatus status = PVMFFailure;
iWriteState = EWriteOK;
if (mCamera == NULL) {
status = PVMFFailure;
LOGE("mCamera is not initialized yet");
} else {
mCamera->setListener(mListener);
if (mCamera->startRecording() != NO_ERROR) {
LOGE("mCamera start recording failed");
status = PVMFFailure;
} else {
iState = STATE_STARTED;
status = PVMFSuccess;
}
}
AddDataEventToQueue(iMilliSecondsPerDataEvent);
return status;
}
PVMFStatus AndroidCameraInput::DoPause()
{
LOGV("DoPause");
iState = STATE_PAUSED;
return PVMFSuccess;
}
// Does this work for reset?
PVMFStatus AndroidCameraInput::DoReset()
{
LOGD("DoReset: E");
// Remove and destroy the clock state observer
RemoveDestroyClockObs();
iDataEventCounter = 0;
iAudioLossDuration = 0;
iWriteState = EWriteOK;
if ( (iState == STATE_STARTED) || (iState == STATE_PAUSED) ) {
if (mCamera != NULL) {
mCamera->setListener(NULL);
mCamera->stopRecording();
ReleaseQueuedFrames();
}
}
while(!iCmdQueue.empty())
{
AndroidCameraInputCmd cmd = iCmdQueue[0];
iCmdQueue.erase(iCmdQueue.begin());
}
Cancel();
iState = STATE_IDLE;
LOGD("DoReset: X");
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::DoFlush(const AndroidCameraInputCmd& aCmd)
{
LOGV("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(aCmd);
}
PVMFStatus AndroidCameraInput::DoStop(const AndroidCameraInputCmd& aCmd)
{
LOGD("DoStop: E");
// Remove and destroy the clock state observer
RemoveDestroyClockObs();
iDataEventCounter = 0;
iWriteState = EWriteOK;
if (mCamera != NULL) {
mCamera->setListener(NULL);
mCamera->stopRecording();
ReleaseQueuedFrames();
}
iState = STATE_STOPPED;
LOGD("DoStop: X");
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::DoRead()
{
LOGV("DoRead");
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::AllocateKvp(PvmiKvp*& aKvp,
PvmiKeyType aKey,
int32 aNumParams)
{
LOGV("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) {
LOGE("Failed to allocate memory to Kvp");
OSCL_LEAVE(OsclErrNoMemory);
}
);
OSCL_FIRST_CATCH_ANY(err,
LOGE("kvp allocation failed");
return PVMFErrNoMemory;
);
PvmiKvp* curKvp = aKvp = new (buf) PvmiKvp;
buf += sizeof(PvmiKvp);
for (int32 i = 1; i < aNumParams; ++i) {
curKvp += i;
curKvp = new (buf) PvmiKvp;
buf += sizeof(PvmiKvp);
}
for (int32 i = 0; i < aNumParams; ++i) {
aKvp[i].key = (char*)buf;
oscl_strncpy(aKvp[i].key, aKey, keyLen);
buf += keyLen;
}
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::VerifyAndSetParameter(PvmiKvp* aKvp,
bool aSetParam)
{
LOGV("VerifyAndSetParameter");
if (!aKvp) {
LOGE("Invalid key-value pair");
return PVMFFailure;
}
if (!pv_mime_strcmp(aKvp->key, OUTPUT_FORMATS_VALTYPE)) {
if(pv_mime_strcmp(aKvp->value.pChar_value, ANDROID_VIDEO_FORMAT) == 0) {
return PVMFSuccess;
} else {
LOGE("Unsupported format %d", aKvp->value.uint32_value);
return PVMFFailure;
}
}
else if (pv_mime_strcmp(aKvp->key, PVMF_AUTHORING_CLOCK_KEY) == 0)
{
LOGV("AndroidCameraInput::VerifyAndSetParameter() PVMF_AUTHORING_CLOCK_KEY value %p", aKvp->value.key_specific_value);
if( (NULL == aKvp->value.key_specific_value) && ( iAuthorClock ) )
{
RemoveDestroyClockObs();
}
iAuthorClock = (PVMFMediaClock*)aKvp->value.key_specific_value;
return PVMFSuccess;
}
LOGE("Unsupported parameter(%s)", aKvp->key);
return PVMFFailure;
}
void AndroidCameraInput::SetPreviewSurface(const sp<android::ISurface>& surface)
{
LOGV("SetPreviewSurface");
mSurface = surface;
if (mCamera != NULL) {
mCamera->setPreviewDisplay(surface);
}
}
PVMFStatus AndroidCameraInput::SetCamera(const sp<android::ICamera>& camera)
{
LOGV("SetCamera");
mFlags &= ~ FLAGS_SET_CAMERA | FLAGS_HOT_CAMERA;
if (camera == NULL) {
LOGV("camera is NULL");
return PVMFErrArgument;
}
// Connect our client to the camera remote
mCamera = Camera::create(camera);
if (mCamera == NULL) {
LOGE("Unable to connect to camera");
return PVMFErrNoResources;
}
LOGV("Connected to camera");
mFlags |= FLAGS_SET_CAMERA;
if (mCamera->previewEnabled()) {
mFlags |= FLAGS_HOT_CAMERA;
LOGV("camera is hot");
}
return PVMFSuccess;
}
PVMFStatus AndroidCameraInput::postWriteAsync(nsecs_t timestamp, const sp<IMemory>& frame)
{
LOGV("postWriteAsync");
if (frame == NULL) {
LOGE("frame is a NULL pointer");
return PVMFFailure;
}
if((!iPeer) || (!isRecorderStarting()) || (iWriteState == EWriteBusy) || (NULL == iAuthorClock) || (iAuthorClock->GetState() != PVMFMediaClock::RUNNING)) {
if( NULL == iAuthorClock )
{
LOGE("Recording is not ready (iPeer %p iState %d iWriteState %d iAuthorClock NULL), frame dropped", iPeer, iState, iWriteState);
}
else
{
LOGE("Recording is not ready (iPeer %p iState %d iWriteState %d iClockState %d), frame dropped", iPeer, iState, iWriteState, iAuthorClock->GetState());
}
mCamera->releaseRecordingFrame(frame);
return PVMFSuccess;
}
// Now compare the video timestamp with the AudioFirstTimestamp
// if video timestamp is earlier to audio drop it
// or else send it downstream with correct timestamp
uint32 ts = (uint32)(timestamp / 1000000L);
// In cases of Video Only recording iAudioFirstFrameTs will always be zero,
// so for such cases assign iAudioFirstFrameTs to Video's first sample TS
// which will make Video samples to start with Timestamp zero.
if (iAudioFirstFrameTs == 0)
iAudioFirstFrameTs = ts;
iAudioLossMutex.Lock();
if (ts < iAudioFirstFrameTs + iAudioLossDuration) {
// Drop the frame
iAudioLossMutex.Unlock();
mCamera->releaseRecordingFrame(frame);
return PVMFSuccess;
} else {
// calculate timestamp as offset from start time plus lost audio
ts -= (iAudioFirstFrameTs + iAudioLossDuration);
}
iAudioLossMutex.Unlock();
// Determine how much video to drop so when we resync to the audio
// time we don't go into old video (we need increasing timestamps)
if (ts <= iTimeStamp) {
LOGD("Dropping video frame to catch up for audio ts %lu, dropUntil %lu", ts, iTimeStamp);
mCamera->releaseRecordingFrame(frame);
return PVMFSuccess;
}
// Make sure that no two samples have the same timestamp
if (iDataEventCounter != 0) {
if (iTimeStamp != ts) {
iTimeStamp = ts;
} else {
++iTimeStamp;
}
}
// get memory offset for frame buffer
ssize_t offset = 0;
size_t size = 0;
sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
LOGV("postWriteAsync: ID = %d, base = %p, offset = %ld, size = %d pointer %p", heap->getHeapID(), heap->base(), offset, size, frame->pointer());
// queue data to be sent to peer
AndroidCameraInputMediaData data;
data.iXferHeader.seq_num = iDataEventCounter++;
data.iXferHeader.timestamp = iTimeStamp;
data.iXferHeader.flags = 0;
data.iXferHeader.duration = 0;
data.iXferHeader.stream_id = 0;
data.iFrameSize = size;
data.iFrameBuffer = frame;
// lock muteddx and queue frame buffer
iFrameQueueMutex.Lock();
{//compose private data
//could size be zero?
if(NULL == pPmemInfo)
{
int iCalculateNoOfCameraPreviewBuffer = heap->getSize() / size;
LOGV("heap->getSize() = %d, size of each frame= %d, iCalculateNoOfCameraPreviewBuffer = %d", heap->getSize(), size, iCalculateNoOfCameraPreviewBuffer);
pPmemInfo = new CAMERA_PMEM_INFO[iCalculateNoOfCameraPreviewBuffer];
if(NULL == pPmemInfo)
{
LOGE("Failed to allocate the camera pmem info buffer array. iCalculateNoOfCameraPreviewBuffer %d",iCalculateNoOfCameraPreviewBuffer);
iFrameQueueMutex.Unlock();
return PVMFFailure;
}
}
int iIndex = offset / size;
pPmemInfo[iIndex].pmem_fd = heap->getHeapID();
pPmemInfo[iIndex].offset = offset;
data.iXferHeader.private_data_ptr = ((OsclAny*)(&pPmemInfo[iIndex]));
LOGV("struct size %d, pmem_info - %x, &pmem_info[iIndex] - %x, iIndex =%d, pmem_info.pmem_fd = %d, pmem_info.offset = %d", sizeof(CAMERA_PMEM_INFO), pPmemInfo, &pPmemInfo[iIndex], iIndex, pPmemInfo[iIndex].pmem_fd, pPmemInfo[iIndex].offset );
}
iFrameQueue.push_back(data);
iFrameQueueMutex.Unlock();
RunIfNotReady();
return PVMFSuccess;
}
// Value is expected to be in ms
void AndroidCameraInput::setAudioLossDuration(uint32 duration)
{
iAudioLossMutex.Lock();
LOGD("Update for lost audio for %lu for existing duration %lu", duration, iAudioLossDuration);
iAudioLossDuration += duration;
iAudioLossMutex.Unlock();
}
// camera callback interface
void AndroidCameraInputListener::postData(int32_t msgType, const sp<IMemory>& dataPtr)
{
}
void AndroidCameraInputListener::postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
{
if ((mCameraInput != NULL) && (msgType == CAMERA_MSG_VIDEO_FRAME)) {
mCameraInput->postWriteAsync(timestamp, dataPtr);
}
}
void AndroidCameraInput::NotificationsInterfaceDestroyed()
{
iClockNotificationsInf = NULL;
}
void AndroidCameraInput::ClockStateUpdated()
{
PVMFMediaClock::PVMFMediaClockState iClockState = iAuthorClock->GetState();
if ((iClockState == PVMFMediaClock::RUNNING) && (iAudioFirstFrameTs == 0)) {
// Get the clock time here
// this will be the time of first audio frame capture
bool tmpbool = false;
iAuthorClock->GetCurrentTime32(iAudioFirstFrameTs, tmpbool, PVMF_MEDIA_CLOCK_MSEC);
LOGV("Audio first ts %d", iAudioFirstFrameTs);
}
}
void AndroidCameraInput::RemoveDestroyClockObs()
{
if (iAuthorClock != NULL) {
if (iClockNotificationsInf != NULL) {
iClockNotificationsInf->RemoveClockStateObserver(*this);
iAuthorClock->DestroyMediaClockNotificationsInterface(iClockNotificationsInf);
iClockNotificationsInf = NULL;
}
}
}