--- a/dom/media/MediaDecoder.h+++ b/dom/media/MediaDecoder.h@@ -1023,17 +1023,22 @@ public: // Increments the parsed and decoded frame counters by the passed in counts. // Can be called on any thread. virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, uint32_t aDropped) override { GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded, aDropped); }- WatchTarget& ReadyStateWatchTarget() { return *mReadyStateWatchTarget; }+ void UpdateReadyState()+ {+ if (mOwner) {+ mOwner->UpdateReadyState();+ }+ } virtual MediaDecoderOwner::NextFrameStatus NextFrameStatus() { return mNextFrameStatus; } protected: virtual ~MediaDecoder(); void SetStateMachineParameters(); static void DormantTimerExpired(nsITimer *aTimer, void *aClosure);@@ -1042,20 +1047,21 @@ protected: void StartDormantTimer(); // Cancel a timer for heuristic dormant. void CancelDormantTimer(); // Return true if the decoder has reached the end of playback bool IsEnded() const;- WatcherHolder mReadyStateWatchTarget;+ // State-watching manager.+ WatchManager<MediaDecoder> mWatchManager; // NextFrameStatus, mirrored from the state machine.- Mirror<MediaDecoderOwner::NextFrameStatus>::Holder mNextFrameStatus;+ Mirror<MediaDecoderOwner::NextFrameStatus> mNextFrameStatus; /****** * The following members should be accessed with the decoder lock held. ******/ // Current decoding position in the stream. This is where the decoder // is up to consuming the stream. This is not adjusted during decoder // seek operations, but it's updated at the end when we start playing@@ -1131,24 +1137,24 @@ protected: nsAutoPtr<DecodedStreamData> mDecodedStream; // Set to one of the valid play states. // This can only be changed on the main thread while holding the decoder // monitor. Thus, it can be safely read while holding the decoder monitor // OR on the main thread. // Any change to the state on the main thread must call NotifyAll on the // monitor so the decode thread can wake up.- Canonical<PlayState>::Holder mPlayState;+ Canonical<PlayState> mPlayState; // This can only be changed on the main thread while holding the decoder // monitor. Thus, it can be safely read while holding the decoder monitor // OR on the main thread. // Any change to the state must call NotifyAll on the monitor. // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING.- Canonical<PlayState>::Holder mNextState;+ Canonical<PlayState> mNextState; public: AbstractCanonical<PlayState>* CanonicalPlayState() { return &mPlayState; } AbstractCanonical<PlayState>* CanonicalNextPlayState() { return &mNextState; } protected: // Position to seek to when the seek notification is received by the // decode thread. // This can only be changed on the main thread while holding the decoder

--- a/dom/media/MediaDecoderOwner.h+++ b/dom/media/MediaDecoderOwner.h@@ -19,16 +19,19 @@ class MediaDecoderOwner { public: // Called by the media decoder to indicate that the download is progressing. virtual void DownloadProgressed() = 0; // Dispatch an asynchronous event to the decoder owner virtual nsresult DispatchAsyncEvent(const nsAString& aName) = 0;+ // Triggers a recomputation of readyState.+ virtual void UpdateReadyState() = 0;+ /** * Fires a timeupdate event. If aPeriodic is true, the event will only * be fired if we've not fired a timeupdate event (for any reason) in the * last 250ms, as required by the spec when the current time is periodically * increasing during playback. */ virtual void FireTimeUpdate(bool aPeriodic) = 0;

--- a/dom/media/MediaDecoderReader.h+++ b/dom/media/MediaDecoderReader.h@@ -162,20 +162,20 @@ public: // in buffering mode. Some readers support a promise-based mechanism by which // they notify the state machine when the data arrives. virtual bool IsWaitForDataSupported() { return false; } virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); } virtual bool HasAudio() = 0; virtual bool HasVideo() = 0;- // The ReadMetadata API is unfortunately synchronous. We should fix that at- // some point, but for now we can make things a bit better by using a- // promise-y API on top of a synchronous call.- nsRefPtr<MetadataPromise> CallReadMetadata();+ // The default implementation of AsyncReadMetadata is implemented in terms of+ // synchronous PreReadMetadata() / ReadMetadata() calls. Implementations may also+ // override AsyncReadMetadata to create a more proper async implementation.+ virtual nsRefPtr<MetadataPromise> AsyncReadMetadata(); // A function that is called before ReadMetadata() call. virtual void PreReadMetadata() {}; // Read header data for all bitstreams in the file. Fills aInfo with // the data required to present the media, and optionally fills *aTags // with tag metadata from the file. // Returns NS_OK on success, or NS_ERROR_FAILURE on failure.

--- a/dom/media/MediaDecoderStateMachine.h+++ b/dom/media/MediaDecoderStateMachine.h@@ -158,16 +158,21 @@ public: void SetAudioCaptured(); // Check if the decoder needs to become dormant state. bool IsDormantNeeded(); // Set/Unset dormant state. void SetDormant(bool aDormant); private:+ // Initialization that needs to happen on the task queue. This is the first+ // task that gets run on the task queue, and is dispatched from the MDSM+ // constructor immediately after the task queue is created.+ void InitializationTask();+ void Shutdown(); public: void DispatchShutdown() { TaskQueue()->Dispatch(NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown)); }@@ -770,16 +775,19 @@ public: // shutting down, the state machine will then release this reference, // causing the decoder to be destroyed. This is accessed on the decode, // state machine, audio and main threads. nsRefPtr<MediaDecoder> mDecoder; // Task queue for running the state machine. nsRefPtr<MediaTaskQueue> mTaskQueue;+ // State-watching manager.+ WatchManager<MediaDecoderStateMachine> mWatchManager;+ // True is we are decoding a realtime stream, like a camera stream. bool mRealTime; // True if we've dispatched a task to run the state machine but the task has // yet to run. bool mDispatchedStateMachine; // Class for managing delayed dispatches of the state machine.@@ -881,35 +889,34 @@ public: int64_t mEndTime; // Will be set when SetDuration has been called with a value != -1 // mDurationSet false doesn't indicate that we do not have a valid duration // as mStartTime and mEndTime could have been set separately. bool mDurationSet; // The current play state and next play state, mirrored from the main thread.- Mirror<MediaDecoder::PlayState>::Holder mPlayState;- Mirror<MediaDecoder::PlayState>::Holder mNextPlayState;+ Mirror<MediaDecoder::PlayState> mPlayState;+ Mirror<MediaDecoder::PlayState> mNextPlayState; // Returns true if we're logically playing, that is, if the Play() has // been called and Pause() has not or we have not yet reached the end // of media. This is irrespective of the seeking state; if the owner // calls Play() and then Seek(), we still count as logically playing. // The decoder monitor must be held. bool IsLogicallyPlaying() { MOZ_ASSERT(OnTaskQueue()); return mPlayState == MediaDecoder::PLAY_STATE_PLAYING || mNextPlayState == MediaDecoder::PLAY_STATE_PLAYING; } // The status of our next frame. Mirrored on the main thread and used to // compute ready state.- WatcherHolder mNextFrameStatusUpdater;- Canonical<NextFrameStatus>::Holder mNextFrameStatus;+ Canonical<NextFrameStatus> mNextFrameStatus; public: AbstractCanonical<NextFrameStatus>* CanonicalNextFrameStatus() { return &mNextFrameStatus; } protected: struct SeekJob { void Steal(SeekJob& aOther) { MOZ_DIAGNOSTIC_ASSERT(!Exists());