2013-02-02 02:13:23 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "MediaBufferDecoder.h"
|
2013-08-15 23:44:14 +04:00
|
|
|
#include "mozilla/dom/AudioContextBinding.h"
|
2016-12-21 12:52:50 +03:00
|
|
|
#include "mozilla/dom/BaseAudioContextBinding.h"
|
2016-12-13 09:00:15 +03:00
|
|
|
#include "mozilla/dom/DOMException.h"
|
2014-05-09 22:51:56 +04:00
|
|
|
#include "mozilla/dom/ScriptSettings.h"
|
2016-11-29 08:03:36 +03:00
|
|
|
#include "mozilla/AbstractThread.h"
|
2013-02-02 02:13:23 +04:00
|
|
|
#include <speex/speex_resampler.h>
|
|
|
|
#include "nsXPCOMCIDInternal.h"
|
|
|
|
#include "nsComponentManagerUtils.h"
|
2017-07-19 12:01:32 +03:00
|
|
|
#include "MediaFormatReader.h"
|
2017-07-20 04:56:08 +03:00
|
|
|
#include "MediaQueue.h"
|
2013-02-02 02:13:23 +04:00
|
|
|
#include "BufferMediaResource.h"
|
|
|
|
#include "DecoderTraits.h"
|
|
|
|
#include "AudioContext.h"
|
|
|
|
#include "AudioBuffer.h"
|
2018-11-22 05:11:15 +03:00
|
|
|
#include "js/MemoryFunctions.h"
|
2017-01-18 03:59:03 +03:00
|
|
|
#include "MediaContainerType.h"
|
2015-10-26 19:14:47 +03:00
|
|
|
#include "nsContentUtils.h"
|
2013-02-02 02:13:23 +04:00
|
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
|
|
#include "nsIScriptError.h"
|
|
|
|
#include "nsMimeTypes.h"
|
2014-12-11 01:03:56 +03:00
|
|
|
#include "VideoUtils.h"
|
2013-10-01 10:22:57 +04:00
|
|
|
#include "WebAudioUtils.h"
|
2014-10-23 14:07:48 +04:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2015-11-25 02:52:48 +03:00
|
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#include "nsPrintfCString.h"
|
2017-08-08 06:38:02 +03:00
|
|
|
#include "AudioNodeEngine.h"
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2015-11-25 02:52:48 +03:00
|
|
|
extern LazyLogModule gMediaDecoderLog;
|
|
|
|
|
2013-02-12 01:56:57 +04:00
|
|
|
using namespace dom;
|
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
class ReportResultTask final : public Runnable {
|
2013-02-02 02:13:23 +04:00
|
|
|
public:
|
|
|
|
ReportResultTask(WebAudioDecodeJob& aDecodeJob,
|
|
|
|
WebAudioDecodeJob::ResultFn aFunction,
|
|
|
|
WebAudioDecodeJob::ErrorCode aErrorCode)
|
2017-06-12 22:34:10 +03:00
|
|
|
: Runnable("ReportResultTask"),
|
|
|
|
mDecodeJob(aDecodeJob),
|
2013-02-02 02:13:23 +04:00
|
|
|
mFunction(aFunction),
|
|
|
|
mErrorCode(aErrorCode) {
|
|
|
|
MOZ_ASSERT(aFunction);
|
|
|
|
}
|
|
|
|
|
2016-08-08 05:18:10 +03:00
|
|
|
NS_IMETHOD Run() override {
|
2013-02-02 02:13:23 +04:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
(mDecodeJob.*mFunction)(mErrorCode);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Note that the mDecodeJob member will probably die when mFunction is run.
|
|
|
|
// Therefore, it is not safe to do anything fancy with it in this class.
|
|
|
|
// Really, this class is only used because nsRunnableMethod doesn't support
|
|
|
|
// methods accepting arguments.
|
|
|
|
WebAudioDecodeJob& mDecodeJob;
|
|
|
|
WebAudioDecodeJob::ResultFn mFunction;
|
|
|
|
WebAudioDecodeJob::ErrorCode mErrorCode;
|
|
|
|
};
|
|
|
|
|
2015-04-28 09:42:00 +03:00
|
|
|
enum class PhaseEnum : int { Decode, AllocateBuffer, Done };
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
class MediaDecodeTask final : public Runnable {
|
2013-02-02 02:13:23 +04:00
|
|
|
public:
|
2017-06-12 22:34:10 +03:00
|
|
|
MediaDecodeTask(const MediaContainerType& aContainerType, uint8_t* aBuffer,
|
2014-12-11 01:03:56 +03:00
|
|
|
uint32_t aLength, WebAudioDecodeJob& aDecodeJob)
|
2017-06-12 22:34:10 +03:00
|
|
|
: Runnable("MediaDecodeTask"),
|
|
|
|
mContainerType(aContainerType),
|
2013-02-02 02:13:23 +04:00
|
|
|
mBuffer(aBuffer),
|
|
|
|
mLength(aLength),
|
|
|
|
mDecodeJob(aDecodeJob),
|
|
|
|
mPhase(PhaseEnum::Decode),
|
2015-05-18 09:13:20 +03:00
|
|
|
mFirstFrameDecoded(false) {
|
2013-02-02 02:13:23 +04:00
|
|
|
MOZ_ASSERT(aBuffer);
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
}
|
|
|
|
|
2017-11-06 06:37:28 +03:00
|
|
|
NS_IMETHOD Run() override;
|
2013-02-02 02:13:23 +04:00
|
|
|
bool CreateReader();
|
2017-07-19 12:01:32 +03:00
|
|
|
MediaFormatReader* Reader() {
|
|
|
|
MOZ_ASSERT(mDecoderReader);
|
|
|
|
return mDecoderReader;
|
|
|
|
}
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
private:
|
|
|
|
void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) {
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
Cleanup();
|
|
|
|
mDecodeJob.OnFailure(aErrorCode);
|
|
|
|
} else {
|
|
|
|
// Take extra care to cleanup on the main thread
|
2017-06-12 22:34:10 +03:00
|
|
|
mMainThread->Dispatch(NewRunnableMethod("MediaDecodeTask::Cleanup", this,
|
|
|
|
&MediaDecodeTask::Cleanup));
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIRunnable> event = new ReportResultTask(
|
|
|
|
mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode);
|
2017-04-04 14:47:47 +03:00
|
|
|
mMainThread->Dispatch(event.forget());
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Decode();
|
2017-05-18 05:20:44 +03:00
|
|
|
void OnMetadataRead(MetadataHolder&& aMetadata);
|
2016-09-10 12:56:50 +03:00
|
|
|
void OnMetadataNotRead(const MediaResult& aError);
|
2014-12-11 01:03:56 +03:00
|
|
|
void RequestSample();
|
2017-05-11 03:30:39 +03:00
|
|
|
void SampleDecoded(RefPtr<AudioData> aData);
|
2016-09-10 09:48:53 +03:00
|
|
|
void SampleNotDecoded(const MediaResult& aError);
|
2014-12-11 01:03:56 +03:00
|
|
|
void FinishDecode();
|
2013-02-02 02:13:23 +04:00
|
|
|
void AllocateBuffer();
|
|
|
|
void CallbackTheResult();
|
|
|
|
|
|
|
|
void Cleanup() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mDecoderReader = nullptr;
|
2014-05-13 16:09:44 +04:00
|
|
|
JS_free(nullptr, mBuffer);
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2017-01-18 03:59:03 +03:00
|
|
|
MediaContainerType mContainerType;
|
2013-02-02 02:13:23 +04:00
|
|
|
uint8_t* mBuffer;
|
|
|
|
uint32_t mLength;
|
|
|
|
WebAudioDecodeJob& mDecodeJob;
|
|
|
|
PhaseEnum mPhase;
|
2017-07-19 12:01:32 +03:00
|
|
|
RefPtr<MediaFormatReader> mDecoderReader;
|
2014-12-11 01:03:56 +03:00
|
|
|
MediaInfo mMediaInfo;
|
2017-03-22 06:25:23 +03:00
|
|
|
MediaQueue<AudioData> mAudioQueue;
|
2017-04-04 14:47:47 +03:00
|
|
|
RefPtr<AbstractThread> mMainThread;
|
2015-05-18 09:13:20 +03:00
|
|
|
bool mFirstFrameDecoded;
|
2013-02-02 02:13:23 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
MediaDecodeTask::Run() {
|
|
|
|
MOZ_ASSERT(mDecoderReader);
|
|
|
|
switch (mPhase) {
|
|
|
|
case PhaseEnum::Decode:
|
|
|
|
Decode();
|
|
|
|
break;
|
|
|
|
case PhaseEnum::AllocateBuffer:
|
|
|
|
AllocateBuffer();
|
|
|
|
break;
|
|
|
|
case PhaseEnum::Done:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MediaDecodeTask::CreateReader() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<BufferMediaResource> resource =
|
2017-08-24 12:04:59 +03:00
|
|
|
new BufferMediaResource(static_cast<uint8_t*>(mBuffer), mLength);
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2016-11-29 08:03:36 +03:00
|
|
|
mMainThread = mDecodeJob.mContext->GetOwnerGlobal()->AbstractMainThreadFor(
|
|
|
|
TaskCategory::Other);
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
// If you change this list to add support for new decoders, please consider
|
2013-03-19 16:23:54 +04:00
|
|
|
// updating HTMLMediaElement::CreateDecoder as well.
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2017-07-13 10:13:12 +03:00
|
|
|
MediaFormatReaderInit init;
|
2017-07-06 11:59:22 +03:00
|
|
|
init.mResource = resource;
|
|
|
|
mDecoderReader = DecoderTraits::CreateReader(mContainerType, init);
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
if (!mDecoderReader) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-10-13 10:28:57 +03:00
|
|
|
nsresult rv = mDecoderReader->Init();
|
2013-02-02 02:13:23 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-28 09:42:00 +03:00
|
|
|
class AutoResampler final {
|
2013-05-13 08:17:00 +04:00
|
|
|
public:
|
|
|
|
AutoResampler() : mResampler(nullptr) {}
|
|
|
|
~AutoResampler() {
|
|
|
|
if (mResampler) {
|
|
|
|
speex_resampler_destroy(mResampler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
operator SpeexResamplerState*() const {
|
|
|
|
MOZ_ASSERT(mResampler);
|
|
|
|
return mResampler;
|
|
|
|
}
|
|
|
|
void operator=(SpeexResamplerState* aResampler) { mResampler = aResampler; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
SpeexResamplerState* mResampler;
|
|
|
|
};
|
|
|
|
|
2013-02-02 02:13:23 +04:00
|
|
|
void MediaDecodeTask::Decode() {
|
2014-04-23 16:56:42 +04:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2015-07-16 21:31:21 +03:00
|
|
|
mDecoderReader->AsyncReadMetadata()->Then(
|
|
|
|
mDecoderReader->OwnerThread(), __func__, this,
|
2015-05-18 09:13:20 +03:00
|
|
|
&MediaDecodeTask::OnMetadataRead, &MediaDecodeTask::OnMetadataNotRead);
|
|
|
|
}
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2017-05-18 05:20:44 +03:00
|
|
|
void MediaDecodeTask::OnMetadataRead(MetadataHolder&& aMetadata) {
|
|
|
|
mMediaInfo = *aMetadata.mInfo;
|
2015-05-18 09:13:20 +03:00
|
|
|
if (!mMediaInfo.HasAudio()) {
|
2014-11-25 04:09:19 +03:00
|
|
|
mDecoderReader->Shutdown();
|
2013-02-02 02:13:23 +04:00
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio);
|
|
|
|
return;
|
|
|
|
}
|
2015-11-25 02:52:48 +03:00
|
|
|
|
|
|
|
nsCString codec;
|
|
|
|
if (!mMediaInfo.mAudio.GetAsAudioInfo()->mMimeType.IsEmpty()) {
|
|
|
|
codec = nsPrintfCString(
|
|
|
|
"webaudio; %s", mMediaInfo.mAudio.GetAsAudioInfo()->mMimeType.get());
|
|
|
|
} else {
|
2016-12-22 03:57:48 +03:00
|
|
|
codec = nsPrintfCString("webaudio;resource; %s",
|
2017-01-18 03:59:03 +03:00
|
|
|
mContainerType.Type().AsString().Data());
|
2015-11-25 02:52:48 +03:00
|
|
|
}
|
|
|
|
|
2017-06-12 22:34:10 +03:00
|
|
|
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
|
|
|
|
"MediaDecodeTask::OnMetadataRead", [codec]() -> void {
|
|
|
|
MOZ_ASSERT(!codec.IsEmpty());
|
|
|
|
MOZ_LOG(gMediaDecoderLog, LogLevel::Debug,
|
|
|
|
("Telemetry (WebAudio) MEDIA_CODEC_USED= '%s'", codec.get()));
|
|
|
|
Telemetry::Accumulate(Telemetry::HistogramID::MEDIA_CODEC_USED, codec);
|
|
|
|
});
|
2017-07-26 11:13:35 +03:00
|
|
|
SystemGroup::Dispatch(TaskCategory::Other, task.forget());
|
2015-11-25 02:52:48 +03:00
|
|
|
|
2014-12-11 01:03:56 +03:00
|
|
|
RequestSample();
|
|
|
|
}
|
|
|
|
|
2016-09-10 12:56:50 +03:00
|
|
|
void MediaDecodeTask::OnMetadataNotRead(const MediaResult& aReason) {
|
2015-05-18 09:13:20 +03:00
|
|
|
mDecoderReader->Shutdown();
|
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
|
|
|
|
}
|
|
|
|
|
2014-12-11 01:03:56 +03:00
|
|
|
void MediaDecodeTask::RequestSample() {
|
2015-07-16 21:31:21 +03:00
|
|
|
mDecoderReader->RequestAudioData()->Then(
|
|
|
|
mDecoderReader->OwnerThread(), __func__, this,
|
2014-12-11 01:03:56 +03:00
|
|
|
&MediaDecodeTask::SampleDecoded, &MediaDecodeTask::SampleNotDecoded);
|
|
|
|
}
|
|
|
|
|
2017-05-11 03:30:39 +03:00
|
|
|
void MediaDecodeTask::SampleDecoded(RefPtr<AudioData> aData) {
|
2014-12-11 01:03:56 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
mAudioQueue.Push(aData);
|
2015-05-18 09:13:20 +03:00
|
|
|
if (!mFirstFrameDecoded) {
|
|
|
|
mDecoderReader->ReadUpdatedMetadata(&mMediaInfo);
|
|
|
|
mFirstFrameDecoded = true;
|
|
|
|
}
|
2014-12-11 01:03:56 +03:00
|
|
|
RequestSample();
|
|
|
|
}
|
|
|
|
|
2016-09-10 09:48:53 +03:00
|
|
|
void MediaDecodeTask::SampleNotDecoded(const MediaResult& aError) {
|
2014-12-11 01:03:56 +03:00
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
2016-09-10 09:48:53 +03:00
|
|
|
if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
|
|
|
|
FinishDecode();
|
|
|
|
} else {
|
2014-12-11 01:03:56 +03:00
|
|
|
mDecoderReader->Shutdown();
|
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
2014-12-11 01:03:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void MediaDecodeTask::FinishDecode() {
|
2014-06-18 09:07:02 +04:00
|
|
|
mDecoderReader->Shutdown();
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2014-12-11 01:03:56 +03:00
|
|
|
uint32_t frameCount = mAudioQueue.FrameCount();
|
|
|
|
uint32_t channelCount = mMediaInfo.mAudio.mChannels;
|
|
|
|
uint32_t sampleRate = mMediaInfo.mAudio.mRate;
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
if (!frameCount || !channelCount || !sampleRate) {
|
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate();
|
2013-05-13 08:17:00 +04:00
|
|
|
AutoResampler resampler;
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
uint32_t resampledFrames = frameCount;
|
|
|
|
if (sampleRate != destSampleRate) {
|
|
|
|
resampledFrames = static_cast<uint32_t>(
|
2013-02-02 02:13:23 +04:00
|
|
|
static_cast<uint64_t>(destSampleRate) *
|
|
|
|
static_cast<uint64_t>(frameCount) / static_cast<uint64_t>(sampleRate));
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
resampler = speex_resampler_init(channelCount, sampleRate, destSampleRate,
|
2013-02-02 02:13:23 +04:00
|
|
|
SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr);
|
2013-05-13 08:17:00 +04:00
|
|
|
speex_resampler_skip_zeros(resampler);
|
|
|
|
resampledFrames += speex_resampler_get_output_latency(resampler);
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
2017-08-17 04:28:25 +03:00
|
|
|
// Allocate contiguous channel buffers. Note that if we end up resampling,
|
|
|
|
// we may write fewer bytes than mResampledFrames to the output buffer, in
|
|
|
|
// which case writeIndex will tell us how many valid samples we have.
|
|
|
|
mDecodeJob.mBuffer.mChannelData.SetLength(channelCount);
|
|
|
|
#if AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_FLOAT32
|
|
|
|
// This buffer has separate channel arrays that could be transferred to
|
|
|
|
// JS_NewArrayBufferWithContents(), but AudioBuffer::RestoreJSChannelData()
|
|
|
|
// does not yet take advantage of this.
|
2017-08-16 09:10:06 +03:00
|
|
|
RefPtr<ThreadSharedFloatArrayBufferList> buffer =
|
2015-08-28 02:15:39 +03:00
|
|
|
ThreadSharedFloatArrayBufferList::Create(channelCount, resampledFrames,
|
|
|
|
fallible);
|
2017-08-16 09:10:06 +03:00
|
|
|
if (!buffer) {
|
2013-05-13 08:17:00 +04:00
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
2017-08-16 09:10:06 +03:00
|
|
|
for (uint32_t i = 0; i < channelCount; ++i) {
|
|
|
|
mDecodeJob.mBuffer.mChannelData[i] = buffer->GetData(i);
|
|
|
|
}
|
2017-08-17 04:28:25 +03:00
|
|
|
#else
|
|
|
|
RefPtr<SharedBuffer> buffer = SharedBuffer::Create(
|
|
|
|
sizeof(AudioDataValue) * resampledFrames * channelCount);
|
|
|
|
if (!buffer) {
|
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto data = static_cast<AudioDataValue*>(floatBuffer->Data());
|
|
|
|
for (uint32_t i = 0; i < channelCount; ++i) {
|
|
|
|
mDecodeJob.mBuffer.mChannelData[i] = data;
|
|
|
|
data += resampledFrames;
|
|
|
|
}
|
|
|
|
#endif
|
2017-08-16 09:10:06 +03:00
|
|
|
mDecodeJob.mBuffer.mBuffer = buffer.forget();
|
|
|
|
mDecodeJob.mBuffer.mVolume = 1.0f;
|
2017-08-17 04:28:25 +03:00
|
|
|
mDecodeJob.mBuffer.mBufferFormat = AUDIO_OUTPUT_FORMAT;
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2017-08-16 09:10:06 +03:00
|
|
|
uint32_t writeIndex = 0;
|
2017-03-22 06:25:23 +03:00
|
|
|
RefPtr<AudioData> audioData;
|
|
|
|
while ((audioData = mAudioQueue.PopFront())) {
|
2013-02-02 02:13:23 +04:00
|
|
|
audioData->EnsureAudioBuffer(); // could lead to a copy :(
|
2017-08-17 04:28:25 +03:00
|
|
|
const AudioDataValue* bufferData =
|
2013-02-02 02:13:23 +04:00
|
|
|
static_cast<AudioDataValue*>(audioData->mAudioBuffer->Data());
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
if (sampleRate != destSampleRate) {
|
2017-08-16 09:10:06 +03:00
|
|
|
const uint32_t maxOutSamples = resampledFrames - writeIndex;
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < audioData->mChannels; ++i) {
|
|
|
|
uint32_t inSamples = audioData->mFrames;
|
2013-12-03 03:09:01 +04:00
|
|
|
uint32_t outSamples = maxOutSamples;
|
2017-08-17 04:28:25 +03:00
|
|
|
AudioDataValue* outData =
|
|
|
|
mDecodeJob.mBuffer.ChannelDataForWrite<AudioDataValue>(i) +
|
|
|
|
writeIndex;
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2013-10-01 10:22:57 +04:00
|
|
|
WebAudioUtils::SpeexResamplerProcess(
|
|
|
|
resampler, i, &bufferData[i * audioData->mFrames], &inSamples,
|
2015-08-28 02:15:39 +03:00
|
|
|
outData, &outSamples);
|
2013-05-13 08:17:00 +04:00
|
|
|
|
|
|
|
if (i == audioData->mChannels - 1) {
|
2017-08-16 09:10:06 +03:00
|
|
|
writeIndex += outSamples;
|
|
|
|
MOZ_ASSERT(writeIndex <= resampledFrames);
|
2013-12-03 03:09:01 +04:00
|
|
|
MOZ_ASSERT(inSamples == audioData->mFrames);
|
2013-05-13 08:17:00 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (uint32_t i = 0; i < audioData->mChannels; ++i) {
|
2017-08-17 04:28:25 +03:00
|
|
|
AudioDataValue* outData =
|
|
|
|
mDecodeJob.mBuffer.ChannelDataForWrite<AudioDataValue>(i) +
|
|
|
|
writeIndex;
|
|
|
|
PodCopy(outData, &bufferData[i * audioData->mFrames],
|
|
|
|
audioData->mFrames);
|
2013-05-13 08:17:00 +04:00
|
|
|
|
|
|
|
if (i == audioData->mChannels - 1) {
|
2017-08-16 09:10:06 +03:00
|
|
|
writeIndex += audioData->mFrames;
|
2013-05-13 08:17:00 +04:00
|
|
|
}
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
}
|
2013-05-13 08:17:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sampleRate != destSampleRate) {
|
2013-12-03 03:09:01 +04:00
|
|
|
uint32_t inputLatency = speex_resampler_get_input_latency(resampler);
|
2017-08-16 09:10:06 +03:00
|
|
|
const uint32_t maxOutSamples = resampledFrames - writeIndex;
|
2013-05-13 08:17:00 +04:00
|
|
|
for (uint32_t i = 0; i < channelCount; ++i) {
|
|
|
|
uint32_t inSamples = inputLatency;
|
2013-12-03 03:09:01 +04:00
|
|
|
uint32_t outSamples = maxOutSamples;
|
2017-08-17 04:28:25 +03:00
|
|
|
AudioDataValue* outData =
|
|
|
|
mDecodeJob.mBuffer.ChannelDataForWrite<AudioDataValue>(i) +
|
|
|
|
writeIndex;
|
2013-05-13 08:17:00 +04:00
|
|
|
|
2013-10-01 10:22:57 +04:00
|
|
|
WebAudioUtils::SpeexResamplerProcess(resampler, i,
|
2013-12-03 03:07:17 +04:00
|
|
|
(AudioDataValue*)nullptr, &inSamples,
|
2015-08-28 02:15:39 +03:00
|
|
|
outData, &outSamples);
|
2013-05-13 08:17:00 +04:00
|
|
|
|
|
|
|
if (i == channelCount - 1) {
|
2017-08-16 09:10:06 +03:00
|
|
|
writeIndex += outSamples;
|
|
|
|
MOZ_ASSERT(writeIndex <= resampledFrames);
|
2013-12-03 03:09:01 +04:00
|
|
|
MOZ_ASSERT(inSamples == inputLatency);
|
2013-05-13 08:17:00 +04:00
|
|
|
}
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-16 09:10:06 +03:00
|
|
|
mDecodeJob.mBuffer.mDuration = writeIndex;
|
2013-05-13 08:17:00 +04:00
|
|
|
mPhase = PhaseEnum::AllocateBuffer;
|
2017-04-04 14:47:47 +03:00
|
|
|
mMainThread->Dispatch(do_AddRef(this));
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
void MediaDecodeTask::AllocateBuffer() {
|
2013-02-02 02:13:23 +04:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
if (!mDecodeJob.AllocateBuffer()) {
|
2013-02-02 02:13:23 +04:00
|
|
|
ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
|
2013-05-13 08:17:00 +04:00
|
|
|
return;
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
mPhase = PhaseEnum::Done;
|
|
|
|
CallbackTheResult();
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
void MediaDecodeTask::CallbackTheResult() {
|
2013-02-02 02:13:23 +04:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
Cleanup();
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2013-05-13 08:17:00 +04:00
|
|
|
// Now, we're ready to call the script back with the resulting buffer
|
|
|
|
mDecodeJob.OnSuccess(WebAudioDecodeJob::NoError);
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool WebAudioDecodeJob::AllocateBuffer() {
|
|
|
|
MOZ_ASSERT(!mOutput);
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
// Now create the AudioBuffer
|
2017-08-16 09:10:06 +03:00
|
|
|
mOutput = AudioBuffer::Create(mContext->GetOwner(), mContext->SampleRate(),
|
2018-05-30 22:15:35 +03:00
|
|
|
std::move(mBuffer));
|
2017-08-16 09:10:06 +03:00
|
|
|
return mOutput != nullptr;
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
2014-12-11 01:03:56 +03:00
|
|
|
void AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
|
|
|
|
uint32_t aLength, WebAudioDecodeJob& aDecodeJob) {
|
2017-01-18 03:59:03 +03:00
|
|
|
Maybe<MediaContainerType> containerType =
|
|
|
|
MakeMediaContainerType(aContentType);
|
2013-02-02 02:13:23 +04:00
|
|
|
// Do not attempt to decode the media if we were not successful at sniffing
|
2017-01-18 03:59:03 +03:00
|
|
|
// the container type.
|
2016-12-22 03:57:48 +03:00
|
|
|
if (!*aContentType || strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0 ||
|
2017-01-18 03:59:03 +03:00
|
|
|
!containerType) {
|
2013-02-02 02:13:23 +04:00
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
|
|
new ReportResultTask(aDecodeJob, &WebAudioDecodeJob::OnFailure,
|
|
|
|
WebAudioDecodeJob::UnknownContent);
|
2014-06-22 22:10:08 +04:00
|
|
|
JS_free(nullptr, aBuffer);
|
2017-04-04 14:47:47 +03:00
|
|
|
aDecodeJob.mContext->Dispatch(event.forget());
|
2013-02-02 02:13:23 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MediaDecodeTask> task =
|
2017-01-18 03:59:03 +03:00
|
|
|
new MediaDecodeTask(*containerType, aBuffer, aLength, aDecodeJob);
|
2013-02-02 02:13:23 +04:00
|
|
|
if (!task->CreateReader()) {
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
|
|
new ReportResultTask(aDecodeJob, &WebAudioDecodeJob::OnFailure,
|
|
|
|
WebAudioDecodeJob::UnknownError);
|
2017-04-04 14:47:47 +03:00
|
|
|
aDecodeJob.mContext->Dispatch(event.forget());
|
2013-02-02 02:13:23 +04:00
|
|
|
} else {
|
2015-05-01 16:14:16 +03:00
|
|
|
// If we did this without a temporary:
|
2015-07-16 21:31:21 +03:00
|
|
|
// task->Reader()->OwnerThread()->Dispatch(task.forget())
|
2015-05-01 16:14:16 +03:00
|
|
|
// we might evaluate the task.forget() before calling Reader(). Enforce
|
|
|
|
// a non-crashy order-of-operations.
|
2015-07-16 21:13:49 +03:00
|
|
|
TaskQueue* taskQueue = task->Reader()->OwnerThread();
|
2017-11-15 09:58:03 +03:00
|
|
|
nsresult rv = taskQueue->Dispatch(task.forget());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
|
2017-12-21 07:12:42 +03:00
|
|
|
Unused << rv;
|
2014-06-18 09:24:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-29 17:02:43 +03:00
|
|
|
WebAudioDecodeJob::WebAudioDecodeJob(AudioContext* aContext, Promise* aPromise,
|
2013-02-02 02:13:23 +04:00
|
|
|
DecodeSuccessCallback* aSuccessCallback,
|
|
|
|
DecodeErrorCallback* aFailureCallback)
|
2017-09-29 17:02:43 +03:00
|
|
|
: mContext(aContext),
|
2014-10-23 14:07:48 +04:00
|
|
|
mPromise(aPromise),
|
2013-02-02 02:13:23 +04:00
|
|
|
mSuccessCallback(aSuccessCallback),
|
|
|
|
mFailureCallback(aFailureCallback) {
|
|
|
|
MOZ_ASSERT(aContext);
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_COUNT_CTOR(WebAudioDecodeJob);
|
|
|
|
}
|
|
|
|
|
|
|
|
WebAudioDecodeJob::~WebAudioDecodeJob() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_COUNT_DTOR(WebAudioDecodeJob);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(aErrorCode == NoError);
|
|
|
|
|
2014-10-23 14:07:48 +04:00
|
|
|
if (mSuccessCallback) {
|
2016-01-07 18:58:39 +03:00
|
|
|
ErrorResult rv;
|
2014-10-23 14:07:48 +04:00
|
|
|
mSuccessCallback->Call(*mOutput, rv);
|
2016-01-07 18:58:39 +03:00
|
|
|
// Ignore errors in calling the callback, since there is not much that we
|
|
|
|
// can do about it here.
|
|
|
|
rv.SuppressException();
|
2014-10-23 14:07:48 +04:00
|
|
|
}
|
|
|
|
mPromise->MaybeResolve(mOutput);
|
2013-02-02 02:13:23 +04:00
|
|
|
|
|
|
|
mContext->RemoveFromDecodeQueue(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
const char* errorMessage;
|
|
|
|
switch (aErrorCode) {
|
|
|
|
case UnknownContent:
|
|
|
|
errorMessage = "MediaDecodeAudioDataUnknownContentType";
|
|
|
|
break;
|
|
|
|
case InvalidContent:
|
|
|
|
errorMessage = "MediaDecodeAudioDataInvalidContent";
|
|
|
|
break;
|
|
|
|
case NoAudio:
|
|
|
|
errorMessage = "MediaDecodeAudioDataNoAudio";
|
|
|
|
break;
|
2018-05-15 04:12:13 +03:00
|
|
|
case NoError:
|
|
|
|
MOZ_FALLTHROUGH_ASSERT("Who passed NoError to OnFailure?");
|
|
|
|
// Fall through to get some sort of a sane error message if this actually
|
|
|
|
// happens at runtime.
|
|
|
|
case UnknownError:
|
|
|
|
MOZ_FALLTHROUGH;
|
|
|
|
default:
|
|
|
|
errorMessage = "MediaDecodeAudioDataUnknownError";
|
|
|
|
break;
|
2013-02-02 02:13:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIDocument* doc = nullptr;
|
2016-01-30 20:05:36 +03:00
|
|
|
if (nsPIDOMWindowInner* pWindow = mContext->GetParentObject()) {
|
2013-02-02 02:13:23 +04:00
|
|
|
doc = pWindow->GetExtantDoc();
|
|
|
|
}
|
|
|
|
nsContentUtils::ReportToConsole(
|
2013-08-21 23:28:26 +04:00
|
|
|
nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Media"), doc,
|
2013-02-02 02:13:23 +04:00
|
|
|
nsContentUtils::eDOM_PROPERTIES, errorMessage);
|
|
|
|
|
|
|
|
// Ignore errors in calling the callback, since there is not much that we can
|
|
|
|
// do about it here.
|
2013-03-05 08:04:51 +04:00
|
|
|
if (mFailureCallback) {
|
2016-12-13 09:00:15 +03:00
|
|
|
nsAutoCString errorString(errorMessage);
|
|
|
|
RefPtr<DOMException> exception = DOMException::Create(
|
|
|
|
NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR, errorString);
|
|
|
|
mFailureCallback->Call(*exception);
|
2013-03-05 08:04:51 +04:00
|
|
|
}
|
2013-02-02 02:13:23 +04:00
|
|
|
|
2014-10-23 14:07:48 +04:00
|
|
|
mPromise->MaybeReject(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
|
|
|
|
|
2013-02-02 02:13:23 +04:00
|
|
|
mContext->RemoveFromDecodeQueue(this);
|
|
|
|
}
|
|
|
|
|
2014-01-04 22:15:41 +04:00
|
|
|
size_t WebAudioDecodeJob::SizeOfExcludingThis(
|
|
|
|
MallocSizeOf aMallocSizeOf) const {
|
|
|
|
size_t amount = 0;
|
|
|
|
if (mSuccessCallback) {
|
|
|
|
amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
if (mFailureCallback) {
|
|
|
|
amount += mFailureCallback->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
if (mOutput) {
|
|
|
|
amount += mOutput->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
}
|
2017-08-16 09:10:06 +03:00
|
|
|
amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
|
2014-01-04 22:15:41 +04:00
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
|
2014-05-01 21:37:54 +04:00
|
|
|
size_t WebAudioDecodeJob::SizeOfIncludingThis(
|
|
|
|
MallocSizeOf aMallocSizeOf) const {
|
|
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace mozilla
|