Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: media/video/capture/linux/v4l2_capture_delegate.cc

Issue 1124723006: VideoCaptureDeviceLinux: Add support for SPLANE+DMABUF V4L2 type capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698