Explorar el Código

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`
Sam Edwards hace 7 años
padre
commit
e430428703
Se han modificado 1 ficheros con 57 adiciones y 0 borrados
  1. 57 0
      panda/src/ffmpeg/ffmpegAudioCursor.cxx

+ 57 - 0
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