summaryrefslogtreecommitdiffstats
path: root/services/camera/libcameraservice/Camera3Device.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/camera/libcameraservice/Camera3Device.cpp')
-rw-r--r--services/camera/libcameraservice/Camera3Device.cpp820
1 files changed, 782 insertions, 38 deletions
diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp
index 04a6e6a..e6fb33e 100644
--- a/services/camera/libcameraservice/Camera3Device.cpp
+++ b/services/camera/libcameraservice/Camera3Device.cpp
@@ -29,13 +29,16 @@
#include <utils/Trace.h>
#include <utils/Timers.h>
#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<String16> &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<CaptureRequest> 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<CaptureRequest> 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::CaptureRequest> 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<CaptureRequest> 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<ANativeWindow> 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<Camera3OutputStream> 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<Camera3Stream> 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<Camera3Device> parent) :
- Thread(false),
- mParent(parent) {
+/**
+ * Camera3Device private methods
+ */
+
+sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(
+ const CameraMetadata &request) {
+ ATRACE_CALL();
+ status_t res;
+
+ sp<CaptureRequest> 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<Camera3OutputStream> 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<camera3_stream_t*> 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<Camera3Device> 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<CaptureRequest> 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<CaptureRequest> 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<camera3_stream_buffer_t> 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<CaptureRequest> &nextRequest,
+ Vector<camera3_stream_buffer_t> &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::CaptureRequest>
+ Camera3Device::RequestThread::waitForNextRequest() {
+ status_t res;
+ sp<CaptureRequest> 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
*/