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

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: 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 // JPEG works as MJPEG on some gspca webcams from field reports.
40 V4L2_PIX_FMT_JPEG};
41
42 // List of supported formats and their respective amount of sub-buffers for
43 // V4L2CaptureDelegateMultiPlane.
44 static const struct {
45 uint32_t fourcc;
46 size_t num_planes;
47 } kMultiPlaneSupportedFormats[] = {
48 {V4L2_PIX_FMT_YUV420M, 3}
49 // TODO(mcasas): add V4L2_PIX_FMT_YVU420M when available in bots.
50 };
51
52 // Returns the input fourcc as a std::string four char representation.
53 static std::string FourccToString(uint32_t fourcc) {
54 return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF,
55 (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF);
56 }
57
58 static std::list<uint32_t> GetListOfUsableFourCcsSinglePlane() {
59 return std::list<uint32_t>(
60 kSinglePlaneSupportedFormats,
61 kSinglePlaneSupportedFormats + arraysize(kSinglePlaneSupportedFormats));
62 }
63
64 static size_t GetNumPlanesForFourCc(uint32_t fourcc) {
65 for (const auto& fourcc_and_pixel_format : kMultiPlaneSupportedFormats) {
66 if (fourcc_and_pixel_format.fourcc == fourcc)
67 return fourcc_and_pixel_format.num_planes;
68 }
69 NOTREACHED() << "Unknown fourcc " << FourccToString(fourcc);
70 return 0;
71 }
72
73 static std::list<uint32_t> GetListOfUsableFourCcsMultiPlane() {
74 std::list<uint32_t> supported_formats;
75 for (const auto& i : kMultiPlaneSupportedFormats)
76 supported_formats.push_back(i.fourcc);
77 return supported_formats;
78 }
79
80 // V4L2 specifics for SPLANE API.
81 class V4L2CaptureDelegateSinglePlane final : public V4L2VideoCaptureDelegate {
82 public:
83 V4L2CaptureDelegateSinglePlane(
84 const VideoCaptureDevice::Name& device_name,
85 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
86 int power_line_frequency)
87 : V4L2VideoCaptureDelegate(device_name,
88 v4l2_task_runner,
89 power_line_frequency) {}
90
91 private:
92 // BufferTracker derivation to implement construction semantics for SPLANE.
93 class BufferTrackerSPlane final : public BufferTracker {
94 public:
95 BufferTrackerSPlane(int fd, const v4l2_buffer& buffer);
Pawel Osciak 2015/03/06 10:43:54 We need Initialize() methods as I mention elsewher
mcasas 2015/03/09 21:23:56 Done.
96
97 private:
98 ~BufferTrackerSPlane() override {};
99 };
100
101 ~V4L2CaptureDelegateSinglePlane() override {};
102
103 // V4L2VideoCaptureDelegate virtual methods implementation.
104 scoped_refptr<BufferTracker> CreateBufferTracker(
105 int fd,
106 const v4l2_buffer& buffer) override;
107 void FillV4L2Format(v4l2_format* format,
108 uint32_t width,
109 uint32_t height,
110 uint32_t pixelformat_fourcc) override;
111 void FinishFillingV4L2Buffer(v4l2_buffer* buffer) override {}
112 void SendBuffer(const v4l2_buffer& buffer) override;
113 };
114
115 // V4L2 specifics for MPLANE API.
116 class V4L2CaptureDelegateMultiPlane final : public V4L2VideoCaptureDelegate {
117 public:
118 V4L2CaptureDelegateMultiPlane(
119 const VideoCaptureDevice::Name& device_name,
120 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
121 int power_line_frequency)
122 : V4L2VideoCaptureDelegate(device_name,
123 v4l2_task_runner,
124 power_line_frequency),
125 fourcc_(0),
126 num_planes_(0) {}
127
128 private:
129 // BufferTracker derivation to implement construction semantics for MPLANE.
130 class BufferTrackerMPlane final : public BufferTracker {
131 public:
132 BufferTrackerMPlane(int fd, const v4l2_buffer& buffer);
133
134 private:
135 ~BufferTrackerMPlane() override {};
136 };
137
138 ~V4L2CaptureDelegateMultiPlane() override {};
139
140 // V4L2VideoCaptureDelegate virtual methods implementation.
141 scoped_refptr<BufferTracker> CreateBufferTracker(
142 int fd,
143 const v4l2_buffer& buffer) override;
144 void FillV4L2Format(v4l2_format* format,
145 uint32_t width,
146 uint32_t height,
147 uint32_t pixelformat_fourcc) override;
148 void FinishFillingV4L2Buffer(v4l2_buffer* buffer) override;
149 void SendBuffer(const v4l2_buffer& buffer) override;
150
151 // Actual pixel format and number of planes, known after FillV4L2Format().
152 uint32_t fourcc_;
153 size_t num_planes_;
154
155 // Scoped_ptr to allocate and track as many v4l2_plane structs as planes,
156 // needed inside v4l2_buffer.
157 scoped_ptr<struct v4l2_plane[]> v4l2_plane_;
158 };
159
160 // static
161 scoped_refptr<V4L2VideoCaptureDelegate>
162 V4L2VideoCaptureDelegate::CreateV4L2VideoCaptureDelegate(
163 const VideoCaptureDevice::Name& device_name,
164 const VideoPixelFormat pixel_format,
165 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
166 int power_line_frequency) {
167 switch (device_name.capture_api_type()) {
168 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE:
169 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane(
170 device_name, v4l2_task_runner, power_line_frequency));
171 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE:
172 if (pixel_format != PIXEL_FORMAT_I420)
Pawel Osciak 2015/03/06 10:43:54 This may bitrot. Please instead look up pixel_form
mcasas 2015/03/09 21:23:56 I think this early-bail-out is not really needed a
Pawel Osciak 2015/03/13 09:52:52 Acknowledged.
173 return scoped_refptr<V4L2VideoCaptureDelegate>();
174 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane(
175 device_name, v4l2_task_runner, power_line_frequency));
176 default:
177 NOTIMPLEMENTED() << "Unknown V4L2 capture API type";
178 return scoped_refptr<V4L2VideoCaptureDelegate>();
179 }
180 }
181
182 // static
183 VideoPixelFormat V4L2VideoCaptureDelegate::V4l2FourCcToChromiumPixelFormat(
184 uint32_t v4l2_fourcc) {
185 const struct {
186 uint32_t fourcc;
187 VideoPixelFormat pixel_format;
188 } kFourCcAndChromiumPixelFormats[] = {
189 {V4L2_PIX_FMT_YUV420M, PIXEL_FORMAT_I420},
190 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420},
191 {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2},
192 {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY},
193 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG},
194 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG},
195 };
196 for (const auto& fourcc_and_pixel_format : kFourCcAndChromiumPixelFormats) {
197 if (fourcc_and_pixel_format.fourcc == v4l2_fourcc)
198 return fourcc_and_pixel_format.pixel_format;
199 }
200 DVLOG(1) << "Unsupported pixel format: " << FourccToString(v4l2_fourcc);
201 return PIXEL_FORMAT_UNKNOWN;
202 }
203
204 // static
205 std::list<uint32_t> V4L2VideoCaptureDelegate::GetListOfUsableFourCss(
emircan 2015/03/04 02:47:47 /s/FourCss/FourCcs/
mcasas 2015/03/09 21:23:56 Done.
206 bool prefer_mjpeg) {
207 std::list<uint32_t> singleplane_formats = GetListOfUsableFourCcsSinglePlane();
208 std::list<uint32_t> multiplane_formats = GetListOfUsableFourCcsMultiPlane();
209 multiplane_formats.insert(multiplane_formats.end(),
210 singleplane_formats.begin(),
211 singleplane_formats.end());
212 // Add MJPEG to the front or the back of the list depending on |prefer_mjpeg|.
213 multiplane_formats.insert(
Pawel Osciak 2015/03/06 10:43:54 if (prefer_mjpeg) multiplane_formats.push_front(
mcasas 2015/03/09 21:23:56 Done.
214 (prefer_mjpeg ? multiplane_formats.begin() : multiplane_formats.end()),
215 V4L2_PIX_FMT_MJPEG);
Pawel Osciak 2015/03/06 10:43:55 This is different from existing code, which always
mcasas 2015/03/09 21:23:56 Done.
216 return multiplane_formats;
217 }
218
219 V4L2VideoCaptureDelegate::BufferTracker::BufferTracker(
Pawel Osciak 2015/03/06 10:43:55 This is not needed?
mcasas 2015/03/09 21:23:56 Needed due to: [chromium-style] Complex class/str
220 int fd,
221 const v4l2_buffer& buffer) {
222 }
223
224 V4L2VideoCaptureDelegate::BufferTracker::~BufferTracker() {
225 for (const auto& plane : planes_) {
226 if (plane->start == NULL)
227 continue;
228 const int result = munmap(plane->start, plane->length);
229 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer";
230 }
231 }
232
233 V4L2VideoCaptureDelegate::V4L2VideoCaptureDelegate(
234 const VideoCaptureDevice::Name& device_name,
235 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
236 int power_line_frequency)
237 : capture_type_((device_name.capture_api_type() ==
238 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE)
239 ? V4L2_BUF_TYPE_VIDEO_CAPTURE
240 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),
241 v4l2_task_runner_(v4l2_task_runner),
242 device_name_(device_name),
243 power_line_frequency_(power_line_frequency),
244 is_capturing_(false),
245 timeout_count_(0),
246 rotation_(0) {
247 }
248
249 void V4L2VideoCaptureDelegate::AllocateAndStart(
250 int width,
251 int height,
252 float frame_rate,
253 scoped_ptr<VideoCaptureDevice::Client> client) {
254 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
255 DCHECK(client);
256 client_ = client.Pass();
257
258 // Need to open camera with O_RDWR after Linux kernel 3.3.
259 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR)));
260 if (!device_fd_.is_valid()) {
261 SetErrorState("Failed to open V4L2 device driver file.");
262 return;
263 }
264
265 v4l2_capability cap = {};
266 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
267 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ||
268 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
269 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
270 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) {
271 device_fd_.reset();
272 SetErrorState("This is not a V4L2 video capture device");
273 return;
274 }
275
276 // Get supported video formats in preferred order.
277 // For large resolutions, favour mjpeg over raw formats.
278 const std::list<uint32_t>& desired_v4l2_formats =
279 GetListOfUsableFourCss(width > kMjpegWidth || height > kMjpegHeight);
280 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end();
281
282 v4l2_fmtdesc fmtdesc = {};
283 fmtdesc.type = capture_type_;
284 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
285 ++fmtdesc.index) {
286 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat);
Pawel Osciak 2015/03/06 10:43:55 Maybe I'm missing something, but I think this will
mcasas 2015/03/09 21:23:56 According to the fact that it works and the list o
Pawel Osciak 2015/03/13 09:52:52 Acknowledged and true. Sorry.
287 }
emircan 2015/03/04 02:47:47 You can end the iteration when (best == desired_v4
mcasas 2015/03/09 21:23:56 That would be an overoptimization, wouldn't it? M
288 if (best == desired_v4l2_formats.end()) {
289 SetErrorState("Failed to find a supported camera format.");
290 return;
291 }
292
293 DVLOG(1) << "chosen pixel format is " << FourccToString(*best);
294
295 v4l2_format video_fmt = {};
296 video_fmt.type = capture_type_;
297 FillV4L2Format(&video_fmt, width, height, *best);
298 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) {
299 SetErrorState("Failed to set video capture format");
300 return;
301 }
302
303 // Set capture framerate in the form of capture interval.
304 v4l2_streamparm streamparm = {};
305 streamparm.type = capture_type_;
306 // The following line checks that the driver knows about framerate get/set.
307 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
308 // Now check if the device is able to accept a capture framerate set.
309 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
310 // |frame_rate| is float, approximate by a fraction.
311 streamparm.parm.capture.timeperframe.numerator =
312 media::kFrameRatePrecision;
313 streamparm.parm.capture.timeperframe.denominator =
314 (frame_rate) ? (frame_rate * media::kFrameRatePrecision)
315 : (kTypicalFramerate * media::kFrameRatePrecision);
316
317 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) <
318 0) {
319 SetErrorState("Failed to set camera framerate");
320 return;
321 }
322 DVLOG(2) << "Actual camera driverframerate: "
323 << streamparm.parm.capture.timeperframe.denominator << "/"
324 << streamparm.parm.capture.timeperframe.numerator;
325 }
326 }
327 // TODO(mcasas): what should be done if the camera driver does not allow
328 // framerate configuration, or the actual one is different from the desired?
329
330 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
331 // operation (|errno| == EINVAL in this case) or plain failure.
332 if ((power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ||
333 (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)) {
334 struct v4l2_control control = {};
335 control.id = V4L2_CID_POWER_LINE_FREQUENCY;
336 control.value = power_line_frequency_;
337 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
338 }
339
340 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
341 video_fmt.fmt.pix.height);
342 capture_format_.frame_rate = frame_rate;
343 capture_format_.pixel_format =
344 V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat);
Pawel Osciak 2015/03/06 10:43:55 This needs to be checked for success, we don't wan
mcasas 2015/03/09 21:23:56 Done, but moved a bit farther above.
345
346 if (!AllocateVideoBuffers()) {
347 SetErrorState("Allocate buffer failed");
348 return;
349 }
350
351 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_))
352 < 0) {
353 SetErrorState("VIDIOC_STREAMON failed");
354 return;
355 }
356
357 is_capturing_ = true;
358 // Post task to start fetching frames from v4l2.
359 v4l2_task_runner_->PostTask(
360 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::DoCapture, this));
361 }
362
363 void V4L2VideoCaptureDelegate::StopAndDeAllocate() {
364 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
365 // The order is important: stop streaming, clear |buffer_pool_|,
366 // thus munmap()ing the v4l2_buffers, and then return them to the OS.
367 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_))
368 < 0) {
369 SetErrorState("VIDIOC_STREAMOFF failed");
370 return;
371 }
372
373 buffer_tracker_pool_.clear();
374
375 v4l2_requestbuffers r_buffer = {};
376 r_buffer.type = capture_type_;
377 r_buffer.memory = V4L2_MEMORY_MMAP;
378 r_buffer.count = 0;
379 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0)
380 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0");
381
382 // At this point we can close the device.
383 // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
384 device_fd_.reset();
385 is_capturing_ = false;
386 client_.reset();
387 }
388
389 void V4L2VideoCaptureDelegate::SetRotation(int rotation) {
390 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
391 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
392 rotation_ = rotation;
393 }
394
395 bool V4L2VideoCaptureDelegate::AllocateVideoBuffers() {
396 v4l2_requestbuffers r_buffer = {};
397 r_buffer.type = capture_type_;
398 r_buffer.memory = V4L2_MEMORY_MMAP;
399 r_buffer.count = kMaxVideoBuffers;
400 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
401 DLOG(ERROR) << "Error requesting MMAP buffers from V4L2";
402 return false;
403 }
404 DCHECK_EQ(r_buffer.count, kMaxVideoBuffers);
405 r_buffer.count = std::min(r_buffer.count, kMaxVideoBuffers);
406 for (unsigned int i = 0; i < r_buffer.count; ++i) {
407 v4l2_buffer buffer = {};
408 buffer.type = capture_type_;
409 buffer.memory = V4L2_MEMORY_MMAP;
410 buffer.index = i;
411 FinishFillingV4L2Buffer(&buffer);
412
413 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
414 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer";
415 return false;
416 }
Pawel Osciak 2015/03/06 10:43:54 As described in h, this all could go to CreateBuff
mcasas 2015/03/09 21:23:56 Acknowledged.
417
418 buffer_tracker_pool_.push_back(CreateBufferTracker(device_fd_.get(),
419 buffer));
420
421 // Enqueue the buffer in the drivers incoming queue.
422 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
423 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver";
424 return false;
425 }
426 }
427 return true;
428 }
429
430 void V4L2VideoCaptureDelegate::DoCapture() {
431 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
432 if (!is_capturing_)
433 return;
434
435 pollfd device_pfd = {};
436 device_pfd.fd = device_fd_.get();
437 device_pfd.events = POLLIN;
438 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs));
439 if (result < 0) {
440 SetErrorState("Poll failed");
441 return;
442 }
443 // Check if poll() timed out; track the amount of times it did in a row and
444 // throw an error if it times out too many times.
445 if (result == 0) {
446 timeout_count_++;
447 if (timeout_count_ >= kContinuousTimeoutLimit) {
448 SetErrorState("Multiple continuous timeouts while read-polling.");
449 timeout_count_ = 0;
450 return;
451 }
452 } else {
453 timeout_count_ = 0;
454 }
455
456 // Deenqueue, send and reenqueue a buffer if the driver has filled one in.
457 if (device_pfd.revents & POLLIN) {
458 v4l2_buffer buffer = {};
459 buffer.type = capture_type_;
460 buffer.memory = V4L2_MEMORY_MMAP;
461 buffer.index = 0;
462 FinishFillingV4L2Buffer(&buffer);
Pawel Osciak 2015/03/06 10:43:54 This would just be FillV4L2Buffer().
mcasas 2015/03/09 21:23:56 Done.
463
464 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) {
465 SetErrorState("Failed to dequeue capture buffer");
466 return;
467 }
468
469 SendBuffer(buffer);
470
471 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
472 SetErrorState("Failed to enqueue capture buffer");
473 return;
474 }
475 }
476
477 v4l2_task_runner_->PostTask(
478 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::DoCapture, this));
479 }
480
481 void V4L2VideoCaptureDelegate::SetErrorState(const std::string& reason) {
482 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
483 is_capturing_ = false;
484 client_->OnError(reason);
485 }
486
487 V4L2VideoCaptureDelegate::~V4L2VideoCaptureDelegate() {
488 }
489
490 scoped_refptr<V4L2VideoCaptureDelegate::BufferTracker>
491 V4L2CaptureDelegateSinglePlane::CreateBufferTracker(int fd,
492 const v4l2_buffer& buffer) {
493 return make_scoped_refptr(new BufferTrackerSPlane(fd, buffer));
494 }
495
496 void V4L2CaptureDelegateSinglePlane::FillV4L2Format(
497 v4l2_format* format,
498 uint32_t width,
499 uint32_t height,
500 uint32_t pixelformat_fourcc) {
501 format->fmt.pix.width = width;
502 format->fmt.pix.height = height;
503 format->fmt.pix.pixelformat = pixelformat_fourcc;
504 }
505
506 void V4L2CaptureDelegateSinglePlane::SendBuffer(const v4l2_buffer& buffer) {
507 BufferTrackerSPlane* const buffer_tracker =
508 reinterpret_cast<BufferTrackerSPlane*>(
509 buffer_tracker_pool()[buffer.index].get());
510 DCHECK_EQ(buffer_tracker->planes()[0]->length, buffer.length);
511 client()->OnIncomingCapturedData(
512 static_cast<uint8*>(buffer_tracker->planes()[0]->start),
513 buffer_tracker->planes()[0]->length,
514 capture_format(),
515 rotation(),
516 base::TimeTicks::Now());
517 }
518
519 V4L2CaptureDelegateSinglePlane::BufferTrackerSPlane::BufferTrackerSPlane(
520 int fd,
521 const v4l2_buffer& buffer)
522 : BufferTracker(fd, buffer) {
523 scoped_ptr<Plane> plane(new Plane());
524 // Some devices require mmap() to be called with both READ and WRITE.
525 // See http://crbug.com/178582.
526 plane->start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
527 fd, buffer.m.offset);
528 if (plane->start == MAP_FAILED) {
529 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace";
530 return;
531 }
532 plane->length = buffer.length;
533 planes().push_back(plane.Pass());
534 }
535
536 scoped_refptr<V4L2VideoCaptureDelegate::BufferTracker>
537 V4L2CaptureDelegateMultiPlane::CreateBufferTracker(int fd,
538 const v4l2_buffer& buffer) {
539 DCHECK_LE(buffer.length, num_planes_);
540 return make_scoped_refptr(new BufferTrackerMPlane(fd, buffer));
541 }
542
543 void V4L2CaptureDelegateMultiPlane::FillV4L2Format(
544 v4l2_format* format,
545 uint32_t width,
546 uint32_t height,
547 uint32_t pixelformat_fourcc) {
548 format->fmt.pix_mp.width = width;
549 format->fmt.pix_mp.height = height;
550
551 fourcc_ = pixelformat_fourcc;
552 format->fmt.pix_mp.pixelformat = fourcc_;
553
554 num_planes_ = GetNumPlanesForFourCc(fourcc_);
555 DCHECK_GT(num_planes_, 0u);
556 DCHECK_LE(num_planes_, static_cast<unsigned long>(VIDEO_MAX_PLANES));
557 format->fmt.pix_mp.num_planes = num_planes_;
558
559 v4l2_plane_.reset(new v4l2_plane[num_planes_]);
Pawel Osciak 2015/03/06 10:43:55 We still don't want to call new foo[0] if GetNumPl
mcasas 2015/03/09 21:23:56 Done. I return a bool (false) if this goes south a
560 }
561
562 void V4L2CaptureDelegateMultiPlane::FinishFillingV4L2Buffer(
563 v4l2_buffer* buffer) {
564 buffer->length = num_planes_;
565 buffer->m.planes = v4l2_plane_.get();
566 }
567
568 void V4L2CaptureDelegateMultiPlane::SendBuffer(const v4l2_buffer& buffer) {
569 DCHECK_EQ(capture_format().pixel_format, PIXEL_FORMAT_I420);
570
571 BufferTrackerMPlane* const buffer_tracker =
572 reinterpret_cast<BufferTrackerMPlane*>(
573 buffer_tracker_pool()[buffer.index].get());
574
575 client()->OnIncomingCapturedYuvData(
576 static_cast<uint8*>(buffer_tracker->planes()[0]->start),
577 static_cast<uint8*>(buffer_tracker->planes()[1]->start),
578 static_cast<uint8*>(buffer_tracker->planes()[2]->start),
579 buffer_tracker->planes()[0]->length,
580 buffer_tracker->planes()[1]->length,
581 buffer_tracker->planes()[2]->length,
582 capture_format(),
583 rotation(),
584 base::TimeTicks::Now());
585 }
586
587 V4L2CaptureDelegateMultiPlane::BufferTrackerMPlane::BufferTrackerMPlane(
588 int fd,
589 const v4l2_buffer& buffer)
590 : BufferTracker(fd, buffer) {
591 for (size_t p = 0; p < buffer.length; ++p) {
592 scoped_ptr<Plane> plane(new Plane());
593 plane->start = mmap(NULL, buffer.m.planes[p].length, PROT_READ | PROT_WRITE,
594 MAP_SHARED, fd, buffer.m.planes[p].m.mem_offset);
595 if (plane->start == MAP_FAILED) {
596 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace";
597 return;
Pawel Osciak 2015/03/06 10:43:55 This way you'd still use the plane as normal if th
mcasas 2015/03/09 21:23:56 Done.
598 }
599 plane->length = buffer.m.planes[p].length;
600 DVLOG(3) << "Mmap()ed plane #" << p << ", length " << plane->length << "B";
601 planes().push_back(plane.Pass());
602 }
603 }
604
605 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698