Backed out changeset ab6ddc8ac28a (bug 1097823)

This commit is contained in:
Bobby Holley 2014-12-07 17:09:06 -08:00
Родитель 5864562a3f
Коммит 60f876f46a
10 изменённых файлов: 212 добавлений и 73 удалений

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

@ -317,4 +317,65 @@ MediaDecoderReader::Shutdown()
mTaskQueue = nullptr; 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<AudioData>& 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<nsIThread> 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 } // namespace mozilla

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

@ -334,6 +334,38 @@ protected:
virtual ~RequestSampleCallback() {} 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<AudioData>& aSample);
// Interrupts a call to Wait().
void Cancel();
protected:
~AudioDecodeRendezvous();
private:
nsRefPtr<MediaDecoderReader> mReader;
Monitor mMonitor;
nsresult mStatus;
nsRefPtr<AudioData> mSample;
bool mHaveResult;
};
} // namespace mozilla } // namespace mozilla
#endif #endif

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

@ -25,6 +25,7 @@
namespace mozilla { namespace mozilla {
extern PRLogModuleInfo* gMediaPromiseLog; extern PRLogModuleInfo* gMediaPromiseLog;
void EnsureMediaPromiseLog();
#define PROMISE_LOG(x, ...) \ #define PROMISE_LOG(x, ...) \
MOZ_ASSERT(gMediaPromiseLog); \ MOZ_ASSERT(gMediaPromiseLog); \
@ -232,9 +233,8 @@ protected:
void DispatchAll() void DispatchAll()
{ {
mMutex.AssertCurrentThreadOwns(); 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[i]->Dispatch(this);
}
mThenValues.Clear(); mThenValues.Clear();
} }

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

@ -976,7 +976,7 @@ bool OggReader::ReadOggPage(ogg_page* aPage)
ogg_packet* OggReader::NextOggPacket(OggCodecState* aCodecState) 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) { if (!aCodecState || !aCodecState->mActive) {
return nullptr; return nullptr;

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

@ -474,7 +474,7 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
uint8_t* data = static_cast<uint8_t*>(JS_StealArrayBufferContents(cx, obj)); uint8_t* data = static_cast<uint8_t*>(JS_StealArrayBufferContents(cx, obj));
// Sniff the content of the media. // Sniff the content of the media.
// Failed type sniffing will be handled by AsyncDecodeWebAudio. // Failed type sniffing will be handled by AsyncDecodeMedia.
nsAutoCString contentType; nsAutoCString contentType;
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, length, contentType); NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, length, contentType);
@ -489,7 +489,7 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
nsRefPtr<WebAudioDecodeJob> job( nsRefPtr<WebAudioDecodeJob> job(
new WebAudioDecodeJob(contentType, this, new WebAudioDecodeJob(contentType, this,
promise, successCallback, failureCallback)); promise, successCallback, failureCallback));
AsyncDecodeWebAudio(contentType.get(), data, length, *job); mDecoder.AsyncDecodeMedia(contentType.get(), data, length, *job);
// Transfer the ownership to mDecodeJobs // Transfer the ownership to mDecodeJobs
mDecodeJobs.AppendElement(job.forget()); mDecodeJobs.AppendElement(job.forget());
@ -585,6 +585,8 @@ AudioContext::Shutdown()
Mute(); Mute();
} }
mDecoder.Shutdown();
// Release references to active nodes. // Release references to active nodes.
// Active AudioNodes don't unregister in destructors, at which point the // Active AudioNodes don't unregister in destructors, at which point the
// Node is already unregistered. // Node is already unregistered.
@ -699,6 +701,7 @@ AudioContext::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
if (mListener) { if (mListener) {
amount += mListener->SizeOfIncludingThis(aMallocSizeOf); amount += mListener->SizeOfIncludingThis(aMallocSizeOf);
} }
amount += mDecoder.SizeOfExcludingThis(aMallocSizeOf);
amount += mDecodeJobs.SizeOfExcludingThis(aMallocSizeOf); amount += mDecodeJobs.SizeOfExcludingThis(aMallocSizeOf);
for (uint32_t i = 0; i < mDecodeJobs.Length(); ++i) { for (uint32_t i = 0; i < mDecodeJobs.Length(); ++i) {
amount += mDecodeJobs[i]->SizeOfIncludingThis(aMallocSizeOf); amount += mDecodeJobs[i]->SizeOfIncludingThis(aMallocSizeOf);

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

@ -271,6 +271,7 @@ private:
const float mSampleRate; const float mSampleRate;
nsRefPtr<AudioDestinationNode> mDestination; nsRefPtr<AudioDestinationNode> mDestination;
nsRefPtr<AudioListener> mListener; nsRefPtr<AudioListener> mListener;
MediaBufferDecoder mDecoder;
nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs; nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs;
// See RegisterActiveNode. These will keep the AudioContext alive while it // See RegisterActiveNode. These will keep the AudioContext alive while it
// is rendering and the window remains alive. // is rendering and the window remains alive.

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

@ -37,10 +37,10 @@ BufferDecoder::~BufferDecoder()
} }
void void
BufferDecoder::BeginDecoding(MediaTaskQueue* aTaskQueueIdentity) BufferDecoder::BeginDecoding(nsIThread* aDecodeThread)
{ {
MOZ_ASSERT(!mTaskQueueIdentity && aTaskQueueIdentity); MOZ_ASSERT(!mDecodeThread && aDecodeThread);
mTaskQueueIdentity = aTaskQueueIdentity; mDecodeThread = aDecodeThread;
} }
ReentrantMonitor& ReentrantMonitor&
@ -66,8 +66,8 @@ BufferDecoder::OnStateMachineThread() const
bool bool
BufferDecoder::OnDecodeThread() const BufferDecoder::OnDecodeThread() const
{ {
MOZ_ASSERT(mTaskQueueIdentity, "Forgot to call BeginDecoding?"); MOZ_ASSERT(mDecodeThread, "Forgot to call BeginDecoding?");
return mTaskQueueIdentity->IsCurrentThreadIn(); return IsCurrentThread(mDecodeThread);
} }
MediaResource* MediaResource*

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

@ -8,7 +8,6 @@
#define BUFFER_DECODER_H_ #define BUFFER_DECODER_H_
#include "AbstractMediaDecoder.h" #include "AbstractMediaDecoder.h"
#include "MediaTaskQueue.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h" #include "mozilla/ReentrantMonitor.h"
@ -28,7 +27,7 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_THREADSAFE_ISUPPORTS
// This has to be called before decoding begins // This has to be called before decoding begins
void BeginDecoding(MediaTaskQueue* aTaskQueueIdentity); void BeginDecoding(nsIThread* aDecodeThread);
virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE; 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 // It's just there in order for us to be able to override
// GetReentrantMonitor correctly. // GetReentrantMonitor correctly.
ReentrantMonitor mReentrantMonitor; ReentrantMonitor mReentrantMonitor;
nsRefPtr<MediaTaskQueue> mTaskQueueIdentity; nsCOMPtr<nsIThread> mDecodeThread;
nsRefPtr<MediaResource> mResource; nsRefPtr<MediaResource> mResource;
}; };

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

@ -20,9 +20,11 @@
#include "nsIScriptObjectPrincipal.h" #include "nsIScriptObjectPrincipal.h"
#include "nsIScriptError.h" #include "nsIScriptError.h"
#include "nsMimeTypes.h" #include "nsMimeTypes.h"
#include "VideoUtils.h"
#include "WebAudioUtils.h" #include "WebAudioUtils.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#ifdef XP_WIN
#include "ThreadPoolCOMListener.h"
#endif
namespace mozilla { namespace mozilla {
@ -93,12 +95,14 @@ class MediaDecodeTask : public nsRunnable
public: public:
MediaDecodeTask(const char* aContentType, uint8_t* aBuffer, MediaDecodeTask(const char* aContentType, uint8_t* aBuffer,
uint32_t aLength, uint32_t aLength,
WebAudioDecodeJob& aDecodeJob) WebAudioDecodeJob& aDecodeJob,
nsIThreadPool* aThreadPool)
: mContentType(aContentType) : mContentType(aContentType)
, mBuffer(aBuffer) , mBuffer(aBuffer)
, mLength(aLength) , mLength(aLength)
, mDecodeJob(aDecodeJob) , mDecodeJob(aDecodeJob)
, mPhase(PhaseEnum::Decode) , mPhase(PhaseEnum::Decode)
, mThreadPool(aThreadPool)
{ {
MOZ_ASSERT(aBuffer); MOZ_ASSERT(aBuffer);
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -113,7 +117,6 @@ public:
NS_IMETHOD Run(); NS_IMETHOD Run();
bool CreateReader(); bool CreateReader();
MediaDecoderReader* Reader() { MOZ_ASSERT(mDecoderReader); return mDecoderReader; }
private: private:
void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) { void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) {
@ -131,10 +134,6 @@ private:
} }
void Decode(); void Decode();
void RequestSample();
void SampleDecoded(AudioData* aData);
void SampleNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
void FinishDecode();
void AllocateBuffer(); void AllocateBuffer();
void CallbackTheResult(); void CallbackTheResult();
@ -155,11 +154,10 @@ private:
uint32_t mLength; uint32_t mLength;
WebAudioDecodeJob& mDecodeJob; WebAudioDecodeJob& mDecodeJob;
PhaseEnum mPhase; PhaseEnum mPhase;
nsCOMPtr<nsIThreadPool> mThreadPool;
nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIPrincipal> mPrincipal;
nsRefPtr<BufferDecoder> mBufferDecoder; nsRefPtr<BufferDecoder> mBufferDecoder;
nsRefPtr<MediaDecoderReader> mDecoderReader; nsRefPtr<MediaDecoderReader> mDecoderReader;
MediaInfo mMediaInfo;
MediaQueue<AudioData> mAudioQueue;
}; };
NS_IMETHODIMP NS_IMETHODIMP
@ -207,10 +205,6 @@ MediaDecodeTask::CreateReader()
return false; return false;
} }
if (!mDecoderReader->EnsureTaskQueue()) {
return false;
}
return true; return true;
} }
@ -244,15 +238,16 @@ MediaDecodeTask::Decode()
{ {
MOZ_ASSERT(!NS_IsMainThread()); 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, // 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 // and that we should not reject files with more channels than the audio
// bakend support. // bakend support.
mDecoderReader->SetIgnoreAudioOutputFormat(); mDecoderReader->SetIgnoreAudioOutputFormat();
MediaInfo mediaInfo;
nsAutoPtr<MetadataTags> tags; nsAutoPtr<MetadataTags> tags;
nsresult rv = mDecoderReader->ReadMetadata(&mMediaInfo, getter_Transfers(tags)); nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags));
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
mDecoderReader->Shutdown(); mDecoderReader->Shutdown();
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
@ -265,46 +260,26 @@ MediaDecodeTask::Decode()
return; return;
} }
RequestSample(); MediaQueue<AudioData> audioQueue;
} nsRefPtr<AudioDecodeRendezvous> barrier(new AudioDecodeRendezvous(mDecoderReader));
while (1) {
void nsRefPtr<AudioData> audio;
MediaDecodeTask::RequestSample() if (NS_FAILED(barrier->RequestAndWait(audio))) {
{ mDecoderReader->Shutdown();
mDecoderReader->RequestAudioData()->Then(mDecoderReader->GetTaskQueue(), __func__, this, ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
&MediaDecodeTask::SampleDecoded, return;
&MediaDecodeTask::SampleNotDecoded); }
} if (!audio) {
// End of stream.
void break;
MediaDecodeTask::SampleDecoded(AudioData* aData) }
{ audioQueue.Push(audio);
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();
} }
}
void
MediaDecodeTask::FinishDecode()
{
mDecoderReader->Shutdown(); mDecoderReader->Shutdown();
uint32_t frameCount = mAudioQueue.FrameCount(); uint32_t frameCount = audioQueue.FrameCount();
uint32_t channelCount = mMediaInfo.mAudio.mChannels; uint32_t channelCount = mediaInfo.mAudio.mChannels;
uint32_t sampleRate = mMediaInfo.mAudio.mRate; uint32_t sampleRate = mediaInfo.mAudio.mRate;
if (!frameCount || !channelCount || !sampleRate) { if (!frameCount || !channelCount || !sampleRate) {
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
@ -352,7 +327,7 @@ MediaDecodeTask::FinishDecode()
} }
nsRefPtr<AudioData> audioData; nsRefPtr<AudioData> audioData;
while ((audioData = mAudioQueue.PopFront())) { while ((audioData = audioQueue.PopFront())) {
audioData->EnsureAudioBuffer(); // could lead to a copy :( audioData->EnsureAudioBuffer(); // could lead to a copy :(
AudioDataValue* bufferData = static_cast<AudioDataValue*> AudioDataValue* bufferData = static_cast<AudioDataValue*>
(audioData->mAudioBuffer->Data()); (audioData->mAudioBuffer->Data());
@ -465,8 +440,9 @@ WebAudioDecodeJob::AllocateBuffer()
} }
void void
AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
uint32_t aLength, WebAudioDecodeJob& aDecodeJob) uint32_t aLength,
WebAudioDecodeJob& aDecodeJob)
{ {
// Do not attempt to decode the media if we were not successful at sniffing // Do not attempt to decode the media if we were not successful at sniffing
// the content type. // the content type.
@ -481,8 +457,20 @@ AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
return; return;
} }
RefPtr<MediaDecodeTask> task = if (!EnsureThreadPoolInitialized()) {
new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob); nsCOMPtr<nsIRunnable> event =
new ReportResultTask(aDecodeJob,
&WebAudioDecodeJob::OnFailure,
WebAudioDecodeJob::UnknownError);
JS_free(nullptr, aBuffer);
NS_DispatchToMainThread(event);
return;
}
MOZ_ASSERT(mThreadPool);
nsRefPtr<MediaDecodeTask> task =
new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, mThreadPool);
if (!task->CreateReader()) { if (!task->CreateReader()) {
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
new ReportResultTask(aDecodeJob, new ReportResultTask(aDecodeJob,
@ -490,7 +478,37 @@ AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
WebAudioDecodeJob::UnknownError); WebAudioDecodeJob::UnknownError);
NS_DispatchToMainThread(event); NS_DispatchToMainThread(event);
} else { } 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<nsIThreadPoolListener> 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;
} }
} }

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

@ -9,6 +9,7 @@
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIThreadPool.h"
#include "nsString.h" #include "nsString.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "mozilla/dom/TypedArray.h" #include "mozilla/dom/TypedArray.h"
@ -69,8 +70,32 @@ private:
~WebAudioDecodeJob(); ~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<nsIThreadPool> mThreadPool;
};
} }