OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 <errno.h> | 5 #include <errno.h> |
6 #include <fcntl.h> | 6 #include <fcntl.h> |
7 #include <linux/videodev2.h> | 7 #include <linux/videodev2.h> |
8 #include <poll.h> | 8 #include <poll.h> |
9 #include <string.h> | 9 #include <string.h> |
10 #include <sys/eventfd.h> | 10 #include <sys/eventfd.h> |
11 #include <sys/ioctl.h> | 11 #include <sys/ioctl.h> |
12 #include <sys/mman.h> | 12 #include <sys/mman.h> |
13 | 13 |
14 #include "base/bind.h" | 14 #include "base/bind.h" |
15 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
16 #include "base/callback.h" | 16 #include "base/callback.h" |
17 #include "base/numerics/safe_conversions.h" | 17 #include "base/numerics/safe_conversions.h" |
| 18 #include "base/single_thread_task_runner.h" |
18 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
19 #include "media/gpu/v4l2_image_processor.h" | 20 #include "media/gpu/v4l2_image_processor.h" |
20 | 21 |
21 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_str) \ | 22 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_str) \ |
22 do { \ | 23 do { \ |
23 if (device_->Ioctl(type, arg) != 0) { \ | 24 if (device_->Ioctl(type, arg) != 0) { \ |
24 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_str; \ | 25 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_str; \ |
25 return value; \ | 26 return value; \ |
26 } \ | 27 } \ |
27 } while (0) | 28 } while (0) |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 job_record->ready_cb = cb; | 229 job_record->ready_cb = cb; |
229 | 230 |
230 device_thread_.message_loop()->PostTask( | 231 device_thread_.message_loop()->PostTask( |
231 FROM_HERE, base::Bind(&V4L2ImageProcessor::ProcessTask, | 232 FROM_HERE, base::Bind(&V4L2ImageProcessor::ProcessTask, |
232 base::Unretained(this), base::Passed(&job_record))); | 233 base::Unretained(this), base::Passed(&job_record))); |
233 } | 234 } |
234 | 235 |
235 void V4L2ImageProcessor::ProcessTask(std::unique_ptr<JobRecord> job_record) { | 236 void V4L2ImageProcessor::ProcessTask(std::unique_ptr<JobRecord> job_record) { |
236 int index = job_record->output_buffer_index; | 237 int index = job_record->output_buffer_index; |
237 DVLOG(3) << __func__ << ": Reusing output buffer, index=" << index; | 238 DVLOG(3) << __func__ << ": Reusing output buffer, index=" << index; |
238 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 239 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
239 | 240 |
240 EnqueueOutput(index); | 241 EnqueueOutput(index); |
241 input_queue_.push(make_linked_ptr(job_record.release())); | 242 input_queue_.push(make_linked_ptr(job_record.release())); |
242 EnqueueInput(); | 243 EnqueueInput(); |
243 } | 244 } |
244 | 245 |
245 void V4L2ImageProcessor::Destroy() { | 246 void V4L2ImageProcessor::Destroy() { |
246 DVLOG(3) << __func__; | 247 DVLOG(3) << __func__; |
247 DCHECK(child_task_runner_->BelongsToCurrentThread()); | 248 DCHECK(child_task_runner_->BelongsToCurrentThread()); |
248 | 249 |
249 weak_this_factory_.InvalidateWeakPtrs(); | 250 weak_this_factory_.InvalidateWeakPtrs(); |
250 | 251 |
251 // If the device thread is running, destroy using posted task. | 252 // If the device thread is running, destroy using posted task. |
252 if (device_thread_.IsRunning()) { | 253 if (device_thread_.IsRunning()) { |
253 device_thread_.message_loop()->PostTask( | 254 device_thread_.message_loop()->PostTask( |
254 FROM_HERE, | 255 FROM_HERE, |
255 base::Bind(&V4L2ImageProcessor::DestroyTask, base::Unretained(this))); | 256 base::Bind(&V4L2ImageProcessor::DestroyTask, base::Unretained(this))); |
256 // Wait for tasks to finish/early-exit. | 257 // Wait for tasks to finish/early-exit. |
257 device_thread_.Stop(); | 258 device_thread_.Stop(); |
258 } else { | 259 } else { |
259 // Otherwise DestroyTask() is not needed. | 260 // Otherwise DestroyTask() is not needed. |
260 DCHECK(!device_poll_thread_.IsRunning()); | 261 DCHECK(!device_poll_thread_.IsRunning()); |
261 } | 262 } |
262 | 263 |
263 delete this; | 264 delete this; |
264 } | 265 } |
265 | 266 |
266 void V4L2ImageProcessor::DestroyTask() { | 267 void V4L2ImageProcessor::DestroyTask() { |
267 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 268 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
268 | 269 |
269 // Stop streaming and the device_poll_thread_. | 270 // Stop streaming and the device_poll_thread_. |
270 StopDevicePoll(); | 271 StopDevicePoll(); |
271 } | 272 } |
272 | 273 |
273 bool V4L2ImageProcessor::CreateInputBuffers() { | 274 bool V4L2ImageProcessor::CreateInputBuffers() { |
274 DVLOG(3) << __func__; | 275 DVLOG(3) << __func__; |
275 DCHECK(child_task_runner_->BelongsToCurrentThread()); | 276 DCHECK(child_task_runner_->BelongsToCurrentThread()); |
276 DCHECK(!input_streamon_); | 277 DCHECK(!input_streamon_); |
277 | 278 |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 memset(&reqbufs, 0, sizeof(reqbufs)); | 412 memset(&reqbufs, 0, sizeof(reqbufs)); |
412 reqbufs.count = 0; | 413 reqbufs.count = 0; |
413 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 414 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
414 reqbufs.memory = V4L2_MEMORY_MMAP; | 415 reqbufs.memory = V4L2_MEMORY_MMAP; |
415 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); | 416 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); |
416 | 417 |
417 output_buffer_map_.clear(); | 418 output_buffer_map_.clear(); |
418 } | 419 } |
419 | 420 |
420 void V4L2ImageProcessor::DevicePollTask(bool poll_device) { | 421 void V4L2ImageProcessor::DevicePollTask(bool poll_device) { |
421 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current()); | 422 DCHECK(device_poll_thread_.task_runner()->BelongsToCurrentThread()); |
422 | 423 |
423 bool event_pending; | 424 bool event_pending; |
424 if (!device_->Poll(poll_device, &event_pending)) { | 425 if (!device_->Poll(poll_device, &event_pending)) { |
425 NotifyError(); | 426 NotifyError(); |
426 return; | 427 return; |
427 } | 428 } |
428 | 429 |
429 // All processing should happen on ServiceDeviceTask(), since we shouldn't | 430 // All processing should happen on ServiceDeviceTask(), since we shouldn't |
430 // touch encoder state from this thread. | 431 // touch encoder state from this thread. |
431 device_thread_.message_loop()->PostTask( | 432 device_thread_.message_loop()->PostTask( |
432 FROM_HERE, base::Bind(&V4L2ImageProcessor::ServiceDeviceTask, | 433 FROM_HERE, base::Bind(&V4L2ImageProcessor::ServiceDeviceTask, |
433 base::Unretained(this))); | 434 base::Unretained(this))); |
434 } | 435 } |
435 | 436 |
436 void V4L2ImageProcessor::ServiceDeviceTask() { | 437 void V4L2ImageProcessor::ServiceDeviceTask() { |
437 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 438 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
438 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(), | 439 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(), |
439 // so either: | 440 // so either: |
440 // * device_poll_thread_ is running normally | 441 // * device_poll_thread_ is running normally |
441 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down, | 442 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down, |
442 // in which case we should early-out. | 443 // in which case we should early-out. |
443 if (!device_poll_thread_.message_loop()) | 444 if (!device_poll_thread_.message_loop()) |
444 return; | 445 return; |
445 | 446 |
446 Dequeue(); | 447 Dequeue(); |
447 EnqueueInput(); | 448 EnqueueInput(); |
(...skipping 10 matching lines...) Expand all Loading... |
458 | 459 |
459 DVLOG(2) << __func__ << ": buffer counts: INPUT[" << input_queue_.size() | 460 DVLOG(2) << __func__ << ": buffer counts: INPUT[" << input_queue_.size() |
460 << "] => DEVICE[" << free_input_buffers_.size() << "+" | 461 << "] => DEVICE[" << free_input_buffers_.size() << "+" |
461 << input_buffer_queued_count_ << "/" << input_buffer_map_.size() | 462 << input_buffer_queued_count_ << "/" << input_buffer_map_.size() |
462 << "->" << output_buffer_map_.size() - output_buffer_queued_count_ | 463 << "->" << output_buffer_map_.size() - output_buffer_queued_count_ |
463 << "+" << output_buffer_queued_count_ << "/" | 464 << "+" << output_buffer_queued_count_ << "/" |
464 << output_buffer_map_.size() << "]"; | 465 << output_buffer_map_.size() << "]"; |
465 } | 466 } |
466 | 467 |
467 void V4L2ImageProcessor::EnqueueInput() { | 468 void V4L2ImageProcessor::EnqueueInput() { |
468 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 469 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
469 | 470 |
470 const int old_inputs_queued = input_buffer_queued_count_; | 471 const int old_inputs_queued = input_buffer_queued_count_; |
471 while (!input_queue_.empty() && !free_input_buffers_.empty()) { | 472 while (!input_queue_.empty() && !free_input_buffers_.empty()) { |
472 if (!EnqueueInputRecord()) | 473 if (!EnqueueInputRecord()) |
473 return; | 474 return; |
474 } | 475 } |
475 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) { | 476 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) { |
476 // We started up a previously empty queue. | 477 // We started up a previously empty queue. |
477 // Queue state changed; signal interrupt. | 478 // Queue state changed; signal interrupt. |
478 if (!device_->SetDevicePollInterrupt()) | 479 if (!device_->SetDevicePollInterrupt()) |
479 return; | 480 return; |
480 // VIDIOC_STREAMON if we haven't yet. | 481 // VIDIOC_STREAMON if we haven't yet. |
481 if (!input_streamon_) { | 482 if (!input_streamon_) { |
482 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | 483 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
483 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); | 484 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); |
484 input_streamon_ = true; | 485 input_streamon_ = true; |
485 } | 486 } |
486 } | 487 } |
487 } | 488 } |
488 | 489 |
489 void V4L2ImageProcessor::EnqueueOutput(int index) { | 490 void V4L2ImageProcessor::EnqueueOutput(int index) { |
490 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 491 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
491 | 492 |
492 const int old_outputs_queued = output_buffer_queued_count_; | 493 const int old_outputs_queued = output_buffer_queued_count_; |
493 if (!EnqueueOutputRecord(index)) | 494 if (!EnqueueOutputRecord(index)) |
494 return; | 495 return; |
495 | 496 |
496 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) { | 497 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) { |
497 // We just started up a previously empty queue. | 498 // We just started up a previously empty queue. |
498 // Queue state changed; signal interrupt. | 499 // Queue state changed; signal interrupt. |
499 if (!device_->SetDevicePollInterrupt()) | 500 if (!device_->SetDevicePollInterrupt()) |
500 return; | 501 return; |
501 // Start VIDIOC_STREAMON if we haven't yet. | 502 // Start VIDIOC_STREAMON if we haven't yet. |
502 if (!output_streamon_) { | 503 if (!output_streamon_) { |
503 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 504 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
504 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); | 505 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); |
505 output_streamon_ = true; | 506 output_streamon_ = true; |
506 } | 507 } |
507 } | 508 } |
508 } | 509 } |
509 | 510 |
510 void V4L2ImageProcessor::Dequeue() { | 511 void V4L2ImageProcessor::Dequeue() { |
511 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 512 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
512 | 513 |
513 // Dequeue completed input (VIDEO_OUTPUT) buffers, | 514 // Dequeue completed input (VIDEO_OUTPUT) buffers, |
514 // and recycle to the free list. | 515 // and recycle to the free list. |
515 struct v4l2_buffer dqbuf; | 516 struct v4l2_buffer dqbuf; |
516 struct v4l2_plane planes[VIDEO_MAX_PLANES]; | 517 struct v4l2_plane planes[VIDEO_MAX_PLANES]; |
517 while (input_buffer_queued_count_ > 0) { | 518 while (input_buffer_queued_count_ > 0) { |
518 DCHECK(input_streamon_); | 519 DCHECK(input_streamon_); |
519 memset(&dqbuf, 0, sizeof(dqbuf)); | 520 memset(&dqbuf, 0, sizeof(dqbuf)); |
520 memset(&planes, 0, sizeof(planes)); | 521 memset(&planes, 0, sizeof(planes)); |
521 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | 522 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
637 qbuf.m.planes = qbuf_planes; | 638 qbuf.m.planes = qbuf_planes; |
638 qbuf.length = output_planes_count_; | 639 qbuf.length = output_planes_count_; |
639 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); | 640 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); |
640 output_record.at_device = true; | 641 output_record.at_device = true; |
641 output_buffer_queued_count_++; | 642 output_buffer_queued_count_++; |
642 return true; | 643 return true; |
643 } | 644 } |
644 | 645 |
645 bool V4L2ImageProcessor::StartDevicePoll() { | 646 bool V4L2ImageProcessor::StartDevicePoll() { |
646 DVLOG(3) << __func__ << ": starting device poll"; | 647 DVLOG(3) << __func__ << ": starting device poll"; |
647 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 648 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
648 DCHECK(!device_poll_thread_.IsRunning()); | 649 DCHECK(!device_poll_thread_.IsRunning()); |
649 | 650 |
650 // Start up the device poll thread and schedule its first DevicePollTask(). | 651 // Start up the device poll thread and schedule its first DevicePollTask(). |
651 if (!device_poll_thread_.Start()) { | 652 if (!device_poll_thread_.Start()) { |
652 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; | 653 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; |
653 NotifyError(); | 654 NotifyError(); |
654 return false; | 655 return false; |
655 } | 656 } |
656 // Enqueue a poll task with no devices to poll on - will wait only for the | 657 // Enqueue a poll task with no devices to poll on - will wait only for the |
657 // poll interrupt | 658 // poll interrupt |
658 device_poll_thread_.message_loop()->PostTask( | 659 device_poll_thread_.message_loop()->PostTask( |
659 FROM_HERE, base::Bind(&V4L2ImageProcessor::DevicePollTask, | 660 FROM_HERE, base::Bind(&V4L2ImageProcessor::DevicePollTask, |
660 base::Unretained(this), false)); | 661 base::Unretained(this), false)); |
661 | 662 |
662 return true; | 663 return true; |
663 } | 664 } |
664 | 665 |
665 bool V4L2ImageProcessor::StopDevicePoll() { | 666 bool V4L2ImageProcessor::StopDevicePoll() { |
666 DVLOG(3) << __func__ << ": stopping device poll"; | 667 DVLOG(3) << __func__ << ": stopping device poll"; |
667 if (device_thread_.IsRunning()) | 668 if (device_thread_.IsRunning()) |
668 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 669 DCHECK(device_thread_.task_runner()->BelongsToCurrentThread()); |
669 | 670 |
670 // Signal the DevicePollTask() to stop, and stop the device poll thread. | 671 // Signal the DevicePollTask() to stop, and stop the device poll thread. |
671 if (!device_->SetDevicePollInterrupt()) | 672 if (!device_->SetDevicePollInterrupt()) |
672 return false; | 673 return false; |
673 device_poll_thread_.Stop(); | 674 device_poll_thread_.Stop(); |
674 | 675 |
675 // Clear the interrupt now, to be sure. | 676 // Clear the interrupt now, to be sure. |
676 if (!device_->ClearDevicePollInterrupt()) | 677 if (!device_->ClearDevicePollInterrupt()) |
677 return false; | 678 return false; |
678 | 679 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 return true; | 712 return true; |
712 } | 713 } |
713 | 714 |
714 void V4L2ImageProcessor::FrameReady(const FrameReadyCB& cb, | 715 void V4L2ImageProcessor::FrameReady(const FrameReadyCB& cb, |
715 int output_buffer_index) { | 716 int output_buffer_index) { |
716 DCHECK(child_task_runner_->BelongsToCurrentThread()); | 717 DCHECK(child_task_runner_->BelongsToCurrentThread()); |
717 cb.Run(output_buffer_index); | 718 cb.Run(output_buffer_index); |
718 } | 719 } |
719 | 720 |
720 } // namespace media | 721 } // namespace media |
OLD | NEW |