summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/AllocationPolicy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/AllocationPolicy.cpp')
-rw-r--r--dom/media/platforms/AllocationPolicy.cpp241
1 files changed, 241 insertions, 0 deletions
diff --git a/dom/media/platforms/AllocationPolicy.cpp b/dom/media/platforms/AllocationPolicy.cpp
new file mode 100644
index 0000000000..a56bf1d3a1
--- /dev/null
+++ b/dom/media/platforms/AllocationPolicy.cpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AllocationPolicy.h"
+
+#include "ImageContainer.h"
+#include "MediaInfo.h"
+#include "PDMFactory.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/SchedulerGroup.h"
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/jni/Utils.h"
+#endif
+
+namespace mozilla {
+
+using TrackType = TrackInfo::TrackType;
+
+class AllocPolicyImpl::AutoDeallocToken : public Token {
+ public:
+ explicit AutoDeallocToken(const RefPtr<AllocPolicyImpl>& aPolicy)
+ : mPolicy(aPolicy) {}
+
+ private:
+ ~AutoDeallocToken() { mPolicy->Dealloc(); }
+
+ RefPtr<AllocPolicyImpl> mPolicy;
+};
+
+AllocPolicyImpl::AllocPolicyImpl(int aDecoderLimit)
+ : mMaxDecoderLimit(aDecoderLimit),
+ mMonitor("AllocPolicyImpl"),
+ mDecoderLimit(aDecoderLimit) {}
+AllocPolicyImpl::~AllocPolicyImpl() { RejectAll(); }
+
+auto AllocPolicyImpl::Alloc() -> RefPtr<Promise> {
+ ReentrantMonitorAutoEnter mon(mMonitor);
+ // No decoder limit set.
+ if (mDecoderLimit < 0) {
+ return Promise::CreateAndResolve(new Token(), __func__);
+ }
+
+ RefPtr<PromisePrivate> p = new PromisePrivate(__func__);
+ mPromises.push(p);
+ ResolvePromise(mon);
+ return p;
+}
+
+void AllocPolicyImpl::Dealloc() {
+ ReentrantMonitorAutoEnter mon(mMonitor);
+ ++mDecoderLimit;
+ ResolvePromise(mon);
+}
+
+void AllocPolicyImpl::ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock) {
+ MOZ_ASSERT(mDecoderLimit >= 0);
+
+ if (mDecoderLimit > 0 && !mPromises.empty()) {
+ --mDecoderLimit;
+ RefPtr<PromisePrivate> p = std::move(mPromises.front());
+ mPromises.pop();
+ p->Resolve(new AutoDeallocToken(this), __func__);
+ }
+}
+
+void AllocPolicyImpl::RejectAll() {
+ ReentrantMonitorAutoEnter mon(mMonitor);
+ while (!mPromises.empty()) {
+ RefPtr<PromisePrivate> p = std::move(mPromises.front());
+ mPromises.pop();
+ p->Reject(true, __func__);
+ }
+}
+
+static int32_t MediaDecoderLimitDefault() {
+#ifdef MOZ_WIDGET_ANDROID
+ if (jni::GetAPIVersion() < 18) {
+ // Older Android versions have broken support for multiple simultaneous
+ // decoders, see bug 1278574.
+ return 1;
+ }
+#endif
+ // Otherwise, set no decoder limit.
+ return -1;
+}
+
+StaticMutex GlobalAllocPolicy::sMutex;
+
+NotNull<AllocPolicy*> GlobalAllocPolicy::Instance(TrackType aTrack) {
+ StaticMutexAutoLock lock(sMutex);
+ if (aTrack == TrackType::kAudioTrack) {
+ static RefPtr<AllocPolicyImpl> sAudioPolicy = []() {
+ SchedulerGroup::Dispatch(
+ TaskCategory::Other,
+ NS_NewRunnableFunction(
+ "GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() {
+ ClearOnShutdown(&sAudioPolicy,
+ ShutdownPhase::XPCOMShutdownThreads);
+ }));
+ return new AllocPolicyImpl(MediaDecoderLimitDefault());
+ }();
+ return WrapNotNull(sAudioPolicy.get());
+ }
+ static RefPtr<AllocPolicyImpl> sVideoPolicy = []() {
+ SchedulerGroup::Dispatch(
+ TaskCategory::Other,
+ NS_NewRunnableFunction(
+ "GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() {
+ ClearOnShutdown(&sVideoPolicy,
+ ShutdownPhase::XPCOMShutdownThreads);
+ }));
+ return new AllocPolicyImpl(MediaDecoderLimitDefault());
+ }();
+ return WrapNotNull(sVideoPolicy.get());
+}
+
+class SingleAllocPolicy::AutoDeallocCombinedToken : public Token {
+ public:
+ AutoDeallocCombinedToken(already_AddRefed<Token> aSingleAllocPolicyToken,
+ already_AddRefed<Token> aGlobalAllocPolicyToken)
+ : mSingleToken(aSingleAllocPolicyToken),
+ mGlobalToken(aGlobalAllocPolicyToken) {}
+
+ private:
+ // Release tokens allocated from GlobalAllocPolicy and LocalAllocPolicy
+ // and process next token request if any.
+ ~AutoDeallocCombinedToken() = default;
+ const RefPtr<Token> mSingleToken;
+ const RefPtr<Token> mGlobalToken;
+};
+
+auto SingleAllocPolicy::Alloc() -> RefPtr<Promise> {
+ MOZ_DIAGNOSTIC_ASSERT(MaxDecoderLimit() == 1,
+ "We can only handle at most one token out at a time.");
+ RefPtr<SingleAllocPolicy> self = this;
+ return AllocPolicyImpl::Alloc()->Then(
+ mOwnerThread, __func__,
+ [self](RefPtr<Token> aToken) {
+ RefPtr<Token> localToken = std::move(aToken);
+ RefPtr<Promise> p = self->mPendingPromise.Ensure(__func__);
+ GlobalAllocPolicy::Instance(self->mTrack)
+ ->Alloc()
+ ->Then(
+ self->mOwnerThread, __func__,
+ [self, localToken = std::move(localToken)](
+ RefPtr<Token> aToken) mutable {
+ self->mTokenRequest.Complete();
+ RefPtr<Token> combinedToken = new AutoDeallocCombinedToken(
+ localToken.forget(), aToken.forget());
+ self->mPendingPromise.Resolve(combinedToken, __func__);
+ },
+ [self]() {
+ self->mTokenRequest.Complete();
+ self->mPendingPromise.Reject(true, __func__);
+ })
+ ->Track(self->mTokenRequest);
+ return p;
+ },
+ []() { return Promise::CreateAndReject(true, __func__); });
+}
+
+SingleAllocPolicy::~SingleAllocPolicy() {
+ mPendingPromise.RejectIfExists(true, __func__);
+ mTokenRequest.DisconnectIfExists();
+}
+
+void SingleAllocPolicy::Cancel() {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ mPendingPromise.RejectIfExists(true, __func__);
+ mTokenRequest.DisconnectIfExists();
+ RejectAll();
+}
+
+AllocationWrapper::AllocationWrapper(
+ already_AddRefed<MediaDataDecoder> aDecoder, already_AddRefed<Token> aToken)
+ : mDecoder(aDecoder), mToken(aToken) {
+ DecoderDoctorLogger::LogConstructionAndBase(
+ "AllocationWrapper", this, static_cast<const MediaDataDecoder*>(this));
+ DecoderDoctorLogger::LinkParentAndChild("AllocationWrapper", this, "decoder",
+ mDecoder.get());
+}
+
+AllocationWrapper::~AllocationWrapper() {
+ DecoderDoctorLogger::LogDestruction("AllocationWrapper", this);
+}
+
+RefPtr<ShutdownPromise> AllocationWrapper::Shutdown() {
+ RefPtr<MediaDataDecoder> decoder = std::move(mDecoder);
+ RefPtr<Token> token = std::move(mToken);
+ return decoder->Shutdown()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [token]() { return ShutdownPromise::CreateAndResolve(true, __func__); });
+}
+/* static */ RefPtr<AllocationWrapper::AllocateDecoderPromise>
+AllocationWrapper::CreateDecoder(const CreateDecoderParams& aParams,
+ AllocPolicy* aPolicy) {
+ RefPtr<AllocateDecoderPromise> p =
+ (aPolicy ? aPolicy : GlobalAllocPolicy::Instance(aParams.mType))
+ ->Alloc()
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [params =
+ CreateDecoderParamsForAsync(aParams)](RefPtr<Token> aToken) {
+ // result may not always be updated by
+ // PDMFactory::CreateDecoder either when the creation
+ // succeeded or failed, as such it must be initialized to a
+ // fatal error by default.
+ MediaResult result =
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ nsPrintfCString("error creating %s decoder",
+ TrackTypeToStr(params.mType)));
+ RefPtr<PDMFactory> pdm = new PDMFactory();
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise> p =
+ pdm->CreateDecoder(params)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [aToken](RefPtr<MediaDataDecoder>&& aDecoder) mutable {
+ RefPtr<AllocationWrapper> wrapper =
+ new AllocationWrapper(aDecoder.forget(),
+ aToken.forget());
+ return AllocateDecoderPromise::CreateAndResolve(
+ wrapper, __func__);
+ },
+ [](const MediaResult& aError) {
+ return AllocateDecoderPromise::CreateAndReject(
+ aError, __func__);
+ });
+ return p;
+ },
+ []() {
+ return AllocateDecoderPromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ "Allocation policy expired"),
+ __func__);
+ });
+ return p;
+}
+
+} // namespace mozilla