From 60f876f46a7c1e2eec6e5756a7bc221e6cc8a6b7 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Sun, 7 Dec 2014 17:09:06 -0800 Subject: [PATCH] Backed out changeset ab6ddc8ac28a (bug 1097823) --- dom/media/MediaDecoderReader.cpp | 61 ++++++++++ dom/media/MediaDecoderReader.h | 32 ++++++ dom/media/MediaPromise.h | 4 +- dom/media/ogg/OggReader.cpp | 2 +- dom/media/webaudio/AudioContext.cpp | 7 +- dom/media/webaudio/AudioContext.h | 1 + dom/media/webaudio/BufferDecoder.cpp | 10 +- dom/media/webaudio/BufferDecoder.h | 5 +- dom/media/webaudio/MediaBufferDecoder.cpp | 134 ++++++++++++---------- dom/media/webaudio/MediaBufferDecoder.h | 29 ++++- 10 files changed, 212 insertions(+), 73 deletions(-) diff --git a/dom/media/MediaDecoderReader.cpp b/dom/media/MediaDecoderReader.cpp index 77d78f36828f..a76aadcf34db 100644 --- a/dom/media/MediaDecoderReader.cpp +++ b/dom/media/MediaDecoderReader.cpp @@ -317,4 +317,65 @@ MediaDecoderReader::Shutdown() mTaskQueue = nullptr; } +AudioDecodeRendezvous::AudioDecodeRendezvous(MediaDecoderReader *aReader) + : mReader(aReader) + , mMonitor("AudioDecodeRendezvous") + , mHaveResult(false) +{ +} + +AudioDecodeRendezvous::~AudioDecodeRendezvous() +{ +} + +void +AudioDecodeRendezvous::OnAudioDecoded(AudioData* aSample) +{ + MonitorAutoLock mon(mMonitor); + mSample = aSample; + mStatus = NS_OK; + mHaveResult = true; + mon.NotifyAll(); +} + +void +AudioDecodeRendezvous::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason) +{ + MonitorAutoLock mon(mMonitor); + mSample = nullptr; + mStatus = aReason == MediaDecoderReader::DECODE_ERROR ? NS_ERROR_FAILURE : NS_OK; + mHaveResult = true; + mon.NotifyAll(); +} + +void +AudioDecodeRendezvous::Reset() +{ + MonitorAutoLock mon(mMonitor); + mHaveResult = false; + mStatus = NS_OK; + mSample = nullptr; +} + +nsresult +AudioDecodeRendezvous::RequestAndWait(nsRefPtr& aSample) +{ + MonitorAutoLock mon(mMonitor); + // XXXbholley: We hackily use the main thread for calling back the rendezvous, + // since the decode thread is blocked. This is fine for correctness but not + // for jank, and so it goes away in a subsequent patch. + nsCOMPtr mainThread; + nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread)); + NS_ENSURE_SUCCESS(rv, rv); + mReader->RequestAudioData()->Then(mainThread.get(), __func__, this, + &AudioDecodeRendezvous::OnAudioDecoded, + &AudioDecodeRendezvous::OnAudioNotDecoded); + while (!mHaveResult) { + mon.Wait(); + } + mHaveResult = false; + aSample = mSample; + return mStatus; +} + } // namespace mozilla diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index 320dd9f3951b..f434abe282ea 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -334,6 +334,38 @@ protected: virtual ~RequestSampleCallback() {} }; +// A RequestSampleCallback implementation that can be passed to the +// MediaDecoderReader to block the thread requesting an audio sample until +// the audio decode is complete. This is used to adapt the asynchronous +// model of the MediaDecoderReader to a synchronous model. +class AudioDecodeRendezvous { +public: + AudioDecodeRendezvous(MediaDecoderReader *aReader); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDecodeRendezvous) + + void OnAudioDecoded(AudioData* aSample); + void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason); + void Reset(); + + // Returns failure on error, or NS_OK. + // If *aSample is null, EOS has been reached. + nsresult RequestAndWait(nsRefPtr& aSample); + + // Interrupts a call to Wait(). + void Cancel(); + +protected: + ~AudioDecodeRendezvous(); + +private: + nsRefPtr mReader; + Monitor mMonitor; + nsresult mStatus; + nsRefPtr mSample; + bool mHaveResult; +}; + } // namespace mozilla #endif diff --git a/dom/media/MediaPromise.h b/dom/media/MediaPromise.h index 6b225714444b..1e9658ee9157 100644 --- a/dom/media/MediaPromise.h +++ b/dom/media/MediaPromise.h @@ -25,6 +25,7 @@ namespace mozilla { extern PRLogModuleInfo* gMediaPromiseLog; +void EnsureMediaPromiseLog(); #define PROMISE_LOG(x, ...) \ MOZ_ASSERT(gMediaPromiseLog); \ @@ -232,9 +233,8 @@ protected: void DispatchAll() { mMutex.AssertCurrentThreadOwns(); - for (size_t i = 0; i < mThenValues.Length(); ++i) { + for (size_t i = 0; i < mThenValues.Length(); ++i) mThenValues[i]->Dispatch(this); - } mThenValues.Clear(); } diff --git a/dom/media/ogg/OggReader.cpp b/dom/media/ogg/OggReader.cpp index 6d32b6f3056e..a39af4ec90ab 100644 --- a/dom/media/ogg/OggReader.cpp +++ b/dom/media/ogg/OggReader.cpp @@ -976,7 +976,7 @@ bool OggReader::ReadOggPage(ogg_page* aPage) ogg_packet* OggReader::NextOggPacket(OggCodecState* aCodecState) { - MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread."); + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); if (!aCodecState || !aCodecState->mActive) { return nullptr; diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index ee626440f49c..24313d623c85 100644 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -474,7 +474,7 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer, uint8_t* data = static_cast(JS_StealArrayBufferContents(cx, obj)); // Sniff the content of the media. - // Failed type sniffing will be handled by AsyncDecodeWebAudio. + // Failed type sniffing will be handled by AsyncDecodeMedia. nsAutoCString contentType; NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, length, contentType); @@ -489,7 +489,7 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer, nsRefPtr job( new WebAudioDecodeJob(contentType, this, promise, successCallback, failureCallback)); - AsyncDecodeWebAudio(contentType.get(), data, length, *job); + mDecoder.AsyncDecodeMedia(contentType.get(), data, length, *job); // Transfer the ownership to mDecodeJobs mDecodeJobs.AppendElement(job.forget()); @@ -585,6 +585,8 @@ AudioContext::Shutdown() Mute(); } + mDecoder.Shutdown(); + // Release references to active nodes. // Active AudioNodes don't unregister in destructors, at which point the // Node is already unregistered. @@ -699,6 +701,7 @@ AudioContext::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const if (mListener) { amount += mListener->SizeOfIncludingThis(aMallocSizeOf); } + amount += mDecoder.SizeOfExcludingThis(aMallocSizeOf); amount += mDecodeJobs.SizeOfExcludingThis(aMallocSizeOf); for (uint32_t i = 0; i < mDecodeJobs.Length(); ++i) { amount += mDecodeJobs[i]->SizeOfIncludingThis(aMallocSizeOf); diff --git a/dom/media/webaudio/AudioContext.h b/dom/media/webaudio/AudioContext.h index 65e759c66694..2da5fec986ae 100644 --- a/dom/media/webaudio/AudioContext.h +++ b/dom/media/webaudio/AudioContext.h @@ -271,6 +271,7 @@ private: const float mSampleRate; nsRefPtr mDestination; nsRefPtr mListener; + MediaBufferDecoder mDecoder; nsTArray > mDecodeJobs; // See RegisterActiveNode. These will keep the AudioContext alive while it // is rendering and the window remains alive. diff --git a/dom/media/webaudio/BufferDecoder.cpp b/dom/media/webaudio/BufferDecoder.cpp index 07639b2609d4..f230c31e1eb9 100644 --- a/dom/media/webaudio/BufferDecoder.cpp +++ b/dom/media/webaudio/BufferDecoder.cpp @@ -37,10 +37,10 @@ BufferDecoder::~BufferDecoder() } void -BufferDecoder::BeginDecoding(MediaTaskQueue* aTaskQueueIdentity) +BufferDecoder::BeginDecoding(nsIThread* aDecodeThread) { - MOZ_ASSERT(!mTaskQueueIdentity && aTaskQueueIdentity); - mTaskQueueIdentity = aTaskQueueIdentity; + MOZ_ASSERT(!mDecodeThread && aDecodeThread); + mDecodeThread = aDecodeThread; } ReentrantMonitor& @@ -66,8 +66,8 @@ BufferDecoder::OnStateMachineThread() const bool BufferDecoder::OnDecodeThread() const { - MOZ_ASSERT(mTaskQueueIdentity, "Forgot to call BeginDecoding?"); - return mTaskQueueIdentity->IsCurrentThreadIn(); + MOZ_ASSERT(mDecodeThread, "Forgot to call BeginDecoding?"); + return IsCurrentThread(mDecodeThread); } MediaResource* diff --git a/dom/media/webaudio/BufferDecoder.h b/dom/media/webaudio/BufferDecoder.h index 66c27834db4e..1653b6144a6f 100644 --- a/dom/media/webaudio/BufferDecoder.h +++ b/dom/media/webaudio/BufferDecoder.h @@ -8,7 +8,6 @@ #define BUFFER_DECODER_H_ #include "AbstractMediaDecoder.h" -#include "MediaTaskQueue.h" #include "mozilla/Attributes.h" #include "mozilla/ReentrantMonitor.h" @@ -28,7 +27,7 @@ public: NS_DECL_THREADSAFE_ISUPPORTS // This has to be called before decoding begins - void BeginDecoding(MediaTaskQueue* aTaskQueueIdentity); + void BeginDecoding(nsIThread* aDecodeThread); virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE; @@ -84,7 +83,7 @@ private: // It's just there in order for us to be able to override // GetReentrantMonitor correctly. ReentrantMonitor mReentrantMonitor; - nsRefPtr mTaskQueueIdentity; + nsCOMPtr mDecodeThread; nsRefPtr mResource; }; diff --git a/dom/media/webaudio/MediaBufferDecoder.cpp b/dom/media/webaudio/MediaBufferDecoder.cpp index 532cfde13541..95c322157596 100644 --- a/dom/media/webaudio/MediaBufferDecoder.cpp +++ b/dom/media/webaudio/MediaBufferDecoder.cpp @@ -20,9 +20,11 @@ #include "nsIScriptObjectPrincipal.h" #include "nsIScriptError.h" #include "nsMimeTypes.h" -#include "VideoUtils.h" #include "WebAudioUtils.h" #include "mozilla/dom/Promise.h" +#ifdef XP_WIN +#include "ThreadPoolCOMListener.h" +#endif namespace mozilla { @@ -93,12 +95,14 @@ class MediaDecodeTask : public nsRunnable public: MediaDecodeTask(const char* aContentType, uint8_t* aBuffer, uint32_t aLength, - WebAudioDecodeJob& aDecodeJob) + WebAudioDecodeJob& aDecodeJob, + nsIThreadPool* aThreadPool) : mContentType(aContentType) , mBuffer(aBuffer) , mLength(aLength) , mDecodeJob(aDecodeJob) , mPhase(PhaseEnum::Decode) + , mThreadPool(aThreadPool) { MOZ_ASSERT(aBuffer); MOZ_ASSERT(NS_IsMainThread()); @@ -113,7 +117,6 @@ public: NS_IMETHOD Run(); bool CreateReader(); - MediaDecoderReader* Reader() { MOZ_ASSERT(mDecoderReader); return mDecoderReader; } private: void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) { @@ -131,10 +134,6 @@ private: } void Decode(); - void RequestSample(); - void SampleDecoded(AudioData* aData); - void SampleNotDecoded(MediaDecoderReader::NotDecodedReason aReason); - void FinishDecode(); void AllocateBuffer(); void CallbackTheResult(); @@ -155,11 +154,10 @@ private: uint32_t mLength; WebAudioDecodeJob& mDecodeJob; PhaseEnum mPhase; + nsCOMPtr mThreadPool; nsCOMPtr mPrincipal; nsRefPtr mBufferDecoder; nsRefPtr mDecoderReader; - MediaInfo mMediaInfo; - MediaQueue mAudioQueue; }; NS_IMETHODIMP @@ -207,10 +205,6 @@ MediaDecodeTask::CreateReader() return false; } - if (!mDecoderReader->EnsureTaskQueue()) { - return false; - } - return true; } @@ -244,15 +238,16 @@ MediaDecodeTask::Decode() { MOZ_ASSERT(!NS_IsMainThread()); - mBufferDecoder->BeginDecoding(mDecoderReader->GetTaskQueue()); + mBufferDecoder->BeginDecoding(NS_GetCurrentThread()); // Tell the decoder reader that we are not going to play the data directly, // and that we should not reject files with more channels than the audio // bakend support. mDecoderReader->SetIgnoreAudioOutputFormat(); + MediaInfo mediaInfo; nsAutoPtr tags; - nsresult rv = mDecoderReader->ReadMetadata(&mMediaInfo, getter_Transfers(tags)); + nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags)); if (NS_FAILED(rv)) { mDecoderReader->Shutdown(); ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); @@ -265,46 +260,26 @@ MediaDecodeTask::Decode() return; } - RequestSample(); -} - -void -MediaDecodeTask::RequestSample() -{ - mDecoderReader->RequestAudioData()->Then(mDecoderReader->GetTaskQueue(), __func__, this, - &MediaDecodeTask::SampleDecoded, - &MediaDecodeTask::SampleNotDecoded); -} - -void -MediaDecodeTask::SampleDecoded(AudioData* aData) -{ - MOZ_ASSERT(!NS_IsMainThread()); - mAudioQueue.Push(aData); - RequestSample(); -} - -void -MediaDecodeTask::SampleNotDecoded(MediaDecoderReader::NotDecodedReason aReason) -{ - MOZ_ASSERT(!NS_IsMainThread()); - if (aReason == MediaDecoderReader::DECODE_ERROR) { - mDecoderReader->Shutdown(); - ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); - } else { - MOZ_ASSERT(aReason == MediaDecoderReader::END_OF_STREAM); - FinishDecode(); + MediaQueue audioQueue; + nsRefPtr barrier(new AudioDecodeRendezvous(mDecoderReader)); + while (1) { + nsRefPtr audio; + if (NS_FAILED(barrier->RequestAndWait(audio))) { + mDecoderReader->Shutdown(); + ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); + return; + } + if (!audio) { + // End of stream. + break; + } + audioQueue.Push(audio); } -} - -void -MediaDecodeTask::FinishDecode() -{ mDecoderReader->Shutdown(); - uint32_t frameCount = mAudioQueue.FrameCount(); - uint32_t channelCount = mMediaInfo.mAudio.mChannels; - uint32_t sampleRate = mMediaInfo.mAudio.mRate; + uint32_t frameCount = audioQueue.FrameCount(); + uint32_t channelCount = mediaInfo.mAudio.mChannels; + uint32_t sampleRate = mediaInfo.mAudio.mRate; if (!frameCount || !channelCount || !sampleRate) { ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); @@ -352,7 +327,7 @@ MediaDecodeTask::FinishDecode() } nsRefPtr audioData; - while ((audioData = mAudioQueue.PopFront())) { + while ((audioData = audioQueue.PopFront())) { audioData->EnsureAudioBuffer(); // could lead to a copy :( AudioDataValue* bufferData = static_cast (audioData->mAudioBuffer->Data()); @@ -465,8 +440,9 @@ WebAudioDecodeJob::AllocateBuffer() } void -AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, - uint32_t aLength, WebAudioDecodeJob& aDecodeJob) +MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer, + uint32_t aLength, + WebAudioDecodeJob& aDecodeJob) { // Do not attempt to decode the media if we were not successful at sniffing // the content type. @@ -481,8 +457,20 @@ AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, return; } - RefPtr task = - new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob); + if (!EnsureThreadPoolInitialized()) { + nsCOMPtr event = + new ReportResultTask(aDecodeJob, + &WebAudioDecodeJob::OnFailure, + WebAudioDecodeJob::UnknownError); + JS_free(nullptr, aBuffer); + NS_DispatchToMainThread(event); + return; + } + + MOZ_ASSERT(mThreadPool); + + nsRefPtr task = + new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, mThreadPool); if (!task->CreateReader()) { nsCOMPtr event = new ReportResultTask(aDecodeJob, @@ -490,7 +478,37 @@ AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, WebAudioDecodeJob::UnknownError); NS_DispatchToMainThread(event); } else { - task->Reader()->GetTaskQueue()->Dispatch(task); + mThreadPool->Dispatch(task, nsIThreadPool::DISPATCH_NORMAL); + } +} + +bool +MediaBufferDecoder::EnsureThreadPoolInitialized() +{ + if (!mThreadPool) { + mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); + if (!mThreadPool) { + return false; + } + mThreadPool->SetName(NS_LITERAL_CSTRING("MediaBufferDecoder")); +#ifdef XP_WIN + // Ensure MSCOM is initialized on the thread pools threads. + nsCOMPtr listener = new MSCOMInitThreadPoolListener(); + nsresult rv = mThreadPool->SetListener(listener); + NS_ENSURE_SUCCESS(rv, nullptr); +#endif + } + return true; +} + +void +MediaBufferDecoder::Shutdown() { + if (mThreadPool) { + // Setting threadLimit to 0 causes threads to exit when all events have + // been run, like nsIThreadPool::Shutdown(), but doesn't run a nested event + // loop nor wait until this has happened. + mThreadPool->SetThreadLimit(0); + mThreadPool = nullptr; } } diff --git a/dom/media/webaudio/MediaBufferDecoder.h b/dom/media/webaudio/MediaBufferDecoder.h index 3dea273409a9..7fdf2f38011c 100644 --- a/dom/media/webaudio/MediaBufferDecoder.h +++ b/dom/media/webaudio/MediaBufferDecoder.h @@ -9,6 +9,7 @@ #include "nsWrapperCache.h" #include "nsCOMPtr.h" +#include "nsIThreadPool.h" #include "nsString.h" #include "nsTArray.h" #include "mozilla/dom/TypedArray.h" @@ -69,8 +70,32 @@ private: ~WebAudioDecodeJob(); }; -void AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, - uint32_t aLength, WebAudioDecodeJob& aDecodeJob); +/** + * This class is used to decode media buffers on a dedicated threadpool. + * + * This class manages the resources that it uses internally (such as the + * thread-pool) and provides a clean external interface. + */ +class MediaBufferDecoder +{ +public: + void AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer, + uint32_t aLength, WebAudioDecodeJob& aDecodeJob); + + ~MediaBufferDecoder() { Shutdown(); } + void Shutdown(); + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const + { + return 0; + } + +private: + bool EnsureThreadPoolInitialized(); + +private: + nsCOMPtr mThreadPool; +}; }