diff options
Diffstat (limited to 'dom/media/platforms/AllocationPolicy.cpp')
-rw-r--r-- | dom/media/platforms/AllocationPolicy.cpp | 241 |
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 |