Handling AVERROR(EAGAIN) and AVERROR_EOF in FFmpeg

Working with the FFmpeg C API requires robust error handling, especially when decoding video or audio streams. This article explains how to properly handle the AVERROR(EAGAIN) and AVERROR_EOF return codes during the packet-sending and frame-receiving process to ensure your application decodes media streams without crashes or data loss.


The FFmpeg Decoding Loop

FFmpeg uses a push-pull model for decoding, split into two main functions: avcodec_send_packet() and avcodec_receive_frame().

Because one input packet can produce zero, one, or multiple output frames, you must handle specific non-fatal return codes to manage the flow of data.


Handling AVERROR(EAGAIN)

AVERROR(EAGAIN) is not a critical error; it is a status indicator telling you that the state of the codec context must change before you can proceed with the current operation.

1. In avcodec_send_packet()

If avcodec_send_packet() returns AVERROR(EAGAIN), it means the decoder’s internal buffer is full. It cannot accept new packets until you drain the existing decoded frames.

2. In avcodec_receive_frame()

If avcodec_receive_frame() returns AVERROR(EAGAIN), it means the decoder requires more input data to produce a new frame.


Handling AVERROR_EOF

AVERROR_EOF signals the “End of File” or end of the stream. Like AVERROR(EAGAIN), it is a control flow signal rather than a failure.

1. In avcodec_send_packet()

If you receive AVERROR_EOF here, it means the decoder has been flushed (you sent a NULL packet to signal the end of the stream) and is in draining port mode. No further packets can be sent to this decoder context.

2. In avcodec_receive_frame()

When avcodec_receive_frame() returns AVERROR_EOF, the decoder has finished processing all buffered packets and has output its final frames.


Code Implementation Example

The following C code demonstrates how to structure a standard decoding loop to handle both return codes correctly.

AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
int response = 0;

while (av_read_frame(format_context, packet) >= 0) {
    if (packet->stream_index == video_stream_index) {
        
        // Send the packet to the decoder
        response = avcodec_send_packet(codec_context, packet);
        if (response < 0) {
            // ERROR(EAGAIN) here means we must drain the decoder before sending more packets
            if (response != AVERROR(EAGAIN) && response != AVERROR_EOF) {
                fprintf(stderr, "Error sending packet to decoder\n");
                return response;
            }
        }

        // Receive decoded frames from the decoder
        while (response >= 0) {
            response = avcodec_receive_frame(codec_context, frame);
            if (response == AVERROR(EAGAIN)) {
                // Decoder needs more packets to produce a frame; safe to break and read next packet
                break;
            } else if (response == AVERROR_EOF) {
                // End of file/stream reached cleanly
                printf("Decoder flushed, end of stream reached.\n");
                break;
            } else if (response < 0) {
                fprintf(stderr, "Error receiving frame from decoder\n");
                return response;
            }

            // Successfully received a frame; process it here
            process_decoded_frame(frame);
            av_frame_unref(frame);
        }
    }
    av_packet_unref(packet);
}

// Clean up resources
av_packet_free(&packet);
av_frame_free(&frame);