调用avformat_write_header后流time_base被修改,高帧率帧丢失如何解决?
问题分析与解决方案
原因分析
Matroska(MKV)复用器默认会根据视频流帧率自动调整time_base,目的是避免时间戳数值过大。当你的帧率达到2000fps时,单帧间隔仅500微秒,而复用器默认选择的毫秒级{1,1000} time_base无法区分相邻帧的时间戳(500微秒在毫秒精度下会被截断为0),导致连续帧的pts值相同,后续提取工具会将这些帧判定为重复帧而丢弃。
另外,仅手动设置AVStream和AVCodecContext的time_base并不足够——复用器在avformat_write_header阶段会根据自身逻辑覆盖这些设置,尤其是当它认为指定的time_base精度超出容器“需求”时。
解决方法
1. 强制Matroska复用器使用微秒级time_base
在调用avformat_write_header之前,通过AVOptions强制锁定流的time_base,阻止复用器自动调整:
// 创建输出流后,先设置基础time_base AVStream* stream = avformat_new_stream(avFormatContext, codec); stream->time_base = (AVRational){1, 1000000}; stream->codecpar->time_base = stream->time_base; // 强制复用器保留指定的time_base参数 av_opt_set_int(stream, "time_base_num", 1, AV_OPT_SEARCH_CHILDREN); av_opt_set_int(stream, "time_base_den", 1000000, AV_OPT_SEARCH_CHILDREN);
2. 对齐编码器与容器的时间戳规则
设置AVCodecContext的time_base为微秒级,同时将帧的pts直接赋值为实际捕获的微秒时间戳(无需额外转换):
AVCodecContext* codecCtx = avcodec_alloc_context3(codec); codecCtx->time_base = (AVRational){1, 1000000}; codecCtx->framerate = (AVRational){2000, 1}; // 明确声明帧率,辅助复用器识别 // 给捕获帧设置微秒级时间戳 frame->pts = capture_timestamp_us; // capture_timestamp_us为实际捕获的微秒数
3. 禁用复用器的自动格式转换
如果上述方法仍无效,可添加全局选项禁止复用器自动调整时间戳和格式:
av_opt_set(avFormatContext, "auto_convert", "0", AV_OPT_SEARCH_CHILDREN); av_opt_set(avFormatContext, "avoid_negative_ts", "make_non_negative", AV_OPT_SEARCH_CHILDREN);
4. 验证结果
编码完成后,可用ffprobe工具验证流参数和帧时间戳:
ffprobe -v error -select_streams v:0 -show_entries stream=time_base,avg_frame_rate -show_entries frame=pts,pkt_pts_time -of csv=p=0 input.mkv
确认输出的time_base为1/1000000,且相邻帧的pkt_pts_time间隔为0.0005秒(500微秒)。
内容的提问来源于stack exchange,提问作者greken
相关产品推荐
相关产品推荐

