FFmpeg解码RTSP流时avcodec_send_packet若干帧后返回-11错误
FFmpeg解码RTSP流到OpenCV cv::Mat时avcodec_send_packet返回-11错误
问题概述
代码编译通过,但解码RTSP视频流若干帧后,avcodec_send_packet返回错误码-11(对应AVERROR(EAGAIN))导致程序终止。已确认RTSP流正常,FFmpeg命令行可正常转码该流。
核心错误原因
- 错误处理逻辑冲突:自定义
checkError函数在错误码<0时直接调用exit终止程序,但后续代码试图处理EAGAIN错误,这部分逻辑永远无法执行。EAGAIN表示解码器需要更多数据,不应直接终止程序。 - AVFrame释放方式错误:使用
av_freep(frame);释放AVFrame结构体,正确方式应为av_frame_free(&frame);。 - 缺少RTSP流适配设置:未配置RTSP相关的超时、缓冲参数,网络波动时易导致解码异常。
- 解码循环不完整:
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
相关产品推荐
相关产品推荐

