Bug 1251460 - MDSM now waits on a promise to enqueue first frame loaded
MediaDecoderStateMachine's EnqueueFIrstFrameLoadedEvent would previously call
into MediaDecoderReader to update MDR's buffered ranges and enqueue the frame
loaded event. This commit aims to instead have the buffered update take place,
and only afterwards enqueue the event. This should remove the possibility that
the event will be fired and handled before the update of the buffered ranges has
taken place.
MozReview-Commit-ID: GP8w2nF4xmj
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -83,16 +83,18 @@ public:
// Note that, conceptually, WaitForData makes sense in a non-exclusive sense.
// But in the current architecture it's only ever used exclusively (by MDSM),
// so we mark it that way to verify our assumptions. If you have a use-case
// for multiple WaitForData consumers, feel free to flip the exclusivity here.
using WaitForDataPromise =
MozPromise<MediaData::Type, WaitForDataRejectValue, IsExclusive>;
+ using BufferedUpdatePromise = MozPromise<bool, bool, IsExclusive>;
+
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
// The caller must ensure that Shutdown() is called before aDecoder is
// destroyed.
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
// on failure.
@@ -227,16 +229,26 @@ public:
void NotifyDataArrived()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
NotifyDataArrivedInternal();
UpdateBuffered();
}
+ // Update the buffered ranges and upon doing so return a promise
+ // to indicate success. Overrides may need to do extra work to ensure
+ // buffered is up to date.
+ virtual RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise()
+ {
+ MOZ_ASSERT(OnTaskQueue());
+ UpdateBuffered();
+ return BufferedUpdatePromise::CreateAndResolve(true, __func__);
+ }
+
virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered()
{
return &mBuffered;
}
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1343,16 +1343,18 @@ MediaDecoderStateMachine::Shutdown()
MOZ_ASSERT(OnTaskQueue());
// Once we've entered the shutdown state here there's no going back.
// Change state before issuing shutdown request to threads so those
// threads can start exiting cleanly during the Shutdown call.
ScheduleStateMachine();
SetState(DECODER_STATE_SHUTDOWN);
+ mBufferedUpdateRequest.DisconnectIfExists();
+
mQueuedSeek.RejectIfExists(__func__);
mPendingSeek.RejectIfExists(__func__);
mCurrentSeek.RejectIfExists(__func__);
#ifdef MOZ_EME
mCDMProxyPromise.DisconnectIfExists();
#endif
@@ -2026,22 +2028,35 @@ MediaDecoderStateMachine::EnqueueLoadedM
Move(visibility));
mSentLoadedMetadataEvent = true;
}
void
MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
{
MOZ_ASSERT(OnTaskQueue());
- MediaDecoderEventVisibility visibility =
- mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed
- : MediaDecoderEventVisibility::Observable;
- mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)),
- Move(visibility));
+ // Track value of mSentFirstFrameLoadedEvent from before updating it
+ bool firstFrameBeenLoaded = mSentFirstFrameLoadedEvent;
mSentFirstFrameLoadedEvent = true;
+ RefPtr<MediaDecoderStateMachine> self = this;
+ mBufferedUpdateRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+ &MediaDecoderReader::UpdateBufferedWithPromise)
+ ->Then(OwnerThread(),
+ __func__,
+ // Resolve
+ [self, firstFrameBeenLoaded]() {
+ self->mBufferedUpdateRequest.Complete();
+ MediaDecoderEventVisibility visibility =
+ firstFrameBeenLoaded ? MediaDecoderEventVisibility::Suppressed
+ : MediaDecoderEventVisibility::Observable;
+ self->mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(self->mInfo)),
+ Move(visibility));
+ },
+ // Reject
+ []() { MOZ_CRASH("Should not reach"); }));
}
bool
MediaDecoderStateMachine::IsDecodingFirstFrame()
{
return mState == DECODER_STATE_DECODING && mDecodingFirstFrame;
}
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -1149,16 +1149,19 @@ private:
MediaInfo mInfo;
nsAutoPtr<MetadataTags> mMetadataTags;
mozilla::MediaMetadataManager mMetadataManager;
mozilla::RollingMean<uint32_t, uint32_t> mCorruptFrames;
+ // Track our request to update the buffered ranges
+ MozPromiseRequestHolder<MediaDecoderReader::BufferedUpdatePromise> mBufferedUpdateRequest;
+
// True if we need to call FinishDecodeFirstFrame() upon frame decoding
// successeeding.
bool mDecodingFirstFrame;
// True if we are back from DECODER_STATE_DORMANT state and
// LoadedMetadataEvent was already sent.
bool mSentLoadedMetadataEvent;
// True if we are back from DECODER_STATE_DORMANT state and
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1605,16 +1605,30 @@ MediaFormatReader::GetBuffered()
if (!intervals.Length() ||
intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
// IntervalSet already starts at 0 or is empty, nothing to shift.
return intervals;
}
return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
}
+// For the MediaFormatReader override we need to force an update to the
+// buffered ranges, so we call NotifyDataArrive
+RefPtr<MediaDecoderReader::BufferedUpdatePromise>
+MediaFormatReader::UpdateBufferedWithPromise() {
+ MOZ_ASSERT(OnTaskQueue());
+ // Call NotifyDataArrive to force a recalculation of the buffered
+ // ranges. UpdateBuffered alone will not force a recalculation, so we
+ // use NotifyDataArrived which sets flags to force this recalculation.
+ // See MediaFormatReader::UpdateReceivedNewData for an example of where
+ // the new data flag is used.
+ NotifyDataArrived();
+ return BufferedUpdatePromise::CreateAndResolve(true, __func__);
+}
+
void MediaFormatReader::ReleaseMediaResources()
{
// Before freeing a video codec, all video buffers needed to be released
// even from graphics pipeline.
if (mVideoFrameContainer) {
mVideoFrameContainer->ClearCurrentFrame();
}
mVideo.mInitPromise.DisconnectIfExists();
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -51,16 +51,18 @@ public:
Seek(SeekTarget aTarget, int64_t aUnused) override;
protected:
void NotifyDataArrivedInternal() override;
public:
media::TimeIntervals GetBuffered() override;
+ RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise() override;
+
bool ForceZeroStartTime() const override;
// For Media Resource Management
void ReleaseMediaResources() override;
nsresult ResetDecode() override;
RefPtr<ShutdownPromise> Shutdown() override;