diff --git a/dom/media/mediasink/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp index 52ec30b1c..5c3cf7850 100644 --- a/dom/media/mediasink/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -6,6 +6,7 @@ #include "mozilla/CheckedInt.h" #include "mozilla/gfx/Point.h" +#include "mozilla/SyncRunnable.h" #include "AudioSegment.h" #include "DecodedStream.h" @@ -18,6 +19,15 @@ namespace mozilla { +/* + * A container class to make it easier to pass the playback info all the + * way to DecodedStreamGraphListener from DecodedStream. + */ +struct PlaybackInfoInit { + int64_t mStartTime; + MediaInfo mInfo; +}; + class DecodedStreamGraphListener : public MediaStreamListener { typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; public: @@ -116,7 +126,8 @@ UpdateStreamSuspended(MediaStream* aStream, bool aBlocking) */ class DecodedStreamData { public: - DecodedStreamData(SourceMediaStream* aStream, + DecodedStreamData(OutputStreamManager aOutputStreamManager, + PlaybackInfoInit&& aInit, MozPromiseHolder&& aPromise); ~DecodedStreamData(); bool IsFinished() const; @@ -137,45 +148,56 @@ public: // the image. RefPtr 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 RefPtr mStream; - RefPtr mListener; + const RefPtr mListener; bool mPlaying; // True if we need to send a compensation video frame to ensure the // StreamTime going forward. bool mEOSVideoCompensation; + + OutputStreamManager mOutputStreamManager; }; -DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream, +DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager, + PlaybackInfoInit&& aInit, MozPromiseHolder&& aPromise) : mAudioFramesWritten(0) - , mNextVideoTime(-1) - , mNextAudioTime(-1) - , mStreamInitialized(false) + , mNextVideoTime(aInit.mStartTime) + , mNextAudioTime(aInit.mStartTime) , mHaveSentFinish(false) , mHaveSentFinishAudio(false) , mHaveSentFinishVideo(false) - , mStream(aStream) - , mPlaying(true) - , mEOSVideoCompensation(false) -{ + , mStream(aOutputStreamManager.Graph()->CreateSourceStream(nullptr)) // DecodedStreamGraphListener will resolve this promise. - mListener = new DecodedStreamGraphListener(mStream, Move(aPromise)); - mStream->AddListener(mListener); - + , mListener(new DecodedStreamGraphListener(mStream, Move(aPromise))) // mPlaying is initially true because MDSM won't start playback until playing // becomes true. This is consistent with the settings of AudioSink. + , mPlaying(true) + , mEOSVideoCompensation(false) + , mOutputStreamManager(aOutputStreamManager) +{ + mStream->AddListener(mListener); + mOutputStreamManager.Connect(mStream); + + // Initialize tracks. + if (aInit.mInfo.HasAudio()) { + mStream->AddAudioTrack(aInit.mInfo.mAudio.mTrackId, + aInit.mInfo.mAudio.mRate, + 0, new AudioSegment()); + } + if (aInit.mInfo.HasVideo()) { + mStream->AddTrack(aInit.mInfo.mVideo.mTrackId, 0, new VideoSegment()); + } } DecodedStreamData::~DecodedStreamData() { + mOutputStreamManager.Disconnect(); mListener->Forget(); mStream->Destroy(); } @@ -377,28 +399,51 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) class R : public nsRunnable { typedef MozPromiseHolder Promise; - typedef void(DecodedStream::*Method)(Promise&&); public: - R(DecodedStream* aThis, Method aMethod, Promise&& aPromise) - : mThis(aThis), mMethod(aMethod) + R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager aManager) + : mInit(Move(aInit)), mOutputStreamManager(aManager) { mPromise = Move(aPromise); } NS_IMETHOD Run() override { - (mThis->*mMethod)(Move(mPromise)); + MOZ_ASSERT(NS_IsMainThread()); + // No need to create a source stream when there are no output streams. This + // happens when RemoveOutput() is called immediately after StartPlayback(). + if (!mOutputStreamManager.Graph()) { + // Resolve the promise to indicate the end of playback. + mPromise.Resolve(true, __func__); + return NS_OK; + } + mData = MakeUnique( + mOutputStreamManager, Move(mInit), Move(mPromise)); return NS_OK; } + UniquePtr ReleaseData() + { + return Move(mData); + } private: - RefPtr mThis; - Method mMethod; + PlaybackInfoInit mInit; Promise mPromise; + OutputStreamManager mOutputStreamManager; + UniquePtr mData; }; MozPromiseHolder promise; mFinishPromise = promise.Ensure(__func__); - nsCOMPtr r = new R(this, &DecodedStream::CreateData, Move(promise)); - AbstractThread::MainThread()->Dispatch(r.forget()); + PlaybackInfoInit init { + aStartTime, aInfo + }; + nsCOMPtr r = new R(Move(init), Move(promise), mOutputStreamManager); + nsCOMPtr mainThread = do_GetMainThread(); + SyncRunnable::DispatchToThread(mainThread, r); + mData = static_cast(r.get())->ReleaseData(); + + if (mData) { + mData->SetPlaying(mPlaying); + SendData(); + } } void @@ -440,54 +485,13 @@ DecodedStream::DestroyData(UniquePtr aData) } DecodedStreamData* data = aData.release(); - RefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction([=] () { - self->mOutputStreamManager.Disconnect(); delete data; }); AbstractThread::MainThread()->Dispatch(r.forget()); } -void -DecodedStream::CreateData(MozPromiseHolder&& aPromise) -{ - MOZ_ASSERT(NS_IsMainThread()); - // No need to create a source stream when there are no output streams. This - // happens when RemoveOutput() is called immediately after StartPlayback(). - // Also we don't create a source stream when MDSM has begun shutdown. - if (!mOutputStreamManager.Graph() || mShuttingDown) { - // Resolve the promise to indicate the end of playback. - aPromise.Resolve(true, __func__); - return; - } - - auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr); - auto data = new DecodedStreamData(source, Move(aPromise)); - mOutputStreamManager.Connect(data->mStream); - - class R : public nsRunnable { - typedef void(DecodedStream::*Method)(UniquePtr); - public: - R(DecodedStream* aThis, Method aMethod, DecodedStreamData* aData) - : mThis(aThis), mMethod(aMethod), mData(aData) {} - NS_IMETHOD Run() override - { - (mThis->*mMethod)(Move(mData)); - return NS_OK; - } - private: - RefPtr mThis; - Method mMethod; - UniquePtr mData; - }; - - // Post a message to ensure |mData| is only updated on the worker thread. - // Note this must be done before MDSM's shutdown since dispatch could fail - // when the worker thread is shut down. - nsCOMPtr r = new R(this, &DecodedStream::OnDataCreated, data); - mOwnerThread->Dispatch(r.forget()); -} bool DecodedStream::HasConsumers() const @@ -495,23 +499,7 @@ DecodedStream::HasConsumers() const return !mOutputStreamManager.IsEmpty(); } -void -DecodedStream::OnDataCreated(UniquePtr aData) -{ - AssertOwnerThread(); - MOZ_ASSERT(!mData, "Already created."); - // Start to send data to the stream immediately - if (mStartTime.isSome()) { - aData->SetPlaying(mPlaying); - mData = Move(aData); - SendData(); - return; - } - - // Playback has ended. Destroy aData which is not needed anymore. - DestroyData(Move(aData)); -} void DecodedStream::AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded) @@ -569,37 +557,6 @@ DecodedStream::SetSameOrigin(bool aSameOrigin) mSameOrigin = aSameOrigin; } -void -DecodedStream::InitTracks() -{ - AssertOwnerThread(); - - if (mData->mStreamInitialized) { - return; - } - - SourceMediaStream* sourceStream = mData->mStream; - - if (mInfo.HasAudio()) { - TrackID audioTrackId = mInfo.mAudio.mTrackId; - AudioSegment* audio = new AudioSegment(); - sourceStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio, - SourceMediaStream::ADDTRACK_QUEUED); - mData->mNextAudioTime = mStartTime.ref(); - } - - if (mInfo.HasVideo()) { - TrackID videoTrackId = mInfo.mVideo.mTrackId; - VideoSegment* video = new VideoSegment(); - sourceStream->AddTrack(videoTrackId, 0, video, - SourceMediaStream::ADDTRACK_QUEUED); - mData->mNextVideoTime = mStartTime.ref(); - } - - sourceStream->FinishAddTracks(); - mData->mStreamInitialized = true; -} - static void SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, MediaData* aData, AudioSegment* aOutput, @@ -832,7 +789,6 @@ DecodedStream::SendData() return; } - InitTracks(); SendAudio(mParams.mVolume, mSameOrigin); SendVideo(mSameOrigin); AdvanceTracks(); diff --git a/dom/media/mediasink/DecodedStream.h b/dom/media/mediasink/DecodedStream.h index 5b384e301..1f7877b76 100644 --- a/dom/media/mediasink/DecodedStream.h +++ b/dom/media/mediasink/DecodedStream.h @@ -27,6 +27,7 @@ class MediaInputPort; class MediaStream; class MediaStreamGraph; class OutputStreamManager; +struct PlaybackInfoInit; class ProcessedMediaStream; class TimeStamp; @@ -131,10 +132,7 @@ protected: virtual ~DecodedStream(); private: - void CreateData(MozPromiseHolder&& aPromise); void DestroyData(UniquePtr aData); - void OnDataCreated(UniquePtr aData); - void InitTracks(); void AdvanceTracks(); void SendAudio(double aVolume, bool aIsSameOrigin); void SendVideo(bool aIsSameOrigin);