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