WebRTC QoS方法四.4(Transport-wide Congestion Control协议笔记)
一、协议链接https://tools.ietf.org/pdf/draft-holmer-rmcat-transport-wide-cc-extensions-01.pdf二、协议定义三、抓包示例四、参数说明version (V): 2 bits。固定是2。padding (P): 1 bit。feedback message type (FMT): 5 bits。TransportCC固定是1
一、协议链接
二、协议定义

三、参数说明
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
参考:
更多推荐



所有评论(0)