基于FFmpeg实现可变帧率保存cv::Mat序列为MP4的PTS设置问题
解决FFmpeg可变帧率MP4保存的PTS/DTS错误问题
针对你遇到的「non-strictly-monotonic PTS」「PTS<DTS」错误,核心原因是FFmpeg对MP4容器(尤其是H.264编码)的时间戳有严格规则:PTS必须严格单调递增,且当存在B帧时PTS≥DTS。以下是具体修复步骤:
1. 统一时间基转换,避免单位混乱
FFmpeg中时间戳(PTS/DTS)的单位是流的time_base,不是毫秒。必须将你的自定义时间戳(如0ms、50ms、75ms)转换为对应流的时间基单位:
// 自定义时间戳(毫秒)转微秒,避免浮点误差 int64_t timestamp_ms = ...; // 当前帧的时间戳(毫秒) int64_t timestamp_us = timestamp_ms * 1000; // 将微秒转换为视频流的time_base单位 AVStream* stream = ...; // 视频流指针 int64_t pts = av_rescale_q(timestamp_us, av_make_q(1, 1000000), stream->time_base);
2. 确保PTS严格单调递增
每帧的PTS必须比前一帧大,哪怕只大1个时间基单位。可以维护一个全局变量记录上一帧的PTS,强制当前帧PTS满足递增要求:
static int64_t last_pts = AV_NOPTS_VALUE; if (last_pts != AV_NOPTS_VALUE && pts <= last_pts) { pts = last_pts + 1; // 强制当前帧PTS大于上一帧 } last_pts = pts;
3. 禁用B帧简化DTS处理(快速解决)
B帧的双向预测特性会导致PTS>DTS,若你的场景不需要B帧,直接禁用可以彻底避免PTS<DTS错误:
AVCodecContext* c = ...; // 编码上下文 // 禁用B帧 c->max_b_frames = 0; // 可选:用ultrafast预设进一步规避编码侧的时间戳异常 av_opt_set(c->priv_data, "preset", "ultrafast", 0);
此时DTS可直接等于PTS:
frame->pts = pts; frame->dts = pts;
4. 正确处理编码器缓存的帧
FFmpeg编码器会缓存帧用于预测,发送完所有原始帧后,必须发送NULL帧触发编码器输出所有缓存内容:
// 发送所有原始帧后,发送NULL帧flush编码器 avcodec_send_frame(c, NULL); AVPacket pkt; while (avcodec_receive_packet(c, &pkt) == 0) { // 将数据包时间戳转换为容器时间基 av_packet_rescale_ts(&pkt, c->time_base, stream->time_base); pkt.stream_index = stream->index; // 写入容器 av_interleaved_write_frame(fmt_ctx, &pkt); av_packet_unref(&pkt); }
错误原因解释
- non-strictly-monotonic PTS:PTS未严格递增,可能是时间基转换错误、重复设置相同PTS,或者帧顺序颠倒导致。
- PTS<DTS:当存在B帧时,DTS是解码顺序时间戳,PTS是显示顺序时间戳,B帧的PTS本应大于DTS,但如果编码侧DTS计算错误或PTS设置不合理,就会触发该错误。禁用B帧是最直接的解决方式。
内容的提问来源于stack exchange,提问作者ollydbg
相关产品推荐
相关产品推荐

