调用avformat_write_header()崩溃,求解决FFmpeg保存BGR内存图像序列为MP4的问题
调用
avformat_write_header()崩溃,求解决FFmpeg保存BGR内存图像序列为MP4的问题 我瞅了你的代码,发现几个关键问题导致了avformat_write_header()崩溃,咱们一步步来修复:
问题1:没打开输出文件的IO通道(最致命)
avformat_alloc_output_context2只是帮你创建了格式上下文的壳子,但并没有实际打开磁盘上的output.mp4文件。没有IO读写通道的情况下,调用写入头的函数肯定会直接崩掉,必须补上打开文件的步骤。
问题2:手动修改流ID搞乱了内部状态
代码里outStream->id = outFormatCtx->nb_streams++;完全是多余操作,avformat_new_stream会自动给新流分配正确的ID,手动改这个值会破坏FFmpeg内部的计数逻辑,导致后续操作出错,直接删掉就行。
问题3:MP4需要全局头参数
MP4容器要求视频流使用全局头,你得在打开编码器之前,给编码器上下文加上AV_CODEC_FLAG_GLOBAL_HEADER标志,不然写入头信息时会出现兼容性问题。
问题4:编码器帧率参数没设置全
你只设置了time_base,但没设置framerate,这会导致流的帧率参数异常,需要补上outCodecCtx->framerate = av_make_q(fps, 1);。
问题5:冗余的CodecPar手动赋值
你已经调用了avcodec_parameters_from_context(outStream->codecpar, outCodecCtx)来同步编码器参数到流,之前手动给outStream->codecpar赋值的代码完全多余,还可能和自动同步的参数冲突,直接删掉就行。
修改后的完整代码
#include <iostream> #include <vector> #include <cstring> #include <fstream> #include <sstream> #include <stdexcept> #include <opencv2/opencv.hpp> extern "C" { #include <libavutil/imgutils.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libavio/avio.h> // 新增:需要avio_open的头文件 } using namespace std; using namespace cv; int main() { // Set up input frames as BGR byte arrays vector<Mat> frames; int width = 640; int height = 480; int num_frames = 100; Scalar black(0, 0, 0); Scalar white(255, 255, 255); int font = FONT_HERSHEY_SIMPLEX; double font_scale = 1.0; int thickness = 2; for (int i = 0; i < num_frames; i++) { Mat frame = Mat::zeros(height, width, CV_8UC3); putText(frame, std::to_string(i), Point(width / 2 - 50, height / 2), font, font_scale, white, thickness); frames.push_back(frame); } // Initialize FFmpeg av_register_all(); // 可选:部分旧版本FFmpeg需要这个初始化 // Set up output file AVFormatContext* outFormatCtx = nullptr; AVCodecContext* outCodecCtx = nullptr; AVStream* outStream = nullptr; AVPacket* outPacket = av_packet_alloc(); // 改用av_packet_alloc更安全 const char* outFile = "output.mp4"; int outWidth = frames[0].cols; int outHeight = frames[0].rows; int fps = 30; // Open output file int ret = avformat_alloc_output_context2(&outFormatCtx, nullptr, nullptr, outFile); if (!outFormatCtx) { cerr << "Error: Could not allocate output format context" << endl; return -1; } // 新增:打开输出文件的IO通道 if (!(outFormatCtx->oformat->flags & AVFMT_NOFILE)) { ret = avio_open(&outFormatCtx->pb, outFile, AVIO_FLAG_WRITE); if (ret < 0) { cerr << "Error: Could not open output file" << endl; avformat_free_context(outFormatCtx); return -1; } } // Set up output codec const AVCodec* outCodec = avcodec_find_encoder(AV_CODEC_ID_H264); if (!outCodec) { cerr << "Error: Could not find H.264 codec" << endl; return -1; } outCodecCtx = avcodec_alloc_context3(outCodec); if (!outCodecCtx) { cerr << "Error: Could not allocate output codec context" << endl; return -1; } outCodecCtx->codec_id = AV_CODEC_ID_H264; outCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; outCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; outCodecCtx->width = outWidth; outCodecCtx->height = outHeight; outCodecCtx->time_base = { 1, fps }; outCodecCtx->framerate = av_make_q(fps, 1); // 新增:设置帧率 outCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 新增:设置全局头 // Open output codec if (avcodec_open2(outCodecCtx, outCodec, nullptr) < 0) { cerr << "Error: Could not open output codec" << endl; return -1; } // Create output stream outStream = avformat_new_stream(outFormatCtx, outCodec); if (!outStream) { cerr << "Error: Could not allocate output stream" << endl; return -1; } // 移除:之前手动设置codecpar的冗余代码,用下面的函数自动同步 ret = avcodec_parameters_from_context(outStream->codecpar, outCodecCtx); if (ret < 0) { cerr << "Error: Could not copy codec parameters to output stream" << endl; return -1; } outStream->avg_frame_rate = outCodecCtx->framerate; // 移除:手动设置outStream->id的错误代码 ret = avformat_write_header(outFormatCtx, nullptr); if (ret < 0) { char err_buf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, err_buf, AV_ERROR_MAX_STRING_SIZE); cerr << "Error: Could not write output header, detail: " << err_buf << endl; return -1; } // Convert frames to YUV format and write to output file for (int i = 0; i < frames.size(); i++) { const auto& frame = frames[i]; AVFrame* yuvFrame = av_frame_alloc(); if (!yuvFrame) { cerr << "Error: Could not allocate YUV frame" << endl; return -1; } ret = av_image_alloc(yuvFrame->data, yuvFrame->linesize, outWidth, outHeight, AV_PIX_FMT_YUV420P, 32); if (ret < 0) { cerr << "Error: Could not allocate YUV frame buffer" << endl; av_frame_free(&yuvFrame); return -1; } // Convert BGR frame to YUV format Mat yuvMat; cvtColor(frame, yuvMat, COLOR_BGR2YUV_I420); memcpy(yuvFrame->data[0], yuvMat.data, outWidth * outHeight); memcpy(yuvFrame->data[1], yuvMat.data + outWidth * outHeight, outWidth * outHeight / 4); memcpy(yuvFrame->data[2], yuvMat.data + outWidth * outHeight * 5 / 4, outWidth * outHeight / 4); yuvFrame->pts = i; // 新增:设置帧的pts,否则视频播放会有问题 // Encode frame and write to output file ret = avcodec_send_frame(outCodecCtx, yuvFrame); if (ret < 0) { cerr << "Error: Could not send frame to output codec" << endl; av_frame_free(&yuvFrame); return -1; } while (ret >= 0) { ret = avcodec_receive_packet(outCodecCtx, outPacket); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { char err_buf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, err_buf, AV_ERROR_MAX_STRING_SIZE); cerr << "Error: Could not receive packet from output codec, detail: " << err_buf << endl; av_frame_free(&yuvFrame); return -1; } av_packet_rescale_ts(outPacket, outCodecCtx->time_base, outStream->time_base); outPacket->stream_index = outStream->index; ret = av_interleaved_write_frame(outFormatCtx, outPacket); if (ret < 0) { char err_buf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, err_buf, AV_ERROR_MAX_STRING_SIZE); cerr << "Error: Could not write packet to output file, detail: " << err_buf << endl; av_frame_free(&yuvFrame); return -1; } av_packet_unref(outPacket); // 新增:释放packet的引用,避免内存泄漏 } av_freep(&yuvFrame->data[0]); // 新增:释放av_image_alloc分配的缓冲区 av_frame_free(&yuvFrame); } // Write output trailer av_write_trailer(outFormatCtx); // Clean up if (!(outFormatCtx->oformat->flags & AVFMT_NOFILE)) { avio_close(outFormatCtx->pb); // 新增:关闭IO通道 } avcodec_close(outCodecCtx); avcodec_free_context(&outCodecCtx); av_packet_free(&outPacket); // 新增:释放packet avformat_free_context(outFormatCtx); cout << "Output file saved successfully!" << endl; return 0; }
额外补充
- 我还新增了帧的
pts设置,否则视频播放时可能出现帧率异常的问题; - 加上了错误信息的详细打印(用
av_strerror),方便后续排查问题; - 修复了内存泄漏的问题,比如释放
av_image_alloc分配的缓冲区,以及AVPacket的引用释放。
备注:内容来源于stack exchange,提问作者ollydbg
相关产品推荐
相关产品推荐

