author | JW Wang <jwwang@mozilla.com> |
Mon, 17 Aug 2015 07:52:28 +0800 | |
changeset 257974 | 6dd75dc65de38db09c04203f906733513a688213 |
parent 257973 | f93e180301f3f227b243fa0d71050bb0f0159405 |
child 257975 | e44c94a159dbf8d50b52a4e2d099498e64576c8b |
push id | 29238 |
push user | ryanvm@gmail.com |
push date | Mon, 17 Aug 2015 13:06:57 +0000 |
treeherder | mozilla-central@a6eeb28458fd [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | roc |
bugs | 1191696 |
milestone | 43.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/DecodedStream.cpp +++ b/dom/media/DecodedStream.cpp @@ -1,21 +1,21 @@ /* -*- 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 "AudioSegment.h" #include "DecodedStream.h" +#include "MediaData.h" +#include "MediaQueue.h" #include "MediaStreamGraph.h" -#include "AudioSegment.h" +#include "SharedBuffer.h" #include "VideoSegment.h" -#include "MediaQueue.h" -#include "MediaData.h" -#include "SharedBuffer.h" #include "VideoUtils.h" namespace mozilla { class DecodedStreamGraphListener : public MediaStreamListener { typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; public: explicit DecodedStreamGraphListener(MediaStream* aStream) @@ -239,24 +239,25 @@ DecodedStream::DecodedStream(MediaQueue< , mVideoQueue(aVideoQueue) { } DecodedStream::~DecodedStream() { } -void +nsRefPtr<GenericPromise> 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() { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mStartTime.reset(); } @@ -675,36 +676,39 @@ DecodedStream::AdvanceTracks() endPosition = std::max(endPosition, videoEnd); } if (!mData->mHaveSentFinish) { mData->mStream->AdvanceKnownTracksTime(endPosition); } } -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); AdvanceTracks(); bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) && (!mInfo.HasVideo() || mVideoQueue.IsFinished()); if (finished && !mData->mHaveSentFinish) { mData->mHaveSentFinish = true; mData->mStream->Finish(); } - - return finished; } int64_t DecodedStream::AudioEndTime() const { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); if (mStartTime.isSome() && mInfo.HasAudio()) { CheckedInt64 t = mStartTime.ref() +
--- a/dom/media/DecodedStream.h +++ b/dom/media/DecodedStream.h @@ -7,16 +7,17 @@ #ifndef DecodedStream_h_ #define DecodedStream_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" namespace mozilla { class DecodedStream; @@ -48,17 +49,21 @@ public: class DecodedStream { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream); public: DecodedStream(MediaQueue<MediaData>& aAudioQueue, MediaQueue<MediaData>& aVideoQueue); // 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<GenericPromise> StartPlayback(int64_t aStartTime, + const MediaInfo& aInfo); // Mimic MDSM::StopAudioThread. void StopPlayback(); void DestroyData(); void RecreateData(); void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded); void Remove(MediaStream* aStream); @@ -66,18 +71,17 @@ public: 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(); + void SendData(); protected: virtual ~DecodedStream(); private: ReentrantMonitor& GetReentrantMonitor() const; void RecreateData(MediaStreamGraph* aGraph); void Connect(OutputStreamData* aStream);
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -371,38 +371,32 @@ int64_t MediaDecoderStateMachine::GetDec } void MediaDecoderStateMachine::SendStreamData() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()"); - bool finished = mDecodedStream->SendData(); + mDecodedStream->SendData(); const auto clockTime = GetClock(); while (true) { const MediaData* a = AudioQueue().PeekFront(); // If we discard audio samples fed to the stream immediately, we will // keep decoding audio samples till the end and consume a lot of memory. // Therefore we only discard those behind the stream clock to throttle // the decoding speed. if (a && a->mTime <= clockTime) { nsRefPtr<MediaData> releaseMe = AudioQueue().PopFront(); continue; } 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) { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); if (AudioQueue().GetSize() == 0 || @@ -1074,22 +1068,17 @@ void MediaDecoderStateMachine::MaybeStar DECODER_LOG("MaybeStartPlayback() starting playback"); mDecoder->DispatchPlaybackStarted(); SetPlayStartTime(TimeStamp::Now()); 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(); } void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime) { MOZ_ASSERT(OnTaskQueue()); SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld)", aTime); @@ -1787,16 +1776,42 @@ MediaDecoderStateMachine::StartAudioThre &MediaDecoderStateMachine::OnAudioSinkError)); mAudioSink->SetVolume(mVolume); mAudioSink->SetPlaybackRate(mPlaybackRate); mAudioSink->SetPreservesPitch(mPreservesPitch); } } +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()); NS_ASSERTION(HasAudio(), "Should only call AudioDecodedUsecs() when we have audio"); // The amount of audio we have decoded is the amount of audio data we've // already decoded and pushed to the hardware, plus the amount of audio // data waiting to be pushed to the hardware. @@ -2390,17 +2405,17 @@ nsresult MediaDecoderStateMachine::RunSt NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded); 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(); + StopDecodedStream(); } return NS_OK; } } return NS_OK; } @@ -2420,17 +2435,17 @@ MediaDecoderStateMachine::Reset() mState == DECODER_STATE_SEEKING || mState == DECODER_STATE_DORMANT || mState == DECODER_STATE_DECODING_NONE); // 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(); + StopDecodedStream(); mVideoFrameEndTime = -1; mDecodedVideoEndTime = -1; mDecodedAudioEndTime = -1; mAudioCompleted = false; AudioQueue().Reset(); VideoQueue().Reset(); mFirstVideoFrameAfterSeek = nullptr; @@ -3071,16 +3086,42 @@ void MediaDecoderStateMachine::OnAudioSi return; } // Otherwise notify media decoder/element about this error for it makes // no sense to play an audio-only file without sound output. 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<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE) : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE); } @@ -3094,33 +3135,34 @@ void MediaDecoderStateMachine::DispatchA ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); if (!self->mAudioCaptured) { // Stop the audio sink if it's running. self->StopAudioThread(); 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(); } }); OwnerThread()->Dispatch(r.forget()); } void MediaDecoderStateMachine::DispatchAudioUncaptured() { nsRefPtr<MediaDecoderStateMachine> self = this; nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void { 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->ScheduleStateMachine(); } }); OwnerThread()->Dispatch(r.forget());
--- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -513,16 +513,20 @@ protected: // Stops the audio thread. The decoder monitor must be held with exactly // one lock count. Called on the state machine thread. void StopAudioThread(); // Starts the audio thread. The decoder monitor must be held with exactly // one lock count. Called on the state machine thread. void StartAudioThread(); + void StopDecodedStream(); + + void StartDecodedStream(); + // Notification method invoked when mPlayState changes. void PlayStateChanged(); // Notification method invoked when mLogicallySeeking changes. void LogicallySeekingChanged(); // Notification method invoked when mSameOriginMedia changes. void SameOriginMediaChanged(); @@ -665,16 +669,20 @@ protected: private: // Resolved by the AudioSink to signal that all outstanding work is complete // and the sink is shutting down. void OnAudioSinkComplete(); // 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(); // The decoder object that created this state machine. The state machine // holds a strong reference to the decoder to ensure that the decoder stays // alive once media element has started the decoder shutdown process, and has // dropped its reference to the decoder. This enables the state machine to @@ -1279,16 +1287,17 @@ private: // can be read on any thread while holding the monitor, or on the main thread // without holding the monitor. nsRefPtr<DecodedStream> mDecodedStream; // Media data resource from the decoder. nsRefPtr<MediaResource> mResource; MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise; + MozPromiseRequestHolder<GenericPromise> mDecodedStreamPromise; MediaEventListener mAudioQueueListener; MediaEventListener mVideoQueueListener; private: // The buffered range. Mirrored from the decoder thread. Mirror<media::TimeIntervals> mBuffered;