/[chrome]/trunk/src/media/video/capture/linux/video_capture_device_linux.cc
Chromium logo

Contents of /trunk/src/media/video/capture/linux/video_capture_device_linux.cc

Parent Directory Parent Directory | Revision Log Revision Log


Revision 283419 - (show annotations)
Wed Jul 16 17:53:20 2014 UTC (4 years ago) by tnagel@chromium.org
File size: 16896 byte(s)
Fix 'except' and 'prefer' spelling.

TBR=marja, stevenjb, yosin
BUG=none

Review URL: https://codereview.chromium.org/382153006
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.
4
5 #include "media/video/capture/linux/video_capture_device_linux.h"
6
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>
15 #include <sys/mman.h>
16
17 #include <list>
18 #include <string>
19
20 #include "base/bind.h"
21 #include "base/files/file_enumerator.h"
22 #include "base/files/scoped_file.h"
23 #include "base/posix/eintr_wrapper.h"
24 #include "base/strings/stringprintf.h"
25
26 namespace media {
27
28 // Max number of video buffers VideoCaptureDeviceLinux can allocate.
29 enum { kMaxVideoBuffers = 2 };
30 // Timeout in microseconds v4l2_thread_ blocks waiting for a frame from the hw.
31 enum { kCaptureTimeoutUs = 200000 };
32 // The number of continuous timeouts tolerated before treated as error.
33 enum { kContinuousTimeoutLimit = 10 };
34 // Time to wait in milliseconds before v4l2_thread_ reschedules OnCaptureTask
35 // if an event is triggered (select) but no video frame is read.
36 enum { kCaptureSelectWaitMs = 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 };
42
43 // V4L2 color formats VideoCaptureDeviceLinux support.
44 static const int32 kV4l2RawFmts[] = {
45 V4L2_PIX_FMT_YUV420,
46 V4L2_PIX_FMT_YUYV
47 };
48
49 // USB VID and PID are both 4 bytes long.
50 static const size_t kVidPidSize = 4;
51
52 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding
53 // USB device info directory.
54 static const char kVidPathTemplate[] =
55 "/sys/class/video4linux/%s/device/../idVendor";
56 static const char kPidPathTemplate[] =
57 "/sys/class/video4linux/%s/device/../idProduct";
58
59 bool ReadIdFile(const std::string path, std::string* id) {
60 char id_buf[kVidPidSize];
61 FILE* file = fopen(path.c_str(), "rb");
62 if (!file)
63 return false;
64 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1;
65 fclose(file);
66 if (!success)
67 return false;
68 id->append(id_buf, kVidPidSize);
69 return true;
70 }
71
72 // This function translates Video4Linux pixel formats to Chromium pixel formats,
73 // should only support those listed in GetListOfUsableFourCCs.
74 // static
75 VideoPixelFormat VideoCaptureDeviceLinux::V4l2ColorToVideoCaptureColorFormat(
76 int32 v4l2_fourcc) {
77 VideoPixelFormat result = PIXEL_FORMAT_UNKNOWN;
78 switch (v4l2_fourcc) {
79 case V4L2_PIX_FMT_YUV420:
80 result = PIXEL_FORMAT_I420;
81 break;
82 case V4L2_PIX_FMT_YUYV:
83 result = PIXEL_FORMAT_YUY2;
84 break;
85 case V4L2_PIX_FMT_MJPEG:
86 case V4L2_PIX_FMT_JPEG:
87 result = PIXEL_FORMAT_MJPEG;
88 break;
89 default:
90 DVLOG(1) << "Unsupported pixel format " << std::hex << v4l2_fourcc;
91 }
92 return result;
93 }
94
95 // static
96 void VideoCaptureDeviceLinux::GetListOfUsableFourCCs(bool favour_mjpeg,
97 std::list<int>* fourccs) {
98 for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i)
99 fourccs->push_back(kV4l2RawFmts[i]);
100 if (favour_mjpeg)
101 fourccs->push_front(V4L2_PIX_FMT_MJPEG);
102 else
103 fourccs->push_back(V4L2_PIX_FMT_MJPEG);
104
105 // JPEG works as MJPEG on some gspca webcams from field reports.
106 // Put it as the least preferred format.
107 fourccs->push_back(V4L2_PIX_FMT_JPEG);
108 }
109
110 const std::string VideoCaptureDevice::Name::GetModel() const {
111 // |unique_id| is of the form "/dev/video2". |file_name| is "video2".
112 const std::string dev_dir = "/dev/";
113 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir));
114 const std::string file_name =
115 unique_id_.substr(dev_dir.length(), unique_id_.length());
116
117 const std::string vidPath =
118 base::StringPrintf(kVidPathTemplate, file_name.c_str());
119 const std::string pidPath =
120 base::StringPrintf(kPidPathTemplate, file_name.c_str());
121
122 std::string usb_id;
123 if (!ReadIdFile(vidPath, &usb_id))
124 return "";
125 usb_id.append(":");
126 if (!ReadIdFile(pidPath, &usb_id))
127 return "";
128
129 return usb_id;
130 }
131
132 VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name& device_name)
133 : state_(kIdle),
134 device_name_(device_name),
135 v4l2_thread_("V4L2Thread"),
136 buffer_pool_(NULL),
137 buffer_pool_size_(0),
138 timeout_count_(0),
139 rotation_(0) {
140 }
141
142 VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
143 state_ = kIdle;
144 // Check if the thread is running.
145 // This means that the device have not been DeAllocated properly.
146 DCHECK(!v4l2_thread_.IsRunning());
147 v4l2_thread_.Stop();
148 }
149
150 void VideoCaptureDeviceLinux::AllocateAndStart(
151 const VideoCaptureParams& params,
152 scoped_ptr<VideoCaptureDevice::Client> client) {
153 if (v4l2_thread_.IsRunning()) {
154 return; // Wrong state.
155 }
156 v4l2_thread_.Start();
157 v4l2_thread_.message_loop()->PostTask(
158 FROM_HERE,
159 base::Bind(&VideoCaptureDeviceLinux::OnAllocateAndStart,
160 base::Unretained(this),
161 params.requested_format.frame_size.width(),
162 params.requested_format.frame_size.height(),
163 params.requested_format.frame_rate,
164 base::Passed(&client)));
165 }
166
167 void VideoCaptureDeviceLinux::StopAndDeAllocate() {
168 if (!v4l2_thread_.IsRunning()) {
169 return; // Wrong state.
170 }
171 v4l2_thread_.message_loop()->PostTask(
172 FROM_HERE,
173 base::Bind(&VideoCaptureDeviceLinux::OnStopAndDeAllocate,
174 base::Unretained(this)));
175 v4l2_thread_.Stop();
176 // Make sure no buffers are still allocated.
177 // This can happen (theoretically) if an error occurs when trying to stop
178 // the camera.
179 DeAllocateVideoBuffers();
180 }
181
182 void VideoCaptureDeviceLinux::SetRotation(int rotation) {
183 if (v4l2_thread_.IsRunning()) {
184 v4l2_thread_.message_loop()->PostTask(
185 FROM_HERE,
186 base::Bind(&VideoCaptureDeviceLinux::SetRotationOnV4L2Thread,
187 base::Unretained(this), rotation));
188 } else {
189 // If the |v4l2_thread_| is not running, there's no race condition and
190 // |rotation_| can be set directly.
191 rotation_ = rotation;
192 }
193 }
194
195 void VideoCaptureDeviceLinux::SetRotationOnV4L2Thread(int rotation) {
196 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
197 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
198 rotation_ = rotation;
199 }
200
201 void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
202 int height,
203 int frame_rate,
204 scoped_ptr<Client> client) {
205 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
206
207 client_ = client.Pass();
208
209 // Need to open camera with O_RDWR after Linux kernel 3.3.
210 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR)));
211 if (!device_fd_.is_valid()) {
212 SetErrorState("Failed to open V4L2 device driver.");
213 return;
214 }
215
216 // Test if this is a V4L2 capture device.
217 v4l2_capability cap;
218 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
219 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
220 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))) {
221 // This is not a V4L2 video capture device.
222 device_fd_.reset();
223 SetErrorState("This is not a V4L2 video capture device");
224 return;
225 }
226
227 // Get supported video formats in preferred order.
228 // For large resolutions, favour mjpeg over raw formats.
229 std::list<int> v4l2_formats;
230 GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight,
231 &v4l2_formats);
232
233 v4l2_fmtdesc fmtdesc = {0};
234 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
235
236 // Enumerate image formats.
237 std::list<int>::iterator best = v4l2_formats.end();
238 while (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) ==
239 0) {
240 best = std::find(v4l2_formats.begin(), best, fmtdesc.pixelformat);
241 fmtdesc.index++;
242 }
243
244 if (best == v4l2_formats.end()) {
245 SetErrorState("Failed to find a supported camera format.");
246 return;
247 }
248
249 // Set format and frame size now.
250 v4l2_format video_fmt;
251 memset(&video_fmt, 0, sizeof(v4l2_format));
252 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
253 video_fmt.fmt.pix.sizeimage = 0;
254 video_fmt.fmt.pix.width = width;
255 video_fmt.fmt.pix.height = height;
256 video_fmt.fmt.pix.pixelformat = *best;
257
258 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) {
259 SetErrorState(
260 base::StringPrintf("Failed to set camera format: %s", strerror(errno)));
261 return;
262 }
263
264 // Set capture framerate in the form of capture interval.
265 v4l2_streamparm streamparm;
266 memset(&streamparm, 0, sizeof(v4l2_streamparm));
267 streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
268 // The following line checks that the driver knows about framerate get/set.
269 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
270 // Now check if the device is able to accept a capture framerate set.
271 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
272 // |frame_rate| is float, approximate by a fraction.
273 streamparm.parm.capture.timeperframe.numerator =
274 media::kFrameRatePrecision;
275 streamparm.parm.capture.timeperframe.denominator = (frame_rate) ?
276 (frame_rate * media::kFrameRatePrecision) :
277 (kTypicalFramerate * media::kFrameRatePrecision);
278
279 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) <
280 0) {
281 SetErrorState("Failed to set camera framerate");
282 return;
283 }
284 DVLOG(2) << "Actual camera driverframerate: "
285 << streamparm.parm.capture.timeperframe.denominator << "/"
286 << streamparm.parm.capture.timeperframe.numerator;
287 }
288 }
289 // TODO(mcasas): what should be done if the camera driver does not allow
290 // framerate configuration, or the actual one is different from the desired?
291
292 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
293 // operation (|errno| == EINVAL in this case) or plain failure.
294 const int power_line_frequency = GetPowerLineFrequencyForLocation();
295 if ((power_line_frequency == kPowerLine50Hz) ||
296 (power_line_frequency == kPowerLine60Hz)) {
297 struct v4l2_control control = {};
298 control.id = V4L2_CID_POWER_LINE_FREQUENCY;
299 control.value = (power_line_frequency == kPowerLine50Hz) ?
300 V4L2_CID_POWER_LINE_FREQUENCY_50HZ :
301 V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
302 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
303 }
304
305 // Store our current width and height.
306 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
307 video_fmt.fmt.pix.height);
308 capture_format_.frame_rate = frame_rate;
309 capture_format_.pixel_format =
310 V4l2ColorToVideoCaptureColorFormat(video_fmt.fmt.pix.pixelformat);
311
312 // Start capturing.
313 if (!AllocateVideoBuffers()) {
314 // Error, We can not recover.
315 SetErrorState("Allocate buffer failed");
316 return;
317 }
318
319 // Start UVC camera.
320 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
321 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) == -1) {
322 SetErrorState("VIDIOC_STREAMON failed");
323 return;
324 }
325
326 state_ = kCapturing;
327 // Post task to start fetching frames from v4l2.
328 v4l2_thread_.message_loop()->PostTask(
329 FROM_HERE,
330 base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask,
331 base::Unretained(this)));
332 }
333
334 void VideoCaptureDeviceLinux::OnStopAndDeAllocate() {
335 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
336
337 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
338 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &type)) < 0) {
339 SetErrorState("VIDIOC_STREAMOFF failed");
340 return;
341 }
342 // We don't dare to deallocate the buffers if we can't stop
343 // the capture device.
344 DeAllocateVideoBuffers();
345
346 // We need to close and open the device if we want to change the settings
347 // Otherwise VIDIOC_S_FMT will return error
348 // Sad but true.
349 device_fd_.reset();
350 state_ = kIdle;
351 client_.reset();
352 }
353
354 void VideoCaptureDeviceLinux::OnCaptureTask() {
355 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
356
357 if (state_ != kCapturing) {
358 return;
359 }
360
361 fd_set r_set;
362 FD_ZERO(&r_set);
363 FD_SET(device_fd_.get(), &r_set);
364 timeval timeout;
365
366 timeout.tv_sec = 0;
367 timeout.tv_usec = kCaptureTimeoutUs;
368
369 // First argument to select is the highest numbered file descriptor +1.
370 // Refer to http://linux.die.net/man/2/select for more information.
371 int result =
372 HANDLE_EINTR(select(device_fd_.get() + 1, &r_set, NULL, NULL, &timeout));
373 // Check if select have failed.
374 if (result < 0) {
375 // EINTR is a signal. This is not really an error.
376 if (errno != EINTR) {
377 SetErrorState("Select failed");
378 return;
379 }
380 v4l2_thread_.message_loop()->PostDelayedTask(
381 FROM_HERE,
382 base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask,
383 base::Unretained(this)),
384 base::TimeDelta::FromMilliseconds(kCaptureSelectWaitMs));
385 }
386
387 // Check if select timeout.
388 if (result == 0) {
389 timeout_count_++;
390 if (timeout_count_ >= kContinuousTimeoutLimit) {
391 SetErrorState(base::StringPrintf(
392 "Continuous timeout %d times", timeout_count_));
393 timeout_count_ = 0;
394 return;
395 }
396 } else {
397 timeout_count_ = 0;
398 }
399
400 // Check if the driver have filled a buffer.
401 if (FD_ISSET(device_fd_.get(), &r_set)) {
402 v4l2_buffer buffer;
403 memset(&buffer, 0, sizeof(buffer));
404 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
405 buffer.memory = V4L2_MEMORY_MMAP;
406 // Dequeue a buffer.
407 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) == 0) {
408 client_->OnIncomingCapturedData(
409 static_cast<uint8*>(buffer_pool_[buffer.index].start),
410 buffer.bytesused,
411 capture_format_,
412 rotation_,
413 base::TimeTicks::Now());
414
415 // Enqueue the buffer again.
416 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) == -1) {
417 SetErrorState(base::StringPrintf(
418 "Failed to enqueue capture buffer errno %d", errno));
419 }
420 } else {
421 SetErrorState(base::StringPrintf(
422 "Failed to dequeue capture buffer errno %d", errno));
423 return;
424 }
425 }
426
427 v4l2_thread_.message_loop()->PostTask(
428 FROM_HERE,
429 base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask,
430 base::Unretained(this)));
431 }
432
433 bool VideoCaptureDeviceLinux::AllocateVideoBuffers() {
434 v4l2_requestbuffers r_buffer;
435 memset(&r_buffer, 0, sizeof(r_buffer));
436
437 r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
438 r_buffer.memory = V4L2_MEMORY_MMAP;
439 r_buffer.count = kMaxVideoBuffers;
440
441 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
442 return false;
443 }
444
445 if (r_buffer.count > kMaxVideoBuffers) {
446 r_buffer.count = kMaxVideoBuffers;
447 }
448
449 buffer_pool_size_ = r_buffer.count;
450
451 // Map the buffers.
452 buffer_pool_ = new Buffer[r_buffer.count];
453 for (unsigned int i = 0; i < r_buffer.count; i++) {
454 v4l2_buffer buffer;
455 memset(&buffer, 0, sizeof(buffer));
456 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
457 buffer.memory = V4L2_MEMORY_MMAP;
458 buffer.index = i;
459
460 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
461 return false;
462 }
463
464 // Some devices require mmap() to be called with both READ and WRITE.
465 // See crbug.com/178582.
466 buffer_pool_[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
467 MAP_SHARED, device_fd_.get(), buffer.m.offset);
468 if (buffer_pool_[i].start == MAP_FAILED) {
469 return false;
470 }
471 buffer_pool_[i].length = buffer.length;
472 // Enqueue the buffer in the drivers incoming queue.
473 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
474 return false;
475 }
476 }
477 return true;
478 }
479
480 void VideoCaptureDeviceLinux::DeAllocateVideoBuffers() {
481 if (!buffer_pool_)
482 return;
483
484 // Unmaps buffers.
485 for (int i = 0; i < buffer_pool_size_; i++) {
486 munmap(buffer_pool_[i].start, buffer_pool_[i].length);
487 }
488 v4l2_requestbuffers r_buffer;
489 memset(&r_buffer, 0, sizeof(r_buffer));
490 r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
491 r_buffer.memory = V4L2_MEMORY_MMAP;
492 r_buffer.count = 0;
493
494 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
495 SetErrorState("Failed to reset buf.");
496 }
497
498 delete [] buffer_pool_;
499 buffer_pool_ = NULL;
500 buffer_pool_size_ = 0;
501 }
502
503 void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) {
504 DCHECK(!v4l2_thread_.IsRunning() ||
505 v4l2_thread_.message_loop() == base::MessageLoop::current());
506 DVLOG(1) << reason;
507 state_ = kError;
508 client_->OnError(reason);
509 }
510
511 } // namespace media

Properties

Name Value
svn:eol-style LF

Powered by ViewVC 1.1.26 ViewVC Help