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 526146156f8b64b473e3b66fa7f4237cf75d648a..50fe2ebca4ece288bff98a1135b8baa2d94a0966 100644 |
--- a/media/gpu/android_video_decode_accelerator.cc |
+++ b/media/gpu/android_video_decode_accelerator.cc |
@@ -249,6 +249,64 @@ class AVDATimerManager { |
return construction_thread_.task_runner(); |
} |
+ // |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 |
+ // is assumed to be on the way out, so we fail its allocation request. |
+ bool AllocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) { |
+ // Nobody has to wait for no surface. |
+ if (surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID) |
+ return true; |
+ |
+ auto iter = surface_waiter_map_.find(surface_id); |
+ if (iter == surface_waiter_map_.end()) { |
+ // SurfaceView isn't allocated. Succeed. |
+ surface_waiter_map_[surface_id].owner = avda; |
+ return true; |
+ } |
+ |
+ // SurfaceView is already allocated. |
+ if (iter->second.waiter) { |
+ // Some other AVDA is waiting. |avda| will replace it, so notify it |
+ // that it will fail. |
+ iter->second.waiter->OnSurfaceAvailable(false); |
+ iter->second.waiter = nullptr; |
+ } |
+ |
+ // |avda| is now waiting. |
+ iter->second.waiter = avda; |
+ return false; |
+ } |
+ |
+ // Clear any waiting request for |surface_id| by |avda|. It is okay if |
+ // |waiter| is not waiting and/or isn't the owner of |surface_id|. |
+ void DeallocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) { |
+ SurfaceWaiterMap::iterator iter = surface_waiter_map_.find(surface_id); |
+ if (iter == surface_waiter_map_.end()) |
+ return; |
+ |
+ // If |avda| was waiting, then remove it without OnSurfaceAvailable. |
+ if (iter->second.waiter == avda) |
+ iter->second.waiter = nullptr; |
+ |
+ // If |avda| is the owner, then let the waiter have it. |
+ if (iter->second.owner != avda) |
+ return; |
+ |
+ AndroidVideoDecodeAccelerator* waiter = iter->second.waiter; |
+ if (!waiter) { |
+ // No waiter -- remove the record and return explicitly since |iter| is |
+ // no longer valid. |
+ surface_waiter_map_.erase(iter); |
+ return; |
+ } |
+ |
+ // Promote |waiter| to be the owner. |
+ iter->second.owner = waiter; |
+ iter->second.waiter = nullptr; |
+ waiter->OnSurfaceAvailable(true); |
+ } |
+ |
private: |
friend struct base::DefaultLazyInstanceTraits<AVDATimerManager>; |
@@ -280,6 +338,14 @@ class AVDATimerManager { |
// All AVDA instances that might like to use the construction thread. |
std::set<AndroidVideoDecodeAccelerator*> thread_avda_instances_; |
+ struct OwnerRecord { |
+ AndroidVideoDecodeAccelerator* owner = nullptr; |
+ AndroidVideoDecodeAccelerator* waiter = nullptr; |
+ }; |
+ // [surface id] = OwnerRecord for that surface. |
+ using SurfaceWaiterMap = std::map<int, OwnerRecord>; |
+ SurfaceWaiterMap surface_waiter_map_; |
+ |
// Since we can't delete while iterating when using a set, defer erasure until |
// after iteration complete. |
bool timer_running_ = false; |
@@ -308,7 +374,6 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( |
: client_(NULL), |
make_context_current_cb_(make_context_current_cb), |
get_gles2_decoder_cb_(get_gles2_decoder_cb), |
- is_encrypted_(false), |
state_(NO_ERROR), |
picturebuffers_requested_(false), |
drain_type_(DRAIN_TYPE_NONE), |
@@ -318,7 +383,6 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( |
error_sequence_token_(0), |
defer_errors_(false), |
deferred_initialization_pending_(false), |
- surface_id_(media::VideoDecodeAccelerator::Config::kNoSurfaceID), |
weak_this_factory_(this) {} |
AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { |
@@ -354,17 +418,17 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
DCHECK(client); |
client_ = client; |
+ config_ = config; |
codec_config_ = new CodecConfig(); |
codec_config_->codec_ = VideoCodecProfileToVideoCodec(config.profile); |
codec_config_->initial_expected_coded_size_ = |
config.initial_expected_coded_size; |
- is_encrypted_ = config.is_encrypted; |
// We signalled that we support deferred initialization, so see if the client |
// does also. |
deferred_initialization_pending_ = config.is_deferred_initialization_allowed; |
- if (is_encrypted_ && !deferred_initialization_pending_) { |
+ if (config_.is_encrypted && !deferred_initialization_pending_) { |
DLOG(ERROR) << "Deferred initialization must be used for encrypted streams"; |
return false; |
} |
@@ -380,7 +444,7 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
// or if the stream is encrypted. |
if ((codec_config_->codec_ == media::kCodecVP8 || |
codec_config_->codec_ == media::kCodecVP9) && |
- !is_encrypted_ && |
+ !config_.is_encrypted && |
media::VideoCodecBridge::IsKnownUnaccelerated( |
codec_config_->codec_, media::MEDIA_CODEC_DECODER)) { |
DVLOG(1) << "Initialization failed: " |
@@ -414,8 +478,27 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
return false; |
} |
- surface_id_ = config.surface_id; |
- codec_config_->surface_ = strategy_->Initialize(surface_id_); |
+ if (g_avda_timer.Pointer()->AllocateSurface(config_.surface_id, this)) { |
+ // We have succesfully owned the surface, so finish initialization now. |
+ return InitializeStrategy(); |
+ } |
+ |
+ // We have to wait for some other AVDA instance to free up the surface. |
+ // OnSurfaceAvailable will be called when it's available. |
+ return true; |
+} |
+ |
+void AndroidVideoDecodeAccelerator::OnSurfaceAvailable(bool success) { |
+ DCHECK(deferred_initialization_pending_); |
+ |
+ if (!success || !InitializeStrategy()) { |
+ NotifyInitializationComplete(false); |
+ deferred_initialization_pending_ = false; |
+ } |
+} |
+ |
+bool AndroidVideoDecodeAccelerator::InitializeStrategy() { |
+ codec_config_->surface_ = strategy_->Initialize(config_.surface_id); |
if (codec_config_->surface_.IsEmpty()) { |
LOG(ERROR) << "Failed to initialize the backing strategy. The returned " |
"Java surface is empty."; |
@@ -444,8 +527,8 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
} |
// If we are encrypted, then we aren't able to create the codec yet. |
- if (is_encrypted_) { |
- InitializeCdm(config.cdm_id); |
+ if (config_.is_encrypted) { |
+ InitializeCdm(); |
return true; |
} |
@@ -455,7 +538,11 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
} |
// If the client doesn't support deferred initialization (WebRTC), then we |
- // should complete it now and return a meaningful result. |
+ // should complete it now and return a meaningful result. Note that it would |
+ // be nice if we didn't have to worry about starting codec configuration at |
+ // all (::Initialize or the wrapper can do it), but then they have to remember |
+ // not to start codec config if we have to wait for the cdm. It's somewhat |
+ // clearer for us to handle both cases. |
return ConfigureMediaCodecSynchronously(); |
} |
@@ -1204,6 +1291,10 @@ void AndroidVideoDecodeAccelerator::ActualDestroy() { |
on_destroying_surface_cb_); |
} |
+ // We no longer care about |surface_id|, in case we did before. It's okay |
+ // if we have no surface and/or weren't the owner or a waiter. |
+ g_avda_timer.Pointer()->DeallocateSurface(config_.surface_id, this); |
+ |
// Note that async codec construction might still be in progress. In that |
// case, the codec will be deleted when it completes once we invalidate all |
// our weak refs. |
@@ -1262,7 +1353,7 @@ void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) { |
TRACE_EVENT0("media", "AVDA::OnDestroyingSurface"); |
DVLOG(1) << __FUNCTION__ << " surface_id: " << surface_id; |
- if (surface_id != surface_id_) |
+ if (surface_id != config_.surface_id) |
return; |
// If we're currently asynchronously configuring a codec, it will be destroyed |
@@ -1296,15 +1387,16 @@ void AndroidVideoDecodeAccelerator::PostError( |
state_ = ERROR; |
} |
-void AndroidVideoDecodeAccelerator::InitializeCdm(int cdm_id) { |
- DVLOG(2) << __FUNCTION__ << ": " << cdm_id; |
+void AndroidVideoDecodeAccelerator::InitializeCdm() { |
+ DVLOG(2) << __FUNCTION__ << ": " << config_.cdm_id; |
#if !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
NOTIMPLEMENTED(); |
NotifyInitializationComplete(false); |
#else |
// Store the CDM to hold a reference to it. |
- cdm_for_reference_holding_only_ = media::MojoCdmService::LegacyGetCdm(cdm_id); |
+ cdm_for_reference_holding_only_ = |
+ media::MojoCdmService::LegacyGetCdm(config_.cdm_id); |
DCHECK(cdm_for_reference_holding_only_); |
// On Android platform the CdmContext must be a MediaDrmBridgeCdmContext. |