代码改变世界

FFMpeg笔记(八) 读取RTP数据时的PTS计算[转载]

2020-04-04 21:29  jiayayao  阅读(3048)  评论(0编辑  收藏  举报

结构体struct RTPDemuxContext中有若干时间戳相关的成员,含义如下

timestamp:上一个接收到的RTP时间戳
base_timestamp:第一个接收到的RTP时间戳
cur_timestamp:未知
unwrapped_timestamp:假如rtp时间没有32位溢出的话,当前的rtp时间应该是多少
range_start_offset:RTSP Range头部指定的开始时间

last_rtcp_ntp_time:上一次收到RTCP SR报告中的ntp时间
last_rtcp_timestamp:上一次收到RTCP SR报告中的rtp时间
first_rtcp_ntp_time:第一次收到的RTCP SR报告中的ntp时间
rtcp_ts_offset:第一次接收到RTCP SR报告中的RTP时间与第一个收到的RTP时间差

每一帧的时间戳是这样计算的,在libavformat/rtpdec.c finalize_packet()

如果rtp只有一个音频流或者一个视频流,或者没有RTCP的SR报告,则

    pkt->pts     = s->unwrapped_timestamp + s->range_start_offset -
                   s->base_timestamp;

表达式右边的顺序换一下,含义就清晰了 s->unwrapped_timestamp - s->base_timestamp 代表当前自运行以来的相对时间,然后再加上s->range_start_offset,变成一个绝对时间。

如果有音视频两个流,且收到了RTCP的SR报告,计算是这样的

            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);
            }

        /* compute pts from timestamp with received ntp_time */
        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;

有点不好懂的是addend这个变量。根据rfc 3550,RTCP SR报告中的ntp时间定义为
http://www.ietf.org/rfc/rfc3550.txt

Wallclock time (absolute date and time) is represented using the timestamp format of the Network Time Protocol (NTP), which is in seconds relative to 0h UTC on 1 January 1900 [4]. The full resolution NTP timestamp is a 64-bit unsigned fixed-point number with the integer part in the first 32 bits and the fractional part in the last 32 bits.

由于读进来的rtcp_ntp_time是32位的定点小数,因此在做时间戳单位转换的时候做了一个32位的左移。因此addend代表最后一个收到的ntp时间距离第一个收到的ntp时间相隔多久。

回到pkt->pts的计算,s->rtcp_ts_offset这部分代表第一次收到的RTCP SR包距离流开始时的时间差,addend代表最后一次收到RTCP SR包距离第一次收到的RTCP SR包的时间差,delta_timestamp代表当前距离最后一次收到RTCP SR包的时间差,这三部分一求和就代表当前距离流开始时的时间差。

ffmpeg这种计算方式用于音视频流同步是有问题的,它会默认音视频流时间戳都从0开始。假如两者的开始时间不一致,会导致音视频不同步。而且显然,它这种计算方式浪费了RTCP SR报告中的ntp与rtp时间对应关系

---------------------------------------------------------------------------------------------------------------------------------------------------
简单总结下:
1. 没有RTCP的SR包时,计算的pts实际上是相对的,音视频的pts计算完事后,都是从0开始,逐步相对递增;
2. 有SR包时,计算一个RTP包的pts实际上是三段相加,第一段是首个RTCP SR包的timestamp减去首个RTP包的timestamp,第二段是最后一个SR包的NTP timestamp减去首个SR包的NTP timestamp,第三段是要计算的这个RTP包的timestamp减去最后一个SR包中的timestamp.

作者:叶迎宪
链接:https://www.jianshu.com/p/67d3e6a1d72e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。