|
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 <speex/speex_resampler.h> |
|
11 #include "nsXPCOMCIDInternal.h" |
|
12 #include "nsComponentManagerUtils.h" |
|
13 #include "MediaDecoderReader.h" |
|
14 #include "BufferMediaResource.h" |
|
15 #include "DecoderTraits.h" |
|
16 #include "AudioContext.h" |
|
17 #include "AudioBuffer.h" |
|
18 #include "nsIScriptObjectPrincipal.h" |
|
19 #include "nsIScriptError.h" |
|
20 #include "nsMimeTypes.h" |
|
21 #include "nsCxPusher.h" |
|
22 #include "WebAudioUtils.h" |
|
23 |
|
24 namespace mozilla { |
|
25 |
|
26 NS_IMPL_CYCLE_COLLECTION_CLASS(WebAudioDecodeJob) |
|
27 |
|
28 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebAudioDecodeJob) |
|
29 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) |
|
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutput) |
|
31 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuccessCallback) |
|
32 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFailureCallback) |
|
33 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
34 |
|
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebAudioDecodeJob) |
|
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) |
|
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutput) |
|
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuccessCallback) |
|
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFailureCallback) |
|
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
42 |
|
43 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WebAudioDecodeJob) |
|
44 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
45 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebAudioDecodeJob, AddRef) |
|
46 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebAudioDecodeJob, Release) |
|
47 |
|
48 using namespace dom; |
|
49 |
|
50 class ReportResultTask : public nsRunnable |
|
51 { |
|
52 public: |
|
53 ReportResultTask(WebAudioDecodeJob& aDecodeJob, |
|
54 WebAudioDecodeJob::ResultFn aFunction, |
|
55 WebAudioDecodeJob::ErrorCode aErrorCode) |
|
56 : mDecodeJob(aDecodeJob) |
|
57 , mFunction(aFunction) |
|
58 , mErrorCode(aErrorCode) |
|
59 { |
|
60 MOZ_ASSERT(aFunction); |
|
61 } |
|
62 |
|
63 NS_IMETHOD Run() |
|
64 { |
|
65 MOZ_ASSERT(NS_IsMainThread()); |
|
66 |
|
67 (mDecodeJob.*mFunction)(mErrorCode); |
|
68 |
|
69 return NS_OK; |
|
70 } |
|
71 |
|
72 private: |
|
73 // Note that the mDecodeJob member will probably die when mFunction is run. |
|
74 // Therefore, it is not safe to do anything fancy with it in this class. |
|
75 // Really, this class is only used because nsRunnableMethod doesn't support |
|
76 // methods accepting arguments. |
|
77 WebAudioDecodeJob& mDecodeJob; |
|
78 WebAudioDecodeJob::ResultFn mFunction; |
|
79 WebAudioDecodeJob::ErrorCode mErrorCode; |
|
80 }; |
|
81 |
|
82 MOZ_BEGIN_ENUM_CLASS(PhaseEnum, int) |
|
83 Decode, |
|
84 AllocateBuffer, |
|
85 Done |
|
86 MOZ_END_ENUM_CLASS(PhaseEnum) |
|
87 |
|
88 class MediaDecodeTask : public nsRunnable |
|
89 { |
|
90 public: |
|
91 MediaDecodeTask(const char* aContentType, uint8_t* aBuffer, |
|
92 uint32_t aLength, |
|
93 WebAudioDecodeJob& aDecodeJob, |
|
94 nsIThreadPool* aThreadPool) |
|
95 : mContentType(aContentType) |
|
96 , mBuffer(aBuffer) |
|
97 , mLength(aLength) |
|
98 , mDecodeJob(aDecodeJob) |
|
99 , mPhase(PhaseEnum::Decode) |
|
100 , mThreadPool(aThreadPool) |
|
101 { |
|
102 MOZ_ASSERT(aBuffer); |
|
103 MOZ_ASSERT(NS_IsMainThread()); |
|
104 |
|
105 nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(mDecodeJob.mContext->GetParentObject()); |
|
106 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = |
|
107 do_QueryInterface(pWindow); |
|
108 if (scriptPrincipal) { |
|
109 mPrincipal = scriptPrincipal->GetPrincipal(); |
|
110 } |
|
111 } |
|
112 |
|
113 NS_IMETHOD Run(); |
|
114 bool CreateReader(); |
|
115 |
|
116 private: |
|
117 void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) { |
|
118 if (NS_IsMainThread()) { |
|
119 Cleanup(); |
|
120 mDecodeJob.OnFailure(aErrorCode); |
|
121 } else { |
|
122 // Take extra care to cleanup on the main thread |
|
123 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &MediaDecodeTask::Cleanup), |
|
124 NS_DISPATCH_NORMAL); |
|
125 |
|
126 nsCOMPtr<nsIRunnable> event = |
|
127 new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode); |
|
128 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
129 } |
|
130 } |
|
131 |
|
132 void Decode(); |
|
133 void AllocateBuffer(); |
|
134 void CallbackTheResult(); |
|
135 |
|
136 void Cleanup() |
|
137 { |
|
138 MOZ_ASSERT(NS_IsMainThread()); |
|
139 // MediaDecoderReader expects that BufferDecoder is alive. |
|
140 // Destruct MediaDecoderReader first. |
|
141 mDecoderReader = nullptr; |
|
142 mBufferDecoder = nullptr; |
|
143 JS_free(nullptr, mBuffer); |
|
144 } |
|
145 |
|
146 private: |
|
147 nsCString mContentType; |
|
148 uint8_t* mBuffer; |
|
149 uint32_t mLength; |
|
150 WebAudioDecodeJob& mDecodeJob; |
|
151 PhaseEnum mPhase; |
|
152 nsCOMPtr<nsIThreadPool> mThreadPool; |
|
153 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
154 nsRefPtr<BufferDecoder> mBufferDecoder; |
|
155 nsAutoPtr<MediaDecoderReader> mDecoderReader; |
|
156 }; |
|
157 |
|
158 NS_IMETHODIMP |
|
159 MediaDecodeTask::Run() |
|
160 { |
|
161 MOZ_ASSERT(mBufferDecoder); |
|
162 MOZ_ASSERT(mDecoderReader); |
|
163 switch (mPhase) { |
|
164 case PhaseEnum::Decode: |
|
165 Decode(); |
|
166 break; |
|
167 case PhaseEnum::AllocateBuffer: |
|
168 AllocateBuffer(); |
|
169 break; |
|
170 case PhaseEnum::Done: |
|
171 break; |
|
172 } |
|
173 |
|
174 return NS_OK; |
|
175 } |
|
176 |
|
177 bool |
|
178 MediaDecodeTask::CreateReader() |
|
179 { |
|
180 MOZ_ASSERT(NS_IsMainThread()); |
|
181 |
|
182 nsRefPtr<BufferMediaResource> resource = |
|
183 new BufferMediaResource(static_cast<uint8_t*> (mBuffer), |
|
184 mLength, mPrincipal, mContentType); |
|
185 |
|
186 MOZ_ASSERT(!mBufferDecoder); |
|
187 mBufferDecoder = new BufferDecoder(resource); |
|
188 |
|
189 // If you change this list to add support for new decoders, please consider |
|
190 // updating HTMLMediaElement::CreateDecoder as well. |
|
191 |
|
192 mDecoderReader = DecoderTraits::CreateReader(mContentType, mBufferDecoder); |
|
193 |
|
194 if (!mDecoderReader) { |
|
195 return false; |
|
196 } |
|
197 |
|
198 nsresult rv = mDecoderReader->Init(nullptr); |
|
199 if (NS_FAILED(rv)) { |
|
200 return false; |
|
201 } |
|
202 |
|
203 return true; |
|
204 } |
|
205 |
|
206 class AutoResampler { |
|
207 public: |
|
208 AutoResampler() |
|
209 : mResampler(nullptr) |
|
210 {} |
|
211 ~AutoResampler() |
|
212 { |
|
213 if (mResampler) { |
|
214 speex_resampler_destroy(mResampler); |
|
215 } |
|
216 } |
|
217 operator SpeexResamplerState*() const |
|
218 { |
|
219 MOZ_ASSERT(mResampler); |
|
220 return mResampler; |
|
221 } |
|
222 void operator=(SpeexResamplerState* aResampler) |
|
223 { |
|
224 mResampler = aResampler; |
|
225 } |
|
226 |
|
227 private: |
|
228 SpeexResamplerState* mResampler; |
|
229 }; |
|
230 |
|
231 void |
|
232 MediaDecodeTask::Decode() |
|
233 { |
|
234 MOZ_ASSERT(!NS_IsMainThread()); |
|
235 |
|
236 mBufferDecoder->BeginDecoding(NS_GetCurrentThread()); |
|
237 |
|
238 // Tell the decoder reader that we are not going to play the data directly, |
|
239 // and that we should not reject files with more channels than the audio |
|
240 // bakend support. |
|
241 mDecoderReader->SetIgnoreAudioOutputFormat(); |
|
242 |
|
243 MediaInfo mediaInfo; |
|
244 nsAutoPtr<MetadataTags> tags; |
|
245 nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags)); |
|
246 if (NS_FAILED(rv)) { |
|
247 ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); |
|
248 return; |
|
249 } |
|
250 |
|
251 if (!mDecoderReader->HasAudio()) { |
|
252 ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio); |
|
253 return; |
|
254 } |
|
255 |
|
256 while (mDecoderReader->DecodeAudioData()) { |
|
257 // consume all of the buffer |
|
258 continue; |
|
259 } |
|
260 |
|
261 MediaQueue<AudioData>& audioQueue = mDecoderReader->AudioQueue(); |
|
262 uint32_t frameCount = audioQueue.FrameCount(); |
|
263 uint32_t channelCount = mediaInfo.mAudio.mChannels; |
|
264 uint32_t sampleRate = mediaInfo.mAudio.mRate; |
|
265 |
|
266 if (!frameCount || !channelCount || !sampleRate) { |
|
267 ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); |
|
268 return; |
|
269 } |
|
270 |
|
271 const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate(); |
|
272 AutoResampler resampler; |
|
273 |
|
274 uint32_t resampledFrames = frameCount; |
|
275 if (sampleRate != destSampleRate) { |
|
276 resampledFrames = static_cast<uint32_t>( |
|
277 static_cast<uint64_t>(destSampleRate) * |
|
278 static_cast<uint64_t>(frameCount) / |
|
279 static_cast<uint64_t>(sampleRate) |
|
280 ); |
|
281 |
|
282 resampler = speex_resampler_init(channelCount, |
|
283 sampleRate, |
|
284 destSampleRate, |
|
285 SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); |
|
286 speex_resampler_skip_zeros(resampler); |
|
287 resampledFrames += speex_resampler_get_output_latency(resampler); |
|
288 } |
|
289 |
|
290 // Allocate the channel buffers. Note that if we end up resampling, we may |
|
291 // write fewer bytes than mResampledFrames to the output buffer, in which |
|
292 // case mWriteIndex will tell us how many valid samples we have. |
|
293 static const fallible_t fallible = fallible_t(); |
|
294 bool memoryAllocationSuccess = true; |
|
295 if (!mDecodeJob.mChannelBuffers.SetLength(channelCount)) { |
|
296 memoryAllocationSuccess = false; |
|
297 } else { |
|
298 for (uint32_t i = 0; i < channelCount; ++i) { |
|
299 mDecodeJob.mChannelBuffers[i] = new(fallible) float[resampledFrames]; |
|
300 if (!mDecodeJob.mChannelBuffers[i]) { |
|
301 memoryAllocationSuccess = false; |
|
302 break; |
|
303 } |
|
304 } |
|
305 } |
|
306 if (!memoryAllocationSuccess) { |
|
307 ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); |
|
308 return; |
|
309 } |
|
310 |
|
311 nsAutoPtr<AudioData> audioData; |
|
312 while ((audioData = audioQueue.PopFront())) { |
|
313 audioData->EnsureAudioBuffer(); // could lead to a copy :( |
|
314 AudioDataValue* bufferData = static_cast<AudioDataValue*> |
|
315 (audioData->mAudioBuffer->Data()); |
|
316 |
|
317 if (sampleRate != destSampleRate) { |
|
318 const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; |
|
319 |
|
320 for (uint32_t i = 0; i < audioData->mChannels; ++i) { |
|
321 uint32_t inSamples = audioData->mFrames; |
|
322 uint32_t outSamples = maxOutSamples; |
|
323 |
|
324 WebAudioUtils::SpeexResamplerProcess( |
|
325 resampler, i, &bufferData[i * audioData->mFrames], &inSamples, |
|
326 mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, |
|
327 &outSamples); |
|
328 |
|
329 if (i == audioData->mChannels - 1) { |
|
330 mDecodeJob.mWriteIndex += outSamples; |
|
331 MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); |
|
332 MOZ_ASSERT(inSamples == audioData->mFrames); |
|
333 } |
|
334 } |
|
335 } else { |
|
336 for (uint32_t i = 0; i < audioData->mChannels; ++i) { |
|
337 ConvertAudioSamples(&bufferData[i * audioData->mFrames], |
|
338 mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, |
|
339 audioData->mFrames); |
|
340 |
|
341 if (i == audioData->mChannels - 1) { |
|
342 mDecodeJob.mWriteIndex += audioData->mFrames; |
|
343 } |
|
344 } |
|
345 } |
|
346 } |
|
347 |
|
348 if (sampleRate != destSampleRate) { |
|
349 uint32_t inputLatency = speex_resampler_get_input_latency(resampler); |
|
350 const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; |
|
351 for (uint32_t i = 0; i < channelCount; ++i) { |
|
352 uint32_t inSamples = inputLatency; |
|
353 uint32_t outSamples = maxOutSamples; |
|
354 |
|
355 WebAudioUtils::SpeexResamplerProcess( |
|
356 resampler, i, (AudioDataValue*)nullptr, &inSamples, |
|
357 mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, |
|
358 &outSamples); |
|
359 |
|
360 if (i == channelCount - 1) { |
|
361 mDecodeJob.mWriteIndex += outSamples; |
|
362 MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); |
|
363 MOZ_ASSERT(inSamples == inputLatency); |
|
364 } |
|
365 } |
|
366 } |
|
367 |
|
368 mPhase = PhaseEnum::AllocateBuffer; |
|
369 NS_DispatchToMainThread(this); |
|
370 } |
|
371 |
|
372 void |
|
373 MediaDecodeTask::AllocateBuffer() |
|
374 { |
|
375 MOZ_ASSERT(NS_IsMainThread()); |
|
376 |
|
377 if (!mDecodeJob.AllocateBuffer()) { |
|
378 ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); |
|
379 return; |
|
380 } |
|
381 |
|
382 mPhase = PhaseEnum::Done; |
|
383 CallbackTheResult(); |
|
384 } |
|
385 |
|
386 void |
|
387 MediaDecodeTask::CallbackTheResult() |
|
388 { |
|
389 MOZ_ASSERT(NS_IsMainThread()); |
|
390 |
|
391 Cleanup(); |
|
392 |
|
393 // Now, we're ready to call the script back with the resulting buffer |
|
394 mDecodeJob.OnSuccess(WebAudioDecodeJob::NoError); |
|
395 } |
|
396 |
|
397 bool |
|
398 WebAudioDecodeJob::AllocateBuffer() |
|
399 { |
|
400 MOZ_ASSERT(!mOutput); |
|
401 MOZ_ASSERT(NS_IsMainThread()); |
|
402 |
|
403 // First, get a JSContext |
|
404 AutoPushJSContext cx(mContext->GetJSContext()); |
|
405 if (!cx) { |
|
406 return false; |
|
407 } |
|
408 // Now create the AudioBuffer |
|
409 ErrorResult rv; |
|
410 mOutput = AudioBuffer::Create(mContext, mChannelBuffers.Length(), |
|
411 mWriteIndex, mContext->SampleRate(), cx, rv); |
|
412 if (rv.Failed()) { |
|
413 return false; |
|
414 } |
|
415 |
|
416 for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) { |
|
417 mOutput->SetRawChannelContents(cx, i, mChannelBuffers[i]); |
|
418 } |
|
419 |
|
420 return true; |
|
421 } |
|
422 |
|
423 void |
|
424 MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer, |
|
425 uint32_t aLength, |
|
426 WebAudioDecodeJob& aDecodeJob) |
|
427 { |
|
428 // Do not attempt to decode the media if we were not successful at sniffing |
|
429 // the content type. |
|
430 if (!*aContentType || |
|
431 strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0) { |
|
432 nsCOMPtr<nsIRunnable> event = |
|
433 new ReportResultTask(aDecodeJob, |
|
434 &WebAudioDecodeJob::OnFailure, |
|
435 WebAudioDecodeJob::UnknownContent); |
|
436 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
437 return; |
|
438 } |
|
439 |
|
440 if (!EnsureThreadPoolInitialized()) { |
|
441 nsCOMPtr<nsIRunnable> event = |
|
442 new ReportResultTask(aDecodeJob, |
|
443 &WebAudioDecodeJob::OnFailure, |
|
444 WebAudioDecodeJob::UnknownError); |
|
445 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
446 return; |
|
447 } |
|
448 |
|
449 MOZ_ASSERT(mThreadPool); |
|
450 |
|
451 nsRefPtr<MediaDecodeTask> task = |
|
452 new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, mThreadPool); |
|
453 if (!task->CreateReader()) { |
|
454 nsCOMPtr<nsIRunnable> event = |
|
455 new ReportResultTask(aDecodeJob, |
|
456 &WebAudioDecodeJob::OnFailure, |
|
457 WebAudioDecodeJob::UnknownError); |
|
458 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
459 } else { |
|
460 mThreadPool->Dispatch(task, nsIThreadPool::DISPATCH_NORMAL); |
|
461 } |
|
462 } |
|
463 |
|
464 bool |
|
465 MediaBufferDecoder::EnsureThreadPoolInitialized() |
|
466 { |
|
467 if (!mThreadPool) { |
|
468 mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); |
|
469 if (!mThreadPool) { |
|
470 return false; |
|
471 } |
|
472 mThreadPool->SetName(NS_LITERAL_CSTRING("MediaBufferDecoder")); |
|
473 } |
|
474 return true; |
|
475 } |
|
476 |
|
477 void |
|
478 MediaBufferDecoder::Shutdown() { |
|
479 if (mThreadPool) { |
|
480 // Setting threadLimit to 0 causes threads to exit when all events have |
|
481 // been run, like nsIThreadPool::Shutdown(), but doesn't run a nested event |
|
482 // loop nor wait until this has happened. |
|
483 mThreadPool->SetThreadLimit(0); |
|
484 mThreadPool = nullptr; |
|
485 } |
|
486 } |
|
487 |
|
488 WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType, |
|
489 AudioContext* aContext, |
|
490 DecodeSuccessCallback* aSuccessCallback, |
|
491 DecodeErrorCallback* aFailureCallback) |
|
492 : mContentType(aContentType) |
|
493 , mWriteIndex(0) |
|
494 , mContext(aContext) |
|
495 , mSuccessCallback(aSuccessCallback) |
|
496 , mFailureCallback(aFailureCallback) |
|
497 { |
|
498 MOZ_ASSERT(aContext); |
|
499 MOZ_ASSERT(aSuccessCallback); |
|
500 MOZ_ASSERT(NS_IsMainThread()); |
|
501 MOZ_COUNT_CTOR(WebAudioDecodeJob); |
|
502 } |
|
503 |
|
504 WebAudioDecodeJob::~WebAudioDecodeJob() |
|
505 { |
|
506 MOZ_ASSERT(NS_IsMainThread()); |
|
507 MOZ_COUNT_DTOR(WebAudioDecodeJob); |
|
508 } |
|
509 |
|
510 void |
|
511 WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode) |
|
512 { |
|
513 MOZ_ASSERT(NS_IsMainThread()); |
|
514 MOZ_ASSERT(aErrorCode == NoError); |
|
515 |
|
516 // Ignore errors in calling the callback, since there is not much that we can |
|
517 // do about it here. |
|
518 ErrorResult rv; |
|
519 mSuccessCallback->Call(*mOutput, rv); |
|
520 |
|
521 mContext->RemoveFromDecodeQueue(this); |
|
522 } |
|
523 |
|
524 void |
|
525 WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode) |
|
526 { |
|
527 MOZ_ASSERT(NS_IsMainThread()); |
|
528 |
|
529 const char* errorMessage; |
|
530 switch (aErrorCode) { |
|
531 case NoError: |
|
532 MOZ_ASSERT(false, "Who passed NoError to OnFailure?"); |
|
533 // Fall through to get some sort of a sane error message if this actually |
|
534 // happens at runtime. |
|
535 case UnknownError: |
|
536 errorMessage = "MediaDecodeAudioDataUnknownError"; |
|
537 break; |
|
538 case UnknownContent: |
|
539 errorMessage = "MediaDecodeAudioDataUnknownContentType"; |
|
540 break; |
|
541 case InvalidContent: |
|
542 errorMessage = "MediaDecodeAudioDataInvalidContent"; |
|
543 break; |
|
544 case NoAudio: |
|
545 errorMessage = "MediaDecodeAudioDataNoAudio"; |
|
546 break; |
|
547 } |
|
548 |
|
549 nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(mContext->GetParentObject()); |
|
550 nsIDocument* doc = nullptr; |
|
551 if (pWindow) { |
|
552 doc = pWindow->GetExtantDoc(); |
|
553 } |
|
554 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, |
|
555 NS_LITERAL_CSTRING("Media"), |
|
556 doc, |
|
557 nsContentUtils::eDOM_PROPERTIES, |
|
558 errorMessage); |
|
559 |
|
560 // Ignore errors in calling the callback, since there is not much that we can |
|
561 // do about it here. |
|
562 if (mFailureCallback) { |
|
563 ErrorResult rv; |
|
564 mFailureCallback->Call(rv); |
|
565 } |
|
566 |
|
567 mContext->RemoveFromDecodeQueue(this); |
|
568 } |
|
569 |
|
570 size_t |
|
571 WebAudioDecodeJob::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
572 { |
|
573 size_t amount = 0; |
|
574 amount += mContentType.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf); |
|
575 if (mSuccessCallback) { |
|
576 amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf); |
|
577 } |
|
578 if (mFailureCallback) { |
|
579 amount += mFailureCallback->SizeOfIncludingThis(aMallocSizeOf); |
|
580 } |
|
581 if (mOutput) { |
|
582 amount += mOutput->SizeOfIncludingThis(aMallocSizeOf); |
|
583 } |
|
584 amount += mChannelBuffers.SizeOfExcludingThis(aMallocSizeOf); |
|
585 for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) { |
|
586 amount += mChannelBuffers[i].SizeOfExcludingThis(aMallocSizeOf); |
|
587 } |
|
588 return amount; |
|
589 } |
|
590 |
|
591 } |
|
592 |