/* * 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 #include #include #include #define LOG_NDEBUG 0 #define LOG_TAG "EmulatedCamera_FakeCamera2" #include #include "EmulatedCameraFactory.h" #include "EmulatedFakeCamera2.h" #include "GrallocModule.h" #define ERROR_CAMERA_NOT_PRESENT -EPIPE #define CAMERA2_EXT_TRIGGER_TESTING_DISCONNECT 0xFFFFFFFF template char (&ArraySizeHelper(T (&array)[N]))[N]; template char (&ArraySizeHelper(const T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) namespace android { const int64_t USEC = 1000LL; const int64_t MSEC = USEC * 1000LL; const int64_t SEC = MSEC * 1000LL; const uint32_t EmulatedFakeCamera2::kAvailableFormats[] = { 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(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(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(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(const cuttlefish::CameraDefinition ¶ms) { status_t res; for (size_t index = 0; index < params.resolutions.size(); ++index) { mAvailableRawSizes.push_back(params.resolutions[index].width); mAvailableRawSizes.push_back(params.resolutions[index].height); mAvailableProcessedSizes.push_back(params.resolutions[index].width); mAvailableProcessedSizes.push_back(params.resolutions[index].height); mAvailableJpegSizes.push_back(params.resolutions[index].width); mAvailableJpegSizes.push_back(params.resolutions[index].height); } // Find max width/height int32_t width = 0, height = 0; for (size_t index = 0; index < params.resolutions.size(); ++index) { if (width <= params.resolutions[index].width && height <= params.resolutions[index].height) { width = params.resolutions[index].width; height = params.resolutions[index].height; } } if (width < 640 || height < 480) { width = 640; height = 480; } mSensorWidth = width; mSensorHeight = height; /* TODO(ender): probably should drop this. */ std::copy(kAvailableRawSizes, kAvailableRawSizes + arraysize(kAvailableRawSizes), std::back_inserter(mAvailableRawSizes)); if (params.orientation == cuttlefish::CameraDefinition::kFront) { std::copy(kAvailableProcessedSizesFront, kAvailableProcessedSizesFront + arraysize(kAvailableProcessedSizesFront), std::back_inserter(mAvailableProcessedSizes)); std::copy(kAvailableJpegSizesFront, kAvailableJpegSizesFront + arraysize(kAvailableJpegSizesFront), std::back_inserter(mAvailableJpegSizes)); } else { std::copy( kAvailableProcessedSizesBack, kAvailableProcessedSizesBack + arraysize(kAvailableProcessedSizesBack), mAvailableProcessedSizes.begin()); std::copy(kAvailableJpegSizesBack, kAvailableJpegSizesBack + arraysize(kAvailableJpegSizesBack), mAvailableJpegSizes.begin()); } 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 = EmulatedCameraFactory::Instance().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 = &mAvailableRawSizes.front(); availableSizeCount = mAvailableRawSizes.size(); break; case HAL_PIXEL_FORMAT_BLOB: availableSizes = &mAvailableJpegSizes.front(); availableSizeCount = mAvailableJpegSizes.size(); 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 = &mAvailableProcessedSizes.front(); availableSizeCount = mAvailableProcessedSizes.size(); 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; EmulatedCameraFactory::Instance().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 = 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) { ALOGV("Configure: Dequeing buffer from stream %d", b.streamId); Stream s = mParent->getStreamInfo(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; } /* Import the buffer from the perspective of the graphics mapper */ res = GrallocModule::getInstance().import(*(b.buffer), &b.importedBuffer); if (res != NO_ERROR) { ALOGE("%s: grbuffer_mapper.import failure: %s (%d)", __FUNCTION__, strerror(-res), res); s.ops->cancel_buffer(s.ops, b.buffer); mParent->signalError(); return false; } /* Lock the buffer from the perspective of the graphics mapper */ const int usage = GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_CAMERA_WRITE; res = GrallocModule::getInstance().lock( b.importedBuffer, usage, 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 { ALOGV("Configure: Acquiring buffer from reprocess stream %d", -b.streamId); ReprocessStream s = mParent->getReprocessStreamInfo(-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; } /* Import the buffer from the perspective of the graphics mapper */ res = GrallocModule::getInstance().import(*(b.buffer), &b.importedBuffer); if (res != NO_ERROR) { ALOGE("%s: grbuffer_mapper.import failure: %s (%d)", __FUNCTION__, strerror(-res), res); s.ops->release_buffer(s.ops, b.buffer); mParent->signalError(); return false; } /* Lock the buffer from the perspective of the graphics mapper */ const int usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_CAMERA_READ; res = GrallocModule::getInstance().lock( b.importedBuffer, usage, 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.importedBuffer); GrallocModule::getInstance().release(b.importedBuffer); 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); } 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) { 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.importedBuffer); GrallocModule::getInstance().release(jpegBuffer.importedBuffer); const Stream &s = mParent->getStreamInfo(jpegBuffer.streamId); s.ops->enqueue_buffer(s.ops, mJpegTimestamp, jpegBuffer.buffer); } void EmulatedFakeCamera2::ReadoutThread::onJpegInputDone( const StreamBuffer &inputBuffer) { status_t res; GrallocModule::getInstance().unlock(inputBuffer.importedBuffer); GrallocModule::getInstance().release(inputBuffer.importedBuffer); 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(mParent->mSensorWidth * 10 / 20), static_cast(mParent->mSensorHeight * 15 / 20), static_cast(mParent->mSensorWidth * 12 / 20), static_cast(mParent->mSensorHeight * 17 / 20), static_cast(mParent->mSensorWidth * 16 / 20), static_cast(mParent->mSensorHeight * 15 / 20), static_cast(mParent->mSensorWidth * 18 / 20), static_cast(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() / (float)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() / (float)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(mParent->mSensorWidth * 10.5 / 20), static_cast(mParent->mSensorHeight * 16 / 20), static_cast(mParent->mSensorWidth * 11.5 / 20), static_cast(mParent->mSensorHeight * 16 / 20), static_cast(mParent->mSensorWidth * 11 / 20), static_cast(mParent->mSensorHeight * 16.5 / 20), static_cast(mParent->mSensorWidth * 16.5 / 20), static_cast(mParent->mSensorHeight * 16 / 20), static_cast(mParent->mSensorWidth * 17.5 / 20), static_cast(mParent->mSensorHeight * 16 / 20), static_cast(mParent->mSensorWidth * 17 / 20), static_cast(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() / (float)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, &minFocusDistance, 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(Sensor::kBlackLevel), static_cast(Sensor::kBlackLevel), static_cast(Sensor::kBlackLevel), static_cast(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, &mAvailableRawSizes.front(), mAvailableRawSizes.size()); ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS, kAvailableRawMinDurations, sizeof(kAvailableRawMinDurations) / sizeof(uint64_t)); ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, &mAvailableProcessedSizes.front(), mAvailableProcessedSizes.size()); ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS, kAvailableProcessedMinDurations, sizeof(kAvailableProcessedMinDurations) / sizeof(uint64_t)); ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, &mAvailableJpegSizes.front(), mAvailableJpegSizes.size()); 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(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(mSensorWidth), static_cast(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 */