michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MediaTaskQueue.h" michael@0: #include "FFmpegRuntimeLinker.h" michael@0: michael@0: #include "FFmpegAACDecoder.h" michael@0: michael@0: #define MAX_CHANNELS 16 michael@0: michael@0: typedef mp4_demuxer::MP4Sample MP4Sample; michael@0: michael@0: namespace mozilla michael@0: { michael@0: michael@0: FFmpegAACDecoder::FFmpegAACDecoder( michael@0: MediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, michael@0: const mp4_demuxer::AudioDecoderConfig &aConfig) michael@0: : FFmpegDataDecoder(aTaskQueue, AV_CODEC_ID_AAC) michael@0: , mCallback(aCallback) michael@0: , mConfig(aConfig) michael@0: { michael@0: MOZ_COUNT_CTOR(FFmpegAACDecoder); michael@0: } michael@0: michael@0: nsresult michael@0: FFmpegAACDecoder::Init() michael@0: { michael@0: nsresult rv = FFmpegDataDecoder::Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static AudioDataValue* michael@0: CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumSamples) michael@0: { michael@0: // These are the only two valid AAC packet sizes. michael@0: NS_ASSERTION(aNumSamples == 960 || aNumSamples == 1024, michael@0: "Should have exactly one AAC audio packet."); michael@0: MOZ_ASSERT(aNumChannels <= MAX_CHANNELS); michael@0: michael@0: nsAutoArrayPtr audio( michael@0: new AudioDataValue[aNumChannels * aNumSamples]); michael@0: michael@0: AudioDataValue** data = reinterpret_cast(aFrame->data); michael@0: michael@0: if (aFrame->format == AV_SAMPLE_FMT_FLT) { michael@0: // Audio data already packed. No need to do anything other than copy it michael@0: // into a buffer we own. michael@0: memcpy(audio, data[0], aNumChannels * aNumSamples * sizeof(AudioDataValue)); michael@0: } else if (aFrame->format == AV_SAMPLE_FMT_FLTP) { michael@0: // Planar audio data. Pack it into something we can understand. michael@0: for (uint32_t channel = 0; channel < aNumChannels; channel++) { michael@0: for (uint32_t sample = 0; sample < aNumSamples; sample++) { michael@0: audio[sample * aNumChannels + channel] = data[channel][sample]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return audio.forget(); michael@0: } michael@0: michael@0: void michael@0: FFmpegAACDecoder::DecodePacket(MP4Sample* aSample) michael@0: { michael@0: nsAutoPtr frame(avcodec_alloc_frame()); michael@0: avcodec_get_frame_defaults(frame); michael@0: michael@0: AVPacket packet; michael@0: av_init_packet(&packet); michael@0: michael@0: packet.data = &(*aSample->data)[0]; michael@0: packet.size = aSample->data->size(); michael@0: packet.pos = aSample->byte_offset; michael@0: packet.dts = aSample->decode_timestamp; michael@0: michael@0: int decoded; michael@0: int bytesConsumed = michael@0: avcodec_decode_audio4(&mCodecContext, frame.get(), &decoded, &packet); michael@0: michael@0: if (bytesConsumed < 0 || !decoded) { michael@0: NS_WARNING("FFmpeg audio decoder error."); michael@0: mCallback->Error(); michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(bytesConsumed == (int)aSample->data->size(), michael@0: "Only one audio packet should be received at a time."); michael@0: michael@0: uint32_t numChannels = mCodecContext.channels; michael@0: michael@0: nsAutoArrayPtr audio( michael@0: CopyAndPackAudio(frame.get(), numChannels, frame->nb_samples)); michael@0: michael@0: nsAutoPtr data(new AudioData(packet.pos, aSample->decode_timestamp, michael@0: aSample->duration, frame->nb_samples, michael@0: audio.forget(), numChannels)); michael@0: michael@0: mCallback->Output(data.forget()); michael@0: michael@0: if (mTaskQueue->IsEmpty()) { michael@0: mCallback->InputExhausted(); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: FFmpegAACDecoder::Input(MP4Sample* aSample) michael@0: { michael@0: mTaskQueue->Dispatch(NS_NewRunnableMethodWithArg >( michael@0: this, &FFmpegAACDecoder::DecodePacket, nsAutoPtr(aSample))); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: FFmpegAACDecoder::Drain() michael@0: { michael@0: // AAC is never delayed; nothing to do here. michael@0: return NS_OK; michael@0: } michael@0: michael@0: FFmpegAACDecoder::~FFmpegAACDecoder() { michael@0: MOZ_COUNT_DTOR(FFmpegAACDecoder); michael@0: } michael@0: michael@0: } // namespace mozilla