OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/video/capture/linux/video_capture_device_linux.h" | 5 #include "media/video/capture/linux/video_capture_device_linux.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <poll.h> | 9 #include <poll.h> |
10 #if defined(OS_OPENBSD) | 10 #if defined(OS_OPENBSD) |
11 #include <sys/videoio.h> | 11 #include <sys/videoio.h> |
12 #else | 12 #else |
13 #include <linux/videodev2.h> | 13 #include <linux/videodev2.h> |
14 #endif | 14 #endif |
15 #include <sys/ioctl.h> | 15 #include <sys/ioctl.h> |
16 #include <sys/mman.h> | 16 #include <sys/mman.h> |
17 | 17 |
18 #include <list> | 18 #include <list> |
19 #include <string> | 19 #include <string> |
20 | 20 |
21 #include "base/bind.h" | 21 #include "base/bind.h" |
22 #include "base/files/file_enumerator.h" | 22 #include "base/files/file_enumerator.h" |
23 #include "base/files/scoped_file.h" | 23 #include "base/files/scoped_file.h" |
24 #include "base/posix/eintr_wrapper.h" | 24 #include "base/posix/eintr_wrapper.h" |
25 #include "base/strings/stringprintf.h" | 25 #include "base/strings/stringprintf.h" |
26 | 26 |
27 namespace media { | 27 namespace media { |
28 | 28 |
29 #define GET_V4L2_FOURCC_CHAR(a, index) ((char)( ((a) >> (8 * index)) & 0xff)) | |
30 | |
29 // Max number of video buffers VideoCaptureDeviceLinux can allocate. | 31 // Max number of video buffers VideoCaptureDeviceLinux can allocate. |
30 enum { kMaxVideoBuffers = 2 }; | 32 enum { kMaxVideoBuffers = 2 }; |
31 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw. | 33 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw. |
32 enum { kCaptureTimeoutMs = 200 }; | 34 enum { kCaptureTimeoutMs = 200 }; |
33 // The number of continuous timeouts tolerated before treated as error. | 35 // The number of continuous timeouts tolerated before treated as error. |
34 enum { kContinuousTimeoutLimit = 10 }; | 36 enum { kContinuousTimeoutLimit = 10 }; |
35 // MJPEG is preferred if the width or height is larger than this. | 37 // MJPEG is preferred if the width or height is larger than this. |
36 enum { kMjpegWidth = 640 }; | 38 enum { kMjpegWidth = 640 }; |
37 enum { kMjpegHeight = 480 }; | 39 enum { kMjpegHeight = 480 }; |
38 // Typical framerate, in fps | 40 // Typical framerate, in fps |
39 enum { kTypicalFramerate = 30 }; | 41 enum { kTypicalFramerate = 30 }; |
40 | 42 |
41 // V4L2 color formats VideoCaptureDeviceLinux support. | 43 // V4L2 color formats VideoCaptureDeviceLinux support. |
42 static const int32 kV4l2RawFmts[] = { | 44 static const int32 kV4l2RawFmts[] = { |
43 V4L2_PIX_FMT_YUV420, | 45 V4L2_PIX_FMT_YUV420, |
44 V4L2_PIX_FMT_YUYV, | 46 V4L2_PIX_FMT_YUYV, |
45 V4L2_PIX_FMT_UYVY | 47 V4L2_PIX_FMT_UYVY |
46 }; | 48 }; |
47 | 49 |
48 // USB VID and PID are both 4 bytes long. | 50 // USB VID and PID are both 4 bytes long. |
49 static const size_t kVidPidSize = 4; | 51 static const size_t kVidPidSize = 4; |
50 | 52 |
51 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding | 53 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding |
52 // USB device info directory. | 54 // USB device info directory. |
53 static const char kVidPathTemplate[] = | 55 static const char kVidPathTemplate[] = |
54 "/sys/class/video4linux/%s/device/../idVendor"; | 56 "/sys/class/video4linux/%s/device/../idVendor"; |
55 static const char kPidPathTemplate[] = | 57 static const char kPidPathTemplate[] = |
56 "/sys/class/video4linux/%s/device/../idProduct"; | 58 "/sys/class/video4linux/%s/device/../idProduct"; |
57 | 59 |
58 bool ReadIdFile(const std::string path, std::string* id) { | 60 static bool ReadIdFile(const std::string path, std::string* id) { |
59 char id_buf[kVidPidSize]; | 61 char id_buf[kVidPidSize]; |
60 FILE* file = fopen(path.c_str(), "rb"); | 62 FILE* file = fopen(path.c_str(), "rb"); |
61 if (!file) | 63 if (!file) |
62 return false; | 64 return false; |
63 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; | 65 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; |
64 fclose(file); | 66 fclose(file); |
65 if (!success) | 67 if (!success) |
66 return false; | 68 return false; |
67 id->append(id_buf, kVidPidSize); | 69 id->append(id_buf, kVidPidSize); |
68 return true; | 70 return true; |
69 } | 71 } |
70 | 72 |
71 // This function translates Video4Linux pixel formats to Chromium pixel formats, | 73 // This function translates Video4Linux pixel formats to Chromium pixel formats, |
72 // should only support those listed in GetListOfUsableFourCCs. | 74 // should only support those listed in GetListOfUsableFourCCs. |
73 // static | 75 // static |
74 VideoPixelFormat VideoCaptureDeviceLinux::V4l2ColorToVideoCaptureColorFormat( | 76 VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat( |
75 int32 v4l2_fourcc) { | 77 uint32 v4l2_fourcc) { |
76 VideoPixelFormat result = PIXEL_FORMAT_UNKNOWN; | 78 VideoPixelFormat result = PIXEL_FORMAT_UNKNOWN; |
77 switch (v4l2_fourcc) { | 79 switch (v4l2_fourcc) { |
78 case V4L2_PIX_FMT_YUV420: | 80 case V4L2_PIX_FMT_YUV420: |
79 result = PIXEL_FORMAT_I420; | 81 result = PIXEL_FORMAT_I420; |
80 break; | 82 break; |
81 case V4L2_PIX_FMT_YUYV: | 83 case V4L2_PIX_FMT_YUYV: |
82 result = PIXEL_FORMAT_YUY2; | 84 result = PIXEL_FORMAT_YUY2; |
83 break; | 85 break; |
84 case V4L2_PIX_FMT_UYVY: | 86 case V4L2_PIX_FMT_UYVY: |
85 result = PIXEL_FORMAT_UYVY; | 87 result = PIXEL_FORMAT_UYVY; |
86 break; | 88 break; |
87 case V4L2_PIX_FMT_MJPEG: | 89 case V4L2_PIX_FMT_MJPEG: |
88 case V4L2_PIX_FMT_JPEG: | 90 case V4L2_PIX_FMT_JPEG: |
89 result = PIXEL_FORMAT_MJPEG; | 91 result = PIXEL_FORMAT_MJPEG; |
90 break; | 92 break; |
91 default: | 93 default: |
92 DVLOG(1) << "Unsupported pixel format " << std::hex << v4l2_fourcc; | 94 DVLOG(1) << "Unsupported pixel format: " |
95 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 0) | |
96 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 1) | |
97 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 2) | |
98 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 3); | |
93 } | 99 } |
94 return result; | 100 return result; |
95 } | 101 } |
96 | 102 |
97 // static | 103 // static |
98 void VideoCaptureDeviceLinux::GetListOfUsableFourCCs(bool favour_mjpeg, | 104 std::list<int> VideoCaptureDeviceLinux::GetListOfUsableFourCCs( |
99 std::list<int>* fourccs) { | 105 bool favour_mjpeg) { |
106 std::list<int> fourccs; | |
100 for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i) | 107 for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i) |
101 fourccs->push_back(kV4l2RawFmts[i]); | 108 fourccs.push_back(kV4l2RawFmts[i]); |
102 if (favour_mjpeg) | 109 if (favour_mjpeg) |
103 fourccs->push_front(V4L2_PIX_FMT_MJPEG); | 110 fourccs.push_front(V4L2_PIX_FMT_MJPEG); |
104 else | 111 else |
105 fourccs->push_back(V4L2_PIX_FMT_MJPEG); | 112 fourccs.push_back(V4L2_PIX_FMT_MJPEG); |
106 | 113 |
107 // JPEG works as MJPEG on some gspca webcams from field reports. | 114 // JPEG works as MJPEG on some gspca webcams from field reports. |
108 // Put it as the least preferred format. | 115 // Put it as the least preferred format. |
109 fourccs->push_back(V4L2_PIX_FMT_JPEG); | 116 fourccs.push_back(V4L2_PIX_FMT_JPEG); |
117 return fourccs; | |
110 } | 118 } |
111 | 119 |
112 const std::string VideoCaptureDevice::Name::GetModel() const { | 120 const std::string VideoCaptureDevice::Name::GetModel() const { |
113 // |unique_id| is of the form "/dev/video2". |file_name| is "video2". | 121 // |unique_id| is of the form "/dev/video2". |file_name| is "video2". |
114 const std::string dev_dir = "/dev/"; | 122 const std::string dev_dir = "/dev/"; |
115 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); | 123 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); |
116 const std::string file_name = | 124 const std::string file_name = |
117 unique_id_.substr(dev_dir.length(), unique_id_.length()); | 125 unique_id_.substr(dev_dir.length(), unique_id_.length()); |
118 | 126 |
119 const std::string vidPath = | 127 const std::string vidPath = |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
220 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && | 228 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && |
221 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))) { | 229 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))) { |
222 // This is not a V4L2 video capture device. | 230 // This is not a V4L2 video capture device. |
223 device_fd_.reset(); | 231 device_fd_.reset(); |
224 SetErrorState("This is not a V4L2 video capture device"); | 232 SetErrorState("This is not a V4L2 video capture device"); |
225 return; | 233 return; |
226 } | 234 } |
227 | 235 |
228 // Get supported video formats in preferred order. | 236 // Get supported video formats in preferred order. |
229 // For large resolutions, favour mjpeg over raw formats. | 237 // For large resolutions, favour mjpeg over raw formats. |
230 std::list<int> v4l2_formats; | 238 const std::list<int>& desired_v4l2_formats = |
231 GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight, | 239 GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight); |
232 &v4l2_formats); | 240 std::list<int>::const_iterator best = desired_v4l2_formats.end(); |
233 | 241 |
234 v4l2_fmtdesc fmtdesc = {0}; | 242 v4l2_fmtdesc fmtdesc = {0}; |
235 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 243 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
236 | 244 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; |
237 // Enumerate image formats. | 245 ++fmtdesc.index) { |
perkj_chrome
2014/12/16 20:28:28
indentation
mcasas
2014/12/16 23:50:31
Done.
| |
238 std::list<int>::iterator best = v4l2_formats.end(); | 246 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); |
239 while (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == | |
240 0) { | |
241 best = std::find(v4l2_formats.begin(), best, fmtdesc.pixelformat); | |
242 fmtdesc.index++; | |
243 } | 247 } |
244 | 248 if (best == desired_v4l2_formats.end()) { |
245 if (best == v4l2_formats.end()) { | |
246 SetErrorState("Failed to find a supported camera format."); | 249 SetErrorState("Failed to find a supported camera format."); |
247 return; | 250 return; |
248 } | 251 } |
249 | 252 |
250 // Set format and frame size now. | 253 // Set format and frame size now. |
251 v4l2_format video_fmt; | 254 v4l2_format video_fmt; |
252 memset(&video_fmt, 0, sizeof(v4l2_format)); | 255 memset(&video_fmt, 0, sizeof(v4l2_format)); |
253 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 256 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
254 video_fmt.fmt.pix.sizeimage = 0; | 257 video_fmt.fmt.pix.sizeimage = 0; |
255 video_fmt.fmt.pix.width = width; | 258 video_fmt.fmt.pix.width = width; |
(...skipping 14 matching lines...) Expand all Loading... | |
270 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { | 273 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { |
271 // Now check if the device is able to accept a capture framerate set. | 274 // Now check if the device is able to accept a capture framerate set. |
272 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { | 275 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { |
273 // |frame_rate| is float, approximate by a fraction. | 276 // |frame_rate| is float, approximate by a fraction. |
274 streamparm.parm.capture.timeperframe.numerator = | 277 streamparm.parm.capture.timeperframe.numerator = |
275 media::kFrameRatePrecision; | 278 media::kFrameRatePrecision; |
276 streamparm.parm.capture.timeperframe.denominator = (frame_rate) ? | 279 streamparm.parm.capture.timeperframe.denominator = (frame_rate) ? |
277 (frame_rate * media::kFrameRatePrecision) : | 280 (frame_rate * media::kFrameRatePrecision) : |
278 (kTypicalFramerate * media::kFrameRatePrecision); | 281 (kTypicalFramerate * media::kFrameRatePrecision); |
279 | 282 |
280 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < | 283 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) |
281 0) { | 284 < 0) { |
perkj_chrome
2014/12/16 20:28:29
was correct from the beginning.
mcasas
2014/12/16 23:50:31
Done.
| |
282 SetErrorState("Failed to set camera framerate"); | 285 SetErrorState("Failed to set camera framerate"); |
283 return; | 286 return; |
284 } | 287 } |
285 DVLOG(2) << "Actual camera driverframerate: " | 288 DVLOG(2) << "Actual camera driverframerate: " |
286 << streamparm.parm.capture.timeperframe.denominator << "/" | 289 << streamparm.parm.capture.timeperframe.denominator << "/" |
287 << streamparm.parm.capture.timeperframe.numerator; | 290 << streamparm.parm.capture.timeperframe.numerator; |
288 } | 291 } |
289 } | 292 } |
290 // TODO(mcasas): what should be done if the camera driver does not allow | 293 // TODO(mcasas): what should be done if the camera driver does not allow |
291 // framerate configuration, or the actual one is different from the desired? | 294 // framerate configuration, or the actual one is different from the desired? |
292 | 295 |
293 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported | 296 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported |
294 // operation (|errno| == EINVAL in this case) or plain failure. | 297 // operation (|errno| == EINVAL in this case) or plain failure. |
295 const int power_line_frequency = GetPowerLineFrequencyForLocation(); | 298 const int power_line_frequency = GetPowerLineFrequencyForLocation(); |
296 if ((power_line_frequency == kPowerLine50Hz) || | 299 if ((power_line_frequency == kPowerLine50Hz) || |
297 (power_line_frequency == kPowerLine60Hz)) { | 300 (power_line_frequency == kPowerLine60Hz)) { |
298 struct v4l2_control control = {}; | 301 struct v4l2_control control = {}; |
299 control.id = V4L2_CID_POWER_LINE_FREQUENCY; | 302 control.id = V4L2_CID_POWER_LINE_FREQUENCY; |
300 control.value = (power_line_frequency == kPowerLine50Hz) ? | 303 control.value = (power_line_frequency == kPowerLine50Hz) ? |
301 V4L2_CID_POWER_LINE_FREQUENCY_50HZ : | 304 V4L2_CID_POWER_LINE_FREQUENCY_50HZ : |
302 V4L2_CID_POWER_LINE_FREQUENCY_60HZ; | 305 V4L2_CID_POWER_LINE_FREQUENCY_60HZ; |
303 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control)); | 306 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control)); |
304 } | 307 } |
305 | 308 |
306 // Store our current width and height. | 309 // Store our current width and height. |
307 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width, | 310 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width, |
308 video_fmt.fmt.pix.height); | 311 video_fmt.fmt.pix.height); |
309 capture_format_.frame_rate = frame_rate; | 312 capture_format_.frame_rate = frame_rate; |
310 capture_format_.pixel_format = | 313 capture_format_.pixel_format = |
311 V4l2ColorToVideoCaptureColorFormat(video_fmt.fmt.pix.pixelformat); | 314 V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat); |
312 | 315 |
313 // Start capturing. | |
314 if (!AllocateVideoBuffers()) { | 316 if (!AllocateVideoBuffers()) { |
315 // Error, We can not recover. | 317 SetErrorState("Allocate buffers failed"); |
316 SetErrorState("Allocate buffer failed"); | |
317 return; | 318 return; |
318 } | 319 } |
319 | 320 |
320 // Start UVC camera. | 321 // Start UVC camera. |
321 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 322 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
322 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) == -1) { | 323 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) == -1) { |
323 SetErrorState("VIDIOC_STREAMON failed"); | 324 SetErrorState("VIDIOC_STREAMON failed"); |
324 return; | 325 return; |
325 } | 326 } |
326 | 327 |
327 is_capturing_ = true; | 328 is_capturing_ = true; |
328 // Post task to start fetching frames from v4l2. | 329 // Post task to start fetching frames from v4l2. |
329 v4l2_thread_.message_loop()->PostTask( | 330 v4l2_thread_.message_loop()->PostTask( |
330 FROM_HERE, | 331 FROM_HERE, |
331 base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask, | 332 base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask, |
332 base::Unretained(this))); | 333 base::Unretained(this))); |
333 } | 334 } |
334 | 335 |
335 void VideoCaptureDeviceLinux::OnStopAndDeAllocate() { | 336 void VideoCaptureDeviceLinux::OnStopAndDeAllocate() { |
336 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); | 337 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); |
337 | 338 |
338 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 339 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
339 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &type)) < 0) { | 340 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &type)) < 0) { |
340 SetErrorState("VIDIOC_STREAMOFF failed"); | 341 SetErrorState("VIDIOC_STREAMOFF failed"); |
341 return; | 342 return; |
342 } | 343 } |
343 // We don't dare to deallocate the buffers if we can't stop | 344 // We don't dare to deallocate the buffers if we can't stop the capture |
344 // the capture device. | 345 // device. |
345 DeAllocateVideoBuffers(); | 346 DeAllocateVideoBuffers(); |
346 | 347 |
347 // We need to close and open the device if we want to change the settings | 348 // We need to close and open the device if we want to change the settings. |
348 // Otherwise VIDIOC_S_FMT will return error | 349 // Otherwise VIDIOC_S_FMT will return error. Sad but true. |
349 // Sad but true. | |
350 device_fd_.reset(); | 350 device_fd_.reset(); |
351 is_capturing_ = false; | 351 is_capturing_ = false; |
352 client_.reset(); | 352 client_.reset(); |
353 } | 353 } |
354 | 354 |
355 void VideoCaptureDeviceLinux::OnCaptureTask() { | 355 void VideoCaptureDeviceLinux::OnCaptureTask() { |
356 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); | 356 DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); |
357 if (!is_capturing_) | 357 if (!is_capturing_) |
358 return; | 358 return; |
359 | 359 |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
484 } | 484 } |
485 | 485 |
486 void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) { | 486 void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) { |
487 DCHECK(!v4l2_thread_.IsRunning() || | 487 DCHECK(!v4l2_thread_.IsRunning() || |
488 v4l2_thread_.message_loop() == base::MessageLoop::current()); | 488 v4l2_thread_.message_loop() == base::MessageLoop::current()); |
489 is_capturing_ = false; | 489 is_capturing_ = false; |
490 client_->OnError(reason); | 490 client_->OnError(reason); |
491 } | 491 } |
492 | 492 |
493 } // namespace media | 493 } // namespace media |
OLD | NEW |