| /* |
| * Copyright (C) 2012 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 a class EmulatedFakeCamera2 that encapsulates |
| * functionality of an advanced fake camera. |
| */ |
| |
| #include <inttypes.h> |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "EmulatedCamera_FakeCamera2" |
| #include <log/log.h> |
| |
| #include "EmulatedFakeCamera2.h" |
| #include "EmulatedCameraFactory.h" |
| #include "GrallocModule.h" |
| |
| #define ERROR_CAMERA_NOT_PRESENT (-EPIPE) |
| |
| #define CAMERA2_EXT_TRIGGER_TESTING_DISCONNECT 0xFFFFFFFF |
| |
| namespace android { |
| |
| const int64_t USEC = 1000LL; |
| const int64_t MSEC = USEC * 1000LL; |
| const int64_t SEC = MSEC * 1000LL; |
| |
| const uint32_t EmulatedFakeCamera2::kAvailableFormats[4] = { |
| HAL_PIXEL_FORMAT_RAW16, |
| HAL_PIXEL_FORMAT_BLOB, |
| HAL_PIXEL_FORMAT_RGBA_8888, |
| // HAL_PIXEL_FORMAT_YV12, |
| HAL_PIXEL_FORMAT_YCrCb_420_SP |
| }; |
| |
| const uint32_t EmulatedFakeCamera2::kAvailableRawSizes[2] = { |
| 640, 480 |
| // mSensorWidth, mSensorHeight |
| }; |
| |
| const uint64_t EmulatedFakeCamera2::kAvailableRawMinDurations[1] = { |
| static_cast<uint64_t>(Sensor::kFrameDurationRange[0]) |
| }; |
| |
| const uint32_t EmulatedFakeCamera2::kAvailableProcessedSizesBack[4] = { |
| 640, 480, 320, 240 |
| // mSensorWidth, mSensorHeight |
| }; |
| |
| const uint32_t EmulatedFakeCamera2::kAvailableProcessedSizesFront[4] = { |
| 320, 240, 160, 120 |
| // mSensorWidth, mSensorHeight |
| }; |
| |
| const uint64_t EmulatedFakeCamera2::kAvailableProcessedMinDurations[1] = { |
| static_cast<uint64_t>(Sensor::kFrameDurationRange[0]) |
| }; |
| |
| const uint32_t EmulatedFakeCamera2::kAvailableJpegSizesBack[2] = { |
| 640, 480 |
| // mSensorWidth, mSensorHeight |
| }; |
| |
| const uint32_t EmulatedFakeCamera2::kAvailableJpegSizesFront[2] = { |
| 320, 240 |
| // mSensorWidth, mSensorHeight |
| }; |
| |
| |
| const uint64_t EmulatedFakeCamera2::kAvailableJpegMinDurations[1] = { |
| static_cast<uint64_t>(Sensor::kFrameDurationRange[0]) |
| }; |
| |
| |
| EmulatedFakeCamera2::EmulatedFakeCamera2(int cameraId, |
| bool facingBack, |
| struct hw_module_t* module) |
| : EmulatedCamera2(cameraId,module), |
| mFacingBack(facingBack), |
| mIsConnected(false) |
| { |
| ALOGD("Constructing emulated fake camera 2 facing %s", |
| facingBack ? "back" : "front"); |
| } |
| |
| EmulatedFakeCamera2::~EmulatedFakeCamera2() { |
| if (mCameraInfo != NULL) { |
| free_camera_metadata(mCameraInfo); |
| } |
| } |
| |
| /**************************************************************************** |
| * Public API overrides |
| ***************************************************************************/ |
| |
| status_t EmulatedFakeCamera2::Initialize() { |
| status_t res; |
| |
| // Find max width/height |
| int32_t width = 0, height = 0; |
| size_t rawSizeCount = sizeof(kAvailableRawSizes)/sizeof(kAvailableRawSizes[0]); |
| for (size_t index = 0; index + 1 < rawSizeCount; index += 2) { |
| if (width <= (int32_t)kAvailableRawSizes[index] && |
| height <= (int32_t)kAvailableRawSizes[index+1]) { |
| width = kAvailableRawSizes[index]; |
| height = kAvailableRawSizes[index+1]; |
| } |
| } |
| |
| if (width < 640 || height < 480) { |
| width = 640; |
| height = 480; |
| } |
| mSensorWidth = width; |
| mSensorHeight = height; |
| |
| res = constructStaticInfo(&mCameraInfo, true); |
| if (res != OK) { |
| ALOGE("%s: Unable to allocate static info: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| res = constructStaticInfo(&mCameraInfo, false); |
| if (res != OK) { |
| ALOGE("%s: Unable to fill in static info: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| if (res != OK) return res; |
| |
| mNextStreamId = 1; |
| mNextReprocessStreamId = 1; |
| mRawStreamCount = 0; |
| mProcessedStreamCount = 0; |
| mJpegStreamCount = 0; |
| mReprocessStreamCount = 0; |
| |
| return NO_ERROR; |
| } |
| |
| /**************************************************************************** |
| * Camera module API overrides |
| ***************************************************************************/ |
| |
| status_t EmulatedFakeCamera2::connectCamera(hw_device_t** device) { |
| status_t res; |
| ALOGV("%s", __FUNCTION__); |
| |
| { |
| Mutex::Autolock l(mMutex); |
| if (!mStatusPresent) { |
| ALOGE("%s: Camera ID %d is unplugged", __FUNCTION__, |
| mCameraID); |
| return -ENODEV; |
| } |
| } |
| |
| mConfigureThread = new ConfigureThread(this); |
| mReadoutThread = new ReadoutThread(this); |
| mControlThread = new ControlThread(this); |
| mSensor = new Sensor(mSensorWidth, mSensorHeight); |
| mJpegCompressor = new JpegCompressor(); |
| |
| mNextStreamId = 1; |
| mNextReprocessStreamId = 1; |
| |
| res = mSensor->startUp(); |
| if (res != NO_ERROR) return res; |
| |
| res = mConfigureThread->run("EmulatedFakeCamera2::configureThread"); |
| if (res != NO_ERROR) return res; |
| |
| res = mReadoutThread->run("EmulatedFakeCamera2::readoutThread"); |
| if (res != NO_ERROR) return res; |
| |
| res = mControlThread->run("EmulatedFakeCamera2::controlThread"); |
| if (res != NO_ERROR) return res; |
| |
| status_t ret = EmulatedCamera2::connectCamera(device); |
| |
| if (ret >= 0) { |
| mIsConnected = true; |
| } |
| |
| return ret; |
| } |
| |
| status_t EmulatedFakeCamera2::plugCamera() { |
| { |
| Mutex::Autolock l(mMutex); |
| |
| if (!mStatusPresent) { |
| ALOGI("%s: Plugged back in", __FUNCTION__); |
| mStatusPresent = true; |
| } |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedFakeCamera2::unplugCamera() { |
| { |
| Mutex::Autolock l(mMutex); |
| |
| if (mStatusPresent) { |
| ALOGI("%s: Unplugged camera", __FUNCTION__); |
| mStatusPresent = false; |
| } |
| } |
| |
| return closeCamera(); |
| } |
| |
| camera_device_status_t EmulatedFakeCamera2::getHotplugStatus() { |
| Mutex::Autolock l(mMutex); |
| return mStatusPresent ? |
| CAMERA_DEVICE_STATUS_PRESENT : |
| CAMERA_DEVICE_STATUS_NOT_PRESENT; |
| } |
| |
| |
| |
| status_t EmulatedFakeCamera2::closeCamera() { |
| { |
| Mutex::Autolock l(mMutex); |
| |
| status_t res; |
| ALOGV("%s", __FUNCTION__); |
| |
| if (!mIsConnected) { |
| return NO_ERROR; |
| } |
| |
| res = mSensor->shutDown(); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Unable to shut down sensor: %d", __FUNCTION__, res); |
| return res; |
| } |
| |
| mConfigureThread->requestExit(); |
| mReadoutThread->requestExit(); |
| mControlThread->requestExit(); |
| mJpegCompressor->cancel(); |
| } |
| |
| // give up the lock since we will now block and the threads |
| // can call back into this object |
| mConfigureThread->join(); |
| mReadoutThread->join(); |
| mControlThread->join(); |
| |
| ALOGV("%s exit", __FUNCTION__); |
| |
| { |
| Mutex::Autolock l(mMutex); |
| mIsConnected = false; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedFakeCamera2::getCameraInfo(struct camera_info *info) { |
| info->facing = mFacingBack ? CAMERA_FACING_BACK : CAMERA_FACING_FRONT; |
| info->orientation = gEmulatedCameraFactory.getFakeCameraOrientation(); |
| return EmulatedCamera2::getCameraInfo(info); |
| } |
| |
| /**************************************************************************** |
| * Camera device API overrides |
| ***************************************************************************/ |
| |
| /** Request input queue */ |
| |
| int EmulatedFakeCamera2::requestQueueNotify() { |
| ALOGV("Request queue notification received"); |
| |
| ALOG_ASSERT(mRequestQueueSrc != NULL, |
| "%s: Request queue src not set, but received queue notification!", |
| __FUNCTION__); |
| ALOG_ASSERT(mFrameQueueDst != NULL, |
| "%s: Request queue src not set, but received queue notification!", |
| __FUNCTION__); |
| ALOG_ASSERT(mStreams.size() != 0, |
| "%s: No streams allocated, but received queue notification!", |
| __FUNCTION__); |
| return mConfigureThread->newRequestAvailable(); |
| } |
| |
| int EmulatedFakeCamera2::getInProgressCount() { |
| Mutex::Autolock l(mMutex); |
| |
| if (!mStatusPresent) { |
| ALOGW("%s: Camera was physically disconnected", __FUNCTION__); |
| return ERROR_CAMERA_NOT_PRESENT; |
| } |
| |
| int requestCount = 0; |
| requestCount += mConfigureThread->getInProgressCount(); |
| requestCount += mReadoutThread->getInProgressCount(); |
| requestCount += mJpegCompressor->isBusy() ? 1 : 0; |
| |
| return requestCount; |
| } |
| |
| int EmulatedFakeCamera2::constructDefaultRequest( |
| int request_template, |
| camera_metadata_t **request) { |
| |
| if (request == NULL) return BAD_VALUE; |
| if (request_template < 0 || request_template >= CAMERA2_TEMPLATE_COUNT) { |
| return BAD_VALUE; |
| } |
| |
| { |
| Mutex::Autolock l(mMutex); |
| if (!mStatusPresent) { |
| ALOGW("%s: Camera was physically disconnected", __FUNCTION__); |
| return ERROR_CAMERA_NOT_PRESENT; |
| } |
| } |
| |
| status_t res; |
| // Pass 1, calculate size and allocate |
| res = constructDefaultRequest(request_template, |
| request, |
| true); |
| if (res != OK) { |
| return res; |
| } |
| // Pass 2, build request |
| res = constructDefaultRequest(request_template, |
| request, |
| false); |
| if (res != OK) { |
| ALOGE("Unable to populate new request for template %d", |
| request_template); |
| } |
| |
| return res; |
| } |
| |
| int EmulatedFakeCamera2::allocateStream( |
| uint32_t width, |
| uint32_t height, |
| int format, |
| const camera2_stream_ops_t *stream_ops, |
| uint32_t *stream_id, |
| uint32_t *format_actual, |
| uint32_t *usage, |
| uint32_t *max_buffers) { |
| Mutex::Autolock l(mMutex); |
| |
| if (!mStatusPresent) { |
| ALOGW("%s: Camera was physically disconnected", __FUNCTION__); |
| return ERROR_CAMERA_NOT_PRESENT; |
| } |
| |
| // Temporary shim until FORMAT_ZSL is removed |
| if (format == CAMERA2_HAL_PIXEL_FORMAT_ZSL) { |
| format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; |
| } |
| |
| if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { |
| unsigned int numFormats = sizeof(kAvailableFormats) / sizeof(uint32_t); |
| unsigned int formatIdx = 0; |
| for (; formatIdx < numFormats; formatIdx++) { |
| if (format == (int)kAvailableFormats[formatIdx]) break; |
| } |
| if (formatIdx == numFormats) { |
| ALOGE("%s: Format 0x%x is not supported", __FUNCTION__, format); |
| return BAD_VALUE; |
| } |
| } |
| |
| const uint32_t *availableSizes; |
| size_t availableSizeCount; |
| switch (format) { |
| case HAL_PIXEL_FORMAT_RAW16: |
| availableSizes = kAvailableRawSizes; |
| availableSizeCount = sizeof(kAvailableRawSizes)/sizeof(uint32_t); |
| break; |
| case HAL_PIXEL_FORMAT_BLOB: |
| availableSizes = mFacingBack ? |
| kAvailableJpegSizesBack : kAvailableJpegSizesFront; |
| availableSizeCount = mFacingBack ? |
| sizeof(kAvailableJpegSizesBack)/sizeof(uint32_t) : |
| sizeof(kAvailableJpegSizesFront)/sizeof(uint32_t); |
| break; |
| case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: |
| case HAL_PIXEL_FORMAT_RGBA_8888: |
| case HAL_PIXEL_FORMAT_YV12: |
| case HAL_PIXEL_FORMAT_YCrCb_420_SP: |
| availableSizes = mFacingBack ? |
| kAvailableProcessedSizesBack : kAvailableProcessedSizesFront; |
| availableSizeCount = mFacingBack ? |
| sizeof(kAvailableProcessedSizesBack)/sizeof(uint32_t) : |
| sizeof(kAvailableProcessedSizesFront)/sizeof(uint32_t); |
| break; |
| default: |
| ALOGE("%s: Unknown format 0x%x", __FUNCTION__, format); |
| return BAD_VALUE; |
| } |
| |
| unsigned int resIdx = 0; |
| for (; resIdx < availableSizeCount; resIdx++) { |
| if (availableSizes[resIdx * 2] == width && |
| availableSizes[resIdx * 2 + 1] == height) break; |
| } |
| if (resIdx == availableSizeCount) { |
| ALOGE("%s: Format 0x%x does not support resolution %d, %d", __FUNCTION__, |
| format, width, height); |
| return BAD_VALUE; |
| } |
| |
| switch (format) { |
| case HAL_PIXEL_FORMAT_RAW16: |
| if (mRawStreamCount >= kMaxRawStreamCount) { |
| ALOGE("%s: Cannot allocate another raw stream (%d already allocated)", |
| __FUNCTION__, mRawStreamCount); |
| return INVALID_OPERATION; |
| } |
| mRawStreamCount++; |
| break; |
| case HAL_PIXEL_FORMAT_BLOB: |
| if (mJpegStreamCount >= kMaxJpegStreamCount) { |
| ALOGE("%s: Cannot allocate another JPEG stream (%d already allocated)", |
| __FUNCTION__, mJpegStreamCount); |
| return INVALID_OPERATION; |
| } |
| mJpegStreamCount++; |
| break; |
| default: |
| if (mProcessedStreamCount >= kMaxProcessedStreamCount) { |
| ALOGE("%s: Cannot allocate another processed stream (%d already allocated)", |
| __FUNCTION__, mProcessedStreamCount); |
| return INVALID_OPERATION; |
| } |
| mProcessedStreamCount++; |
| } |
| |
| Stream newStream; |
| newStream.ops = stream_ops; |
| newStream.width = width; |
| newStream.height = height; |
| newStream.format = format; |
| // TODO: Query stride from gralloc |
| newStream.stride = width; |
| |
| mStreams.add(mNextStreamId, newStream); |
| |
| *stream_id = mNextStreamId; |
| if (format_actual) *format_actual = format; |
| *usage = GRALLOC_USAGE_HW_CAMERA_WRITE; |
| *max_buffers = kMaxBufferCount; |
| |
| ALOGV("Stream allocated: %d, %d x %d, 0x%x. U: %x, B: %d", |
| *stream_id, width, height, format, *usage, *max_buffers); |
| |
| mNextStreamId++; |
| return NO_ERROR; |
| } |
| |
| int EmulatedFakeCamera2::registerStreamBuffers( |
| uint32_t stream_id, |
| int num_buffers, |
| buffer_handle_t *buffers) { |
| Mutex::Autolock l(mMutex); |
| |
| if (!mStatusPresent) { |
| ALOGW("%s: Camera was physically disconnected", __FUNCTION__); |
| return ERROR_CAMERA_NOT_PRESENT; |
| } |
| |
| ALOGV("%s: Stream %d registering %d buffers", __FUNCTION__, |
| stream_id, num_buffers); |
| // Need to find out what the final concrete pixel format for our stream is |
| // Assumes that all buffers have the same format. |
| if (num_buffers < 1) { |
| ALOGE("%s: Stream %d only has %d buffers!", |
| __FUNCTION__, stream_id, num_buffers); |
| return BAD_VALUE; |
| } |
| |
| ssize_t streamIndex = mStreams.indexOfKey(stream_id); |
| if (streamIndex < 0) { |
| ALOGE("%s: Unknown stream id %d!", __FUNCTION__, stream_id); |
| return BAD_VALUE; |
| } |
| |
| Stream &stream = mStreams.editValueAt(streamIndex); |
| |
| int finalFormat = stream.format; |
| |
| if (finalFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { |
| finalFormat = HAL_PIXEL_FORMAT_RGBA_8888; |
| } |
| |
| ALOGV("%s: Stream %d format set to %x, previously %x", |
| __FUNCTION__, stream_id, finalFormat, stream.format); |
| |
| stream.format = finalFormat; |
| |
| return NO_ERROR; |
| } |
| |
| int EmulatedFakeCamera2::releaseStream(uint32_t stream_id) { |
| Mutex::Autolock l(mMutex); |
| |
| ssize_t streamIndex = mStreams.indexOfKey(stream_id); |
| if (streamIndex < 0) { |
| ALOGE("%s: Unknown stream id %d!", __FUNCTION__, stream_id); |
| return BAD_VALUE; |
| } |
| |
| if (isStreamInUse(stream_id)) { |
| ALOGE("%s: Cannot release stream %d; in use!", __FUNCTION__, |
| stream_id); |
| return BAD_VALUE; |
| } |
| |
| switch(mStreams.valueAt(streamIndex).format) { |
| case HAL_PIXEL_FORMAT_RAW16: |
| mRawStreamCount--; |
| break; |
| case HAL_PIXEL_FORMAT_BLOB: |
| mJpegStreamCount--; |
| break; |
| default: |
| mProcessedStreamCount--; |
| break; |
| } |
| |
| mStreams.removeItemsAt(streamIndex); |
| |
| return NO_ERROR; |
| } |
| |
| int EmulatedFakeCamera2::allocateReprocessStreamFromStream( |
| uint32_t output_stream_id, |
| const camera2_stream_in_ops_t *stream_ops, |
| uint32_t *stream_id) { |
| Mutex::Autolock l(mMutex); |
| |
| if (!mStatusPresent) { |
| ALOGW("%s: Camera was physically disconnected", __FUNCTION__); |
| return ERROR_CAMERA_NOT_PRESENT; |
| } |
| |
| ssize_t baseStreamIndex = mStreams.indexOfKey(output_stream_id); |
| if (baseStreamIndex < 0) { |
| ALOGE("%s: Unknown output stream id %d!", __FUNCTION__, output_stream_id); |
| return BAD_VALUE; |
| } |
| |
| const Stream &baseStream = mStreams[baseStreamIndex]; |
| |
| // We'll reprocess anything we produced |
| |
| if (mReprocessStreamCount >= kMaxReprocessStreamCount) { |
| ALOGE("%s: Cannot allocate another reprocess stream (%d already allocated)", |
| __FUNCTION__, mReprocessStreamCount); |
| return INVALID_OPERATION; |
| } |
| mReprocessStreamCount++; |
| |
| ReprocessStream newStream; |
| newStream.ops = stream_ops; |
| newStream.width = baseStream.width; |
| newStream.height = baseStream.height; |
| newStream.format = baseStream.format; |
| newStream.stride = baseStream.stride; |
| newStream.sourceStreamId = output_stream_id; |
| |
| *stream_id = mNextReprocessStreamId; |
| mReprocessStreams.add(mNextReprocessStreamId, newStream); |
| |
| ALOGV("Reprocess stream allocated: %d: %d, %d, 0x%x. Parent stream: %d", |
| *stream_id, newStream.width, newStream.height, newStream.format, |
| output_stream_id); |
| |
| mNextReprocessStreamId++; |
| return NO_ERROR; |
| } |
| |
| int EmulatedFakeCamera2::releaseReprocessStream(uint32_t stream_id) { |
| Mutex::Autolock l(mMutex); |
| |
| ssize_t streamIndex = mReprocessStreams.indexOfKey(stream_id); |
| if (streamIndex < 0) { |
| ALOGE("%s: Unknown reprocess stream id %d!", __FUNCTION__, stream_id); |
| return BAD_VALUE; |
| } |
| |
| if (isReprocessStreamInUse(stream_id)) { |
| ALOGE("%s: Cannot release reprocessing stream %d; in use!", __FUNCTION__, |
| stream_id); |
| return BAD_VALUE; |
| } |
| |
| mReprocessStreamCount--; |
| mReprocessStreams.removeItemsAt(streamIndex); |
| |
| return NO_ERROR; |
| } |
| |
| int EmulatedFakeCamera2::triggerAction(uint32_t trigger_id, |
| int32_t ext1, |
| int32_t ext2) { |
| Mutex::Autolock l(mMutex); |
| |
| if (trigger_id == CAMERA2_EXT_TRIGGER_TESTING_DISCONNECT) { |
| ALOGI("%s: Disconnect trigger - camera must be closed", __FUNCTION__); |
| mStatusPresent = false; |
| |
| gEmulatedCameraFactory.onStatusChanged( |
| mCameraID, |
| CAMERA_DEVICE_STATUS_NOT_PRESENT); |
| } |
| |
| if (!mStatusPresent) { |
| ALOGW("%s: Camera was physically disconnected", __FUNCTION__); |
| return ERROR_CAMERA_NOT_PRESENT; |
| } |
| |
| return mControlThread->triggerAction(trigger_id, |
| ext1, ext2); |
| } |
| |
| /** Shutdown and debug methods */ |
| |
| int EmulatedFakeCamera2::dump(int fd) { |
| String8 result; |
| |
| result.appendFormat(" Camera HAL device: EmulatedFakeCamera2\n"); |
| result.appendFormat(" Streams:\n"); |
| for (size_t i = 0; i < mStreams.size(); i++) { |
| int id = mStreams.keyAt(i); |
| const Stream& s = mStreams.valueAt(i); |
| result.appendFormat( |
| " Stream %d: %d x %d, format 0x%x, stride %d\n", |
| id, s.width, s.height, s.format, s.stride); |
| } |
| |
| write(fd, result.string(), result.size()); |
| |
| return NO_ERROR; |
| } |
| |
| void EmulatedFakeCamera2::signalError() { |
| // TODO: Let parent know so we can shut down cleanly |
| ALOGE("Worker thread is signaling a serious error"); |
| } |
| |
| /** Pipeline control worker thread methods */ |
| |
| EmulatedFakeCamera2::ConfigureThread::ConfigureThread(EmulatedFakeCamera2 *parent): |
| Thread(false), |
| mParent(parent), |
| mRequestCount(0), |
| mNextBuffers(NULL) { |
| mRunning = false; |
| } |
| |
| EmulatedFakeCamera2::ConfigureThread::~ConfigureThread() { |
| } |
| |
| status_t EmulatedFakeCamera2::ConfigureThread::readyToRun() { |
| Mutex::Autolock lock(mInputMutex); |
| |
| ALOGV("Starting up ConfigureThread"); |
| mRequest = NULL; |
| mActive = false; |
| mRunning = true; |
| |
| mInputSignal.signal(); |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedFakeCamera2::ConfigureThread::waitUntilRunning() { |
| Mutex::Autolock lock(mInputMutex); |
| if (!mRunning) { |
| ALOGV("Waiting for configure thread to start"); |
| mInputSignal.wait(mInputMutex); |
| } |
| return OK; |
| } |
| |
| status_t EmulatedFakeCamera2::ConfigureThread::newRequestAvailable() { |
| waitUntilRunning(); |
| |
| Mutex::Autolock lock(mInputMutex); |
| |
| mActive = true; |
| mInputSignal.signal(); |
| |
| return OK; |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::isStreamInUse(uint32_t id) { |
| Mutex::Autolock lock(mInternalsMutex); |
| |
| if (mNextBuffers == NULL) return false; |
| for (size_t i=0; i < mNextBuffers->size(); i++) { |
| if ((*mNextBuffers)[i].streamId == (int)id) return true; |
| } |
| return false; |
| } |
| |
| int EmulatedFakeCamera2::ConfigureThread::getInProgressCount() { |
| Mutex::Autolock lock(mInputMutex); |
| return mRequestCount; |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::threadLoop() { |
| status_t res; |
| |
| // Check if we're currently processing or just waiting |
| { |
| Mutex::Autolock lock(mInputMutex); |
| if (!mActive) { |
| // Inactive, keep waiting until we've been signaled |
| status_t res; |
| res = mInputSignal.waitRelative(mInputMutex, kWaitPerLoop); |
| if (res != NO_ERROR && res != TIMED_OUT) { |
| ALOGE("%s: Error waiting for input requests: %d", |
| __FUNCTION__, res); |
| return false; |
| } |
| if (!mActive) return true; |
| ALOGV("New request available"); |
| } |
| // Active |
| } |
| |
| if (mRequest == NULL) { |
| Mutex::Autolock il(mInternalsMutex); |
| |
| ALOGV("Configure: Getting next request"); |
| res = mParent->mRequestQueueSrc->dequeue_request( |
| mParent->mRequestQueueSrc, |
| &mRequest); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Error dequeuing next request: %d", __FUNCTION__, res); |
| mParent->signalError(); |
| return false; |
| } |
| if (mRequest == NULL) { |
| ALOGV("Configure: Request queue empty, going inactive"); |
| // No requests available, go into inactive mode |
| Mutex::Autolock lock(mInputMutex); |
| mActive = false; |
| return true; |
| } else { |
| Mutex::Autolock lock(mInputMutex); |
| mRequestCount++; |
| } |
| |
| camera_metadata_entry_t type; |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_TYPE, |
| &type); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading request type", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| bool success = false;; |
| switch (type.data.u8[0]) { |
| case ANDROID_REQUEST_TYPE_CAPTURE: |
| success = setupCapture(); |
| break; |
| case ANDROID_REQUEST_TYPE_REPROCESS: |
| success = setupReprocess(); |
| break; |
| default: |
| ALOGE("%s: Unexpected request type %d", |
| __FUNCTION__, type.data.u8[0]); |
| mParent->signalError(); |
| break; |
| } |
| if (!success) return false; |
| |
| } |
| |
| if (mWaitingForReadout) { |
| bool readoutDone; |
| readoutDone = mParent->mReadoutThread->waitForReady(kWaitPerLoop); |
| if (!readoutDone) return true; |
| |
| if (mNextNeedsJpeg) { |
| ALOGV("Configure: Waiting for JPEG compressor"); |
| } else { |
| ALOGV("Configure: Waiting for sensor"); |
| } |
| mWaitingForReadout = false; |
| } |
| |
| if (mNextNeedsJpeg) { |
| bool jpegDone; |
| jpegDone = mParent->mJpegCompressor->waitForDone(kWaitPerLoop); |
| if (!jpegDone) return true; |
| |
| ALOGV("Configure: Waiting for sensor"); |
| mNextNeedsJpeg = false; |
| } |
| |
| if (mNextIsCapture) { |
| return configureNextCapture(); |
| } else { |
| return configureNextReprocess(); |
| } |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::setupCapture() { |
| status_t res; |
| |
| mNextIsCapture = true; |
| // Get necessary parameters for sensor config |
| mParent->mControlThread->processRequest(mRequest); |
| |
| camera_metadata_entry_t streams; |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_OUTPUT_STREAMS, |
| &streams); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading output stream tag", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| |
| mNextBuffers = new Buffers; |
| mNextNeedsJpeg = false; |
| ALOGV("Configure: Setting up buffers for capture"); |
| for (size_t i = 0; i < streams.count; i++) { |
| int streamId = streams.data.i32[i]; |
| const Stream &s = mParent->getStreamInfo(streamId); |
| if (s.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { |
| ALOGE("%s: Stream %d does not have a concrete pixel format, but " |
| "is included in a request!", __FUNCTION__, streamId); |
| mParent->signalError(); |
| return false; |
| } |
| StreamBuffer b; |
| b.streamId = streams.data.u8[i]; |
| b.width = s.width; |
| b.height = s.height; |
| b.format = s.format; |
| b.stride = s.stride; |
| mNextBuffers->push_back(b); |
| ALOGV("Configure: Buffer %zu: Stream %d, %d x %d, format 0x%x, " |
| "stride %d", |
| i, b.streamId, b.width, b.height, b.format, b.stride); |
| if (b.format == HAL_PIXEL_FORMAT_BLOB) { |
| mNextNeedsJpeg = true; |
| } |
| } |
| |
| camera_metadata_entry_t e; |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_FRAME_COUNT, |
| &e); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading frame count tag: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| mNextFrameNumber = *e.data.i32; |
| |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_SENSOR_EXPOSURE_TIME, |
| &e); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading exposure time tag: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| mNextExposureTime = *e.data.i64; |
| |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_SENSOR_FRAME_DURATION, |
| &e); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading frame duration tag", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| mNextFrameDuration = *e.data.i64; |
| |
| if (mNextFrameDuration < |
| mNextExposureTime + Sensor::kMinVerticalBlank) { |
| mNextFrameDuration = mNextExposureTime + Sensor::kMinVerticalBlank; |
| } |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_SENSOR_SENSITIVITY, |
| &e); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading sensitivity tag", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| mNextSensitivity = *e.data.i32; |
| |
| // Start waiting on readout thread |
| mWaitingForReadout = true; |
| ALOGV("Configure: Waiting for readout thread"); |
| |
| return true; |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::configureNextCapture() { |
| bool vsync = mParent->mSensor->waitForVSync(kWaitPerLoop); |
| if (!vsync) return true; |
| |
| Mutex::Autolock il(mInternalsMutex); |
| ALOGV("Configure: Configuring sensor for capture %d", mNextFrameNumber); |
| mParent->mSensor->setExposureTime(mNextExposureTime); |
| mParent->mSensor->setFrameDuration(mNextFrameDuration); |
| mParent->mSensor->setSensitivity(mNextSensitivity); |
| |
| getBuffers(); |
| |
| ALOGV("Configure: Done configure for capture %d", mNextFrameNumber); |
| mParent->mReadoutThread->setNextOperation(true, mRequest, mNextBuffers); |
| mParent->mSensor->setDestinationBuffers(mNextBuffers); |
| |
| mRequest = NULL; |
| mNextBuffers = NULL; |
| |
| Mutex::Autolock lock(mInputMutex); |
| mRequestCount--; |
| |
| return true; |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::setupReprocess() { |
| status_t res; |
| |
| mNextNeedsJpeg = true; |
| mNextIsCapture = false; |
| |
| camera_metadata_entry_t reprocessStreams; |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_INPUT_STREAMS, |
| &reprocessStreams); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading output stream tag", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| |
| mNextBuffers = new Buffers; |
| |
| ALOGV("Configure: Setting up input buffers for reprocess"); |
| for (size_t i = 0; i < reprocessStreams.count; i++) { |
| int streamId = reprocessStreams.data.i32[i]; |
| const ReprocessStream &s = mParent->getReprocessStreamInfo(streamId); |
| if (s.format != HAL_PIXEL_FORMAT_RGB_888) { |
| ALOGE("%s: Only ZSL reprocessing supported!", |
| __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| StreamBuffer b; |
| b.streamId = -streamId; |
| b.width = s.width; |
| b.height = s.height; |
| b.format = s.format; |
| b.stride = s.stride; |
| mNextBuffers->push_back(b); |
| } |
| |
| camera_metadata_entry_t streams; |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_OUTPUT_STREAMS, |
| &streams); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading output stream tag", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| |
| ALOGV("Configure: Setting up output buffers for reprocess"); |
| for (size_t i = 0; i < streams.count; i++) { |
| int streamId = streams.data.i32[i]; |
| const Stream &s = mParent->getStreamInfo(streamId); |
| if (s.format != HAL_PIXEL_FORMAT_BLOB) { |
| // TODO: Support reprocess to YUV |
| ALOGE("%s: Non-JPEG output stream %d for reprocess not supported", |
| __FUNCTION__, streamId); |
| mParent->signalError(); |
| return false; |
| } |
| StreamBuffer b; |
| b.streamId = streams.data.u8[i]; |
| b.width = s.width; |
| b.height = s.height; |
| b.format = s.format; |
| b.stride = s.stride; |
| mNextBuffers->push_back(b); |
| ALOGV("Configure: Buffer %zu: Stream %d, %d x %d, format 0x%x, " |
| "stride %d", |
| i, b.streamId, b.width, b.height, b.format, b.stride); |
| } |
| |
| camera_metadata_entry_t e; |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_FRAME_COUNT, |
| &e); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading frame count tag: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| mNextFrameNumber = *e.data.i32; |
| |
| return true; |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::configureNextReprocess() { |
| Mutex::Autolock il(mInternalsMutex); |
| |
| getBuffers(); |
| |
| ALOGV("Configure: Done configure for reprocess %d", mNextFrameNumber); |
| mParent->mReadoutThread->setNextOperation(false, mRequest, mNextBuffers); |
| |
| mRequest = NULL; |
| mNextBuffers = NULL; |
| |
| Mutex::Autolock lock(mInputMutex); |
| mRequestCount--; |
| |
| return true; |
| } |
| |
| bool EmulatedFakeCamera2::ConfigureThread::getBuffers() { |
| status_t res; |
| /** Get buffers to fill for this frame */ |
| for (size_t i = 0; i < mNextBuffers->size(); i++) { |
| StreamBuffer &b = mNextBuffers->editItemAt(i); |
| |
| if (b.streamId > 0) { |
| Stream s = mParent->getStreamInfo(b.streamId); |
| ALOGV("Configure: Dequeing buffer from stream %d", b.streamId); |
| res = s.ops->dequeue_buffer(s.ops, &(b.buffer) ); |
| if (res != NO_ERROR || b.buffer == NULL) { |
| ALOGE("%s: Unable to dequeue buffer from stream %d: %s (%d)", |
| __FUNCTION__, b.streamId, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| |
| /* Lock the buffer from the perspective of the graphics mapper */ |
| res = GrallocModule::getInstance().lock(*(b.buffer), |
| GRALLOC_USAGE_HW_CAMERA_WRITE, |
| 0, 0, s.width, s.height, |
| (void**)&(b.img)); |
| |
| |
| if (res != NO_ERROR) { |
| ALOGE("%s: grbuffer_mapper.lock failure: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| s.ops->cancel_buffer(s.ops, |
| b.buffer); |
| mParent->signalError(); |
| return false; |
| } |
| } else { |
| ReprocessStream s = mParent->getReprocessStreamInfo(-b.streamId); |
| ALOGV("Configure: Acquiring buffer from reprocess stream %d", |
| -b.streamId); |
| res = s.ops->acquire_buffer(s.ops, &(b.buffer) ); |
| if (res != NO_ERROR || b.buffer == NULL) { |
| ALOGE("%s: Unable to acquire buffer from reprocess stream %d: " |
| "%s (%d)", __FUNCTION__, -b.streamId, |
| strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| |
| /* Lock the buffer from the perspective of the graphics mapper */ |
| res = GrallocModule::getInstance().lock(*(b.buffer), |
| GRALLOC_USAGE_HW_CAMERA_READ, |
| 0, 0, s.width, s.height, |
| (void**)&(b.img) ); |
| if (res != NO_ERROR) { |
| ALOGE("%s: grbuffer_mapper.lock failure: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| s.ops->release_buffer(s.ops, |
| b.buffer); |
| mParent->signalError(); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| EmulatedFakeCamera2::ReadoutThread::ReadoutThread(EmulatedFakeCamera2 *parent): |
| Thread(false), |
| mParent(parent), |
| mRunning(false), |
| mActive(false), |
| mRequestCount(0), |
| mRequest(NULL), |
| mBuffers(NULL) { |
| mInFlightQueue = new InFlightQueue[kInFlightQueueSize]; |
| mInFlightHead = 0; |
| mInFlightTail = 0; |
| } |
| |
| EmulatedFakeCamera2::ReadoutThread::~ReadoutThread() { |
| delete[] mInFlightQueue; |
| } |
| |
| status_t EmulatedFakeCamera2::ReadoutThread::readyToRun() { |
| Mutex::Autolock lock(mInputMutex); |
| ALOGV("Starting up ReadoutThread"); |
| mRunning = true; |
| mInputSignal.signal(); |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedFakeCamera2::ReadoutThread::waitUntilRunning() { |
| Mutex::Autolock lock(mInputMutex); |
| if (!mRunning) { |
| ALOGV("Waiting for readout thread to start"); |
| mInputSignal.wait(mInputMutex); |
| } |
| return OK; |
| } |
| |
| bool EmulatedFakeCamera2::ReadoutThread::waitForReady(nsecs_t timeout) { |
| status_t res; |
| Mutex::Autolock lock(mInputMutex); |
| while (!readyForNextCapture()) { |
| res = mReadySignal.waitRelative(mInputMutex, timeout); |
| if (res == TIMED_OUT) return false; |
| if (res != OK) { |
| ALOGE("%s: Error waiting for ready: %s (%d)", __FUNCTION__, |
| strerror(-res), res); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool EmulatedFakeCamera2::ReadoutThread::readyForNextCapture() { |
| return (mInFlightTail + 1) % kInFlightQueueSize != mInFlightHead; |
| } |
| |
| void EmulatedFakeCamera2::ReadoutThread::setNextOperation( |
| bool isCapture, |
| camera_metadata_t *request, |
| Buffers *buffers) { |
| Mutex::Autolock lock(mInputMutex); |
| if ( !readyForNextCapture() ) { |
| ALOGE("In flight queue full, dropping captures"); |
| mParent->signalError(); |
| return; |
| } |
| mInFlightQueue[mInFlightTail].isCapture = isCapture; |
| mInFlightQueue[mInFlightTail].request = request; |
| mInFlightQueue[mInFlightTail].buffers = buffers; |
| mInFlightTail = (mInFlightTail + 1) % kInFlightQueueSize; |
| mRequestCount++; |
| |
| if (!mActive) { |
| mActive = true; |
| mInputSignal.signal(); |
| } |
| } |
| |
| bool EmulatedFakeCamera2::ReadoutThread::isStreamInUse(uint32_t id) { |
| // acquire in same order as threadLoop |
| Mutex::Autolock iLock(mInternalsMutex); |
| Mutex::Autolock lock(mInputMutex); |
| |
| size_t i = mInFlightHead; |
| while (i != mInFlightTail) { |
| for (size_t j = 0; j < mInFlightQueue[i].buffers->size(); j++) { |
| if ( (*(mInFlightQueue[i].buffers))[j].streamId == (int)id ) |
| return true; |
| } |
| i = (i + 1) % kInFlightQueueSize; |
| } |
| |
| |
| if (mBuffers != NULL) { |
| for (i = 0; i < mBuffers->size(); i++) { |
| if ( (*mBuffers)[i].streamId == (int)id) return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| int EmulatedFakeCamera2::ReadoutThread::getInProgressCount() { |
| Mutex::Autolock lock(mInputMutex); |
| |
| return mRequestCount; |
| } |
| |
| bool EmulatedFakeCamera2::ReadoutThread::threadLoop() { |
| static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms |
| status_t res; |
| int32_t frameNumber; |
| |
| // Check if we're currently processing or just waiting |
| { |
| Mutex::Autolock lock(mInputMutex); |
| if (!mActive) { |
| // Inactive, keep waiting until we've been signaled |
| res = mInputSignal.waitRelative(mInputMutex, kWaitPerLoop); |
| if (res != NO_ERROR && res != TIMED_OUT) { |
| ALOGE("%s: Error waiting for capture requests: %d", |
| __FUNCTION__, res); |
| mParent->signalError(); |
| return false; |
| } |
| if (!mActive) return true; |
| } |
| // Active, see if we need a new request |
| if (mRequest == NULL) { |
| if (mInFlightHead == mInFlightTail) { |
| // Go inactive |
| ALOGV("Waiting for sensor data"); |
| mActive = false; |
| return true; |
| } else { |
| Mutex::Autolock iLock(mInternalsMutex); |
| mReadySignal.signal(); |
| mIsCapture = mInFlightQueue[mInFlightHead].isCapture; |
| mRequest = mInFlightQueue[mInFlightHead].request; |
| mBuffers = mInFlightQueue[mInFlightHead].buffers; |
| mInFlightQueue[mInFlightHead].request = NULL; |
| mInFlightQueue[mInFlightHead].buffers = NULL; |
| mInFlightHead = (mInFlightHead + 1) % kInFlightQueueSize; |
| ALOGV("Ready to read out request %p, %zu buffers", |
| mRequest, mBuffers->size()); |
| } |
| } |
| } |
| |
| // Active with request, wait on sensor to complete |
| |
| nsecs_t captureTime; |
| |
| if (mIsCapture) { |
| bool gotFrame; |
| gotFrame = mParent->mSensor->waitForNewFrame(kWaitPerLoop, |
| &captureTime); |
| |
| if (!gotFrame) return true; |
| } |
| |
| Mutex::Autolock iLock(mInternalsMutex); |
| |
| camera_metadata_entry_t entry; |
| if (!mIsCapture) { |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_SENSOR_TIMESTAMP, |
| &entry); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading reprocessing timestamp: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| captureTime = entry.data.i64[0]; |
| } |
| |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_FRAME_COUNT, |
| &entry); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading frame count tag: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| frameNumber = *entry.data.i32; |
| |
| res = find_camera_metadata_entry(mRequest, |
| ANDROID_REQUEST_METADATA_MODE, |
| &entry); |
| if (res != NO_ERROR) { |
| ALOGE("%s: error reading metadata mode tag: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| mParent->signalError(); |
| return false; |
| } |
| |
| // Got sensor data and request, construct frame and send it out |
| ALOGV("Readout: Constructing metadata and frames for request %d", |
| frameNumber); |
| |
| if (*entry.data.u8 == ANDROID_REQUEST_METADATA_MODE_FULL) { |
| ALOGV("Readout: Metadata requested, constructing"); |
| |
| camera_metadata_t *frame = NULL; |
| |
| size_t frame_entries = get_camera_metadata_entry_count(mRequest); |
| size_t frame_data = get_camera_metadata_data_count(mRequest); |
| |
| // TODO: Dynamically calculate based on enabled statistics, etc |
| frame_entries += 10; |
| frame_data += 100; |
| |
| res = mParent->mFrameQueueDst->dequeue_frame(mParent->mFrameQueueDst, |
| frame_entries, frame_data, &frame); |
| |
| if (res != NO_ERROR || frame == NULL) { |
| ALOGE("%s: Unable to dequeue frame metadata buffer", __FUNCTION__); |
| mParent->signalError(); |
| return false; |
| } |
| |
| res = append_camera_metadata(frame, mRequest); |
| if (res != NO_ERROR) { |
| ALOGE("Unable to append request metadata"); |
| } |
| |
| if (mIsCapture) { |
| add_camera_metadata_entry(frame, |
| ANDROID_SENSOR_TIMESTAMP, |
| &captureTime, |
| 1); |
| |
| collectStatisticsMetadata(frame); |
| // TODO: Collect all final values used from sensor in addition to timestamp |
| } |
| |
| ALOGV("Readout: Enqueue frame %d", frameNumber); |
| mParent->mFrameQueueDst->enqueue_frame(mParent->mFrameQueueDst, |
| frame); |
| } |
| ALOGV("Readout: Free request"); |
| res = mParent->mRequestQueueSrc->free_request(mParent->mRequestQueueSrc, mRequest); |
| if (res != NO_ERROR) { |
| ALOGE("%s: Unable to return request buffer to queue: %d", |
| __FUNCTION__, res); |
| mParent->signalError(); |
| return false; |
| } |
| mRequest = NULL; |
| |
| int compressedBufferIndex = -1; |
| ALOGV("Readout: Processing %zu buffers", mBuffers->size()); |
| for (size_t i = 0; i < mBuffers->size(); i++) { |
| const StreamBuffer &b = (*mBuffers)[i]; |
| ALOGV("Readout: Buffer %zu: Stream %d, %d x %d, format 0x%x, stride %d", |
| i, b.streamId, b.width, b.height, b.format, b.stride); |
| if (b.streamId > 0) { |
| if (b.format == HAL_PIXEL_FORMAT_BLOB) { |
| // Assumes only one BLOB buffer type per capture |
| compressedBufferIndex = i; |
| } else { |
| ALOGV("Readout: Sending image buffer %zu (%p) to output stream %d", |
| i, (void*)*(b.buffer), b.streamId); |
| GrallocModule::getInstance().unlock(*(b.buffer)); |
| const Stream &s = mParent->getStreamInfo(b.streamId); |
| res = s.ops->enqueue_buffer(s.ops, captureTime, b.buffer); |
| if (res != OK) { |
| ALOGE("Error enqueuing image buffer %p: %s (%d)", b.buffer, |
| strerror(-res), res); |
| mParent->signalError(); |
| } |
| } |
| } |
| } |
| |
| if (compressedBufferIndex == -1) { |
| delete mBuffers; |
| } else { |
| ALOGV("Readout: Starting JPEG compression for buffer %d, stream %d", |
| compressedBufferIndex, |
| (*mBuffers)[compressedBufferIndex].streamId); |
| mJpegTimestamp = captureTime; |
| // Takes ownership of mBuffers |
| mParent->mJpegCompressor->start(mBuffers, this, nullptr); |
| } |
| mBuffers = NULL; |
| |
| Mutex::Autolock l(mInputMutex); |
| mRequestCount--; |
| ALOGV("Readout: Done with request %d", frameNumber); |
| return true; |
| } |
| |
| void EmulatedFakeCamera2::ReadoutThread::onJpegDone( |
| const StreamBuffer &jpegBuffer, bool success) { |
| status_t res; |
| if (!success) { |
| ALOGE("%s: Error queueing compressed image buffer %p", |
| __FUNCTION__, jpegBuffer.buffer); |
| mParent->signalError(); |
| return; |
| } |
| |
| // Write to JPEG output stream |
| ALOGV("%s: Compression complete, pushing to stream %d", __FUNCTION__, |
| jpegBuffer.streamId); |
| |
| GrallocModule::getInstance().unlock(*(jpegBuffer.buffer)); |
| const Stream &s = mParent->getStreamInfo(jpegBuffer.streamId); |
| res = s.ops->enqueue_buffer(s.ops, mJpegTimestamp, jpegBuffer.buffer); |
| } |
| |
| void EmulatedFakeCamera2::ReadoutThread::onJpegInputDone( |
| const StreamBuffer &inputBuffer) { |
| status_t res; |
| GrallocModule::getInstance().unlock(*(inputBuffer.buffer)); |
| const ReprocessStream &s = |
| mParent->getReprocessStreamInfo(-inputBuffer.streamId); |
| res = s.ops->release_buffer(s.ops, inputBuffer.buffer); |
| if (res != OK) { |
| ALOGE("Error releasing reprocess buffer %p: %s (%d)", |
| inputBuffer.buffer, strerror(-res), res); |
| mParent->signalError(); |
| } |
| } |
| |
| status_t EmulatedFakeCamera2::ReadoutThread::collectStatisticsMetadata( |
| camera_metadata_t *frame) { |
| // Completely fake face rectangles, don't correspond to real faces in scene |
| ALOGV("Readout: Collecting statistics metadata"); |
| |
| status_t res; |
| camera_metadata_entry_t entry; |
| res = find_camera_metadata_entry(frame, |
| ANDROID_STATISTICS_FACE_DETECT_MODE, |
| &entry); |
| if (res != OK) { |
| ALOGE("%s: Unable to find face detect mode!", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| if (entry.data.u8[0] == ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) return OK; |
| |
| // The coordinate system for the face regions is the raw sensor pixel |
| // coordinates. Here, we map from the scene coordinates (0-19 in both axis) |
| // to raw pixels, for the scene defined in fake-pipeline2/Scene.cpp. We |
| // approximately place two faces on top of the windows of the house. No |
| // actual faces exist there, but might one day. Note that this doesn't |
| // account for the offsets used to account for aspect ratio differences, so |
| // the rectangles don't line up quite right. |
| const size_t numFaces = 2; |
| int32_t rects[numFaces * 4] = { |
| static_cast<int32_t>(mParent->mSensorWidth * 10 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 15 / 20), |
| static_cast<int32_t>(mParent->mSensorWidth * 12 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 17 / 20), |
| |
| static_cast<int32_t>(mParent->mSensorWidth * 16 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 15 / 20), |
| static_cast<int32_t>(mParent->mSensorWidth * 18 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 17 / 20) |
| }; |
| // To simulate some kind of real detection going on, we jitter the rectangles on |
| // each frame by a few pixels in each dimension. |
| for (size_t i = 0; i < numFaces * 4; i++) { |
| rects[i] += (int32_t)(((float)rand() / RAND_MAX) * 6 - 3); |
| } |
| // The confidence scores (0-100) are similarly jittered. |
| uint8_t scores[numFaces] = { 85, 95 }; |
| for (size_t i = 0; i < numFaces; i++) { |
| scores[i] += (int32_t)(((float)rand() / RAND_MAX) * 10 - 5); |
| } |
| |
| res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_RECTANGLES, |
| rects, numFaces * 4); |
| if (res != OK) { |
| ALOGE("%s: Unable to add face rectangles!", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_SCORES, |
| scores, numFaces); |
| if (res != OK) { |
| ALOGE("%s: Unable to add face scores!", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| if (entry.data.u8[0] == ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE) return OK; |
| |
| // Advanced face detection options - add eye/mouth coordinates. The |
| // coordinates in order are (leftEyeX, leftEyeY, rightEyeX, rightEyeY, |
| // mouthX, mouthY). The mapping is the same as the face rectangles. |
| int32_t features[numFaces * 6] = { |
| static_cast<int32_t>(mParent->mSensorWidth * 10.5 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 16 / 20), |
| static_cast<int32_t>(mParent->mSensorWidth * 11.5 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 16 / 20), |
| static_cast<int32_t>(mParent->mSensorWidth * 11 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 16.5 / 20), |
| |
| static_cast<int32_t>(mParent->mSensorWidth * 16.5 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 16 / 20), |
| static_cast<int32_t>(mParent->mSensorWidth * 17.5 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 16 / 20), |
| static_cast<int32_t>(mParent->mSensorWidth * 17 / 20), |
| static_cast<int32_t>(mParent->mSensorHeight * 16.5 / 20), |
| }; |
| // Jitter these a bit less than the rects |
| for (size_t i = 0; i < numFaces * 6; i++) { |
| features[i] += (int32_t)(((float)rand() / RAND_MAX) * 4 - 2); |
| } |
| // These are unique IDs that are used to identify each face while it's |
| // visible to the detector (if a face went away and came back, it'd get a |
| // new ID). |
| int32_t ids[numFaces] = { |
| 100, 200 |
| }; |
| |
| res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_LANDMARKS, |
| features, numFaces * 6); |
| if (res != OK) { |
| ALOGE("%s: Unable to add face landmarks!", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_IDS, |
| ids, numFaces); |
| if (res != OK) { |
| ALOGE("%s: Unable to add face scores!", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| return OK; |
| } |
| |
| EmulatedFakeCamera2::ControlThread::ControlThread(EmulatedFakeCamera2 *parent): |
| Thread(false), |
| mParent(parent) { |
| mRunning = false; |
| } |
| |
| EmulatedFakeCamera2::ControlThread::~ControlThread() { |
| } |
| |
| status_t EmulatedFakeCamera2::ControlThread::readyToRun() { |
| Mutex::Autolock lock(mInputMutex); |
| |
| ALOGV("Starting up ControlThread"); |
| mRunning = true; |
| mStartAf = false; |
| mCancelAf = false; |
| mStartPrecapture = false; |
| |
| mControlMode = ANDROID_CONTROL_MODE_AUTO; |
| |
| mEffectMode = ANDROID_CONTROL_EFFECT_MODE_OFF; |
| mSceneMode = ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY; |
| |
| mAfMode = ANDROID_CONTROL_AF_MODE_AUTO; |
| mAfModeChange = false; |
| |
| mAeMode = ANDROID_CONTROL_AE_MODE_ON; |
| mAwbMode = ANDROID_CONTROL_AWB_MODE_AUTO; |
| |
| mAfTriggerId = 0; |
| mPrecaptureTriggerId = 0; |
| |
| mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE; |
| mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE; |
| mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE; |
| |
| mExposureTime = kNormalExposureTime; |
| |
| mInputSignal.signal(); |
| return NO_ERROR; |
| } |
| |
| status_t EmulatedFakeCamera2::ControlThread::waitUntilRunning() { |
| Mutex::Autolock lock(mInputMutex); |
| if (!mRunning) { |
| ALOGV("Waiting for control thread to start"); |
| mInputSignal.wait(mInputMutex); |
| } |
| return OK; |
| } |
| |
| // Override android.control.* fields with 3A values before sending request to sensor |
| status_t EmulatedFakeCamera2::ControlThread::processRequest(camera_metadata_t *request) { |
| Mutex::Autolock lock(mInputMutex); |
| // TODO: Add handling for all android.control.* fields here |
| camera_metadata_entry_t mode; |
| status_t res; |
| |
| #define READ_IF_OK(res, what, def) \ |
| (((res) == OK) ? (what) : (uint8_t)(def)) |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_MODE, |
| &mode); |
| mControlMode = READ_IF_OK(res, mode.data.u8[0], ANDROID_CONTROL_MODE_OFF); |
| |
| // disable all 3A |
| if (mControlMode == ANDROID_CONTROL_MODE_OFF) { |
| mEffectMode = ANDROID_CONTROL_EFFECT_MODE_OFF; |
| mSceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED; |
| mAfMode = ANDROID_CONTROL_AF_MODE_OFF; |
| mAeLock = ANDROID_CONTROL_AE_LOCK_ON; |
| mAeMode = ANDROID_CONTROL_AE_MODE_OFF; |
| mAfModeChange = true; |
| mStartAf = false; |
| mCancelAf = true; |
| mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE; |
| mAwbMode = ANDROID_CONTROL_AWB_MODE_OFF; |
| return res; |
| } |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_EFFECT_MODE, |
| &mode); |
| mEffectMode = READ_IF_OK(res, mode.data.u8[0], |
| ANDROID_CONTROL_EFFECT_MODE_OFF); |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_SCENE_MODE, |
| &mode); |
| mSceneMode = READ_IF_OK(res, mode.data.u8[0], |
| ANDROID_CONTROL_SCENE_MODE_DISABLED); |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_AF_MODE, |
| &mode); |
| if (mAfMode != mode.data.u8[0]) { |
| ALOGV("AF new mode: %d, old mode %d", mode.data.u8[0], mAfMode); |
| mAfMode = mode.data.u8[0]; |
| mAfModeChange = true; |
| mStartAf = false; |
| mCancelAf = false; |
| } |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_AE_MODE, |
| &mode); |
| mAeMode = READ_IF_OK(res, mode.data.u8[0], |
| ANDROID_CONTROL_AE_MODE_OFF); |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_AE_LOCK, |
| &mode); |
| uint8_t aeLockVal = READ_IF_OK(res, mode.data.u8[0], |
| ANDROID_CONTROL_AE_LOCK_ON); |
| bool aeLock = (aeLockVal == ANDROID_CONTROL_AE_LOCK_ON); |
| if (mAeLock && !aeLock) { |
| mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE; |
| } |
| mAeLock = aeLock; |
| |
| res = find_camera_metadata_entry(request, |
| ANDROID_CONTROL_AWB_MODE, |
| &mode); |
| mAwbMode = READ_IF_OK(res, mode.data.u8[0], |
| ANDROID_CONTROL_AWB_MODE_OFF); |
| |
| // TODO: Override more control fields |
| |
| if (mAeMode != ANDROID_CONTROL_AE_MODE_OFF) { |
| camera_metadata_entry_t exposureTime; |
| res = find_camera_metadata_entry(request, |
| ANDROID_SENSOR_EXPOSURE_TIME, |
| &exposureTime); |
| if (res == OK) { |
| exposureTime.data.i64[0] = mExposureTime; |
| } |
| } |
| |
| #undef READ_IF_OK |
| |
| return OK; |
| } |
| |
| status_t EmulatedFakeCamera2::ControlThread::triggerAction(uint32_t msgType, |
| int32_t ext1, int32_t ext2) { |
| ALOGV("%s: Triggering %d (%d, %d)", __FUNCTION__, msgType, ext1, ext2); |
| Mutex::Autolock lock(mInputMutex); |
| switch (msgType) { |
| case CAMERA2_TRIGGER_AUTOFOCUS: |
| mAfTriggerId = ext1; |
| mStartAf = true; |
| mCancelAf = false; |
| break; |
| case CAMERA2_TRIGGER_CANCEL_AUTOFOCUS: |
| mAfTriggerId = ext1; |
| mStartAf = false; |
| mCancelAf = true; |
| break; |
| case CAMERA2_TRIGGER_PRECAPTURE_METERING: |
| mPrecaptureTriggerId = ext1; |
| mStartPrecapture = true; |
| break; |
| default: |
| ALOGE("%s: Unknown action triggered: %d (arguments %d %d)", |
| __FUNCTION__, msgType, ext1, ext2); |
| return BAD_VALUE; |
| } |
| return OK; |
| } |
| |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kControlCycleDelay = 100 * MSEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMinAfDuration = 500 * MSEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMaxAfDuration = 900 * MSEC; |
| const float EmulatedFakeCamera2::ControlThread::kAfSuccessRate = 0.9; |
| // Once every 5 seconds |
| const float EmulatedFakeCamera2::ControlThread::kContinuousAfStartRate = |
| kControlCycleDelay / 5.0 * SEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMinAeDuration = 500 * MSEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMaxAeDuration = 2 * SEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMinPrecaptureAeDuration = 100 * MSEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMaxPrecaptureAeDuration = 400 * MSEC; |
| // Once every 3 seconds |
| const float EmulatedFakeCamera2::ControlThread::kAeScanStartRate = |
| kControlCycleDelay / 3000000000.0; |
| |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kNormalExposureTime = 10 * MSEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kExposureJump = 2 * MSEC; |
| const nsecs_t EmulatedFakeCamera2::ControlThread::kMinExposureTime = 1 * MSEC; |
| |
| bool EmulatedFakeCamera2::ControlThread::threadLoop() { |
| bool afModeChange = false; |
| bool afTriggered = false; |
| bool afCancelled = false; |
| uint8_t afState; |
| uint8_t afMode; |
| int32_t afTriggerId; |
| bool precaptureTriggered = false; |
| uint8_t aeState; |
| uint8_t aeMode; |
| bool aeLock; |
| int32_t precaptureTriggerId; |
| nsecs_t nextSleep = kControlCycleDelay; |
| |
| { |
| Mutex::Autolock lock(mInputMutex); |
| if (mStartAf) { |
| ALOGD("Starting AF trigger processing"); |
| afTriggered = true; |
| mStartAf = false; |
| } else if (mCancelAf) { |
| ALOGD("Starting cancel AF trigger processing"); |
| afCancelled = true; |
| mCancelAf = false; |
| } |
| afState = mAfState; |
| afMode = mAfMode; |
| afModeChange = mAfModeChange; |
| mAfModeChange = false; |
| |
| afTriggerId = mAfTriggerId; |
| |
| if(mStartPrecapture) { |
| ALOGD("Starting precapture trigger processing"); |
| precaptureTriggered = true; |
| mStartPrecapture = false; |
| } |
| aeState = mAeState; |
| aeMode = mAeMode; |
| aeLock = mAeLock; |
| precaptureTriggerId = mPrecaptureTriggerId; |
| } |
| |
| if (afCancelled || afModeChange) { |
| ALOGV("Resetting AF state due to cancel/mode change"); |
| afState = ANDROID_CONTROL_AF_STATE_INACTIVE; |
| updateAfState(afState, afTriggerId); |
| mAfScanDuration = 0; |
| mLockAfterPassiveScan = false; |
| } |
| |
| if (afTriggered) { |
| afState = processAfTrigger(afMode, afState); |
| } |
| |
| afState = maybeStartAfScan(afMode, afState); |
| afState = updateAfScan(afMode, afState, &nextSleep); |
| updateAfState(afState, afTriggerId); |
| |
| if (precaptureTriggered) { |
| aeState = processPrecaptureTrigger(aeMode, aeState); |
| } |
| |
| aeState = maybeStartAeScan(aeMode, aeLock, aeState); |
| aeState = updateAeScan(aeMode, aeLock, aeState, &nextSleep); |
| updateAeState(aeState, precaptureTriggerId); |
| |
| int ret; |
| timespec t; |
| t.tv_sec = 0; |
| t.tv_nsec = nextSleep; |
| do { |
| ret = nanosleep(&t, &t); |
| } while (ret != 0); |
| |
| if (mAfScanDuration > 0) { |
| mAfScanDuration -= nextSleep; |
| } |
| if (mAeScanDuration > 0) { |
| mAeScanDuration -= nextSleep; |
| } |
| |
| return true; |
| } |
| |
| int EmulatedFakeCamera2::ControlThread::processAfTrigger(uint8_t afMode, |
| uint8_t afState) { |
| switch (afMode) { |
| case ANDROID_CONTROL_AF_MODE_OFF: |
| case ANDROID_CONTROL_AF_MODE_EDOF: |
| // Do nothing |
| break; |
| case ANDROID_CONTROL_AF_MODE_MACRO: |
| case ANDROID_CONTROL_AF_MODE_AUTO: |
| switch (afState) { |
| case ANDROID_CONTROL_AF_STATE_INACTIVE: |
| case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED: |
| case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: |
| // Start new focusing cycle |
| mAfScanDuration = ((double)rand() / RAND_MAX) * |
| (kMaxAfDuration - kMinAfDuration) + kMinAfDuration; |
| afState = ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN; |
| ALOGV("%s: AF scan start, duration %" PRId64 " ms", |
| __FUNCTION__, mAfScanDuration / 1000000); |
| break; |
| case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN: |
| // Ignore new request, already scanning |
| break; |
| default: |
| ALOGE("Unexpected AF state in AUTO/MACRO AF mode: %d", |
| afState); |
| } |
| break; |
| case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE: |
| switch (afState) { |
| // Picture mode waits for passive scan to complete |
| case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN: |
| mLockAfterPassiveScan = true; |
| break; |
| case ANDROID_CONTROL_AF_STATE_INACTIVE: |
| afState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED; |
| break; |
| case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED: |
| afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED; |
| break; |
| case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED: |
| case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: |
| // Must cancel to get out of these states |
| break; |
| default: |
| ALOGE("Unexpected AF state in CONTINUOUS_PICTURE AF mode: %d", |
| afState); |
| } |
| break; |
| case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO: |
| switch (afState) { |
| // Video mode does not wait for passive scan to complete |
| case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN: |
| case ANDROID_CONTROL_AF_STATE_INACTIVE: |
| afState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED; |
| break; |
| case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED: |
| afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED; |
| break; |
| case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED: |
| case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: |
| // Must cancel to get out of these states |
| break; |
| default: |
| ALOGE("Unexpected AF state in CONTINUOUS_VIDEO AF mode: %d", |
| afState); |
| } |
| break; |
| default: |
| break; |
| } |
| return afState; |
| } |
| |
| int EmulatedFakeCamera2::ControlThread::maybeStartAfScan(uint8_t afMode, |
| uint8_t afState) { |
| if ((afMode == ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO || |
| afMode == ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE) && |
| (afState == ANDROID_CONTROL_AF_STATE_INACTIVE || |
| afState == ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED)) { |
| |
| bool startScan = ((double)rand() / RAND_MAX) < kContinuousAfStartRate; |
| if (startScan) { |
| // Start new passive focusing cycle |
| mAfScanDuration = ((double)rand() / RAND_MAX) * |
| (kMaxAfDuration - kMinAfDuration) + kMinAfDuration; |
| afState = ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN; |
| ALOGV("%s: AF passive scan start, duration %" PRId64 " ms", |
| __FUNCTION__, mAfScanDuration / 1000000); |
| } |
| } |
| return afState; |
| } |
| |
| int EmulatedFakeCamera2::ControlThread::updateAfScan(uint8_t afMode, |
| uint8_t afState, nsecs_t *maxSleep) { |
| if (! (afState == ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN || |
| afState == ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN ) ) { |
| return afState; |
| } |
| |
| if (mAfScanDuration <= 0) { |
| ALOGV("%s: AF scan done", __FUNCTION__); |
| switch (afMode) { |
| case ANDROID_CONTROL_AF_MODE_MACRO: |
| case ANDROID_CONTROL_AF_MODE_AUTO: { |
| bool success = ((double)rand() / RAND_MAX) < kAfSuccessRate; |
| if (success) { |
| afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED; |
| } else { |
| afState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED; |
| } |
| break; |
| } |
| case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE: |
| if (mLockAfterPassiveScan) { |
| afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED; |
| mLockAfterPassiveScan = false; |
| } else { |
| afState = ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED; |
| } |
| break; |
| case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO: |
| afState = ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED; |
| break; |
| default: |
| ALOGE("Unexpected AF mode in scan state"); |
| } |
| } else { |
| if (mAfScanDuration <= *maxSleep) { |
| *maxSleep = mAfScanDuration; |
| } |
| } |
| return afState; |
| } |
| |
| void EmulatedFakeCamera2::ControlThread::updateAfState(uint8_t newState, |
| int32_t triggerId) { |
| Mutex::Autolock lock(mInputMutex); |
| if (mAfState != newState) { |
| ALOGV("%s: Autofocus state now %d, id %d", __FUNCTION__, |
| newState, triggerId); |
| mAfState = newState; |
| mParent->sendNotification(CAMERA2_MSG_AUTOFOCUS, |
| newState, triggerId, 0); |
| } |
| } |
| |
| int EmulatedFakeCamera2::ControlThread::processPrecaptureTrigger(uint8_t aeMode, |
| uint8_t aeState) { |
| switch (aeMode) { |
| case ANDROID_CONTROL_AE_MODE_OFF: |
| // Don't do anything for these |
| return aeState; |
| case ANDROID_CONTROL_AE_MODE_ON: |
| case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH: |
| case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH: |
| case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: |
| // Trigger a precapture cycle |
| aeState = ANDROID_CONTROL_AE_STATE_PRECAPTURE; |
| mAeScanDuration = ((double)rand() / RAND_MAX) * |
| (kMaxPrecaptureAeDuration - kMinPrecaptureAeDuration) + |
| kMinPrecaptureAeDuration; |
| ALOGD("%s: AE precapture scan start, duration %" PRId64 " ms", |
| __FUNCTION__, mAeScanDuration / 1000000); |
| |
| } |
| return aeState; |
| } |
| |
| int EmulatedFakeCamera2::ControlThread::maybeStartAeScan(uint8_t aeMode, |
| bool aeLocked, |
| uint8_t aeState) { |
| if (aeLocked) return aeState; |
| switch (aeMode) { |
| case ANDROID_CONTROL_AE_MODE_OFF: |
| break; |
| case ANDROID_CONTROL_AE_MODE_ON: |
| case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH: |
| case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH: |
| case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: { |
| if (aeState != ANDROID_CONTROL_AE_STATE_INACTIVE && |
| aeState != ANDROID_CONTROL_AE_STATE_CONVERGED) break; |
| |
| bool startScan = ((double)rand() / RAND_MAX) < kAeScanStartRate; |
| if (startScan) { |
| mAeScanDuration = ((double)rand() / RAND_MAX) * |
| (kMaxAeDuration - kMinAeDuration) + kMinAeDuration; |
| aeState = ANDROID_CONTROL_AE_STATE_SEARCHING; |
| ALOGV("%s: AE scan start, duration %" PRId64 " ms", |
| __FUNCTION__, mAeScanDuration / 1000000); |
| } |
| } |
| } |
| |
| return aeState; |
| } |
| |
| int EmulatedFakeCamera2::ControlThread::updateAeScan(uint8_t aeMode, |
| bool aeLock, uint8_t aeState, nsecs_t *maxSleep) { |
| if (aeLock && aeState != ANDROID_CONTROL_AE_STATE_PRECAPTURE) { |
| mAeScanDuration = 0; |
| aeState = ANDROID_CONTROL_AE_STATE_LOCKED; |
| } else if ((aeState == ANDROID_CONTROL_AE_STATE_SEARCHING) || |
| (aeState == ANDROID_CONTROL_AE_STATE_PRECAPTURE ) ) { |
| if (mAeScanDuration <= 0) { |
| ALOGV("%s: AE scan done", __FUNCTION__); |
| aeState = aeLock ? |
| ANDROID_CONTROL_AE_STATE_LOCKED :ANDROID_CONTROL_AE_STATE_CONVERGED; |
| |
| Mutex::Autolock lock(mInputMutex); |
| mExposureTime = kNormalExposureTime; |
| } else { |
| if (mAeScanDuration <= *maxSleep) { |
| *maxSleep = mAeScanDuration; |
| } |
| |
| int64_t exposureDelta = |
| ((double)rand() / RAND_MAX) * 2 * kExposureJump - |
| kExposureJump; |
| Mutex::Autolock lock(mInputMutex); |
| mExposureTime = mExposureTime + exposureDelta; |
| if (mExposureTime < kMinExposureTime) mExposureTime = kMinExposureTime; |
| } |
| } |
| |
| return aeState; |
| } |
| |
| |
| void EmulatedFakeCamera2::ControlThread::updateAeState(uint8_t newState, |
| int32_t triggerId) { |
| Mutex::Autolock lock(mInputMutex); |
| if (mAeState != newState) { |
| ALOGV("%s: Autoexposure state now %d, id %d", __FUNCTION__, |
| newState, triggerId); |
| mAeState = newState; |
| mParent->sendNotification(CAMERA2_MSG_AUTOEXPOSURE, |
| newState, triggerId, 0); |
| } |
| } |
| |
| /** Private methods */ |
| |
| status_t EmulatedFakeCamera2::constructStaticInfo( |
| camera_metadata_t **info, |
| bool sizeRequest) const { |
| |
| size_t entryCount = 0; |
| size_t dataCount = 0; |
| status_t ret; |
| |
| #define ADD_OR_SIZE( tag, data, count ) \ |
| if ( ( ret = addOrSize(*info, sizeRequest, &entryCount, &dataCount, \ |
| tag, data, count) ) != OK ) return ret |
| |
| // android.lens |
| |
| // 5 cm min focus distance for back camera, infinity (fixed focus) for front |
| const float minFocusDistance = mFacingBack ? 1.0/0.05 : 0.0; |
| ADD_OR_SIZE(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, |
| &minFocusDistance, 1); |
| // 5 m hyperfocal distance for back camera, infinity (fixed focus) for front |
| const float hyperFocalDistance = mFacingBack ? 1.0/5.0 : 0.0; |
| ADD_OR_SIZE(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE, |
| &hyperFocalDistance, 1); |
| |
| static const float focalLength = 3.30f; // mm |
| ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, |
| &focalLength, 1); |
| static const float aperture = 2.8f; |
| ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_APERTURES, |
| &aperture, 1); |
| static const float filterDensity = 0; |
| ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES, |
| &filterDensity, 1); |
| static const uint8_t availableOpticalStabilization = |
| ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION, |
| &availableOpticalStabilization, 1); |
| |
| static const int32_t lensShadingMapSize[] = {1, 1}; |
| ADD_OR_SIZE(ANDROID_LENS_INFO_SHADING_MAP_SIZE, lensShadingMapSize, |
| sizeof(lensShadingMapSize)/sizeof(int32_t)); |
| |
| int32_t lensFacing = mFacingBack ? |
| ANDROID_LENS_FACING_BACK : ANDROID_LENS_FACING_FRONT; |
| ADD_OR_SIZE(ANDROID_LENS_FACING, &lensFacing, 1); |
| |
| // android.sensor |
| |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE, |
| Sensor::kExposureTimeRange, 2); |
| |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, |
| &Sensor::kFrameDurationRange[1], 1); |
| |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE, |
| Sensor::kSensitivityRange, |
| sizeof(Sensor::kSensitivityRange) |
| /sizeof(int32_t)); |
| |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, |
| &Sensor::kColorFilterArrangement, 1); |
| |
| static const float sensorPhysicalSize[2] = {3.20f, 2.40f}; // mm |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, |
| sensorPhysicalSize, 2); |
| |
| const int32_t pixelArray[] = {mSensorWidth, mSensorHeight}; |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, |
| pixelArray, 2); |
| |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, |
| pixelArray, 2); |
| |
| ADD_OR_SIZE(ANDROID_SENSOR_INFO_WHITE_LEVEL, |
| &Sensor::kMaxRawValue, 1); |
| |
| static const int32_t blackLevelPattern[4] = { |
| static_cast<int32_t>(Sensor::kBlackLevel), |
| static_cast<int32_t>(Sensor::kBlackLevel), |
| static_cast<int32_t>(Sensor::kBlackLevel), |
| static_cast<int32_t>(Sensor::kBlackLevel) |
| }; |
| ADD_OR_SIZE(ANDROID_SENSOR_BLACK_LEVEL_PATTERN, |
| blackLevelPattern, sizeof(blackLevelPattern)/sizeof(int32_t)); |
| |
| //TODO: sensor color calibration fields |
| |
| // android.flash |
| static const uint8_t flashAvailable = 0; |
| ADD_OR_SIZE(ANDROID_FLASH_INFO_AVAILABLE, &flashAvailable, 1); |
| |
| static const int64_t flashChargeDuration = 0; |
| ADD_OR_SIZE(ANDROID_FLASH_INFO_CHARGE_DURATION, &flashChargeDuration, 1); |
| |
| // android.tonemap |
| |
| static const int32_t tonemapCurvePoints = 128; |
| ADD_OR_SIZE(ANDROID_TONEMAP_MAX_CURVE_POINTS, &tonemapCurvePoints, 1); |
| |
| // android.scaler |
| |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_FORMATS, |
| kAvailableFormats, |
| sizeof(kAvailableFormats)/sizeof(uint32_t)); |
| |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_RAW_SIZES, |
| kAvailableRawSizes, |
| sizeof(kAvailableRawSizes)/sizeof(uint32_t)); |
| |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS, |
| kAvailableRawMinDurations, |
| sizeof(kAvailableRawMinDurations)/sizeof(uint64_t)); |
| |
| if (mFacingBack) { |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, |
| kAvailableProcessedSizesBack, |
| sizeof(kAvailableProcessedSizesBack)/sizeof(uint32_t)); |
| } else { |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, |
| kAvailableProcessedSizesFront, |
| sizeof(kAvailableProcessedSizesFront)/sizeof(uint32_t)); |
| } |
| |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS, |
| kAvailableProcessedMinDurations, |
| sizeof(kAvailableProcessedMinDurations)/sizeof(uint64_t)); |
| |
| if (mFacingBack) { |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, |
| kAvailableJpegSizesBack, |
| sizeof(kAvailableJpegSizesBack)/sizeof(uint32_t)); |
| } else { |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, |
| kAvailableJpegSizesFront, |
| sizeof(kAvailableJpegSizesFront)/sizeof(uint32_t)); |
| } |
| |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS, |
| kAvailableJpegMinDurations, |
| sizeof(kAvailableJpegMinDurations)/sizeof(uint64_t)); |
| |
| static const float maxZoom = 10; |
| ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, |
| &maxZoom, 1); |
| |
| // android.jpeg |
| |
| static const int32_t jpegThumbnailSizes[] = { |
| 0, 0, |
| 160, 120, |
| 320, 240 |
| }; |
| ADD_OR_SIZE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, |
| jpegThumbnailSizes, sizeof(jpegThumbnailSizes)/sizeof(int32_t)); |
| |
| static const int32_t jpegMaxSize = JpegCompressor::kMaxJpegSize; |
| ADD_OR_SIZE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1); |
| |
| // android.stats |
| |
| static const uint8_t availableFaceDetectModes[] = { |
| ANDROID_STATISTICS_FACE_DETECT_MODE_OFF, |
| ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE, |
| ANDROID_STATISTICS_FACE_DETECT_MODE_FULL |
| }; |
| |
| ADD_OR_SIZE(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, |
| availableFaceDetectModes, |
| sizeof(availableFaceDetectModes)); |
| |
| static const int32_t maxFaceCount = 8; |
| ADD_OR_SIZE(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, |
| &maxFaceCount, 1); |
| |
| static const int32_t histogramSize = 64; |
| ADD_OR_SIZE(ANDROID_STATISTICS_INFO_HISTOGRAM_BUCKET_COUNT, |
| &histogramSize, 1); |
| |
| static const int32_t maxHistogramCount = 1000; |
| ADD_OR_SIZE(ANDROID_STATISTICS_INFO_MAX_HISTOGRAM_COUNT, |
| &maxHistogramCount, 1); |
| |
| static const int32_t sharpnessMapSize[2] = {64, 64}; |
| ADD_OR_SIZE(ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE, |
| sharpnessMapSize, sizeof(sharpnessMapSize)/sizeof(int32_t)); |
| |
| static const int32_t maxSharpnessMapValue = 1000; |
| ADD_OR_SIZE(ANDROID_STATISTICS_INFO_MAX_SHARPNESS_MAP_VALUE, |
| &maxSharpnessMapValue, 1); |
| |
| // android.control |
| |
| static const uint8_t availableSceneModes[] = { |
| ANDROID_CONTROL_SCENE_MODE_DISABLED |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, |
| availableSceneModes, sizeof(availableSceneModes)); |
| |
| static const uint8_t availableEffects[] = { |
| ANDROID_CONTROL_EFFECT_MODE_OFF |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AVAILABLE_EFFECTS, |
| availableEffects, sizeof(availableEffects)); |
| |
| static const int32_t max3aRegions[] = {/*AE*/ 0,/*AWB*/ 0,/*AF*/ 0}; |
| ADD_OR_SIZE(ANDROID_CONTROL_MAX_REGIONS, |
| max3aRegions, sizeof(max3aRegions)/sizeof(max3aRegions[0])); |
| |
| static const uint8_t availableAeModes[] = { |
| ANDROID_CONTROL_AE_MODE_OFF, |
| ANDROID_CONTROL_AE_MODE_ON |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_MODES, |
| availableAeModes, sizeof(availableAeModes)); |
| |
| static const camera_metadata_rational exposureCompensationStep = { |
| 1, 3 |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_COMPENSATION_STEP, |
| &exposureCompensationStep, 1); |
| |
| int32_t exposureCompensationRange[] = {-9, 9}; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_COMPENSATION_RANGE, |
| exposureCompensationRange, |
| sizeof(exposureCompensationRange)/sizeof(int32_t)); |
| |
| static const int32_t availableTargetFpsRanges[] = { |
| 5, 30, 15, 30 |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, |
| availableTargetFpsRanges, |
| sizeof(availableTargetFpsRanges)/sizeof(int32_t)); |
| |
| static const uint8_t availableAntibandingModes[] = { |
| ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF, |
| ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, |
| availableAntibandingModes, sizeof(availableAntibandingModes)); |
| |
| static const uint8_t availableAwbModes[] = { |
| ANDROID_CONTROL_AWB_MODE_OFF, |
| ANDROID_CONTROL_AWB_MODE_AUTO, |
| ANDROID_CONTROL_AWB_MODE_INCANDESCENT, |
| ANDROID_CONTROL_AWB_MODE_FLUORESCENT, |
| ANDROID_CONTROL_AWB_MODE_DAYLIGHT, |
| ANDROID_CONTROL_AWB_MODE_SHADE |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AWB_AVAILABLE_MODES, |
| availableAwbModes, sizeof(availableAwbModes)); |
| |
| static const uint8_t availableAfModesBack[] = { |
| ANDROID_CONTROL_AF_MODE_OFF, |
| ANDROID_CONTROL_AF_MODE_AUTO, |
| ANDROID_CONTROL_AF_MODE_MACRO, |
| ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO, |
| ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE |
| }; |
| |
| static const uint8_t availableAfModesFront[] = { |
| ANDROID_CONTROL_AF_MODE_OFF |
| }; |
| |
| if (mFacingBack) { |
| ADD_OR_SIZE(ANDROID_CONTROL_AF_AVAILABLE_MODES, |
| availableAfModesBack, sizeof(availableAfModesBack)); |
| } else { |
| ADD_OR_SIZE(ANDROID_CONTROL_AF_AVAILABLE_MODES, |
| availableAfModesFront, sizeof(availableAfModesFront)); |
| } |
| |
| static const uint8_t availableVstabModes[] = { |
| ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, |
| availableVstabModes, sizeof(availableVstabModes)); |
| |
| #undef ADD_OR_SIZE |
| /** Allocate metadata if sizing */ |
| if (sizeRequest) { |
| ALOGV("Allocating %zu entries, %zu extra bytes for " |
| "static camera info", |
| entryCount, dataCount); |
| *info = allocate_camera_metadata(entryCount, dataCount); |
| if (*info == NULL) { |
| ALOGE("Unable to allocate camera static info" |
| "(%zu entries, %zu bytes extra data)", |
| entryCount, dataCount); |
| return NO_MEMORY; |
| } |
| } |
| return OK; |
| } |
| |
| status_t EmulatedFakeCamera2::constructDefaultRequest( |
| int request_template, |
| camera_metadata_t **request, |
| bool sizeRequest) const { |
| |
| size_t entryCount = 0; |
| size_t dataCount = 0; |
| status_t ret; |
| |
| #define ADD_OR_SIZE( tag, data, count ) \ |
| if ( ( ret = addOrSize(*request, sizeRequest, &entryCount, &dataCount, \ |
| tag, data, count) ) != OK ) return ret |
| |
| /** android.request */ |
| |
| static const uint8_t requestType = ANDROID_REQUEST_TYPE_CAPTURE; |
| ADD_OR_SIZE(ANDROID_REQUEST_TYPE, &requestType, 1); |
| |
| static const uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL; |
| ADD_OR_SIZE(ANDROID_REQUEST_METADATA_MODE, &metadataMode, 1); |
| |
| static const int32_t id = 0; |
| ADD_OR_SIZE(ANDROID_REQUEST_ID, &id, 1); |
| |
| static const int32_t frameCount = 0; |
| ADD_OR_SIZE(ANDROID_REQUEST_FRAME_COUNT, &frameCount, 1); |
| |
| // OUTPUT_STREAMS set by user |
| entryCount += 1; |
| dataCount += 5; // TODO: Should be maximum stream number |
| |
| /** android.lens */ |
| |
| static const float focusDistance = 0; |
| ADD_OR_SIZE(ANDROID_LENS_FOCUS_DISTANCE, &focusDistance, 1); |
| |
| static const float aperture = 2.8f; |
| ADD_OR_SIZE(ANDROID_LENS_APERTURE, &aperture, 1); |
| |
| static const float focalLength = 5.0f; |
| ADD_OR_SIZE(ANDROID_LENS_FOCAL_LENGTH, &focalLength, 1); |
| |
| static const float filterDensity = 0; |
| ADD_OR_SIZE(ANDROID_LENS_FILTER_DENSITY, &filterDensity, 1); |
| |
| static const uint8_t opticalStabilizationMode = |
| ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_LENS_OPTICAL_STABILIZATION_MODE, |
| &opticalStabilizationMode, 1); |
| |
| // FOCUS_RANGE set only in frame |
| |
| /** android.sensor */ |
| |
| static const int64_t exposureTime = 10 * MSEC; |
| ADD_OR_SIZE(ANDROID_SENSOR_EXPOSURE_TIME, &exposureTime, 1); |
| |
| static const int64_t frameDuration = 33333333L; // 1/30 s |
| ADD_OR_SIZE(ANDROID_SENSOR_FRAME_DURATION, &frameDuration, 1); |
| |
| static const int32_t sensitivity = 100; |
| ADD_OR_SIZE(ANDROID_SENSOR_SENSITIVITY, &sensitivity, 1); |
| |
| // TIMESTAMP set only in frame |
| |
| /** android.flash */ |
| |
| static const uint8_t flashMode = ANDROID_FLASH_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_FLASH_MODE, &flashMode, 1); |
| |
| static const uint8_t flashPower = 10; |
| ADD_OR_SIZE(ANDROID_FLASH_FIRING_POWER, &flashPower, 1); |
| |
| static const int64_t firingTime = 0; |
| ADD_OR_SIZE(ANDROID_FLASH_FIRING_TIME, &firingTime, 1); |
| |
| /** Processing block modes */ |
| uint8_t hotPixelMode = 0; |
| uint8_t demosaicMode = 0; |
| uint8_t noiseMode = 0; |
| uint8_t shadingMode = 0; |
| uint8_t colorMode = 0; |
| uint8_t tonemapMode = 0; |
| uint8_t edgeMode = 0; |
| switch (request_template) { |
| case CAMERA2_TEMPLATE_STILL_CAPTURE: |
| // fall-through |
| case CAMERA2_TEMPLATE_VIDEO_SNAPSHOT: |
| // fall-through |
| case CAMERA2_TEMPLATE_ZERO_SHUTTER_LAG: |
| hotPixelMode = ANDROID_HOT_PIXEL_MODE_HIGH_QUALITY; |
| demosaicMode = ANDROID_DEMOSAIC_MODE_HIGH_QUALITY; |
| noiseMode = ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY; |
| shadingMode = ANDROID_SHADING_MODE_HIGH_QUALITY; |
| colorMode = ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY; |
| tonemapMode = ANDROID_TONEMAP_MODE_HIGH_QUALITY; |
| edgeMode = ANDROID_EDGE_MODE_HIGH_QUALITY; |
| break; |
| case CAMERA2_TEMPLATE_PREVIEW: |
| // fall-through |
| case CAMERA2_TEMPLATE_VIDEO_RECORD: |
| // fall-through |
| default: |
| hotPixelMode = ANDROID_HOT_PIXEL_MODE_FAST; |
| demosaicMode = ANDROID_DEMOSAIC_MODE_FAST; |
| noiseMode = ANDROID_NOISE_REDUCTION_MODE_FAST; |
| shadingMode = ANDROID_SHADING_MODE_FAST; |
| colorMode = ANDROID_COLOR_CORRECTION_MODE_FAST; |
| tonemapMode = ANDROID_TONEMAP_MODE_FAST; |
| edgeMode = ANDROID_EDGE_MODE_FAST; |
| break; |
| } |
| ADD_OR_SIZE(ANDROID_HOT_PIXEL_MODE, &hotPixelMode, 1); |
| ADD_OR_SIZE(ANDROID_DEMOSAIC_MODE, &demosaicMode, 1); |
| ADD_OR_SIZE(ANDROID_NOISE_REDUCTION_MODE, &noiseMode, 1); |
| ADD_OR_SIZE(ANDROID_SHADING_MODE, &shadingMode, 1); |
| ADD_OR_SIZE(ANDROID_COLOR_CORRECTION_MODE, &colorMode, 1); |
| ADD_OR_SIZE(ANDROID_TONEMAP_MODE, &tonemapMode, 1); |
| ADD_OR_SIZE(ANDROID_EDGE_MODE, &edgeMode, 1); |
| |
| /** android.noise */ |
| static const uint8_t noiseStrength = 5; |
| ADD_OR_SIZE(ANDROID_NOISE_REDUCTION_STRENGTH, &noiseStrength, 1); |
| |
| /** android.color */ |
| static const float colorTransform[9] = { |
| 1.0f, 0.f, 0.f, |
| 0.f, 1.f, 0.f, |
| 0.f, 0.f, 1.f |
| }; |
| ADD_OR_SIZE(ANDROID_COLOR_CORRECTION_TRANSFORM, colorTransform, 9); |
| |
| /** android.tonemap */ |
| static const float tonemapCurve[4] = { |
| 0.f, 0.f, |
| 1.f, 1.f |
| }; |
| ADD_OR_SIZE(ANDROID_TONEMAP_CURVE_RED, tonemapCurve, 4); |
| ADD_OR_SIZE(ANDROID_TONEMAP_CURVE_GREEN, tonemapCurve, 4); |
| ADD_OR_SIZE(ANDROID_TONEMAP_CURVE_BLUE, tonemapCurve, 4); |
| |
| /** android.edge */ |
| static const uint8_t edgeStrength = 5; |
| ADD_OR_SIZE(ANDROID_EDGE_STRENGTH, &edgeStrength, 1); |
| |
| /** android.scaler */ |
| static const int32_t cropRegion[3] = { |
| 0, 0, static_cast<int32_t>(mSensorWidth) |
| }; |
| ADD_OR_SIZE(ANDROID_SCALER_CROP_REGION, cropRegion, 3); |
| |
| /** android.jpeg */ |
| static const int32_t jpegQuality = 80; |
| ADD_OR_SIZE(ANDROID_JPEG_QUALITY, &jpegQuality, 1); |
| |
| static const int32_t thumbnailSize[2] = { |
| 640, 480 |
| }; |
| ADD_OR_SIZE(ANDROID_JPEG_THUMBNAIL_SIZE, thumbnailSize, 2); |
| |
| static const int32_t thumbnailQuality = 80; |
| ADD_OR_SIZE(ANDROID_JPEG_THUMBNAIL_QUALITY, &thumbnailQuality, 1); |
| |
| static const double gpsCoordinates[2] = { |
| 0, 0 |
| }; |
| ADD_OR_SIZE(ANDROID_JPEG_GPS_COORDINATES, gpsCoordinates, 2); |
| |
| static const uint8_t gpsProcessingMethod[32] = "None"; |
| ADD_OR_SIZE(ANDROID_JPEG_GPS_PROCESSING_METHOD, gpsProcessingMethod, 32); |
| |
| static const int64_t gpsTimestamp = 0; |
| ADD_OR_SIZE(ANDROID_JPEG_GPS_TIMESTAMP, &gpsTimestamp, 1); |
| |
| static const int32_t jpegOrientation = 0; |
| ADD_OR_SIZE(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1); |
| |
| /** android.stats */ |
| |
| static const uint8_t faceDetectMode = |
| ANDROID_STATISTICS_FACE_DETECT_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_STATISTICS_FACE_DETECT_MODE, &faceDetectMode, 1); |
| |
| static const uint8_t histogramMode = ANDROID_STATISTICS_HISTOGRAM_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_STATISTICS_HISTOGRAM_MODE, &histogramMode, 1); |
| |
| static const uint8_t sharpnessMapMode = |
| ANDROID_STATISTICS_SHARPNESS_MAP_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_STATISTICS_SHARPNESS_MAP_MODE, &sharpnessMapMode, 1); |
| |
| // faceRectangles, faceScores, faceLandmarks, faceIds, histogram, |
| // sharpnessMap only in frames |
| |
| /** android.control */ |
| |
| uint8_t controlIntent = 0; |
| switch (request_template) { |
| case CAMERA2_TEMPLATE_PREVIEW: |
| controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW; |
| break; |
| case CAMERA2_TEMPLATE_STILL_CAPTURE: |
| controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE; |
| break; |
| case CAMERA2_TEMPLATE_VIDEO_RECORD: |
| controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD; |
| break; |
| case CAMERA2_TEMPLATE_VIDEO_SNAPSHOT: |
| controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT; |
| break; |
| case CAMERA2_TEMPLATE_ZERO_SHUTTER_LAG: |
| controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG; |
| break; |
| default: |
| controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_CUSTOM; |
| break; |
| } |
| ADD_OR_SIZE(ANDROID_CONTROL_CAPTURE_INTENT, &controlIntent, 1); |
| |
| static const uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO; |
| ADD_OR_SIZE(ANDROID_CONTROL_MODE, &controlMode, 1); |
| |
| static const uint8_t effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_CONTROL_EFFECT_MODE, &effectMode, 1); |
| |
| static const uint8_t sceneMode = ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY; |
| ADD_OR_SIZE(ANDROID_CONTROL_SCENE_MODE, &sceneMode, 1); |
| |
| static const uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_MODE, &aeMode, 1); |
| |
| static const uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_LOCK, &aeLock, 1); |
| |
| static const int32_t controlRegions[5] = { |
| 0, 0, |
| static_cast<int32_t>(mSensorWidth), |
| static_cast<int32_t>(mSensorHeight), |
| 1000 |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_REGIONS, controlRegions, 5); |
| |
| static const int32_t aeExpCompensation = 0; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &aeExpCompensation, 1); |
| |
| static const int32_t aeTargetFpsRange[2] = { |
| 10, 30 |
| }; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, aeTargetFpsRange, 2); |
| |
| static const uint8_t aeAntibandingMode = |
| ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO; |
| ADD_OR_SIZE(ANDROID_CONTROL_AE_ANTIBANDING_MODE, &aeAntibandingMode, 1); |
| |
| static const uint8_t awbMode = |
| ANDROID_CONTROL_AWB_MODE_AUTO; |
| ADD_OR_SIZE(ANDROID_CONTROL_AWB_MODE, &awbMode, 1); |
| |
| static const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF; |
| ADD_OR_SIZE(ANDROID_CONTROL_AWB_LOCK, &awbLock, 1); |
| |
| ADD_OR_SIZE(ANDROID_CONTROL_AWB_REGIONS, controlRegions, 5); |
| |
| uint8_t afMode = 0; |
| switch (request_template) { |
| case CAMERA2_TEMPLATE_PREVIEW: |
| afMode = ANDROID_CONTROL_AF_MODE_AUTO; |
| break; |
| case CAMERA2_TEMPLATE_STILL_CAPTURE: |
| afMode = ANDROID_CONTROL_AF_MODE_AUTO; |
| break; |
| case CAMERA2_TEMPLATE_VIDEO_RECORD: |
| afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO; |
| break; |
| case CAMERA2_TEMPLATE_VIDEO_SNAPSHOT: |
| afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO; |
| break; |
| case CAMERA2_TEMPLATE_ZERO_SHUTTER_LAG: |
| afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE; |
| break; |
| default: |
| afMode = ANDROID_CONTROL_AF_MODE_AUTO; |
| break; |
| } |
| ADD_OR_SIZE(ANDROID_CONTROL_AF_MODE, &afMode, 1); |
| |
| ADD_OR_SIZE(ANDROID_CONTROL_AF_REGIONS, controlRegions, 5); |
| |
| static const uint8_t vstabMode = |
| ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF; |
| ADD_OR_SIZE(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &vstabMode, 1); |
| |
| // aeState, awbState, afState only in frame |
| |
| /** Allocate metadata if sizing */ |
| if (sizeRequest) { |
| ALOGV("Allocating %zu entries, %zu extra bytes for " |
| "request template type %d", |
| entryCount, dataCount, request_template); |
| *request = allocate_camera_metadata(entryCount, dataCount); |
| if (*request == NULL) { |
| ALOGE("Unable to allocate new request template type %d " |
| "(%zu entries, %zu bytes extra data)", request_template, |
| entryCount, dataCount); |
| return NO_MEMORY; |
| } |
| } |
| return OK; |
| #undef ADD_OR_SIZE |
| } |
| |
| status_t EmulatedFakeCamera2::addOrSize(camera_metadata_t *request, |
| bool sizeRequest, |
| size_t *entryCount, |
| size_t *dataCount, |
| uint32_t tag, |
| const void *entryData, |
| size_t entryDataCount) { |
| if (!sizeRequest) { |
| return add_camera_metadata_entry(request, tag, entryData, |
| entryDataCount); |
| } else { |
| int type = get_camera_metadata_tag_type(tag); |
| if (type < 0 ) return BAD_VALUE; |
| (*entryCount)++; |
| (*dataCount) += calculate_camera_metadata_entry_data_size(type, |
| entryDataCount); |
| return OK; |
| } |
| } |
| |
| bool EmulatedFakeCamera2::isStreamInUse(uint32_t id) { |
| // Assumes mMutex is locked; otherwise new requests could enter |
| // configureThread while readoutThread is being checked |
| |
| // Order of isStreamInUse calls matters |
| if (mConfigureThread->isStreamInUse(id) || |
| mReadoutThread->isStreamInUse(id) || |
| mJpegCompressor->isStreamInUse(id) ) { |
| ALOGE("%s: Stream %d is in use in active requests!", |
| __FUNCTION__, id); |
| return true; |
| } |
| return false; |
| } |
| |
| bool EmulatedFakeCamera2::isReprocessStreamInUse(uint32_t id) { |
| // TODO: implement |
| return false; |
| } |
| |
| const Stream& EmulatedFakeCamera2::getStreamInfo(uint32_t streamId) { |
| Mutex::Autolock lock(mMutex); |
| |
| return mStreams.valueFor(streamId); |
| } |
| |
| const ReprocessStream& EmulatedFakeCamera2::getReprocessStreamInfo(uint32_t streamId) { |
| Mutex::Autolock lock(mMutex); |
| |
| return mReprocessStreams.valueFor(streamId); |
| } |
| |
| }; /* namespace android */ |