ffmpeg rtp时间戳
ffmpeg rtp时间戳
一、介绍
在ffmpeg中,每帧都会存在一个pts用来表示该帧图像在视频流中的位置。而在多路流(比如视频、音频)时,往往需要进行多媒体的同步,使得画面和声音同步,这时便需要使用两者的pts来做同步。那么pts是如何计算得到的呢,如何使用它做同步呢?
1.1 时间基转换
ffmpeg中时间存在一个基,可以理解成单位,比如把1s分成1000000等份,每个等份就是1us,那么1s就可以表示成1000000;而如果把1s分成90000等份,那么1s的值就是90000。
基的转换,把a从b基转到c基,计算公式为:,比如2s以1000000为基则是2000000,转换成以90000为基,则有2000000 / 1000000 * 90000 = 180000。
ffmpeg中提供了两个函数用于基的转换,可以更好地处理溢出与round问题:
av_rescale(a, b, c): 时间基从c -- > b。b, c可以直接是数字。
av_rescale_q(a, b, c): 时间基从 b --> c。 b,c需要使用AVRaional结构。
1.2 时间戳类型
ffmpeg中常用的几个时间戳:
rtcp_ntp_timestamp: 真实时间, 绝对时间,在网络传输时的时间基(1 << 32),
rtcp_timestamp: rtcp时间,一般会有一个base, 在网络传输时的时间基90000
rtp_timestamp: rtp时间,和rtcp_timestamp类似,网络时间基90000
Avpacket->pts: 通过如上计算得到,video一般是以90000为基
1.3 pts刚开始为负值
为什么刚开始的Avpacket->pts的值是负的?
因为我们实现时,rtcp_timestamp是使用clock_gettime()获取当前时间,而rtp_timestamp是用的h264 buffer里的时间, 所以rtp_timestamp < rtcp_timestamp, 而又是以rtcp_timestamp为基准0, 所以出现了刚开始帧的pts为负值。将rtcp_timestamp和rtp_timestamp使用相同的值,pts则从0开始。
二、Encode
在推流时,要将rtcp时间戳、rtp时间戳写入到包中,以供客户端解析,下面介绍如何将三个值写入。

2.1 rtcp编码时间戳:
这里写了两个时间戳,这个值我们实现的时候是以clock_gettime()获取的时间戳,在此基础上分别计算rtcp_ntp_time和rtcp_time:
rtcp_ntp_timestamp: 以
为基
rtcp_send_sr(s1, av_get_cur_time());
#define NTP_TO_RTP_FORMAT(x) av_rescale((x), INT64_C(1) << 32, 1000000)
val = NTP_TO_RTP_FORMAT(ntp_time);
*((int *)&rtcp_header[8]) = htonl(val >> 32);
*((int *)&rtcp_header[12]) = htonl(val & 0xffffffff);
last_rtcp_timestamp: 从1000000rescale到90000
我们这边又再加上一个随机值base_timestamp,这个base_timestamp一次连接中是不变的:
rtp_ts = av_rescale_q(ntp_time, (AVRational){1, 1000000},
s1->streams[0]->time_base) + s->base_timestamp;
*((int *)&rtcp_header[16]) = htonl(rtp_ts);
2.2 rtp编码时间戳:
在我们的实现中,rtp时间戳是由输入packet的pts计算得到,而packet.pts最开始是h264 buffer的timestamp 从1000000rescale到90000:
packet.pts = av_rescale_q(packet.pts,
in->time_base,
out->time_base);
s->cur_timestamp = s->base_timestamp + pkt->pts;
把cur_timestamp写入到 rtp包中:
s->timestamp = s->cur_timestamp;
*((short *)&rtp_header[2]) = htons(s->seq);
*((int *)&rtp_header[4]) = htonl(s->timestamp);
*((int *)&rtp_header[8]) = htonl(s->ssrc);
可以看到rtcp_time和rtp_time都是以90000以基,而rtcp_ntp_time是为基,所以在使用rtcp_ntp_time时要注意基的转换。
三、Decode
ffmpeg rtsp, rtp解码主要流程:

3.1 解析 rtp packet:
读取的代码在libavofrmat/rtpdec.c --> rtp_parse_packet_internal()函数中:
seq = AV_RB16(buf + 2);
timestamp = AV_RB32(buf + 4);
ssrc = AV_RB32(buf + 8);
读出的timestamp会传入到finalize_packet
中计算pts,如下方式传入:
// now perform timestamp things....
finalize_packet(s, pkt, timestamp);
当然只有rtp_time还是不够的,还需要rtcp_time,在多个流中还需要rtcp_ntp_time做多个流之间的同步。
3.2 解析rtcp时间戳:
rtpdec.c --> rtcp_parse_packet()函数中:
s->last_rtcp_ntp_time = AV_RB64(buf + 8);
s->last_rtcp_timestamp = AV_RB32(buf + 16);
if (s->first_rtcp_ntp_time == AV_NOPTS_VALUE) {
s->first_rtcp_ntp_time = s->last_rtcp_ntp_time;
if (!s->base_timestamp)
s->base_timestamp = s->last_rtcp_timestamp;
s->rtcp_ts_offset = (int32_t)(s->last_rtcp_timestamp - s->base_timestamp);
}
其中,last_rtcp_ntp_time是ntp时间戳,last_rtcp_timestamp是rtcp时间戳,这两个值会在rtcp同步时进行更新。
第一次的时候,会执行s->first_rtcp_ntp_time = s->last_rtcp_ntp_time;
,first_rtcp_ntp_time一旦会一直保持这个值不变,后面rtcp同步的时候只会修改last_rtcp_ntp_time。
另外,s->base_timestamp = s->last_rtcp_timestamp
, 这个值也会一直不变,有了这两个基准,其它的就是要和这两个比较,最后计算出pts。
从上面的计算方式可以知道,rtcp_ts_offset为0,这个值在一个流中也不会变,不过不同流之间或许有差别。
3.3 pts计算
av_read_frame会返回一个Avpacket对象,其中的pts变量存储了计算后的时间戳,计算方式在rtpdec.c --> finalize_packet()函数中,如下,分两种情况:
1. 如果是多路(如同时包含video, audio)
我们知道,传入的timestamp是rtp时间戳,需要使用ntp时间做同步:
delta_timestamp = timestamp - s->last_rtcp_timestamp;
/* convert to the PTS timebase */
addend = av_rescale(s->last_rtcp_ntp_time - s->first_rtcp_ntp_time,
s->st->time_base.den, (uint64_t) s->st->time_base.num << 32);
pkt->pts = s->range_start_offset + s->rtcp_ts_offset + addend + delta_timestamp;
range_start_offset = 0
rtcp_ts_offset = 0
addend: 最后一次rtcp同步的ntp时间 - first_rtcp_ntp_time,相当于做了一次ntp time同步,可以清除之前的rtp计算累积的误差
delta_timestamp: rtp时间戳 - 最后一次rtcp同步的rtcp时间

测试打印:
printf("multi stream: %ld, range_start_off: %ld, rtcp_ts_offset: %ld,addend: %ld, last timestamp: %ld, timestamp: %ld, dalta: %ld, rescale: %ld\n", pkt->pts, s->range_start_offset, s->rtcp_ts_offset, addend, s->last_rtcp_timestamp, timestamp, delta_timestamp, av_rescale(pkt->pts - old_pts, 1e6, 90000));
输出:
multi stream: 71279, range_start_off: 0, rtcp_ts_offset: 0,addend: 0, last timestamp: 3619407542, timestamp: 3619478821, dalta: 71279, rescale: 0
2. 如果是单路:
单路计算pts代码如下,可以看到单路不需要用到rtcp_ntp_time,只需要rtcp_time, rtp_time就可以了:
/* unwrapped是rtp时间累加 */
s->unwrapped_timestamp += (int32_t)(timestamp - s->timestamp);
/* unwrapped时间最后要减去rtcp_base_time */
pkt->pts = s->unwrapped_timestamp + s->range_start_offset - s->base_timestamp;
unwrapped_timestamp: 如果是第1帧,则为第1帧的rtp_time, 之后的值是当前帧与上一帧差rtp_time逐渐累加的结果,那么,实际上一般情况下unwrapped_timestamp就等于当前帧的rtp_time
range_start_offset是0,
base_timestamp是rtcp解析时最初的rtcp_timestamp

测试打印:
printf("single stream: %ld, base: %u, unwrapped: %ld, range: %ld, last timestamp:%ld, timestamp: %ld\n", pkt->pts, s->base_timestamp, s->unwrapped_timestamp, s->range_start_offset, old_timestamp, timestamp);
输出:
single stream: 6321193, base: 3079643606, unwrapped: 3085964799, range: 0, last timestamp:3085964799, timestamp: 3085964799
single stream: 6325714, base: 3079643606, unwrapped: 3085969320, range: 0, last timestamp:3085969320, timestamp: 3085969320
四、Reference
https://www.cnblogs.com/yinxiangpei/articles/3892982.html
http://www.cppblog.com/gtwdaizi/articles/65515.html
ffmpeg rtp时间戳的更多相关文章
- (转)关于RTP时间戳及多媒体通信同步的问题
下载 多媒体通信同步方法,主要有时间戳同步法.同步标记法.多路复用同步法三种.下面主要讨论时间戳同步法,特别是 RTP 时间戳同步.内容包括 RTP 媒体间同步的实现,为什么需要 RTCP 的 NTP ...
- RTP 时间戳的处理
RTP 时间戳的处理 在RTP传输音频数据时,一般选定逻辑时间戳速率与采样速率相同, 但是在传输视频数据时,必须使时间戳速率大于每帧的一个滴答(这样才能使图像回放更为平滑--<用TCP/IP ...
- RTP时间戳
http://xingyunbaijunwei.blog.163.com/blog/static/7653806720126121014111/ ——————————————————————————— ...
- 多媒体开之之rtp 时间戳和负载类型介绍
(1)时间戳 (2)负载类型 (3)rtp 包头 (1)时间戳 有三个 一个实时间单位 timestamp_increse=(unsigned int)(90000.0 / framerate); / ...
- ffmpeg 按时间戳读取文件 -re
ffmpeg读取文件有两种方式:一种是直接读取,文件被迅速读完;一种是按时间戳读取.一般都是按时间戳读取文件, 命令行加入-re,表示按时间戳读取文件,在ffmpeg_opt.c 中可以看到re对应的 ...
- ffmpeg rtp rtmp udp 推流命令
推组播 组播地址指的范围是224.0.0.0—239.255.255.255 ffmpeg -re -i chunwan.h264 -vcodec mpeg2video -f mpeg2video u ...
- ffmpeg修复时间戳
ffmpeg -re -i e:/media/baifa.mp4 -filter_complex -hls_wrap -hls_time d:/demo/hls/cctv13/playlist.m3u ...
- 关于RTP时间戳及多媒体通信同步的问题(转)
文章转载自:罗索实验室 [http://www.rosoo.net/a/201101/10776.html]
- 【FFMPEG】谈谈RTP传输中的负载类型和时间戳
谈谈RTP传输中的负载类型和时间戳 最近被RTP的负载类型和时间戳搞郁闷了,一个问题调试了近一周,终于圆满解决,回头看看,发现其实主要原因还是自己没有真正地搞清楚RTP协议中负载类型和时间戳的含义.虽 ...
随机推荐
- [luoguP1941] 飞扬的小鸟(DP)
传送门 动归,用f[i][j]表示到达第I列高度为j时最少需要飞的次数,容易想到最裸的转移: f[i][j]=min(min(f[i-1][j-up[i-1]*k]+k),f[i-1][j+down[ ...
- HDU 4923 (贪心+证明)
Room and Moor Problem Description PM Room defines a sequence A = {A1, A2,..., AN}, each of which is ...
- CF558E A simple task 线段树
这道题好猥琐啊啊啊啊啊啊 写了一个上午啊啊啊啊 没有在update里写pushup啊啊啊啊 题目大意: 给你一个字符串s,有q个操作 l r 1 :把sl..rsl..r按升序排序 l r 0 :把s ...
- 解决CDH SparkStreaming任务启动之后executor不停增长的问题,num-executors配置不管用。
spark2-submit --class SparkKafka --master yarn --executor-memory 1G --num-executors 6 --driver-memor ...
- Linux下汇编语言学习笔记75 ---
这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...
- C - How Many Tables 并查集
Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to kn ...
- [bzoj1084][SCOI2005]最大子矩阵(DP)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1084 分析: m=1时:相当于只有一行数,让你取出p段,使得总和最大 明显可以DP,f ...
- golang中channels的本质详解,经典!
原文:https://www.goinggo.net/2014/02/the-nature-of-channels-in-go.html The Nature Of Channels In Go 这篇 ...
- 第十五周oj刷题——Problem M: C++习题 矩阵求和--重载运算符
Description 有两个矩阵a和b,均为2行3列.求两个矩阵之和.重载运算符"+",使之能用于矩阵相加(如c=a+b). 重载流插入运算符"<<&quo ...
- 一个人开发的.Net版轻量级博客,欢迎吐槽,互相学习!
项目架构采用:Asp.Net MVC4.0 + EntityFramework6.0 code first + AutoMapper + Unity(IOC) + SqlServer2012 项目地址 ...