为什么从rtsp拉取的媒体流,每个流的rtp时间戳都不是从0开始的

在这里插入图片描述

1、为什么从rtsp拉取的媒体流,每个流的rtp时间戳都不是从0开始的

因为 RTP 时间戳根本不要求从 0 开始,协议里就是“随机初始值 + 每采样 +1”——这样设计省掉了“回绕清零”带来的同步风暴,也让多摄像头/多麦克风同时上线时不会“撞钟”。


RFC 3550 原话

The timestamp is a 32-bit field that reflects the sampling instant of the first octet in the RTP data packet.
It must be chosen randomly for each stream.


实际原因

  1. 防止 90 kHz 回绕冲突

    90 000 Hz × 15 h ≈ 2³²,如果全从 0 开始,全网设备同时上线,15 小时后一起回绕,服务器做 RTCP 统计时很难区分是谁的包。

  2. 多流同时存在时天然“错峰”

    摄像头 A 随机到 0x3c5a1234,摄像头 B 随机到 0x9f00abcd,即使它们同一时刻出帧,RTP 时间戳也不同,接收端靠 RTCP SR 各自映射到 NTP 即可,不会把两路流当成同一条。

  3. 厂家实现简单

    芯片上电后把内部 90 kHz 自由运行计数器当前值直接当“第一帧”戳,写完 SDP 就发流,无需额外清零逻辑,也免了断电重启后“时间戳倒退”的麻烦。

  4. RTSP/ONVIF 并不要求“归零”

    PLAY 响应里的 RTP-Info: rtptime=xxxx 可以填任意值;FFmpeg 收到后会把 xxxx 当“起点”减掉,再换算成 pts,所以观众端依旧从 0 开始看,对业务毫无影响。


2、多流同时传输时,每个流开始的rtp时间戳都不相同,那怎么做同步?

在多流 RTP 各自独立起播、时间戳不同 的场景里,真正用来做音视频同步的并不是 RTP 时间戳本身,而是会话级的 RTCP SR(Sender Report) 里携带的 RTP↔NTP 映射关系。

只要拿到每个流的 SR,就可以把任意一个 RTP 时间戳换算成绝对时钟(NTP)时间,然后再把不同流的数据对齐到同一个时间轴,后面再按解码/渲染时钟做平滑播放即可。流程如下:

1. 发端:每个流周期发 RTCP SR

  • 在 SR 包里填 2 个关键字段

    – RTP timestamp(对应本流采样时刻)

    – NTP timestamp(64-bit,UTC 时钟,单位 1/2³² s)

  • 两字段给出线性映射:

  NTP = f(RTP) = k·RTP + b

其中斜率 k = 1/采样时钟,截距 b 由发端首次打包时确定。

  • 不同流的“起点”可以不同,但 SR 把各自的 RTP 都锚定到同一个 NTP 时钟。

2. 收端:收到 SR → 建立每流“时间映射表”

  • 每收到一次 SR,就更新一条记录
  stream_id | last_S RTP | last_S NTP | 采样时钟
  • 以后凡是属于该流的 RTP 包,都能插值得出对应的 NTP 时间:
  NTP_pkt = NTP_SR + (RTP_pkt – RTP_SR) / 采样时钟

误差 ≤ 一个 RTCP 周期(通常 15 s,可配)。

3. 同步:把所有流都换算到“共同时钟”

  • 对任意要同时播放的音/视帧,先算出各自的 NTP_present。
  • 取 max(NTP_audio, NTP_video) 作为“同步呈现点”,或者按自定义策略(如音频为主轴)。
  • 把该 NTP 再反算回各流自己的 RTP 时间戳/采样序号,通知解码器/渲染器在该时刻出图/出声。
  • 如果解码/渲染端用的是一个共用系统时钟(如基于 NTP 或 PTPClock),只需在“到点”时喂数据即可;否则再做一次平滑锁相(PLL)消除网络抖动。

4. 网络抖动/乱序补偿

  • SR 映射只解决“零点对齐”,网络抖动靠接收缓冲(jitter buffer)。
  • 视频一般用较大缓冲(100300 ms),音频用较小缓冲(2080 ms),再靠时间戳拉伸/重采样做微调。
  • 若采用 AVSync 主从模式,通常选音频为主轴,视频帧按需重复/丢帧。

3、抓包显示rtsp拉取媒体流的时候,没有收到rtcp sr包,那么音视频如何同步?ffmpeg是怎么做的?

先给结论:

  1. 没有 RTCP SR 时,理论上无法把两条流的 RTP 时间戳映射到同一条绝对时间轴,所以“教科书”做法是做不了跨流同步的。
  2. 但 FFmpeg 的 RTSP 模块 并不是“死等” SR,它有两条退路:
    • “RTP-Info 冷启动” —— 利用 RTSP 200 OK 里 RTP-Info: url=…;seq=…;rtptime=… 给出的 第一帧 RTP 时间戳,把音视频起点强行对齐到 0(虚拟 NTP)。
    • “后期补偿” —— 只要后面 任何一次 收到 SR(哪怕几十秒以后),立刻用真实 NTP 重新校正斜率/偏移,把之前累积的误差慢慢拉回。
  3. 因此你抓包看不到 SR,不代表 FFmpeg 没有同步机制,而是它先“假对齐”再“后修正”,肉眼看到的就是“居然还能对上”。

下面把细节拆开说明。


一、SR 包缺失时到底缺了什么

SR 的核心作用只干一件事:

NTP_real = k × RTP + b

其中 k 是采样频率(90 kHz/48 kHz…),b 是两条流 各自的随机偏移量。

没有 SR 就求不出 b,两条流的 b 可能相差几百毫秒到几秒,所以仅靠 RTP 时间戳无法知道“哪一帧音频对应哪一帧视频”。


二、FFmpeg 的“冷启动” fallback(rtsp.c → rtpdec.c)

  1. 发送 PLAY 后解析服务器返回的

RTP-Info: url=trackID=1;seq=123;rtptime=900000, url=trackID=2;seq=456;rtptime=810000`

把音频、视频的 第一个 rtptime 记为 rtp_start_[a/v]

  1. ff_rtsp_fetch_packet() 里第一次收到对应流的 RTP 包时,把

    pts = rtp_ts - rtp_start_

    再除以采样频率,得到 “从 0 开始的虚拟壁钟”。

    两条流都这样做,相当于强行把 b 设为 0,误差就被压缩到“第一帧到达时间差”以内(通常 0–50 ms)。

  2. 解码线程拿到 AVPacket 后,pts 已经是同一虚拟时间轴,直接往时钟里一扔即可播放;后续就按帧率/采样率累加,保证 流内 不漂移。


三、后期再遇到 SR 时的“在线校正”

哪怕 30 s 后才收到 SR,FFmpeg 会:

  1. 用 SR 里的 <rtp,ntp> 重新估算 真实斜率与偏移。
  2. 把估算结果写进 RTSPState->rtcp_ts_offset[a/v]
  3. 后续再算 pts 时加上这个 offset,慢慢把之前 0 对齐造成的系统误差拉回来(通常每秒修正几毫秒,观众感觉不到)。

4、ffmpeg读取到的不同容器的avpacket中的时间戳是怎么来的?

不管容器多五花八门,FFmpeg 的目标都是“把各容器自己的时钟/计数器翻译成统一的 AVPacket->pts(单位 = AVStream->time_base)”,上层永远只认这一个数字。

下面把主流容器“原生时间戳 → AVPacket->pts” 的映射方式一次列清:

容器格式原生时间戳来源PTS/DTS计算方式时间基 (time_base)备注
MP4/MOV/ISOBMFFstts + ctts + elst 盒子累加 stts.delta,加 ctts 偏移,再减 elst 起始偏移1/timescale (常见 90000、24000)时间基通常为 1/timescale
MKV/WebMBlock / BlockGroup 里的 Timestamp 字段(单位 ns)pts = timestamp / av_q2d(time_base)通常由 Stream->time_base 自动选择 (如 1/1,000,000,000)av_q2d(time_base) 用于将时间基转换为秒/格
TS (MPEG-TS)​基于 90 kHz PCR 的 PTS/DTS (33 bit)直接拷贝时间戳,若发生回绕则需处理1/90000时间基固定为 1/90000
FLVTagHeader 的 Timestamp / TimestampExtended (单位毫秒)pts = timestamp_ms × 1000 / time_base.den通常为 1/1000计算时需将毫秒转换为时间基单位[ccitation:4]
AVIdwSample 索引 × dwScale/dwRatepts = sample_index × dwScale / dwRate{ dwScale, dwRate }通过样本索引和比例计算显示时间
OGGgranule_position (采样计数)pts = granule – pre_roll1/采样率时间基与音频采样率相关
ASF/WMA/WMVObject 里的 Presentation Time (100-ns 单位)pts = pr_time / 10000000 / av_q2d(time_base)自动约分原始时间单位是100纳秒
WAV/AIFF无显式时间戳,按 sample_index / 采样率 计算pts = sample_index1/采样率对于无压缩音频,通过样本索引和采样率计算时间
RAW H264/H265无容器时间戳;若为RTP流,则使用RTP时间戳若读取裸流,通常PTS设为0或由用户指定需要外部注入原始码流通常不包含显示时间戳
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值