Bug 1135424 - Switch the MDSM to a task queue. r=mattwoodrow
This commit is contained in:
@@ -191,12 +191,12 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
|
|||||||
if (mIsDormant) {
|
if (mIsDormant) {
|
||||||
DECODER_LOG("UpdateDormantState() entering DORMANT state");
|
DECODER_LOG("UpdateDormantState() entering DORMANT state");
|
||||||
// enter dormant state
|
// enter dormant state
|
||||||
nsCOMPtr<nsIRunnable> event =
|
RefPtr<nsRunnable> event =
|
||||||
NS_NewRunnableMethodWithArg<bool>(
|
NS_NewRunnableMethodWithArg<bool>(
|
||||||
mDecoderStateMachine,
|
mDecoderStateMachine,
|
||||||
&MediaDecoderStateMachine::SetDormant,
|
&MediaDecoderStateMachine::SetDormant,
|
||||||
true);
|
true);
|
||||||
mDecoderStateMachine->GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
|
mDecoderStateMachine->TaskQueue()->Dispatch(event);
|
||||||
|
|
||||||
if (IsEnded()) {
|
if (IsEnded()) {
|
||||||
mWasEndedWhenEnteredDormant = true;
|
mWasEndedWhenEnteredDormant = true;
|
||||||
@@ -207,12 +207,12 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
|
|||||||
DECODER_LOG("UpdateDormantState() leaving DORMANT state");
|
DECODER_LOG("UpdateDormantState() leaving DORMANT state");
|
||||||
// exit dormant state
|
// exit dormant state
|
||||||
// trigger to state machine.
|
// trigger to state machine.
|
||||||
nsCOMPtr<nsIRunnable> event =
|
RefPtr<nsRunnable> event =
|
||||||
NS_NewRunnableMethodWithArg<bool>(
|
NS_NewRunnableMethodWithArg<bool>(
|
||||||
mDecoderStateMachine,
|
mDecoderStateMachine,
|
||||||
&MediaDecoderStateMachine::SetDormant,
|
&MediaDecoderStateMachine::SetDormant,
|
||||||
false);
|
false);
|
||||||
mDecoderStateMachine->GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
|
mDecoderStateMachine->TaskQueue()->Dispatch(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -750,7 +750,8 @@ nsresult MediaDecoder::ScheduleStateMachineThread()
|
|||||||
if (mShuttingDown)
|
if (mShuttingDown)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||||
return mDecoderStateMachine->ScheduleStateMachine();
|
mDecoderStateMachine->ScheduleStateMachine();
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult MediaDecoder::Play()
|
nsresult MediaDecoder::Play()
|
||||||
@@ -763,8 +764,7 @@ nsresult MediaDecoder::Play()
|
|||||||
if (mPausedForPlaybackRateNull) {
|
if (mPausedForPlaybackRateNull) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
nsresult res = ScheduleStateMachineThread();
|
ScheduleStateMachineThread();
|
||||||
NS_ENSURE_SUCCESS(res,res);
|
|
||||||
if (IsEnded()) {
|
if (IsEnded()) {
|
||||||
return Seek(0, SeekTarget::PrevSyncPoint);
|
return Seek(0, SeekTarget::PrevSyncPoint);
|
||||||
} else if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) {
|
} else if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) {
|
||||||
@@ -1317,7 +1317,7 @@ void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
|
|||||||
mDecoderStateMachine->Play();
|
mDecoderStateMachine->Play();
|
||||||
break;
|
break;
|
||||||
case PLAY_STATE_SEEKING:
|
case PLAY_STATE_SEEKING:
|
||||||
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->GetStateMachineThread(),
|
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(),
|
||||||
mDecoderStateMachine.get(), __func__,
|
mDecoderStateMachine.get(), __func__,
|
||||||
&MediaDecoderStateMachine::Seek, mRequestedSeekTarget)
|
&MediaDecoderStateMachine::Seek, mRequestedSeekTarget)
|
||||||
->RefableThen(NS_GetCurrentThread(), __func__, this,
|
->RefableThen(NS_GetCurrentThread(), __func__, this,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "MediaDecoderStateMachine.h"
|
#include "MediaDecoderStateMachine.h"
|
||||||
#include "MediaDecoderStateMachineScheduler.h"
|
#include "MediaTimer.h"
|
||||||
#include "AudioSink.h"
|
#include "AudioSink.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "MediaDecoder.h"
|
#include "MediaDecoder.h"
|
||||||
@@ -201,9 +201,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||||||
MediaDecoderReader* aReader,
|
MediaDecoderReader* aReader,
|
||||||
bool aRealTime) :
|
bool aRealTime) :
|
||||||
mDecoder(aDecoder),
|
mDecoder(aDecoder),
|
||||||
mScheduler(new MediaDecoderStateMachineScheduler(
|
mRealTime(aRealTime),
|
||||||
aDecoder->GetReentrantMonitor(),
|
mDispatchedStateMachine(false),
|
||||||
&MediaDecoderStateMachine::TimeoutExpired, this, aRealTime)),
|
mDelayedScheduler(this),
|
||||||
mState(DECODER_STATE_DECODING_NONE),
|
mState(DECODER_STATE_DECODING_NONE),
|
||||||
mPlayDuration(0),
|
mPlayDuration(0),
|
||||||
mStartTime(-1),
|
mStartTime(-1),
|
||||||
@@ -248,6 +248,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||||||
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.");
|
||||||
|
|
||||||
|
// Set up our task queue.
|
||||||
|
RefPtr<SharedThreadPool> threadPool(
|
||||||
|
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
|
||||||
|
mTaskQueue = new MediaTaskQueue(threadPool.forget());
|
||||||
|
|
||||||
static bool sPrefCacheInit = false;
|
static bool sPrefCacheInit = false;
|
||||||
if (!sPrefCacheInit) {
|
if (!sPrefCacheInit) {
|
||||||
sPrefCacheInit = true;
|
sPrefCacheInit = true;
|
||||||
@@ -387,7 +392,7 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(OnStateMachineThread(), "Should be on state machine thread");
|
MOZ_ASSERT(OnStateMachineThread(), "Should be on state machine thread");
|
||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
MOZ_ASSERT(!mAudioSink, "Should've been stopped in CallRunStateMachine()");
|
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
|
||||||
|
|
||||||
DecodedStreamData* stream = mDecoder->GetDecodedStream();
|
DecodedStreamData* stream = mDecoder->GetDecodedStream();
|
||||||
|
|
||||||
@@ -405,7 +410,7 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||||||
mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
|
mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
|
||||||
SourceMediaStream::ADDTRACK_QUEUED);
|
SourceMediaStream::ADDTRACK_QUEUED);
|
||||||
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
|
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
|
||||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
TaskQueue(), GetWakeDecoderRunnable());
|
||||||
stream->mNextAudioTime = mStartTime + stream->mInitialTime;
|
stream->mNextAudioTime = mStartTime + stream->mInitialTime;
|
||||||
}
|
}
|
||||||
if (mInfo.HasVideo()) {
|
if (mInfo.HasVideo()) {
|
||||||
@@ -414,7 +419,7 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||||||
mediaStream->AddTrack(videoTrackId, 0, video,
|
mediaStream->AddTrack(videoTrackId, 0, video,
|
||||||
SourceMediaStream::ADDTRACK_QUEUED);
|
SourceMediaStream::ADDTRACK_QUEUED);
|
||||||
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
|
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
|
||||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
TaskQueue(), GetWakeDecoderRunnable());
|
||||||
|
|
||||||
// TODO: We can't initialize |mNextVideoTime| until |mStartTime|
|
// TODO: We can't initialize |mNextVideoTime| until |mStartTime|
|
||||||
// is set. This is a good indication that DecodedStreamData is in
|
// is set. This is a good indication that DecodedStreamData is in
|
||||||
@@ -570,7 +575,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
|
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
|
||||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
TaskQueue(), GetWakeDecoderRunnable());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -593,7 +598,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
|
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
|
||||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
TaskQueue(), GetWakeDecoderRunnable());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -855,7 +860,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
|||||||
"Readers that send WAITING_FOR_DATA need to implement WaitForData");
|
"Readers that send WAITING_FOR_DATA need to implement WaitForData");
|
||||||
WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||||
&MediaDecoderReader::WaitForData, aType)
|
&MediaDecoderReader::WaitForData, aType)
|
||||||
->RefableThen(mScheduler.get(), __func__, this,
|
->RefableThen(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::OnWaitForDataResolved,
|
&MediaDecoderStateMachine::OnWaitForDataResolved,
|
||||||
&MediaDecoderStateMachine::OnWaitForDataRejected));
|
&MediaDecoderStateMachine::OnWaitForDataRejected));
|
||||||
return;
|
return;
|
||||||
@@ -1084,7 +1089,7 @@ MediaDecoderStateMachine::CheckIfSeekComplete()
|
|||||||
mDecodeToSeekTarget = false;
|
mDecodeToSeekTarget = false;
|
||||||
RefPtr<nsIRunnable> task(
|
RefPtr<nsIRunnable> task(
|
||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
|
||||||
nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL);
|
nsresult rv = TaskQueue()->Dispatch(task);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
DecodeError();
|
DecodeError();
|
||||||
}
|
}
|
||||||
@@ -1146,10 +1151,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
|
|||||||
cloneReader = aCloneDonor->mReader;
|
cloneReader = aCloneDonor->mReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = mScheduler->Init();
|
nsresult rv = mReader->Init(cloneReader);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
rv = mReader->Init(cloneReader);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@@ -1343,7 +1345,7 @@ int64_t MediaDecoderStateMachine::GetCurrentTimeUs() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MediaDecoderStateMachine::IsRealTime() const {
|
bool MediaDecoderStateMachine::IsRealTime() const {
|
||||||
return mScheduler->IsRealTime();
|
return mRealTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t MediaDecoderStateMachine::GetDuration()
|
int64_t MediaDecoderStateMachine::GetDuration()
|
||||||
@@ -1523,9 +1525,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("Changed state to SHUTDOWN");
|
ScheduleStateMachine();
|
||||||
SetState(DECODER_STATE_SHUTDOWN);
|
SetState(DECODER_STATE_SHUTDOWN);
|
||||||
mScheduler->ScheduleAndShutdown();
|
|
||||||
if (mAudioSink) {
|
if (mAudioSink) {
|
||||||
mAudioSink->PrepareToShutdown();
|
mAudioSink->PrepareToShutdown();
|
||||||
}
|
}
|
||||||
@@ -1784,7 +1785,7 @@ MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
|
|||||||
|
|
||||||
RefPtr<nsIRunnable> task(
|
RefPtr<nsIRunnable> task(
|
||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
|
||||||
nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL);
|
nsresult rv = TaskQueue()->Dispatch(task);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@@ -1931,7 +1932,7 @@ MediaDecoderStateMachine::InitiateSeek()
|
|||||||
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||||
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
|
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
|
||||||
GetEndTime())
|
GetEndTime())
|
||||||
->RefableThen(mScheduler.get(), __func__, this,
|
->RefableThen(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::OnSeekCompleted,
|
&MediaDecoderStateMachine::OnSeekCompleted,
|
||||||
&MediaDecoderStateMachine::OnSeekFailed));
|
&MediaDecoderStateMachine::OnSeekFailed));
|
||||||
}
|
}
|
||||||
@@ -1979,7 +1980,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
|
|||||||
|
|
||||||
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||||
__func__, &MediaDecoderReader::RequestAudioData)
|
__func__, &MediaDecoderReader::RequestAudioData)
|
||||||
->RefableThen(mScheduler.get(), __func__, this,
|
->RefableThen(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||||
|
|
||||||
@@ -2039,7 +2040,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
|||||||
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||||
&MediaDecoderReader::RequestVideoData,
|
&MediaDecoderReader::RequestVideoData,
|
||||||
skipToNextKeyFrame, currentTime)
|
skipToNextKeyFrame, currentTime)
|
||||||
->RefableThen(mScheduler.get(), __func__, this,
|
->RefableThen(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@@ -2177,9 +2178,9 @@ MediaDecoderStateMachine::DecodeError()
|
|||||||
// Change state to shutdown before sending error report to MediaDecoder
|
// Change state to shutdown before sending error report to MediaDecoder
|
||||||
// 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_WARN("Decode error, changed state to SHUTDOWN due to error");
|
ScheduleStateMachine();
|
||||||
SetState(DECODER_STATE_SHUTDOWN);
|
SetState(DECODER_STATE_SHUTDOWN);
|
||||||
mScheduler->ScheduleAndShutdown();
|
DECODER_WARN("Decode error, changed state to SHUTDOWN due to error");
|
||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
|
|
||||||
// Dispatch the event to call DecodeError synchronously. This ensures
|
// Dispatch the event to call DecodeError synchronously. This ensures
|
||||||
@@ -2332,12 +2333,12 @@ MediaDecoderStateMachine::DecodeFirstFrame()
|
|||||||
if (HasAudio()) {
|
if (HasAudio()) {
|
||||||
RefPtr<nsIRunnable> decodeTask(
|
RefPtr<nsIRunnable> decodeTask(
|
||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded));
|
||||||
AudioQueue().AddPopListener(decodeTask, GetStateMachineThread());
|
AudioQueue().AddPopListener(decodeTask, TaskQueue());
|
||||||
}
|
}
|
||||||
if (HasVideo()) {
|
if (HasVideo()) {
|
||||||
RefPtr<nsIRunnable> decodeTask(
|
RefPtr<nsIRunnable> decodeTask(
|
||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
|
||||||
VideoQueue().AddPopListener(decodeTask, GetStateMachineThread());
|
VideoQueue().AddPopListener(decodeTask, TaskQueue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsRealTime()) {
|
if (IsRealTime()) {
|
||||||
@@ -2355,7 +2356,7 @@ MediaDecoderStateMachine::DecodeFirstFrame()
|
|||||||
if (HasAudio()) {
|
if (HasAudio()) {
|
||||||
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||||
__func__, &MediaDecoderReader::RequestAudioData)
|
__func__, &MediaDecoderReader::RequestAudioData)
|
||||||
->RefableThen(mScheduler.get(), __func__, this,
|
->RefableThen(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||||
}
|
}
|
||||||
@@ -2363,7 +2364,7 @@ MediaDecoderStateMachine::DecodeFirstFrame()
|
|||||||
mVideoDecodeStartTime = TimeStamp::Now();
|
mVideoDecodeStartTime = TimeStamp::Now();
|
||||||
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||||
__func__, &MediaDecoderReader::RequestVideoData, false, int64_t(0))
|
__func__, &MediaDecoderReader::RequestVideoData, false, int64_t(0))
|
||||||
->RefableThen(mScheduler.get(), __func__, this,
|
->RefableThen(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||||
}
|
}
|
||||||
@@ -2570,6 +2571,8 @@ public:
|
|||||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||||
MOZ_ASSERT(mStateMachine);
|
MOZ_ASSERT(mStateMachine);
|
||||||
MOZ_ASSERT(mDecoder);
|
MOZ_ASSERT(mDecoder);
|
||||||
|
mStateMachine->TaskQueue()->BeginShutdown();
|
||||||
|
mStateMachine->TaskQueue()->AwaitShutdownAndIdle();
|
||||||
mStateMachine->BreakCycles();
|
mStateMachine->BreakCycles();
|
||||||
mDecoder->BreakCycles();
|
mDecoder->BreakCycles();
|
||||||
mStateMachine = nullptr;
|
mStateMachine = nullptr;
|
||||||
@@ -2604,7 +2607,7 @@ void
|
|||||||
MediaDecoderStateMachine::ShutdownReader()
|
MediaDecoderStateMachine::ShutdownReader()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnDecodeThread());
|
MOZ_ASSERT(OnDecodeThread());
|
||||||
mReader->Shutdown()->Then(mScheduler.get(), __func__, this,
|
mReader->Shutdown()->Then(TaskQueue(), __func__, this,
|
||||||
&MediaDecoderStateMachine::FinishShutdown,
|
&MediaDecoderStateMachine::FinishShutdown,
|
||||||
&MediaDecoderStateMachine::FinishShutdown);
|
&MediaDecoderStateMachine::FinishShutdown);
|
||||||
}
|
}
|
||||||
@@ -2640,15 +2643,24 @@ MediaDecoderStateMachine::FinishShutdown()
|
|||||||
// finished and released its monitor/references. That event then will
|
// finished and released its monitor/references. That event then will
|
||||||
// dispatch an event to the main thread to release the decoder and
|
// dispatch an event to the main thread to release the decoder and
|
||||||
// state machine.
|
// state machine.
|
||||||
GetStateMachineThread()->Dispatch(
|
RefPtr<nsIRunnable> task = new nsDispatchDisposeEvent(mDecoder, this);
|
||||||
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
|
TaskQueue()->Dispatch(task);
|
||||||
|
|
||||||
DECODER_LOG("Dispose Event Dispatched");
|
DECODER_LOG("Dispose Event Dispatched");
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult MediaDecoderStateMachine::RunStateMachine()
|
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();
|
MediaResource* resource = mDecoder->GetResource();
|
||||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||||
@@ -2738,7 +2750,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||||||
DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
|
DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
|
||||||
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
||||||
(mQuickBuffering ? "(quick exit)" : ""));
|
(mQuickBuffering ? "(quick exit)" : ""));
|
||||||
ScheduleStateMachine(USECS_PER_S);
|
ScheduleStateMachineIn(USECS_PER_S);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
} else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
|
} else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
|
||||||
@@ -3062,7 +3074,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||||||
// Don't go straight back to the state machine loop since that might
|
// 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
|
// cause us to start decoding again and we could flip-flop between
|
||||||
// decoding and quick-buffering.
|
// decoding and quick-buffering.
|
||||||
ScheduleStateMachine(USECS_PER_S);
|
ScheduleStateMachineIn(USECS_PER_S);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3124,7 +3136,12 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||||||
// ready state. Post an update to do so.
|
// ready state. Post an update to do so.
|
||||||
UpdateReadyState();
|
UpdateReadyState();
|
||||||
|
|
||||||
ScheduleStateMachine(remainingTime / mPlaybackRate);
|
int64_t delay = remainingTime / mPlaybackRate;
|
||||||
|
if (delay > 0) {
|
||||||
|
ScheduleStateMachineIn(delay);
|
||||||
|
} else {
|
||||||
|
ScheduleStateMachine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@@ -3370,33 +3387,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<MediaDecoderStateMachine*>(aClosure);
|
|
||||||
return p->CallRunStateMachine();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
DispatchAudioDecodeTaskIfNeeded();
|
DispatchAudioDecodeTaskIfNeeded();
|
||||||
DispatchVideoDecodeTaskIfNeeded();
|
DispatchVideoDecodeTaskIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
|
void
|
||||||
return mScheduler->Schedule(aUsecs);
|
MediaDecoderStateMachine::ScheduleStateMachine() {
|
||||||
|
AssertCurrentThreadInMonitor();
|
||||||
|
if (mState == DECODER_STATE_SHUTDOWN) {
|
||||||
|
NS_WARNING("Refusing to schedule shutdown state machine");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDispatchedStateMachine) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mDispatchedStateMachine = true;
|
||||||
|
|
||||||
|
RefPtr<nsIRunnable> 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
|
bool MediaDecoderStateMachine::OnDecodeThread() const
|
||||||
@@ -3406,17 +3449,12 @@ bool MediaDecoderStateMachine::OnDecodeThread() const
|
|||||||
|
|
||||||
bool MediaDecoderStateMachine::OnStateMachineThread() const
|
bool MediaDecoderStateMachine::OnStateMachineThread() const
|
||||||
{
|
{
|
||||||
return mScheduler->OnStateMachineThread();
|
return TaskQueue()->IsCurrentThreadIn();
|
||||||
}
|
|
||||||
|
|
||||||
nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() const
|
|
||||||
{
|
|
||||||
return mScheduler->GetStateMachineThread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaDecoderStateMachine::IsStateMachineScheduled() const
|
bool MediaDecoderStateMachine::IsStateMachineScheduled() const
|
||||||
{
|
{
|
||||||
return mScheduler->IsScheduled();
|
return mDispatchedStateMachine || mDelayedScheduler.IsScheduled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
|
void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ hardware (via AudioStream).
|
|||||||
#include "MediaDecoderReader.h"
|
#include "MediaDecoderReader.h"
|
||||||
#include "MediaDecoderOwner.h"
|
#include "MediaDecoderOwner.h"
|
||||||
#include "MediaMetadataManager.h"
|
#include "MediaMetadataManager.h"
|
||||||
#include "MediaDecoderStateMachineScheduler.h"
|
|
||||||
#include "mozilla/RollingMean.h"
|
#include "mozilla/RollingMean.h"
|
||||||
|
#include "MediaTimer.h"
|
||||||
|
|
||||||
class nsITimer;
|
class nsITimer;
|
||||||
|
|
||||||
@@ -211,8 +211,8 @@ public:
|
|||||||
void Play()
|
void Play()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
nsRefPtr<nsRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal);
|
RefPtr<nsRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal);
|
||||||
GetStateMachineThread()->Dispatch(r, NS_DISPATCH_NORMAL);
|
TaskQueue()->Dispatch(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -311,21 +311,30 @@ public:
|
|||||||
|
|
||||||
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||||
|
|
||||||
// Returns the shared state machine thread.
|
// Returns the state machine task queue.
|
||||||
nsIEventTarget* GetStateMachineThread() const;
|
MediaTaskQueue* TaskQueue() const { return mTaskQueue; }
|
||||||
|
|
||||||
// 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.
|
||||||
void ScheduleStateMachineWithLockAndWakeDecoder();
|
void ScheduleStateMachineWithLockAndWakeDecoder();
|
||||||
|
|
||||||
// Schedules the shared state machine thread to run the state machine
|
// Schedules the shared state machine thread to run the state machine.
|
||||||
// in aUsecs microseconds from now, if it's not already scheduled to run
|
void ScheduleStateMachine();
|
||||||
// earlier, in which case the request is discarded.
|
|
||||||
nsresult ScheduleStateMachine(int64_t aUsecs = 0);
|
|
||||||
|
|
||||||
// Callback function registered with MediaDecoderStateMachineScheduler
|
// Invokes ScheduleStateMachine to run in |aMicroseconds| microseconds,
|
||||||
// to run state machine cycles.
|
// unless it's already scheduled to run earlier, in which case the
|
||||||
static nsresult TimeoutExpired(void* aClosure);
|
// 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.
|
// Set the media fragment end time. aEndTime is in microseconds.
|
||||||
void SetFragmentEndTime(int64_t aEndTime);
|
void SetFragmentEndTime(int64_t aEndTime);
|
||||||
@@ -689,9 +698,6 @@ protected:
|
|||||||
void SendStreamAudio(AudioData* aAudio, DecodedStreamData* aStream,
|
void SendStreamAudio(AudioData* aAudio, DecodedStreamData* aStream,
|
||||||
AudioSegment* aOutput);
|
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
|
// 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
|
// a video frame to be displayed, and generally manages the decode. Called
|
||||||
// periodically via timer to ensure the video stays in sync.
|
// periodically via timer to ensure the video stays in sync.
|
||||||
@@ -745,9 +751,60 @@ 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
|
// Task queue for running the state machine.
|
||||||
// the life cycle of the state machine.
|
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||||
const nsRefPtr<MediaDecoderStateMachineScheduler> mScheduler;
|
|
||||||
|
// 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<MediaTimer> mMediaTimer;
|
||||||
|
MediaPromiseConsumerHolder<mozilla::MediaTimerPromise> mRequest;
|
||||||
|
TimeStamp mTarget;
|
||||||
|
|
||||||
|
} mDelayedScheduler;
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -955,7 +1012,7 @@ protected:
|
|||||||
// samples we must consume before are considered to be finished prerolling.
|
// samples we must consume before are considered to be finished prerolling.
|
||||||
uint32_t AudioPrerollUsecs() const
|
uint32_t AudioPrerollUsecs() const
|
||||||
{
|
{
|
||||||
if (mScheduler->IsRealTime()) {
|
if (IsRealTime()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -966,7 +1023,7 @@ protected:
|
|||||||
|
|
||||||
uint32_t VideoPrerollFrames() const
|
uint32_t VideoPrerollFrames() const
|
||||||
{
|
{
|
||||||
return mScheduler->IsRealTime() ? 0 : GetAmpleVideoFrames() / 2;
|
return IsRealTime() ? 0 : GetAmpleVideoFrames() / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DonePrerollingAudio()
|
bool DonePrerollingAudio()
|
||||||
|
|||||||
@@ -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<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 (NS_WARN_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
|
|
||||||
@@ -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<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__
|
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "MediaPromise.h"
|
#include "MediaPromise.h"
|
||||||
|
|
||||||
#include "MediaDecoderStateMachineScheduler.h"
|
|
||||||
#include "MediaTaskQueue.h"
|
#include "MediaTaskQueue.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
|
||||||
@@ -25,12 +24,6 @@ DispatchMediaPromiseRunnable(nsIEventTarget* aEventTarget, nsIRunnable* aRunnabl
|
|||||||
return aEventTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
|
return aEventTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
|
||||||
DispatchMediaPromiseRunnable(MediaDecoderStateMachineScheduler* aScheduler, nsIRunnable* aRunnable)
|
|
||||||
{
|
|
||||||
return aScheduler->GetStateMachineThread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
AssertOnThread(MediaTaskQueue* aQueue)
|
AssertOnThread(MediaTaskQueue* aQueue)
|
||||||
{
|
{
|
||||||
@@ -44,11 +37,5 @@ void AssertOnThread(nsIEventTarget* aTarget)
|
|||||||
MOZ_ASSERT(NS_GetCurrentThread() == targetThread);
|
MOZ_ASSERT(NS_GetCurrentThread() == targetThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
AssertOnThread(MediaDecoderStateMachineScheduler* aScheduler)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aScheduler->OnStateMachineThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -33,17 +33,14 @@ extern PRLogModuleInfo* gMediaPromiseLog;
|
|||||||
PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__))
|
PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__))
|
||||||
|
|
||||||
class MediaTaskQueue;
|
class MediaTaskQueue;
|
||||||
class MediaDecoderStateMachineScheduler;
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
nsresult DispatchMediaPromiseRunnable(MediaTaskQueue* aQueue, nsIRunnable* aRunnable);
|
nsresult DispatchMediaPromiseRunnable(MediaTaskQueue* aQueue, nsIRunnable* aRunnable);
|
||||||
nsresult DispatchMediaPromiseRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable);
|
nsresult DispatchMediaPromiseRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable);
|
||||||
nsresult DispatchMediaPromiseRunnable(MediaDecoderStateMachineScheduler* aScheduler, nsIRunnable* aRunnable);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void AssertOnThread(MediaTaskQueue* aQueue);
|
void AssertOnThread(MediaTaskQueue* aQueue);
|
||||||
void AssertOnThread(nsIEventTarget* aTarget);
|
void AssertOnThread(nsIEventTarget* aTarget);
|
||||||
void AssertOnThread(MediaDecoderStateMachineScheduler* aScheduler);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ template <class T> class MediaQueue : private nsDeque {
|
|||||||
mPopListeners.Clear();
|
mPopListeners.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddPopListener(nsIRunnable* aRunnable, nsIEventTarget* aTarget) {
|
void AddPopListener(nsIRunnable* aRunnable, MediaTaskQueue* aTarget) {
|
||||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||||
mPopListeners.AppendElement(Listener(aRunnable, aTarget));
|
mPopListeners.AppendElement(Listener(aRunnable, aTarget));
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ private:
|
|||||||
mutable ReentrantMonitor mReentrantMonitor;
|
mutable ReentrantMonitor mReentrantMonitor;
|
||||||
|
|
||||||
struct Listener {
|
struct Listener {
|
||||||
Listener(nsIRunnable* aRunnable, nsIEventTarget* aTarget)
|
Listener(nsIRunnable* aRunnable, MediaTaskQueue* aTarget)
|
||||||
: mRunnable(aRunnable)
|
: mRunnable(aRunnable)
|
||||||
, mTarget(aTarget)
|
, mTarget(aTarget)
|
||||||
{
|
{
|
||||||
@@ -177,7 +177,7 @@ private:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
RefPtr<nsIRunnable> mRunnable;
|
RefPtr<nsIRunnable> mRunnable;
|
||||||
RefPtr<nsIEventTarget> mTarget;
|
RefPtr<MediaTaskQueue> mTarget;
|
||||||
};
|
};
|
||||||
|
|
||||||
nsTArray<Listener> mPopListeners;
|
nsTArray<Listener> mPopListeners;
|
||||||
@@ -185,7 +185,7 @@ private:
|
|||||||
void NotifyPopListeners() {
|
void NotifyPopListeners() {
|
||||||
for (uint32_t i = 0; i < mPopListeners.Length(); i++) {
|
for (uint32_t i = 0; i < mPopListeners.Length(); i++) {
|
||||||
Listener& l = mPopListeners[i];
|
Listener& l = mPopListeners[i];
|
||||||
l.mTarget->Dispatch(l.mRunnable, NS_DISPATCH_NORMAL);
|
l.mTarget->Dispatch(l.mRunnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ MediaStreamGraphImpl::UpdateBufferSufficiencyState(SourceMediaStream* aStream)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < runnables.Length(); ++i) {
|
for (uint32_t i = 0; i < runnables.Length(); ++i) {
|
||||||
runnables[i].mTarget->Dispatch(runnables[i].mRunnable, 0);
|
runnables[i].mTarget->Dispatch(runnables[i].mRunnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2493,21 +2493,21 @@ SourceMediaStream::GetEndOfAppendedData(TrackID aID)
|
|||||||
|
|
||||||
void
|
void
|
||||||
SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
|
SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
|
||||||
nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable)
|
MediaTaskQueue* aSignalQueue, nsIRunnable* aSignalRunnable)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
TrackData* data = FindDataForTrack(aID);
|
TrackData* data = FindDataForTrack(aID);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
aSignalThread->Dispatch(aSignalRunnable, 0);
|
aSignalQueue->Dispatch(aSignalRunnable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->mHaveEnough) {
|
if (data->mHaveEnough) {
|
||||||
if (data->mDispatchWhenNotEnough.IsEmpty()) {
|
if (data->mDispatchWhenNotEnough.IsEmpty()) {
|
||||||
data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalThread, aSignalRunnable);
|
data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalQueue, aSignalRunnable);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
aSignalThread->Dispatch(aSignalRunnable, 0);
|
aSignalQueue->Dispatch(aSignalRunnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "VideoFrameContainer.h"
|
#include "VideoFrameContainer.h"
|
||||||
#include "VideoSegment.h"
|
#include "VideoSegment.h"
|
||||||
#include "MainThreadUtils.h"
|
#include "MainThreadUtils.h"
|
||||||
|
#include "MediaTaskQueue.h"
|
||||||
#include "nsAutoRef.h"
|
#include "nsAutoRef.h"
|
||||||
#include "GraphDriver.h"
|
#include "GraphDriver.h"
|
||||||
#include <speex/speex_resampler.h>
|
#include <speex/speex_resampler.h>
|
||||||
@@ -783,7 +784,7 @@ public:
|
|||||||
* does not exist. No op if a runnable is already present for this track.
|
* does not exist. No op if a runnable is already present for this track.
|
||||||
*/
|
*/
|
||||||
void DispatchWhenNotEnoughBuffered(TrackID aID,
|
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
|
* Indicate that a track has ended. Do not do any more API calls
|
||||||
* affecting this track.
|
* affecting this track.
|
||||||
@@ -847,14 +848,14 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct ThreadAndRunnable {
|
struct ThreadAndRunnable {
|
||||||
void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable)
|
void Init(MediaTaskQueue* aTarget, nsIRunnable* aRunnable)
|
||||||
{
|
{
|
||||||
mTarget = aTarget;
|
mTarget = aTarget;
|
||||||
mRunnable = aRunnable;
|
mRunnable = aRunnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIEventTarget> mTarget;
|
nsRefPtr<MediaTaskQueue> mTarget;
|
||||||
nsCOMPtr<nsIRunnable> mRunnable;
|
RefPtr<nsIRunnable> mRunnable;
|
||||||
};
|
};
|
||||||
enum TrackCommands {
|
enum TrackCommands {
|
||||||
TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
|
TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ EXPORTS += [
|
|||||||
'MediaDecoderOwner.h',
|
'MediaDecoderOwner.h',
|
||||||
'MediaDecoderReader.h',
|
'MediaDecoderReader.h',
|
||||||
'MediaDecoderStateMachine.h',
|
'MediaDecoderStateMachine.h',
|
||||||
'MediaDecoderStateMachineScheduler.h',
|
|
||||||
'MediaInfo.h',
|
'MediaInfo.h',
|
||||||
'MediaMetadataManager.h',
|
'MediaMetadataManager.h',
|
||||||
'MediaPromise.h',
|
'MediaPromise.h',
|
||||||
@@ -112,6 +111,7 @@ EXPORTS += [
|
|||||||
'MediaSegment.h',
|
'MediaSegment.h',
|
||||||
'MediaStreamGraph.h',
|
'MediaStreamGraph.h',
|
||||||
'MediaTaskQueue.h',
|
'MediaTaskQueue.h',
|
||||||
|
'MediaTimer.h',
|
||||||
'MediaTrack.h',
|
'MediaTrack.h',
|
||||||
'MediaTrackList.h',
|
'MediaTrackList.h',
|
||||||
'MP3FrameParser.h',
|
'MP3FrameParser.h',
|
||||||
@@ -181,7 +181,6 @@ UNIFIED_SOURCES += [
|
|||||||
'MediaDecoder.cpp',
|
'MediaDecoder.cpp',
|
||||||
'MediaDecoderReader.cpp',
|
'MediaDecoderReader.cpp',
|
||||||
'MediaDecoderStateMachine.cpp',
|
'MediaDecoderStateMachine.cpp',
|
||||||
'MediaDecoderStateMachineScheduler.cpp',
|
|
||||||
'MediaDevices.cpp',
|
'MediaDevices.cpp',
|
||||||
'MediaManager.cpp',
|
'MediaManager.cpp',
|
||||||
'MediaPromise.cpp',
|
'MediaPromise.cpp',
|
||||||
|
|||||||
Reference in New Issue
Block a user