You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2728 lines
90 KiB
2728 lines
90 KiB
/*
|
|
* 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 <ui/GraphicBufferMapper.h>
|
|
#include <ui/Rect.h>
|
|
|
|
#include "EmulatedFakeCamera2.h"
|
|
#include "EmulatedCameraFactory.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,
|
|
GraphicBufferMapper* gbm)
|
|
: EmulatedCamera2(cameraId,module),
|
|
mFacingBack(facingBack),
|
|
mIsConnected(false),
|
|
mGBM(gbm)
|
|
{
|
|
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(mGBM);
|
|
|
|
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 = mParent->mGBM->lock(*(b.buffer),
|
|
GRALLOC_USAGE_HW_CAMERA_WRITE,
|
|
Rect(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 = GraphicBufferMapper::get().lock(*(b.buffer),
|
|
GRALLOC_USAGE_HW_CAMERA_READ,
|
|
Rect(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);
|
|
mParent->mGBM->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);
|
|
|
|
mParent->mGBM->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;
|
|
mParent->mGBM->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 */
|