void MediaDecodeTask::AllocateBuffer() { MOZ_ASSERT(NS_IsMainThread()); if (!mDecodeJob.AllocateBuffer()) { ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } mPhase = PhaseEnum::Done; CallbackTheResult(); }
void MediaDecodeTask::Decode() { MOZ_ASSERT(!mThreadPool == NS_IsMainThread(), "We should be on the main thread only if we don't have a thread pool"); mBufferDecoder->BeginDecoding(NS_GetCurrentThread()); // Tell the decoder reader that we are not going to play the data directly, // and that we should not reject files with more channels than the audio // bakend support. mDecoderReader->SetIgnoreAudioOutputFormat(); mDecoderReader->OnDecodeThreadStart(); VideoInfo videoInfo; nsAutoPtr<MetadataTags> tags; nsresult rv = mDecoderReader->ReadMetadata(&videoInfo, getter_Transfers(tags)); if (NS_FAILED(rv)) { ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); return; } if (!mDecoderReader->HasAudio()) { ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio); return; } while (mDecoderReader->DecodeAudioData()) { // consume all of the buffer continue; } mDecoderReader->OnDecodeThreadFinish(); MediaQueue<AudioData>& audioQueue = mDecoderReader->AudioQueue(); uint32_t frameCount = audioQueue.FrameCount(); uint32_t channelCount = videoInfo.mAudioChannels; uint32_t sampleRate = videoInfo.mAudioRate; if (!frameCount || !channelCount || !sampleRate) { ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); return; } const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate(); AutoResampler resampler; uint32_t resampledFrames = frameCount; if (sampleRate != destSampleRate) { resampledFrames = static_cast<uint32_t>( static_cast<uint64_t>(destSampleRate) * static_cast<uint64_t>(frameCount) / static_cast<uint64_t>(sampleRate) ); resampler = speex_resampler_init(channelCount, sampleRate, destSampleRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); speex_resampler_skip_zeros(resampler); resampledFrames += speex_resampler_get_output_latency(resampler); } // Allocate the channel buffers. Note that if we end up resampling, we may // write fewer bytes than mResampledFrames to the output buffer, in which // case mWriteIndex will tell us how many valid samples we have. static const fallible_t fallible = fallible_t(); bool memoryAllocationSuccess = true; if (!mDecodeJob.mChannelBuffers.SetLength(channelCount)) { memoryAllocationSuccess = false; } else { for (uint32_t i = 0; i < channelCount; ++i) { mDecodeJob.mChannelBuffers[i] = new(fallible) float[resampledFrames]; if (!mDecodeJob.mChannelBuffers[i]) { memoryAllocationSuccess = false; break; } } } if (!memoryAllocationSuccess) { ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } nsAutoPtr<AudioData> audioData; while ((audioData = audioQueue.PopFront())) { audioData->EnsureAudioBuffer(); // could lead to a copy :( AudioDataValue* bufferData = static_cast<AudioDataValue*> (audioData->mAudioBuffer->Data()); if (sampleRate != destSampleRate) { const uint32_t expectedOutSamples = static_cast<uint32_t>( static_cast<uint64_t>(destSampleRate) * static_cast<uint64_t>(audioData->mFrames) / static_cast<uint64_t>(sampleRate) ); #ifdef MOZ_SAMPLE_TYPE_S16 AudioDataValue* resampledBuffer = new(fallible) AudioDataValue[channelCount * expectedOutSamples]; #endif for (uint32_t i = 0; i < audioData->mChannels; ++i) { uint32_t inSamples = audioData->mFrames; uint32_t outSamples = expectedOutSamples; #ifdef MOZ_SAMPLE_TYPE_S16 speex_resampler_process_int(resampler, i, &bufferData[i * audioData->mFrames], &inSamples, &resampledBuffer[i * expectedOutSamples], &outSamples); ConvertAudioSamples(&resampledBuffer[i * expectedOutSamples], mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, outSamples); #else speex_resampler_process_float(resampler, i, &bufferData[i * audioData->mFrames], &inSamples, mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, &outSamples); #endif if (i == audioData->mChannels - 1) { mDecodeJob.mWriteIndex += outSamples; MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); } } #ifdef MOZ_SAMPLE_TYPE_S16 delete[] resampledBuffer; #endif } else { for (uint32_t i = 0; i < audioData->mChannels; ++i) { ConvertAudioSamples(&bufferData[i * audioData->mFrames], mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, audioData->mFrames); if (i == audioData->mChannels - 1) { mDecodeJob.mWriteIndex += audioData->mFrames; } } } } if (sampleRate != destSampleRate) { int inputLatency = speex_resampler_get_input_latency(resampler); int outputLatency = speex_resampler_get_output_latency(resampler); AudioDataValue* zero = (AudioDataValue*)calloc(inputLatency, sizeof(AudioDataValue)); #ifdef MOZ_SAMPLE_TYPE_S16 AudioDataValue* resampledBuffer = new(fallible) AudioDataValue[channelCount * outputLatency]; if (!resampledBuffer || !zero) { #else if (!zero) { #endif // Out of memory! ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } for (uint32_t i = 0; i < channelCount; ++i) { uint32_t inSamples = inputLatency; uint32_t outSamples = outputLatency; #ifdef MOZ_SAMPLE_TYPE_S16 speex_resampler_process_int(resampler, i, zero, &inSamples, &resampledBuffer[i * outputLatency], &outSamples); ConvertAudioSamples(&resampledBuffer[i * outputLatency], mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, outSamples); #else speex_resampler_process_float(resampler, i, zero, &inSamples, mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, &outSamples); #endif if (i == channelCount - 1) { mDecodeJob.mWriteIndex += outSamples; MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); } } free(zero); #ifdef MOZ_SAMPLE_TYPE_S16 delete[] resampledBuffer; #endif } mPhase = PhaseEnum::AllocateBuffer; RunNextPhase(); } void MediaDecodeTask::AllocateBuffer() { MOZ_ASSERT(NS_IsMainThread()); if (!mDecodeJob.AllocateBuffer()) { ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } mPhase = PhaseEnum::Done; CallbackTheResult(); }