Chromium Code Reviews| 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_decode_accelerator.h" | 5 #include "media/gpu/v4l2_video_decode_accelerator.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <fcntl.h> | 9 #include <fcntl.h> |
| 10 #include <linux/videodev2.h> | 10 #include <linux/videodev2.h> |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 egl_image_format_fourcc_(0), | 179 egl_image_format_fourcc_(0), |
| 180 egl_image_planes_count_(0), | 180 egl_image_planes_count_(0), |
| 181 weak_this_factory_(this) { | 181 weak_this_factory_(this) { |
| 182 weak_this_ = weak_this_factory_.GetWeakPtr(); | 182 weak_this_ = weak_this_factory_.GetWeakPtr(); |
| 183 } | 183 } |
| 184 | 184 |
| 185 V4L2VideoDecodeAccelerator::~V4L2VideoDecodeAccelerator() { | 185 V4L2VideoDecodeAccelerator::~V4L2VideoDecodeAccelerator() { |
| 186 DCHECK(!decoder_thread_.IsRunning()); | 186 DCHECK(!decoder_thread_.IsRunning()); |
| 187 DCHECK(!device_poll_thread_.IsRunning()); | 187 DCHECK(!device_poll_thread_.IsRunning()); |
| 188 | 188 |
| 189 DestroyInputBuffers(); | |
| 190 DestroyOutputBuffers(); | |
| 191 | |
| 192 // These maps have members that should be manually destroyed, e.g. file | 189 // These maps have members that should be manually destroyed, e.g. file |
| 193 // descriptors, mmap() segments, etc. | 190 // descriptors, mmap() segments, etc. |
| 194 DCHECK(input_buffer_map_.empty()); | 191 DCHECK(input_buffer_map_.empty()); |
| 195 DCHECK(output_buffer_map_.empty()); | 192 DCHECK(output_buffer_map_.empty()); |
| 196 } | 193 } |
| 197 | 194 |
| 198 bool V4L2VideoDecodeAccelerator::Initialize(const Config& config, | 195 bool V4L2VideoDecodeAccelerator::Initialize(const Config& config, |
| 199 Client* client) { | 196 Client* client) { |
| 200 DVLOGF(3) << "profile: " << config.profile; | 197 DVLOGF(3) << "profile: " << config.profile; |
| 201 DCHECK(child_task_runner_->BelongsToCurrentThread()); | 198 DCHECK(child_task_runner_->BelongsToCurrentThread()); |
| (...skipping 1367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1569 decoder_frames_at_client_ = 0; | 1566 decoder_frames_at_client_ = 0; |
| 1570 while (!decoder_input_queue_.empty()) | 1567 while (!decoder_input_queue_.empty()) |
| 1571 decoder_input_queue_.pop(); | 1568 decoder_input_queue_.pop(); |
| 1572 decoder_flushing_ = false; | 1569 decoder_flushing_ = false; |
| 1573 | 1570 |
| 1574 if (image_processor_) | 1571 if (image_processor_) |
| 1575 image_processor_.release()->Destroy(); | 1572 image_processor_.release()->Destroy(); |
| 1576 | 1573 |
| 1577 // Set our state to kError. Just in case. | 1574 // Set our state to kError. Just in case. |
| 1578 decoder_state_ = kError; | 1575 decoder_state_ = kError; |
| 1576 | |
| 1577 DestroyInputBuffers(); | |
| 1578 DestroyOutputBuffers(); | |
| 1579 } | 1579 } |
| 1580 | 1580 |
| 1581 bool V4L2VideoDecodeAccelerator::StartDevicePoll() { | 1581 bool V4L2VideoDecodeAccelerator::StartDevicePoll() { |
| 1582 DVLOGF(3); | 1582 DVLOGF(3); |
| 1583 DCHECK(!device_poll_thread_.IsRunning()); | 1583 DCHECK(!device_poll_thread_.IsRunning()); |
| 1584 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 1584 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 1585 | 1585 |
| 1586 // Start up the device poll thread and schedule its first DevicePollTask(). | 1586 // Start up the device poll thread and schedule its first DevicePollTask(). |
| 1587 if (!device_poll_thread_.Start()) { | 1587 if (!device_poll_thread_.Start()) { |
| 1588 LOGF(ERROR) << "Device thread failed to start"; | 1588 LOGF(ERROR) << "Device thread failed to start"; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1682 decoder_state_ = kChangingResolution; | 1682 decoder_state_ = kChangingResolution; |
| 1683 | 1683 |
| 1684 if (!image_processor_bitstream_buffer_ids_.empty()) { | 1684 if (!image_processor_bitstream_buffer_ids_.empty()) { |
| 1685 DVLOGF(3) << "Wait image processor to finish before destroying buffers."; | 1685 DVLOGF(3) << "Wait image processor to finish before destroying buffers."; |
| 1686 return; | 1686 return; |
| 1687 } | 1687 } |
| 1688 | 1688 |
| 1689 if (image_processor_) | 1689 if (image_processor_) |
| 1690 image_processor_.release()->Destroy(); | 1690 image_processor_.release()->Destroy(); |
| 1691 | 1691 |
| 1692 // Post a task to clean up buffers on child thread. This will also ensure | 1692 if (!DestroyOutputBuffers()) { |
| 1693 // that we won't accept ReusePictureBuffer() anymore after that. | 1693 LOGF(ERROR) << "Failed destroying output buffers."; |
| 1694 child_task_runner_->PostTask( | 1694 NOTIFY_ERROR(PLATFORM_FAILURE); |
| 1695 FROM_HERE, | 1695 return; |
| 1696 base::Bind(&V4L2VideoDecodeAccelerator::ResolutionChangeDestroyBuffers, | 1696 } |
| 1697 weak_this_)); | 1697 |
| 1698 FinishResolutionChange(); | |
| 1698 } | 1699 } |
| 1699 | 1700 |
| 1700 void V4L2VideoDecodeAccelerator::FinishResolutionChange() { | 1701 void V4L2VideoDecodeAccelerator::FinishResolutionChange() { |
| 1701 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 1702 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 1702 DCHECK_EQ(decoder_state_, kChangingResolution); | 1703 DCHECK_EQ(decoder_state_, kChangingResolution); |
| 1703 DVLOGF(3); | 1704 DVLOGF(3); |
| 1704 | 1705 |
| 1705 if (decoder_state_ == kError) { | 1706 if (decoder_state_ == kError) { |
| 1706 DVLOGF(2) << "early out: kError state"; | 1707 DVLOGF(2) << "early out: kError state"; |
| 1707 return; | 1708 return; |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2117 // done with this one. After we get the buffers, we'll go back into kIdle and | 2118 // done with this one. After we get the buffers, we'll go back into kIdle and |
| 2118 // kick off further event processing, and eventually go back into kDecoding | 2119 // kick off further event processing, and eventually go back into kDecoding |
| 2119 // once no more events are pending (if any). | 2120 // once no more events are pending (if any). |
| 2120 decoder_state_ = kAwaitingPictureBuffers; | 2121 decoder_state_ = kAwaitingPictureBuffers; |
| 2121 | 2122 |
| 2122 return true; | 2123 return true; |
| 2123 } | 2124 } |
| 2124 | 2125 |
| 2125 void V4L2VideoDecodeAccelerator::DestroyInputBuffers() { | 2126 void V4L2VideoDecodeAccelerator::DestroyInputBuffers() { |
| 2126 DVLOGF(3); | 2127 DVLOGF(3); |
| 2127 DCHECK(child_task_runner_->BelongsToCurrentThread()); | 2128 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 2128 DCHECK(!input_streamon_); | 2129 DCHECK(!input_streamon_); |
| 2129 | 2130 |
| 2130 for (size_t i = 0; i < input_buffer_map_.size(); ++i) { | 2131 for (size_t i = 0; i < input_buffer_map_.size(); ++i) { |
| 2131 if (input_buffer_map_[i].address != NULL) { | 2132 if (input_buffer_map_[i].address != NULL) { |
| 2132 device_->Munmap(input_buffer_map_[i].address, | 2133 device_->Munmap(input_buffer_map_[i].address, |
| 2133 input_buffer_map_[i].length); | 2134 input_buffer_map_[i].length); |
| 2134 } | 2135 } |
| 2135 } | 2136 } |
| 2136 | 2137 |
| 2137 struct v4l2_requestbuffers reqbufs; | 2138 struct v4l2_requestbuffers reqbufs; |
| 2138 memset(&reqbufs, 0, sizeof(reqbufs)); | 2139 memset(&reqbufs, 0, sizeof(reqbufs)); |
| 2139 reqbufs.count = 0; | 2140 reqbufs.count = 0; |
| 2140 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | 2141 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| 2141 reqbufs.memory = V4L2_MEMORY_MMAP; | 2142 reqbufs.memory = V4L2_MEMORY_MMAP; |
| 2142 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); | 2143 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); |
| 2143 | 2144 |
| 2144 input_buffer_map_.clear(); | 2145 input_buffer_map_.clear(); |
| 2145 free_input_buffers_.clear(); | 2146 free_input_buffers_.clear(); |
| 2146 } | 2147 } |
| 2147 | 2148 |
| 2148 bool V4L2VideoDecodeAccelerator::DestroyOutputBuffers() { | 2149 bool V4L2VideoDecodeAccelerator::DestroyOutputBuffers() { |
| 2149 DVLOGF(3); | 2150 DVLOGF(3); |
| 2150 DCHECK(child_task_runner_->BelongsToCurrentThread()); | 2151 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 2151 DCHECK(!output_streamon_); | 2152 DCHECK(!output_streamon_); |
| 2152 bool success = true; | 2153 bool success = true; |
| 2153 | 2154 |
| 2154 for (size_t i = 0; i < output_buffer_map_.size(); ++i) { | 2155 for (size_t i = 0; i < output_buffer_map_.size(); ++i) { |
| 2155 OutputRecord& output_record = output_buffer_map_[i]; | 2156 OutputRecord& output_record = output_buffer_map_[i]; |
| 2156 | 2157 |
| 2157 if (output_record.egl_image != EGL_NO_IMAGE_KHR) { | 2158 if (output_record.egl_image != EGL_NO_IMAGE_KHR) { |
| 2158 if (egl_image_device_->DestroyEGLImage( | 2159 child_task_runner_->PostTask( |
| 2159 egl_display_, output_record.egl_image) != EGL_TRUE) { | 2160 FROM_HERE, |
| 2160 DVLOGF(1) << "DestroyEGLImage failed."; | 2161 base::Bind(base::IgnoreResult(&V4L2Device::DestroyEGLImage), device_, |
|
kcwu
2016/09/12 10:59:48
Add log if destroy failed at least.
wuchengli
2016/09/12 11:15:36
V4L2SVDA has three base::IgnoreResult(&V4L2Device:
| |
| 2161 success = false; | 2162 egl_display_, output_record.egl_image)); |
| 2162 } | |
| 2163 } | 2163 } |
| 2164 | 2164 |
| 2165 if (output_record.egl_sync != EGL_NO_SYNC_KHR) { | 2165 if (output_record.egl_sync != EGL_NO_SYNC_KHR) { |
| 2166 if (eglDestroySyncKHR(egl_display_, output_record.egl_sync) != EGL_TRUE) { | 2166 if (eglDestroySyncKHR(egl_display_, output_record.egl_sync) != EGL_TRUE) { |
|
kcwu
2016/09/12 10:59:48
Is it okay to call eglDestroySyncKHR on non-child
wuchengli
2016/09/12 11:15:36
Yes. It's OK. Also see line 1296. eglDestroySyncKH
| |
| 2167 DVLOGF(1) << "eglDestroySyncKHR failed."; | 2167 DVLOGF(1) << "eglDestroySyncKHR failed."; |
| 2168 success = false; | 2168 success = false; |
| 2169 } | 2169 } |
| 2170 } | 2170 } |
| 2171 | 2171 |
| 2172 DVLOGF(1) << "dismissing PictureBuffer id=" << output_record.picture_id; | 2172 DVLOGF(1) << "dismissing PictureBuffer id=" << output_record.picture_id; |
| 2173 child_task_runner_->PostTask( | 2173 child_task_runner_->PostTask( |
| 2174 FROM_HERE, base::Bind(&Client::DismissPictureBuffer, client_, | 2174 FROM_HERE, base::Bind(&Client::DismissPictureBuffer, client_, |
| 2175 output_record.picture_id)); | 2175 output_record.picture_id)); |
| 2176 } | 2176 } |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 2189 while (!free_output_buffers_.empty()) | 2189 while (!free_output_buffers_.empty()) |
| 2190 free_output_buffers_.pop(); | 2190 free_output_buffers_.pop(); |
| 2191 output_buffer_queued_count_ = 0; | 2191 output_buffer_queued_count_ = 0; |
| 2192 // The client may still hold some buffers. The texture holds a reference to | 2192 // The client may still hold some buffers. The texture holds a reference to |
| 2193 // the buffer. It is OK to free the buffer and destroy EGLImage here. | 2193 // the buffer. It is OK to free the buffer and destroy EGLImage here. |
| 2194 decoder_frames_at_client_ = 0; | 2194 decoder_frames_at_client_ = 0; |
| 2195 | 2195 |
| 2196 return success; | 2196 return success; |
| 2197 } | 2197 } |
| 2198 | 2198 |
| 2199 void V4L2VideoDecodeAccelerator::ResolutionChangeDestroyBuffers() { | |
| 2200 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
| 2201 DVLOGF(3); | |
| 2202 | |
| 2203 if (!DestroyOutputBuffers()) { | |
| 2204 LOGF(ERROR) << "Failed destroying output buffers."; | |
| 2205 NOTIFY_ERROR(PLATFORM_FAILURE); | |
| 2206 return; | |
| 2207 } | |
| 2208 | |
| 2209 // Finish resolution change on decoder thread. | |
| 2210 decoder_thread_.task_runner()->PostTask( | |
| 2211 FROM_HERE, base::Bind(&V4L2VideoDecodeAccelerator::FinishResolutionChange, | |
| 2212 base::Unretained(this))); | |
| 2213 } | |
| 2214 | |
| 2215 void V4L2VideoDecodeAccelerator::SendPictureReady() { | 2199 void V4L2VideoDecodeAccelerator::SendPictureReady() { |
| 2216 DVLOGF(3); | 2200 DVLOGF(3); |
| 2217 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 2201 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 2218 bool resetting_or_flushing = | 2202 bool resetting_or_flushing = |
| 2219 (decoder_state_ == kResetting || decoder_flushing_); | 2203 (decoder_state_ == kResetting || decoder_flushing_); |
| 2220 while (pending_picture_ready_.size() > 0) { | 2204 while (pending_picture_ready_.size() > 0) { |
| 2221 bool cleared = pending_picture_ready_.front().cleared; | 2205 bool cleared = pending_picture_ready_.front().cleared; |
| 2222 const Picture& picture = pending_picture_ready_.front().picture; | 2206 const Picture& picture = pending_picture_ready_.front().picture; |
| 2223 if (cleared && picture_clearing_count_ == 0) { | 2207 if (cleared && picture_clearing_count_ == 0) { |
| 2224 // This picture is cleared. It can be posted to a thread different than | 2208 // This picture is cleared. It can be posted to a thread different than |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2294 StartResolutionChange(); | 2278 StartResolutionChange(); |
| 2295 } | 2279 } |
| 2296 } | 2280 } |
| 2297 | 2281 |
| 2298 void V4L2VideoDecodeAccelerator::ImageProcessorError() { | 2282 void V4L2VideoDecodeAccelerator::ImageProcessorError() { |
| 2299 LOGF(ERROR) << "Image processor error"; | 2283 LOGF(ERROR) << "Image processor error"; |
| 2300 NOTIFY_ERROR(PLATFORM_FAILURE); | 2284 NOTIFY_ERROR(PLATFORM_FAILURE); |
| 2301 } | 2285 } |
| 2302 | 2286 |
| 2303 } // namespace media | 2287 } // namespace media |
| OLD | NEW |