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