Chromium Code Reviews| Index: media/gpu/android_video_decode_accelerator.cc |
| diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc |
| index c81728d5969afde50339a696739e33983d062a55..db33847ff5eb26451b6d5b7030fefe5ff7e72aa3 100644 |
| --- a/media/gpu/android_video_decode_accelerator.cc |
| +++ b/media/gpu/android_video_decode_accelerator.cc |
| @@ -115,6 +115,14 @@ enum FormatChangedValue { |
| MissingFormatChanged = true |
| }; |
| +// Maximum number of concurrent, incomplete codec creations that we'll allow |
| +// before turning off autodection of codec type. |
| +enum { kMaxOutstandingCodecAutodetections = 4 }; |
| + |
| +static inline const base::TimeDelta CodecConstructionTimeout() { |
| + return base::TimeDelta::FromSeconds(1); |
| +} |
| + |
| static inline void RecordFormatChangedMetric(FormatChangedValue value) { |
| UMA_HISTOGRAM_BOOLEAN("Media.AVDA.MissingFormatChanged", !!value); |
| } |
| @@ -185,7 +193,10 @@ class AVDATimerManager { |
| bool StartThread(AndroidVideoDecodeAccelerator* avda_instance) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| - if (thread_avda_instances_.empty()) { |
| + // The thread might already be started even if there are no avda instances, |
|
DaleCurtis
2016/06/21 21:17:56
remove ,
liberato (no reviews please)
2016/06/22 17:56:13
i restructured the sentence a litt.e
|
| + // if we chose not to shut it down due to pending codec constructions. |
| + // Plus, sometimes we just fail to start the thread. |
| + if (!construction_thread_.IsRunning()) { |
| if (!construction_thread_.Start()) { |
| LOG(ERROR) << "Failed to start construction thread."; |
| return false; |
| @@ -202,8 +213,17 @@ class AVDATimerManager { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| thread_avda_instances_.erase(avda_instance); |
| - if (thread_avda_instances_.empty()) |
| - construction_thread_.Stop(); |
| + if (!thread_avda_instances_.empty()) |
| + return; |
| + |
| + // Don't stop the thread if there are outstanding requests, since they |
| + // might be hung. They also might simply be incomplete, and the thread |
| + // will stay running until we try to shut it down again. |
| + base::AutoLock _l(autodetection_info_.lock_); |
| + if (autodetection_info_.outstanding_) |
| + return; |
| + |
| + construction_thread_.Stop(); |
| } |
| // Request periodic callback of |avda_instance|->DoIOTask(). Does nothing if |
| @@ -249,6 +269,60 @@ class AVDATimerManager { |
| return construction_thread_.task_runner(); |
| } |
| + scoped_refptr<base::SingleThreadTaskRunner> BackupTaskRunner() { |
|
DaleCurtis
2016/06/21 21:17:56
Instead of this, should we just stop using async c
liberato (no reviews please)
2016/06/22 17:56:13
Done, except the UMA stat. i'm still trying to fi
|
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + // Try to start the backup thread if needed. Note that we never bother to |
| + // stop this once it starts. |
| + if (!backup_thread_.IsRunning()) |
|
DaleCurtis
2016/06/21 21:17:56
Collapse to ternary if not deleting.
liberato (no reviews please)
2016/06/22 17:56:13
Done.
|
| + if (!backup_thread_.Start()) |
| + return nullptr; |
| + return backup_thread_.task_runner(); |
| + } |
| + |
| + // Called on the main thread when codec autodetection starts. There may be |
|
DaleCurtis
2016/06/21 21:17:56
I think this is a bit more complicated than necess
liberato (no reviews please)
2016/06/22 17:56:13
keeping codec construction off the main thread is
|
| + // several calls to this before any call to OnAnyThread. |
| + void OnAsyncCodecAutodetectionStarted() { |
| + base::AutoLock _l(autodetection_info_.lock_); |
| + if (!autodetection_info_.outstanding_) |
| + autodetection_info_.current_start_ = base::TimeTicks::Now(); |
| + ++autodetection_info_.outstanding_; |
| + } |
| + |
| + // Called on any thread when a codec is constructed with autodetection. This |
| + // assumes that requests are ordered, so please don't mix sync and async codec |
| + // construction here. This may be called on any thread. |
| + void OnAsyncCodecAutodetectionComplete() { |
| + base::AutoLock _l(autodetection_info_.lock_); |
| + DCHECK(autodetection_info_.outstanding_ > 0); |
| + --autodetection_info_.outstanding_; |
| + |
| + // The next request, if any, will start approximately now. |
| + autodetection_info_.current_start_ = base::TimeTicks::Now(); |
| + } |
| + |
| + // Return a hint about whether autodetecting the codec type is safe or not. |
| + bool IsCodecAutodetectionProbablySafe() { |
| + base::AutoLock _l(autodetection_info_.lock_); |
| + |
| + // If there are no outstanding autodetections, then assume that it's okay |
| + // to try one. |
| + if (autodetection_info_.outstanding_ == 0) |
| + return true; |
| + |
| + // Hard-limit the number of outstanding autodetections, to try to prevent |
| + // falling back to software on an autodetect. This seems to be when |
| + // things start to cause problems. While this doesn't limit the number of |
| + // hardware decoders that are in use concurrently, it might help during |
| + // initial page load if there are a lot of tags. |
| + if (autodetection_info_.outstanding_ >= kMaxOutstandingCodecAutodetections) |
| + return false; |
| + |
| + // Make sure that we've made progress recently. |
| + base::TimeTicks now = base::TimeTicks::Now(); |
| + return (now - autodetection_info_.current_start_) < |
| + CodecConstructionTimeout(); |
| + } |
| + |
| // |avda| would like to use |surface_id|. If it is not busy, then mark it |
| // as busy and return true. If it is busy, then replace any existing waiter, |
| // make |avda| the current waiter, and return false. Any existing waiter |
| @@ -310,7 +384,9 @@ class AVDATimerManager { |
| private: |
| friend struct base::DefaultLazyInstanceTraits<AVDATimerManager>; |
| - AVDATimerManager() : construction_thread_("AVDAThread") {} |
| + AVDATimerManager() |
| + : construction_thread_("AVDAThread"), |
| + backup_thread_("AVDABackupThread") {} |
| ~AVDATimerManager() { NOTREACHED(); } |
| void RunTimer() { |
| @@ -354,7 +430,20 @@ class AVDATimerManager { |
| // Repeating timer responsible for draining pending IO to the codecs. |
| base::RepeatingTimer io_timer_; |
| + // Data for determining if codec creation is hanging. |
| + struct { |
| + // Lock that protects other members of this struct. |
| + base::Lock lock_; |
| + |
| + // Number of currently pending autodetection requests. |
| + int outstanding_ = 0; |
| + |
| + // Time at which the most recent autodetection started. |
| + base::TimeTicks current_start_; |
| + } autodetection_info_; |
| + |
| base::Thread construction_thread_; |
| + base::Thread backup_thread_; |
| base::ThreadChecker thread_checker_; |
| @@ -1047,9 +1136,24 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { |
| strategy_->CodecChanged(nullptr); |
| } |
| + // Choose whether to autodetect the codec type. |
| + codec_config_->allow_autodetection_ = |
| + g_avda_timer.Pointer()->IsCodecAutodetectionProbablySafe(); |
| + codec_config_->notify_completion_ = codec_config_->allow_autodetection_; |
| + if (codec_config_->allow_autodetection_) |
| + g_avda_timer.Pointer()->OnAsyncCodecAutodetectionStarted(); |
| + |
| + // If we're not trying autodetection, then also use the backup thread. The |
| + // main one might be blocked. |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
| - g_avda_timer.Pointer()->ConstructionTaskRunner(); |
| - CHECK(task_runner); |
| + codec_config_->allow_autodetection_ |
| + ? g_avda_timer.Pointer()->ConstructionTaskRunner() |
| + : g_avda_timer.Pointer()->BackupTaskRunner(); |
| + if (!task_runner) { |
| + // If we don't have a thread, then just fail. |
| + OnCodecConfigured(nullptr); |
| + return; |
| + } |
| base::PostTaskAndReplyWithResult( |
| task_runner.get(), FROM_HERE, |
| @@ -1061,6 +1165,15 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { |
| bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { |
| state_ = WAITING_FOR_CODEC; |
| + |
| + // Decide whether to allow autodetection or not. Since we're on the main |
| + // thread, and this request is unordered with respect to pending async config |
| + // attempts, don't record it. It may break book-keeping, and there's not |
| + // much we can do anyway. |
| + codec_config_->allow_autodetection_ = |
| + g_avda_timer.Pointer()->IsCodecAutodetectionProbablySafe(); |
| + codec_config_->notify_completion_ = false; |
| + |
| std::unique_ptr<VideoCodecBridge> media_codec = |
| ConfigureMediaCodecOnAnyThread(codec_config_); |
| OnCodecConfigured(std::move(media_codec)); |
| @@ -1079,10 +1192,20 @@ AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread( |
| // |needs_protected_surface_| implies encrypted stream. |
| DCHECK(!codec_config->needs_protected_surface_ || media_crypto); |
| - return std::unique_ptr<VideoCodecBridge>(VideoCodecBridge::CreateDecoder( |
| + const bool require_software = !codec_config->allow_autodetection_; |
| + |
| + std::unique_ptr<VideoCodecBridge> codec(VideoCodecBridge::CreateDecoder( |
| codec_config->codec_, codec_config->needs_protected_surface_, |
| codec_config->initial_expected_coded_size_, |
| - codec_config->surface_.j_surface().obj(), media_crypto, true)); |
| + codec_config->surface_.j_surface().obj(), media_crypto, true, |
| + require_software)); |
| + |
| + // If we successfully completed after an autodetect, then reset the timer so |
| + // that other instances know that autodetection is okay. |
| + if (codec_config->notify_completion_) |
| + g_avda_timer.Pointer()->OnAsyncCodecAutodetectionComplete(); |
| + |
| + return codec; |
| } |
| void AndroidVideoDecodeAccelerator::OnCodecConfigured( |