mirror of
https://github.com/livepeer/lpms
synced 2025-09-26 19:51:36 +08:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user