Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / media / video / capture / linux / video_capture_device_linux.cc
blob60d8f77d55cfe0497fe3835cdfe76f6016ff037a
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"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <poll.h>
10 #if defined(OS_OPENBSD)
11 #include <sys/videoio.h>
12 #else
13 #include <linux/videodev2.h>
14 #endif
15 #include <sys/ioctl.h>
16 #include <sys/mman.h>
18 #include <list>
19 #include <string>
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"
27 namespace media {
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>{
45 public:
46 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,
52 int height,
53 float frame_rate,
54 scoped_ptr<Client> client);
55 void StopAndDeAllocate();
56 void SetRotation(int rotation);
57 bool DeAllocateVideoBuffers();
59 private:
60 // Buffers used to receive captured frames from v4l2.
61 struct Buffer {
62 Buffer() : start(0), length(0) {}
63 void* start;
64 size_t length;
67 friend class base::RefCountedThreadSafe<V4L2CaptureDelegate>;
68 ~V4L2CaptureDelegate();
70 void DoCapture();
71 bool AllocateVideoBuffers();
72 void SetErrorState(const std::string& reason);
74 const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner_;
76 bool is_capturing_;
77 scoped_ptr<VideoCaptureDevice::Client> client_;
78 const Name device_name_;
79 base::ScopedFD device_fd_; // File descriptor for the opened camera device.
80 Buffer* buffer_pool_;
81 int buffer_pool_size_; // Number of allocated buffers.
82 int timeout_count_;
83 VideoCaptureFormat capture_format_;
84 const int power_line_frequency_;
86 // Clockwise rotation in degrees. This value should be 0, 90, 180, or 270.
87 int rotation_;
89 DISALLOW_IMPLICIT_CONSTRUCTORS(V4L2CaptureDelegate);
92 // V4L2 color formats VideoCaptureDeviceLinux support.
93 static const int32 kV4l2RawFmts[] = {
94 V4L2_PIX_FMT_YUV420,
95 V4L2_PIX_FMT_YUYV,
96 V4L2_PIX_FMT_UYVY
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");
112 if (!file)
113 return false;
114 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1;
115 fclose(file);
116 if (!success)
117 return false;
118 id->append(id_buf, kVidPidSize);
119 return true;
122 // This function translates Video4Linux pixel formats to Chromium pixel formats,
123 // should only support those listed in GetListOfUsableFourCCs.
124 // static
125 VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat(
126 uint32 v4l2_fourcc) {
127 const struct {
128 uint32 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;
149 // static
150 std::list<int> VideoCaptureDeviceLinux::GetListOfUsableFourCCs(
151 bool favour_mjpeg) {
152 std::list<int> fourccs;
153 for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i)
154 fourccs.push_back(kV4l2RawFmts[i]);
155 if (favour_mjpeg)
156 fourccs.push_front(V4L2_PIX_FMT_MJPEG);
157 else
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);
163 return fourccs;
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());
178 std::string usb_id;
179 if (!ReadIdFile(vidPath, &usb_id))
180 return "";
181 usb_id.append(":");
182 if (!ReadIdFile(pidPath, &usb_id))
183 return "";
185 return 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());
197 v4l2_thread_.Stop();
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(
211 FROM_HERE,
212 base::Bind(
213 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart,
214 capture_impl_,
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(
225 FROM_HERE,
226 base::Bind(
227 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate,
228 capture_impl_));
229 v4l2_thread_.Stop();
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(
241 FROM_HERE,
242 base::Bind(
243 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation,
244 capture_impl_,
245 rotation));
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),
256 buffer_pool_(NULL),
257 buffer_pool_size_(0),
258 timeout_count_(0),
259 power_line_frequency_(power_line_frequency),
260 rotation_(0) {
263 VideoCaptureDeviceLinux::V4L2CaptureDelegate::~V4L2CaptureDelegate() {
264 DCHECK(!client_);
267 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
268 int width,
269 int height,
270 float frame_rate,
271 scoped_ptr<Client> client) {
272 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
273 DCHECK(client);
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.");
280 return;
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)))) {
287 device_fd_.reset();
288 SetErrorState("This is not a V4L2 video capture device");
289 return;
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;
301 ++fmtdesc.index) {
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.");
306 return;
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");
317 return;
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)) <
335 0) {
336 SetErrorState("Failed to set camera framerate");
337 return;
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)");
367 return;
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");
373 return;
376 is_capturing_ = true;
377 // Post task to start fetching frames from v4l2.
378 v4l2_task_runner_->PostTask(
379 FROM_HERE,
380 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
381 this));
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");
390 return;
392 // We don't dare to deallocate the buffers if we can't stop the capture
393 // device.
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.
399 device_fd_.reset();
400 is_capturing_ = false;
401 client_.reset();
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());
412 if (!is_capturing_)
413 return;
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));
419 if (result < 0) {
420 SetErrorState("Poll failed");
421 return;
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.
425 if (result == 0) {
426 timeout_count_++;
427 if (timeout_count_ >= kContinuousTimeoutLimit) {
428 SetErrorState("Multiple continuous timeouts while read-polling.");
429 timeout_count_ = 0;
430 return;
432 } else {
433 timeout_count_ = 0;
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");
443 return;
445 client_->OnIncomingCapturedData(
446 static_cast<uint8*>(buffer_pool_[buffer.index].start),
447 buffer.bytesused,
448 capture_format_,
449 rotation_,
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(
457 FROM_HERE,
458 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
459 this));
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";
469 return false;
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;
479 buffer.index = i;
480 buffer.length = 1;
481 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
482 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer";
483 return false;
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";
492 return false;
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) {
498 DLOG(ERROR)
499 << "Error enqueuing a V4L2 buffer back to the drivers incoming queue";
500 return false;
503 return true;
506 bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::DeAllocateVideoBuffers() {
507 if (!buffer_pool_)
508 return true;
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;
516 r_buffer.count = 0;
517 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0)
518 return false;
520 delete [] buffer_pool_;
521 buffer_pool_ = NULL;
522 buffer_pool_size_ = 0;
523 return true;
526 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState(
527 const std::string& reason) {
528 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
529 is_capturing_ = false;
530 client_->OnError(reason);
533 } // namespace media