diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 7658abf8ffb1..c30e20ef2b58 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -6,6 +6,7 @@ #include "MediaFormatReader.h" +#include "AllocationPolicy.h" #include "MediaData.h" #include "MediaInfo.h" #include "VideoFrameContainer.h" @@ -22,9 +23,6 @@ #include "mozilla/Unused.h" #include "nsContentUtils.h" #include "nsPrintfCString.h" -#ifdef MOZ_WIDGET_ANDROID -#include "mozilla/jni/Utils.h" -#endif #include #include @@ -137,162 +135,6 @@ std::map GPUProcessCrashTelemetryLogger::sGPUCrashDataMap; StaticMutex GPUProcessCrashTelemetryLogger::sGPUCrashMapMutex; -/** - * This is a singleton which controls the number of decoders that can be - * created concurrently. Before calling PDMFactory::CreateDecoder(), Alloc() - * must be called to get a token object as a permission to create a decoder. - * The token should stay alive until Shutdown() is called on the decoder. - * The destructor of the token will restore the decoder count so it is available - * for next calls of Alloc(). - */ -class GlobalAllocPolicy -{ - using TrackType = TrackInfo::TrackType; - -public: - class Token - { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Token) - protected: - virtual ~Token() {} - }; - - using Promise = MozPromise, bool, true>; - - // Acquire a token for decoder creation. Thread-safe. - auto Alloc() -> RefPtr; - - // Called by ClearOnShutdown() to delete the singleton. - void operator=(decltype(nullptr)); - - // Get the singleton for the given track type. Thread-safe. - static GlobalAllocPolicy& Instance(TrackType aTrack); - -private: - class AutoDeallocToken; - using PromisePrivate = Promise::Private; - GlobalAllocPolicy(); - ~GlobalAllocPolicy(); - // Called by the destructor of TokenImpl to restore the decoder limit. - void Dealloc(); - // Decrement the decoder limit and resolve a promise if available. - void ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock); - - // Protect access to Instance(). - static StaticMutex sMutex; - - ReentrantMonitor mMonitor; - // The number of decoders available for creation. - int mDecoderLimit; - // Requests to acquire tokens. - std::queue> mPromises; -}; - -StaticMutex GlobalAllocPolicy::sMutex; - -class GlobalAllocPolicy::AutoDeallocToken : public Token -{ -public: - explicit AutoDeallocToken(GlobalAllocPolicy& aPolicy) : mPolicy(aPolicy) { } - -private: - ~AutoDeallocToken() - { - mPolicy.Dealloc(); - } - - GlobalAllocPolicy& mPolicy; // reference to a singleton object. -}; - -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; -} - -GlobalAllocPolicy::GlobalAllocPolicy() - : mMonitor("DecoderAllocPolicy::mMonitor") - , mDecoderLimit(MediaDecoderLimitDefault()) -{ - SystemGroup::Dispatch( - TaskCategory::Other, - NS_NewRunnableFunction("GlobalAllocPolicy::GlobalAllocPolicy", [this]() { - ClearOnShutdown(this, ShutdownPhase::ShutdownThreads); - })); -} - -GlobalAllocPolicy::~GlobalAllocPolicy() -{ - while (!mPromises.empty()) { - RefPtr p = mPromises.front().forget(); - mPromises.pop(); - p->Reject(true, __func__); - } -} - -GlobalAllocPolicy& -GlobalAllocPolicy::Instance(TrackType aTrack) -{ - StaticMutexAutoLock lock(sMutex); - if (aTrack == TrackType::kAudioTrack) { - static auto sAudioPolicy = new GlobalAllocPolicy(); - return *sAudioPolicy; - } else { - static auto sVideoPolicy = new GlobalAllocPolicy(); - return *sVideoPolicy; - } -} - -auto -GlobalAllocPolicy::Alloc() -> RefPtr -{ - // No decoder limit set. - if (mDecoderLimit < 0) { - return Promise::CreateAndResolve(new Token(), __func__); - } - - ReentrantMonitorAutoEnter mon(mMonitor); - RefPtr p = new PromisePrivate(__func__); - mPromises.push(p); - ResolvePromise(mon); - return p.forget(); -} - -void -GlobalAllocPolicy::Dealloc() -{ - ReentrantMonitorAutoEnter mon(mMonitor); - ++mDecoderLimit; - ResolvePromise(mon); -} - -void -GlobalAllocPolicy::ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock) -{ - MOZ_ASSERT(mDecoderLimit >= 0); - - if (mDecoderLimit > 0 && !mPromises.empty()) { - --mDecoderLimit; - RefPtr p = mPromises.front().forget(); - mPromises.pop(); - p->Resolve(new AutoDeallocToken(*this), __func__); - } -} - -void -GlobalAllocPolicy::operator=(std::nullptr_t) -{ - delete this; -} - /** * This class addresses the concern of bug 1339310 comment 4 where the Widevine * CDM doesn't support running multiple instances of a video decoder at once per @@ -621,8 +463,6 @@ public: } private: - class Wrapper; - enum class Stage : int8_t { None, @@ -663,72 +503,6 @@ MediaFormatReader::DecoderFactory::CreateDecoder(TrackType aTrack) RunStage(aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo); } -class MediaFormatReader::DecoderFactory::Wrapper : public MediaDataDecoder -{ - using Token = GlobalAllocPolicy::Token; - -public: - Wrapper(already_AddRefed aDecoder, - already_AddRefed aToken) - : mDecoder(aDecoder) - , mToken(aToken) - { - DecoderDoctorLogger::LogConstructionAndBase( - "MediaFormatReader::DecoderFactory::Wrapper", - this, - static_cast(this)); - DecoderDoctorLogger::LinkParentAndChild( - "MediaFormatReader::DecoderFactory::Wrapper", - this, - "decoder", - mDecoder.get()); - } - - ~Wrapper() - { - DecoderDoctorLogger::LogDestruction( - "MediaFormatReader::DecoderFactory::Wrapper", this); - } - - RefPtr Init() override { return mDecoder->Init(); } - RefPtr Decode(MediaRawData* aSample) override - { - return mDecoder->Decode(aSample); - } - RefPtr Drain() override { return mDecoder->Drain(); } - RefPtr Flush() override { return mDecoder->Flush(); } - bool IsHardwareAccelerated(nsACString& aFailureReason) const override - { - return mDecoder->IsHardwareAccelerated(aFailureReason); - } - nsCString GetDescriptionName() const override - { - return mDecoder->GetDescriptionName(); - } - void SetSeekThreshold(const TimeUnit& aTime) override - { - mDecoder->SetSeekThreshold(aTime); - } - bool SupportDecoderRecycling() const override - { - return mDecoder->SupportDecoderRecycling(); - } - RefPtr Shutdown() override - { - RefPtr decoder = mDecoder.forget(); - RefPtr token = mToken.forget(); - return decoder->Shutdown()->Then( - AbstractThread::GetCurrent(), __func__, - [token]() { - return ShutdownPromise::CreateAndResolve(true, __func__); - }); - } - -private: - RefPtr mDecoder; - RefPtr mToken; -}; - void MediaFormatReader::DecoderFactory::RunStage(Data& aData) { @@ -777,7 +551,8 @@ MediaFormatReader::DecoderFactory::RunStage(Data& aData) return; } - aData.mDecoder = new Wrapper(aData.mDecoder.forget(), aData.mToken.forget()); + aData.mDecoder = + new AllocationWrapper(aData.mDecoder.forget(), aData.mToken.forget()); DecoderDoctorLogger::LinkParentAndChild( aData.mDecoder.get(), "decoder", diff --git a/dom/media/platforms/AllocationPolicy.cpp b/dom/media/platforms/AllocationPolicy.cpp new file mode 100644 index 000000000000..1bd6e447598e --- /dev/null +++ b/dom/media/platforms/AllocationPolicy.cpp @@ -0,0 +1,152 @@ +/* -*- 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 "mozilla/ClearOnShutdown.h" +#include "mozilla/SystemGroup.h" +#ifdef MOZ_WIDGET_ANDROID +#include "mozilla/jni/Utils.h" +#endif + +namespace mozilla { + +using TrackType = TrackInfo::TrackType; + +StaticMutex GlobalAllocPolicy::sMutex; + +class GlobalAllocPolicy::AutoDeallocToken : public Token +{ +public: + explicit AutoDeallocToken(GlobalAllocPolicy& aPolicy) : mPolicy(aPolicy) { } + +private: + ~AutoDeallocToken() + { + mPolicy.Dealloc(); + } + + GlobalAllocPolicy& mPolicy; // reference to a singleton object. +}; + +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; +} + +GlobalAllocPolicy::GlobalAllocPolicy() + : mMonitor("DecoderAllocPolicy::mMonitor") + , mDecoderLimit(MediaDecoderLimitDefault()) +{ + SystemGroup::Dispatch( + TaskCategory::Other, + NS_NewRunnableFunction("GlobalAllocPolicy::GlobalAllocPolicy", [this]() { + ClearOnShutdown(this, ShutdownPhase::ShutdownThreads); + })); +} + +GlobalAllocPolicy::~GlobalAllocPolicy() +{ + while (!mPromises.empty()) { + RefPtr p = mPromises.front().forget(); + mPromises.pop(); + p->Reject(true, __func__); + } +} + +GlobalAllocPolicy& +GlobalAllocPolicy::Instance(TrackType aTrack) +{ + StaticMutexAutoLock lock(sMutex); + if (aTrack == TrackType::kAudioTrack) { + static auto sAudioPolicy = new GlobalAllocPolicy(); + return *sAudioPolicy; + } else { + static auto sVideoPolicy = new GlobalAllocPolicy(); + return *sVideoPolicy; + } +} + +auto +GlobalAllocPolicy::Alloc() -> RefPtr +{ + // No decoder limit set. + if (mDecoderLimit < 0) { + return Promise::CreateAndResolve(new Token(), __func__); + } + + ReentrantMonitorAutoEnter mon(mMonitor); + RefPtr p = new PromisePrivate(__func__); + mPromises.push(p); + ResolvePromise(mon); + return p.forget(); +} + +void +GlobalAllocPolicy::Dealloc() +{ + ReentrantMonitorAutoEnter mon(mMonitor); + ++mDecoderLimit; + ResolvePromise(mon); +} + +void +GlobalAllocPolicy::ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock) +{ + MOZ_ASSERT(mDecoderLimit >= 0); + + if (mDecoderLimit > 0 && !mPromises.empty()) { + --mDecoderLimit; + RefPtr p = mPromises.front().forget(); + mPromises.pop(); + p->Resolve(new AutoDeallocToken(*this), __func__); + } +} + +void +GlobalAllocPolicy::operator=(std::nullptr_t) +{ + delete this; +} + +AllocationWrapper::AllocationWrapper( + already_AddRefed aDecoder, + already_AddRefed aToken) + : mDecoder(aDecoder) + , mToken(aToken) +{ + DecoderDoctorLogger::LogConstructionAndBase( + "AllocationWrapper", this, static_cast(this)); + DecoderDoctorLogger::LinkParentAndChild( + "AllocationWrapper", this, "decoder", mDecoder.get()); +} + +AllocationWrapper::~AllocationWrapper() +{ + DecoderDoctorLogger::LogDestruction("AllocationWrapper", this); +} + +RefPtr +AllocationWrapper::Shutdown() +{ + RefPtr decoder = mDecoder.forget(); + RefPtr token = mToken.forget(); + return decoder->Shutdown()->Then( + AbstractThread::GetCurrent(), __func__, [token]() { + return ShutdownPromise::CreateAndResolve(true, __func__); + }); +} + +} // namespace mozilla diff --git a/dom/media/platforms/AllocationPolicy.h b/dom/media/platforms/AllocationPolicy.h new file mode 100644 index 000000000000..647d4b1a50c5 --- /dev/null +++ b/dom/media/platforms/AllocationPolicy.h @@ -0,0 +1,110 @@ +/* -*- 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/. */ + +#ifndef AllocationPolicy_h_ +#define AllocationPolicy_h_ + +#include "MediaInfo.h" +#include "PlatformDecoderModule.h" +#include "TimeUnits.h" +#include "mozilla/MozPromise.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/ReentrantMonitor.h" +#include + +namespace mozilla { + +/** + * This is a singleton which controls the number of decoders that can be + * created concurrently. Before calling PDMFactory::CreateDecoder(), Alloc() + * must be called to get a token object as a permission to create a decoder. + * The token should stay alive until Shutdown() is called on the decoder. + * The destructor of the token will restore the decoder count so it is available + * for next calls of Alloc(). + */ +class GlobalAllocPolicy +{ +public: + class Token + { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Token) + protected: + virtual ~Token() {} + }; + + using Promise = MozPromise, bool, true>; + + // Acquire a token for decoder creation. Thread-safe. + RefPtr Alloc(); + + // Called by ClearOnShutdown() to delete the singleton. + void operator=(decltype(nullptr)); + + // Get the singleton for the given track type. Thread-safe. + static GlobalAllocPolicy& Instance(TrackInfo::TrackType aTrack); + +private: + class AutoDeallocToken; + using PromisePrivate = Promise::Private; + GlobalAllocPolicy(); + ~GlobalAllocPolicy(); + // Called by the destructor of TokenImpl to restore the decoder limit. + void Dealloc(); + // Decrement the decoder limit and resolve a promise if available. + void ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock); + + // Protect access to Instance(). + static StaticMutex sMutex; + + ReentrantMonitor mMonitor; + // The number of decoders available for creation. + int mDecoderLimit; + // Requests to acquire tokens. + std::queue> mPromises; +}; + +class AllocationWrapper : public MediaDataDecoder +{ + using Token = GlobalAllocPolicy::Token; + +public: + AllocationWrapper(already_AddRefed aDecoder, + already_AddRefed aToken); + ~AllocationWrapper(); + + RefPtr Init() override { return mDecoder->Init(); } + RefPtr Decode(MediaRawData* aSample) override + { + return mDecoder->Decode(aSample); + } + RefPtr Drain() override { return mDecoder->Drain(); } + RefPtr Flush() override { return mDecoder->Flush(); } + bool IsHardwareAccelerated(nsACString& aFailureReason) const override + { + return mDecoder->IsHardwareAccelerated(aFailureReason); + } + nsCString GetDescriptionName() const override + { + return mDecoder->GetDescriptionName(); + } + void SetSeekThreshold(const media::TimeUnit& aTime) override + { + mDecoder->SetSeekThreshold(aTime); + } + bool SupportDecoderRecycling() const override + { + return mDecoder->SupportDecoderRecycling(); + } + RefPtr Shutdown() override; + +private: + RefPtr mDecoder; + RefPtr mToken; +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/platforms/moz.build b/dom/media/platforms/moz.build index c16925501470..8d17ec51f9fa 100644 --- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -11,6 +11,7 @@ EXPORTS += [ 'agnostic/TheoraDecoder.h', 'agnostic/VorbisDecoder.h', 'agnostic/VPXDecoder.h', + 'AllocationPolicy.h', 'MediaTelemetryConstants.h', 'PDMFactory.h', 'PlatformDecoderModule.h', @@ -31,6 +32,7 @@ UNIFIED_SOURCES += [ 'agnostic/VorbisDecoder.cpp', 'agnostic/VPXDecoder.cpp', 'agnostic/WAVDecoder.cpp', + 'AllocationPolicy.cpp', 'PDMFactory.cpp', 'wrappers/H264Converter.cpp', 'wrappers/MediaDataDecoderProxy.cpp'