From 4d44cad22ea925a651463f2d51d6586c14d4b787 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Sat, 11 Apr 2015 13:15:45 -0700 Subject: Camera2: Add prepare for output streams The prepare call asynchronously pre-allocates buffers for a given output stream, and then fires the onPrepared callback. Not implemented for Camera2Device or used in Camera2Client. Change-Id: I1cccdfff846dd6985133c591dbdceed823929ade --- .../libcameraservice/api2/CameraDeviceClient.cpp | 44 +++++ .../libcameraservice/api2/CameraDeviceClient.h | 4 + .../libcameraservice/common/Camera2ClientBase.cpp | 8 + .../libcameraservice/common/Camera2ClientBase.h | 2 +- .../libcameraservice/common/CameraDeviceBase.h | 7 + .../libcameraservice/device2/Camera2Device.cpp | 6 + .../libcameraservice/device2/Camera2Device.h | 3 + .../libcameraservice/device3/Camera3Device.cpp | 200 ++++++++++++++++++++- .../libcameraservice/device3/Camera3Device.h | 53 +++++- .../device3/Camera3DummyStream.cpp | 2 +- .../libcameraservice/device3/Camera3DummyStream.h | 2 +- .../device3/Camera3IOStreamBase.cpp | 31 ++-- .../libcameraservice/device3/Camera3IOStreamBase.h | 2 +- .../device3/Camera3InputStream.cpp | 2 +- .../libcameraservice/device3/Camera3InputStream.h | 2 +- .../device3/Camera3OutputStream.cpp | 9 +- .../libcameraservice/device3/Camera3OutputStream.h | 2 +- .../libcameraservice/device3/Camera3Stream.cpp | 132 +++++++++++++- .../libcameraservice/device3/Camera3Stream.h | 105 ++++++++++- .../device3/Camera3StreamInterface.h | 62 +++++++ 20 files changed, 641 insertions(+), 37 deletions(-) (limited to 'services') diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 0016174..bf1692d 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -671,6 +671,42 @@ status_t CameraDeviceClient::flush(int64_t* lastFrameNumber) { return mDevice->flush(lastFrameNumber); } +status_t CameraDeviceClient::prepare(int streamId) { + ATRACE_CALL(); + ALOGV("%s", __FUNCTION__); + + status_t res = OK; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + Mutex::Autolock icl(mBinderSerializationLock); + + // Guard against trying to prepare non-created streams + ssize_t index = NAME_NOT_FOUND; + for (size_t i = 0; i < mStreamMap.size(); ++i) { + if (streamId == mStreamMap.valueAt(i)) { + index = i; + break; + } + } + + if (index == NAME_NOT_FOUND) { + ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream " + "created yet", __FUNCTION__, mCameraId, streamId); + return BAD_VALUE; + } + + // Also returns BAD_VALUE if stream ID was not valid + res = mDevice->prepare(streamId); + + if (res == BAD_VALUE) { + ALOGE("%s: Camera %d: Unexpected BAD_VALUE when preparing stream, but we" + " already checked and the stream ID (%d) should be valid.", + __FUNCTION__, mCameraId, streamId); + } + + return res; +} + status_t CameraDeviceClient::dump(int fd, const Vector& args) { String8 result; result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n", @@ -730,6 +766,14 @@ void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras, } } +void CameraDeviceClient::notifyPrepared(int streamId) { + // Thread safe. Don't bother locking. + sp remoteCb = getRemoteCallback(); + if (remoteCb != 0) { + remoteCb->onPrepared(streamId); + } +} + void CameraDeviceClient::detachDevice() { if (mDevice == 0) return; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index f2d8899..b8d8bea 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -109,6 +109,9 @@ public: virtual status_t flush(/*out*/ int64_t* lastFrameNumber = NULL); + // Prepare stream by preallocating its buffers + virtual status_t prepare(int streamId); + /** * Interface used by CameraService */ @@ -135,6 +138,7 @@ public: virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp); + virtual void notifyPrepared(int streamId); /** * Interface used by independent components of CameraDeviceClient. diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index c0c2314..ba0b264 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -280,6 +280,14 @@ void Camera2ClientBase::notifyAutoWhitebalance(uint8_t newState, } template +void Camera2ClientBase::notifyPrepared(int streamId) { + (void)streamId; + + ALOGV("%s: Stream %d now prepared", + __FUNCTION__, streamId); +} + +template int Camera2ClientBase::getCameraId() const { return TClientBase::mCameraId; } diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index 168ea0a..f1cacdf 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -72,7 +72,7 @@ public: virtual void notifyAutoExposure(uint8_t newState, int triggerId); virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId); - + virtual void notifyPrepared(int streamId); int getCameraId() const; const sp& diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index 6ece359..f02fc32 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -199,6 +199,7 @@ class CameraDeviceBase : public virtual RefBase { virtual void notifyIdle() = 0; virtual void notifyShutter(const CaptureResultExtras &resultExtras, nsecs_t timestamp) = 0; + virtual void notifyPrepared(int streamId) = 0; // Required only for API1 virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0; @@ -281,6 +282,12 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t flush(int64_t *lastFrameNumber = NULL) = 0; /** + * Prepare stream by preallocating buffers for it asynchronously. + * Calls notifyPrepared() once allocation is complete. + */ + virtual status_t prepare(int streamId) = 0; + + /** * Get the HAL device version. */ virtual uint32_t getDeviceVersion() = 0; diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index 3c5ea9d..f6645f3 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -618,6 +618,12 @@ status_t Camera2Device::flush(int64_t* /*lastFrameNumber*/) { return waitUntilDrained(); } +status_t Camera2Device::prepare(int streamId) { + ATRACE_CALL(); + ALOGE("%s: Camera %d: unimplemented", __FUNCTION__, mId); + return NO_INIT; +} + uint32_t Camera2Device::getDeviceVersion() { ATRACE_CALL(); return mDeviceVersion; diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index 9972606..fd1240a 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -84,6 +84,9 @@ class Camera2Device: public CameraDeviceBase { buffer_handle_t *buffer, wp listener); // Flush implemented as just a wait virtual status_t flush(int64_t *lastFrameNumber = NULL); + // Prepare is a no-op + virtual status_t prepare(int streamId); + virtual uint32_t getDeviceVersion(); virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index dc752a6..ec9c70c 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -175,6 +175,8 @@ status_t Camera3Device::initialize(CameraModule *module) return res; } + mPreparerThread = new PreparerThread(); + /** Everything is good to go */ mDeviceVersion = device->common.version; @@ -1190,7 +1192,8 @@ status_t Camera3Device::setNotifyCallback(NotificationListener *listener) { ALOGW("%s: Replacing old callback listener", __FUNCTION__); } mListener = listener; - mRequestThread->setNotifyCallback(listener); + mRequestThread->setNotificationListener(listener); + mPreparerThread->setNotificationListener(listener); return OK; } @@ -1336,6 +1339,34 @@ status_t Camera3Device::flush(int64_t *frameNumber) { return res; } +status_t Camera3Device::prepare(int streamId) { + ATRACE_CALL(); + ALOGV("%s: Camera %d: Preparing stream %d", __FUNCTION__, mId, streamId); + + sp stream; + ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId); + if (outputStreamIdx == NAME_NOT_FOUND) { + CLOGE("Stream %d does not exist", streamId); + return BAD_VALUE; + } + + stream = mOutputStreams.editValueAt(outputStreamIdx); + + if (stream->isUnpreparable() || stream->hasOutstandingBuffers() ) { + ALOGE("%s: Camera %d: Stream %d has already been a request target", + __FUNCTION__, mId, streamId); + return BAD_VALUE; + } + + if (mRequestThread->isStreamPending(stream)) { + ALOGE("%s: Camera %d: Stream %d is already a target in a pending request", + __FUNCTION__, mId, streamId); + return BAD_VALUE; + } + + return mPreparerThread->prepare(stream); +} + uint32_t Camera3Device::getDeviceVersion() { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); @@ -1409,6 +1440,11 @@ sp Camera3Device::createCaptureRequest( return NULL; } } + // Check if stream is being prepared + if (mInputStream->isPreparing()) { + CLOGE("Request references an input stream that's being prepared!"); + return NULL; + } newRequest->mInputStream = mInputStream; newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS); @@ -1441,6 +1477,11 @@ sp Camera3Device::createCaptureRequest( return NULL; } } + // Check if stream is being prepared + if (stream->isPreparing()) { + CLOGE("Request references an output stream that's being prepared!"); + return NULL; + } newRequest->mOutputStreams.push(stream); } @@ -1916,7 +1957,6 @@ bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag, return true; } - void Camera3Device::returnOutputBuffers( const camera3_stream_buffer_t *outputBuffers, size_t numBuffers, nsecs_t timestamp) { @@ -2416,7 +2456,7 @@ CameraMetadata Camera3Device::getLatestRequestLocked() { Camera3Device::RequestThread::RequestThread(wp parent, sp statusTracker, camera3_device_t *hal3Device) : - Thread(false), + Thread(/*canCallJava*/false), mParent(parent), mStatusTracker(statusTracker), mHal3Device(hal3Device), @@ -2432,7 +2472,7 @@ Camera3Device::RequestThread::RequestThread(wp parent, mStatusId = statusTracker->addComponent(); } -void Camera3Device::RequestThread::setNotifyCallback( +void Camera3Device::RequestThread::setNotificationListener( NotificationListener *listener) { Mutex::Autolock l(mRequestLock); mListener = listener; @@ -2846,6 +2886,26 @@ CameraMetadata Camera3Device::RequestThread::getLatestRequest() const { return mLatestRequest; } +bool Camera3Device::RequestThread::isStreamPending( + sp& stream) { + Mutex::Autolock l(mRequestLock); + + for (const auto& request : mRequestQueue) { + for (const auto& s : request->mOutputStreams) { + if (stream == s) return true; + } + if (stream == request->mInputStream) return true; + } + + for (const auto& request : mRepeatingRequests) { + for (const auto& s : request->mOutputStreams) { + if (stream == s) return true; + } + if (stream == request->mInputStream) return true; + } + + return false; +} void Camera3Device::RequestThread::cleanUpFailedRequest( camera3_capture_request_t &request, @@ -3193,6 +3253,138 @@ status_t Camera3Device::RequestThread::addDummyTriggerIds( return OK; } +/** + * PreparerThread inner class methods + */ + +Camera3Device::PreparerThread::PreparerThread() : + Thread(/*canCallJava*/false), mActive(false), mCancelNow(false) { +} + +Camera3Device::PreparerThread::~PreparerThread() { + Thread::requestExitAndWait(); + if (mCurrentStream != nullptr) { + mCurrentStream->cancelPrepare(); + ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId()); + mCurrentStream.clear(); + } + clear(); +} + +status_t Camera3Device::PreparerThread::prepare(sp& stream) { + status_t res; + + Mutex::Autolock l(mLock); + + res = stream->startPrepare(); + if (res == OK) { + // No preparation needed, fire listener right off + ALOGV("%s: Stream %d already prepared", __FUNCTION__, stream->getId()); + if (mListener) { + mListener->notifyPrepared(stream->getId()); + } + return OK; + } else if (res != NOT_ENOUGH_DATA) { + return res; + } + + // Need to prepare, start up thread if necessary + if (!mActive) { + // mRunning will change to false before the thread fully shuts down, so wait to be sure it + // isn't running + Thread::requestExitAndWait(); + res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND); + if (res != OK) { + ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__, res, strerror(-res)); + if (mListener) { + mListener->notifyPrepared(stream->getId()); + } + return res; + } + mCancelNow = false; + mActive = true; + ALOGV("%s: Preparer stream started", __FUNCTION__); + } + + // queue up the work + mPendingStreams.push_back(stream); + ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId()); + + return OK; +} + +status_t Camera3Device::PreparerThread::clear() { + status_t res; + + Mutex::Autolock l(mLock); + + for (const auto& stream : mPendingStreams) { + stream->cancelPrepare(); + } + mPendingStreams.clear(); + mCancelNow = true; + + return OK; +} + +void Camera3Device::PreparerThread::setNotificationListener(NotificationListener *listener) { + Mutex::Autolock l(mLock); + mListener = listener; +} + +bool Camera3Device::PreparerThread::threadLoop() { + status_t res; + { + Mutex::Autolock l(mLock); + if (mCurrentStream == nullptr) { + // End thread if done with work + if (mPendingStreams.empty()) { + ALOGV("%s: Preparer stream out of work", __FUNCTION__); + // threadLoop _must not_ re-acquire mLock after it sets mActive to false; would + // cause deadlock with prepare()'s requestExitAndWait triggered by !mActive. + mActive = false; + return false; + } + + // Get next stream to prepare + auto it = mPendingStreams.begin(); + mCurrentStream = *it; + mPendingStreams.erase(it); + ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId()); + ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId()); + } else if (mCancelNow) { + mCurrentStream->cancelPrepare(); + ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId()); + ALOGV("%s: Cancelling stream %d prepare", __FUNCTION__, mCurrentStream->getId()); + mCurrentStream.clear(); + mCancelNow = false; + return true; + } + } + + res = mCurrentStream->prepareNextBuffer(); + if (res == NOT_ENOUGH_DATA) return true; + if (res != OK) { + // Something bad happened; try to recover by cancelling prepare and + // signalling listener anyway + ALOGE("%s: Stream %d returned error %d (%s) during prepare", __FUNCTION__, + mCurrentStream->getId(), res, strerror(-res)); + mCurrentStream->cancelPrepare(); + } + + // This stream has finished, notify listener + Mutex::Autolock l(mLock); + if (mListener) { + ALOGV("%s: Stream %d prepare done, signaling listener", __FUNCTION__, + mCurrentStream->getId()); + mListener->notifyPrepared(mCurrentStream->getId()); + } + + ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId()); + mCurrentStream.clear(); + + return true; +} /** * Static callback forwarding methods from HAL to instance diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index b08ba81..4fbcb2e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -138,6 +138,8 @@ class Camera3Device : virtual status_t flush(int64_t *lastFrameNumber = NULL); + virtual status_t prepare(int streamId); + virtual uint32_t getDeviceVersion(); virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; @@ -374,7 +376,7 @@ class Camera3Device : sp statusTracker, camera3_device_t *hal3Device); - void setNotifyCallback(NotificationListener *listener); + void setNotificationListener(NotificationListener *listener); /** * Call after stream (re)-configuration is completed. @@ -438,6 +440,12 @@ class Camera3Device : */ CameraMetadata getLatestRequest() const; + /** + * Returns true if the stream is a target of any queued or repeating + * capture request + */ + bool isStreamPending(sp& stream); + protected: virtual bool threadLoop(); @@ -559,7 +567,6 @@ class Camera3Device : Vector pendingOutputBuffers; - // Fields used by the partial result only struct PartialResultInFlight { // Set by process_capture_result once 3A has been sent to clients @@ -610,7 +617,8 @@ class Camera3Device : resultExtras(extras), hasInputBuffer(hasInput){ } -}; + }; + // Map from frame number to the in-flight request state typedef KeyedVector InFlightMap; @@ -642,6 +650,45 @@ class Camera3Device : sp mStatusTracker; /** + * Thread for preparing streams + */ + class PreparerThread : private Thread, public virtual RefBase { + public: + PreparerThread(); + ~PreparerThread(); + + void setNotificationListener(NotificationListener *listener); + + /** + * Queue up a stream to be prepared. Streams are processed by + * a background thread in FIFO order + */ + status_t prepare(sp& stream); + + /** + * Cancel all current and pending stream preparation + */ + status_t clear(); + + private: + Mutex mLock; + + virtual bool threadLoop(); + + // Guarded by mLock + + NotificationListener *mListener; + List > mPendingStreams; + bool mActive; + bool mCancelNow; + + // Only accessed by threadLoop and the destructor + + sp mCurrentStream; + }; + sp mPreparerThread; + + /** * Output result queue and current HAL device 3A state */ diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp index 01edfff..ecb8ac8 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp @@ -87,7 +87,7 @@ status_t Camera3DummyStream::disconnectLocked() { return OK; } -status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) { +status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) const { *usage = DUMMY_USAGE; return OK; } diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h index d023c57..3a3dbf4 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.h +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h @@ -89,7 +89,7 @@ class Camera3DummyStream : virtual status_t configureQueueLocked(); - virtual status_t getEndpointUsage(uint32_t *usage); + virtual status_t getEndpointUsage(uint32_t *usage) const; }; // class Camera3DummyStream diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp index 8696413..23b1c45 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -67,13 +67,18 @@ bool Camera3IOStreamBase::hasOutstandingBuffersLocked() const { void Camera3IOStreamBase::dump(int fd, const Vector &args) const { (void) args; String8 lines; + + uint32_t consumerUsage = 0; + status_t res = getEndpointUsage(&consumerUsage); + if (res != OK) consumerUsage = 0; + lines.appendFormat(" State: %d\n", mState); - lines.appendFormat(" Dims: %d x %d, format 0x%x\n", + lines.appendFormat(" Dims: %d x %d, format 0x%x, dataspace 0x%x\n", camera3_stream::width, camera3_stream::height, - camera3_stream::format); + camera3_stream::format, camera3_stream::data_space); lines.appendFormat(" Max size: %zu\n", mMaxSize); - lines.appendFormat(" Usage: %d, max HAL buffers: %d\n", - camera3_stream::usage, camera3_stream::max_buffers); + lines.appendFormat(" Combined usage: %d, max HAL buffers: %d\n", + camera3_stream::usage | consumerUsage, camera3_stream::max_buffers); lines.appendFormat(" Frames produced: %d, last timestamp: %" PRId64 " ns\n", mFrameCount, mLastTimestamp); lines.appendFormat(" Total buffers: %zu, currently dequeued: %zu\n", @@ -156,13 +161,11 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer, // Inform tracker about becoming busy if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG && - mState != STATE_IN_RECONFIG) { + mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) { /** * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers * before/after register_stream_buffers during initial configuration - * or re-configuration. - * - * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for statusTracker = mStatusTracker.promote(); if (statusTracker != 0) { @@ -177,9 +180,11 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer, } status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const { - // Allow dequeue during IN_[RE]CONFIG for registration + // Allow dequeue during IN_[RE]CONFIG for registration, in + // PREPARING for pre-allocation if (mState != STATE_CONFIGURED && - mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { + mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG && + mState != STATE_PREPARING) { ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d", __FUNCTION__, mId, mState); return INVALID_OPERATION; @@ -240,13 +245,11 @@ status_t Camera3IOStreamBase::returnAnyBufferLocked( mHandoutTotalBufferCount--; if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG && - mState != STATE_IN_RECONFIG) { + mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) { /** * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers * before/after register_stream_buffers during initial configuration - * or re-configuration. - * - * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for 0) { + ALOGE("%s: Stream %d: Can't prepare stream that has outstanding buffers", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + + if (mPrepared) return OK; + + size_t bufferCount = getBufferCountLocked(); + + mPreparedBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount); + mPreparedBufferIdx = 0; + + mState = STATE_PREPARING; + + return NOT_ENOUGH_DATA; +} + +bool Camera3Stream::isPreparing() const { + Mutex::Autolock l(mLock); + return mState == STATE_PREPARING; +} + +status_t Camera3Stream::prepareNextBuffer() { + ATRACE_CALL(); + + Mutex::Autolock l(mLock); + status_t res = OK; + + // This function should be only called when the stream is preparing + if (mState != STATE_PREPARING) { + ALOGE("%s: Stream %d: Can't prepare buffer if stream is not in PREPARING " + "state %d", __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Get next buffer - this may allocate, and take a while for large buffers + res = getBufferLocked( &mPreparedBuffers.editItemAt(mPreparedBufferIdx) ); + if (res != OK) { + ALOGE("%s: Stream %d: Unable to allocate buffer %d during preparation", + __FUNCTION__, mId, mPreparedBufferIdx); + return NO_INIT; + } + + mPreparedBufferIdx++; + + // Check if we still have buffers left to allocate + if (mPreparedBufferIdx < mPreparedBuffers.size()) { + return NOT_ENOUGH_DATA; + } + + // Done with prepare - mark stream as such, and return all buffers + // via cancelPrepare + mPrepared = true; + + return cancelPrepareLocked(); +} + +status_t Camera3Stream::cancelPrepare() { + ATRACE_CALL(); + + Mutex::Autolock l(mLock); + + return cancelPrepareLocked(); +} + +status_t Camera3Stream::cancelPrepareLocked() { + status_t res = OK; + + // This function should be only called when the stream is mid-preparing. + if (mState != STATE_PREPARING) { + ALOGE("%s: Stream %d: Can't cancel prepare stream if stream is not in " + "PREPARING state %d", __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Return all valid buffers to stream, in ERROR state to indicate + // they weren't filled. + for (size_t i = 0; i < mPreparedBufferIdx; i++) { + mPreparedBuffers.editItemAt(i).release_fence = -1; + mPreparedBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; + returnBufferLocked(mPreparedBuffers[i], 0); + } + mPreparedBuffers.clear(); + mPreparedBufferIdx = 0; + + mState = STATE_CONFIGURED; + + return res; +} + status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) { ATRACE_CALL(); Mutex::Autolock l(mLock); @@ -427,15 +551,13 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; " "must be set to NULL in camera3_device::ops", __FUNCTION__); return INVALID_OPERATION; - } else { - ALOGD("%s: Skipping NULL check for deprecated register_stream_buffers", __FUNCTION__); } return OK; - } else { - ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__); } + ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__); + status_t res; size_t bufferCount = getBufferCountLocked(); @@ -491,6 +613,8 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { returnBufferLocked(streamBuffers[i], 0); } + mPrepared = true; + return res; } diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index e89361e..0543c66 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -57,8 +57,15 @@ namespace camera3 { * 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. + * HAL (if necessary). The stream's getBuffer/returnBuffer work. The priv + * pointer may still be modified. + * + * STATE_PREPARING: The stream's buffers are being pre-allocated for use. On + * older HALs, this is done as part of configuration, but in newer HALs + * buffers may be allocated at time of first use. But some use cases require + * buffer allocation upfront, to minmize disruption due to lengthy allocation + * duration. In this state, only prepareNextBuffer() and cancelPrepare() + * may be called. * * Transition table: * @@ -82,6 +89,12 @@ namespace camera3 { * STATE_CONFIGURED => STATE_CONSTRUCTED: * When disconnect() is called after making sure stream is idle with * waitUntilIdle(). + * STATE_CONFIGURED => STATE_PREPARING: + * When startPrepare is called before the stream has a buffer + * queued back into it for the first time. + * STATE_PREPARING => STATE_CONFIGURED: + * When sufficient prepareNextBuffer calls have been made to allocate + * all stream buffers, or cancelPrepare is called. * * Status Tracking: * Each stream is tracked by StatusTracker as a separate component, @@ -167,6 +180,73 @@ class Camera3Stream : status_t cancelConfiguration(); /** + * Determine whether the stream has already become in-use (has received + * a valid filled buffer), which determines if a stream can still have + * prepareNextBuffer called on it. + */ + bool isUnpreparable(); + + /** + * Start stream preparation. May only be called in the CONFIGURED state, + * when no valid buffers have yet been returned to this stream. + * + * If no prepartion is necessary, returns OK and does not transition to + * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions + * to PREPARING. + * + * This call performs no allocation, so is quick to call. + * + * Returns: + * OK if no more buffers need to be preallocated + * NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish + * buffer pre-allocation, and transitions to the PREPARING state. + * NO_INIT in case of a serious error from the HAL device + * INVALID_OPERATION if called when not in CONFIGURED state, or a + * valid buffer has already been returned to this stream. + */ + status_t startPrepare(); + + /** + * Check if the stream is mid-preparing. + */ + bool isPreparing() const; + + /** + * Continue stream buffer preparation by allocating the next + * buffer for this stream. May only be called in the PREPARED state. + * + * Returns OK and transitions to the CONFIGURED state if all buffers + * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA. + * + * This call allocates one buffer, which may take several milliseconds for + * large buffers. + * + * Returns: + * OK if no more buffers need to be preallocated, and transitions + * to the CONFIGURED state. + * NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish + * buffer pre-allocation. + * NO_INIT in case of a serious error from the HAL device + * INVALID_OPERATION if called when not in CONFIGURED state, or a + * valid buffer has already been returned to this stream. + */ + status_t prepareNextBuffer(); + + /** + * Cancel stream preparation early. In case allocation needs to be + * stopped, this method transitions the stream back to the CONFIGURED state. + * Buffers that have been allocated with prepareNextBuffer remain that way, + * but a later use of prepareNextBuffer will require just as many + * calls as if the earlier prepare attempt had not existed. + * + * Returns: + * OK if cancellation succeeded, and transitions to the CONFIGURED state + * INVALID_OPERATION if not in the PREPARING state + * NO_INIT in case of a serious error from the HAL device + */ + status_t cancelPrepare(); + + /** * Fill in the camera3_stream_buffer with the next valid buffer for this * stream, to hand over to the HAL. * @@ -263,7 +343,8 @@ class Camera3Stream : STATE_CONSTRUCTED, STATE_IN_CONFIG, STATE_IN_RECONFIG, - STATE_CONFIGURED + STATE_CONFIGURED, + STATE_PREPARING } mState; mutable Mutex mLock; @@ -312,13 +393,17 @@ class Camera3Stream : // Get the usage flags for the other endpoint, or return // INVALID_OPERATION if they cannot be obtained. - virtual status_t getEndpointUsage(uint32_t *usage) = 0; + virtual status_t getEndpointUsage(uint32_t *usage) const = 0; // Tracking for idle state wp mStatusTracker; // Status tracker component ID int mStatusId; + // Tracking for stream prepare - whether this stream can still have + // prepareNextBuffer called on it. + bool mStreamUnpreparable; + private: uint32_t oldUsage; uint32_t oldMaxBuffers; @@ -333,6 +418,18 @@ class Camera3Stream : bool acquired, bool output); List > mBufferListenerList; + status_t cancelPrepareLocked(); + + // Tracking for PREPARING state + + // State of buffer preallocation. Only true if either prepareNextBuffer + // has been called sufficient number of times, or stream configuration + // had to register buffers with the HAL + bool mPrepared; + + Vector mPreparedBuffers; + size_t mPreparedBufferIdx; + }; // class Camera3Stream }; // namespace camera3 diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index ea90dd9..d177b57 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -89,6 +89,68 @@ class Camera3StreamInterface : public virtual RefBase { virtual status_t cancelConfiguration() = 0; /** + * Determine whether the stream has already become in-use (has received + * a valid filled buffer), which determines if a stream can still have + * prepareNextBuffer called on it. + */ + virtual bool isUnpreparable() = 0; + + /** + * Start stream preparation. May only be called in the CONFIGURED state, + * when no valid buffers have yet been returned to this stream. + * + * If no prepartion is necessary, returns OK and does not transition to + * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions + * to PREPARING. + * + * Returns: + * OK if no more buffers need to be preallocated + * NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish + * buffer pre-allocation, and transitions to the PREPARING state. + * NO_INIT in case of a serious error from the HAL device + * INVALID_OPERATION if called when not in CONFIGURED state, or a + * valid buffer has already been returned to this stream. + */ + virtual status_t startPrepare() = 0; + + /** + * Check if the stream is mid-preparing. + */ + virtual bool isPreparing() const = 0; + + /** + * Continue stream buffer preparation by allocating the next + * buffer for this stream. May only be called in the PREPARED state. + * + * Returns OK and transitions to the CONFIGURED state if all buffers + * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA. + * + * Returns: + * OK if no more buffers need to be preallocated, and transitions + * to the CONFIGURED state. + * NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish + * buffer pre-allocation. + * NO_INIT in case of a serious error from the HAL device + * INVALID_OPERATION if called when not in CONFIGURED state, or a + * valid buffer has already been returned to this stream. + */ + virtual status_t prepareNextBuffer() = 0; + + /** + * Cancel stream preparation early. In case allocation needs to be + * stopped, this method transitions the stream back to the CONFIGURED state. + * Buffers that have been allocated with prepareNextBuffer remain that way, + * but a later use of prepareNextBuffer will require just as many + * calls as if the earlier prepare attempt had not existed. + * + * Returns: + * OK if cancellation succeeded, and transitions to the CONFIGURED state + * INVALID_OPERATION if not in the PREPARING state + * NO_INIT in case of a serious error from the HAL device + */ + virtual status_t cancelPrepare() = 0; + + /** * Fill in the camera3_stream_buffer with the next valid buffer for this * stream, to hand over to the HAL. * -- cgit v1.1