diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index f3bdd42286ae..c8a4d6e0e1b8 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -191,12 +191,12 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity) if (mIsDormant) { DECODER_LOG("UpdateDormantState() entering DORMANT state"); // enter dormant state - nsCOMPtr event = + RefPtr event = NS_NewRunnableMethodWithArg( mDecoderStateMachine, &MediaDecoderStateMachine::SetDormant, true); - mDecoderStateMachine->GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL); + mDecoderStateMachine->TaskQueue()->Dispatch(event); if (IsEnded()) { mWasEndedWhenEnteredDormant = true; @@ -207,12 +207,12 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity) DECODER_LOG("UpdateDormantState() leaving DORMANT state"); // exit dormant state // trigger to state machine. - nsCOMPtr event = + RefPtr event = NS_NewRunnableMethodWithArg( mDecoderStateMachine, &MediaDecoderStateMachine::SetDormant, false); - mDecoderStateMachine->GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL); + mDecoderStateMachine->TaskQueue()->Dispatch(event); } } @@ -750,7 +750,8 @@ nsresult MediaDecoder::ScheduleStateMachineThread() if (mShuttingDown) return NS_OK; ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - return mDecoderStateMachine->ScheduleStateMachine(); + mDecoderStateMachine->ScheduleStateMachine(); + return NS_OK; } nsresult MediaDecoder::Play() @@ -763,8 +764,7 @@ nsresult MediaDecoder::Play() if (mPausedForPlaybackRateNull) { return NS_OK; } - nsresult res = ScheduleStateMachineThread(); - NS_ENSURE_SUCCESS(res,res); + ScheduleStateMachineThread(); if (IsEnded()) { return Seek(0, SeekTarget::PrevSyncPoint); } else if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) { @@ -1317,7 +1317,7 @@ void MediaDecoder::ApplyStateToStateMachine(PlayState aState) mDecoderStateMachine->Play(); break; case PLAY_STATE_SEEKING: - mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->GetStateMachineThread(), + mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(), mDecoderStateMachine.get(), __func__, &MediaDecoderStateMachine::Seek, mRequestedSeekTarget) ->RefableThen(NS_GetCurrentThread(), __func__, this, diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 9a58cf77dcba..f61f8c42255e 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -13,7 +13,7 @@ #include #include "MediaDecoderStateMachine.h" -#include "MediaDecoderStateMachineScheduler.h" +#include "MediaTimer.h" #include "AudioSink.h" #include "nsTArray.h" #include "MediaDecoder.h" @@ -201,9 +201,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaDecoderReader* aReader, bool aRealTime) : mDecoder(aDecoder), - mScheduler(new MediaDecoderStateMachineScheduler( - aDecoder->GetReentrantMonitor(), - &MediaDecoderStateMachine::TimeoutExpired, this, aRealTime)), + mRealTime(aRealTime), + mDispatchedStateMachine(false), + mDelayedScheduler(this), mState(DECODER_STATE_DECODING_NONE), mPlayDuration(0), mStartTime(-1), @@ -248,6 +248,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MOZ_COUNT_CTOR(MediaDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); + // Set up our task queue. + RefPtr threadPool( + SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1)); + mTaskQueue = new MediaTaskQueue(threadPool.forget()); + static bool sPrefCacheInit = false; if (!sPrefCacheInit) { sPrefCacheInit = true; @@ -387,7 +392,7 @@ void MediaDecoderStateMachine::SendStreamData() { MOZ_ASSERT(OnStateMachineThread(), "Should be on state machine thread"); AssertCurrentThreadInMonitor(); - MOZ_ASSERT(!mAudioSink, "Should've been stopped in CallRunStateMachine()"); + MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()"); DecodedStreamData* stream = mDecoder->GetDecodedStream(); @@ -405,7 +410,7 @@ void MediaDecoderStateMachine::SendStreamData() mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio, SourceMediaStream::ADDTRACK_QUEUED); stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId, - GetStateMachineThread(), GetWakeDecoderRunnable()); + TaskQueue(), GetWakeDecoderRunnable()); stream->mNextAudioTime = mStartTime + stream->mInitialTime; } if (mInfo.HasVideo()) { @@ -414,7 +419,7 @@ void MediaDecoderStateMachine::SendStreamData() mediaStream->AddTrack(videoTrackId, 0, video, SourceMediaStream::ADDTRACK_QUEUED); stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId, - GetStateMachineThread(), GetWakeDecoderRunnable()); + TaskQueue(), GetWakeDecoderRunnable()); // TODO: We can't initialize |mNextVideoTime| until |mStartTime| // is set. This is a good indication that DecodedStreamData is in @@ -570,7 +575,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs) return false; } stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId, - GetStateMachineThread(), GetWakeDecoderRunnable()); + TaskQueue(), GetWakeDecoderRunnable()); } return true; @@ -593,7 +598,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo() return false; } stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId, - GetStateMachineThread(), GetWakeDecoderRunnable()); + TaskQueue(), GetWakeDecoderRunnable()); } return true; @@ -855,7 +860,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType, "Readers that send WAITING_FOR_DATA need to implement WaitForData"); WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::WaitForData, aType) - ->RefableThen(mScheduler.get(), __func__, this, + ->RefableThen(TaskQueue(), __func__, this, &MediaDecoderStateMachine::OnWaitForDataResolved, &MediaDecoderStateMachine::OnWaitForDataRejected)); return; @@ -1084,7 +1089,7 @@ MediaDecoderStateMachine::CheckIfSeekComplete() mDecodeToSeekTarget = false; RefPtr task( NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted)); - nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL); + nsresult rv = TaskQueue()->Dispatch(task); if (NS_FAILED(rv)) { DecodeError(); } @@ -1146,10 +1151,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) cloneReader = aCloneDonor->mReader; } - nsresult rv = mScheduler->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mReader->Init(cloneReader); + nsresult rv = mReader->Init(cloneReader); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -1343,7 +1345,7 @@ int64_t MediaDecoderStateMachine::GetCurrentTimeUs() const } bool MediaDecoderStateMachine::IsRealTime() const { - return mScheduler->IsRealTime(); + return mRealTime; } int64_t MediaDecoderStateMachine::GetDuration() @@ -1513,9 +1515,8 @@ void MediaDecoderStateMachine::Shutdown() // Change state before issuing shutdown request to threads so those // threads can start exiting cleanly during the Shutdown call. - DECODER_LOG("Changed state to SHUTDOWN"); + ScheduleStateMachine(); SetState(DECODER_STATE_SHUTDOWN); - mScheduler->ScheduleAndShutdown(); if (mAudioSink) { mAudioSink->PrepareToShutdown(); } @@ -1774,7 +1775,7 @@ MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask() RefPtr task( NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame)); - nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL); + nsresult rv = TaskQueue()->Dispatch(task); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -1921,7 +1922,7 @@ MediaDecoderStateMachine::InitiateSeek() mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime, GetEndTime()) - ->RefableThen(mScheduler.get(), __func__, this, + ->RefableThen(TaskQueue(), __func__, this, &MediaDecoderStateMachine::OnSeekCompleted, &MediaDecoderStateMachine::OnSeekFailed)); } @@ -1969,7 +1970,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued() mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::RequestAudioData) - ->RefableThen(mScheduler.get(), __func__, this, + ->RefableThen(TaskQueue(), __func__, this, &MediaDecoderStateMachine::OnAudioDecoded, &MediaDecoderStateMachine::OnAudioNotDecoded)); @@ -2029,7 +2030,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued() mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::RequestVideoData, skipToNextKeyFrame, currentTime) - ->RefableThen(mScheduler.get(), __func__, this, + ->RefableThen(TaskQueue(), __func__, this, &MediaDecoderStateMachine::OnVideoDecoded, &MediaDecoderStateMachine::OnVideoNotDecoded)); return NS_OK; @@ -2167,9 +2168,9 @@ MediaDecoderStateMachine::DecodeError() // Change state to shutdown before sending error report to MediaDecoder // and the HTMLMediaElement, so that our pipeline can start exiting // cleanly during the sync dispatch below. - DECODER_WARN("Decode error, changed state to SHUTDOWN due to error"); + ScheduleStateMachine(); SetState(DECODER_STATE_SHUTDOWN); - mScheduler->ScheduleAndShutdown(); + DECODER_WARN("Decode error, changed state to SHUTDOWN due to error"); mDecoder->GetReentrantMonitor().NotifyAll(); // Dispatch the event to call DecodeError synchronously. This ensures @@ -2322,12 +2323,12 @@ MediaDecoderStateMachine::DecodeFirstFrame() if (HasAudio()) { RefPtr decodeTask( NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded)); - AudioQueue().AddPopListener(decodeTask, GetStateMachineThread()); + AudioQueue().AddPopListener(decodeTask, TaskQueue()); } if (HasVideo()) { RefPtr decodeTask( NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded)); - VideoQueue().AddPopListener(decodeTask, GetStateMachineThread()); + VideoQueue().AddPopListener(decodeTask, TaskQueue()); } if (IsRealTime()) { @@ -2345,7 +2346,7 @@ MediaDecoderStateMachine::DecodeFirstFrame() if (HasAudio()) { mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::RequestAudioData) - ->RefableThen(mScheduler.get(), __func__, this, + ->RefableThen(TaskQueue(), __func__, this, &MediaDecoderStateMachine::OnAudioDecoded, &MediaDecoderStateMachine::OnAudioNotDecoded)); } @@ -2353,7 +2354,7 @@ MediaDecoderStateMachine::DecodeFirstFrame() mVideoDecodeStartTime = TimeStamp::Now(); mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::RequestVideoData, false, int64_t(0)) - ->RefableThen(mScheduler.get(), __func__, this, + ->RefableThen(TaskQueue(), __func__, this, &MediaDecoderStateMachine::OnVideoDecoded, &MediaDecoderStateMachine::OnVideoNotDecoded)); } @@ -2560,6 +2561,8 @@ public: NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); MOZ_ASSERT(mStateMachine); MOZ_ASSERT(mDecoder); + mStateMachine->TaskQueue()->BeginShutdown(); + mStateMachine->TaskQueue()->AwaitShutdownAndIdle(); mStateMachine->BreakCycles(); mDecoder->BreakCycles(); mStateMachine = nullptr; @@ -2594,7 +2597,7 @@ void MediaDecoderStateMachine::ShutdownReader() { MOZ_ASSERT(OnDecodeThread()); - mReader->Shutdown()->Then(mScheduler.get(), __func__, this, + mReader->Shutdown()->Then(TaskQueue(), __func__, this, &MediaDecoderStateMachine::FinishShutdown, &MediaDecoderStateMachine::FinishShutdown); } @@ -2630,15 +2633,24 @@ MediaDecoderStateMachine::FinishShutdown() // finished and released its monitor/references. That event then will // dispatch an event to the main thread to release the decoder and // state machine. - GetStateMachineThread()->Dispatch( - new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL); + RefPtr task = new nsDispatchDisposeEvent(mDecoder, this); + TaskQueue()->Dispatch(task); DECODER_LOG("Dispose Event Dispatched"); } nsresult MediaDecoderStateMachine::RunStateMachine() { - AssertCurrentThreadInMonitor(); + MOZ_ASSERT(OnStateMachineThread()); + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + + mDelayedScheduler.Reset(); // Must happen on state machine thread. + mDispatchedStateMachine = false; + + // If audio is being captured, stop the audio sink if it's running + if (mAudioCaptured) { + StopAudioThread(); + } MediaResource* resource = mDecoder->GetResource(); NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER); @@ -2739,7 +2751,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine() DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s", mBufferingWait, mBufferingWait - elapsed.ToSeconds(), (mQuickBuffering ? "(quick exit)" : "")); - ScheduleStateMachine(USECS_PER_S); + ScheduleStateMachineIn(USECS_PER_S); return NS_OK; } } else if (OutOfDecodedAudio() || OutOfDecodedVideo()) { @@ -3063,7 +3075,7 @@ void MediaDecoderStateMachine::AdvanceFrame() // Don't go straight back to the state machine loop since that might // cause us to start decoding again and we could flip-flop between // decoding and quick-buffering. - ScheduleStateMachine(USECS_PER_S); + ScheduleStateMachineIn(USECS_PER_S); return; } } @@ -3125,7 +3137,12 @@ void MediaDecoderStateMachine::AdvanceFrame() // ready state. Post an update to do so. UpdateReadyState(); - ScheduleStateMachine(remainingTime / mPlaybackRate); + int64_t delay = remainingTime / mPlaybackRate; + if (delay > 0) { + ScheduleStateMachineIn(delay); + } else { + ScheduleStateMachine(); + } } nsresult @@ -3371,33 +3388,59 @@ void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp) } } -nsresult MediaDecoderStateMachine::CallRunStateMachine() -{ - AssertCurrentThreadInMonitor(); - NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); - - // If audio is being captured, stop the audio sink if it's running - if (mAudioCaptured) { - StopAudioThread(); - } - - return RunStateMachine(); -} - -nsresult MediaDecoderStateMachine::TimeoutExpired(void* aClosure) -{ - MediaDecoderStateMachine* p = static_cast(aClosure); - return p->CallRunStateMachine(); -} - void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); DispatchAudioDecodeTaskIfNeeded(); DispatchVideoDecodeTaskIfNeeded(); } -nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) { - return mScheduler->Schedule(aUsecs); +void +MediaDecoderStateMachine::ScheduleStateMachine() { + AssertCurrentThreadInMonitor(); + if (mState == DECODER_STATE_SHUTDOWN) { + NS_WARNING("Refusing to schedule shutdown state machine"); + return; + } + + if (mDispatchedStateMachine) { + return; + } + mDispatchedStateMachine = true; + + RefPtr task = + NS_NewRunnableMethod(this, &MediaDecoderStateMachine::RunStateMachine); + nsresult rv = TaskQueue()->Dispatch(task); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + (void) rv; +} + +void +MediaDecoderStateMachine::ScheduleStateMachineIn(int64_t aMicroseconds) +{ + AssertCurrentThreadInMonitor(); + MOZ_ASSERT(OnStateMachineThread()); // mDelayedScheduler.Ensure() may Disconnect() + // the promise, which must happen on the state + // machine thread. + MOZ_ASSERT(aMicroseconds > 0); + if (mState == DECODER_STATE_SHUTDOWN) { + NS_WARNING("Refusing to schedule shutdown state machine"); + return; + } + + if (mDispatchedStateMachine) { + return; + } + + // Real-time weirdness. + if (IsRealTime()) { + aMicroseconds = std::min(aMicroseconds, int64_t(40000)); + } + + TimeStamp now = TimeStamp::Now(); + TimeStamp target = now + TimeDuration::FromMicroseconds(aMicroseconds); + + SAMPLE_LOG("Scheduling state machine for %lf ms from now", (target - now).ToMilliseconds()); + mDelayedScheduler.Ensure(target); } bool MediaDecoderStateMachine::OnDecodeThread() const @@ -3407,17 +3450,12 @@ bool MediaDecoderStateMachine::OnDecodeThread() const bool MediaDecoderStateMachine::OnStateMachineThread() const { - return mScheduler->OnStateMachineThread(); -} - -nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() const -{ - return mScheduler->GetStateMachineThread(); + return TaskQueue()->IsCurrentThreadIn(); } bool MediaDecoderStateMachine::IsStateMachineScheduled() const { - return mScheduler->IsScheduled(); + return mDispatchedStateMachine || mDelayedScheduler.IsScheduled(); } void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate) diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index aa5d1e6be4c9..28e38a1c21e7 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -89,8 +89,8 @@ hardware (via AudioStream). #include "MediaDecoderReader.h" #include "MediaDecoderOwner.h" #include "MediaMetadataManager.h" -#include "MediaDecoderStateMachineScheduler.h" #include "mozilla/RollingMean.h" +#include "MediaTimer.h" class nsITimer; @@ -211,8 +211,8 @@ public: void Play() { MOZ_ASSERT(NS_IsMainThread()); - nsRefPtr r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal); - GetStateMachineThread()->Dispatch(r, NS_DISPATCH_NORMAL); + RefPtr r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal); + TaskQueue()->Dispatch(r); } private: @@ -311,21 +311,30 @@ public: void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); - // Returns the shared state machine thread. - nsIEventTarget* GetStateMachineThread() const; + // Returns the state machine task queue. + MediaTaskQueue* TaskQueue() const { return mTaskQueue; } // Calls ScheduleStateMachine() after taking the decoder lock. Also // notifies the decoder thread in case it's waiting on the decoder lock. void ScheduleStateMachineWithLockAndWakeDecoder(); - // Schedules the shared state machine thread to run the state machine - // in aUsecs microseconds from now, if it's not already scheduled to run - // earlier, in which case the request is discarded. - nsresult ScheduleStateMachine(int64_t aUsecs = 0); + // Schedules the shared state machine thread to run the state machine. + void ScheduleStateMachine(); - // Callback function registered with MediaDecoderStateMachineScheduler - // to run state machine cycles. - static nsresult TimeoutExpired(void* aClosure); + // Invokes ScheduleStateMachine to run in |aMicroseconds| microseconds, + // unless it's already scheduled to run earlier, in which case the + // request is discarded. + void ScheduleStateMachineIn(int64_t aMicroseconds); + + void OnDelayedSchedule() + { + MOZ_ASSERT(OnStateMachineThread()); + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + mDelayedScheduler.CompleteRequest(); + ScheduleStateMachine(); + } + + void NotReached() { MOZ_DIAGNOSTIC_ASSERT(false); } // Set the media fragment end time. aEndTime is in microseconds. void SetFragmentEndTime(int64_t aEndTime); @@ -689,9 +698,6 @@ protected: void SendStreamAudio(AudioData* aAudio, DecodedStreamData* aStream, AudioSegment* aOutput); - // State machine thread run function. Defers to RunStateMachine(). - nsresult CallRunStateMachine(); - // Performs one "cycle" of the state machine. Polls the state, and may send // a video frame to be displayed, and generally manages the decode. Called // periodically via timer to ensure the video stays in sync. @@ -745,9 +751,60 @@ protected: // state machine, audio and main threads. nsRefPtr mDecoder; - // Used to schedule state machine cycles. This should never outlive - // the life cycle of the state machine. - const nsRefPtr mScheduler; + // Task queue for running the state machine. + nsRefPtr mTaskQueue; + + // True is we are decoding a realtime stream, like a camera stream. + bool mRealTime; + + // True if we've dispatched a task to run the state machine but the task has + // yet to run. + bool mDispatchedStateMachine; + + // Class for managing delayed dispatches of the state machine. + class DelayedScheduler { + public: + explicit DelayedScheduler(MediaDecoderStateMachine* aSelf) + : mSelf(aSelf), mMediaTimer(new MediaTimer()) {} + + bool IsScheduled() const { return !mTarget.IsNull(); } + + void Reset() + { + MOZ_ASSERT(mSelf->OnStateMachineThread(), + "Must be on state machine queue to disconnect"); + if (IsScheduled()) { + mRequest.Disconnect(); + mTarget = TimeStamp(); + } + } + + void Ensure(mozilla::TimeStamp& aTarget) + { + if (IsScheduled() && mTarget <= aTarget) { + return; + } + Reset(); + mTarget = aTarget; + mRequest.Begin(mMediaTimer->WaitUntil(mTarget, __func__)->RefableThen( + mSelf->TaskQueue(), __func__, mSelf, + &MediaDecoderStateMachine::OnDelayedSchedule, + &MediaDecoderStateMachine::NotReached)); + } + + void CompleteRequest() + { + mRequest.Complete(); + mTarget = TimeStamp(); + } + + private: + MediaDecoderStateMachine* mSelf; + nsRefPtr mMediaTimer; + MediaPromiseConsumerHolder mRequest; + TimeStamp mTarget; + + } mDelayedScheduler; // Time at which the last video sample was requested. If it takes too long // before the sample arrives, we will increase the amount of audio we buffer. @@ -955,7 +1012,7 @@ protected: // samples we must consume before are considered to be finished prerolling. uint32_t AudioPrerollUsecs() const { - if (mScheduler->IsRealTime()) { + if (IsRealTime()) { return 0; } @@ -966,7 +1023,7 @@ protected: uint32_t VideoPrerollFrames() const { - return mScheduler->IsRealTime() ? 0 : GetAmpleVideoFrames() / 2; + return IsRealTime() ? 0 : GetAmpleVideoFrames() / 2; } bool DonePrerollingAudio() diff --git a/dom/media/MediaDecoderStateMachineScheduler.cpp b/dom/media/MediaDecoderStateMachineScheduler.cpp deleted file mode 100644 index 84efafef9760..000000000000 --- a/dom/media/MediaDecoderStateMachineScheduler.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "MediaDecoderStateMachineScheduler.h" -#include "SharedThreadPool.h" -#include "mozilla/Preferences.h" -#include "mozilla/ReentrantMonitor.h" -#include "nsITimer.h" -#include "nsComponentManagerUtils.h" -#include "VideoUtils.h" - -namespace { -class TimerEvent : public nsITimerCallback, public nsRunnable { - typedef mozilla::MediaDecoderStateMachineScheduler Scheduler; - NS_DECL_ISUPPORTS_INHERITED -public: - TimerEvent(Scheduler* aScheduler, int aTimerId) - : mScheduler(aScheduler), mTimerId(aTimerId) {} - - NS_IMETHOD Run() MOZ_OVERRIDE { - return mScheduler->TimeoutExpired(mTimerId); - } - - NS_IMETHOD Notify(nsITimer* aTimer) MOZ_OVERRIDE { - return mScheduler->TimeoutExpired(mTimerId); - } -private: - ~TimerEvent() {} - Scheduler* const mScheduler; - const int mTimerId; -}; - -NS_IMPL_ISUPPORTS_INHERITED(TimerEvent, nsRunnable, nsITimerCallback); -} // anonymous namespace - -static already_AddRefed -CreateStateMachineThread() -{ - using mozilla::SharedThreadPool; - using mozilla::RefPtr; - RefPtr threadPool( - SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1)); - nsCOMPtr rv = threadPool.get(); - return rv.forget(); -} - -namespace mozilla { - -MediaDecoderStateMachineScheduler::MediaDecoderStateMachineScheduler( - ReentrantMonitor& aMonitor, - nsresult (*aTimeoutCallback)(void*), - void* aClosure, bool aRealTime) - : mTimeoutCallback(aTimeoutCallback) - , mClosure(aClosure) - // Only enable realtime mode when "media.realtime_decoder.enabled" is true. - , mRealTime(aRealTime && - Preferences::GetBool("media.realtime_decoder.enabled", false)) - , mMonitor(aMonitor) - , mEventTarget(CreateStateMachineThread()) - , mTimer(do_CreateInstance("@mozilla.org/timer;1")) - , mTimerId(0) - , mState(SCHEDULER_STATE_NONE) - , mInRunningStateMachine(false) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_COUNT_CTOR(MediaDecoderStateMachineScheduler); -} - -MediaDecoderStateMachineScheduler::~MediaDecoderStateMachineScheduler() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_COUNT_DTOR(MediaDecoderStateMachineScheduler); -} - -nsresult -MediaDecoderStateMachineScheduler::Init() -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mEventTarget, NS_ERROR_FAILURE); - nsresult rv = mTimer->SetTarget(mEventTarget); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -nsresult -MediaDecoderStateMachineScheduler::Schedule(int64_t aUsecs) -{ - mMonitor.AssertCurrentThreadIn(); - - if (NS_WARN_IF(mState == SCHEDULER_STATE_SHUTDOWN)) { - return NS_ERROR_FAILURE; - } - - aUsecs = std::max(aUsecs, 0); - - TimeStamp timeout = TimeStamp::Now() + - TimeDuration::FromMilliseconds(static_cast(aUsecs) / USECS_PER_MS); - - if (!mTimeout.IsNull() && timeout >= mTimeout) { - // We've already scheduled a timer set to expire at or before this time, - // or have an event dispatched to run the state machine. - return NS_OK; - } - - uint32_t ms = static_cast((aUsecs / USECS_PER_MS) & 0xFFFFFFFF); - if (IsRealTime() && ms > 40) { - ms = 40; - } - - // Don't cancel the timer here for this function will be called from - // different threads. - - nsresult rv = NS_ERROR_FAILURE; - nsRefPtr event = new TimerEvent(this, mTimerId+1); - - if (ms == 0) { - // Dispatch a runnable to the state machine thread when delay is 0. - // It will has less latency than dispatching a runnable to the state - // machine thread which will then schedule a zero-delay timer. - rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL); - } else if (OnStateMachineThread()) { - rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT); - } else { - MOZ_ASSERT(false, "non-zero delay timer should be only " - "scheduled in state machine thread"); - } - - if (NS_SUCCEEDED(rv)) { - mTimeout = timeout; - ++mTimerId; - } else { - NS_WARNING("Failed to schedule state machine"); - } - - return rv; -} - -nsresult -MediaDecoderStateMachineScheduler::TimeoutExpired(int aTimerId) -{ - ReentrantMonitorAutoEnter mon(mMonitor); - MOZ_ASSERT(OnStateMachineThread()); - MOZ_ASSERT(!mInRunningStateMachine, - "State machine cycles must run in sequence!"); - - mInRunningStateMachine = true; - // Only run state machine cycles when id matches. - nsresult rv = NS_OK; - if (mTimerId == aTimerId) { - ResetTimer(); - rv = mTimeoutCallback(mClosure); - } - mInRunningStateMachine = false; - - return rv; -} - -void -MediaDecoderStateMachineScheduler::ScheduleAndShutdown() -{ - mMonitor.AssertCurrentThreadIn(); - // Schedule next cycle to handle SHUTDOWN in state machine thread. - Schedule(); - // This must be set after calling Schedule() - // which does nothing in shutdown state. - mState = SCHEDULER_STATE_SHUTDOWN; -} - -bool -MediaDecoderStateMachineScheduler::OnStateMachineThread() const -{ - bool rv = false; - mEventTarget->IsOnCurrentThread(&rv); - return rv; -} - -bool -MediaDecoderStateMachineScheduler::IsScheduled() const -{ - mMonitor.AssertCurrentThreadIn(); - return !mTimeout.IsNull(); -} - -void -MediaDecoderStateMachineScheduler::ResetTimer() -{ - mMonitor.AssertCurrentThreadIn(); - mTimer->Cancel(); - mTimeout = TimeStamp(); -} - -} // namespace mozilla diff --git a/dom/media/MediaDecoderStateMachineScheduler.h b/dom/media/MediaDecoderStateMachineScheduler.h deleted file mode 100644 index aced98b6c96f..000000000000 --- a/dom/media/MediaDecoderStateMachineScheduler.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 MediaDecoderStateMachineScheduler_h__ -#define MediaDecoderStateMachineScheduler_h__ - -#include "nsCOMPtr.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/DebugOnly.h" - -class nsITimer; -class nsIEventTarget; - -namespace mozilla { - -class ReentrantMonitor; - -class MediaDecoderStateMachineScheduler { - enum State { - SCHEDULER_STATE_NONE, - SCHEDULER_STATE_SHUTDOWN - }; -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachineScheduler) - MediaDecoderStateMachineScheduler(ReentrantMonitor& aMonitor, - nsresult (*aTimeoutCallback)(void*), - void* aClosure, bool aRealTime); - nsresult Init(); - nsresult Schedule(int64_t aUsecs = 0); - void ScheduleAndShutdown(); - nsresult TimeoutExpired(int aTimerId); - - bool OnStateMachineThread() const; - bool IsScheduled() const; - - bool IsRealTime() const { - return mRealTime; - } - - nsIEventTarget* GetStateMachineThread() const { - return mEventTarget; - } - -private: - ~MediaDecoderStateMachineScheduler(); - void ResetTimer(); - - // Callback function provided by MediaDecoderStateMachine to run - // state machine cycles. - nsresult (*const mTimeoutCallback)(void*); - // Since StateMachineScheduler will never outlive the state machine, - // it is safe to keep a raw pointer only to avoid reference cycles. - void* const mClosure; - // True is we are decoding a realtime stream, like a camera stream - const bool mRealTime; - // Monitor of the decoder - ReentrantMonitor& mMonitor; - // State machine thread - const nsCOMPtr mEventTarget; - // Timer to schedule callbacks to run the state machine cycles. - nsCOMPtr mTimer; - // Timestamp at which the next state machine cycle will run. - TimeStamp mTimeout; - // The id of timer tasks, timer callback will only run if id matches. - int mTimerId; - // No more state machine cycles in shutdown state. - State mState; - - // Used to check if state machine cycles are running in sequence. - DebugOnly mInRunningStateMachine; -}; - -} // namespace mozilla - -#endif // MediaDecoderStateMachineScheduler_h__ diff --git a/dom/media/MediaPromise.cpp b/dom/media/MediaPromise.cpp index 30b5575bf2c2..42b7ce34aaeb 100644 --- a/dom/media/MediaPromise.cpp +++ b/dom/media/MediaPromise.cpp @@ -6,7 +6,6 @@ #include "MediaPromise.h" -#include "MediaDecoderStateMachineScheduler.h" #include "MediaTaskQueue.h" #include "nsThreadUtils.h" @@ -25,12 +24,6 @@ DispatchMediaPromiseRunnable(nsIEventTarget* aEventTarget, nsIRunnable* aRunnabl return aEventTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL); } -nsresult -DispatchMediaPromiseRunnable(MediaDecoderStateMachineScheduler* aScheduler, nsIRunnable* aRunnable) -{ - return aScheduler->GetStateMachineThread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL); -} - void AssertOnThread(MediaTaskQueue* aQueue) { @@ -44,11 +37,5 @@ void AssertOnThread(nsIEventTarget* aTarget) MOZ_ASSERT(NS_GetCurrentThread() == targetThread); } -void -AssertOnThread(MediaDecoderStateMachineScheduler* aScheduler) -{ - MOZ_ASSERT(aScheduler->OnStateMachineThread()); -} - } } // namespace mozilla diff --git a/dom/media/MediaPromise.h b/dom/media/MediaPromise.h index a7315905d4cf..2c3d8984d843 100644 --- a/dom/media/MediaPromise.h +++ b/dom/media/MediaPromise.h @@ -33,17 +33,14 @@ extern PRLogModuleInfo* gMediaPromiseLog; PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__)) class MediaTaskQueue; -class MediaDecoderStateMachineScheduler; namespace detail { nsresult DispatchMediaPromiseRunnable(MediaTaskQueue* aQueue, nsIRunnable* aRunnable); nsresult DispatchMediaPromiseRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable); -nsresult DispatchMediaPromiseRunnable(MediaDecoderStateMachineScheduler* aScheduler, nsIRunnable* aRunnable); #ifdef DEBUG void AssertOnThread(MediaTaskQueue* aQueue); void AssertOnThread(nsIEventTarget* aTarget); -void AssertOnThread(MediaDecoderStateMachineScheduler* aScheduler); #endif } // namespace detail diff --git a/dom/media/MediaQueue.h b/dom/media/MediaQueue.h index d49486d60e27..c4aa178eadc4 100644 --- a/dom/media/MediaQueue.h +++ b/dom/media/MediaQueue.h @@ -157,7 +157,7 @@ template class MediaQueue : private nsDeque { mPopListeners.Clear(); } - void AddPopListener(nsIRunnable* aRunnable, nsIEventTarget* aTarget) { + void AddPopListener(nsIRunnable* aRunnable, MediaTaskQueue* aTarget) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); mPopListeners.AppendElement(Listener(aRunnable, aTarget)); } @@ -166,7 +166,7 @@ private: mutable ReentrantMonitor mReentrantMonitor; struct Listener { - Listener(nsIRunnable* aRunnable, nsIEventTarget* aTarget) + Listener(nsIRunnable* aRunnable, MediaTaskQueue* aTarget) : mRunnable(aRunnable) , mTarget(aTarget) { @@ -177,7 +177,7 @@ private: { } RefPtr mRunnable; - RefPtr mTarget; + RefPtr mTarget; }; nsTArray mPopListeners; @@ -185,7 +185,7 @@ private: void NotifyPopListeners() { for (uint32_t i = 0; i < mPopListeners.Length(); i++) { Listener& l = mPopListeners[i]; - l.mTarget->Dispatch(l.mRunnable, NS_DISPATCH_NORMAL); + l.mTarget->Dispatch(l.mRunnable); } } diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index 3574669be5ff..32cdabcd5d79 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -278,7 +278,7 @@ MediaStreamGraphImpl::UpdateBufferSufficiencyState(SourceMediaStream* aStream) } for (uint32_t i = 0; i < runnables.Length(); ++i) { - runnables[i].mTarget->Dispatch(runnables[i].mRunnable, 0); + runnables[i].mTarget->Dispatch(runnables[i].mRunnable); } } @@ -2476,21 +2476,21 @@ SourceMediaStream::GetEndOfAppendedData(TrackID aID) void SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID, - nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable) + MediaTaskQueue* aSignalQueue, nsIRunnable* aSignalRunnable) { MutexAutoLock lock(mMutex); TrackData* data = FindDataForTrack(aID); if (!data) { - aSignalThread->Dispatch(aSignalRunnable, 0); + aSignalQueue->Dispatch(aSignalRunnable); return; } if (data->mHaveEnough) { if (data->mDispatchWhenNotEnough.IsEmpty()) { - data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalThread, aSignalRunnable); + data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalQueue, aSignalRunnable); } } else { - aSignalThread->Dispatch(aSignalRunnable, 0); + aSignalQueue->Dispatch(aSignalRunnable); } } diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index d64c92847e4e..881a21e4563d 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -16,6 +16,7 @@ #include "VideoFrameContainer.h" #include "VideoSegment.h" #include "MainThreadUtils.h" +#include "MediaTaskQueue.h" #include "nsAutoRef.h" #include "GraphDriver.h" #include @@ -783,7 +784,7 @@ public: * does not exist. No op if a runnable is already present for this track. */ void DispatchWhenNotEnoughBuffered(TrackID aID, - nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable); + MediaTaskQueue* aSignalQueue, nsIRunnable* aSignalRunnable); /** * Indicate that a track has ended. Do not do any more API calls * affecting this track. @@ -847,14 +848,14 @@ public: protected: struct ThreadAndRunnable { - void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable) + void Init(MediaTaskQueue* aTarget, nsIRunnable* aRunnable) { mTarget = aTarget; mRunnable = aRunnable; } - nsCOMPtr mTarget; - nsCOMPtr mRunnable; + nsRefPtr mTarget; + RefPtr mRunnable; }; enum TrackCommands { TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED, diff --git a/dom/media/moz.build b/dom/media/moz.build index a686d44c4925..eb19e704230a 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -102,7 +102,6 @@ EXPORTS += [ 'MediaDecoderOwner.h', 'MediaDecoderReader.h', 'MediaDecoderStateMachine.h', - 'MediaDecoderStateMachineScheduler.h', 'MediaInfo.h', 'MediaMetadataManager.h', 'MediaPromise.h', @@ -112,6 +111,7 @@ EXPORTS += [ 'MediaSegment.h', 'MediaStreamGraph.h', 'MediaTaskQueue.h', + 'MediaTimer.h', 'MediaTrack.h', 'MediaTrackList.h', 'MP3FrameParser.h', @@ -181,7 +181,6 @@ UNIFIED_SOURCES += [ 'MediaDecoder.cpp', 'MediaDecoderReader.cpp', 'MediaDecoderStateMachine.cpp', - 'MediaDecoderStateMachineScheduler.cpp', 'MediaDevices.cpp', 'MediaManager.cpp', 'MediaPromise.cpp',