#include "cutscene/ffmpeg/AudioDecoder.h" #include "tracing/tracing.h" namespace { const int OUT_CH_LAYOUT = AV_CH_LAYOUT_STEREO; const int OUT_SAMPLE_RATE = 48000; const AVSampleFormat OUT_SAMPLE_FORMAT = AV_SAMPLE_FMT_S16; const int OUT_NUM_CHANNELS = av_popcount64(OUT_CH_LAYOUT); const int DEFAULT_SRC_NUM_SAMPLES = 1024; SwrContext* getSWRContext(uint64_t layout, int rate, AVSampleFormat inFmt) { auto swr = swr_alloc(); av_opt_set_int(swr, "in_channel_layout", layout, 0); av_opt_set_int(swr, "in_sample_rate", rate, 0); av_opt_set_int(swr, "in_sample_fmt", inFmt, 0); av_opt_set_int(swr, "out_channel_layout", OUT_CH_LAYOUT, 0); av_opt_set_int(swr, "out_sample_rate", OUT_SAMPLE_RATE, 0); av_opt_set_int(swr, "out_sample_fmt", OUT_SAMPLE_FORMAT, 0); swr_init(swr); return swr; } int alloc_array_and_samples(uint8_t*** outData, int* linesize, int channels, int samples, AVSampleFormat format) { auto planes = av_sample_fmt_is_planar(format) ? channels : 1; *outData = reinterpret_cast(av_mallocz(planes * sizeof(**outData))); auto ret = av_samples_alloc(*outData, linesize, channels, samples, format, 0); if (ret < 0) { av_freep(outData); } return ret; } int64_t getDelay(SwrContext* ctx, int64_t sampleRate) { #ifdef WITH_LIBAV return avresample_get_delay(ctx); #else return swr_get_delay(ctx, sampleRate); #endif } int resample_convert(SwrContext* ctx, uint8_t** output, int out_plane_size, int out_samples, uint8_t** input, int in_plane_size, int in_samples) { #ifdef WITH_LIBAV return avresample_convert(ctx, output, out_plane_size, out_samples, input, in_plane_size, in_samples); #else (void)out_plane_size; (void)in_plane_size; return swr_convert(ctx, output, out_samples, (const uint8_t**) input, in_samples); #endif } } namespace cutscene { namespace ffmpeg { AudioDecoder::AudioDecoder(DecoderStatus* status) : FFMPEGStreamDecoder(status) { m_audioBuffer.reserve(static_cast(OUT_SAMPLE_RATE * OUT_NUM_CHANNELS / 2)); m_resampleCtx = getSWRContext(m_status->audioCodecPars.channel_layout, m_status->audioCodecPars.sample_rate, m_status->audioCodecPars.audio_format); /* * compute the number of converted samples: buffering is avoided * ensuring that the output buffer will contain at least all the * converted input samples */ m_maxOutNumSamples = m_outNumSamples = static_cast(av_rescale_rnd(DEFAULT_SRC_NUM_SAMPLES, OUT_SAMPLE_RATE, m_status->audioCodecPars.sample_rate, AV_ROUND_UP)); auto ret = alloc_array_and_samples(&m_outData, &m_outLinesize, OUT_NUM_CHANNELS, m_outNumSamples, OUT_SAMPLE_FORMAT); if (ret < 0) { mprintf(("FFMPEG: Failed to allocate samples array!\n")); } } AudioDecoder::~AudioDecoder() { if (m_outData) { av_freep(&m_outData[0]); } av_freep(&m_outData); swr_free(&m_resampleCtx); } void AudioDecoder::flushAudioBuffer() { if (m_audioBuffer.empty()) { // Nothing to do here return; } AudioFramePtr audioFrame(new AudioFrame()); audioFrame->channels = OUT_NUM_CHANNELS; audioFrame->rate = OUT_SAMPLE_RATE; audioFrame->audioData.assign(m_audioBuffer.begin(), m_audioBuffer.end()); pushFrame(std::move(audioFrame)); m_audioBuffer.clear(); } void AudioDecoder::handleDecodedFrame(AVFrame* frame) { /* compute destination number of samples */ m_outNumSamples = static_cast(av_rescale_rnd(getDelay(m_resampleCtx, frame->sample_rate) + frame->nb_samples, OUT_SAMPLE_RATE, frame->sample_rate, AV_ROUND_UP)); if (m_outNumSamples > m_maxOutNumSamples) { av_freep(&m_outData[0]); auto ret = av_samples_alloc(m_outData, &m_outLinesize, OUT_NUM_CHANNELS, m_outNumSamples, OUT_SAMPLE_FORMAT, 1); if (ret < 0) { mprintf(("FFMPEG: Failed to allocate samples!!!\n")); return; } m_maxOutNumSamples = m_outNumSamples; } /* convert to destination format */ auto ret = resample_convert(m_resampleCtx, m_outData, 0, m_outNumSamples, (uint8_t**) frame->data, 0, frame->nb_samples); if (ret < 0) { mprintf(("FFMPEG: Error while converting audio!\n")); return; } auto outBufsize = av_samples_get_buffer_size(&m_outLinesize, OUT_NUM_CHANNELS, ret, OUT_SAMPLE_FORMAT, 1); if (outBufsize < 0) { mprintf(("FFMPEG: Could not get sample buffer size!\n")); return; } auto begin = reinterpret_cast(m_outData[0]); auto end = reinterpret_cast(m_outData[0] + outBufsize); auto size = std::distance(begin, end); auto newSize = m_audioBuffer.size() + size; if (newSize <= m_audioBuffer.capacity()) { // We haven't filled the buffer yet m_audioBuffer.insert(m_audioBuffer.end(), begin, end); } else { flushAudioBuffer(); m_audioBuffer.assign(begin, end); } } void AudioDecoder::decodePacket(AVPacket* packet) { TRACE_SCOPE(tracing::CutsceneFFmpegAudioDecoder); #if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255) int send_result; do { send_result = avcodec_send_packet(m_status->audioCodecCtx, packet); while (avcodec_receive_frame(m_status->audioCodecCtx, m_decodeFrame) == 0) { handleDecodedFrame(m_decodeFrame); } } while (send_result == AVERROR(EAGAIN)); #else int finishedFrame = 0; auto err = avcodec_decode_audio4(m_status->audioCodecCtx, m_decodeFrame, &finishedFrame, packet); if (err >= 0 && finishedFrame) { handleDecodedFrame(m_decodeFrame); } #endif } void AudioDecoder::finishDecoding() { TRACE_SCOPE(tracing::CutsceneFFmpegAudioDecoder); #if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255) // Send flush packet avcodec_send_packet(m_status->audioCodecCtx, nullptr); // Handle those decoders that have a delay while (true) { auto ret = avcodec_receive_frame(m_status->audioCodecCtx, m_decodeFrame); if (ret == 0) { handleDecodedFrame(m_decodeFrame); } else { // Everything consumed or error break; } } #else // Handle those decoders that have a delay AVPacket nullPacket; memset(&nullPacket, 0, sizeof(nullPacket)); nullPacket.data = nullptr; nullPacket.size = 0; while (true) { int finishedFrame = 1; auto err = avcodec_decode_audio4(m_status->audioCodecCtx, m_decodeFrame, &finishedFrame, &nullPacket); if (err < 0 || !finishedFrame) { break; } handleDecodedFrame(m_decodeFrame); } #endif // Push the last bits of audio data into the queue flushAudioBuffer(); } void AudioDecoder::flushBuffers() { avcodec_flush_buffers(m_status->audioCodecCtx); } } }