Add format querying.
Query formats, sizes, and frame rates.
BUG: https://b/29394024, https://b/30605346
TEST: Test java program shows a bunch of formats & rates,
can take pictures in different sizes now.
Change-Id: I203fd8f200dda79cf6946c58f130548b05269fbd
diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp
index f8c1c61..87a7a31 100644
--- a/modules/camera/3_4/v4l2_camera.cpp
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -51,7 +51,7 @@
V4L2Camera* V4L2Camera::NewV4L2Camera(int id, const std::string path) {
HAL_LOG_ENTER();
- std::unique_ptr<V4L2Wrapper> v4l2_wrapper(V4L2Wrapper::NewV4L2Wrapper(path));
+ std::shared_ptr<V4L2Wrapper> v4l2_wrapper(V4L2Wrapper::NewV4L2Wrapper(path));
if (!v4l2_wrapper) {
HAL_LOGE("Failed to initialize V4L2 wrapper.");
return nullptr;
@@ -60,7 +60,7 @@
return new V4L2Camera(id, std::move(v4l2_wrapper));
}
-V4L2Camera::V4L2Camera(int id, std::unique_ptr<V4L2Wrapper> v4l2_wrapper)
+V4L2Camera::V4L2Camera(int id, std::shared_ptr<V4L2Wrapper> v4l2_wrapper)
: default_camera_hal::Camera(id),
mV4L2Device(std::move(v4l2_wrapper)),
mTemplatesInitialized(false),
@@ -75,10 +75,15 @@
int V4L2Camera::connect() {
HAL_LOG_ENTER();
- int res = mV4L2Device->Connect();
- if (res) {
+ if (mConnection) {
+ HAL_LOGE("Already connected. Please disconnect and try again.");
+ return -EIO;
+ }
+
+ mConnection.reset(new V4L2Wrapper::Connection(mV4L2Device));
+ if (mConnection->status()) {
HAL_LOGE("Failed to connect to device.");
- return res;
+ return mConnection->status();
}
// TODO(b/29185945): confirm this is a supported device.
@@ -96,7 +101,7 @@
void V4L2Camera::disconnect() {
HAL_LOG_ENTER();
- mV4L2Device->Disconnect();
+ mConnection.reset();
// TODO(b/29158098): Inform service of any flashes that are available again
// because this camera is no longer in use.
@@ -1460,35 +1465,77 @@
mVideoStabilizationModes.push_back(
ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF);
- // Need to support YUV_420_888 and JPEG.
- // TODO(b/29394024): query VIDIOC_ENUM_FMT.
- std::vector<uint32_t> formats = {{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420}};
- // We want to find the smallest maximum frame duration amongst formats.
- mMaxFrameDuration = std::numeric_limits<int64_t>::max();
- int64_t min_yuv_frame_duration = std::numeric_limits<int64_t>::max();
- for (auto format : formats) {
- int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(format);
+ // Need to be connected to query the device.
+ V4L2Wrapper::Connection temp_connection(mV4L2Device);
+ if (temp_connection.status()) {
+ HAL_LOGE("Failed to connect to device.");
+ return temp_connection.status();
+ }
+
+ // Get all supported formats.
+ std::set<uint32_t> v4l2_formats;
+ int res = mV4L2Device->GetFormats(&v4l2_formats);
+ if (res) {
+ HAL_LOGE("Failed to get device formats.");
+ return res;
+ }
+ std::set<int32_t> hal_formats;
+ for (auto v4l2_format : v4l2_formats) {
+ int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(v4l2_format);
if (hal_format < 0) {
// Unrecognized/unused format. Skip it.
continue;
}
- // TODO(b/29394024): query VIDIOC_ENUM_FRAMESIZES.
- // For now, just 640x480.
- ArrayVector<int32_t, 2> frame_sizes;
- frame_sizes.push_back({{640, 480}});
- size_t num_frame_sizes = frame_sizes.num_arrays();
- for (size_t i = 0; i < num_frame_sizes; ++i) {
- const int32_t* frame_size = frame_sizes[i];
+ hal_formats.insert(hal_format);
+ }
+ // In addition to well-defined formats, must support "Implementation Defined"
+ // (in this case what that means is managed by the StreamFormat class).
+ hal_formats.insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+
+ // Requirements check: need to support YCbCr_420_888 and JPEG.
+ if (hal_formats.find(HAL_PIXEL_FORMAT_YCbCr_420_888) == hal_formats.end()) {
+ HAL_LOGE("YCbCr_420_888 not supported by device.");
+ return -ENODEV;
+ } else if (hal_formats.find(HAL_PIXEL_FORMAT_BLOB) == hal_formats.end()) {
+ HAL_LOGE("JPEG not supported by device.");
+ return -ENODEV;
+ }
+
+ // Find sizes and frame durations for all formats.
+ // We also want to find the smallest max frame duration amongst all formats.
+ mMaxFrameDuration = std::numeric_limits<int64_t>::max();
+ int64_t min_yuv_frame_duration = std::numeric_limits<int64_t>::max();
+ for (auto hal_format : hal_formats) {
+ uint32_t v4l2_format = StreamFormat::HalToV4L2PixelFormat(hal_format);
+ if (v4l2_format == 0) {
+ // Unrecognized/unused format. Should never happen since hal_formats
+ // came from translating a bunch of V4L2 formats above.
+ HAL_LOGE("Couldn't find V4L2 format for HAL format %d", hal_format);
+ return -ENODEV;
+ }
+
+ std::set<std::array<int32_t, 2>> frame_sizes;
+ res = mV4L2Device->GetFormatFrameSizes(v4l2_format, &frame_sizes);
+ if (res) {
+ HAL_LOGE("Failed to get all frame sizes for format %d", v4l2_format);
+ return res;
+ }
+
+ for (const auto& frame_size : frame_sizes) {
mStreamConfigs.push_back({{hal_format, frame_size[0], frame_size[1],
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}});
- // TODO(b/29394024): query VIDIOC_ENUM_FRAMEINTERVALS.
- // For now, using the emulator max value of .3 sec.
- int64_t max_frame_duration = 300000000;
- // For now, min value of 30 fps = 1/30 spf ~= 33333333 ns.
- // For whatever reason the goldfish/qcom cameras report this as
- // 33331760, so copying that.
- int64_t min_frame_duration = 33331760;
+ std::array<int64_t, 2> duration_range;
+ res = mV4L2Device->GetFormatFrameDurationRange(
+ v4l2_format, frame_size, &duration_range);
+ if (res) {
+ HAL_LOGE("Failed to get frame duration range for format %d, "
+ "size %u x %u", v4l2_format, frame_size[0], frame_size[1]);
+ return res;
+ }
+
+ int64_t min_frame_duration = duration_range[0];
+ int64_t max_frame_duration = duration_range[1];
mMinFrameDurations.push_back(
{{hal_format, frame_size[0], frame_size[1], min_frame_duration}});
diff --git a/modules/camera/3_4/v4l2_camera.h b/modules/camera/3_4/v4l2_camera.h
index 17770b6..a4621f8 100644
--- a/modules/camera/3_4/v4l2_camera.h
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -46,7 +46,7 @@
private:
// Constructor private to allow failing on bad input.
// Use NewV4L2Camera instead.
- V4L2Camera(int id, std::unique_ptr<V4L2Wrapper> v4l2_wrapper);
+ V4L2Camera(int id, std::shared_ptr<V4L2Wrapper> v4l2_wrapper);
// default_camera_hal::Camera virtual methods.
// Connect to the device: open dev nodes, etc.
@@ -77,7 +77,8 @@
int getResultSettings(camera_metadata_t** metadata, uint64_t* timestamp);
// V4L2 helper.
- std::unique_ptr<V4L2Wrapper> mV4L2Device;
+ std::shared_ptr<V4L2Wrapper> mV4L2Device;
+ std::unique_ptr<V4L2Wrapper::Connection> mConnection;
bool mTemplatesInitialized;
int initTemplates();
diff --git a/modules/camera/3_4/v4l2_gralloc.cpp b/modules/camera/3_4/v4l2_gralloc.cpp
index bb708c9..5812649 100644
--- a/modules/camera/3_4/v4l2_gralloc.cpp
+++ b/modules/camera/3_4/v4l2_gralloc.cpp
@@ -87,7 +87,7 @@
int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer,
uint32_t bytes_per_line,
- /*out*/ v4l2_buffer* device_buffer) {
+ v4l2_buffer* device_buffer) {
HAL_LOG_ENTER();
// Lock the camera buffer (varies depending on if the buffer is YUV or not).
@@ -95,7 +95,6 @@
camera_buffer, nullptr, bytes_per_line});
buffer_handle_t buffer = *camera_buffer->buffer;
void* data;
- size_t size;
camera3_stream_t* stream = camera_buffer->stream;
switch(stream->format) {
// TODO(b/30119452): support more YCbCr formats.
@@ -103,8 +102,6 @@
android_ycbcr yuv_data;
mModule->lock_ycbcr(mModule, buffer, stream->usage, 0, 0,
stream->width, stream->height, &yuv_data);
- // YUV_420_888 = 1.5 planes (1 Y plane, 1/4 Cb, 1/4 Cr).
- size = bytes_per_line * stream->height * 3 / 2;
// Check if gralloc format matches v4l2 format
// (same padding, not interleaved, contiguous).
@@ -124,16 +121,15 @@
// If not, allocate a contiguous buffer of appropriate size
// (to be transformed back upon unlock).
HAL_LOGV("Need to transform V4L2 YUV to gralloc YUV.");
- data = new uint8_t[size];
+ data = new uint8_t[device_buffer->length];
// Make a dynamically-allocated copy of yuv_data,
// since it will be needed at transform time.
buffer_data->transform_dest.reset(new android_ycbcr(yuv_data));
}
break;
case HAL_PIXEL_FORMAT_BLOB:
- // Jpeg buffers are just contiguous blobs; always max_size * 1.
- size = V4L2_MAX_JPEG_SIZE;
- mModule->lock(mModule, buffer, stream->usage, 0, 0, size, 1, &data);
+ // Jpeg buffers are just contiguous blobs; lock length * 1.
+ mModule->lock(mModule, buffer, stream->usage, 0, 0, device_buffer->length, 1, &data);
break;
default:
return -EINVAL;
@@ -144,7 +140,6 @@
"void* must be able to fit in the v4l2_buffer m.userptr "
"field (unsigned long) for this code to work");
device_buffer->m.userptr = reinterpret_cast<unsigned long>(data);
- device_buffer->length = size;
// Note the mapping of data:buffer info for when unlock is called.
mBufferMap.emplace(data, buffer_data.release());
diff --git a/modules/camera/3_4/v4l2_gralloc.h b/modules/camera/3_4/v4l2_gralloc.h
index 4a49b9b..7ece59d 100644
--- a/modules/camera/3_4/v4l2_gralloc.h
+++ b/modules/camera/3_4/v4l2_gralloc.h
@@ -27,8 +27,8 @@
namespace v4l2_camera_hal {
-// Generously allow up to 3MB.
-static constexpr size_t V4L2_MAX_JPEG_SIZE = 3000000;
+// Generously allow up to 6MB (the largest JPEG on the RPi camera is about 5MB).
+static constexpr size_t V4L2_MAX_JPEG_SIZE = 6000000;
// V4L2Gralloc is a wrapper around relevant parts of a gralloc module,
// with some assistive transformations.
@@ -39,7 +39,7 @@
static V4L2Gralloc* NewV4L2Gralloc();
virtual ~V4L2Gralloc();
- // Lock a camera buffer. Sets device buffer user pointer and length.
+ // Lock a camera buffer. Uses device buffer length, sets user pointer.
int lock(const camera3_stream_buffer_t* camera_buffer,
uint32_t bytes_per_line,
v4l2_buffer* device_buffer);
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
index d331a77..7d8e38c 100644
--- a/modules/camera/3_4/v4l2_wrapper.cpp
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -16,6 +16,10 @@
#include "v4l2_wrapper.h"
+#include <algorithm>
+#include <limits>
+#include <vector>
+
#include <fcntl.h>
#include <linux/videodev2.h>
#include <sys/stat.h>
@@ -32,6 +36,9 @@
namespace v4l2_camera_hal {
+const std::vector<std::array<int32_t, 2>> kStandardSizes(
+ {{{1920, 1080}}, {{1280, 720}}, {{640, 480}}, {{320, 240}}});
+
V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
HAL_LOG_ENTER();
@@ -48,7 +55,8 @@
std::unique_ptr<V4L2Gralloc> gralloc)
: device_path_(std::move(device_path)),
gralloc_(std::move(gralloc)),
- max_buffers_(0) {
+ max_buffers_(0),
+ connection_count_(0) {
HAL_LOG_ENTER();
}
@@ -56,28 +64,26 @@
int V4L2Wrapper::Connect() {
HAL_LOG_ENTER();
- std::lock_guard<std::mutex> lock(device_lock_);
+ std::lock_guard<std::mutex> lock(connection_lock_);
if (connected()) {
- HAL_LOGE("Camera device %s is already connected. Close it first",
- device_path_.c_str());
- return -EIO;
+ HAL_LOGV("Camera device %s is already connected.", device_path_.c_str());
+ ++connection_count_;
+ return 0;
}
int fd = TEMP_FAILURE_RETRY(open(device_path_.c_str(), O_RDWR));
if (fd < 0) {
HAL_LOGE("failed to open %s (%s)", device_path_.c_str(), strerror(errno));
- return -errno;
+ return -ENODEV;
}
device_fd_.reset(fd);
+ ++connection_count_;
// 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.
- int res = TEMP_FAILURE_RETRY(
- ioctl(device_fd_.get(), VIDIOC_QUERY_EXT_CTRL, &query));
- extended_query_supported_ = (res == 0);
+ extended_query_supported_ = (IoctlLocked(VIDIOC_QUERY_EXT_CTRL, &query) == 0);
// TODO(b/29185945): confirm this is a supported device.
// This is checked by the HAL, but the device at device_path_ may
@@ -90,7 +96,21 @@
void V4L2Wrapper::Disconnect() {
HAL_LOG_ENTER();
- std::lock_guard<std::mutex> lock(device_lock_);
+ std::lock_guard<std::mutex> lock(connection_lock_);
+
+ if (connection_count_ == 0) {
+ // Not connected.
+ HAL_LOGE("Camera device %s is not connected, cannot disconnect.",
+ device_path_.c_str(), connection_count_);
+ return;
+ }
+
+ --connection_count_;
+ if (connection_count_ > 0) {
+ HAL_LOGV("Disconnected from camera device %s. %d connections remain.",
+ device_path_.c_str(), connection_count_);
+ return;
+ }
device_fd_.reset(); // Includes close().
format_.reset();
@@ -102,7 +122,7 @@
// Helper function. Should be used instead of ioctl throughout this class.
template <typename T>
int V4L2Wrapper::IoctlLocked(int request, T data) {
- HAL_LOG_ENTER();
+ // Potentially called so many times logging entry is a bad idea.
std::lock_guard<std::mutex> lock(device_lock_);
if (!connected()) {
@@ -247,6 +267,136 @@
return 0;
}
+int V4L2Wrapper::GetFormats(std::set<uint32_t>* v4l2_formats) {
+ HAL_LOG_ENTER();
+
+ v4l2_fmtdesc format_query;
+ memset(&format_query, 0, sizeof(format_query));
+ // TODO(b/30000211): multiplanar support.
+ format_query.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ while (IoctlLocked(VIDIOC_ENUM_FMT, &format_query) >= 0) {
+ v4l2_formats->insert(format_query.pixelformat);
+ ++format_query.index;
+ }
+
+ if (errno != EINVAL) {
+ HAL_LOGE("ENUM_FMT fails at index %d: %s", format_query.index,
+ strerror(errno));
+ return -ENODEV;
+ }
+ return 0;
+}
+
+int V4L2Wrapper::GetFormatFrameSizes(uint32_t v4l2_format,
+ std::set<std::array<int32_t, 2>>* sizes) {
+ HAL_LOG_ENTER();
+
+ v4l2_frmsizeenum size_query;
+ memset(&size_query, 0, sizeof(size_query));
+ size_query.pixel_format = v4l2_format;
+ if (IoctlLocked(VIDIOC_ENUM_FRAMESIZES, &size_query) < 0) {
+ HAL_LOGE("ENUM_FRAMESIZES failed: %s", strerror(errno));
+ return -ENODEV;
+ }
+ if (size_query.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ // Discrete: enumerate all sizes using VIDIOC_ENUM_FRAMESIZES.
+ // Assuming that a driver with discrete frame sizes has a reasonable number
+ // of them.
+ do {
+ sizes->insert({{{static_cast<int32_t>(size_query.discrete.width),
+ static_cast<int32_t>(size_query.discrete.height)}}});
+ ++size_query.index;
+ } while (IoctlLocked(VIDIOC_ENUM_FRAMESIZES, &size_query) >= 0);
+ if (errno != EINVAL) {
+ HAL_LOGE("ENUM_FRAMESIZES fails at index %d: %s", size_query.index,
+ strerror(errno));
+ return -ENODEV;
+ }
+ } else {
+ // Continuous/Step-wise: based on the stepwise struct returned by the query.
+ // Fully listing all possible sizes, with large enough range/small enough
+ // step size, may produce far too many potential sizes. Instead, find the
+ // closest to a set of standard sizes plus largest possible.
+ sizes->insert({{{static_cast<int32_t>(size_query.stepwise.max_width),
+ static_cast<int32_t>(size_query.stepwise.max_height)}}});
+ for (const auto& size : kStandardSizes) {
+ // Find the closest size, rounding up.
+ uint32_t desired_width = size[0];
+ uint32_t desired_height = size[1];
+ if (desired_width < size_query.stepwise.min_width ||
+ desired_height < size_query.stepwise.min_height) {
+ HAL_LOGV("Standard size %u x %u is too small for format %d",
+ desired_width, desired_height, v4l2_format);
+ continue;
+ } else if (desired_width > size_query.stepwise.max_width &&
+ desired_height > size_query.stepwise.max_height) {
+ HAL_LOGV("Standard size %u x %u is too big for format %d",
+ desired_width, desired_height, v4l2_format);
+ continue;
+ }
+
+ // Round up.
+ uint32_t width_steps = (desired_width - size_query.stepwise.min_width +
+ size_query.stepwise.step_width - 1) /
+ size_query.stepwise.step_width;
+ uint32_t height_steps = (desired_height - size_query.stepwise.min_height +
+ size_query.stepwise.step_height - 1) /
+ size_query.stepwise.step_height;
+ sizes->insert(
+ {{{static_cast<int32_t>(size_query.stepwise.min_width +
+ width_steps * size_query.stepwise.step_width),
+ static_cast<int32_t>(size_query.stepwise.min_height +
+ height_steps *
+ size_query.stepwise.step_height)}}});
+ }
+ }
+ return 0;
+}
+
+// Converts a v4l2_fract with units of seconds to an int64_t with units of ns.
+inline int64_t FractToNs(const v4l2_fract& fract) {
+ return (1000000000LL * fract.numerator) / fract.denominator;
+}
+
+int V4L2Wrapper::GetFormatFrameDurationRange(
+ uint32_t v4l2_format, const std::array<int32_t, 2>& size,
+ std::array<int64_t, 2>* duration_range) {
+ // Potentially called so many times logging entry is a bad idea.
+
+ v4l2_frmivalenum duration_query;
+ memset(&duration_query, 0, sizeof(duration_query));
+ duration_query.pixel_format = v4l2_format;
+ duration_query.width = size[0];
+ duration_query.height = size[1];
+ if (IoctlLocked(VIDIOC_ENUM_FRAMEINTERVALS, &duration_query) < 0) {
+ HAL_LOGE("ENUM_FRAMEINTERVALS failed: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ int64_t min = std::numeric_limits<int64_t>::max();
+ int64_t max = std::numeric_limits<int64_t>::min();
+ if (duration_query.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ // Discrete: enumerate all durations using VIDIOC_ENUM_FRAMEINTERVALS.
+ do {
+ min = std::min(min, FractToNs(duration_query.discrete));
+ max = std::max(max, FractToNs(duration_query.discrete));
+ ++duration_query.index;
+ } while (IoctlLocked(VIDIOC_ENUM_FRAMEINTERVALS, &duration_query) >= 0);
+ if (errno != EINVAL) {
+ HAL_LOGE("ENUM_FRAMEINTERVALS fails at index %d: %s",
+ duration_query.index, strerror(errno));
+ return -ENODEV;
+ }
+ } else {
+ // Continuous/Step-wise: simply convert the given min and max.
+ min = FractToNs(duration_query.stepwise.min);
+ max = FractToNs(duration_query.stepwise.max);
+ }
+ (*duration_range)[0] = min;
+ (*duration_range)[1] = max;
+ return 0;
+}
+
int V4L2Wrapper::SetFormat(const default_camera_hal::Stream& stream,
uint32_t* result_max_buffers) {
HAL_LOG_ENTER();
@@ -342,20 +492,19 @@
v4l2_buffer device_buffer;
memset(&device_buffer, 0, sizeof(device_buffer));
device_buffer.type = format_->type();
+ // 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;
- // Use QUERYBUF to ensure our buffer/device is in good shape.
+ // Use QUERYBUF to ensure our buffer/device is in good shape,
+ // and fill out remaining fields.
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.
+ // Lock the buffer for writing (fills in the user pointer field).
int res =
gralloc_->lock(camera_buffer, format_->bytes_per_line(), &device_buffer);
if (res) {
@@ -363,7 +512,7 @@
return res;
}
if (IoctlLocked(VIDIOC_QBUF, &device_buffer) < 0) {
- HAL_LOGE("QBUF (%d) fails: %s", 0, strerror(errno));
+ HAL_LOGE("QBUF fails: %s", strerror(errno));
gralloc_->unlock(&device_buffer);
return -ENODEV;
}
diff --git a/modules/camera/3_4/v4l2_wrapper.h b/modules/camera/3_4/v4l2_wrapper.h
index 113499a..da38e5a 100644
--- a/modules/camera/3_4/v4l2_wrapper.h
+++ b/modules/camera/3_4/v4l2_wrapper.h
@@ -17,8 +17,10 @@
#ifndef V4L2_CAMERA_HAL_V4L2_WRAPPER_H_
#define V4L2_CAMERA_HAL_V4L2_WRAPPER_H_
+#include <array>
#include <memory>
#include <mutex>
+#include <set>
#include <string>
#include <nativehelper/ScopedFd.h>
@@ -36,9 +38,20 @@
static V4L2Wrapper* NewV4L2Wrapper(const std::string device_path);
virtual ~V4L2Wrapper();
- // Connect or disconnect to the device.
- int Connect();
- void Disconnect();
+ // Helper class to ensure all opened connections are closed.
+ class Connection {
+ public:
+ Connection(std::shared_ptr<V4L2Wrapper> device)
+ : device_(std::move(device)), connect_result_(device_->Connect()) {}
+ ~Connection() { if (connect_result_ == 0) device_->Disconnect(); }
+ // Check whether the connection succeeded or not.
+ inline int status() const { return connect_result_; }
+
+ private:
+ std::shared_ptr<V4L2Wrapper> device_;
+ const int connect_result_;
+ };
+
// Turn the stream on or off.
int StreamOn();
int StreamOff();
@@ -48,26 +61,37 @@
int SetControl(uint32_t control_id, int32_t desired,
int32_t* result = nullptr);
// Manage format.
+ int GetFormats(std::set<uint32_t>* v4l2_formats);
+ int GetFormatFrameSizes(uint32_t v4l2_format,
+ std::set<std::array<int32_t, 2>>* sizes);
+ // Durations are returned in ns.
+ int GetFormatFrameDurationRange(uint32_t v4l2_format,
+ const std::array<int32_t, 2>& size,
+ std::array<int64_t, 2>* duration_range);
int SetFormat(const default_camera_hal::Stream& stream,
uint32_t* result_max_buffers);
// 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);
+ // Connect or disconnect to the device. Access by creating/destroying
+ // a V4L2Wrapper::Connection object.
+ int Connect();
+ void Disconnect();
// Perform an ioctl call in a thread-safe fashion.
template <typename T>
int IoctlLocked(int request, T data);
// Adjust buffers any time a device is connected/reformatted.
int SetupBuffers();
+ inline bool connected() { return device_fd_.get() >= 0; }
+
// The camera device path. For example, /dev/video0.
const std::string device_path_;
// The opened device fd.
@@ -82,7 +106,12 @@
uint32_t max_buffers_;
// Lock protecting use of the device.
std::mutex device_lock_;
+ // Lock protecting connecting/disconnecting the device.
+ std::mutex connection_lock_;
+ // Reference count connections.
+ int connection_count_;
+ friend class Connection;
friend class V4L2WrapperMock;
DISALLOW_COPY_AND_ASSIGN(V4L2Wrapper);