Bug 1104964 - Make MediaDecoderReader own the task queue. r=cpearce

This commit is contained in:
Bobby Holley 2014-12-01 21:51:03 -08:00
Родитель e693d3e685
Коммит dba2651569
6 изменённых файлов: 79 добавлений и 48 удалений

Просмотреть файл

@ -63,6 +63,7 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
, mDecoder(aDecoder)
, mIgnoreAudioOutputFormat(false)
, mStartTime(-1)
, mTaskQueueIsBorrowed(false)
, mAudioDiscontinuity(false)
, mVideoDiscontinuity(false)
, mShutdown(false)
@ -247,10 +248,18 @@ MediaDecoderReader::SetCallback(RequestSampleCallback* aCallback)
mSampleDecodedCallback = aCallback;
}
void
MediaDecoderReader::SetTaskQueue(MediaTaskQueue* aTaskQueue)
MediaTaskQueue*
MediaDecoderReader::EnsureTaskQueue()
{
mTaskQueue = aTaskQueue;
if (!mTaskQueue) {
MOZ_ASSERT(!mTaskQueueIsBorrowed);
RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool());
NS_ENSURE_TRUE(decodePool, nullptr);
mTaskQueue = new MediaTaskQueue(decodePool.forget());
}
return mTaskQueue;
}
void
@ -266,9 +275,15 @@ MediaDecoderReader::BreakCycles()
void
MediaDecoderReader::Shutdown()
{
MOZ_ASSERT(mDecoder->OnDecodeThread());
MOZ_ASSERT(OnDecodeThread());
mShutdown = true;
ReleaseMediaResources();
if (mTaskQueue && !mTaskQueueIsBorrowed) {
// We may be running in the task queue ourselves, so we don't block this
// thread on task queue draining, since that would deadlock.
mTaskQueue->BeginShutdown();
}
mTaskQueue = nullptr;
}
AudioDecodeRendezvous::AudioDecodeRendezvous()

Просмотреть файл

@ -61,7 +61,19 @@ public:
virtual void Shutdown();
virtual void SetCallback(RequestSampleCallback* aDecodedSampleCallback);
virtual void SetTaskQueue(MediaTaskQueue* aTaskQueue);
MediaTaskQueue* EnsureTaskQueue();
virtual bool OnDecodeThread()
{
return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn();
}
void SetBorrowedTaskQueue(MediaTaskQueue* aTaskQueue)
{
MOZ_ASSERT(!mTaskQueue && aTaskQueue);
mTaskQueue = aTaskQueue;
mTaskQueueIsBorrowed = true;
}
// Resets all state related to decoding, emptying all buffers etc.
// Cancels all pending Request*Data() request callbacks, and flushes the
@ -261,6 +273,7 @@ private:
nsRefPtr<RequestSampleCallback> mSampleDecodedCallback;
nsRefPtr<MediaTaskQueue> mTaskQueue;
bool mTaskQueueIsBorrowed;
// Flags whether a the next audio/video sample comes after a "gap" or
// "discontinuity" in the stream. For example after a seek.

Просмотреть файл

@ -244,7 +244,6 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
NS_ASSERTION(!mPendingWakeDecoder.get(),
"WakeDecoder should have been revoked already");
MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN");
mReader = nullptr;
#ifdef XP_WIN
@ -1028,7 +1027,7 @@ MediaDecoderStateMachine::CheckIfSeekComplete()
mDecodeToSeekTarget = false;
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
if (NS_FAILED(rv)) {
DecodeError();
}
@ -1081,11 +1080,9 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool());
NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE);
mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget());
NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE);
if (NS_WARN_IF(!mReader->EnsureTaskQueue())) {
return NS_ERROR_FAILURE;
}
MediaDecoderReader* cloneReader = nullptr;
if (aCloneDonor) {
@ -1098,9 +1095,8 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
// Note: This creates a cycle, broken in shutdown.
mMediaDecodedListener =
new MediaDataDecodedListener<MediaDecoderStateMachine>(this,
mDecodeTaskQueue);
DecodeTaskQueue());
mReader->SetCallback(mMediaDecodedListener);
mReader->SetTaskQueue(mDecodeTaskQueue);
rv = mReader->Init(cloneReader);
NS_ENSURE_SUCCESS(rv, rv);
@ -1439,7 +1435,7 @@ void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this,
&MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged));
mDecodeTaskQueue->Dispatch(task);
DecodeTaskQueue()->Dispatch(task);
}
void MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged()
@ -1658,7 +1654,7 @@ MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeMetadata));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -1671,7 +1667,7 @@ MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -1749,7 +1745,7 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
if (needIdle) {
RefPtr<nsIRunnable> event = NS_NewRunnableMethod(
this, &MediaDecoderStateMachine::SetReaderIdle);
nsresult rv = mDecodeTaskQueue->Dispatch(event.forget());
nsresult rv = DecodeTaskQueue()->Dispatch(event.forget());
if (NS_FAILED(rv) && mState != DECODER_STATE_SHUTDOWN) {
DECODER_WARN("Failed to dispatch event to set decoder idle state");
}
@ -1775,7 +1771,7 @@ MediaDecoderStateMachine::EnqueueDecodeSeekTask()
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeSeek));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
if (NS_FAILED(rv)) {
DECODER_WARN("Dispatch DecodeSeek task failed.");
mCurrentSeekTarget.Reset();
@ -1817,7 +1813,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
if (IsAudioDecoding() && !mAudioRequestPending && !mWaitingForDecoderSeek) {
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeAudio));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
if (NS_SUCCEEDED(rv)) {
mAudioRequestPending = true;
} else {
@ -1862,7 +1858,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
if (IsVideoDecoding() && !mVideoRequestPending && !mWaitingForDecoderSeek) {
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeVideo));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
if (NS_SUCCEEDED(rv)) {
mVideoRequestPending = true;
} else {
@ -2102,12 +2098,12 @@ MediaDecoderStateMachine::DecodeFirstFrame()
if (HasAudio()) {
RefPtr<nsIRunnable> decodeTask(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded));
AudioQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
AudioQueue().AddPopListener(decodeTask, DecodeTaskQueue());
}
if (HasVideo()) {
RefPtr<nsIRunnable> decodeTask(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
VideoQueue().AddPopListener(decodeTask, DecodeTaskQueue());
}
if (mScheduler->IsRealTime()) {
@ -2271,7 +2267,7 @@ void MediaDecoderStateMachine::DecodeSeek()
if (!currentTimeChanged) {
DECODER_LOG("Seek !currentTimeChanged...");
nsresult rv = mDecodeTaskQueue->Dispatch(
nsresult rv = DecodeTaskQueue()->Dispatch(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
if (NS_FAILED(rv)) {
DecodeError();
@ -2479,17 +2475,16 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
StopAudioThread();
FlushDecoding();
// Put a task in the decode queue to shutdown the reader.
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::Shutdown);
mDecodeTaskQueue->Dispatch(task);
// Put a task in the decode queue to shutdown the reader and wait for
// the queue to spin down.
{
// Wait for the thread decoding to exit.
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::Shutdown);
nsRefPtr<MediaTaskQueue> queue = DecodeTaskQueue();
DebugOnly<nsresult> rv = queue->Dispatch(task);
MOZ_ASSERT(NS_SUCCEEDED(rv));
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
mDecodeTaskQueue->BeginShutdown();
mDecodeTaskQueue->AwaitShutdownAndIdle();
mDecodeTaskQueue = nullptr;
queue->AwaitShutdownAndIdle();
}
// The reader's listeners hold references to the state machine,
@ -2536,7 +2531,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
// Wait for the thread decoding, if any, to exit.
mDecodeTaskQueue->AwaitIdle();
DecodeTaskQueue()->AwaitIdle();
mReader->ReleaseMediaResources();
}
return NS_OK;
@ -2703,7 +2698,7 @@ MediaDecoderStateMachine::FlushDecoding()
// and shutdown on B2G will fail as there are outstanding video frames
// alive.
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
mDecodeTaskQueue->FlushAndDispatch(task);
DecodeTaskQueue()->FlushAndDispatch(task);
}
// We must reset playback so that all references to frames queued
@ -3202,7 +3197,7 @@ nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
bool MediaDecoderStateMachine::OnDecodeThread() const
{
return mDecodeTaskQueue->IsCurrentThreadIn();
return !DecodeTaskQueue() || DecodeTaskQueue()->IsCurrentThreadIn();
}
bool MediaDecoderStateMachine::OnStateMachineThread() const
@ -3329,7 +3324,7 @@ void MediaDecoderStateMachine::OnAudioSinkError()
// no sense to play an audio-only file without sound output.
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::AcquireMonitorAndInvokeDecodeError));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
nsresult rv = DecodeTaskQueue()->Dispatch(task);
if (NS_FAILED(rv)) {
DECODER_WARN("Failed to dispatch AcquireMonitorAndInvokeDecodeError");
}

Просмотреть файл

@ -354,7 +354,7 @@ public:
// be held.
bool IsPlaying() const;
// Dispatch DoNotifyWaitingForResourcesStatusChanged task to mDecodeTaskQueue.
// Dispatch DoNotifyWaitingForResourcesStatusChanged task to the task queue.
// Called when the reader may have acquired the hardware resources required
// to begin decoding. The decoder monitor must be held while calling this.
void NotifyWaitingForResourcesStatusChanged();
@ -731,7 +731,7 @@ protected:
// The task queue in which we run decode tasks. This is referred to as
// the "decode thread", though in practise tasks can run on a different
// thread every time they're called.
RefPtr<MediaTaskQueue> mDecodeTaskQueue;
MediaTaskQueue* DecodeTaskQueue() const { return mReader->GetTaskQueue(); }
// The time that playback started from the system clock. This is used for
// timing the presentation of video frames when there's no audio.

Просмотреть файл

@ -36,8 +36,7 @@ public:
decoder->SetResource(resource);
reader->Init(nullptr);
reader->SetTaskQueue(
new MediaTaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("TestMP4Reader"))));
reader->EnsureTaskQueue();
{
// This needs to be done before invoking GetBuffered. This is normally
// done by MediaDecoderStateMachine.
@ -57,11 +56,11 @@ public:
private:
virtual ~TestBinding()
{
reader->GetTaskQueue()->Dispatch(NS_NewRunnableMethod(reader,
&MP4Reader::Shutdown));
reader->GetTaskQueue()->BeginShutdown();
reader->GetTaskQueue()->AwaitShutdownAndIdle();
{
nsRefPtr<MediaTaskQueue> queue = reader->GetTaskQueue();
queue->Dispatch(NS_NewRunnableMethod(reader, &MP4Reader::Shutdown));
queue->AwaitShutdownAndIdle();
}
decoder = nullptr;
resource = nullptr;
reader = nullptr;

Просмотреть файл

@ -367,13 +367,22 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType)
reader->SetStartTime(0);
}
// This part is icky. It would be nicer to just give each subreader its own
// task queue. Unfortunately though, Request{Audio,Video}Data implementations
// currently assert that they're on "the decode thread", and so having
// separate task queues makes MediaSource stuff unnecessarily cumbersome. We
// should remove the need for these assertions (which probably involves making
// all Request*Data implementations fully async), and then get rid of the
// borrowing.
reader->SetBorrowedTaskQueue(GetTaskQueue());
// Set a callback on the subreader that forwards calls to this reader.
// This reader will then forward them onto the state machine via this
// reader's callback.
RefPtr<MediaDataDecodedListener<MediaSourceReader>> callback =
new MediaDataDecodedListener<MediaSourceReader>(this, GetTaskQueue());
new MediaDataDecodedListener<MediaSourceReader>(this, reader->GetTaskQueue());
reader->SetCallback(callback);
reader->SetTaskQueue(GetTaskQueue());
#ifdef MOZ_FMP4
reader->SetSharedDecoderManager(mSharedDecoderManager);
#endif