diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index b7053e847..b5078e1d0 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -19,6 +19,7 @@ #include "mediasink/AudioSinkWrapper.h" #include "mediasink/VideoSink.h" #include "mediasink/DecodedStream.h" +#include "mediasink/OutputStreamManager.h" #include "mozilla/DebugOnly.h" #include "mozilla/Logging.h" #include "mozilla/mozalloc.h" @@ -288,7 +289,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mSentLoadedMetadataEvent(false), mSentFirstFrameLoadedEvent(false, "MediaDecoderStateMachine::mSentFirstFrameLoadedEvent"), mSentPlaybackEndedEvent(false), - mStreamSink(new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue)), + mOutputStreamManager(new OutputStreamManager()), + mStreamSink(new DecodedStream( + mTaskQueue, mAudioQueue, mVideoQueue, mOutputStreamManager)), mResource(aDecoder->GetResource()), mAudioOffloading(false), mBuffered(mTaskQueue, TimeIntervals(), @@ -3141,7 +3144,7 @@ void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream, { MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("AddOutputStream aStream=%p!", aStream); - mStreamSink->AddOutput(aStream, aFinishWhenEnded); + mOutputStreamManager->Add(aStream, aFinishWhenEnded); nsCOMPtr r = NS_NewRunnableMethodWithArg( this, &MediaDecoderStateMachine::SetAudioCaptured, true); OwnerThread()->Dispatch(r.forget()); @@ -3151,8 +3154,8 @@ void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream) { MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("RemoveOutputStream=%p!", aStream); - mStreamSink->RemoveOutput(aStream); - if (!mStreamSink->HasConsumers()) { + mOutputStreamManager->Remove(aStream); + if (mOutputStreamManager->IsEmpty()) { nsCOMPtr r = NS_NewRunnableMethodWithArg( this, &MediaDecoderStateMachine::SetAudioCaptured, false); OwnerThread()->Dispatch(r.forget()); diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 54f4b8b9b..c970d2d66 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -105,6 +105,7 @@ class MediaSink; class AudioSegment; class DecodedStream; +class OutputStreamManager; class TaskQueue; extern LazyLogModule gMediaDecoderLog; @@ -1185,6 +1186,9 @@ private: bool mSentPlaybackEndedEvent; + // Data about MediaStreams that are being fed by the decoder. + const RefPtr mOutputStreamManager; + // The SourceMediaStream we are using to feed the mOutputStreams. This stream // is never exposed outside the decoder. // Only written on the main thread while holding the monitor. Therefore it diff --git a/dom/media/mediasink/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp index 5c3cf7850..8e9c18641 100644 --- a/dom/media/mediasink/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -13,6 +13,7 @@ #include "MediaData.h" #include "MediaQueue.h" #include "MediaStreamGraph.h" +#include "OutputStreamManager.h" #include "SharedBuffer.h" #include "VideoSegment.h" #include "VideoUtils.h" @@ -126,7 +127,7 @@ UpdateStreamSuspended(MediaStream* aStream, bool aBlocking) */ class DecodedStreamData { public: - DecodedStreamData(OutputStreamManager aOutputStreamManager, + DecodedStreamData(OutputStreamManager* aOutputStreamManager, PlaybackInfoInit&& aInit, MozPromiseHolder&& aPromise); ~DecodedStreamData(); @@ -160,10 +161,10 @@ public: // StreamTime going forward. bool mEOSVideoCompensation; - OutputStreamManager mOutputStreamManager; + const RefPtr mOutputStreamManager; }; -DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager, +DecodedStreamData::DecodedStreamData(OutputStreamManager* aOutputStreamManager, PlaybackInfoInit&& aInit, MozPromiseHolder&& aPromise) : mAudioFramesWritten(0) @@ -172,7 +173,7 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager, , mHaveSentFinish(false) , mHaveSentFinishAudio(false) , mHaveSentFinishVideo(false) - , mStream(aOutputStreamManager.Graph()->CreateSourceStream(nullptr)) + , mStream(aOutputStreamManager->Graph()->CreateSourceStream(nullptr)) // DecodedStreamGraphListener will resolve this promise. , mListener(new DecodedStreamGraphListener(mStream, Move(aPromise))) // mPlaying is initially true because MDSM won't start playback until playing @@ -182,7 +183,7 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager, , mOutputStreamManager(aOutputStreamManager) { mStream->AddListener(mListener); - mOutputStreamManager.Connect(mStream); + mOutputStreamManager->Connect(mStream); // Initialize tracks. if (aInit.mInfo.HasAudio()) { @@ -197,7 +198,7 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager, DecodedStreamData::~DecodedStreamData() { - mOutputStreamManager.Disconnect(); + mOutputStreamManager->Disconnect(); mListener->Forget(); mStream->Destroy(); } @@ -223,119 +224,12 @@ DecodedStreamData::SetPlaying(bool aPlaying) } } -OutputStreamData::~OutputStreamData() -{ - MOZ_ASSERT(NS_IsMainThread()); - // Break the connection to the input stream if necessary. - if (mPort) { - mPort->Destroy(); - } -} - -void -OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream) -{ - mOwner = aOwner; - mStream = aStream; -} - -void -OutputStreamData::Connect(MediaStream* aStream) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mPort, "Already connected?"); - MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream."); - - mPort = mStream->AllocateInputPort(aStream); -} - -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; - } - return true; -} - -MediaStreamGraph* -OutputStreamData::Graph() const -{ - return mStream->Graph(); -} - -void -OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded) -{ - MOZ_ASSERT(NS_IsMainThread()); - // All streams must belong to the same graph. - MOZ_ASSERT(!Graph() || Graph() == aStream->Graph()); - - // 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(AbstractThread* aOwnerThread, MediaQueue& aAudioQueue, - MediaQueue& aVideoQueue) + MediaQueue& aVideoQueue, + OutputStreamManager* aOutputStreamManager) : mOwnerThread(aOwnerThread) + , mOutputStreamManager(aOutputStreamManager) , mShuttingDown(false) , mPlaying(false) , mSameOrigin(false) @@ -400,7 +294,7 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) class R : public nsRunnable { typedef MozPromiseHolder Promise; public: - R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager aManager) + R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager* aManager) : mInit(Move(aInit)), mOutputStreamManager(aManager) { mPromise = Move(aPromise); @@ -410,7 +304,7 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) 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()) { + if (!mOutputStreamManager->Graph()) { // Resolve the promise to indicate the end of playback. mPromise.Resolve(true, __func__); return NS_OK; @@ -426,7 +320,7 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) private: PlaybackInfoInit mInit; Promise mPromise; - OutputStreamManager mOutputStreamManager; + RefPtr mOutputStreamManager; UniquePtr mData; }; @@ -491,28 +385,6 @@ DecodedStream::DestroyData(UniquePtr aData) AbstractThread::MainThread()->Dispatch(r.forget()); } - - -bool -DecodedStream::HasConsumers() const -{ - return !mOutputStreamManager.IsEmpty(); -} - - - -void -DecodedStream::AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded) -{ - mOutputStreamManager.Add(aStream, aFinishWhenEnded); -} - -void -DecodedStream::RemoveOutput(MediaStream* aStream) -{ - mOutputStreamManager.Remove(aStream); -} - void DecodedStream::SetPlaying(bool aPlaying) { diff --git a/dom/media/mediasink/DecodedStream.h b/dom/media/mediasink/DecodedStream.h index 1f7877b76..31c524a19 100644 --- a/dom/media/mediasink/DecodedStream.h +++ b/dom/media/mediasink/DecodedStream.h @@ -7,7 +7,6 @@ #ifndef DecodedStream_h_ #define DecodedStream_h_ -#include "nsTArray.h" #include "MediaEventSource.h" #include "MediaInfo.h" #include "MediaSink.h" @@ -20,12 +19,9 @@ namespace mozilla { -class DecodedStream; class DecodedStreamData; class MediaData; -class MediaInputPort; class MediaStream; -class MediaStreamGraph; class OutputStreamManager; struct PlaybackInfoInit; class ProcessedMediaStream; @@ -33,69 +29,14 @@ class TimeStamp; template class MediaQueue; -class OutputStreamData { -public: - ~OutputStreamData(); - 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(); - // 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; - } - // Return the graph mStream belongs to. - MediaStreamGraph* Graph() const; - -private: - OutputStreamManager* mOwner; - RefPtr mStream; - // mPort connects our mStream to an input stream. - RefPtr mPort; -}; - -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(); - // Return the graph these streams belong to or null if empty. - MediaStreamGraph* Graph() const - { - MOZ_ASSERT(NS_IsMainThread()); - return !IsEmpty() ? mStreams[0].Graph() : nullptr; - } - -private: - // Keep the input stream so we can connect the output streams that - // are added after Connect(). - RefPtr mInputStream; - nsTArray mStreams; -}; - class DecodedStream : public media::MediaSink { using media::MediaSink::PlaybackParams; public: DecodedStream(AbstractThread* aOwnerThread, MediaQueue& aAudioQueue, - MediaQueue& aVideoQueue); + MediaQueue& aVideoQueue, + OutputStreamManager* aOutputStreamManager); // MediaSink functions. const PlaybackParams& GetPlaybackParams() const override; @@ -122,11 +63,8 @@ public: // TODO: fix these functions that don't fit into the interface of MediaSink. void BeginShutdown(); - void AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded); - void RemoveOutput(MediaStream* aStream); void SetSameOrigin(bool aSameOrigin); bool IsFinished() const; - bool HasConsumers() const; protected: virtual ~DecodedStream(); @@ -151,7 +89,7 @@ private: * Main thread only members. */ // Data about MediaStreams that are being fed by the decoder. - OutputStreamManager mOutputStreamManager; + const RefPtr mOutputStreamManager; // True if MDSM has begun shutdown. bool mShuttingDown; diff --git a/dom/media/mediasink/OutputStreamManager.cpp b/dom/media/mediasink/OutputStreamManager.cpp new file mode 100644 index 000000000..1d7c03448 --- /dev/null +++ b/dom/media/mediasink/OutputStreamManager.cpp @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "MediaStreamGraph.h" +#include "OutputStreamManager.h" + +namespace mozilla { + +OutputStreamData::~OutputStreamData() +{ + MOZ_ASSERT(NS_IsMainThread()); + // Break the connection to the input stream if necessary. + if (mPort) { + mPort->Destroy(); + } +} + +void +OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream) +{ + mOwner = aOwner; + mStream = aStream; +} + +void +OutputStreamData::Connect(MediaStream* aStream) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mPort, "Already connected?"); + MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream."); + + mPort = mStream->AllocateInputPort(aStream); +} + +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; + } + return true; +} + +bool +OutputStreamData::Equals(MediaStream* aStream) const +{ + return mStream == aStream; +} + +MediaStreamGraph* +OutputStreamData::Graph() const +{ + return mStream->Graph(); +} + +void +OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded) +{ + MOZ_ASSERT(NS_IsMainThread()); + // All streams must belong to the same graph. + MOZ_ASSERT(!Graph() || Graph() == aStream->Graph()); + + // 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); + } + } +} + +} // namespace mozilla diff --git a/dom/media/mediasink/OutputStreamManager.h b/dom/media/mediasink/OutputStreamManager.h new file mode 100644 index 000000000..4245fb7ec --- /dev/null +++ b/dom/media/mediasink/OutputStreamManager.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef OutputStreamManager_h +#define OutputStreamManager_h + +#include "mozilla/RefPtr.h" +#include "nsTArray.h" + +namespace mozilla { + +class MediaInputPort; +class MediaStream; +class MediaStreamGraph; +class OutputStreamManager; +class ProcessedMediaStream; + +class OutputStreamData { +public: + ~OutputStreamData(); + 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(); + // Return true if aStream points to the same object as mStream. + // Used by OutputStreamManager to remove an output stream. + bool Equals(MediaStream* aStream) const; + // Return the graph mStream belongs to. + MediaStreamGraph* Graph() const; + +private: + OutputStreamManager* mOwner; + RefPtr mStream; + // mPort connects our mStream to an input stream. + RefPtr mPort; +}; + +class OutputStreamManager { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(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(); + // Return the graph these streams belong to or null if empty. + MediaStreamGraph* Graph() const + { + MOZ_ASSERT(NS_IsMainThread()); + return !IsEmpty() ? mStreams[0].Graph() : nullptr; + } + +private: + ~OutputStreamManager() {} + // Keep the input stream so we can connect the output streams that + // are added after Connect(). + RefPtr mInputStream; + nsTArray mStreams; +}; + +} // namespace mozilla + +#endif // OutputStreamManager_h diff --git a/dom/media/mediasink/moz.build b/dom/media/mediasink/moz.build index 228bd2e28..91875c199 100644 --- a/dom/media/mediasink/moz.build +++ b/dom/media/mediasink/moz.build @@ -8,6 +8,7 @@ UNIFIED_SOURCES += [ 'AudioSinkWrapper.cpp', 'DecodedAudioDataSink.cpp', 'DecodedStream.cpp', + 'OutputStreamManager.cpp', 'VideoSink.cpp', ]