Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(644)

Unified Diff: media/gpu/generic_v4l2_device.cc

Issue 2398883002: Add support for multiple V4L2 video devices of the same type. (Closed)
Patch Set: comments addressed Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/gpu/generic_v4l2_device.cc
diff --git a/media/gpu/generic_v4l2_device.cc b/media/gpu/generic_v4l2_device.cc
index c7d94904a1d3709050d405bd091a1c5774dbc7bc..e98ea72eddc3499a55845abd4011f2c08a36ec21 100644
--- a/media/gpu/generic_v4l2_device.cc
+++ b/media/gpu/generic_v4l2_device.cc
@@ -15,11 +15,13 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <algorithm>
#include <memory>
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "media/gpu/generic_v4l2_device.h"
@@ -41,27 +43,18 @@ static const base::FilePath::CharType kV4l2Lib[] =
namespace media {
-namespace {
-const char kDecoderDevice[] = "/dev/video-dec";
-const char kEncoderDevice[] = "/dev/video-enc";
-const char kImageProcessorDevice[] = "/dev/image-proc0";
-const char kJpegDecoderDevice[] = "/dev/jpeg-dec";
-}
-
-GenericV4L2Device::GenericV4L2Device(Type type) : V4L2Device(type) {
+GenericV4L2Device::GenericV4L2Device() {
#if defined(USE_LIBV4L2)
use_libv4l2_ = false;
#endif
}
GenericV4L2Device::~GenericV4L2Device() {
-#if defined(USE_LIBV4L2)
- if (use_libv4l2_ && device_fd_.is_valid())
- v4l2_close(device_fd_.release());
-#endif
+ CloseDevice();
}
int GenericV4L2Device::Ioctl(int request, void* arg) {
+ DCHECK(device_fd_.is_valid());
#if defined(USE_LIBV4L2)
if (use_libv4l2_)
return HANDLE_EINTR(v4l2_ioctl(device_fd_.get(), request, arg));
@@ -99,6 +92,7 @@ void* GenericV4L2Device::Mmap(void* addr,
int prot,
int flags,
unsigned int offset) {
+ DCHECK(device_fd_.is_valid());
return mmap(addr, len, prot, flags, device_fd_.get(), offset);
}
@@ -136,62 +130,49 @@ bool GenericV4L2Device::ClearDevicePollInterrupt() {
}
bool GenericV4L2Device::Initialize() {
- const char* device_path = NULL;
static bool v4l2_functions_initialized = PostSandboxInitialization();
if (!v4l2_functions_initialized) {
LOG(ERROR) << "Failed to initialize LIBV4L2 libs";
return false;
}
- switch (type_) {
- case kDecoder:
- device_path = kDecoderDevice;
- break;
- case kEncoder:
- device_path = kEncoderDevice;
- break;
- case kImageProcessor:
- device_path = kImageProcessorDevice;
- break;
- case kJpegDecoder:
- device_path = kJpegDecoderDevice;
- break;
- }
+ return true;
+}
- DVLOG(2) << "Initialize(): opening device: " << device_path;
- // Open the video device.
- device_fd_.reset(
- HANDLE_EINTR(open(device_path, O_RDWR | O_NONBLOCK | O_CLOEXEC)));
- if (!device_fd_.is_valid()) {
+bool GenericV4L2Device::Open(Type type, uint32_t v4l2_pixfmt) {
+ std::string path = GetDevicePathFor(type, v4l2_pixfmt);
+
+ if (path.empty()) {
+ DVLOG(1) << "No devices supporting " << std::hex << "0x" << v4l2_pixfmt
+ << " for type: " << static_cast<int>(type);
return false;
}
-#if defined(USE_LIBV4L2)
- if (type_ == kEncoder &&
- HANDLE_EINTR(v4l2_fd_open(device_fd_.get(), V4L2_DISABLE_CONVERSION)) !=
- -1) {
- DVLOG(2) << "Using libv4l2 for " << device_path;
- use_libv4l2_ = true;
+
+ if (!OpenDevicePath(path, type)) {
+ LOG(ERROR) << "Failed opening " << path;
+ return false;
}
-#endif
device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
if (!device_poll_interrupt_fd_.is_valid()) {
+ LOG(ERROR) << "Failed creating a poll interrupt fd";
return false;
}
+
return true;
}
std::vector<base::ScopedFD> GenericV4L2Device::GetDmabufsForV4L2Buffer(
int index,
size_t num_planes,
- enum v4l2_buf_type type) {
- DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type));
+ enum v4l2_buf_type buf_type) {
+ DCHECK(V4L2_TYPE_IS_MULTIPLANAR(buf_type));
std::vector<base::ScopedFD> dmabuf_fds;
for (size_t i = 0; i < num_planes; ++i) {
struct v4l2_exportbuffer expbuf;
memset(&expbuf, 0, sizeof(expbuf));
- expbuf.type = type;
+ expbuf.type = buf_type;
expbuf.index = index;
expbuf.plane = i;
expbuf.flags = O_CLOEXEC;
@@ -307,11 +288,115 @@ GLenum GenericV4L2Device::GetTextureTarget() {
return GL_TEXTURE_EXTERNAL_OES;
}
-uint32_t GenericV4L2Device::PreferredInputFormat() {
- // TODO(posciak): We should support "dontcare" returns here once we
- // implement proper handling (fallback, negotiation) for this in users.
- CHECK_EQ(type_, kEncoder);
- return V4L2_PIX_FMT_NV12M;
+uint32_t GenericV4L2Device::PreferredInputFormat(Type type) {
+ if (type == Type::kEncoder)
+ return V4L2_PIX_FMT_NV12M;
+
+ return 0;
+}
+
+std::vector<uint32_t> GenericV4L2Device::GetSupportedImageProcessorPixelformats(
+ v4l2_buf_type buf_type) {
+ std::vector<uint32_t> supported_pixelformats;
+
+ Type type = Type::kImageProcessor;
+ const auto& devices = GetDevicesForType(type);
+ for (const auto& device : devices) {
+ if (!OpenDevicePath(device.first, type)) {
+ LOG(ERROR) << "Failed opening " << device.first;
+ continue;
+ }
+
+ std::vector<uint32_t> pixelformats =
+ EnumerateSupportedPixelformats(buf_type);
+
+ supported_pixelformats.insert(supported_pixelformats.end(),
+ pixelformats.begin(), pixelformats.end());
+ CloseDevice();
+ }
+
+ return supported_pixelformats;
+}
+
+VideoDecodeAccelerator::SupportedProfiles
+GenericV4L2Device::GetSupportedDecodeProfiles(const size_t num_formats,
+ const uint32_t pixelformats[]) {
+ VideoDecodeAccelerator::SupportedProfiles supported_profiles;
+
+ Type type = Type::kDecoder;
+ const auto& devices = GetDevicesForType(type);
+ for (const auto& device : devices) {
+ if (!OpenDevicePath(device.first, type)) {
+ LOG(ERROR) << "Failed opening " << device.first;
+ continue;
+ }
+
+ const auto& profiles =
+ EnumerateSupportedDecodeProfiles(num_formats, pixelformats);
+ supported_profiles.insert(supported_profiles.end(), profiles.begin(),
+ profiles.end());
+ CloseDevice();
+ }
+
+ return supported_profiles;
+}
+
+VideoEncodeAccelerator::SupportedProfiles
+GenericV4L2Device::GetSupportedEncodeProfiles() {
+ VideoEncodeAccelerator::SupportedProfiles supported_profiles;
+
+ Type type = Type::kEncoder;
+ const auto& devices = GetDevicesForType(type);
+ for (const auto& device : devices) {
+ if (!OpenDevicePath(device.first, type)) {
+ LOG(ERROR) << "Failed opening " << device.first;
+ continue;
+ }
+
+ const auto& profiles = EnumerateSupportedEncodeProfiles();
+ supported_profiles.insert(supported_profiles.end(), profiles.begin(),
+ profiles.end());
+ CloseDevice();
+ }
+
+ return supported_profiles;
+}
+
+bool GenericV4L2Device::IsImageProcessingSupported() {
+ const auto& devices = GetDevicesForType(Type::kImageProcessor);
+ return !devices.empty();
+}
+
+bool GenericV4L2Device::IsJpegDecodingSupported() {
+ const auto& devices = GetDevicesForType(Type::kJpegDecoder);
+ return !devices.empty();
+}
+
+bool GenericV4L2Device::OpenDevicePath(const std::string& path, Type type) {
+ DCHECK(!device_fd_.is_valid());
+
+ device_fd_.reset(
+ HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC)));
+ if (!device_fd_.is_valid())
+ return false;
+
+#if defined(USE_LIBV4L2)
+ if (type == Type::kEncoder &&
+ HANDLE_EINTR(v4l2_fd_open(device_fd_.get(), V4L2_DISABLE_CONVERSION)) !=
+ -1) {
+ DVLOG(2) << "Using libv4l2 for " << path;
+ use_libv4l2_ = true;
+ }
+#endif
+ return true;
+}
+
+void GenericV4L2Device::CloseDevice() {
+#if defined(USE_LIBV4L2)
+ if (use_libv4l2_ && device_fd_.is_valid())
+ v4l2_close(device_fd_.release());
+#endif
+ device_fd_.reset();
}
// static
@@ -326,4 +411,85 @@ bool GenericV4L2Device::PostSandboxInitialization() {
#endif
}
+void GenericV4L2Device::EnumerateDevicesForType(Type type) {
+ static const std::string kDecoderDevicePattern = "/dev/video-dec";
+ static const std::string kEncoderDevicePattern = "/dev/video-enc";
+ static const std::string kImageProcessorDevicePattern = "/dev/image-proc";
+ static const std::string kJpegDecoderDevicePattern = "/dev/jpeg-dec";
+
+ std::string device_pattern;
+ v4l2_buf_type buf_type;
+ switch (type) {
+ case Type::kDecoder:
+ device_pattern = kDecoderDevicePattern;
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ break;
+ case Type::kEncoder:
+ device_pattern = kEncoderDevicePattern;
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ break;
+ case Type::kImageProcessor:
+ device_pattern = kImageProcessorDevicePattern;
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ break;
+ case Type::kJpegDecoder:
+ device_pattern = kJpegDecoderDevicePattern;
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ break;
+ }
+
+ std::vector<std::string> candidate_paths;
+
+ // TODO(posciak): Remove this legacy unnumbered device once
+ // all platforms are updated to use numbered devices.
+ candidate_paths.push_back(device_pattern);
+
+ // We are sandboxed, so we can't query directory contents to check which
+ // devices are actually available. Try to open the first 10; if not present,
+ // we will just fail to open immediately.
+ for (int i = 0; i < 10; ++i) {
+ candidate_paths.push_back(
+ base::StringPrintf("%s%d", device_pattern.c_str(), i));
+ }
+
+ Devices devices;
+ for (const auto& path : candidate_paths) {
+ if (!OpenDevicePath(path, type))
+ continue;
+
+ const auto& supported_pixelformats =
+ EnumerateSupportedPixelformats(buf_type);
+ if (!supported_pixelformats.empty()) {
+ DVLOG(1) << "Found device: " << path;
+ devices.push_back(std::make_pair(path, supported_pixelformats));
+ }
+
+ CloseDevice();
+ }
+
+ DCHECK_EQ(devices_by_type_.count(type), 0u);
+ devices_by_type_[type] = devices;
+}
+
+const GenericV4L2Device::Devices& GenericV4L2Device::GetDevicesForType(
+ Type type) {
+ if (devices_by_type_.count(type) == 0)
+ EnumerateDevicesForType(type);
+
+ DCHECK_NE(devices_by_type_.count(type), 0u);
+ return devices_by_type_[type];
+}
+
+std::string GenericV4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) {
+ const Devices& devices = GetDevicesForType(type);
+
+ for (const auto& device : devices) {
+ if (std::find(device.second.begin(), device.second.end(), pixfmt) !=
+ device.second.end())
+ return device.first;
+ }
+
+ return std::string();
+}
+
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698