Properly support asynchronous decoders that return multiple frames at a time

This commit is contained in:
Cameron Gutman 2021-12-11 18:15:49 -06:00
parent 9579b2c85e
commit 80128e8293

View File

@ -902,6 +902,7 @@ int FFmpegVideoDecoder::submitDecodeUnit(PDECODE_UNIT du)
{ {
PLENTRY entry = du->bufferList; PLENTRY entry = du->bufferList;
int err; int err;
bool submittedFrame = false;
SDL_assert(!m_TestOnly); SDL_assert(!m_TestOnly);
@ -991,48 +992,61 @@ int FFmpegVideoDecoder::submitDecodeUnit(PDECODE_UNIT du)
m_FramesIn++; m_FramesIn++;
AVFrame* frame = av_frame_alloc(); // We can receive 0 or more frames after submission of a packet, so we must
if (!frame) { // try to read until we get EAGAIN to ensure the queue is drained. Some decoders
// Failed to allocate a frame but we did submit, // run asynchronously and may return several frames at once after warming up.
// so we can return DR_OK do {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, AVFrame* frame = av_frame_alloc();
"Failed to allocate frame"); if (!frame) {
return DR_OK; // Failed to allocate a frame but we did submit,
} // so we can return DR_OK
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to allocate frame");
return DR_OK;
}
err = avcodec_receive_frame(m_VideoDecoderCtx, frame); err = avcodec_receive_frame(m_VideoDecoderCtx, frame);
if (err == 0) { if (err == 0) {
m_FramesOut++; m_FramesOut++;
// Reset failed decodes count if we reached this far // Reset failed decodes count if we reached this far
m_ConsecutiveFailedDecodes = 0; m_ConsecutiveFailedDecodes = 0;
// Restore default log level after a successful decode // Restore default log level after a successful decode
av_log_set_level(AV_LOG_INFO); av_log_set_level(AV_LOG_INFO);
// Store the presentation time // Store the presentation time
frame->pts = du->presentationTimeMs; // FIXME: This is wrong when reading a batch of frames
frame->pts = du->presentationTimeMs;
// Capture a frame timestamp to measuring pacing delay // Capture a frame timestamp to measuring pacing delay
frame->pkt_dts = SDL_GetTicks(); frame->pkt_dts = SDL_GetTicks();
// Count time in avcodec_send_packet() and avcodec_receive_frame() // Count time in avcodec_send_packet() and avcodec_receive_frame()
// as time spent decoding. Also count time spent in the decode unit // as time spent decoding. Also count time spent in the decode unit
// queue because that's directly caused by decoder latency. // queue because that's directly caused by decoder latency.
m_ActiveWndVideoStats.totalDecodeTime += LiGetMillis() - du->enqueueTimeMs; m_ActiveWndVideoStats.totalDecodeTime += LiGetMillis() - du->enqueueTimeMs;
// Also count the frame-to-frame delay if the decoder is delaying frames // Also count the frame-to-frame delay if the decoder is delaying frames
// until a subsequent frame is submitted. // until a subsequent frame is submitted.
m_ActiveWndVideoStats.totalDecodeTime += (m_FramesIn - m_FramesOut) * (1000 / m_StreamFps); m_ActiveWndVideoStats.totalDecodeTime += (m_FramesIn - m_FramesOut) * (1000 / m_StreamFps);
m_ActiveWndVideoStats.decodedFrames++; m_ActiveWndVideoStats.decodedFrames++;
// Queue the frame for rendering (or render now if pacer is disabled) // Queue the frame for rendering (or render now if pacer is disabled)
m_Pacer->submitFrame(frame); m_Pacer->submitFrame(frame);
} submittedFrame = true;
else { }
av_frame_free(&frame); else {
av_frame_free(&frame);
}
} while (err == 0);
// Treat this as a failed decode if we don't manage to receive a single frame or
// if we finish the loop above with an error other than EAGAIN. Note that some
// limited number of "failed decodes" with EAGAIN are expected for asynchronous
// decoders, so we only reset the decoder if we get a ton of them in a row.
if (!submittedFrame || err != AVERROR(EAGAIN)) {
char errorstring[512]; char errorstring[512];
av_strerror(err, errorstring, sizeof(errorstring)); av_strerror(err, errorstring, sizeof(errorstring));
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,