mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 996465 - Extract code running state machine cycles into a class. r=cpearce
This commit is contained in:
parent
e854923edc
commit
80b90738fb
@ -13,6 +13,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "MediaDecoderStateMachine.h"
|
#include "MediaDecoderStateMachine.h"
|
||||||
|
#include "MediaDecoderStateMachineScheduler.h"
|
||||||
#include "AudioSink.h"
|
#include "AudioSink.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "MediaDecoder.h"
|
#include "MediaDecoder.h"
|
||||||
@ -168,8 +169,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||||||
MediaDecoderReader* aReader,
|
MediaDecoderReader* aReader,
|
||||||
bool aRealTime) :
|
bool aRealTime) :
|
||||||
mDecoder(aDecoder),
|
mDecoder(aDecoder),
|
||||||
|
mScheduler(new MediaDecoderStateMachineScheduler(
|
||||||
|
aDecoder->GetReentrantMonitor(),
|
||||||
|
&MediaDecoderStateMachine::TimeoutExpired,
|
||||||
|
MOZ_THIS_IN_INITIALIZER_LIST(), aRealTime)),
|
||||||
mState(DECODER_STATE_DECODING_METADATA),
|
mState(DECODER_STATE_DECODING_METADATA),
|
||||||
mInRunningStateMachine(false),
|
|
||||||
mSyncPointInMediaStream(-1),
|
mSyncPointInMediaStream(-1),
|
||||||
mSyncPointInDecodedStream(-1),
|
mSyncPointInDecodedStream(-1),
|
||||||
mPlayDuration(0),
|
mPlayDuration(0),
|
||||||
@ -198,30 +202,24 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||||||
mQuickBuffering(false),
|
mQuickBuffering(false),
|
||||||
mMinimizePreroll(false),
|
mMinimizePreroll(false),
|
||||||
mDecodeThreadWaiting(false),
|
mDecodeThreadWaiting(false),
|
||||||
mRealTime(aRealTime),
|
|
||||||
mDispatchedDecodeMetadataTask(false),
|
mDispatchedDecodeMetadataTask(false),
|
||||||
mDropAudioUntilNextDiscontinuity(false),
|
mDropAudioUntilNextDiscontinuity(false),
|
||||||
mDropVideoUntilNextDiscontinuity(false),
|
mDropVideoUntilNextDiscontinuity(false),
|
||||||
mDecodeToSeekTarget(false),
|
mDecodeToSeekTarget(false),
|
||||||
mCurrentTimeBeforeSeek(0),
|
mCurrentTimeBeforeSeek(0),
|
||||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
|
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
|
||||||
mTimerId(0)
|
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
|
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
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 =
|
mAmpleVideoFrames =
|
||||||
std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
|
std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
|
||||||
|
|
||||||
mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S;
|
mBufferingWait = mScheduler->IsRealTime() ? 0 : BUFFERING_WAIT_S;
|
||||||
mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS;
|
mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS;
|
||||||
|
|
||||||
mVideoPrerollFrames = mRealTime ? 0 : mAmpleVideoFrames / 2;
|
mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2;
|
||||||
mAudioPrerollUsecs = mRealTime ? 0 : LOW_AUDIO_USECS * 2;
|
mAudioPrerollUsecs = mScheduler->IsRealTime() ? 0 : LOW_AUDIO_USECS * 2;
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
// Ensure high precision timers are enabled on Windows, otherwise the state
|
// Ensure high precision timers are enabled on Windows, otherwise the state
|
||||||
@ -241,8 +239,6 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
|
|||||||
"WakeDecoder should have been revoked already");
|
"WakeDecoder should have been revoked already");
|
||||||
|
|
||||||
MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN");
|
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;
|
mReader = nullptr;
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
@ -1063,10 +1059,6 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
|
|||||||
RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool());
|
RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool());
|
||||||
NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
RefPtr<SharedThreadPool> stateMachinePool(
|
|
||||||
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
|
|
||||||
NS_ENSURE_TRUE(stateMachinePool, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget());
|
mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget());
|
||||||
NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
@ -1075,12 +1067,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
|
|||||||
cloneReader = aCloneDonor->mReader;
|
cloneReader = aCloneDonor->mReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
mStateMachineThreadPool = stateMachinePool;
|
nsresult rv = mScheduler->Init();
|
||||||
|
|
||||||
nsresult rv;
|
|
||||||
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
rv = mTimer->SetTarget(GetStateMachineThread());
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Note: This creates a cycle, broken in shutdown.
|
// 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
|
// Change state before issuing shutdown request to threads so those
|
||||||
// threads can start exiting cleanly during the Shutdown call.
|
// threads can start exiting cleanly during the Shutdown call.
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN");
|
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN");
|
||||||
ScheduleStateMachine();
|
|
||||||
mState = DECODER_STATE_SHUTDOWN;
|
mState = DECODER_STATE_SHUTDOWN;
|
||||||
|
mScheduler->ScheduleAndShutdown();
|
||||||
if (mAudioSink) {
|
if (mAudioSink) {
|
||||||
mAudioSink->PrepareToShutdown();
|
mAudioSink->PrepareToShutdown();
|
||||||
}
|
}
|
||||||
@ -1753,6 +1740,7 @@ MediaDecoderStateMachine::StartAudioThread()
|
|||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed");
|
DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed");
|
||||||
mState = DECODER_STATE_SHUTDOWN;
|
mState = DECODER_STATE_SHUTDOWN;
|
||||||
|
mScheduler->ScheduleAndShutdown();
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1830,8 +1818,8 @@ MediaDecoderStateMachine::DecodeError()
|
|||||||
// and the HTMLMediaElement, so that our pipeline can start exiting
|
// and the HTMLMediaElement, so that our pipeline can start exiting
|
||||||
// cleanly during the sync dispatch below.
|
// cleanly during the sync dispatch below.
|
||||||
DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error");
|
DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error");
|
||||||
ScheduleStateMachine();
|
|
||||||
mState = DECODER_STATE_SHUTDOWN;
|
mState = DECODER_STATE_SHUTDOWN;
|
||||||
|
mScheduler->ScheduleAndShutdown();
|
||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
|
|
||||||
// Dispatch the event to call DecodeError synchronously. This ensures
|
// Dispatch the event to call DecodeError synchronously. This ensures
|
||||||
@ -1911,7 +1899,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||||||
VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
|
VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mRealTime) {
|
if (mScheduler->IsRealTime()) {
|
||||||
SetStartTime(0);
|
SetStartTime(0);
|
||||||
res = FinishDecodeMetadata();
|
res = FinishDecodeMetadata();
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
@ -1940,7 +1928,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mRealTime) {
|
if (!mScheduler->IsRealTime()) {
|
||||||
|
|
||||||
const VideoData* v = VideoQueue().PeekFront();
|
const VideoData* v = VideoQueue().PeekFront();
|
||||||
const AudioData* a = AudioQueue().PeekFront();
|
const AudioData* a = AudioQueue().PeekFront();
|
||||||
@ -2292,8 +2280,6 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||||||
GetStateMachineThread()->Dispatch(
|
GetStateMachineThread()->Dispatch(
|
||||||
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
|
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
|
||||||
|
|
||||||
mTimer->Cancel();
|
|
||||||
mTimer = nullptr;
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2589,7 +2575,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||||||
#endif
|
#endif
|
||||||
if (VideoQueue().GetSize() > 0) {
|
if (VideoQueue().GetSize() > 0) {
|
||||||
VideoData* frame = VideoQueue().PeekFront();
|
VideoData* frame = VideoQueue().PeekFront();
|
||||||
while (mRealTime || clock_time >= frame->mTime) {
|
while (mScheduler->IsRealTime() || clock_time >= frame->mTime) {
|
||||||
mVideoFrameEndTime = frame->GetEndTime();
|
mVideoFrameEndTime = frame->GetEndTime();
|
||||||
currentFrame = frame;
|
currentFrame = frame;
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
@ -2929,24 +2915,13 @@ nsresult MediaDecoderStateMachine::CallRunStateMachine()
|
|||||||
StopAudioThread();
|
StopAudioThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(!mInRunningStateMachine, "State machine cycles must run in sequence!");
|
return RunStateMachine();
|
||||||
mTimeout = TimeStamp();
|
|
||||||
mInRunningStateMachine = true;
|
|
||||||
nsresult res = RunStateMachine();
|
|
||||||
mInRunningStateMachine = false;
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId)
|
nsresult MediaDecoderStateMachine::TimeoutExpired(void* aClosure)
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
MediaDecoderStateMachine* p = static_cast<MediaDecoderStateMachine*>(aClosure);
|
||||||
NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread");
|
return p->CallRunStateMachine();
|
||||||
mTimer->Cancel();
|
|
||||||
if (mTimerId == aTimerId) {
|
|
||||||
return CallRunStateMachine();
|
|
||||||
} else {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
||||||
@ -2955,75 +2930,8 @@ void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
|||||||
DispatchVideoDecodeTaskIfNeeded();
|
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<MediaDecoderStateMachine> mStateMachine;
|
|
||||||
int mTimerId;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable);
|
|
||||||
|
|
||||||
nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
|
nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
|
||||||
AssertCurrentThreadInMonitor();
|
return mScheduler->Schedule(aUsecs);
|
||||||
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<int64_t>(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<uint32_t>((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<TimerEvent> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaDecoderStateMachine::OnDecodeThread() const
|
bool MediaDecoderStateMachine::OnDecodeThread() const
|
||||||
@ -3033,14 +2941,17 @@ bool MediaDecoderStateMachine::OnDecodeThread() const
|
|||||||
|
|
||||||
bool MediaDecoderStateMachine::OnStateMachineThread() const
|
bool MediaDecoderStateMachine::OnStateMachineThread() const
|
||||||
{
|
{
|
||||||
bool rv = false;
|
return mScheduler->OnStateMachineThread();
|
||||||
mStateMachineThreadPool->IsOnCurrentThread(&rv);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
|
||||||
|
@ -100,6 +100,7 @@ class VideoSegment;
|
|||||||
class MediaTaskQueue;
|
class MediaTaskQueue;
|
||||||
class SharedThreadPool;
|
class SharedThreadPool;
|
||||||
class AudioSink;
|
class AudioSink;
|
||||||
|
class MediaDecoderStateMachineScheduler;
|
||||||
|
|
||||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||||
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
|
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
|
||||||
@ -279,7 +280,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the shared state machine thread.
|
// Returns the shared state machine thread.
|
||||||
nsIEventTarget* GetStateMachineThread();
|
nsIEventTarget* GetStateMachineThread() const;
|
||||||
|
|
||||||
// Calls ScheduleStateMachine() after taking the decoder lock. Also
|
// Calls ScheduleStateMachine() after taking the decoder lock. Also
|
||||||
// notifies the decoder thread in case it's waiting on the decoder lock.
|
// 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.
|
// earlier, in which case the request is discarded.
|
||||||
nsresult ScheduleStateMachine(int64_t aUsecs = 0);
|
nsresult ScheduleStateMachine(int64_t aUsecs = 0);
|
||||||
|
|
||||||
// Timer function to implement ScheduleStateMachine(aUsecs).
|
// Callback function registered with MediaDecoderStateMachineScheduler
|
||||||
nsresult TimeoutExpired(int aGeneration);
|
// to run state machine cycles.
|
||||||
|
static nsresult TimeoutExpired(void* aClosure);
|
||||||
|
|
||||||
// Set the media fragment end time. aEndTime is in microseconds.
|
// Set the media fragment end time. aEndTime is in microseconds.
|
||||||
void SetFragmentEndTime(int64_t aEndTime);
|
void SetFragmentEndTime(int64_t aEndTime);
|
||||||
@ -447,7 +449,7 @@ protected:
|
|||||||
|
|
||||||
// Orders the Reader to stop decoding, and blocks until the Reader
|
// Orders the Reader to stop decoding, and blocks until the Reader
|
||||||
// has stopped decoding and finished delivering samples, then calls
|
// has stopped decoding and finished delivering samples, then calls
|
||||||
// ResetPlayback() to discard all enqueued data.
|
// ResetPlayback() to discard all enqueued data.
|
||||||
void FlushDecoding();
|
void FlushDecoding();
|
||||||
|
|
||||||
// Returns the audio clock, if we have audio, or -1 if we don't.
|
// 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.
|
// periodically via timer to ensure the video stays in sync.
|
||||||
nsresult RunStateMachine();
|
nsresult RunStateMachine();
|
||||||
|
|
||||||
bool IsStateMachineScheduled() const {
|
bool IsStateMachineScheduled() const;
|
||||||
AssertCurrentThreadInMonitor();
|
|
||||||
return !mTimeout.IsNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if we're not playing and the decode thread has filled its
|
// 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
|
// 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.
|
// state machine, audio and main threads.
|
||||||
nsRefPtr<MediaDecoder> mDecoder;
|
nsRefPtr<MediaDecoder> mDecoder;
|
||||||
|
|
||||||
|
// Used to schedule state machine cycles. This should never outlive
|
||||||
|
// the life cycle of the state machine.
|
||||||
|
const nsAutoPtr<MediaDecoderStateMachineScheduler> mScheduler;
|
||||||
|
|
||||||
// Time at which the last video sample was requested. If it takes too long
|
// 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.
|
// before the sample arrives, we will increase the amount of audio we buffer.
|
||||||
// This is necessary for legacy synchronous decoders to prevent underruns.
|
// This is necessary for legacy synchronous decoders to prevent underruns.
|
||||||
@ -674,19 +677,6 @@ protected:
|
|||||||
// thread every time they're called.
|
// thread every time they're called.
|
||||||
RefPtr<MediaTaskQueue> mDecodeTaskQueue;
|
RefPtr<MediaTaskQueue> mDecodeTaskQueue;
|
||||||
|
|
||||||
RefPtr<SharedThreadPool> mStateMachineThreadPool;
|
|
||||||
|
|
||||||
// Timer to run the state machine cycles. Used by
|
|
||||||
// ScheduleStateMachine(). Access protected by decoder monitor.
|
|
||||||
nsCOMPtr<nsITimer> 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<bool> mInRunningStateMachine;
|
|
||||||
|
|
||||||
// The time that playback started from the system clock. This is used for
|
// The time that playback started from the system clock. This is used for
|
||||||
// timing the presentation of video frames when there's no audio.
|
// timing the presentation of video frames when there's no audio.
|
||||||
// Accessed only via the state machine thread. Must be set via SetPlayStartTime.
|
// Accessed only via the state machine thread. Must be set via SetPlayStartTime.
|
||||||
@ -911,9 +901,6 @@ protected:
|
|||||||
// by the decoder monitor.
|
// by the decoder monitor.
|
||||||
bool mDecodeThreadWaiting;
|
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
|
// 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
|
// ReadMetadata on the reader. We maintain a flag to ensure that we don't
|
||||||
// dispatch multiple tasks to re-do the metadata loading.
|
// dispatch multiple tasks to re-do the metadata loading.
|
||||||
@ -942,9 +929,6 @@ protected:
|
|||||||
mozilla::MediaMetadataManager mMetadataManager;
|
mozilla::MediaMetadataManager mMetadataManager;
|
||||||
|
|
||||||
MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
|
MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
|
||||||
|
|
||||||
// The id of timer tasks, used to ignore tasks that are scheduled previously.
|
|
||||||
int mTimerId;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla;
|
} // namespace mozilla;
|
||||||
|
194
content/media/MediaDecoderStateMachineScheduler.cpp
Normal file
194
content/media/MediaDecoderStateMachineScheduler.cpp
Normal file
@ -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<nsIEventTarget>
|
||||||
|
CreateStateMachineThread()
|
||||||
|
{
|
||||||
|
using mozilla::SharedThreadPool;
|
||||||
|
using mozilla::RefPtr;
|
||||||
|
RefPtr<SharedThreadPool> threadPool(
|
||||||
|
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
|
||||||
|
nsCOMPtr<nsIEventTarget> 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<int64_t>(aUsecs, 0);
|
||||||
|
|
||||||
|
TimeStamp timeout = TimeStamp::Now() +
|
||||||
|
TimeDuration::FromMilliseconds(static_cast<double>(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<uint32_t>((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<TimerEvent> 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
|
77
content/media/MediaDecoderStateMachineScheduler.h
Normal file
77
content/media/MediaDecoderStateMachineScheduler.h
Normal file
@ -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<nsIEventTarget> mEventTarget;
|
||||||
|
// Timer to schedule callbacks to run the state machine cycles.
|
||||||
|
nsCOMPtr<nsITimer> 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<bool> mInRunningStateMachine;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // MediaDecoderStateMachineScheduler_h__
|
@ -144,6 +144,7 @@ UNIFIED_SOURCES += [
|
|||||||
'MediaDecoder.cpp',
|
'MediaDecoder.cpp',
|
||||||
'MediaDecoderReader.cpp',
|
'MediaDecoderReader.cpp',
|
||||||
'MediaDecoderStateMachine.cpp',
|
'MediaDecoderStateMachine.cpp',
|
||||||
|
'MediaDecoderStateMachineScheduler.cpp',
|
||||||
'MediaRecorder.cpp',
|
'MediaRecorder.cpp',
|
||||||
'MediaResource.cpp',
|
'MediaResource.cpp',
|
||||||
'MediaShutdownManager.cpp',
|
'MediaShutdownManager.cpp',
|
||||||
|
Loading…
Reference in New Issue
Block a user