| 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) { |
| 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 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 |