Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / media / capture / video / linux / video_capture_device_factory_linux.cc
blobc37ad28a3c71efd5aafdb12b0ccceb84b4760677
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/capture/video/linux/video_capture_device_factory_linux.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #if defined(OS_OPENBSD)
10 #include <sys/videoio.h>
11 #else
12 #include <linux/videodev2.h>
13 #endif
14 #include <sys/ioctl.h>
16 #include "base/files/file_enumerator.h"
17 #include "base/files/scoped_file.h"
18 #include "base/posix/eintr_wrapper.h"
19 #include "base/strings/stringprintf.h"
20 #if defined(OS_CHROMEOS)
21 #include "media/capture/video/linux/video_capture_device_chromeos.h"
22 #endif
23 #include "media/capture/video/linux/video_capture_device_linux.h"
25 namespace media {
27 static bool HasUsableFormats(int fd, uint32 capabilities) {
28 const std::list<uint32_t>& usable_fourccs =
29 VideoCaptureDeviceLinux::GetListOfUsableFourCCs(false);
31 static const struct {
32 int capability;
33 v4l2_buf_type buf_type;
34 } kCapabilityAndBufferTypes[] = {
35 {V4L2_CAP_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_CAPTURE},
36 {V4L2_CAP_VIDEO_CAPTURE_MPLANE, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE}};
38 for (const auto& capability_and_buffer_type : kCapabilityAndBufferTypes) {
39 v4l2_fmtdesc fmtdesc = {};
40 if (capabilities & capability_and_buffer_type.capability) {
41 fmtdesc.type = capability_and_buffer_type.buf_type;
42 for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
43 ++fmtdesc.index) {
44 if (std::find(usable_fourccs.begin(), usable_fourccs.end(),
45 fmtdesc.pixelformat) != usable_fourccs.end())
46 return true;
50 DLOG(ERROR) << "No usable formats found";
51 return false;
54 static std::list<float> GetFrameRateList(int fd,
55 uint32 fourcc,
56 uint32 width,
57 uint32 height) {
58 std::list<float> frame_rates;
60 v4l2_frmivalenum frame_interval = {};
61 frame_interval.pixel_format = fourcc;
62 frame_interval.width = width;
63 frame_interval.height = height;
64 for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_interval)) ==
66 ++frame_interval.index) {
67 if (frame_interval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
68 if (frame_interval.discrete.numerator != 0) {
69 frame_rates.push_back(
70 frame_interval.discrete.denominator /
71 static_cast<float>(frame_interval.discrete.numerator));
73 } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_CONTINUOUS ||
74 frame_interval.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
75 // TODO(mcasas): see http://crbug.com/249953, support these devices.
76 NOTIMPLEMENTED();
77 break;
80 // Some devices, e.g. Kinect, do not enumerate any frame rates, see
81 // http://crbug.com/412284. Set their frame_rate to zero.
82 if (frame_rates.empty())
83 frame_rates.push_back(0);
84 return frame_rates;
87 static void GetSupportedFormatsForV4L2BufferType(
88 int fd,
89 v4l2_buf_type buf_type,
90 media::VideoCaptureFormats* supported_formats) {
91 v4l2_fmtdesc v4l2_format = {};
92 v4l2_format.type = buf_type;
93 for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &v4l2_format)) == 0;
94 ++v4l2_format.index) {
95 VideoCaptureFormat supported_format;
96 supported_format.pixel_format =
97 VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat(
98 v4l2_format.pixelformat);
100 if (supported_format.pixel_format == VIDEO_CAPTURE_PIXEL_FORMAT_UNKNOWN)
101 continue;
103 v4l2_frmsizeenum frame_size = {};
104 frame_size.pixel_format = v4l2_format.pixelformat;
105 for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frame_size)) == 0;
106 ++frame_size.index) {
107 if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
108 supported_format.frame_size.SetSize(frame_size.discrete.width,
109 frame_size.discrete.height);
110 } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE ||
111 frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
112 // TODO(mcasas): see http://crbug.com/249953, support these devices.
113 NOTIMPLEMENTED();
116 const std::list<float> frame_rates = GetFrameRateList(
117 fd, v4l2_format.pixelformat, frame_size.discrete.width,
118 frame_size.discrete.height);
119 for (const auto& frame_rate : frame_rates) {
120 supported_format.frame_rate = frame_rate;
121 supported_formats->push_back(supported_format);
122 DVLOG(1) << VideoCaptureFormat::ToString(supported_format);
128 VideoCaptureDeviceFactoryLinux::VideoCaptureDeviceFactoryLinux(
129 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
130 : ui_task_runner_(ui_task_runner) {
133 VideoCaptureDeviceFactoryLinux::~VideoCaptureDeviceFactoryLinux() {
136 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryLinux::Create(
137 const VideoCaptureDevice::Name& device_name) {
138 DCHECK(thread_checker_.CalledOnValidThread());
139 #if defined(OS_CHROMEOS)
140 VideoCaptureDeviceChromeOS* self =
141 new VideoCaptureDeviceChromeOS(ui_task_runner_, device_name);
142 #else
143 VideoCaptureDeviceLinux* self = new VideoCaptureDeviceLinux(device_name);
144 #endif
145 if (!self)
146 return scoped_ptr<VideoCaptureDevice>();
147 // Test opening the device driver. This is to make sure it is available.
148 // We will reopen it again in our worker thread when someone
149 // allocates the camera.
150 base::ScopedFD fd(HANDLE_EINTR(open(device_name.id().c_str(), O_RDONLY)));
151 if (!fd.is_valid()) {
152 DLOG(ERROR) << "Cannot open device";
153 delete self;
154 return scoped_ptr<VideoCaptureDevice>();
157 return scoped_ptr<VideoCaptureDevice>(self);
160 void VideoCaptureDeviceFactoryLinux::GetDeviceNames(
161 VideoCaptureDevice::Names* const device_names) {
162 DCHECK(thread_checker_.CalledOnValidThread());
163 DCHECK(device_names->empty());
164 const base::FilePath path("/dev/");
165 base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
166 "video*");
168 while (!enumerator.Next().empty()) {
169 const base::FileEnumerator::FileInfo info = enumerator.GetInfo();
170 const std::string unique_id = path.value() + info.GetName().value();
171 const base::ScopedFD fd(HANDLE_EINTR(open(unique_id.c_str(), O_RDONLY)));
172 if (!fd.is_valid()) {
173 DLOG(ERROR) << "Couldn't open " << info.GetName().value();
174 continue;
176 // Test if this is a V4L2 capture device and if it has at least one
177 // supported capture format. Devices that have capture and output
178 // capabilities at the same time are memory-to-memory and are skipped, see
179 // http://crbug.com/139356.
180 v4l2_capability cap;
181 if ((HANDLE_EINTR(ioctl(fd.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
182 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ||
183 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
184 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
185 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) &&
186 HasUsableFormats(fd.get(), cap.capabilities)) {
187 device_names->push_back(VideoCaptureDevice::Name(
188 base::StringPrintf("%s", cap.card), unique_id,
189 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
190 ? VideoCaptureDevice::Name::V4L2_MULTI_PLANE
191 : VideoCaptureDevice::Name::V4L2_SINGLE_PLANE));
196 void VideoCaptureDeviceFactoryLinux::GetDeviceSupportedFormats(
197 const VideoCaptureDevice::Name& device,
198 VideoCaptureFormats* supported_formats) {
199 DCHECK(thread_checker_.CalledOnValidThread());
200 if (device.id().empty())
201 return;
202 base::ScopedFD fd(HANDLE_EINTR(open(device.id().c_str(), O_RDONLY)));
203 if (!fd.is_valid()) // Failed to open this device.
204 return;
205 supported_formats->clear();
207 DCHECK_NE(device.capture_api_type(),
208 VideoCaptureDevice::Name::API_TYPE_UNKNOWN);
209 const v4l2_buf_type buf_type =
210 (device.capture_api_type() == VideoCaptureDevice::Name::V4L2_MULTI_PLANE)
211 ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
212 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
213 GetSupportedFormatsForV4L2BufferType(fd.get(), buf_type, supported_formats);
215 return;
218 // static
219 VideoCaptureDeviceFactory*
220 VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
221 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
222 return new VideoCaptureDeviceFactoryLinux(ui_task_runner);
225 } // namespace media