From e430428703c149f400922399fa42c56a92023bb9 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sat, 31 Mar 2018 03:12:00 -0600 Subject: [PATCH] ffmpeg: Add async audio decoding loop This leverages libavcodec >= 57.37.100's new asynchronous API, which both allows decoding in hardware and in a separate thread, and in any case would free up more CPU time for Panda's app loop. This also avoids use of the now-deprecated `avcodec_decode_audio4` --- panda/src/ffmpeg/ffmpegAudioCursor.cxx | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/panda/src/ffmpeg/ffmpegAudioCursor.cxx b/panda/src/ffmpeg/ffmpegAudioCursor.cxx index bb12085774..651ec7cfb6 100644 --- a/panda/src/ffmpeg/ffmpegAudioCursor.cxx +++ b/panda/src/ffmpeg/ffmpegAudioCursor.cxx @@ -299,6 +299,62 @@ fetch_packet() { */ bool FfmpegAudioCursor:: reload_buffer() { +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 100) + // lavc >= 57.37.100 deprecates the old (avcodec_decode_audio*) API in favor + // of a newer, asynchronous API. This is great for our purposes - it gives + // the codec the opportunity to decode in the background (e.g. in another + // thread or on a dedicated hardware coprocessor). + + // First, let's fill the codec's input buffer with as many packets as it'll + // take: + int ret; + while (_packet->data != nullptr) { + ret = avcodec_send_packet(_audio_ctx, _packet); + + if (ret != 0) { + // Nonzero return code is an error. + break; + } + + // If we got here, the codec took the packet! Fetch another one. + fetch_packet(); + if (_packet->data == nullptr) { + // fetch_packet() says we're out of packets. Let the codec know. + ret = avcodec_send_packet(_audio_ctx, NULL); + } + } + + // Expected ret codes are 0 (we ran out of packets) and EAGAIN (codec full) + if ((ret != 0) && (ret != AVERROR(EAGAIN))) { + // Some odd error happened. We can't proceed. + ffmpeg_cat.error() + << "avcodec_send_packet returned " << ret << "\n"; + return false; + } + + // Now we retrieve our frame! + ret = avcodec_receive_frame(_audio_ctx, _frame); + + if (ret == AVERROR_EOF) { + // The only way for this to happen is if we're out of packets. + nassertr(_packet->data == nullptr, false); + + // Synthesize silence: + _buffer_head = 0; + _buffer_tail = _buffer_size; + memset(_buffer, 0, _buffer_size * 2); + return true; + + } else if (ret != 0) { + // Some odd error happened. We can't proceed. + ffmpeg_cat.error() + << "avcodec_receive_frame returned " << ret << "\n"; + return false; + } + + // We now have _frame. It will be handled below. + +#else int got_frame = 0; while (!got_frame) { // If we're out of packets, generate silence. @@ -339,6 +395,7 @@ reload_buffer() { _packet_data += len; _packet_size -= len; } +#endif int bufsize; #ifdef HAVE_SWRESAMPLE