You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FFmpeg解码RTSP流时avcodec_send_packet若干帧后返回-11错误

FFmpeg解码RTSP流到OpenCV cv::Mat时avcodec_send_packet返回-11错误

问题概述

代码编译通过,但解码RTSP视频流若干帧后,avcodec_send_packet返回错误码-11(对应AVERROR(EAGAIN))导致程序终止。已确认RTSP流正常,FFmpeg命令行可正常转码该流。

核心错误原因

  1. 错误处理逻辑冲突:自定义checkError函数在错误码<0时直接调用exit终止程序,但后续代码试图处理EAGAIN错误,这部分逻辑永远无法执行。EAGAIN表示解码器需要更多数据,不应直接终止程序。
  2. AVFrame释放方式错误:使用av_freep(frame);释放AVFrame结构体,正确方式应为av_frame_free(&frame);。
  3. 缺少RTSP流适配设置:未配置RTSP相关的超时、缓冲参数,网络波动时易导致解码异常。
  4. 解码循环不完整:avcodec_receive_frame需循环调用,单个数据包可能解码出多帧。

修正后的代码

#include <iostream>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}

#include <opencv2/opencv.hpp>

// 改进的FFmpeg错误检查函数,非致命错误不终止程序
inline int checkError(int error, const std::string& message, bool fatal = true) {
    if (error < 0) {
        char err_buf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(error, err_buf, sizeof(err_buf));
        std::cerr << message << ": " << err_buf << " (" << error << ")" << std::endl;
        if (fatal) {
            exit(EXIT_FAILURE);
        }
    }
    return error;
}

int main() {
    // 初始化FFmpeg
    av_log_set_level(AV_LOG_WARNING);
    avformat_network_init();

    // 打开RTSP流,设置超时和缓冲参数
    std::string fileName = "rtsp://xyz@192.168.15.112:554//Streaming/Channels/101";
    AVFormatContext* formatContext = nullptr;
    
    // RTSP配置:优先TCP传输、5秒超时、增大缓冲
    AVDictionary* opts = nullptr;
    av_dict_set(&opts, "rtsp_transport", "tcp", 0);
    av_dict_set(&opts, "stimeout", "5000000", 0);
    av_dict_set(&opts, "buffer_size", "1024000", 0);
    
    int error = avformat_open_input(&formatContext, fileName.c_str(), nullptr, &opts);
    av_dict_free(&opts);
    checkError(error, "打开输入流失败");

    // 获取流信息
    error = avformat_find_stream_info(formatContext, nullptr);
    checkError(error, "获取流信息失败");

    // 查找视频流
    AVStream* videoStream = nullptr;
    int videoStreamIdx = -1;
    for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = formatContext->streams[i];
            videoStreamIdx = i;
            break;
        }
    }
    if (!videoStream) {
        std::cerr << "输入流中无视频流" << std::endl;
        exit(EXIT_FAILURE);
    }

    // 创建并初始化解码器上下文
    const AVCodec* videoCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
    AVCodecContext* videoCodecContext = avcodec_alloc_context3(videoCodec);
    if (!videoCodecContext) {
        std::cerr << "分配解码器上下文失败" << std::endl;
        exit(EXIT_FAILURE);
    }
    error = avcodec_parameters_to_context(videoCodecContext, videoStream->codecpar);
    checkError(error, "设置解码器参数失败");
    // 设置解码器线程数提升效率
    videoCodecContext->thread_count = 4;
    error = avcodec_open2(videoCodecContext, videoCodec, nullptr);
    checkError(error, "打开解码器失败");

    // 创建图像转换上下文
    int width = videoCodecContext->width;
    int height = videoCodecContext->height;
    std::cout << "帧宽度: " << width << std::endl;
    std::cout << "帧高度: " << height << std::endl;
    std::cout << "像素格式: " << av_get_pix_fmt_name(videoCodecContext->pix_fmt) << std::endl;

    struct SwsContext* frameScaler = sws_getContext(width, height, videoCodecContext->pix_fmt,
                                                    width, height, AV_PIX_FMT_BGR24,
                                                    SWS_BICUBIC, nullptr, nullptr, nullptr);
    if (!frameScaler) {
        std::cerr << "创建图像转换上下文失败" << std::endl;
        exit(EXIT_FAILURE);
    }

    // 预分配转换后的帧内存,避免重复分配
    AVFrame* scaledFrame = av_frame_alloc();
    int ret = av_image_alloc(scaledFrame->data, scaledFrame->linesize, width, height, AV_PIX_FMT_BGR24, 32);
    checkError(ret, "分配缩放帧内存失败");

    // 读取并解码帧
    std::vector<cv::Mat> videoFrames;
    AVPacket packet;
    av_init_packet(&packet);
    int frameCount = 0;

    while (av_read_frame(formatContext, &packet) == 0) {
        if (packet.stream_index != videoStreamIdx) {
            av_packet_unref(&packet);
            continue;
        }

        // 发送数据包到解码器,处理EAGAIN错误
        error = avcodec_send_packet(videoCodecContext, &packet);
        if (checkError(error, "发送数据包到解码器失败", false) == AVERROR(EAGAIN)) {
            av_packet_unref(&packet);
            continue;
        } else if (error < 0) {
            av_packet_unref(&packet);
            break;
        }

        // 循环接收解码后的帧(单个包可能对应多帧)
        while (true) {
            AVFrame* frame = av_frame_alloc();
            error = avcodec_receive_frame(videoCodecContext, frame);
            if (error == AVERROR(EAGAIN) || error == AVERROR_EOF) {
                av_frame_free(&frame);
                break;
            } else if (checkError(error, "接收解码帧失败", false) < 0) {
                av_frame_free(&frame);
                break;
            }

            // 转换图像格式到BGR24
            sws_scale(frameScaler, frame->data, frame->linesize, 0, height,
                      scaledFrame->data, scaledFrame->linesize);

            // 转换为cv::Mat并保存
            cv::Mat mat(height, width, CV_8UC3, scaledFrame->data[0], scaledFrame->linesize[0]);
            videoFrames.push_back(mat.clone());

            // 可选:实时显示图像
            // cv::imshow("RTSP Stream", mat);
            // if (cv::waitKey(1) == 27) break; // ESC键退出

            std::cout << "已解码帧: " << ++frameCount << std::endl;
            av_frame_free(&frame);
        }

        av_packet_unref(&packet);
        // 检查窗口是否关闭,退出循环
        // if (cv::getWindowProperty("RTSP Stream", cv::WND_PROP_VISIBLE) < 1) break;
    }

    // 释放所有资源
    av_freep(&scaledFrame->data[0]);
    av_frame_free(&scaledFrame);
    sws_freeContext(frameScaler);
    avcodec_free_context(&videoCodecContext);
    avformat_close_input(&formatContext);
    avformat_network_deinit();
    // cv::destroyAllWindows();

    return 0;
}

关键修正点说明

  • 错误处理优化:修改checkError函数,支持非致命错误不终止程序,正确处理EAGAIN错误。
  • RTSP稳定性配置:添加TCP传输、超时时间、缓冲大小设置,降低网络波动对解码的影响。
  • 解码逻辑完善:循环调用avcodec_receive_frame处理单个数据包对应多帧的场景。
  • 资源管理优化:预分配缩放帧内存,使用正确的API释放AVFrame结构体,减少内存泄漏风险。
  • 性能提升:设置解码器线程数,提高解码效率。

内容的提问来源于stack exchange,提问作者Xingdi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:09:51