Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/v4l2_capture_delegate.h" | 5 #include "media/video/capture/linux/v4l2_capture_delegate.h" |
| 6 | 6 |
| 7 #include <linux/version.h> | |
| 7 #include <poll.h> | 8 #include <poll.h> |
| 8 #include <sys/fcntl.h> | 9 #include <sys/fcntl.h> |
| 9 #include <sys/ioctl.h> | 10 #include <sys/ioctl.h> |
| 10 #include <sys/mman.h> | 11 #include <sys/mman.h> |
| 11 | 12 |
| 12 #include "base/bind.h" | 13 #include "base/bind.h" |
| 13 #include "base/files/file_enumerator.h" | 14 #include "base/files/file_enumerator.h" |
| 14 #include "base/posix/eintr_wrapper.h" | 15 #include "base/posix/eintr_wrapper.h" |
| 15 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 16 #include "media/base/bind_to_current_loop.h" | 17 #include "media/base/bind_to_current_loop.h" |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 // MJPEG is usually sitting fairly low since we don't want to have to decode. | 54 // MJPEG is usually sitting fairly low since we don't want to have to decode. |
| 54 // However, is needed for large resolutions due to USB bandwidth limitations, | 55 // However, is needed for large resolutions due to USB bandwidth limitations, |
| 55 // so GetListOfUsableFourCcs() can duplicate it on top, see that method. | 56 // so GetListOfUsableFourCcs() can duplicate it on top, see that method. |
| 56 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1}, | 57 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1}, |
| 57 // JPEG works as MJPEG on some gspca webcams from field reports, see | 58 // JPEG works as MJPEG on some gspca webcams from field reports, see |
| 58 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the least | 59 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the least |
| 59 // preferred format. | 60 // preferred format. |
| 60 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1}, | 61 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1}, |
| 61 }; | 62 }; |
| 62 | 63 |
| 64 // This function investigates if the driver and the underlying platform support | |
| 65 // Dma-Buf and both have the appropriate format | |
| 66 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) | |
| 67 bool IsDmaBufCaptureSupported(const std::string& device_fd, | |
|
Pawel Osciak
2015/06/15 10:34:54
static
s/device_fd/device_name/
mcasas
2015/06/17 01:30:53
Done.
| |
| 68 VideoCaptureDevice::Client* client, | |
| 69 v4l2_buf_type buf_type) { | |
| 70 const base::ScopedFD fd(HANDLE_EINTR(open(device_fd.c_str(), O_RDONLY))); | |
| 71 if (!fd.is_valid()) { | |
| 72 return false; | |
| 73 } | |
| 74 // Check if the V4L2 device supports Dma Buf capture. | |
| 75 v4l2_requestbuffers r_buffer = {}; | |
| 76 r_buffer.type = buf_type; | |
| 77 r_buffer.memory = V4L2_MEMORY_DMABUF; | |
| 78 r_buffer.count = 1u; | |
| 79 if (HANDLE_EINTR(ioctl(fd.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | |
|
Pawel Osciak
2015/06/15 10:34:55
Unfortunately, we check if the V4L2 capturer suppo
mcasas
2015/06/17 01:30:52
This is true, and I think just landing this code "
| |
| 80 return false; | |
| 81 | |
| 82 // Check if the platform supports Dma-Buf capture. | |
| 83 scoped_ptr<VideoCaptureDevice::Client::Buffer> capture_buffer = | |
| 84 client->ReserveOutputBuffer(media::PIXEL_FORMAT_GPUMEMORYBUFFER, | |
|
Pawel Osciak
2015/06/15 10:34:55
This is not really a detail of V4L2 capture, so I
mcasas
2015/06/17 01:30:53
That sounds reasonable. As you can see, at the lev
| |
| 85 gfx::Size(0, 0)); | |
| 86 if (capture_buffer->GetType() != gfx::OZONE_NATIVE_BUFFER) | |
| 87 return false; | |
| 88 | |
| 89 // TODO(mcasas): Check |capture_buffer|'s underlying supported formats and | |
| 90 // correlate with those we support. For the time being, just continue. | |
| 91 | |
| 92 // TODO(mcasas): Query VCM to see if we have failed in the past to | |
| 93 // initialize | |
| 94 return true; | |
| 95 } | |
| 96 #endif | |
| 97 | |
| 63 // static | 98 // static |
| 64 scoped_refptr<V4L2CaptureDelegate> | 99 scoped_refptr<V4L2CaptureDelegate> |
| 65 V4L2CaptureDelegate::CreateV4L2CaptureDelegate( | 100 V4L2CaptureDelegate::CreateV4L2CaptureDelegate( |
| 66 const VideoCaptureDevice::Name& device_name, | 101 const VideoCaptureDevice::Name& device_name, |
| 67 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, | 102 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, |
| 103 VideoCaptureDevice::Client* client, | |
| 68 int power_line_frequency) { | 104 int power_line_frequency) { |
| 69 switch (device_name.capture_api_type()) { | 105 switch (device_name.capture_api_type()) { |
| 70 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE: | 106 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE: |
| 71 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane( | 107 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane( |
| 72 device_name, v4l2_task_runner, power_line_frequency)); | 108 device_name, v4l2_task_runner, power_line_frequency, |
| 109 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) | |
| 110 false | |
| 111 #else | |
| 112 IsDmaBufCaptureSupported(device_name.id(), client, | |
| 113 V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
| 114 #endif | |
| 115 )); | |
| 73 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE: | 116 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE: |
| 74 #if !defined(OS_OPENBSD) | 117 #if !defined(OS_OPENBSD) |
| 75 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane( | 118 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane( |
| 76 device_name, v4l2_task_runner, power_line_frequency)); | 119 device_name, v4l2_task_runner, power_line_frequency)); |
| 77 default: | 120 default: |
| 78 #endif | 121 #endif |
| 79 NOTIMPLEMENTED() << "Unknown V4L2 capture API type"; | 122 NOTIMPLEMENTED() << "Unknown V4L2 capture API type"; |
| 80 return scoped_refptr<V4L2CaptureDelegate>(); | 123 return scoped_refptr<V4L2CaptureDelegate>(); |
| 81 } | 124 } |
| 82 } | 125 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; | 178 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; |
| 136 } | 179 } |
| 137 } | 180 } |
| 138 | 181 |
| 139 void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start, | 182 void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start, |
| 140 size_t length) { | 183 size_t length) { |
| 141 Plane plane; | 184 Plane plane; |
| 142 plane.start = start; | 185 plane.start = start; |
| 143 plane.length = length; | 186 plane.length = length; |
| 144 plane.payload_size = 0; | 187 plane.payload_size = 0; |
| 188 plane.fd = -1; | |
| 145 planes_.push_back(plane); | 189 planes_.push_back(plane); |
| 146 } | 190 } |
| 147 | 191 |
| 192 void V4L2CaptureDelegate::BufferTracker::AddNonMmapedPlane(int fd) { | |
| 193 Plane plane; | |
| 194 plane.start = nullptr; | |
|
Pawel Osciak
2015/06/15 10:34:55
Zero-initialization could be done in Plane::Plane(
mcasas
2015/06/17 01:30:52
Done.
| |
| 195 plane.length = 0u; | |
| 196 plane.payload_size = 0; | |
| 197 plane.fd = fd; | |
| 198 planes_.push_back(plane); | |
| 199 } | |
| 200 | |
| 148 V4L2CaptureDelegate::V4L2CaptureDelegate( | 201 V4L2CaptureDelegate::V4L2CaptureDelegate( |
| 149 const VideoCaptureDevice::Name& device_name, | 202 const VideoCaptureDevice::Name& device_name, |
| 150 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, | 203 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, |
| 151 int power_line_frequency) | 204 int power_line_frequency) |
| 152 : capture_type_((device_name.capture_api_type() == | 205 : capture_type_((device_name.capture_api_type() == |
| 153 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE) | 206 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE) |
| 154 ? V4L2_BUF_TYPE_VIDEO_CAPTURE | 207 ? V4L2_BUF_TYPE_VIDEO_CAPTURE |
| 155 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), | 208 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), |
| 156 v4l2_task_runner_(v4l2_task_runner), | 209 v4l2_task_runner_(v4l2_task_runner), |
| 157 device_name_(device_name), | 210 device_name_(device_name), |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 DVLOG(1) << "Error setting power line frequency removal"; | 321 DVLOG(1) << "Error setting power line frequency removal"; |
| 269 } | 322 } |
| 270 | 323 |
| 271 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, | 324 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, |
| 272 video_fmt_.fmt.pix.height); | 325 video_fmt_.fmt.pix.height); |
| 273 capture_format_.frame_rate = frame_rate; | 326 capture_format_.frame_rate = frame_rate; |
| 274 capture_format_.pixel_format = pixel_format; | 327 capture_format_.pixel_format = pixel_format; |
| 275 | 328 |
| 276 v4l2_requestbuffers r_buffer = {}; | 329 v4l2_requestbuffers r_buffer = {}; |
| 277 r_buffer.type = capture_type_; | 330 r_buffer.type = capture_type_; |
| 278 r_buffer.memory = V4L2_MEMORY_MMAP; | |
| 279 r_buffer.count = kNumVideoBuffers; | 331 r_buffer.count = kNumVideoBuffers; |
| 332 FinishFillingV4L2RequestBuffers(&r_buffer); | |
|
Pawel Osciak
2015/06/15 10:34:55
I'd suggest just r_buffer.memory = memory(); if we
mcasas
2015/06/17 01:30:53
Done.
| |
| 280 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { | 333 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| 281 SetErrorState("Error requesting MMAP buffers from V4L2"); | 334 SetErrorState("Error requesting buffers from V4L2"); |
| 282 return; | 335 return; |
| 283 } | 336 } |
| 284 for (unsigned int i = 0; i < r_buffer.count; ++i) { | 337 for (unsigned int i = 0; i < r_buffer.count; ++i) { |
| 285 if (!MapAndQueueBuffer(i)) { | 338 if (!MapAndQueueBuffer(i)) { |
| 286 SetErrorState("Allocate buffer failed"); | 339 SetErrorState("Allocate buffer failed"); |
| 287 return; | 340 return; |
| 288 } | 341 } |
| 289 } | 342 } |
| 290 | 343 |
| 291 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) | 344 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 307 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) | 360 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) |
| 308 < 0) { | 361 < 0) { |
| 309 SetErrorState("VIDIOC_STREAMOFF failed"); | 362 SetErrorState("VIDIOC_STREAMOFF failed"); |
| 310 return; | 363 return; |
| 311 } | 364 } |
| 312 | 365 |
| 313 buffer_tracker_pool_.clear(); | 366 buffer_tracker_pool_.clear(); |
| 314 | 367 |
| 315 v4l2_requestbuffers r_buffer = {}; | 368 v4l2_requestbuffers r_buffer = {}; |
| 316 r_buffer.type = capture_type_; | 369 r_buffer.type = capture_type_; |
| 317 r_buffer.memory = V4L2_MEMORY_MMAP; | |
| 318 r_buffer.count = 0; | 370 r_buffer.count = 0; |
| 371 FinishFillingV4L2RequestBuffers(&r_buffer); | |
| 319 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 372 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
| 320 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0"); | 373 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0"); |
| 321 | 374 |
| 322 // At this point we can close the device. | 375 // At this point we can close the device. |
| 323 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 376 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. |
| 324 device_fd_.reset(); | 377 device_fd_.reset(); |
| 325 is_capturing_ = false; | 378 is_capturing_ = false; |
| 326 client_.reset(); | 379 client_.reset(); |
| 327 } | 380 } |
| 328 | 381 |
| 329 void V4L2CaptureDelegate::SetRotation(int rotation) { | 382 void V4L2CaptureDelegate::SetRotation(int rotation) { |
| 330 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 383 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 331 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0); | 384 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0); |
| 332 rotation_ = rotation; | 385 rotation_ = rotation; |
| 333 } | 386 } |
| 334 | 387 |
| 335 bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { | 388 bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { |
| 336 v4l2_buffer buffer; | 389 v4l2_buffer buffer; |
| 337 FillV4L2Buffer(&buffer, index); | 390 FillV4L2Buffer(&buffer, index, true /* for_enqueue */); |
| 338 | 391 |
| 339 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) { | 392 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) { |
| 340 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer"; | 393 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer"; |
| 341 return false; | 394 return false; |
| 342 } | 395 } |
| 343 | 396 |
| 344 const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker(); | 397 const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker(); |
| 345 if (!buffer_tracker->Init(device_fd_.get(), buffer)) { | 398 if (!buffer_tracker->Init(device_fd_.get(), buffer)) { |
| 346 DLOG(ERROR) << "Error creating BufferTracker"; | 399 DLOG(ERROR) << "Error creating BufferTracker"; |
| 347 return false; | 400 return false; |
| 348 } | 401 } |
| 349 buffer_tracker_pool_.push_back(buffer_tracker); | 402 buffer_tracker_pool_.push_back(buffer_tracker); |
| 350 | 403 |
| 351 // Enqueue the buffer in the drivers incoming queue. | 404 // Enqueue the buffer in the drivers incoming queue. |
| 352 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { | 405 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 353 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver"; | 406 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver"; |
| 354 return false; | 407 return false; |
| 355 } | 408 } |
| 356 return true; | 409 return true; |
| 357 } | 410 } |
| 358 | 411 |
| 359 void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer, | 412 void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer, |
| 360 int i) const { | 413 int i, |
| 414 bool for_enqueue) const { | |
| 361 memset(buffer, 0, sizeof(*buffer)); | 415 memset(buffer, 0, sizeof(*buffer)); |
| 362 buffer->memory = V4L2_MEMORY_MMAP; | |
| 363 buffer->index = i; | 416 buffer->index = i; |
| 364 FinishFillingV4L2Buffer(buffer); | 417 FinishFillingV4L2Buffer(buffer, for_enqueue); |
| 365 } | 418 } |
| 366 | 419 |
| 367 void V4L2CaptureDelegate::DoCapture() { | 420 void V4L2CaptureDelegate::DoCapture() { |
| 368 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 421 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 369 if (!is_capturing_) | 422 if (!is_capturing_) |
| 370 return; | 423 return; |
| 371 | 424 |
| 372 pollfd device_pfd = {}; | 425 pollfd device_pfd = {}; |
| 373 device_pfd.fd = device_fd_.get(); | 426 device_pfd.fd = device_fd_.get(); |
| 374 device_pfd.events = POLLIN; | 427 device_pfd.events = POLLIN; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 386 timeout_count_ = 0; | 439 timeout_count_ = 0; |
| 387 return; | 440 return; |
| 388 } | 441 } |
| 389 } else { | 442 } else { |
| 390 timeout_count_ = 0; | 443 timeout_count_ = 0; |
| 391 } | 444 } |
| 392 | 445 |
| 393 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. | 446 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. |
| 394 if (device_pfd.revents & POLLIN) { | 447 if (device_pfd.revents & POLLIN) { |
| 395 v4l2_buffer buffer; | 448 v4l2_buffer buffer; |
| 396 FillV4L2Buffer(&buffer, 0); | 449 FillV4L2Buffer(&buffer, 0, false /* for_enqueue */); |
| 397 | 450 |
| 398 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { | 451 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { |
| 399 SetErrorState("Failed to dequeue capture buffer"); | 452 SetErrorState("Failed to dequeue capture buffer"); |
| 400 return; | 453 return; |
| 401 } | 454 } |
| 402 | 455 |
| 403 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); | 456 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); |
| 404 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); | 457 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); |
| 405 | 458 |
| 406 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { | 459 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 407 SetErrorState("Failed to enqueue capture buffer"); | 460 SetErrorState("Failed to enqueue capture buffer"); |
| 408 return; | 461 return; |
| 409 } | 462 } |
| 410 } | 463 } |
| 411 | 464 |
| 412 v4l2_task_runner_->PostTask( | 465 v4l2_task_runner_->PostTask( |
| 413 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); | 466 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
| 414 } | 467 } |
| 415 | 468 |
| 416 void V4L2CaptureDelegate::SetErrorState(const std::string& reason) { | 469 void V4L2CaptureDelegate::SetErrorState(const std::string& reason) { |
| 417 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 470 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 418 is_capturing_ = false; | 471 is_capturing_ = false; |
| 419 client_->OnError(reason); | 472 client_->OnError(reason); |
| 420 } | 473 } |
| 421 | 474 |
| 422 } // namespace media | 475 } // namespace media |
| OLD | NEW |