音视频时间戳必须基于流的time_base整数表示,不可直接用time.Time;FFmpeg原始PTS需用math.Round转换,避免浮点误差;修改时间戳须用av.Rescale对齐时基,而非直接加time.Duration;RTP时间戳应按采样率递增,与wall clock无关。

Go 里处理音视频时间戳时,time.Time 不是直接可用的单位
音视频流的时间戳(PTS/DTS)本质是基于某个时基(time_base)的整数,比如 1/90000 或 1/1000,单位是秒的有理数表达。直接用 time.Now() 的纳秒精度毫无意义——它和解复用器输出的帧时间戳不在同一坐标系,也无法对齐编码器或播放器期望的时基。
常见错误是把 time.Since(start) 硬塞进 av.Packet.Time 字段,结果导致 go2rtc 同步失败、joy4 过滤器丢帧、或 FFmpeg 输出花屏。
- 所有音视频库(如
github.com/grafov/m3u8、github.com/edgeware/mp4ff、github.com/aler9/gortsplib)都要求你传入int64类型的时间戳,单位由当前 track 的time_base决定 - FFmpeg 的
-vsync或-copyts行为会严格校验输入包时间戳是否单调、是否符合时基对齐,错一位就可能触发重采样或丢弃 - go2rtc 的
pkg/core/track.go中Sender.Input()会检查packet.Time是否在合理抖动范围内,超出门限直接drop
从 FFmpeg 获取原始时间戳后,如何转成 Go 可控的 int64
当你用 os/exec 调起 FFmpeg 并通过 stderr 解析进度时,time=00:00:52.12 这类字符串只是人类可读格式,不是原始 PTS。真正可靠的是解析 FFmpeg 的 -debug_ts 输出或改用 libav 绑定(如 capi)拿到原始 AVPacket.pts。
若必须走命令行路径,建议加参数:-v debug -show_entries frame=pkt_pts_time,pkt_dts_time -of csv=p=0,再逐行读取 CSV 输出。每行类似:1250.234567,1250.234567 —— 这才是浮点秒值。
- 转换时别用
float64 * 1000然后int64()强转:浮点误差会导致1250.234567 * 1000 = 1250234.5669999998,取整变1250234,少了一纳秒 - 正确做法是用
math.Round(timeSec * float64(denominator)),其中denominator是目标时基分母(如 90000),再转int64 - joy4 的
av.Packet中Time字段类型是time.Duration,但底层仍按纳秒存;实际写入 muxer 前需调用pkt.RescaleTime(timeBase)对齐到容器要求的时基
在自定义过滤器中修改时间戳,为什么加 time.Millisecond * 100 会出问题
示例代码里注释掉的 // pkt.Time += time.Millisecond * 100 看似无害,实则危险:它把 time.Duration(纳秒单位)直接加到本应按流时基解释的字段上。如果该流时基是 1/90000,100ms 应表示为 9000(即 0.1 * 90000),而不是 100_000_000 纳秒。
- 正确做法是先获取当前 track 的
time_base,再用av.Rescale工具函数换算:newPts := av.Rescale(pkt.Time, time.Nanosecond, track.TimeBase) - go2rtc 的
pkg/core/track.go里Receiver会在接收包时自动做一次Rescale到内部统一时基(通常是1/1000000000),但如果你在Receiver之前就修改了pkt.Time,这个预处理就失效了 - 调试时可用
fmt.Printf("PTS=%d, TB=%v\n", pkt.Time, track.TimeBase)打印原始值,比猜更可靠
WebRTC 场景下,time.Now().UnixNano() 不能替代 RTP 时间戳
RTCP 和 RTP 协议规定时间戳字段是 32 位无符号整数,以媒体采样率(如音频 48kHz、视频 90kHz)为单位递增。它和 wall clock 完全无关,只反映相对播放顺序。用 time.Now().UnixNano() 填充会导致播放器无法计算 jitter、丢包恢复失败、音画彻底脱节。
- 音频 track 的 RTP 时间戳增量 = 采样率 × 每包样本数(如 48000 × 960 = 46_080_000)
- 视频 track 通常固定用 90kHz,每帧增量 =
90000 / fps(如 30fps → 3000) - go2rtc 的
pkg/core/track.go在Input()中会根据前一包时间戳和帧间隔自动推算下一包应填的 RTP TS,手动覆盖会破坏这个逻辑
时间戳精度失控往往发生在“以为自己在控制时间,其实只是在制造漂移”的瞬间。最稳妥的方式是:始终信任源头(FFmpeg/libav/设备驱动)给出的原始 PTS,只在必要时用 av.Rescale 对齐目标时基,而非用 wall clock 去拟合。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











