diff options
author | Eino-Ville Talvala <etalvala@google.com> | 2013-07-25 17:12:35 -0700 |
---|---|---|
committer | Eino-Ville Talvala <etalvala@google.com> | 2013-07-30 10:58:44 -0700 |
commit | 7b82efe7a376c882f8f938e1c41b8311a8cdda4a (patch) | |
tree | d7ed69f0a495bc1a873a285ba11e72a9867c5565 /services/camera/libcameraservice/device3 | |
parent | d054c32443a493513ab63529b0c8b1aca290278c (diff) | |
download | frameworks_av-7b82efe7a376c882f8f938e1c41b8311a8cdda4a.zip frameworks_av-7b82efe7a376c882f8f938e1c41b8311a8cdda4a.tar.gz frameworks_av-7b82efe7a376c882f8f938e1c41b8311a8cdda4a.tar.bz2 |
Camera: Rename new API to camera2, rearrange camera service
- Support API rename from photography to camera2
- Reorganize camera service files
- API support files to api1/, api2/, api_pro/
- HAL device support files into device{1,2,3}/
- Common files into common/
- Camera service remains at top-level
Change-Id: Ie474c12536f543832fba0a2dc936ac4fd39fe6a9
Diffstat (limited to 'services/camera/libcameraservice/device3')
15 files changed, 4919 insertions, 0 deletions
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp new file mode 100644 index 0000000..0a4a24c --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -0,0 +1,1974 @@ +/* + * Copyright (C) 2013 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 "Camera3-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 + +// Convenience macro for transient errors +#define CLOGE(fmt, ...) ALOGE("Camera %d: %s: " fmt, mId, __FUNCTION__, \ + ##__VA_ARGS__) + +// Convenience macros for transitioning to the error state +#define SET_ERR(fmt, ...) setErrorState( \ + "%s: " fmt, __FUNCTION__, \ + ##__VA_ARGS__) +#define SET_ERR_L(fmt, ...) setErrorStateLocked( \ + "%s: " fmt, __FUNCTION__, \ + ##__VA_ARGS__) + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <utils/Timers.h> + +#include "device3/Camera3Device.h" +#include "device3/Camera3OutputStream.h" +#include "device3/Camera3InputStream.h" +#include "device3/Camera3ZslStream.h" + +using namespace android::camera3; + +namespace android { + +Camera3Device::Camera3Device(int id): + mId(id), + mHal3Device(NULL), + mStatus(STATUS_UNINITIALIZED), + mNextResultFrameNumber(0), + mNextShutterFrameNumber(0), + mListener(NULL) +{ + ATRACE_CALL(); + camera3_callback_ops::notify = &sNotify; + camera3_callback_ops::process_capture_result = &sProcessCaptureResult; + ALOGV("%s: Created device for camera %d", __FUNCTION__, id); +} + +Camera3Device::~Camera3Device() +{ + ATRACE_CALL(); + ALOGV("%s: Tearing down for camera id %d", __FUNCTION__, mId); + disconnect(); +} + +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 (mStatus != STATUS_UNINITIALIZED) { + CLOGE("Already initialized!"); + return INVALID_OPERATION; + } + + /** Open HAL device */ + + status_t res; + String8 deviceName = String8::format("%d", mId); + + camera3_device_t *device; + + res = module->common.methods->open(&module->common, deviceName.string(), + reinterpret_cast<hw_device_t**>(&device)); + + if (res != OK) { + SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res); + return res; + } + + /** Cross-check device version */ + + if (device->common.version != CAMERA_DEVICE_API_VERSION_3_0) { + SET_ERR_L("Could not open camera: " + "Camera device is not version %x, reports %x instead", + CAMERA_DEVICE_API_VERSION_3_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) { + SET_ERR_L("HAL reporting mismatched camera_info version (%x)" + " and device version (%x).", + device->common.version, info.device_version); + device->common.close(&device->common); + return BAD_VALUE; + } + + /** Initialize device with callback functions */ + + ATRACE_BEGIN("camera3->initialize"); + res = device->ops->initialize(device, this); + ATRACE_END(); + + if (res != OK) { + SET_ERR_L("Unable to initialize HAL device: %s (%d)", + strerror(-res), res); + device->common.close(&device->common); + return BAD_VALUE; + } + + /** Get vendor metadata tags */ + + mVendorTagOps.get_camera_vendor_section_name = NULL; + + ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops"); + device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps); + ATRACE_END(); + + if (mVendorTagOps.get_camera_vendor_section_name != NULL) { + res = set_camera_metadata_vendor_tag_ops(&mVendorTagOps); + if (res != OK) { + SET_ERR_L("Unable to set tag ops: %s (%d)", + strerror(-res), res); + device->common.close(&device->common); + return res; + } + } + + /** Start up request queue thread */ + + mRequestThread = new RequestThread(this, device); + res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string()); + if (res != OK) { + SET_ERR_L("Unable to start request queue thread: %s (%d)", + strerror(-res), res); + device->common.close(&device->common); + mRequestThread.clear(); + return res; + } + + /** Everything is good to go */ + + mDeviceInfo = info.static_camera_characteristics; + mHal3Device = device; + mStatus = STATUS_IDLE; + mNextStreamId = 0; + mNeedConfig = true; + + return OK; +} + +status_t Camera3Device::disconnect() { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + ALOGV("%s: E", __FUNCTION__); + + status_t res = OK; + if (mStatus == STATUS_UNINITIALIZED) return res; + + if (mStatus == STATUS_ACTIVE || + (mStatus == STATUS_ERROR && mRequestThread != NULL)) { + res = mRequestThread->clearRepeatingRequests(); + if (res != OK) { + SET_ERR_L("Can't stop streaming"); + // Continue to close device even in case of error + } else { + res = waitUntilDrainedLocked(); + if (res != OK) { + SET_ERR_L("Timeout waiting for HAL to drain"); + // Continue to close device even in case of error + } + } + } + assert(mStatus == STATUS_IDLE || mStatus == STATUS_ERROR); + + if (mStatus == STATUS_ERROR) { + CLOGE("Shutting down in an error state"); + } + + if (mRequestThread != NULL) { + mRequestThread->requestExit(); + } + + mOutputStreams.clear(); + mInputStream.clear(); + + if (mRequestThread != NULL) { + if (mStatus != STATUS_ERROR) { + // HAL may be in a bad state, so waiting for request thread + // (which may be stuck in the HAL processCaptureRequest call) + // could be dangerous. + mRequestThread->join(); + } + mRequestThread.clear(); + } + + if (mHal3Device != NULL) { + mHal3Device->common.close(&mHal3Device->common); + mHal3Device = NULL; + } + + mStatus = STATUS_UNINITIALIZED; + + ALOGV("%s: X", __FUNCTION__); + return res; +} + +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); + if (mStatus == STATUS_ERROR) { + lines.appendFormat(" Error cause: %s\n", mErrorCause.string()); + } + 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); + } + + lines = String8(" In-flight requests:\n"); + if (mInFlightMap.size() == 0) { + lines.append(" None\n"); + } else { + for (size_t i = 0; i < mInFlightMap.size(); i++) { + InFlightRequest r = mInFlightMap.valueAt(i); + lines.appendFormat(" Frame %d | Timestamp: %lld, metadata" + " arrived: %s, buffers left: %d\n", mInFlightMap.keyAt(i), + r.captureTimestamp, r.haveResultMetadata ? "true" : "false", + r.numBuffersLeft); + } + } + write(fd, lines.string(), lines.size()); + + 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)) { + ALOGW("%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(); + Mutex::Autolock l(mLock); + + // TODO: take ownership of the request + + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized"); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + + sp<CaptureRequest> newRequest = setUpRequestLocked(request); + if (newRequest == NULL) { + CLOGE("Can't create capture request"); + return BAD_VALUE; + } + + return mRequestThread->queueRequest(newRequest); +} + + +status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized"); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + + sp<CaptureRequest> newRepeatingRequest = setUpRequestLocked(request); + if (newRepeatingRequest == NULL) { + CLOGE("Can't create repeating request"); + 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) { + SET_ERR_L("Can't set up streams: %s (%d)", 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: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized"); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + + return mRequestThread->clearRepeatingRequests(); +} + +status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) { + ATRACE_CALL(); + + return mRequestThread->waitUntilRequestProcessed(requestId, timeout); +} + +status_t Camera3Device::createInputStream( + uint32_t width, uint32_t height, int format, int *id) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + 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); + + if (mInputStream != 0) { + ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__); + return INVALID_OPERATION; + } + + sp<Camera3InputStream> newStream = new Camera3InputStream(mNextStreamId, + width, height, format); + + mInputStream = newStream; + + *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::createZslStream( + uint32_t width, uint32_t height, + int depth, + /*out*/ + int *id, + sp<Camera3ZslStream>* zslStream) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + 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); + + if (mInputStream != 0) { + ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__); + return INVALID_OPERATION; + } + + sp<Camera3ZslStream> newStream = new Camera3ZslStream(mNextStreamId, + width, height, depth); + + 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; + } + mInputStream = newStream; + + *id = mNextStreamId++; + *zslStream = newStream; + + // 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::createStream(sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size, int *id) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res; + bool wasActive = false; + + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized"); + 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__); + return res; + } + wasActive = true; + break; + default: + SET_ERR_L("Unexpected status: %d", 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) { + SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res); + return res; + } + + *id = mNextStreamId++; + mNeedConfig = true; + + // Continue captures if active at start + if (wasActive) { + ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__); + res = configureStreamsLocked(); + if (res != OK) { + CLOGE("Can't reconfigure device for new stream %d: %s (%d)", + mNextStreamId, strerror(-res), res); + return res; + } + mRequestThread->setPaused(false); + } + + return OK; +} + +status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) { + ATRACE_CALL(); + (void)outputId; (void)id; + + CLOGE("Unimplemented"); + return INVALID_OPERATION; +} + + +status_t Camera3Device::getStreamInfo(int id, + uint32_t *width, uint32_t *height, uint32_t *format) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized!"); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + CLOGE("Stream %d is unknown", 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(); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device not initialized"); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + CLOGE("Stream %d does not exist", + id); + return BAD_VALUE; + } + + return mOutputStreams.editValueAt(idx)->setTransform(transform); +} + +status_t Camera3Device::deleteStream(int id) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + status_t res; + + ALOGV("%s: Camera %d: Deleting stream %d", __FUNCTION__, mId, id); + + // CameraDevice semantics require device to already be idle before + // deleteStream is called, unlike for createStream. + if (mStatus != STATUS_IDLE) { + ALOGV("%s: Camera %d: Device not idle", __FUNCTION__, mId); + return -EBUSY; + } + + sp<Camera3StreamInterface> deletedStream; + if (mInputStream != NULL && id == mInputStream->getId()) { + deletedStream = mInputStream; + mInputStream.clear(); + } else { + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + CLOGE("Stream %d does not exist", 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) { + SET_ERR_L("Can't disconnect deleted stream %d", id); + // fall through since we want to still list the stream as deleted. + } + mDeletedStreams.add(deletedStream); + mNeedConfig = true; + + return res; +} + +status_t Camera3Device::deleteReprocessStream(int id) { + ATRACE_CALL(); + (void)id; + + CLOGE("Unimplemented"); + return INVALID_OPERATION; +} + + +status_t Camera3Device::createDefaultRequest(int templateId, + CameraMetadata *request) { + ATRACE_CALL(); + ALOGV("%s: for template %d", __FUNCTION__, templateId); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + CLOGE("Device has encountered a serious error"); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + CLOGE("Device is not initialized!"); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + SET_ERR_L("Unexpected status: %d", mStatus); + return INVALID_OPERATION; + } + + const camera_metadata_t *rawRequest; + ATRACE_BEGIN("camera3->construct_default_request_settings"); + rawRequest = mHal3Device->ops->construct_default_request_settings( + mHal3Device, templateId); + ATRACE_END(); + if (rawRequest == NULL) { + SET_ERR_L("HAL is unable to construct default settings for template %d", + templateId); + return DEAD_OBJECT; + } + *request = rawRequest; + + return OK; +} + +status_t Camera3Device::waitUntilDrained() { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + 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: + SET_ERR_L("Unexpected status: %d",mStatus); + return INVALID_OPERATION; + } + + if (mRequestThread != NULL) { + res = mRequestThread->waitUntilPaused(kShutdownTimeout); + if (res != OK) { + SET_ERR_L("Can't stop request thread in %f seconds!", + kShutdownTimeout/1e9); + return res; + } + } + if (mInputStream != NULL) { + res = mInputStream->waitUntilIdle(kShutdownTimeout); + if (res != OK) { + SET_ERR_L("Can't idle input stream %d in %f seconds!", + mInputStream->getId(), kShutdownTimeout/1e9); + return res; + } + } + for (size_t i = 0; i < mOutputStreams.size(); i++) { + res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout); + if (res != OK) { + SET_ERR_L("Can't idle output stream %d in %f seconds!", + mOutputStreams.keyAt(i), kShutdownTimeout/1e9); + return res; + } + } + + if (mStatus != STATUS_ERROR) { + mStatus = STATUS_IDLE; + } + + return OK; +} + +status_t Camera3Device::setNotifyCallback(NotificationListener *listener) { + ATRACE_CALL(); + Mutex::Autolock l(mOutputLock); + + if (listener != NULL && mListener != NULL) { + ALOGW("%s: Replacing old callback listener", __FUNCTION__); + } + mListener = listener; + + return OK; +} + +bool Camera3Device::willNotify3A() { + return false; +} + +status_t Camera3Device::waitForNextFrame(nsecs_t timeout) { + ATRACE_CALL(); + status_t res; + Mutex::Autolock l(mOutputLock); + + while (mResultQueue.empty()) { + res = mResultSignal.waitRelative(mOutputLock, timeout); + if (res == TIMED_OUT) { + return res; + } else if (res != OK) { + ALOGW("%s: Camera %d: No frame in %lld ns: %s (%d)", + __FUNCTION__, mId, timeout, strerror(-res), res); + return res; + } + } + return OK; +} + +status_t Camera3Device::getNextFrame(CameraMetadata *frame) { + ATRACE_CALL(); + Mutex::Autolock l(mOutputLock); + + if (mResultQueue.empty()) { + return NOT_ENOUGH_DATA; + } + + CameraMetadata &result = *(mResultQueue.begin()); + frame->acquire(result); + mResultQueue.erase(mResultQueue.begin()); + + return OK; +} + +status_t Camera3Device::triggerAutofocus(uint32_t id) { + ATRACE_CALL(); + + ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id); + // Mix-in this trigger into the next request and only the next request. + RequestTrigger trigger[] = { + { + ANDROID_CONTROL_AF_TRIGGER, + ANDROID_CONTROL_AF_TRIGGER_START + }, + { + ANDROID_CONTROL_AF_TRIGGER_ID, + static_cast<int32_t>(id) + }, + }; + + return mRequestThread->queueTrigger(trigger, + sizeof(trigger)/sizeof(trigger[0])); +} + +status_t Camera3Device::triggerCancelAutofocus(uint32_t id) { + ATRACE_CALL(); + + ALOGV("%s: Triggering cancel autofocus, id %d", __FUNCTION__, id); + // Mix-in this trigger into the next request and only the next request. + RequestTrigger trigger[] = { + { + ANDROID_CONTROL_AF_TRIGGER, + ANDROID_CONTROL_AF_TRIGGER_CANCEL + }, + { + ANDROID_CONTROL_AF_TRIGGER_ID, + static_cast<int32_t>(id) + }, + }; + + return mRequestThread->queueTrigger(trigger, + sizeof(trigger)/sizeof(trigger[0])); +} + +status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) { + ATRACE_CALL(); + + ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id); + // Mix-in this trigger into the next request and only the next request. + RequestTrigger trigger[] = { + { + ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, + ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START + }, + { + ANDROID_CONTROL_AE_PRECAPTURE_ID, + static_cast<int32_t>(id) + }, + }; + + return mRequestThread->queueTrigger(trigger, + sizeof(trigger)/sizeof(trigger[0])); +} + +status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId, + buffer_handle_t *buffer, wp<BufferReleasedListener> listener) { + ATRACE_CALL(); + (void)reprocessStreamId; (void)buffer; (void)listener; + + CLOGE("Unimplemented"); + return INVALID_OPERATION; +} + +/** + * 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]) { + CLOGE("Request references unknown input stream %d", + 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) { + SET_ERR_L("Unable to finish configuring input stream %d:" + " %s (%d)", + 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) { + CLOGE("Zero output streams specified!"); + return NULL; + } + + for (size_t i = 0; i < streams.count; i++) { + int idx = mOutputStreams.indexOfKey(streams.data.u8[i]); + if (idx == NAME_NOT_FOUND) { + CLOGE("Request references unknown stream %d", + streams.data.u8[i]); + return NULL; + } + sp<Camera3OutputStreamInterface> stream = + mOutputStreams.editValueAt(idx); + + // Lazy completion of stream configuration (allocation/registration) + // on first use + if (stream->isConfiguring()) { + res = stream->finishConfiguration(mHal3Device); + if (res != OK) { + SET_ERR_L("Unable to finish configuring stream %d: %s (%d)", + stream->getId(), strerror(-res), res); + return NULL; + } + } + + newRequest->mOutputStreams.push(stream); + } + newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS); + + return newRequest; +} + +status_t Camera3Device::configureStreamsLocked() { + ATRACE_CALL(); + status_t res; + + if (mStatus != STATUS_IDLE) { + CLOGE("Not idle"); + return INVALID_OPERATION; + } + + if (!mNeedConfig) { + ALOGV("%s: Skipping config, no stream changes", __FUNCTION__); + mStatus = STATUS_ACTIVE; + return OK; + } + + // 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) { + SET_ERR_L("Can't start input stream configuration"); + return INVALID_OPERATION; + } + streams.add(inputStream); + } + + for (size_t i = 0; i < mOutputStreams.size(); i++) { + + // Don't configure bidi streams twice, nor add them twice to the list + if (mOutputStreams[i].get() == + static_cast<Camera3StreamInterface*>(mInputStream.get())) { + + config.num_streams--; + continue; + } + + camera3_stream_t *outputStream; + outputStream = mOutputStreams.editValueAt(i)->startConfiguration(); + if (outputStream == NULL) { + SET_ERR_L("Can't start output stream configuration"); + return INVALID_OPERATION; + } + streams.add(outputStream); + } + + config.streams = streams.editArray(); + + // Do the HAL configuration; will potentially touch stream + // max_buffers, usage, priv fields. + ATRACE_BEGIN("camera3->configure_streams"); + res = mHal3Device->ops->configure_streams(mHal3Device, &config); + ATRACE_END(); + + if (res != OK) { + SET_ERR_L("Unable to configure streams with HAL: %s (%d)", + strerror(-res), res); + return res; + } + + // Finish all stream configuration immediately. + // TODO: Try to relax this later back to lazy completion, which should be + // faster + + if (mInputStream != NULL && mInputStream->isConfiguring()) { + res = mInputStream->finishConfiguration(mHal3Device); + if (res != OK) { + SET_ERR_L("Can't finish configuring input stream %d: %s (%d)", + mInputStream->getId(), strerror(-res), res); + return res; + } + } + + for (size_t i = 0; i < mOutputStreams.size(); i++) { + sp<Camera3OutputStreamInterface> outputStream = + mOutputStreams.editValueAt(i); + if (outputStream->isConfiguring()) { + res = outputStream->finishConfiguration(mHal3Device); + if (res != OK) { + SET_ERR_L("Can't finish configuring output stream %d: %s (%d)", + outputStream->getId(), 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; + mNeedConfig = false; + + return OK; +} + +void Camera3Device::setErrorState(const char *fmt, ...) { + Mutex::Autolock l(mLock); + va_list args; + va_start(args, fmt); + + setErrorStateLockedV(fmt, args); + + va_end(args); +} + +void Camera3Device::setErrorStateV(const char *fmt, va_list args) { + Mutex::Autolock l(mLock); + setErrorStateLockedV(fmt, args); +} + +void Camera3Device::setErrorStateLocked(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + + setErrorStateLockedV(fmt, args); + + va_end(args); +} + +void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) { + // Print out all error messages to log + String8 errorCause = String8::formatV(fmt, args); + ALOGE("Camera %d: %s", mId, errorCause.string()); + + // But only do error state transition steps for the first error + if (mStatus == STATUS_ERROR || mStatus == STATUS_UNINITIALIZED) return; + + mErrorCause = errorCause; + + mRequestThread->setPaused(true); + mStatus = STATUS_ERROR; +} + +/** + * In-flight request management + */ + +status_t Camera3Device::registerInFlight(int32_t frameNumber, + int32_t numBuffers) { + ATRACE_CALL(); + Mutex::Autolock l(mInFlightLock); + + ssize_t res; + res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers)); + if (res < 0) return res; + + return OK; +} + +/** + * Camera HAL device callback methods + */ + +void Camera3Device::processCaptureResult(const camera3_capture_result *result) { + ATRACE_CALL(); + + status_t res; + + uint32_t frameNumber = result->frame_number; + if (result->result == NULL && result->num_output_buffers == 0) { + SET_ERR("No result data provided by HAL for frame %d", + frameNumber); + return; + } + + // Get capture timestamp from list of in-flight requests, where it was added + // by the shutter notification for this frame. Then update the in-flight + // status and remove the in-flight entry if all result data has been + // received. + nsecs_t timestamp = 0; + { + Mutex::Autolock l(mInFlightLock); + ssize_t idx = mInFlightMap.indexOfKey(frameNumber); + if (idx == NAME_NOT_FOUND) { + SET_ERR("Unknown frame number for capture result: %d", + frameNumber); + return; + } + InFlightRequest &request = mInFlightMap.editValueAt(idx); + timestamp = request.captureTimestamp; + if (timestamp == 0) { + SET_ERR("Called before shutter notify for frame %d", + frameNumber); + return; + } + + if (result->result != NULL) { + if (request.haveResultMetadata) { + SET_ERR("Called multiple times with metadata for frame %d", + frameNumber); + return; + } + request.haveResultMetadata = true; + } + + request.numBuffersLeft -= result->num_output_buffers; + + if (request.numBuffersLeft < 0) { + SET_ERR("Too many buffers returned for frame %d", + frameNumber); + return; + } + + if (request.haveResultMetadata && request.numBuffersLeft == 0) { + ATRACE_ASYNC_END("frame capture", frameNumber); + mInFlightMap.removeItemsAt(idx, 1); + } + + // Sanity check - if we have too many in-flight frames, something has + // likely gone wrong + if (mInFlightMap.size() > kInFlightWarnLimit) { + CLOGE("In-flight list too large: %d", mInFlightMap.size()); + } + + } + + // Process the result metadata, if provided + if (result->result != NULL) { + Mutex::Autolock l(mOutputLock); + + if (frameNumber != mNextResultFrameNumber) { + SET_ERR("Out-of-order capture result metadata submitted! " + "(got frame number %d, expecting %d)", + frameNumber, mNextResultFrameNumber); + return; + } + mNextResultFrameNumber++; + + CameraMetadata &captureResult = + *mResultQueue.insert(mResultQueue.end(), CameraMetadata()); + + captureResult = result->result; + if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT, + (int32_t*)&frameNumber, 1) != OK) { + SET_ERR("Failed to set frame# in metadata (%d)", + frameNumber); + } else { + ALOGVV("%s: Camera %d: Set frame# in metadata (%d)", + __FUNCTION__, mId, frameNumber); + } + + // Check that there's a timestamp in the result metadata + + camera_metadata_entry entry = + captureResult.find(ANDROID_SENSOR_TIMESTAMP); + if (entry.count == 0) { + SET_ERR("No timestamp provided by HAL for frame %d!", + frameNumber); + } else if (timestamp != entry.data.i64[0]) { + SET_ERR("Timestamp mismatch between shutter notify and result" + " metadata for frame %d (%lld vs %lld respectively)", + frameNumber, timestamp, entry.data.i64[0]); + } + } // scope for mOutputLock + + // Return completed buffers to their streams with the timestamp + + for (size_t i = 0; i < result->num_output_buffers; i++) { + Camera3Stream *stream = + Camera3Stream::cast(result->output_buffers[i].stream); + res = stream->returnBuffer(result->output_buffers[i], timestamp); + // Note: stream may be deallocated at this point, if this buffer was the + // last reference to it. + if (res != OK) { + SET_ERR("Can't return buffer %d for frame %d to its stream: " + " %s (%d)", i, frameNumber, strerror(-res), res); + } + } + + // Finally, signal any waiters for new frames + + if (result->result != NULL) { + mResultSignal.signal(); + } + +} + + + +void Camera3Device::notify(const camera3_notify_msg *msg) { + ATRACE_CALL(); + NotificationListener *listener; + { + Mutex::Autolock l(mOutputLock); + listener = mListener; + } + + if (msg == NULL) { + SET_ERR("HAL sent NULL notify message!"); + return; + } + + switch (msg->type) { + case CAMERA3_MSG_ERROR: { + int streamId = 0; + if (msg->message.error.error_stream != NULL) { + Camera3Stream *stream = + Camera3Stream::cast( + msg->message.error.error_stream); + streamId = stream->getId(); + } + ALOGV("Camera %d: %s: HAL error, frame %d, stream %d: %d", + mId, __FUNCTION__, msg->message.error.frame_number, + streamId, msg->message.error.error_code); + if (listener != NULL) { + listener->notifyError(msg->message.error.error_code, + msg->message.error.frame_number, streamId); + } + break; + } + case CAMERA3_MSG_SHUTTER: { + ssize_t idx; + uint32_t frameNumber = msg->message.shutter.frame_number; + nsecs_t timestamp = msg->message.shutter.timestamp; + // Verify ordering of shutter notifications + { + Mutex::Autolock l(mOutputLock); + if (frameNumber != mNextShutterFrameNumber) { + SET_ERR("Shutter notification out-of-order. Expected " + "notification for frame %d, got frame %d", + mNextShutterFrameNumber, frameNumber); + break; + } + mNextShutterFrameNumber++; + } + + // Set timestamp for the request in the in-flight tracking + { + Mutex::Autolock l(mInFlightLock); + idx = mInFlightMap.indexOfKey(frameNumber); + if (idx >= 0) { + mInFlightMap.editValueAt(idx).captureTimestamp = timestamp; + } + } + if (idx < 0) { + SET_ERR("Shutter notification for non-existent frame number %d", + frameNumber); + break; + } + ALOGVV("Camera %d: %s: Shutter fired for frame %d at %lld", + mId, __FUNCTION__, frameNumber, timestamp); + // Call listener, if any + if (listener != NULL) { + listener->notifyShutter(frameNumber, timestamp); + } + break; + } + default: + SET_ERR("Unknown notify message from HAL: %d", + msg->type); + } +} + +/** + * RequestThread inner class methods + */ + +Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent, + camera3_device_t *hal3Device) : + Thread(false), + mParent(parent), + mHal3Device(hal3Device), + mId(getId(parent)), + mReconfigured(false), + mDoPause(false), + mPaused(true), + mFrameNumber(0), + mLatestRequestId(NAME_NOT_FOUND) { +} + +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::queueTrigger( + RequestTrigger trigger[], + size_t count) { + + Mutex::Autolock l(mTriggerMutex); + status_t ret; + + for (size_t i = 0; i < count; ++i) { + ret = queueTriggerLocked(trigger[i]); + + if (ret != OK) { + return ret; + } + } + + return OK; +} + +int Camera3Device::RequestThread::getId(const wp<Camera3Device> &device) { + sp<Camera3Device> d = device.promote(); + if (d != NULL) return d->mId; + return 0; +} + +status_t Camera3Device::RequestThread::queueTriggerLocked( + RequestTrigger trigger) { + + uint32_t tag = trigger.metadataTag; + ssize_t index = mTriggerMap.indexOfKey(tag); + + switch (trigger.getTagType()) { + case TYPE_BYTE: + // fall-through + case TYPE_INT32: + break; + default: + ALOGE("%s: Type not supported: 0x%x", __FUNCTION__, + trigger.getTagType()); + return INVALID_OPERATION; + } + + /** + * Collect only the latest trigger, since we only have 1 field + * in the request settings per trigger tag, and can't send more than 1 + * trigger per request. + */ + if (index != NAME_NOT_FOUND) { + mTriggerMap.editValueAt(index) = trigger; + } else { + mTriggerMap.add(tag, trigger); + } + + 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) { + ATRACE_CALL(); + status_t res; + Mutex::Autolock l(mPauseLock); + while (!mPaused) { + res = mPausedSignal.waitRelative(mPauseLock, timeout); + if (res == TIMED_OUT) { + return res; + } + } + return OK; +} + +status_t Camera3Device::RequestThread::waitUntilRequestProcessed( + int32_t requestId, nsecs_t timeout) { + Mutex::Autolock l(mLatestRequestMutex); + status_t res; + while (mLatestRequestId != requestId) { + nsecs_t startTime = systemTime(); + + res = mLatestRequestSignal.waitRelative(mLatestRequestMutex, timeout); + if (res != OK) return res; + + timeout -= (systemTime() - startTime); + } + + 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(); + Vector<camera3_stream_buffer_t> outputBuffers; + + // Insert any queued triggers (before metadata is locked) + int32_t triggerCount; + res = insertTriggers(nextRequest); + if (res < 0) { + SET_ERR("RequestThread: Unable to insert triggers " + "(capture request %d, HAL device: %s (%d)", + (mFrameNumber+1), strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return false; + } + triggerCount = res; + + bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0); + + // If the request is the same as last, or we had triggers last time + if (mPrevRequest != nextRequest || triggersMixedIn) { + /** + * The request should be presorted so accesses in HAL + * are O(logn). Sidenote, sorting a sorted metadata is nop. + */ + nextRequest->mSettings.sort(); + request.settings = nextRequest->mSettings.getAndLock(); + mPrevRequest = nextRequest; + ALOGVV("%s: Request settings are NEW", __FUNCTION__); + + IF_ALOGV() { + camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t(); + find_camera_metadata_ro_entry( + request.settings, + ANDROID_CONTROL_AF_TRIGGER, + &e + ); + if (e.count > 0) { + ALOGV("%s: Request (frame num %d) had AF trigger 0x%x", + __FUNCTION__, + mFrameNumber+1, + e.data.u8[0]); + } + } + } else { + // leave request.settings NULL to indicate 'reuse latest given' + ALOGVV("%s: Request settings are REUSED", + __FUNCTION__); + } + + camera3_stream_buffer_t inputBuffer; + + // Fill in buffers + + if (nextRequest->mInputStream != NULL) { + request.input_buffer = &inputBuffer; + res = nextRequest->mInputStream->getInputBuffer(&inputBuffer); + if (res != OK) { + SET_ERR("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) { + SET_ERR("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++; + + // Log request in the in-flight queue + sp<Camera3Device> parent = mParent.promote(); + if (parent == NULL) { + CLOGE("RequestThread: Parent is gone"); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return false; + } + + res = parent->registerInFlight(request.frame_number, + request.num_output_buffers); + if (res != OK) { + SET_ERR("RequestThread: Unable to register new in-flight request:" + " %s (%d)", strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return false; + } + + // Submit request and block until ready for next one + ATRACE_ASYNC_BEGIN("frame capture", request.frame_number); + ATRACE_BEGIN("camera3->process_capture_request"); + res = mHal3Device->ops->process_capture_request(mHal3Device, &request); + ATRACE_END(); + + if (res != OK) { + SET_ERR("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); + } + + // Remove any previously queued triggers (after unlock) + res = removeTriggers(mPrevRequest); + if (res != OK) { + SET_ERR("RequestThread: Unable to remove triggers " + "(capture request %d, HAL device: %s (%d)", + request.frame_number, strerror(-res), res); + return false; + } + mPrevTriggers = triggerCount; + + // Read android.request.id from the request settings metadata + // - inform waitUntilRequestProcessed thread of a new request ID + { + Mutex::Autolock al(mLatestRequestMutex); + + camera_metadata_entry_t requestIdEntry = + nextRequest->mSettings.find(ANDROID_REQUEST_ID); + if (requestIdEntry.count > 0) { + mLatestRequestId = requestIdEntry.data.i32[0]; + } else { + ALOGW("%s: Did not have android.request.id set in the request", + __FUNCTION__); + mLatestRequestId = NAME_NOT_FOUND; + } + + mLatestRequestSignal.signal(); + } + + // Return input buffer back to framework + if (request.input_buffer != NULL) { + Camera3Stream *stream = + Camera3Stream::cast(request.input_buffer->stream); + res = stream->returnInputBuffer(*(request.input_buffer)); + // Note: stream may be deallocated at this point, if this buffer was the + // last reference to it. + if (res != OK) { + ALOGE("%s: RequestThread: Can't return input buffer for frame %d to" + " its stream:%s (%d)", __FUNCTION__, + request.frame_number, strerror(-res), res); + // TODO: Report error upstream + } + } + + + + 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->returnInputBuffer(*(request.input_buffer)); + } + 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); + } +} + +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; +} + +void Camera3Device::RequestThread::setErrorState(const char *fmt, ...) { + sp<Camera3Device> parent = mParent.promote(); + if (parent != NULL) { + va_list args; + va_start(args, fmt); + + parent->setErrorStateV(fmt, args); + + va_end(args); + } +} + +status_t Camera3Device::RequestThread::insertTriggers( + const sp<CaptureRequest> &request) { + + Mutex::Autolock al(mTriggerMutex); + + CameraMetadata &metadata = request->mSettings; + size_t count = mTriggerMap.size(); + + for (size_t i = 0; i < count; ++i) { + RequestTrigger trigger = mTriggerMap.valueAt(i); + + uint32_t tag = trigger.metadataTag; + camera_metadata_entry entry = metadata.find(tag); + + if (entry.count > 0) { + /** + * Already has an entry for this trigger in the request. + * Rewrite it with our requested trigger value. + */ + RequestTrigger oldTrigger = trigger; + + oldTrigger.entryValue = entry.data.u8[0]; + + mTriggerReplacedMap.add(tag, oldTrigger); + } else { + /** + * More typical, no trigger entry, so we just add it + */ + mTriggerRemovedMap.add(tag, trigger); + } + + status_t res; + + switch (trigger.getTagType()) { + case TYPE_BYTE: { + uint8_t entryValue = static_cast<uint8_t>(trigger.entryValue); + res = metadata.update(tag, + &entryValue, + /*count*/1); + break; + } + case TYPE_INT32: + res = metadata.update(tag, + &trigger.entryValue, + /*count*/1); + break; + default: + ALOGE("%s: Type not supported: 0x%x", + __FUNCTION__, + trigger.getTagType()); + return INVALID_OPERATION; + } + + if (res != OK) { + ALOGE("%s: Failed to update request metadata with trigger tag %s" + ", value %d", __FUNCTION__, trigger.getTagName(), + trigger.entryValue); + return res; + } + + ALOGV("%s: Mixed in trigger %s, value %d", __FUNCTION__, + trigger.getTagName(), + trigger.entryValue); + } + + mTriggerMap.clear(); + + return count; +} + +status_t Camera3Device::RequestThread::removeTriggers( + const sp<CaptureRequest> &request) { + Mutex::Autolock al(mTriggerMutex); + + CameraMetadata &metadata = request->mSettings; + + /** + * Replace all old entries with their old values. + */ + for (size_t i = 0; i < mTriggerReplacedMap.size(); ++i) { + RequestTrigger trigger = mTriggerReplacedMap.valueAt(i); + + status_t res; + + uint32_t tag = trigger.metadataTag; + switch (trigger.getTagType()) { + case TYPE_BYTE: { + uint8_t entryValue = static_cast<uint8_t>(trigger.entryValue); + res = metadata.update(tag, + &entryValue, + /*count*/1); + break; + } + case TYPE_INT32: + res = metadata.update(tag, + &trigger.entryValue, + /*count*/1); + break; + default: + ALOGE("%s: Type not supported: 0x%x", + __FUNCTION__, + trigger.getTagType()); + return INVALID_OPERATION; + } + + if (res != OK) { + ALOGE("%s: Failed to restore request metadata with trigger tag %s" + ", trigger value %d", __FUNCTION__, + trigger.getTagName(), trigger.entryValue); + return res; + } + } + mTriggerReplacedMap.clear(); + + /** + * Remove all new entries. + */ + for (size_t i = 0; i < mTriggerRemovedMap.size(); ++i) { + RequestTrigger trigger = mTriggerRemovedMap.valueAt(i); + status_t res = metadata.erase(trigger.metadataTag); + + if (res != OK) { + ALOGE("%s: Failed to erase metadata with trigger tag %s" + ", trigger value %d", __FUNCTION__, + trigger.getTagName(), trigger.entryValue); + return res; + } + } + mTriggerRemovedMap.clear(); + + return OK; +} + + + +/** + * Static callback forwarding methods from HAL to instance + */ + +void Camera3Device::sProcessCaptureResult(const camera3_callback_ops *cb, + const camera3_capture_result *result) { + Camera3Device *d = + const_cast<Camera3Device*>(static_cast<const Camera3Device*>(cb)); + d->processCaptureResult(result); +} + +void Camera3Device::sNotify(const camera3_callback_ops *cb, + const camera3_notify_msg *msg) { + Camera3Device *d = + const_cast<Camera3Device*>(static_cast<const Camera3Device*>(cb)); + d->notify(msg); +} + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h new file mode 100644 index 0000000..76c08ae --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3DEVICE_H +#define ANDROID_SERVERS_CAMERA3DEVICE_H + +#include <utils/Condition.h> +#include <utils/Errors.h> +#include <utils/List.h> +#include <utils/Mutex.h> +#include <utils/Thread.h> +#include <utils/KeyedVector.h> +#include <hardware/camera3.h> + +#include "common/CameraDeviceBase.h" + +/** + * Function pointer types with C calling convention to + * use for HAL callback functions. + */ +extern "C" { + typedef void (callbacks_process_capture_result_t)( + const struct camera3_callback_ops *, + const camera3_capture_result_t *); + + typedef void (callbacks_notify_t)( + const struct camera3_callback_ops *, + const camera3_notify_msg_t *); +} + +namespace android { + +namespace camera3 { + +class Camera3Stream; +class Camera3ZslStream; +class Camera3OutputStreamInterface; +class Camera3StreamInterface; + +} + +/** + * CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_3_0 + */ +class Camera3Device : + public CameraDeviceBase, + private camera3_callback_ops { + public: + Camera3Device(int id); + + virtual ~Camera3Device(); + + /** + * 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<String16> &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<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size, + int *id); + virtual status_t createInputStream( + uint32_t width, uint32_t height, int format, + int *id); + virtual status_t createZslStream( + uint32_t width, uint32_t height, + int depth, + /*out*/ + int *id, + sp<camera3::Camera3ZslStream>* zslStream); + 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 bool willNotify3A(); + 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<BufferReleasedListener> listener); + + private: + static const size_t kInFlightWarnLimit = 20; + static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec + struct RequestTrigger; + + 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; + + // Tracking cause of fatal errors when in STATUS_ERROR + String8 mErrorCause; + + // Mapping of stream IDs to stream instances + typedef KeyedVector<int, sp<camera3::Camera3OutputStreamInterface> > + StreamSet; + + StreamSet mOutputStreams; + sp<camera3::Camera3Stream> mInputStream; + int mNextStreamId; + bool mNeedConfig; + + // Need to hold on to stream references until configure completes. + Vector<sp<camera3::Camera3StreamInterface> > mDeletedStreams; + + /**** End scope for mLock ****/ + + class CaptureRequest : public LightRefBase<CaptureRequest> { + public: + CameraMetadata mSettings; + sp<camera3::Camera3Stream> mInputStream; + Vector<sp<camera3::Camera3OutputStreamInterface> > + mOutputStreams; + }; + typedef List<sp<CaptureRequest> > 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<CaptureRequest> setUpRequestLocked(const CameraMetadata &request); + + /** + * Build a CaptureRequest request from the CameraDeviceBase request + * settings. + */ + sp<CaptureRequest> createCaptureRequest(const CameraMetadata &request); + + /** + * 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(); + + /** + * Set device into an error state due to some fatal failure, and set an + * error message to indicate why. Only the first call's message will be + * used. The message is also sent to the log. + */ + void setErrorState(const char *fmt, ...); + void setErrorStateV(const char *fmt, va_list args); + void setErrorStateLocked(const char *fmt, ...); + void setErrorStateLockedV(const char *fmt, va_list args); + + struct RequestTrigger { + // Metadata tag number, e.g. android.control.aePrecaptureTrigger + uint32_t metadataTag; + // Metadata value, e.g. 'START' or the trigger ID + int32_t entryValue; + + // The last part of the fully qualified path, e.g. afTrigger + const char *getTagName() const { + return get_camera_metadata_tag_name(metadataTag) ?: "NULL"; + } + + // e.g. TYPE_BYTE, TYPE_INT32, etc. + int getTagType() const { + return get_camera_metadata_tag_type(metadataTag); + } + }; + + /** + * Thread for managing capture request submission to HAL device. + */ + class RequestThread : public Thread { + + public: + + RequestThread(wp<Camera3Device> 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<CaptureRequest> request); + + /** + * Queue a trigger to be dispatched with the next outgoing + * process_capture_request. The settings for that request only + * will be temporarily rewritten to add the trigger tag/value. + * Subsequent requests will not be rewritten (for this tag). + */ + status_t queueTrigger(RequestTrigger trigger[], size_t count); + + /** + * 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); + + /** + * Wait until thread processes the capture request with settings' + * android.request.id == requestId. + * + * Returns TIMED_OUT in case the thread does not process the request + * within the timeout. + */ + status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout); + + protected: + + virtual bool threadLoop(); + + private: + static int getId(const wp<Camera3Device> &device); + + status_t queueTriggerLocked(RequestTrigger trigger); + // Mix-in queued triggers into this request + int32_t insertTriggers(const sp<CaptureRequest> &request); + // Purge the queued triggers from this request, + // restoring the old field values for those tags. + status_t removeTriggers(const sp<CaptureRequest> &request); + + static const nsecs_t kRequestTimeout = 50e6; // 50 ms + + // Waits for a request, or returns NULL if times out. + sp<CaptureRequest> 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<CaptureRequest> &nextRequest, + Vector<camera3_stream_buffer_t> &outputBuffers); + + // Pause handling + bool waitIfPaused(); + + // Relay error to parent device object setErrorState + void setErrorState(const char *fmt, ...); + + wp<Camera3Device> mParent; + camera3_device_t *mHal3Device; + + const int mId; + + 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; + + sp<CaptureRequest> mPrevRequest; + int32_t mPrevTriggers; + + uint32_t mFrameNumber; + + Mutex mLatestRequestMutex; + Condition mLatestRequestSignal; + // android.request.id for latest process_capture_request + int32_t mLatestRequestId; + + typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap; + Mutex mTriggerMutex; + TriggerMap mTriggerMap; + TriggerMap mTriggerRemovedMap; + TriggerMap mTriggerReplacedMap; + }; + sp<RequestThread> mRequestThread; + + /** + * In-flight queue for tracking completion of capture requests. + */ + + struct InFlightRequest { + // Set by notify() SHUTTER call. + nsecs_t captureTimestamp; + // Set by process_capture_result call with valid metadata + bool haveResultMetadata; + // Decremented by calls to process_capture_result with valid output + // buffers + int numBuffersLeft; + + InFlightRequest() : + captureTimestamp(0), + haveResultMetadata(false), + numBuffersLeft(0) { + } + + explicit InFlightRequest(int numBuffers) : + captureTimestamp(0), + haveResultMetadata(false), + numBuffersLeft(numBuffers) { + } + }; + // Map from frame number to the in-flight request state + typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap; + + Mutex mInFlightLock; // Protects mInFlightMap + InFlightMap mInFlightMap; + + status_t registerInFlight(int32_t frameNumber, int32_t numBuffers); + + /** + * Output result queue and current HAL device 3A state + */ + + // Lock for output side of device + Mutex mOutputLock; + + /**** Scope for mOutputLock ****/ + + uint32_t mNextResultFrameNumber; + uint32_t mNextShutterFrameNumber; + List<CameraMetadata> mResultQueue; + Condition mResultSignal; + NotificationListener *mListener; + + /**** End scope for mOutputLock ****/ + + /** + * Callback functions from HAL device + */ + void processCaptureResult(const camera3_capture_result *result); + + void notify(const camera3_notify_msg *msg); + + /** + * Static callback forwarding methods from HAL to instance + */ + static callbacks_process_capture_result_t sProcessCaptureResult; + + static callbacks_notify_t sNotify; + +}; // class Camera3Device + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp new file mode 100644 index 0000000..0850566 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2013 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 "Camera3-IOStreamBase" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +// This is needed for stdint.h to define INT64_MAX in C++ +#define __STDC_LIMIT_MACROS + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3IOStreamBase.h" + +namespace android { + +namespace camera3 { + +Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type, + uint32_t width, uint32_t height, size_t maxSize, int format) : + Camera3Stream(id, type, + width, height, maxSize, format), + mTotalBufferCount(0), + mDequeuedBufferCount(0), + mFrameCount(0), + mLastTimestamp(0) { + + mCombinedFence = new Fence(); + + if (maxSize > 0 && format != HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__, + format); + mState = STATE_ERROR; + } +} + +Camera3IOStreamBase::~Camera3IOStreamBase() { + disconnectLocked(); +} + +bool Camera3IOStreamBase::hasOutstandingBuffersLocked() const { + nsecs_t signalTime = mCombinedFence->getSignalTime(); + ALOGV("%s: Stream %d: Has %d outstanding buffers," + " buffer signal time is %lld", + __FUNCTION__, mId, mDequeuedBufferCount, signalTime); + if (mDequeuedBufferCount > 0 || signalTime == INT64_MAX) { + return true; + } + return false; +} + +status_t Camera3IOStreamBase::waitUntilIdle(nsecs_t timeout) { + status_t res; + { + Mutex::Autolock l(mLock); + while (mDequeuedBufferCount > 0) { + if (timeout != TIMEOUT_NEVER) { + nsecs_t startTime = systemTime(); + res = mBufferReturnedSignal.waitRelative(mLock, timeout); + if (res == TIMED_OUT) { + return res; + } else if (res != OK) { + ALOGE("%s: Error waiting for outstanding buffers: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + nsecs_t deltaTime = systemTime() - startTime; + if (timeout <= deltaTime) { + timeout = 0; + } else { + timeout -= deltaTime; + } + } else { + res = mBufferReturnedSignal.wait(mLock); + if (res != OK) { + ALOGE("%s: Error waiting for outstanding buffers: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + } + } + } + + // No lock + + unsigned int timeoutMs; + if (timeout == TIMEOUT_NEVER) { + timeoutMs = Fence::TIMEOUT_NEVER; + } else if (timeout == 0) { + timeoutMs = 0; + } else { + // Round up to wait at least 1 ms + timeoutMs = (timeout + 999999) / 1000000; + } + + return mCombinedFence->wait(timeoutMs); +} + +void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const { + (void) args; + String8 lines; + lines.appendFormat(" State: %d\n", mState); + lines.appendFormat(" Dims: %d x %d, format 0x%x\n", + camera3_stream::width, camera3_stream::height, + camera3_stream::format); + lines.appendFormat(" Max size: %d\n", mMaxSize); + lines.appendFormat(" Usage: %d, max HAL buffers: %d\n", + camera3_stream::usage, camera3_stream::max_buffers); + lines.appendFormat(" Frames produced: %d, last timestamp: %lld ns\n", + mFrameCount, mLastTimestamp); + lines.appendFormat(" Total buffers: %d, currently dequeued: %d\n", + mTotalBufferCount, mDequeuedBufferCount); + write(fd, lines.string(), lines.size()); +} + +status_t Camera3IOStreamBase::configureQueueLocked() { + status_t res; + + switch (mState) { + case STATE_IN_RECONFIG: + res = disconnectLocked(); + if (res != OK) { + return res; + } + break; + case STATE_IN_CONFIG: + // OK + break; + default: + ALOGE("%s: Bad state: %d", __FUNCTION__, mState); + return INVALID_OPERATION; + } + + return OK; +} + +size_t Camera3IOStreamBase::getBufferCountLocked() { + return mTotalBufferCount; +} + +status_t Camera3IOStreamBase::disconnectLocked() { + switch (mState) { + case STATE_IN_RECONFIG: + case STATE_CONFIGURED: + // OK + break; + default: + // No connection, nothing to do + ALOGV("%s: Stream %d: Already disconnected", + __FUNCTION__, mId); + return -ENOTCONN; + } + + if (mDequeuedBufferCount > 0) { + ALOGE("%s: Can't disconnect with %d buffers still dequeued!", + __FUNCTION__, mDequeuedBufferCount); + return INVALID_OPERATION; + } + + return OK; +} + +void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer, + buffer_handle_t *handle, + int acquireFence, + int releaseFence, + camera3_buffer_status_t status) { + /** + * Note that all fences are now owned by HAL. + */ + + // Handing out a raw pointer to this object. Increment internal refcount. + incStrong(this); + buffer.stream = this; + buffer.buffer = handle; + buffer.acquire_fence = acquireFence; + buffer.release_fence = releaseFence; + buffer.status = status; + + mDequeuedBufferCount++; +} + +status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const { + // Allow dequeue during IN_[RE]CONFIG for registration + if (mState != STATE_CONFIGURED && + mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { + ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Only limit dequeue amount when fully configured + if (mState == STATE_CONFIGURED && + mDequeuedBufferCount == camera3_stream::max_buffers) { + ALOGE("%s: Stream %d: Already dequeued maximum number of simultaneous" + " buffers (%d)", __FUNCTION__, mId, + camera3_stream::max_buffers); + return INVALID_OPERATION; + } + + return OK; +} + +status_t Camera3IOStreamBase::returnBufferPreconditionCheckLocked() const { + // Allow buffers to be returned in the error state, to allow for disconnect + // and in the in-config states for registration + if (mState == STATE_CONSTRUCTED) { + ALOGE("%s: Stream %d: Can't return buffers in unconfigured state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + if (mDequeuedBufferCount == 0) { + ALOGE("%s: Stream %d: No buffers outstanding to return", __FUNCTION__, + mId); + return INVALID_OPERATION; + } + + return OK; +} + +status_t Camera3IOStreamBase::returnAnyBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output) { + status_t res; + + // returnBuffer may be called from a raw pointer, not a sp<>, and we'll be + // decrementing the internal refcount next. In case this is the last ref, we + // might get destructed on the decStrong(), so keep an sp around until the + // end of the call - otherwise have to sprinkle the decStrong on all exit + // points. + sp<Camera3IOStreamBase> keepAlive(this); + decStrong(this); + + if ((res = returnBufferPreconditionCheckLocked()) != OK) { + return res; + } + + sp<Fence> releaseFence; + res = returnBufferCheckedLocked(buffer, timestamp, output, + &releaseFence); + if (res != OK) { + return res; + } + + mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence); + + mDequeuedBufferCount--; + mBufferReturnedSignal.signal(); + + if (output) { + mLastTimestamp = timestamp; + } + + return OK; +} + + + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h new file mode 100644 index 0000000..74c4484 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_IO_STREAM_BASE_H +#define ANDROID_SERVERS_CAMERA3_IO_STREAM_BASE_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> + +#include "Camera3Stream.h" + +namespace android { + +namespace camera3 { + +/** + * A base class for managing a single stream of I/O data from the camera device. + */ +class Camera3IOStreamBase : + public Camera3Stream { + protected: + Camera3IOStreamBase(int id, camera3_stream_type_t type, + uint32_t width, uint32_t height, size_t maxSize, int format); + + public: + + virtual ~Camera3IOStreamBase(); + + /** + * Camera3Stream interface + */ + + virtual status_t waitUntilIdle(nsecs_t timeout); + virtual void dump(int fd, const Vector<String16> &args) const; + + protected: + size_t mTotalBufferCount; + // sum of input and output buffers that are currently acquired by HAL + size_t mDequeuedBufferCount; + Condition mBufferReturnedSignal; + uint32_t mFrameCount; + // Last received output buffer's timestamp + nsecs_t mLastTimestamp; + + // The merged release fence for all returned buffers + sp<Fence> mCombinedFence; + + status_t returnAnyBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output); + + virtual status_t returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut) = 0; + + /** + * Internal Camera3Stream interface + */ + virtual bool hasOutstandingBuffersLocked() const; + + virtual size_t getBufferCountLocked(); + + status_t getBufferPreconditionCheckLocked() const; + status_t returnBufferPreconditionCheckLocked() const; + + // State check only + virtual status_t configureQueueLocked(); + // State checks only + virtual status_t disconnectLocked(); + + // Hand out the buffer to a native location, + // incrementing the internal refcount and dequeued buffer count + void handoutBufferLocked(camera3_stream_buffer &buffer, + buffer_handle_t *handle, + int acquire_fence, + int release_fence, + camera3_buffer_status_t status); + +}; // class Camera3IOStreamBase + +} // namespace camera3 + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp new file mode 100644 index 0000000..e9a9c2b --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2013 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 "Camera3-InputStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3InputStream.h" + +namespace android { + +namespace camera3 { + +Camera3InputStream::Camera3InputStream(int id, + uint32_t width, uint32_t height, int format) : + Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, + /*maxSize*/0, format) { + + if (format == HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__); + mState = STATE_ERROR; + } +} + +Camera3InputStream::~Camera3InputStream() { + disconnectLocked(); +} + +status_t Camera3InputStream::getInputBufferLocked( + camera3_stream_buffer *buffer) { + ATRACE_CALL(); + status_t res; + + // FIXME: will not work in (re-)registration + if (mState == STATE_IN_CONFIG || mState == STATE_IN_RECONFIG) { + ALOGE("%s: Stream %d: Buffer registration for input streams" + " not implemented (state %d)", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + if ((res = getBufferPreconditionCheckLocked()) != OK) { + return res; + } + + ANativeWindowBuffer* anb; + int fenceFd; + + assert(mConsumer != 0); + + BufferItem bufferItem; + res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false); + + if (res != OK) { + ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + anb = bufferItem.mGraphicBuffer->getNativeBuffer(); + assert(anb != NULL); + fenceFd = bufferItem.mFence->dup(); + + /** + * FenceFD now owned by HAL except in case of error, + * in which case we reassign it to acquire_fence + */ + handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, + /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK); + mBuffersInFlight.push_back(bufferItem); + + return OK; +} + +status_t Camera3InputStream::returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut) { + + (void)timestamp; + (void)output; + ALOG_ASSERT(!output, "Expected output to be false"); + + status_t res; + + bool bufferFound = false; + BufferItem bufferItem; + { + // Find the buffer we are returning + Vector<BufferItem>::iterator it, end; + for (it = mBuffersInFlight.begin(), end = mBuffersInFlight.end(); + it != end; + ++it) { + + const BufferItem& tmp = *it; + ANativeWindowBuffer *anb = tmp.mGraphicBuffer->getNativeBuffer(); + if (anb != NULL && &(anb->handle) == buffer.buffer) { + bufferFound = true; + bufferItem = tmp; + mBuffersInFlight.erase(it); + mDequeuedBufferCount--; + } + } + } + if (!bufferFound) { + ALOGE("%s: Stream %d: Can't return buffer that wasn't sent to HAL", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { + if (buffer.release_fence != -1) { + ALOGE("%s: Stream %d: HAL should not set release_fence(%d) when " + "there is an error", __FUNCTION__, mId, buffer.release_fence); + close(buffer.release_fence); + } + + /** + * Reassign release fence as the acquire fence incase of error + */ + const_cast<camera3_stream_buffer*>(&buffer)->release_fence = + buffer.acquire_fence; + } + + /** + * Unconditionally return buffer to the buffer queue. + * - Fwk takes over the release_fence ownership + */ + sp<Fence> releaseFence = new Fence(buffer.release_fence); + res = mConsumer->releaseBuffer(bufferItem, releaseFence); + if (res != OK) { + ALOGE("%s: Stream %d: Error releasing buffer back to buffer queue:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); + return res; + } + + *releaseFenceOut = releaseFence; + + return OK; +} + +status_t Camera3InputStream::returnInputBufferLocked( + const camera3_stream_buffer &buffer) { + ATRACE_CALL(); + + return returnAnyBufferLocked(buffer, /*timestamp*/0, /*output*/false); +} + +status_t Camera3InputStream::disconnectLocked() { + + status_t res; + + if ((res = Camera3IOStreamBase::disconnectLocked()) != OK) { + return res; + } + + assert(mBuffersInFlight.size() == 0); + + /** + * no-op since we can't disconnect the producer from the consumer-side + */ + + mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG + : STATE_CONSTRUCTED; + return OK; +} + +sp<IGraphicBufferProducer> Camera3InputStream::getProducerInterface() const { + return mConsumer->getProducerInterface(); +} + +void Camera3InputStream::dump(int fd, const Vector<String16> &args) const { + (void) args; + String8 lines; + lines.appendFormat(" Stream[%d]: Input\n", mId); + write(fd, lines.string(), lines.size()); + + Camera3IOStreamBase::dump(fd, args); +} + +status_t Camera3InputStream::configureQueueLocked() { + status_t res; + + if ((res = Camera3IOStreamBase::configureQueueLocked()) != OK) { + return res; + } + + assert(mMaxSize == 0); + assert(camera3_stream::format != HAL_PIXEL_FORMAT_BLOB); + + mTotalBufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS + + camera3_stream::max_buffers; + mDequeuedBufferCount = 0; + mFrameCount = 0; + + if (mConsumer.get() == 0) { + sp<BufferQueue> bq = new BufferQueue(); + mConsumer = new BufferItemConsumer(bq, camera3_stream::usage, + mTotalBufferCount); + mConsumer->setName(String8::format("Camera3-InputStream-%d", mId)); + } + + res = mConsumer->setDefaultBufferSize(camera3_stream::width, + camera3_stream::height); + if (res != OK) { + ALOGE("%s: Stream %d: Could not set buffer dimensions %dx%d", + __FUNCTION__, mId, camera3_stream::width, camera3_stream::height); + return res; + } + res = mConsumer->setDefaultBufferFormat(camera3_stream::format); + if (res != OK) { + ALOGE("%s: Stream %d: Could not set buffer format %d", + __FUNCTION__, mId, camera3_stream::format); + return res; + } + + return OK; +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h new file mode 100644 index 0000000..8adda88 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3InputStream.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_INPUT_STREAM_H +#define ANDROID_SERVERS_CAMERA3_INPUT_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> +#include <gui/BufferItemConsumer.h> + +#include "Camera3IOStreamBase.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single stream of input data to the camera device. + * + * This class serves as a consumer adapter for the HAL, and will consume the + * buffers by feeding them into the HAL, as well as releasing the buffers back + * the buffers once the HAL is done with them. + */ +class Camera3InputStream : public Camera3IOStreamBase { + public: + /** + * Set up a stream for formats that have fixed size, such as RAW and YUV. + */ + Camera3InputStream(int id, uint32_t width, uint32_t height, int format); + ~Camera3InputStream(); + + virtual void dump(int fd, const Vector<String16> &args) const; + + /** + * Get the producer interface for this stream, to hand off to a producer. + * The producer must be connected to the provided interface before + * finishConfigure is called on this stream. + */ + sp<IGraphicBufferProducer> getProducerInterface() const; + + private: + + typedef BufferItemConsumer::BufferItem BufferItem; + + sp<BufferItemConsumer> mConsumer; + Vector<BufferItem> mBuffersInFlight; + + /** + * Camera3IOStreamBase + */ + virtual status_t returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut); + + /** + * Camera3Stream interface + */ + + virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnInputBufferLocked( + const camera3_stream_buffer &buffer); + virtual status_t disconnectLocked(); + + virtual status_t configureQueueLocked(); + +}; // class Camera3InputStream + +}; // namespace camera3 + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp new file mode 100644 index 0000000..0ec2b05 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2013 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 "Camera3-OutputStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3OutputStream.h" + +#ifndef container_of +#define container_of(ptr, type, member) \ + (type *)((char*)(ptr) - offsetof(type, member)) +#endif + +namespace android { + +namespace camera3 { + +Camera3OutputStream::Camera3OutputStream(int id, + sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format) : + Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, + /*maxSize*/0, format), + mConsumer(consumer), + mTransform(0) { + + if (mConsumer == NULL) { + ALOGE("%s: Consumer is NULL!", __FUNCTION__); + mState = STATE_ERROR; + } +} + +Camera3OutputStream::Camera3OutputStream(int id, + sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, size_t maxSize, int format) : + Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize, + format), + mConsumer(consumer), + mTransform(0) { + + if (format != HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__, + format); + mState = STATE_ERROR; + } + + if (mConsumer == NULL) { + ALOGE("%s: Consumer is NULL!", __FUNCTION__); + mState = STATE_ERROR; + } +} + +Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type, + uint32_t width, uint32_t height, + int format) : + Camera3IOStreamBase(id, type, width, height, + /*maxSize*/0, + format), + mTransform(0) { + + // Subclasses expected to initialize mConsumer themselves +} + + +Camera3OutputStream::~Camera3OutputStream() { + disconnectLocked(); +} + +status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + status_t res; + + if ((res = getBufferPreconditionCheckLocked()) != OK) { + return res; + } + + ANativeWindowBuffer* anb; + int fenceFd; + + res = mConsumer->dequeueBuffer(mConsumer.get(), &anb, &fenceFd); + if (res != OK) { + ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + /** + * FenceFD now owned by HAL except in case of error, + * in which case we reassign it to acquire_fence + */ + handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, + /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK); + + return OK; +} + +status_t Camera3OutputStream::returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + ATRACE_CALL(); + + status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true); + + if (res != OK) { + return res; + } + + mLastTimestamp = timestamp; + + return OK; +} + +status_t Camera3OutputStream::returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut) { + + (void)output; + ALOG_ASSERT(output, "Expected output to be true"); + + status_t res; + sp<Fence> releaseFence; + + /** + * Fence management - calculate Release Fence + */ + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { + if (buffer.release_fence != -1) { + ALOGE("%s: Stream %d: HAL should not set release_fence(%d) when " + "there is an error", __FUNCTION__, mId, buffer.release_fence); + close(buffer.release_fence); + } + + /** + * Reassign release fence as the acquire fence in case of error + */ + releaseFence = new Fence(buffer.acquire_fence); + } else { + res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp); + if (res != OK) { + ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + releaseFence = new Fence(buffer.release_fence); + } + + int anwReleaseFence = releaseFence->dup(); + + /** + * Release the lock briefly to avoid deadlock with + * StreamingProcessor::startStream -> Camera3Stream::isConfiguring (this + * thread will go into StreamingProcessor::onFrameAvailable) during + * queueBuffer + */ + sp<ANativeWindow> currentConsumer = mConsumer; + mLock.unlock(); + + /** + * Return buffer back to ANativeWindow + */ + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { + // Cancel buffer + res = currentConsumer->cancelBuffer(currentConsumer.get(), + container_of(buffer.buffer, ANativeWindowBuffer, handle), + anwReleaseFence); + if (res != OK) { + ALOGE("%s: Stream %d: Error cancelling buffer to native window:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); + } + } else { + res = currentConsumer->queueBuffer(currentConsumer.get(), + container_of(buffer.buffer, ANativeWindowBuffer, handle), + anwReleaseFence); + if (res != OK) { + ALOGE("%s: Stream %d: Error queueing buffer to native window: " + "%s (%d)", __FUNCTION__, mId, strerror(-res), res); + } + } + mLock.lock(); + if (res != OK) { + close(anwReleaseFence); + return res; + } + + *releaseFenceOut = releaseFence; + + return OK; +} + +void Camera3OutputStream::dump(int fd, const Vector<String16> &args) const { + (void) args; + String8 lines; + lines.appendFormat(" Stream[%d]: Output\n", mId); + write(fd, lines.string(), lines.size()); + + Camera3IOStreamBase::dump(fd, args); +} + +status_t Camera3OutputStream::setTransform(int transform) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return setTransformLocked(transform); +} + +status_t Camera3OutputStream::setTransformLocked(int transform) { + status_t res = OK; + if (mState == STATE_ERROR) { + ALOGE("%s: Stream in error state", __FUNCTION__); + return INVALID_OPERATION; + } + + mTransform = transform; + if (mState == STATE_CONFIGURED) { + res = native_window_set_buffers_transform(mConsumer.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 Camera3OutputStream::configureQueueLocked() { + status_t res; + + if ((res = Camera3IOStreamBase::configureQueueLocked()) != OK) { + return res; + } + + ALOG_ASSERT(mConsumer != 0, "mConsumer should never be NULL"); + + // Configure consumer-side ANativeWindow interface + res = native_window_api_connect(mConsumer.get(), + NATIVE_WINDOW_API_CAMERA); + if (res != OK) { + ALOGE("%s: Unable to connect to native window for stream %d", + __FUNCTION__, mId); + return res; + } + + res = native_window_set_usage(mConsumer.get(), camera3_stream::usage); + if (res != OK) { + ALOGE("%s: Unable to configure usage %08x for stream %d", + __FUNCTION__, camera3_stream::usage, mId); + return res; + } + + res = native_window_set_scaling_mode(mConsumer.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; + } + + if (mMaxSize == 0) { + // For buffers of known size + res = native_window_set_buffers_geometry(mConsumer.get(), + camera3_stream::width, camera3_stream::height, + camera3_stream::format); + } else { + // For buffers with bounded size + res = native_window_set_buffers_geometry(mConsumer.get(), + mMaxSize, 1, + camera3_stream::format); + } + if (res != OK) { + ALOGE("%s: Unable to configure stream buffer geometry" + " %d x %d, format %x for stream %d", + __FUNCTION__, camera3_stream::width, camera3_stream::height, + camera3_stream::format, mId); + return res; + } + + int maxConsumerBuffers; + res = mConsumer->query(mConsumer.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; + } + + ALOGV("%s: Consumer wants %d buffers, HAL wants %d", __FUNCTION__, + maxConsumerBuffers, camera3_stream::max_buffers); + if (camera3_stream::max_buffers == 0) { + ALOGE("%s: Camera HAL requested max_buffer count: %d, requires at least 1", + __FUNCTION__, camera3_stream::max_buffers); + return INVALID_OPERATION; + } + + mTotalBufferCount = maxConsumerBuffers + camera3_stream::max_buffers; + mDequeuedBufferCount = 0; + mFrameCount = 0; + mLastTimestamp = 0; + + res = native_window_set_buffer_count(mConsumer.get(), + mTotalBufferCount); + if (res != OK) { + ALOGE("%s: Unable to set buffer count for stream %d", + __FUNCTION__, mId); + return res; + } + + res = native_window_set_buffers_transform(mConsumer.get(), + mTransform); + if (res != OK) { + ALOGE("%s: Unable to configure stream transform to %x: %s (%d)", + __FUNCTION__, mTransform, strerror(-res), res); + } + + return OK; +} + +status_t Camera3OutputStream::disconnectLocked() { + status_t res; + + if ((res = Camera3IOStreamBase::disconnectLocked()) != OK) { + return res; + } + + res = native_window_api_disconnect(mConsumer.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)); + mState = STATE_ERROR; + return res; + } + + mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG + : STATE_CONSTRUCTED; + return OK; +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h new file mode 100644 index 0000000..774fbdd --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H +#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> + +#include "Camera3Stream.h" +#include "Camera3IOStreamBase.h" +#include "Camera3OutputStreamInterface.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single stream of output data from the camera device. + */ +class Camera3OutputStream : + public Camera3IOStreamBase, + public Camera3OutputStreamInterface { + public: + /** + * Set up a stream for formats that have 2 dimensions, such as RAW and YUV. + */ + Camera3OutputStream(int id, sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format); + + /** + * Set up a stream for formats that have a variable buffer size for the same + * dimensions, such as compressed JPEG. + */ + Camera3OutputStream(int id, sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, size_t maxSize, int format); + + virtual ~Camera3OutputStream(); + + /** + * Camera3Stream interface + */ + + virtual void dump(int fd, const Vector<String16> &args) const; + + /** + * Set the transform on the output stream; one of the + * HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants. + */ + status_t setTransform(int transform); + + protected: + Camera3OutputStream(int id, camera3_stream_type_t type, + uint32_t width, uint32_t height, int format); + + /** + * Note that we release the lock briefly in this function + */ + virtual status_t returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut); + + sp<ANativeWindow> mConsumer; + private: + int mTransform; + + virtual status_t setTransformLocked(int transform); + + /** + * Internal Camera3Stream interface + */ + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp); + + virtual status_t configureQueueLocked(); + virtual status_t disconnectLocked(); +}; // class Camera3OutputStream + +} // namespace camera3 + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h new file mode 100644 index 0000000..aae72cf --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_INTERFACE_H +#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_INTERFACE_H + +#include "Camera3StreamInterface.h" + +namespace android { + +namespace camera3 { + +/** + * An interface for managing a single stream of output data from the camera + * device. + */ +class Camera3OutputStreamInterface : public virtual Camera3StreamInterface { + public: + /** + * Set the transform on the output stream; one of the + * HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants. + */ + virtual status_t setTransform(int transform) = 0; +}; + +} // namespace camera3 + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp new file mode 100644 index 0000000..ab563df --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2013 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 "Camera3-Stream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3Stream.h" + +namespace android { + +namespace camera3 { + +Camera3Stream::~Camera3Stream() { +} + +Camera3Stream* Camera3Stream::cast(camera3_stream *stream) { + return static_cast<Camera3Stream*>(stream); +} + +const Camera3Stream* Camera3Stream::cast(const camera3_stream *stream) { + return static_cast<const Camera3Stream*>(stream); +} + +Camera3Stream::Camera3Stream(int id, + camera3_stream_type type, + uint32_t width, uint32_t height, size_t maxSize, int format) : + camera3_stream(), + mId(id), + mName(String8::format("Camera3Stream[%d]", id)), + mMaxSize(maxSize), + mState(STATE_CONSTRUCTED) { + + camera3_stream::stream_type = type; + camera3_stream::width = width; + camera3_stream::height = height; + camera3_stream::format = format; + camera3_stream::usage = 0; + camera3_stream::max_buffers = 0; + camera3_stream::priv = NULL; + + if (format == HAL_PIXEL_FORMAT_BLOB && maxSize == 0) { + ALOGE("%s: BLOB format with size == 0", __FUNCTION__); + mState = STATE_ERROR; + } +} + +int Camera3Stream::getId() const { + return mId; +} + +uint32_t Camera3Stream::getWidth() const { + return camera3_stream::width; +} + +uint32_t Camera3Stream::getHeight() const { + return camera3_stream::height; +} + +int Camera3Stream::getFormat() const { + return camera3_stream::format; +} + +camera3_stream* Camera3Stream::startConfiguration() { + Mutex::Autolock l(mLock); + + switch (mState) { + case STATE_ERROR: + ALOGE("%s: In error state", __FUNCTION__); + return NULL; + case STATE_CONSTRUCTED: + // OK + break; + case STATE_IN_CONFIG: + case STATE_IN_RECONFIG: + // Can start config again with no trouble; but don't redo + // oldUsage/oldMaxBuffers + return this; + case STATE_CONFIGURED: + if (stream_type == CAMERA3_STREAM_INPUT) { + ALOGE("%s: Cannot configure an input stream twice", + __FUNCTION__); + return NULL; + } else if (hasOutstandingBuffersLocked()) { + ALOGE("%s: Cannot configure stream; has outstanding buffers", + __FUNCTION__); + return NULL; + } + break; + default: + ALOGE("%s: Unknown state %d", __FUNCTION__, mState); + return NULL; + } + + oldUsage = usage; + oldMaxBuffers = max_buffers; + + if (mState == STATE_CONSTRUCTED) { + mState = STATE_IN_CONFIG; + } else { // mState == STATE_CONFIGURED + mState = STATE_IN_RECONFIG; + } + + return this; +} + +bool Camera3Stream::isConfiguring() const { + Mutex::Autolock l(mLock); + return (mState == STATE_IN_CONFIG) || (mState == STATE_IN_RECONFIG); +} + +status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) { + Mutex::Autolock l(mLock); + switch (mState) { + case STATE_ERROR: + ALOGE("%s: In error state", __FUNCTION__); + return INVALID_OPERATION; + case STATE_IN_CONFIG: + case STATE_IN_RECONFIG: + // OK + break; + case STATE_CONSTRUCTED: + case STATE_CONFIGURED: + ALOGE("%s: Cannot finish configuration that hasn't been started", + __FUNCTION__); + return INVALID_OPERATION; + default: + ALOGE("%s: Unknown state", __FUNCTION__); + return INVALID_OPERATION; + } + + // Check if the stream configuration is unchanged, and skip reallocation if + // so. As documented in hardware/camera3.h:configure_streams(). + if (mState == STATE_IN_RECONFIG && + oldUsage == usage && + oldMaxBuffers == max_buffers) { + mState = STATE_CONFIGURED; + return OK; + } + + status_t res; + res = configureQueueLocked(); + if (res != OK) { + ALOGE("%s: Unable to configure stream %d queue: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + mState = STATE_ERROR; + return res; + } + + res = registerBuffersLocked(hal3Device); + if (res != OK) { + ALOGE("%s: Unable to register stream buffers with HAL: %s (%d)", + __FUNCTION__, strerror(-res), res); + mState = STATE_ERROR; + return res; + } + + mState = STATE_CONFIGURED; + + return res; +} + +status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res = getBufferLocked(buffer); + if (res == OK) { + fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true); + } + + return res; +} + +status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res = returnBufferLocked(buffer, timestamp); + if (res == OK) { + fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/true); + } + + return res; +} + +status_t Camera3Stream::getInputBuffer(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res = getInputBufferLocked(buffer); + if (res == OK) { + fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/false); + } + + return res; +} + +status_t Camera3Stream::returnInputBuffer(const camera3_stream_buffer &buffer) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res = returnInputBufferLocked(buffer); + if (res == OK) { + fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/false); + } + return res; +} + +void Camera3Stream::fireBufferListenersLocked( + const camera3_stream_buffer& /*buffer*/, bool acquired, bool output) { + List<wp<Camera3StreamBufferListener> >::iterator it, end; + + // TODO: finish implementing + + Camera3StreamBufferListener::BufferInfo info = + Camera3StreamBufferListener::BufferInfo(); + info.mOutput = output; + // TODO: rest of fields + + for (it = mBufferListenerList.begin(), end = mBufferListenerList.end(); + it != end; + ++it) { + + sp<Camera3StreamBufferListener> listener = it->promote(); + if (listener != 0) { + if (acquired) { + listener->onBufferAcquired(info); + } else { + listener->onBufferReleased(info); + } + } + } +} + +bool Camera3Stream::hasOutstandingBuffers() const { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return hasOutstandingBuffersLocked(); +} + +status_t Camera3Stream::disconnect() { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + ALOGV("%s: Stream %d: Disconnecting...", __FUNCTION__, mId); + status_t res = disconnectLocked(); + + if (res == -ENOTCONN) { + // "Already disconnected" -- not an error + return OK; + } else { + return res; + } +} + +status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { + ATRACE_CALL(); + status_t res; + + size_t bufferCount = getBufferCountLocked(); + + Vector<buffer_handle_t*> buffers; + buffers.insertAt(NULL, 0, bufferCount); + + camera3_stream_buffer_set bufferSet = camera3_stream_buffer_set(); + bufferSet.stream = this; + bufferSet.num_buffers = bufferCount; + bufferSet.buffers = buffers.editArray(); + + Vector<camera3_stream_buffer_t> streamBuffers; + streamBuffers.insertAt(camera3_stream_buffer_t(), 0, bufferCount); + + // Register all buffers with the HAL. This means getting all the buffers + // from the stream, providing them to the HAL with the + // register_stream_buffers() method, and then returning them back to the + // stream in the error state, since they won't have valid data. + // + // Only registered buffers can be sent to the HAL. + + uint32_t bufferIdx = 0; + for (; bufferIdx < bufferCount; bufferIdx++) { + res = getBufferLocked( &streamBuffers.editItemAt(bufferIdx) ); + if (res != OK) { + ALOGE("%s: Unable to get buffer %d for registration with HAL", + __FUNCTION__, bufferIdx); + // Skip registering, go straight to cleanup + break; + } + + sp<Fence> fence = new Fence(streamBuffers[bufferIdx].acquire_fence); + fence->waitForever("Camera3Stream::registerBuffers"); + + buffers.editItemAt(bufferIdx) = streamBuffers[bufferIdx].buffer; + } + if (bufferIdx == bufferCount) { + // Got all buffers, register with HAL + ALOGV("%s: Registering %d buffers with camera HAL", + __FUNCTION__, bufferCount); + ATRACE_BEGIN("camera3->register_stream_buffers"); + res = hal3Device->ops->register_stream_buffers(hal3Device, + &bufferSet); + ATRACE_END(); + } + + // Return all valid buffers to stream, in ERROR state to indicate + // they weren't filled. + for (size_t i = 0; i < bufferIdx; i++) { + streamBuffers.editItemAt(i).release_fence = -1; + streamBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; + returnBufferLocked(streamBuffers[i], 0); + } + + return res; +} + +status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *) { + ALOGE("%s: This type of stream does not support output", __FUNCTION__); + return INVALID_OPERATION; +} +status_t Camera3Stream::returnBufferLocked(const camera3_stream_buffer &, + nsecs_t) { + ALOGE("%s: This type of stream does not support output", __FUNCTION__); + return INVALID_OPERATION; +} +status_t Camera3Stream::getInputBufferLocked(camera3_stream_buffer *) { + ALOGE("%s: This type of stream does not support input", __FUNCTION__); + return INVALID_OPERATION; +} +status_t Camera3Stream::returnInputBufferLocked( + const camera3_stream_buffer &) { + ALOGE("%s: This type of stream does not support input", __FUNCTION__); + return INVALID_OPERATION; +} + +void Camera3Stream::addBufferListener( + wp<Camera3StreamBufferListener> listener) { + Mutex::Autolock l(mLock); + mBufferListenerList.push_back(listener); +} + +void Camera3Stream::removeBufferListener( + const sp<Camera3StreamBufferListener>& listener) { + Mutex::Autolock l(mLock); + + bool erased = true; + List<wp<Camera3StreamBufferListener> >::iterator it, end; + for (it = mBufferListenerList.begin(), end = mBufferListenerList.end(); + it != end; + ) { + + if (*it == listener) { + it = mBufferListenerList.erase(it); + erased = true; + } else { + ++it; + } + } + + if (!erased) { + ALOGW("%s: Could not find listener to remove, already removed", + __FUNCTION__); + } +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h new file mode 100644 index 0000000..69d81e4 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_STREAM_H +#define ANDROID_SERVERS_CAMERA3_STREAM_H + +#include <gui/Surface.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/List.h> + +#include "hardware/camera3.h" + +#include "Camera3StreamBufferListener.h" +#include "Camera3StreamInterface.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single stream of input or output data from the camera + * device. + * + * The stream has an internal state machine to track whether it's + * connected/configured/etc. + * + * States: + * + * STATE_ERROR: A serious error has occurred, stream is unusable. Outstanding + * buffers may still be returned. + * + * STATE_CONSTRUCTED: The stream is ready for configuration, but buffers cannot + * be gotten yet. Not connected to any endpoint, no buffers are registered + * with the HAL. + * + * STATE_IN_CONFIG: Configuration has started, but not yet concluded. During this + * time, the usage, max_buffers, and priv fields of camera3_stream returned by + * startConfiguration() may be modified. + * + * STATE_IN_RE_CONFIG: Configuration has started, and the stream has been + * configured before. Need to track separately from IN_CONFIG to avoid + * re-registering buffers with HAL. + * + * STATE_CONFIGURED: Stream is configured, and has registered buffers with the + * HAL. The stream's getBuffer/returnBuffer work. The priv pointer may still be + * modified. + * + * Transition table: + * + * <none> => STATE_CONSTRUCTED: + * When constructed with valid arguments + * <none> => STATE_ERROR: + * When constructed with invalid arguments + * STATE_CONSTRUCTED => STATE_IN_CONFIG: + * When startConfiguration() is called + * STATE_IN_CONFIG => STATE_CONFIGURED: + * When finishConfiguration() is called + * STATE_IN_CONFIG => STATE_ERROR: + * When finishConfiguration() fails to allocate or register buffers. + * STATE_CONFIGURED => STATE_IN_RE_CONFIG: * + * When startConfiguration() is called again, after making sure stream is + * idle with waitUntilIdle(). + * STATE_IN_RE_CONFIG => STATE_CONFIGURED: + * When finishConfiguration() is called. + * STATE_IN_RE_CONFIG => STATE_ERROR: + * When finishConfiguration() fails to allocate or register buffers. + * STATE_CONFIGURED => STATE_CONSTRUCTED: + * When disconnect() is called after making sure stream is idle with + * waitUntilIdle(). + */ +class Camera3Stream : + protected camera3_stream, + public virtual Camera3StreamInterface, + public virtual RefBase { + public: + + virtual ~Camera3Stream(); + + static Camera3Stream* cast(camera3_stream *stream); + static const Camera3Stream* cast(const camera3_stream *stream); + + /** + * Get the stream's ID + */ + int getId() const; + + /** + * Get the stream's dimensions and format + */ + uint32_t getWidth() const; + uint32_t getHeight() const; + int getFormat() const; + + /** + * Start the stream configuration process. Returns a handle to the stream's + * information to be passed into the HAL device's configure_streams call. + * + * Until finishConfiguration() is called, no other methods on the stream may be + * called. The usage and max_buffers fields of camera3_stream may be modified + * between start/finishConfiguration, but may not be changed after that. + * The priv field of camera3_stream may be modified at any time after + * startConfiguration. + * + * Returns NULL in case of error starting configuration. + */ + camera3_stream* startConfiguration(); + + /** + * Check if the stream is mid-configuration (start has been called, but not + * finish). Used for lazy completion of configuration. + */ + bool isConfiguring() const; + + /** + * Completes the stream configuration process. During this call, the stream + * may call the device's register_stream_buffers() method. The stream + * information structure returned by startConfiguration() may no longer be + * modified after this call, but can still be read until the destruction of + * the stream. + * + * Returns: + * OK on a successful configuration + * NO_INIT in case of a serious error from the HAL device + * NO_MEMORY in case of an error registering buffers + * INVALID_OPERATION in case connecting to the consumer failed + */ + status_t finishConfiguration(camera3_device *hal3Device); + + /** + * Fill in the camera3_stream_buffer with the next valid buffer for this + * stream, to hand over to the HAL. + * + * This method may only be called once finishConfiguration has been called. + * For bidirectional streams, this method applies to the output-side + * buffers. + * + */ + status_t getBuffer(camera3_stream_buffer *buffer); + + /** + * Return a buffer to the stream after use by the HAL. + * + * This method may only be called for buffers provided by getBuffer(). + * For bidirectional streams, this method applies to the output-side buffers + */ + status_t returnBuffer(const camera3_stream_buffer &buffer, + nsecs_t timestamp); + + /** + * Fill in the camera3_stream_buffer with the next valid buffer for this + * stream, to hand over to the HAL. + * + * This method may only be called once finishConfiguration has been called. + * For bidirectional streams, this method applies to the input-side + * buffers. + * + */ + status_t getInputBuffer(camera3_stream_buffer *buffer); + + /** + * Return a buffer to the stream after use by the HAL. + * + * This method may only be called for buffers provided by getBuffer(). + * For bidirectional streams, this method applies to the input-side buffers + */ + status_t returnInputBuffer(const camera3_stream_buffer &buffer); + + /** + * Whether any of the stream's buffers are currently in use by the HAL, + * including buffers that have been returned but not yet had their + * release fence signaled. + */ + bool hasOutstandingBuffers() const; + + enum { + TIMEOUT_NEVER = -1 + }; + /** + * Wait until the HAL is done with all of this stream's buffers, including + * signalling all release fences. Returns TIMED_OUT if the timeout is exceeded, + * OK on success. Pass in TIMEOUT_NEVER for timeout to indicate an indefinite wait. + */ + virtual status_t waitUntilIdle(nsecs_t timeout) = 0; + + /** + * Disconnect stream from its non-HAL endpoint. After this, + * start/finishConfiguration must be called before the stream can be used + * again. This cannot be called if the stream has outstanding dequeued + * buffers. + */ + status_t disconnect(); + + /** + * Debug dump of the stream's state. + */ + virtual void dump(int fd, const Vector<String16> &args) const = 0; + + void addBufferListener( + wp<Camera3StreamBufferListener> listener); + void removeBufferListener( + const sp<Camera3StreamBufferListener>& listener); + + protected: + const int mId; + const String8 mName; + // Zero for formats with fixed buffer size for given dimensions. + const size_t mMaxSize; + + enum { + STATE_ERROR, + STATE_CONSTRUCTED, + STATE_IN_CONFIG, + STATE_IN_RECONFIG, + STATE_CONFIGURED + } mState; + + mutable Mutex mLock; + + Camera3Stream(int id, camera3_stream_type type, + uint32_t width, uint32_t height, size_t maxSize, int format); + + /** + * Interface to be implemented by derived classes + */ + + // getBuffer / returnBuffer implementations + + // Since camera3_stream_buffer includes a raw pointer to the stream, + // cast to camera3_stream*, implementations must increment the + // refcount of the stream manually in getBufferLocked, and decrement it in + // returnBufferLocked. + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer, + nsecs_t timestamp); + virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnInputBufferLocked( + const camera3_stream_buffer &buffer); + virtual bool hasOutstandingBuffersLocked() const = 0; + // Can return -ENOTCONN when we are already disconnected (not an error) + virtual status_t disconnectLocked() = 0; + + // Configure the buffer queue interface to the other end of the stream, + // after the HAL has provided usage and max_buffers values. After this call, + // the stream must be ready to produce all buffers for registration with + // HAL. + virtual status_t configureQueueLocked() = 0; + + // Get the total number of buffers in the queue + virtual size_t getBufferCountLocked() = 0; + + private: + uint32_t oldUsage; + uint32_t oldMaxBuffers; + + // Gets all buffers from endpoint and registers them with the HAL. + status_t registerBuffersLocked(camera3_device *hal3Device); + + void fireBufferListenersLocked(const camera3_stream_buffer& buffer, + bool acquired, bool output); + List<wp<Camera3StreamBufferListener> > mBufferListenerList; + +}; // class Camera3Stream + +}; // namespace camera3 + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h new file mode 100644 index 0000000..62ea6c0 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_STREAMBUFFERLISTENER_H +#define ANDROID_SERVERS_CAMERA3_STREAMBUFFERLISTENER_H + +#include <gui/Surface.h> +#include <utils/RefBase.h> + +namespace android { + +namespace camera3 { + +class Camera3StreamBufferListener : public virtual RefBase { +public: + + struct BufferInfo { + bool mOutput; // if false then input buffer + Rect mCrop; + uint32_t mTransform; + uint32_t mScalingMode; + int64_t mTimestamp; + uint64_t mFrameNumber; + }; + + // Buffer was acquired by the HAL + virtual void onBufferAcquired(const BufferInfo& bufferInfo) = 0; + // Buffer was released by the HAL + virtual void onBufferReleased(const BufferInfo& bufferInfo) = 0; +}; + +}; //namespace camera3 +}; //namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h new file mode 100644 index 0000000..4768536 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_STREAM_INTERFACE_H +#define ANDROID_SERVERS_CAMERA3_STREAM_INTERFACE_H + +#include <utils/RefBase.h> +#include "Camera3StreamBufferListener.h" + +struct camera3_stream_buffer; + +namespace android { + +namespace camera3 { + +/** + * An interface for managing a single stream of input and/or output data from + * the camera device. + */ +class Camera3StreamInterface : public virtual RefBase { + public: + /** + * Get the stream's ID + */ + virtual int getId() const = 0; + + /** + * Get the stream's dimensions and format + */ + virtual uint32_t getWidth() const = 0; + virtual uint32_t getHeight() const = 0; + virtual int getFormat() const = 0; + + /** + * Start the stream configuration process. Returns a handle to the stream's + * information to be passed into the HAL device's configure_streams call. + * + * Until finishConfiguration() is called, no other methods on the stream may + * be called. The usage and max_buffers fields of camera3_stream may be + * modified between start/finishConfiguration, but may not be changed after + * that. The priv field of camera3_stream may be modified at any time after + * startConfiguration. + * + * Returns NULL in case of error starting configuration. + */ + virtual camera3_stream* startConfiguration() = 0; + + /** + * Check if the stream is mid-configuration (start has been called, but not + * finish). Used for lazy completion of configuration. + */ + virtual bool isConfiguring() const = 0; + + /** + * Completes the stream configuration process. During this call, the stream + * may call the device's register_stream_buffers() method. The stream + * information structure returned by startConfiguration() may no longer be + * modified after this call, but can still be read until the destruction of + * the stream. + * + * Returns: + * OK on a successful configuration + * NO_INIT in case of a serious error from the HAL device + * NO_MEMORY in case of an error registering buffers + * INVALID_OPERATION in case connecting to the consumer failed + */ + virtual status_t finishConfiguration(camera3_device *hal3Device) = 0; + + /** + * Fill in the camera3_stream_buffer with the next valid buffer for this + * stream, to hand over to the HAL. + * + * This method may only be called once finishConfiguration has been called. + * For bidirectional streams, this method applies to the output-side + * buffers. + * + */ + virtual status_t getBuffer(camera3_stream_buffer *buffer) = 0; + + /** + * Return a buffer to the stream after use by the HAL. + * + * This method may only be called for buffers provided by getBuffer(). + * For bidirectional streams, this method applies to the output-side buffers + */ + virtual status_t returnBuffer(const camera3_stream_buffer &buffer, + nsecs_t timestamp) = 0; + + /** + * Fill in the camera3_stream_buffer with the next valid buffer for this + * stream, to hand over to the HAL. + * + * This method may only be called once finishConfiguration has been called. + * For bidirectional streams, this method applies to the input-side + * buffers. + * + */ + virtual status_t getInputBuffer(camera3_stream_buffer *buffer) = 0; + + /** + * Return a buffer to the stream after use by the HAL. + * + * This method may only be called for buffers provided by getBuffer(). + * For bidirectional streams, this method applies to the input-side buffers + */ + virtual status_t returnInputBuffer(const camera3_stream_buffer &buffer) = 0; + + /** + * Whether any of the stream's buffers are currently in use by the HAL, + * including buffers that have been returned but not yet had their + * release fence signaled. + */ + virtual bool hasOutstandingBuffers() const = 0; + + enum { + TIMEOUT_NEVER = -1 + }; + /** + * Wait until the HAL is done with all of this stream's buffers, including + * signalling all release fences. Returns TIMED_OUT if the timeout is + * exceeded, OK on success. Pass in TIMEOUT_NEVER for timeout to indicate + * an indefinite wait. + */ + virtual status_t waitUntilIdle(nsecs_t timeout) = 0; + + /** + * Disconnect stream from its non-HAL endpoint. After this, + * start/finishConfiguration must be called before the stream can be used + * again. This cannot be called if the stream has outstanding dequeued + * buffers. + */ + virtual status_t disconnect() = 0; + + /** + * Debug dump of the stream's state. + */ + virtual void dump(int fd, const Vector<String16> &args) const = 0; + + virtual void addBufferListener( + wp<Camera3StreamBufferListener> listener) = 0; + virtual void removeBufferListener( + const sp<Camera3StreamBufferListener>& listener) = 0; +}; + +} // namespace camera3 + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp new file mode 100644 index 0000000..8790c8c --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2013 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 "Camera3-ZslStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3ZslStream.h" + +typedef android::RingBufferConsumer::PinnedBufferItem PinnedBufferItem; + +namespace android { + +namespace camera3 { + +namespace { +struct TimestampFinder : public RingBufferConsumer::RingBufferComparator { + typedef RingBufferConsumer::BufferInfo BufferInfo; + + enum { + SELECT_I1 = -1, + SELECT_I2 = 1, + SELECT_NEITHER = 0, + }; + + TimestampFinder(nsecs_t timestamp) : mTimestamp(timestamp) {} + ~TimestampFinder() {} + + template <typename T> + static void swap(T& a, T& b) { + T tmp = a; + a = b; + b = tmp; + } + + /** + * Try to find the best candidate for a ZSL buffer. + * Match priority from best to worst: + * 1) Timestamps match. + * 2) Timestamp is closest to the needle (and lower). + * 3) Timestamp is closest to the needle (and higher). + * + */ + virtual int compare(const BufferInfo *i1, + const BufferInfo *i2) const { + // Try to select non-null object first. + if (i1 == NULL) { + return SELECT_I2; + } else if (i2 == NULL) { + return SELECT_I1; + } + + // Best result: timestamp is identical + if (i1->mTimestamp == mTimestamp) { + return SELECT_I1; + } else if (i2->mTimestamp == mTimestamp) { + return SELECT_I2; + } + + const BufferInfo* infoPtrs[2] = { + i1, + i2 + }; + int infoSelectors[2] = { + SELECT_I1, + SELECT_I2 + }; + + // Order i1,i2 so that always i1.timestamp < i2.timestamp + if (i1->mTimestamp > i2->mTimestamp) { + swap(infoPtrs[0], infoPtrs[1]); + swap(infoSelectors[0], infoSelectors[1]); + } + + // Second best: closest (lower) timestamp + if (infoPtrs[1]->mTimestamp < mTimestamp) { + return infoSelectors[1]; + } else if (infoPtrs[0]->mTimestamp < mTimestamp) { + return infoSelectors[0]; + } + + // Worst: closest (higher) timestamp + return infoSelectors[0]; + + /** + * The above cases should cover all the possibilities, + * and we get an 'empty' result only if the ring buffer + * was empty itself + */ + } + + const nsecs_t mTimestamp; +}; // struct TimestampFinder +} // namespace anonymous + +Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height, + int depth) : + Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL, + width, height, + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), + mDepth(depth), + mProducer(new RingBufferConsumer(GRALLOC_USAGE_HW_CAMERA_ZSL, + depth)) { + + mConsumer = new Surface(mProducer->getProducerInterface()); +} + +Camera3ZslStream::~Camera3ZslStream() { +} + +status_t Camera3ZslStream::getInputBufferLocked(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + + status_t res; + + // TODO: potentially register from inputBufferLocked + // this should be ok, registerBuffersLocked only calls getBuffer for now + // register in output mode instead of input mode for ZSL streams. + if (mState == STATE_IN_CONFIG || mState == STATE_IN_RECONFIG) { + ALOGE("%s: Stream %d: Buffer registration for input streams" + " not implemented (state %d)", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + if ((res = getBufferPreconditionCheckLocked()) != OK) { + return res; + } + + ANativeWindowBuffer* anb; + int fenceFd; + + assert(mProducer != 0); + + sp<PinnedBufferItem> bufferItem; + { + List<sp<RingBufferConsumer::PinnedBufferItem> >::iterator it, end; + it = mInputBufferQueue.begin(); + end = mInputBufferQueue.end(); + + // Need to call enqueueInputBufferByTimestamp as a prerequisite + if (it == end) { + ALOGE("%s: Stream %d: No input buffer was queued", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + bufferItem = *it; + mInputBufferQueue.erase(it); + } + + anb = bufferItem->getBufferItem().mGraphicBuffer->getNativeBuffer(); + assert(anb != NULL); + fenceFd = bufferItem->getBufferItem().mFence->dup(); + + /** + * FenceFD now owned by HAL except in case of error, + * in which case we reassign it to acquire_fence + */ + handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, + /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK); + + mBuffersInFlight.push_back(bufferItem); + + return OK; +} + +status_t Camera3ZslStream::returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut) { + + if (output) { + // Output stream path + return Camera3OutputStream::returnBufferCheckedLocked(buffer, + timestamp, + output, + releaseFenceOut); + } + + /** + * Input stream path + */ + bool bufferFound = false; + sp<PinnedBufferItem> bufferItem; + { + // Find the buffer we are returning + Vector<sp<PinnedBufferItem> >::iterator it, end; + for (it = mBuffersInFlight.begin(), end = mBuffersInFlight.end(); + it != end; + ++it) { + + const sp<PinnedBufferItem>& tmp = *it; + ANativeWindowBuffer *anb = + tmp->getBufferItem().mGraphicBuffer->getNativeBuffer(); + if (anb != NULL && &(anb->handle) == buffer.buffer) { + bufferFound = true; + bufferItem = tmp; + mBuffersInFlight.erase(it); + break; + } + } + } + if (!bufferFound) { + ALOGE("%s: Stream %d: Can't return buffer that wasn't sent to HAL", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + + int releaseFenceFd = buffer.release_fence; + + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { + if (buffer.release_fence != -1) { + ALOGE("%s: Stream %d: HAL should not set release_fence(%d) when " + "there is an error", __FUNCTION__, mId, buffer.release_fence); + close(buffer.release_fence); + } + + /** + * Reassign release fence as the acquire fence incase of error + */ + releaseFenceFd = buffer.acquire_fence; + } + + /** + * Unconditionally return buffer to the buffer queue. + * - Fwk takes over the release_fence ownership + */ + sp<Fence> releaseFence = new Fence(releaseFenceFd); + bufferItem->getBufferItem().mFence = releaseFence; + bufferItem.clear(); // dropping last reference unpins buffer + + *releaseFenceOut = releaseFence; + + return OK; +} + +status_t Camera3ZslStream::returnInputBufferLocked( + const camera3_stream_buffer &buffer) { + ATRACE_CALL(); + + status_t res = returnAnyBufferLocked(buffer, /*timestamp*/0, + /*output*/false); + + return res; +} + +void Camera3ZslStream::dump(int fd, const Vector<String16> &args) const { + (void) args; + + String8 lines; + lines.appendFormat(" Stream[%d]: ZSL\n", mId); + write(fd, lines.string(), lines.size()); + + Camera3IOStreamBase::dump(fd, args); + + lines = String8(); + lines.appendFormat(" Input buffers pending: %d, in flight %d\n", + mInputBufferQueue.size(), mBuffersInFlight.size()); + write(fd, lines.string(), lines.size()); +} + +status_t Camera3ZslStream::enqueueInputBufferByTimestamp( + nsecs_t timestamp, + nsecs_t* actualTimestamp) { + + Mutex::Autolock l(mLock); + + TimestampFinder timestampFinder = TimestampFinder(timestamp); + + sp<RingBufferConsumer::PinnedBufferItem> pinnedBuffer = + mProducer->pinSelectedBuffer(timestampFinder, + /*waitForFence*/false); + + if (pinnedBuffer == 0) { + ALOGE("%s: No ZSL buffers were available yet", __FUNCTION__); + return NO_BUFFER_AVAILABLE; + } + + nsecs_t actual = pinnedBuffer->getBufferItem().mTimestamp; + + if (actual != timestamp) { + ALOGW("%s: ZSL buffer candidate search didn't find an exact match --" + " requested timestamp = %lld, actual timestamp = %lld", + __FUNCTION__, timestamp, actual); + } + + mInputBufferQueue.push_back(pinnedBuffer); + + if (actualTimestamp != NULL) { + *actualTimestamp = actual; + } + + return OK; +} + +status_t Camera3ZslStream::clearInputRingBuffer() { + Mutex::Autolock l(mLock); + + mInputBufferQueue.clear(); + + return mProducer->clear(); +} + +status_t Camera3ZslStream::setTransform(int /*transform*/) { + ALOGV("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.h b/services/camera/libcameraservice/device3/Camera3ZslStream.h new file mode 100644 index 0000000..c7f4490 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_ZSL_STREAM_H +#define ANDROID_SERVERS_CAMERA3_ZSL_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> +#include <gui/RingBufferConsumer.h> + +#include "Camera3OutputStream.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single opaque ZSL stream to/from the camera device. + * This acts as a bidirectional stream at the HAL layer, caching and discarding + * most output buffers, and when directed, pushes a buffer back to the HAL for + * processing. + */ +class Camera3ZslStream : + public Camera3OutputStream { + public: + /** + * Set up a ZSL stream of a given resolution. Depth is the number of buffers + * cached within the stream that can be retrieved for input. + */ + Camera3ZslStream(int id, uint32_t width, uint32_t height, int depth); + ~Camera3ZslStream(); + + virtual void dump(int fd, const Vector<String16> &args) const; + + enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; + + /** + * Locate a buffer matching this timestamp in the RingBufferConsumer, + * and mark it to be queued at the next getInputBufferLocked invocation. + * + * Errors: Returns NO_BUFFER_AVAILABLE if we could not find a match. + * + */ + status_t enqueueInputBufferByTimestamp(nsecs_t timestamp, + nsecs_t* actualTimestamp); + + /** + * Clears the buffers that can be used by enqueueInputBufferByTimestamp + */ + status_t clearInputRingBuffer(); + + protected: + + /** + * Camera3OutputStreamInterface implementation + */ + status_t setTransform(int transform); + + private: + + int mDepth; + // Input buffers pending to be queued into HAL + List<sp<RingBufferConsumer::PinnedBufferItem> > mInputBufferQueue; + sp<RingBufferConsumer> mProducer; + + // Input buffers in flight to HAL + Vector<sp<RingBufferConsumer::PinnedBufferItem> > mBuffersInFlight; + + /** + * Camera3Stream interface + */ + + // getInputBuffer/returnInputBuffer operate the input stream side of the + // ZslStream. + virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnInputBufferLocked( + const camera3_stream_buffer &buffer); + + // Actual body to return either input or output buffers + virtual status_t returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut); +}; // class Camera3ZslStream + +}; // namespace camera3 + +}; // namespace android + +#endif |