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

Unified Diff: media/gpu/avda_codec_allocator.cc

Issue 2508053002: media: Do a TimedWait() for video surface teardown in AVDA (Closed)
Patch Set: change timeout to 2 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
« no previous file with comments | « media/gpu/avda_codec_allocator.h ('k') | media/gpu/avda_codec_allocator_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/gpu/avda_codec_allocator.cc
diff --git a/media/gpu/avda_codec_allocator.cc b/media/gpu/avda_codec_allocator.cc
index f1e2f1c69f245b9bf7de64def825c8ace83926cd..c947f40c7a3ea7f7330445fba51698b9ca73f322 100644
--- a/media/gpu/avda_codec_allocator.cc
+++ b/media/gpu/avda_codec_allocator.cc
@@ -9,7 +9,6 @@
#include <memory>
#include "base/logging.h"
-#include "base/metrics/histogram.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/threading/thread.h"
@@ -17,24 +16,39 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "base/trace_event/trace_event.h"
+#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/limits.h"
#include "media/base/media.h"
#include "media/base/timestamp_constants.h"
+#include "media/gpu/android_video_decode_accelerator.h"
namespace media {
namespace {
-// Give tasks on the construction thread 800ms before considering them hung.
-// MediaCodec.configure() calls typically take 100-200ms on a N5, so 800ms is
-// expected to very rarely result in false positives. Also, false positives have
-// low impact because we resume using the thread if its apparently hung task
-// completes.
+base::LazyInstance<AVDACodecAllocator>::Leaky g_avda_codec_allocator =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Give tasks 800ms before considering them hung. MediaCodec.configure() calls
+// typically take 100-200ms on a N5, so 800ms is expected to very rarely result
+// in false positives. Also, false positives have low impact because we resume
+// using the thread when the task completes.
constexpr base::TimeDelta kHungTaskDetectionTimeout =
base::TimeDelta::FromMilliseconds(800);
+// Delete |codec| and signal |done_event| if it's not null.
+void DeleteMediaCodecAndSignal(std::unique_ptr<VideoCodecBridge> codec,
+ base::WaitableEvent* done_event) {
+ codec.reset();
+ if (done_event)
+ done_event->Signal();
+}
+
} // namespace
+CodecConfig::CodecConfig() {}
+CodecConfig::~CodecConfig() {}
+
AVDACodecAllocator::TestInformation::TestInformation() {}
AVDACodecAllocator::TestInformation::~TestInformation() {}
@@ -62,8 +76,13 @@ bool AVDACodecAllocator::HangDetector::IsThreadLikelyHung() {
kHungTaskDetectionTimeout;
}
-// Make sure the construction threads are started for |avda|.
-bool AVDACodecAllocator::StartThread(AndroidVideoDecodeAccelerator* avda) {
+// static
+AVDACodecAllocator* AVDACodecAllocator::Instance() {
+ return g_avda_codec_allocator.Pointer();
+}
+
+// Make sure the construction threads are started for |client|.
+bool AVDACodecAllocator::StartThread(AVDACodecAllocatorClient* client) {
DCHECK(thread_checker_.CalledOnValidThread());
// Cancel any pending StopThreadTask()s because we need the threads now.
@@ -93,17 +112,14 @@ bool AVDACodecAllocator::StartThread(AndroidVideoDecodeAccelerator* avda) {
if (!threads_[TaskType::AUTO_CODEC]->thread.IsRunning())
return false;
- thread_avda_instances_.insert(avda);
- UMA_HISTOGRAM_ENUMERATION("Media.AVDA.NumAVDAInstances",
- thread_avda_instances_.size(),
- 31); // PRESUBMIT_IGNORE_UMA_MAX
+ clients_.insert(client);
return true;
}
-void AVDACodecAllocator::StopThread(AndroidVideoDecodeAccelerator* avda) {
+void AVDACodecAllocator::StopThread(AVDACodecAllocatorClient* client) {
DCHECK(thread_checker_.CalledOnValidThread());
- thread_avda_instances_.erase(avda);
+ clients_.erase(client);
// Post a task to stop the thread through the thread's task runner and back
// to this thread. This ensures that all pending tasks are run first. If the
// thread is hung we don't post a task to avoid leaking an unbounded number
@@ -114,7 +130,7 @@ void AVDACodecAllocator::StopThread(AndroidVideoDecodeAccelerator* avda) {
// be canceled by invalidating its weak pointer.
base::WaitableEvent* event =
(test_info_ ? test_info_->stop_event_.get() : nullptr);
- if (!thread_avda_instances_.empty()) {
+ if (!clients_.empty()) {
// If we aren't stopping, then signal immediately.
if (event)
event->Signal();
@@ -150,6 +166,182 @@ scoped_refptr<base::SingleThreadTaskRunner> AVDACodecAllocator::TaskRunnerFor(
return thread.task_runner();
}
+bool AVDACodecAllocator::AllocateSurface(AVDACodecAllocatorClient* client,
+ int surface_id) {
+ DVLOG(1) << __func__ << ": " << surface_id;
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (surface_id == SurfaceManager::kNoSurfaceID)
+ return true;
+
+ // If it's not owned or being released, |client| now owns it.
+ if (!surface_owners_.count(surface_id) &&
+ !pending_codec_releases_.count(surface_id)) {
+ surface_owners_[surface_id].owner = client;
+ return true;
+ }
+
+ // Otherwise |client| replaces the previous waiter (if any).
+ OwnerRecord& record = surface_owners_[surface_id];
+ if (record.waiter)
+ record.waiter->OnSurfaceAvailable(false);
+ record.waiter = client;
+ return false;
+}
+
+void AVDACodecAllocator::DeallocateSurface(AVDACodecAllocatorClient* client,
+ int surface_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (surface_id == SurfaceManager::kNoSurfaceID ||
+ !surface_owners_.count(surface_id)) {
+ return;
+ }
+
+ OwnerRecord& record = surface_owners_[surface_id];
+ if (record.owner == client)
+ record.owner = nullptr;
+ else if (record.waiter == client)
+ record.waiter = nullptr;
+
+ // Promote the waiter if possible.
+ if (record.waiter && !record.owner &&
+ !pending_codec_releases_.count(surface_id)) {
+ record.owner = record.waiter;
+ record.waiter = nullptr;
+ record.owner->OnSurfaceAvailable(true);
+ return;
+ }
+
+ // Remove the record if it's now unused.
+ if (!record.owner && !record.waiter)
+ surface_owners_.erase(surface_id);
+}
+
+// During surface teardown we have to handle the following cases.
+// 1) No AVDA has acquired the surface, or the surface has already been
+// completely released.
+// 2) A MediaCodec is currently being configured with the surface on another
+// thread. Whether an AVDA owns the surface or has already deallocated it,
+// the MediaCodec should be dropped when configuration completes.
+// 3) An AVDA owns the surface and it responds to OnSurfaceDestroyed() by:
+// a) Replacing the destroyed surface by calling MediaCodec#setSurface().
+// b) Releasing the MediaCodec it's attached to.
+// 4) No AVDA owns the surface, but the MediaCodec it's attached to is currently
+// being destroyed on another thread.
+void AVDACodecAllocator::OnSurfaceDestroyed(int surface_id) {
+ DVLOG(1) << __func__ << ": " << surface_id;
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Notify the owner and waiter (if any).
+ if (surface_owners_.count(surface_id)) {
+ OwnerRecord& record = surface_owners_[surface_id];
+ if (record.waiter) {
+ record.waiter->OnSurfaceAvailable(false);
+ record.waiter = nullptr;
+ }
+
+ if (record.owner)
+ record.owner->OnSurfaceDestroyed();
+
+ surface_owners_.erase(surface_id);
+ }
+
+ // The codec might have been released above in OnSurfaceDestroyed(), or was
+ // already pending release.
+ if (!pending_codec_releases_.count(surface_id))
+ return;
+
+ // The codec is being released so we have to wait for it here. It's a
+ // TimedWait() because the MediaCodec release may hang due to framework bugs.
+ // And in that case we don't want to hang the browser UI thread. Android ANRs
+ // occur when the UI thread is blocked for 5 seconds, so waiting for 2 seconds
+ // gives us leeway to avoid an ANR. Verified no ANR on a Nexus 7.
+ base::WaitableEvent& released =
+ pending_codec_releases_.find(surface_id)->second;
+ released.TimedWait(base::TimeDelta::FromSeconds(2));
+ if (!released.IsSignaled())
+ DLOG(WARNING) << __func__ << ": timed out waiting for MediaCodec#release()";
+}
+
+std::unique_ptr<VideoCodecBridge> AVDACodecAllocator::CreateMediaCodecSync(
+ scoped_refptr<CodecConfig> codec_config) {
+ TRACE_EVENT0("media", "AVDA::CreateMediaCodecSync");
+
+ 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_ == 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 AVDACodecAllocator::CreateMediaCodecAsync(
+ base::WeakPtr<AVDACodecAllocatorClient> client,
+ scoped_refptr<CodecConfig> codec_config) {
+ base::PostTaskAndReplyWithResult(
+ TaskRunnerFor(codec_config->task_type_).get(), FROM_HERE,
+ base::Bind(&AVDACodecAllocator::CreateMediaCodecSync,
+ base::Unretained(this), codec_config),
+ base::Bind(&AVDACodecAllocatorClient::OnCodecConfigured, client));
+}
+
+void AVDACodecAllocator::ReleaseMediaCodec(
+ std::unique_ptr<VideoCodecBridge> media_codec,
+ TaskType task_type,
+ int surface_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(media_codec);
+
+ // No need to track the release if it's a SurfaceTexture.
+ if (surface_id == SurfaceManager::kNoSurfaceID) {
+ TaskRunnerFor(task_type)->PostTask(
+ FROM_HERE, base::Bind(&DeleteMediaCodecAndSignal,
+ base::Passed(std::move(media_codec)), nullptr));
+ return;
+ }
+
+ pending_codec_releases_.emplace(
+ std::piecewise_construct, std::forward_as_tuple(surface_id),
+ std::forward_as_tuple(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED));
+ base::WaitableEvent* released =
+ &pending_codec_releases_.find(surface_id)->second;
+
+ // TODO(watk): Even if this is the current thread, things will work, but we
+ // should refactor this to not tolerate threads failing to start.
+ TaskRunnerFor(task_type)->PostTaskAndReply(
+ FROM_HERE, base::Bind(&DeleteMediaCodecAndSignal,
+ base::Passed(std::move(media_codec)), released),
+ base::Bind(&AVDACodecAllocator::OnMediaCodecAndSurfaceReleased,
+ base::Unretained(this), surface_id));
+}
+
+void AVDACodecAllocator::OnMediaCodecAndSurfaceReleased(int surface_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ pending_codec_releases_.erase(surface_id);
+ if (!surface_owners_.count(surface_id))
+ return;
+
+ OwnerRecord& record = surface_owners_[surface_id];
+ if (!record.owner && record.waiter) {
+ record.owner = record.waiter;
+ record.waiter = nullptr;
+ record.owner->OnSurfaceAvailable(true);
+ }
+}
+
// Returns a hint about whether the construction thread has hung for
// |task_type|. Note that if a thread isn't started, then we'll just return
// "not hung", since it'll run on the current thread anyway. The hang
@@ -160,10 +352,10 @@ bool AVDACodecAllocator::IsThreadLikelyHung(TaskType task_type) {
}
bool AVDACodecAllocator::IsAnyRegisteredAVDA() {
- return !thread_avda_instances_.empty();
+ return !clients_.empty();
}
-AVDACodecAllocator::TaskType AVDACodecAllocator::TaskTypeForAllocation() {
+TaskType AVDACodecAllocator::TaskTypeForAllocation() {
if (!IsThreadLikelyHung(TaskType::AUTO_CODEC))
return TaskType::AUTO_CODEC;
« no previous file with comments | « media/gpu/avda_codec_allocator.h ('k') | media/gpu/avda_codec_allocator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698