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