ffmpeg: Handle EAGAIN from decoder and drain

This usually happens with CUVID if the decoder needs to be reset
internally for whatever reason, such as a mid-stream resolution
change.

Also block demuxing until decoder is ready to receive packets again.
This commit is contained in:
Josh Allmann
2024-08-09 06:24:56 +00:00
parent f03385968e
commit def71fab18
2 changed files with 20 additions and 3 deletions

View File

@@ -28,6 +28,7 @@ static int send_flush_pkt(struct input_ctx *ictx)
if (!ictx->flush_pkt) return lpms_ERR_INPUT_NOKF;
int ret = avcodec_send_packet(ictx->vc, ictx->flush_pkt);
if (ret == AVERROR(EAGAIN)) return ret; // decoder is mid-reset
ictx->sentinel_count++;
if (ret < 0) {
LPMS_ERR(packet_cleanup, "Error sending flush packet");
@@ -79,7 +80,14 @@ int decode_in(struct input_ctx *ictx, AVPacket *pkt, AVFrame *frame, int *stream
}
ret = lpms_send_packet(ictx, decoder, pkt);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
// Usually means the decoder needs to drain itself - block demuxing until then
// Seems to happen during mid-stream resolution changes
if (ictx->blocked_pkt) LPMS_ERR_RETURN("unexpectedly got multiple blocked packets");
ictx->blocked_pkt = av_packet_clone(pkt);
if (!ictx->blocked_pkt) LPMS_ERR_RETURN("could not clone packet for blocking");
// continue in an attempt to drain the decoder
} else if (ret < 0) {
LPMS_ERR_RETURN("Error sending packet to decoder");
}
ret = lpms_receive_frame(ictx, decoder, frame);
@@ -110,7 +118,9 @@ int flush_in(struct input_ctx *ictx, AVFrame *frame, int *stream_index)
if (ictx->vc && !ictx->flushed && ictx->pkt_diff > 0) {
ictx->flushing = 1;
ret = send_flush_pkt(ictx);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
// do nothing; decoder recently reset and needs to drain so let it
} else if (ret < 0) {
ictx->flushed = 1;
return ret;
}
@@ -142,7 +152,10 @@ int process_in(struct input_ctx *ictx, AVFrame *frame, AVPacket *pkt,
av_packet_unref(pkt);
// Demux next packet
ret = demux_in(ictx, pkt);
if (ictx->blocked_pkt) {
av_packet_move_ref(pkt, ictx->blocked_pkt);
av_packet_free(&ictx->blocked_pkt);
} else ret = demux_in(ictx, pkt);
// See if we got anything
if (ret == AVERROR_EOF) {
// no more packets, flush the decoder(s)
@@ -381,5 +394,6 @@ void free_input(struct input_ctx *inctx)
if (inctx->hw_device_ctx) av_buffer_unref(&inctx->hw_device_ctx);
if (inctx->last_frame_v) av_frame_free(&inctx->last_frame_v);
if (inctx->last_frame_a) av_frame_free(&inctx->last_frame_a);
if (inctx->blocked_pkt) av_packet_free(&inctx->blocked_pkt);
}

View File

@@ -33,6 +33,9 @@ struct input_ctx {
#define SENTINEL_MAX 8
uint16_t sentinel_count;
// Packet held while decoder is blocked and needs to drain
AVPacket *blocked_pkt;
// Filter flush
AVFrame *last_frame_v, *last_frame_a;