使用FFmpeg读取视频帧到OpenCV Mat时触发libswscale断言错误
问题:FFmpeg转OpenCV Mat触发swscale断言错误
我尝试用C++的FFmpeg库读取视频所有帧并转换为OpenCV的Mat对象,运行时触发如下断言错误:
Assertion desc failed at libswscale/swscale_internal.h:727
经GDB调试,错误发生在创建sws上下文的代码行:
struct SwsContext *frameScaler = sws_getContext(width, height, videoCodecContext->pix_fmt, width, height, AV_PIX_FMT_BGR24, SWS_BICUBIC, nullptr, nullptr, nullptr);
完整代码如下:
#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 <opencv2/opencv.hpp> // FFmpeg错误检查辅助函数 inline void checkError(int error, const std::string &message) { if (error < 0) { std::cerr << message << ": " << av_err2str(error) << std::endl; exit(EXIT_FAILURE); } } int main() { // 初始化FFmpeg av_log_set_level(AV_LOG_ERROR); avformat_network_init(); // 打开输入文件 std::string fileName = "input.mp4"; AVFormatContext *formatContext = nullptr; int error = avformat_open_input(&formatContext, fileName.c_str(), nullptr, nullptr); checkError(error, "打开输入文件失败"); // 查找视频流 AVStream *videoStream = nullptr; for (unsigned int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !videoStream) { videoStream = formatContext->streams[i]; } } 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, "设置解码器上下文参数失败"); error = avcodec_open2(videoCodecContext, videoCodec, nullptr); checkError(error, "打开视频解码器失败"); // 创建帧缩放器 int width = videoCodecContext->width; int height = videoCodecContext->height; struct SwsContext *frameScaler = sws_getContext(width, height, videoCodecContext->pix_fmt, width, height, AV_PIX_FMT_BGR24, SWS_BICUBIC, nullptr, nullptr, nullptr); // 读取数据包并解码视频帧 std::vector<cv::Mat> videoFrames; AVPacket packet; while (av_read_frame(formatContext, &packet) == 0) { if (packet.stream_index == videoStream->index) { // 解码视频帧 AVFrame *frame = av_frame_alloc(); int gotFrame = 0; error = avcodec_send_packet(videoCodecContext, &packet); checkError(error, "向解码器发送数据包失败"); error = avcodec_receive_frame(videoCodecContext, frame); checkError(error, "从解码器接收帧失败"); if (error == 0) { gotFrame = 1; } if (gotFrame) { // 将帧缩转为目标格式 AVFrame *scaledFrame = av_frame_alloc(); av_image_alloc(scaledFrame->data, scaledFrame->linesize, width, height, AV_PIX_FMT_BGR24, 32); 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()); // 清理资源 av_freep(&scaledFrame->data[0]); av_frame_free(&scaledFrame); } av_frame_free(&frame); } av_packet_unref(&packet); } // 清理资源 sws_freeContext(frameScaler); avcodec_free_context(&videoCodecContext); avformat_close_input(&formatContext); return 0; }
问题分析与解决
这个断言错误本质是swscale无法处理输入的像素格式,或者输入格式参数无效。以下是具体原因和修复方案:
1. 检查像素格式有效性
打开解码器后,videoCodecContext->pix_fmt可能未被正确初始化(比如某些视频的codec参数未填充该字段),导致sws_getContext接收到无效格式。添加检查:
if (videoCodecContext->pix_fmt == AV_PIX_FMT_NONE) { std::cerr << "错误:无效的像素格式" << std::endl; exit(EXIT_FAILURE); }
2. 处理硬件解码/非标准格式
如果视频使用硬件加速解码,返回的帧格式可能是硬件专用格式(如AV_PIX_FMT_CUDA),swscale无法直接处理。强制使用软件解码:
// 在avcodec_open2之前添加 videoCodecContext->hw_device_ctx = nullptr; // 禁用硬件解码
3. 检查sws上下文创建结果
sws_getContext创建失败时会返回nullptr,原代码未做检查,后续调用sws_scale会直接崩溃。添加判断:
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 << "创建sws缩放上下文失败" << std::endl; exit(EXIT_FAILURE); }
4. 修正解码循环的错误处理
原代码中avcodec_receive_frame返回EOF或EAGAIN时直接调用checkError,导致程序异常退出。正确处理逻辑:
// 替换原解码循环内的接收帧代码 error = avcodec_send_packet(videoCodecContext, &packet); checkError(error, "向解码器发送数据包失败"); while (true) { error = avcodec_receive_frame(videoCodecContext, frame); if (error == AVERROR_EOF) { break; // 解码完成 } else if (error == AVERROR(EAGAIN)) { break; // 需要更多数据包 } else if (error < 0) { checkError(error, "从解码器接收帧失败"); } else { // 这里处理缩放和Mat转换逻辑 // ... av_frame_unref(frame); // 重置帧,准备接收下一个 } }
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

