зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1484242 - P1. Add AllocationPolicy objects. r=bryce
We extract the GlobalAllocationPolicy and the MediaDataDecoder wrapper from MediaFormatReader. They will be used to create a new wrapping class that will serialize allocation and initalization of decoders if the platform requires it. Differential Revision: https://phabricator.services.mozilla.com/D3676
This commit is contained in:
Родитель
2a827026aa
Коммит
d3b19c39b3
|
@ -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 <algorithm>
|
||||
#include <map>
|
||||
|
@ -137,162 +135,6 @@ std::map<MediaDecoderOwnerID, GPUProcessCrashTelemetryLogger::GPUCrashData>
|
|||
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<RefPtr<Token>, bool, true>;
|
||||
|
||||
// Acquire a token for decoder creation. Thread-safe.
|
||||
auto Alloc() -> RefPtr<Promise>;
|
||||
|
||||
// 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<RefPtr<PromisePrivate>> 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<PromisePrivate> 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<Promise>
|
||||
{
|
||||
// No decoder limit set.
|
||||
if (mDecoderLimit < 0) {
|
||||
return Promise::CreateAndResolve(new Token(), __func__);
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
RefPtr<PromisePrivate> 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<PromisePrivate> 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<MediaDataDecoder> aDecoder,
|
||||
already_AddRefed<Token> aToken)
|
||||
: mDecoder(aDecoder)
|
||||
, mToken(aToken)
|
||||
{
|
||||
DecoderDoctorLogger::LogConstructionAndBase(
|
||||
"MediaFormatReader::DecoderFactory::Wrapper",
|
||||
this,
|
||||
static_cast<const MediaDataDecoder*>(this));
|
||||
DecoderDoctorLogger::LinkParentAndChild(
|
||||
"MediaFormatReader::DecoderFactory::Wrapper",
|
||||
this,
|
||||
"decoder",
|
||||
mDecoder.get());
|
||||
}
|
||||
|
||||
~Wrapper()
|
||||
{
|
||||
DecoderDoctorLogger::LogDestruction(
|
||||
"MediaFormatReader::DecoderFactory::Wrapper", this);
|
||||
}
|
||||
|
||||
RefPtr<InitPromise> Init() override { return mDecoder->Init(); }
|
||||
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override
|
||||
{
|
||||
return mDecoder->Decode(aSample);
|
||||
}
|
||||
RefPtr<DecodePromise> Drain() override { return mDecoder->Drain(); }
|
||||
RefPtr<FlushPromise> 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<ShutdownPromise> Shutdown() override
|
||||
{
|
||||
RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
|
||||
RefPtr<Token> token = mToken.forget();
|
||||
return decoder->Shutdown()->Then(
|
||||
AbstractThread::GetCurrent(), __func__,
|
||||
[token]() {
|
||||
return ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<MediaDataDecoder> mDecoder;
|
||||
RefPtr<Token> 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",
|
||||
|
|
|
@ -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<PromisePrivate> 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<Promise>
|
||||
{
|
||||
// No decoder limit set.
|
||||
if (mDecoderLimit < 0) {
|
||||
return Promise::CreateAndResolve(new Token(), __func__);
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
RefPtr<PromisePrivate> 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<PromisePrivate> p = mPromises.front().forget();
|
||||
mPromises.pop();
|
||||
p->Resolve(new AutoDeallocToken(*this), __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GlobalAllocPolicy::operator=(std::nullptr_t)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
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 = mDecoder.forget();
|
||||
RefPtr<Token> token = mToken.forget();
|
||||
return decoder->Shutdown()->Then(
|
||||
AbstractThread::GetCurrent(), __func__, [token]() {
|
||||
return ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -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 <queue>
|
||||
|
||||
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<RefPtr<Token>, bool, true>;
|
||||
|
||||
// Acquire a token for decoder creation. Thread-safe.
|
||||
RefPtr<Promise> 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<RefPtr<PromisePrivate>> mPromises;
|
||||
};
|
||||
|
||||
class AllocationWrapper : public MediaDataDecoder
|
||||
{
|
||||
using Token = GlobalAllocPolicy::Token;
|
||||
|
||||
public:
|
||||
AllocationWrapper(already_AddRefed<MediaDataDecoder> aDecoder,
|
||||
already_AddRefed<Token> aToken);
|
||||
~AllocationWrapper();
|
||||
|
||||
RefPtr<InitPromise> Init() override { return mDecoder->Init(); }
|
||||
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override
|
||||
{
|
||||
return mDecoder->Decode(aSample);
|
||||
}
|
||||
RefPtr<DecodePromise> Drain() override { return mDecoder->Drain(); }
|
||||
RefPtr<FlushPromise> 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<ShutdownPromise> Shutdown() override;
|
||||
|
||||
private:
|
||||
RefPtr<MediaDataDecoder> mDecoder;
|
||||
RefPtr<Token> mToken;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -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'
|
||||
|
|
Загрузка…
Ссылка в новой задаче