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 "MediaTaskQueue.h" |
michael@0 | 8 | #include "FFmpegRuntimeLinker.h" |
michael@0 | 9 | |
michael@0 | 10 | #include "FFmpegAACDecoder.h" |
michael@0 | 11 | |
michael@0 | 12 | #define MAX_CHANNELS 16 |
michael@0 | 13 | |
michael@0 | 14 | typedef mp4_demuxer::MP4Sample MP4Sample; |
michael@0 | 15 | |
michael@0 | 16 | namespace mozilla |
michael@0 | 17 | { |
michael@0 | 18 | |
michael@0 | 19 | FFmpegAACDecoder::FFmpegAACDecoder( |
michael@0 | 20 | MediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, |
michael@0 | 21 | const mp4_demuxer::AudioDecoderConfig &aConfig) |
michael@0 | 22 | : FFmpegDataDecoder(aTaskQueue, AV_CODEC_ID_AAC) |
michael@0 | 23 | , mCallback(aCallback) |
michael@0 | 24 | , mConfig(aConfig) |
michael@0 | 25 | { |
michael@0 | 26 | MOZ_COUNT_CTOR(FFmpegAACDecoder); |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | nsresult |
michael@0 | 30 | FFmpegAACDecoder::Init() |
michael@0 | 31 | { |
michael@0 | 32 | nsresult rv = FFmpegDataDecoder::Init(); |
michael@0 | 33 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 34 | |
michael@0 | 35 | return NS_OK; |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | static AudioDataValue* |
michael@0 | 39 | CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumSamples) |
michael@0 | 40 | { |
michael@0 | 41 | // These are the only two valid AAC packet sizes. |
michael@0 | 42 | NS_ASSERTION(aNumSamples == 960 || aNumSamples == 1024, |
michael@0 | 43 | "Should have exactly one AAC audio packet."); |
michael@0 | 44 | MOZ_ASSERT(aNumChannels <= MAX_CHANNELS); |
michael@0 | 45 | |
michael@0 | 46 | nsAutoArrayPtr<AudioDataValue> audio( |
michael@0 | 47 | new AudioDataValue[aNumChannels * aNumSamples]); |
michael@0 | 48 | |
michael@0 | 49 | AudioDataValue** data = reinterpret_cast<AudioDataValue**>(aFrame->data); |
michael@0 | 50 | |
michael@0 | 51 | if (aFrame->format == AV_SAMPLE_FMT_FLT) { |
michael@0 | 52 | // Audio data already packed. No need to do anything other than copy it |
michael@0 | 53 | // into a buffer we own. |
michael@0 | 54 | memcpy(audio, data[0], aNumChannels * aNumSamples * sizeof(AudioDataValue)); |
michael@0 | 55 | } else if (aFrame->format == AV_SAMPLE_FMT_FLTP) { |
michael@0 | 56 | // Planar audio data. Pack it into something we can understand. |
michael@0 | 57 | for (uint32_t channel = 0; channel < aNumChannels; channel++) { |
michael@0 | 58 | for (uint32_t sample = 0; sample < aNumSamples; sample++) { |
michael@0 | 59 | audio[sample * aNumChannels + channel] = data[channel][sample]; |
michael@0 | 60 | } |
michael@0 | 61 | } |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | return audio.forget(); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | void |
michael@0 | 68 | FFmpegAACDecoder::DecodePacket(MP4Sample* aSample) |
michael@0 | 69 | { |
michael@0 | 70 | nsAutoPtr<AVFrame> frame(avcodec_alloc_frame()); |
michael@0 | 71 | avcodec_get_frame_defaults(frame); |
michael@0 | 72 | |
michael@0 | 73 | AVPacket packet; |
michael@0 | 74 | av_init_packet(&packet); |
michael@0 | 75 | |
michael@0 | 76 | packet.data = &(*aSample->data)[0]; |
michael@0 | 77 | packet.size = aSample->data->size(); |
michael@0 | 78 | packet.pos = aSample->byte_offset; |
michael@0 | 79 | packet.dts = aSample->decode_timestamp; |
michael@0 | 80 | |
michael@0 | 81 | int decoded; |
michael@0 | 82 | int bytesConsumed = |
michael@0 | 83 | avcodec_decode_audio4(&mCodecContext, frame.get(), &decoded, &packet); |
michael@0 | 84 | |
michael@0 | 85 | if (bytesConsumed < 0 || !decoded) { |
michael@0 | 86 | NS_WARNING("FFmpeg audio decoder error."); |
michael@0 | 87 | mCallback->Error(); |
michael@0 | 88 | return; |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | NS_ASSERTION(bytesConsumed == (int)aSample->data->size(), |
michael@0 | 92 | "Only one audio packet should be received at a time."); |
michael@0 | 93 | |
michael@0 | 94 | uint32_t numChannels = mCodecContext.channels; |
michael@0 | 95 | |
michael@0 | 96 | nsAutoArrayPtr<AudioDataValue> audio( |
michael@0 | 97 | CopyAndPackAudio(frame.get(), numChannels, frame->nb_samples)); |
michael@0 | 98 | |
michael@0 | 99 | nsAutoPtr<AudioData> data(new AudioData(packet.pos, aSample->decode_timestamp, |
michael@0 | 100 | aSample->duration, frame->nb_samples, |
michael@0 | 101 | audio.forget(), numChannels)); |
michael@0 | 102 | |
michael@0 | 103 | mCallback->Output(data.forget()); |
michael@0 | 104 | |
michael@0 | 105 | if (mTaskQueue->IsEmpty()) { |
michael@0 | 106 | mCallback->InputExhausted(); |
michael@0 | 107 | } |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | nsresult |
michael@0 | 111 | FFmpegAACDecoder::Input(MP4Sample* aSample) |
michael@0 | 112 | { |
michael@0 | 113 | mTaskQueue->Dispatch(NS_NewRunnableMethodWithArg<nsAutoPtr<MP4Sample> >( |
michael@0 | 114 | this, &FFmpegAACDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample))); |
michael@0 | 115 | |
michael@0 | 116 | return NS_OK; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | nsresult |
michael@0 | 120 | FFmpegAACDecoder::Drain() |
michael@0 | 121 | { |
michael@0 | 122 | // AAC is never delayed; nothing to do here. |
michael@0 | 123 | return NS_OK; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | FFmpegAACDecoder::~FFmpegAACDecoder() { |
michael@0 | 127 | MOZ_COUNT_DTOR(FFmpegAACDecoder); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | } // namespace mozilla |