Camera3Device: Support batch requests
Refactor request threadLoop to three parts: waiting for next
batch of requests, preparing HAL requests and output buffers for
next batch of requests, and submitting the batch of requests to
HAL.
Set the batch size to the size of the request list if it's a video
recording request in a high speed video configuration.
Add a flush lock so that HAL's flush() won't be called while
submitting a batch of requests.
Bug: 23360060
Change-Id: Icd395b1f955a9b336eec6fa5aff6b17741ce08c7
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ec3591f..98d0a41 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -565,6 +565,18 @@
ALOGV("%s: requestId = %" PRId32, __FUNCTION__, newRequest->mResultExtras.requestId);
}
+
+ // Setup batch size if this is a high speed video recording request.
+ if (mIsConstrainedHighSpeedConfiguration && requestList->size() > 0) {
+ auto firstRequest = requestList->begin();
+ for (auto& outputStream : (*firstRequest)->mOutputStreams) {
+ if (outputStream->isVideoStream()) {
+ (*firstRequest)->mBatchSize = requestList->size();
+ break;
+ }
+ }
+ }
+
return OK;
}
@@ -1406,7 +1418,7 @@
status_t res;
if (mHal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_1) {
- res = mHal3Device->ops->flush(mHal3Device);
+ res = mRequestThread->flush();
} else {
Mutex::Autolock l(mLock);
res = waitUntilDrainedLocked();
@@ -1595,6 +1607,7 @@
newRequest->mOutputStreams.push(stream);
}
newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS);
+ newRequest->mBatchSize = 1;
return newRequest;
}
@@ -2766,6 +2779,17 @@
return OK;
}
+status_t Camera3Device::RequestThread::flush() {
+ ATRACE_CALL();
+ Mutex::Autolock l(mFlushLock);
+
+ if (mHal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_1) {
+ return mHal3Device->ops->flush(mHal3Device);
+ }
+
+ return -ENOTSUP;
+}
+
void Camera3Device::RequestThread::setPaused(bool paused) {
Mutex::Autolock l(mPauseLock);
mDoPause = paused;
@@ -2856,7 +2880,7 @@
}
bool Camera3Device::RequestThread::threadLoop() {
-
+ ATRACE_CALL();
status_t res;
// Handle paused state.
@@ -2864,147 +2888,33 @@
return true;
}
- // Get work to do
-
- sp<CaptureRequest> nextRequest = waitForNextRequest();
- if (nextRequest == NULL) {
+ // Get next batch of requests.
+ Vector<NextRequest> nextRequests;
+ waitForNextRequestBatch(&nextRequests);
+ const size_t numRequests = nextRequests.size();
+ if (numRequests == 0) {
return true;
}
- // Create request to HAL
- camera3_capture_request_t request = camera3_capture_request_t();
- request.frame_number = nextRequest->mResultExtras.frameNumber;
- Vector<camera3_stream_buffer_t> outputBuffers;
-
- // Get the request ID, if any
- int requestId;
- camera_metadata_entry_t requestIdEntry =
- nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+ // Get the latest request ID, if any
+ int latestRequestId;
+ camera_metadata_entry_t requestIdEntry = nextRequests[nextRequests.size() - 1].
+ captureRequest->mSettings.find(ANDROID_REQUEST_ID);
if (requestIdEntry.count > 0) {
- requestId = requestIdEntry.data.i32[0];
+ latestRequestId = requestIdEntry.data.i32[0];
} else {
- ALOGW("%s: Did not have android.request.id set in the request",
- __FUNCTION__);
- requestId = NAME_NOT_FOUND;
+ ALOGW("%s: Did not have android.request.id set in the request.", __FUNCTION__);
+ latestRequestId = NAME_NOT_FOUND;
}
- // 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)",
- request.frame_number, 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) {
- /**
- * HAL workaround:
- * Insert a dummy trigger ID if a trigger is set but no trigger ID is
- */
- res = addDummyTriggerIds(nextRequest);
- if (res != OK) {
- SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
- "(capture request %d, HAL device: %s (%d)",
- request.frame_number, strerror(-res), res);
- cleanUpFailedRequest(request, nextRequest, outputBuffers);
- return false;
- }
-
- /**
- * 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__,
- request.frame_number,
- e.data.u8[0]);
- }
- }
- } else {
- // leave request.settings NULL to indicate 'reuse latest given'
- ALOGVV("%s: Request settings are REUSED",
- __FUNCTION__);
- }
-
- uint32_t totalNumBuffers = 0;
-
- // Fill in buffers
- if (nextRequest->mInputStream != NULL) {
- request.input_buffer = &nextRequest->mInputBuffer;
- totalNumBuffers += 1;
- } 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) {
- // Can't get output buffer from gralloc queue - this could be due to
- // abandoned queue or other consumer misbehavior, so not a fatal
- // error
- ALOGE("RequestThread: Can't get output buffer, skipping request:"
- " %s (%d)", strerror(-res), res);
- {
- Mutex::Autolock l(mRequestLock);
- if (mListener != NULL) {
- mListener->notifyError(
- ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
- nextRequest->mResultExtras);
- }
- }
- cleanUpFailedRequest(request, nextRequest, outputBuffers);
- return true;
- }
- request.num_output_buffers++;
- }
- totalNumBuffers += request.num_output_buffers;
-
- // Log request in the in-flight queue
- sp<Camera3Device> parent = mParent.promote();
- if (parent == NULL) {
- // Should not happen, and nowhere to send errors to, so just log it
- CLOGE("RequestThread: Parent is gone");
- cleanUpFailedRequest(request, nextRequest, outputBuffers);
- return false;
- }
-
- res = parent->registerInFlight(request.frame_number,
- totalNumBuffers, nextRequest->mResultExtras,
- /*hasInput*/request.input_buffer != NULL,
- nextRequest->mAeTriggerCancelOverride);
- ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
- ", burstId = %" PRId32 ".",
- __FUNCTION__,
- nextRequest->mResultExtras.requestId, nextRequest->mResultExtras.frameNumber,
- nextRequest->mResultExtras.burstId);
- if (res != OK) {
- SET_ERR("RequestThread: Unable to register new in-flight request:"
- " %s (%d)", strerror(-res), res);
- cleanUpFailedRequest(request, nextRequest, outputBuffers);
+ // Prepare a batch of HAL requests and output buffers.
+ res = prepareHalRequests(&nextRequests);
+ if (res == TIMED_OUT) {
+ // Not a fatal error if getting output buffers time out.
+ cleanUpFailedRequests(&nextRequests, /*sendRequestError*/ true);
+ return true;
+ } else if (res != OK) {
+ cleanUpFailedRequests(&nextRequests, /*sendRequestError*/ false);
return false;
}
@@ -3012,57 +2922,214 @@
{
Mutex::Autolock al(mLatestRequestMutex);
- mLatestRequestId = requestId;
+ mLatestRequestId = latestRequestId;
mLatestRequestSignal.signal();
}
- // 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();
+ // Submit a batch of requests to HAL.
+ // Use flush lock only when submitting multilple requests in a batch.
+ // TODO: The problem with flush lock is flush() will be blocked by process_capture_request()
+ // which may take a long time to finish so synchronizing flush() and
+ // process_capture_request() defeats the purpose of cancelling requests ASAP with flush().
+ // For now, only synchronize for high speed recording and we should figure something out for
+ // removing the synchronization.
+ bool useFlushLock = nextRequests.size() > 1;
- if (res != OK) {
- // Should only get a failure here for malformed requests or device-level
- // errors, so consider all errors fatal. Bad metadata failures should
- // come through notify.
- 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 (useFlushLock) {
+ mFlushLock.lock();
}
- // Update the latest request sent to HAL
- if (request.settings != NULL) { // Don't update them if they were unchanged
- Mutex::Autolock al(mLatestRequestMutex);
+ ALOGVV("%s: %d: submitting %d requests in a batch.", __FUNCTION__, __LINE__,
+ nextRequests.size());
+ for (auto& nextRequest : nextRequests) {
+ // Submit request and block until ready for next one
+ ATRACE_ASYNC_BEGIN("frame capture", nextRequest.halRequest.frame_number);
+ ATRACE_BEGIN("camera3->process_capture_request");
+ res = mHal3Device->ops->process_capture_request(mHal3Device, &nextRequest.halRequest);
+ ATRACE_END();
- camera_metadata_t* cloned = clone_camera_metadata(request.settings);
- mLatestRequest.acquire(cloned);
+ if (res != OK) {
+ // Should only get a failure here for malformed requests or device-level
+ // errors, so consider all errors fatal. Bad metadata failures should
+ // come through notify.
+ SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
+ " device: %s (%d)", nextRequest.halRequest.frame_number, strerror(-res),
+ res);
+ cleanUpFailedRequests(&nextRequests, /*sendRequestError*/ false);
+ if (useFlushLock) {
+ mFlushLock.unlock();
+ }
+ return false;
+ }
+
+ // Mark that the request has be submitted successfully.
+ nextRequest.submitted = true;
+
+ // Update the latest request sent to HAL
+ if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
+ mLatestRequest.acquire(cloned);
+ }
+
+ if (nextRequest.halRequest.settings != NULL) {
+ nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.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)",
+ nextRequest.halRequest.frame_number, strerror(-res), res);
+ cleanUpFailedRequests(&nextRequests, /*sendRequestError*/ false);
+ if (useFlushLock) {
+ mFlushLock.unlock();
+ }
+ return false;
+ }
}
- if (request.settings != NULL) {
- nextRequest->mSettings.unlock(request.settings);
+ if (useFlushLock) {
+ mFlushLock.unlock();
}
// Unset as current request
{
Mutex::Autolock l(mRequestLock);
- mNextRequest.clear();
+ mNextRequests.clear();
}
- // 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;
-
return true;
}
+status_t Camera3Device::RequestThread::prepareHalRequests(Vector<NextRequest> *nextRequests) {
+ ATRACE_CALL();
+
+ if (nextRequests == nullptr) {
+ return BAD_VALUE;
+ }
+
+ for (auto& nextRequest : *nextRequests) {
+ sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
+ camera3_capture_request_t* halRequest = &nextRequest.halRequest;
+ Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
+
+ // Prepare a request to HAL
+ halRequest->frame_number = captureRequest->mResultExtras.frameNumber;
+
+ // Insert any queued triggers (before metadata is locked)
+ status_t res = insertTriggers(captureRequest);
+
+ if (res < 0) {
+ SET_ERR("RequestThread: Unable to insert triggers "
+ "(capture request %d, HAL device: %s (%d)",
+ halRequest->frame_number, strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ int triggerCount = res;
+ bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
+ mPrevTriggers = triggerCount;
+
+ // If the request is the same as last, or we had triggers last time
+ if (mPrevRequest != captureRequest || triggersMixedIn) {
+ /**
+ * HAL workaround:
+ * Insert a dummy trigger ID if a trigger is set but no trigger ID is
+ */
+ res = addDummyTriggerIds(captureRequest);
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
+ "(capture request %d, HAL device: %s (%d)",
+ halRequest->frame_number, strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * The request should be presorted so accesses in HAL
+ * are O(logn). Sidenote, sorting a sorted metadata is nop.
+ */
+ captureRequest->mSettings.sort();
+ halRequest->settings = captureRequest->mSettings.getAndLock();
+ mPrevRequest = captureRequest;
+ 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(
+ halRequest->settings,
+ ANDROID_CONTROL_AF_TRIGGER,
+ &e
+ );
+ if (e.count > 0) {
+ ALOGV("%s: Request (frame num %d) had AF trigger 0x%x",
+ __FUNCTION__,
+ halRequest->frame_number,
+ e.data.u8[0]);
+ }
+ }
+ } else {
+ // leave request.settings NULL to indicate 'reuse latest given'
+ ALOGVV("%s: Request settings are REUSED",
+ __FUNCTION__);
+ }
+
+ uint32_t totalNumBuffers = 0;
+
+ // Fill in buffers
+ if (captureRequest->mInputStream != NULL) {
+ halRequest->input_buffer = &captureRequest->mInputBuffer;
+ totalNumBuffers += 1;
+ } else {
+ halRequest->input_buffer = NULL;
+ }
+
+ outputBuffers->insertAt(camera3_stream_buffer_t(), 0,
+ captureRequest->mOutputStreams.size());
+ halRequest->output_buffers = outputBuffers->array();
+ for (size_t i = 0; i < captureRequest->mOutputStreams.size(); i++) {
+ res = captureRequest->mOutputStreams.editItemAt(i)->
+ getBuffer(&outputBuffers->editItemAt(i));
+ if (res != OK) {
+ // Can't get output buffer from gralloc queue - this could be due to
+ // abandoned queue or other consumer misbehavior, so not a fatal
+ // error
+ ALOGE("RequestThread: Can't get output buffer, skipping request:"
+ " %s (%d)", strerror(-res), res);
+
+ return TIMED_OUT;
+ }
+ halRequest->num_output_buffers++;
+ }
+ totalNumBuffers += halRequest->num_output_buffers;
+
+ // Log request in the in-flight queue
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == NULL) {
+ // Should not happen, and nowhere to send errors to, so just log it
+ CLOGE("RequestThread: Parent is gone");
+ return INVALID_OPERATION;
+ }
+ res = parent->registerInFlight(halRequest->frame_number,
+ totalNumBuffers, captureRequest->mResultExtras,
+ /*hasInput*/halRequest->input_buffer != NULL,
+ captureRequest->mAeTriggerCancelOverride);
+ ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
+ ", burstId = %" PRId32 ".",
+ __FUNCTION__,
+ captureRequest->mResultExtras.requestId, captureRequest->mResultExtras.frameNumber,
+ captureRequest->mResultExtras.burstId);
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to register new in-flight request:"
+ " %s (%d)", strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ }
+
+ return OK;
+}
+
CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
Mutex::Autolock al(mLatestRequestMutex);
@@ -3075,11 +3142,11 @@
sp<Camera3StreamInterface>& stream) {
Mutex::Autolock l(mRequestLock);
- if (mNextRequest != nullptr) {
- for (const auto& s : mNextRequest->mOutputStreams) {
+ for (const auto& nextRequest : mNextRequests) {
+ for (const auto& s : nextRequest->mOutputStreams) {
if (stream == s) return true;
}
- if (stream == mNextRequest->mInputStream) return true;
+ if (stream == nextRequest->mInputStream) return true;
}
for (const auto& request : mRequestQueue) {
@@ -3099,37 +3166,104 @@
return false;
}
-void Camera3Device::RequestThread::cleanUpFailedRequest(
- camera3_capture_request_t &request,
- sp<CaptureRequest> &nextRequest,
- Vector<camera3_stream_buffer_t> &outputBuffers) {
+void Camera3Device::RequestThread::cleanUpFailedRequests(Vector<NextRequest> *nextRequests,
+ bool sendRequestError) {
+ if (nextRequests == nullptr) {
+ return;
+ }
- if (request.settings != NULL) {
- nextRequest->mSettings.unlock(request.settings);
- }
- if (nextRequest->mInputStream != NULL) {
- nextRequest->mInputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
- nextRequest->mInputStream->returnInputBuffer(nextRequest->mInputBuffer);
- }
- 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);
+ for (auto& nextRequest : *nextRequests) {
+ // Skip the ones that have been submitted successfully.
+ if (nextRequest.submitted) {
+ continue;
+ }
+
+ sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
+ camera3_capture_request_t* halRequest = &nextRequest.halRequest;
+ Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
+
+ if (halRequest->settings != NULL) {
+ captureRequest->mSettings.unlock(halRequest->settings);
+ }
+
+ if (captureRequest->mInputStream != NULL) {
+ captureRequest->mInputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ captureRequest->mInputStream->returnInputBuffer(captureRequest->mInputBuffer);
+ }
+
+ for (size_t i = 0; i < halRequest->num_output_buffers; i++) {
+ outputBuffers->editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
+ captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0);
+ }
+
+ if (sendRequestError) {
+ Mutex::Autolock l(mRequestLock);
+ if (mListener != NULL) {
+ mListener->notifyError(
+ ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
+ captureRequest->mResultExtras);
+ }
+ }
}
Mutex::Autolock l(mRequestLock);
- mNextRequest.clear();
+ mNextRequests.clear();
+ nextRequests->clear();
}
-sp<Camera3Device::CaptureRequest>
- Camera3Device::RequestThread::waitForNextRequest() {
- status_t res;
- sp<CaptureRequest> nextRequest;
+void Camera3Device::RequestThread::waitForNextRequestBatch(Vector<NextRequest> *nextRequests) {
+ if (nextRequests == nullptr) {
+ return;
+ }
// 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);
+ nextRequests->clear();
+ assert(mNextRequests.empty());
+
+ NextRequest nextRequest;
+ nextRequest.captureRequest = waitForNextRequestLocked();
+ if (nextRequest.captureRequest == nullptr) {
+ return;
+ }
+
+ nextRequest.halRequest = camera3_capture_request_t();
+ nextRequest.submitted = false;
+ nextRequests->add(nextRequest);
+ mNextRequests.push_back(nextRequest.captureRequest);
+
+ // Wait for additional requests
+ const size_t batchSize = nextRequest.captureRequest->mBatchSize;
+
+ for (size_t i = 1; i < batchSize; i++) {
+ NextRequest additionalRequest;
+ additionalRequest.captureRequest = waitForNextRequestLocked();
+ if (additionalRequest.captureRequest == nullptr) {
+ break;
+ }
+
+ additionalRequest.halRequest = camera3_capture_request_t();
+ additionalRequest.submitted = false;
+ nextRequests->add(additionalRequest);
+ mNextRequests.push_back(additionalRequest.captureRequest);
+ }
+
+ if (nextRequests->size() < batchSize) {
+ ALOGE("RequestThread: only get %d out of %d requests. Skipping requests.",
+ nextRequests->size(), batchSize);
+ cleanUpFailedRequests(nextRequests, /*sendRequestError*/true);
+ }
+
+ return;
+}
+
+sp<Camera3Device::CaptureRequest>
+ Camera3Device::RequestThread::waitForNextRequestLocked() {
+ status_t res;
+ sp<CaptureRequest> nextRequest;
+
while (mRequestQueue.empty()) {
if (!mRepeatingRequests.empty()) {
// Always atomically enqueue all requests in a repeating request
@@ -3224,8 +3358,6 @@
handleAePrecaptureCancelRequest(nextRequest);
- mNextRequest = nextRequest;
-
return nextRequest;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 1f39940..a3abbaf 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -264,6 +264,11 @@
// Used to cancel AE precapture trigger for devices doesn't support
// CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
AeTriggerCancelOverride_t mAeTriggerCancelOverride;
+ // The number of requests that should be submitted to HAL at a time.
+ // For example, if batch size is 8, this request and the following 7
+ // requests will be submitted to HAL at a time. The batch size for
+ // the following 7 requests will be ignored by the request thread.
+ int mBatchSize;
};
typedef List<sp<CaptureRequest> > RequestList;
@@ -441,6 +446,11 @@
int64_t *lastFrameNumber = NULL);
/**
+ * Flush all pending requests in HAL.
+ */
+ status_t flush();
+
+ /**
* 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.
@@ -501,16 +511,28 @@
static const nsecs_t kRequestTimeout = 50e6; // 50 ms
- // Waits for a request, or returns NULL if times out.
- sp<CaptureRequest> waitForNextRequest();
+ // Used to prepare a batch of requests.
+ struct NextRequest {
+ sp<CaptureRequest> captureRequest;
+ camera3_capture_request_t halRequest;
+ Vector<camera3_stream_buffer_t> outputBuffers;
+ bool submitted;
+ };
- // 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);
+ // Wait for the next batch of requests.
+ void waitForNextRequestBatch(Vector<NextRequest> *nextRequests);
+
+ // Waits for a request, or returns NULL if times out. Must be called with mRequestLock hold.
+ sp<CaptureRequest> waitForNextRequestLocked();
+
+ // Prepare a HAL request and output buffers. Return TIMED_OUT if getting any output buffer
+ // timed out. If an error is returned, the caller should clean up the pending request batch.
+ status_t prepareHalRequests(Vector<NextRequest> *nextRequests);
+
+ // Return buffers, etc, for a request that couldn't be fully constructed and send request
+ // errors if sendRequestError is true. The buffers will be returned in the ERROR state
+ // to mark them as not having valid data. nextRequests will be modified.
+ void cleanUpFailedRequests(Vector<NextRequest> *nextRequests, bool sendRequestError);
// Pause handling
bool waitIfPaused();
@@ -539,10 +561,13 @@
Condition mRequestSignal;
RequestList mRequestQueue;
RequestList mRepeatingRequests;
- // The next request being prepped for submission to the HAL, no longer
+ // The next requests being prepped for submission to the HAL, no longer
// on the request queue. Read-only even with mRequestLock held, outside
// of threadLoop
- sp<const CaptureRequest> mNextRequest;
+ RequestList mNextRequests;
+
+ // To protect flush() and sending a request batch to HAL.
+ Mutex mFlushLock;
bool mReconfigured;
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index ecb8ac8..1d9d04f 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -92,6 +92,10 @@
return OK;
}
+bool Camera3DummyStream::isVideoStream() const {
+ return false;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 3a3dbf4..97c0c96 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -54,6 +54,11 @@
status_t setTransform(int transform);
+ /**
+ * Return if this output stream is for video encoding.
+ */
+ bool isVideoStream() const;
+
protected:
/**
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 8c611d5..3f0a736 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -426,6 +426,17 @@
return res;
}
+bool Camera3OutputStream::isVideoStream() const {
+ uint32_t usage = 0;
+ status_t res = getEndpointUsage(&usage);
+ if (res != OK) {
+ ALOGE("%s: getting end point usage failed: %s (%d).", __FUNCTION__, strerror(-res), res);
+ return false;
+ }
+
+ return (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) != 0;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 941d693..3c083ec 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -64,6 +64,11 @@
*/
status_t setTransform(int transform);
+ /**
+ * Return if this output stream is for video encoding.
+ */
+ bool isVideoStream() const;
+
protected:
Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, int format,
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index aae72cf..df89b34 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -34,6 +34,11 @@
* HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants.
*/
virtual status_t setTransform(int transform) = 0;
+
+ /**
+ * Return if this output stream is for video encoding.
+ */
+ virtual bool isVideoStream() const = 0;
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 7163d62..691764f 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -455,8 +455,9 @@
res = mOutputBufferReturnedSignal.waitRelative(mLock, kWaitForBufferDuration);
if (res != OK) {
if (res == TIMED_OUT) {
- ALOGE("%s: wait for output buffer return timed out after %lldms", __FUNCTION__,
- kWaitForBufferDuration / 1000000LL);
+ ALOGE("%s: wait for output buffer return timed out after %lldms (max_buffers %d)",
+ __FUNCTION__, kWaitForBufferDuration / 1000000LL,
+ camera3_stream::max_buffers);
}
return res;
}