1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AllocationPolicy.h"
9 #include "ImageContainer.h"
10 #include "MediaInfo.h"
11 #include "PDMFactory.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/SchedulerGroup.h"
14 #ifdef MOZ_WIDGET_ANDROID
15 # include "mozilla/jni/Utils.h"
20 using TrackType
= TrackInfo::TrackType
;
22 class AllocPolicyImpl::AutoDeallocToken
: public Token
{
24 explicit AutoDeallocToken(const RefPtr
<AllocPolicyImpl
>& aPolicy
)
28 ~AutoDeallocToken() { mPolicy
->Dealloc(); }
30 RefPtr
<AllocPolicyImpl
> mPolicy
;
33 AllocPolicyImpl::AllocPolicyImpl(int aDecoderLimit
)
34 : mMaxDecoderLimit(aDecoderLimit
),
35 mMonitor("AllocPolicyImpl"),
36 mDecoderLimit(aDecoderLimit
) {}
37 AllocPolicyImpl::~AllocPolicyImpl() { RejectAll(); }
39 auto AllocPolicyImpl::Alloc() -> RefPtr
<Promise
> {
40 ReentrantMonitorAutoEnter
mon(mMonitor
);
41 // No decoder limit set.
42 if (mDecoderLimit
< 0) {
43 return Promise::CreateAndResolve(new Token(), __func__
);
46 RefPtr
<PromisePrivate
> p
= new PromisePrivate(__func__
);
52 void AllocPolicyImpl::Dealloc() {
53 ReentrantMonitorAutoEnter
mon(mMonitor
);
58 void AllocPolicyImpl::ResolvePromise(ReentrantMonitorAutoEnter
& aProofOfLock
) {
59 MOZ_ASSERT(mDecoderLimit
>= 0);
61 if (mDecoderLimit
> 0 && !mPromises
.empty()) {
63 RefPtr
<PromisePrivate
> p
= std::move(mPromises
.front());
65 p
->Resolve(new AutoDeallocToken(this), __func__
);
69 void AllocPolicyImpl::RejectAll() {
70 ReentrantMonitorAutoEnter
mon(mMonitor
);
71 while (!mPromises
.empty()) {
72 RefPtr
<PromisePrivate
> p
= std::move(mPromises
.front());
74 p
->Reject(true, __func__
);
78 static int32_t MediaDecoderLimitDefault() {
79 #ifdef MOZ_WIDGET_ANDROID
80 if (jni::GetAPIVersion() < 18) {
81 // Older Android versions have broken support for multiple simultaneous
82 // decoders, see bug 1278574.
86 // Otherwise, set no decoder limit.
90 StaticMutex
GlobalAllocPolicy::sMutex
;
92 NotNull
<AllocPolicy
*> GlobalAllocPolicy::Instance(TrackType aTrack
) {
93 StaticMutexAutoLock
lock(sMutex
);
94 if (aTrack
== TrackType::kAudioTrack
) {
95 static RefPtr
<AllocPolicyImpl
> sAudioPolicy
= []() {
96 SchedulerGroup::Dispatch(NS_NewRunnableFunction(
97 "GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() {
98 ClearOnShutdown(&sAudioPolicy
, ShutdownPhase::XPCOMShutdownThreads
);
100 return new AllocPolicyImpl(MediaDecoderLimitDefault());
102 return WrapNotNull(sAudioPolicy
.get());
104 static RefPtr
<AllocPolicyImpl
> sVideoPolicy
= []() {
105 SchedulerGroup::Dispatch(NS_NewRunnableFunction(
106 "GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() {
107 ClearOnShutdown(&sVideoPolicy
, ShutdownPhase::XPCOMShutdownThreads
);
109 return new AllocPolicyImpl(MediaDecoderLimitDefault());
111 return WrapNotNull(sVideoPolicy
.get());
114 class SingleAllocPolicy::AutoDeallocCombinedToken
: public Token
{
116 AutoDeallocCombinedToken(already_AddRefed
<Token
> aSingleAllocPolicyToken
,
117 already_AddRefed
<Token
> aGlobalAllocPolicyToken
)
118 : mSingleToken(aSingleAllocPolicyToken
),
119 mGlobalToken(aGlobalAllocPolicyToken
) {}
122 // Release tokens allocated from GlobalAllocPolicy and LocalAllocPolicy
123 // and process next token request if any.
124 ~AutoDeallocCombinedToken() = default;
125 const RefPtr
<Token
> mSingleToken
;
126 const RefPtr
<Token
> mGlobalToken
;
129 auto SingleAllocPolicy::Alloc() -> RefPtr
<Promise
> {
130 MOZ_DIAGNOSTIC_ASSERT(MaxDecoderLimit() == 1,
131 "We can only handle at most one token out at a time.");
132 RefPtr
<SingleAllocPolicy
> self
= this;
133 return AllocPolicyImpl::Alloc()->Then(
134 mOwnerThread
, __func__
,
135 [self
](RefPtr
<Token
> aToken
) {
136 RefPtr
<Token
> localToken
= std::move(aToken
);
137 RefPtr
<Promise
> p
= self
->mPendingPromise
.Ensure(__func__
);
138 GlobalAllocPolicy::Instance(self
->mTrack
)
141 self
->mOwnerThread
, __func__
,
142 [self
, localToken
= std::move(localToken
)](
143 RefPtr
<Token
> aToken
) mutable {
144 self
->mTokenRequest
.Complete();
145 RefPtr
<Token
> combinedToken
= new AutoDeallocCombinedToken(
146 localToken
.forget(), aToken
.forget());
147 self
->mPendingPromise
.Resolve(combinedToken
, __func__
);
150 self
->mTokenRequest
.Complete();
151 self
->mPendingPromise
.Reject(true, __func__
);
153 ->Track(self
->mTokenRequest
);
156 []() { return Promise::CreateAndReject(true, __func__
); });
159 SingleAllocPolicy::~SingleAllocPolicy() {
160 mPendingPromise
.RejectIfExists(true, __func__
);
161 mTokenRequest
.DisconnectIfExists();
164 void SingleAllocPolicy::Cancel() {
165 MOZ_ASSERT(mOwnerThread
->IsCurrentThreadIn());
166 mPendingPromise
.RejectIfExists(true, __func__
);
167 mTokenRequest
.DisconnectIfExists();
171 AllocationWrapper::AllocationWrapper(
172 already_AddRefed
<MediaDataDecoder
> aDecoder
, already_AddRefed
<Token
> aToken
)
173 : mDecoder(aDecoder
), mToken(aToken
) {
174 DecoderDoctorLogger::LogConstructionAndBase(
175 "AllocationWrapper", this, static_cast<const MediaDataDecoder
*>(this));
176 DecoderDoctorLogger::LinkParentAndChild("AllocationWrapper", this, "decoder",
180 AllocationWrapper::~AllocationWrapper() {
181 DecoderDoctorLogger::LogDestruction("AllocationWrapper", this);
184 RefPtr
<ShutdownPromise
> AllocationWrapper::Shutdown() {
185 RefPtr
<MediaDataDecoder
> decoder
= std::move(mDecoder
);
186 RefPtr
<Token
> token
= std::move(mToken
);
187 return decoder
->Shutdown()->Then(
188 GetCurrentSerialEventTarget(), __func__
,
189 [token
]() { return ShutdownPromise::CreateAndResolve(true, __func__
); });
191 /* static */ RefPtr
<AllocationWrapper::AllocateDecoderPromise
>
192 AllocationWrapper::CreateDecoder(const CreateDecoderParams
& aParams
,
193 AllocPolicy
* aPolicy
) {
194 RefPtr
<AllocateDecoderPromise
> p
=
195 (aPolicy
? aPolicy
: GlobalAllocPolicy::Instance(aParams
.mType
))
198 GetCurrentSerialEventTarget(), __func__
,
200 CreateDecoderParamsForAsync(aParams
)](RefPtr
<Token
> aToken
) {
201 // result may not always be updated by
202 // PDMFactory::CreateDecoder either when the creation
203 // succeeded or failed, as such it must be initialized to a
204 // fatal error by default.
206 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR
,
207 nsPrintfCString("error creating %s decoder",
208 TrackTypeToStr(params
.mType
)));
209 RefPtr
<PDMFactory
> pdm
= new PDMFactory();
210 RefPtr
<PlatformDecoderModule::CreateDecoderPromise
> p
=
211 pdm
->CreateDecoder(params
)->Then(
212 GetCurrentSerialEventTarget(), __func__
,
213 [aToken
](RefPtr
<MediaDataDecoder
>&& aDecoder
) mutable {
214 RefPtr
<AllocationWrapper
> wrapper
=
215 new AllocationWrapper(aDecoder
.forget(),
217 return AllocateDecoderPromise::CreateAndResolve(
220 [](const MediaResult
& aError
) {
221 return AllocateDecoderPromise::CreateAndReject(
227 return AllocateDecoderPromise::CreateAndReject(
228 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR
,
229 "Allocation policy expired"),
235 } // namespace mozilla