一、协议链接

https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01icon-default.png?t=O83Ahttps://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01

二、协议定义

三、参数说明

version (V): 2 bits。固定是2。

padding (P): 1 bit。

feedback message type (FMT): 5 bits。TransportCC固定是15。

payload type (PT): 8 bits。TransportCC固定是205。

SSRC of packet sender: 32 bits。

SSRC of media source: 32 bits。

base sequence number: 16 bits。TransportFeedback包中记录的第一个RTP包的transport sequence number,在反馈的各个TransportFeedback RTCP包中,这个字段不一定是递增的,也有可能比之前的RTCP包小。

packet status count: 16 bits。表示这个TransportFeedback包记录了多少个RTP包信息,这些RTP的transport sequence number以base sequence number为基准。比如记录的第一个RTP包的transport sequence number为base sequence number,那么记录的第二个RTP包transport sequence number为base sequence number+1

reference time: 24 bits。表示参考时间,以64ms为单位,RTCP包记录的RTP包到达时间信息以这个reference time为基准进行计算。

feedback packet count: 8 bits。用于计数发送的每个TransportFeedback包,相当于RTCP包的序列号。可用于检测TransportFeedback包的丢包情况。

packet chunk: 16 bits。类似Flex FEC头的mask,是每个报文recv delta间隔时间占用长度位的一个map表。有的recv delta占用一个字节,有的占用两个字节。在chunk里面标识具体长度。具体细节请参见下面介绍。

recv delta: 8 bits For each "packet received" 状态的包,也就是收到的RTP包,在recv delta列表中添加对应的的到达时间间隔信息,用于记录RTP包到达时间信息。通过前面的reference time以及recv delta信息,我们就可以得到RTP包到达时间。

以250us(0.25ms)为单位,表示RTP包到达时间与前面一个RTP包到达时间的间隔,对于记录的第一个RTP包,该包的时间间隔是相对reference time的。

如果在packet chunk记录了一个"Packet received, small delta"状态的包,那么就会在receive delta列表中添加一个无符号1字节长度receive delta,无符号1字节取值范围[0,255],由于Receive Delta以0.25ms为单位,故此时Receive Delta取值范围[0, 63.75]ms
如果在packet chunk记录了一个"Packet received, large or negative delta"状态的包,那么就会在receive delta列表中添加一个有符号2字节长度的receive delta,范围[-8192.0, 8191.75] ms
如果时间间隔超过了最大限制,那么就会构建一个新的TransportFeedback RTCP包,由于reference time长度为3字节,所以目前的包中3字节长度能够覆盖很大范围了
以上说明总结起来就是:对于收到的RTP包在TransportFeedback RTCP receive delta列表中通过时间间隔记录到达时间,如果与前面包时间间隔小,那么使用1字节表示,否则2字节,超过最大取值范围,就另起新RTCP包了。

对于"Packet received, small delta"状态的包来说,receive delta最大值63.75ms,那么一秒时间跨度最少能标识1000/63.75~=16个包。由于receive delta为250us的倍数,所以一秒时间跨度最多能标识4000个包。

五、packet chunk

packet chunk有两种类型,Run length chunk(行程长度编码数据块)与Status vector chunk(状态矢量编码数据块),对应packet chunk结构的两种编码方式。packet chunk的第一bit标识chunk类型。

Run length chunk:行程长度编码是一种简单的数据压缩算法,其基本思想是将重复且连续出现多次的字符使用“连续出现次数+字符”来描述,例如:aaabbbcdddd通过Run length编码就可以压缩为3a3bc4d。Run length chunk中就使用了Run length编码标识连续多个相同状态的包。

 

chunk type (T): 1 bit。Run length chunk时固定为0。

packet status symbol (S): 2 bits。包到达状态。 

 run length (L): 13 bits。行程长度,标识有多少个连续包为相同状态。

示例:

第一个chunk是0x2001=0010 0000 0000 0001b

chunk type (T) = 0b;Run length chunk时固定为0。

packet status symbol (S) = 01b;根据协议,包已经接收,并且recv delta占用一个字节

run length (L) = 0 0000 0000 0001b;只有一个连续包为相同状态。

 recv delta = 0xb4;单位是0.25ms*0xb4=45ms。

 Status Vector Chunk:

chunk type (T): 1 bit。Status Vector Chunk固定为1。

symbol size (S): 1 bit。为0表示只包含"packet not received" (0)以及"packet received"(1)状态,每个状态使用1bit表示,这样后面14bits的symbol list能标识14个包的状态。为1表示使用2bits来标识包状态,这样symbol list中我们只能标识7个包的状态。

symbol list: 14 bits。标识一系列包的状态, 总共能标识7或14个包的状态。

chunk type 为1,symbol size为0详细说明:

symbol size为0,这样能标识14个包的状态。第一个包状态为"packet not received"(0),接着后面5个包状态为"packet received"(1),再接着三个包状态为"packet not received",再接着三个包状态为"packet received",最后两个包状态为"packet not received"。

详细示例如下: 

第一个chunk是0x97a6=1001 0111 1010 0110b

chunk type =1b

symbol size=0b

symbol list=01 0111 1010 0110b

根据Base Sequence Number是15706,可以获取到15706报文是丢了。

接收端收到的包list是:01 0111 1010 0110b

chunk type 为1,symbol size为1详细说明:

symbol size为1,这样只能标识7个包的状态。第一个包为"packet not received"(00)状态,第二个包为 "packet received, w/o timestamp"(11)状态,再接着三个包为"packet received"(01)状态,最后两个包为"packet not received"(00)状态。

协议上是上面描述,但是实际上,在WebRTC代码中,这个字段表示recv delta占用字节数。

TransportFeedback::Parse

详细示例如下: 

第三个chunk是0xC544=1100 0101 0100 0100b

chunk type =1b

symbol size=1b

symbol list=00 01 01 01 00 01 00b

六、transport CC在webrtc上的使用

1、BWE根据TransportCC进行基于延时的码率估计用到该报文。

 2、GoogCcNetworkController::OnTransportPacketsFeedback 网络带宽探测也用到transportCC协议实现。

七、发送transport CC RTCP报文调用栈

RunPlatformThread
->ProcessThreadImpl::Process
->RemoteEstimatorProxy::Process
->RemoteEstimatorProxy::SendPeriodicFeedbacks---------周期调用,还有一处是立即返回
->PacketRouter::SendCombinedRtcpPacket
->ModuleRtpRtcpImpl2::SendCombinedRtcpPacket
->RTCPSender::SendCombinedRtcpPacket
->RTCPSender::PacketSender::AppendPacket
->rtcp::TransportFeedback::Create

八、发送带TransportSequenceNumber扩展头字段报文笔记

参见webrtc代码走读二十:extension扩展头协商及初始化流程_CrystalShaw的博客-CSDN博客

RtpPacket::AllocateRawExtension函数配置TransportSequenceNumber扩展头字段。

FindOrCreateExtensionInfo

VideoChannel::SetRemoteContent_w协商RTP扩展头

发送之前会记录两个时间:报文启动发送时间和报文实际发送时间。

启动发送的调用栈如下:

ProcessThreadImpl::Process
->PacedSender::Process
->PacingController::ProcessPackets
->PacketRouter::SendPacket
->ModuleRtpRtcpImpl2::TrySendPacket
->RtpSenderEgress::SendPacket
->RtpSenderEgress::AddPacketToTransportFeedback
->RtpTransportControllerSend::OnAddPacket

 实际发送时间,是udp socket一路发送信号量到Call::OnSentPacket函数。

ProcessThreadImpl::Process()
->PacedSender::Process()    
->PacingController::ProcessPackets()
->PacketRouter::SendPacket
->ModuleRtpRtcpImpl2::TrySendPacket
->RtpSenderEgress::SendPacket
->RtpSenderEgress::SendPacketToNetwork
->WebRtcVoiceMediaChannel::SendRtp
->MediaChannel::SendRtp
->MediaChannel::SendPacket
->MediaChannel::DoSendPacket
->BaseChannel::SendPacket
->SrtpTransport::SendRtpPacket
->RtpTransport::SendPacket
->DtlsTransport::SendPacket
->P2PTransportChannel::SendPacket
->ProxyConnection::Send
->UDPPort::SendTo
->AsyncUDPSocket::SendTo---后续参照下面调用关系,一路发送信号量到TransportFeedbackAdapter::ProcessSentPacket<-RtpTransportControllerSend::OnSentPacket

RtpTransportControllerSend::OnSentPacket
Call::OnSentPacket
RtpTransport::OnSentPacket(rtc::PacketTransportInternal * packet_transport, const rtc::SentPacket & sent_packet) 
DtlsTransport::OnSentPacket(rtc::PacketTransportInternal * transport, const rtc::SentPacket & sent_packet) 
P2PTransportChannel::OnSentPacket(const rtc::SentPacket & sent_packet)
OnSentPacket(rtc::AsyncPacketSocket * socket, const rtc::SentPacket & sent_packet) 
AsyncUDPSocket::SendTo(const void * pv, unsigned __int64 cb, const rtc::SocketAddress & addr, const rtc::PacketOptions & options) 
UDPPort::SendTo(const void * data, unsigned __int64 size, const rtc::SocketAddress & addr, const rtc::PacketOptions & options, bool payload) 
ProxyConnection::Send(const void * data, unsigned __int64 size, const rtc::PacketOptions & options) 
P2PTransportChannel::SendPacket(const char * data, unsigned __int64 len, const rtc::PacketOptions & options, int flags) 
DtlsTransport::SendPacket(const char * data, unsigned __int64 size, const rtc::PacketOptions & options, int flags) 
RtpTransport::SendPacket(bool rtcp, rtc::CopyOnWriteBuffer * packet, const rtc::PacketOptions & options, int flags) 
SrtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer * packet, const rtc::PacketOptions & options, int flags) 
BaseChannel::SendPacket(bool rtcp, rtc::CopyOnWriteBuffer * packet, const rtc::PacketOptions & options) 
Thread::QueuedTaskHandler::OnMessage(rtc::Message * msg) 
Thread::Dispatch(rtc::Message * pmsg) 
Thread::ProcessMessages(int cmsLoop) 
Thread::PreRun(void * pv) 

 

transport CC的RTCP报文的referenceTime时间戳是在AsyncUDPSocket::OnReadEvent函数获取

 RemoteEstimatorProxy::IncomingPacket获取到达时间

 

 八、参考

WebRTC研究:Transport-cc之RTP及RTCP - 剑痴乎

https://www.rfc-editor.org/rfc/rfc8888.pdf

参考:

Logo

一站式 AI 云服务平台

更多推荐