| 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 supports Dma-Buf. Such capacity is |
| 65 // not present in non-Ozone platforms nor for certain kernel versions. |
| 66 static bool IsDmaBufCaptureSupported(const std::string& device_name, |
| 67 v4l2_buf_type buf_type) { |
| 68 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) || !defined(USE_OZONE) |
| 69 return false; |
| 70 #else |
| 71 const base::ScopedFD fd(HANDLE_EINTR(open(device_name.c_str(), O_RDONLY))); |
| 72 if (!fd.is_valid()) { |
| 73 return false; |
| 74 } |
| 75 // Check if the V4L2 device supports Dma Buf capture. |
| 76 v4l2_requestbuffers r_buffer = {}; |
| 77 r_buffer.type = buf_type; |
| 78 r_buffer.memory = V4L2_MEMORY_DMABUF; |
| 79 r_buffer.count = 1u; |
| 80 if (HANDLE_EINTR(ioctl(fd.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
| 81 return false; |
| 82 return true; |
| 83 #endif |
| 84 } |
| 85 |
| 63 // static | 86 // static |
| 64 scoped_refptr<V4L2CaptureDelegate> | 87 scoped_refptr<V4L2CaptureDelegate> |
| 65 V4L2CaptureDelegate::CreateV4L2CaptureDelegate( | 88 V4L2CaptureDelegate::CreateV4L2CaptureDelegate( |
| 66 const VideoCaptureDevice::Name& device_name, | 89 const VideoCaptureDevice::Name& device_name, |
| 67 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, | 90 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, |
| 91 VideoCaptureDevice::Client* client, |
| 92 bool allow_using_dma_bufs, |
| 68 int power_line_frequency) { | 93 int power_line_frequency) { |
| 69 switch (device_name.capture_api_type()) { | 94 switch (device_name.capture_api_type()) { |
| 70 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE: | 95 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE: |
| 71 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane( | 96 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane( |
| 72 device_name, v4l2_task_runner, power_line_frequency)); | 97 device_name, v4l2_task_runner, |
| 98 power_line_frequency, |
| 99 allow_using_dma_bufs && |
| 100 IsDmaBufCaptureSupported(device_name.id(), |
| 101 V4L2_BUF_TYPE_VIDEO_CAPTURE))); |
| 73 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE: | 102 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE: |
| 74 #if !defined(OS_OPENBSD) | 103 #if !defined(OS_OPENBSD) |
| 75 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane( | 104 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane( |
| 76 device_name, v4l2_task_runner, power_line_frequency)); | 105 device_name, v4l2_task_runner, power_line_frequency)); |
| 77 default: | 106 default: |
| 78 #endif | 107 #endif |
| 79 NOTIMPLEMENTED() << "Unknown V4L2 capture API type"; | 108 NOTIMPLEMENTED() << "Unknown V4L2 capture API type"; |
| 80 return scoped_refptr<V4L2CaptureDelegate>(); | 109 return scoped_refptr<V4L2CaptureDelegate>(); |
| 81 } | 110 } |
| 82 } | 111 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 for (const auto& plane : planes_) { | 160 for (const auto& plane : planes_) { |
| 132 if (plane.start == nullptr) | 161 if (plane.start == nullptr) |
| 133 continue; | 162 continue; |
| 134 const int result = munmap(plane.start, plane.length); | 163 const int result = munmap(plane.start, plane.length); |
| 135 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; | 164 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; |
| 136 } | 165 } |
| 137 } | 166 } |
| 138 | 167 |
| 139 void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start, | 168 void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start, |
| 140 size_t length) { | 169 size_t length) { |
| 141 Plane plane; | 170 Plane plane = {}; |
| 142 plane.start = start; | 171 plane.start = start; |
| 143 plane.length = length; | 172 plane.length = length; |
| 144 plane.payload_size = 0; | 173 plane.payload_size = 0; |
| 174 plane.fd = -1; |
| 175 planes_.push_back(plane); |
| 176 } |
| 177 |
| 178 void V4L2CaptureDelegate::BufferTracker::AddNonMmapedPlane(int fd) { |
| 179 Plane plane = {}; |
| 180 plane.fd = fd; |
| 145 planes_.push_back(plane); | 181 planes_.push_back(plane); |
| 146 } | 182 } |
| 147 | 183 |
| 148 V4L2CaptureDelegate::V4L2CaptureDelegate( | 184 V4L2CaptureDelegate::V4L2CaptureDelegate( |
| 149 const VideoCaptureDevice::Name& device_name, | 185 const VideoCaptureDevice::Name& device_name, |
| 150 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, | 186 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, |
| 187 v4l2_memory memory_type, |
| 151 int power_line_frequency) | 188 int power_line_frequency) |
| 152 : capture_type_((device_name.capture_api_type() == | 189 : capture_type_((device_name.capture_api_type() == |
| 153 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE) | 190 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE) |
| 154 ? V4L2_BUF_TYPE_VIDEO_CAPTURE | 191 ? V4L2_BUF_TYPE_VIDEO_CAPTURE |
| 155 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), | 192 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), |
| 156 v4l2_task_runner_(v4l2_task_runner), | 193 v4l2_task_runner_(v4l2_task_runner), |
| 157 device_name_(device_name), | 194 device_name_(device_name), |
| 158 power_line_frequency_(power_line_frequency), | 195 power_line_frequency_(power_line_frequency), |
| 196 memory_type_(memory_type), |
| 159 is_capturing_(false), | 197 is_capturing_(false), |
| 160 timeout_count_(0), | 198 timeout_count_(0), |
| 161 rotation_(0) { | 199 rotation_(0) { |
| 162 } | 200 } |
| 163 | 201 |
| 164 V4L2CaptureDelegate::~V4L2CaptureDelegate() { | 202 V4L2CaptureDelegate::~V4L2CaptureDelegate() { |
| 165 } | 203 } |
| 166 | 204 |
| 167 void V4L2CaptureDelegate::AllocateAndStart( | 205 void V4L2CaptureDelegate::AllocateAndStart( |
| 168 int width, | 206 int width, |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 DVLOG(1) << "Error setting power line frequency removal"; | 306 DVLOG(1) << "Error setting power line frequency removal"; |
| 269 } | 307 } |
| 270 | 308 |
| 271 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, | 309 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, |
| 272 video_fmt_.fmt.pix.height); | 310 video_fmt_.fmt.pix.height); |
| 273 capture_format_.frame_rate = frame_rate; | 311 capture_format_.frame_rate = frame_rate; |
| 274 capture_format_.pixel_format = pixel_format; | 312 capture_format_.pixel_format = pixel_format; |
| 275 | 313 |
| 276 v4l2_requestbuffers r_buffer = {}; | 314 v4l2_requestbuffers r_buffer = {}; |
| 277 r_buffer.type = capture_type_; | 315 r_buffer.type = capture_type_; |
| 278 r_buffer.memory = V4L2_MEMORY_MMAP; | |
| 279 r_buffer.count = kNumVideoBuffers; | 316 r_buffer.count = kNumVideoBuffers; |
| 317 r_buffer.memory = memory_type(); |
| 280 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { | 318 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| 281 SetErrorState("Error requesting MMAP buffers from V4L2"); | 319 SetErrorState("Error requesting buffers from V4L2"); |
| 282 return; | 320 return; |
| 283 } | 321 } |
| 284 for (unsigned int i = 0; i < r_buffer.count; ++i) { | 322 for (unsigned int i = 0; i < r_buffer.count; ++i) { |
| 285 if (!MapAndQueueBuffer(i)) { | 323 if (!MapAndQueueBuffer(i)) { |
| 286 SetErrorState("Allocate buffer failed"); | 324 SetErrorState("Allocate buffer failed"); |
| 287 return; | 325 return; |
| 288 } | 326 } |
| 289 } | 327 } |
| 290 | 328 |
| 291 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) | 329 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_)) | 345 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) |
| 308 < 0) { | 346 < 0) { |
| 309 SetErrorState("VIDIOC_STREAMOFF failed"); | 347 SetErrorState("VIDIOC_STREAMOFF failed"); |
| 310 return; | 348 return; |
| 311 } | 349 } |
| 312 | 350 |
| 313 buffer_tracker_pool_.clear(); | 351 buffer_tracker_pool_.clear(); |
| 314 | 352 |
| 315 v4l2_requestbuffers r_buffer = {}; | 353 v4l2_requestbuffers r_buffer = {}; |
| 316 r_buffer.type = capture_type_; | 354 r_buffer.type = capture_type_; |
| 317 r_buffer.memory = V4L2_MEMORY_MMAP; | |
| 318 r_buffer.count = 0; | 355 r_buffer.count = 0; |
| 356 r_buffer.memory = memory_type(); |
| 319 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 357 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
| 320 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0"); | 358 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0"); |
| 321 | 359 |
| 322 // At this point we can close the device. | 360 // At this point we can close the device. |
| 323 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 361 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. |
| 324 device_fd_.reset(); | 362 device_fd_.reset(); |
| 325 is_capturing_ = false; | 363 is_capturing_ = false; |
| 326 client_.reset(); | 364 client_.reset(); |
| 327 } | 365 } |
| 328 | 366 |
| 329 void V4L2CaptureDelegate::SetRotation(int rotation) { | 367 void V4L2CaptureDelegate::SetRotation(int rotation) { |
| 330 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 368 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 331 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0); | 369 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0); |
| 332 rotation_ = rotation; | 370 rotation_ = rotation; |
| 333 } | 371 } |
| 334 | 372 |
| 335 bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { | 373 bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { |
| 336 v4l2_buffer buffer; | 374 v4l2_buffer buffer; |
| 337 FillV4L2Buffer(&buffer, index); | 375 FillV4L2Buffer(&buffer, index, true /* for_enqueue */); |
| 338 | 376 |
| 339 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) { | 377 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) { |
| 340 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer"; | 378 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer"; |
| 341 return false; | 379 return false; |
| 342 } | 380 } |
| 343 | 381 |
| 344 const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker(); | 382 const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker(); |
| 345 if (!buffer_tracker->Init(device_fd_.get(), buffer)) { | 383 if (!buffer_tracker->Init(device_fd_.get(), buffer)) { |
| 346 DLOG(ERROR) << "Error creating BufferTracker"; | 384 DLOG(ERROR) << "Error creating BufferTracker"; |
| 347 return false; | 385 return false; |
| 348 } | 386 } |
| 349 buffer_tracker_pool_.push_back(buffer_tracker); | 387 buffer_tracker_pool_.push_back(buffer_tracker); |
| 350 | 388 |
| 351 // Enqueue the buffer in the drivers incoming queue. | 389 // Enqueue the buffer in the drivers incoming queue. |
| 352 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { | 390 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 353 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver"; | 391 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver"; |
| 354 return false; | 392 return false; |
| 355 } | 393 } |
| 356 return true; | 394 return true; |
| 357 } | 395 } |
| 358 | 396 |
| 359 void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer, | 397 void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer, |
| 360 int i) const { | 398 int i, |
| 399 bool for_enqueue) const { |
| 361 memset(buffer, 0, sizeof(*buffer)); | 400 memset(buffer, 0, sizeof(*buffer)); |
| 362 buffer->memory = V4L2_MEMORY_MMAP; | |
| 363 buffer->index = i; | 401 buffer->index = i; |
| 364 FinishFillingV4L2Buffer(buffer); | 402 buffer->memory = memory_type(); |
| 403 FinishFillingV4L2Buffer(buffer, for_enqueue); |
| 365 } | 404 } |
| 366 | 405 |
| 367 void V4L2CaptureDelegate::DoCapture() { | 406 void V4L2CaptureDelegate::DoCapture() { |
| 368 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 407 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 369 if (!is_capturing_) | 408 if (!is_capturing_) |
| 370 return; | 409 return; |
| 371 | 410 |
| 372 pollfd device_pfd = {}; | 411 pollfd device_pfd = {}; |
| 373 device_pfd.fd = device_fd_.get(); | 412 device_pfd.fd = device_fd_.get(); |
| 374 device_pfd.events = POLLIN; | 413 device_pfd.events = POLLIN; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 386 timeout_count_ = 0; | 425 timeout_count_ = 0; |
| 387 return; | 426 return; |
| 388 } | 427 } |
| 389 } else { | 428 } else { |
| 390 timeout_count_ = 0; | 429 timeout_count_ = 0; |
| 391 } | 430 } |
| 392 | 431 |
| 393 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. | 432 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. |
| 394 if (device_pfd.revents & POLLIN) { | 433 if (device_pfd.revents & POLLIN) { |
| 395 v4l2_buffer buffer; | 434 v4l2_buffer buffer; |
| 396 FillV4L2Buffer(&buffer, 0); | 435 FillV4L2Buffer(&buffer, 0, false /* for_enqueue */); |
| 397 | 436 |
| 398 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { | 437 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { |
| 399 SetErrorState("Failed to dequeue capture buffer"); | 438 SetErrorState("Failed to dequeue capture buffer"); |
| 400 return; | 439 return; |
| 401 } | 440 } |
| 402 | 441 |
| 403 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); | 442 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); |
| 404 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); | 443 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); |
| 405 | 444 |
| 406 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { | 445 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 407 SetErrorState("Failed to enqueue capture buffer"); | 446 SetErrorState("Failed to enqueue capture buffer"); |
| 408 return; | 447 return; |
| 409 } | 448 } |
| 410 } | 449 } |
| 411 | 450 |
| 412 v4l2_task_runner_->PostTask( | 451 v4l2_task_runner_->PostTask( |
| 413 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); | 452 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
| 414 } | 453 } |
| 415 | 454 |
| 416 void V4L2CaptureDelegate::SetErrorState(const std::string& reason) { | 455 void V4L2CaptureDelegate::SetErrorState(const std::string& reason) { |
| 417 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 456 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 418 is_capturing_ = false; | 457 is_capturing_ = false; |
| 419 client_->OnError(reason); | 458 client_->OnError(reason); |
| 420 } | 459 } |
| 421 | 460 |
| 422 } // namespace media | 461 } // namespace media |
| OLD | NEW |