diff --git a/content/media/MediaDecoderStateMachine.cpp b/content/media/MediaDecoderStateMachine.cpp index ad741664903..c5bcd7a9fb2 100644 --- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -13,6 +13,7 @@ #include #include "MediaDecoderStateMachine.h" +#include "MediaDecoderStateMachineScheduler.h" #include "AudioSink.h" #include "nsTArray.h" #include "MediaDecoder.h" @@ -168,8 +169,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaDecoderReader* aReader, bool aRealTime) : mDecoder(aDecoder), + mScheduler(new MediaDecoderStateMachineScheduler( + aDecoder->GetReentrantMonitor(), + &MediaDecoderStateMachine::TimeoutExpired, + MOZ_THIS_IN_INITIALIZER_LIST(), aRealTime)), mState(DECODER_STATE_DECODING_METADATA), - mInRunningStateMachine(false), mSyncPointInMediaStream(-1), mSyncPointInDecodedStream(-1), mPlayDuration(0), @@ -198,30 +202,24 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mQuickBuffering(false), mMinimizePreroll(false), mDecodeThreadWaiting(false), - mRealTime(aRealTime), mDispatchedDecodeMetadataTask(false), mDropAudioUntilNextDiscontinuity(false), mDropVideoUntilNextDiscontinuity(false), mDecodeToSeekTarget(false), mCurrentTimeBeforeSeek(0), - mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED), - mTimerId(0) + mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED) { MOZ_COUNT_CTOR(MediaDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - // Only enable realtime mode when "media.realtime_decoder.enabled" is true. - if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false) - mRealTime = false; - mAmpleVideoFrames = std::max(Preferences::GetUint("media.video-queue.default-size", 10), 3); - mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S; - mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS; + mBufferingWait = mScheduler->IsRealTime() ? 0 : BUFFERING_WAIT_S; + mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS; - mVideoPrerollFrames = mRealTime ? 0 : mAmpleVideoFrames / 2; - mAudioPrerollUsecs = mRealTime ? 0 : LOW_AUDIO_USECS * 2; + mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2; + mAudioPrerollUsecs = mScheduler->IsRealTime() ? 0 : LOW_AUDIO_USECS * 2; #ifdef XP_WIN // Ensure high precision timers are enabled on Windows, otherwise the state @@ -241,8 +239,6 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine() "WakeDecoder should have been revoked already"); MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN"); - // No need to cancel the timer here for we've done that in SHUTDOWN. - MOZ_ASSERT(!mTimer, "Should be released in SHUTDOWN"); mReader = nullptr; #ifdef XP_WIN @@ -1063,10 +1059,6 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) RefPtr decodePool(GetMediaDecodeThreadPool()); NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE); - RefPtr stateMachinePool( - SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1)); - NS_ENSURE_TRUE(stateMachinePool, NS_ERROR_FAILURE); - mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget()); NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE); @@ -1075,12 +1067,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) cloneReader = aCloneDonor->mReader; } - mStateMachineThreadPool = stateMachinePool; - - nsresult rv; - mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = mTimer->SetTarget(GetStateMachineThread()); + nsresult rv = mScheduler->Init(); NS_ENSURE_SUCCESS(rv, rv); // Note: This creates a cycle, broken in shutdown. @@ -1342,8 +1329,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(PR_LOG_DEBUG, "Changed state to SHUTDOWN"); - ScheduleStateMachine(); mState = DECODER_STATE_SHUTDOWN; + mScheduler->ScheduleAndShutdown(); if (mAudioSink) { mAudioSink->PrepareToShutdown(); } @@ -1753,6 +1740,7 @@ MediaDecoderStateMachine::StartAudioThread() if (NS_FAILED(rv)) { DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed"); mState = DECODER_STATE_SHUTDOWN; + mScheduler->ScheduleAndShutdown(); return rv; } @@ -1830,8 +1818,8 @@ MediaDecoderStateMachine::DecodeError() // and the HTMLMediaElement, so that our pipeline can start exiting // cleanly during the sync dispatch below. DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error"); - ScheduleStateMachine(); mState = DECODER_STATE_SHUTDOWN; + mScheduler->ScheduleAndShutdown(); mDecoder->GetReentrantMonitor().NotifyAll(); // Dispatch the event to call DecodeError synchronously. This ensures @@ -1911,7 +1899,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata() VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue); } - if (mRealTime) { + if (mScheduler->IsRealTime()) { SetStartTime(0); res = FinishDecodeMetadata(); NS_ENSURE_SUCCESS(res, res); @@ -1940,7 +1928,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata() return NS_ERROR_FAILURE; } - if (!mRealTime) { + if (!mScheduler->IsRealTime()) { const VideoData* v = VideoQueue().PeekFront(); const AudioData* a = AudioQueue().PeekFront(); @@ -2292,8 +2280,6 @@ nsresult MediaDecoderStateMachine::RunStateMachine() GetStateMachineThread()->Dispatch( new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL); - mTimer->Cancel(); - mTimer = nullptr; return NS_OK; } @@ -2589,7 +2575,7 @@ void MediaDecoderStateMachine::AdvanceFrame() #endif if (VideoQueue().GetSize() > 0) { VideoData* frame = VideoQueue().PeekFront(); - while (mRealTime || clock_time >= frame->mTime) { + while (mScheduler->IsRealTime() || clock_time >= frame->mTime) { mVideoFrameEndTime = frame->GetEndTime(); currentFrame = frame; #ifdef PR_LOGGING @@ -2929,24 +2915,13 @@ nsresult MediaDecoderStateMachine::CallRunStateMachine() StopAudioThread(); } - MOZ_ASSERT(!mInRunningStateMachine, "State machine cycles must run in sequence!"); - mTimeout = TimeStamp(); - mInRunningStateMachine = true; - nsresult res = RunStateMachine(); - mInRunningStateMachine = false; - return res; + return RunStateMachine(); } -nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId) +nsresult MediaDecoderStateMachine::TimeoutExpired(void* aClosure) { - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread"); - mTimer->Cancel(); - if (mTimerId == aTimerId) { - return CallRunStateMachine(); - } else { - return NS_OK; - } + MediaDecoderStateMachine* p = static_cast(aClosure); + return p->CallRunStateMachine(); } void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { @@ -2955,75 +2930,8 @@ void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { DispatchVideoDecodeTaskIfNeeded(); } -class TimerEvent : public nsITimerCallback, public nsRunnable { - NS_DECL_THREADSAFE_ISUPPORTS -public: - TimerEvent(MediaDecoderStateMachine* aStateMachine, int aTimerId) - : mStateMachine(aStateMachine), mTimerId(aTimerId) {} - - NS_IMETHOD Run() MOZ_OVERRIDE { - return mStateMachine->TimeoutExpired(mTimerId); - } - - NS_IMETHOD Notify(nsITimer* aTimer) { - return mStateMachine->TimeoutExpired(mTimerId); - } -private: - ~TimerEvent() {} - - const nsRefPtr mStateMachine; - int mTimerId; -}; - -NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable); - nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) { - AssertCurrentThreadInMonitor(); - NS_ABORT_IF_FALSE(GetStateMachineThread(), - "Must have a state machine thread to schedule"); - - if (mState == DECODER_STATE_SHUTDOWN) { - return NS_ERROR_FAILURE; - } - aUsecs = std::max(aUsecs, 0); - - TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs); - 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 (mRealTime && 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 = GetStateMachineThread()->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; + return mScheduler->Schedule(aUsecs); } bool MediaDecoderStateMachine::OnDecodeThread() const @@ -3033,14 +2941,17 @@ bool MediaDecoderStateMachine::OnDecodeThread() const bool MediaDecoderStateMachine::OnStateMachineThread() const { - bool rv = false; - mStateMachineThreadPool->IsOnCurrentThread(&rv); - return rv; + return mScheduler->OnStateMachineThread(); } -nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() +nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() const { - return mStateMachineThreadPool->GetEventTarget(); + return mScheduler->GetStateMachineThread(); +} + +bool MediaDecoderStateMachine::IsStateMachineScheduled() const +{ + return mScheduler->IsScheduled(); } void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate) diff --git a/content/media/MediaDecoderStateMachine.h b/content/media/MediaDecoderStateMachine.h index e4861096eec..78b0597a604 100644 --- a/content/media/MediaDecoderStateMachine.h +++ b/content/media/MediaDecoderStateMachine.h @@ -100,6 +100,7 @@ class VideoSegment; class MediaTaskQueue; class SharedThreadPool; class AudioSink; +class MediaDecoderStateMachineScheduler; // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime @@ -279,7 +280,7 @@ public: } // Returns the shared state machine thread. - nsIEventTarget* GetStateMachineThread(); + nsIEventTarget* GetStateMachineThread() const; // Calls ScheduleStateMachine() after taking the decoder lock. Also // notifies the decoder thread in case it's waiting on the decoder lock. @@ -290,8 +291,9 @@ public: // earlier, in which case the request is discarded. nsresult ScheduleStateMachine(int64_t aUsecs = 0); - // Timer function to implement ScheduleStateMachine(aUsecs). - nsresult TimeoutExpired(int aGeneration); + // Callback function registered with MediaDecoderStateMachineScheduler + // to run state machine cycles. + static nsresult TimeoutExpired(void* aClosure); // Set the media fragment end time. aEndTime is in microseconds. void SetFragmentEndTime(int64_t aEndTime); @@ -447,7 +449,7 @@ protected: // Orders the Reader to stop decoding, and blocks until the Reader // has stopped decoding and finished delivering samples, then calls - // ResetPlayback() to discard all enqueued data. + // ResetPlayback() to discard all enqueued data. void FlushDecoding(); // Returns the audio clock, if we have audio, or -1 if we don't. @@ -610,10 +612,7 @@ protected: // periodically via timer to ensure the video stays in sync. nsresult RunStateMachine(); - bool IsStateMachineScheduled() const { - AssertCurrentThreadInMonitor(); - return !mTimeout.IsNull(); - } + bool IsStateMachineScheduled() const; // Returns true if we're not playing and the decode thread has filled its // decode buffers and is waiting. We can shut the decode thread down in this @@ -650,6 +649,10 @@ 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 nsAutoPtr mScheduler; + // 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. // This is necessary for legacy synchronous decoders to prevent underruns. @@ -674,19 +677,6 @@ protected: // thread every time they're called. RefPtr mDecodeTaskQueue; - RefPtr mStateMachineThreadPool; - - // Timer to run the state machine cycles. Used by - // ScheduleStateMachine(). Access protected by decoder monitor. - nsCOMPtr mTimer; - - // Timestamp at which the next state machine cycle will run. - // Access protected by decoder monitor. - TimeStamp mTimeout; - - // Used to check if there are state machine cycles are running in sequence. - DebugOnly mInRunningStateMachine; - // The time that playback started from the system clock. This is used for // timing the presentation of video frames when there's no audio. // Accessed only via the state machine thread. Must be set via SetPlayStartTime. @@ -911,9 +901,6 @@ protected: // by the decoder monitor. bool mDecodeThreadWaiting; - // True is we are decoding a realtime stream, like a camera stream - bool mRealTime; - // True if we've dispatched a task to the decode task queue to call // ReadMetadata on the reader. We maintain a flag to ensure that we don't // dispatch multiple tasks to re-do the metadata loading. @@ -942,9 +929,6 @@ protected: mozilla::MediaMetadataManager mMetadataManager; MediaDecoderOwner::NextFrameStatus mLastFrameStatus; - - // The id of timer tasks, used to ignore tasks that are scheduled previously. - int mTimerId; }; } // namespace mozilla; diff --git a/content/media/MediaDecoderStateMachineScheduler.cpp b/content/media/MediaDecoderStateMachineScheduler.cpp new file mode 100644 index 00000000000..c5b49d67a6f --- /dev/null +++ b/content/media/MediaDecoderStateMachineScheduler.cpp @@ -0,0 +1,194 @@ +/* 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_THREADSAFE_ISUPPORTS +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(TimerEvent, nsITimerCallback, nsIRunnable); +} // 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 (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/content/media/MediaDecoderStateMachineScheduler.h b/content/media/MediaDecoderStateMachineScheduler.h new file mode 100644 index 00000000000..f79cd15066e --- /dev/null +++ b/content/media/MediaDecoderStateMachineScheduler.h @@ -0,0 +1,77 @@ +/* -*- 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: + MediaDecoderStateMachineScheduler(ReentrantMonitor& aMonitor, + nsresult (*aTimeoutCallback)(void*), + void* aClosure, bool aRealTime); + ~MediaDecoderStateMachineScheduler(); + 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: + 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/content/media/moz.build b/content/media/moz.build index a688e41c7ef..272d5b79160 100644 --- a/content/media/moz.build +++ b/content/media/moz.build @@ -144,6 +144,7 @@ UNIFIED_SOURCES += [ 'MediaDecoder.cpp', 'MediaDecoderReader.cpp', 'MediaDecoderStateMachine.cpp', + 'MediaDecoderStateMachineScheduler.cpp', 'MediaRecorder.cpp', 'MediaResource.cpp', 'MediaShutdownManager.cpp',