summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorEino-Ville Talvala <etalvala@google.com>2015-04-11 13:15:45 -0700
committerEino-Ville Talvala <etalvala@google.com>2015-04-15 10:51:33 -0700
commit4d44cad22ea925a651463f2d51d6586c14d4b787 (patch)
tree3e9fa49f6f98a8deee591f58054488ddc6e46505 /services
parent4123d6db0642cd13e69230705b12d6b6fee6f73f (diff)
downloadframeworks_av-4d44cad22ea925a651463f2d51d6586c14d4b787.zip
frameworks_av-4d44cad22ea925a651463f2d51d6586c14d4b787.tar.gz
frameworks_av-4d44cad22ea925a651463f2d51d6586c14d4b787.tar.bz2
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
Diffstat (limited to 'services')
-rw-r--r--services/camera/libcameraservice/api2/CameraDeviceClient.cpp44
-rw-r--r--services/camera/libcameraservice/api2/CameraDeviceClient.h4
-rw-r--r--services/camera/libcameraservice/common/Camera2ClientBase.cpp8
-rw-r--r--services/camera/libcameraservice/common/Camera2ClientBase.h2
-rw-r--r--services/camera/libcameraservice/common/CameraDeviceBase.h7
-rw-r--r--services/camera/libcameraservice/device2/Camera2Device.cpp6
-rw-r--r--services/camera/libcameraservice/device2/Camera2Device.h3
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.cpp200
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.h53
-rw-r--r--services/camera/libcameraservice/device3/Camera3DummyStream.cpp2
-rw-r--r--services/camera/libcameraservice/device3/Camera3DummyStream.h2
-rw-r--r--services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp31
-rw-r--r--services/camera/libcameraservice/device3/Camera3IOStreamBase.h2
-rw-r--r--services/camera/libcameraservice/device3/Camera3InputStream.cpp2
-rw-r--r--services/camera/libcameraservice/device3/Camera3InputStream.h2
-rw-r--r--services/camera/libcameraservice/device3/Camera3OutputStream.cpp9
-rw-r--r--services/camera/libcameraservice/device3/Camera3OutputStream.h2
-rw-r--r--services/camera/libcameraservice/device3/Camera3Stream.cpp132
-rw-r--r--services/camera/libcameraservice/device3/Camera3Stream.h105
-rw-r--r--services/camera/libcameraservice/device3/Camera3StreamInterface.h62
20 files changed, 641 insertions, 37 deletions
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<String16>& 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<ICameraDeviceCallbacks> 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<TClientBase>::notifyAutoWhitebalance(uint8_t newState,
}
template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyPrepared(int streamId) {
+ (void)streamId;
+
+ ALOGV("%s: Stream %d now prepared",
+ __FUNCTION__, streamId);
+}
+
+template <typename TClientBase>
int Camera2ClientBase<TClientBase>::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<CameraDeviceBase>&
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<BufferReleasedListener> 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<Camera3StreamInterface> 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::CaptureRequest> 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::CaptureRequest> 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<Camera3Device> parent,
sp<StatusTracker> statusTracker,
camera3_device_t *hal3Device) :
- Thread(false),
+ Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
mHal3Device(hal3Device),
@@ -2432,7 +2472,7 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> 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<Camera3StreamInterface>& 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<Camera3StreamInterface>& 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<camera3::StatusTracker> 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<camera3::Camera3StreamInterface>& stream);
+
protected:
virtual bool threadLoop();
@@ -559,7 +567,6 @@ class Camera3Device :
Vector<camera3_stream_buffer_t> 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<uint32_t, InFlightRequest> InFlightMap;
@@ -642,6 +650,45 @@ class Camera3Device :
sp<camera3::StatusTracker> 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<camera3::Camera3StreamInterface>& stream);
+
+ /**
+ * Cancel all current and pending stream preparation
+ */
+ status_t clear();
+
+ private:
+ Mutex mLock;
+
+ virtual bool threadLoop();
+
+ // Guarded by mLock
+
+ NotificationListener *mListener;
+ List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
+ bool mActive;
+ bool mCancelNow;
+
+ // Only accessed by threadLoop and the destructor
+
+ sp<camera3::Camera3StreamInterface> mCurrentStream;
+ };
+ sp<PreparerThread> 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<String16> &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 <HAL3.2
+ * or re-configuration, or during prepare pre-allocation
*/
sp<StatusTracker> 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 <HAL3.2
+ * or re-configuration, or during prepare pre-allocation
*/
ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__,
mId);
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index abcf2b1..f5727e8 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -84,7 +84,7 @@ class Camera3IOStreamBase :
virtual size_t getHandoutInputBufferCountLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+ virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
status_t getBufferPreconditionCheckLocked() const;
status_t returnBufferPreconditionCheckLocked() const;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index fa97e57..84c5754 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -275,7 +275,7 @@ status_t Camera3InputStream::configureQueueLocked() {
return OK;
}
-status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) const {
// Per HAL3 spec, input streams have 0 for their initial usage field.
*usage = 0;
return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 7ba36c9..9f3de10 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -75,7 +75,7 @@ class Camera3InputStream : public Camera3IOStreamBase {
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage);
+ virtual status_t getEndpointUsage(uint32_t *usage) const;
}; // class Camera3InputStream
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 8d9b360..7a0331b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -209,6 +209,13 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(
}
}
mLock.lock();
+
+ // Once a valid buffer has been returned to the queue, can no longer
+ // dequeue all buffers for preallocation.
+ if (buffer.status != CAMERA3_BUFFER_STATUS_ERROR) {
+ mStreamUnpreparable = true;
+ }
+
if (res != OK) {
close(anwReleaseFence);
}
@@ -390,7 +397,7 @@ status_t Camera3OutputStream::disconnectLocked() {
return OK;
}
-status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) const {
status_t res;
int32_t u = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 12b2ebb..513b695 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -99,7 +99,7 @@ class Camera3OutputStream :
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage);
+ virtual status_t getEndpointUsage(uint32_t *usage) const;
}; // class Camera3OutputStream
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index d3c5cc3..3821da1 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -194,6 +194,11 @@ status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) {
return OK;
}
+ // Reset prepared state, since buffer config has changed, and existing
+ // allocations are no longer valid
+ mPrepared = false;
+ mStreamUnpreparable = false;
+
status_t res;
res = configureQueueLocked();
if (res != OK) {
@@ -244,6 +249,125 @@ status_t Camera3Stream::cancelConfiguration() {
return OK;
}
+bool Camera3Stream::isUnpreparable() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ return mStreamUnpreparable;
+}
+
+status_t Camera3Stream::startPrepare() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ status_t res = OK;
+
+ // This function should be only called when the stream is configured already.
+ if (mState != STATE_CONFIGURED) {
+ ALOGE("%s: Stream %d: Can't prepare stream if stream is not in CONFIGURED "
+ "state %d", __FUNCTION__, mId, mState);
+ return INVALID_OPERATION;
+ }
+
+ // This function can't be called if the stream has already received filled
+ // buffers
+ if (mStreamUnpreparable) {
+ ALOGE("%s: Stream %d: Can't prepare stream that's already in use",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ if (getHandoutOutputBufferCountLocked() > 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<StatusTracker> 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<wp<Camera3StreamBufferListener> > 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<camera3_stream_buffer_t> 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.
*