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

Unified Diff: media/gpu/android_video_decode_accelerator.cc

Issue 2508053002: media: Do a TimedWait() for video surface teardown in AVDA (Closed)
Patch Set: Created 4 years, 1 month 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 249abd6f41f70fd1f487587039fea4d742637c8a..1ca637b7fc2b7d55dfedb5b93eaefdc7164afdb9 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -16,6 +16,7 @@
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/sys_info.h"
@@ -106,11 +107,23 @@ constexpr base::TimeDelta NoWaitTimeOut = base::TimeDelta::FromMicroseconds(0);
constexpr base::TimeDelta IdleTimerTimeOut = base::TimeDelta::FromSeconds(1);
+// Time between when we notice an error, and when we actually notify somebody.
+// This is to prevent codec errors caused by SurfaceView fullscreen transitions
+// from breaking the pipeline, if we're about to be reset anyway.
+constexpr base::TimeDelta ErrorPostingDelay = base::TimeDelta::FromSeconds(2);
+
+// On low end devices (< KitKat is always low-end due to buggy MediaCodec),
+// defer the surface creation until the codec is actually used if we know no
+// software fallback exists.
+bool ShouldDeferSurfaceCreation(int surface_id, VideoCodec codec) {
+ return surface_id == SurfaceManager::kNoSurfaceID && codec == kCodecH264 &&
+ AVDACodecAllocator::Get().IsAnyRegisteredAVDA() &&
+ (base::android::BuildInfo::GetInstance()->sdk_int() <= 18 ||
+ base::SysInfo::IsLowEndDevice());
+}
+
} // namespace
-static base::LazyInstance<AVDACodecAllocator>::Leaky g_avda_codec_allocator =
- LAZY_INSTANCE_INITIALIZER;
-
// AVDAManager manages shared resources for a number of AVDA instances.
// Its responsibilities include:
// - Starting and stopping a shared "construction" thread for instantiating and
@@ -158,74 +171,6 @@ class AVDAManager {
io_timer_.Stop();
}
- // |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 == SurfaceManager::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);
- }
-
- // On low end devices (< KitKat is always low-end due to buggy MediaCodec),
- // defer the surface creation until the codec is actually used if we know no
- // software fallback exists.
- bool ShouldDeferSurfaceCreation(int surface_id, VideoCodec codec) {
- return surface_id == SurfaceManager::kNoSurfaceID && codec == kCodecH264 &&
- g_avda_codec_allocator.Get().IsAnyRegisteredAVDA() &&
- (base::android::BuildInfo::GetInstance()->sdk_int() <= 18 ||
- base::SysInfo::IsLowEndDevice());
- }
-
private:
friend struct base::DefaultLazyInstanceTraits<AVDAManager>;
@@ -254,14 +199,6 @@ class AVDAManager {
// All AVDA instances that would like us to poll DoIOTask.
std::set<AndroidVideoDecodeAccelerator*> timer_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;
@@ -278,10 +215,6 @@ class AVDAManager {
static base::LazyInstance<AVDAManager>::Leaky g_avda_manager =
LAZY_INSTANCE_INITIALIZER;
-AndroidVideoDecodeAccelerator::CodecConfig::CodecConfig() {}
-
-AndroidVideoDecodeAccelerator::CodecConfig::~CodecConfig() {}
-
AndroidVideoDecodeAccelerator::BitstreamRecord::BitstreamRecord(
const BitstreamBuffer& bitstream_buffer)
: buffer(bitstream_buffer) {
@@ -316,7 +249,7 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator(
AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
DCHECK(thread_checker_.CalledOnValidThread());
g_avda_manager.Get().StopTimer(this);
- g_avda_codec_allocator.Get().StopThread(this);
+ AVDACodecAllocator::Get().StopThread(this);
#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
if (!media_drm_bridge_cdm_context_)
@@ -395,14 +328,10 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
// If we're low on resources, we may decide to defer creation of the surface
// until the codec is actually used.
- if (g_avda_manager.Get().ShouldDeferSurfaceCreation(config_.surface_id,
- codec_config_->codec_)) {
+ if (ShouldDeferSurfaceCreation(config_.surface_id, codec_config_->codec_)) {
DCHECK(!deferred_initialization_pending_);
-
// We should never be here if a SurfaceView is required.
DCHECK_EQ(config_.surface_id, SurfaceManager::kNoSurfaceID);
- DCHECK(g_avda_manager.Get().AllocateSurface(config_.surface_id, this));
liberato (no reviews please) 2016/11/17 07:28:19 where did this go?
watk 2016/11/17 20:37:05 Ah, I added a unit test that mostly obseletes this
-
defer_surface_creation_ = true;
NotifyInitializationComplete(true);
return true;
@@ -416,8 +345,8 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
return false;
}
- if (g_avda_manager.Get().AllocateSurface(config_.surface_id, this)) {
- // We have successfully owned the surface, so finish initialization now.
+ if (AVDACodecAllocator::Get().AllocateSurface(this, config_.surface_id)) {
+ // We now own the surface, so finish initialization.
return InitializePictureBufferManager();
}
@@ -447,13 +376,7 @@ bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() {
if (codec_config_->surface_.IsEmpty())
return false;
- on_destroying_surface_cb_ =
- base::Bind(&AndroidVideoDecodeAccelerator::OnDestroyingSurface,
- weak_this_factory_.GetWeakPtr());
- AVDASurfaceTracker::GetInstance()->RegisterOnDestroyingSurfaceCallback(
- on_destroying_surface_cb_);
-
- if (!g_avda_codec_allocator.Get().StartThread(this))
+ if (!AVDACodecAllocator::Get().StartThread(this))
return false;
// If we are encrypted, then we aren't able to create the codec yet.
@@ -957,27 +880,17 @@ void AndroidVideoDecodeAccelerator::Flush() {
void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
DCHECK(thread_checker_.CalledOnValidThread());
- // It's probably okay just to return here, since the codec will be configured
- // asynchronously. It's unclear that any state for the new request could
- // be different, unless somebody modifies |codec_config_| while we're already
- // waiting for a codec. One shouldn't do that for thread safety.
DCHECK_NE(state_, WAITING_FOR_CODEC);
-
state_ = WAITING_FOR_CODEC;
- // Tell the picture buffer manager that we're changing codecs. The codec
- // itself could be used normally, since we don't replace it until we're back
- // on the main thread. However, if we're using an output surface, then the
- // incoming codec might access that surface while the main thread is drawing.
- // Telling the manager to forget the codec avoids this.
if (media_codec_) {
- ReleaseMediaCodec();
+ AVDACodecAllocator::Get().ReleaseMediaCodec(
+ std::move(media_codec_), codec_config_->task_type_, config_.surface_id);
picture_buffer_manager_.CodecChanged(nullptr);
}
- codec_config_->task_type_ =
- g_avda_codec_allocator.Get().TaskTypeForAllocation();
- if (codec_config_->task_type_ == AVDACodecAllocator::TaskType::FAILED_CODEC) {
+ codec_config_->task_type_ = AVDACodecAllocator::Get().TaskTypeForAllocation();
+ if (codec_config_->task_type_ == TaskType::FAILED_CODEC) {
// If there is no free thread, then just fail.
OnCodecConfigured(nullptr);
return;
@@ -985,65 +898,31 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
// If autodetection is disallowed, fall back to Chrome's software decoders
// instead of using the software decoders provided by MediaCodec.
- if (codec_config_->task_type_ == AVDACodecAllocator::TaskType::SW_CODEC &&
+ if (codec_config_->task_type_ == TaskType::SW_CODEC &&
IsMediaCodecSoftwareDecodingForbidden()) {
OnCodecConfigured(nullptr);
return;
}
- base::PostTaskAndReplyWithResult(
- g_avda_codec_allocator.Get()
- .TaskRunnerFor(codec_config_->task_type_)
- .get(),
- FROM_HERE,
- base::Bind(&AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread,
- codec_config_),
- base::Bind(&AndroidVideoDecodeAccelerator::OnCodecConfigured,
- weak_this_factory_.GetWeakPtr()));
+ AVDACodecAllocator::Get().CreateMediaCodecAsync(
+ weak_this_factory_.GetWeakPtr(), codec_config_);
}
bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() {
state_ = WAITING_FOR_CODEC;
- ReleaseMediaCodec();
-
- codec_config_->task_type_ =
- g_avda_codec_allocator.Get().TaskTypeForAllocation();
- if (codec_config_->task_type_ == AVDACodecAllocator::TaskType::FAILED_CODEC) {
+ codec_config_->task_type_ = AVDACodecAllocator::Get().TaskTypeForAllocation();
+ if (codec_config_->task_type_ == TaskType::FAILED_CODEC) {
OnCodecConfigured(nullptr);
return false;
}
std::unique_ptr<VideoCodecBridge> media_codec =
- ConfigureMediaCodecOnAnyThread(codec_config_);
+ AVDACodecAllocator::Get().CreateMediaCodecSync(codec_config_);
OnCodecConfigured(std::move(media_codec));
return !!media_codec_;
}
-std::unique_ptr<VideoCodecBridge>
-AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread(
- scoped_refptr<CodecConfig> codec_config) {
- TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec");
-
- jobject media_crypto = codec_config->media_crypto_
- ? codec_config->media_crypto_->obj()
- : nullptr;
-
- // |needs_protected_surface_| implies encrypted stream.
- DCHECK(!codec_config->needs_protected_surface_ || media_crypto);
-
- const bool require_software_codec =
- codec_config->task_type_ == AVDACodecAllocator::TaskType::SW_CODEC;
-
- 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,
- codec_config->csd0_, codec_config->csd1_, true, require_software_codec));
-
- return codec;
-}
-
void AndroidVideoDecodeAccelerator::OnCodecConfigured(
std::unique_ptr<VideoCodecBridge> media_codec) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -1233,6 +1112,9 @@ void AndroidVideoDecodeAccelerator::Reset() {
}
void AndroidVideoDecodeAccelerator::SetSurface(int32_t surface_id) {
+ DVLOG(1) << __func__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+
if (surface_id == config_.surface_id) {
pending_surface_id_.reset();
return;
@@ -1240,7 +1122,7 @@ void AndroidVideoDecodeAccelerator::SetSurface(int32_t surface_id) {
// Surface changes never take effect immediately, they will be handled during
// DequeOutput() once we get to a good switch point or immediately during an
- // OnDestroyingSurface() call.
+ // OnSurfaceDestroyed() call.
pending_surface_id_ = surface_id;
}
@@ -1270,22 +1152,18 @@ void AndroidVideoDecodeAccelerator::ActualDestroy() {
DVLOG(1) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
- if (!on_destroying_surface_cb_.is_null()) {
- AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback(
- 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_manager.Get().DeallocateSurface(config_.surface_id, this);
+ AVDACodecAllocator::Get().DeallocateSurface(this, config_.surface_id);
// 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.
weak_this_factory_.InvalidateWeakPtrs();
+ g_avda_manager.Get().StopTimer(this);
liberato (no reviews please) 2016/11/17 07:28:19 hrm, that seems like an improvement.
if (media_codec_) {
- g_avda_manager.Get().StopTimer(this);
- ReleaseMediaCodec();
+ AVDACodecAllocator::Get().ReleaseMediaCodec(
+ std::move(media_codec_), codec_config_->task_type_, config_.surface_id);
}
delete this;
@@ -1306,13 +1184,10 @@ AndroidVideoDecodeAccelerator::GetGlDecoder() const {
return get_gles2_decoder_cb_.Run();
}
-void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) {
+void AndroidVideoDecodeAccelerator::OnSurfaceDestroyed() {
+ DVLOG(1) << __func__;
+ TRACE_EVENT0("media", "AVDA::OnSurfaceDestroyed");
DCHECK(thread_checker_.CalledOnValidThread());
- TRACE_EVENT0("media", "AVDA::OnDestroyingSurface");
- DVLOG(1) << __FUNCTION__ << " surface_id: " << surface_id;
-
- if (surface_id != config_.surface_id)
- return;
// If the API is available avoid having to restart the decoder in order to
// leave fullscreen. If we don't clear the surface immediately during this
@@ -1339,9 +1214,11 @@ void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) {
// SURFACE_DESTROYED.
state_ = SURFACE_DESTROYED;
if (media_codec_) {
- ReleaseMediaCodec();
+ AVDACodecAllocator::Get().ReleaseMediaCodec(
+ std::move(media_codec_), codec_config_->task_type_, config_.surface_id);
picture_buffer_manager_.CodecChanged(media_codec_.get());
}
+
// If we're draining, signal completion now because the drain can no longer
// proceed.
if (drain_type_ != DRAIN_TYPE_NONE)
@@ -1472,18 +1349,6 @@ void AndroidVideoDecodeAccelerator::ManageTimer(bool did_work) {
g_avda_manager.Get().StopTimer(this);
}
-void AndroidVideoDecodeAccelerator::ReleaseMediaCodec() {
- if (!media_codec_)
- return;
-
- // Post it to the same thread as was used to allocate it, and it'll get freed
- // if that thread isn't hung waiting on mediaserver. We can't release it
- // on this thread since it might hang up.
- g_avda_codec_allocator.Get()
- .TaskRunnerFor(codec_config_->task_type_)
- ->DeleteSoon(FROM_HERE, media_codec_.release());
-}
-
// static
VideoDecodeAccelerator::Capabilities
AndroidVideoDecodeAccelerator::GetCapabilities(
@@ -1594,6 +1459,17 @@ bool AndroidVideoDecodeAccelerator::UpdateSurface() {
DCHECK(pending_surface_id_);
DCHECK_NE(config_.surface_id, pending_surface_id_.value());
+ // Deallocate our current surface.
+ AVDACodecAllocator::Get().DeallocateSurface(this, config_.surface_id);
liberato (no reviews please) 2016/11/17 07:28:19 one of these has to be "no surface", i think. wor
watk 2016/11/17 20:37:05 sg will do
+ config_.surface_id = pending_surface_id_.value();
+ pending_surface_id_.reset();
+
+ // TODO(watk): Fix this so we can wait for the new surface to be allocated.
+ if (!AVDACodecAllocator::Get().AllocateSurface(this, config_.surface_id)) {
+ NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to allocate the new surface");
+ return false;
+ }
+
// Ensure the current context is active when switching surfaces; we may need
// to create a new texture.
if (!make_context_current_cb_.Run()) {
@@ -1603,9 +1479,8 @@ bool AndroidVideoDecodeAccelerator::UpdateSurface() {
return false;
}
- config_.surface_id = pending_surface_id_.value();
codec_config_->surface_ =
- picture_buffer_manager_.Initialize(pending_surface_id_.value());
+ picture_buffer_manager_.Initialize(config_.surface_id);
if (codec_config_->surface_.IsEmpty()) {
NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to switch surfaces.");
return false;
@@ -1617,7 +1492,6 @@ bool AndroidVideoDecodeAccelerator::UpdateSurface() {
return false;
}
- pending_surface_id_.reset();
return true;
}

Powered by Google App Engine
This is Rietveld 408576698