| 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 |