diff --git a/dom/media/DecodedStream.cpp b/dom/media/DecodedStream.cpp index 18c1828f3b3..c5edfab1e0b 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 { @@ -244,14 +244,15 @@ DecodedStream::~DecodedStream() { } -void +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: fix me in next patches. + return nullptr; } void DecodedStream::StopPlayback() @@ -680,12 +681,17 @@ DecodedStream::AdvanceTracks() } } -bool +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(mVolume, mSameOrigin); SendVideo(mSameOrigin); @@ -698,8 +704,6 @@ DecodedStream::SendData() mData->mHaveSentFinish = true; mData->mStream->Finish(); } - - return finished; } int64_t diff --git a/dom/media/DecodedStream.h b/dom/media/DecodedStream.h index f0436f1da94..b303e52bd03 100644 --- a/dom/media/DecodedStream.h +++ b/dom/media/DecodedStream.h @@ -12,6 +12,7 @@ #include "mozilla/CheckedInt.h" #include "mozilla/Maybe.h" +#include "mozilla/MozPromise.h" #include "mozilla/nsRefPtr.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/UniquePtr.h" @@ -53,7 +54,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(); @@ -71,8 +76,7 @@ public: bool IsFinished() const; bool HasConsumers() const; - // Return true if stream is finished. - bool SendData(); + void SendData(); protected: virtual ~DecodedStream(); diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index c5ae939ff12..3337b70ca17 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -376,7 +376,7 @@ void MediaDecoderStateMachine::SendStreamData() AssertCurrentThreadInMonitor(); MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()"); - bool finished = mDecodedStream->SendData(); + mDecodedStream->SendData(); const auto clockTime = GetClock(); while (true) { @@ -392,12 +392,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) @@ -1079,12 +1073,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback() MOZ_ASSERT(IsPlaying()); StartAudioThread(); - - // 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); - } + StartDecodedStream(); DispatchDecodeTasksIfNeeded(); } @@ -1792,6 +1781,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()); @@ -2395,7 +2410,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine() // Stop audio sink after call to AudioEndTime() above, otherwise it will // return an incorrect value due to a null mAudioSink. StopAudioThread(); - mDecodedStream->StopPlayback(); + StopDecodedStream(); } return NS_OK; @@ -2425,7 +2440,7 @@ MediaDecoderStateMachine::Reset() // outside of the decoder monitor while we are clearing the queue and causes // crash for no samples to be popped. StopAudioThread(); - mDecodedStream->StopPlayback(); + StopDecodedStream(); mVideoFrameEndTime = -1; mDecodedVideoEndTime = -1; @@ -3076,6 +3091,32 @@ 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()); @@ -3099,7 +3140,7 @@ void MediaDecoderStateMachine::DispatchAudioCaptured() // 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(); } @@ -3115,7 +3156,8 @@ 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(); diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 4917d9cd278..1f9076db0a9 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -518,6 +518,10 @@ protected: // one lock count. Called on the state machine thread. void StartAudioThread(); + void StopDecodedStream(); + + void StartDecodedStream(); + // Notification method invoked when mPlayState changes. void PlayStateChanged(); @@ -670,6 +674,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(); @@ -1284,6 +1292,7 @@ private: nsRefPtr mResource; MozPromiseRequestHolder mAudioSinkPromise; + MozPromiseRequestHolder mDecodedStreamPromise; MediaEventListener mAudioQueueListener; MediaEventListener mVideoQueueListener;