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

Side by Side Diff: content/common/gpu/media/v4l2_image_processor.cc

Issue 1882373004: Migrate content/common/gpu/media code to media/gpu (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix several more bot-identified build issues Created 4 years, 8 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 2014 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 <errno.h>
6 #include <fcntl.h>
7 #include <linux/videodev2.h>
8 #include <poll.h>
9 #include <string.h>
10 #include <sys/eventfd.h>
11 #include <sys/ioctl.h>
12 #include <sys/mman.h>
13
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/callback.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "content/common/gpu/media/v4l2_image_processor.h"
19 #include "media/base/bind_to_current_loop.h"
20
21 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_str) \
22 do { \
23 if (device_->Ioctl(type, arg) != 0) { \
24 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_str; \
25 return value; \
26 } \
27 } while (0)
28
29 #define IOCTL_OR_ERROR_RETURN(type, arg) \
30 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0), #type)
31
32 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
33 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false, #type)
34
35 #define IOCTL_OR_LOG_ERROR(type, arg) \
36 do { \
37 if (device_->Ioctl(type, arg) != 0) \
38 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
39 } while (0)
40
41 namespace content {
42
43 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) {
44 }
45
46 V4L2ImageProcessor::InputRecord::~InputRecord() {
47 }
48
49 V4L2ImageProcessor::OutputRecord::OutputRecord() : at_device(false) {}
50
51 V4L2ImageProcessor::OutputRecord::~OutputRecord() {
52 }
53
54 V4L2ImageProcessor::JobRecord::JobRecord() : output_buffer_index(-1) {}
55
56 V4L2ImageProcessor::JobRecord::~JobRecord() {
57 }
58
59 V4L2ImageProcessor::V4L2ImageProcessor(const scoped_refptr<V4L2Device>& device)
60 : input_format_(media::PIXEL_FORMAT_UNKNOWN),
61 output_format_(media::PIXEL_FORMAT_UNKNOWN),
62 input_format_fourcc_(0),
63 output_format_fourcc_(0),
64 input_planes_count_(0),
65 output_planes_count_(0),
66 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
67 device_(device),
68 device_thread_("V4L2ImageProcessorThread"),
69 device_poll_thread_("V4L2ImageProcessorDevicePollThread"),
70 input_streamon_(false),
71 input_buffer_queued_count_(0),
72 output_streamon_(false),
73 output_buffer_queued_count_(0),
74 num_buffers_(0),
75 weak_this_factory_(this) {
76 weak_this_ = weak_this_factory_.GetWeakPtr();
77 }
78
79 V4L2ImageProcessor::~V4L2ImageProcessor() {
80 DCHECK(child_task_runner_->BelongsToCurrentThread());
81 DCHECK(!device_thread_.IsRunning());
82 DCHECK(!device_poll_thread_.IsRunning());
83
84 DestroyInputBuffers();
85 DestroyOutputBuffers();
86 }
87
88 void V4L2ImageProcessor::NotifyError() {
89 LOG(ERROR) << __func__;
90 DCHECK(!child_task_runner_->BelongsToCurrentThread());
91 child_task_runner_->PostTask(
92 FROM_HERE, base::Bind(&V4L2ImageProcessor::NotifyErrorOnChildThread,
93 weak_this_, error_cb_));
94 }
95
96 void V4L2ImageProcessor::NotifyErrorOnChildThread(
97 const base::Closure& error_cb) {
98 DCHECK(child_task_runner_->BelongsToCurrentThread());
99 error_cb_.Run();
100 }
101
102 bool V4L2ImageProcessor::Initialize(media::VideoPixelFormat input_format,
103 media::VideoPixelFormat output_format,
104 gfx::Size input_visible_size,
105 gfx::Size output_visible_size,
106 gfx::Size output_allocated_size,
107 int num_buffers,
108 const base::Closure& error_cb) {
109 DCHECK(!error_cb.is_null());
110 error_cb_ = error_cb;
111
112 // TODO(posciak): Replace Exynos-specific format/parameter hardcoding in this
113 // class with proper capability enumeration.
114 DCHECK_EQ(input_format, media::PIXEL_FORMAT_I420);
115 DCHECK_EQ(output_format, media::PIXEL_FORMAT_NV12);
116 DCHECK_GT(num_buffers, 0);
117
118 input_format_ = input_format;
119 output_format_ = output_format;
120 input_format_fourcc_ = V4L2Device::VideoPixelFormatToV4L2PixFmt(input_format);
121 output_format_fourcc_ =
122 V4L2Device::VideoPixelFormatToV4L2PixFmt(output_format);
123 num_buffers_ = num_buffers;
124
125 if (!input_format_fourcc_ || !output_format_fourcc_) {
126 LOG(ERROR) << "Unrecognized format(s)";
127 return false;
128 }
129
130 input_visible_size_ = input_visible_size;
131 output_visible_size_ = output_visible_size;
132 output_allocated_size_ = output_allocated_size;
133
134 input_planes_count_ = media::VideoFrame::NumPlanes(input_format);
135 DCHECK_LE(input_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
136 output_planes_count_ = media::VideoFrame::NumPlanes(output_format);
137 DCHECK_LE(output_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
138
139 // Capabilities check.
140 struct v4l2_capability caps;
141 memset(&caps, 0, sizeof(caps));
142 const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
143 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
144 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
145 LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
146 "caps check failed: 0x" << std::hex << caps.capabilities;
147 return false;
148 }
149
150 if (!CreateInputBuffers() || !CreateOutputBuffers())
151 return false;
152
153 if (!device_thread_.Start()) {
154 LOG(ERROR) << "Initialize(): encoder thread failed to start";
155 return false;
156 }
157
158 // StartDevicePoll will NotifyError on failure, so IgnoreResult is fine here.
159 device_thread_.message_loop()->PostTask(
160 FROM_HERE,
161 base::Bind(base::IgnoreResult(&V4L2ImageProcessor::StartDevicePoll),
162 base::Unretained(this)));
163
164 DVLOG(1) << "V4L2ImageProcessor initialized for "
165 << " input_format:" << media::VideoPixelFormatToString(input_format)
166 << ", output_format:"
167 << media::VideoPixelFormatToString(output_format)
168 << ", input_visible_size: " << input_visible_size.ToString()
169 << ", input_allocated_size: " << input_allocated_size_.ToString()
170 << ", output_visible_size: " << output_visible_size.ToString()
171 << ", output_allocated_size: " << output_allocated_size_.ToString();
172
173 return true;
174 }
175
176 std::vector<base::ScopedFD> V4L2ImageProcessor::GetDmabufsForOutputBuffer(
177 int output_buffer_index) {
178 DCHECK_GE(output_buffer_index, 0);
179 DCHECK_LT(output_buffer_index, num_buffers_);
180 return device_->GetDmabufsForV4L2Buffer(output_buffer_index,
181 output_planes_count_,
182 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
183 }
184
185 void V4L2ImageProcessor::Process(const scoped_refptr<media::VideoFrame>& frame,
186 int output_buffer_index,
187 const FrameReadyCB& cb) {
188 DVLOG(3) << __func__ << ": ts=" << frame->timestamp().InMilliseconds();
189
190 std::unique_ptr<JobRecord> job_record(new JobRecord());
191 job_record->frame = frame;
192 job_record->output_buffer_index = output_buffer_index;
193 job_record->ready_cb = cb;
194
195 device_thread_.message_loop()->PostTask(
196 FROM_HERE,
197 base::Bind(&V4L2ImageProcessor::ProcessTask,
198 base::Unretained(this),
199 base::Passed(&job_record)));
200 }
201
202 void V4L2ImageProcessor::ProcessTask(std::unique_ptr<JobRecord> job_record) {
203 int index = job_record->output_buffer_index;
204 DVLOG(3) << __func__ << ": Reusing output buffer, index=" << index;
205 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
206
207 EnqueueOutput(index);
208 input_queue_.push(make_linked_ptr(job_record.release()));
209 EnqueueInput();
210 }
211
212 void V4L2ImageProcessor::Destroy() {
213 DVLOG(3) << __func__;
214 DCHECK(child_task_runner_->BelongsToCurrentThread());
215
216 weak_this_factory_.InvalidateWeakPtrs();
217
218 // If the device thread is running, destroy using posted task.
219 if (device_thread_.IsRunning()) {
220 device_thread_.message_loop()->PostTask(
221 FROM_HERE,
222 base::Bind(&V4L2ImageProcessor::DestroyTask, base::Unretained(this)));
223 // Wait for tasks to finish/early-exit.
224 device_thread_.Stop();
225 } else {
226 // Otherwise DestroyTask() is not needed.
227 DCHECK(!device_poll_thread_.IsRunning());
228 }
229
230 delete this;
231 }
232
233 void V4L2ImageProcessor::DestroyTask() {
234 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
235
236 // Stop streaming and the device_poll_thread_.
237 StopDevicePoll();
238 }
239
240 bool V4L2ImageProcessor::CreateInputBuffers() {
241 DVLOG(3) << __func__;
242 DCHECK(child_task_runner_->BelongsToCurrentThread());
243 DCHECK(!input_streamon_);
244
245 struct v4l2_control control;
246 memset(&control, 0, sizeof(control));
247 control.id = V4L2_CID_ROTATE;
248 control.value = 0;
249 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
250
251 memset(&control, 0, sizeof(control));
252 control.id = V4L2_CID_HFLIP;
253 control.value = 0;
254 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
255
256 memset(&control, 0, sizeof(control));
257 control.id = V4L2_CID_VFLIP;
258 control.value = 0;
259 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
260
261 memset(&control, 0, sizeof(control));
262 control.id = V4L2_CID_ALPHA_COMPONENT;
263 control.value = 255;
264 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
265
266 struct v4l2_format format;
267 memset(&format, 0, sizeof(format));
268 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
269 format.fmt.pix_mp.width = input_visible_size_.width();
270 format.fmt.pix_mp.height = input_visible_size_.height();
271 format.fmt.pix_mp.pixelformat = input_format_fourcc_;
272 format.fmt.pix_mp.num_planes = input_planes_count_;
273 for (size_t i = 0; i < input_planes_count_; ++i) {
274 format.fmt.pix_mp.plane_fmt[i].sizeimage =
275 media::VideoFrame::PlaneSize(input_format_, i, input_allocated_size_)
276 .GetArea();
277 format.fmt.pix_mp.plane_fmt[i].bytesperline =
278 base::checked_cast<__u32>(input_allocated_size_.width());
279 }
280 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
281
282 input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format);
283 DCHECK(gfx::Rect(input_allocated_size_).Contains(
284 gfx::Rect(input_visible_size_)));
285
286 struct v4l2_crop crop;
287 memset(&crop, 0, sizeof(crop));
288 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
289 crop.c.left = 0;
290 crop.c.top = 0;
291 crop.c.width = base::checked_cast<__u32>(input_visible_size_.width());
292 crop.c.height = base::checked_cast<__u32>(input_visible_size_.height());
293 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
294
295 struct v4l2_requestbuffers reqbufs;
296 memset(&reqbufs, 0, sizeof(reqbufs));
297 reqbufs.count = num_buffers_;
298 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
299 reqbufs.memory = V4L2_MEMORY_USERPTR;
300 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
301 if (reqbufs.count != num_buffers_) {
302 LOG(ERROR) << "Failed to allocate input buffers. reqbufs.count="
303 << reqbufs.count << ", num_buffers=" << num_buffers_;
304 return false;
305 }
306
307 DCHECK(input_buffer_map_.empty());
308 input_buffer_map_.resize(reqbufs.count);
309
310 for (size_t i = 0; i < input_buffer_map_.size(); ++i)
311 free_input_buffers_.push_back(i);
312
313 return true;
314 }
315
316 bool V4L2ImageProcessor::CreateOutputBuffers() {
317 DVLOG(3) << __func__;
318 DCHECK(child_task_runner_->BelongsToCurrentThread());
319 DCHECK(!output_streamon_);
320
321 struct v4l2_format format;
322 memset(&format, 0, sizeof(format));
323 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
324 format.fmt.pix_mp.width = output_allocated_size_.width();
325 format.fmt.pix_mp.height = output_allocated_size_.height();
326 format.fmt.pix_mp.pixelformat = output_format_fourcc_;
327 format.fmt.pix_mp.num_planes = output_planes_count_;
328 for (size_t i = 0; i < output_planes_count_; ++i) {
329 format.fmt.pix_mp.plane_fmt[i].sizeimage =
330 media::VideoFrame::PlaneSize(output_format_, i, output_allocated_size_)
331 .GetArea();
332 format.fmt.pix_mp.plane_fmt[i].bytesperline =
333 base::checked_cast<__u32>(output_allocated_size_.width());
334 }
335 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
336
337 gfx::Size adjusted_allocated_size =
338 V4L2Device::CodedSizeFromV4L2Format(format);
339 DCHECK(gfx::Rect(adjusted_allocated_size).Contains(
340 gfx::Rect(output_allocated_size_)));
341 output_allocated_size_ = adjusted_allocated_size;
342
343 struct v4l2_crop crop;
344 memset(&crop, 0, sizeof(crop));
345 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
346 crop.c.left = 0;
347 crop.c.top = 0;
348 crop.c.width = base::checked_cast<__u32>(output_visible_size_.width());
349 crop.c.height = base::checked_cast<__u32>(output_visible_size_.height());
350 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
351
352 struct v4l2_requestbuffers reqbufs;
353 memset(&reqbufs, 0, sizeof(reqbufs));
354 reqbufs.count = num_buffers_;
355 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
356 reqbufs.memory = V4L2_MEMORY_MMAP;
357 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
358 if (reqbufs.count != num_buffers_) {
359 LOG(ERROR) << "Failed to allocate output buffers. reqbufs.count="
360 << reqbufs.count << ", num_buffers=" << num_buffers_;
361 return false;
362 }
363
364 DCHECK(output_buffer_map_.empty());
365 output_buffer_map_.resize(reqbufs.count);
366
367 return true;
368 }
369
370 void V4L2ImageProcessor::DestroyInputBuffers() {
371 DCHECK(child_task_runner_->BelongsToCurrentThread());
372 DCHECK(!input_streamon_);
373
374 struct v4l2_requestbuffers reqbufs;
375 memset(&reqbufs, 0, sizeof(reqbufs));
376 reqbufs.count = 0;
377 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
378 reqbufs.memory = V4L2_MEMORY_USERPTR;
379 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
380
381 input_buffer_map_.clear();
382 free_input_buffers_.clear();
383 }
384
385 void V4L2ImageProcessor::DestroyOutputBuffers() {
386 DCHECK(child_task_runner_->BelongsToCurrentThread());
387 DCHECK(!output_streamon_);
388
389 struct v4l2_requestbuffers reqbufs;
390 memset(&reqbufs, 0, sizeof(reqbufs));
391 reqbufs.count = 0;
392 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
393 reqbufs.memory = V4L2_MEMORY_MMAP;
394 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
395
396 output_buffer_map_.clear();
397 }
398
399 void V4L2ImageProcessor::DevicePollTask(bool poll_device) {
400 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current());
401
402 bool event_pending;
403 if (!device_->Poll(poll_device, &event_pending)) {
404 NotifyError();
405 return;
406 }
407
408 // All processing should happen on ServiceDeviceTask(), since we shouldn't
409 // touch encoder state from this thread.
410 device_thread_.message_loop()->PostTask(
411 FROM_HERE,
412 base::Bind(&V4L2ImageProcessor::ServiceDeviceTask,
413 base::Unretained(this)));
414 }
415
416 void V4L2ImageProcessor::ServiceDeviceTask() {
417 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
418 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
419 // so either:
420 // * device_poll_thread_ is running normally
421 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
422 // in which case we should early-out.
423 if (!device_poll_thread_.message_loop())
424 return;
425
426 Dequeue();
427 EnqueueInput();
428
429 if (!device_->ClearDevicePollInterrupt())
430 return;
431
432 bool poll_device =
433 (input_buffer_queued_count_ > 0 && output_buffer_queued_count_ > 0);
434
435 device_poll_thread_.message_loop()->PostTask(
436 FROM_HERE,
437 base::Bind(&V4L2ImageProcessor::DevicePollTask,
438 base::Unretained(this),
439 poll_device));
440
441 DVLOG(2) << __func__ << ": buffer counts: INPUT[" << input_queue_.size()
442 << "] => DEVICE[" << free_input_buffers_.size() << "+"
443 << input_buffer_queued_count_ << "/" << input_buffer_map_.size()
444 << "->" << output_buffer_map_.size() - output_buffer_queued_count_
445 << "+" << output_buffer_queued_count_ << "/"
446 << output_buffer_map_.size() << "]";
447 }
448
449 void V4L2ImageProcessor::EnqueueInput() {
450 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
451
452 const int old_inputs_queued = input_buffer_queued_count_;
453 while (!input_queue_.empty() && !free_input_buffers_.empty()) {
454 if (!EnqueueInputRecord())
455 return;
456 }
457 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) {
458 // We started up a previously empty queue.
459 // Queue state changed; signal interrupt.
460 if (!device_->SetDevicePollInterrupt())
461 return;
462 // VIDIOC_STREAMON if we haven't yet.
463 if (!input_streamon_) {
464 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
465 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
466 input_streamon_ = true;
467 }
468 }
469 }
470
471 void V4L2ImageProcessor::EnqueueOutput(int index) {
472 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
473
474 const int old_outputs_queued = output_buffer_queued_count_;
475 if (!EnqueueOutputRecord(index))
476 return;
477
478 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) {
479 // We just started up a previously empty queue.
480 // Queue state changed; signal interrupt.
481 if (!device_->SetDevicePollInterrupt())
482 return;
483 // Start VIDIOC_STREAMON if we haven't yet.
484 if (!output_streamon_) {
485 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
486 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
487 output_streamon_ = true;
488 }
489 }
490 }
491
492 void V4L2ImageProcessor::Dequeue() {
493 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
494
495 // Dequeue completed input (VIDEO_OUTPUT) buffers,
496 // and recycle to the free list.
497 struct v4l2_buffer dqbuf;
498 struct v4l2_plane planes[VIDEO_MAX_PLANES];
499 while (input_buffer_queued_count_ > 0) {
500 DCHECK(input_streamon_);
501 memset(&dqbuf, 0, sizeof(dqbuf));
502 memset(&planes, 0, sizeof(planes));
503 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
504 dqbuf.memory = V4L2_MEMORY_USERPTR;
505 dqbuf.m.planes = planes;
506 dqbuf.length = input_planes_count_;
507 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
508 if (errno == EAGAIN) {
509 // EAGAIN if we're just out of buffers to dequeue.
510 break;
511 }
512 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
513 NotifyError();
514 return;
515 }
516 InputRecord& input_record = input_buffer_map_[dqbuf.index];
517 DCHECK(input_record.at_device);
518 input_record.at_device = false;
519 input_record.frame = NULL;
520 free_input_buffers_.push_back(dqbuf.index);
521 input_buffer_queued_count_--;
522 }
523
524 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
525 // Return the finished buffer to the client via the job ready callback.
526 while (output_buffer_queued_count_ > 0) {
527 DCHECK(output_streamon_);
528 memset(&dqbuf, 0, sizeof(dqbuf));
529 memset(&planes, 0, sizeof(planes));
530 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
531 dqbuf.memory = V4L2_MEMORY_MMAP;
532 dqbuf.m.planes = planes;
533 dqbuf.length = output_planes_count_;
534 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
535 if (errno == EAGAIN) {
536 // EAGAIN if we're just out of buffers to dequeue.
537 break;
538 }
539 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
540 NotifyError();
541 return;
542 }
543 OutputRecord& output_record = output_buffer_map_[dqbuf.index];
544 DCHECK(output_record.at_device);
545 output_record.at_device = false;
546 output_buffer_queued_count_--;
547
548 // Jobs are always processed in FIFO order.
549 DCHECK(!running_jobs_.empty());
550 linked_ptr<JobRecord> job_record = running_jobs_.front();
551 running_jobs_.pop();
552
553 DVLOG(3) << "Processing finished, returning frame, index=" << dqbuf.index;
554
555 child_task_runner_->PostTask(
556 FROM_HERE, base::Bind(&V4L2ImageProcessor::FrameReady, weak_this_,
557 job_record->ready_cb, dqbuf.index));
558 }
559 }
560
561 bool V4L2ImageProcessor::EnqueueInputRecord() {
562 DCHECK(!input_queue_.empty());
563 DCHECK(!free_input_buffers_.empty());
564
565 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
566 linked_ptr<JobRecord> job_record = input_queue_.front();
567 input_queue_.pop();
568 const int index = free_input_buffers_.back();
569 InputRecord& input_record = input_buffer_map_[index];
570 DCHECK(!input_record.at_device);
571 input_record.frame = job_record->frame;
572 struct v4l2_buffer qbuf;
573 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
574 memset(&qbuf, 0, sizeof(qbuf));
575 memset(qbuf_planes, 0, sizeof(qbuf_planes));
576 qbuf.index = index;
577 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
578 qbuf.memory = V4L2_MEMORY_USERPTR;
579 qbuf.m.planes = qbuf_planes;
580 qbuf.length = input_planes_count_;
581 for (size_t i = 0; i < input_planes_count_; ++i) {
582 qbuf.m.planes[i].bytesused = media::VideoFrame::PlaneSize(
583 input_record.frame->format(), i, input_allocated_size_).GetArea();
584 qbuf.m.planes[i].length = qbuf.m.planes[i].bytesused;
585 qbuf.m.planes[i].m.userptr =
586 reinterpret_cast<unsigned long>(input_record.frame->data(i));
587 }
588 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
589 input_record.at_device = true;
590 running_jobs_.push(job_record);
591 free_input_buffers_.pop_back();
592 input_buffer_queued_count_++;
593
594 DVLOG(3) << __func__ << ": enqueued frame ts="
595 << job_record->frame->timestamp().InMilliseconds() << " to device.";
596
597 return true;
598 }
599
600 bool V4L2ImageProcessor::EnqueueOutputRecord(int index) {
601 DCHECK_GE(index, 0);
602 DCHECK_LT(index, output_buffer_map_.size());
603 // Enqueue an output (VIDEO_CAPTURE) buffer.
604 OutputRecord& output_record = output_buffer_map_[index];
605 DCHECK(!output_record.at_device);
606 struct v4l2_buffer qbuf;
607 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
608 memset(&qbuf, 0, sizeof(qbuf));
609 memset(qbuf_planes, 0, sizeof(qbuf_planes));
610 qbuf.index = index;
611 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
612 qbuf.memory = V4L2_MEMORY_MMAP;
613 qbuf.m.planes = qbuf_planes;
614 qbuf.length = output_planes_count_;
615 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
616 output_record.at_device = true;
617 output_buffer_queued_count_++;
618 return true;
619 }
620
621 bool V4L2ImageProcessor::StartDevicePoll() {
622 DVLOG(3) << __func__ << ": starting device poll";
623 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
624 DCHECK(!device_poll_thread_.IsRunning());
625
626 // Start up the device poll thread and schedule its first DevicePollTask().
627 if (!device_poll_thread_.Start()) {
628 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
629 NotifyError();
630 return false;
631 }
632 // Enqueue a poll task with no devices to poll on - will wait only for the
633 // poll interrupt
634 device_poll_thread_.message_loop()->PostTask(
635 FROM_HERE,
636 base::Bind(
637 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false));
638
639 return true;
640 }
641
642 bool V4L2ImageProcessor::StopDevicePoll() {
643 DVLOG(3) << __func__ << ": stopping device poll";
644 if (device_thread_.IsRunning())
645 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
646
647 // Signal the DevicePollTask() to stop, and stop the device poll thread.
648 if (!device_->SetDevicePollInterrupt())
649 return false;
650 device_poll_thread_.Stop();
651
652 // Clear the interrupt now, to be sure.
653 if (!device_->ClearDevicePollInterrupt())
654 return false;
655
656 if (input_streamon_) {
657 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
658 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
659 }
660 input_streamon_ = false;
661
662 if (output_streamon_) {
663 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
664 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
665 }
666 output_streamon_ = false;
667
668 // Reset all our accounting info.
669 while (!input_queue_.empty())
670 input_queue_.pop();
671
672 while (!running_jobs_.empty())
673 running_jobs_.pop();
674
675 free_input_buffers_.clear();
676 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
677 InputRecord& input_record = input_buffer_map_[i];
678 input_record.at_device = false;
679 input_record.frame = NULL;
680 free_input_buffers_.push_back(i);
681 }
682 input_buffer_queued_count_ = 0;
683
684 output_buffer_map_.clear();
685 output_buffer_map_.resize(num_buffers_);
686 output_buffer_queued_count_ = 0;
687
688 return true;
689 }
690
691 void V4L2ImageProcessor::FrameReady(const FrameReadyCB& cb,
692 int output_buffer_index) {
693 DCHECK(child_task_runner_->BelongsToCurrentThread());
694 cb.Run(output_buffer_index);
695 }
696
697 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698