目录
一、先说结论
二、函数原型
三、timestamp 的单位:90% 的人第一坑
错误理解
正确规则
四、flags:av_seek_frame 的灵魂(也是坑源)
1️.AVSEEK_FLAG_BACKWARD(最重要)
2️.AVSEEK_FLAG_ANY(播放器杀手)
3️.AVSEEK_FLAG_FRAME(几乎没人用)
4️. AVSEEK_FLAG_BYTE(老派硬核)
五、av_seek_frame 到底“做了什么”
六、最小可用但正确的 av_seek_frame 示例
七、av_seek_frame vs avformat_seek_file(再对比一次)
八、什么时候你“必须”用 av_seek_frame?
九、一个经典误解(必须点破)
十、总结
av_seek_frame是 FFmpeg 的底层 seek 原语,只干一件事:把 demuxer 跳到指定时间戳附近,不负责画面正常。
stream_index≥0时 timestamp用流 time_base,填 -1才用微秒;视频必带 AVSEEK_FLAG_BACKWARD保证跳到 I 帧前,AVSEEK_FLAG_ANY拖进度条必花屏。
它成功后不会 flush 解码器、不清旧包,必须配套 avcodec_flush_buffers + 清队列 + 丢包到目标 PTS才算真正 seek。
MKV/WebM 无 Cues 时容易“跳过头直接 EOF”,容错差。
一句话:它是螺丝刀不是成品工具,现代播放器应优先用 avformat_seek_file,理解它只为懂原理和修老代码。
如果你翻过老代码 / ffplay 旧版本 / 各种 GitHub 播放器 Demo,一定见过它:
av_seek_frame(fmt_ctx, stream_index, timestamp, flags);看起来很简单,四个参数,一个返回值。
但只要你敢往生产环境扔,它多半会教你做人:
MKV 拖到结尾直接播完
WebM 花屏 3 秒
H264 跳过去画面糊成油画
今天这篇,把av_seek_frame从“能用”讲到“什么时候不该用”。
一、先说结论
av_seek_frame是底层 seek 原语不是给 UI 拖进度条直接用的
它只干一件事:把 demuxer 的文件位置设到某个时间戳附近
不保证精确、不保证舒服、不负责用户体验
一句话:
懂原理时用它,做播放器时包一层再用。
二、函数原型
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);参数 | 含义 |
|---|---|
| AVFormatContext |
| 参考流 index(-1 = 所有流按 AV_TIME_BASE) |
| 时间戳 |
| seek 行为控制 AVSEEK_FLAG_BYTE,按字节大小进行跳转。 |
三、timestamp 的单位:90% 的人第一坑
错误理解
av_seek_frame(fmt, 0, 12.34, ...); // 秒?毫秒?微秒?正确规则
stream_index | timestamp 单位 |
|---|---|
|
|
|
|
MKV 视频流 ={1,1000}→ timestamp 是毫秒
正确写法:
int64_t ts = target_sec * 1000; // MKV av_seek_frame(fmt, vidx, ts, AVSEEK_FLAG_BACKWARD);或者通用版:
int64_t ts = av_rescale_q(target_sec * AV_TIME_BASE, AV_TIME_BASE_Q, fmt->streams[vidx]->time_base);四、flags:av_seek_frame 的灵魂(也是坑源)
1️.AVSEEK_FLAG_BACKWARD(最重要)
AVSEEK_FLAG_BACKWARD含义:
跳到 ≤ timestamp 的最近点
视频 seek 必选
保证 keyframe 在前
会偏早(要自己丢包)
2️.AVSEEK_FLAG_ANY(播放器杀手)
AVSEEK_FLAG_ANY含义:
不管是不是 keyframe,直接跳
视频拖进度条绝对禁止
只适合:
音频
裸流分析
抽帧截图(且你知道自己在干嘛)
3️.AVSEEK_FLAG_FRAME(几乎没人用)
AVSEEK_FLAG_FRAME以 frame number 为 anchor(前提是 demuxer 支持)
MKV / WebM:基本忽略
MP4:部分支持
实战建议:别碰
4️.AVSEEK_FLAG_BYTE(老派硬核)
AVSEEK_FLAG_BYTEtimestamp = file offset
用于 flvcut / ts splice
播放器别用
五、av_seek_frame 到底“做了什么”
它只做了三件事:
找到对应流的时间索引(Cues / stts / index)
把 demuxer 的 IO 位置调到对应 cluster/packet
下次
av_read_frame()从那里开始吐包
它没做:
没 flush 解码器
没清 B 帧参考
没管你线程里还有多少旧 packet
没保证画面立刻正常
所以你看到的现象:
现象 | 原因 |
|---|---|
花屏 | GOP 缺参考 |
卡一下才动 | BACKWARD + 没丢包 |
EOF | 无 Cues + 跳过头 |
六、最小可用但正确的 av_seek_frame 示例
int64_t ts = av_rescale_q(target_sec * AV_TIME_BASE, AV_TIME_BASE_Q, vst->time_base); av_seek_frame(fmt_ctx, vidx, ts, AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers(vctx); avcodec_flush_buffers(actx); clear_queues(); // 丢到目标 PTS AVPacket pkt; while (av_read_frame(fmt_ctx, &pkt) >= 0) { if (pkt.stream_index == vidx && pkt.pts != AV_NOPTS_VALUE && pkt.pts >= ts) break; av_packet_unref(&pkt); }这才叫“正确使用 av_seek_frame”
七、av_seek_frame vs avformat_seek_file(再对比一次)
维度 | av_seek_frame | avformat_seek_file |
|---|---|---|
设计年代 | 老 | 新 |
min/max | ❌ | ✅ |
容错 | 弱 | 强 |
demuxer 自由度 | 小 | 大 |
播放器友好度 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
ffplay 现在用谁 | ❌ | ✅ |
一句话:
av_seek_frame = 螺丝刀
avformat_seek_file = 电动螺丝刀
八、什么时候你“必须”用 av_seek_frame?
说实话,只剩这几种:
FFmpeg < 2.x 老代码维护
自己写 demux wrapper(不用 min/max)
想精确控制“我不希望 FFmpeg 帮我 clamp”
教学 / 理解 demux 原理
新播放器项目:一律 avformat_seek_file
九、一个经典误解(必须点破)
“我用 av_seek_frame 成功返回了,为什么画面还是旧的?”
因为:
av_seek_frame ≠ 播放器 seek 它只是: demuxer.position = somewhere真正的播放器 seek =
av_seek_frame / avformat_seek_file + avcodec_flush_buffers + 清队列 + 丢包到目标时间 + 重置音视频时钟少一步都不算 seek。
十、总结
av_seek_frame只负责“跳文件位置”,不负责“让你看到画面”;它是 FFmpeg 的底层能力,不是播放器的成品方案。