| /* |
| * 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. |
| */ |
| |
| |
| |
| |
| #define LOG_TAG "CameraHAL" |
| |
| #include "ANativeWindowDisplayAdapter.h" |
| #include <OMX_IVCommon.h> |
| #include <ui/GraphicBuffer.h> |
| #include <ui/GraphicBufferMapper.h> |
| #include <hal_public.h> |
| |
| namespace android { |
| |
| ///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, (const char *) CameraParameters::PIXEL_FORMAT_YUV422I) == 0) |
| { |
| CAMHAL_LOGDA("CbYCrY format selected"); |
| pixFormat = OMX_COLOR_FormatCbYCrY; |
| } |
| else if(strcmp(parameters_format, (const char *) CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) |
| { |
| CAMHAL_LOGDA("YUV420SP format selected"); |
| pixFormat = OMX_COLOR_FormatYUV420SemiPlanar; |
| } |
| else if(strcmp(parameters_format, (const char *) CameraParameters::PIXEL_FORMAT_RGB565) == 0) |
| { |
| CAMHAL_LOGDA("RGB565 format selected"); |
| pixFormat = OMX_COLOR_Format16bitRGB565; |
| } |
| else |
| { |
| CAMHAL_LOGDA("Invalid format, CbYCrY format selected as default"); |
| pixFormat = OMX_COLOR_FormatCbYCrY; |
| } |
| } |
| else { |
| CAMHAL_LOGEA("Preview format is NULL, defaulting to CbYCrY"); |
| pixFormat = OMX_COLOR_FormatCbYCrY; |
| } |
| |
| return pixFormat; |
| } |
| |
| const char* getPixFormatConstant(const char* parameters_format) |
| { |
| const char* pixFormat; |
| |
| if ( parameters_format != NULL ) |
| { |
| if (strcmp(parameters_format, (const char *) CameraParameters::PIXEL_FORMAT_YUV422I) == 0) |
| { |
| CAMHAL_LOGVA("CbYCrY format selected"); |
| pixFormat = (const char *) CameraParameters::PIXEL_FORMAT_YUV422I; |
| } |
| else if(strcmp(parameters_format, (const char *) CameraParameters::PIXEL_FORMAT_YUV420SP) == 0 || |
| strcmp(parameters_format, (const char *) CameraParameters::PIXEL_FORMAT_YUV420P) == 0) |
| { |
| // TODO(XXX): We are treating YV12 the same as YUV420SP |
| CAMHAL_LOGVA("YUV420SP format selected"); |
| pixFormat = (const char *) CameraParameters::PIXEL_FORMAT_YUV420SP; |
| } |
| else if(strcmp(parameters_format, (const char *) CameraParameters::PIXEL_FORMAT_RGB565) == 0) |
| { |
| CAMHAL_LOGVA("RGB565 format selected"); |
| pixFormat = (const char *) CameraParameters::PIXEL_FORMAT_RGB565; |
| } |
| else |
| { |
| CAMHAL_LOGEA("Invalid format, CbYCrY format selected as default"); |
| pixFormat = (const char *) CameraParameters::PIXEL_FORMAT_YUV422I; |
| } |
| } |
| else |
| { |
| CAMHAL_LOGEA("Preview format is NULL, defaulting to CbYCrY"); |
| pixFormat = (const char *) CameraParameters::PIXEL_FORMAT_YUV422I; |
| } |
| |
| return pixFormat; |
| } |
| |
| const size_t getBufSize(const char* parameters_format, int width, int height) |
| { |
| int buf_size; |
| |
| if ( parameters_format != NULL ) { |
| if (strcmp(parameters_format, |
| (const char *) CameraParameters::PIXEL_FORMAT_YUV422I) == 0) { |
| buf_size = width * height * 2; |
| } |
| else if((strcmp(parameters_format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) || |
| (strcmp(parameters_format, CameraParameters::PIXEL_FORMAT_YUV420P) == 0)) { |
| buf_size = width * height * 3 / 2; |
| } |
| else if(strcmp(parameters_format, |
| (const char *) CameraParameters::PIXEL_FORMAT_RGB565) == 0) { |
| buf_size = width * height * 2; |
| } else { |
| CAMHAL_LOGEA("Invalid format"); |
| buf_size = 0; |
| } |
| } else { |
| CAMHAL_LOGEA("Preview format is NULL"); |
| buf_size = 0; |
| } |
| |
| return buf_size; |
| } |
| /*--------------------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; |
| mBufferHandleMap = NULL; |
| mGrallocHandleMap = 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() |
| { |
| Semaphore sem; |
| TIUTILS::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", 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 = -EINVAL; |
| } |
| |
| 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 ) |
| { |
| Mutex::Autolock 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, S3DParameters *s3dParams) |
| { |
| Semaphore sem; |
| TIUTILS::Message msg; |
| |
| LOG_FUNCTION_NAME; |
| |
| if ( mDisplayEnabled ) |
| { |
| CAMHAL_LOGDA("Display is already enabled"); |
| LOG_FUNCTION_NAME_EXIT; |
| |
| return NO_ERROR; |
| } |
| |
| #if 0 //TODO: s3d is not part of bringup...will reenable |
| if (s3dParams) |
| mOverlay->set_s3d_params(s3dParams->mode, s3dParams->framePacking, |
| s3dParams->order, s3dParams->subSampling); |
| #endif |
| |
| #if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS |
| |
| if ( NULL != refTime ) |
| { |
| Mutex::Autolock 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); |
| |
| 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; |
| GraphicBufferMapper &mapper = 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->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 |
| Semaphore sem; |
| sem.Create(); |
| TIUTILS::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(); |
| |
| } |
| |
| Mutex::Autolock lock(mLock); |
| { |
| ///Reset the display enabled flag |
| mDisplayEnabled = 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; |
| |
| { |
| Mutex::Autolock 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 |
| void* ANativeWindowDisplayAdapter::allocateBuffer(int width, int height, const char* format, int &bytes, int numBufs) |
| { |
| LOG_FUNCTION_NAME; |
| status_t err; |
| int i = -1; |
| const int lnumBufs = numBufs; |
| mBufferHandleMap = new buffer_handle_t*[lnumBufs]; |
| mGrallocHandleMap = new IMG_native_handle_t*[lnumBufs]; |
| int undequeued = 0; |
| GraphicBufferMapper &mapper = GraphicBufferMapper::get(); |
| Rect bounds; |
| |
| |
| if ( NULL == mANativeWindow ) { |
| return NULL; |
| } |
| |
| // Set gralloc usage bits for window. |
| err = mANativeWindow->set_usage(mANativeWindow, CAMHAL_GRALLOC_USAGE); |
| if (err != 0) { |
| LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == 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 (err != 0) { |
| LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == 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 (err != 0) { |
| LOGE("native_window_set_buffers_geometry failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == 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 ( mBufferHandleMap == NULL ) |
| { |
| CAMHAL_LOGEA("Couldn't create array for ANativeWindow buffers"); |
| LOG_FUNCTION_NAME_EXIT; |
| return NULL; |
| } |
| |
| mANativeWindow->get_min_undequeued_buffer_count(mANativeWindow, &undequeued); |
| |
| for ( i=0; i < mBufferCount; i++ ) |
| { |
| IMG_native_handle_t** hndl2hndl; |
| IMG_native_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, (buffer_handle_t**) &hndl2hndl, &stride); |
| |
| if (err != 0) { |
| CAMHAL_LOGEB("dequeueBuffer failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == err ) { |
| CAMHAL_LOGEA("Preview surface abandoned!"); |
| mANativeWindow = NULL; |
| } |
| |
| goto fail; |
| } |
| |
| handle = *hndl2hndl; |
| |
| mBufferHandleMap[i] = (buffer_handle_t*) hndl2hndl; |
| mGrallocHandleMap[i] = handle; |
| mFramesWithCameraAdapterMap.add((int) mGrallocHandleMap[i], i); |
| |
| bytes = getBufSize(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]; |
| |
| mANativeWindow->lock_buffer(mANativeWindow, mBufferHandleMap[i]); |
| |
| mapper.lock((buffer_handle_t) mGrallocHandleMap[i], CAMHAL_GRALLOC_USAGE, bounds, y_uv); |
| mFrameProvider->addFramePointers(mGrallocHandleMap[i] , y_uv); |
| } |
| |
| // return the rest of the buffers back to ANativeWindow |
| for(i = (mBufferCount-undequeued); i >= 0 && i < mBufferCount; i++) |
| { |
| err = mANativeWindow->cancel_buffer(mANativeWindow, mBufferHandleMap[i]); |
| if (err != 0) { |
| CAMHAL_LOGEB("cancel_buffer failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == err ) { |
| CAMHAL_LOGEA("Preview surface abandoned!"); |
| mANativeWindow = NULL; |
| } |
| |
| goto fail; |
| } |
| mFramesWithCameraAdapterMap.removeItem((int) mGrallocHandleMap[i]); |
| //LOCK UNLOCK TO GET YUV POINTERS |
| void *y_uv[2]; |
| mapper.lock((buffer_handle_t) mGrallocHandleMap[i], CAMHAL_GRALLOC_USAGE, bounds, y_uv); |
| mFrameProvider->addFramePointers(mGrallocHandleMap[i] , y_uv); |
| mapper.unlock((buffer_handle_t) mGrallocHandleMap[i]); |
| } |
| |
| mFirstInit = true; |
| mPixelFormat = getPixFormatConstant(format); |
| mFrameWidth = width; |
| mFrameHeight = height; |
| |
| return mGrallocHandleMap; |
| |
| fail: |
| // need to cancel buffers if any were dequeued |
| for (int start = 0; start < i && i > 0; start++) { |
| int err = mANativeWindow->cancel_buffer(mANativeWindow, mBufferHandleMap[start]); |
| if (err != 0) { |
| CAMHAL_LOGEB("cancelBuffer failed w/ error 0x%08x", err); |
| break; |
| } |
| mFramesWithCameraAdapterMap.removeItem((int) mGrallocHandleMap[start]); |
| } |
| |
| freeBuffer(mGrallocHandleMap); |
| |
| CAMHAL_LOGEA("Error occurred, performing cleanup"); |
| |
| if ( NULL != mErrorNotifier.get() ) |
| { |
| mErrorNotifier->errorNotify(-ENOMEM); |
| } |
| |
| LOG_FUNCTION_NAME_EXIT; |
| 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( mBufferHandleMap == NULL) |
| { |
| CAMHAL_LOGEA("Buffers not allocated yet!!"); |
| goto fail; |
| } |
| |
| if(mOffsetsMap == NULL) |
| { |
| mOffsetsMap = new uint32_t[lnumBufs]; |
| for(int i = 0; i < mBufferCount; i++) |
| { |
| IMG_native_handle_t* handle = (IMG_native_handle_t*) *(mBufferHandleMap[i]); |
| mOffsetsMap[i] = 0; |
| } |
| } |
| |
| LOG_FUNCTION_NAME_EXIT; |
| |
| return mOffsetsMap; |
| |
| fail: |
| |
| if ( NULL != mOffsetsMap ) |
| { |
| delete [] mOffsetsMap; |
| mOffsetsMap = NULL; |
| } |
| |
| if ( NULL != mErrorNotifier.get() ) |
| { |
| mErrorNotifier->errorNotify(-ENOSYS); |
| } |
| |
| LOG_FUNCTION_NAME_EXIT; |
| |
| return NULL; |
| } |
| |
| int ANativeWindowDisplayAdapter::maxQueueableBuffers(unsigned int& queueable) |
| { |
| LOG_FUNCTION_NAME; |
| int ret = NO_ERROR; |
| int undequeued = 0; |
| |
| if(mBufferCount == 0) |
| { |
| ret = -ENOSYS; |
| goto end; |
| } |
| |
| if(!mANativeWindow) |
| { |
| ret = -ENOSYS; |
| goto end; |
| } |
| |
| ret = mANativeWindow->get_min_undequeued_buffer_count(mANativeWindow, &undequeued); |
| if ( NO_ERROR != ret ) { |
| CAMHAL_LOGEB("get_min_undequeued_buffer_count failed: %s (%d)", strerror(-ret), -ret); |
| |
| if ( ENODEV == ret ) { |
| CAMHAL_LOGEA("Preview surface abandoned!"); |
| mANativeWindow = NULL; |
| } |
| |
| return -ret; |
| } |
| |
| queueable = mBufferCount - undequeued; |
| |
| end: |
| return ret; |
| LOG_FUNCTION_NAME_EXIT; |
| } |
| |
| int ANativeWindowDisplayAdapter::getFd() |
| { |
| LOG_FUNCTION_NAME; |
| |
| if(mFD == -1) |
| { |
| IMG_native_handle_t* handle = (IMG_native_handle_t*) *(mBufferHandleMap[0]); |
| // TODO: should we dup the fd? not really necessary and another thing for ANativeWindow |
| // to manage and close... |
| mFD = dup(handle->fd[0]); |
| } |
| |
| LOG_FUNCTION_NAME_EXIT; |
| |
| return mFD; |
| |
| } |
| |
| status_t ANativeWindowDisplayAdapter::returnBuffersToWindow() |
| { |
| status_t ret = NO_ERROR; |
| |
| GraphicBufferMapper &mapper = 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); |
| |
| // unlock buffer before giving it up |
| mapper.unlock((buffer_handle_t) mGrallocHandleMap[value]); |
| |
| ret = mANativeWindow->cancel_buffer(mANativeWindow, mBufferHandleMap[value]); |
| if ( ENODEV == ret ) { |
| CAMHAL_LOGEA("Preview surface abandoned!"); |
| mANativeWindow = NULL; |
| return -ret; |
| } else if ( NO_ERROR != ret ) { |
| CAMHAL_LOGEB("cancel_buffer() failed: %s (%d)", |
| strerror(-ret), |
| -ret); |
| return -ret; |
| } |
| } |
| else |
| LOGE("mANativeWindow is NULL"); |
| |
| ///Clear the frames with camera adapter map |
| mFramesWithCameraAdapterMap.clear(); |
| |
| return ret; |
| |
| } |
| |
| int ANativeWindowDisplayAdapter::freeBuffer(void* buf) |
| { |
| LOG_FUNCTION_NAME; |
| |
| int *buffers = (int *) buf; |
| status_t ret = NO_ERROR; |
| |
| Mutex::Autolock lock(mLock); |
| |
| if((int *)mGrallocHandleMap != buffers) |
| { |
| CAMHAL_LOGEA("CameraHal passed wrong set of buffers to free!!!"); |
| if (mGrallocHandleMap != NULL) |
| delete []mGrallocHandleMap; |
| mGrallocHandleMap = NULL; |
| } |
| |
| |
| returnBuffersToWindow(); |
| |
| if ( NULL != buf ) |
| { |
| delete [] buffers; |
| mGrallocHandleMap = NULL; |
| } |
| |
| if( mBufferHandleMap != NULL) |
| { |
| delete [] mBufferHandleMap; |
| mBufferHandleMap = NULL; |
| } |
| |
| if ( NULL != mOffsetsMap ) |
| { |
| delete [] mOffsetsMap; |
| mOffsetsMap = NULL; |
| } |
| |
| if( mFD != -1) |
| { |
| close(mFD); // close duped handle |
| mFD = -1; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| |
| bool ANativeWindowDisplayAdapter::supportsExternalBuffering() |
| { |
| return false; |
| } |
| |
| int ANativeWindowDisplayAdapter::useBuffers(void *bufArr, int num) |
| { |
| return NO_ERROR; |
| } |
| |
| void ANativeWindowDisplayAdapter::displayThread() |
| { |
| bool shouldLive = true; |
| int timeout = 0; |
| status_t ret; |
| |
| LOG_FUNCTION_NAME; |
| |
| while(shouldLive) |
| { |
| ret = TIUTILS::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 |
| { |
| TIUTILS::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() |
| { |
| TIUTILS::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; |
| |
| 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"); |
| Semaphore &sem = *((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; |
| GraphicBufferMapper &mapper = 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 (!mGrallocHandleMap || !dispFrame.mBuffer) { |
| CAMHAL_LOGEA("NULL sent to PostFrame"); |
| return -EINVAL; |
| } |
| |
| for ( i = 0; i < mBufferCount; i++ ) |
| { |
| if ( ((int) dispFrame.mBuffer ) == (int)mGrallocHandleMap[i] ) |
| { |
| break; |
| } |
| } |
| |
| if ( mDisplayState == ANativeWindowDisplayAdapter::DISPLAY_STARTED && |
| (!mPaused || CameraFrame::CameraFrame::SNAPSHOT_FRAME == dispFrame.mType) && |
| !mSuspend) |
| { |
| Mutex::Autolock lock(mLock); |
| uint32_t xOff = (dispFrame.mOffset% PAGE_SIZE); |
| uint32_t yOff = (dispFrame.mOffset / PAGE_SIZE); |
| |
| // Set crop only if current x and y offsets do not match with frame offsets |
| if((mXOff!=xOff) || (mYOff!=yOff)) |
| { |
| CAMHAL_LOGDB("Offset %d xOff = %d, yOff = %d", dispFrame.mOffset, xOff, yOff); |
| uint8_t bytesPerPixel; |
| ///Calculate bytes per pixel based on the pixel format |
| if(strcmp(mPixelFormat, (const char *) CameraParameters::PIXEL_FORMAT_YUV422I) == 0) |
| { |
| bytesPerPixel = 2; |
| } |
| else if(strcmp(mPixelFormat, (const char *) CameraParameters::PIXEL_FORMAT_RGB565) == 0) |
| { |
| bytesPerPixel = 2; |
| } |
| else if(strcmp(mPixelFormat, (const char *) CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) |
| { |
| bytesPerPixel = 1; |
| } |
| else |
| { |
| bytesPerPixel = 1; |
| } |
| |
| CAMHAL_LOGVB(" crop.left = %d crop.top = %d crop.right = %d crop.bottom = %d", |
| xOff/bytesPerPixel, yOff , (xOff/bytesPerPixel)+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/bytesPerPixel, yOff, |
| (xOff/bytesPerPixel)+mPreviewWidth, yOff+mPreviewHeight); |
| |
| ///Update the current x and y offsets |
| mXOff = xOff; |
| mYOff = yOff; |
| } |
| |
| // unlock buffer before sending to display |
| mapper.unlock((buffer_handle_t) mGrallocHandleMap[i]); |
| ret = mANativeWindow->enqueue_buffer(mANativeWindow, mBufferHandleMap[i]); |
| if (ret != 0) { |
| LOGE("Surface::queueBuffer returned error %d", ret); |
| } |
| |
| mFramesWithCameraAdapterMap.removeItem((int) dispFrame.mBuffer); |
| |
| |
| // HWComposer has not minimum buffer requirement. We should be able to dequeue |
| // the buffer immediately |
| TIUTILS::Message msg; |
| mDisplayQ.put(&msg); |
| |
| |
| #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 |
| |
| } |
| else |
| { |
| Mutex::Autolock lock(mLock); |
| |
| // unlock buffer before giving it up |
| mapper.unlock((buffer_handle_t) mGrallocHandleMap[i]); |
| |
| // cancel buffer and dequeue another one |
| ret = mANativeWindow->cancel_buffer(mANativeWindow, mBufferHandleMap[i]); |
| if (ret != 0) { |
| LOGE("Surface::queueBuffer returned error %d", ret); |
| } |
| |
| mFramesWithCameraAdapterMap.removeItem((int) dispFrame.mBuffer); |
| |
| TIUTILS::Message msg; |
| mDisplayQ.put(&msg); |
| ret = NO_ERROR; |
| } |
| |
| return ret; |
| } |
| |
| |
| bool ANativeWindowDisplayAdapter::handleFrameReturn() |
| { |
| status_t err; |
| buffer_handle_t* buf; |
| int i = 0; |
| int stride; // dummy variable to get stride |
| GraphicBufferMapper &mapper = GraphicBufferMapper::get(); |
| Rect bounds; |
| 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_LOGEB("dequeueBuffer failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == err ) { |
| CAMHAL_LOGEA("Preview surface abandoned!"); |
| mANativeWindow = NULL; |
| } |
| |
| return false; |
| } |
| |
| err = mANativeWindow->lock_buffer(mANativeWindow, buf); |
| if (err != 0) { |
| CAMHAL_LOGEB("lockbuffer failed: %s (%d)", strerror(-err), -err); |
| |
| if ( ENODEV == err ) { |
| CAMHAL_LOGEA("Preview surface abandoned!"); |
| mANativeWindow = NULL; |
| } |
| |
| return false; |
| } |
| |
| for(i = 0; i < mBufferCount; i++) |
| { |
| if (mBufferHandleMap[i] == buf) |
| break; |
| } |
| |
| // 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) mGrallocHandleMap[i], 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); |
| } |
| |
| mFramesWithCameraAdapterMap.add((int) mGrallocHandleMap[i], i); |
| |
| CAMHAL_LOGVB("handleFrameReturn: found graphic buffer %d of %d", i, mBufferCount-1); |
| mFrameProvider->returnFrame( (void*)mGrallocHandleMap[i], CameraFrame::PREVIEW_FRAME_SYNC); |
| 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-----------------------------*/ |
| |
| }; |
| |