зеркало из https://github.com/mozilla/gecko-dev.git
# 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:
Родитель
950ccdcdff
Коммит
fd7dbdb682
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
ErrorResult rv;
|
||||
mSuccessCallback->Call(*mOutput, rv);
|
||||
if (mSuccessCallback) {
|
||||
ErrorResult rv;
|
||||
mSuccessCallback->Call(*mOutput, rv);
|
||||
}
|
||||
|
||||
mContext->RemoveFromDecodeQueue(this);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче