# ebe5edd8c0ac367c043437a674d4200cf4525757

Bug 848652 - Implement ArrayBuffer version of AudioContext.createBuffer r=ehsan

--HG--
rename : content/media/webaudio/test/test_decodeAudioData.html => content/media/webaudio/test/test_mediaDecoding.html
extra : rebase_source : 7b0926091743dd6cd17a0adee9a6d3c5181dfa4d
This commit is contained in:
James Willcox 2013-05-03 16:42:28 -04:00
parent d67cf0e8c6
commit 9b1a0377f4
7 changed files with 129 additions and 25 deletions

View File

@ -110,6 +110,30 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
return buffer.forget();
}
already_AddRefed<AudioBuffer>
AudioContext::CreateBuffer(JSContext* aJSContext, ArrayBuffer& aBuffer,
bool aMixToMono, ErrorResult& aRv)
{
// TODO: handle aMixToMono
// Sniff the content of the media.
// Failed type sniffing will be handled by SyncDecodeMedia.
nsAutoCString contentType;
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr,
aBuffer.Data(), aBuffer.Length(),
contentType);
WebAudioDecodeJob job(contentType, aBuffer, this);
if (mDecoder.SyncDecodeMedia(contentType.get(),
job.mBuffer, job.mLength, job) &&
job.mOutput) {
return job.mOutput.forget();
}
return nullptr;
}
namespace {
bool IsValidBufferSize(uint32_t aBufferSize) {

View File

@ -98,6 +98,10 @@ public:
uint32_t aLength, float aSampleRate,
ErrorResult& aRv);
already_AddRefed<AudioBuffer>
CreateBuffer(JSContext* aJSContext, ArrayBuffer& aBuffer,
bool aMixToMono, ErrorResult& aRv);
already_AddRefed<ScriptProcessorNode>
CreateScriptProcessor(uint32_t aBufferSize,
uint32_t aNumberOfInputChannels,

View File

@ -337,6 +337,8 @@ private:
}
}
void RunNextPhase();
void Decode();
void AllocateBuffer();
void CopyBuffer();
@ -413,6 +415,35 @@ MediaDecodeTask::CreateReader()
return true;
}
void
MediaDecodeTask::RunNextPhase()
{
// This takes care of handling the logic of where to run the next phase.
// If we were invoked synchronously, we do not have a thread pool and
// everything happens on the main thread. Just invoke Run() in that case.
// Otherwise, some things happen on the main thread and others are run
// in the thread pool.
if (!mThreadPool) {
Run();
return;
}
switch (mPhase) {
case PhaseEnum::AllocateBuffer:
case PhaseEnum::Done:
MOZ_ASSERT(!NS_IsMainThread());
NS_DispatchToMainThread(this);
break;
case PhaseEnum::CopyBuffer:
MOZ_ASSERT(NS_IsMainThread());
mThreadPool->Dispatch(this, nsIThreadPool::DISPATCH_NORMAL);
break;
case PhaseEnum::Decode:
MOZ_NOT_REACHED("Invalid phase Decode");
break;
}
}
void
MediaDecodeTask::Decode()
{
@ -469,7 +500,7 @@ MediaDecodeTask::Decode()
}
mPhase = PhaseEnum::AllocateBuffer;
NS_DispatchToMainThread(this);
RunNextPhase();
}
void
@ -483,13 +514,15 @@ MediaDecodeTask::AllocateBuffer()
}
mPhase = PhaseEnum::CopyBuffer;
mThreadPool->Dispatch(this, nsIThreadPool::DISPATCH_NORMAL);
RunNextPhase();
}
void
MediaDecodeTask::CopyBuffer()
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mThreadPool == NS_IsMainThread(),
"We should be on the main thread only if we don't have a thread pool");
MOZ_ASSERT(mDecodeJob.mOutput);
MOZ_ASSERT(mDecodeJob.mChannels);
MOZ_ASSERT(mDecoderReader);
@ -566,7 +599,7 @@ MediaDecodeTask::CopyBuffer()
}
mPhase = PhaseEnum::Done;
NS_DispatchToMainThread(this);
RunNextPhase();
}
void
@ -680,6 +713,31 @@ MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
}
}
bool
MediaBufferDecoder::SyncDecodeMedia(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.
if (!*aContentType ||
strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0) {
return false;
}
MOZ_ASSERT(!mThreadPool);
nsRefPtr<MediaDecodeTask> task =
new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, nullptr);
if (!task->CreateReader()) {
return false;
}
task->Run();
return true;
}
bool
MediaBufferDecoder::EnsureThreadPoolInitialized()
{
@ -719,9 +777,12 @@ WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType,
, mFailureCallback(aFailureCallback)
{
MOZ_ASSERT(aContext);
MOZ_ASSERT(aSuccessCallback);
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(WebAudioDecodeJob);
MOZ_ASSERT((aSuccessCallback && aFailureCallback) ||
(!aSuccessCallback && !aFailureCallback),
"You cannot pass only one of the success and failure callbacks");
}
WebAudioDecodeJob::~WebAudioDecodeJob()
@ -738,8 +799,10 @@ WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode)
// Ignore errors in calling the callback, since there is not much that we can
// do about it here.
if (mSuccessCallback) {
ErrorResult rv;
mSuccessCallback->Call(*mOutput, rv);
}
mContext->RemoveFromDecodeQueue(this);
}

View File

@ -28,11 +28,13 @@ class DecodeSuccessCallback;
struct WebAudioDecodeJob
{
// You may omit both the success and failure callback, or you must pass both.
// The callbacks are only necessary for asynchronous operation.
WebAudioDecodeJob(const nsACString& aContentType,
const dom::ArrayBuffer& aBuffer,
dom::AudioContext* aContext,
dom::DecodeSuccessCallback* aSuccessCallback,
dom::DecodeErrorCallback* aFailureCallback);
dom::DecodeSuccessCallback* aSuccessCallback = nullptr,
dom::DecodeErrorCallback* aFailureCallback = nullptr);
~WebAudioDecodeJob();
enum ErrorCode {
@ -78,6 +80,9 @@ public:
void AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
bool SyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
void Shutdown();
private:

View File

@ -39,9 +39,9 @@ MOCHITEST_FILES := \
test_currentTime.html \
test_delayNode.html \
test_delayNodeWithGain.html \
test_decodeAudioData.html \
test_dynamicsCompressorNode.html \
test_gainNode.html \
test_mediaDecoding.html \
test_mixingRules.html \
test_nodeToParamConnection.html \
test_pannerNode.html \

View File

@ -209,6 +209,23 @@ function getFuzzTolerance(test) {
return kIsMobile ? test.fuzzToleranceMobile : test.fuzzTolerance;
}
function checkAudioBuffer(buffer, test, callback) {
is(buffer.numberOfChannels, test.numberOfChannels, "Correct number of channels");
ok(Math.abs(buffer.duration - test.duration) < 1e-4, "Correct duration");
is(buffer.sampleRate, cx.sampleRate, "Correct sample rate");
is(buffer.length, test.length, "Correct length");
var wave = createWaveFileData(buffer);
var getExpected = new XMLHttpRequest();
getExpected.open("GET", test.expected, true);
getExpected.responseType = "arraybuffer";
getExpected.onload = function() {
ok(fuzzyMemcmp(wave, new Uint8Array(getExpected.response), getFuzzTolerance(test)), "Received expected decoded data");
callback();
};
getExpected.send();
}
function runTest(test, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", test.url, true);
@ -218,20 +235,11 @@ function runTest(test, callback) {
cx.decodeAudioData(xhr.response, function onSuccess(result) {
ok(expectCallback, "Success callback should fire asynchronously");
ok(test.valid, "Did expect success for test " + test.url);
is(result.numberOfChannels, test.numberOfChannels, "Correct number of channels");
ok(Math.abs(result.duration - test.duration) < 1e-4, "Correct duration");
is(result.sampleRate, cx.sampleRate, "Correct sample rate");
is(result.length, test.length, "Correct length");
var wave = createWaveFileData(result);
var getExpected = new XMLHttpRequest();
getExpected.open("GET", test.expected, true);
getExpected.responseType = "arraybuffer";
getExpected.onload = function() {
ok(fuzzyMemcmp(wave, new Uint8Array(getExpected.response), getFuzzTolerance(test)), "Received expected decoded data");
callback();
};
getExpected.send();
checkAudioBuffer(result, test, function() {
result = cx.createBuffer(xhr.response, false);
checkAudioBuffer(result, test, callback);
});
}, function onFailure() {
ok(expectCallback, "Failure callback should fire asynchronously");
ok(!test.valid, "Did not expect failure for test " + test.url);

View File

@ -24,8 +24,8 @@ interface AudioContext : EventTarget {
[Creator, Throws]
AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long length, float sampleRate);
// [Creator, Throws]
// AudioBuffer createBuffer(ArrayBuffer buffer, boolean mixToMono);
[Creator, Throws]
AudioBuffer? createBuffer(ArrayBuffer buffer, boolean mixToMono);
void decodeAudioData(ArrayBuffer audioData,
DecodeSuccessCallback successCallback,