RTCP介绍及发送间隔控制

1、简述

RTP实时传输协议,广泛应用于流媒体传输应用场景,根据rfc3550介绍,RTP协议应用场景有如下几种:

Ø  简单多播音频会议(Simple Multicast Audio Conference)

Ø  音频和视频会议(Audioand Video Conference)

Ø  混频器和转换器(MixersandTranslators)

Ø  分层编码(LayeredEncodings)

        在实时音视频应用场合,考虑低延迟问题一般都使用RTP over UDP进行流媒体数据的传输,因此对于丢包、延迟、流畅性的考虑,发送端必须了解发送出去的流媒体数据到达对端的统计信息,RTP 控制协议 RTCP,就是用于监控服务质量和传达关于在一个正在进行的会议中的参与者的信息,包括对抗卡顿、网络拥塞控制扩展功能的实现,均利用RTCP报文实现,有名的是Google的GCC(拥塞控制算法(Google Congestion Control,简称GCC[1]))),RTCP的Nack、fir、pli报文都是实现抗丢包的策略,详细可查看rfc4585;

2、RFC3550中关于RTCP的介绍

2.1、基本场景介绍

       RTP 控制协议(RTCP)向会议中所有成员周期性发送控制包。它使用与数据包相同的传输机制。底层协议必须提供数据包和控制包的复用,例如用不同的 UDP 端口或相同的UDP端口(采用复用模式下)。RTCP 提供以下四个功能:

Ø  基本功能是提供数据传输质量的反馈;

     这是 RTP 作为一种传输协议的主要作用,它与其他协议的流量和拥塞控制相关。反馈可能对自适应编码有直接作用,并且 IP 组播的实验表明它对于从接收端得到反馈信息以诊断传输故障也有决定性作用。向所有成员发送接收反馈可以使"观察员"评估这些问题是局部的还是全局的。利用类似多点广播的传输机制,可以使某些实体,诸如没有加入会议的网络业务观察员,接收到反馈信息并作为第三方监视员来诊断网络故障。反馈功能通过 RTCP 发送者和接收者报告实现。

Ø  RTCP 为每个 RTP 源传输一个固定的识别符,称为规范名(CNAME);

     由于当发生冲突或程序重启时 SSRC可能改变,接收者要用 CNAME 来跟踪每个成员。接收者还要用 CNAME 来关联一系列相关 RTP 会话中来自同一个成员的多个数据流,例如同步语音和图像。

Ø  1和2功能要求参与方都发送RTCP报文,为保证会议参与方增长,必须严格控制发送包速率,避免过多占用本端带宽,导致视频质量差;

    通过让每个成员向所有成员发送控制包,各个成员都可以独立地观察会议中所有成员的数目。此数目可以用来估计发包速率。

Ø  传输最少的会议控制信息;

    例如在用户接口中显示参与的成员。这最可能在"松散控制"的会议中起作用,在"松散控制"会议里,成员可以不经过资格控制和参数协商而加入或退出会议。

2.2、包格类型介绍

类型

类型简称

描述

RFC文档

192

FIR

关键帧重传请求(IDR帧,无需参考帧可解码)

RFC2032

193

NACK

否定确认,NACK重传(丢包)

RFC2032

194

SMPTETC

SMPTE time-code 映射

RFC5484

195

IJ

extended inter-arrival jitter report.

RFC5450

200

SR

发送者报告,描述作为活跃发送者成员的发送和接收统计数字

RFC3550

201

RR

接收者报告,描述非活跃发送者成员的接收统计数字;

RFC3550

202

SDES

源描述项,其中包括规范名 CNAME。

RFC3550

203

BYE

表明参与者将结束会话。

RFC3550

204

APP

应用描述功能

RFC3550

205

RTPFB

通用RTP反馈

RFC4585

 

206

 

PSFB

PLI

 

有效载荷比

 

RFC4585

SLI

RPSI

207

XR

RTCP扩展

RFC3611

208

AVB

AVB RTCP数据包

IEEE1733

209

RSI

接收端汇总信息

RFC5760

 

 

 

 

注:其中206Type的PSFB中又拥有三个子项,详见rfc4585第六章

PSFB(Payload-Specific FB)消息被定义为载荷类型为PSFB的RTCP消息;

PLI:The PLI FB messageis identified by PT=PSFB and FMT=1. Picture Loss Indication,为整个图像帧丢失后发送

SLI:The SLI FB messageis identified by PT=PSFB and FMT=2. Slice Loss Indication,为帧内部分块损坏后发送

RPSI:The RPSI FB messageis identified by PT=PSFB and FMT=3. Reference Picture Selection Indication

 

其中各种类型报文均有用途,一般使用中各种类型有如下用途:

I 、关键帧请求

主要包括SLI/PLI/FIR,集中报文手段,目的是在关键帧丢失无法解码时,请求发送方重新生成并发送一个关键帧。本质是一种重传,但是跟传输层的重传的区别是,它重传是最新生成的帧。

PLI 是Picture LossIndication,SLI 是Slice Loss Indication。发送方接收到接收方反馈的PLI或SLI需要重新让编码器生成关键帧并发送给接收端。

FIR 是Full Intra Request,这里面Intra的含义可能很多人不知道。Intra的含义是图像内编码,不需要其他图像信息即可解码;Inter指图像间编码,解码需要参考帧。故Intra Frame其实就是指I帧,Inter Frame指P帧或B帧。

那么为什么在PLI和SLI之外还需要一个FIR呢?原因是使用场景不同,FIR更多是在一个中心化的Video Conference中,新的参与者加入,就需要发送一个FIR,其他的参与者给他发送一个关键帧这样才能解码,而PLI和SLI的含义更多是在发生丢包或解码错误时使用。

 

II 、重传请求

主要包括RTX/NACK/RPSI

这个重传跟关键帧请求的区别是它可以要求任意帧进行重传

 

III、码率控制

主要包括REMB/TMMBR/TMMBN

TMMBR是Temporal Max MediaBitrate Request,表示临时最大码率请求。表明接收端当前带宽受限,告诉发送端控制码率。

REMB是ReceiverEstimatedMax Bitrate,接收端估计的最大码率。

TMMBN是Temporal Max MediaBitrate Notification

另外,除了关键帧请求和重传,Webrtc还支持RED/FEC等冗余编码和前向纠错手段来保证视频质量。

以上报文用途,摘录自http://blog.csdn.net/wangruihit/article/details/47041515的博客;

2.3、RTCP传输时间间隔

        为保证会议参与方人数稳定上升,必须对RTCP报文带宽进行管理控制,如一个上千人的会议,就必须对这个进行管理考虑,否则单端带宽占用严重,但对于MCU集中混屏会议来说,客户端不需要做这点,需要服务器去做。

        RTP 被设计为允许应用自动适应不同的规模的会话――从几个参与者到几千个参与者的会话。对每一个会话,我们假定数据传输受到一个上限――会话带宽的限制。会话带宽分配给所有的参与者。这个带宽会被预留,并由网络所限制。如果没有预留,基于环境的其他约束将会确定合理的最大带宽供会话使用,这就是会话带宽。会话带宽在一定程度上独立于媒体编码,但媒体编码却依赖于会话带宽。

        此参数由单个发送者选择的编码方式的数据带宽算出。会话管理可能会基于多播范围的规则或其他标准确定带宽限制。所有的参与者应使用相同的会话带宽值以保证计算出相同的 RTCP 间隔。

        控制传输带宽应当是会话带宽的一小部分,这部分所占总的会话带宽的百分比应是已知的一小部分;

        传输协议的首要功能是传输数据;已知:控制传输带可以被放进带宽描述中提供给资源预留协议,并且使每个参与者都可以独立的计算出他所占有的带宽份额。

        控制传输带宽作为额外的一部分加入到会话带宽中。建议 RTCP 控制传输带宽为 RTCP 会话带宽的 5%。其中的 1/4 分配给发送者;当发送者的比例超过所有参与者的 1/4 时,其 RTCP 控制带宽相应增加。所有的会话参与者必须使用相同的常数(以上提到的百分比),以便计算出相同的发送时间间隔。这些常数应在一个特殊的描述文件中确定。

        计算出的 RTCP 复合包的发送时间间隔应该有一个下限,以免参与者数量较少时大量发送 RTCP 包。这也使网络暂时断开时,发送间隔不会太小。在应用开始时,一个延迟应加到第一个的 TCP 复合包发送之前,以便从其他参与者接收 RTCP 复合包。这样,发送时间间隔能更快的收敛到正确的值。这个延迟可以设为最小时间间隔的一半。固定的时间间隔建议为 5 秒。

        一个实现可能使 RTCP 最小发送时间间隔与会话带宽参数成比例,则应满足下列约束:

1、对多播会话,只有活动的数据发送者使用减小的最小化的值计算 RTCP 复合包的发送时间间隔。

2、对单播会话,减小的值也可能被不是活动的数据发送者使用,发送初始的 RTCP 复合包之前的延迟可能是 0。

3、 对所有会话,在计算参与者的离开时间时,这个固定最小值会被用到。因此,不使用减小的值进行 RTCP包的发送,就不会被其他参与者提前宣布超时。

4、减小的最小时间间隔建议为:360/sb(秒),其中 sb:会话带宽(千字节/秒)。当sb>72kb/s 时,最小时间间隔将小于 5s。

5、计算出的 RTCP 包的时间间隔与组中参与者的人数成正比。(参与者越多,发送时间间隔越长,每个参与者占有的 RTCP 带宽越小)。

6、 RTCP 包的(真实)时间间隔是计算出的时间间隔的 0.5~1.5 倍之间某个随机的值,以避免所有的参与者意外的同步。

7、RTCP 复合包的平均大小将会被动态估计,包括所有发送的包和接收的包。以自动适应携带的控制信息数量的变化。

8、由于计算出的时间间隔依赖于组中的人数。因此,当一个的用户加入一个已经存在的会话或者大量的用户几乎同时加入一个新的会话时,就会有意外的初始化效应。这些新       用户将在开始时错误的估计组中的人数(估计太小)。因此他们的 RTCP 包的发送时间间隔就会太短。如果许多用户同时加入一个会话,这个问题就很重要了。为了处理这处问题考虑了一种叫“时间重估”的算法。这个算法使得组中人数增加时,用户能够支持 RTCP 包的传输。当有用户离开会话,不管是发送 BYE 包还是超时,组中的人数会减少。计算出的时间间隔也应当减少。

     因此,应用“逆向重估”算法,使组中的成员更快的减少他们的时间间隔,以对组中的人数减少做出响应。

9、BYE 包的处理和其他 RTCP 包的处理不同。BYE 包的发送用到一个“放弃支持”算法。以避免大量的 BYE包同时发送,使大量参与者同时离开会话。

这个算法适用于所有参与者都允许 RTCP 包的情况。此时,会话带宽=每个发送者的带宽×会话中参与者的总人数。

同时针对不同类型的RTCP报文,发送间隔时间不同,这样适应性更强,如语音包的反馈报文比视频包的间隔长,Tmmbr需立即发送,SSRC报文较短。

3、WebRtc中RTCP报文发送控制

      Webrtc中RTCP报文发送间隔控制是相当好的基本处理流程如下:

     

RTCPSender::TimeToSendRTCPReport(boolsendKeyframeBeforeRTP)接口中写道

对于音频,我们使用一个固定的5秒间隔。在带宽小于360 kbit/s时,视频使用1秒的时间间隔,在视频带宽低于10kbit/s时,表面上我们打破最大5% RTCP 带宽限制,但那应该是非常罕见的。

RFC3550写道

最大的RTCP带宽应是会会话带宽的5%,一个SR报文在包含CNAME时,大约是65个字节,一个RR报文大约为28字节。

最小发送间隔的值推荐为360/会话带宽,发送间隔单位为秒,带宽单位为kbps,这个最低在带宽大于72 kb / s时,这个最小值小于5秒。如果参与者尚未发送RTCP包(包已经初始化),则常量Tmin设置为2.5秒,否则设置为5秒。

RTCP包的发送间隔是在[ 0.5,1.5 ]倍计算值之间随机变化的,避免所有参与者同步出现的意外情况

1.   如果我们需要发送报文

      参与者是发送者(we_sent为真),常数c设置为RTCP包大小平均值(avg_rtcp_size)除以RTCP带宽的25%(rtcp_bw),和常数n设置为发送的方数。

2.   如果我们只接收报文

      如果we_sent是不为真,常数c设置为RTCP包大小平均值(avg_rtcp_size)除以RTCP带宽的75%。常数n被设置为接收的方数(members - senders)。如果发件人                  senders的数量大于25%,senders和members一起处理。

      P2p不需要重新考虑

      “timer reconsideration”是使用的.该算法实现了一种简单的回退机制,会导致用户阻止RTCP包传输如果群体规模正在增加。

      N = number of members

      C = avg_size /(rtcpbw / 4)

3.   确定性计算区间Td设置为最大(Tmin,N×C)。

4.   计算出的区间t被设置为均匀分布在确定计算区间的0.5到1.5倍之间的数。

5.   由此产生的t值除以3 / 2 = 1.21828,弥补了定时器复议算法收敛到一个值低于预期的平均RTCP带宽

位于rtcp_sender.cc中

 

以下为WebRtc中计算法源码:

void RTCPSender::PrepareReport(const std::set<RTCPPacketType>& packetTypes,

                               const FeedbackState& feedback_state){

  // Add all flags as volatile. Non volatile entries will not be overwritten

  // and all new volatile flags added will be consumed by the end of this call.

  SetFlags(packetTypes, true);

 

  if (packet_type_counter_.first_packet_time_ms== -1)

    packet_type_counter_.first_packet_time_ms= clock_->TimeInMilliseconds();

 

  bool generate_report;

  if (IsFlagPresent(kRtcpSr)|| IsFlagPresent(kRtcpRr)){

    // Report type already explicitly set, don't automatically populate.

    generate_report = true;

    RTC_DCHECK(ConsumeFlag(kRtcpReport)== false);

  } else {

    generate_report =

        (ConsumeFlag(kRtcpReport)&& method_ == RtcpMode::kReducedSize)||

        method_ == RtcpMode::kCompound;

    if (generate_report)

      SetFlag(sending_? kRtcpSr : kRtcpRr, true);

  }

 

  if (IsFlagPresent(kRtcpSr)|| (IsFlagPresent(kRtcpRr)&& !cname_.empty()))

    SetFlag(kRtcpSdes, true);

 

  if (generate_report) {

    if (!sending_ && xr_send_receiver_reference_time_enabled_)

      SetFlag(kRtcpXrReceiverReferenceTime, true);

    if (feedback_state.has_last_xr_rr)

      SetFlag(kRtcpXrDlrrReportBlock, true);

 

    // generate next time to send an RTCP report

    uint32_t minIntervalMs = RTCP_INTERVAL_AUDIO_MS;

 

    if (!audio_) {

      if (sending_) {

        // Calculate bandwidth for video; 360 / send bandwidth in kbit/s.

        uint32_t send_bitrate_kbit = feedback_state.send_bitrate/ 1000;

        if (send_bitrate_kbit !=0)

          minIntervalMs =360000 / send_bitrate_kbit;

      }

      if (minIntervalMs > RTCP_INTERVAL_VIDEO_MS)

        minIntervalMs = RTCP_INTERVAL_VIDEO_MS;

    }

    // The interval between RTCP packets is varied randomly over the

    // range [1/2,3/2] times the calculated interval.

    uint32_t timeToNext =

        random_.Rand(minIntervalMs* 1 / 2, minIntervalMs * 3/ 2);

    next_time_to_send_rtcp_ = clock_->TimeInMilliseconds()+ timeToNext;

 

    StatisticianMap statisticians =

        receive_statistics_->GetActiveStatisticians();

    RTC_DCHECK(report_blocks_.empty());

    for (auto& it: statisticians){

      AddReportBlock(feedback_state, it.first, it.second);

    }

  }

}

 

发布了22 篇原创文章 · 获赞 21 · 访问量 13万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览