Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(376)

Side by Side Diff: media/gpu/avda_codec_allocator.cc

Issue 2508053002: media: Do a TimedWait() for video surface teardown in AVDA (Closed)
Patch Set: change timeout to 2 Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/gpu/avda_codec_allocator.h ('k') | media/gpu/avda_codec_allocator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/avda_codec_allocator.h" 5 #include "media/gpu/avda_codec_allocator.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <memory> 9 #include <memory>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/sys_info.h" 12 #include "base/sys_info.h"
14 #include "base/task_runner_util.h" 13 #include "base/task_runner_util.h"
15 #include "base/threading/thread.h" 14 #include "base/threading/thread.h"
16 #include "base/threading/thread_checker.h" 15 #include "base/threading/thread_checker.h"
17 #include "base/threading/thread_task_runner_handle.h" 16 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/time/default_tick_clock.h" 17 #include "base/time/default_tick_clock.h"
19 #include "base/trace_event/trace_event.h" 18 #include "base/trace_event/trace_event.h"
19 #include "media/base/android/sdk_media_codec_bridge.h"
20 #include "media/base/limits.h" 20 #include "media/base/limits.h"
21 #include "media/base/media.h" 21 #include "media/base/media.h"
22 #include "media/base/timestamp_constants.h" 22 #include "media/base/timestamp_constants.h"
23 #include "media/gpu/android_video_decode_accelerator.h"
23 24
24 namespace media { 25 namespace media {
25 26
26 namespace { 27 namespace {
27 28
28 // Give tasks on the construction thread 800ms before considering them hung. 29 base::LazyInstance<AVDACodecAllocator>::Leaky g_avda_codec_allocator =
29 // MediaCodec.configure() calls typically take 100-200ms on a N5, so 800ms is 30 LAZY_INSTANCE_INITIALIZER;
30 // expected to very rarely result in false positives. Also, false positives have 31
31 // low impact because we resume using the thread if its apparently hung task 32 // Give tasks 800ms before considering them hung. MediaCodec.configure() calls
32 // completes. 33 // typically take 100-200ms on a N5, so 800ms is expected to very rarely result
34 // in false positives. Also, false positives have low impact because we resume
35 // using the thread when the task completes.
33 constexpr base::TimeDelta kHungTaskDetectionTimeout = 36 constexpr base::TimeDelta kHungTaskDetectionTimeout =
34 base::TimeDelta::FromMilliseconds(800); 37 base::TimeDelta::FromMilliseconds(800);
35 38
39 // Delete |codec| and signal |done_event| if it's not null.
40 void DeleteMediaCodecAndSignal(std::unique_ptr<VideoCodecBridge> codec,
41 base::WaitableEvent* done_event) {
42 codec.reset();
43 if (done_event)
44 done_event->Signal();
45 }
46
36 } // namespace 47 } // namespace
37 48
49 CodecConfig::CodecConfig() {}
50 CodecConfig::~CodecConfig() {}
51
38 AVDACodecAllocator::TestInformation::TestInformation() {} 52 AVDACodecAllocator::TestInformation::TestInformation() {}
39 AVDACodecAllocator::TestInformation::~TestInformation() {} 53 AVDACodecAllocator::TestInformation::~TestInformation() {}
40 54
41 AVDACodecAllocator::HangDetector::HangDetector(base::TickClock* tick_clock) 55 AVDACodecAllocator::HangDetector::HangDetector(base::TickClock* tick_clock)
42 : tick_clock_(tick_clock) {} 56 : tick_clock_(tick_clock) {}
43 57
44 void AVDACodecAllocator::HangDetector::WillProcessTask( 58 void AVDACodecAllocator::HangDetector::WillProcessTask(
45 const base::PendingTask& pending_task) { 59 const base::PendingTask& pending_task) {
46 base::AutoLock l(lock_); 60 base::AutoLock l(lock_);
47 task_start_time_ = tick_clock_->NowTicks(); 61 task_start_time_ = tick_clock_->NowTicks();
48 } 62 }
49 63
50 void AVDACodecAllocator::HangDetector::DidProcessTask( 64 void AVDACodecAllocator::HangDetector::DidProcessTask(
51 const base::PendingTask& pending_task) { 65 const base::PendingTask& pending_task) {
52 base::AutoLock l(lock_); 66 base::AutoLock l(lock_);
53 task_start_time_ = base::TimeTicks(); 67 task_start_time_ = base::TimeTicks();
54 } 68 }
55 69
56 bool AVDACodecAllocator::HangDetector::IsThreadLikelyHung() { 70 bool AVDACodecAllocator::HangDetector::IsThreadLikelyHung() {
57 base::AutoLock l(lock_); 71 base::AutoLock l(lock_);
58 if (task_start_time_.is_null()) 72 if (task_start_time_.is_null())
59 return false; 73 return false;
60 74
61 return (tick_clock_->NowTicks() - task_start_time_) > 75 return (tick_clock_->NowTicks() - task_start_time_) >
62 kHungTaskDetectionTimeout; 76 kHungTaskDetectionTimeout;
63 } 77 }
64 78
65 // Make sure the construction threads are started for |avda|. 79 // static
66 bool AVDACodecAllocator::StartThread(AndroidVideoDecodeAccelerator* avda) { 80 AVDACodecAllocator* AVDACodecAllocator::Instance() {
81 return g_avda_codec_allocator.Pointer();
82 }
83
84 // Make sure the construction threads are started for |client|.
85 bool AVDACodecAllocator::StartThread(AVDACodecAllocatorClient* client) {
67 DCHECK(thread_checker_.CalledOnValidThread()); 86 DCHECK(thread_checker_.CalledOnValidThread());
68 87
69 // Cancel any pending StopThreadTask()s because we need the threads now. 88 // Cancel any pending StopThreadTask()s because we need the threads now.
70 weak_this_factory_.InvalidateWeakPtrs(); 89 weak_this_factory_.InvalidateWeakPtrs();
71 90
72 // Try to start all threads if they haven't been started. Remember that 91 // Try to start all threads if they haven't been started. Remember that
73 // threads fail to start fairly often. 92 // threads fail to start fairly often.
74 for (size_t i = 0; i < threads_.size(); i++) { 93 for (size_t i = 0; i < threads_.size(); i++) {
75 if (threads_[i]->thread.IsRunning()) 94 if (threads_[i]->thread.IsRunning())
76 continue; 95 continue;
77 96
78 if (!threads_[i]->thread.Start()) 97 if (!threads_[i]->thread.Start())
79 continue; 98 continue;
80 99
81 // Register the hang detector to observe the thread's MessageLoop. 100 // Register the hang detector to observe the thread's MessageLoop.
82 threads_[i]->thread.task_runner()->PostTask( 101 threads_[i]->thread.task_runner()->PostTask(
83 FROM_HERE, 102 FROM_HERE,
84 base::Bind(&base::MessageLoop::AddTaskObserver, 103 base::Bind(&base::MessageLoop::AddTaskObserver,
85 base::Unretained(threads_[i]->thread.message_loop()), 104 base::Unretained(threads_[i]->thread.message_loop()),
86 &threads_[i]->hang_detector)); 105 &threads_[i]->hang_detector));
87 } 106 }
88 107
89 // Make sure that the construction thread started, else refuse to run. 108 // Make sure that the construction thread started, else refuse to run.
90 // If other threads fail to start, then we'll post to the GPU main thread for 109 // If other threads fail to start, then we'll post to the GPU main thread for
91 // those cases. SW allocation failures are much less rare, so this usually 110 // those cases. SW allocation failures are much less rare, so this usually
92 // just costs us the latency of doing the codec allocation on the main thread. 111 // just costs us the latency of doing the codec allocation on the main thread.
93 if (!threads_[TaskType::AUTO_CODEC]->thread.IsRunning()) 112 if (!threads_[TaskType::AUTO_CODEC]->thread.IsRunning())
94 return false; 113 return false;
95 114
96 thread_avda_instances_.insert(avda); 115 clients_.insert(client);
97 UMA_HISTOGRAM_ENUMERATION("Media.AVDA.NumAVDAInstances",
98 thread_avda_instances_.size(),
99 31); // PRESUBMIT_IGNORE_UMA_MAX
100 return true; 116 return true;
101 } 117 }
102 118
103 void AVDACodecAllocator::StopThread(AndroidVideoDecodeAccelerator* avda) { 119 void AVDACodecAllocator::StopThread(AVDACodecAllocatorClient* client) {
104 DCHECK(thread_checker_.CalledOnValidThread()); 120 DCHECK(thread_checker_.CalledOnValidThread());
105 121
106 thread_avda_instances_.erase(avda); 122 clients_.erase(client);
107 // Post a task to stop the thread through the thread's task runner and back 123 // Post a task to stop the thread through the thread's task runner and back
108 // to this thread. This ensures that all pending tasks are run first. If the 124 // to this thread. This ensures that all pending tasks are run first. If the
109 // thread is hung we don't post a task to avoid leaking an unbounded number 125 // thread is hung we don't post a task to avoid leaking an unbounded number
110 // of tasks on its queue. If the thread is not hung, but appears to be, it 126 // of tasks on its queue. If the thread is not hung, but appears to be, it
111 // will stay alive until next time an AVDA tries to stop it. We're 127 // will stay alive until next time an AVDA tries to stop it. We're
112 // guaranteed to not run StopThreadTask() when the thread is hung because if 128 // guaranteed to not run StopThreadTask() when the thread is hung because if
113 // an AVDA queues tasks after DoNothing(), the StopThreadTask() reply will 129 // an AVDA queues tasks after DoNothing(), the StopThreadTask() reply will
114 // be canceled by invalidating its weak pointer. 130 // be canceled by invalidating its weak pointer.
115 base::WaitableEvent* event = 131 base::WaitableEvent* event =
116 (test_info_ ? test_info_->stop_event_.get() : nullptr); 132 (test_info_ ? test_info_->stop_event_.get() : nullptr);
117 if (!thread_avda_instances_.empty()) { 133 if (!clients_.empty()) {
118 // If we aren't stopping, then signal immediately. 134 // If we aren't stopping, then signal immediately.
119 if (event) 135 if (event)
120 event->Signal(); 136 event->Signal();
121 return; 137 return;
122 } 138 }
123 139
124 for (size_t i = 0; i < threads_.size(); i++) { 140 for (size_t i = 0; i < threads_.size(); i++) {
125 if (threads_[i]->thread.IsRunning() && 141 if (threads_[i]->thread.IsRunning() &&
126 !threads_[i]->hang_detector.IsThreadLikelyHung()) { 142 !threads_[i]->hang_detector.IsThreadLikelyHung()) {
127 threads_[i]->thread.task_runner()->PostTaskAndReply( 143 threads_[i]->thread.task_runner()->PostTaskAndReply(
(...skipping 15 matching lines...) Expand all
143 // Fail over to the main thread if this thread failed to start. Note that 159 // Fail over to the main thread if this thread failed to start. Note that
144 // if the AUTO_CODEC thread fails to start, then AVDA init will fail. 160 // if the AUTO_CODEC thread fails to start, then AVDA init will fail.
145 // We won't fall back autodetection to the main thread, even without a 161 // We won't fall back autodetection to the main thread, even without a
146 // special case here. 162 // special case here.
147 if (!thread.IsRunning()) 163 if (!thread.IsRunning())
148 return base::ThreadTaskRunnerHandle::Get(); 164 return base::ThreadTaskRunnerHandle::Get();
149 165
150 return thread.task_runner(); 166 return thread.task_runner();
151 } 167 }
152 168
169 bool AVDACodecAllocator::AllocateSurface(AVDACodecAllocatorClient* client,
170 int surface_id) {
171 DVLOG(1) << __func__ << ": " << surface_id;
172 DCHECK(thread_checker_.CalledOnValidThread());
173
174 if (surface_id == SurfaceManager::kNoSurfaceID)
175 return true;
176
177 // If it's not owned or being released, |client| now owns it.
178 if (!surface_owners_.count(surface_id) &&
179 !pending_codec_releases_.count(surface_id)) {
180 surface_owners_[surface_id].owner = client;
181 return true;
182 }
183
184 // Otherwise |client| replaces the previous waiter (if any).
185 OwnerRecord& record = surface_owners_[surface_id];
186 if (record.waiter)
187 record.waiter->OnSurfaceAvailable(false);
188 record.waiter = client;
189 return false;
190 }
191
192 void AVDACodecAllocator::DeallocateSurface(AVDACodecAllocatorClient* client,
193 int surface_id) {
194 DCHECK(thread_checker_.CalledOnValidThread());
195 if (surface_id == SurfaceManager::kNoSurfaceID ||
196 !surface_owners_.count(surface_id)) {
197 return;
198 }
199
200 OwnerRecord& record = surface_owners_[surface_id];
201 if (record.owner == client)
202 record.owner = nullptr;
203 else if (record.waiter == client)
204 record.waiter = nullptr;
205
206 // Promote the waiter if possible.
207 if (record.waiter && !record.owner &&
208 !pending_codec_releases_.count(surface_id)) {
209 record.owner = record.waiter;
210 record.waiter = nullptr;
211 record.owner->OnSurfaceAvailable(true);
212 return;
213 }
214
215 // Remove the record if it's now unused.
216 if (!record.owner && !record.waiter)
217 surface_owners_.erase(surface_id);
218 }
219
220 // During surface teardown we have to handle the following cases.
221 // 1) No AVDA has acquired the surface, or the surface has already been
222 // completely released.
223 // 2) A MediaCodec is currently being configured with the surface on another
224 // thread. Whether an AVDA owns the surface or has already deallocated it,
225 // the MediaCodec should be dropped when configuration completes.
226 // 3) An AVDA owns the surface and it responds to OnSurfaceDestroyed() by:
227 // a) Replacing the destroyed surface by calling MediaCodec#setSurface().
228 // b) Releasing the MediaCodec it's attached to.
229 // 4) No AVDA owns the surface, but the MediaCodec it's attached to is currently
230 // being destroyed on another thread.
231 void AVDACodecAllocator::OnSurfaceDestroyed(int surface_id) {
232 DVLOG(1) << __func__ << ": " << surface_id;
233 DCHECK(thread_checker_.CalledOnValidThread());
234
235 // Notify the owner and waiter (if any).
236 if (surface_owners_.count(surface_id)) {
237 OwnerRecord& record = surface_owners_[surface_id];
238 if (record.waiter) {
239 record.waiter->OnSurfaceAvailable(false);
240 record.waiter = nullptr;
241 }
242
243 if (record.owner)
244 record.owner->OnSurfaceDestroyed();
245
246 surface_owners_.erase(surface_id);
247 }
248
249 // The codec might have been released above in OnSurfaceDestroyed(), or was
250 // already pending release.
251 if (!pending_codec_releases_.count(surface_id))
252 return;
253
254 // The codec is being released so we have to wait for it here. It's a
255 // TimedWait() because the MediaCodec release may hang due to framework bugs.
256 // And in that case we don't want to hang the browser UI thread. Android ANRs
257 // occur when the UI thread is blocked for 5 seconds, so waiting for 2 seconds
258 // gives us leeway to avoid an ANR. Verified no ANR on a Nexus 7.
259 base::WaitableEvent& released =
260 pending_codec_releases_.find(surface_id)->second;
261 released.TimedWait(base::TimeDelta::FromSeconds(2));
262 if (!released.IsSignaled())
263 DLOG(WARNING) << __func__ << ": timed out waiting for MediaCodec#release()";
264 }
265
266 std::unique_ptr<VideoCodecBridge> AVDACodecAllocator::CreateMediaCodecSync(
267 scoped_refptr<CodecConfig> codec_config) {
268 TRACE_EVENT0("media", "AVDA::CreateMediaCodecSync");
269
270 jobject media_crypto = codec_config->media_crypto_
271 ? codec_config->media_crypto_->obj()
272 : nullptr;
273
274 // |needs_protected_surface_| implies encrypted stream.
275 DCHECK(!codec_config->needs_protected_surface_ || media_crypto);
276
277 const bool require_software_codec =
278 codec_config->task_type_ == TaskType::SW_CODEC;
279
280 std::unique_ptr<VideoCodecBridge> codec(VideoCodecBridge::CreateDecoder(
281 codec_config->codec_, codec_config->needs_protected_surface_,
282 codec_config->initial_expected_coded_size_,
283 codec_config->surface_.j_surface().obj(), media_crypto,
284 codec_config->csd0_, codec_config->csd1_, true, require_software_codec));
285
286 return codec;
287 }
288
289 void AVDACodecAllocator::CreateMediaCodecAsync(
290 base::WeakPtr<AVDACodecAllocatorClient> client,
291 scoped_refptr<CodecConfig> codec_config) {
292 base::PostTaskAndReplyWithResult(
293 TaskRunnerFor(codec_config->task_type_).get(), FROM_HERE,
294 base::Bind(&AVDACodecAllocator::CreateMediaCodecSync,
295 base::Unretained(this), codec_config),
296 base::Bind(&AVDACodecAllocatorClient::OnCodecConfigured, client));
297 }
298
299 void AVDACodecAllocator::ReleaseMediaCodec(
300 std::unique_ptr<VideoCodecBridge> media_codec,
301 TaskType task_type,
302 int surface_id) {
303 DCHECK(thread_checker_.CalledOnValidThread());
304 DCHECK(media_codec);
305
306 // No need to track the release if it's a SurfaceTexture.
307 if (surface_id == SurfaceManager::kNoSurfaceID) {
308 TaskRunnerFor(task_type)->PostTask(
309 FROM_HERE, base::Bind(&DeleteMediaCodecAndSignal,
310 base::Passed(std::move(media_codec)), nullptr));
311 return;
312 }
313
314 pending_codec_releases_.emplace(
315 std::piecewise_construct, std::forward_as_tuple(surface_id),
316 std::forward_as_tuple(base::WaitableEvent::ResetPolicy::MANUAL,
317 base::WaitableEvent::InitialState::NOT_SIGNALED));
318 base::WaitableEvent* released =
319 &pending_codec_releases_.find(surface_id)->second;
320
321 // TODO(watk): Even if this is the current thread, things will work, but we
322 // should refactor this to not tolerate threads failing to start.
323 TaskRunnerFor(task_type)->PostTaskAndReply(
324 FROM_HERE, base::Bind(&DeleteMediaCodecAndSignal,
325 base::Passed(std::move(media_codec)), released),
326 base::Bind(&AVDACodecAllocator::OnMediaCodecAndSurfaceReleased,
327 base::Unretained(this), surface_id));
328 }
329
330 void AVDACodecAllocator::OnMediaCodecAndSurfaceReleased(int surface_id) {
331 DCHECK(thread_checker_.CalledOnValidThread());
332
333 pending_codec_releases_.erase(surface_id);
334 if (!surface_owners_.count(surface_id))
335 return;
336
337 OwnerRecord& record = surface_owners_[surface_id];
338 if (!record.owner && record.waiter) {
339 record.owner = record.waiter;
340 record.waiter = nullptr;
341 record.owner->OnSurfaceAvailable(true);
342 }
343 }
344
153 // Returns a hint about whether the construction thread has hung for 345 // Returns a hint about whether the construction thread has hung for
154 // |task_type|. Note that if a thread isn't started, then we'll just return 346 // |task_type|. Note that if a thread isn't started, then we'll just return
155 // "not hung", since it'll run on the current thread anyway. The hang 347 // "not hung", since it'll run on the current thread anyway. The hang
156 // detector will see no pending jobs in that case, so it's automatic. 348 // detector will see no pending jobs in that case, so it's automatic.
157 bool AVDACodecAllocator::IsThreadLikelyHung(TaskType task_type) { 349 bool AVDACodecAllocator::IsThreadLikelyHung(TaskType task_type) {
158 DCHECK(thread_checker_.CalledOnValidThread()); 350 DCHECK(thread_checker_.CalledOnValidThread());
159 return threads_[task_type]->hang_detector.IsThreadLikelyHung(); 351 return threads_[task_type]->hang_detector.IsThreadLikelyHung();
160 } 352 }
161 353
162 bool AVDACodecAllocator::IsAnyRegisteredAVDA() { 354 bool AVDACodecAllocator::IsAnyRegisteredAVDA() {
163 return !thread_avda_instances_.empty(); 355 return !clients_.empty();
164 } 356 }
165 357
166 AVDACodecAllocator::TaskType AVDACodecAllocator::TaskTypeForAllocation() { 358 TaskType AVDACodecAllocator::TaskTypeForAllocation() {
167 if (!IsThreadLikelyHung(TaskType::AUTO_CODEC)) 359 if (!IsThreadLikelyHung(TaskType::AUTO_CODEC))
168 return TaskType::AUTO_CODEC; 360 return TaskType::AUTO_CODEC;
169 361
170 if (!IsThreadLikelyHung(TaskType::SW_CODEC)) 362 if (!IsThreadLikelyHung(TaskType::SW_CODEC))
171 return TaskType::SW_CODEC; 363 return TaskType::SW_CODEC;
172 364
173 // If nothing is working, then we can't allocate anyway. 365 // If nothing is working, then we can't allocate anyway.
174 return TaskType::FAILED_CODEC; 366 return TaskType::FAILED_CODEC;
175 } 367 }
176 368
(...skipping 29 matching lines...) Expand all
206 } 398 }
207 399
208 void AVDACodecAllocator::StopThreadTask(size_t index, 400 void AVDACodecAllocator::StopThreadTask(size_t index,
209 base::WaitableEvent* event) { 401 base::WaitableEvent* event) {
210 threads_[index]->thread.Stop(); 402 threads_[index]->thread.Stop();
211 if (event) 403 if (event)
212 event->Signal(); 404 event->Signal();
213 } 405 }
214 406
215 } // namespace media 407 } // namespace media
OLDNEW
« no previous file with comments | « media/gpu/avda_codec_allocator.h ('k') | media/gpu/avda_codec_allocator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698