Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "MediaBufferDecoder.h"
8 : #include "BufferDecoder.h"
9 : #include "mozilla/dom/AudioContextBinding.h"
10 : #include "mozilla/dom/BaseAudioContextBinding.h"
11 : #include "mozilla/dom/DOMException.h"
12 : #include "mozilla/dom/ScriptSettings.h"
13 : #include "mozilla/AbstractThread.h"
14 : #include <speex/speex_resampler.h>
15 : #include "nsXPCOMCIDInternal.h"
16 : #include "nsComponentManagerUtils.h"
17 : #include "MediaDecoderReader.h"
18 : #include "BufferMediaResource.h"
19 : #include "DecoderTraits.h"
20 : #include "AudioContext.h"
21 : #include "AudioBuffer.h"
22 : #include "MediaContainerType.h"
23 : #include "nsContentUtils.h"
24 : #include "nsIScriptObjectPrincipal.h"
25 : #include "nsIScriptError.h"
26 : #include "nsMimeTypes.h"
27 : #include "VideoUtils.h"
28 : #include "WebAudioUtils.h"
29 : #include "mozilla/dom/Promise.h"
30 : #include "mozilla/Telemetry.h"
31 : #include "nsPrintfCString.h"
32 :
33 : namespace mozilla {
34 :
35 : extern LazyLogModule gMediaDecoderLog;
36 :
37 : using namespace dom;
38 :
39 0 : class ReportResultTask final : public Runnable
40 : {
41 : public:
42 0 : ReportResultTask(WebAudioDecodeJob& aDecodeJob,
43 : WebAudioDecodeJob::ResultFn aFunction,
44 : WebAudioDecodeJob::ErrorCode aErrorCode)
45 0 : : Runnable("ReportResultTask")
46 : , mDecodeJob(aDecodeJob)
47 : , mFunction(aFunction)
48 0 : , mErrorCode(aErrorCode)
49 : {
50 0 : MOZ_ASSERT(aFunction);
51 0 : }
52 :
53 0 : NS_IMETHOD Run() override
54 : {
55 0 : MOZ_ASSERT(NS_IsMainThread());
56 :
57 0 : (mDecodeJob.*mFunction)(mErrorCode);
58 :
59 0 : return NS_OK;
60 : }
61 :
62 : private:
63 : // Note that the mDecodeJob member will probably die when mFunction is run.
64 : // Therefore, it is not safe to do anything fancy with it in this class.
65 : // Really, this class is only used because nsRunnableMethod doesn't support
66 : // methods accepting arguments.
67 : WebAudioDecodeJob& mDecodeJob;
68 : WebAudioDecodeJob::ResultFn mFunction;
69 : WebAudioDecodeJob::ErrorCode mErrorCode;
70 : };
71 :
72 : enum class PhaseEnum : int
73 : {
74 : Decode,
75 : AllocateBuffer,
76 : Done
77 : };
78 :
79 0 : class MediaDecodeTask final : public Runnable
80 : {
81 : public:
82 0 : MediaDecodeTask(const MediaContainerType& aContainerType,
83 : uint8_t* aBuffer,
84 : uint32_t aLength,
85 : WebAudioDecodeJob& aDecodeJob)
86 0 : : Runnable("MediaDecodeTask")
87 : , mContainerType(aContainerType)
88 : , mBuffer(aBuffer)
89 : , mLength(aLength)
90 : , mDecodeJob(aDecodeJob)
91 : , mPhase(PhaseEnum::Decode)
92 0 : , mFirstFrameDecoded(false)
93 : {
94 0 : MOZ_ASSERT(aBuffer);
95 0 : MOZ_ASSERT(NS_IsMainThread());
96 0 : }
97 :
98 : NS_IMETHOD Run();
99 : bool CreateReader();
100 0 : MediaDecoderReader* Reader() { MOZ_ASSERT(mDecoderReader); return mDecoderReader; }
101 :
102 : private:
103 0 : void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) {
104 0 : if (NS_IsMainThread()) {
105 0 : Cleanup();
106 0 : mDecodeJob.OnFailure(aErrorCode);
107 : } else {
108 : // Take extra care to cleanup on the main thread
109 0 : mMainThread->Dispatch(NewRunnableMethod(
110 0 : "MediaDecodeTask::Cleanup", this, &MediaDecodeTask::Cleanup));
111 :
112 : nsCOMPtr<nsIRunnable> event =
113 0 : new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode);
114 0 : mMainThread->Dispatch(event.forget());
115 : }
116 0 : }
117 :
118 : void Decode();
119 : void OnMetadataRead(MetadataHolder&& aMetadata);
120 : void OnMetadataNotRead(const MediaResult& aError);
121 : void RequestSample();
122 : void SampleDecoded(RefPtr<AudioData> aData);
123 : void SampleNotDecoded(const MediaResult& aError);
124 : void FinishDecode();
125 : void AllocateBuffer();
126 : void CallbackTheResult();
127 :
128 0 : void Cleanup()
129 : {
130 0 : MOZ_ASSERT(NS_IsMainThread());
131 : // MediaDecoderReader expects that BufferDecoder is alive.
132 : // Destruct MediaDecoderReader first.
133 0 : mDecoderReader = nullptr;
134 0 : mBufferDecoder = nullptr;
135 0 : JS_free(nullptr, mBuffer);
136 0 : }
137 :
138 : private:
139 : MediaContainerType mContainerType;
140 : uint8_t* mBuffer;
141 : uint32_t mLength;
142 : WebAudioDecodeJob& mDecodeJob;
143 : PhaseEnum mPhase;
144 : RefPtr<BufferDecoder> mBufferDecoder;
145 : RefPtr<MediaDecoderReader> mDecoderReader;
146 : MediaInfo mMediaInfo;
147 : MediaQueue<AudioData> mAudioQueue;
148 : RefPtr<AbstractThread> mMainThread;
149 : bool mFirstFrameDecoded;
150 : };
151 :
152 : NS_IMETHODIMP
153 0 : MediaDecodeTask::Run()
154 : {
155 0 : MOZ_ASSERT(mBufferDecoder);
156 0 : MOZ_ASSERT(mDecoderReader);
157 0 : switch (mPhase) {
158 : case PhaseEnum::Decode:
159 0 : Decode();
160 0 : break;
161 : case PhaseEnum::AllocateBuffer:
162 0 : AllocateBuffer();
163 0 : break;
164 : case PhaseEnum::Done:
165 0 : break;
166 : }
167 :
168 0 : return NS_OK;
169 : }
170 :
171 : bool
172 0 : MediaDecodeTask::CreateReader()
173 : {
174 0 : MOZ_ASSERT(NS_IsMainThread());
175 :
176 0 : nsPIDOMWindowInner* parent = mDecodeJob.mContext->GetParentObject();
177 0 : MOZ_ASSERT(parent);
178 :
179 0 : nsCOMPtr<nsIPrincipal> principal;
180 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(parent);
181 0 : if (sop) {
182 0 : principal = sop->GetPrincipal();
183 : }
184 :
185 : RefPtr<BufferMediaResource> resource =
186 0 : new BufferMediaResource(static_cast<uint8_t*>(mBuffer), mLength, principal);
187 :
188 0 : MOZ_ASSERT(!mBufferDecoder);
189 : mMainThread =
190 0 : mDecodeJob.mContext->GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other);
191 0 : mBufferDecoder = new BufferDecoder(resource, mMainThread);
192 :
193 : // If you change this list to add support for new decoders, please consider
194 : // updating HTMLMediaElement::CreateDecoder as well.
195 :
196 0 : MediaDecoderReaderInit init(mBufferDecoder);
197 0 : init.mResource = resource;
198 0 : mDecoderReader = DecoderTraits::CreateReader(mContainerType, init);
199 :
200 0 : if (!mDecoderReader) {
201 0 : return false;
202 : }
203 :
204 0 : nsresult rv = mDecoderReader->Init();
205 0 : if (NS_FAILED(rv)) {
206 0 : return false;
207 : }
208 :
209 0 : return true;
210 : }
211 :
212 : class AutoResampler final
213 : {
214 : public:
215 0 : AutoResampler()
216 0 : : mResampler(nullptr)
217 0 : {}
218 0 : ~AutoResampler()
219 0 : {
220 0 : if (mResampler) {
221 0 : speex_resampler_destroy(mResampler);
222 : }
223 0 : }
224 0 : operator SpeexResamplerState*() const
225 : {
226 0 : MOZ_ASSERT(mResampler);
227 0 : return mResampler;
228 : }
229 0 : void operator=(SpeexResamplerState* aResampler)
230 : {
231 0 : mResampler = aResampler;
232 0 : }
233 :
234 : private:
235 : SpeexResamplerState* mResampler;
236 : };
237 :
238 : void
239 0 : MediaDecodeTask::Decode()
240 : {
241 0 : MOZ_ASSERT(!NS_IsMainThread());
242 :
243 0 : mBufferDecoder->BeginDecoding(mDecoderReader->OwnerThread());
244 :
245 : // Tell the decoder reader that we are not going to play the data directly,
246 : // and that we should not reject files with more channels than the audio
247 : // backend support.
248 0 : mDecoderReader->SetIgnoreAudioOutputFormat();
249 :
250 0 : mDecoderReader->AsyncReadMetadata()->Then(mDecoderReader->OwnerThread(), __func__, this,
251 : &MediaDecodeTask::OnMetadataRead,
252 0 : &MediaDecodeTask::OnMetadataNotRead);
253 0 : }
254 :
255 : void
256 0 : MediaDecodeTask::OnMetadataRead(MetadataHolder&& aMetadata)
257 : {
258 0 : mMediaInfo = *aMetadata.mInfo;
259 0 : if (!mMediaInfo.HasAudio()) {
260 0 : mDecoderReader->Shutdown();
261 0 : ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio);
262 0 : return;
263 : }
264 :
265 0 : nsCString codec;
266 0 : if (!mMediaInfo.mAudio.GetAsAudioInfo()->mMimeType.IsEmpty()) {
267 0 : codec = nsPrintfCString("webaudio; %s", mMediaInfo.mAudio.GetAsAudioInfo()->mMimeType.get());
268 : } else {
269 0 : codec = nsPrintfCString("webaudio;resource; %s",
270 0 : mContainerType.Type().AsString().Data());
271 : }
272 :
273 0 : nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
274 0 : "MediaDecodeTask::OnMetadataRead", [codec]() -> void {
275 0 : MOZ_ASSERT(!codec.IsEmpty());
276 0 : MOZ_LOG(gMediaDecoderLog,
277 : LogLevel::Debug,
278 : ("Telemetry (WebAudio) MEDIA_CODEC_USED= '%s'", codec.get()));
279 0 : Telemetry::Accumulate(Telemetry::HistogramID::MEDIA_CODEC_USED, codec);
280 0 : });
281 : SystemGroup::Dispatch("MediaDecodeTask::OnMetadataRead()::report_telemetry",
282 : TaskCategory::Other,
283 0 : task.forget());
284 :
285 0 : RequestSample();
286 : }
287 :
288 : void
289 0 : MediaDecodeTask::OnMetadataNotRead(const MediaResult& aReason)
290 : {
291 0 : mDecoderReader->Shutdown();
292 0 : ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
293 0 : }
294 :
295 : void
296 0 : MediaDecodeTask::RequestSample()
297 : {
298 0 : mDecoderReader->RequestAudioData()->Then(mDecoderReader->OwnerThread(), __func__, this,
299 : &MediaDecodeTask::SampleDecoded,
300 0 : &MediaDecodeTask::SampleNotDecoded);
301 0 : }
302 :
303 : void
304 0 : MediaDecodeTask::SampleDecoded(RefPtr<AudioData> aData)
305 : {
306 0 : MOZ_ASSERT(!NS_IsMainThread());
307 0 : mAudioQueue.Push(aData);
308 0 : if (!mFirstFrameDecoded) {
309 0 : mDecoderReader->ReadUpdatedMetadata(&mMediaInfo);
310 0 : mFirstFrameDecoded = true;
311 : }
312 0 : RequestSample();
313 0 : }
314 :
315 : void
316 0 : MediaDecodeTask::SampleNotDecoded(const MediaResult& aError)
317 : {
318 0 : MOZ_ASSERT(!NS_IsMainThread());
319 0 : if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
320 0 : FinishDecode();
321 : } else {
322 0 : mDecoderReader->Shutdown();
323 0 : ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
324 : }
325 0 : }
326 :
327 : void
328 0 : MediaDecodeTask::FinishDecode()
329 : {
330 0 : mDecoderReader->Shutdown();
331 :
332 0 : uint32_t frameCount = mAudioQueue.FrameCount();
333 0 : uint32_t channelCount = mMediaInfo.mAudio.mChannels;
334 0 : uint32_t sampleRate = mMediaInfo.mAudio.mRate;
335 :
336 0 : if (!frameCount || !channelCount || !sampleRate) {
337 0 : ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
338 0 : return;
339 : }
340 :
341 0 : const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate();
342 0 : AutoResampler resampler;
343 :
344 0 : uint32_t resampledFrames = frameCount;
345 0 : if (sampleRate != destSampleRate) {
346 0 : resampledFrames = static_cast<uint32_t>(
347 0 : static_cast<uint64_t>(destSampleRate) *
348 0 : static_cast<uint64_t>(frameCount) /
349 0 : static_cast<uint64_t>(sampleRate)
350 : );
351 :
352 0 : resampler = speex_resampler_init(channelCount,
353 : sampleRate,
354 : destSampleRate,
355 0 : SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr);
356 0 : speex_resampler_skip_zeros(resampler);
357 0 : resampledFrames += speex_resampler_get_output_latency(resampler);
358 : }
359 :
360 : // Allocate the channel buffers. Note that if we end up resampling, we may
361 : // write fewer bytes than mResampledFrames to the output buffer, in which
362 : // case mWriteIndex will tell us how many valid samples we have.
363 0 : mDecodeJob.mBuffer = ThreadSharedFloatArrayBufferList::
364 0 : Create(channelCount, resampledFrames, fallible);
365 0 : if (!mDecodeJob.mBuffer) {
366 0 : ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
367 0 : return;
368 : }
369 :
370 0 : RefPtr<AudioData> audioData;
371 0 : while ((audioData = mAudioQueue.PopFront())) {
372 0 : audioData->EnsureAudioBuffer(); // could lead to a copy :(
373 : AudioDataValue* bufferData = static_cast<AudioDataValue*>
374 0 : (audioData->mAudioBuffer->Data());
375 :
376 0 : if (sampleRate != destSampleRate) {
377 0 : const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex;
378 :
379 0 : for (uint32_t i = 0; i < audioData->mChannels; ++i) {
380 0 : uint32_t inSamples = audioData->mFrames;
381 0 : uint32_t outSamples = maxOutSamples;
382 : float* outData =
383 0 : mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
384 :
385 0 : WebAudioUtils::SpeexResamplerProcess(
386 0 : resampler, i, &bufferData[i * audioData->mFrames], &inSamples,
387 0 : outData, &outSamples);
388 :
389 0 : if (i == audioData->mChannels - 1) {
390 0 : mDecodeJob.mWriteIndex += outSamples;
391 0 : MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames);
392 0 : MOZ_ASSERT(inSamples == audioData->mFrames);
393 : }
394 : }
395 : } else {
396 0 : for (uint32_t i = 0; i < audioData->mChannels; ++i) {
397 : float* outData =
398 0 : mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
399 0 : ConvertAudioSamples(&bufferData[i * audioData->mFrames],
400 0 : outData, audioData->mFrames);
401 :
402 0 : if (i == audioData->mChannels - 1) {
403 0 : mDecodeJob.mWriteIndex += audioData->mFrames;
404 : }
405 : }
406 : }
407 : }
408 :
409 0 : if (sampleRate != destSampleRate) {
410 0 : uint32_t inputLatency = speex_resampler_get_input_latency(resampler);
411 0 : const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex;
412 0 : for (uint32_t i = 0; i < channelCount; ++i) {
413 0 : uint32_t inSamples = inputLatency;
414 0 : uint32_t outSamples = maxOutSamples;
415 : float* outData =
416 0 : mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
417 :
418 0 : WebAudioUtils::SpeexResamplerProcess(
419 : resampler, i, (AudioDataValue*)nullptr, &inSamples,
420 0 : outData, &outSamples);
421 :
422 0 : if (i == channelCount - 1) {
423 0 : mDecodeJob.mWriteIndex += outSamples;
424 0 : MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames);
425 0 : MOZ_ASSERT(inSamples == inputLatency);
426 : }
427 : }
428 : }
429 :
430 0 : mPhase = PhaseEnum::AllocateBuffer;
431 0 : mMainThread->Dispatch(do_AddRef(this));
432 : }
433 :
434 : void
435 0 : MediaDecodeTask::AllocateBuffer()
436 : {
437 0 : MOZ_ASSERT(NS_IsMainThread());
438 :
439 0 : if (!mDecodeJob.AllocateBuffer()) {
440 0 : ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
441 0 : return;
442 : }
443 :
444 0 : mPhase = PhaseEnum::Done;
445 0 : CallbackTheResult();
446 : }
447 :
448 : void
449 0 : MediaDecodeTask::CallbackTheResult()
450 : {
451 0 : MOZ_ASSERT(NS_IsMainThread());
452 :
453 0 : Cleanup();
454 :
455 : // Now, we're ready to call the script back with the resulting buffer
456 0 : mDecodeJob.OnSuccess(WebAudioDecodeJob::NoError);
457 0 : }
458 :
459 : bool
460 0 : WebAudioDecodeJob::AllocateBuffer()
461 : {
462 0 : MOZ_ASSERT(!mOutput);
463 0 : MOZ_ASSERT(NS_IsMainThread());
464 :
465 : // Now create the AudioBuffer
466 0 : ErrorResult rv;
467 0 : uint32_t channelCount = mBuffer->GetChannels();
468 0 : mOutput = AudioBuffer::Create(mContext->GetOwner(), channelCount,
469 : mWriteIndex, mContext->SampleRate(),
470 0 : mBuffer.forget(), rv);
471 0 : return !rv.Failed();
472 : }
473 :
474 : void
475 0 : AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
476 : uint32_t aLength, WebAudioDecodeJob& aDecodeJob)
477 : {
478 0 : Maybe<MediaContainerType> containerType = MakeMediaContainerType(aContentType);
479 : // Do not attempt to decode the media if we were not successful at sniffing
480 : // the container type.
481 0 : if (!*aContentType ||
482 0 : strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0 ||
483 0 : !containerType) {
484 : nsCOMPtr<nsIRunnable> event =
485 : new ReportResultTask(aDecodeJob,
486 : &WebAudioDecodeJob::OnFailure,
487 0 : WebAudioDecodeJob::UnknownContent);
488 0 : JS_free(nullptr, aBuffer);
489 0 : aDecodeJob.mContext->Dispatch(event.forget());
490 0 : return;
491 : }
492 :
493 : RefPtr<MediaDecodeTask> task =
494 0 : new MediaDecodeTask(*containerType, aBuffer, aLength, aDecodeJob);
495 0 : if (!task->CreateReader()) {
496 : nsCOMPtr<nsIRunnable> event =
497 : new ReportResultTask(aDecodeJob,
498 : &WebAudioDecodeJob::OnFailure,
499 0 : WebAudioDecodeJob::UnknownError);
500 0 : aDecodeJob.mContext->Dispatch(event.forget());
501 : } else {
502 : // If we did this without a temporary:
503 : // task->Reader()->OwnerThread()->Dispatch(task.forget())
504 : // we might evaluate the task.forget() before calling Reader(). Enforce
505 : // a non-crashy order-of-operations.
506 0 : TaskQueue* taskQueue = task->Reader()->OwnerThread();
507 0 : taskQueue->Dispatch(task.forget());
508 : }
509 : }
510 :
511 0 : WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType,
512 : AudioContext* aContext,
513 : Promise* aPromise,
514 : DecodeSuccessCallback* aSuccessCallback,
515 0 : DecodeErrorCallback* aFailureCallback)
516 : : mContentType(aContentType)
517 : , mWriteIndex(0)
518 : , mContext(aContext)
519 : , mPromise(aPromise)
520 : , mSuccessCallback(aSuccessCallback)
521 0 : , mFailureCallback(aFailureCallback)
522 : {
523 0 : MOZ_ASSERT(aContext);
524 0 : MOZ_ASSERT(NS_IsMainThread());
525 0 : MOZ_COUNT_CTOR(WebAudioDecodeJob);
526 0 : }
527 :
528 0 : WebAudioDecodeJob::~WebAudioDecodeJob()
529 : {
530 0 : MOZ_ASSERT(NS_IsMainThread());
531 0 : MOZ_COUNT_DTOR(WebAudioDecodeJob);
532 0 : }
533 :
534 : void
535 0 : WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode)
536 : {
537 0 : MOZ_ASSERT(NS_IsMainThread());
538 0 : MOZ_ASSERT(aErrorCode == NoError);
539 :
540 0 : if (mSuccessCallback) {
541 0 : ErrorResult rv;
542 0 : mSuccessCallback->Call(*mOutput, rv);
543 : // Ignore errors in calling the callback, since there is not much that we can
544 : // do about it here.
545 0 : rv.SuppressException();
546 : }
547 0 : mPromise->MaybeResolve(mOutput);
548 :
549 0 : mContext->RemoveFromDecodeQueue(this);
550 :
551 0 : }
552 :
553 : void
554 0 : WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode)
555 : {
556 0 : MOZ_ASSERT(NS_IsMainThread());
557 :
558 : const char* errorMessage;
559 0 : switch (aErrorCode) {
560 : case NoError:
561 0 : MOZ_FALLTHROUGH_ASSERT("Who passed NoError to OnFailure?");
562 : // Fall through to get some sort of a sane error message if this actually
563 : // happens at runtime.
564 : case UnknownError:
565 0 : errorMessage = "MediaDecodeAudioDataUnknownError";
566 0 : break;
567 : case UnknownContent:
568 0 : errorMessage = "MediaDecodeAudioDataUnknownContentType";
569 0 : break;
570 : case InvalidContent:
571 0 : errorMessage = "MediaDecodeAudioDataInvalidContent";
572 0 : break;
573 : case NoAudio:
574 0 : errorMessage = "MediaDecodeAudioDataNoAudio";
575 0 : break;
576 : }
577 :
578 0 : nsIDocument* doc = nullptr;
579 0 : if (nsPIDOMWindowInner* pWindow = mContext->GetParentObject()) {
580 0 : doc = pWindow->GetExtantDoc();
581 : }
582 0 : nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
583 0 : NS_LITERAL_CSTRING("Media"),
584 : doc,
585 : nsContentUtils::eDOM_PROPERTIES,
586 0 : errorMessage);
587 :
588 : // Ignore errors in calling the callback, since there is not much that we can
589 : // do about it here.
590 0 : if (mFailureCallback) {
591 0 : nsAutoCString errorString(errorMessage);
592 : RefPtr<DOMException> exception =
593 0 : DOMException::Create(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR,
594 0 : errorString);
595 0 : mFailureCallback->Call(*exception);
596 : }
597 :
598 0 : mPromise->MaybeReject(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
599 :
600 0 : mContext->RemoveFromDecodeQueue(this);
601 0 : }
602 :
603 : size_t
604 0 : WebAudioDecodeJob::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
605 : {
606 0 : size_t amount = 0;
607 0 : amount += mContentType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
608 0 : if (mSuccessCallback) {
609 0 : amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf);
610 : }
611 0 : if (mFailureCallback) {
612 0 : amount += mFailureCallback->SizeOfIncludingThis(aMallocSizeOf);
613 : }
614 0 : if (mOutput) {
615 0 : amount += mOutput->SizeOfIncludingThis(aMallocSizeOf);
616 : }
617 0 : if (mBuffer) {
618 0 : amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
619 : }
620 0 : return amount;
621 : }
622 :
623 : size_t
624 0 : WebAudioDecodeJob::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
625 : {
626 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
627 : }
628 :
629 : } // namespace mozilla
|