michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MediaBufferDecoder.h" michael@0: #include "BufferDecoder.h" michael@0: #include "mozilla/dom/AudioContextBinding.h" michael@0: #include michael@0: #include "nsXPCOMCIDInternal.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "MediaDecoderReader.h" michael@0: #include "BufferMediaResource.h" michael@0: #include "DecoderTraits.h" michael@0: #include "AudioContext.h" michael@0: #include "AudioBuffer.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "nsCxPusher.h" michael@0: #include "WebAudioUtils.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(WebAudioDecodeJob) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebAudioDecodeJob) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutput) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuccessCallback) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mFailureCallback) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebAudioDecodeJob) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutput) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuccessCallback) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFailureCallback) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WebAudioDecodeJob) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebAudioDecodeJob, AddRef) michael@0: NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebAudioDecodeJob, Release) michael@0: michael@0: using namespace dom; michael@0: michael@0: class ReportResultTask : public nsRunnable michael@0: { michael@0: public: michael@0: ReportResultTask(WebAudioDecodeJob& aDecodeJob, michael@0: WebAudioDecodeJob::ResultFn aFunction, michael@0: WebAudioDecodeJob::ErrorCode aErrorCode) michael@0: : mDecodeJob(aDecodeJob) michael@0: , mFunction(aFunction) michael@0: , mErrorCode(aErrorCode) michael@0: { michael@0: MOZ_ASSERT(aFunction); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: (mDecodeJob.*mFunction)(mErrorCode); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: // Note that the mDecodeJob member will probably die when mFunction is run. michael@0: // Therefore, it is not safe to do anything fancy with it in this class. michael@0: // Really, this class is only used because nsRunnableMethod doesn't support michael@0: // methods accepting arguments. michael@0: WebAudioDecodeJob& mDecodeJob; michael@0: WebAudioDecodeJob::ResultFn mFunction; michael@0: WebAudioDecodeJob::ErrorCode mErrorCode; michael@0: }; michael@0: michael@0: MOZ_BEGIN_ENUM_CLASS(PhaseEnum, int) michael@0: Decode, michael@0: AllocateBuffer, michael@0: Done michael@0: MOZ_END_ENUM_CLASS(PhaseEnum) michael@0: michael@0: class MediaDecodeTask : public nsRunnable michael@0: { michael@0: public: michael@0: MediaDecodeTask(const char* aContentType, uint8_t* aBuffer, michael@0: uint32_t aLength, michael@0: WebAudioDecodeJob& aDecodeJob, michael@0: nsIThreadPool* aThreadPool) michael@0: : mContentType(aContentType) michael@0: , mBuffer(aBuffer) michael@0: , mLength(aLength) michael@0: , mDecodeJob(aDecodeJob) michael@0: , mPhase(PhaseEnum::Decode) michael@0: , mThreadPool(aThreadPool) michael@0: { michael@0: MOZ_ASSERT(aBuffer); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr pWindow = do_QueryInterface(mDecodeJob.mContext->GetParentObject()); michael@0: nsCOMPtr scriptPrincipal = michael@0: do_QueryInterface(pWindow); michael@0: if (scriptPrincipal) { michael@0: mPrincipal = scriptPrincipal->GetPrincipal(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHOD Run(); michael@0: bool CreateReader(); michael@0: michael@0: private: michael@0: void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) { michael@0: if (NS_IsMainThread()) { michael@0: Cleanup(); michael@0: mDecodeJob.OnFailure(aErrorCode); michael@0: } else { michael@0: // Take extra care to cleanup on the main thread michael@0: NS_DispatchToMainThread(NS_NewRunnableMethod(this, &MediaDecodeTask::Cleanup), michael@0: NS_DISPATCH_NORMAL); michael@0: michael@0: nsCOMPtr event = michael@0: new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode); michael@0: NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: michael@0: void Decode(); michael@0: void AllocateBuffer(); michael@0: void CallbackTheResult(); michael@0: michael@0: void Cleanup() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: // MediaDecoderReader expects that BufferDecoder is alive. michael@0: // Destruct MediaDecoderReader first. michael@0: mDecoderReader = nullptr; michael@0: mBufferDecoder = nullptr; michael@0: JS_free(nullptr, mBuffer); michael@0: } michael@0: michael@0: private: michael@0: nsCString mContentType; michael@0: uint8_t* mBuffer; michael@0: uint32_t mLength; michael@0: WebAudioDecodeJob& mDecodeJob; michael@0: PhaseEnum mPhase; michael@0: nsCOMPtr mThreadPool; michael@0: nsCOMPtr mPrincipal; michael@0: nsRefPtr mBufferDecoder; michael@0: nsAutoPtr mDecoderReader; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: MediaDecodeTask::Run() michael@0: { michael@0: MOZ_ASSERT(mBufferDecoder); michael@0: MOZ_ASSERT(mDecoderReader); michael@0: switch (mPhase) { michael@0: case PhaseEnum::Decode: michael@0: Decode(); michael@0: break; michael@0: case PhaseEnum::AllocateBuffer: michael@0: AllocateBuffer(); michael@0: break; michael@0: case PhaseEnum::Done: michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: MediaDecodeTask::CreateReader() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr resource = michael@0: new BufferMediaResource(static_cast (mBuffer), michael@0: mLength, mPrincipal, mContentType); michael@0: michael@0: MOZ_ASSERT(!mBufferDecoder); michael@0: mBufferDecoder = new BufferDecoder(resource); michael@0: michael@0: // If you change this list to add support for new decoders, please consider michael@0: // updating HTMLMediaElement::CreateDecoder as well. michael@0: michael@0: mDecoderReader = DecoderTraits::CreateReader(mContentType, mBufferDecoder); michael@0: michael@0: if (!mDecoderReader) { michael@0: return false; michael@0: } michael@0: michael@0: nsresult rv = mDecoderReader->Init(nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: class AutoResampler { michael@0: public: michael@0: AutoResampler() michael@0: : mResampler(nullptr) michael@0: {} michael@0: ~AutoResampler() michael@0: { michael@0: if (mResampler) { michael@0: speex_resampler_destroy(mResampler); michael@0: } michael@0: } michael@0: operator SpeexResamplerState*() const michael@0: { michael@0: MOZ_ASSERT(mResampler); michael@0: return mResampler; michael@0: } michael@0: void operator=(SpeexResamplerState* aResampler) michael@0: { michael@0: mResampler = aResampler; michael@0: } michael@0: michael@0: private: michael@0: SpeexResamplerState* mResampler; michael@0: }; michael@0: michael@0: void michael@0: MediaDecodeTask::Decode() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: mBufferDecoder->BeginDecoding(NS_GetCurrentThread()); michael@0: michael@0: // Tell the decoder reader that we are not going to play the data directly, michael@0: // and that we should not reject files with more channels than the audio michael@0: // bakend support. michael@0: mDecoderReader->SetIgnoreAudioOutputFormat(); michael@0: michael@0: MediaInfo mediaInfo; michael@0: nsAutoPtr tags; michael@0: nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags)); michael@0: if (NS_FAILED(rv)) { michael@0: ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); michael@0: return; michael@0: } michael@0: michael@0: if (!mDecoderReader->HasAudio()) { michael@0: ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio); michael@0: return; michael@0: } michael@0: michael@0: while (mDecoderReader->DecodeAudioData()) { michael@0: // consume all of the buffer michael@0: continue; michael@0: } michael@0: michael@0: MediaQueue& audioQueue = mDecoderReader->AudioQueue(); michael@0: uint32_t frameCount = audioQueue.FrameCount(); michael@0: uint32_t channelCount = mediaInfo.mAudio.mChannels; michael@0: uint32_t sampleRate = mediaInfo.mAudio.mRate; michael@0: michael@0: if (!frameCount || !channelCount || !sampleRate) { michael@0: ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); michael@0: return; michael@0: } michael@0: michael@0: const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate(); michael@0: AutoResampler resampler; michael@0: michael@0: uint32_t resampledFrames = frameCount; michael@0: if (sampleRate != destSampleRate) { michael@0: resampledFrames = static_cast( michael@0: static_cast(destSampleRate) * michael@0: static_cast(frameCount) / michael@0: static_cast(sampleRate) michael@0: ); michael@0: michael@0: resampler = speex_resampler_init(channelCount, michael@0: sampleRate, michael@0: destSampleRate, michael@0: SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); michael@0: speex_resampler_skip_zeros(resampler); michael@0: resampledFrames += speex_resampler_get_output_latency(resampler); michael@0: } michael@0: michael@0: // Allocate the channel buffers. Note that if we end up resampling, we may michael@0: // write fewer bytes than mResampledFrames to the output buffer, in which michael@0: // case mWriteIndex will tell us how many valid samples we have. michael@0: static const fallible_t fallible = fallible_t(); michael@0: bool memoryAllocationSuccess = true; michael@0: if (!mDecodeJob.mChannelBuffers.SetLength(channelCount)) { michael@0: memoryAllocationSuccess = false; michael@0: } else { michael@0: for (uint32_t i = 0; i < channelCount; ++i) { michael@0: mDecodeJob.mChannelBuffers[i] = new(fallible) float[resampledFrames]; michael@0: if (!mDecodeJob.mChannelBuffers[i]) { michael@0: memoryAllocationSuccess = false; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: if (!memoryAllocationSuccess) { michael@0: ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); michael@0: return; michael@0: } michael@0: michael@0: nsAutoPtr audioData; michael@0: while ((audioData = audioQueue.PopFront())) { michael@0: audioData->EnsureAudioBuffer(); // could lead to a copy :( michael@0: AudioDataValue* bufferData = static_cast michael@0: (audioData->mAudioBuffer->Data()); michael@0: michael@0: if (sampleRate != destSampleRate) { michael@0: const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; michael@0: michael@0: for (uint32_t i = 0; i < audioData->mChannels; ++i) { michael@0: uint32_t inSamples = audioData->mFrames; michael@0: uint32_t outSamples = maxOutSamples; michael@0: michael@0: WebAudioUtils::SpeexResamplerProcess( michael@0: resampler, i, &bufferData[i * audioData->mFrames], &inSamples, michael@0: mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, michael@0: &outSamples); michael@0: michael@0: if (i == audioData->mChannels - 1) { michael@0: mDecodeJob.mWriteIndex += outSamples; michael@0: MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); michael@0: MOZ_ASSERT(inSamples == audioData->mFrames); michael@0: } michael@0: } michael@0: } else { michael@0: for (uint32_t i = 0; i < audioData->mChannels; ++i) { michael@0: ConvertAudioSamples(&bufferData[i * audioData->mFrames], michael@0: mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, michael@0: audioData->mFrames); michael@0: michael@0: if (i == audioData->mChannels - 1) { michael@0: mDecodeJob.mWriteIndex += audioData->mFrames; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (sampleRate != destSampleRate) { michael@0: uint32_t inputLatency = speex_resampler_get_input_latency(resampler); michael@0: const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; michael@0: for (uint32_t i = 0; i < channelCount; ++i) { michael@0: uint32_t inSamples = inputLatency; michael@0: uint32_t outSamples = maxOutSamples; michael@0: michael@0: WebAudioUtils::SpeexResamplerProcess( michael@0: resampler, i, (AudioDataValue*)nullptr, &inSamples, michael@0: mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, michael@0: &outSamples); michael@0: michael@0: if (i == channelCount - 1) { michael@0: mDecodeJob.mWriteIndex += outSamples; michael@0: MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); michael@0: MOZ_ASSERT(inSamples == inputLatency); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mPhase = PhaseEnum::AllocateBuffer; michael@0: NS_DispatchToMainThread(this); michael@0: } michael@0: michael@0: void michael@0: MediaDecodeTask::AllocateBuffer() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mDecodeJob.AllocateBuffer()) { michael@0: ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); michael@0: return; michael@0: } michael@0: michael@0: mPhase = PhaseEnum::Done; michael@0: CallbackTheResult(); michael@0: } michael@0: michael@0: void michael@0: MediaDecodeTask::CallbackTheResult() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: Cleanup(); michael@0: michael@0: // Now, we're ready to call the script back with the resulting buffer michael@0: mDecodeJob.OnSuccess(WebAudioDecodeJob::NoError); michael@0: } michael@0: michael@0: bool michael@0: WebAudioDecodeJob::AllocateBuffer() michael@0: { michael@0: MOZ_ASSERT(!mOutput); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // First, get a JSContext michael@0: AutoPushJSContext cx(mContext->GetJSContext()); michael@0: if (!cx) { michael@0: return false; michael@0: } michael@0: // Now create the AudioBuffer michael@0: ErrorResult rv; michael@0: mOutput = AudioBuffer::Create(mContext, mChannelBuffers.Length(), michael@0: mWriteIndex, mContext->SampleRate(), cx, rv); michael@0: if (rv.Failed()) { michael@0: return false; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) { michael@0: mOutput->SetRawChannelContents(cx, i, mChannelBuffers[i]); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer, michael@0: uint32_t aLength, michael@0: WebAudioDecodeJob& aDecodeJob) michael@0: { michael@0: // Do not attempt to decode the media if we were not successful at sniffing michael@0: // the content type. michael@0: if (!*aContentType || michael@0: strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0) { michael@0: nsCOMPtr event = michael@0: new ReportResultTask(aDecodeJob, michael@0: &WebAudioDecodeJob::OnFailure, michael@0: WebAudioDecodeJob::UnknownContent); michael@0: NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); michael@0: return; michael@0: } michael@0: michael@0: if (!EnsureThreadPoolInitialized()) { michael@0: nsCOMPtr event = michael@0: new ReportResultTask(aDecodeJob, michael@0: &WebAudioDecodeJob::OnFailure, michael@0: WebAudioDecodeJob::UnknownError); michael@0: NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(mThreadPool); michael@0: michael@0: nsRefPtr task = michael@0: new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, mThreadPool); michael@0: if (!task->CreateReader()) { michael@0: nsCOMPtr event = michael@0: new ReportResultTask(aDecodeJob, michael@0: &WebAudioDecodeJob::OnFailure, michael@0: WebAudioDecodeJob::UnknownError); michael@0: NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); michael@0: } else { michael@0: mThreadPool->Dispatch(task, nsIThreadPool::DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MediaBufferDecoder::EnsureThreadPoolInitialized() michael@0: { michael@0: if (!mThreadPool) { michael@0: mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); michael@0: if (!mThreadPool) { michael@0: return false; michael@0: } michael@0: mThreadPool->SetName(NS_LITERAL_CSTRING("MediaBufferDecoder")); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MediaBufferDecoder::Shutdown() { michael@0: if (mThreadPool) { michael@0: // Setting threadLimit to 0 causes threads to exit when all events have michael@0: // been run, like nsIThreadPool::Shutdown(), but doesn't run a nested event michael@0: // loop nor wait until this has happened. michael@0: mThreadPool->SetThreadLimit(0); michael@0: mThreadPool = nullptr; michael@0: } michael@0: } michael@0: michael@0: WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType, michael@0: AudioContext* aContext, michael@0: DecodeSuccessCallback* aSuccessCallback, michael@0: DecodeErrorCallback* aFailureCallback) michael@0: : mContentType(aContentType) michael@0: , mWriteIndex(0) michael@0: , mContext(aContext) michael@0: , mSuccessCallback(aSuccessCallback) michael@0: , mFailureCallback(aFailureCallback) michael@0: { michael@0: MOZ_ASSERT(aContext); michael@0: MOZ_ASSERT(aSuccessCallback); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_COUNT_CTOR(WebAudioDecodeJob); michael@0: } michael@0: michael@0: WebAudioDecodeJob::~WebAudioDecodeJob() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_COUNT_DTOR(WebAudioDecodeJob); michael@0: } michael@0: michael@0: void michael@0: WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aErrorCode == NoError); michael@0: michael@0: // Ignore errors in calling the callback, since there is not much that we can michael@0: // do about it here. michael@0: ErrorResult rv; michael@0: mSuccessCallback->Call(*mOutput, rv); michael@0: michael@0: mContext->RemoveFromDecodeQueue(this); michael@0: } michael@0: michael@0: void michael@0: WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: const char* errorMessage; michael@0: switch (aErrorCode) { michael@0: case NoError: michael@0: MOZ_ASSERT(false, "Who passed NoError to OnFailure?"); michael@0: // Fall through to get some sort of a sane error message if this actually michael@0: // happens at runtime. michael@0: case UnknownError: michael@0: errorMessage = "MediaDecodeAudioDataUnknownError"; michael@0: break; michael@0: case UnknownContent: michael@0: errorMessage = "MediaDecodeAudioDataUnknownContentType"; michael@0: break; michael@0: case InvalidContent: michael@0: errorMessage = "MediaDecodeAudioDataInvalidContent"; michael@0: break; michael@0: case NoAudio: michael@0: errorMessage = "MediaDecodeAudioDataNoAudio"; michael@0: break; michael@0: } michael@0: michael@0: nsCOMPtr pWindow = do_QueryInterface(mContext->GetParentObject()); michael@0: nsIDocument* doc = nullptr; michael@0: if (pWindow) { michael@0: doc = pWindow->GetExtantDoc(); michael@0: } michael@0: nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, michael@0: NS_LITERAL_CSTRING("Media"), michael@0: doc, michael@0: nsContentUtils::eDOM_PROPERTIES, michael@0: errorMessage); michael@0: michael@0: // Ignore errors in calling the callback, since there is not much that we can michael@0: // do about it here. michael@0: if (mFailureCallback) { michael@0: ErrorResult rv; michael@0: mFailureCallback->Call(rv); michael@0: } michael@0: michael@0: mContext->RemoveFromDecodeQueue(this); michael@0: } michael@0: michael@0: size_t michael@0: WebAudioDecodeJob::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t amount = 0; michael@0: amount += mContentType.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf); michael@0: if (mSuccessCallback) { michael@0: amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: if (mFailureCallback) { michael@0: amount += mFailureCallback->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: if (mOutput) { michael@0: amount += mOutput->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: amount += mChannelBuffers.SizeOfExcludingThis(aMallocSizeOf); michael@0: for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) { michael@0: amount += mChannelBuffers[i].SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: return amount; michael@0: } michael@0: michael@0: } michael@0: