| 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 <fcntl.h> | 5 #include <fcntl.h> |
| 6 #include <linux/videodev2.h> | 6 #include <linux/videodev2.h> |
| 7 #include <poll.h> | 7 #include <poll.h> |
| 8 #include <sys/eventfd.h> | 8 #include <sys/eventfd.h> |
| 9 #include <sys/ioctl.h> | 9 #include <sys/ioctl.h> |
| 10 #include <sys/mman.h> | 10 #include <sys/mman.h> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
| 14 #include "base/callback.h" | 14 #include "base/callback.h" |
| 15 #include "base/message_loop/message_loop_proxy.h" | 15 #include "base/message_loop/message_loop_proxy.h" |
| 16 #include "base/numerics/safe_conversions.h" | 16 #include "base/numerics/safe_conversions.h" |
| 17 #include "content/common/gpu/media/v4l2_image_processor.h" | 17 #include "content/common/gpu/media/v4l2_image_processor.h" |
| 18 #include "media/base/bind_to_current_loop.h" | 18 #include "media/base/bind_to_current_loop.h" |
| 19 | 19 |
| 20 #define NOTIFY_ERROR() \ | 20 #define NOTIFY_ERROR() \ |
| 21 do { \ | 21 do { \ |
| 22 DLOG(ERROR) << "calling NotifyError()"; \ | 22 LOG(ERROR) << "calling NotifyError()"; \ |
| 23 NotifyError(); \ | 23 NotifyError(); \ |
| 24 } while (0) | 24 } while (0) |
| 25 | 25 |
| 26 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value) \ | 26 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value) \ |
| 27 do { \ | 27 do { \ |
| 28 if (device_->Ioctl(type, arg) != 0) { \ | 28 if (device_->Ioctl(type, arg) != 0) { \ |
| 29 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | 29 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ |
| 30 return value; \ | 30 return value; \ |
| 31 } \ | 31 } \ |
| 32 } while (0) | 32 } while (0) |
| 33 | 33 |
| 34 #define IOCTL_OR_ERROR_RETURN(type, arg) \ | 34 #define IOCTL_OR_ERROR_RETURN(type, arg) \ |
| 35 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0)) | 35 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0)) |
| 36 | 36 |
| 37 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \ | 37 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \ |
| 38 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false) | 38 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false) |
| 39 | 39 |
| 40 #define IOCTL_OR_LOG_ERROR(type, arg) \ | 40 #define IOCTL_OR_LOG_ERROR(type, arg) \ |
| 41 do { \ | 41 do { \ |
| 42 if (device_->Ioctl(type, arg) != 0) \ | 42 if (device_->Ioctl(type, arg) != 0) \ |
| 43 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | 43 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ |
| 44 } while (0) | 44 } while (0) |
| 45 | 45 |
| 46 namespace content { | 46 namespace content { |
| 47 | 47 |
| 48 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) { | 48 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) { |
| 49 } | 49 } |
| 50 | 50 |
| 51 V4L2ImageProcessor::OutputRecord::OutputRecord() | 51 V4L2ImageProcessor::OutputRecord::OutputRecord() |
| 52 : at_device(false), at_client(false) { | 52 : at_device(false), at_client(false) { |
| 53 } | 53 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 DCHECK_EQ(input_format, media::VideoFrame::I420); | 103 DCHECK_EQ(input_format, media::VideoFrame::I420); |
| 104 DCHECK_EQ(output_format, media::VideoFrame::NV12); | 104 DCHECK_EQ(output_format, media::VideoFrame::NV12); |
| 105 | 105 |
| 106 input_format_ = input_format; | 106 input_format_ = input_format; |
| 107 output_format_ = output_format; | 107 output_format_ = output_format; |
| 108 input_format_fourcc_ = V4L2Device::VideoFrameFormatToV4L2PixFmt(input_format); | 108 input_format_fourcc_ = V4L2Device::VideoFrameFormatToV4L2PixFmt(input_format); |
| 109 output_format_fourcc_ = | 109 output_format_fourcc_ = |
| 110 V4L2Device::VideoFrameFormatToV4L2PixFmt(output_format); | 110 V4L2Device::VideoFrameFormatToV4L2PixFmt(output_format); |
| 111 | 111 |
| 112 if (!input_format_fourcc_ || !output_format_fourcc_) { | 112 if (!input_format_fourcc_ || !output_format_fourcc_) { |
| 113 DLOG(ERROR) << "Unrecognized format(s)"; | 113 LOG(ERROR) << "Unrecognized format(s)"; |
| 114 return false; | 114 return false; |
| 115 } | 115 } |
| 116 | 116 |
| 117 input_visible_size_ = input_visible_size; | 117 input_visible_size_ = input_visible_size; |
| 118 output_visible_size_ = output_visible_size; | 118 output_visible_size_ = output_visible_size; |
| 119 output_allocated_size_ = output_allocated_size; | 119 output_allocated_size_ = output_allocated_size; |
| 120 | 120 |
| 121 input_planes_count_ = media::VideoFrame::NumPlanes(input_format); | 121 input_planes_count_ = media::VideoFrame::NumPlanes(input_format); |
| 122 DCHECK_LE(input_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); | 122 DCHECK_LE(input_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); |
| 123 output_planes_count_ = media::VideoFrame::NumPlanes(output_format); | 123 output_planes_count_ = media::VideoFrame::NumPlanes(output_format); |
| 124 DCHECK_LE(output_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); | 124 DCHECK_LE(output_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); |
| 125 | 125 |
| 126 // Capabilities check. | 126 // Capabilities check. |
| 127 struct v4l2_capability caps; | 127 struct v4l2_capability caps; |
| 128 memset(&caps, 0, sizeof(caps)); | 128 memset(&caps, 0, sizeof(caps)); |
| 129 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE | | 129 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE | |
| 130 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; | 130 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; |
| 131 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps); | 131 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps); |
| 132 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { | 132 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { |
| 133 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " | 133 LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " |
| 134 "caps check failed: 0x" << std::hex << caps.capabilities; | 134 "caps check failed: 0x" << std::hex << caps.capabilities; |
| 135 return false; | 135 return false; |
| 136 } | 136 } |
| 137 | 137 |
| 138 if (!CreateInputBuffers() || !CreateOutputBuffers()) | 138 if (!CreateInputBuffers() || !CreateOutputBuffers()) |
| 139 return false; | 139 return false; |
| 140 | 140 |
| 141 if (!device_thread_.Start()) { | 141 if (!device_thread_.Start()) { |
| 142 DLOG(ERROR) << "Initialize(): encoder thread failed to start"; | 142 LOG(ERROR) << "Initialize(): encoder thread failed to start"; |
| 143 return false; | 143 return false; |
| 144 } | 144 } |
| 145 | 145 |
| 146 // StartDevicePoll will NOTIFY_ERROR on failure, so IgnoreResult is fine here. | 146 // StartDevicePoll will NOTIFY_ERROR on failure, so IgnoreResult is fine here. |
| 147 device_thread_.message_loop()->PostTask( | 147 device_thread_.message_loop()->PostTask( |
| 148 FROM_HERE, | 148 FROM_HERE, |
| 149 base::Bind(base::IgnoreResult(&V4L2ImageProcessor::StartDevicePoll), | 149 base::Bind(base::IgnoreResult(&V4L2ImageProcessor::StartDevicePoll), |
| 150 base::Unretained(this))); | 150 base::Unretained(this))); |
| 151 | 151 |
| 152 DVLOG(1) << "V4L2ImageProcessor initialized for " | 152 DVLOG(1) << "V4L2ImageProcessor initialized for " |
| (...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 496 memset(&planes, 0, sizeof(planes)); | 496 memset(&planes, 0, sizeof(planes)); |
| 497 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | 497 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| 498 dqbuf.memory = V4L2_MEMORY_USERPTR; | 498 dqbuf.memory = V4L2_MEMORY_USERPTR; |
| 499 dqbuf.m.planes = planes; | 499 dqbuf.m.planes = planes; |
| 500 dqbuf.length = input_planes_count_; | 500 dqbuf.length = input_planes_count_; |
| 501 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { | 501 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { |
| 502 if (errno == EAGAIN) { | 502 if (errno == EAGAIN) { |
| 503 // EAGAIN if we're just out of buffers to dequeue. | 503 // EAGAIN if we're just out of buffers to dequeue. |
| 504 break; | 504 break; |
| 505 } | 505 } |
| 506 DPLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; | 506 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; |
| 507 NOTIFY_ERROR(); | 507 NOTIFY_ERROR(); |
| 508 return; | 508 return; |
| 509 } | 509 } |
| 510 InputRecord& input_record = input_buffer_map_[dqbuf.index]; | 510 InputRecord& input_record = input_buffer_map_[dqbuf.index]; |
| 511 DCHECK(input_record.at_device); | 511 DCHECK(input_record.at_device); |
| 512 input_record.at_device = false; | 512 input_record.at_device = false; |
| 513 input_record.frame = NULL; | 513 input_record.frame = NULL; |
| 514 free_input_buffers_.push_back(dqbuf.index); | 514 free_input_buffers_.push_back(dqbuf.index); |
| 515 input_buffer_queued_count_--; | 515 input_buffer_queued_count_--; |
| 516 } | 516 } |
| 517 | 517 |
| 518 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list. | 518 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list. |
| 519 // Return the finished buffer to the client via the job ready callback. | 519 // Return the finished buffer to the client via the job ready callback. |
| 520 while (output_buffer_queued_count_ > 0) { | 520 while (output_buffer_queued_count_ > 0) { |
| 521 DCHECK(output_streamon_); | 521 DCHECK(output_streamon_); |
| 522 memset(&dqbuf, 0, sizeof(dqbuf)); | 522 memset(&dqbuf, 0, sizeof(dqbuf)); |
| 523 memset(&planes, 0, sizeof(planes)); | 523 memset(&planes, 0, sizeof(planes)); |
| 524 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | 524 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| 525 dqbuf.memory = V4L2_MEMORY_DMABUF; | 525 dqbuf.memory = V4L2_MEMORY_DMABUF; |
| 526 dqbuf.m.planes = planes; | 526 dqbuf.m.planes = planes; |
| 527 dqbuf.length = output_planes_count_; | 527 dqbuf.length = output_planes_count_; |
| 528 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { | 528 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { |
| 529 if (errno == EAGAIN) { | 529 if (errno == EAGAIN) { |
| 530 // EAGAIN if we're just out of buffers to dequeue. | 530 // EAGAIN if we're just out of buffers to dequeue. |
| 531 break; | 531 break; |
| 532 } | 532 } |
| 533 DPLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; | 533 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; |
| 534 NOTIFY_ERROR(); | 534 NOTIFY_ERROR(); |
| 535 return; | 535 return; |
| 536 } | 536 } |
| 537 OutputRecord& output_record = output_buffer_map_[dqbuf.index]; | 537 OutputRecord& output_record = output_buffer_map_[dqbuf.index]; |
| 538 DCHECK(output_record.at_device); | 538 DCHECK(output_record.at_device); |
| 539 output_record.at_device = false; | 539 output_record.at_device = false; |
| 540 output_record.at_client = true; | 540 output_record.at_client = true; |
| 541 output_buffer_queued_count_--; | 541 output_buffer_queued_count_--; |
| 542 | 542 |
| 543 // Jobs are always processed in FIFO order. | 543 // Jobs are always processed in FIFO order. |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 return true; | 641 return true; |
| 642 } | 642 } |
| 643 | 643 |
| 644 bool V4L2ImageProcessor::StartDevicePoll() { | 644 bool V4L2ImageProcessor::StartDevicePoll() { |
| 645 DVLOG(3) << __func__ << ": starting device poll"; | 645 DVLOG(3) << __func__ << ": starting device poll"; |
| 646 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | 646 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); |
| 647 DCHECK(!device_poll_thread_.IsRunning()); | 647 DCHECK(!device_poll_thread_.IsRunning()); |
| 648 | 648 |
| 649 // Start up the device poll thread and schedule its first DevicePollTask(). | 649 // Start up the device poll thread and schedule its first DevicePollTask(). |
| 650 if (!device_poll_thread_.Start()) { | 650 if (!device_poll_thread_.Start()) { |
| 651 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; | 651 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; |
| 652 NOTIFY_ERROR(); | 652 NOTIFY_ERROR(); |
| 653 return false; | 653 return false; |
| 654 } | 654 } |
| 655 // Enqueue a poll task with no devices to poll on - will wait only for the | 655 // Enqueue a poll task with no devices to poll on - will wait only for the |
| 656 // poll interrupt | 656 // poll interrupt |
| 657 device_poll_thread_.message_loop()->PostTask( | 657 device_poll_thread_.message_loop()->PostTask( |
| 658 FROM_HERE, | 658 FROM_HERE, |
| 659 base::Bind( | 659 base::Bind( |
| 660 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false)); | 660 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false)); |
| 661 | 661 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 output_record.at_device = false; | 710 output_record.at_device = false; |
| 711 if (!output_record.at_client) | 711 if (!output_record.at_client) |
| 712 free_output_buffers_.push_back(i); | 712 free_output_buffers_.push_back(i); |
| 713 } | 713 } |
| 714 output_buffer_queued_count_ = 0; | 714 output_buffer_queued_count_ = 0; |
| 715 | 715 |
| 716 return true; | 716 return true; |
| 717 } | 717 } |
| 718 | 718 |
| 719 } // namespace content | 719 } // namespace content |
| OLD | NEW |