| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| /* |
| * Contains implementation of an abstract class EmulatedCameraDevice that defines |
| * functionality expected from an emulated physical camera device: |
| * - Obtaining and setting camera parameters |
| * - Capturing frames |
| * - Streaming video |
| * - etc. |
| */ |
| |
| #define LOG_NDEBUG 0 |
| #define LOG_TAG "EmulatedCamera_Device" |
| #include <cutils/log.h> |
| #include <sys/select.h> |
| #include <cmath> |
| #include "Alignment.h" |
| #include "EmulatedCamera.h" |
| #include "EmulatedCameraDevice.h" |
| |
| #undef min |
| #undef max |
| #include <algorithm> |
| |
| namespace android { |
| |
| const float GAMMA_CORRECTION = 2.2f; |
| EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal) |
| : mObjectLock(), |
| mCameraHAL(camera_hal), |
| mExposureCompensation(1.0f), |
| mWhiteBalanceScale(NULL), |
| mSupportedWhiteBalanceScale(), |
| mState(ECDS_CONSTRUCTED), |
| mTriggerAutoFocus(false) |
| { |
| } |
| |
| EmulatedCameraDevice::~EmulatedCameraDevice() |
| { |
| ALOGV("EmulatedCameraDevice destructor"); |
| for (size_t i = 0; i < mSupportedWhiteBalanceScale.size(); ++i) { |
| if (mSupportedWhiteBalanceScale.valueAt(i) != NULL) { |
| delete[] mSupportedWhiteBalanceScale.valueAt(i); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Emulated camera device public API |
| ***************************************************************************/ |
| |
| status_t EmulatedCameraDevice::Initialize() |
| { |
| if (isInitialized()) { |
| ALOGW("%s: Emulated camera device is already initialized: mState = %d", |
| __FUNCTION__, mState); |
| return NO_ERROR; |
| } |
| |
| mState = ECDS_INITIALIZED; |
| |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedCameraDevice::startDeliveringFrames(bool one_burst) |
| { |
| ALOGV("%s", __FUNCTION__); |
| |
| if (!isStarted()) { |
| ALOGE("%s: Device is not started", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| /* Frames will be delivered from the thread routine. */ |
| const status_t res = startWorkerThread(one_burst); |
| ALOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__); |
| return res; |
| } |
| |
| status_t EmulatedCameraDevice::stopDeliveringFrames() |
| { |
| ALOGV("%s", __FUNCTION__); |
| |
| if (!isStarted()) { |
| ALOGW("%s: Device is not started", __FUNCTION__); |
| return NO_ERROR; |
| } |
| |
| const status_t res = stopWorkerThread(); |
| ALOGE_IF(res != NO_ERROR, "%s: stopWorkerThread failed", __FUNCTION__); |
| return res; |
| } |
| |
| status_t EmulatedCameraDevice::setPreviewFrameRate(int framesPerSecond) { |
| if (framesPerSecond <= 0) { |
| return EINVAL; |
| } |
| mFramesPerSecond = framesPerSecond; |
| return NO_ERROR; |
| } |
| |
| void EmulatedCameraDevice::setExposureCompensation(const float ev) { |
| ALOGV("%s", __FUNCTION__); |
| |
| if (!isStarted()) { |
| ALOGW("%s: Fake camera device is not started.", __FUNCTION__); |
| } |
| |
| mExposureCompensation = std::pow(2.0f, ev / GAMMA_CORRECTION); |
| ALOGV("New exposure compensation is %f", mExposureCompensation); |
| } |
| |
| void EmulatedCameraDevice::initializeWhiteBalanceModes(const char* mode, |
| const float r_scale, |
| const float b_scale) { |
| ALOGV("%s with %s, %f, %f", __FUNCTION__, mode, r_scale, b_scale); |
| float* value = new float[3]; |
| value[0] = r_scale; value[1] = 1.0f; value[2] = b_scale; |
| mSupportedWhiteBalanceScale.add(String8(mode), value); |
| } |
| |
| void EmulatedCameraDevice::setWhiteBalanceMode(const char* mode) { |
| ALOGV("%s with white balance %s", __FUNCTION__, mode); |
| mWhiteBalanceScale = |
| mSupportedWhiteBalanceScale.valueFor(String8(mode)); |
| } |
| |
| /* Computes the pixel value after adjusting the white balance to the current |
| * one. The input the y, u, v channel of the pixel and the adjusted value will |
| * be stored in place. The adjustment is done in RGB space. |
| */ |
| void EmulatedCameraDevice::changeWhiteBalance(uint8_t& y, |
| uint8_t& u, |
| uint8_t& v) const { |
| float r_scale = mWhiteBalanceScale[0]; |
| float b_scale = mWhiteBalanceScale[2]; |
| int r = static_cast<float>(YUV2R(y, u, v)) / r_scale; |
| int g = YUV2G(y, u, v); |
| int b = static_cast<float>(YUV2B(y, u, v)) / b_scale; |
| |
| y = RGB2Y(r, g, b); |
| u = RGB2U(r, g, b); |
| v = RGB2V(r, g, b); |
| } |
| |
| void EmulatedCameraDevice::checkAutoFocusTrigger() { |
| // The expected value is a reference so we need it to be a variable |
| bool expectedTrigger = true; |
| if (mTriggerAutoFocus.compare_exchange_strong(expectedTrigger, false)) { |
| // If the compare exchange returns true then the value was the expected |
| // 'true' and was successfully set to 'false'. So that means it's time |
| // to trigger an auto-focus event and that we have disabled that trigger |
| // so it won't happen until another request is received. |
| mCameraHAL->autoFocusComplete(); |
| } |
| } |
| |
| status_t EmulatedCameraDevice::getCurrentFrameImpl(const uint8_t* source, |
| uint8_t* dest, |
| uint32_t pixelFormat) const { |
| if (pixelFormat == mPixelFormat) { |
| memcpy(dest, source, mFrameBufferSize); |
| return NO_ERROR; |
| } else if (pixelFormat == V4L2_PIX_FMT_YUV420 && |
| mPixelFormat == V4L2_PIX_FMT_YVU420) { |
| // Convert from YV12 to YUV420 without alignment |
| const int ySize = mYStride * mFrameHeight; |
| const int uvSize = mUVStride * (mFrameHeight / 2); |
| if (mYStride == mFrameWidth) { |
| // Copy Y straight up |
| memcpy(dest, source, ySize); |
| } else { |
| // Strip alignment |
| for (int y = 0; y < mFrameHeight; ++y) { |
| memcpy(dest + y * mFrameWidth, |
| source + y * mYStride, |
| mFrameWidth); |
| } |
| } |
| |
| if (mUVStride == mFrameWidth / 2) { |
| // Swap U and V |
| memcpy(dest + ySize, source + ySize + uvSize, uvSize); |
| memcpy(dest + ySize + uvSize, source + ySize, uvSize); |
| } else { |
| // Strip alignment |
| uint8_t* uvDest = dest + mFrameWidth * mFrameHeight; |
| const uint8_t* uvSource = source + ySize + uvSize; |
| |
| for (int i = 0; i < 2; ++i) { |
| for (int y = 0; y < mFrameHeight / 2; ++y) { |
| memcpy(uvDest + y * (mFrameWidth / 2), |
| uvSource + y * mUVStride, |
| mFrameWidth / 2); |
| } |
| uvDest += (mFrameHeight / 2) * (mFrameWidth / 2); |
| uvSource -= uvSize; |
| } |
| } |
| return NO_ERROR; |
| } |
| ALOGE("%s: Invalid pixel format conversion [%.4s to %.4s] requested", |
| __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat), |
| reinterpret_cast<const char*>(&pixelFormat)); |
| return EINVAL; |
| } |
| |
| status_t EmulatedCameraDevice::getCurrentFrame(void* buffer, |
| uint32_t pixelFormat, |
| int64_t* timestamp) |
| { |
| if (!isStarted()) { |
| ALOGE("%s: Device is not started", __FUNCTION__); |
| return EINVAL; |
| } |
| if (buffer == nullptr) { |
| ALOGE("%s: Invalid buffer provided", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| FrameLock lock(*this); |
| const void* source = mCameraThread->getPrimaryBuffer(); |
| if (source == nullptr) { |
| ALOGE("%s: No framebuffer", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| if (timestamp != nullptr) { |
| *timestamp = mCameraThread->getPrimaryTimestamp(); |
| } |
| |
| return getCurrentFrameImpl(reinterpret_cast<const uint8_t*>(source), |
| reinterpret_cast<uint8_t*>(buffer), |
| pixelFormat); |
| } |
| |
| status_t EmulatedCameraDevice::getCurrentPreviewFrame(void* buffer, |
| int64_t* timestamp) |
| { |
| if (!isStarted()) { |
| ALOGE("%s: Device is not started", __FUNCTION__); |
| return EINVAL; |
| } |
| if (buffer == nullptr) { |
| ALOGE("%s: Invalid buffer provided", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| FrameLock lock(*this); |
| const void* currentFrame = mCameraThread->getPrimaryBuffer(); |
| if (currentFrame == nullptr) { |
| ALOGE("%s: No framebuffer", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| if (timestamp != nullptr) { |
| *timestamp = mCameraThread->getPrimaryTimestamp(); |
| } |
| |
| /* In emulation the framebuffer is never RGB. */ |
| switch (mPixelFormat) { |
| case V4L2_PIX_FMT_YVU420: |
| YV12ToRGB32(currentFrame, buffer, mFrameWidth, mFrameHeight); |
| return NO_ERROR; |
| case V4L2_PIX_FMT_YUV420: |
| YU12ToRGB32(currentFrame, buffer, mFrameWidth, mFrameHeight); |
| return NO_ERROR; |
| case V4L2_PIX_FMT_NV21: |
| NV21ToRGB32(currentFrame, buffer, mFrameWidth, mFrameHeight); |
| return NO_ERROR; |
| case V4L2_PIX_FMT_NV12: |
| NV12ToRGB32(currentFrame, buffer, mFrameWidth, mFrameHeight); |
| return NO_ERROR; |
| |
| default: |
| ALOGE("%s: Unknown pixel format %.4s", |
| __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat)); |
| return EINVAL; |
| } |
| } |
| |
| const void* EmulatedCameraDevice::getCurrentFrame() { |
| if (mCameraThread.get()) { |
| return mCameraThread->getPrimaryBuffer(); |
| } |
| return nullptr; |
| } |
| |
| EmulatedCameraDevice::FrameLock::FrameLock(EmulatedCameraDevice& cameraDevice) |
| : mCameraDevice(cameraDevice) { |
| mCameraDevice.lockCurrentFrame(); |
| } |
| |
| EmulatedCameraDevice::FrameLock::~FrameLock() { |
| mCameraDevice.unlockCurrentFrame(); |
| } |
| |
| status_t EmulatedCameraDevice::setAutoFocus() { |
| mTriggerAutoFocus = true; |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedCameraDevice::cancelAutoFocus() { |
| mTriggerAutoFocus = false; |
| return NO_ERROR; |
| } |
| |
| bool EmulatedCameraDevice::requestRestart(int width, int height, |
| uint32_t pixelFormat, |
| bool takingPicture, bool oneBurst) { |
| if (mCameraThread.get() == nullptr) { |
| ALOGE("%s: No thread alive to perform the restart, is preview on?", |
| __FUNCTION__); |
| return false; |
| } |
| mCameraThread->requestRestart(width, height, pixelFormat, |
| takingPicture, oneBurst); |
| return true; |
| } |
| |
| /**************************************************************************** |
| * Emulated camera device private API |
| ***************************************************************************/ |
| |
| status_t EmulatedCameraDevice::commonStartDevice(int width, |
| int height, |
| uint32_t pix_fmt) |
| { |
| /* Validate pixel format, and calculate framebuffer size at the same time. */ |
| switch (pix_fmt) { |
| case V4L2_PIX_FMT_YVU420: |
| case V4L2_PIX_FMT_YUV420: |
| // For these pixel formats the strides have to be aligned to 16 byte |
| // boundaries as per the format specification |
| // https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12 |
| mYStride = align(width, 16); |
| mUVStride = align(mYStride / 2, 16); |
| // The second term should use half the height but since there are |
| // two planes the multiplication with two cancels that out |
| mFrameBufferSize = mYStride * height + mUVStride * height; |
| break; |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV12: |
| mYStride = width; |
| // Because of interleaving the UV stride is the same as the Y stride |
| // since it covers two pixels, one U and one V. |
| mUVStride = mYStride; |
| // Since the U/V stride covers both U and V we don't multiply by two |
| mFrameBufferSize = mYStride * height + mUVStride * (height / 2); |
| break; |
| default: |
| ALOGE("%s: Unknown pixel format %.4s", |
| __FUNCTION__, reinterpret_cast<const char*>(&pix_fmt)); |
| return EINVAL; |
| } |
| |
| /* Cache framebuffer info. */ |
| mFrameWidth = width; |
| mFrameHeight = height; |
| mPixelFormat = pix_fmt; |
| mTotalPixels = width * height; |
| |
| /* Allocate framebuffer. */ |
| mFrameBuffers[0].resize(mFrameBufferSize); |
| mFrameBuffers[1].resize(mFrameBufferSize); |
| ALOGV("%s: Allocated %zu bytes for %d pixels in %.4s[%dx%d] frame", |
| __FUNCTION__, mFrameBufferSize, mTotalPixels, |
| reinterpret_cast<const char*>(&mPixelFormat), mFrameWidth, mFrameHeight); |
| return NO_ERROR; |
| } |
| |
| void EmulatedCameraDevice::commonStopDevice() |
| { |
| mFrameWidth = mFrameHeight = mTotalPixels = 0; |
| mPixelFormat = 0; |
| |
| mFrameBuffers[0].clear(); |
| mFrameBuffers[1].clear(); |
| // No need to keep all that memory allocated if the camera isn't running |
| mFrameBuffers[0].shrink_to_fit(); |
| mFrameBuffers[1].shrink_to_fit(); |
| } |
| |
| /**************************************************************************** |
| * Worker thread management. |
| ***************************************************************************/ |
| |
| status_t EmulatedCameraDevice::startWorkerThread(bool one_burst) |
| { |
| ALOGV("%s", __FUNCTION__); |
| |
| if (!isInitialized()) { |
| ALOGE("%s: Emulated camera device is not initialized", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| mCameraThread = new CameraThread(this, staticProduceFrame, this); |
| if (mCameraThread == NULL) { |
| ALOGE("%s: Unable to instantiate CameraThread object", __FUNCTION__); |
| return ENOMEM; |
| } |
| status_t res = mCameraThread->startThread(one_burst); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Unable to start CameraThread: %s", |
| __FUNCTION__, strerror(res)); |
| return res; |
| } |
| |
| return res; |
| } |
| |
| status_t EmulatedCameraDevice::stopWorkerThread() |
| { |
| ALOGV("%s", __FUNCTION__); |
| |
| if (!isInitialized()) { |
| ALOGE("%s: Emulated camera device is not initialized", __FUNCTION__); |
| return EINVAL; |
| } |
| |
| status_t res = mCameraThread->stopThread(); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Unable to stop CameraThread", __FUNCTION__); |
| return res; |
| } |
| res = mCameraThread->joinThread(); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Unable to join CameraThread", __FUNCTION__); |
| return res; |
| } |
| |
| // Destroy the thread as well |
| mCameraThread.clear(); |
| return res; |
| } |
| |
| EmulatedCameraDevice::CameraThread::CameraThread(EmulatedCameraDevice* dev, |
| ProduceFrameFunc producer, |
| void* producerOpaque) |
| : WorkerThread("Camera_CameraThread", dev, dev->mCameraHAL), |
| mCurFrameTimestamp(0), |
| mProducerFunc(producer), |
| mProducerOpaque(producerOpaque), |
| mRestartWidth(0), |
| mRestartHeight(0), |
| mRestartPixelFormat(0), |
| mRestartOneBurst(false), |
| mRestartTakingPicture(false), |
| mRestartRequested(false) { |
| |
| } |
| |
| const void* EmulatedCameraDevice::CameraThread::getPrimaryBuffer() const { |
| if (mFrameProducer.get()) { |
| return mFrameProducer->getPrimaryBuffer(); |
| } |
| return nullptr; |
| } |
| |
| int64_t EmulatedCameraDevice::CameraThread::getPrimaryTimestamp() const { |
| if (mFrameProducer.get()) { |
| return mFrameProducer->getPrimaryTimestamp(); |
| } |
| return 0L; |
| } |
| |
| void EmulatedCameraDevice::CameraThread::lockPrimaryBuffer() { |
| mFrameProducer->lockPrimaryBuffer(); |
| } |
| |
| void EmulatedCameraDevice::CameraThread::unlockPrimaryBuffer() { |
| mFrameProducer->unlockPrimaryBuffer(); |
| } |
| |
| bool |
| EmulatedCameraDevice::CameraThread::waitForFrameOrTimeout(nsecs_t timeout) { |
| // Keep waiting until the frame producer indicates that a frame is available |
| // This does introduce some unnecessary latency to the first frame delivery |
| // but avoids a lot of thread synchronization. |
| do { |
| // We don't have any specific fd we want to select so we pass in -1 |
| // timeout is in nanoseconds but Select expects microseconds |
| Mutex::Autolock lock(mRunningMutex); |
| mRunningCondition.waitRelative(mRunningMutex, timeout); |
| if (!mRunning) { |
| ALOGV("%s: CameraThread has been terminated.", __FUNCTION__); |
| return false; |
| } |
| // Set a short timeout in case there is no frame available and we are |
| // going to loop. This way we ensure a sleep but keep a decent latency |
| timeout = milliseconds(5); |
| } while (!mFrameProducer->hasFrame()); |
| |
| return true; |
| } |
| |
| bool EmulatedCameraDevice::CameraThread::inWorkerThread() { |
| /* Wait till FPS timeout expires, or thread exit message is received. */ |
| nsecs_t wakeAt = |
| mCurFrameTimestamp + 1000000000.0 / mCameraDevice->mFramesPerSecond; |
| nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); |
| nsecs_t timeout = std::max<nsecs_t>(0, wakeAt - now); |
| |
| if (!waitForFrameOrTimeout(timeout)) { |
| return false; |
| } |
| |
| /* Check if a restart and potentially apply the requested changes */ |
| if (!checkRestartRequest()) { |
| return false; |
| } |
| |
| /* Check if an auto-focus event needs to be triggered */ |
| mCameraDevice->checkAutoFocusTrigger(); |
| |
| mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC); |
| mCameraHAL->onNextFrameAvailable(mCurFrameTimestamp, mCameraDevice); |
| |
| return true; |
| } |
| |
| status_t EmulatedCameraDevice::CameraThread::onThreadStart() { |
| void* primaryBuffer = mCameraDevice->getPrimaryBuffer(); |
| void* secondaryBuffer = mCameraDevice->getSecondaryBuffer(); |
| mFrameProducer = new FrameProducer(mCameraDevice, |
| mProducerFunc, mProducerOpaque, |
| primaryBuffer, secondaryBuffer); |
| if (mFrameProducer.get() == nullptr) { |
| ALOGE("%s: Could not instantiate FrameProducer object", __FUNCTION__); |
| return ENOMEM; |
| } |
| return mFrameProducer->startThread(mOneBurst); |
| } |
| |
| void EmulatedCameraDevice::CameraThread::onThreadExit() { |
| if (mFrameProducer.get()) { |
| if (mFrameProducer->stopThread() == NO_ERROR) { |
| mFrameProducer->joinThread(); |
| mFrameProducer.clear(); |
| } |
| } |
| } |
| |
| EmulatedCameraDevice::CameraThread::FrameProducer::FrameProducer( |
| EmulatedCameraDevice* dev, |
| ProduceFrameFunc producer, |
| void* opaque, |
| void* primaryBuffer, |
| void* secondaryBuffer) |
| : WorkerThread("Camera_FrameProducer", dev, dev->mCameraHAL), |
| mProducer(producer), |
| mOpaque(opaque), |
| mPrimaryBuffer(primaryBuffer), |
| mSecondaryBuffer(secondaryBuffer), |
| mPrimaryTimestamp(0L), |
| mSecondaryTimestamp(0L), |
| mLastFrame(0), |
| mHasFrame(false) { |
| |
| } |
| |
| const void* |
| EmulatedCameraDevice::CameraThread::FrameProducer::getPrimaryBuffer() const { |
| return mPrimaryBuffer; |
| } |
| |
| int64_t |
| EmulatedCameraDevice::CameraThread::FrameProducer::getPrimaryTimestamp() const { |
| return mPrimaryTimestamp; |
| } |
| |
| void EmulatedCameraDevice::CameraThread::FrameProducer::lockPrimaryBuffer() { |
| mBufferMutex.lock(); |
| } |
| void EmulatedCameraDevice::CameraThread::FrameProducer::unlockPrimaryBuffer() { |
| mBufferMutex.unlock(); |
| } |
| |
| void EmulatedCameraDevice::CameraThread::requestRestart(int width, |
| int height, |
| uint32_t pixelFormat, |
| bool takingPicture, |
| bool oneBurst) { |
| Mutex::Autolock lock(mRequestMutex); |
| mRestartWidth = width; |
| mRestartHeight = height; |
| mRestartPixelFormat = pixelFormat; |
| mRestartTakingPicture = takingPicture; |
| mRestartOneBurst = oneBurst; |
| mRestartRequested = true; |
| } |
| |
| bool EmulatedCameraDevice::CameraThread::FrameProducer::hasFrame() const { |
| return mHasFrame; |
| } |
| |
| bool EmulatedCameraDevice::CameraThread::checkRestartRequest() { |
| Mutex::Autolock lock(mRequestMutex); |
| if (mRestartRequested) { |
| mRestartRequested = false; |
| status_t res = mFrameProducer->stopThread(); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Could not stop frame producer thread", __FUNCTION__); |
| mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED); |
| return false; |
| } |
| res = mFrameProducer->joinThread(); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Could not join frame producer thread", __FUNCTION__); |
| mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED); |
| return false; |
| } |
| mFrameProducer.clear(); |
| res = mCameraDevice->stopDevice(); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Could not stop device", __FUNCTION__); |
| mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED); |
| return false; |
| } |
| res = mCameraDevice->startDevice(mRestartWidth, |
| mRestartHeight, |
| mRestartPixelFormat); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Could not start device", __FUNCTION__); |
| mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED); |
| return false; |
| } |
| if (mRestartTakingPicture) { |
| mCameraHAL->setTakingPicture(true); |
| } |
| mOneBurst = mRestartOneBurst; |
| |
| // Pretend like this a thread start, performs the remaining setup |
| if (onThreadStart() != NO_ERROR) { |
| mCameraDevice->stopDevice(); |
| mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED); |
| return false; |
| } |
| |
| // Now wait for the frame producer to start producing before we proceed |
| return waitForFrameOrTimeout(0); |
| } |
| return true; |
| } |
| |
| bool EmulatedCameraDevice::CameraThread::FrameProducer::inWorkerThread() { |
| nsecs_t nextFrame = |
| mLastFrame + 1000000000 / mCameraDevice->mFramesPerSecond; |
| nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); |
| nsecs_t timeout = std::max<nsecs_t>(0, nextFrame - now); |
| |
| { |
| Mutex::Autolock lock(mRunningMutex); |
| mRunningCondition.waitRelative(mRunningMutex, timeout); |
| if (!mRunning) { |
| ALOGV("%s: FrameProducer has been terminated.", __FUNCTION__); |
| return false; |
| } |
| } |
| |
| // Produce one frame and place it in the secondary buffer |
| mLastFrame = systemTime(SYSTEM_TIME_MONOTONIC); |
| if (!mProducer(mOpaque, mSecondaryBuffer, &mSecondaryTimestamp)) { |
| ALOGE("FrameProducer could not produce frame, exiting thread"); |
| mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED); |
| return false; |
| } |
| |
| { |
| // Switch buffers now that the secondary buffer is ready |
| Mutex::Autolock lock(mBufferMutex); |
| std::swap(mPrimaryBuffer, mSecondaryBuffer); |
| std::swap(mPrimaryTimestamp, mSecondaryTimestamp); |
| } |
| mHasFrame = true; |
| return true; |
| } |
| |
| void EmulatedCameraDevice::lockCurrentFrame() { |
| mCameraThread->lockPrimaryBuffer(); |
| } |
| |
| void EmulatedCameraDevice::unlockCurrentFrame() { |
| mCameraThread->unlockPrimaryBuffer(); |
| } |
| |
| }; /* namespace android */ |