diff --git a/dom/media/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp similarity index 93% rename from dom/media/DecodedStream.cpp rename to dom/media/mediasink/DecodedStream.cpp index cfd5a2843a07..bc2c9facc92c 100644 --- a/dom/media/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -4,6 +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 "mozilla/CheckedInt.h" +#include "mozilla/gfx/Point.h" + #include "AudioSegment.h" #include "DecodedStream.h" #include "MediaData.h" @@ -358,7 +361,6 @@ DecodedStream::DecodedStream(AbstractThread* aOwnerThread, : mOwnerThread(aOwnerThread) , mShuttingDown(false) , mPlaying(false) - , mVolume(1.0) , mSameOrigin(false) , mAudioQueue(aAudioQueue) , mVideoQueue(aVideoQueue) @@ -370,21 +372,52 @@ DecodedStream::~DecodedStream() MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended."); } +const media::MediaSink::PlaybackParams& +DecodedStream::GetPlaybackParams() const +{ + AssertOwnerThread(); + return mParams; +} + void -DecodedStream::Shutdown() +DecodedStream::SetPlaybackParams(const PlaybackParams& aParams) +{ + AssertOwnerThread(); + mParams = aParams; +} + +nsRefPtr +DecodedStream::OnEnded(TrackType aType) +{ + AssertOwnerThread(); + MOZ_ASSERT(mStartTime.isSome()); + + if (aType == TrackInfo::kAudioTrack) { + // TODO: we should return a promise which is resolved when the audio track + // is finished. For now this promise is resolved when the whole stream is + // finished. + return mFinishPromise; + } + // TODO: handle video track. + return nullptr; +} + +void +DecodedStream::BeginShutdown() { MOZ_ASSERT(NS_IsMainThread()); mShuttingDown = true; } -nsRefPtr -DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo) +void +DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) { AssertOwnerThread(); MOZ_ASSERT(mStartTime.isNothing(), "playback already started."); mStartTime.emplace(aStartTime); mInfo = aInfo; + mPlaying = true; ConnectListener(); class R : public nsRunnable { @@ -408,30 +441,33 @@ DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo) }; MozPromiseHolder promise; - nsRefPtr rv = promise.Ensure(__func__); + mFinishPromise = promise.Ensure(__func__); nsCOMPtr r = new R(this, &DecodedStream::CreateData, Move(promise)); AbstractThread::MainThread()->Dispatch(r.forget()); - - return rv.forget(); } -void DecodedStream::StopPlayback() +void +DecodedStream::Stop() { AssertOwnerThread(); - - // Playback didn't even start at all. - if (mStartTime.isNothing()) { - return; - } + MOZ_ASSERT(mStartTime.isSome(), "playback not started."); mStartTime.reset(); DisconnectListener(); + mFinishPromise = nullptr; // Clear mData immediately when this playback session ends so we won't // send data to the wrong stream in SendData() in next playback session. DestroyData(Move(mData)); } +bool +DecodedStream::IsStarted() const +{ + AssertOwnerThread(); + return mStartTime.isSome(); +} + void DecodedStream::DestroyData(UniquePtr aData) { @@ -531,6 +567,12 @@ void DecodedStream::SetPlaying(bool aPlaying) { AssertOwnerThread(); + + // Resume/pause matters only when playback started. + if (mStartTime.isNothing()) { + return; + } + mPlaying = aPlaying; if (mData) { mData->SetPlaying(aPlaying); @@ -541,7 +583,21 @@ void DecodedStream::SetVolume(double aVolume) { AssertOwnerThread(); - mVolume = aVolume; + mParams.volume = aVolume; +} + +void +DecodedStream::SetPlaybackRate(double aPlaybackRate) +{ + AssertOwnerThread(); + mParams.playbackRate = aPlaybackRate; +} + +void +DecodedStream::SetPreservesPitch(bool aPreservesPitch) +{ + AssertOwnerThread(); + mParams.preservesPitch = aPreservesPitch; } void @@ -815,7 +871,7 @@ DecodedStream::SendData() } InitTracks(); - SendAudio(mVolume, mSameOrigin); + SendAudio(mParams.volume, mSameOrigin); SendVideo(mSameOrigin); AdvanceTracks(); @@ -829,26 +885,31 @@ DecodedStream::SendData() } int64_t -DecodedStream::AudioEndTime() const +DecodedStream::GetEndTime(TrackType aType) const { AssertOwnerThread(); - if (mStartTime.isSome() && mInfo.HasAudio() && mData) { + if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio() && mData) { CheckedInt64 t = mStartTime.ref() + FramesToUsecs(mData->mAudioFramesWritten, mInfo.mAudio.mRate); if (t.isValid()) { return t.value(); } + } else if (aType == TrackInfo::kVideoTrack && mData) { + return mData->mNextVideoTime; } return -1; } int64_t -DecodedStream::GetPosition() const +DecodedStream::GetPosition(TimeStamp* aTimeStamp) const { AssertOwnerThread(); // This is only called after MDSM starts playback. So mStartTime is // guaranteed to be something. MOZ_ASSERT(mStartTime.isSome()); + if (aTimeStamp) { + *aTimeStamp = TimeStamp::Now(); + } return mStartTime.ref() + (mData ? mData->GetPosition() : 0); } diff --git a/dom/media/DecodedStream.h b/dom/media/mediasink/DecodedStream.h similarity index 80% rename from dom/media/DecodedStream.h rename to dom/media/mediasink/DecodedStream.h index 463bd4ae2f07..5b55d6851758 100644 --- a/dom/media/DecodedStream.h +++ b/dom/media/mediasink/DecodedStream.h @@ -10,15 +10,13 @@ #include "nsTArray.h" #include "MediaEventSource.h" #include "MediaInfo.h" +#include "MediaSink.h" #include "mozilla/AbstractThread.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" namespace mozilla { @@ -31,7 +29,7 @@ class MediaStreamGraph; class OutputStreamListener; class OutputStreamManager; class ProcessedMediaStream; -class ReentrantMonitor; +class TimeStamp; template class MediaQueue; @@ -99,34 +97,41 @@ private: nsTArray mStreams; }; -class DecodedStream { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream); +class DecodedStream : public media::MediaSink { + using media::MediaSink::PlaybackParams; + public: DecodedStream(AbstractThread* aOwnerThread, MediaQueue& aAudioQueue, MediaQueue& aVideoQueue); - void Shutdown(); + // MediaSink functions. + const PlaybackParams& GetPlaybackParams() const override; + void SetPlaybackParams(const PlaybackParams& aParams) override; - // Mimic MDSM::StartAudioThread. - // Must be called before any calls to SendData(). - // - // 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(); + nsRefPtr OnEnded(TrackType aType) override; + int64_t GetEndTime(TrackType aType) const override; + int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override; + bool HasUnplayedFrames(TrackType aType) const override + { + // TODO: implement this. + return false; + } + void SetVolume(double aVolume) override; + void SetPlaybackRate(double aPlaybackRate) override; + void SetPreservesPitch(bool aPreservesPitch) override; + void SetPlaying(bool aPlaying) override; + + void Start(int64_t aStartTime, const MediaInfo& aInfo) override; + void Stop() override; + bool IsStarted() const override; + + // 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 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; @@ -164,10 +169,11 @@ private: * Worker thread only members. */ UniquePtr mData; + nsRefPtr mFinishPromise; bool mPlaying; - double mVolume; bool mSameOrigin; + PlaybackParams mParams; Maybe mStartTime; MediaInfo mInfo;