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

Unified Diff: media/gpu/android_video_decode_accelerator.cc

Issue 2084143002: Make AVDA fall back to software decoding if needed. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: symbolic constants, coments. Created 4 years, 6 months 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 side-by-side diff with in-line comments
Download patch
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(

Powered by Google App Engine
This is Rietveld 408576698