/* * 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 #include #include #include 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), mUseExternalBufferLocking(false) { 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 { CAMHAL_LOGEA("disableDisplay: resetting mANativeWindow to NULL"); returnBuffersToWindow(); 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!"); returnBuffersToWindow(); 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!"); returnBuffersToWindow(); 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!"); returnBuffersToWindow(); 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!"); returnBuffersToWindow(); 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); if (mUseExternalBufferLocking) { mapper.unlock(*handle); } } // 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!"); returnBuffersToWindow(); 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!"); returnBuffersToWindow(); 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; } if (!mUseExternalBufferLocking) { // unlock buffer before giving it up mapper.unlock(*handle); } ret = mANativeWindow->cancel_buffer(mANativeWindow, handle); if ( NO_INIT == ret ) { CAMHAL_LOGEA("Preview surface abandoned!"); returnBuffersToWindow(); 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; if (!mUseExternalBufferLocking) { // 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; if (!mUseExternalBufferLocking) { // 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!"); returnBuffersToWindow(); 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!"); returnBuffersToWindow(); 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); } if (!mUseExternalBufferLocking) { // 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); } void ANativeWindowDisplayAdapter::setExternalLocking(bool extBuffLocking) { mUseExternalBufferLocking = extBuffLocking; } /*--------------------ANativeWindowDisplayAdapter Class ENDS here-----------------------------*/ } // namespace Camera } // namespace Ti