diff --git a/dom/media/ADTSDemuxer.cpp b/dom/media/ADTSDemuxer.cpp index 9dea02bdb8..01c5cb3ab9 100644 --- a/dom/media/ADTSDemuxer.cpp +++ b/dom/media/ADTSDemuxer.cpp @@ -450,13 +450,6 @@ ADTSTrackDemuxer::Seek(media::TimeUnit aTime) return SeekPromise::CreateAndResolve(seekTime, __func__); } -int64_t -ADTSTrackDemuxer::GetEvictionOffset(media::TimeUnit aTime) -{ - // Unused. - return 0; -} - media::TimeUnit ADTSTrackDemuxer::FastSeek(const media::TimeUnit& aTime) { diff --git a/dom/media/ADTSDemuxer.h b/dom/media/ADTSDemuxer.h index e9ccf2110c..5ea0c4992a 100644 --- a/dom/media/ADTSDemuxer.h +++ b/dom/media/ADTSDemuxer.h @@ -69,8 +69,6 @@ public: int64_t GetResourceOffset() const override; media::TimeIntervals GetBuffered() override; - int64_t GetEvictionOffset(media::TimeUnit aTime) override; - private: // Destructor. ~ADTSTrackDemuxer(); diff --git a/dom/media/DecodedStream.cpp b/dom/media/DecodedStream.cpp index 5d1067fb4b..1271ff173a 100644 --- a/dom/media/DecodedStream.cpp +++ b/dom/media/DecodedStream.cpp @@ -4,13 +4,13 @@ * 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 "DecodedStream.h" -#include "MediaStreamGraph.h" #include "AudioSegment.h" -#include "VideoSegment.h" -#include "MediaQueue.h" +#include "DecodedStream.h" #include "MediaData.h" +#include "MediaQueue.h" +#include "MediaStreamGraph.h" #include "SharedBuffer.h" +#include "VideoSegment.h" #include "VideoUtils.h" namespace mozilla { @@ -18,11 +18,15 @@ namespace mozilla { class DecodedStreamGraphListener : public MediaStreamListener { typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; public: - explicit DecodedStreamGraphListener(MediaStream* aStream) + DecodedStreamGraphListener(MediaStream* aStream, + MozPromiseHolder&& aPromise) : mMutex("DecodedStreamGraphListener::mMutex") , mStream(aStream) , mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime())) - , mStreamFinishedOnMainThread(false) {} + , mStreamFinishedOnMainThread(false) + { + mFinishPromise = Move(aPromise); + } void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) override { @@ -43,6 +47,7 @@ public: void DoNotifyFinished() { + mFinishPromise.ResolveIfExists(true, __func__); MutexAutoLock lock(mMutex); mStreamFinishedOnMainThread = true; } @@ -56,6 +61,7 @@ public: void Forget() { MOZ_ASSERT(NS_IsMainThread()); + mFinishPromise.ResolveIfExists(true, __func__); MutexAutoLock lock(mMutex); mStream = nullptr; } @@ -72,6 +78,8 @@ private: nsRefPtr mStream; int64_t mLastOutputTime; // microseconds bool mStreamFinishedOnMainThread; + // Main thread only. + MozPromiseHolder mFinishPromise; }; static void @@ -87,6 +95,54 @@ UpdateStreamBlocking(MediaStream* aStream, bool aBlocking) } } +/* + * All MediaStream-related data is protected by the decoder's monitor. + * We have at most one DecodedStreamDaata per MediaDecoder. Its stream + * is used as the input for each ProcessedMediaStream created by calls to + * captureStream(UntilEnded). Seeking creates a new source stream, as does + * replaying after the input as ended. In the latter case, the new source is + * not connected to streams created by captureStreamUntilEnded. + */ +class DecodedStreamData { +public: + DecodedStreamData(SourceMediaStream* aStream, bool aPlaying); + ~DecodedStreamData(); + bool IsFinished() const; + int64_t GetPosition() const; + void SetPlaying(bool aPlaying); + + /* The following group of fields are protected by the decoder's monitor + * and can be read or written on any thread. + */ + // Count of audio frames written to the stream + int64_t mAudioFramesWritten; + // mNextVideoTime is the end timestamp for the last packet sent to the stream. + // Therefore video packets starting at or after this time need to be copied + // to the output stream. + int64_t mNextVideoTime; // microseconds + int64_t mNextAudioTime; // microseconds + // The last video image sent to the stream. Useful if we need to replicate + // the image. + nsRefPtr mLastVideoImage; + gfx::IntSize mLastVideoImageDisplaySize; + // This is set to true when the stream is initialized (audio and + // video tracks added). + bool mStreamInitialized; + bool mHaveSentFinish; + bool mHaveSentFinishAudio; + bool mHaveSentFinishVideo; + + // The decoder is responsible for calling Destroy() on this stream. + const nsRefPtr mStream; + nsRefPtr mListener; + bool mPlaying; + // True if we need to send a compensation video frame to ensure the + // StreamTime going forward. + bool mEOSVideoCompensation; + // This promise will be resolved when the SourceMediaStream is finished. + nsRefPtr mFinishPromise; +}; + DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream, bool aPlaying) : mAudioFramesWritten(0) , mNextVideoTime(-1) @@ -99,7 +155,10 @@ DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream, bool aPlaying) , mPlaying(aPlaying) , mEOSVideoCompensation(false) { - mListener = new DecodedStreamGraphListener(mStream); + MozPromiseHolder promise; + mFinishPromise = promise.Ensure(__func__); + // DecodedStreamGraphListener will resolve this promise. + mListener = new DecodedStreamGraphListener(mStream, Move(promise)); mStream->AddListener(mListener); // Block the stream if we are not playing. @@ -138,8 +197,7 @@ DecodedStreamData::SetPlaying(bool aPlaying) class OutputStreamListener : public MediaStreamListener { typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; public: - OutputStreamListener(DecodedStream* aDecodedStream, MediaStream* aStream) - : mDecodedStream(aDecodedStream), mStream(aStream) {} + explicit OutputStreamListener(OutputStreamData* aOwner) : mOwner(aOwner) {} void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override { @@ -153,55 +211,169 @@ public: void Forget() { MOZ_ASSERT(NS_IsMainThread()); - mDecodedStream = nullptr; + mOwner = nullptr; } private: void DoNotifyFinished() { MOZ_ASSERT(NS_IsMainThread()); - if (mDecodedStream) { + if (mOwner) { // Remove the finished stream so it won't block the decoded stream. - mDecodedStream->Remove(mStream); + mOwner->Remove(); } } // Main thread only - DecodedStream* mDecodedStream; - nsRefPtr mStream; + OutputStreamData* mOwner; }; OutputStreamData::~OutputStreamData() { + MOZ_ASSERT(NS_IsMainThread()); mListener->Forget(); + // Break the connection to the input stream if necessary. + if (mPort) { + mPort->Destroy(); + } } void -OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream) +OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream) { + mOwner = aOwner; mStream = aStream; - mListener = new OutputStreamListener(aDecodedStream, aStream); + mListener = new OutputStreamListener(this); aStream->AddListener(mListener); } +void +OutputStreamData::Connect(MediaStream* aStream) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mPort, "Already connected?"); + MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream."); + + // The output stream must stay in sync with the input stream, so if + // either stream is blocked, we block the other. + mPort = mStream->AllocateInputPort(aStream, + MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT); + // Unblock the output stream now. The input stream is responsible for + // controlling blocking from now on. + mStream->ChangeExplicitBlockerCount(-1); +} + +bool +OutputStreamData::Disconnect() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // During cycle collection, DOMMediaStream can be destroyed and send + // its Destroy message before this decoder is destroyed. So we have to + // be careful not to send any messages after the Destroy(). + if (mStream->IsDestroyed()) { + return false; + } + + // Disconnect the existing port if necessary. + if (mPort) { + mPort->Destroy(); + mPort = nullptr; + } + // Block the stream again. It will be unlocked when connecting + // to the input stream. + mStream->ChangeExplicitBlockerCount(1); + return true; +} + +void +OutputStreamData::Remove() +{ + MOZ_ASSERT(NS_IsMainThread()); + mOwner->Remove(mStream); +} + +void +OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded) +{ + MOZ_ASSERT(NS_IsMainThread()); + // Ensure that aStream finishes the moment mDecodedStream does. + if (aFinishWhenEnded) { + aStream->SetAutofinish(true); + } + + OutputStreamData* p = mStreams.AppendElement(); + p->Init(this, aStream); + + // Connect to the input stream if we have one. Otherwise the output stream + // will be connected in Connect(). + if (mInputStream) { + p->Connect(mInputStream); + } +} + +void +OutputStreamManager::Remove(MediaStream* aStream) +{ + MOZ_ASSERT(NS_IsMainThread()); + for (int32_t i = mStreams.Length() - 1; i >= 0; --i) { + if (mStreams[i].Equals(aStream)) { + mStreams.RemoveElementAt(i); + break; + } + } +} + +void +OutputStreamManager::Connect(MediaStream* aStream) +{ + MOZ_ASSERT(NS_IsMainThread()); + mInputStream = aStream; + for (auto&& os : mStreams) { + os.Connect(aStream); + } +} + +void +OutputStreamManager::Disconnect() +{ + MOZ_ASSERT(NS_IsMainThread()); + mInputStream = nullptr; + for (int32_t i = mStreams.Length() - 1; i >= 0; --i) { + if (!mStreams[i].Disconnect()) { + // Probably the DOMMediaStream was GCed. Clean up. + mStreams.RemoveElementAt(i); + } + } +} + DecodedStream::DecodedStream(MediaQueue& aAudioQueue, MediaQueue& aVideoQueue) : mMonitor("DecodedStream::mMonitor") , mPlaying(false) + , mVolume(1.0) , mAudioQueue(aAudioQueue) , mVideoQueue(aVideoQueue) { - // } -void +DecodedStream::~DecodedStream() +{ +} + +nsRefPtr DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo) { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - if (mStartTime.isNothing()) { - mStartTime.emplace(aStartTime); - mInfo = aInfo; - } + MOZ_ASSERT(mStartTime.isNothing(), "playback already started."); + mStartTime.emplace(aStartTime); + mInfo = aInfo; + + // TODO: Unfortunately, current call flow of MDSM guarantees mData is non-null + // when StartPlayback() is called which imposes an obscure dependency on MDSM. + // We will align the life cycle of mData with {Start,Stop}Playback so that + // DecodedStream doesn't need to make assumptions about mData's life cycle. + return mData->mFinishPromise; } void DecodedStream::StopPlayback() @@ -221,27 +393,7 @@ DecodedStream::DestroyData() return; } - // All streams are having their SourceMediaStream disconnected, so they - // need to be explicitly blocked again. - auto& outputStreams = OutputStreams(); - for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) { - OutputStreamData& os = outputStreams[i]; - // Explicitly remove all existing ports. - // This is not strictly necessary but it's good form. - MOZ_ASSERT(os.mPort, "Double-delete of the ports!"); - os.mPort->Destroy(); - os.mPort = nullptr; - // During cycle collection, nsDOMMediaStream can be destroyed and send - // its Destroy message before this decoder is destroyed. So we have to - // be careful not to send any messages after the Destroy(). - if (os.mStream->IsDestroyed()) { - // Probably the DOM MediaStream was GCed. Clean up. - outputStreams.RemoveElementAt(i); - } else { - os.mStream->ChangeExplicitBlockerCount(1); - } - } - + mOutputStreamManager.Disconnect(); mData = nullptr; } @@ -260,38 +412,26 @@ DecodedStream::RecreateData(MediaStreamGraph* aGraph) { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - MOZ_ASSERT((aGraph && !mData && OutputStreams().IsEmpty()) || // first time + MOZ_ASSERT((aGraph && !mData && !HasConsumers()) || // first time (!aGraph && mData)); // 2nd time and later + if (!aGraph) { + aGraph = mData->mStream->Graph(); + } auto source = aGraph->CreateSourceStream(nullptr); DestroyData(); mData.reset(new DecodedStreamData(source, mPlaying)); - // Note that the delay between removing ports in DestroyDecodedStream + // Note that the delay between removing ports in DestroyData // and adding new ones won't cause a glitch since all graph operations // between main-thread stable states take effect atomically. - auto& outputStreams = OutputStreams(); - for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) { - OutputStreamData& os = outputStreams[i]; - MOZ_ASSERT(!os.mStream->IsDestroyed(), "Should've been removed in DestroyData()"); - Connect(&os); - } -} - -nsTArray& -DecodedStream::OutputStreams() -{ - MOZ_ASSERT(NS_IsMainThread()); - GetReentrantMonitor().AssertCurrentThreadIn(); - return mOutputStreams; + mOutputStreamManager.Connect(mData->mStream); } bool DecodedStream::HasConsumers() const { - MOZ_ASSERT(NS_IsMainThread()); - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - return mOutputStreams.IsEmpty(); + return !mOutputStreamManager.IsEmpty(); } ReentrantMonitor& @@ -300,22 +440,6 @@ DecodedStream::GetReentrantMonitor() const return mMonitor; } -void -DecodedStream::Connect(OutputStreamData* aStream) -{ - MOZ_ASSERT(NS_IsMainThread()); - GetReentrantMonitor().AssertCurrentThreadIn(); - NS_ASSERTION(!aStream->mPort, "Already connected?"); - - // The output stream must stay in sync with the decoded stream, so if - // either stream is blocked, we block the other. - aStream->mPort = aStream->mStream->AllocateInputPort(mData->mStream, - MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT); - // Unblock the output stream now. While it's connected to DecodedStream, - // DecodedStream is responsible for controlling blocking. - aStream->mStream->ChangeExplicitBlockerCount(-1); -} - void DecodedStream::Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded) { @@ -326,34 +450,13 @@ DecodedStream::Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded) RecreateData(aStream->Graph()); } - OutputStreamData* os = OutputStreams().AppendElement(); - os->Init(this, aStream); - Connect(os); - if (aFinishWhenEnded) { - // Ensure that aStream finishes the moment mDecodedStream does. - aStream->SetAutofinish(true); - } + mOutputStreamManager.Add(aStream, aFinishWhenEnded); } void DecodedStream::Remove(MediaStream* aStream) { - MOZ_ASSERT(NS_IsMainThread()); - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - - auto& streams = OutputStreams(); - for (int32_t i = streams.Length() - 1; i >= 0; --i) { - auto& os = streams[i]; - MediaStream* p = os.mStream.get(); - if (p == aStream) { - if (os.mPort) { - os.mPort->Destroy(); - os.mPort = nullptr; - } - streams.RemoveElementAt(i); - break; - } - } + mOutputStreamManager.Remove(aStream); } void @@ -366,6 +469,20 @@ DecodedStream::SetPlaying(bool aPlaying) } } +void +DecodedStream::SetVolume(double aVolume) +{ + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + mVolume = aVolume; +} + +void +DecodedStream::SetSameOrigin(bool aSameOrigin) +{ + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + mSameOrigin = aSameOrigin; +} + void DecodedStream::InitTracks() { @@ -613,15 +730,20 @@ DecodedStream::AdvanceTracks() } } -bool -DecodedStream::SendData(double aVolume, bool aIsSameOrigin) +void +DecodedStream::SendData() { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()"); + // Nothing to do when the stream is finished. + if (mData->mHaveSentFinish) { + return; + } + InitTracks(); - SendAudio(aVolume, aIsSameOrigin); - SendVideo(aIsSameOrigin); + SendAudio(mVolume, mSameOrigin); + SendVideo(mSameOrigin); AdvanceTracks(); bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) && @@ -631,8 +753,6 @@ DecodedStream::SendData(double aVolume, bool aIsSameOrigin) mData->mHaveSentFinish = true; mData->mStream->Finish(); } - - return finished; } int64_t diff --git a/dom/media/DecodedStream.h b/dom/media/DecodedStream.h index 39455e67f1..478ec3dcef 100644 --- a/dom/media/DecodedStream.h +++ b/dom/media/DecodedStream.h @@ -7,29 +7,29 @@ #ifndef DecodedStream_h_ #define DecodedStream_h_ -#include "mozilla/nsRefPtr.h" #include "nsTArray.h" #include "MediaInfo.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/Maybe.h" +#include "mozilla/MozPromise.h" +#include "mozilla/nsRefPtr.h" +#include "mozilla/ReentrantMonitor.h" #include "mozilla/UniquePtr.h" #include "mozilla/gfx/Point.h" -#include "mozilla/CheckedInt.h" -#include "mozilla/ReentrantMonitor.h" -#include "mozilla/Maybe.h" namespace mozilla { -class MediaData; -class AudioSegment; -class MediaStream; -class MediaInputPort; -class SourceMediaStream; -class ProcessedMediaStream; class DecodedStream; -class DecodedStreamGraphListener; -class OutputStreamListener; -class ReentrantMonitor; +class DecodedStreamData; +class MediaData; +class MediaInputPort; +class MediaStream; class MediaStreamGraph; +class OutputStreamListener; +class OutputStreamManager; +class ProcessedMediaStream; +class ReentrantMonitor; template class MediaQueue; @@ -37,62 +37,58 @@ namespace layers { class Image; } // namespace layers -/* - * All MediaStream-related data is protected by the decoder's monitor. - * We have at most one DecodedStreamDaata per MediaDecoder. Its stream - * is used as the input for each ProcessedMediaStream created by calls to - * captureStream(UntilEnded). Seeking creates a new source stream, as does - * replaying after the input as ended. In the latter case, the new source is - * not connected to streams created by captureStreamUntilEnded. - */ -class DecodedStreamData { -public: - DecodedStreamData(SourceMediaStream* aStream, bool aPlaying); - ~DecodedStreamData(); - bool IsFinished() const; - int64_t GetPosition() const; - void SetPlaying(bool aPlaying); - - /* The following group of fields are protected by the decoder's monitor - * and can be read or written on any thread. - */ - // Count of audio frames written to the stream - int64_t mAudioFramesWritten; - // mNextVideoTime is the end timestamp for the last packet sent to the stream. - // Therefore video packets starting at or after this time need to be copied - // to the output stream. - int64_t mNextVideoTime; // microseconds - int64_t mNextAudioTime; // microseconds - // The last video image sent to the stream. Useful if we need to replicate - // the image. - nsRefPtr mLastVideoImage; - gfx::IntSize mLastVideoImageDisplaySize; - // This is set to true when the stream is initialized (audio and - // video tracks added). - bool mStreamInitialized; - bool mHaveSentFinish; - bool mHaveSentFinishAudio; - bool mHaveSentFinishVideo; - - // The decoder is responsible for calling Destroy() on this stream. - const nsRefPtr mStream; - nsRefPtr mListener; - bool mPlaying; - // True if we need to send a compensation video frame to ensure the - // StreamTime going forward. - bool mEOSVideoCompensation; -}; - class OutputStreamData { public: ~OutputStreamData(); - void Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream); + void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream); + + // Connect mStream to the input stream. + void Connect(MediaStream* aStream); + // Disconnect mStream from its input stream. + // Return false is mStream is already destroyed, otherwise true. + bool Disconnect(); + // Called by OutputStreamListener to remove self from the output streams + // managed by OutputStreamManager. + void Remove(); + // Return true if aStream points to the same object as mStream. + // Used by OutputStreamManager to remove an output stream. + bool Equals(MediaStream* aStream) + { + return mStream == aStream; + } + +private: + OutputStreamManager* mOwner; nsRefPtr mStream; - // mPort connects DecodedStreamData::mStream to our mStream. + // mPort connects our mStream to an input stream. nsRefPtr mPort; nsRefPtr mListener; }; +class OutputStreamManager { +public: + // Add the output stream to the collection. + void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded); + // Remove the output stream from the collection. + void Remove(MediaStream* aStream); + // Return true if the collection empty. + bool IsEmpty() const + { + MOZ_ASSERT(NS_IsMainThread()); + return mStreams.IsEmpty(); + } + // Connect all output streams in the collection to the input stream. + void Connect(MediaStream* aStream); + // Disconnect all output streams from the input stream. + void Disconnect(); + +private: + // Keep the input stream so we can connect the output streams that + // are added after Connect(). + nsRefPtr mInputStream; + nsTArray mStreams; +}; + class DecodedStream { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream); public: @@ -101,7 +97,11 @@ public: // Mimic MDSM::StartAudioThread. // Must be called before any calls to SendData(). - void StartPlayback(int64_t aStartTime, const MediaInfo& aInfo); + // + // Return a promise which will be resolved when the stream is finished + // or rejected if any error. + nsRefPtr StartPlayback(int64_t aStartTime, + const MediaInfo& aInfo); // Mimic MDSM::StopAudioThread. void StopPlayback(); @@ -109,23 +109,24 @@ public: void RecreateData(); void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded); void Remove(MediaStream* aStream); + void SetPlaying(bool aPlaying); + void SetVolume(double aVolume); + void SetSameOrigin(bool aSameOrigin); + int64_t AudioEndTime() const; int64_t GetPosition() const; bool IsFinished() const; bool HasConsumers() const; - // Return true if stream is finished. - bool SendData(double aVolume, bool aIsSameOrigin); + void SendData(); protected: - virtual ~DecodedStream() {} + virtual ~DecodedStream(); private: ReentrantMonitor& GetReentrantMonitor() const; void RecreateData(MediaStreamGraph* aGraph); - void Connect(OutputStreamData* aStream); - nsTArray& OutputStreams(); void InitTracks(); void AdvanceTracks(); void SendAudio(double aVolume, bool aIsSameOrigin); @@ -133,7 +134,7 @@ private: UniquePtr mData; // Data about MediaStreams that are being fed by the decoder. - nsTArray mOutputStreams; + OutputStreamManager mOutputStreamManager; // TODO: This is a temp solution to get rid of decoder monitor on the main // thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as @@ -145,6 +146,9 @@ private: mutable ReentrantMonitor mMonitor; bool mPlaying; + double mVolume; + bool mSameOrigin; + Maybe mStartTime; MediaInfo mInfo; diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h index e201e74d79..48df457ed8 100644 --- a/dom/media/MediaData.h +++ b/dom/media/MediaData.h @@ -308,7 +308,7 @@ public: bool aKeyframe, int64_t aTimecode, IntSize aDisplay, - int32_t aFrameID); + uint32_t aFrameID); protected: ~VideoData(); diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 20361e16f3..6199ee8816 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -345,7 +345,6 @@ MediaDecoder::MediaDecoder() : mLogicalPosition(0.0), mDuration(std::numeric_limits::quiet_NaN()), mMediaSeekable(true), - mSameOriginMedia(false), mReentrantMonitor("media.decoder"), mIgnoreProgressData(false), mInfiniteStream(false), @@ -391,7 +390,9 @@ MediaDecoder::MediaDecoder() : mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED, "MediaDecoder::mNextState (Canonical)"), mLogicallySeeking(AbstractThread::MainThread(), false, - "MediaDecoder::mLogicallySeeking (Canonical)") + "MediaDecoder::mLogicallySeeking (Canonical)"), + mSameOriginMedia(AbstractThread::MainThread(), false, + "MediaDecoder::mSameOriginMedia (Canonical)") { MOZ_COUNT_CTOR(MediaDecoder); MOZ_ASSERT(NS_IsMainThread()); @@ -518,7 +519,7 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor) // set them now SetStateMachineParameters(); - return ScheduleStateMachine(); + return NS_OK; } void MediaDecoder::SetStateMachineParameters() @@ -540,19 +541,6 @@ void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts() MOZ_DIAGNOSTIC_ASSERT(!mDecoderStateMachine); } -nsresult MediaDecoder::ScheduleStateMachine() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (mShuttingDown) - return NS_OK; - - MOZ_ASSERT(mDecoderStateMachine); - NS_ENSURE_STATE(mDecoderStateMachine); - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - mDecoderStateMachine->ScheduleStateMachineCrossThread(); - return NS_OK; -} - nsresult MediaDecoder::Play() { MOZ_ASSERT(NS_IsMainThread()); @@ -563,7 +551,7 @@ nsresult MediaDecoder::Play() if (mPausedForPlaybackRateNull) { return NS_OK; } - ScheduleStateMachine(); + if (IsEnded()) { return Seek(0, SeekTarget::PrevSyncPoint); } else if (mPlayState == PLAY_STATE_LOADING) { @@ -768,12 +756,6 @@ void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin) mSameOriginMedia = aSameOrigin; } -bool MediaDecoder::IsSameOriginMedia() -{ - GetReentrantMonitor().AssertCurrentThreadIn(); - return mSameOriginMedia; -} - bool MediaDecoder::IsSeeking() const { MOZ_ASSERT(NS_IsMainThread()); @@ -1012,7 +994,6 @@ void MediaDecoder::ChangeState(PlayState aState) } if (mPlayState == PLAY_STATE_SHUTDOWN) { - GetReentrantMonitor().NotifyAll(); return; } @@ -1026,13 +1007,9 @@ void MediaDecoder::ChangeState(PlayState aState) RemoveMediaTracks(); } - ScheduleStateMachine(); - CancelDormantTimer(); // Start dormant timer if necessary StartDormantTimer(); - - GetReentrantMonitor().NotifyAll(); } void MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility) diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index d0e2d8a045..d301ab5bd7 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -569,10 +569,6 @@ public: // The actual playback rate computation. The monitor must be held. virtual double ComputePlaybackRate(bool* aReliable); - // Return true when the media is same-origin with the element. The monitor - // must be held. - bool IsSameOriginMedia(); - // Returns true if we can play the entire media through without stopping // to buffer, given the current download and playback rates. virtual bool CanPlayThrough(); @@ -709,10 +705,6 @@ public: static bool IsAppleMP3Enabled(); #endif - // Schedules the state machine to run one cycle on the shared state - // machine thread. Main thread only. - nsresult ScheduleStateMachine(); - struct Statistics { // Estimate of the current playback rate (bytes/second). double mPlaybackRate; @@ -932,10 +924,6 @@ protected: // True if the media is seekable (i.e. supports random access). bool mMediaSeekable; - // True if the media is same-origin with the element. Data can only be - // passed to MediaStreams when this is true. - bool mSameOriginMedia; - /****** * The following member variables can be accessed from any thread. ******/ @@ -1094,20 +1082,20 @@ protected: // 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 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 mNextState; // True if the decoder is seeking. Canonical mLogicallySeeking; + // True if the media is same-origin with the element. Data can only be + // passed to MediaStreams when this is true. + Canonical mSameOriginMedia; + public: AbstractCanonical* CanonicalDurationOrNull() override; AbstractCanonical* CanonicalVolume() { @@ -1134,6 +1122,9 @@ public: AbstractCanonical* CanonicalLogicallySeeking() { return &mLogicallySeeking; } + AbstractCanonical* CanonicalSameOriginMedia() { + return &mSameOriginMedia; + } }; } // namespace mozilla diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index e64b4223fa..801e0fddfc 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -14,7 +14,7 @@ #include "MediaDecoderStateMachine.h" #include "MediaTimer.h" -#include "AudioSink.h" +#include "mediasink/DecodedAudioDataSink.h" #include "nsTArray.h" #include "MediaDecoder.h" #include "MediaDecoderReader.h" @@ -39,6 +39,7 @@ #include "nsPrintfCString.h" #include "DOMMediaStream.h" #include "DecodedStream.h" +#include "mozilla/Logging.h" #include @@ -216,7 +217,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mCorruptFrames(30), mDecodingFirstFrame(true), mDisabledHardwareAcceleration(false), - mDecodingFrozenAtStateDecoding(false), mSentLoadedMetadataEvent(false), mSentFirstFrameLoadedEvent(false), mSentPlaybackEndedEvent(false), @@ -239,6 +239,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, "MediaDecoderStateMachine::mLogicalPlaybackRate (Mirror)"), mPreservesPitch(mTaskQueue, true, "MediaDecoderStateMachine::mPreservesPitch (Mirror)"), + mSameOriginMedia(mTaskQueue, false, + "MediaDecoderStateMachine::mSameOriginMedia (Mirror)"), mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mDuration (Canonical"), mIsShutdown(mTaskQueue, false, @@ -278,17 +280,10 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, timeBeginPeriod(1); #endif - nsRefPtr self = this; - - AudioQueue().AddPopListener( - [self] (const MediaData* aSample) { - self->OnAudioPopped(aSample->As()); - }, mTaskQueue); - - VideoQueue().AddPopListener( - [self] (const MediaData* aSample) { - self->OnVideoPopped(aSample->As()); - }, mTaskQueue); + mAudioQueueListener = AudioQueue().PopEvent().Connect( + mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped); + mVideoQueueListener = VideoQueue().PopEvent().Connect( + mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped); } MediaDecoderStateMachine::~MediaDecoderStateMachine() @@ -318,6 +313,7 @@ MediaDecoderStateMachine::InitializationTask() mVolume.Connect(mDecoder->CanonicalVolume()); mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate()); mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch()); + mSameOriginMedia.Connect(mDecoder->CanonicalSameOriginMedia()); // Initialize watchers. mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated); @@ -331,6 +327,7 @@ MediaDecoderStateMachine::InitializationTask() mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration); mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged); mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged); + mWatchManager.Watch(mSameOriginMedia, &MediaDecoderStateMachine::SameOriginMediaChanged); } bool MediaDecoderStateMachine::HasFutureAudio() @@ -374,7 +371,7 @@ void MediaDecoderStateMachine::SendStreamData() AssertCurrentThreadInMonitor(); MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()"); - bool finished = mDecodedStream->SendData(mVolume, mDecoder->IsSameOriginMedia()); + mDecodedStream->SendData(); const auto clockTime = GetClock(); while (true) { @@ -390,12 +387,6 @@ void MediaDecoderStateMachine::SendStreamData() } break; } - - // To be consistent with AudioSink, |mAudioCompleted| is not set - // until all samples are drained. - if (finished && AudioQueue().GetSize() == 0) { - mAudioCompleted = true; - } } bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs) @@ -659,10 +650,6 @@ MediaDecoderStateMachine::Push(VideoData* aSample) VideoQueue().Push(aSample); UpdateNextFrameStatus(); DispatchDecodeTasksIfNeeded(); - - if (mAudioSink) { - mAudioSink->NotifyData(); - } } void @@ -677,7 +664,7 @@ MediaDecoderStateMachine::PushFront(VideoData* aSample) } void -MediaDecoderStateMachine::OnAudioPopped(const AudioData* aSample) +MediaDecoderStateMachine::OnAudioPopped(const nsRefPtr& aSample) { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); @@ -687,16 +674,13 @@ MediaDecoderStateMachine::OnAudioPopped(const AudioData* aSample) } void -MediaDecoderStateMachine::OnVideoPopped(const VideoData* aSample) +MediaDecoderStateMachine::OnVideoPopped(const nsRefPtr& aSample) { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mDecoder->UpdatePlaybackOffset(aSample->mOffset); UpdateNextFrameStatus(); DispatchVideoDecodeTaskIfNeeded(); - // Notify the decode thread that the video queue's buffers may have - // free'd up space for more frames. - mDecoder->GetReentrantMonitor().NotifyAll(); } void @@ -777,11 +761,6 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType, return; } CheckIfDecodeComplete(); - mDecoder->GetReentrantMonitor().NotifyAll(); - // Tell AudioSink to wake up for audio queue is finished. - if (mAudioSink) { - mAudioSink->NotifyData(); - } // Schedule the state machine to notify track ended as soon as possible. if (mAudioCaptured) { ScheduleStateMachine(); @@ -1034,7 +1013,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) nsresult rv = mReader->Init(cloneReader); NS_ENSURE_SUCCESS(rv, rv); - + ScheduleStateMachineCrossThread(); return NS_OK; } @@ -1088,15 +1067,9 @@ void MediaDecoderStateMachine::MaybeStartPlayback() SetPlayStartTime(TimeStamp::Now()); MOZ_ASSERT(IsPlaying()); - StartAudioThread(); + StartAudioSink(); + StartDecodedStream(); - // Tell DecodedStream to start playback with specified start time and media - // info. This is consistent with how we create AudioSink in StartAudioThread(). - if (mAudioCaptured) { - mDecodedStream->StartPlayback(GetMediaTime(), mInfo); - } - - mDecoder->GetReentrantMonitor().NotifyAll(); DispatchDecodeTasksIfNeeded(); } @@ -1172,6 +1145,7 @@ void MediaDecoderStateMachine::VolumeChanged() if (mAudioSink) { mAudioSink->SetVolume(mVolume); } + mDecodedStream->SetVolume(mVolume); } void MediaDecoderStateMachine::RecomputeDuration() @@ -1259,13 +1233,10 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant) // it here as well. nsCOMPtr r = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources); DecodeTaskQueue()->Dispatch(r.forget()); - mDecoder->GetReentrantMonitor().NotifyAll(); } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) { - mDecodingFrozenAtStateDecoding = true; ScheduleStateMachine(); mDecodingFirstFrame = true; SetState(DECODER_STATE_DECODING_NONE); - mDecoder->GetReentrantMonitor().NotifyAll(); } } @@ -1386,11 +1357,6 @@ void MediaDecoderStateMachine::PlayStateChanged() DispatchDecodeTasksIfNeeded(); } - if (mDecodingFrozenAtStateDecoding) { - mDecodingFrozenAtStateDecoding = false; - DispatchDecodeTasksIfNeeded(); - } - // Some state transitions still happen synchronously on the main thread. So // if the main thread invokes Play() and then Seek(), the seek will initiate // synchronously on the main thread, and the asynchronous PlayInternal task @@ -1422,6 +1388,13 @@ void MediaDecoderStateMachine::LogicallySeekingChanged() ScheduleStateMachine(); } +void MediaDecoderStateMachine::SameOriginMediaChanged() +{ + MOZ_ASSERT(OnTaskQueue()); + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + mDecodedStream->SetSameOrigin(mSameOriginMedia); +} + void MediaDecoderStateMachine::BufferedRangeUpdated() { MOZ_ASSERT(OnTaskQueue()); @@ -1446,8 +1419,6 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget) MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - mDecodingFrozenAtStateDecoding = false; - if (IsShutdown()) { return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__); } @@ -1480,7 +1451,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget) return mPendingSeek.mPromise.Ensure(__func__); } -void MediaDecoderStateMachine::StopAudioThread() +void MediaDecoderStateMachine::StopAudioSink() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); @@ -1505,11 +1476,6 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded() return; } - if (mState == DECODER_STATE_DECODING && mDecodingFrozenAtStateDecoding) { - DECODER_LOG("DispatchDecodeTasksIfNeeded return due to " - "mFreezeDecodingAtStateDecoding"); - return; - } // NeedToDecodeAudio() can go from false to true while we hold the // monitor, but it can't go from true to false. This can happen because // NeedToDecodeAudio() takes into account the amount of decoded audio @@ -1778,7 +1744,7 @@ MediaDecoderStateMachine::RequestVideoData() } void -MediaDecoderStateMachine::StartAudioThread() +MediaDecoderStateMachine::StartAudioSink() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); @@ -1789,9 +1755,9 @@ MediaDecoderStateMachine::StartAudioThread() if (HasAudio() && !mAudioSink) { mAudioCompleted = false; - mAudioSink = new AudioSink(mAudioQueue, - GetMediaTime(), mInfo.mAudio, - mDecoder->GetAudioChannel()); + mAudioSink = new DecodedAudioDataSink(mAudioQueue, + GetMediaTime(), mInfo.mAudio, + mDecoder->GetAudioChannel()); mAudioSinkPromise.Begin( mAudioSink->Init()->Then( @@ -1805,6 +1771,32 @@ MediaDecoderStateMachine::StartAudioThread() } } +void +MediaDecoderStateMachine::StopDecodedStream() +{ + MOZ_ASSERT(OnTaskQueue()); + AssertCurrentThreadInMonitor(); + mDecodedStream->StopPlayback(); + mDecodedStreamPromise.DisconnectIfExists(); +} + +void +MediaDecoderStateMachine::StartDecodedStream() +{ + MOZ_ASSERT(OnTaskQueue()); + AssertCurrentThreadInMonitor(); + + // Tell DecodedStream to start playback with specified start time and media + // info. This is consistent with how we create AudioSink in StartAudioThread(). + if (mAudioCaptured && !mDecodedStreamPromise.Exists()) { + mDecodedStreamPromise.Begin( + mDecodedStream->StartPlayback(GetMediaTime(), mInfo)->Then( + OwnerThread(), __func__, this, + &MediaDecoderStateMachine::OnDecodedStreamFinish, + &MediaDecoderStateMachine::OnDecodedStreamError)); + } +} + int64_t MediaDecoderStateMachine::AudioDecodedUsecs() { MOZ_ASSERT(OnTaskQueue()); @@ -1906,10 +1898,6 @@ MediaDecoderStateMachine::DecodeError() ScheduleStateMachine(); DECODER_WARN("Decode error, changed state to ERROR"); - // Is anybody actually waiting on this monitor, or is it just - // a leftover from when we used to do sync dispatch for the below? - mDecoder->GetReentrantMonitor().NotifyAll(); - // MediaDecoder::DecodeError notifies the owner, and then shuts down the state // machine. nsCOMPtr event = @@ -2203,8 +2191,10 @@ MediaDecoderStateMachine::FinishShutdown() // The reader's listeners hold references to the state machine, // creating a cycle which keeps the state machine and its shared // thread pools alive. So break it here. - AudioQueue().ClearListeners(); - VideoQueue().ClearListeners(); + + // Prevent dangling pointers by disconnecting the listeners. + mAudioQueueListener.Disconnect(); + mVideoQueueListener.Disconnect(); // Disconnect canonicals and mirrors before shutting down our task queue. mBuffered.DisconnectIfConnected(); @@ -2216,6 +2206,7 @@ MediaDecoderStateMachine::FinishShutdown() mVolume.DisconnectIfConnected(); mLogicalPlaybackRate.DisconnectIfConnected(); mPreservesPitch.DisconnectIfConnected(); + mSameOriginMedia.DisconnectIfConnected(); mDuration.DisconnectAll(); mIsShutdown.DisconnectAll(); mNextFrameStatus.DisconnectAll(); @@ -2350,8 +2341,6 @@ nsresult MediaDecoderStateMachine::RunStateMachine() DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds()); StartDecoding(); - // Notify to allow blocked decoder thread to continue - mDecoder->GetReentrantMonitor().NotifyAll(); NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled"); return NS_OK; } @@ -2407,12 +2396,12 @@ nsresult MediaDecoderStateMachine::RunStateMachine() AbstractThread::MainThread()->Dispatch(event.forget()); mSentPlaybackEndedEvent = true; - } - // Stop audio sink after call to AudioEndTime() above, otherwise it will - // return an incorrect value due to a null mAudioSink. - StopAudioThread(); - mDecodedStream->StopPlayback(); + // Stop audio sink after call to AudioEndTime() above, otherwise it will + // return an incorrect value due to a null mAudioSink. + StopAudioSink(); + StopDecodedStream(); + } return NS_OK; } @@ -2440,8 +2429,8 @@ MediaDecoderStateMachine::Reset() // Stop the audio thread. Otherwise, AudioSink might be accessing AudioQueue // outside of the decoder monitor while we are clearing the queue and causes // crash for no samples to be popped. - StopAudioThread(); - mDecodedStream->StopPlayback(); + StopAudioSink(); + StopDecodedStream(); mVideoFrameEndTime = -1; mDecodedVideoEndTime = -1; @@ -2487,6 +2476,7 @@ void MediaDecoderStateMachine::CheckTurningOffHardwareDecoder(VideoData* aData) NS_NewRunnableMethod(mReader, &MediaDecoderReader::DisableHardwareAcceleration); DecodeTaskQueue()->Dispatch(task.forget()); mDisabledHardwareAcceleration = true; + gfxCriticalNote << "Too many dropped/corrupted frames, disabling DXVA"; } } else { mCorruptFrames.insert(0); @@ -2541,7 +2531,7 @@ void MediaDecoderStateMachine::RenderVideoFrames(int32_t aMaxFrames, img->mFrameID = frame->mFrameID; img->mProducerID = mProducerID; - VERBOSE_LOG("playing video frame %lld (id=%d) (queued=%i, state-machine=%i, decoder-queued=%i)", + VERBOSE_LOG("playing video frame %lld (id=%x) (queued=%i, state-machine=%i, decoder-queued=%i)", frame->mTime, frame->mFrameID, VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(), VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames()); @@ -3057,7 +3047,7 @@ MediaDecoderStateMachine::AudioEndTime() const return mDecodedStream->AudioEndTime(); } // Don't call this after mAudioSink becomes null since we can't distinguish - // "before StartAudioThread" and "after StopAudioThread" where mAudioSink + // "before StartAudioSink" and "after StopAudioSink" where mAudioSink // is null in both cases. MOZ_ASSERT(!mAudioCompleted); return -1; @@ -3072,9 +3062,6 @@ void MediaDecoderStateMachine::OnAudioSinkComplete() mAudioSinkPromise.Complete(); ResyncAudioClock(); mAudioCompleted = true; - - // Kick the decode thread; it may be sleeping waiting for this to finish. - mDecoder->GetReentrantMonitor().NotifyAll(); } void MediaDecoderStateMachine::OnAudioSinkError() @@ -3097,9 +3084,35 @@ void MediaDecoderStateMachine::OnAudioSinkError() DecodeError(); } +void +MediaDecoderStateMachine::OnDecodedStreamFinish() +{ + MOZ_ASSERT(OnTaskQueue()); + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + MOZ_ASSERT(mAudioCaptured, "Audio should be captured."); + + mDecodedStreamPromise.Complete(); + if (mInfo.HasAudio()) { + mAudioCompleted = true; + } + // To notify PlaybackEnded as soon as possible. + ScheduleStateMachine(); +} + +void +MediaDecoderStateMachine::OnDecodedStreamError() +{ + MOZ_ASSERT(OnTaskQueue()); + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + MOZ_ASSERT(mAudioCaptured, "Audio should be captured."); + + mDecodedStreamPromise.Complete(); + DecodeError(); +} uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const { + MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated()) ? std::max(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE) @@ -3115,12 +3128,12 @@ void MediaDecoderStateMachine::DispatchAudioCaptured() ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); if (!self->mAudioCaptured) { // Stop the audio sink if it's running. - self->StopAudioThread(); + self->StopAudioSink(); self->mAudioCaptured = true; // Start DecodedStream if we are already playing. Otherwise it will be // handled in MaybeStartPlayback(). if (self->IsPlaying()) { - self->mDecodedStream->StartPlayback(self->GetMediaTime(), self->mInfo); + self->StartDecodedStream(); } self->ScheduleStateMachine(); } @@ -3136,10 +3149,11 @@ void MediaDecoderStateMachine::DispatchAudioUncaptured() MOZ_ASSERT(self->OnTaskQueue()); ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); if (self->mAudioCaptured) { - // Start again the audio sink + self->StopDecodedStream(); + // Start again the audio sink. self->mAudioCaptured = false; if (self->IsPlaying()) { - self->StartAudioThread(); + self->StartAudioSink(); } self->ScheduleStateMachine(); } diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 188b0d2db5..f3ca4813f9 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -91,6 +91,7 @@ hardware (via AudioStream). #include "MediaDecoder.h" #include "MediaDecoderReader.h" #include "MediaDecoderOwner.h" +#include "MediaEventSource.h" #include "MediaMetadataManager.h" #include "MediaTimer.h" #include "DecodedStream.h" @@ -98,8 +99,11 @@ hardware (via AudioStream). namespace mozilla { -class AudioSegment; +namespace media { class AudioSink; +} + +class AudioSegment; class TaskQueue; extern PRLogModuleInfo* gMediaDecoderLog; @@ -111,9 +115,8 @@ extern PRLogModuleInfo* gMediaSampleLog; state machine thread, and controls the audio "push" thread. All internal state is synchronised via the decoder monitor. State changes - are either propagated by NotifyAll on the monitor (typically when state - changes need to be propagated to non-state machine threads) or by scheduling - the state machine to run another cycle on the shared state machine thread. + are propagated by scheduling the state machine to run another cycle on the + shared state machine thread. See MediaDecoder.h for more details. */ @@ -399,8 +402,8 @@ protected: void PushFront(AudioData* aSample); void PushFront(VideoData* aSample); - void OnAudioPopped(const AudioData* aSample); - void OnVideoPopped(const VideoData* aSample); + void OnAudioPopped(const nsRefPtr& aSample); + void OnVideoPopped(const nsRefPtr& aSample); void VolumeChanged(); void LogicalPlaybackRateChanged(); @@ -510,13 +513,19 @@ protected: // state machine thread. void UpdateRenderedVideoFrames(); - // Stops the audio thread. The decoder monitor must be held with exactly - // one lock count. Called on the state machine thread. - void StopAudioThread(); + // Stops the audio sink and shut it down. + // The decoder monitor must be held with exactly one lock count. + // Called on the state machine thread. + void StopAudioSink(); - // Starts the audio thread. The decoder monitor must be held with exactly - // one lock count. Called on the state machine thread. - void StartAudioThread(); + // Create and start the audio sink. + // The decoder monitor must be held with exactly one lock count. + // Called on the state machine thread. + void StartAudioSink(); + + void StopDecodedStream(); + + void StartDecodedStream(); // Notification method invoked when mPlayState changes. void PlayStateChanged(); @@ -524,6 +533,9 @@ protected: // Notification method invoked when mLogicallySeeking changes. void LogicallySeekingChanged(); + // Notification method invoked when mSameOriginMedia changes. + void SameOriginMediaChanged(); + // Sets internal state which causes playback of media to pause. // The decoder monitor must be held. void StopPlayback(); @@ -667,6 +679,10 @@ private: // Rejected by the AudioSink to signal errors. void OnAudioSinkError(); + void OnDecodedStreamFinish(); + + void OnDecodedStreamError(); + // Return true if the video decoder's decode speed can not catch up the // play time. bool NeedToSkipToNextKeyframe(); @@ -885,8 +901,6 @@ private: MediaQueue mVideoQueue; // The decoder monitor must be obtained before modifying this state. - // NotifyAll on the monitor must be called when the state is changed so - // that interested threads can wake up and alter behaviour if appropriate // Accessed on state machine, audio, main, and AV thread. Watchable mState; @@ -988,7 +1002,7 @@ private: int64_t mFragmentEndTime; // The audio sink resource. Used on state machine and audio threads. - RefPtr mAudioSink; + RefPtr mAudioSink; // The reader, don't call its methods with the decoder monitor held. // This is created in the state machine's constructor. @@ -1259,10 +1273,6 @@ private: bool mDisabledHardwareAcceleration; - // mDecodingFrozenAtStateDecoding: turn on/off at - // SetDormant/Seek,Play. - bool mDecodingFrozenAtStateDecoding; - // True if we are back from DECODER_STATE_DORMANT state and // LoadedMetadataEvent was already sent. bool mSentLoadedMetadataEvent; @@ -1286,6 +1296,10 @@ private: nsRefPtr mResource; MozPromiseRequestHolder mAudioSinkPromise; + MozPromiseRequestHolder mDecodedStreamPromise; + + MediaEventListener mAudioQueueListener; + MediaEventListener mVideoQueueListener; private: // The buffered range. Mirrored from the decoder thread. @@ -1313,6 +1327,10 @@ private: // Pitch preservation for the playback rate. Mirror mPreservesPitch; + // True if the media is same-origin with the element. Data can only be + // passed to MediaStreams when this is true. + Mirror mSameOriginMedia; + // Duration of the media. This is guaranteed to be non-null after we finish // decoding the first frame. Canonical mDuration; diff --git a/dom/media/MediaQueue.h b/dom/media/MediaQueue.h index 8842ebe312..1b664445ce 100644 --- a/dom/media/MediaQueue.h +++ b/dom/media/MediaQueue.h @@ -8,10 +8,9 @@ #include "mozilla/ReentrantMonitor.h" #include "mozilla/TaskQueue.h" -#include "mozilla/UniquePtr.h" #include "nsDeque.h" -#include "nsTArray.h" +#include "MediaEventSource.h" namespace mozilla { @@ -26,36 +25,6 @@ class MediaQueueDeallocator : public nsDequeFunctor { template class MediaQueue : private nsDeque { - struct Listener { - virtual ~Listener() {} - virtual void Dispatch(T* aItem) = 0; - }; - - template - class PopListener : public Listener { - public: - explicit PopListener(const Function& aFunction, TaskQueue* aTarget) - : mFunction(aFunction), mTarget(aTarget) {} - - void Dispatch(T* aItem) override { - nsRefPtr item = aItem; - Function function = mFunction; - nsCOMPtr r = NS_NewRunnableFunction([=] () { - function(item); - }); - mTarget->Dispatch(r.forget()); - } - private: - Function mFunction; - nsRefPtr mTarget; - }; - - void NotifyPopListeners(T* aItem) { - for (auto&& l : mPopListeners) { - l->Dispatch(aItem); - } - } - public: MediaQueue() : nsDeque(new MediaQueueDeallocator()), @@ -77,6 +46,7 @@ public: MOZ_ASSERT(aItem); NS_ADDREF(aItem); nsDeque::Push(aItem); + mPushEvent.Notify(); } inline void PushFront(T* aItem) { @@ -84,13 +54,14 @@ public: MOZ_ASSERT(aItem); NS_ADDREF(aItem); nsDeque::PushFront(aItem); + mPushEvent.Notify(); } inline already_AddRefed PopFront() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsRefPtr rv = dont_AddRef(static_cast(nsDeque::PopFront())); if (rv) { - NotifyPopListeners(rv); + mPopEvent.Notify(rv); } return rv.forget(); } @@ -135,6 +106,7 @@ public: void Finish() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); mEndOfStream = true; + mFinishEvent.Notify(); } // Returns the approximate number of microseconds of items in the queue. @@ -190,21 +162,23 @@ public: return frames; } - void ClearListeners() { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - mPopListeners.Clear(); + MediaEventSource>& PopEvent() { + return mPopEvent; } - template - void AddPopListener(const Function& aFunction, TaskQueue* aTarget) { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - mPopListeners.AppendElement()->reset( - new PopListener(aFunction, aTarget)); + MediaEventSource& PushEvent() { + return mPushEvent; + } + + MediaEventSource& FinishEvent() { + return mFinishEvent; } private: mutable ReentrantMonitor mReentrantMonitor; - nsTArray> mPopListeners; + MediaEventProducer> mPopEvent; + MediaEventProducer mPushEvent; + MediaEventProducer mFinishEvent; // True when we've decoded the last frame of data in the // bitstream for which we're queueing frame data. bool mEndOfStream; diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp index eb5cef3e05..af0441de7b 100644 --- a/dom/media/MediaResource.cpp +++ b/dom/media/MediaResource.cpp @@ -733,25 +733,27 @@ nsresult ChannelMediaResource::ReadAt(int64_t aOffset, } already_AddRefed -ChannelMediaResource::SilentReadAt(int64_t aOffset, uint32_t aCount) +ChannelMediaResource::MediaReadAt(int64_t aOffset, uint32_t aCount) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); nsRefPtr bytes = new MediaByteBuffer(); - bool ok = bytes->SetCapacity(aCount, fallible); + bool ok = bytes->SetLength(aCount, fallible); NS_ENSURE_TRUE(ok, nullptr); - int64_t pos = mCacheStream.Tell(); char* curr = reinterpret_cast(bytes->Elements()); + const char* start = curr; while (aCount > 0) { uint32_t bytesRead; nsresult rv = mCacheStream.ReadAt(aOffset, curr, aCount, &bytesRead); NS_ENSURE_SUCCESS(rv, nullptr); - NS_ENSURE_TRUE(bytesRead > 0, nullptr); + if (!bytesRead) { + break; + } aOffset += bytesRead; aCount -= bytesRead; curr += bytesRead; } - mCacheStream.Seek(nsISeekableStream::NS_SEEK_SET, pos); + bytes->SetLength(curr - start); return bytes.forget(); } @@ -1225,6 +1227,7 @@ public: virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override {} virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override; + virtual already_AddRefed MediaReadAt(int64_t aOffset, uint32_t aCount) override; virtual int64_t Tell() override; // Any thread @@ -1287,6 +1290,8 @@ private: // Ensures mSize is initialized, if it can be. // mLock must be held when this is called, and mInput must be non-null. void EnsureSizeInitialized(); + already_AddRefed UnsafeMediaReadAt( + int64_t aOffset, uint32_t aCount); // The file size, or -1 if not known. Immutable after Open(). // Can be used from any thread. @@ -1532,6 +1537,39 @@ nsresult FileMediaResource::ReadAt(int64_t aOffset, char* aBuffer, return rv; } +already_AddRefed +FileMediaResource::MediaReadAt(int64_t aOffset, uint32_t aCount) +{ + NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); + + MutexAutoLock lock(mLock); + return UnsafeMediaReadAt(aOffset, aCount); +} + +already_AddRefed +FileMediaResource::UnsafeMediaReadAt(int64_t aOffset, uint32_t aCount) +{ + nsRefPtr bytes = new MediaByteBuffer(); + bool ok = bytes->SetLength(aCount, fallible); + NS_ENSURE_TRUE(ok, nullptr); + nsresult rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset); + NS_ENSURE_SUCCESS(rv, nullptr); + char* curr = reinterpret_cast(bytes->Elements()); + const char* start = curr; + while (aCount > 0) { + uint32_t bytesRead; + rv = UnsafeRead(curr, aCount, &bytesRead); + NS_ENSURE_SUCCESS(rv, nullptr); + if (!bytesRead) { + break; + } + aCount -= bytesRead; + curr += bytesRead; + } + bytes->SetLength(curr - start); + return bytes.forget(); +} + nsresult FileMediaResource::UnsafeSeek(int32_t aWhence, int64_t aOffset) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); @@ -1688,26 +1726,6 @@ void BaseMediaResource::DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset NS_DispatchToMainThread(event); } -nsresult -MediaResourceIndex::ReadAt(int64_t aOffset, char* aBuffer, - uint32_t aCount, uint32_t* aBytes) const -{ - *aBytes = 0; - while (aCount > 0) { - uint32_t bytesRead = 0; - nsresult rv = mResource->ReadAt(aOffset, aBuffer, aCount, &bytesRead); - NS_ENSURE_SUCCESS(rv, rv); - if (!bytesRead) { - break; - } - *aBytes += bytesRead; - aOffset += bytesRead; - aBuffer += bytesRead; - aCount -= bytesRead; - } - return NS_OK; -} - nsresult MediaResourceIndex::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) { diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h index 29fe5e370c..e6af3b90e5 100644 --- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -283,6 +283,31 @@ public: // results and requirements are the same as per the Read method. virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) = 0; + // This method returns nullptr if anything fails. + // Otherwise, it returns an owned buffer. + // MediaReadAt may return fewer bytes than requested if end of stream is + // encountered. There is no need to call it again to get more data. + virtual already_AddRefed MediaReadAt(int64_t aOffset, uint32_t aCount) + { + nsRefPtr bytes = new MediaByteBuffer(); + bool ok = bytes->SetLength(aCount, fallible); + NS_ENSURE_TRUE(ok, nullptr); + char* curr = reinterpret_cast(bytes->Elements()); + const char* start = curr; + while (aCount > 0) { + uint32_t bytesRead; + nsresult rv = ReadAt(aOffset, curr, aCount, &bytesRead); + NS_ENSURE_SUCCESS(rv, nullptr); + if (!bytesRead) { + break; + } + aOffset += bytesRead; + aCount -= bytesRead; + curr += bytesRead; + } + bytes->SetLength(curr - start); + return bytes.forget(); + } // Report the current offset in bytes from the start of the stream. // This is used to approximate where we currently are in the playback of a @@ -620,7 +645,7 @@ public: virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override; virtual nsresult ReadAt(int64_t offset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override; - virtual already_AddRefed SilentReadAt(int64_t aOffset, uint32_t aCount) override; + virtual already_AddRefed MediaReadAt(int64_t aOffset, uint32_t aCount) override; virtual int64_t Tell() override; // Any thread diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index cd917f631b..2df1967821 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1228,7 +1228,6 @@ MediaStreamGraphImpl::PrepareUpdatesToMainThreadState(bool aFinalUpdate) continue; } StreamUpdate* update = mStreamUpdates.AppendElement(); - update->mGraphUpdateIndex = stream->mGraphUpdateIndices.GetAt(IterationEnd()); update->mStream = stream; update->mNextMainThreadCurrentTime = GraphTimeToStreamTime(stream, IterationEnd()); @@ -1304,7 +1303,6 @@ MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecision) // batch corresponding to an event loop task). This isolates the performance // of different scripts to some extent. for (uint32_t i = 0; i < mFrontMessageQueue.Length(); ++i) { - mProcessingGraphUpdateIndex = mFrontMessageQueue[i].mGraphUpdateIndex; nsTArray >& messages = mFrontMessageQueue[i].mMessages; for (uint32_t j = 0; j < messages.Length(); ++j) { @@ -1687,8 +1685,6 @@ MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG) if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) { MessageBlock* block = mBackMessageQueue.AppendElement(); block->mMessages.SwapElements(mCurrentTaskMessageQueue); - block->mGraphUpdateIndex = mNextGraphUpdateIndex; - ++mNextGraphUpdateIndex; EnsureNextIterationLocked(); } @@ -1860,7 +1856,6 @@ MediaStream::MediaStream(DOMMediaStream* aWrapper) : mBufferStartTime(0) , mExplicitBlockerCount(0) , mBlocked(false) - , mGraphUpdateIndices(0) , mFinished(false) , mNotifiedFinished(false) , mNotifiedBlocked(false) @@ -1905,7 +1900,6 @@ MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const amount += mMainThreadListeners.ShallowSizeOfExcludingThis(aMallocSizeOf); amount += mDisabledTrackIDs.ShallowSizeOfExcludingThis(aMallocSizeOf); amount += mBlocked.SizeOfExcludingThis(aMallocSizeOf); - amount += mGraphUpdateIndices.SizeOfExcludingThis(aMallocSizeOf); amount += mConsumers.ShallowSizeOfExcludingThis(aMallocSizeOf); return amount; @@ -1978,12 +1972,6 @@ MediaStream::FinishOnGraphThread() GraphImpl()->FinishStream(this); } -int64_t -MediaStream::GetProcessingGraphUpdateIndex() -{ - return GraphImpl()->GetProcessingGraphUpdateIndex(); -} - StreamBuffer::Track* MediaStream::EnsureTrack(TrackID aTrackId) { @@ -2851,7 +2839,6 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, bool aStartWithAudioDriver, dom::AudioChannel aChannel) : MediaStreamGraph(aSampleRate) - , mProcessingGraphUpdateIndex(0) , mPortCount(0) , mNeedAnotherIteration(false) , mGraphDriverAsleep(false) diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index 5d3a15e5b7..269024b850 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -556,10 +556,6 @@ public: GraphTime StreamTimeToGraphTime(StreamTime aTime); bool IsFinishedOnGraphThread() { return mFinished; } void FinishOnGraphThread(); - /** - * Identify which graph update index we are currently processing. - */ - int64_t GetProcessingGraphUpdateIndex(); bool HasCurrentData() { return mHasCurrentData; } @@ -589,8 +585,6 @@ protected: void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, GraphTime aBlockedTime) { mBufferStartTime += aBlockedTime; - mGraphUpdateIndices.InsertTimeAtStart(aBlockedTime); - mGraphUpdateIndices.AdvanceCurrentTime(aCurrentTime); mExplicitBlockerCount.AdvanceCurrentTime(aCurrentTime); mBuffer.ForgetUpTo(aCurrentTime - mBufferStartTime); @@ -655,8 +649,6 @@ protected: // as necessary to account for that time instead) --- this avoids us having to // record the entire history of the stream's blocking-ness in mBlocked. TimeVarying mBlocked; - // Maps graph time to the graph update that affected this stream at that time - TimeVarying mGraphUpdateIndices; // MediaInputPorts to which this is connected nsTArray mConsumers; @@ -1309,8 +1301,7 @@ public: protected: explicit MediaStreamGraph(TrackRate aSampleRate) - : mNextGraphUpdateIndex(1) - , mSampleRate(aSampleRate) + : mSampleRate(aSampleRate) { MOZ_COUNT_CTOR(MediaStreamGraph); } @@ -1322,11 +1313,6 @@ protected: // Media graph thread only nsTArray > mPendingUpdateRunnables; - // Main thread only - // The number of updates we have sent to the media graph thread + 1. - // We start this at 1 just to ensure that 0 is usable as a special value. - int64_t mNextGraphUpdateIndex; - /** * Sample rate at which this graph runs. For real time graphs, this is * the rate of the audio mixer. For offline graphs, this is the rate specified diff --git a/dom/media/MediaStreamGraphImpl.h b/dom/media/MediaStreamGraphImpl.h index daf0441503..a24485bce4 100644 --- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -31,7 +31,6 @@ class AudioOutputObserver; * main thread. */ struct StreamUpdate { - int64_t mGraphUpdateIndex; nsRefPtr mStream; StreamTime mNextMainThreadCurrentTime; bool mNextMainThreadFinished; @@ -72,7 +71,6 @@ protected: class MessageBlock { public: - int64_t mGraphUpdateIndex; nsTArray > mMessages; }; @@ -412,11 +410,6 @@ public: return mStreams.IsEmpty() && mSuspendedStreams.IsEmpty() && mPortCount == 0; } - // For use by control messages, on graph thread only. - /** - * Identify which graph update index we are currently processing. - */ - int64_t GetProcessingGraphUpdateIndex() { return mProcessingGraphUpdateIndex; } /** * Add aStream to the graph and initializes its graph-specific state. */ @@ -553,10 +546,6 @@ public: * Date of the last time we updated the main thread with the graph state. */ TimeStamp mLastMainThreadUpdate; - /** - * Which update batch we are currently processing. - */ - int64_t mProcessingGraphUpdateIndex; /** * Number of active MediaInputPorts */ diff --git a/dom/media/mediasink/AudioSink.h b/dom/media/mediasink/AudioSink.h new file mode 100644 index 0000000000..49d4fa2cc5 --- /dev/null +++ b/dom/media/mediasink/AudioSink.h @@ -0,0 +1,67 @@ +/* -*- 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/. */ +#if !defined(AudioSink_h__) +#define AudioSink_h__ + +#include "mozilla/nsRefPtr.h" +#include "nsISupportsImpl.h" + +namespace mozilla { + +class MediaData; +template class MediaQueue; + +namespace media { + +/* + * Define basic APIs for derived class instance to operate or obtain + * information from it. + */ +class AudioSink { +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink) + AudioSink(MediaQueue& aAudioQueue) + : mAudioQueue(aAudioQueue) + {} + + // Return a promise which will be resolved when AudioSink finishes playing, + // or rejected if any error. + virtual nsRefPtr Init() = 0; + + virtual int64_t GetEndTime() const = 0; + virtual int64_t GetPosition() = 0; + + // Check whether we've pushed more frames to the audio + // hardware than it has played. + virtual bool HasUnplayedFrames() = 0; + + // Shut down the AudioSink's resources. + virtual void Shutdown() = 0; + + // Change audio playback setting. + virtual void SetVolume(double aVolume) = 0; + virtual void SetPlaybackRate(double aPlaybackRate) = 0; + virtual void SetPreservesPitch(bool aPreservesPitch) = 0; + + // Change audio playback status pause/resume. + virtual void SetPlaying(bool aPlaying) = 0; + +protected: + virtual ~AudioSink() {} + + virtual MediaQueue& AudioQueue() const { + return mAudioQueue; + } + + // To queue audio data (no matter it's plain or encoded or encrypted, depends + // on the subclass) + MediaQueue& mAudioQueue; +}; + +} // namespace media +} // namespace mozilla + +#endif diff --git a/dom/media/AudioSink.cpp b/dom/media/mediasink/DecodedAudioDataSink.cpp similarity index 80% rename from dom/media/AudioSink.cpp rename to dom/media/mediasink/DecodedAudioDataSink.cpp index a60ba86f91..75dd113ace 100644 --- a/dom/media/AudioSink.cpp +++ b/dom/media/mediasink/DecodedAudioDataSink.cpp @@ -4,9 +4,9 @@ * 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 "AudioSink.h" #include "AudioStream.h" #include "MediaQueue.h" +#include "DecodedAudioDataSink.h" #include "VideoUtils.h" #include "mozilla/CheckedInt.h" @@ -16,19 +16,23 @@ namespace mozilla { extern PRLogModuleInfo* gMediaDecoderLog; #define SINK_LOG(msg, ...) \ - MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__)) + MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \ + ("DecodedAudioDataSink=%p " msg, this, ##__VA_ARGS__)) #define SINK_LOG_V(msg, ...) \ - MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__)) + MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, \ + ("DecodedAudioDataSink=%p " msg, this, ##__VA_ARGS__)) + +namespace media { // The amount of audio frames that is used to fuzz rounding errors. static const int64_t AUDIO_FUZZ_FRAMES = 1; -AudioSink::AudioSink(MediaQueue& aAudioQueue, - int64_t aStartTime, - const AudioInfo& aInfo, - dom::AudioChannel aChannel) - : mAudioQueue(aAudioQueue) - , mMonitor("AudioSink::mMonitor") +DecodedAudioDataSink::DecodedAudioDataSink(MediaQueue& aAudioQueue, + int64_t aStartTime, + const AudioInfo& aInfo, + dom::AudioChannel aChannel) + : AudioSink(aAudioQueue) + , mMonitor("DecodedAudioDataSink::mMonitor") , mState(AUDIOSINK_STATE_INIT) , mAudioLoopScheduled(false) , mStartTime(aStartTime) @@ -42,14 +46,14 @@ AudioSink::AudioSink(MediaQueue& aAudioQueue, } void -AudioSink::SetState(State aState) +DecodedAudioDataSink::SetState(State aState) { AssertOnAudioThread(); mPendingState = Some(aState); } void -AudioSink::DispatchTask(already_AddRefed&& event) +DecodedAudioDataSink::DispatchTask(already_AddRefed&& event) { DebugOnly rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL); // There isn't much we can do if Dispatch() fails. @@ -58,22 +62,49 @@ AudioSink::DispatchTask(already_AddRefed&& event) } void -AudioSink::ScheduleNextLoop() +DecodedAudioDataSink::OnAudioQueueEvent() +{ + AssertOnAudioThread(); + if (!mAudioLoopScheduled) { + AudioLoop(); + } +} + +void +DecodedAudioDataSink::ConnectListener() +{ + AssertOnAudioThread(); + mPushListener = AudioQueue().PushEvent().Connect( + mThread, this, &DecodedAudioDataSink::OnAudioQueueEvent); + mFinishListener = AudioQueue().FinishEvent().Connect( + mThread, this, &DecodedAudioDataSink::OnAudioQueueEvent); +} + +void +DecodedAudioDataSink::DisconnectListener() +{ + AssertOnAudioThread(); + mPushListener.Disconnect(); + mFinishListener.Disconnect(); +} + +void +DecodedAudioDataSink::ScheduleNextLoop() { AssertOnAudioThread(); if (mAudioLoopScheduled) { return; } mAudioLoopScheduled = true; - nsCOMPtr r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop); + nsCOMPtr r = NS_NewRunnableMethod(this, &DecodedAudioDataSink::AudioLoop); DispatchTask(r.forget()); } void -AudioSink::ScheduleNextLoopCrossThread() +DecodedAudioDataSink::ScheduleNextLoopCrossThread() { AssertNotOnAudioThread(); - nsRefPtr self = this; + nsRefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([self] () { // Do nothing if there is already a pending task waiting for its turn. if (!self->mAudioLoopScheduled) { @@ -84,7 +115,7 @@ AudioSink::ScheduleNextLoopCrossThread() } nsRefPtr -AudioSink::Init() +DecodedAudioDataSink::Init() { nsRefPtr p = mEndPromise.Ensure(__func__); nsresult rv = NS_NewNamedThread("Media Audio", @@ -101,7 +132,7 @@ AudioSink::Init() } int64_t -AudioSink::GetPosition() +DecodedAudioDataSink::GetPosition() { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); @@ -116,7 +147,7 @@ AudioSink::GetPosition() } bool -AudioSink::HasUnplayedFrames() +DecodedAudioDataSink::HasUnplayedFrames() { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); // Experimentation suggests that GetPositionInFrames() is zero-indexed, @@ -125,7 +156,7 @@ AudioSink::HasUnplayedFrames() } void -AudioSink::Shutdown() +DecodedAudioDataSink::Shutdown() { { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); @@ -133,7 +164,7 @@ AudioSink::Shutdown() mAudioStream->Cancel(); } } - nsRefPtr self = this; + nsRefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([=] () { self->mStopAudioThread = true; if (!self->mAudioLoopScheduled) { @@ -157,10 +188,10 @@ AudioSink::Shutdown() } void -AudioSink::SetVolume(double aVolume) +DecodedAudioDataSink::SetVolume(double aVolume) { AssertNotOnAudioThread(); - nsRefPtr self = this; + nsRefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([=] () { if (self->mState == AUDIOSINK_STATE_PLAYING) { self->mAudioStream->SetVolume(aVolume); @@ -170,11 +201,11 @@ AudioSink::SetVolume(double aVolume) } void -AudioSink::SetPlaybackRate(double aPlaybackRate) +DecodedAudioDataSink::SetPlaybackRate(double aPlaybackRate) { AssertNotOnAudioThread(); MOZ_ASSERT(aPlaybackRate != 0, "Don't set the playbackRate to 0 on AudioStream"); - nsRefPtr self = this; + nsRefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([=] () { if (self->mState == AUDIOSINK_STATE_PLAYING) { self->mAudioStream->SetPlaybackRate(aPlaybackRate); @@ -184,10 +215,10 @@ AudioSink::SetPlaybackRate(double aPlaybackRate) } void -AudioSink::SetPreservesPitch(bool aPreservesPitch) +DecodedAudioDataSink::SetPreservesPitch(bool aPreservesPitch) { AssertNotOnAudioThread(); - nsRefPtr self = this; + nsRefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([=] () { if (self->mState == AUDIOSINK_STATE_PLAYING) { self->mAudioStream->SetPreservesPitch(aPreservesPitch); @@ -197,10 +228,10 @@ AudioSink::SetPreservesPitch(bool aPreservesPitch) } void -AudioSink::SetPlaying(bool aPlaying) +DecodedAudioDataSink::SetPlaying(bool aPlaying) { AssertNotOnAudioThread(); - nsRefPtr self = this; + nsRefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([=] () { if (self->mState != AUDIOSINK_STATE_PLAYING || self->mPlaying == aPlaying) { @@ -221,14 +252,8 @@ AudioSink::SetPlaying(bool aPlaying) DispatchTask(r.forget()); } -void -AudioSink::NotifyData() -{ - ScheduleNextLoopCrossThread(); -} - nsresult -AudioSink::InitializeAudioStream() +DecodedAudioDataSink::InitializeAudioStream() { // AudioStream initialization can block for extended periods in unusual // circumstances, so we take care to drop the decoder monitor while @@ -248,7 +273,7 @@ AudioSink::InitializeAudioStream() } void -AudioSink::Drain() +DecodedAudioDataSink::Drain() { AssertOnAudioThread(); MOZ_ASSERT(mPlaying && !mAudioStream->IsPaused()); @@ -259,7 +284,7 @@ AudioSink::Drain() } void -AudioSink::Cleanup() +DecodedAudioDataSink::Cleanup() { AssertOnAudioThread(); mEndPromise.Resolve(true, __func__); @@ -269,13 +294,13 @@ AudioSink::Cleanup() } bool -AudioSink::ExpectMoreAudioData() +DecodedAudioDataSink::ExpectMoreAudioData() { return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished(); } bool -AudioSink::WaitingForAudioToPlay() +DecodedAudioDataSink::WaitingForAudioToPlay() { AssertOnAudioThread(); // Return true if we're not playing, and we're not shutting down, or we're @@ -287,7 +312,7 @@ AudioSink::WaitingForAudioToPlay() } bool -AudioSink::IsPlaybackContinuing() +DecodedAudioDataSink::IsPlaybackContinuing() { AssertOnAudioThread(); // If we're shutting down, captured, or at EOS, break out and exit the audio @@ -300,7 +325,7 @@ AudioSink::IsPlaybackContinuing() } void -AudioSink::AudioLoop() +DecodedAudioDataSink::AudioLoop() { AssertOnAudioThread(); mAudioLoopScheduled = false; @@ -316,12 +341,13 @@ AudioSink::AudioLoop() break; } SetState(AUDIOSINK_STATE_PLAYING); + ConnectListener(); break; } case AUDIOSINK_STATE_PLAYING: { if (WaitingForAudioToPlay()) { - // NotifyData() will schedule next loop. + // OnAudioQueueEvent() will schedule next loop. break; } if (!IsPlaybackContinuing()) { @@ -338,6 +364,7 @@ AudioSink::AudioLoop() } case AUDIOSINK_STATE_COMPLETE: { + DisconnectListener(); FinishAudioLoop(); SetState(AUDIOSINK_STATE_SHUTDOWN); break; @@ -363,7 +390,7 @@ AudioSink::AudioLoop() } bool -AudioSink::PlayAudio() +DecodedAudioDataSink::PlayAudio() { // See if there's a gap in the audio. If there is, push silence into the // audio hardware, so we can play across the gap. @@ -397,7 +424,7 @@ AudioSink::PlayAudio() } void -AudioSink::FinishAudioLoop() +DecodedAudioDataSink::FinishAudioLoop() { AssertOnAudioThread(); MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream()); @@ -410,7 +437,7 @@ AudioSink::FinishAudioLoop() } uint32_t -AudioSink::PlaySilence(uint32_t aFrames) +DecodedAudioDataSink::PlaySilence(uint32_t aFrames) { // Maximum number of bytes we'll allocate and write at once to the audio // hardware when the audio stream contains missing frames and we're @@ -429,7 +456,7 @@ AudioSink::PlaySilence(uint32_t aFrames) } uint32_t -AudioSink::PlayFromAudioQueue() +DecodedAudioDataSink::PlayFromAudioQueue() { AssertOnAudioThread(); NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused"); @@ -452,7 +479,7 @@ AudioSink::PlayFromAudioQueue() } void -AudioSink::StartAudioStreamPlaybackIfNeeded() +DecodedAudioDataSink::StartAudioStreamPlaybackIfNeeded() { // This value has been chosen empirically. const uint32_t MIN_WRITE_BEFORE_START_USECS = 200000; @@ -465,7 +492,7 @@ AudioSink::StartAudioStreamPlaybackIfNeeded() } void -AudioSink::WriteSilence(uint32_t aFrames) +DecodedAudioDataSink::WriteSilence(uint32_t aFrames) { uint32_t numSamples = aFrames * mInfo.mChannels; nsAutoTArray buf; @@ -477,7 +504,7 @@ AudioSink::WriteSilence(uint32_t aFrames) } int64_t -AudioSink::GetEndTime() const +DecodedAudioDataSink::GetEndTime() const { CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mInfo.mRate) + mStartTime; if (!playedUsecs.isValid()) { @@ -488,15 +515,16 @@ AudioSink::GetEndTime() const } void -AudioSink::AssertOnAudioThread() +DecodedAudioDataSink::AssertOnAudioThread() { MOZ_ASSERT(NS_GetCurrentThread() == mThread); } void -AudioSink::AssertNotOnAudioThread() +DecodedAudioDataSink::AssertNotOnAudioThread() { MOZ_ASSERT(NS_GetCurrentThread() != mThread); } +} // namespace media } // namespace mozilla diff --git a/dom/media/AudioSink.h b/dom/media/mediasink/DecodedAudioDataSink.h similarity index 79% rename from dom/media/AudioSink.h rename to dom/media/mediasink/DecodedAudioDataSink.h index d13402e8be..ea3a2ec50a 100644 --- a/dom/media/AudioSink.h +++ b/dom/media/mediasink/DecodedAudioDataSink.h @@ -3,9 +3,10 @@ /* 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/. */ -#if !defined(AudioSink_h__) -#define AudioSink_h__ +#if !defined(DecodedAudioDataSink_h__) +#define DecodedAudioDataSink_h__ +#include "AudioSink.h" #include "MediaInfo.h" #include "mozilla/RefPtr.h" #include "nsISupportsImpl.h" @@ -17,45 +18,37 @@ #include "mozilla/ReentrantMonitor.h" namespace mozilla { +namespace media { -class AudioData; -class AudioStream; -template class MediaQueue; - -class AudioSink { +class DecodedAudioDataSink : public AudioSink { public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink) - AudioSink(MediaQueue& aAudioQueue, - int64_t aStartTime, - const AudioInfo& aInfo, - dom::AudioChannel aChannel); + DecodedAudioDataSink(MediaQueue& aAudioQueue, + int64_t aStartTime, + const AudioInfo& aInfo, + dom::AudioChannel aChannel); - // Return a promise which will be resolved when AudioSink finishes playing, - // or rejected if any error. - nsRefPtr Init(); + // Return a promise which will be resolved when DecodedAudioDataSink + // finishes playing, or rejected if any error. + nsRefPtr Init() override; /* * All public functions below are thread-safe. */ - int64_t GetPosition(); - int64_t GetEndTime() const; + int64_t GetPosition() override; + int64_t GetEndTime() const override; // Check whether we've pushed more frames to the audio hardware than it has // played. - bool HasUnplayedFrames(); + bool HasUnplayedFrames() override; - // Shut down the AudioSink's resources. - void Shutdown(); + // Shut down the DecodedAudioDataSink's resources. + void Shutdown() override; - void SetVolume(double aVolume); - void SetPlaybackRate(double aPlaybackRate); - void SetPreservesPitch(bool aPreservesPitch); - void SetPlaying(bool aPlaying); - - // Wake up the audio loop if it is waiting for data to play or the audio - // queue is finished. - void NotifyData(); + void SetVolume(double aVolume) override; + void SetPlaybackRate(double aPlaybackRate) override; + void SetPreservesPitch(bool aPreservesPitch) override; + void SetPlaying(bool aPlaying) override; private: enum State { @@ -66,13 +59,17 @@ private: AUDIOSINK_STATE_ERROR }; - ~AudioSink() {} + virtual ~DecodedAudioDataSink() {} void DispatchTask(already_AddRefed&& event); void SetState(State aState); void ScheduleNextLoop(); void ScheduleNextLoopCrossThread(); + void OnAudioQueueEvent(); + void ConnectListener(); + void DisconnectListener(); + // The main loop for the audio thread. Sent to the thread as // an nsRunnableMethod. This continually does blocking writes to // to audio stream to play audio data. @@ -119,10 +116,6 @@ private: void StartAudioStreamPlaybackIfNeeded(); void WriteSilence(uint32_t aFrames); - MediaQueue& AudioQueue() const { - return mAudioQueue; - } - ReentrantMonitor& GetReentrantMonitor() const { return mMonitor; } @@ -134,7 +127,6 @@ private: void AssertOnAudioThread(); void AssertNotOnAudioThread(); - MediaQueue& mAudioQueue; mutable ReentrantMonitor mMonitor; // There members are accessed on the audio thread only. @@ -168,15 +160,19 @@ private: const AudioInfo mInfo; - dom::AudioChannel mChannel; + const dom::AudioChannel mChannel; bool mStopAudioThread; bool mPlaying; MozPromiseHolder mEndPromise; + + MediaEventListener mPushListener; + MediaEventListener mFinishListener; }; +} // namespace media } // namespace mozilla #endif diff --git a/dom/media/mediasink/moz.build b/dom/media/mediasink/moz.build new file mode 100644 index 0000000000..6fd1f5d06d --- /dev/null +++ b/dom/media/mediasink/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +UNIFIED_SOURCES += [ + 'DecodedAudioDataSink.cpp', +] + +FINAL_LIBRARY = 'xul' + +FAIL_ON_WARNINGS = True diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 93eb9583d6..c92baa7f19 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -64,7 +64,7 @@ MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*) NS_ENSURE_SUCCESS(rv, rv); SetStateMachineParameters(); - return ScheduleStateMachine(); + return NS_OK; } media::TimeIntervals diff --git a/dom/media/moz.build b/dom/media/moz.build index 862099b7a9..1df49bf88b 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -25,6 +25,7 @@ DIRS += [ 'gmp-plugin', 'gmp-plugin-openh264', 'imagecapture', + 'mediasink', 'mediasource', 'ogg', 'platforms', @@ -192,7 +193,6 @@ UNIFIED_SOURCES += [ 'AudioChannelFormat.cpp', 'AudioCompactor.cpp', 'AudioSegment.cpp', - 'AudioSink.cpp', 'AudioStream.cpp', 'AudioStreamTrack.cpp', 'AudioTrack.cpp', diff --git a/dom/media/webm/WebMBufferedParser.cpp b/dom/media/webm/WebMBufferedParser.cpp index 60516a0cb0..8b80aa46d7 100644 --- a/dom/media/webm/WebMBufferedParser.cpp +++ b/dom/media/webm/WebMBufferedParser.cpp @@ -383,7 +383,7 @@ void WebMBufferedState::UpdateIndex(const nsTArray& aRanges, Med } } } - nsRefPtr bytes = aResource->SilentReadAt(offset, length); + nsRefPtr bytes = aResource->MediaReadAt(offset, length); if(bytes) { NotifyDataArrived(bytes->Elements(), bytes->Length(), offset); } diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index b93d6f5114..338bdf3da5 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -403,7 +403,7 @@ WebMDemuxer::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) aCount = length - mOffset; WEBM_DEBUG("will only return %ld", aCount); } - nsRefPtr bytes = mResource->SilentReadAt(mOffset, aCount); + nsRefPtr bytes = mResource->MediaReadAt(mOffset, aCount); if (!bytes) { return NS_ERROR_FAILURE; } @@ -461,7 +461,7 @@ WebMDemuxer::EnsureUpToDateIndex() } mBufferedState->UpdateIndex(byteRanges, mResource); if (!mInitData && mBufferedState->GetInitEndOffset() != -1) { - mInitData = mResource->SilentReadAt(0, mBufferedState->GetInitEndOffset()); + mInitData = mResource->MediaReadAt(0, mBufferedState->GetInitEndOffset()); } mNeedReIndex = false; } @@ -880,7 +880,8 @@ WebMTrackDemuxer::SetNextKeyFrameTime() skipSamplesQueue.PushFront(sample); } while(skipSamplesQueue.GetSize()) { - mSamples.PushFront(skipSamplesQueue.PopFront()); + nsRefPtr data = skipSamplesQueue.PopFront(); + mSamples.PushFront(data); } if (frameTime == -1) { frameTime = mParent->GetNextKeyframeTime(); diff --git a/dom/media/webm/WebMDemuxer.h b/dom/media/webm/WebMDemuxer.h index 10765d76cf..f464984dfd 100644 --- a/dom/media/webm/WebMDemuxer.h +++ b/dom/media/webm/WebMDemuxer.h @@ -31,10 +31,10 @@ class MediaRawDataQueue { mQueue.push_front(aItem); } - nsRefPtr PopFront() { + already_AddRefed PopFront() { nsRefPtr result = mQueue.front(); mQueue.pop_front(); - return result; + return result.forget(); } void Reset() { diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index 25ab9b8b51..ae03564fde 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -288,8 +288,8 @@ public: explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS); - typedef int32_t FrameID; - typedef int32_t ProducerID; + typedef uint32_t FrameID; + typedef uint32_t ProducerID; /** diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh index 1987dd4a5f..681da5c49c 100644 --- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -388,8 +388,8 @@ struct TimedTexture { MaybeFence fence; TimeStamp timeStamp; IntRect picture; - int32_t frameID; - int32_t producerID; + uint32_t frameID; + uint32_t producerID; }; /** @@ -483,8 +483,8 @@ struct ImageCompositeNotification { PImageContainer imageContainer; TimeStamp imageTimeStamp; TimeStamp firstCompositeTimeStamp; - int32_t frameID; - int32_t producerID; + uint32_t frameID; + uint32_t producerID; }; // Unit of a "changeset reply". This is a weird abstraction, probably diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h index 6aacd29597..990cca8da9 100644 --- a/xpcom/threads/MozPromise.h +++ b/xpcom/threads/MozPromise.h @@ -820,6 +820,12 @@ public: MozPromiseRequestHolder() {} ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); } + void Begin(nsRefPtr&& aRequest) + { + MOZ_DIAGNOSTIC_ASSERT(!Exists()); + mRequest = Move(aRequest); + } + void Begin(typename PromiseType::Request* aRequest) { MOZ_DIAGNOSTIC_ASSERT(!Exists());