一、协议链接
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固定是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。参见下面详细介绍。
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
首先先了解下RTP包状态,目前定义了如下四种状态,每个状态值2bits,用来标识RTP包的到达状态,以及与前面RTP包的时间间隔大小信息:
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。行程长度,标识有多少个连续包为相同状态。
示例:
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个包的状态。
symbol size为0,这样能标识14个包的状态。第一个包状态为"packet not received"(0),接着后面5个包状态为"packet received"(1),再接着三个包状态为"packet not received",再接着三个包状态为"packet received",最后两个包状态为"packet not received"。
symbol size为1,这样只能标识7个包的状态。第一个包为"packet not received"(00)状态,第二个包为 "packet received, w/o timestamp"(11)状态,再接着三个包为"packet received"(01)状态,最后两个包为"packet not received"(00)状态。
六、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
参考: