void
MediaDecodeTask::AllocateBuffer()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (!mDecodeJob.AllocateBuffer()) {
    ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
    return;
  }

  mPhase = PhaseEnum::Done;
  CallbackTheResult();
}
示例#2
0
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();
}