blob: ca3bd6b97c558b534721e1256879b23bb54bb9ea [file] [log] [blame]
/*
* 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.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_FakeCamera2"
#include <utils/Log.h>
#include "EmulatedFakeCamera2.h"
#include "EmulatedCameraFactory.h"
#include <ui/Rect.h>
#include <ui/GraphicBufferMapper.h>
namespace android {
const uint32_t EmulatedFakeCamera2::kAvailableFormats[1] = {
HAL_PIXEL_FORMAT_RAW_SENSOR
};
const uint32_t EmulatedFakeCamera2::kAvailableSizesPerFormat[1] = {
1
};
const uint32_t EmulatedFakeCamera2::kAvailableSizes[2] = {
640, 480
// Sensor::kResolution[0], Sensor::kResolution[1]
};
const uint64_t EmulatedFakeCamera2::kAvailableMinFrameDurations[1] = {
Sensor::kFrameDurationRange[0]
};
EmulatedFakeCamera2::EmulatedFakeCamera2(int cameraId,
bool facingBack,
struct hw_module_t* module)
: EmulatedCamera2(cameraId,module),
mFacingBack(facingBack)
{
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;
mCameraInfo = allocate_camera_metadata(10,100);
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SENSOR_EXPOSURE_TIME_RANGE,
Sensor::kExposureTimeRange, 2);
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SENSOR_MAX_FRAME_DURATION,
&Sensor::kFrameDurationRange[1], 1);
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SENSOR_AVAILABLE_SENSITIVITIES,
Sensor::kAvailableSensitivities,
sizeof(Sensor::kAvailableSensitivities)
/sizeof(uint32_t));
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SENSOR_COLOR_FILTER_ARRANGEMENT,
&Sensor::kColorFilterArrangement, 1);
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SENSOR_PIXEL_ARRAY_SIZE,
Sensor::kResolution, 2);
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SENSOR_ACTIVE_ARRAY_SIZE,
Sensor::kResolution, 2);
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SCALER_AVAILABLE_FORMATS,
kAvailableFormats,
sizeof(kAvailableFormats)/sizeof(uint32_t));
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SCALER_AVAILABLE_SIZES_PER_FORMAT,
kAvailableSizesPerFormat,
sizeof(kAvailableSizesPerFormat)/sizeof(uint32_t));
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SCALER_AVAILABLE_SIZES,
kAvailableSizes,
sizeof(kAvailableSizes)/sizeof(uint32_t));
res = add_camera_metadata_entry(mCameraInfo,
ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
kAvailableMinFrameDurations,
sizeof(kAvailableMinFrameDurations)/sizeof(uint32_t));
// TODO: Add all the others
return NO_ERROR;
}
/****************************************************************************
* Camera module API overrides
***************************************************************************/
status_t EmulatedFakeCamera2::connectCamera(hw_device_t** device) {
status_t res;
ALOGV("%s", __FUNCTION__);
mConfigureThread = new ConfigureThread(this);
mReadoutThread = new ReadoutThread(this);
mSensor = new Sensor();
mNextStreamId = 0;
mRawStreamOps = NULL;
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;
return EmulatedCamera2::connectCamera(device);
}
status_t EmulatedFakeCamera2::closeCamera() {
Mutex::Autolock l(mMutex);
status_t res;
ALOGV("%s", __FUNCTION__);
res = mSensor->shutDown();
if (res != NO_ERROR) {
ALOGE("%s: Unable to shut down sensor: %d", __FUNCTION__, res);
return res;
}
mConfigureThread->requestExit();
mReadoutThread->requestExit();
mConfigureThread->join();
mReadoutThread->join();
ALOGV("%s exit", __FUNCTION__);
return NO_ERROR;
}
status_t EmulatedFakeCamera2::getCameraInfo(struct camera_info *info) {
info->facing = mFacingBack ? CAMERA_FACING_BACK : CAMERA_FACING_FRONT;
info->orientation = 0;
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(mRawStreamOps != NULL,
"%s: No raw stream allocated, but received queue notification!",
__FUNCTION__);
return mConfigureThread->newRequestAvailable();
}
int EmulatedFakeCamera2::allocateStream(
uint32_t width,
uint32_t height,
int format,
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 (mNextStreamId > 0) {
// TODO: Support more than one stream
ALOGW("%s: Only one stream supported", __FUNCTION__);
return BAD_VALUE;
}
unsigned int numFormats = sizeof(kAvailableFormats) / sizeof(uint32_t);
unsigned int formatIdx = 0;
unsigned int sizeOffsetIdx = 0;
for (; formatIdx < numFormats; formatIdx++) {
if (format == (int)kAvailableFormats[formatIdx]) break;
sizeOffsetIdx += kAvailableSizesPerFormat[formatIdx];
}
if (formatIdx == numFormats) {
ALOGW("%s: Format 0x%x is not supported", __FUNCTION__, format);
return BAD_VALUE;
}
unsigned int resIdx = 0;
for (; resIdx < kAvailableSizesPerFormat[formatIdx]; resIdx++) {
uint32_t widthMatch = kAvailableSizes[ (sizeOffsetIdx + resIdx)*2 + 0];
uint32_t heightMatch = kAvailableSizes[ (sizeOffsetIdx + resIdx)*2 + 1];
if (width == widthMatch && height == heightMatch) break;
}
if (resIdx == kAvailableSizesPerFormat[formatIdx]) {
ALOGW("%s: Format 0x%x does not support resolution %d, %d", __FUNCTION__,
format, width, height);
return BAD_VALUE;
}
// TODO: Generalize below to work for variable types of streams, etc.
// Currently only correct for raw sensor format, sensor resolution.
ALOG_ASSERT(format == HAL_PIXEL_FORMAT_RAW_SENSOR,
"%s: TODO: Only supporting raw sensor format right now", __FUNCTION__);
ALOG_ASSERT(width == Sensor::kResolution[0],
"%s: TODO: Only supporting raw sensor size right now", __FUNCTION__);
ALOG_ASSERT(height == Sensor::kResolution[1],
"%s: TODO: Only supporting raw sensor size right now", __FUNCTION__);
mRawStreamOps = stream_ops;
*stream_id = mNextStreamId;
if (format_actual) *format_actual = format;
*usage = GRALLOC_USAGE_SW_WRITE_OFTEN;
*max_buffers = 4;
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) {
// Emulator doesn't need to register these with V4L2, etc.
ALOGV("%s: Stream %d registering %d buffers", __FUNCTION__,
stream_id, num_buffers);
return NO_ERROR;
}
int EmulatedFakeCamera2::releaseStream(uint32_t stream_id) {
Mutex::Autolock l(mMutex);
ALOG_ASSERT(stream_id == 0,
"%s: TODO: Only one stream supported", __FUNCTION__);
// TODO: Need to clean up better than this - in-flight buffers likely
mRawStreamOps = NULL;
return NO_ERROR;
}
/** Custom tag definitions */
// Emulator camera metadata sections
enum {
EMULATOR_SCENE = VENDOR_SECTION,
END_EMULATOR_SECTIONS
};
enum {
EMULATOR_SCENE_START = EMULATOR_SCENE << 16,
};
// Emulator camera metadata tags
enum {
// Hour of day to use for lighting calculations (0-23). Default: 12
EMULATOR_SCENE_HOUROFDAY = EMULATOR_SCENE_START,
EMULATOR_SCENE_END
};
unsigned int emulator_metadata_section_bounds[END_EMULATOR_SECTIONS -
VENDOR_SECTION][2] = {
{ EMULATOR_SCENE_START, EMULATOR_SCENE_END }
};
const char *emulator_metadata_section_names[END_EMULATOR_SECTIONS -
VENDOR_SECTION] = {
"com.android.emulator.scene"
};
typedef struct emulator_tag_info {
const char *tag_name;
uint8_t tag_type;
} emulator_tag_info_t;
emulator_tag_info_t emulator_scene[EMULATOR_SCENE_END - EMULATOR_SCENE_START] = {
{ "hourOfDay", TYPE_INT32 }
};
emulator_tag_info_t *tag_info[END_EMULATOR_SECTIONS -
VENDOR_SECTION] = {
emulator_scene
};
const char* EmulatedFakeCamera2::getVendorSectionName(uint32_t tag) {
ALOGV("%s", __FUNCTION__);
uint32_t section = tag >> 16;
if (section < VENDOR_SECTION || section > END_EMULATOR_SECTIONS) return NULL;
return emulator_metadata_section_names[section - VENDOR_SECTION];
}
const char* EmulatedFakeCamera2::getVendorTagName(uint32_t tag) {
ALOGV("%s", __FUNCTION__);
uint32_t section = tag >> 16;
if (section < VENDOR_SECTION || section > END_EMULATOR_SECTIONS) return NULL;
uint32_t section_index = section - VENDOR_SECTION;
if (tag >= emulator_metadata_section_bounds[section_index][1]) {
return NULL;
}
uint32_t tag_index = tag & 0xFFFF;
return tag_info[section_index][tag_index].tag_name;
}
int EmulatedFakeCamera2::getVendorTagType(uint32_t tag) {
ALOGV("%s", __FUNCTION__);
uint32_t section = tag >> 16;
if (section < VENDOR_SECTION || section > END_EMULATOR_SECTIONS) return -1;
uint32_t section_index = section - VENDOR_SECTION;
if (tag >= emulator_metadata_section_bounds[section_index][1]) {
return -1;
}
uint32_t tag_index = tag & 0xFFFF;
return tag_info[section_index][tag_index].tag_type;
}
/** Shutdown and debug methods */
int EmulatedFakeCamera2::dump(int fd) {
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) {
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::threadLoop() {
static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms
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) {
ALOGV("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("Request queue empty, going inactive");
// No requests available, go into inactive mode
Mutex::Autolock lock(mInputMutex);
mActive = false;
return true;
}
// Get necessary parameters for sensor config
sort_camera_metadata(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;
}
// TODO: Only raw stream supported
if (streams.count != 1 || streams.data.u8[0] != 0) {
ALOGE("%s: TODO: Only raw stream supported", __FUNCTION__);
mParent->signalError();
return false;
}
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", __FUNCTION__);
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", __FUNCTION__);
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;
res = find_camera_metadata_entry(mRequest,
EMULATOR_SCENE_HOUROFDAY,
&e);
if (res == NO_ERROR) {
ALOGV("Setting hour: %d", *e.data.i32);
mParent->mSensor->getScene().setHour(*e.data.i32);
}
// TODO: Fetch stride from gralloc
mNextBufferStride = Sensor::kResolution[0];
// Start waiting on sensor
ALOGV("Waiting for sensor");
}
bool vsync = mParent->mSensor->waitForVSync(kWaitPerLoop);
if (vsync) {
ALOGV("Configuring sensor for frame %d", mNextFrameNumber);
mParent->mSensor->setExposureTime(mNextExposureTime);
mParent->mSensor->setFrameDuration(mNextFrameDuration);
mParent->mSensor->setSensitivity(mNextSensitivity);
/** Get buffer to fill for this frame */
// TODO: Only does raw stream
/* Get next buffer from raw stream */
mNextBuffer = NULL;
res = mParent->mRawStreamOps->dequeue_buffer(mParent->mRawStreamOps,
&mNextBuffer);
if (res != NO_ERROR || mNextBuffer == NULL) {
ALOGE("%s: Unable to dequeue buffer from stream %d: %d",
__FUNCTION__, 0, res);
mParent->signalError();
return false;
}
/* Lock the buffer from the perspective of the graphics mapper */
uint8_t *img;
const Rect rect(Sensor::kResolution[0], Sensor::kResolution[1]);
res = GraphicBufferMapper::get().lock(*mNextBuffer,
GRALLOC_USAGE_SW_WRITE_OFTEN,
rect, (void**)&img);
if (res != NO_ERROR) {
ALOGE("%s: grbuffer_mapper.lock failure: %d", __FUNCTION__, res);
mParent->mRawStreamOps->cancel_buffer(mParent->mRawStreamOps,
mNextBuffer);
mParent->signalError();
return false;
}
mParent->mSensor->setDestinationBuffer(img, mNextBufferStride);
mParent->mReadoutThread->setNextCapture(mRequest, mNextBuffer);
mRequest = NULL;
}
return true;
}
EmulatedFakeCamera2::ReadoutThread::ReadoutThread(EmulatedFakeCamera2 *parent):
Thread(false),
mParent(parent),
mRunning(false),
mActive(false),
mRequest(NULL),
mBuffer(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;
}
void EmulatedFakeCamera2::ReadoutThread::setNextCapture(camera_metadata_t *request,
buffer_handle_t *buffer) {
Mutex::Autolock lock(mInputMutex);
if ( (mInFlightTail + 1) % kInFlightQueueSize == mInFlightHead) {
ALOGE("In flight queue full, dropping captures");
mParent->signalError();
return;
}
mInFlightQueue[mInFlightTail].request = request;
mInFlightQueue[mInFlightTail].buffer = buffer;
mInFlightTail = (mInFlightTail + 1) % kInFlightQueueSize;
if (!mActive) {
mActive = true;
mInputSignal.signal();
}
}
bool EmulatedFakeCamera2::ReadoutThread::threadLoop() {
static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms
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
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 {
mRequest = mInFlightQueue[mInFlightHead].request;
mBuffer = mInFlightQueue[mInFlightHead].buffer;
mInFlightQueue[mInFlightHead].request = NULL;
mInFlightQueue[mInFlightHead].buffer = NULL;
mInFlightHead = (mInFlightHead + 1) % kInFlightQueueSize;
}
}
}
// Active with request, wait on sensor to complete
nsecs_t captureTime;
bool gotFrame;
gotFrame = mParent->mSensor->waitForNewFrame(kWaitPerLoop,
&captureTime);
if (!gotFrame) return true;
// Got sensor data, construct frame and send it out
ALOGV("Readout: Constructing metadata and frames");
camera_metadata_entry_t metadataMode;
res = find_camera_metadata_entry(mRequest,
ANDROID_REQUEST_METADATA_MODE,
&metadataMode);
if (*metadataMode.data.u8 == ANDROID_REQUEST_METADATA_FULL) {
ALOGV("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);
frame_entries += 2;
frame_data += 8;
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");
}
add_camera_metadata_entry(frame,
ANDROID_SENSOR_TIMESTAMP,
&captureTime,
1);
int32_t hourOfDay = (int32_t)mParent->mSensor->getScene().getHour();
camera_metadata_entry_t requestedHour;
res = find_camera_metadata_entry(frame,
EMULATOR_SCENE_HOUROFDAY,
&requestedHour);
if (res == NAME_NOT_FOUND) {
ALOGV("Adding vendor tag");
res = add_camera_metadata_entry(frame,
EMULATOR_SCENE_HOUROFDAY,
&hourOfDay, 1);
if (res != NO_ERROR) {
ALOGE("Unable to add vendor tag");
}
} else if (res == OK) {
ALOGV("Replacing value in vendor tag");
*requestedHour.data.i32 = hourOfDay;
} else {
ALOGE("Error looking up vendor tag");
}
// TODO: Collect all final values used from sensor in addition to timestamp
mParent->mFrameQueueDst->enqueue_frame(mParent->mFrameQueueDst,
frame);
}
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;
ALOGV("Sending image buffer to output stream.");
GraphicBufferMapper::get().unlock(*mBuffer);
mParent->mRawStreamOps->enqueue_buffer(mParent->mRawStreamOps,
captureTime, mBuffer);
mBuffer = NULL;
return true;
}
}; /* namespace android */