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> |
7 #if defined(OS_OPENBSD) | 10 #if defined(OS_OPENBSD) |
8 #include <sys/videoio.h> | 11 #include <sys/videoio.h> |
9 #else | 12 #else |
10 #include <linux/videodev2.h> | 13 #include <linux/videodev2.h> |
11 #endif | 14 #endif |
| 15 #include <sys/ioctl.h> |
| 16 #include <sys/mman.h> |
12 | 17 |
13 #include <list> | 18 #include <list> |
14 #include <string> | 19 #include <string> |
15 | 20 |
16 #include "base/bind.h" | 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" |
17 #include "base/strings/stringprintf.h" | 25 #include "base/strings/stringprintf.h" |
18 #include "media/video/capture/linux/v4l2_capture_delegate.h" | |
19 | 26 |
20 namespace media { | 27 namespace media { |
21 | 28 |
| 29 #define GET_V4L2_FOURCC_CHAR(a, index) ((char)( ((a) >> (8 * index)) & 0xff)) |
| 30 |
| 31 // Desired number of video buffers to allocate. The actual number of allocated |
| 32 // buffers by v4l2 driver can be higher or lower than this number. |
| 33 // kNumVideoBuffers should not be too small, or Chrome may not return enough |
| 34 // buffers back to driver in time. |
| 35 const uint32 kNumVideoBuffers = 4; |
| 36 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw. |
| 37 enum { kCaptureTimeoutMs = 200 }; |
| 38 // The number of continuous timeouts tolerated before treated as error. |
| 39 enum { kContinuousTimeoutLimit = 10 }; |
| 40 // MJPEG is preferred if the width or height is larger than this. |
| 41 enum { kMjpegWidth = 640 }; |
| 42 enum { kMjpegHeight = 480 }; |
| 43 // Typical framerate, in fps |
| 44 enum { kTypicalFramerate = 30 }; |
| 45 |
| 46 class VideoCaptureDeviceLinux::V4L2CaptureDelegate |
| 47 : public base::RefCountedThreadSafe<V4L2CaptureDelegate>{ |
| 48 public: |
| 49 V4L2CaptureDelegate( |
| 50 const Name& device_name, |
| 51 const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner, |
| 52 int power_line_frequency); |
| 53 |
| 54 void AllocateAndStart(int width, |
| 55 int height, |
| 56 float frame_rate, |
| 57 scoped_ptr<Client> client); |
| 58 void StopAndDeAllocate(); |
| 59 void SetRotation(int rotation); |
| 60 bool DeAllocateVideoBuffers(); |
| 61 |
| 62 private: |
| 63 // Buffers used to receive captured frames from v4l2. |
| 64 struct Buffer { |
| 65 Buffer() : start(0), length(0) {} |
| 66 void* start; |
| 67 size_t length; |
| 68 }; |
| 69 |
| 70 friend class base::RefCountedThreadSafe<V4L2CaptureDelegate>; |
| 71 ~V4L2CaptureDelegate(); |
| 72 |
| 73 void DoCapture(); |
| 74 bool AllocateVideoBuffers(); |
| 75 void SetErrorState(const std::string& reason); |
| 76 |
| 77 const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner_; |
| 78 |
| 79 bool is_capturing_; |
| 80 scoped_ptr<VideoCaptureDevice::Client> client_; |
| 81 const Name device_name_; |
| 82 base::ScopedFD device_fd_; // File descriptor for the opened camera device. |
| 83 Buffer* buffer_pool_; |
| 84 int buffer_pool_size_; // Number of allocated buffers. |
| 85 int timeout_count_; |
| 86 VideoCaptureFormat capture_format_; |
| 87 const int power_line_frequency_; |
| 88 |
| 89 // Clockwise rotation in degrees. This value should be 0, 90, 180, or 270. |
| 90 int rotation_; |
| 91 |
| 92 DISALLOW_IMPLICIT_CONSTRUCTORS(V4L2CaptureDelegate); |
| 93 }; |
| 94 |
| 95 // V4L2 color formats VideoCaptureDeviceLinux support. |
| 96 static const int32 kV4l2RawFmts[] = { |
| 97 V4L2_PIX_FMT_YUV420, |
| 98 V4L2_PIX_FMT_YUYV, |
| 99 V4L2_PIX_FMT_UYVY |
| 100 }; |
| 101 |
22 // USB VID and PID are both 4 bytes long. | 102 // USB VID and PID are both 4 bytes long. |
23 static const size_t kVidPidSize = 4; | 103 static const size_t kVidPidSize = 4; |
24 | 104 |
25 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding | 105 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding |
26 // USB device info directory. | 106 // USB device info directory. |
27 static const char kVidPathTemplate[] = | 107 static const char kVidPathTemplate[] = |
28 "/sys/class/video4linux/%s/device/../idVendor"; | 108 "/sys/class/video4linux/%s/device/../idVendor"; |
29 static const char kPidPathTemplate[] = | 109 static const char kPidPathTemplate[] = |
30 "/sys/class/video4linux/%s/device/../idProduct"; | 110 "/sys/class/video4linux/%s/device/../idProduct"; |
31 | 111 |
32 static bool ReadIdFile(const std::string path, std::string* id) { | 112 static bool ReadIdFile(const std::string path, std::string* id) { |
33 char id_buf[kVidPidSize]; | 113 char id_buf[kVidPidSize]; |
34 FILE* file = fopen(path.c_str(), "rb"); | 114 FILE* file = fopen(path.c_str(), "rb"); |
35 if (!file) | 115 if (!file) |
36 return false; | 116 return false; |
37 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; | 117 const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; |
38 fclose(file); | 118 fclose(file); |
39 if (!success) | 119 if (!success) |
40 return false; | 120 return false; |
41 id->append(id_buf, kVidPidSize); | 121 id->append(id_buf, kVidPidSize); |
42 return true; | 122 return true; |
43 } | 123 } |
44 | 124 |
45 // Translates Video4Linux pixel formats to Chromium pixel formats. | 125 // This function translates Video4Linux pixel formats to Chromium pixel formats, |
| 126 // should only support those listed in GetListOfUsableFourCCs. |
46 // static | 127 // static |
47 VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat( | 128 VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat( |
48 uint32 v4l2_fourcc) { | 129 uint32 v4l2_fourcc) { |
49 return V4L2CaptureDelegate::V4l2FourCcToChromiumPixelFormat(v4l2_fourcc); | 130 const struct { |
| 131 uint32 fourcc; |
| 132 VideoPixelFormat pixel_format; |
| 133 } kFourCcAndChromiumPixelFormat[] = { |
| 134 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420}, |
| 135 {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2}, |
| 136 {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY}, |
| 137 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG}, |
| 138 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG}, |
| 139 }; |
| 140 for (const auto& fourcc_and_pixel_format : kFourCcAndChromiumPixelFormat) { |
| 141 if (fourcc_and_pixel_format.fourcc == v4l2_fourcc) |
| 142 return fourcc_and_pixel_format.pixel_format; |
| 143 } |
| 144 DVLOG(1) << "Unsupported pixel format: " |
| 145 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 0) |
| 146 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 1) |
| 147 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 2) |
| 148 << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 3); |
| 149 return PIXEL_FORMAT_UNKNOWN; |
50 } | 150 } |
51 | 151 |
52 // Gets a list of usable Four CC formats prioritised. | |
53 // static | 152 // static |
54 std::list<uint32_t> VideoCaptureDeviceLinux::GetListOfUsableFourCCs( | 153 std::list<int> VideoCaptureDeviceLinux::GetListOfUsableFourCCs( |
55 bool favour_mjpeg) { | 154 bool favour_mjpeg) { |
56 return V4L2CaptureDelegate::GetListOfUsableFourCcs(favour_mjpeg); | 155 std::list<int> fourccs; |
| 156 for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i) |
| 157 fourccs.push_back(kV4l2RawFmts[i]); |
| 158 if (favour_mjpeg) |
| 159 fourccs.push_front(V4L2_PIX_FMT_MJPEG); |
| 160 else |
| 161 fourccs.push_back(V4L2_PIX_FMT_MJPEG); |
| 162 |
| 163 // JPEG works as MJPEG on some gspca webcams from field reports. |
| 164 // Put it as the least preferred format. |
| 165 fourccs.push_back(V4L2_PIX_FMT_JPEG); |
| 166 return fourccs; |
57 } | 167 } |
58 | 168 |
59 const std::string VideoCaptureDevice::Name::GetModel() const { | 169 const std::string VideoCaptureDevice::Name::GetModel() const { |
60 // |unique_id| is of the form "/dev/video2". |file_name| is "video2". | 170 // |unique_id| is of the form "/dev/video2". |file_name| is "video2". |
61 const std::string dev_dir = "/dev/"; | 171 const std::string dev_dir = "/dev/"; |
62 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); | 172 DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); |
63 const std::string file_name = | 173 const std::string file_name = |
64 unique_id_.substr(dev_dir.length(), unique_id_.length()); | 174 unique_id_.substr(dev_dir.length(), unique_id_.length()); |
65 | 175 |
66 const std::string vidPath = | 176 const std::string vidPath = |
(...skipping 23 matching lines...) Expand all Loading... |
90 v4l2_thread_.Stop(); | 200 v4l2_thread_.Stop(); |
91 } | 201 } |
92 | 202 |
93 void VideoCaptureDeviceLinux::AllocateAndStart( | 203 void VideoCaptureDeviceLinux::AllocateAndStart( |
94 const VideoCaptureParams& params, | 204 const VideoCaptureParams& params, |
95 scoped_ptr<VideoCaptureDevice::Client> client) { | 205 scoped_ptr<VideoCaptureDevice::Client> client) { |
96 DCHECK(!capture_impl_); | 206 DCHECK(!capture_impl_); |
97 if (v4l2_thread_.IsRunning()) | 207 if (v4l2_thread_.IsRunning()) |
98 return; // Wrong state. | 208 return; // Wrong state. |
99 v4l2_thread_.Start(); | 209 v4l2_thread_.Start(); |
100 | 210 capture_impl_ = new V4L2CaptureDelegate(device_name_, |
101 const int line_frequency = | 211 v4l2_thread_.message_loop_proxy(), |
102 TranslatePowerLineFrequencyToV4L2(GetPowerLineFrequencyForLocation()); | 212 GetPowerLineFrequencyForLocation()); |
103 capture_impl_ = V4L2CaptureDelegate::CreateV4L2CaptureDelegate( | |
104 device_name_, v4l2_thread_.message_loop_proxy(), line_frequency); | |
105 if (!capture_impl_) { | |
106 client->OnError("Failed to create VideoCaptureDelegate"); | |
107 return; | |
108 } | |
109 v4l2_thread_.message_loop()->PostTask( | 213 v4l2_thread_.message_loop()->PostTask( |
110 FROM_HERE, | 214 FROM_HERE, |
111 base::Bind(&V4L2CaptureDelegate::AllocateAndStart, capture_impl_, | 215 base::Bind( |
112 params.requested_format.frame_size.width(), | 216 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart, |
113 params.requested_format.frame_size.height(), | 217 capture_impl_, |
114 params.requested_format.frame_rate, base::Passed(&client))); | 218 params.requested_format.frame_size.width(), |
| 219 params.requested_format.frame_size.height(), |
| 220 params.requested_format.frame_rate, |
| 221 base::Passed(&client))); |
115 } | 222 } |
116 | 223 |
117 void VideoCaptureDeviceLinux::StopAndDeAllocate() { | 224 void VideoCaptureDeviceLinux::StopAndDeAllocate() { |
118 if (!v4l2_thread_.IsRunning()) | 225 if (!v4l2_thread_.IsRunning()) |
119 return; // Wrong state. | 226 return; // Wrong state. |
120 v4l2_thread_.message_loop()->PostTask( | 227 v4l2_thread_.message_loop()->PostTask( |
121 FROM_HERE, | 228 FROM_HERE, |
122 base::Bind(&V4L2CaptureDelegate::StopAndDeAllocate, capture_impl_)); | 229 base::Bind( |
| 230 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate, |
| 231 capture_impl_)); |
123 v4l2_thread_.Stop(); | 232 v4l2_thread_.Stop(); |
124 | 233 // TODO(mcasas): VCDLinux called DeAllocateVideoBuffers() a second time after |
| 234 // stopping |v4l2_thread_| to make sure buffers were completely deallocated. |
| 235 // Investigate if that's needed, otherwise remove the following line and make |
| 236 // V4L2CaptureDelegate::DeAllocateVideoBuffers() private. |
| 237 capture_impl_->DeAllocateVideoBuffers(); |
125 capture_impl_ = NULL; | 238 capture_impl_ = NULL; |
126 } | 239 } |
127 | 240 |
128 void VideoCaptureDeviceLinux::SetRotation(int rotation) { | 241 void VideoCaptureDeviceLinux::SetRotation(int rotation) { |
129 if (v4l2_thread_.IsRunning()) { | 242 if (v4l2_thread_.IsRunning()) { |
130 v4l2_thread_.message_loop()->PostTask( | 243 v4l2_thread_.message_loop()->PostTask( |
131 FROM_HERE, base::Bind(&V4L2CaptureDelegate::SetRotation, | 244 FROM_HERE, |
132 capture_impl_, rotation)); | 245 base::Bind( |
133 } | 246 &VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation, |
134 } | 247 capture_impl_, |
135 | 248 rotation)); |
136 // static | 249 } |
137 int VideoCaptureDeviceLinux::TranslatePowerLineFrequencyToV4L2(int frequency) { | 250 } |
138 switch (frequency) { | 251 |
139 case kPowerLine50Hz: | 252 VideoCaptureDeviceLinux::V4L2CaptureDelegate::V4L2CaptureDelegate( |
140 return V4L2_CID_POWER_LINE_FREQUENCY_50HZ; | 253 const Name& device_name, |
141 case kPowerLine60Hz: | 254 const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner, |
142 return V4L2_CID_POWER_LINE_FREQUENCY_60HZ; | 255 int power_line_frequency) |
143 default: | 256 : v4l2_task_runner_(v4l2_task_runner), |
144 // If we have no idea of the frequency, at least try and set it to AUTO. | 257 is_capturing_(false), |
145 return V4L2_CID_POWER_LINE_FREQUENCY_AUTO; | 258 device_name_(device_name), |
146 } | 259 buffer_pool_(NULL), |
| 260 buffer_pool_size_(0), |
| 261 timeout_count_(0), |
| 262 power_line_frequency_(power_line_frequency), |
| 263 rotation_(0) { |
| 264 } |
| 265 |
| 266 VideoCaptureDeviceLinux::V4L2CaptureDelegate::~V4L2CaptureDelegate() { |
| 267 DCHECK(!client_); |
| 268 } |
| 269 |
| 270 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart( |
| 271 int width, |
| 272 int height, |
| 273 float frame_rate, |
| 274 scoped_ptr<Client> client) { |
| 275 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 276 DCHECK(client); |
| 277 client_ = client.Pass(); |
| 278 |
| 279 // Need to open camera with O_RDWR after Linux kernel 3.3. |
| 280 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR))); |
| 281 if (!device_fd_.is_valid()) { |
| 282 SetErrorState("Failed to open V4L2 device driver file."); |
| 283 return; |
| 284 } |
| 285 |
| 286 v4l2_capability cap = {}; |
| 287 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && |
| 288 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE && |
| 289 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)))) { |
| 290 device_fd_.reset(); |
| 291 SetErrorState("This is not a V4L2 video capture device"); |
| 292 return; |
| 293 } |
| 294 |
| 295 // Get supported video formats in preferred order. |
| 296 // For large resolutions, favour mjpeg over raw formats. |
| 297 const std::list<int>& desired_v4l2_formats = |
| 298 GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight); |
| 299 std::list<int>::const_iterator best = desired_v4l2_formats.end(); |
| 300 |
| 301 v4l2_fmtdesc fmtdesc = {}; |
| 302 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 303 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; |
| 304 ++fmtdesc.index) { |
| 305 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); |
| 306 } |
| 307 if (best == desired_v4l2_formats.end()) { |
| 308 SetErrorState("Failed to find a supported camera format."); |
| 309 return; |
| 310 } |
| 311 |
| 312 v4l2_format video_fmt = {}; |
| 313 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 314 video_fmt.fmt.pix.sizeimage = 0; |
| 315 video_fmt.fmt.pix.width = width; |
| 316 video_fmt.fmt.pix.height = height; |
| 317 video_fmt.fmt.pix.pixelformat = *best; |
| 318 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) { |
| 319 SetErrorState("Failed to set video capture format"); |
| 320 return; |
| 321 } |
| 322 |
| 323 // Set capture framerate in the form of capture interval. |
| 324 v4l2_streamparm streamparm = {}; |
| 325 streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 326 // The following line checks that the driver knows about framerate get/set. |
| 327 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { |
| 328 // Now check if the device is able to accept a capture framerate set. |
| 329 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { |
| 330 // |frame_rate| is float, approximate by a fraction. |
| 331 streamparm.parm.capture.timeperframe.numerator = |
| 332 media::kFrameRatePrecision; |
| 333 streamparm.parm.capture.timeperframe.denominator = (frame_rate) ? |
| 334 (frame_rate * media::kFrameRatePrecision) : |
| 335 (kTypicalFramerate * media::kFrameRatePrecision); |
| 336 |
| 337 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < |
| 338 0) { |
| 339 SetErrorState("Failed to set camera framerate"); |
| 340 return; |
| 341 } |
| 342 DVLOG(2) << "Actual camera driverframerate: " |
| 343 << streamparm.parm.capture.timeperframe.denominator << "/" |
| 344 << streamparm.parm.capture.timeperframe.numerator; |
| 345 } |
| 346 } |
| 347 // TODO(mcasas): what should be done if the camera driver does not allow |
| 348 // framerate configuration, or the actual one is different from the desired? |
| 349 |
| 350 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported |
| 351 // operation (|errno| == EINVAL in this case) or plain failure. |
| 352 if ((power_line_frequency_ == kPowerLine50Hz) || |
| 353 (power_line_frequency_ == kPowerLine60Hz)) { |
| 354 struct v4l2_control control = {}; |
| 355 control.id = V4L2_CID_POWER_LINE_FREQUENCY; |
| 356 control.value = (power_line_frequency_ == kPowerLine50Hz) |
| 357 ? V4L2_CID_POWER_LINE_FREQUENCY_50HZ |
| 358 : V4L2_CID_POWER_LINE_FREQUENCY_60HZ; |
| 359 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control)); |
| 360 } |
| 361 |
| 362 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width, |
| 363 video_fmt.fmt.pix.height); |
| 364 capture_format_.frame_rate = frame_rate; |
| 365 capture_format_.pixel_format = |
| 366 V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat); |
| 367 |
| 368 if (!AllocateVideoBuffers()) { |
| 369 SetErrorState("Allocate buffer failed (Cannot recover from this error)"); |
| 370 return; |
| 371 } |
| 372 |
| 373 const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 374 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) < 0) { |
| 375 SetErrorState("VIDIOC_STREAMON failed"); |
| 376 return; |
| 377 } |
| 378 |
| 379 is_capturing_ = true; |
| 380 // Post task to start fetching frames from v4l2. |
| 381 v4l2_task_runner_->PostTask( |
| 382 FROM_HERE, |
| 383 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture, |
| 384 this)); |
| 385 } |
| 386 |
| 387 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate() { |
| 388 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 389 |
| 390 const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 391 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &type)) < 0) { |
| 392 SetErrorState("VIDIOC_STREAMOFF failed"); |
| 393 return; |
| 394 } |
| 395 // We don't dare to deallocate the buffers if we can't stop the capture |
| 396 // device. |
| 397 if (!DeAllocateVideoBuffers()) |
| 398 SetErrorState("Failed to reset buffers"); |
| 399 |
| 400 // We need to close and open the device if we want to change the settings. |
| 401 // Otherwise VIDIOC_S_FMT will return error. Sad but true. |
| 402 device_fd_.reset(); |
| 403 is_capturing_ = false; |
| 404 client_.reset(); |
| 405 } |
| 406 |
| 407 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation(int rotation) { |
| 408 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 409 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0); |
| 410 rotation_ = rotation; |
| 411 } |
| 412 |
| 413 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture() { |
| 414 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 415 if (!is_capturing_) |
| 416 return; |
| 417 |
| 418 pollfd device_pfd = {}; |
| 419 device_pfd.fd = device_fd_.get(); |
| 420 device_pfd.events = POLLIN; |
| 421 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs)); |
| 422 if (result < 0) { |
| 423 SetErrorState("Poll failed"); |
| 424 return; |
| 425 } |
| 426 // Check if poll() timed out; track the amount of times it did in a row and |
| 427 // throw an error if it times out too many times. |
| 428 if (result == 0) { |
| 429 timeout_count_++; |
| 430 if (timeout_count_ >= kContinuousTimeoutLimit) { |
| 431 SetErrorState("Multiple continuous timeouts while read-polling."); |
| 432 timeout_count_ = 0; |
| 433 return; |
| 434 } |
| 435 } else { |
| 436 timeout_count_ = 0; |
| 437 } |
| 438 |
| 439 // Check if the driver has filled a buffer. |
| 440 if (device_pfd.revents & POLLIN) { |
| 441 v4l2_buffer buffer = {}; |
| 442 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 443 buffer.memory = V4L2_MEMORY_MMAP; |
| 444 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { |
| 445 SetErrorState("Failed to dequeue capture buffer"); |
| 446 return; |
| 447 } |
| 448 client_->OnIncomingCapturedData( |
| 449 static_cast<uint8*>(buffer_pool_[buffer.index].start), |
| 450 buffer.bytesused, |
| 451 capture_format_, |
| 452 rotation_, |
| 453 base::TimeTicks::Now()); |
| 454 |
| 455 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) |
| 456 SetErrorState("Failed to enqueue capture buffer"); |
| 457 } |
| 458 |
| 459 v4l2_task_runner_->PostTask( |
| 460 FROM_HERE, |
| 461 base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture, |
| 462 this)); |
| 463 } |
| 464 |
| 465 bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateVideoBuffers() { |
| 466 v4l2_requestbuffers r_buffer = {}; |
| 467 r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 468 r_buffer.memory = V4L2_MEMORY_MMAP; |
| 469 r_buffer.count = kNumVideoBuffers; |
| 470 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| 471 DLOG(ERROR) << "Error requesting MMAP buffers from V4L2"; |
| 472 return false; |
| 473 } |
| 474 buffer_pool_size_ = r_buffer.count; |
| 475 buffer_pool_ = new Buffer[buffer_pool_size_]; |
| 476 for (unsigned int i = 0; i < r_buffer.count; ++i) { |
| 477 v4l2_buffer buffer = {}; |
| 478 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 479 buffer.memory = V4L2_MEMORY_MMAP; |
| 480 buffer.index = i; |
| 481 buffer.length = 1; |
| 482 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) { |
| 483 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer"; |
| 484 return false; |
| 485 } |
| 486 |
| 487 // Some devices require mmap() to be called with both READ and WRITE. |
| 488 // See http://crbug.com/178582. |
| 489 buffer_pool_[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, |
| 490 MAP_SHARED, device_fd_.get(), buffer.m.offset); |
| 491 if (buffer_pool_[i].start == MAP_FAILED) { |
| 492 DLOG(ERROR) << "Error mmmap()ing a V4L2 buffer into userspace"; |
| 493 return false; |
| 494 } |
| 495 |
| 496 buffer_pool_[i].length = buffer.length; |
| 497 // Enqueue the buffer in the drivers incoming queue. |
| 498 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 499 DLOG(ERROR) |
| 500 << "Error enqueuing a V4L2 buffer back to the drivers incoming queue"; |
| 501 return false; |
| 502 } |
| 503 } |
| 504 return true; |
| 505 } |
| 506 |
| 507 bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::DeAllocateVideoBuffers() { |
| 508 if (!buffer_pool_) |
| 509 return true; |
| 510 |
| 511 for (int i = 0; i < buffer_pool_size_; ++i) |
| 512 munmap(buffer_pool_[i].start, buffer_pool_[i].length); |
| 513 |
| 514 v4l2_requestbuffers r_buffer = {}; |
| 515 r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 516 r_buffer.memory = V4L2_MEMORY_MMAP; |
| 517 r_buffer.count = 0; |
| 518 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
| 519 return false; |
| 520 |
| 521 delete [] buffer_pool_; |
| 522 buffer_pool_ = NULL; |
| 523 buffer_pool_size_ = 0; |
| 524 return true; |
| 525 } |
| 526 |
| 527 void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState( |
| 528 const std::string& reason) { |
| 529 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 530 is_capturing_ = false; |
| 531 client_->OnError(reason); |
147 } | 532 } |
148 | 533 |
149 } // namespace media | 534 } // namespace media |
OLD | NEW |