/*
* 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.
*/
#define LOG_TAG "Camera2-Device"
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
//#define LOG_NNDEBUG 0 // Per-frame verbose logging
#ifdef LOG_NNDEBUG
#define ALOGVV(...) ALOGV(__VA_ARGS__)
#else
#define ALOGVV(...) ((void)0)
#endif
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/Timers.h>
#include "Camera2Device.h"
namespace android {
Camera2Device::Camera2Device(int id):
mId(id),
mHal2Device(NULL)
{
ATRACE_CALL();
ALOGV("%s: Created device for camera %d", __FUNCTION__, id);
}
Camera2Device::~Camera2Device()
{
ATRACE_CALL();
ALOGV("%s: Tearing down for camera id %d", __FUNCTION__, mId);
disconnect();
}
int Camera2Device::getId() const {
return mId;
}
status_t Camera2Device::initialize(camera_module_t *module)
{
ATRACE_CALL();
ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
if (mHal2Device != NULL) {
ALOGE("%s: Already initialized!", __FUNCTION__);
return INVALID_OPERATION;
}
status_t res;
char name[10];
snprintf(name, sizeof(name), "%d", mId);
camera2_device_t *device;
res = module->common.methods->open(&module->common, name,
reinterpret_cast<hw_device_t**>(&device));
if (res != OK) {
ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__,
mId, strerror(-res), res);
return res;
}
if (device->common.version != CAMERA_DEVICE_API_VERSION_2_0) {
ALOGE("%s: Could not open camera %d: "
"Camera device is not version %x, reports %x instead",
__FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_2_0,
device->common.version);
device->common.close(&device->common);
return BAD_VALUE;
}
camera_info info;
res = module->get_camera_info(mId, &info);
if (res != OK ) return res;
if (info.device_version != device->common.version) {
ALOGE("%s: HAL reporting mismatched camera_info version (%x)"
" and device version (%x).", __FUNCTION__,
device->common.version, info.device_version);
device->common.close(&device->common);
return BAD_VALUE;
}
res = mRequestQueue.setConsumerDevice(device);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to connect request queue to device: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
device->common.close(&device->common);
return res;
}
res = mFrameQueue.setProducerDevice(device);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to connect frame queue to device: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
device->common.close(&device->common);
return res;
}
res = device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps);
if (res != OK ) {
ALOGE("%s: Camera %d: Unable to retrieve tag ops from device: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
device->common.close(&device->common);
return res;
}
res = set_camera_metadata_vendor_tag_ops(mVendorTagOps);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
device->common.close(&device->common);
return res;
}
res = device->ops->set_notify_callback(device, notificationCallback,
NULL);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to initialize notification callback!",
__FUNCTION__, mId);
device->common.close(&device->common);
return res;
}
mDeviceInfo = info.static_camera_characteristics;
mHal2Device = device;
return OK;
}
status_t Camera2Device::disconnect() {
ATRACE_CALL();
status_t res = OK;
if (mHal2Device) {
ALOGV("%s: Closing device for camera %d", __FUNCTION__, mId);
int inProgressCount = mHal2Device->ops->get_in_progress_count(mHal2Device);
if (inProgressCount > 0) {
ALOGW("%s: Closing camera device %d with %d requests in flight!",
__FUNCTION__, mId, inProgressCount);
}
mReprocessStreams.clear();
mStreams.clear();
res = mHal2Device->common.close(&mHal2Device->common);
if (res != OK) {
ALOGE("%s: Could not close camera %d: %s (%d)",
__FUNCTION__,
mId, strerror(-res), res);
}
mHal2Device = NULL;
ALOGV("%s: Shutdown complete", __FUNCTION__);
}
return res;
}
status_t Camera2Device::dump(int fd, const Vector<String16>& args) {
ATRACE_CALL();
String8 result;
int detailLevel = 0;
int n = args.size();
String16 detailOption("-d");
for (int i = 0; i + 1 < n; i++) {
if (args[i] == detailOption) {
String8 levelStr(args[i+1]);
detailLevel = atoi(levelStr.string());
}
}
result.appendFormat(" Camera2Device[%d] dump (detail level %d):\n",
mId, detailLevel);
if (detailLevel > 0) {
result = " Request queue contents:\n";
write(fd, result.string(), result.size());
mRequestQueue.dump(fd, args);
result = " Frame queue contents:\n";
write(fd, result.string(), result.size());
mFrameQueue.dump(fd, args);
}
result = " Active streams:\n";
write(fd, result.string(), result.size());
for (StreamList::iterator s = mStreams.begin(); s != mStreams.end(); s++) {
(*s)->dump(fd, args);
}
result = " HAL device dump:\n";
write(fd, result.string(), result.size());
status_t res;
res = mHal2Device->ops->dump(mHal2Device, fd);
return res;
}
const CameraMetadata& Camera2Device::info() const {
ALOGVV("%s: E", __FUNCTION__);
return mDeviceInfo;
}
status_t Camera2Device::capture(CameraMetadata &request) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
mRequestQueue.enqueue(request.release());
return OK;
}
status_t Camera2Device::setStreamingRequest(const CameraMetadata &request) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
CameraMetadata streamRequest(request);
return mRequestQueue.setStreamSlot(streamRequest.release());
}
status_t Camera2Device::clearStreamingRequest() {
ATRACE_CALL();
return mRequestQueue.setStreamSlot(NULL);
}
status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
ATRACE_CALL();
return mRequestQueue.waitForDequeue(requestId, timeout);
}
status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format, size_t size, int *id) {
ATRACE_CALL();
status_t res;
ALOGV("%s: E", __FUNCTION__);
sp<StreamAdapter> stream = new StreamAdapter(mHal2Device);
res = stream->connectToDevice(consumer, width, height, format, size);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create stream (%d x %d, format %x):"
"%s (%d)",
__FUNCTION__, mId, width, height, format, strerror(-res), res);
return res;
}
*id = stream->getId();
mStreams.push_back(stream);
return OK;
}
status_t Camera2Device::createReprocessStreamFromStream(int outputId, int *id) {
ATRACE_CALL();
status_t res;
ALOGV("%s: E", __FUNCTION__);
bool found = false;
StreamList::iterator streamI;
for (streamI = mStreams.begin();
streamI != mStreams.end(); streamI++) {
if ((*streamI)->getId() == outputId) {
found = true;
break;
}
}
if (!found) {
ALOGE("%s: Camera %d: Output stream %d doesn't exist; can't create "
"reprocess stream from it!", __FUNCTION__, mId, outputId);
return BAD_VALUE;
}
sp<ReprocessStreamAdapter> stream = new ReprocessStreamAdapter(mHal2Device);
res = stream->connectToDevice((*streamI));
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create reprocessing stream from "\
"stream %d: %s (%d)", __FUNCTION__, mId, outputId,
strerror(-res), res);
return res;
}
*id = stream->getId();
mReprocessStreams.push_back(stream);
return OK;
}
status_t Camera2Device::getStreamInfo(int id,
uint32_t *width, uint32_t *height, uint32_t *format) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
bool found = false;
StreamList::iterator streamI;
for (streamI = mStreams.begin();
streamI != mStreams.end(); streamI++) {
if ((*streamI)->getId() == id) {
found = true;
break;
}
}
if (!found) {
ALOGE("%s: Camera %d: Stream %d does not exist",
__FUNCTION__, mId, id);
return BAD_VALUE;
}
if (width) *width = (*streamI)->getWidth();
if (height) *height = (*streamI)->getHeight();
if (format) *format = (*streamI)->getFormat();
return OK;
}
status_t Camera2Device::setStreamTransform(int id,
int transform) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
bool found = false;
StreamList::iterator streamI;
for (streamI = mStreams.begin();
streamI != mStreams.end(); streamI++) {
if ((*streamI)->getId() == id) {
found = true;
break;
}
}
if (!found) {
ALOGE("%s: Camera %d: Stream %d does not exist",
__FUNCTION__, mId, id);
return BAD_VALUE;
}
return (*streamI)->setTransform(transform);
}
status_t Camera2Device::deleteStream(int id) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
bool found = false;
for (StreamList::iterator streamI = mStreams.begin();
streamI != mStreams.end(); streamI++) {
if ((*streamI)->getId() == id) {
status_t res = (*streamI)->release();
if (res != OK) {
ALOGE("%s: Unable to release stream %d from HAL device: "
"%s (%d)", __FUNCTION__, id, strerror(-res), res);
return res;
}
mStreams.erase(streamI);
found = true;
break;
}
}
if (!found) {
ALOGE("%s: Camera %d: Unable to find stream %d to delete",
__FUNCTION__, mId, id);
return BAD_VALUE;
}
return OK;
}
status_t Camera2Device::deleteReprocessStream(int id) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
bool found = false;
for (ReprocessStreamList::iterator streamI = mReprocessStreams.begin();
streamI != mReprocessStreams.end(); streamI++) {
if ((*streamI)->getId() == id) {
status_t res = (*streamI)->release();
if (res != OK) {
ALOGE("%s: Unable to release reprocess stream %d from "
"HAL device: %s (%d)", __FUNCTION__, id,
strerror(-res), res);
return res;
}
mReprocessStreams.erase(streamI);
found = true;
break;
}
}
if (!found) {
ALOGE("%s: Camera %d: Unable to find stream %d to delete",
__FUNCTION__, mId, id);
return BAD_VALUE;
}
return OK;
}
status_t Camera2Device::createDefaultRequest(int templateId,
CameraMetadata *request) {
ATRACE_CALL();
status_t err;
ALOGV("%s: E", __FUNCTION__);
camera_metadata_t *rawRequest;
err = mHal2Device->ops->construct_default_request(
mHal2Device, templateId, &rawRequest);
request->acquire(rawRequest);
return err;
}
status_t Camera2Device::waitUntilDrained() {
ATRACE_CALL();
static const uint32_t kSleepTime = 50000; // 50 ms
static const uint32_t kMaxSleepTime = 10000000; // 10 s
ALOGV("%s: Camera %d: Starting wait", __FUNCTION__, mId);
if (mRequestQueue.getBufferCount() ==
CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS) return INVALID_OPERATION;
// TODO: Set up notifications from HAL, instead of sleeping here
uint32_t totalTime = 0;
while (mHal2Device->ops->get_in_progress_count(mHal2Device) > 0) {
usleep(kSleepTime);
totalTime += kSleepTime;
if (totalTime > kMaxSleepTime) {
ALOGE("%s: Waited %d us, %d requests still in flight", __FUNCTION__,
totalTime, mHal2Device->ops->get_in_progress_count(mHal2Device));
return TIMED_OUT;
}
}
ALOGV("%s: Camera %d: HAL is idle", __FUNCTION__, mId);
return OK;
}
status_t Camera2Device::setNotifyCallback(NotificationListener *listener) {
ATRACE_CALL();
status_t res;
res = mHal2Device->ops->set_notify_callback(mHal2Device, notificationCallback,
reinterpret_cast<void*>(listener) );
if (res != OK) {
ALOGE("%s: Unable to set notification callback!", __FUNCTION__);
}
return res;
}
void Camera2Device::notificationCallback(int32_t msg_type,
int32_t ext1,
int32_t ext2,
int32_t ext3,
void *user) {
ATRACE_CALL();
NotificationListener *listener = reinterpret_cast<NotificationListener*>(user);
ALOGV("%s: Notification %d, arguments %d, %d, %d", __FUNCTION__, msg_type,
ext1, ext2, ext3);
if (listener != NULL) {
switch (msg_type) {
case CAMERA2_MSG_ERROR:
listener->notifyError(ext1, ext2, ext3);
break;
case CAMERA2_MSG_SHUTTER: {
nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
listener->notifyShutter(ext1, timestamp);
break;
}
case CAMERA2_MSG_AUTOFOCUS:
listener->notifyAutoFocus(ext1, ext2);
break;
case CAMERA2_MSG_AUTOEXPOSURE:
listener->notifyAutoExposure(ext1, ext2);
break;
case CAMERA2_MSG_AUTOWB:
listener->notifyAutoWhitebalance(ext1, ext2);
break;
default:
ALOGE("%s: Unknown notification %d (arguments %d, %d, %d)!",
__FUNCTION__, msg_type, ext1, ext2, ext3);
}
}
}
status_t Camera2Device::waitForNextFrame(nsecs_t timeout) {
return mFrameQueue.waitForBuffer(timeout);
}
status_t Camera2Device::getNextFrame(CameraMetadata *frame) {
ATRACE_CALL();
status_t res;
camera_metadata_t *rawFrame;
res = mFrameQueue.dequeue(&rawFrame);
if (rawFrame == NULL) {
return NOT_ENOUGH_DATA;
} else if (res == OK) {
frame->acquire(rawFrame);
}
return res;
}
status_t Camera2Device::triggerAutofocus(uint32_t id) {
ATRACE_CALL();
status_t res;
ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
res = mHal2Device->ops->trigger_action(mHal2Device,
CAMERA2_TRIGGER_AUTOFOCUS, id, 0);
if (res != OK) {
ALOGE("%s: Error triggering autofocus (id %d)",
__FUNCTION__, id);
}
return res;
}
status_t Camera2Device::triggerCancelAutofocus(uint32_t id) {
ATRACE_CALL();
status_t res;
ALOGV("%s: Canceling autofocus, id %d", __FUNCTION__, id);
res = mHal2Device->ops->trigger_action(mHal2Device,
CAMERA2_TRIGGER_CANCEL_AUTOFOCUS, id, 0);
if (res != OK) {
ALOGE("%s: Error canceling autofocus (id %d)",
__FUNCTION__, id);
}
return res;
}
status_t Camera2Device::triggerPrecaptureMetering(uint32_t id) {
ATRACE_CALL();
status_t res;
ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
res = mHal2Device->ops->trigger_action(mHal2Device,
CAMERA2_TRIGGER_PRECAPTURE_METERING, id, 0);
if (res != OK) {
ALOGE("%s: Error triggering precapture metering (id %d)",
__FUNCTION__, id);
}
return res;
}
status_t Camera2Device::pushReprocessBuffer(int reprocessStreamId,
buffer_handle_t *buffer, wp<BufferReleasedListener> listener) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
bool found = false;
status_t res = OK;
for (ReprocessStreamList::iterator streamI = mReprocessStreams.begin();
streamI != mReprocessStreams.end(); streamI++) {
if ((*streamI)->getId() == reprocessStreamId) {
res = (*streamI)->pushIntoStream(buffer, listener);
if (res != OK) {
ALOGE("%s: Unable to push buffer to reprocess stream %d: %s (%d)",
__FUNCTION__, reprocessStreamId, strerror(-res), res);
return res;
}
found = true;
break;
}
}
if (!found) {
ALOGE("%s: Camera %d: Unable to find reprocess stream %d",
__FUNCTION__, mId, reprocessStreamId);
res = BAD_VALUE;
}
return res;
}
/**
* Camera2Device::MetadataQueue
*/
Camera2Device::MetadataQueue::MetadataQueue():
mHal2Device(NULL),
mFrameCount(0),
mLatestRequestId(0),
mCount(0),
mStreamSlotCount(0),
mSignalConsumer(true)
{
ATRACE_CALL();
camera2_request_queue_src_ops::dequeue_request = consumer_dequeue;
camera2_request_queue_src_ops::request_count = consumer_buffer_count;
camera2_request_queue_src_ops::free_request = consumer_free;
camera2_frame_queue_dst_ops::dequeue_frame = producer_dequeue;
camera2_frame_queue_dst_ops::cancel_frame = producer_cancel;
camera2_frame_queue_dst_ops::enqueue_frame = producer_enqueue;
}
Camera2Device::MetadataQueue::~MetadataQueue() {
ATRACE_CALL();
Mutex::Autolock l(mMutex);
freeBuffers(mEntries.begin(), mEntries.end());
freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
}
// Connect to camera2 HAL as consumer (input requests/reprocessing)
status_t Camera2Device::MetadataQueue::setConsumerDevice(camera2_device_t *d) {
ATRACE_CALL();
status_t res;
res = d->ops->set_request_queue_src_ops(d,
this);
if (res != OK) return res;
mHal2Device = d;
return OK;
}
status_t Camera2Device::MetadataQueue::setProducerDevice(camera2_device_t *d) {
ATRACE_CALL();
status_t res;
res = d->ops->set_frame_queue_dst_ops(d,
this);
return res;
}
// Real interfaces
status_t Camera2Device::MetadataQueue::enqueue(camera_metadata_t *buf) {
ATRACE_CALL();
ALOGVV("%s: E", __FUNCTION__);
Mutex::Autolock l(mMutex);
mCount++;
mEntries.push_back(buf);
return signalConsumerLocked();
}
int Camera2Device::MetadataQueue::getBufferCount() {
ATRACE_CALL();
Mutex::Autolock l(mMutex);
if (mStreamSlotCount > 0) {
return CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS;
}
return mCount;
}
status_t Camera2Device::MetadataQueue::dequeue(camera_metadata_t **buf,
bool incrementCount)
{
ATRACE_CALL();
ALOGVV("%s: E", __FUNCTION__);
status_t res;
Mutex::Autolock l(mMutex);
if (mCount == 0) {
if (mStreamSlotCount == 0) {
ALOGVV("%s: Empty", __FUNCTION__);
*buf = NULL;
mSignalConsumer = true;
return OK;
}
ALOGVV("%s: Streaming %d frames to queue", __FUNCTION__,
mStreamSlotCount);
for (List<camera_metadata_t*>::iterator slotEntry = mStreamSlot.begin();
slotEntry != mStreamSlot.end();
slotEntry++ ) {
size_t entries = get_camera_metadata_entry_count(*slotEntry);
size_t dataBytes = get_camera_metadata_data_count(*slotEntry);
camera_metadata_t *copy =
allocate_camera_metadata(entries, dataBytes);
append_camera_metadata(copy, *slotEntry);
mEntries.push_back(copy);
}
mCount = mStreamSlotCount;
}
ALOGVV("MetadataQueue: deque (%d buffers)", mCount);
camera_metadata_t *b = *(mEntries.begin());
mEntries.erase(mEntries.begin());
if (incrementCount) {
ATRACE_INT("cam2_request", mFrameCount);
camera_metadata_entry_t frameCount;
res = find_camera_metadata_entry(b,
ANDROID_REQUEST_FRAME_COUNT,
&frameCount);
if (res != OK) {
ALOGE("%s: Unable to add frame count: %s (%d)",
__FUNCTION__, strerror(-res), res);
} else {
*frameCount.data.i32 = mFrameCount;
}
mFrameCount++;
}
// Check for request ID, and if present, signal waiters.
camera_metadata_entry_t requestId;
res = find_camera_metadata_entry(b,
ANDROID_REQUEST_ID,
&requestId);
if (res == OK) {
mLatestRequestId = requestId.data.i32[0];
mNewRequestId.signal();
}
*buf = b;
mCount--;
return OK;
}
status_t Camera2Device::MetadataQueue::waitForBuffer(nsecs_t timeout)
{
Mutex::Autolock l(mMutex);
status_t res;
while (mCount == 0) {
res = notEmpty.waitRelative(mMutex,timeout);
if (res != OK) return res;
}
return OK;
}
status_t Camera2Device::MetadataQueue::waitForDequeue(int32_t id,
nsecs_t timeout) {
Mutex::Autolock l(mMutex);
status_t res;
while (mLatestRequestId != id) {
nsecs_t startTime = systemTime();
res = mNewRequestId.waitRelative(mMutex, timeout);
if (res != OK) return res;
timeout -= (systemTime() - startTime);
}
return OK;
}
status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf)
{
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock l(mMutex);
if (buf == NULL) {
freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
mStreamSlotCount = 0;
return OK;
}
camera_metadata_t *buf2 = clone_camera_metadata(buf);
if (!buf2) {
ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__);
return NO_MEMORY;
}
if (mStreamSlotCount > 1) {
List<camera_metadata_t*>::iterator deleter = ++mStreamSlot.begin();
freeBuffers(++mStreamSlot.begin(), mStreamSlot.end());
mStreamSlotCount = 1;
}
if (mStreamSlotCount == 1) {
free_camera_metadata( *(mStreamSlot.begin()) );
*(mStreamSlot.begin()) = buf2;
} else {
mStreamSlot.push_front(buf2);
mStreamSlotCount = 1;
}
return signalConsumerLocked();
}
status_t Camera2Device::MetadataQueue::setStreamSlot(
const List<camera_metadata_t*> &bufs)
{
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock l(mMutex);
if (mStreamSlotCount > 0) {
freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
}
mStreamSlotCount = 0;
for (List<camera_metadata_t*>::const_iterator r = bufs.begin();
r != bufs.end(); r++) {
camera_metadata_t *r2 = clone_camera_metadata(*r);
if (!r2) {
ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__);
return NO_MEMORY;
}
mStreamSlot.push_back(r2);
mStreamSlotCount++;
}
return signalConsumerLocked();
}
status_t Camera2Device::MetadataQueue::dump(int fd,
const Vector<String16>& /*args*/) {
ATRACE_CALL();
String8 result;
status_t notLocked;
notLocked = mMutex.tryLock();
if (notLocked) {
result.append(" (Unable to lock queue mutex)\n");
}
result.appendFormat(" Current frame number: %d\n", mFrameCount);
if (mStreamSlotCount == 0) {
result.append(" Stream slot: Empty\n");
write(fd, result.string(), result.size());
} else {
result.appendFormat(" Stream slot: %d entries\n",
mStreamSlot.size());
int i = 0;
for (List<camera_metadata_t*>::iterator r = mStreamSlot.begin();
r != mStreamSlot.end(); r++) {
result = String8::format(" Stream slot buffer %d:\n", i);
write(fd, result.string(), result.size());
dump_indented_camera_metadata(*r, fd, 2, 10);
i++;
}
}
if (mEntries.size() == 0) {
result = " Main queue is empty\n";
write(fd, result.string(), result.size());
} else {
result = String8::format(" Main queue has %d entries:\n",
mEntries.size());
int i = 0;
for (List<camera_metadata_t*>::iterator r = mEntries.begin();
r != mEntries.end(); r++) {
result = String8::format(" Queue entry %d:\n", i);
write(fd, result.string(), result.size());
dump_indented_camera_metadata(*r, fd, 2, 10);
i++;
}
}
if (notLocked == 0) {
mMutex.unlock();
}
return OK;
}
status_t Camera2Device::MetadataQueue::signalConsumerLocked() {
ATRACE_CALL();
status_t res = OK;
notEmpty.signal();
if (mSignalConsumer && mHal2Device != NULL) {
mSignalConsumer = false;
mMutex.unlock();
ALOGV("%s: Signaling consumer", __FUNCTION__);
res = mHal2Device->ops->notify_request_queue_not_empty(mHal2Device);
mMutex.lock();
}
return res;
}
status_t Camera2Device::MetadataQueue::freeBuffers(
List<camera_metadata_t*>::iterator start,
List<camera_metadata_t*>::iterator end)
{
ATRACE_CALL();
while (start != end) {
free_camera_metadata(*start);
start = mStreamSlot.erase(start);
}
return OK;
}
Camera2Device::MetadataQueue* Camera2Device::MetadataQueue::getInstance(
const camera2_request_queue_src_ops_t *q)
{
const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q);
return const_cast<MetadataQueue*>(cmq);
}
Camera2Device::MetadataQueue* Camera2Device::MetadataQueue::getInstance(
const camera2_frame_queue_dst_ops_t *q)
{
const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q);
return const_cast<MetadataQueue*>(cmq);
}
int Camera2Device::MetadataQueue::consumer_buffer_count(
const camera2_request_queue_src_ops_t *q)
{
MetadataQueue *queue = getInstance(q);
return queue->getBufferCount();
}
int Camera2Device::MetadataQueue::consumer_dequeue(
const camera2_request_queue_src_ops_t *q,
camera_metadata_t **buffer)
{
MetadataQueue *queue = getInstance(q);
return queue->dequeue(buffer, true);
}
int Camera2Device::MetadataQueue::consumer_free(
const camera2_request_queue_src_ops_t *q,
camera_metadata_t *old_buffer)
{
ATRACE_CALL();
MetadataQueue *queue = getInstance(q);
(void)queue;
free_camera_metadata(old_buffer);
return OK;
}
int Camera2Device::MetadataQueue::producer_dequeue(
const camera2_frame_queue_dst_ops_t * /*q*/,
size_t entries, size_t bytes,
camera_metadata_t **buffer)
{
ATRACE_CALL();
camera_metadata_t *new_buffer =
allocate_camera_metadata(entries, bytes);
if (new_buffer == NULL) return NO_MEMORY;
*buffer = new_buffer;
return OK;
}
int Camera2Device::MetadataQueue::producer_cancel(
const camera2_frame_queue_dst_ops_t * /*q*/,
camera_metadata_t *old_buffer)
{
ATRACE_CALL();
free_camera_metadata(old_buffer);
return OK;
}
int Camera2Device::MetadataQueue::producer_enqueue(
const camera2_frame_queue_dst_ops_t *q,
camera_metadata_t *filled_buffer)
{
MetadataQueue *queue = getInstance(q);
return queue->enqueue(filled_buffer);
}
/**
* Camera2Device::StreamAdapter
*/
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char*)(ptr) - offsetof(type, member))
#endif
Camera2Device::StreamAdapter::StreamAdapter(camera2_device_t *d):
mState(RELEASED),
mHal2Device(d),
mId(-1),
mWidth(0), mHeight(0), mFormat(0), mSize(0), mUsage(0),
mMaxProducerBuffers(0), mMaxConsumerBuffers(0),
mTotalBuffers(0),
mFormatRequested(0),
mActiveBuffers(0),
mFrameCount(0),
mLastTimestamp(0)
{
camera2_stream_ops::dequeue_buffer = dequeue_buffer;
camera2_stream_ops::enqueue_buffer = enqueue_buffer;
camera2_stream_ops::cancel_buffer = cancel_buffer;
camera2_stream_ops::set_crop = set_crop;
}
Camera2Device::StreamAdapter::~StreamAdapter() {
ATRACE_CALL();
if (mState != RELEASED) {
release();
}
}
status_t Camera2Device::StreamAdapter::connectToDevice(
sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format, size_t size) {
ATRACE_CALL();
status_t res;
ALOGV("%s: E", __FUNCTION__);
if (mState != RELEASED) return INVALID_OPERATION;
if (consumer == NULL) {
ALOGE("%s: Null consumer passed to stream adapter", __FUNCTION__);
return BAD_VALUE;
}
ALOGV("%s: New stream parameters %d x %d, format 0x%x, size %d",
__FUNCTION__, width, height, format, size);
mConsumerInterface = consumer;
mWidth = width;
mHeight = height;
mSize = (format == HAL_PIXEL_FORMAT_BLOB) ? size : 0;
mFormatRequested = format;
// Allocate device-side stream interface
uint32_t id;
uint32_t formatActual;
uint32_t usage;
uint32_t maxBuffers = 2;
res = mHal2Device->ops->allocate_stream(mHal2Device,
mWidth, mHeight, mFormatRequested, getStreamOps(),
&id, &formatActual, &usage, &maxBuffers);
if (res != OK) {
ALOGE("%s: Device stream allocation failed: %s (%d)",
__FUNCTION__, strerror(-res), res);
return res;
}
ALOGV("%s: Allocated stream id %d, actual format 0x%x, "
"usage 0x%x, producer wants %d buffers", __FUNCTION__,
id, formatActual, usage, maxBuffers);
mId = id;
mFormat = formatActual;
mUsage = usage;
mMaxProducerBuffers = maxBuffers;
mState = ALLOCATED;
// Configure consumer-side ANativeWindow interface
res = native_window_api_connect(mConsumerInterface.get(),
NATIVE_WINDOW_API_CAMERA);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mId);
return res;
}
mState = CONNECTED;
res = native_window_set_usage(mConsumerInterface.get(), mUsage);
if (res != OK) {
ALOGE("%s: Unable to configure usage %08x for stream %d",
__FUNCTION__, mUsage, mId);
return res;
}
res = native_window_set_scaling_mode(mConsumerInterface.get(),
NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
if (res != OK) {
ALOGE("%s: Unable to configure stream scaling: %s (%d)",
__FUNCTION__, strerror(-res), res);
return res;
}
res = setTransform(0);
if (res != OK) {
return res;
}
if (mFormat == HAL_PIXEL_FORMAT_BLOB) {
res = native_window_set_buffers_geometry(mConsumerInterface.get(),
mSize, 1, mFormat);
if (res != OK) {
ALOGE("%s: Unable to configure compressed stream buffer geometry"
" %d x %d, size %d for stream %d",
__FUNCTION__, mWidth, mHeight, mSize, mId);
return res;
}
} else {
res = native_window_set_buffers_geometry(mConsumerInterface.get(),
mWidth, mHeight, mFormat);
if (res != OK) {
ALOGE("%s: Unable to configure stream buffer geometry"
" %d x %d, format 0x%x for stream %d",
__FUNCTION__, mWidth, mHeight, mFormat, mId);
return res;
}
}
int maxConsumerBuffers;
res = mConsumerInterface->query(mConsumerInterface.get(),
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
if (res != OK) {
ALOGE("%s: Unable to query consumer undequeued"
" buffer count for stream %d", __FUNCTION__, mId);
return res;
}
mMaxConsumerBuffers = maxConsumerBuffers;
ALOGV("%s: Consumer wants %d buffers", __FUNCTION__,
mMaxConsumerBuffers);
mTotalBuffers = mMaxConsumerBuffers + mMaxProducerBuffers;
mActiveBuffers = 0;
mFrameCount = 0;
mLastTimestamp = 0;
res = native_window_set_buffer_count(mConsumerInterface.get(),
mTotalBuffers);
if (res != OK) {
ALOGE("%s: Unable to set buffer count for stream %d",
__FUNCTION__, mId);
return res;
}
// Register allocated buffers with HAL device
buffer_handle_t *buffers = new buffer_handle_t[mTotalBuffers];
ANativeWindowBuffer **anwBuffers = new ANativeWindowBuffer*[mTotalBuffers];
uint32_t bufferIdx = 0;
for (; bufferIdx < mTotalBuffers; bufferIdx++) {
res = native_window_dequeue_buffer_and_wait(mConsumerInterface.get(),
&anwBuffers[bufferIdx]);
if (res != OK) {
ALOGE("%s: Unable to dequeue buffer %d for initial registration for "
"stream %d", __FUNCTION__, bufferIdx, mId);
goto cleanUpBuffers;
}
buffers[bufferIdx] = anwBuffers[bufferIdx]->handle;
ALOGV("%s: Buffer %p allocated", __FUNCTION__, (void*)buffers[bufferIdx]);
}
ALOGV("%s: Registering %d buffers with camera HAL", __FUNCTION__, mTotalBuffers);
res = mHal2Device->ops->register_stream_buffers(mHal2Device,
mId,
mTotalBuffers,
buffers);
if (res != OK) {
ALOGE("%s: Unable to register buffers with HAL device for stream %d",
__FUNCTION__, mId);
} else {
mState = ACTIVE;
}
cleanUpBuffers:
ALOGV("%s: Cleaning up %d buffers", __FUNCTION__, bufferIdx);
for (uint32_t i = 0; i < bufferIdx; i++) {
res = mConsumerInterface->cancelBuffer(mConsumerInterface.get(),
anwBuffers[i], -1);
if (res != OK) {
ALOGE("%s: Unable to cancel buffer %d after registration",
__FUNCTION__, i);
}
}
delete[] anwBuffers;
delete[] buffers;
return res;
}
status_t Camera2Device::StreamAdapter::release() {
ATRACE_CALL();
status_t res;
ALOGV("%s: Releasing stream %d (%d x %d, format %d)", __FUNCTION__, mId,
mWidth, mHeight, mFormat);
if (mState >= ALLOCATED) {
res = mHal2Device->ops->release_stream(mHal2Device, mId);
if (res != OK) {
ALOGE("%s: Unable to release stream %d",
__FUNCTION__, mId);
return res;
}
}
if (mState >= CONNECTED) {
res = native_window_api_disconnect(mConsumerInterface.get(),
NATIVE_WINDOW_API_CAMERA);
/* this is not an error. if client calling process dies,
the window will also die and all calls to it will return
DEAD_OBJECT, thus it's already "disconnected" */
if (res == DEAD_OBJECT) {
ALOGW("%s: While disconnecting stream %d from native window, the"
" native window died from under us", __FUNCTION__, mId);
}
else if (res != OK) {
ALOGE("%s: Unable to disconnect stream %d from native window (error %d %s)",
__FUNCTION__, mId, res, strerror(-res));
return res;
}
}
mId = -1;
mState = RELEASED;
return OK;
}
status_t Camera2Device::StreamAdapter::setTransform(int transform) {
ATRACE_CALL();
status_t res;
if (mState < CONNECTED) {
ALOGE("%s: Cannot set transform on unconnected stream", __FUNCTION__);
return INVALID_OPERATION;
}
res = native_window_set_buffers_transform(mConsumerInterface.get(),
transform);
if (res != OK) {
ALOGE("%s: Unable to configure stream transform to %x: %s (%d)",
__FUNCTION__, transform, strerror(-res), res);
}
return res;
}
status_t Camera2Device::StreamAdapter::dump(int fd,
const Vector<String16>& /*args*/) {
ATRACE_CALL();
String8 result = String8::format(" Stream %d: %d x %d, format 0x%x\n",
mId, mWidth, mHeight, mFormat);
result.appendFormat(" size %d, usage 0x%x, requested format 0x%x\n",
mSize, mUsage, mFormatRequested);
result.appendFormat(" total buffers: %d, dequeued buffers: %d\n",
mTotalBuffers, mActiveBuffers);
result.appendFormat(" frame count: %d, last timestamp %lld\n",
mFrameCount, mLastTimestamp);
write(fd, result.string(), result.size());
return OK;
}
const camera2_stream_ops *Camera2Device::StreamAdapter::getStreamOps() {
return static_cast<camera2_stream_ops *>(this);
}
ANativeWindow* Camera2Device::StreamAdapter::toANW(
const camera2_stream_ops_t *w) {
return static_cast<const StreamAdapter*>(w)->mConsumerInterface.get();
}
int Camera2Device::StreamAdapter::dequeue_buffer(const camera2_stream_ops_t *w,
buffer_handle_t** buffer) {
ATRACE_CALL();
int res;
StreamAdapter* stream =
const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w));
if (stream->mState != ACTIVE) {
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
return INVALID_OPERATION;
}
ANativeWindow *a = toANW(w);
ANativeWindowBuffer* anb;
res = native_window_dequeue_buffer_and_wait(a, &anb);
if (res != OK) {
ALOGE("Stream %d dequeue: Error from native_window: %s (%d)", stream->mId,
strerror(-res), res);
return res;
}
*buffer = &(anb->handle);
stream->mActiveBuffers++;
ALOGVV("Stream %d dequeue: Buffer %p dequeued", stream->mId, (void*)(**buffer));
return res;
}
int Camera2Device::StreamAdapter::enqueue_buffer(const camera2_stream_ops_t* w,
int64_t timestamp,
buffer_handle_t* buffer) {
ATRACE_CALL();
StreamAdapter *stream =
const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w));
stream->mFrameCount++;
ALOGVV("Stream %d enqueue: Frame %d (%p) captured at %lld ns",
stream->mId, stream->mFrameCount, (void*)(*buffer), timestamp);
int state = stream->mState;
if (state != ACTIVE) {
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
return INVALID_OPERATION;
}
ANativeWindow *a = toANW(w);
status_t err;
err = native_window_set_buffers_timestamp(a, timestamp);
if (err != OK) {
ALOGE("%s: Error setting timestamp on native window: %s (%d)",
__FUNCTION__, strerror(-err), err);
return err;
}
err = a->queueBuffer(a,
container_of(buffer, ANativeWindowBuffer, handle), -1);
if (err != OK) {
ALOGE("%s: Error queueing buffer to native window: %s (%d)",
__FUNCTION__, strerror(-err), err);
return err;
}
stream->mActiveBuffers--;
stream->mLastTimestamp = timestamp;
return OK;
}
int Camera2Device::StreamAdapter::cancel_buffer(const camera2_stream_ops_t* w,
buffer_handle_t* buffer) {
ATRACE_CALL();
StreamAdapter *stream =
const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w));
ALOGVV("Stream %d cancel: Buffer %p",
stream->mId, (void*)(*buffer));
if (stream->mState != ACTIVE) {
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
return INVALID_OPERATION;
}
ANativeWindow *a = toANW(w);
int err = a->cancelBuffer(a,
container_of(buffer, ANativeWindowBuffer, handle), -1);
if (err != OK) {
ALOGE("%s: Error canceling buffer to native window: %s (%d)",
__FUNCTION__, strerror(-err), err);
return err;
}
stream->mActiveBuffers--;
return OK;
}
int Camera2Device::StreamAdapter::set_crop(const camera2_stream_ops_t* w,
int left, int top, int right, int bottom) {
ATRACE_CALL();
int state = static_cast<const StreamAdapter*>(w)->mState;
if (state != ACTIVE) {
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
return INVALID_OPERATION;
}
ANativeWindow *a = toANW(w);
android_native_rect_t crop = { left, top, right, bottom };
return native_window_set_crop(a, &crop);
}
/**
* Camera2Device::ReprocessStreamAdapter
*/
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char*)(ptr) - offsetof(type, member))
#endif
Camera2Device::ReprocessStreamAdapter::ReprocessStreamAdapter(camera2_device_t *d):
mState(RELEASED),
mHal2Device(d),
mId(-1),
mWidth(0), mHeight(0), mFormat(0),
mActiveBuffers(0),
mFrameCount(0)
{
ATRACE_CALL();
camera2_stream_in_ops::acquire_buffer = acquire_buffer;
camera2_stream_in_ops::release_buffer = release_buffer;
}
Camera2Device::ReprocessStreamAdapter::~ReprocessStreamAdapter() {
ATRACE_CALL();
if (mState != RELEASED) {
release();
}
}
status_t Camera2Device::ReprocessStreamAdapter::connectToDevice(
const sp<StreamAdapter> &outputStream) {
ATRACE_CALL();
status_t res;
ALOGV("%s: E", __FUNCTION__);
if (mState != RELEASED) return INVALID_OPERATION;
if (outputStream == NULL) {
ALOGE("%s: Null base stream passed to reprocess stream adapter",
__FUNCTION__);
return BAD_VALUE;
}
mBaseStream = outputStream;
mWidth = outputStream->getWidth();
mHeight = outputStream->getHeight();
mFormat = outputStream->getFormat();
ALOGV("%s: New reprocess stream parameters %d x %d, format 0x%x",
__FUNCTION__, mWidth, mHeight, mFormat);
// Allocate device-side stream interface
uint32_t id;
res = mHal2Device->ops->allocate_reprocess_stream_from_stream(mHal2Device,
outputStream->getId(), getStreamOps(),
&id);
if (res != OK) {
ALOGE("%s: Device reprocess stream allocation failed: %s (%d)",
__FUNCTION__, strerror(-res), res);
return res;
}
ALOGV("%s: Allocated reprocess stream id %d based on stream %d",
__FUNCTION__, id, outputStream->getId());
mId = id;
mState = ACTIVE;
return OK;
}
status_t Camera2Device::ReprocessStreamAdapter::release() {
ATRACE_CALL();
status_t res;
ALOGV("%s: Releasing stream %d", __FUNCTION__, mId);
if (mState >= ACTIVE) {
res = mHal2Device->ops->release_reprocess_stream(mHal2Device, mId);
if (res != OK) {
ALOGE("%s: Unable to release stream %d",
__FUNCTION__, mId);
return res;
}
}
List<QueueEntry>::iterator s;
for (s = mQueue.begin(); s != mQueue.end(); s++) {
sp<BufferReleasedListener> listener = s->releaseListener.promote();
if (listener != 0) listener->onBufferReleased(s->handle);
}
for (s = mInFlightQueue.begin(); s != mInFlightQueue.end(); s++) {
sp<BufferReleasedListener> listener = s->releaseListener.promote();
if (listener != 0) listener->onBufferReleased(s->handle);
}
mQueue.clear();
mInFlightQueue.clear();
mState = RELEASED;
return OK;
}
status_t Camera2Device::ReprocessStreamAdapter::pushIntoStream(
buffer_handle_t *handle, const wp<BufferReleasedListener> &releaseListener) {
ATRACE_CALL();
// TODO: Some error checking here would be nice
ALOGV("%s: Pushing buffer %p to stream", __FUNCTION__, (void*)(*handle));
QueueEntry entry;
entry.handle = handle;
entry.releaseListener = releaseListener;
mQueue.push_back(entry);
return OK;
}
status_t Camera2Device::ReprocessStreamAdapter::dump(int fd,
const Vector<String16>& /*args*/) {
ATRACE_CALL();
String8 result =
String8::format(" Reprocess stream %d: %d x %d, fmt 0x%x\n",
mId, mWidth, mHeight, mFormat);
result.appendFormat(" acquired buffers: %d\n",
mActiveBuffers);
result.appendFormat(" frame count: %d\n",
mFrameCount);
write(fd, result.string(), result.size());
return OK;
}
const camera2_stream_in_ops *Camera2Device::ReprocessStreamAdapter::getStreamOps() {
return static_cast<camera2_stream_in_ops *>(this);
}
int Camera2Device::ReprocessStreamAdapter::acquire_buffer(
const camera2_stream_in_ops_t *w,
buffer_handle_t** buffer) {
ATRACE_CALL();
ReprocessStreamAdapter* stream =
const_cast<ReprocessStreamAdapter*>(
static_cast<const ReprocessStreamAdapter*>(w));
if (stream->mState != ACTIVE) {
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
return INVALID_OPERATION;
}
if (stream->mQueue.empty()) {
*buffer = NULL;
return OK;
}
QueueEntry &entry = *(stream->mQueue.begin());
*buffer = entry.handle;
stream->mInFlightQueue.push_back(entry);
stream->mQueue.erase(stream->mQueue.begin());
stream->mActiveBuffers++;
ALOGV("Stream %d acquire: Buffer %p acquired", stream->mId,
(void*)(**buffer));
return OK;
}
int Camera2Device::ReprocessStreamAdapter::release_buffer(
const camera2_stream_in_ops_t* w,
buffer_handle_t* buffer) {
ATRACE_CALL();
ReprocessStreamAdapter *stream =
const_cast<ReprocessStreamAdapter*>(
static_cast<const ReprocessStreamAdapter*>(w) );
stream->mFrameCount++;
ALOGV("Reprocess stream %d release: Frame %d (%p)",
stream->mId, stream->mFrameCount, (void*)*buffer);
int state = stream->mState;
if (state != ACTIVE) {
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
return INVALID_OPERATION;
}
stream->mActiveBuffers--;
List<QueueEntry>::iterator s;
for (s = stream->mInFlightQueue.begin(); s != stream->mInFlightQueue.end(); s++) {
if ( s->handle == buffer ) break;
}
if (s == stream->mInFlightQueue.end()) {
ALOGE("%s: Can't find buffer %p in in-flight list!", __FUNCTION__,
buffer);
return INVALID_OPERATION;
}
sp<BufferReleasedListener> listener = s->releaseListener.promote();
if (listener != 0) {
listener->onBufferReleased(s->handle);
} else {
ALOGE("%s: Can't free buffer - missing listener", __FUNCTION__);
}
stream->mInFlightQueue.erase(s);
return OK;
}
}; // namespace android