| 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 "media/gpu/v4l2_video_encode_accelerator.h" | 5 #include "media/gpu/v4l2_video_encode_accelerator.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <linux/videodev2.h> | 8 #include <linux/videodev2.h> |
| 9 #include <poll.h> | 9 #include <poll.h> |
| 10 #include <string.h> | 10 #include <string.h> |
| 11 #include <sys/eventfd.h> | 11 #include <sys/eventfd.h> |
| 12 #include <sys/ioctl.h> | 12 #include <sys/ioctl.h> |
| 13 #include <sys/mman.h> | 13 #include <sys/mman.h> |
| 14 | 14 |
| 15 #include <utility> | 15 #include <utility> |
| 16 | 16 |
| 17 #include "base/callback.h" | 17 #include "base/callback.h" |
| 18 #include "base/command_line.h" | 18 #include "base/command_line.h" |
| 19 #include "base/macros.h" | 19 #include "base/macros.h" |
| 20 #include "base/numerics/safe_conversions.h" | 20 #include "base/numerics/safe_conversions.h" |
| 21 #include "base/single_thread_task_runner.h" |
| 21 #include "base/threading/thread_task_runner_handle.h" | 22 #include "base/threading/thread_task_runner_handle.h" |
| 22 #include "base/trace_event/trace_event.h" | 23 #include "base/trace_event/trace_event.h" |
| 23 #include "media/base/bind_to_current_loop.h" | 24 #include "media/base/bind_to_current_loop.h" |
| 24 #include "media/base/bitstream_buffer.h" | 25 #include "media/base/bitstream_buffer.h" |
| 25 #include "media/gpu/shared_memory_region.h" | 26 #include "media/gpu/shared_memory_region.h" |
| 26 | 27 |
| 27 #define NOTIFY_ERROR(x) \ | 28 #define NOTIFY_ERROR(x) \ |
| 28 do { \ | 29 do { \ |
| 29 LOG(ERROR) << "Setting error state:" << x; \ | 30 LOG(ERROR) << "Setting error state:" << x; \ |
| 30 SetErrorState(x); \ | 31 SetErrorState(x); \ |
| (...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 ImageProcessorInputRecord record = image_processor_input_queue_.front(); | 399 ImageProcessorInputRecord record = image_processor_input_queue_.front(); |
| 399 image_processor_input_queue_.pop(); | 400 image_processor_input_queue_.pop(); |
| 400 Encode(record.frame, record.force_keyframe); | 401 Encode(record.frame, record.force_keyframe); |
| 401 } | 402 } |
| 402 } | 403 } |
| 403 | 404 |
| 404 void V4L2VideoEncodeAccelerator::EncodeTask( | 405 void V4L2VideoEncodeAccelerator::EncodeTask( |
| 405 const scoped_refptr<VideoFrame>& frame, | 406 const scoped_refptr<VideoFrame>& frame, |
| 406 bool force_keyframe) { | 407 bool force_keyframe) { |
| 407 DVLOG(3) << "EncodeTask(): force_keyframe=" << force_keyframe; | 408 DVLOG(3) << "EncodeTask(): force_keyframe=" << force_keyframe; |
| 408 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 409 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 409 DCHECK_NE(encoder_state_, kUninitialized); | 410 DCHECK_NE(encoder_state_, kUninitialized); |
| 410 | 411 |
| 411 if (encoder_state_ == kError) { | 412 if (encoder_state_ == kError) { |
| 412 DVLOG(2) << "EncodeTask(): early out: kError state"; | 413 DVLOG(2) << "EncodeTask(): early out: kError state"; |
| 413 return; | 414 return; |
| 414 } | 415 } |
| 415 | 416 |
| 416 encoder_input_queue_.push(frame); | 417 encoder_input_queue_.push(frame); |
| 417 Enqueue(); | 418 Enqueue(); |
| 418 | 419 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 441 NOTIFY_ERROR(kPlatformFailureError); | 442 NOTIFY_ERROR(kPlatformFailureError); |
| 442 return; | 443 return; |
| 443 } | 444 } |
| 444 } | 445 } |
| 445 } | 446 } |
| 446 } | 447 } |
| 447 | 448 |
| 448 void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask( | 449 void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask( |
| 449 std::unique_ptr<BitstreamBufferRef> buffer_ref) { | 450 std::unique_ptr<BitstreamBufferRef> buffer_ref) { |
| 450 DVLOG(3) << "UseOutputBitstreamBufferTask(): id=" << buffer_ref->id; | 451 DVLOG(3) << "UseOutputBitstreamBufferTask(): id=" << buffer_ref->id; |
| 451 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 452 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 452 | 453 |
| 453 encoder_output_queue_.push_back( | 454 encoder_output_queue_.push_back( |
| 454 linked_ptr<BitstreamBufferRef>(buffer_ref.release())); | 455 linked_ptr<BitstreamBufferRef>(buffer_ref.release())); |
| 455 Enqueue(); | 456 Enqueue(); |
| 456 | 457 |
| 457 if (encoder_state_ == kInitialized) { | 458 if (encoder_state_ == kInitialized) { |
| 458 // Finish setting up our OUTPUT queue. See: Initialize(). | 459 // Finish setting up our OUTPUT queue. See: Initialize(). |
| 459 // VIDIOC_REQBUFS on OUTPUT queue. | 460 // VIDIOC_REQBUFS on OUTPUT queue. |
| 460 if (!CreateInputBuffers()) | 461 if (!CreateInputBuffers()) |
| 461 return; | 462 return; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 472 | 473 |
| 473 // Stop streaming and the device_poll_thread_. | 474 // Stop streaming and the device_poll_thread_. |
| 474 StopDevicePoll(); | 475 StopDevicePoll(); |
| 475 | 476 |
| 476 // Set our state to kError, and early-out all tasks. | 477 // Set our state to kError, and early-out all tasks. |
| 477 encoder_state_ = kError; | 478 encoder_state_ = kError; |
| 478 } | 479 } |
| 479 | 480 |
| 480 void V4L2VideoEncodeAccelerator::ServiceDeviceTask() { | 481 void V4L2VideoEncodeAccelerator::ServiceDeviceTask() { |
| 481 DVLOG(3) << "ServiceDeviceTask()"; | 482 DVLOG(3) << "ServiceDeviceTask()"; |
| 482 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 483 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 483 DCHECK_NE(encoder_state_, kUninitialized); | 484 DCHECK_NE(encoder_state_, kUninitialized); |
| 484 DCHECK_NE(encoder_state_, kInitialized); | 485 DCHECK_NE(encoder_state_, kInitialized); |
| 485 | 486 |
| 486 if (encoder_state_ == kError) { | 487 if (encoder_state_ == kError) { |
| 487 DVLOG(2) << "ServiceDeviceTask(): early out: kError state"; | 488 DVLOG(2) << "ServiceDeviceTask(): early out: kError state"; |
| 488 return; | 489 return; |
| 489 } | 490 } |
| 490 | 491 |
| 491 Dequeue(); | 492 Dequeue(); |
| 492 Enqueue(); | 493 Enqueue(); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 516 << free_input_buffers_.size() << "+" | 517 << free_input_buffers_.size() << "+" |
| 517 << input_buffer_queued_count_ << "/" | 518 << input_buffer_queued_count_ << "/" |
| 518 << input_buffer_map_.size() << "->" | 519 << input_buffer_map_.size() << "->" |
| 519 << free_output_buffers_.size() << "+" | 520 << free_output_buffers_.size() << "+" |
| 520 << output_buffer_queued_count_ << "/" | 521 << output_buffer_queued_count_ << "/" |
| 521 << output_buffer_map_.size() << "] => OUT[" | 522 << output_buffer_map_.size() << "] => OUT[" |
| 522 << encoder_output_queue_.size() << "]"; | 523 << encoder_output_queue_.size() << "]"; |
| 523 } | 524 } |
| 524 | 525 |
| 525 void V4L2VideoEncodeAccelerator::Enqueue() { | 526 void V4L2VideoEncodeAccelerator::Enqueue() { |
| 526 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 527 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 527 | 528 |
| 528 DVLOG(3) << "Enqueue() " | 529 DVLOG(3) << "Enqueue() " |
| 529 << "free_input_buffers: " << free_input_buffers_.size() | 530 << "free_input_buffers: " << free_input_buffers_.size() |
| 530 << "input_queue: " << encoder_input_queue_.size(); | 531 << "input_queue: " << encoder_input_queue_.size(); |
| 531 | 532 |
| 532 // Enqueue all the inputs we can. | 533 // Enqueue all the inputs we can. |
| 533 const int old_inputs_queued = input_buffer_queued_count_; | 534 const int old_inputs_queued = input_buffer_queued_count_; |
| 534 // while (!ready_input_buffers_.empty()) { | 535 // while (!ready_input_buffers_.empty()) { |
| 535 while (!encoder_input_queue_.empty() && !free_input_buffers_.empty()) { | 536 while (!encoder_input_queue_.empty() && !free_input_buffers_.empty()) { |
| 536 if (!EnqueueInputRecord()) | 537 if (!EnqueueInputRecord()) |
| (...skipping 27 matching lines...) Expand all Loading... |
| 564 if (!output_streamon_) { | 565 if (!output_streamon_) { |
| 565 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 566 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| 566 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); | 567 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); |
| 567 output_streamon_ = true; | 568 output_streamon_ = true; |
| 568 } | 569 } |
| 569 } | 570 } |
| 570 } | 571 } |
| 571 | 572 |
| 572 void V4L2VideoEncodeAccelerator::Dequeue() { | 573 void V4L2VideoEncodeAccelerator::Dequeue() { |
| 573 DVLOG(3) << "Dequeue()"; | 574 DVLOG(3) << "Dequeue()"; |
| 574 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 575 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 575 | 576 |
| 576 // Dequeue completed input (VIDEO_OUTPUT) buffers, and recycle to the free | 577 // Dequeue completed input (VIDEO_OUTPUT) buffers, and recycle to the free |
| 577 // list. | 578 // list. |
| 578 struct v4l2_buffer dqbuf; | 579 struct v4l2_buffer dqbuf; |
| 579 struct v4l2_plane planes[VIDEO_MAX_PLANES]; | 580 struct v4l2_plane planes[VIDEO_MAX_PLANES]; |
| 580 while (input_buffer_queued_count_ > 0) { | 581 while (input_buffer_queued_count_ > 0) { |
| 581 DVLOG(4) << "inputs queued: " << input_buffer_queued_count_; | 582 DVLOG(4) << "inputs queued: " << input_buffer_queued_count_; |
| 582 DCHECK(input_streamon_); | 583 DCHECK(input_streamon_); |
| 583 memset(&dqbuf, 0, sizeof(dqbuf)); | 584 memset(&dqbuf, 0, sizeof(dqbuf)); |
| 584 memset(&planes, 0, sizeof(planes)); | 585 memset(&planes, 0, sizeof(planes)); |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 759 output_record.at_device = true; | 760 output_record.at_device = true; |
| 760 output_record.buffer_ref = output_buffer; | 761 output_record.buffer_ref = output_buffer; |
| 761 encoder_output_queue_.pop_back(); | 762 encoder_output_queue_.pop_back(); |
| 762 free_output_buffers_.pop_back(); | 763 free_output_buffers_.pop_back(); |
| 763 output_buffer_queued_count_++; | 764 output_buffer_queued_count_++; |
| 764 return true; | 765 return true; |
| 765 } | 766 } |
| 766 | 767 |
| 767 bool V4L2VideoEncodeAccelerator::StartDevicePoll() { | 768 bool V4L2VideoEncodeAccelerator::StartDevicePoll() { |
| 768 DVLOG(3) << "StartDevicePoll()"; | 769 DVLOG(3) << "StartDevicePoll()"; |
| 769 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 770 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 770 DCHECK(!device_poll_thread_.IsRunning()); | 771 DCHECK(!device_poll_thread_.IsRunning()); |
| 771 | 772 |
| 772 // Start up the device poll thread and schedule its first DevicePollTask(). | 773 // Start up the device poll thread and schedule its first DevicePollTask(). |
| 773 if (!device_poll_thread_.Start()) { | 774 if (!device_poll_thread_.Start()) { |
| 774 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; | 775 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; |
| 775 NOTIFY_ERROR(kPlatformFailureError); | 776 NOTIFY_ERROR(kPlatformFailureError); |
| 776 return false; | 777 return false; |
| 777 } | 778 } |
| 778 // Enqueue a poll task with no devices to poll on -- it will wait only on the | 779 // Enqueue a poll task with no devices to poll on -- it will wait only on the |
| 779 // interrupt fd. | 780 // interrupt fd. |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 829 output_buffer_queued_count_ = 0; | 830 output_buffer_queued_count_ = 0; |
| 830 | 831 |
| 831 encoder_output_queue_.clear(); | 832 encoder_output_queue_.clear(); |
| 832 | 833 |
| 833 DVLOG(3) << "StopDevicePoll(): device poll stopped"; | 834 DVLOG(3) << "StopDevicePoll(): device poll stopped"; |
| 834 return true; | 835 return true; |
| 835 } | 836 } |
| 836 | 837 |
| 837 void V4L2VideoEncodeAccelerator::DevicePollTask(bool poll_device) { | 838 void V4L2VideoEncodeAccelerator::DevicePollTask(bool poll_device) { |
| 838 DVLOG(3) << "DevicePollTask()"; | 839 DVLOG(3) << "DevicePollTask()"; |
| 839 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current()); | 840 DCHECK(device_poll_thread_.task_runner()->BelongsToCurrentThread()); |
| 840 | 841 |
| 841 bool event_pending; | 842 bool event_pending; |
| 842 if (!device_->Poll(poll_device, &event_pending)) { | 843 if (!device_->Poll(poll_device, &event_pending)) { |
| 843 NOTIFY_ERROR(kPlatformFailureError); | 844 NOTIFY_ERROR(kPlatformFailureError); |
| 844 return; | 845 return; |
| 845 } | 846 } |
| 846 | 847 |
| 847 // All processing should happen on ServiceDeviceTask(), since we shouldn't | 848 // All processing should happen on ServiceDeviceTask(), since we shouldn't |
| 848 // touch encoder state from this thread. | 849 // touch encoder state from this thread. |
| 849 encoder_thread_.message_loop()->PostTask( | 850 encoder_thread_.message_loop()->PostTask( |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 884 NotifyError(error); | 885 NotifyError(error); |
| 885 | 886 |
| 886 encoder_state_ = kError; | 887 encoder_state_ = kError; |
| 887 } | 888 } |
| 888 | 889 |
| 889 void V4L2VideoEncodeAccelerator::RequestEncodingParametersChangeTask( | 890 void V4L2VideoEncodeAccelerator::RequestEncodingParametersChangeTask( |
| 890 uint32_t bitrate, | 891 uint32_t bitrate, |
| 891 uint32_t framerate) { | 892 uint32_t framerate) { |
| 892 DVLOG(3) << "RequestEncodingParametersChangeTask(): bitrate=" << bitrate | 893 DVLOG(3) << "RequestEncodingParametersChangeTask(): bitrate=" << bitrate |
| 893 << ", framerate=" << framerate; | 894 << ", framerate=" << framerate; |
| 894 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 895 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 895 | 896 |
| 896 if (bitrate < 1) | 897 if (bitrate < 1) |
| 897 bitrate = 1; | 898 bitrate = 1; |
| 898 if (framerate < 1) | 899 if (framerate < 1) |
| 899 framerate = 1; | 900 framerate = 1; |
| 900 | 901 |
| 901 std::vector<struct v4l2_ext_control> ctrls; | 902 std::vector<struct v4l2_ext_control> ctrls; |
| 902 struct v4l2_ext_control ctrl; | 903 struct v4l2_ext_control ctrl; |
| 903 memset(&ctrl, 0, sizeof(ctrl)); | 904 memset(&ctrl, 0, sizeof(ctrl)); |
| 904 ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE; | 905 ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE; |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1139 // Ignore return value as these controls are optional. | 1140 // Ignore return value as these controls are optional. |
| 1140 SetExtCtrls(ctrls); | 1141 SetExtCtrls(ctrls); |
| 1141 | 1142 |
| 1142 return true; | 1143 return true; |
| 1143 } | 1144 } |
| 1144 | 1145 |
| 1145 bool V4L2VideoEncodeAccelerator::CreateInputBuffers() { | 1146 bool V4L2VideoEncodeAccelerator::CreateInputBuffers() { |
| 1146 DVLOG(3) << "CreateInputBuffers()"; | 1147 DVLOG(3) << "CreateInputBuffers()"; |
| 1147 // This function runs on encoder_thread_ after output buffers have been | 1148 // This function runs on encoder_thread_ after output buffers have been |
| 1148 // provided by the client. | 1149 // provided by the client. |
| 1149 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current()); | 1150 DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 1150 DCHECK(!input_streamon_); | 1151 DCHECK(!input_streamon_); |
| 1151 | 1152 |
| 1152 struct v4l2_requestbuffers reqbufs; | 1153 struct v4l2_requestbuffers reqbufs; |
| 1153 memset(&reqbufs, 0, sizeof(reqbufs)); | 1154 memset(&reqbufs, 0, sizeof(reqbufs)); |
| 1154 // Driver will modify to the appropriate number of buffers. | 1155 // Driver will modify to the appropriate number of buffers. |
| 1155 reqbufs.count = 1; | 1156 reqbufs.count = 1; |
| 1156 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | 1157 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| 1157 // TODO(posciak): Once we start doing zero-copy, we should decide based on | 1158 // TODO(posciak): Once we start doing zero-copy, we should decide based on |
| 1158 // the current pipeline setup which memory type to use. This should probably | 1159 // the current pipeline setup which memory type to use. This should probably |
| 1159 // be decided based on an argument to Initialize(). | 1160 // be decided based on an argument to Initialize(). |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1247 reqbufs.count = 0; | 1248 reqbufs.count = 0; |
| 1248 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 1249 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| 1249 reqbufs.memory = V4L2_MEMORY_MMAP; | 1250 reqbufs.memory = V4L2_MEMORY_MMAP; |
| 1250 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); | 1251 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); |
| 1251 | 1252 |
| 1252 output_buffer_map_.clear(); | 1253 output_buffer_map_.clear(); |
| 1253 free_output_buffers_.clear(); | 1254 free_output_buffers_.clear(); |
| 1254 } | 1255 } |
| 1255 | 1256 |
| 1256 } // namespace media | 1257 } // namespace media |
| OLD | NEW |