目录
[三、AVRational 工具函数表](#三、AVRational 工具函数表)
[四、FFmpeg 内置时间基常量](#四、FFmpeg 内置时间基常量)
[1️. Packet PTS → 毫秒(最常用)](#1️. Packet PTS → 毫秒(最常用))
[2️. Frame PTS → 秒(double)](#2️. Frame PTS → 秒(double))
[3️. 音频 frame → 下一帧 pts(防抖动)](#3️. 音频 frame → 下一帧 pts(防抖动))
[4️. Seek 时间戳(必须 round)](#4️. Seek 时间戳(必须 round))
[5️. 音视频同步(master clock = audio clock)](#5️. 音视频同步(master clock = audio clock))
[六、av_rescale_q_rnd 的 rounding 参数详解](#六、av_rescale_q_rnd 的 rounding 参数详解)
[七、Stream / Codec / Frame 时间基到底用哪个?](#七、Stream / Codec / Frame 时间基到底用哪个?)
觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。
由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。
FFmpeg 在处理视频和音频时,经常需要与时间基(time base)相关联的操作。时间基是视频和音频处理中的一个核心概念,它定义了帧之间的时间间隔。FFmpeg 提供了一系列的函数和工具来帮助开发者处理与时间基相关的问题。
一、核心时间基概念速记
| 名字 | 类型 | 含义 |
|---|---|---|
AVRational |
{int num, den} |
时间单位:1 / den秒 |
time_base |
AVRational |
一单位 pts 等于多少秒 |
pts / dts |
int64_t |
时间戳(单位是 time_base) |
AV_TIME_BASE |
1000000 |
ffmpeg 内部时间:微秒 |
公式一句话:
真实时间(s) = pts × num / den
二、时间基转换「主函数表」(最重要)
| 函数 | 原型 / 参数 | 用途 | 常见场景 |
|---|---|---|---|
**av_rescale_q()** |
(a, src_tb, dst_tb) |
pts 从 A 时间基 → B 时间基 | ✅ 99% 用这个 |
**av_rescale_q_rnd()** |
(a, src, dst, enum AVRounding) |
同上 + 四舍五入 / ceil | 音频 / 精确对齐 |
av_rescale() |
(a, b, c) |
a*b/c防止溢出 |
自己算 ratio |
av_rescale_rnd() |
(a, b, c, rnd) |
带 round 的整数 rescale | 底层封装 |
av_mul_q() |
(AVRational a, AVRational b) |
rational 乘法 | 推导 time_base |
av_div_q() |
(a, b) |
rational 除法 | fps ↔ duration |
av_add_q()/ av_sub_q() |
(a,b) |
rational 加减 | 偏移时间 |
记住一句话:FFmpeg 里永远用 av_rescale_q()做 pts 转换
三、AVRational 工具函数表
| 函数 | 作用 | 示例 |
|---|---|---|
av_make_q(num, den) |
构造 AVRational | AVRational tb = av_make_q(1, 1000); |
av_d2q(d, max) |
double → AVRational | tb = av_d2q(1.0/44100, 100000); |
av_q2d(tb) |
tb → double 秒 | sec = pkt->pts * av_q2d(tb); |
av_inv_q(tb) |
倒数 | fps → 每帧秒数 |
av_reduce() |
约分 rational | 内部用 |
四、FFmpeg 内置时间基常量
| 常量 / API | 含义 |
|---|---|
AV_TIME_BASE |
1000000(微秒) |
AV_TIME_BASE_Q |
(AVRational){1, AV_TIME_BASE} |
av_get_time_base_q() |
返回 AV_TIME_BASE_Q |
音视频同步常用目标:
dst_tb = AV_TIME_BASE_Q; // 微秒
dst_tb = av_make_q(1,1000); // 毫秒
五、不同场景「怎么选函数」对照表
1️. Packet PTS → 毫秒(最常用)
int64_t ms = av_rescale_q(pkt->pts,
stream->time_base,
av_make_q(1, 1000));
用 av_rescale_q
2️. Frame PTS → 秒(double)
double sec = pkt->pts * av_q2d(stream->time_base);
用 av_q2d(只读不写)
3️. 音频 frame → 下一帧 pts(防抖动)
AVRational tb = codec_ctx->pkt_timebase;
int64_t next_pts = av_rescale_q(
frame->nb_samples,
av_make_q(1, frame->sample_rate),
tb
);
推导音频时钟
4️. Seek 时间戳(必须 round)
int64_t seek_ts = av_rescale_q_rnd(
target_ms,
av_make_q(1, 1000),
stream->time_base,
AV_ROUND_DOWN // 或 AV_ROUND_NEAR_INF
);
avformat_seek_file(fmt_ctx, audio_idx, INT64_MIN, seek_ts, INT64_MAX, 0);
seek 必须用 _rnd
5️. 音视频同步(master clock = audio clock)
double vid_clk = vid_pkt->pts * av_q2d(vid_tb);
double aud_clk = aud_pkt->pts * av_q2d(aud_tb);
double diff = aud_clk - vid_clk;
六、av_rescale_q_rnd 的 rounding 参数详解
| 宏 | 行为 | 用途 |
|---|---|---|
AV_ROUND_ZERO |
向 0 取整 | 不推荐 |
AV_ROUND_INF |
远离 0 | 很少用 |
AV_ROUND_DOWN |
向下 | ✅ seek(避免越界) |
AV_ROUND_UP |
向上 | buffer size |
AV_ROUND_NEAR_INF |
四舍五入 | ✅ 显示时间 / 音频对齐 |
AV_ROUND_PASS_MINMAX |
保留 INT64_MIN/MAX | 内部用 |
推荐记忆:
seek → AV_ROUND_DOWN
ui ms → AV_ROUND_NEAR_INF
七、Stream / Codec / Frame 时间基到底用哪个?
这是新手地狱,直接给结论表:
| 对象 | pts 所在 timebase | 怎么转 |
|---|---|---|
AVPacket |
AVStream::time_base |
av_rescale_q(pkt->pts, st->time_base, dst) |
AVFrame(video) |
AVCodecContext::pkt_timebase或 st->time_base |
解码后一般继承 stream |
AVFrame(audio) |
解码后建议转成 1/sample_rate |
自己算 |
avformat_seek_file() |
AVStream::time_base | 别用 AV_TIME_BASE |
经典错误:
avformat_seek_file(fmt_ctx, idx, ..., ms, ..., 0); // ❌ ms 直接塞
八、完整"黄金模板"
static int64_t pts_ms(AVPacket *pkt, AVStream *st)
{
return av_rescale_q_rnd(
pkt->pts,
st->time_base,
av_make_q(1, 1000),
AV_ROUND_NEAR_INF
);
}
static int64_t pts_us(AVFrame *frm, AVStream *st)
{
return av_rescale_q(frm->pts,
st->time_base,
AV_TIME_BASE_Q);
}
九、时间基"血泪坑"速查表
| 坑 | 结果 |
|---|---|
| pkt pts 当秒用 | 快进 1000 倍 |
| seek 用微秒直接塞 | seek 到天边 |
| audio frame 用 video timebase | 声音飞走 |
| rescale 不 rnd | 帧跳 |
| pkt/dts 混用 | B 帧倒放 |
十、一句话总结
**FFmpeg 时间基只有三条铁律:**
1️.pts 永远是
int64_t,单位是AVRational2️.转换只用
av_rescale_q()/av_rescale_q_rnd()3️.seek / 显示 / 同步各选一个主时钟,统一 rescale 再比