Backed out changeset ab6ddc8ac28a (bug 1097823)

This commit is contained in:
Bobby Holley 2014-12-07 17:09:06 -08:00
parent 381a1aaa8c
commit a476d023e6
10 changed files with 212 additions and 73 deletions

View File

@ -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<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

View File

@ -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<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
#endif

View File

@ -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();
}

View File

@ -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;

View File

@ -474,7 +474,7 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
uint8_t* data = static_cast<uint8_t*>(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<WebAudioDecodeJob> 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);

View File

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

View File

@ -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*

View File

@ -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<MediaTaskQueue> mTaskQueueIdentity;
nsCOMPtr<nsIThread> mDecodeThread;
nsRefPtr<MediaResource> mResource;
};

View File

@ -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<nsIThreadPool> mThreadPool;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsRefPtr<BufferDecoder> mBufferDecoder;
nsRefPtr<MediaDecoderReader> mDecoderReader;
MediaInfo mMediaInfo;
MediaQueue<AudioData> 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<MetadataTags> 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<AudioData> audioQueue;
nsRefPtr<AudioDecodeRendezvous> barrier(new AudioDecodeRendezvous(mDecoderReader));
while (1) {
nsRefPtr<AudioData> 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> audioData;
while ((audioData = mAudioQueue.PopFront())) {
while ((audioData = audioQueue.PopFront())) {
audioData->EnsureAudioBuffer(); // could lead to a copy :(
AudioDataValue* bufferData = static_cast<AudioDataValue*>
(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<MediaDecodeTask> task =
new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob);
if (!EnsureThreadPoolInitialized()) {
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()) {
nsCOMPtr<nsIRunnable> 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<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;
}
}

View File

@ -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<nsIThreadPool> mThreadPool;
};
}