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/function_thread.h b/modules/camera/3_4/function_thread.h
new file mode 100644
index 0000000..7a7b1e3
--- /dev/null
+++ b/modules/camera/3_4/function_thread.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_FUNCTION_THREAD_H_
+#define V4L2_CAMERA_HAL_FUNCTION_THREAD_H_
+
+#include <functional>
+
+#include <utils/Thread.h>
+
+#include "common.h"
+
+namespace v4l2_camera_hal {
+
+class FunctionThread : public android::Thread {
+ public:
+ FunctionThread(std::function<bool()> function) : function_(function){};
+
+ private:
+ bool threadLoop() override {
+ bool result = function_();
+ return result;
+ };
+
+ std::function<bool()> function_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_FUNCTION_THREAD_H_
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;
diff --git a/modules/camera/3_4/v4l2_camera.h b/modules/camera/3_4/v4l2_camera.h
index d299b9f..29583b0 100644
--- a/modules/camera/3_4/v4l2_camera.h
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -20,10 +20,13 @@
#define V4L2_CAMERA_HAL_V4L2_CAMERA_H_
#include <array>
+#include <condition_variable>
#include <queue>
#include <string>
#include <camera/CameraMetadata.h>
+#include <utils/StrongPointer.h>
+#include <utils/Thread.h>
#include "camera.h"
#include "common.h"
@@ -78,13 +81,16 @@
int enqueueRequest(
std::shared_ptr<default_camera_hal::CaptureRequest> request) override;
- // Async request processing.
+ // Async request processing helpers.
// Dequeue a request from the waiting queue.
+ // Blocks until a request is available.
std::shared_ptr<default_camera_hal::CaptureRequest> dequeueRequest();
+
+ // Thread functions. Return true to loop, false to exit.
// Pass buffers for enqueued requests to the device.
- void enqueueRequestBuffers();
+ bool enqueueRequestBuffers();
// Retreive buffers from the device.
- void dequeueRequestBuffers();
+ bool dequeueRequestBuffers();
// V4L2 helper.
std::shared_ptr<V4L2Wrapper> device_;
@@ -95,6 +101,11 @@
request_queue_;
std::mutex in_flight_lock_;
std::queue<std::shared_ptr<default_camera_hal::CaptureRequest>> in_flight_;
+ // Threads require holding an Android strong pointer.
+ android::sp<android::Thread> buffer_enqueuer_;
+ android::sp<android::Thread> buffer_dequeuer_;
+ std::condition_variable requests_available_;
+ std::condition_variable buffers_in_flight_;
int32_t max_input_streams_;
std::array<int, 3> max_output_streams_; // {raw, non-stalling, stalling}.
@@ -104,4 +115,4 @@
} // namespace v4l2_camera_hal
-#endif // V4L2_CAMERA_HAL_V4L2_CAMERA_H
+#endif // V4L2_CAMERA_HAL_V4L2_CAMERA_H_
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
index 3d7ba85..40db4f6 100644
--- a/modules/camera/3_4/v4l2_wrapper.cpp
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -156,8 +156,9 @@
HAL_LOG_ENTER();
if (!format_) {
- HAL_LOGE("Stream format must be set to turn off stream.");
- return -ENODEV;
+ // Can't have turned on the stream wihtout format being set,
+ // so nothing to turn off here.
+ return 0;
}
int32_t type = format_->type();