From 3b53bc9b41c262d22f094406e3751bc5a41ef2ef Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Wed, 27 Feb 2013 18:02:26 -0800 Subject: [PATCH] Camera: Add input side to camera3 HAL device - Stream creation/deletion - Request submission - Request thread loop - Add get() to CameraMetadata to allow HAL submission while retaining ownership. Change-Id: I271f72bcbe9557eded43cbcbfe789109857f8144 --- camera/CameraMetadata.cpp | 117 ++- include/camera/CameraMetadata.h | 20 +- services/camera/libcameraservice/Camera3Device.cpp | 820 ++++++++++++++++++++- services/camera/libcameraservice/Camera3Device.h | 163 +++- 4 files changed, 1058 insertions(+), 62 deletions(-) diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp index fdd06105f4..6c3e233a79 100644 --- a/camera/CameraMetadata.cpp +++ b/camera/CameraMetadata.cpp @@ -23,19 +23,22 @@ namespace android { CameraMetadata::CameraMetadata() : - mBuffer(NULL) { + mBuffer(NULL), mLocked(false) { } -CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) +CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) : + mLocked(false) { mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity); } -CameraMetadata::CameraMetadata(const CameraMetadata &other) { +CameraMetadata::CameraMetadata(const CameraMetadata &other) : + mLocked(false) { mBuffer = clone_camera_metadata(other.mBuffer); } -CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : mBuffer(NULL) { +CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : + mBuffer(NULL), mLocked(false) { acquire(buffer); } @@ -44,6 +47,11 @@ CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) { } CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) { + if (mLocked) { + ALOGE("%s: Assignment to a locked CameraMetadata!", __FUNCTION__); + return *this; + } + if (CC_LIKELY(buffer != mBuffer)) { camera_metadata_t *newBuffer = clone_camera_metadata(buffer); clear(); @@ -53,16 +61,44 @@ CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) { } CameraMetadata::~CameraMetadata() { + mLocked = false; clear(); } +const camera_metadata_t* CameraMetadata::getAndLock() { + mLocked = true; + return mBuffer; +} + +status_t CameraMetadata::unlock(const camera_metadata_t *buffer) { + if (!mLocked) { + ALOGE("%s: Can't unlock a non-locked CameraMetadata!", __FUNCTION__); + return INVALID_OPERATION; + } + if (buffer != mBuffer) { + ALOGE("%s: Can't unlock CameraMetadata with wrong pointer!", + __FUNCTION__); + return BAD_VALUE; + } + mLocked = false; + return OK; +} + camera_metadata_t* CameraMetadata::release() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return NULL; + } camera_metadata_t *released = mBuffer; mBuffer = NULL; return released; } void CameraMetadata::clear() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } if (mBuffer) { free_camera_metadata(mBuffer); mBuffer = NULL; @@ -70,15 +106,27 @@ void CameraMetadata::clear() { } void CameraMetadata::acquire(camera_metadata_t *buffer) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } clear(); mBuffer = buffer; } void CameraMetadata::acquire(CameraMetadata &other) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } acquire(other.release()); } status_t CameraMetadata::append(const CameraMetadata &other) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } return append_camera_metadata(mBuffer, other.mBuffer); } @@ -92,6 +140,10 @@ bool CameraMetadata::isEmpty() const { } status_t CameraMetadata::sort() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } return sort_camera_metadata(mBuffer); } @@ -115,69 +167,101 @@ status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) { status_t CameraMetadata::update(uint32_t tag, const int32_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_INT32)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const uint8_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_BYTE)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const float *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_FLOAT)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const int64_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_INT64)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const double *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const camera_metadata_rational_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const String8 &string) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_BYTE)) != OK) { return res; } - return update(tag, (const void*)string.string(), string.size()); + return updateImpl(tag, (const void*)string.string(), string.size()); } -status_t CameraMetadata::update(uint32_t tag, const void *data, +status_t CameraMetadata::updateImpl(uint32_t tag, const void *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } int type = get_camera_metadata_tag_type(tag); if (type == -1) { ALOGE("%s: Tag %d not found", __FUNCTION__, tag); @@ -216,6 +300,11 @@ bool CameraMetadata::exists(uint32_t tag) const { camera_metadata_entry_t CameraMetadata::find(uint32_t tag) { status_t res; camera_metadata_entry entry; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + entry.count = 0; + return entry; + } res = find_camera_metadata_entry(mBuffer, tag, &entry); if (CC_UNLIKELY( res != OK )) { entry.count = 0; @@ -238,6 +327,10 @@ camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const { status_t CameraMetadata::erase(uint32_t tag) { camera_metadata_entry_t entry; status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } res = find_camera_metadata_entry(mBuffer, tag, &entry); if (res == NAME_NOT_FOUND) { return OK; diff --git a/include/camera/CameraMetadata.h b/include/camera/CameraMetadata.h index 4289126726..8eeb2e771a 100644 --- a/include/camera/CameraMetadata.h +++ b/include/camera/CameraMetadata.h @@ -49,6 +49,23 @@ class CameraMetadata { CameraMetadata &operator=(const camera_metadata_t *buffer); /** + * Get reference to the underlying metadata buffer. Ownership remains with + * the CameraMetadata object, but non-const CameraMetadata methods will not + * work until unlock() is called. Note that the lock has nothing to do with + * thread-safety, it simply prevents the camera_metadata_t pointer returned + * here from being accidentally invalidated by CameraMetadata operations. + */ + const camera_metadata_t* getAndLock(); + + /** + * Unlock the CameraMetadata for use again. After this unlock, the pointer + * given from getAndLock() may no longer be used. The pointer passed out + * from getAndLock must be provided to guarantee that the right object is + * being unlocked. + */ + status_t unlock(const camera_metadata_t *buffer); + + /** * Release a raw metadata buffer to the caller. After this call, * CameraMetadata no longer references the buffer, and the caller takes * responsibility for freeing the raw metadata buffer (using @@ -154,6 +171,7 @@ class CameraMetadata { private: camera_metadata_t *mBuffer; + bool mLocked; /** * Check if tag has a given type @@ -163,7 +181,7 @@ class CameraMetadata { /** * Base update entry method */ - status_t update(uint32_t tag, const void *data, size_t data_count); + status_t updateImpl(uint32_t tag, const void *data, size_t data_count); /** * Resize metadata buffer if needed by reallocating it and copying it over. diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp index 04a6e6afa6..e6fb33e523 100644 --- a/services/camera/libcameraservice/Camera3Device.cpp +++ b/services/camera/libcameraservice/Camera3Device.cpp @@ -29,13 +29,16 @@ #include #include #include "Camera3Device.h" +#include "camera3/Camera3OutputStream.h" -namespace android { +using namespace android::camera3; +namespace android { Camera3Device::Camera3Device(int id): mId(id), - mHal3Device(NULL) + mHal3Device(NULL), + mStatus(STATUS_UNINITIALIZED) { ATRACE_CALL(); camera3_callback_ops::notify = &sNotify; @@ -54,11 +57,17 @@ int Camera3Device::getId() const { return mId; } +/** + * CameraDeviceBase interface + */ + status_t Camera3Device::initialize(camera_module_t *module) { ATRACE_CALL(); + Mutex::Autolock l(mLock); + ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId); - if (mHal3Device != NULL) { + if (mStatus != STATUS_UNINITIALIZED) { ALOGE("%s: Already initialized!", __FUNCTION__); return INVALID_OPERATION; } @@ -76,6 +85,7 @@ status_t Camera3Device::initialize(camera_module_t *module) if (res != OK) { ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__, mId, strerror(-res), res); + mStatus = STATUS_ERROR; return res; } @@ -87,6 +97,7 @@ status_t Camera3Device::initialize(camera_module_t *module) __FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_3_0, device->common.version); device->common.close(&device->common); + mStatus = STATUS_ERROR; return BAD_VALUE; } @@ -99,6 +110,7 @@ status_t Camera3Device::initialize(camera_module_t *module) " and device version (%x).", __FUNCTION__, device->common.version, info.device_version); device->common.close(&device->common); + mStatus = STATUS_ERROR; return BAD_VALUE; } @@ -109,6 +121,7 @@ status_t Camera3Device::initialize(camera_module_t *module) ALOGE("%s: Camera %d: Unable to initialize HAL device: %s (%d)", __FUNCTION__, mId, strerror(-res), res); device->common.close(&device->common); + mStatus = STATUS_ERROR; return BAD_VALUE; } @@ -124,18 +137,21 @@ status_t Camera3Device::initialize(camera_module_t *module) ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)", __FUNCTION__, mId, strerror(-res), res); device->common.close(&device->common); + mStatus = STATUS_ERROR; return res; } } /** Start up request queue thread */ - requestThread = new RequestThread(this); - res = requestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string()); + mRequestThread = new RequestThread(this, device); + res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string()); if (res != OK) { ALOGE("%s: Camera %d: Unable to start request queue thread: %s (%d)", __FUNCTION__, mId, strerror(-res), res); device->common.close(&device->common); + mRequestThread.clear(); + mStatus = STATUS_ERROR; return res; } @@ -143,54 +159,205 @@ status_t Camera3Device::initialize(camera_module_t *module) mDeviceInfo = info.static_camera_characteristics; mHal3Device = device; + mStatus = STATUS_IDLE; + mNextStreamId = 0; return OK; } status_t Camera3Device::disconnect() { ATRACE_CALL(); + Mutex::Autolock l(mLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + ALOGV("%s: E", __FUNCTION__); + + status_t res; + if (mStatus == STATUS_UNINITIALIZED) return OK; + + if (mStatus == STATUS_ACTIVE || + (mStatus == STATUS_ERROR && mRequestThread != NULL)) { + res = mRequestThread->clearRepeatingRequests(); + if (res != OK) { + ALOGE("%s: Can't stop streaming", __FUNCTION__); + return res; + } + res = waitUntilDrainedLocked(); + if (res != OK) { + ALOGE("%s: Timeout waiting for HAL to drain", __FUNCTION__); + return res; + } + } + assert(mStatus == STATUS_IDLE || mStatus == STATUS_ERROR); + + if (mRequestThread != NULL) { + mRequestThread->requestExit(); + } + + mOutputStreams.clear(); + mInputStream.clear(); + + if (mRequestThread != NULL) { + mRequestThread->join(); + mRequestThread.clear(); + } + + if (mHal3Device != NULL) { + mHal3Device->common.close(&mHal3Device->common); + mHal3Device = NULL; + } + + mStatus = STATUS_UNINITIALIZED; + + ALOGV("%s: X", __FUNCTION__); + return OK; } status_t Camera3Device::dump(int fd, const Vector &args) { ATRACE_CALL(); (void)args; + String8 lines; + + const char *status = + mStatus == STATUS_ERROR ? "ERROR" : + mStatus == STATUS_UNINITIALIZED ? "UNINITIALIZED" : + mStatus == STATUS_IDLE ? "IDLE" : + mStatus == STATUS_ACTIVE ? "ACTIVE" : + "Unknown"; + lines.appendFormat(" Device status: %s\n", status); + lines.appendFormat(" Stream configuration:\n"); + + if (mInputStream != NULL) { + write(fd, lines.string(), lines.size()); + mInputStream->dump(fd, args); + } else { + lines.appendFormat(" No input stream.\n"); + write(fd, lines.string(), lines.size()); + } + for (size_t i = 0; i < mOutputStreams.size(); i++) { + mOutputStreams[i]->dump(fd,args); + } - mHal3Device->ops->dump(mHal3Device, fd); + if (mHal3Device != NULL) { + lines = String8(" HAL device dump:\n"); + write(fd, lines.string(), lines.size()); + mHal3Device->ops->dump(mHal3Device, fd); + } return OK; } const CameraMetadata& Camera3Device::info() const { ALOGVV("%s: E", __FUNCTION__); - + if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED || + mStatus == STATUS_ERROR)) { + ALOGE("%s: Access to static info %s!", __FUNCTION__, + mStatus == STATUS_ERROR ? + "when in error state" : "before init"); + } return mDeviceInfo; } status_t Camera3Device::capture(CameraMetadata &request) { ATRACE_CALL(); - (void)request; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + sp newRequest = setUpRequestLocked(request); + if (newRequest == NULL) { + ALOGE("%s: Can't create capture request", __FUNCTION__); + return BAD_VALUE; + } + + return mRequestThread->queueRequest(newRequest); } status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) { ATRACE_CALL(); - (void)request; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + sp newRepeatingRequest = setUpRequestLocked(request); + if (newRepeatingRequest == NULL) { + ALOGE("%s: Can't create repeating request", __FUNCTION__); + return BAD_VALUE; + } + + RequestList newRepeatingRequests; + newRepeatingRequests.push_back(newRepeatingRequest); + + return mRequestThread->setRepeatingRequests(newRepeatingRequests); +} + + +sp Camera3Device::setUpRequestLocked( + const CameraMetadata &request) { + status_t res; + + if (mStatus == STATUS_IDLE) { + res = configureStreamsLocked(); + if (res != OK) { + ALOGE("%s: Can't set up streams: %s (%d)", + __FUNCTION__, strerror(-res), res); + return NULL; + } + } + + sp newRequest = createCaptureRequest(request); + return newRequest; } status_t Camera3Device::clearStreamingRequest() { ATRACE_CALL(); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + return mRequestThread->clearRepeatingRequests(); } status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) { @@ -204,11 +371,70 @@ status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time status_t Camera3Device::createStream(sp consumer, uint32_t width, uint32_t height, int format, size_t size, int *id) { ATRACE_CALL(); - (void)consumer; (void)width; (void)height; (void)format; - (void)size; (void)id; + Mutex::Autolock l(mLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + status_t res; + bool wasActive = false; + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + // OK + break; + case STATUS_ACTIVE: + ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__); + mRequestThread->setPaused(true); + res = waitUntilDrainedLocked(); + if (res != OK) { + ALOGE("%s: Can't pause captures to reconfigure streams!", + __FUNCTION__); + mStatus = STATUS_ERROR; + return res; + } + wasActive = true; + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } + assert(mStatus == STATUS_IDLE); + + sp newStream; + if (format == HAL_PIXEL_FORMAT_BLOB) { + newStream = new Camera3OutputStream(mNextStreamId, consumer, + width, height, size, format); + } else { + newStream = new Camera3OutputStream(mNextStreamId, consumer, + width, height, format); + } + + res = mOutputStreams.add(mNextStreamId, newStream); + if (res < 0) { + ALOGE("%s: Can't add new stream to set: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + *id = mNextStreamId++; + + // Continue captures if active at start + if (wasActive) { + ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__); + res = configureStreamsLocked(); + if (res != OK) { + ALOGE("%s: Can't reconfigure device for new stream %d: %s (%d)", + __FUNCTION__, mNextStreamId, strerror(-res), res); + return res; + } + mRequestThread->setPaused(false); + } + + return OK; } status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) { @@ -223,27 +449,104 @@ status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) { status_t Camera3Device::getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format) { ATRACE_CALL(); - (void)id; (void)width; (void)height; (void)format; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized!", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Stream %d is unknown", __FUNCTION__, id); + return idx; + } + + if (width) *width = mOutputStreams[idx]->getWidth(); + if (height) *height = mOutputStreams[idx]->getHeight(); + if (format) *format = mOutputStreams[idx]->getFormat(); + + return OK; } status_t Camera3Device::setStreamTransform(int id, int transform) { ATRACE_CALL(); - (void)id; (void)transform; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Stream %d does not exist", + __FUNCTION__, id); + return BAD_VALUE; + } + + return mOutputStreams.editValueAt(idx)->setTransform(transform); } status_t Camera3Device::deleteStream(int id) { ATRACE_CALL(); - (void)id; + Mutex::Autolock l(mLock); + status_t res; - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + // CameraDevice semantics require device to already be idle before + // deleteStream is called, unlike for createStream. + if (mStatus != STATUS_IDLE) { + ALOGE("%s: Device not idle", __FUNCTION__); + return INVALID_OPERATION; + } + + sp deletedStream; + if (mInputStream != NULL && id == mInputStream->getId()) { + deletedStream = mInputStream; + mInputStream.clear(); + } else { + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Stream %d does not exist", + __FUNCTION__, id); + return BAD_VALUE; + } + deletedStream = mOutputStreams.editValueAt(idx); + mOutputStreams.removeItem(id); + } + + // Free up the stream endpoint so that it can be used by some other stream + res = deletedStream->disconnect(); + if (res != OK) { + ALOGE("%s: Can't disconnect deleted stream", __FUNCTION__); + // fall through since we want to still list the stream as deleted. + } + mDeletedStreams.add(deletedStream); + + return res; } status_t Camera3Device::deleteReprocessStream(int id) { @@ -259,6 +562,23 @@ status_t Camera3Device::createDefaultRequest(int templateId, CameraMetadata *request) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device is not initialized!", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } const camera_metadata_t *rawRequest; rawRequest = mHal3Device->ops->construct_default_request_settings( @@ -271,9 +591,63 @@ status_t Camera3Device::createDefaultRequest(int templateId, status_t Camera3Device::waitUntilDrained() { ATRACE_CALL(); + Mutex::Autolock l(mLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + return waitUntilDrainedLocked(); +} + +status_t Camera3Device::waitUntilDrainedLocked() { + ATRACE_CALL(); + status_t res; + + switch (mStatus) { + case STATUS_UNINITIALIZED: + case STATUS_IDLE: + ALOGV("%s: Already idle", __FUNCTION__); + return OK; + case STATUS_ERROR: + case STATUS_ACTIVE: + // Need to shut down + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } + + if (mRequestThread != NULL) { + res = mRequestThread->waitUntilPaused(kShutdownTimeout); + if (res != OK) { + ALOGE("%s: Can't stop request thread in %f seconds!", + __FUNCTION__, kShutdownTimeout/1e9); + mStatus = STATUS_ERROR; + return res; + } + } + if (mInputStream != NULL) { + res = mInputStream->waitUntilIdle(kShutdownTimeout); + if (res != OK) { + ALOGE("%s: Can't idle input stream %d in %f seconds!", + __FUNCTION__, mInputStream->getId(), kShutdownTimeout/1e9); + mStatus = STATUS_ERROR; + return res; + } + } + for (size_t i = 0; i < mOutputStreams.size(); i++) { + res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout); + if (res != OK) { + ALOGE("%s: Can't idle output stream %d in %f seconds!", + __FUNCTION__, mOutputStreams.keyAt(i), + kShutdownTimeout/1e9); + mStatus = STATUS_ERROR; + return res; + } + } + + if (mStatus != STATUS_ERROR) { + mStatus = STATUS_IDLE; + } + + return OK; } status_t Camera3Device::setNotifyCallback(NotificationListener *listener) { @@ -335,17 +709,149 @@ status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId, return INVALID_OPERATION; } -Camera3Device::RequestThread::RequestThread(wp parent) : - Thread(false), - mParent(parent) { +/** + * Camera3Device private methods + */ + +sp Camera3Device::createCaptureRequest( + const CameraMetadata &request) { + ATRACE_CALL(); + status_t res; + + sp newRequest = new CaptureRequest; + newRequest->mSettings = request; + + camera_metadata_entry_t inputStreams = + newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS); + if (inputStreams.count > 0) { + if (mInputStream == NULL || + mInputStream->getId() != inputStreams.data.u8[0]) { + ALOGE("%s: Request references unknown input stream %d", + __FUNCTION__, inputStreams.data.u8[0]); + return NULL; + } + // Lazy completion of stream configuration (allocation/registration) + // on first use + if (mInputStream->isConfiguring()) { + res = mInputStream->finishConfiguration(mHal3Device); + if (res != OK) { + ALOGE("%s: Unable to finish configuring input stream %d:" + " %s (%d)", + __FUNCTION__, mInputStream->getId(), + strerror(-res), res); + return NULL; + } + } + + newRequest->mInputStream = mInputStream; + newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS); + } + + camera_metadata_entry_t streams = + newRequest->mSettings.find(ANDROID_REQUEST_OUTPUT_STREAMS); + if (streams.count == 0) { + ALOGE("%s: Zero output streams specified!", __FUNCTION__); + return NULL; + } + + for (size_t i = 0; i < streams.count; i++) { + int idx = mOutputStreams.indexOfKey(streams.data.u8[i]); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Request references unknown stream %d", + __FUNCTION__, streams.data.u8[i]); + return NULL; + } + sp stream = mOutputStreams.editValueAt(idx); + + // Lazy completion of stream configuration (allocation/registration) + // on first use + if (stream->isConfiguring()) { + res = stream->finishConfiguration(mHal3Device); + if (res != OK) { + ALOGE("%s: Unable to finish configuring stream %d: %s (%d)", + __FUNCTION__, stream->getId(), strerror(-res), res); + return NULL; + } + } + + newRequest->mOutputStreams.push(stream); + } + newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS); + + return newRequest; } -bool Camera3Device::RequestThread::threadLoop() { - ALOGE("%s: Unimplemented", __FUNCTION__); +status_t Camera3Device::configureStreamsLocked() { + ATRACE_CALL(); + status_t res; - return false; + if (mStatus != STATUS_IDLE) { + ALOGE("%s: Not idle", __FUNCTION__); + return INVALID_OPERATION; + } + + // Start configuring the streams + + camera3_stream_configuration config; + + config.num_streams = (mInputStream != NULL) + mOutputStreams.size(); + + Vector streams; + streams.setCapacity(config.num_streams); + + if (mInputStream != NULL) { + camera3_stream_t *inputStream; + inputStream = mInputStream->startConfiguration(); + if (inputStream == NULL) { + ALOGE("%s: Can't start input stream configuration", + __FUNCTION__); + // TODO: Make sure the error flow here is correct + return INVALID_OPERATION; + } + streams.add(inputStream); + } + + for (size_t i = 0; i < mOutputStreams.size(); i++) { + camera3_stream_t *outputStream; + outputStream = mOutputStreams.editValueAt(i)->startConfiguration(); + if (outputStream == NULL) { + ALOGE("%s: Can't start output stream configuration", + __FUNCTION__); + // TODO: Make sure the error flow here is correct + return INVALID_OPERATION; + } + streams.add(outputStream); + } + + config.streams = streams.editArray(); + + // Do the HAL configuration; will potentially touch stream + // max_buffers, usage, priv fields. + + res = mHal3Device->ops->configure_streams(mHal3Device, &config); + + if (res != OK) { + ALOGE("%s: Unable to configure streams with HAL: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + // Request thread needs to know to avoid using repeat-last-settings protocol + // across configure_streams() calls + mRequestThread->configurationComplete(); + + // Finish configuring the streams lazily on first reference + + mStatus = STATUS_ACTIVE; + + return OK; } + +/** + * Camera HAL device callback methods + */ + void Camera3Device::processCaptureResult(const camera3_capture_result *result) { (void)result; @@ -359,6 +865,244 @@ void Camera3Device::notify(const camera3_notify_msg *msg) { } /** + * RequestThread inner class methods + */ + +Camera3Device::RequestThread::RequestThread(wp parent, + camera3_device_t *hal3Device) : + Thread(false), + mParent(parent), + mHal3Device(hal3Device), + mReconfigured(false), + mDoPause(false), + mPaused(true), + mFrameNumber(0) { +} + +void Camera3Device::RequestThread::configurationComplete() { + Mutex::Autolock l(mRequestLock); + mReconfigured = true; +} + +status_t Camera3Device::RequestThread::queueRequest( + sp request) { + Mutex::Autolock l(mRequestLock); + mRequestQueue.push_back(request); + + return OK; +} + +status_t Camera3Device::RequestThread::setRepeatingRequests( + const RequestList &requests) { + Mutex::Autolock l(mRequestLock); + mRepeatingRequests.clear(); + mRepeatingRequests.insert(mRepeatingRequests.begin(), + requests.begin(), requests.end()); + return OK; +} + +status_t Camera3Device::RequestThread::clearRepeatingRequests() { + Mutex::Autolock l(mRequestLock); + mRepeatingRequests.clear(); + return OK; +} + +void Camera3Device::RequestThread::setPaused(bool paused) { + Mutex::Autolock l(mPauseLock); + mDoPause = paused; + mDoPauseSignal.signal(); +} + +status_t Camera3Device::RequestThread::waitUntilPaused(nsecs_t timeout) { + status_t res; + Mutex::Autolock l(mPauseLock); + while (!mPaused) { + res = mPausedSignal.waitRelative(mPauseLock, timeout); + if (res == TIMED_OUT) { + return res; + } + } + return OK; +} + +bool Camera3Device::RequestThread::threadLoop() { + + status_t res; + + // Handle paused state. + if (waitIfPaused()) { + return true; + } + + // Get work to do + + sp nextRequest = waitForNextRequest(); + if (nextRequest == NULL) { + return true; + } + + // Create request to HAL + + camera3_capture_request_t request = camera3_capture_request_t(); + + if (mPrevRequest != nextRequest) { + request.settings = nextRequest->mSettings.getAndLock(); + mPrevRequest = nextRequest; + } // else leave request.settings NULL to indicate 'reuse latest given' + + camera3_stream_buffer_t inputBuffer; + Vector outputBuffers; + + // Fill in buffers + + if (nextRequest->mInputStream != NULL) { + request.input_buffer = &inputBuffer; + res = nextRequest->mInputStream->getBuffer(&inputBuffer); + if (res != OK) { + ALOGE("RequestThread: Can't get input buffer, skipping request:" + " %s (%d)", strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return true; + } + } else { + request.input_buffer = NULL; + } + + outputBuffers.insertAt(camera3_stream_buffer_t(), 0, + nextRequest->mOutputStreams.size()); + request.output_buffers = outputBuffers.array(); + for (size_t i = 0; i < nextRequest->mOutputStreams.size(); i++) { + res = nextRequest->mOutputStreams.editItemAt(i)-> + getBuffer(&outputBuffers.editItemAt(i)); + if (res != OK) { + ALOGE("RequestThread: Can't get output buffer, skipping request:" + "%s (%d)", strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return true; + } + request.num_output_buffers++; + } + + request.frame_number = mFrameNumber++; + + // Submit request and block until ready for next one + + res = mHal3Device->ops->process_capture_request(mHal3Device, &request); + if (res != OK) { + ALOGE("RequestThread: Unable to submit capture request %d to HAL" + " device: %s (%d)", request.frame_number, strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return false; + } + + if (request.settings != NULL) { + nextRequest->mSettings.unlock(request.settings); + } + return true; +} + +void Camera3Device::RequestThread::cleanUpFailedRequest( + camera3_capture_request_t &request, + sp &nextRequest, + Vector &outputBuffers) { + + if (request.settings != NULL) { + nextRequest->mSettings.unlock(request.settings); + } + if (request.input_buffer != NULL) { + request.input_buffer->status = CAMERA3_BUFFER_STATUS_ERROR; + nextRequest->mInputStream->returnBuffer(*(request.input_buffer), 0); + } + for (size_t i = 0; i < request.num_output_buffers; i++) { + outputBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; + nextRequest->mOutputStreams.editItemAt(i)->returnBuffer( + outputBuffers[i], 0); + } + // TODO: Report error upstream +} + +sp + Camera3Device::RequestThread::waitForNextRequest() { + status_t res; + sp nextRequest; + + // Optimized a bit for the simple steady-state case (single repeating + // request), to avoid putting that request in the queue temporarily. + Mutex::Autolock l(mRequestLock); + + while (mRequestQueue.empty()) { + if (!mRepeatingRequests.empty()) { + // Always atomically enqueue all requests in a repeating request + // list. Guarantees a complete in-sequence set of captures to + // application. + const RequestList &requests = mRepeatingRequests; + RequestList::const_iterator firstRequest = + requests.begin(); + nextRequest = *firstRequest; + mRequestQueue.insert(mRequestQueue.end(), + ++firstRequest, + requests.end()); + // No need to wait any longer + break; + } + + res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout); + + if (res == TIMED_OUT) { + // Signal that we're paused by starvation + Mutex::Autolock pl(mPauseLock); + if (mPaused == false) { + mPaused = true; + mPausedSignal.signal(); + } + // Stop waiting for now and let thread management happen + return NULL; + } + } + + if (nextRequest == NULL) { + // Don't have a repeating request already in hand, so queue + // must have an entry now. + RequestList::iterator firstRequest = + mRequestQueue.begin(); + nextRequest = *firstRequest; + mRequestQueue.erase(firstRequest); + } + + // Not paused + Mutex::Autolock pl(mPauseLock); + mPaused = false; + + // Check if we've reconfigured since last time, and reset the preview + // request if so. Can't use 'NULL request == repeat' across configure calls. + if (mReconfigured) { + mPrevRequest.clear(); + mReconfigured = false; + } + + return nextRequest; +} + +bool Camera3Device::RequestThread::waitIfPaused() { + status_t res; + Mutex::Autolock l(mPauseLock); + while (mDoPause) { + // Signal that we're paused by request + if (mPaused == false) { + mPaused = true; + mPausedSignal.signal(); + } + res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout); + if (res == TIMED_OUT) { + return true; + } + } + // We don't set mPaused to false here, because waitForNextRequest needs + // to further manage the paused state in case of starvation. + return false; +} + +/** * Static callback forwarding methods from HAL to instance */ diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h index df7352cf4b..86f4c6a655 100644 --- a/services/camera/libcameraservice/Camera3Device.h +++ b/services/camera/libcameraservice/Camera3Device.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_SERVERS_CAMERA_CAMERA3DEVICE_H -#define ANDROID_SERVERS_CAMERA_CAMERA3DEVICE_H +#ifndef ANDROID_SERVERS_CAMERA3DEVICE_H +#define ANDROID_SERVERS_CAMERA3DEVICE_H #include #include @@ -24,6 +24,8 @@ #include #include "CameraDeviceBase.h" +#include "camera3/Camera3Stream.h" +#include "camera3/Camera3OutputStream.h" #include "hardware/camera3.h" @@ -55,63 +57,202 @@ class Camera3Device : virtual ~Camera3Device(); /** - * CameraDevice interface + * CameraDeviceBase interface */ + virtual int getId() const; + + // Transitions to idle state on success. virtual status_t initialize(camera_module_t *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector &args); virtual const CameraMetadata& info() const; + + // Capture and setStreamingRequest will configure streams if currently in + // idle state virtual status_t capture(CameraMetadata &request); virtual status_t setStreamingRequest(const CameraMetadata &request); virtual status_t clearStreamingRequest(); + virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); + + // Actual stream creation/deletion is delayed until first request is submitted + // If adding streams while actively capturing, will pause device before adding + // stream, reconfiguring device, and unpausing. virtual status_t createStream(sp consumer, uint32_t width, uint32_t height, int format, size_t size, int *id); virtual status_t createReprocessStreamFromStream(int outputId, int *id); + virtual status_t getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format); virtual status_t setStreamTransform(int id, int transform); + virtual status_t deleteStream(int id); virtual status_t deleteReprocessStream(int id); + virtual status_t createDefaultRequest(int templateId, CameraMetadata *request); + + // Transitions to the idle state on success virtual status_t waitUntilDrained(); + virtual status_t setNotifyCallback(NotificationListener *listener); virtual status_t waitForNextFrame(nsecs_t timeout); virtual status_t getNextFrame(CameraMetadata *frame); + virtual status_t triggerAutofocus(uint32_t id); virtual status_t triggerCancelAutofocus(uint32_t id); virtual status_t triggerPrecaptureMetering(uint32_t id); + virtual status_t pushReprocessBuffer(int reprocessStreamId, buffer_handle_t *buffer, wp listener); private: - const int mId; - camera3_device_t *mHal3Device; + static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec + + + Mutex mLock; + + /**** Scope for mLock ****/ + + const int mId; + camera3_device_t *mHal3Device; + + CameraMetadata mDeviceInfo; + vendor_tag_query_ops_t mVendorTagOps; + + enum { + STATUS_ERROR, + STATUS_UNINITIALIZED, + STATUS_IDLE, + STATUS_ACTIVE + } mStatus; + + // Mapping of stream IDs to stream instances + typedef KeyedVector > StreamSet; + + StreamSet mOutputStreams; + sp mInputStream; + int mNextStreamId; + + // Need to hold on to stream references until configure completes. + Vector > mDeletedStreams; + + /**** End scope for mLock ****/ + + class CaptureRequest : public LightRefBase { + public: + CameraMetadata mSettings; + sp mInputStream; + Vector > mOutputStreams; + }; + typedef List > RequestList; + + /** + * Lock-held version of waitUntilDrained. Will transition to IDLE on + * success. + */ + status_t waitUntilDrainedLocked(); + + /** + * Do common work for setting up a streaming or single capture request. + * On success, will transition to ACTIVE if in IDLE. + */ + sp setUpRequestLocked(const CameraMetadata &request); + + /** + * Build a CaptureRequest request from the CameraDeviceBase request + * settings. + */ + sp createCaptureRequest(const CameraMetadata &request); - CameraMetadata mDeviceInfo; - vendor_tag_query_ops_t mVendorTagOps; + /** + * Take the currently-defined set of streams and configure the HAL to use + * them. This is a long-running operation (may be several hundered ms). + */ + status_t configureStreamsLocked(); /** * Thread for managing capture request submission to HAL device. */ - class RequestThread: public Thread { + class RequestThread : public Thread { public: - RequestThread(wp parent); + RequestThread(wp parent, + camera3_device_t *hal3Device); + + /** + * Call after stream (re)-configuration is completed. + */ + void configurationComplete(); + + /** + * Set or clear the list of repeating requests. Does not block + * on either. Use waitUntilPaused to wait until request queue + * has emptied out. + */ + status_t setRepeatingRequests(const RequestList& requests); + status_t clearRepeatingRequests(); + + status_t queueRequest(sp request); + + /** + * Pause/unpause the capture thread. Doesn't block, so use + * waitUntilPaused to wait until the thread is paused. + */ + void setPaused(bool paused); + + /** + * Wait until thread is paused, either due to setPaused(true) + * or due to lack of input requests. Returns TIMED_OUT in case + * the thread does not pause within the timeout. + */ + status_t waitUntilPaused(nsecs_t timeout); protected: virtual bool threadLoop(); private: + static const nsecs_t kRequestTimeout = 50e6; // 50 ms + + // Waits for a request, or returns NULL if times out. + sp waitForNextRequest(); + + // Return buffers, etc, for a request that couldn't be fully + // constructed. The buffers will be returned in the ERROR state + // to mark them as not having valid data. + // All arguments will be modified. + void cleanUpFailedRequest(camera3_capture_request_t &request, + sp &nextRequest, + Vector &outputBuffers); + + // Pause handling + bool waitIfPaused(); + + wp mParent; + camera3_device_t *mHal3Device; + + Mutex mRequestLock; + Condition mRequestSignal; + RequestList mRequestQueue; + RequestList mRepeatingRequests; + + bool mReconfigured; + + // Used by waitIfPaused, waitForNextRequest, and waitUntilPaused + Mutex mPauseLock; + bool mDoPause; + Condition mDoPauseSignal; + bool mPaused; + Condition mPausedSignal; - wp mParent; + sp mPrevRequest; + int32_t mFrameNumber; }; - sp requestThread; + sp mRequestThread; /** * Callback functions from HAL device -- 2.11.0