ffmpeg关于avformat_write_header改变流时间基time_base的说明

本文讲述了作者在使用FFmpeg进行直播推流时遇到的问题,视频播放过快、音频时长缩短。通过深入分析发现时间基被错误改变,并解决了与封装格式mp4和flv相关的时基问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在用ffmpeg做直播推流功能,之前做的本地录制,封装格式是mp4,直播的时候,mp4不支持,需要改为flv,但是改为flv之后,用ffmpeg命令行进行拉流,拉下来的流存储成flv文件。播放起来,视频显得特别快,原本2分钟的视频,10秒不到就播放完了。

为此将音频和视频单独推流,视频没问题,音频出了问题,录制4分钟的音频,在文件中大概只有2分钟。

下面是用ffmpeg拉流时的命令行,从红色方框可以看出,1分钟拉下来的流不到30ms。正常情况下,这个speed应该在比1大一点点。

在这里插入图片描述

下面一个正常拉流情况下的speed值。
在这里插入图片描述

为此用ffmpeg命令行推流试了下,ffmpeg命令行推流是ok的,然后调试ffmpeg工具,如何调试,本人写了博客。
vs2017调试ffmpeg源码

经过一段时间,发现,问题出在avformat_write_header函数上,在write之前,已经对avformat_new_stream的AVStream做了time_base设置,设置为1/48000,跟音频的采样率保持一致。
但是avformat_write_header之后,time_base变成了1/1000。

封装格式为mp4的时候,不会出现这种情况,看来应该是跟封装格式有关。
下面的图片来自博客ffmpeg # 各种封装格式的time_base

在这里插入图片描述

<think>嗯,用户遇到了FFmpegavformat_write_header函数报错-22的问题,需要分析原因和解决方案。这个错误码对应EINVAL,通常表示无效参数,但具体原因可能涉及多个方面。 从用户提供的引用内容来看,用户应该是在处理视频封装相关任务,比如从MP4提取H264数据[^3][^4]。这类操作涉及复杂的处理和格式转换,容易引发头文件写入错误。结合FFmpeg常见问题,我需要从几个关键维度分析: 首先是编码器参数匹配问题。引用[4]提到需要从extradata获取sps/pps数据,如果输出格式要求AVC1而用户错误配置为Annex-B,或者编码参数不完整,就会导致-22错误。比如MOV/MP4容器必须使用AVC1格式的extradata[^3]。 其次是配置一致性。用户提到"找出best的视频索引"[^4],但如果输出上下文未正确添加,或输入输出参数不匹配(如分辨率/帧率),avformat_write_header就会校验失败。特别要注意音频配置,即使只处理视频也需要正确初始化音频。 第三是I/O操作问题。引用[1]提到编译时头文件路径问题,类似地,运行时若输出路径不可写、文件被占用或磁盘空间不足也会引发该错误。需要确认用户是否具有写权限,以及文件是否被其他进程锁定。 最后是封装格式限制。比如尝试将HEVC视频写入不支持该编码的AVI文件,或者时间基(time_base)设置不合理。用户引用[3]强调不同封装格式对数据存储的要求差异,这点很关键。 解决方案应该分步骤:先用av_err2str获取详细错误描述;检查输出格式是否支持当前编码;验证extradata是否正确附加;确保所有都有codecpar且参数有效;测试不同封装格式;最后检查文件系统权限。调试时可以尝试用null作为输出文件名,若成功则说明是I/O问题。 用户似乎对FFmpeg有较深研究(引用[2]提到编解码程分析),但可能忽略了封装格式的细节要求。建议用户重点验证H264的extradata格式是否符合容器要求,这是MP4封装最常见的失败点。</think>FFmpeg 中 `avformat_write_header` 返回 `-22 (EINVAL)` 错误("Could not write header to file")通常表示传递给函数的参数无效或上下文状态不一致。以下是常见原因和解决方案: --- ### **常见原因及解决方案** 1. **输出格式/容器不匹配** * **问题**:输出容器的格式(如MP4、MKV)编码器配置、类型或封装选项不兼容。 * **解决**: * 检查编码器参数是否被目标容器支持(如AVC1编码适用于MP4)。 * 确认 `oformat` 是否正确设置: ```c AVFormatContext *ofmt_ctx = NULL; avformat_alloc_output_context2(&ofmt_ctx, NULL, "mp4", NULL); // 明确指定容器格式 ``` * 尝试不同容器格式测试兼容性。 2. **未正确添加(Stream)** * **问题**:输出上下文 (`ofmt_ctx`) 未添加,或的编解码参数 (`AVCodecParameters`) 未正确初始化。 * **解决**: ```c // 添加输出 AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL); // 从输入复制参数(或手动设置) avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); ``` 3. **缺少关键头数据(如SPS/PPS)** * **问题**:H.264/H.265等编码的 `extradata`(包含SPS/PPS/VPS)未正确设置。 * **解决**: * 从输入提取并附加到输出: ```c if (in_stream->codecpar->extradata) { out_stream->codecpar->extradata = av_malloc(in_stream->codecpar->extradata_size); memcpy(out_stream->codecpar->extradata, in_stream->codecpar->extradata, in_stream->codecpar->extradata_size); out_stream->codecpar->extradata_size = in_stream->codecpar->extradata_size; } ``` * 对于手动编码,需在编码器初始化后获取 `extradata`。 4. **I/O上下文未打开** * **问题**:输出文件未以写模式打开。 * **解决**: ```c if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) { int ret = avio_open(&ofmt_ctx->pb, output_filename, AVIO_FLAG_WRITE); if (ret < 0) { /* 处理错误 */ } } ``` 5. **时间基Time Base)无效** * **问题**:输出的 `time_base` 未设置或设置错误。 * **解决**: ```c out_stream->time_base = (AVRational){1, 90000}; // 示例:90kHz时钟 ``` 6. **文件路径/权限问题** * **问题**:输出路径不可写、磁盘空间不足或文件被占用。 * **解决**: * 检查路径权限和磁盘空间。 * 确保文件未被其他进程锁定。 7. **封装器选项冲突** * **问题**:通过 `AVDictionary` 设置的选项(如 `movflags`)参数冲突。 * **解决**:清空选项字典或检查选项有效性: ```c AVDictionary *options = NULL; av_dict_set(&options, "movflags", "frag_keyframe", 0); // 示例 int ret = avformat_write_header(ofmt_ctx, &options); ``` --- ### **调试步骤** 1. **启用FFmpeg日志** 添加 `av_log_set_level(AV_LOG_DEBUG);` 查看详细错误原因。 2. **验证输出上下文** 检查 `ofmt_ctx` 是否成功初始化: ```c if (!ofmt_ctx) { fprintf(stderr, "Could not create output context\n"); return; } ``` 3. **检查参数** 遍历输出,确认 `codecpar` 和 `time_base` 有效: ```c for (int i = 0; i < ofmt_ctx->nb_streams; i++) { AVStream *stream = ofmt_ctx->streams[i]; if (!stream->codecpar->codec_tag || !stream->time_base.num) { // 修正参数或手动初始化 } } ``` 4. **简化测试** 尝试写入空文件(`avio_open` 后直接调用 `avformat_write_header` 不写数据),排除数据问题。 --- ### **关键代码示例** ```c // 初始化输出上下文 avformat_alloc_output_context2(&ofmt_ctx, NULL, "mp4", output_filename); if (!ofmt_ctx) { /* 处理错误 */ } // 添加并配置输出 AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL); avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); out_stream->time_base = in_stream->time_base; // 打开输出文件 if (avio_open(&ofmt_ctx->pb, output_filename, AVIO_FLAG_WRITE) < 0) { /* 处理错误 */ } // 写入文件头(检查选项) AVDictionary *options = NULL; int ret = avformat_write_header(ofmt_ctx, &options); if (ret < 0) { char errbuf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, errbuf, sizeof(errbuf)); fprintf(stderr, "avformat_write_header failed: %s\n", errbuf); } ``` --- ### **典型修复场景** - **MP4封装H.264**:确保 `extradata` 包含SPS/PPS(参考引用[3][^3]),且格式为AVC1而非Annex-B[^4]。 - **复制(Stream Copy)**:直接复制 `codecpar` 时需同步复制 `extradata`。 - **实时输出**:使用 `fragmented` 模式(如 `movflags frag_keyframe+empty_moov`)。 > **引用说明** > 本文参考了FFmpeg封装程中关于头数据处理[^3][^4]和编解码参数配置[^2]的经验,具体问题需结合日志和参数验证分析。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值