--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -305,50 +305,105 @@ LocalAllocPolicy::ProcessRequest()
void
LocalAllocPolicy::Cancel()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mPendingPromise.RejectIfExists(true, __func__);
mTokenRequest.DisconnectIfExists();
}
+/**
+ * This class tracks shutdown promises to ensure all decoders are shut down
+ * completely before MFR continues the rest of the shutdown procedure.
+ */
+class MediaFormatReader::ShutdownPromisePool
+{
+public:
+ ShutdownPromisePool()
+ : mOnShutdownComplete(new ShutdownPromise::Private(__func__))
+ {
+ }
+
+ // Return a promise which will be resolved when all the tracking promises
+ // are resolved. Note no more promises should be added for tracking once
+ // this function is called.
+ RefPtr<ShutdownPromise> Shutdown();
+
+ // Track a shutdown promise.
+ void Track(RefPtr<ShutdownPromise> aPromise);
+
+ // Shut down a decoder and track its shutdown promise.
+ void ShutdownDecoder(already_AddRefed<MediaDataDecoder> aDecoder)
+ {
+ Track(RefPtr<MediaDataDecoder>(aDecoder)->Shutdown());
+ }
+
+private:
+ bool mShutdown = false;
+ const RefPtr<ShutdownPromise::Private> mOnShutdownComplete;
+ nsTHashtable<nsRefPtrHashKey<ShutdownPromise>> mPromises;
+};
+
+RefPtr<ShutdownPromise>
+MediaFormatReader::ShutdownPromisePool::Shutdown()
+{
+ MOZ_DIAGNOSTIC_ASSERT(!mShutdown);
+ mShutdown = true;
+ if (mPromises.Count() == 0) {
+ mOnShutdownComplete->Resolve(true, __func__);
+ }
+ return mOnShutdownComplete;
+}
+
+void
+MediaFormatReader::ShutdownPromisePool::Track(RefPtr<ShutdownPromise> aPromise)
+{
+ MOZ_DIAGNOSTIC_ASSERT(!mShutdown);
+ MOZ_DIAGNOSTIC_ASSERT(!mPromises.Contains(aPromise));
+ mPromises.PutEntry(aPromise);
+ aPromise->Then(
+ AbstractThread::GetCurrent(), __func__,
+ [aPromise, this]() {
+ MOZ_DIAGNOSTIC_ASSERT(mPromises.Contains(aPromise));
+ mPromises.RemoveEntry(aPromise);
+ if (mShutdown && mPromises.Count() == 0) {
+ mOnShutdownComplete->Resolve(true, __func__);
+ }
+ });
+}
+
class MediaFormatReader::DecoderFactory
{
using InitPromise = MediaDataDecoder::InitPromise;
using TokenPromise = GlobalAllocPolicy::Promise;
using Token = GlobalAllocPolicy::Token;
public:
explicit DecoderFactory(MediaFormatReader* aOwner)
: mAudio(aOwner->mAudio, TrackInfo::kAudioTrack, aOwner->OwnerThread())
, mVideo(aOwner->mVideo, TrackInfo::kVideoTrack, aOwner->OwnerThread())
, mOwner(WrapNotNull(aOwner)) { }
void CreateDecoder(TrackType aTrack);
- // Shutdown any decoder pending initialization.
- RefPtr<ShutdownPromise> ShutdownDecoder(TrackType aTrack)
+
+ // Shutdown any decoder pending initialization and reset mAudio/mVideo to its
+ // pristine state so CreateDecoder() is ready to be called again immediately.
+ void ShutdownDecoder(TrackType aTrack)
{
MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack
|| aTrack == TrackInfo::kVideoTrack);
auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
data.mPolicy->Cancel();
data.mTokenRequest.DisconnectIfExists();
data.mInitRequest.DisconnectIfExists();
- if (!data.mDecoder) {
- return ShutdownPromise::CreateAndResolve(true, __func__);
+ if (data.mDecoder) {
+ mOwner->mShutdownPromisePool->ShutdownDecoder(data.mDecoder.forget());
}
- if (data.mShutdownRequest.Exists()) {
- // A shutdown is already in progress due to a prior initialization error,
- // return the existing promise.
- data.mShutdownRequest.Disconnect();
- RefPtr<ShutdownPromise> p = data.mShutdownPromise.forget();
- return p;
- }
- RefPtr<MediaDataDecoder> decoder = data.mDecoder.forget();
- return decoder->Shutdown();
+ data.mStage = Stage::None;
+ MOZ_ASSERT(!data.mToken);
}
private:
class Wrapper;
enum class Stage : int8_t
{
None,
@@ -366,18 +421,16 @@ private:
DecoderData& mOwnerData;
const TrackType mTrack;
RefPtr<LocalAllocPolicy> mPolicy;
Stage mStage = Stage::None;
RefPtr<Token> mToken;
RefPtr<MediaDataDecoder> mDecoder;
MozPromiseRequestHolder<TokenPromise> mTokenRequest;
MozPromiseRequestHolder<InitPromise> mInitRequest;
- MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
- RefPtr<ShutdownPromise> mShutdownPromise;
} mAudio, mVideo;
void RunStage(Data& aData);
MediaResult DoCreateDecoder(Data& aData);
void DoInitDecoder(Data& aData);
// guaranteed to be valid by the owner.
const NotNull<MediaFormatReader*> mOwner;
@@ -578,28 +631,18 @@ MediaFormatReader::DecoderFactory::DoIni
mOwner->SetVideoDecodeThreshold();
mOwner->ScheduleUpdate(aTrack);
},
[this, &aData, &ownerData](const MediaResult& aError) {
aData.mInitRequest.Complete();
MOZ_RELEASE_ASSERT(!ownerData.mDecoder,
"Can't have a decoder already set");
aData.mStage = Stage::None;
- aData.mShutdownPromise = aData.mDecoder->Shutdown();
- aData.mShutdownPromise
- ->Then(
- mOwner->OwnerThread(), __func__,
- [this, &aData, aError]() {
- aData.mShutdownRequest.Complete();
- aData.mShutdownPromise = nullptr;
- aData.mDecoder = nullptr;
- mOwner->NotifyError(aData.mTrack, aError);
- },
- []() { MOZ_RELEASE_ASSERT(false, "Can't ever get here"); })
- ->Track(aData.mShutdownRequest);
+ mOwner->mShutdownPromisePool->ShutdownDecoder(aData.mDecoder.forget());
+ mOwner->NotifyError(aData.mTrack, aError);
})
->Track(aData.mInitRequest);
}
// DemuxerProxy ensures that the original main demuxer is only ever accessed
// via its own dedicated task queue.
// This ensure that the reader's taskqueue will never blocked while a demuxer
// is itself blocked attempting to access the MediaCache or the MediaResource.
@@ -973,16 +1016,17 @@ MediaFormatReader::MediaFormatReader(Abs
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
, mInitDone(false)
, mTrackDemuxersMayBlock(false)
, mSeekScheduled(false)
, mVideoFrameContainer(aVideoFrameContainer)
, mDecoderFactory(new DecoderFactory(this))
+ , mShutdownPromisePool(new ShutdownPromisePool())
{
MOZ_ASSERT(aDemuxer);
MOZ_COUNT_CTOR(MediaFormatReader);
if (aDecoder && aDecoder->CompositorUpdatedEvent()) {
mCompositorUpdatedListener =
aDecoder->CompositorUpdatedEvent()->Connect(
mTaskQueue, this, &MediaFormatReader::NotifyCompositorUpdated);
@@ -1010,42 +1054,40 @@ MediaFormatReader::Shutdown()
if (mAudio.HasPromise()) {
mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
}
if (mVideo.HasPromise()) {
mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
}
- nsTArray<RefPtr<ShutdownPromise>> promises;
-
if (HasAudio()) {
mAudio.ResetDemuxer();
mAudio.mTrackDemuxer->BreakCycles();
mAudio.mTrackDemuxer = nullptr;
mAudio.ResetState();
- promises.AppendElement(ShutdownDecoderWithPromise(TrackInfo::kAudioTrack));
+ mShutdownPromisePool->Track(ShutdownDecoderWithPromise(TrackInfo::kAudioTrack));
}
if (HasVideo()) {
mVideo.ResetDemuxer();
mVideo.mTrackDemuxer->BreakCycles();
mVideo.mTrackDemuxer = nullptr;
mVideo.ResetState();
- promises.AppendElement(ShutdownDecoderWithPromise(TrackInfo::kVideoTrack));
+ mShutdownPromisePool->Track(ShutdownDecoderWithPromise(TrackInfo::kVideoTrack));
}
- promises.AppendElement(mDemuxer->Shutdown());
+ mShutdownPromisePool->Track(mDemuxer->Shutdown());
mDemuxer = nullptr;
mCompositorUpdatedListener.DisconnectIfExists();
mOnTrackWaitingForKeyListener.Disconnect();
RefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
- ShutdownPromise::All(OwnerThread(), promises)
+ mShutdownPromisePool->Shutdown()
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::TearDownDecoders,
&MediaFormatReader::TearDownDecoders);
mShutdown = true;
return p;
}
@@ -1070,17 +1112,18 @@ MediaFormatReader::ShutdownDecoderWithPr
return decoder.mShutdownPromise.Ensure(__func__);
}
if (!decoder.mDecoder) {
// Shutdown any decoders that may be in the process of being initialized
// in the Decoder Factory.
// This will be a no-op until we're processing the final decoder shutdown
// prior to the MediaFormatReader being shutdown.
- return mDecoderFactory->ShutdownDecoder(aTrack);
+ mDecoderFactory->ShutdownDecoder(aTrack);
+ return ShutdownPromise::CreateAndResolve(true, __func__);
}
// Finally, let's just shut down the currently active decoder.
decoder.ShutdownDecoder();
return decoder.mShutdownPromise.Ensure(__func__);
}
void
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -570,16 +570,19 @@ private:
RefPtr<GMPCrashHelper> mCrashHelper;
void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
class DecoderFactory;
UniquePtr<DecoderFactory> mDecoderFactory;
+ class ShutdownPromisePool;
+ UniquePtr<ShutdownPromisePool> mShutdownPromisePool;
+
MediaEventListener mCompositorUpdatedListener;
MediaEventListener mOnTrackWaitingForKeyListener;
void OnFirstDemuxCompleted(TrackInfo::TrackType aType,
RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
void OnFirstDemuxFailed(TrackInfo::TrackType aType, const MediaResult& aError);