1 // Copyright 2015 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/v4l2_capture_delegate.h"
12 #include "base/bind.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/stringprintf.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "media/capture/video/linux/v4l2_capture_delegate_multi_plane.h"
18 #include "media/capture/video/linux/v4l2_capture_delegate_single_plane.h"
19 #include "media/capture/video/linux/video_capture_device_linux.h"
23 // Desired number of video buffers to allocate. The actual number of allocated
24 // buffers by v4l2 driver can be higher or lower than this number.
25 // kNumVideoBuffers should not be too small, or Chrome may not return enough
26 // buffers back to driver in time.
27 const uint32 kNumVideoBuffers
= 4;
28 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw.
29 const int kCaptureTimeoutMs
= 200;
30 // The number of continuous timeouts tolerated before treated as error.
31 const int kContinuousTimeoutLimit
= 10;
32 // MJPEG is preferred if the requested width or height is larger than this.
33 const int kMjpegWidth
= 640;
34 const int kMjpegHeight
= 480;
35 // Typical framerate, in fps
36 const int kTypicalFramerate
= 30;
38 // V4L2 color formats supported by V4L2CaptureDelegate derived classes.
39 // This list is ordered by precedence of use -- but see caveats for MJPEG.
42 VideoCapturePixelFormat pixel_format
;
44 } const kSupportedFormatsAndPlanarity
[] = {
45 {V4L2_PIX_FMT_YUV420
, VIDEO_CAPTURE_PIXEL_FORMAT_I420
, 1},
46 {V4L2_PIX_FMT_YUYV
, VIDEO_CAPTURE_PIXEL_FORMAT_YUY2
, 1},
47 {V4L2_PIX_FMT_UYVY
, VIDEO_CAPTURE_PIXEL_FORMAT_UYVY
, 1},
48 {V4L2_PIX_FMT_RGB24
, VIDEO_CAPTURE_PIXEL_FORMAT_RGB24
, 1},
49 #if !defined(OS_OPENBSD)
50 // TODO(mcasas): add V4L2_PIX_FMT_YVU420M when available in bots.
51 {V4L2_PIX_FMT_YUV420M
, VIDEO_CAPTURE_PIXEL_FORMAT_I420
, 3},
53 // MJPEG is usually sitting fairly low since we don't want to have to
55 // However, is needed for large resolutions due to USB bandwidth
57 // so GetListOfUsableFourCcs() can duplicate it on top, see that method.
58 {V4L2_PIX_FMT_MJPEG
, VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG
, 1},
59 // JPEG works as MJPEG on some gspca webcams from field reports, see
60 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the
63 {V4L2_PIX_FMT_JPEG
, VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG
, 1},
67 scoped_refptr
<V4L2CaptureDelegate
>
68 V4L2CaptureDelegate::CreateV4L2CaptureDelegate(
69 const VideoCaptureDevice::Name
& device_name
,
70 const scoped_refptr
<base::SingleThreadTaskRunner
>& v4l2_task_runner
,
71 int power_line_frequency
) {
72 switch (device_name
.capture_api_type()) {
73 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE
:
74 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane(
75 device_name
, v4l2_task_runner
, power_line_frequency
));
76 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE
:
77 #if !defined(OS_OPENBSD)
78 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane(
79 device_name
, v4l2_task_runner
, power_line_frequency
));
82 NOTIMPLEMENTED() << "Unknown V4L2 capture API type";
83 return scoped_refptr
<V4L2CaptureDelegate
>();
88 size_t V4L2CaptureDelegate::GetNumPlanesForFourCc(uint32_t fourcc
) {
89 for (const auto& fourcc_and_pixel_format
: kSupportedFormatsAndPlanarity
) {
90 if (fourcc_and_pixel_format
.fourcc
== fourcc
)
91 return fourcc_and_pixel_format
.num_planes
;
93 DVLOG(1) << "Unknown fourcc " << FourccToString(fourcc
);
98 VideoCapturePixelFormat
V4L2CaptureDelegate::V4l2FourCcToChromiumPixelFormat(
99 uint32_t v4l2_fourcc
) {
100 for (const auto& fourcc_and_pixel_format
: kSupportedFormatsAndPlanarity
) {
101 if (fourcc_and_pixel_format
.fourcc
== v4l2_fourcc
)
102 return fourcc_and_pixel_format
.pixel_format
;
104 // Not finding a pixel format is OK during device capabilities enumeration.
105 // Let the caller decide if VIDEO_CAPTURE_PIXEL_FORMAT_UNKNOWN is an error or
107 DVLOG(1) << "Unsupported pixel format: " << FourccToString(v4l2_fourcc
);
108 return VIDEO_CAPTURE_PIXEL_FORMAT_UNKNOWN
;
112 std::list
<uint32_t> V4L2CaptureDelegate::GetListOfUsableFourCcs(
114 std::list
<uint32_t> supported_formats
;
115 for (const auto& format
: kSupportedFormatsAndPlanarity
)
116 supported_formats
.push_back(format
.fourcc
);
118 // Duplicate MJPEG on top of the list depending on |prefer_mjpeg|.
120 supported_formats
.push_front(V4L2_PIX_FMT_MJPEG
);
122 return supported_formats
;
126 std::string
V4L2CaptureDelegate::FourccToString(uint32_t fourcc
) {
127 return base::StringPrintf("%c%c%c%c", fourcc
& 0xFF, (fourcc
>> 8) & 0xFF,
128 (fourcc
>> 16) & 0xFF, (fourcc
>> 24) & 0xFF);
131 V4L2CaptureDelegate::BufferTracker::BufferTracker() {
134 V4L2CaptureDelegate::BufferTracker::~BufferTracker() {
135 for (const auto& plane
: planes_
) {
136 if (plane
.start
== nullptr)
138 const int result
= munmap(plane
.start
, plane
.length
);
139 PLOG_IF(ERROR
, result
< 0) << "Error munmap()ing V4L2 buffer";
143 void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start
,
147 plane
.length
= length
;
148 plane
.payload_size
= 0;
149 planes_
.push_back(plane
);
152 V4L2CaptureDelegate::V4L2CaptureDelegate(
153 const VideoCaptureDevice::Name
& device_name
,
154 const scoped_refptr
<base::SingleThreadTaskRunner
>& v4l2_task_runner
,
155 int power_line_frequency
)
156 : capture_type_((device_name
.capture_api_type() ==
157 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE
)
158 ? V4L2_BUF_TYPE_VIDEO_CAPTURE
159 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
),
160 v4l2_task_runner_(v4l2_task_runner
),
161 device_name_(device_name
),
162 power_line_frequency_(power_line_frequency
),
163 is_capturing_(false),
168 V4L2CaptureDelegate::~V4L2CaptureDelegate() {
171 void V4L2CaptureDelegate::AllocateAndStart(
175 scoped_ptr
<VideoCaptureDevice::Client
> client
) {
176 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
178 client_
= client
.Pass();
180 // Need to open camera with O_RDWR after Linux kernel 3.3.
181 device_fd_
.reset(HANDLE_EINTR(open(device_name_
.id().c_str(), O_RDWR
)));
182 if (!device_fd_
.is_valid()) {
183 SetErrorState("Failed to open V4L2 device driver file.");
187 v4l2_capability cap
= {};
188 if (!((HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QUERYCAP
, &cap
)) == 0) &&
189 ((cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
||
190 cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE_MPLANE
) &&
191 !(cap
.capabilities
& V4L2_CAP_VIDEO_OUTPUT
) &&
192 !(cap
.capabilities
& V4L2_CAP_VIDEO_OUTPUT_MPLANE
)))) {
194 SetErrorState("This is not a V4L2 video capture device");
198 // Get supported video formats in preferred order.
199 // For large resolutions, favour mjpeg over raw formats.
200 const std::list
<uint32_t>& desired_v4l2_formats
=
201 GetListOfUsableFourCcs(width
> kMjpegWidth
|| height
> kMjpegHeight
);
202 std::list
<uint32_t>::const_iterator best
= desired_v4l2_formats
.end();
204 v4l2_fmtdesc fmtdesc
= {};
205 fmtdesc
.type
= capture_type_
;
206 for (; HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_ENUM_FMT
, &fmtdesc
)) == 0;
208 best
= std::find(desired_v4l2_formats
.begin(), best
, fmtdesc
.pixelformat
);
210 if (best
== desired_v4l2_formats
.end()) {
211 SetErrorState("Failed to find a supported camera format.");
215 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best
);
217 video_fmt_
.type
= capture_type_
;
218 if (!FillV4L2Format(&video_fmt_
, width
, height
, *best
)) {
219 SetErrorState("Failed filling in V4L2 Format");
223 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_S_FMT
, &video_fmt_
)) < 0) {
224 SetErrorState("Failed to set video capture format");
227 const VideoCapturePixelFormat pixel_format
=
228 V4l2FourCcToChromiumPixelFormat(video_fmt_
.fmt
.pix
.pixelformat
);
229 if (pixel_format
== VIDEO_CAPTURE_PIXEL_FORMAT_UNKNOWN
) {
230 SetErrorState("Unsupported pixel format");
234 // Set capture framerate in the form of capture interval.
235 v4l2_streamparm streamparm
= {};
236 streamparm
.type
= capture_type_
;
237 // The following line checks that the driver knows about framerate get/set.
238 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_G_PARM
, &streamparm
)) >= 0) {
239 // Now check if the device is able to accept a capture framerate set.
240 if (streamparm
.parm
.capture
.capability
& V4L2_CAP_TIMEPERFRAME
) {
241 // |frame_rate| is float, approximate by a fraction.
242 streamparm
.parm
.capture
.timeperframe
.numerator
=
243 media::kFrameRatePrecision
;
244 streamparm
.parm
.capture
.timeperframe
.denominator
=
245 (frame_rate
) ? (frame_rate
* media::kFrameRatePrecision
)
246 : (kTypicalFramerate
* media::kFrameRatePrecision
);
248 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_S_PARM
, &streamparm
)) <
250 SetErrorState("Failed to set camera framerate");
253 DVLOG(2) << "Actual camera driverframerate: "
254 << streamparm
.parm
.capture
.timeperframe
.denominator
<< "/"
255 << streamparm
.parm
.capture
.timeperframe
.numerator
;
258 // TODO(mcasas): what should be done if the camera driver does not allow
259 // framerate configuration, or the actual one is different from the desired?
261 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
262 // operation (|errno| == EINVAL in this case) or plain failure.
263 if ((power_line_frequency_
== V4L2_CID_POWER_LINE_FREQUENCY_50HZ
) ||
264 (power_line_frequency_
== V4L2_CID_POWER_LINE_FREQUENCY_60HZ
) ||
265 (power_line_frequency_
== V4L2_CID_POWER_LINE_FREQUENCY_AUTO
)) {
266 struct v4l2_control control
= {};
267 control
.id
= V4L2_CID_POWER_LINE_FREQUENCY
;
268 control
.value
= power_line_frequency_
;
270 HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_S_CTRL
, &control
));
272 DVLOG(1) << "Error setting power line frequency removal";
275 capture_format_
.frame_size
.SetSize(video_fmt_
.fmt
.pix
.width
,
276 video_fmt_
.fmt
.pix
.height
);
277 capture_format_
.frame_rate
= frame_rate
;
278 capture_format_
.pixel_format
= pixel_format
;
280 v4l2_requestbuffers r_buffer
= {};
281 r_buffer
.type
= capture_type_
;
282 r_buffer
.memory
= V4L2_MEMORY_MMAP
;
283 r_buffer
.count
= kNumVideoBuffers
;
284 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_REQBUFS
, &r_buffer
)) < 0) {
285 SetErrorState("Error requesting MMAP buffers from V4L2");
288 for (unsigned int i
= 0; i
< r_buffer
.count
; ++i
) {
289 if (!MapAndQueueBuffer(i
)) {
290 SetErrorState("Allocate buffer failed");
295 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_STREAMON
, &capture_type_
)) <
297 SetErrorState("VIDIOC_STREAMON failed");
301 is_capturing_
= true;
302 // Post task to start fetching frames from v4l2.
303 v4l2_task_runner_
->PostTask(
304 FROM_HERE
, base::Bind(&V4L2CaptureDelegate::DoCapture
, this));
307 void V4L2CaptureDelegate::StopAndDeAllocate() {
308 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
309 // The order is important: stop streaming, clear |buffer_pool_|,
310 // thus munmap()ing the v4l2_buffers, and then return them to the OS.
311 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_STREAMOFF
, &capture_type_
)) <
313 SetErrorState("VIDIOC_STREAMOFF failed");
317 buffer_tracker_pool_
.clear();
319 v4l2_requestbuffers r_buffer
= {};
320 r_buffer
.type
= capture_type_
;
321 r_buffer
.memory
= V4L2_MEMORY_MMAP
;
323 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_REQBUFS
, &r_buffer
)) < 0)
324 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0");
326 // At this point we can close the device.
327 // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
329 is_capturing_
= false;
333 void V4L2CaptureDelegate::SetRotation(int rotation
) {
334 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
335 DCHECK(rotation
>= 0 && rotation
< 360 && rotation
% 90 == 0);
336 rotation_
= rotation
;
339 bool V4L2CaptureDelegate::MapAndQueueBuffer(int index
) {
341 FillV4L2Buffer(&buffer
, index
);
343 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QUERYBUF
, &buffer
)) < 0) {
344 DLOG(ERROR
) << "Error querying status of a MMAP V4L2 buffer";
348 const scoped_refptr
<BufferTracker
>& buffer_tracker
= CreateBufferTracker();
349 if (!buffer_tracker
->Init(device_fd_
.get(), buffer
)) {
350 DLOG(ERROR
) << "Error creating BufferTracker";
353 buffer_tracker_pool_
.push_back(buffer_tracker
);
355 // Enqueue the buffer in the drivers incoming queue.
356 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QBUF
, &buffer
)) < 0) {
357 DLOG(ERROR
) << "Error enqueuing a V4L2 buffer back into the driver";
363 void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer
* buffer
, int i
) const {
364 memset(buffer
, 0, sizeof(*buffer
));
365 buffer
->memory
= V4L2_MEMORY_MMAP
;
367 FinishFillingV4L2Buffer(buffer
);
370 void V4L2CaptureDelegate::DoCapture() {
371 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
375 pollfd device_pfd
= {};
376 device_pfd
.fd
= device_fd_
.get();
377 device_pfd
.events
= POLLIN
;
378 const int result
= HANDLE_EINTR(poll(&device_pfd
, 1, kCaptureTimeoutMs
));
380 SetErrorState("Poll failed");
383 // Check if poll() timed out; track the amount of times it did in a row and
384 // throw an error if it times out too many times.
387 if (timeout_count_
>= kContinuousTimeoutLimit
) {
388 SetErrorState("Multiple continuous timeouts while read-polling.");
396 // Deenqueue, send and reenqueue a buffer if the driver has filled one in.
397 if (device_pfd
.revents
& POLLIN
) {
399 FillV4L2Buffer(&buffer
, 0);
401 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_DQBUF
, &buffer
)) < 0) {
402 SetErrorState("Failed to dequeue capture buffer");
406 SetPayloadSize(buffer_tracker_pool_
[buffer
.index
], buffer
);
407 SendBuffer(buffer_tracker_pool_
[buffer
.index
], video_fmt_
);
409 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QBUF
, &buffer
)) < 0) {
410 SetErrorState("Failed to enqueue capture buffer");
415 v4l2_task_runner_
->PostTask(
416 FROM_HERE
, base::Bind(&V4L2CaptureDelegate::DoCapture
, this));
419 void V4L2CaptureDelegate::SetErrorState(const std::string
& reason
) {
420 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
421 is_capturing_
= false;
422 client_
->OnError(reason
);