Add buffer management to V4L2 wrapper.
Functions to enqueue and dequeue buffers.
Extended gralloc helper with a mass-unlock function.
Change-Id: I72c83138fd37463d314bf02b5df5befc68bc05af
diff --git a/modules/camera/3_4/StreamFormat.h b/modules/camera/3_4/StreamFormat.h
index 3686564..1b00068 100644
--- a/modules/camera/3_4/StreamFormat.h
+++ b/modules/camera/3_4/StreamFormat.h
@@ -40,7 +40,10 @@
void FillFormatRequest(v4l2_format* format) const;
FormatCategory Category() const;
- inline uint32_t get_type() const { return type_; };
+
+ // Accessors.
+ inline uint32_t type() const { return type_; };
+ inline uint32_t bytes_per_line() const { return bytes_per_line_; };
bool operator==(const StreamFormat& other) const;
bool operator!=(const StreamFormat& other) const;
diff --git a/modules/camera/3_4/V4L2Gralloc.cpp b/modules/camera/3_4/V4L2Gralloc.cpp
index d46d119..111996e 100644
--- a/modules/camera/3_4/V4L2Gralloc.cpp
+++ b/modules/camera/3_4/V4L2Gralloc.cpp
@@ -82,14 +82,7 @@
HAL_LOG_ENTER();
// Unlock buffers that are still locked.
- for (auto const& entry : mBufferMap) {
- mModule->unlock(mModule, *entry.second->camera_buffer->buffer);
- // Clean up dynamically allocated stuff.
- if (entry.second->transform_dest) {
- delete [] reinterpret_cast<uint8_t*>(entry.first);
- }
- delete entry.second;
- }
+ unlockAllBuffers();
}
int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer,
@@ -270,4 +263,31 @@
return 0;
}
+int V4L2Gralloc::unlockAllBuffers() {
+ HAL_LOG_ENTER();
+
+ bool failed = false;
+ for (auto const& entry : mBufferMap) {
+ int res = mModule->unlock(mModule, *entry.second->camera_buffer->buffer);
+ if (res) {
+ failed = true;
+ }
+ // When there is a transform to be made, the buffer returned by lock()
+ // is dynamically allocated (to hold the pre-transform data).
+ if (entry.second->transform_dest) {
+ delete [] reinterpret_cast<uint8_t*>(entry.first);
+ }
+ // The BufferData entry is always dynamically allocated in lock().
+ delete entry.second;
+ }
+
+ // If any unlock failed, return error.
+ if (failed) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
} // namespace default_camera_hal
diff --git a/modules/camera/3_4/V4L2Gralloc.h b/modules/camera/3_4/V4L2Gralloc.h
index a58a84d..ebec963 100644
--- a/modules/camera/3_4/V4L2Gralloc.h
+++ b/modules/camera/3_4/V4L2Gralloc.h
@@ -46,6 +46,8 @@
// Unlock a buffer that was locked by this helper (equality determined
// based on buffer user pointer, not the specific object).
int unlock(const v4l2_buffer* device_buffer);
+ // Release all held locks.
+ int unlockAllBuffers();
private:
// Constructor is private to allow failing on bad input.
diff --git a/modules/camera/3_4/V4L2Wrapper.cpp b/modules/camera/3_4/V4L2Wrapper.cpp
index fdcd179..495467a 100644
--- a/modules/camera/3_4/V4L2Wrapper.cpp
+++ b/modules/camera/3_4/V4L2Wrapper.cpp
@@ -28,11 +28,25 @@
#include "Common.h"
#include "Stream.h"
#include "StreamFormat.h"
+#include "V4L2Gralloc.h"
namespace v4l2_camera_hal {
-V4L2Wrapper::V4L2Wrapper(const std::string device_path)
- : device_path_(device_path), max_buffers_(0) {
+V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
+ HAL_LOG_ENTER();
+
+ std::unique_ptr<V4L2Gralloc> gralloc(V4L2Gralloc::NewV4L2Gralloc());
+ if (!gralloc) {
+ HAL_LOGE("Failed to initialize gralloc helper.");
+ return nullptr;
+ }
+
+ return new V4L2Wrapper(device_path, std::move(gralloc));
+}
+
+V4L2Wrapper::V4L2Wrapper(const std::string device_path,
+ std::unique_ptr<V4L2Gralloc> gralloc)
+ : device_path_(device_path), gralloc_(std::move(gralloc)), max_buffers_(0) {
HAL_LOG_ENTER();
}
@@ -58,7 +72,7 @@
// Check if this connection has the extended control query capability.
v4l2_query_ext_ctrl query;
query.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
- // Already holding the lock, so don't call ioctlLocked.
+ // Already holding the lock, so don't call IoctlLocked.
int res = TEMP_FAILURE_RETRY(
ioctl(device_fd_.get(), VIDIOC_QUERY_EXT_CTRL, &query));
extended_query_supported_ = (res == 0);
@@ -79,6 +93,8 @@
device_fd_.reset(); // Includes close().
format_.reset();
max_buffers_ = 0;
+ // Closing the device releases all queued buffers back to the user.
+ gralloc_->unlockAllBuffers();
}
// Helper function. Should be used instead of ioctl throughout this class.
@@ -102,7 +118,7 @@
return -EINVAL;
}
- int32_t type = format_->get_type();
+ int32_t type = format_->type();
if (IoctlLocked(VIDIOC_STREAMON, &type) < 0) {
HAL_LOGE("STREAMON fails: %s", strerror(errno));
return -ENODEV;
@@ -114,11 +130,18 @@
int V4L2Wrapper::StreamOff() {
HAL_LOG_ENTER();
- int32_t type = format_->get_type();
- if (IoctlLocked(VIDIOC_STREAMOFF, &type) < 0) {
+ int32_t type = format_->type();
+ int res = IoctlLocked(VIDIOC_STREAMOFF, &type);
+ // Calling STREAMOFF releases all queued buffers back to the user.
+ int gralloc_res = gralloc_->unlockAllBuffers();
+ if (res < 0) {
HAL_LOGE("STREAMOFF fails: %s", strerror(errno));
return -ENODEV;
}
+ if (gralloc_res < 0) {
+ HAL_LOGE("Failed to unlock all buffers after turning stream off.");
+ return gralloc_res;
+ }
return 0;
}
@@ -256,17 +279,81 @@
// tells V4L2 to switch into userspace buffer mode).
v4l2_requestbuffers req_buffers;
memset(&req_buffers, 0, sizeof(req_buffers));
- req_buffers.type = format_->get_type();
+ req_buffers.type = format_->type();
req_buffers.memory = V4L2_MEMORY_USERPTR;
req_buffers.count = 1;
- if (IoctlLocked(VIDIOC_REQBUFS, &req_buffers) < 0) {
+
+ int res = IoctlLocked(VIDIOC_REQBUFS, &req_buffers);
+ // Calling REQBUFS releases all queued buffers back to the user.
+ int gralloc_res = gralloc_->unlockAllBuffers();
+ if (res < 0) {
HAL_LOGE("REQBUFS failed: %s", strerror(errno));
return -ENODEV;
}
+ if (gralloc_res < 0) {
+ HAL_LOGE("Failed to unlock all buffers when setting up new buffers.");
+ return gralloc_res;
+ }
// V4L2 will set req_buffers.count to a number of buffers it can handle.
max_buffers_ = req_buffers.count;
return 0;
}
+int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer) {
+ HAL_LOG_ENTER();
+
+ // Set up a v4l2 buffer struct.
+ v4l2_buffer device_buffer;
+ memset(&device_buffer, 0, sizeof(device_buffer));
+ device_buffer.type = format_->type();
+
+ // Use QUERYBUF to ensure our buffer/device is in good shape.
+ if (IoctlLocked(VIDIOC_QUERYBUF, &device_buffer) < 0) {
+ HAL_LOGE("QUERYBUF fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ // Configure the device buffer based on the stream buffer.
+ device_buffer.memory = V4L2_MEMORY_USERPTR;
+ // TODO(b/29334616): when this is async, actually limit the number
+ // of buffers used to the known max, and set this according to the
+ // queue length.
+ device_buffer.index = 0;
+ // Lock the buffer for writing.
+ int res =
+ gralloc_->lock(camera_buffer, format_->bytes_per_line(), &device_buffer);
+ if (res) {
+ HAL_LOGE("Gralloc failed to lock buffer.");
+ return res;
+ }
+ if (IoctlLocked(VIDIOC_QBUF, &device_buffer) < 0) {
+ HAL_LOGE("QBUF (%d) fails: %s", 0, strerror(errno));
+ gralloc_->unlock(&device_buffer);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int V4L2Wrapper::DequeueBuffer(v4l2_buffer* buffer) {
+ HAL_LOG_ENTER();
+
+ memset(buffer, 0, sizeof(*buffer));
+ buffer->type = format_->type();
+ buffer->memory = V4L2_MEMORY_USERPTR;
+ if (IoctlLocked(VIDIOC_DQBUF, buffer) < 0) {
+ HAL_LOGE("DQBUF fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ // Now that we're done painting the buffer, we can unlock it.
+ int res = gralloc_->unlock(buffer);
+ if (res) {
+ return res;
+ }
+
+ return 0;
+}
+
} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/V4L2Wrapper.h b/modules/camera/3_4/V4L2Wrapper.h
index 4126e90..5fc8af6 100644
--- a/modules/camera/3_4/V4L2Wrapper.h
+++ b/modules/camera/3_4/V4L2Wrapper.h
@@ -26,11 +26,14 @@
#include "Common.h"
#include "Stream.h"
#include "StreamFormat.h"
+#include "V4L2Gralloc.h"
namespace v4l2_camera_hal {
class V4L2Wrapper {
public:
- V4L2Wrapper(const std::string device_path);
+ // Use this method to create V4L2Wrapper objects. Functionally equivalent
+ // to "new V4L2Wrapper", except that it may return nullptr in case of failure.
+ static V4L2Wrapper* NewV4L2Wrapper(const std::string device_path);
virtual ~V4L2Wrapper();
// Connect or disconnect to the device.
@@ -45,10 +48,18 @@
int SetControl(uint32_t control_id, int32_t desired, int32_t* result);
// Manage format.
int SetFormat(const default_camera_hal::Stream& stream);
+ // Manage buffers.
+ int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer);
+ int DequeueBuffer(v4l2_buffer* buffer);
inline bool connected() { return device_fd_.get() >= 0; }
private:
+ // Constructor is private to allow failing on bad input.
+ // Use NewV4L2Wrapper instead.
+ V4L2Wrapper(const std::string device_path,
+ std::unique_ptr<V4L2Gralloc> gralloc);
+
// Perform an ioctl call in a thread-safe fashion.
template <typename T>
int IoctlLocked(int request, T data);
@@ -59,6 +70,8 @@
const std::string device_path_;
// The opened device fd.
ScopedFd device_fd_;
+ // The underlying gralloc module.
+ std::unique_ptr<V4L2Gralloc> gralloc_;
// Whether or not the device supports the extended control query.
bool extended_query_supported_;
// The format this device is set up for.