1 // Copyright (c) 2012 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/video/capture/linux/video_capture_device_linux.h"
10 #if defined(OS_OPENBSD)
11 #include <sys/videoio.h>
13 #include <linux/videodev2.h>
15 #include <sys/ioctl.h>
21 #include "base/bind.h"
22 #include "base/files/file_enumerator.h"
23 #include "base/files/scoped_file.h"
24 #include "base/posix/eintr_wrapper.h"
25 #include "base/strings/stringprintf.h"
29 #define GET_V4L2_FOURCC_CHAR(a, index) ((char)( ((a) >> (8 * index)) & 0xff))
31 // Max number of video buffers VideoCaptureDeviceLinux can allocate.
32 const uint32 kMaxVideoBuffers
= 2;
33 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw.
34 enum { kCaptureTimeoutMs
= 200 };
35 // The number of continuous timeouts tolerated before treated as error.
36 enum { kContinuousTimeoutLimit
= 10 };
37 // MJPEG is preferred if the width or height is larger than this.
38 enum { kMjpegWidth
= 640 };
39 enum { kMjpegHeight
= 480 };
40 // Typical framerate, in fps
41 enum { kTypicalFramerate
= 30 };
43 class VideoCaptureDeviceLinux::V4L2CaptureDelegate
44 : public base::RefCountedThreadSafe
<V4L2CaptureDelegate
>{
47 const Name
& device_name
,
48 const scoped_refptr
<base::SingleThreadTaskRunner
> v4l2_task_runner
,
49 int power_line_frequency
);
51 void AllocateAndStart(int width
,
54 scoped_ptr
<Client
> client
);
55 void StopAndDeAllocate();
56 void SetRotation(int rotation
);
57 bool DeAllocateVideoBuffers();
60 // Buffers used to receive captured frames from v4l2.
62 Buffer() : start(0), length(0) {}
67 friend class base::RefCountedThreadSafe
<V4L2CaptureDelegate
>;
68 ~V4L2CaptureDelegate();
71 bool AllocateVideoBuffers();
72 void SetErrorState(const std::string
& reason
);
74 const scoped_refptr
<base::SingleThreadTaskRunner
> v4l2_task_runner_
;
77 scoped_ptr
<VideoCaptureDevice::Client
> client_
;
78 const Name device_name_
;
79 base::ScopedFD device_fd_
; // File descriptor for the opened camera device.
81 int buffer_pool_size_
; // Number of allocated buffers.
83 VideoCaptureFormat capture_format_
;
84 const int power_line_frequency_
;
86 // Clockwise rotation in degrees. This value should be 0, 90, 180, or 270.
89 DISALLOW_IMPLICIT_CONSTRUCTORS(V4L2CaptureDelegate
);
92 // V4L2 color formats VideoCaptureDeviceLinux support.
93 static const int32 kV4l2RawFmts
[] = {
99 // USB VID and PID are both 4 bytes long.
100 static const size_t kVidPidSize
= 4;
102 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding
103 // USB device info directory.
104 static const char kVidPathTemplate
[] =
105 "/sys/class/video4linux/%s/device/../idVendor";
106 static const char kPidPathTemplate
[] =
107 "/sys/class/video4linux/%s/device/../idProduct";
109 static bool ReadIdFile(const std::string path
, std::string
* id
) {
110 char id_buf
[kVidPidSize
];
111 FILE* file
= fopen(path
.c_str(), "rb");
114 const bool success
= fread(id_buf
, kVidPidSize
, 1, file
) == 1;
118 id
->append(id_buf
, kVidPidSize
);
122 // This function translates Video4Linux pixel formats to Chromium pixel formats,
123 // should only support those listed in GetListOfUsableFourCCs.
125 VideoPixelFormat
VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat(
126 uint32 v4l2_fourcc
) {
129 VideoPixelFormat pixel_format
;
130 } kFourCcAndChromiumPixelFormat
[] = {
131 {V4L2_PIX_FMT_YUV420
, PIXEL_FORMAT_I420
},
132 {V4L2_PIX_FMT_YUYV
, PIXEL_FORMAT_YUY2
},
133 {V4L2_PIX_FMT_UYVY
, PIXEL_FORMAT_UYVY
},
134 {V4L2_PIX_FMT_MJPEG
, PIXEL_FORMAT_MJPEG
},
135 {V4L2_PIX_FMT_JPEG
, PIXEL_FORMAT_MJPEG
},
137 for (const auto& fourcc_and_pixel_format
: kFourCcAndChromiumPixelFormat
) {
138 if (fourcc_and_pixel_format
.fourcc
== v4l2_fourcc
)
139 return fourcc_and_pixel_format
.pixel_format
;
141 DVLOG(1) << "Unsupported pixel format: "
142 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc
, 0)
143 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc
, 1)
144 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc
, 2)
145 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc
, 3);
146 return PIXEL_FORMAT_UNKNOWN
;
150 std::list
<int> VideoCaptureDeviceLinux::GetListOfUsableFourCCs(
152 std::list
<int> fourccs
;
153 for (size_t i
= 0; i
< arraysize(kV4l2RawFmts
); ++i
)
154 fourccs
.push_back(kV4l2RawFmts
[i
]);
156 fourccs
.push_front(V4L2_PIX_FMT_MJPEG
);
158 fourccs
.push_back(V4L2_PIX_FMT_MJPEG
);
160 // JPEG works as MJPEG on some gspca webcams from field reports.
161 // Put it as the least preferred format.
162 fourccs
.push_back(V4L2_PIX_FMT_JPEG
);
166 const std::string
VideoCaptureDevice::Name::GetModel() const {
167 // |unique_id| is of the form "/dev/video2". |file_name| is "video2".
168 const std::string dev_dir
= "/dev/";
169 DCHECK_EQ(0, unique_id_
.compare(0, dev_dir
.length(), dev_dir
));
170 const std::string file_name
=
171 unique_id_
.substr(dev_dir
.length(), unique_id_
.length());
173 const std::string vidPath
=
174 base::StringPrintf(kVidPathTemplate
, file_name
.c_str());
175 const std::string pidPath
=
176 base::StringPrintf(kPidPathTemplate
, file_name
.c_str());
179 if (!ReadIdFile(vidPath
, &usb_id
))
182 if (!ReadIdFile(pidPath
, &usb_id
))
188 VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name
& device_name
)
189 : v4l2_thread_("V4L2CaptureThread"),
190 device_name_(device_name
) {
193 VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
194 // Check if the thread is running.
195 // This means that the device has not been StopAndDeAllocate()d properly.
196 DCHECK(!v4l2_thread_
.IsRunning());
200 void VideoCaptureDeviceLinux::AllocateAndStart(
201 const VideoCaptureParams
& params
,
202 scoped_ptr
<VideoCaptureDevice::Client
> client
) {
203 DCHECK(!capture_impl_
);
204 if (v4l2_thread_
.IsRunning())
205 return; // Wrong state.
206 v4l2_thread_
.Start();
207 capture_impl_
= new V4L2CaptureDelegate(device_name_
,
208 v4l2_thread_
.message_loop_proxy(),
209 GetPowerLineFrequencyForLocation());
210 v4l2_thread_
.message_loop()->PostTask(
213 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart
,
215 params
.requested_format
.frame_size
.width(),
216 params
.requested_format
.frame_size
.height(),
217 params
.requested_format
.frame_rate
,
218 base::Passed(&client
)));
221 void VideoCaptureDeviceLinux::StopAndDeAllocate() {
222 if (!v4l2_thread_
.IsRunning())
223 return; // Wrong state.
224 v4l2_thread_
.message_loop()->PostTask(
227 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate
,
230 // TODO(mcasas): VCDLinux called DeAllocateVideoBuffers() a second time after
231 // stopping |v4l2_thread_| to make sure buffers were completely deallocated.
232 // Investigate if that's needed, otherwise remove the following line and make
233 // V4L2CaptureDelegate::DeAllocateVideoBuffers() private.
234 capture_impl_
->DeAllocateVideoBuffers();
235 capture_impl_
= NULL
;
238 void VideoCaptureDeviceLinux::SetRotation(int rotation
) {
239 if (v4l2_thread_
.IsRunning()) {
240 v4l2_thread_
.message_loop()->PostTask(
243 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation
,
249 VideoCaptureDeviceLinux::V4L2CaptureDelegate::V4L2CaptureDelegate(
250 const Name
& device_name
,
251 const scoped_refptr
<base::SingleThreadTaskRunner
> v4l2_task_runner
,
252 int power_line_frequency
)
253 : v4l2_task_runner_(v4l2_task_runner
),
254 is_capturing_(false),
255 device_name_(device_name
),
257 buffer_pool_size_(0),
259 power_line_frequency_(power_line_frequency
),
263 VideoCaptureDeviceLinux::V4L2CaptureDelegate::~V4L2CaptureDelegate() {
267 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
271 scoped_ptr
<Client
> client
) {
272 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
274 client_
= client
.Pass();
276 // Need to open camera with O_RDWR after Linux kernel 3.3.
277 device_fd_
.reset(HANDLE_EINTR(open(device_name_
.id().c_str(), O_RDWR
)));
278 if (!device_fd_
.is_valid()) {
279 SetErrorState("Failed to open V4L2 device driver file.");
283 v4l2_capability cap
= {};
284 if (!((HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QUERYCAP
, &cap
)) == 0) &&
285 (cap
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
&&
286 !(cap
.capabilities
& V4L2_CAP_VIDEO_OUTPUT
)))) {
288 SetErrorState("This is not a V4L2 video capture device");
292 // Get supported video formats in preferred order.
293 // For large resolutions, favour mjpeg over raw formats.
294 const std::list
<int>& desired_v4l2_formats
=
295 GetListOfUsableFourCCs(width
> kMjpegWidth
|| height
> kMjpegHeight
);
296 std::list
<int>::const_iterator best
= desired_v4l2_formats
.end();
298 v4l2_fmtdesc fmtdesc
= {};
299 fmtdesc
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
300 for (; HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_ENUM_FMT
, &fmtdesc
)) == 0;
302 best
= std::find(desired_v4l2_formats
.begin(), best
, fmtdesc
.pixelformat
);
304 if (best
== desired_v4l2_formats
.end()) {
305 SetErrorState("Failed to find a supported camera format.");
309 v4l2_format video_fmt
= {};
310 video_fmt
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
311 video_fmt
.fmt
.pix
.sizeimage
= 0;
312 video_fmt
.fmt
.pix
.width
= width
;
313 video_fmt
.fmt
.pix
.height
= height
;
314 video_fmt
.fmt
.pix
.pixelformat
= *best
;
315 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_S_FMT
, &video_fmt
)) < 0) {
316 SetErrorState("Failed to set video capture format");
320 // Set capture framerate in the form of capture interval.
321 v4l2_streamparm streamparm
= {};
322 streamparm
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
323 // The following line checks that the driver knows about framerate get/set.
324 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_G_PARM
, &streamparm
)) >= 0) {
325 // Now check if the device is able to accept a capture framerate set.
326 if (streamparm
.parm
.capture
.capability
& V4L2_CAP_TIMEPERFRAME
) {
327 // |frame_rate| is float, approximate by a fraction.
328 streamparm
.parm
.capture
.timeperframe
.numerator
=
329 media::kFrameRatePrecision
;
330 streamparm
.parm
.capture
.timeperframe
.denominator
= (frame_rate
) ?
331 (frame_rate
* media::kFrameRatePrecision
) :
332 (kTypicalFramerate
* media::kFrameRatePrecision
);
334 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_S_PARM
, &streamparm
)) <
336 SetErrorState("Failed to set camera framerate");
339 DVLOG(2) << "Actual camera driverframerate: "
340 << streamparm
.parm
.capture
.timeperframe
.denominator
<< "/"
341 << streamparm
.parm
.capture
.timeperframe
.numerator
;
344 // TODO(mcasas): what should be done if the camera driver does not allow
345 // framerate configuration, or the actual one is different from the desired?
347 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
348 // operation (|errno| == EINVAL in this case) or plain failure.
349 if ((power_line_frequency_
== kPowerLine50Hz
) ||
350 (power_line_frequency_
== kPowerLine60Hz
)) {
351 struct v4l2_control control
= {};
352 control
.id
= V4L2_CID_POWER_LINE_FREQUENCY
;
353 control
.value
= (power_line_frequency_
== kPowerLine50Hz
)
354 ? V4L2_CID_POWER_LINE_FREQUENCY_50HZ
355 : V4L2_CID_POWER_LINE_FREQUENCY_60HZ
;
356 HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_S_CTRL
, &control
));
359 capture_format_
.frame_size
.SetSize(video_fmt
.fmt
.pix
.width
,
360 video_fmt
.fmt
.pix
.height
);
361 capture_format_
.frame_rate
= frame_rate
;
362 capture_format_
.pixel_format
=
363 V4l2FourCcToChromiumPixelFormat(video_fmt
.fmt
.pix
.pixelformat
);
365 if (!AllocateVideoBuffers()) {
366 SetErrorState("Allocate buffer failed (Cannot recover from this error)");
370 const v4l2_buf_type type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
371 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_STREAMON
, &type
)) < 0) {
372 SetErrorState("VIDIOC_STREAMON failed");
376 is_capturing_
= true;
377 // Post task to start fetching frames from v4l2.
378 v4l2_task_runner_
->PostTask(
380 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture
,
384 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate() {
385 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
387 const v4l2_buf_type type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
388 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_STREAMOFF
, &type
)) < 0) {
389 SetErrorState("VIDIOC_STREAMOFF failed");
392 // We don't dare to deallocate the buffers if we can't stop the capture
394 if (!DeAllocateVideoBuffers())
395 SetErrorState("Failed to reset buffers");
397 // We need to close and open the device if we want to change the settings.
398 // Otherwise VIDIOC_S_FMT will return error. Sad but true.
400 is_capturing_
= false;
404 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation(int rotation
) {
405 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
406 DCHECK(rotation
>= 0 && rotation
< 360 && rotation
% 90 == 0);
407 rotation_
= rotation
;
410 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture() {
411 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
415 pollfd device_pfd
= {};
416 device_pfd
.fd
= device_fd_
.get();
417 device_pfd
.events
= POLLIN
;
418 const int result
= HANDLE_EINTR(poll(&device_pfd
, 1, kCaptureTimeoutMs
));
420 SetErrorState("Poll failed");
423 // Check if poll() timed out; track the amount of times it did in a row and
424 // throw an error if it times out too many times.
427 if (timeout_count_
>= kContinuousTimeoutLimit
) {
428 SetErrorState("Multiple continuous timeouts while read-polling.");
436 // Check if the driver has filled a buffer.
437 if (device_pfd
.revents
& POLLIN
) {
438 v4l2_buffer buffer
= {};
439 buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
440 buffer
.memory
= V4L2_MEMORY_MMAP
;
441 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_DQBUF
, &buffer
)) < 0) {
442 SetErrorState("Failed to dequeue capture buffer");
445 client_
->OnIncomingCapturedData(
446 static_cast<uint8
*>(buffer_pool_
[buffer
.index
].start
),
450 base::TimeTicks::Now());
452 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QBUF
, &buffer
)) < 0)
453 SetErrorState("Failed to enqueue capture buffer");
456 v4l2_task_runner_
->PostTask(
458 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture
,
462 bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateVideoBuffers() {
463 v4l2_requestbuffers r_buffer
= {};
464 r_buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
465 r_buffer
.memory
= V4L2_MEMORY_MMAP
;
466 r_buffer
.count
= kMaxVideoBuffers
;
467 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_REQBUFS
, &r_buffer
)) < 0) {
468 DLOG(ERROR
) << "Error requesting MMAP buffers from V4L2";
471 DCHECK_EQ(r_buffer
.count
, kMaxVideoBuffers
);
472 r_buffer
.count
= std::min(r_buffer
.count
, kMaxVideoBuffers
);
473 buffer_pool_size_
= r_buffer
.count
;
474 buffer_pool_
= new Buffer
[buffer_pool_size_
];
475 for (unsigned int i
= 0; i
< r_buffer
.count
; ++i
) {
476 v4l2_buffer buffer
= {};
477 buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
478 buffer
.memory
= V4L2_MEMORY_MMAP
;
481 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QUERYBUF
, &buffer
)) < 0) {
482 DLOG(ERROR
) << "Error querying status of a MMAP V4L2 buffer";
486 // Some devices require mmap() to be called with both READ and WRITE.
487 // See http://crbug.com/178582.
488 buffer_pool_
[i
].start
= mmap(NULL
, buffer
.length
, PROT_READ
| PROT_WRITE
,
489 MAP_SHARED
, device_fd_
.get(), buffer
.m
.offset
);
490 if (buffer_pool_
[i
].start
== MAP_FAILED
) {
491 DLOG(ERROR
) << "Error mmmap()ing a V4L2 buffer into userspace";
495 buffer_pool_
[i
].length
= buffer
.length
;
496 // Enqueue the buffer in the drivers incoming queue.
497 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_QBUF
, &buffer
)) < 0) {
499 << "Error enqueuing a V4L2 buffer back to the drivers incoming queue";
506 bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::DeAllocateVideoBuffers() {
510 for (int i
= 0; i
< buffer_pool_size_
; ++i
)
511 munmap(buffer_pool_
[i
].start
, buffer_pool_
[i
].length
);
513 v4l2_requestbuffers r_buffer
= {};
514 r_buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
515 r_buffer
.memory
= V4L2_MEMORY_MMAP
;
517 if (HANDLE_EINTR(ioctl(device_fd_
.get(), VIDIOC_REQBUFS
, &r_buffer
)) < 0)
520 delete [] buffer_pool_
;
522 buffer_pool_size_
= 0;
526 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState(
527 const std::string
& reason
) {
528 DCHECK(v4l2_task_runner_
->BelongsToCurrentThread());
529 is_capturing_
= false;
530 client_
->OnError(reason
);