/*
* Copyright (C) Texas Instruments - http://www.ti.com/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ANativeWindowDisplayAdapter.h"
#include <OMX_IVCommon.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferMapper.h>
#include <hal_public.h>
namespace Ti {
namespace Camera {
///Constant declarations
///@todo Check the time units
const int ANativeWindowDisplayAdapter::DISPLAY_TIMEOUT = 1000; // seconds
//Suspends buffers after given amount of failed dq's
const int ANativeWindowDisplayAdapter::FAILED_DQS_TO_SUSPEND = 3;
OMX_COLOR_FORMATTYPE toOMXPixFormat(const char* parameters_format)
{
OMX_COLOR_FORMATTYPE pixFormat;
if ( parameters_format != NULL )
{
if (strcmp(parameters_format, android::CameraParameters::PIXEL_FORMAT_YUV422I) == 0)
{
CAMHAL_LOGDA("CbYCrY format selected");
pixFormat = OMX_COLOR_FormatCbYCrY;
}
else if(strcmp(parameters_format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0)
{
CAMHAL_LOGDA("YUV420SP format selected");
pixFormat = OMX_COLOR_FormatYUV420SemiPlanar;
}
else if(strcmp(parameters_format, android::CameraParameters::PIXEL_FORMAT_RGB565) == 0)
{
CAMHAL_LOGDA("RGB565 format selected");
pixFormat = OMX_COLOR_Format16bitRGB565;
}
else
{
CAMHAL_LOGDA("Invalid format, NV12 format selected as default");
pixFormat = OMX_COLOR_FormatYUV420SemiPlanar;
}
}
else {
CAMHAL_LOGEA("Preview format is NULL, defaulting to NV12");
pixFormat = OMX_COLOR_FormatYUV420SemiPlanar;
}
return pixFormat;
}
/*--------------------ANativeWindowDisplayAdapter Class STARTS here-----------------------------*/
/**
* Display Adapter class STARTS here..
*/
ANativeWindowDisplayAdapter::ANativeWindowDisplayAdapter():mDisplayThread(NULL),
mDisplayState(ANativeWindowDisplayAdapter::DISPLAY_INIT),
mDisplayEnabled(false),
mBufferCount(0)
{
LOG_FUNCTION_NAME;
#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
mShotToShot = false;
mStartCapture.tv_sec = 0;
mStartCapture.tv_usec = 0;
mStandbyToShot.tv_sec = 0;
mStandbyToShot.tv_usec = 0;
mMeasureStandby = false;
#endif
mPixelFormat = NULL;
mBuffers = NULL;
mOffsetsMap = NULL;
mFrameProvider = NULL;
mANativeWindow = NULL;
mFrameWidth = 0;
mFrameHeight = 0;
mPreviewWidth = 0;
mPreviewHeight = 0;
mSuspend = false;
mFailedDQs = 0;
mPaused = false;
mXOff = -1;
mYOff = -1;
mFirstInit = false;
mFD = -1;
LOG_FUNCTION_NAME_EXIT;
}
ANativeWindowDisplayAdapter::~ANativeWindowDisplayAdapter()
{
Utils::Semaphore sem;
Utils::Message msg;
LOG_FUNCTION_NAME;
///If Frame provider exists
if (mFrameProvider) {
// Unregister with the frame provider
mFrameProvider->disableFrameNotification(CameraFrame::ALL_FRAMES);
delete mFrameProvider;
mFrameProvider = NULL;
}
///The ANativeWindow object will get destroyed here
destroy();
///If Display thread exists
if(mDisplayThread.get())
{
///Kill the display thread
sem.Create();
msg.command = DisplayThread::DISPLAY_EXIT;
// Send the semaphore to signal once the command is completed
msg.arg1 = &sem;
///Post the message to display thread
mDisplayThread->msgQ().put(&msg);
///Wait for the ACK - implies that the thread is now started and waiting for frames
sem.Wait();
// Exit and cleanup the thread
mDisplayThread->requestExitAndWait();
// Delete the display thread
mDisplayThread.clear();
}
LOG_FUNCTION_NAME_EXIT;
}
status_t ANativeWindowDisplayAdapter::initialize()
{
LOG_FUNCTION_NAME;
///Create the display thread
mDisplayThread = new DisplayThread(this);
if ( !mDisplayThread.get() )
{
CAMHAL_LOGEA("Couldn't create display thread");
LOG_FUNCTION_NAME_EXIT;
return NO_MEMORY;
}
///Start the display thread
status_t ret = mDisplayThread->run("DisplayThread", android::PRIORITY_URGENT_DISPLAY);
if ( ret != NO_ERROR )
{
CAMHAL_LOGEA("Couldn't run display thread");
LOG_FUNCTION_NAME_EXIT;
return ret;
}
LOG_FUNCTION_NAME_EXIT;
return ret;
}
int ANativeWindowDisplayAdapter::setPreviewWindow(preview_stream_ops_t* window)
{
LOG_FUNCTION_NAME;
///Note that Display Adapter cannot work without a valid window object
if ( !window)
{
CAMHAL_LOGEA("NULL window object passed to DisplayAdapter");
LOG_FUNCTION_NAME_EXIT;
return BAD_VALUE;
}
if ( window == mANativeWindow ) {
return ALREADY_EXISTS;
}
///Destroy the existing window object, if it exists
destroy();
///Move to new window obj
mANativeWindow = window;
LOG_FUNCTION_NAME_EXIT;
return NO_ERROR;
}
int ANativeWindowDisplayAdapter::setFrameProvider(FrameNotifier *frameProvider)
{
LOG_FUNCTION_NAME;
// Check for NULL pointer
if ( !frameProvider ) {
CAMHAL_LOGEA("NULL passed for frame provider");
LOG_FUNCTION_NAME_EXIT;
return BAD_VALUE;
}
//Release any previous frame providers
if ( NULL != mFrameProvider ) {
delete mFrameProvider;
}
/** Dont do anything here, Just save the pointer for use when display is
actually enabled or disabled
*/
mFrameProvider = new FrameProvider(frameProvider, this, frameCallbackRelay);
LOG_FUNCTION_NAME_EXIT;
return NO_ERROR;
}
int ANativeWindowDisplayAdapter::setErrorHandler(ErrorNotifier *errorNotifier)
{
status_t ret = NO_ERROR;
LOG_FUNCTION_NAME;
if ( NULL == errorNotifier ) {
CAMHAL_LOGEA("Invalid Error Notifier reference");
ret = BAD_VALUE;
}
if ( NO_ERROR == ret )
{
mErrorNotifier = errorNotifier;
}
LOG_FUNCTION_NAME_EXIT;
return ret;
}
#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
status_t ANativeWindowDisplayAdapter::setSnapshotTimeRef(struct timeval *refTime)
{
status_t ret = NO_ERROR;
LOG_FUNCTION_NAME;
if ( NULL != refTime )
{
android::AutoMutex lock(mLock);
memcpy(&mStartCapture, refTime, sizeof(struct timeval));
}
LOG_FUNCTION_NAME_EXIT;
return ret;
}
#endif
int ANativeWindowDisplayAdapter::enableDisplay(int width, int height, struct timeval *refTime)
{
Utils::Semaphore sem;
Utils::Message msg;
LOG_FUNCTION_NAME;
if ( mDisplayEnabled )
{
CAMHAL_LOGDA("Display is already enabled");
LOG_FUNCTION_NAME_EXIT;
return NO_ERROR;
}
#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
if ( NULL != refTime )
{
android::AutoMutex lock(mLock);
memcpy(&mStandbyToShot, refTime, sizeof(struct timeval));
mMeasureStandby = true;
}
#endif
//Send START_DISPLAY COMMAND to display thread. Display thread will start and then wait for a message
sem.Create();
msg.command = DisplayThread::DISPLAY_START;
// Send the semaphore to signal once the command is completed
msg.arg1 = &sem;
///Post the message to display thread
mDisplayThread->msgQ().put(&msg);
///Wait for the ACK - implies that the thread is now started and waiting for frames
sem.Wait();
// Register with the frame provider for frames
mFrameProvider->enableFrameNotification(CameraFrame::PREVIEW_FRAME_SYNC);
mFrameProvider->enableFrameNotification(CameraFrame::SNAPSHOT_FRAME);
mDisplayEnabled = true;
mPreviewWidth = width;
mPreviewHeight = height;
CAMHAL_LOGVB("mPreviewWidth = %d mPreviewHeight = %d", mPreviewWidth, mPreviewHeight);
LOG_FUNCTION_NAME_EXIT;
return NO_ERROR;
}
int ANativeWindowDisplayAdapter::disableDisplay(bool cancel_buffer)
{
status_t ret = NO_ERROR;
android::GraphicBufferMapper &mapper = android::GraphicBufferMapper::get();
LOG_FUNCTION_NAME;
if(!mDisplayEnabled)
{
CAMHAL_LOGDA("Display is already disabled");
LOG_FUNCTION_NAME_EXIT;
return ALREADY_EXISTS;
}
// Unregister with the frame provider here
mFrameProvider->disableFrameNotification(CameraFrame::PREVIEW_FRAME_SYNC);
mFrameProvider->disableFrameNotification(CameraFrame::SNAPSHOT_FRAME);
mFrameProvider->removeFramePointers();
if ( NULL != mDisplayThread.get() )
{
//Send STOP_DISPLAY COMMAND to display thread. Display thread will stop and dequeue all messages
// and then wait for message
Utils::Semaphore sem;
sem.Create();
Utils::Message msg;
msg.command = DisplayThread::DISPLAY_STOP;
// Send the semaphore to signal once the command is completed
msg.arg1 = &sem;
///Post the message to display thread
mDisplayThread->msgQ().put(&msg);
///Wait for the ACK for display to be disabled
sem.Wait();
}
android::AutoMutex lock(mLock);
{
///Reset the display enabled flag
mDisplayEnabled = false;
// Reset pause flag since display is being disabled
mPaused = false;
///Reset the offset values
mXOff = -1;
mYOff = -1;
///Reset the frame width and height values
mFrameWidth =0;
mFrameHeight = 0;
mPreviewWidth = 0;
mPreviewHeight = 0;
if(cancel_buffer)
{
// Return the buffers to ANativeWindow here, the mFramesWithCameraAdapterMap is also cleared inside
returnBuffersToWindow();
}
else
{
mANativeWindow = NULL;
// Clear the frames with camera adapter map
mFramesWithCameraAdapterMap.clear();
}
}
LOG_FUNCTION_NAME_EXIT;
return NO_ERROR;
}
status_t ANativeWindowDisplayAdapter::pauseDisplay(bool pause)
{
status_t ret = NO_ERROR;
LOG_FUNCTION_NAME;
{
android::AutoMutex lock(mLock);
mPaused = pause;
}
LOG_FUNCTION_NAME_EXIT;
return ret;
}
void ANativeWindowDisplayAdapter::destroy()
{
LOG_FUNCTION_NAME;
///Check if the display is disabled, if not disable it
if ( mDisplayEnabled )
{
CAMHAL_LOGDA("WARNING: Calling destroy of Display adapter when display enabled. Disabling display..");
disableDisplay(false);
}
mBufferCount = 0;
LOG_FUNCTION_NAME_EXIT;
}
// Implementation of inherited interfaces
CameraBuffer* ANativeWindowDisplayAdapter::allocateBufferList(int width, int height, const char* format, int &bytes, int numBufs)
{
LOG_FUNCTION_NAME;
status_t err;
int i = -1;
const int lnumBufs = numBufs;
int undequeued = 0;
android::GraphicBufferMapper &mapper = android::GraphicBufferMapper::get();
android::Rect bounds;
mBuffers = new CameraBuffer [lnumBufs];
memset (mBuffers, 0, sizeof(CameraBuffer) * lnumBufs);
mFramesType.clear();
if ( NULL == mANativeWindow ) {
return NULL;
}
// Set gralloc usage bits for window.
err = mANativeWindow->set_usage(mANativeWindow, CAMHAL_GRALLOC_USAGE);
if ( NO_ERROR != err ) {
CAMHAL_LOGE("Surface::setUsage failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
return NULL;
}
CAMHAL_LOGDB("Number of buffers set to ANativeWindow %d", numBufs);
///Set the number of buffers needed for camera preview
err = mANativeWindow->set_buffer_count(mANativeWindow, numBufs);
if ( NO_ERROR != err ) {
CAMHAL_LOGE("Surface::setBufferCount failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
return NULL;
}
CAMHAL_LOGDB("Configuring %d buffers for ANativeWindow", numBufs);
mBufferCount = numBufs;
// Set window geometry
err = mANativeWindow->set_buffers_geometry(
mANativeWindow,
width,
height,
/*toOMXPixFormat(format)*/HAL_PIXEL_FORMAT_TI_NV12); // Gralloc only supports NV12 alloc!
if ( NO_ERROR != err ) {
CAMHAL_LOGE("native_window_set_buffers_geometry failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
return NULL;
}
///We just return the buffers from ANativeWindow, if the width and height are same, else (vstab, vnf case)
///re-allocate buffers using ANativeWindow and then get them
///@todo - Re-allocate buffers for vnf and vstab using the width, height, format, numBufs etc
if ( mBuffers == NULL )
{
CAMHAL_LOGEA("Couldn't create array for ANativeWindow buffers");
LOG_FUNCTION_NAME_EXIT;
return NULL;
}
mANativeWindow->get_min_undequeued_buffer_count(mANativeWindow, &undequeued);
mPixelFormat = CameraHal::getPixelFormatConstant(format);
for ( i=0; i < mBufferCount; i++ )
{
buffer_handle_t *handle;
int stride; // dummy variable to get stride
// TODO(XXX): Do we need to keep stride information in camera hal?
err = mANativeWindow->dequeue_buffer(mANativeWindow, &handle, &stride);
if ( NO_ERROR != err ) {
CAMHAL_LOGE("Surface::dequeueBuffer failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
goto fail;
}
CAMHAL_LOGDB("got handle %p", handle);
mBuffers[i].opaque = (void *)handle;
mBuffers[i].type = CAMERA_BUFFER_ANW;
mBuffers[i].format = mPixelFormat;
mFramesWithCameraAdapterMap.add(handle, i);
// Tag remaining preview buffers as preview frames
if ( i >= ( mBufferCount - undequeued ) ) {
mFramesType.add( (int) mBuffers[i].opaque,
CameraFrame::PREVIEW_FRAME_SYNC);
}
bytes = CameraHal::calculateBufferSize(format, width, height);
}
// lock the initial queueable buffers
bounds.left = 0;
bounds.top = 0;
bounds.right = width;
bounds.bottom = height;
for( i = 0; i < mBufferCount-undequeued; i++ )
{
void *y_uv[2];
buffer_handle_t *handle = (buffer_handle_t *) mBuffers[i].opaque;
mANativeWindow->lock_buffer(mANativeWindow, handle);
mapper.lock(*handle, CAMHAL_GRALLOC_USAGE, bounds, y_uv);
mBuffers[i].mapped = y_uv[0];
mFrameProvider->addFramePointers(&mBuffers[i], y_uv);
}
// return the rest of the buffers back to ANativeWindow
for(i = (mBufferCount-undequeued); i >= 0 && i < mBufferCount; i++)
{
buffer_handle_t *handle = (buffer_handle_t *) mBuffers[i].opaque;
err = mANativeWindow->cancel_buffer(mANativeWindow, handle);
if ( NO_ERROR != err ) {
CAMHAL_LOGE("Surface::cancelBuffer failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
goto fail;
}
mFramesWithCameraAdapterMap.removeItem((buffer_handle_t *) mBuffers[i].opaque);
//LOCK UNLOCK TO GET YUV POINTERS
void *y_uv[2];
mapper.lock(*(buffer_handle_t *) mBuffers[i].opaque, CAMHAL_GRALLOC_USAGE, bounds, y_uv);
mBuffers[i].mapped = y_uv[0];
mFrameProvider->addFramePointers(&mBuffers[i], y_uv);
mapper.unlock(*(buffer_handle_t *) mBuffers[i].opaque);
}
mFirstInit = true;
mFrameWidth = width;
mFrameHeight = height;
return mBuffers;
fail:
// need to cancel buffers if any were dequeued
for (int start = 0; start < i && i > 0; start++) {
status_t err = mANativeWindow->cancel_buffer(mANativeWindow,
(buffer_handle_t *) mBuffers[start].opaque);
if ( NO_ERROR != err ) {
CAMHAL_LOGE("Surface::cancelBuffer failed w/ error 0x%08x", err);
break;
}
mFramesWithCameraAdapterMap.removeItem((buffer_handle_t *) mBuffers[start].opaque);
}
freeBufferList(mBuffers);
CAMHAL_LOGEA("Error occurred, performing cleanup");
if ( NULL != mErrorNotifier.get() ) {
mErrorNotifier->errorNotify(NO_MEMORY);
}
LOG_FUNCTION_NAME_EXIT;
return NULL;
}
CameraBuffer* ANativeWindowDisplayAdapter::getBufferList(int *numBufs) {
LOG_FUNCTION_NAME;
if (numBufs) *numBufs = -1;
return NULL;
}
uint32_t * ANativeWindowDisplayAdapter::getOffsets()
{
const int lnumBufs = mBufferCount;
LOG_FUNCTION_NAME;
// TODO(XXX): Need to remove getOffsets from the API. No longer needed
if ( NULL == mANativeWindow )
{
CAMHAL_LOGEA("mANativeWindow reference is missing");
goto fail;
}
if( mBuffers == NULL)
{
CAMHAL_LOGEA("Buffers not allocated yet!!");
goto fail;
}
if(mOffsetsMap == NULL)
{
mOffsetsMap = new uint32_t[lnumBufs];
for(int i = 0; i < mBufferCount; i++)
{
mOffsetsMap[i] = 0;
}
}
LOG_FUNCTION_NAME_EXIT;
return mOffsetsMap;
fail:
if ( NULL != mOffsetsMap )
{
delete [] mOffsetsMap;
mOffsetsMap = NULL;
}
if ( NULL != mErrorNotifier.get() ) {
mErrorNotifier->errorNotify(INVALID_OPERATION);
}
LOG_FUNCTION_NAME_EXIT;
return NULL;
}
status_t ANativeWindowDisplayAdapter::minUndequeueableBuffers(int& undequeueable) {
LOG_FUNCTION_NAME;
status_t ret = NO_ERROR;
if(!mANativeWindow) {
ret = INVALID_OPERATION;
goto end;
}
ret = mANativeWindow->get_min_undequeued_buffer_count(mANativeWindow, &undequeueable);
if ( NO_ERROR != ret ) {
CAMHAL_LOGEB("get_min_undequeued_buffer_count failed: %s (%d)", strerror(-ret), -ret);
if ( NO_INIT == ret ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
return ret;
}
end:
return ret;
LOG_FUNCTION_NAME_EXIT;
}
status_t ANativeWindowDisplayAdapter::maxQueueableBuffers(unsigned int& queueable)
{
LOG_FUNCTION_NAME;
status_t ret = NO_ERROR;
int undequeued = 0;
if(mBufferCount == 0)
{
ret = INVALID_OPERATION;
goto end;
}
ret = minUndequeueableBuffers(undequeued);
if (ret != NO_ERROR) {
goto end;
}
queueable = mBufferCount - undequeued;
end:
return ret;
LOG_FUNCTION_NAME_EXIT;
}
int ANativeWindowDisplayAdapter::getFd()
{
LOG_FUNCTION_NAME;
if(mFD == -1)
{
buffer_handle_t *handle = (buffer_handle_t *)mBuffers[0].opaque;
IMG_native_handle_t *img = (IMG_native_handle_t *)handle;
// TODO: should we dup the fd? not really necessary and another thing for ANativeWindow
// to manage and close...
mFD = dup(img->fd[0]);
}
LOG_FUNCTION_NAME_EXIT;
return mFD;
}
status_t ANativeWindowDisplayAdapter::returnBuffersToWindow()
{
status_t ret = NO_ERROR;
android::GraphicBufferMapper &mapper = android::GraphicBufferMapper::get();
//Give the buffers back to display here - sort of free it
if (mANativeWindow)
for(unsigned int i = 0; i < mFramesWithCameraAdapterMap.size(); i++) {
int value = mFramesWithCameraAdapterMap.valueAt(i);
buffer_handle_t *handle = (buffer_handle_t *) mBuffers[value].opaque;
// if buffer index is out of bounds skip
if ((value < 0) || (value >= mBufferCount)) {
CAMHAL_LOGEA("Potential out bounds access to handle...skipping");
continue;
}
// unlock buffer before giving it up
mapper.unlock(*handle);
ret = mANativeWindow->cancel_buffer(mANativeWindow, handle);
if ( NO_INIT == ret ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
return ret;
} else if ( NO_ERROR != ret ) {
CAMHAL_LOGE("Surface::cancelBuffer() failed: %s (%d)",
strerror(-ret),
-ret);
return ret;
}
}
else
CAMHAL_LOGE("mANativeWindow is NULL");
///Clear the frames with camera adapter map
mFramesWithCameraAdapterMap.clear();
return ret;
}
int ANativeWindowDisplayAdapter::freeBufferList(CameraBuffer * buflist)
{
LOG_FUNCTION_NAME;
status_t ret = NO_ERROR;
android::AutoMutex lock(mLock);
if(mBuffers != buflist)
{
CAMHAL_LOGEA("CameraHal passed wrong set of buffers to free!!!");
if (mBuffers != NULL)
delete []mBuffers;
mBuffers = NULL;
}
/* FIXME this will probably want the list that was just deleted */
returnBuffersToWindow();
if ( NULL != buflist )
{
delete [] buflist;
mBuffers = NULL;
}
if( mBuffers != NULL)
{
delete [] mBuffers;
mBuffers = NULL;
}
if ( NULL != mOffsetsMap )
{
delete [] mOffsetsMap;
mOffsetsMap = NULL;
}
if( mFD != -1)
{
close(mFD); // close duped handle
mFD = -1;
}
mFramesType.clear();
return NO_ERROR;
}
bool ANativeWindowDisplayAdapter::supportsExternalBuffering()
{
return false;
}
void ANativeWindowDisplayAdapter::displayThread()
{
bool shouldLive = true;
int timeout = 0;
status_t ret;
LOG_FUNCTION_NAME;
while(shouldLive)
{
ret = Utils::MessageQueue::waitForMsg(&mDisplayThread->msgQ()
, &mDisplayQ
, NULL
, ANativeWindowDisplayAdapter::DISPLAY_TIMEOUT);
if ( !mDisplayThread->msgQ().isEmpty() )
{
///Received a message from CameraHal, process it
shouldLive = processHalMsg();
}
else if( !mDisplayQ.isEmpty())
{
if ( mDisplayState== ANativeWindowDisplayAdapter::DISPLAY_INIT )
{
///If display adapter is not started, continue
continue;
}
else
{
Utils::Message msg;
///Get the dummy msg from the displayQ
if(mDisplayQ.get(&msg)!=NO_ERROR)
{
CAMHAL_LOGEA("Error in getting message from display Q");
continue;
}
// There is a frame from ANativeWindow for us to dequeue
// We dequeue and return the frame back to Camera adapter
if(mDisplayState == ANativeWindowDisplayAdapter::DISPLAY_STARTED)
{
handleFrameReturn();
}
if (mDisplayState == ANativeWindowDisplayAdapter::DISPLAY_EXITED)
{
///we exit the thread even though there are frames still to dequeue. They will be dequeued
///in disableDisplay
shouldLive = false;
}
}
}
}
LOG_FUNCTION_NAME_EXIT;
}
bool ANativeWindowDisplayAdapter::processHalMsg()
{
Utils::Message msg;
LOG_FUNCTION_NAME;
mDisplayThread->msgQ().get(&msg);
bool ret = true, invalidCommand = false;
switch ( msg.command )
{
case DisplayThread::DISPLAY_START:
CAMHAL_LOGDA("Display thread received DISPLAY_START command from Camera HAL");
mDisplayState = ANativeWindowDisplayAdapter::DISPLAY_STARTED;
break;
case DisplayThread::DISPLAY_STOP:
///@bug There is no API to disable SF without destroying it
///@bug Buffers might still be w/ display and will get displayed
///@remarks Ideal seqyence should be something like this
///mOverlay->setParameter("enabled", false);
CAMHAL_LOGDA("Display thread received DISPLAY_STOP command from Camera HAL");
mDisplayState = ANativeWindowDisplayAdapter::DISPLAY_STOPPED;
// flush frame message queue
while ( !mDisplayQ.isEmpty() ) {
Utils::Message message;
mDisplayQ.get(&message);
}
break;
case DisplayThread::DISPLAY_EXIT:
CAMHAL_LOGDA("Display thread received DISPLAY_EXIT command from Camera HAL.");
CAMHAL_LOGDA("Stopping display thread...");
mDisplayState = ANativeWindowDisplayAdapter::DISPLAY_EXITED;
///Note that the SF can have pending buffers when we disable the display
///This is normal and the expectation is that they may not be displayed.
///This is to ensure that the user experience is not impacted
ret = false;
break;
default:
CAMHAL_LOGEB("Invalid Display Thread Command 0x%x.", msg.command);
invalidCommand = true;
break;
}
///Signal the semaphore if it is sent as part of the message
if ( ( msg.arg1 ) && ( !invalidCommand ) )
{
CAMHAL_LOGDA("+Signalling display semaphore");
Utils::Semaphore &sem = *((Utils::Semaphore*)msg.arg1);
sem.Signal();
CAMHAL_LOGDA("-Signalling display semaphore");
}
LOG_FUNCTION_NAME_EXIT;
return ret;
}
status_t ANativeWindowDisplayAdapter::PostFrame(ANativeWindowDisplayAdapter::DisplayFrame &dispFrame)
{
status_t ret = NO_ERROR;
uint32_t actualFramesWithDisplay = 0;
android_native_buffer_t *buffer = NULL;
android::GraphicBufferMapper &mapper = android::GraphicBufferMapper::get();
int i;
///@todo Do cropping based on the stabilized frame coordinates
///@todo Insert logic to drop frames here based on refresh rate of
///display or rendering rate whichever is lower
///Queue the buffer to overlay
if ( NULL == mANativeWindow ) {
return NO_INIT;
}
if (!mBuffers || !dispFrame.mBuffer) {
CAMHAL_LOGEA("NULL sent to PostFrame");
return BAD_VALUE;
}
for ( i = 0; i < mBufferCount; i++ )
{
if ( dispFrame.mBuffer == &mBuffers[i] )
{
break;
}
}
#if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
if ( mMeasureStandby ) {
CameraHal::PPM("Standby to first shot: Sensor Change completed - ", &mStandbyToShot);
mMeasureStandby = false;
} else if (CameraFrame::CameraFrame::SNAPSHOT_FRAME == dispFrame.mType) {
CameraHal::PPM("Shot to snapshot: ", &mStartCapture);
mShotToShot = true;
} else if ( mShotToShot ) {
CameraHal::PPM("Shot to shot: ", &mStartCapture);
mShotToShot = false;
}
#endif
android::AutoMutex lock(mLock);
mFramesType.add( (int)mBuffers[i].opaque, dispFrame.mType);
if ( mDisplayState == ANativeWindowDisplayAdapter::DISPLAY_STARTED &&
(!mPaused || CameraFrame::CameraFrame::SNAPSHOT_FRAME == dispFrame.mType) &&
!mSuspend)
{
uint32_t xOff, yOff;
CameraHal::getXYFromOffset(&xOff, &yOff, dispFrame.mOffset, PAGE_SIZE, mPixelFormat);
// Set crop only if current x and y offsets do not match with frame offsets
if ((mXOff != xOff) || (mYOff != yOff)) {
CAMHAL_LOGDB("offset = %u left = %d top = %d right = %d bottom = %d",
dispFrame.mOffset, xOff, yOff ,
xOff + mPreviewWidth, yOff + mPreviewHeight);
// We'll ignore any errors here, if the surface is
// already invalid, we'll know soon enough.
mANativeWindow->set_crop(mANativeWindow, xOff, yOff,
xOff + mPreviewWidth, yOff + mPreviewHeight);
// Update the current x and y offsets
mXOff = xOff;
mYOff = yOff;
}
{
buffer_handle_t *handle = (buffer_handle_t *) mBuffers[i].opaque;
// unlock buffer before sending to display
mapper.unlock(*handle);
ret = mANativeWindow->enqueue_buffer(mANativeWindow, handle);
}
if ( NO_ERROR != ret ) {
CAMHAL_LOGE("Surface::queueBuffer returned error %d", ret);
}
mFramesWithCameraAdapterMap.removeItem((buffer_handle_t *) dispFrame.mBuffer->opaque);
// HWComposer has not minimum buffer requirement. We should be able to dequeue
// the buffer immediately
Utils::Message msg;
mDisplayQ.put(&msg);
}
else
{
buffer_handle_t *handle = (buffer_handle_t *) mBuffers[i].opaque;
// unlock buffer before giving it up
mapper.unlock(*handle);
// cancel buffer and dequeue another one
ret = mANativeWindow->cancel_buffer(mANativeWindow, handle);
if ( NO_ERROR != ret ) {
CAMHAL_LOGE("Surface::cancelBuffer returned error %d", ret);
}
mFramesWithCameraAdapterMap.removeItem((buffer_handle_t *) dispFrame.mBuffer->opaque);
Utils::Message msg;
mDisplayQ.put(&msg);
ret = NO_ERROR;
}
return ret;
}
bool ANativeWindowDisplayAdapter::handleFrameReturn()
{
status_t err;
buffer_handle_t *buf;
int i = 0;
unsigned int k;
int stride; // dummy variable to get stride
android::GraphicBufferMapper &mapper = android::GraphicBufferMapper::get();
android::Rect bounds;
CameraFrame::FrameType frameType = CameraFrame::PREVIEW_FRAME_SYNC;
void *y_uv[2];
// TODO(XXX): Do we need to keep stride information in camera hal?
if ( NULL == mANativeWindow ) {
return false;
}
err = mANativeWindow->dequeue_buffer(mANativeWindow, &buf, &stride);
if (err != 0) {
CAMHAL_LOGE("Surface::dequeueBuffer failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
return false;
}
err = mANativeWindow->lock_buffer(mANativeWindow, buf);
if ( NO_ERROR != err ) {
CAMHAL_LOGE("Surface::lockBuffer failed: %s (%d)", strerror(-err), -err);
if ( NO_INIT == err ) {
CAMHAL_LOGEA("Preview surface abandoned!");
mANativeWindow = NULL;
}
return false;
}
for(i = 0; i < mBufferCount; i++)
{
if (mBuffers[i].opaque == buf)
break;
}
if (i == mBufferCount) {
CAMHAL_LOGEB("Failed to find handle %p", buf);
}
// lock buffer before sending to FrameProvider for filling
bounds.left = 0;
bounds.top = 0;
bounds.right = mFrameWidth;
bounds.bottom = mFrameHeight;
int lock_try_count = 0;
while (mapper.lock(*(buffer_handle_t *) mBuffers[i].opaque, CAMHAL_GRALLOC_USAGE, bounds, y_uv) < 0){
if (++lock_try_count > LOCK_BUFFER_TRIES){
if ( NULL != mErrorNotifier.get() ){
mErrorNotifier->errorNotify(CAMERA_ERROR_UNKNOWN);
}
return false;
}
CAMHAL_LOGEA("Gralloc Lock FrameReturn Error: Sleeping 15ms");
usleep(15000);
}
{
android::AutoMutex lock(mLock);
mFramesWithCameraAdapterMap.add((buffer_handle_t *) mBuffers[i].opaque, i);
for( k = 0; k < mFramesType.size() ; k++) {
if(mFramesType.keyAt(k) == (int)mBuffers[i].opaque)
break;
}
if ( k == mFramesType.size() ) {
CAMHAL_LOGE("Frame type for preview buffer 0%x not found!!", mBuffers[i].opaque);
return false;
}
frameType = (CameraFrame::FrameType) mFramesType.valueAt(k);
mFramesType.removeItem((int) mBuffers[i].opaque);
}
CAMHAL_LOGVB("handleFrameReturn: found graphic buffer %d of %d", i, mBufferCount-1);
mFrameProvider->returnFrame(&mBuffers[i], frameType);
return true;
}
void ANativeWindowDisplayAdapter::frameCallbackRelay(CameraFrame* caFrame)
{
if ( NULL != caFrame )
{
if ( NULL != caFrame->mCookie )
{
ANativeWindowDisplayAdapter *da = (ANativeWindowDisplayAdapter*) caFrame->mCookie;
da->frameCallback(caFrame);
}
else
{
CAMHAL_LOGEB("Invalid Cookie in Camera Frame = %p, Cookie = %p", caFrame, caFrame->mCookie);
}
}
else
{
CAMHAL_LOGEB("Invalid Camera Frame = %p", caFrame);
}
}
void ANativeWindowDisplayAdapter::frameCallback(CameraFrame* caFrame)
{
///Call queueBuffer of overlay in the context of the callback thread
DisplayFrame df;
df.mBuffer = caFrame->mBuffer;
df.mType = (CameraFrame::FrameType) caFrame->mFrameType;
df.mOffset = caFrame->mOffset;
df.mWidthStride = caFrame->mAlignment;
df.mLength = caFrame->mLength;
df.mWidth = caFrame->mWidth;
df.mHeight = caFrame->mHeight;
PostFrame(df);
}
/*--------------------ANativeWindowDisplayAdapter Class ENDS here-----------------------------*/
} // namespace Camera
} // namespace Ti