| 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> | |
| 8 #include <fcntl.h> | |
| 9 #include <poll.h> | |
| 10 #if defined(OS_OPENBSD) | 7 #if defined(OS_OPENBSD) |
| 11 #include <sys/videoio.h> | 8 #include <sys/videoio.h> |
| 12 #else | 9 #else |
| 13 #include <linux/videodev2.h> | 10 #include <linux/videodev2.h> |
| 14 #endif | 11 #endif |
| 15 #include <sys/ioctl.h> | |
| 16 #include <sys/mman.h> | |
| 17 | 12 |
| 18 #include <list> | 13 #include <list> |
| 19 #include <string> | 14 #include <string> |
| 20 | 15 |
| 21 #include "base/bind.h" | 16 #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" | 17 #include "base/strings/stringprintf.h" |
| 18 #include "media/video/capture/linux/v4l2_video_capture_delegate.h" |
| 26 | 19 |
| 27 namespace media { | 20 namespace media { |
| 28 | 21 |
| 29 #define GET_V4L2_FOURCC_CHAR(a, index) ((char)( ((a) >> (8 * index)) & 0xff)) | |
| 30 | |
| 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 }; | |
| 42 | |
| 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); | |
| 50 | |
| 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(); | |
| 58 | |
| 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; | |
| 65 }; | |
| 66 | |
| 67 friend class base::RefCountedThreadSafe<V4L2CaptureDelegate>; | |
| 68 ~V4L2CaptureDelegate(); | |
| 69 | |
| 70 void DoCapture(); | |
| 71 bool AllocateVideoBuffers(); | |
| 72 void SetErrorState(const std::string& reason); | |
| 73 | |
| 74 const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner_; | |
| 75 | |
| 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_; | |
| 85 | |
| 86 // Clockwise rotation in degrees. This value should be 0, 90, 180, or 270. | |
| 87 int rotation_; | |
| 88 | |
| 89 DISALLOW_IMPLICIT_CONSTRUCTORS(V4L2CaptureDelegate); | |
| 90 }; | |
| 91 | |
| 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 | |
| 97 }; | |
| 98 | |
| 99 // USB VID and PID are both 4 bytes long. | 22 // USB VID and PID are both 4 bytes long. |
| 100 static const size_t kVidPidSize = 4; | 23 static const size_t kVidPidSize = 4; |
| 101 | 24 |
| 102 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding | 25 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding |
| 103 // USB device info directory. | 26 // USB device info directory. |
| 104 static const char kVidPathTemplate[] = | 27 static const char kVidPathTemplate[] = |
| 105 "/sys/class/video4linux/%s/device/../idVendor"; | 28 "/sys/class/video4linux/%s/device/../idVendor"; |
| 106 static const char kPidPathTemplate[] = | 29 static const char kPidPathTemplate[] = |
| 107 "/sys/class/video4linux/%s/device/../idProduct"; | 30 "/sys/class/video4linux/%s/device/../idProduct"; |
| 108 | 31 |
| 109 static bool ReadIdFile(const std::string path, std::string* id) { | 32 static bool ReadIdFile(const std::string path, std::string* id) { |
| 110 char id_buf[kVidPidSize]; | 33 char id_buf[kVidPidSize]; |
| 111 FILE* file = fopen(path.c_str(), "rb"); | 34 FILE* file = fopen(path.c_str(), "rb"); |
| 112 if (!file) | 35 if (!file) |
| 113 return false; | 36 return false; |
| 114 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; | 37 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; |
| 115 fclose(file); | 38 fclose(file); |
| 116 if (!success) | 39 if (!success) |
| 117 return false; | 40 return false; |
| 118 id->append(id_buf, kVidPidSize); | 41 id->append(id_buf, kVidPidSize); |
| 119 return true; | 42 return true; |
| 120 } | 43 } |
| 121 | 44 |
| 122 // This function translates Video4Linux pixel formats to Chromium pixel formats, | 45 // Translates Video4Linux pixel formats to Chromium pixel formats. |
| 123 // should only support those listed in GetListOfUsableFourCCs. | |
| 124 // static | 46 // static |
| 125 VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat( | 47 VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat( |
| 126 uint32 v4l2_fourcc) { | 48 uint32 v4l2_fourcc) { |
| 127 const struct { | 49 return V4L2VideoCaptureDelegate::V4l2FourCcToChromiumPixelFormat(v4l2_fourcc); |
| 128 uint32 fourcc; | 50 } |
| 129 VideoPixelFormat pixel_format; | 51 |
| 130 } kFourCcAndChromiumPixelFormat[] = { | 52 // Gets a list of usable Four CC formats prioritised. |
| 131 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420}, | 53 // static |
| 132 {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2}, | 54 std::list<uint32_t> VideoCaptureDeviceLinux::GetListOfUsableFourCCs( |
| 133 {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY}, | 55 bool favour_mjpeg) { |
| 134 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG}, | 56 return V4L2VideoCaptureDelegate::GetListOfUsableFourCss(favour_mjpeg); |
| 135 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG}, | |
| 136 }; | |
| 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; | |
| 140 } | |
| 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; | |
| 147 } | 57 } |
| 148 | 58 |
| 149 // static | 59 // static |
| 150 std::list<int> VideoCaptureDeviceLinux::GetListOfUsableFourCCs( | 60 int VideoCaptureDeviceLinux::TranslatePowerLineFrequencyToV4L2(int frequency) { |
| 151 bool favour_mjpeg) { | 61 switch (frequency) { |
| 152 std::list<int> fourccs; | 62 case kPowerLine50Hz: |
| 153 for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i) | 63 return V4L2_CID_POWER_LINE_FREQUENCY_50HZ; |
| 154 fourccs.push_back(kV4l2RawFmts[i]); | 64 case kPowerLine60Hz: |
| 155 if (favour_mjpeg) | 65 return V4L2_CID_POWER_LINE_FREQUENCY_60HZ; |
| 156 fourccs.push_front(V4L2_PIX_FMT_MJPEG); | 66 default: |
| 157 else | 67 // If we have no idea of the frequency, at least try and set it to AUTO. |
| 158 fourccs.push_back(V4L2_PIX_FMT_MJPEG); | 68 return V4L2_CID_POWER_LINE_FREQUENCY_AUTO; |
| 159 | 69 } |
| 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; | |
| 164 } | 70 } |
| 165 | 71 |
| 166 const std::string VideoCaptureDevice::Name::GetModel() const { | 72 const std::string VideoCaptureDevice::Name::GetModel() const { |
| 167 // |unique_id| is of the form "/dev/video2". |file_name| is "video2". | 73 // |unique_id| is of the form "/dev/video2". |file_name| is "video2". |
| 168 const std::string dev_dir = "/dev/"; | 74 const std::string dev_dir = "/dev/"; |
| 169 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); | 75 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); |
| 170 const std::string file_name = | 76 const std::string file_name = |
| 171 unique_id_.substr(dev_dir.length(), unique_id_.length()); | 77 unique_id_.substr(dev_dir.length(), unique_id_.length()); |
| 172 | 78 |
| 173 const std::string vidPath = | 79 const std::string vidPath = |
| (...skipping 23 matching lines...) Expand all Loading... |
| 197 v4l2_thread_.Stop(); | 103 v4l2_thread_.Stop(); |
| 198 } | 104 } |
| 199 | 105 |
| 200 void VideoCaptureDeviceLinux::AllocateAndStart( | 106 void VideoCaptureDeviceLinux::AllocateAndStart( |
| 201 const VideoCaptureParams& params, | 107 const VideoCaptureParams& params, |
| 202 scoped_ptr<VideoCaptureDevice::Client> client) { | 108 scoped_ptr<VideoCaptureDevice::Client> client) { |
| 203 DCHECK(!capture_impl_); | 109 DCHECK(!capture_impl_); |
| 204 if (v4l2_thread_.IsRunning()) | 110 if (v4l2_thread_.IsRunning()) |
| 205 return; // Wrong state. | 111 return; // Wrong state. |
| 206 v4l2_thread_.Start(); | 112 v4l2_thread_.Start(); |
| 207 capture_impl_ = new V4L2CaptureDelegate(device_name_, | 113 |
| 208 v4l2_thread_.message_loop_proxy(), | 114 const int line_frequency = |
| 209 GetPowerLineFrequencyForLocation()); | 115 TranslatePowerLineFrequencyToV4L2(GetPowerLineFrequencyForLocation()); |
| 116 capture_impl_ = V4L2VideoCaptureDelegate::CreateV4L2VideoCaptureDelegate( |
| 117 device_name_, params.requested_format.pixel_format, |
| 118 v4l2_thread_.message_loop_proxy(), line_frequency); |
| 119 if (!capture_impl_) { |
| 120 client->OnError("Failed to create VideoCaptureDelegate"); |
| 121 return; |
| 122 } |
| 210 v4l2_thread_.message_loop()->PostTask( | 123 v4l2_thread_.message_loop()->PostTask( |
| 211 FROM_HERE, | 124 FROM_HERE, |
| 212 base::Bind( | 125 base::Bind(&V4L2VideoCaptureDelegate::AllocateAndStart, capture_impl_, |
| 213 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart, | 126 params.requested_format.frame_size.width(), |
| 214 capture_impl_, | 127 params.requested_format.frame_size.height(), |
| 215 params.requested_format.frame_size.width(), | 128 params.requested_format.frame_rate, base::Passed(&client))); |
| 216 params.requested_format.frame_size.height(), | |
| 217 params.requested_format.frame_rate, | |
| 218 base::Passed(&client))); | |
| 219 } | 129 } |
| 220 | 130 |
| 221 void VideoCaptureDeviceLinux::StopAndDeAllocate() { | 131 void VideoCaptureDeviceLinux::StopAndDeAllocate() { |
| 222 if (!v4l2_thread_.IsRunning()) | 132 if (!v4l2_thread_.IsRunning()) |
| 223 return; // Wrong state. | 133 return; // Wrong state. |
| 224 v4l2_thread_.message_loop()->PostTask( | 134 v4l2_thread_.message_loop()->PostTask( |
| 225 FROM_HERE, | 135 FROM_HERE, |
| 226 base::Bind( | 136 base::Bind(&V4L2VideoCaptureDelegate::StopAndDeAllocate, capture_impl_)); |
| 227 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate, | |
| 228 capture_impl_)); | |
| 229 v4l2_thread_.Stop(); | 137 v4l2_thread_.Stop(); |
| 230 // TODO(mcasas): VCDLinux called DeAllocateVideoBuffers() a second time after | 138 |
| 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; | 139 capture_impl_ = NULL; |
| 236 } | 140 } |
| 237 | 141 |
| 238 void VideoCaptureDeviceLinux::SetRotation(int rotation) { | 142 void VideoCaptureDeviceLinux::SetRotation(int rotation) { |
| 239 if (v4l2_thread_.IsRunning()) { | 143 if (v4l2_thread_.IsRunning()) { |
| 240 v4l2_thread_.message_loop()->PostTask( | 144 v4l2_thread_.message_loop()->PostTask( |
| 241 FROM_HERE, | 145 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::SetRotation, |
| 242 base::Bind( | 146 capture_impl_, rotation)); |
| 243 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation, | |
| 244 capture_impl_, | |
| 245 rotation)); | |
| 246 } | 147 } |
| 247 } | 148 } |
| 248 | 149 |
| 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) { | |
| 261 } | |
| 262 | |
| 263 VideoCaptureDeviceLinux::V4L2CaptureDelegate::~V4L2CaptureDelegate() { | |
| 264 DCHECK(!client_); | |
| 265 } | |
| 266 | |
| 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(); | |
| 275 | |
| 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; | |
| 281 } | |
| 282 | |
| 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; | |
| 290 } | |
| 291 | |
| 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(); | |
| 297 | |
| 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); | |
| 303 } | |
| 304 if (best == desired_v4l2_formats.end()) { | |
| 305 SetErrorState("Failed to find a supported camera format."); | |
| 306 return; | |
| 307 } | |
| 308 | |
| 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; | |
| 318 } | |
| 319 | |
| 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); | |
| 333 | |
| 334 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < | |
| 335 0) { | |
| 336 SetErrorState("Failed to set camera framerate"); | |
| 337 return; | |
| 338 } | |
| 339 DVLOG(2) << "Actual camera driverframerate: " | |
| 340 << streamparm.parm.capture.timeperframe.denominator << "/" | |
| 341 << streamparm.parm.capture.timeperframe.numerator; | |
| 342 } | |
| 343 } | |
| 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? | |
| 346 | |
| 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)); | |
| 357 } | |
| 358 | |
| 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); | |
| 364 | |
| 365 if (!AllocateVideoBuffers()) { | |
| 366 SetErrorState("Allocate buffer failed (Cannot recover from this error)"); | |
| 367 return; | |
| 368 } | |
| 369 | |
| 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; | |
| 374 } | |
| 375 | |
| 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)); | |
| 382 } | |
| 383 | |
| 384 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate() { | |
| 385 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | |
| 386 | |
| 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; | |
| 391 } | |
| 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"); | |
| 396 | |
| 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(); | |
| 402 } | |
| 403 | |
| 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; | |
| 408 } | |
| 409 | |
| 410 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture() { | |
| 411 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | |
| 412 if (!is_capturing_) | |
| 413 return; | |
| 414 | |
| 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; | |
| 422 } | |
| 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; | |
| 431 } | |
| 432 } else { | |
| 433 timeout_count_ = 0; | |
| 434 } | |
| 435 | |
| 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; | |
| 444 } | |
| 445 client_->OnIncomingCapturedData( | |
| 446 static_cast<uint8*>(buffer_pool_[buffer.index].start), | |
| 447 buffer.bytesused, | |
| 448 capture_format_, | |
| 449 rotation_, | |
| 450 base::TimeTicks::Now()); | |
| 451 | |
| 452 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) | |
| 453 SetErrorState("Failed to enqueue capture buffer"); | |
| 454 } | |
| 455 | |
| 456 v4l2_task_runner_->PostTask( | |
| 457 FROM_HERE, | |
| 458 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture, | |
| 459 this)); | |
| 460 } | |
| 461 | |
| 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; | |
| 470 } | |
| 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; | |
| 484 } | |
| 485 | |
| 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; | |
| 493 } | |
| 494 | |
| 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; | |
| 501 } | |
| 502 } | |
| 503 return true; | |
| 504 } | |
| 505 | |
| 506 bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::DeAllocateVideoBuffers() { | |
| 507 if (!buffer_pool_) | |
| 508 return true; | |
| 509 | |
| 510 for (int i = 0; i < buffer_pool_size_; ++i) | |
| 511 munmap(buffer_pool_[i].start, buffer_pool_[i].length); | |
| 512 | |
| 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; | |
| 519 | |
| 520 delete [] buffer_pool_; | |
| 521 buffer_pool_ = NULL; | |
| 522 buffer_pool_size_ = 0; | |
| 523 return true; | |
| 524 } | |
| 525 | |
| 526 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState( | |
| 527 const std::string& reason) { | |
| 528 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | |
| 529 is_capturing_ = false; | |
| 530 client_->OnError(reason); | |
| 531 } | |
| 532 | |
| 533 } // namespace media | 150 } // namespace media |
| OLD | NEW |