Add threading to camera.
Camera now has special synchronized threads to enqueue
buffers and dequeue buffers.
BUG: 29334616
TEST: manually tested with test app
Change-Id: Ibce14cd6b269c4d26403534b3f7985914263bf2b
diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp
index f7c75d2..80c2b1f 100644
--- a/modules/camera/3_4/v4l2_camera.cpp
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -27,6 +27,7 @@
#include <hardware/camera3.h>
#include "common.h"
+#include "function_thread.h"
#include "metadata/metadata_common.h"
#include "stream_format.h"
#include "v4l2_metadata_factory.h"
@@ -73,7 +74,11 @@
device_(std::move(v4l2_wrapper)),
metadata_(std::move(metadata)),
max_input_streams_(0),
- max_output_streams_({{0, 0, 0}}) {
+ max_output_streams_({{0, 0, 0}}),
+ buffer_enqueuer_(new FunctionThread(
+ std::bind(&V4L2Camera::enqueueRequestBuffers, this))),
+ buffer_dequeuer_(new FunctionThread(
+ std::bind(&V4L2Camera::dequeueRequestBuffers, this))) {
HAL_LOG_ENTER();
}
@@ -160,7 +165,23 @@
int V4L2Camera::initDevice() {
HAL_LOG_ENTER();
- // Nothing to do.
+
+ // Start the buffer enqueue/dequeue threads if they're not already running.
+ if (!buffer_enqueuer_->isRunning()) {
+ android::status_t res = buffer_enqueuer_->run("Enqueue buffers");
+ if (res != android::OK) {
+ HAL_LOGE("Failed to start buffer enqueue thread: %d", res);
+ return -ENODEV;
+ }
+ }
+ if (!buffer_dequeuer_->isRunning()) {
+ android::status_t res = buffer_dequeuer_->run("Dequeue buffers");
+ if (res != android::OK) {
+ HAL_LOGE("Failed to start buffer dequeue thread: %d", res);
+ return -ENODEV;
+ }
+ }
+
return 0;
}
@@ -175,119 +196,108 @@
std::lock_guard<std::mutex> guard(request_queue_lock_);
request_queue_.push(request);
}
-
- // TODO(b/29334616): Enqueueing of request buffers should be
- // done by a consumer thread loop instead of here
- enqueueRequestBuffers();
+ requests_available_.notify_one();
return 0;
}
std::shared_ptr<default_camera_hal::CaptureRequest>
V4L2Camera::dequeueRequest() {
- std::lock_guard<std::mutex> guard(request_queue_lock_);
- if (request_queue_.empty()) {
- return nullptr;
+ std::unique_lock<std::mutex> lock(request_queue_lock_);
+ while (request_queue_.empty()) {
+ requests_available_.wait(lock);
}
+
std::shared_ptr<default_camera_hal::CaptureRequest> request =
request_queue_.front();
request_queue_.pop();
return request;
}
-void V4L2Camera::enqueueRequestBuffers() {
- // Get a request from the queue.
+bool V4L2Camera::enqueueRequestBuffers() {
+ // Get a request from the queue (blocks this thread until one is available).
std::shared_ptr<default_camera_hal::CaptureRequest> request =
dequeueRequest();
- if (!request) {
- return;
- }
// Assume request validated before being added to the queue
// (For now, always exactly 1 output buffer, no inputs).
- {
- std::lock_guard<std::mutex> guard(in_flight_lock_);
- // Set the requested settings
- int res = metadata_->SetRequestSettings(request->settings);
- if (res) {
- HAL_LOGE("Failed to set settings.");
- completeRequest(request, res);
- return;
- }
+ // Setting and getting settings are best effort here,
+ // since there's no way to know through V4L2 exactly what
+ // settings are used for a buffer unless we were to enqueue them
+ // one at a time, which would be too slow.
- // Replace the requested settings with a snapshot of
- // the used settings/state.
- res = metadata_->FillResultMetadata(&request->settings);
- if (res) {
- HAL_LOGE("Failed to fill result metadata.");
- completeRequest(request, res);
- return;
- }
-
- // Actually enqueue the buffer for capture.
- res = device_->EnqueueBuffer(&request->output_buffers[0]);
- if (res) {
- HAL_LOGE("Device failed to enqueue buffer.");
- completeRequest(request, res);
- return;
- }
-
- if (in_flight_.empty()) {
- // Nothing in flight, stream should be off, so it needs to be turned on.
- res = device_->StreamOn();
- if (res) {
- HAL_LOGE("Device failed to turn on stream.");
- completeRequest(request, res);
- return;
- }
- }
- in_flight_.push(request);
+ // Set the requested settings
+ int res = metadata_->SetRequestSettings(request->settings);
+ if (res) {
+ HAL_LOGE("Failed to set settings.");
+ completeRequest(request, res);
+ return true;
}
- // TODO(b/29334616): Dequeueing of request buffers should be
- // done by a consumer thread loop instead of here
- dequeueRequestBuffers();
-}
+ // Actually enqueue the buffer for capture.
+ res = device_->EnqueueBuffer(&request->output_buffers[0]);
+ if (res) {
+ HAL_LOGE("Device failed to enqueue buffer.");
+ completeRequest(request, res);
+ return true;
+ }
-void V4L2Camera::dequeueRequestBuffers() {
- std::shared_ptr<default_camera_hal::CaptureRequest> request;
+ // Replace the requested settings with a snapshot of
+ // the used settings/state immediately after enqueue.
+ res = metadata_->FillResultMetadata(&request->settings);
+ if (res) {
+ HAL_LOGE("Failed to fill result metadata.");
+ completeRequest(request, res);
+ return true;
+ }
+
+ // Make sure the stream is on (no effect if already on).
+ res = device_->StreamOn();
+ if (res) {
+ HAL_LOGE("Device failed to turn on stream.");
+ completeRequest(request, res);
+ return true;
+ }
+
{
std::lock_guard<std::mutex> guard(in_flight_lock_);
- if (in_flight_.empty()) {
- return;
- }
+ in_flight_.push(request);
+ }
+ buffers_in_flight_.notify_one();
- request = in_flight_.front();
- in_flight_.pop();
+ return true;
+}
- // Dequeue the buffer. For now, since each request has only 1 buffer,
- // and the in_flight_lock_ is held while enqueueing and dequeuing
- // from the device, just assume that the dequeued buffer corresponds
- // to the dequeued request.
- // TODO(b/31657008): in_flight_ should map buffer handles to requests;
- // this consumer should just dequeue whatever it gets, then match
- // that to a handle.
- v4l2_buffer result_buffer;
- int res = device_->DequeueBuffer(&result_buffer);
- if (res) {
- HAL_LOGE("Device failed to dequeue buffer.");
- completeRequest(request, res);
- return;
- }
+bool V4L2Camera::dequeueRequestBuffers() {
+ std::unique_lock<std::mutex> lock(in_flight_lock_);
+ while (in_flight_.empty()) {
+ buffers_in_flight_.wait(lock);
+ }
- // All done, turn off the stream if it's empty.
- if (in_flight_.empty()) {
- res = device_->StreamOff();
- if (res) {
- HAL_LOGE("Device failed to turn off stream.");
- completeRequest(request, res);
- return;
- }
- }
+ std::shared_ptr<default_camera_hal::CaptureRequest> request =
+ in_flight_.front();
+ in_flight_.pop();
+
+ lock.unlock();
+
+ // Dequeue the buffer. For now, since each request has only 1 buffer,
+ // and the in_flight_lock_ is held while enqueueing and dequeuing
+ // from the device, just assume that the dequeued buffer corresponds
+ // to the dequeued request.
+ // TODO(b/31657008): in_flight_ should map buffer handles to requests;
+ // this consumer should just dequeue whatever it gets, then match
+ // that to a handle.
+ v4l2_buffer result_buffer;
+ int res = device_->DequeueBuffer(&result_buffer);
+ if (res) {
+ HAL_LOGE("Device failed to dequeue buffer.");
+ completeRequest(request, res);
+ return true;
}
completeRequest(request, 0);
+ return true;
}
bool V4L2Camera::isSupportedStreamSet(default_camera_hal::Stream** streams,
@@ -401,7 +411,13 @@
// claims it's underdocumented; the implementation lets the HAL overwrite it.
stream->setDataSpace(HAL_DATASPACE_V0_JFIF);
- int res = device_->SetFormat(*stream, max_buffers);
+ int res = device_->StreamOff();
+ if (res) {
+ HAL_LOGE("Device failed to turn off stream for reconfiguration.");
+ return -ENODEV;
+ }
+
+ res = device_->SetFormat(*stream, max_buffers);
if (res) {
HAL_LOGE("Failed to set device to correct format for stream.");
return res;