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"
9 #if defined(OS_OPENBSD)
10 #include <sys/videoio.h>
12 #include <linux/videodev2.h>
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"
23 #include "media/capture/video/linux/video_capture_device_linux.h"
27 static bool HasUsableFormats(int fd
, uint32 capabilities
) {
28 const std::list
<uint32_t>& usable_fourccs
=
29 VideoCaptureDeviceLinux::GetListOfUsableFourCCs(false);
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;
44 if (std::find(usable_fourccs
.begin(), usable_fourccs
.end(),
45 fmtdesc
.pixelformat
) != usable_fourccs
.end())
50 DLOG(ERROR
) << "No usable formats found";
54 static std::list
<float> GetFrameRateList(int fd
,
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.
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);
87 static void GetSupportedFormatsForV4L2BufferType(
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
)
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.
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
);
143 VideoCaptureDeviceLinux
* self
= new VideoCaptureDeviceLinux(device_name
);
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";
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
,
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();
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.
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())
202 base::ScopedFD
fd(HANDLE_EINTR(open(device
.id().c_str(), O_RDONLY
)));
203 if (!fd
.is_valid()) // Failed to open this device.
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
);
219 VideoCaptureDeviceFactory
*
220 VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
221 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
222 return new VideoCaptureDeviceFactoryLinux(ui_task_runner
);