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

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

Issue 967793002: Linux Video Capture: Add V4L2VideoCaptureDelegate{Single,Multi}Plane. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: magjed@ comments Created 5 years, 9 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/video/capture/linux/v4l2_video_capture_delegate.h"
6
7 #include <poll.h>
8 #include <sys/fcntl.h>
9 #include <sys/ioctl.h>
10 #include <sys/mman.h>
11
12 #include "base/bind.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/stringprintf.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "media/video/capture/linux/video_capture_device_linux.h"
18
19 namespace media {
20
21 // Max number of video buffers VideoCaptureDeviceLinux can allocate.
22 const uint32_t kMaxVideoBuffers = 2;
23 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw.
24 const int kCaptureTimeoutMs = 200;
25 // The number of continuous timeouts tolerated before treated as error.
26 const int kContinuousTimeoutLimit = 10;
27 // MJPEG is preferred if the requested width or height is larger than this.
28 const int kMjpegWidth = 640;
29 const int kMjpegHeight = 480;
30 // Typical framerate, in fps
31 const int kTypicalFramerate = 30;
32
33 // V4L2 color formats supported by V4L2CaptureDelegateSinglePlane. This list is
34 // ordered by precedence of use.
35 static const uint32_t kSinglePlaneSupportedFormats[] = {
36 V4L2_PIX_FMT_YUV420,
37 V4L2_PIX_FMT_YUYV,
38 V4L2_PIX_FMT_UYVY};
39
40 // List of supported formats and their respective amount of sub-buffers for
41 // V4L2CaptureDelegateMultiPlane.
42 static const struct {
43 uint32_t fourcc;
44 size_t num_planes;
45 } kMultiPlaneSupportedFormats[] = {
46 {V4L2_PIX_FMT_YUV420M, 3}
47 // TODO(mcasas): add V4L2_PIX_FMT_YVU420M when available in bots.
48 };
49
50 // Returns the input fourcc as a std::string four char representation.
51 static std::string FourccToString(uint32_t fourcc) {
52 return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF,
53 (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF);
54 }
55
56 static std::list<uint32_t> GetListOfUsableFourCcsSinglePlane() {
57 return std::list<uint32_t>(
58 kSinglePlaneSupportedFormats,
59 kSinglePlaneSupportedFormats + arraysize(kSinglePlaneSupportedFormats));
60 }
61
62 static size_t GetNumPlanesForFourCc(uint32_t fourcc) {
63 for (const auto& fourcc_and_pixel_format : kMultiPlaneSupportedFormats) {
64 if (fourcc_and_pixel_format.fourcc == fourcc)
65 return fourcc_and_pixel_format.num_planes;
66 }
67 NOTREACHED() << "Unknown fourcc " << FourccToString(fourcc);
68 return 0;
69 }
70
71 static std::list<uint32_t> GetListOfUsableFourCcsMultiPlane() {
72 std::list<uint32_t> supported_formats;
73 for (const auto& i : kMultiPlaneSupportedFormats)
74 supported_formats.push_back(i.fourcc);
75 return supported_formats;
76 }
77
78 // Class keeping track of SPLANE/MPLANE V4L2 buffers, mmap()ed on construction
79 // and munmap()ed on destruction. Destruction is syntactically equal for
80 // S/MPLANE but not construction, so this is implemented in derived classes.
81 // Internally it has a vector of planes, which for SPLANE will contain only
82 // one element.
83 class V4L2VideoCaptureDelegate::BufferTracker
84 : public base::RefCounted<BufferTracker> {
85 public:
86 struct Plane {
Pawel Osciak 2015/03/13 09:52:52 This doesn't need to be public.
mcasas 2015/03/14 03:36:11 Done, see below.
87 void* start;
88 size_t length;
89 };
90 virtual bool Init(int fd, const v4l2_buffer& buffer) = 0;
Pawel Osciak 2015/03/13 09:52:53 Documentation please.
mcasas 2015/03/14 03:36:11 Done.
91
92 uint8_t* const GetPlaneStart(size_t plane) const {
93 return static_cast<uint8_t* const>(planes_[plane].start);
94 }
95 size_t GetPlaneLength(size_t plane) const { return planes_[plane].length; }
96
97 std::vector<Plane>& planes() { return planes_; }
Pawel Osciak 2015/03/13 09:52:52 As mentioned before, this shouldn't be public non-
mcasas 2015/03/14 03:36:11 (Cont'd from elsewhere) Since the only non-const o
98
99 protected:
100 friend class base::RefCounted<BufferTracker>;
101 virtual ~BufferTracker();
102
103 private:
104 std::vector<Plane> planes_;
105 };
106
107 // V4L2 specifics for SPLANE API.
108 class V4L2CaptureDelegateSinglePlane final : public V4L2VideoCaptureDelegate {
109 public:
110 V4L2CaptureDelegateSinglePlane(
111 const VideoCaptureDevice::Name& device_name,
112 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
113 int power_line_frequency)
114 : V4L2VideoCaptureDelegate(device_name,
115 v4l2_task_runner,
116 power_line_frequency) {}
117
118 private:
119 // BufferTracker derivation to implement construction semantics for SPLANE.
120 class BufferTrackerSPlane final : public BufferTracker {
121 public:
122 bool Init(int fd, const v4l2_buffer& buffer) override;
123
124 private:
125 ~BufferTrackerSPlane() override {};
Pawel Osciak 2015/03/13 09:52:52 s/;//
mcasas 2015/03/14 03:36:12 Done.
126 };
127
128 ~V4L2CaptureDelegateSinglePlane() override {};
Pawel Osciak 2015/03/13 09:52:53 s/;//
mcasas 2015/03/14 03:36:11 Done.
129
130 // V4L2VideoCaptureDelegate virtual methods implementation.
131 scoped_refptr<BufferTracker> CreateBufferTracker() override;
132 bool FillV4L2Format(v4l2_format* format,
133 uint32_t width,
134 uint32_t height,
135 uint32_t pixelformat_fourcc) override;
136 void FinishFillingV4L2Buffer(v4l2_buffer* buffer) const override;
137 void SendBuffer(const scoped_refptr<BufferTracker>& buffer) override;
138 };
139
140 // V4L2 specifics for MPLANE API.
141 class V4L2CaptureDelegateMultiPlane final : public V4L2VideoCaptureDelegate {
142 public:
143 V4L2CaptureDelegateMultiPlane(
144 const VideoCaptureDevice::Name& device_name,
145 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
146 int power_line_frequency)
147 : V4L2VideoCaptureDelegate(device_name,
148 v4l2_task_runner,
149 power_line_frequency),
150 fourcc_(0),
151 num_planes_(0) {}
152
153 private:
154 // BufferTracker derivation to implement construction semantics for MPLANE.
155 class BufferTrackerMPlane final : public BufferTracker {
156 public:
157 bool Init(int fd, const v4l2_buffer& buffer) override;
158
159 private:
160 ~BufferTrackerMPlane() override {};
Pawel Osciak 2015/03/13 09:52:53 s/;//
mcasas 2015/03/14 03:36:11 Done.
161 };
162
163 ~V4L2CaptureDelegateMultiPlane() override {};
Pawel Osciak 2015/03/13 09:52:53 s/;//
mcasas 2015/03/14 03:36:11 Done.
164
165 // V4L2VideoCaptureDelegate virtual methods implementation.
166 scoped_refptr<BufferTracker> CreateBufferTracker() override;
167 bool FillV4L2Format(v4l2_format* format,
168 uint32_t width,
169 uint32_t height,
170 uint32_t pixelformat_fourcc) override;
171 void FinishFillingV4L2Buffer(v4l2_buffer* buffer) const override;
172 void SendBuffer(const scoped_refptr<BufferTracker>& buffer) override;
173
174 // Actual pixel format and number of planes, known after FillV4L2Format().
175 uint32_t fourcc_;
Pawel Osciak 2015/03/13 09:52:52 We don't seem to be using it anywhere but in FillV
mcasas 2015/03/14 03:36:12 Well spotted, removed.
176 size_t num_planes_;
Pawel Osciak 2015/03/13 09:52:52 Better use v4l2_plane_.size() and remove this, als
mcasas 2015/03/14 03:36:11 I'm answering this in l.607 comments.
177
178 // Scoped_ptr to allocate and track as many v4l2_plane structs as planes,
179 // needed inside v4l2_buffer.
180 scoped_ptr<struct v4l2_plane[]> v4l2_plane_;
181 };
182
183 // static
184 scoped_refptr<V4L2VideoCaptureDelegate>
185 V4L2VideoCaptureDelegate::CreateV4L2VideoCaptureDelegate(
186 const VideoCaptureDevice::Name& device_name,
187 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
188 int power_line_frequency) {
189 switch (device_name.capture_api_type()) {
190 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE:
191 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane(
192 device_name, v4l2_task_runner, power_line_frequency));
193 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE:
194 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane(
195 device_name, v4l2_task_runner, power_line_frequency));
196 default:
197 NOTIMPLEMENTED() << "Unknown V4L2 capture API type";
198 return scoped_refptr<V4L2VideoCaptureDelegate>();
199 }
200 }
201
202 // static
203 VideoPixelFormat V4L2VideoCaptureDelegate::V4l2FourCcToChromiumPixelFormat(
204 uint32_t v4l2_fourcc) {
205 const struct {
206 uint32_t fourcc;
207 VideoPixelFormat pixel_format;
208 } kFourCcAndChromiumPixelFormats[] = {
209 {V4L2_PIX_FMT_YUV420M, PIXEL_FORMAT_I420},
Pawel Osciak 2015/03/13 09:52:52 Could we please have one array instead of three (k
mcasas 2015/03/14 03:36:11 I think it mixes things that pertain to different
210 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420},
211 {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2},
212 {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY},
213 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG},
214 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG},
215 };
216 for (const auto& fourcc_and_pixel_format : kFourCcAndChromiumPixelFormats) {
217 if (fourcc_and_pixel_format.fourcc == v4l2_fourcc)
218 return fourcc_and_pixel_format.pixel_format;
219 }
220 DVLOG(1) << "Unsupported pixel format: " << FourccToString(v4l2_fourcc);
221 return PIXEL_FORMAT_UNKNOWN;
222 }
223
224 // static
225 std::list<uint32_t> V4L2VideoCaptureDelegate::GetListOfUsableFourCcs(
226 bool prefer_mjpeg) {
227 std::list<uint32_t> singleplane_formats = GetListOfUsableFourCcsSinglePlane();
228 std::list<uint32_t> multiplane_formats = GetListOfUsableFourCcsMultiPlane();
229 multiplane_formats.insert(multiplane_formats.end(),
230 singleplane_formats.begin(),
231 singleplane_formats.end());
232 // Add MJPEG to the front or the back of the list depending on |prefer_mjpeg|.
233 if (prefer_mjpeg)
234 multiplane_formats.insert(multiplane_formats.begin(), V4L2_PIX_FMT_MJPEG);
235 else
236 multiplane_formats.insert(multiplane_formats.end(), V4L2_PIX_FMT_MJPEG);
237
238 // JPEG works as MJPEG on some gspca webcams from field reports.
239 // Put it as the least preferred format.
240 multiplane_formats.push_back(V4L2_PIX_FMT_JPEG);
241
242 return multiplane_formats;
243 }
244
245 V4L2VideoCaptureDelegate::BufferTracker::~BufferTracker() {
246 for (const auto& plane : planes_) {
247 if (plane.start == NULL)
Pawel Osciak 2015/03/13 09:52:53 s/NULL/nullptr/
mcasas 2015/03/14 03:36:11 Done.
248 continue;
249 const int result = munmap(plane.start, plane.length);
250 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer";
251 }
252 }
253
254 V4L2VideoCaptureDelegate::V4L2VideoCaptureDelegate(
255 const VideoCaptureDevice::Name& device_name,
256 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
257 int power_line_frequency)
258 : capture_type_((device_name.capture_api_type() ==
259 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE)
260 ? V4L2_BUF_TYPE_VIDEO_CAPTURE
261 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),
262 v4l2_task_runner_(v4l2_task_runner),
263 device_name_(device_name),
264 power_line_frequency_(power_line_frequency),
265 is_capturing_(false),
266 timeout_count_(0),
267 rotation_(0) {
268 }
269
270 void V4L2VideoCaptureDelegate::AllocateAndStart(
271 int width,
272 int height,
273 float frame_rate,
274 scoped_ptr<VideoCaptureDevice::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_CAPTURE_MPLANE) &&
290 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
291 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) {
292 device_fd_.reset();
293 SetErrorState("This is not a V4L2 video capture device");
294 return;
295 }
296
297 // Get supported video formats in preferred order.
298 // For large resolutions, favour mjpeg over raw formats.
299 const std::list<uint32_t>& desired_v4l2_formats =
300 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight);
301 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end();
302
303 v4l2_fmtdesc fmtdesc = {};
304 fmtdesc.type = capture_type_;
305 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
306 ++fmtdesc.index) {
307 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat);
308 }
309 if (best == desired_v4l2_formats.end()) {
310 SetErrorState("Failed to find a supported camera format.");
311 return;
312 }
313
314 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best);
315
316 v4l2_format video_fmt = {};
317 video_fmt.type = capture_type_;
318 if (!FillV4L2Format(&video_fmt, width, height, *best)) {
319 SetErrorState("Failed filling in V4L2 Format");
320 return;
321 }
322
323 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) {
324 SetErrorState("Failed to set video capture format");
325 return;
326 }
327 const VideoPixelFormat pixel_format =
328 V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat);
329 if (pixel_format == PIXEL_FORMAT_UNKNOWN) {
330 SetErrorState("Unsupported pixel format");
331 return;
332 }
333
334 // Set capture framerate in the form of capture interval.
335 v4l2_streamparm streamparm = {};
336 streamparm.type = capture_type_;
337 // The following line checks that the driver knows about framerate get/set.
338 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
339 // Now check if the device is able to accept a capture framerate set.
340 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
341 // |frame_rate| is float, approximate by a fraction.
342 streamparm.parm.capture.timeperframe.numerator =
343 media::kFrameRatePrecision;
344 streamparm.parm.capture.timeperframe.denominator =
345 (frame_rate) ? (frame_rate * media::kFrameRatePrecision)
346 : (kTypicalFramerate * media::kFrameRatePrecision);
347
348 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) <
349 0) {
350 SetErrorState("Failed to set camera framerate");
351 return;
352 }
353 DVLOG(2) << "Actual camera driverframerate: "
354 << streamparm.parm.capture.timeperframe.denominator << "/"
355 << streamparm.parm.capture.timeperframe.numerator;
356 }
357 }
358 // TODO(mcasas): what should be done if the camera driver does not allow
359 // framerate configuration, or the actual one is different from the desired?
360
361 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
362 // operation (|errno| == EINVAL in this case) or plain failure.
363 if ((power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ||
364 (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)) {
Pawel Osciak 2015/03/13 09:52:53 What if it's V4L2_CID_POWER_LINE_FREQUENCY_AUTO? W
mcasas 2015/03/14 03:36:11 Done.
365 struct v4l2_control control = {};
366 control.id = V4L2_CID_POWER_LINE_FREQUENCY;
367 control.value = power_line_frequency_;
368 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
Pawel Osciak 2015/03/13 09:52:53 Better DVLOG on failure at least, even if not fata
mcasas 2015/03/14 03:36:11 Done.
369 }
370
371 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
372 video_fmt.fmt.pix.height);
373 capture_format_.frame_rate = frame_rate;
374 capture_format_.pixel_format = pixel_format;
375
376 v4l2_requestbuffers r_buffer = {};
377 r_buffer.type = capture_type_;
378 r_buffer.memory = V4L2_MEMORY_MMAP;
379 r_buffer.count = kMaxVideoBuffers;
380 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
381 SetErrorState("Error requesting MMAP buffers from V4L2");
382 return;
383 }
384 DCHECK_EQ(r_buffer.count, kMaxVideoBuffers);
385 for (unsigned int i = 0; i < r_buffer.count; ++i) {
386 if (!AllocateVideoBuffer(i)) {
387 SetErrorState("Allocate buffer failed");
388 return;
389 }
390 }
391
392 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_))
393 < 0) {
394 SetErrorState("VIDIOC_STREAMON failed");
395 return;
396 }
397
398 is_capturing_ = true;
399 // Post task to start fetching frames from v4l2.
400 v4l2_task_runner_->PostTask(
401 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::DoCapture, this));
402 }
403
404 void V4L2VideoCaptureDelegate::StopAndDeAllocate() {
405 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
406 // The order is important: stop streaming, clear |buffer_pool_|,
407 // thus munmap()ing the v4l2_buffers, and then return them to the OS.
408 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_))
409 < 0) {
410 SetErrorState("VIDIOC_STREAMOFF failed");
411 return;
412 }
413
414 buffer_tracker_pool_.clear();
415
416 v4l2_requestbuffers r_buffer = {};
417 r_buffer.type = capture_type_;
418 r_buffer.memory = V4L2_MEMORY_MMAP;
419 r_buffer.count = 0;
420 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0)
421 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0");
422
423 // At this point we can close the device.
424 // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
425 device_fd_.reset();
426 is_capturing_ = false;
427 client_.reset();
428 }
429
430 void V4L2VideoCaptureDelegate::SetRotation(int rotation) {
431 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
432 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
433 rotation_ = rotation;
434 }
435
436 bool V4L2VideoCaptureDelegate::AllocateVideoBuffer(int index) {
Pawel Osciak 2015/03/13 09:52:52 This is not allocating, but mapping and queuing on
mcasas 2015/03/14 03:36:11 Done.
437 v4l2_buffer buffer = {};
Pawel Osciak 2015/03/13 09:52:53 s/ = {};/;/ Fill...() already memsets it.
mcasas 2015/03/14 03:36:11 Done.
438 FillV4L2Buffer(&buffer, index);
439
440 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
441 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer";
442 return false;
443 }
444
445 const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker();
446 if (!buffer_tracker->Init(device_fd_.get(), buffer)) {
447 DLOG(ERROR) << "Error creating BufferTracker";
448 return false;
449 }
450 buffer_tracker_pool_.push_back(buffer_tracker);
451
452 // Enqueue the buffer in the drivers incoming queue.
453 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
454 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver";
455 return false;
456 }
457 return true;
458 }
459
460 void V4L2VideoCaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer,
461 int i) const {
462 memset(buffer, 0, sizeof(*buffer));
463 buffer->memory = V4L2_MEMORY_MMAP;
464 buffer->index = i;
465 FinishFillingV4L2Buffer(buffer);
466 }
467
468 void V4L2VideoCaptureDelegate::DoCapture() {
469 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
470 if (!is_capturing_)
471 return;
472
473 pollfd device_pfd = {};
474 device_pfd.fd = device_fd_.get();
475 device_pfd.events = POLLIN;
476 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs));
477 if (result < 0) {
478 SetErrorState("Poll failed");
479 return;
480 }
481 // Check if poll() timed out; track the amount of times it did in a row and
482 // throw an error if it times out too many times.
483 if (result == 0) {
484 timeout_count_++;
485 if (timeout_count_ >= kContinuousTimeoutLimit) {
486 SetErrorState("Multiple continuous timeouts while read-polling.");
487 timeout_count_ = 0;
488 return;
489 }
490 } else {
491 timeout_count_ = 0;
492 }
493
494 // Deenqueue, send and reenqueue a buffer if the driver has filled one in.
495 if (device_pfd.revents & POLLIN) {
496 v4l2_buffer buffer = {};
Pawel Osciak 2015/03/13 09:52:53 You could replace lines 496-500 with FillV4L2Buffe
mcasas 2015/03/14 03:36:11 Done.
497 buffer.type = capture_type_;
498 buffer.memory = V4L2_MEMORY_MMAP;
499 buffer.index = 0;
500 FinishFillingV4L2Buffer(&buffer);
501
502 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) {
503 SetErrorState("Failed to dequeue capture buffer");
504 return;
505 }
506
507 SendBuffer(buffer_tracker_pool_[buffer.index]);
508
509 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
510 SetErrorState("Failed to enqueue capture buffer");
511 return;
512 }
513 }
514
515 v4l2_task_runner_->PostTask(
516 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::DoCapture, this));
517 }
518
519 void V4L2VideoCaptureDelegate::SetErrorState(const std::string& reason) {
520 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
521 is_capturing_ = false;
522 client_->OnError(reason);
523 }
524
525 V4L2VideoCaptureDelegate::~V4L2VideoCaptureDelegate() {
526 }
527
528 scoped_refptr<V4L2VideoCaptureDelegate::BufferTracker>
529 V4L2CaptureDelegateSinglePlane::CreateBufferTracker() {
530 return make_scoped_refptr(new BufferTrackerSPlane());
531 }
532
533 bool V4L2CaptureDelegateSinglePlane::FillV4L2Format(
534 v4l2_format* format,
535 uint32_t width,
536 uint32_t height,
537 uint32_t pixelformat_fourcc) {
538 format->fmt.pix.width = width;
539 format->fmt.pix.height = height;
540 format->fmt.pix.pixelformat = pixelformat_fourcc;
541 return true;
542 }
543
544 void V4L2CaptureDelegateSinglePlane::FinishFillingV4L2Buffer(
545 v4l2_buffer* buffer) const {
546 buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
547 }
548
549 void V4L2CaptureDelegateSinglePlane::SendBuffer(
550 const scoped_refptr<BufferTracker>& buffer) {
551 BufferTrackerSPlane* const buffer_tracker =
552 reinterpret_cast<BufferTrackerSPlane*>(buffer.get());
Pawel Osciak 2015/03/13 09:52:53 Downcasting shouldn't be needed. GetPlaneStart/Len
mcasas 2015/03/14 03:36:11 Done.
553 client()->OnIncomingCapturedData(
554 buffer_tracker->GetPlaneStart(0),
555 buffer_tracker->GetPlaneLength(0),
556 capture_format(),
557 rotation(),
558 base::TimeTicks::Now());
559 }
560
561 bool V4L2CaptureDelegateSinglePlane::BufferTrackerSPlane::Init(
562 int fd,
563 const v4l2_buffer& buffer) {
564 Plane plane;
565 // Some devices require mmap() to be called with both READ and WRITE.
566 // See http://crbug.com/178582.
567 plane.start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
568 fd, buffer.m.offset);
569 if (plane.start == MAP_FAILED) {
570 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace";
Pawel Osciak 2015/03/13 09:52:52 plane.start = nullptr;
mcasas 2015/03/14 03:36:12 Actually unneeded given the change in BufferTrack
571 return false;
572 }
573 plane.length = buffer.length;
574 planes().push_back(plane);
575 return true;
576 }
577
578 scoped_refptr<V4L2VideoCaptureDelegate::BufferTracker>
579 V4L2CaptureDelegateMultiPlane::CreateBufferTracker() {
580 return make_scoped_refptr(new BufferTrackerMPlane());
581 }
582
583 bool V4L2CaptureDelegateMultiPlane::FillV4L2Format(
584 v4l2_format* format,
585 uint32_t width,
586 uint32_t height,
587 uint32_t pixelformat_fourcc) {
588 format->fmt.pix_mp.width = width;
589 format->fmt.pix_mp.height = height;
590
591 fourcc_ = pixelformat_fourcc;
592 format->fmt.pix_mp.pixelformat = fourcc_;
593
594 num_planes_ = GetNumPlanesForFourCc(fourcc_);
595 if (num_planes_ == 0u)
596 return false;
597 DCHECK_LE(num_planes_, static_cast<unsigned long>(VIDEO_MAX_PLANES));
598 format->fmt.pix_mp.num_planes = num_planes_;
599
600 v4l2_plane_.reset(new v4l2_plane[num_planes_]);
601 return true;
602 }
603
604 void V4L2CaptureDelegateMultiPlane::FinishFillingV4L2Buffer(
605 v4l2_buffer* buffer) const {
606 buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
607 buffer->length = num_planes_;
Pawel Osciak 2015/03/13 09:52:53 v4l2_plane_.size()
mcasas 2015/03/14 03:36:11 Actually it's not that simple. We need to have an
Pawel Osciak 2015/03/17 11:05:25 Oh, I forgot that you were using this as a scoped_
608 buffer->m.planes = v4l2_plane_.get();
609 }
610
611 void V4L2CaptureDelegateMultiPlane::SendBuffer(
612 const scoped_refptr<BufferTracker>& buffer) {
613 DCHECK_EQ(capture_format().pixel_format, PIXEL_FORMAT_I420);
614
615 BufferTrackerMPlane* const buffer_tracker =
616 reinterpret_cast<BufferTrackerMPlane*>(buffer.get());
Pawel Osciak 2015/03/13 09:52:53 Please remove downcast.
mcasas 2015/03/14 03:36:12 Done.
617 client()->OnIncomingCapturedYuvData(buffer_tracker->GetPlaneStart(0),
Pawel Osciak 2015/03/13 09:52:52 S_FMT may adjust values for plane sizes, resolutio
mcasas 2015/03/14 03:36:11 It's totally accepted that the device might, and o
Pawel Osciak 2015/03/17 11:05:25 My bad, I misread the code, you are actually using
618 buffer_tracker->GetPlaneStart(1),
619 buffer_tracker->GetPlaneStart(2),
620 buffer_tracker->GetPlaneLength(0),
621 buffer_tracker->GetPlaneLength(1),
622 buffer_tracker->GetPlaneLength(2),
623 capture_format(),
624 rotation(),
625 base::TimeTicks::Now());
626 }
627
628 bool V4L2CaptureDelegateMultiPlane::BufferTrackerMPlane::Init(
629 int fd,
630 const v4l2_buffer& buffer) {
631 for (size_t p = 0; p < buffer.length; ++p) {
632 Plane plane;
633 plane.start = mmap(NULL, buffer.m.planes[p].length, PROT_READ | PROT_WRITE,
634 MAP_SHARED, fd, buffer.m.planes[p].m.mem_offset);
635 if (plane.start == MAP_FAILED) {
636 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace";
637 plane.start = nullptr;
638 return false;
639 }
640 plane.length = buffer.m.planes[p].length;
641 DVLOG(3) << "Mmap()ed plane #" << p << ", length " << plane.length << "B";
642 planes().push_back(plane);
643 }
644 return true;
645 }
646
647 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698