Android IOS WebRTC 音视频开发总结(八十六)-- WebRTC中RTP/RTCP协议实现分析

本文主要介绍WebRTC中的RTP/RTCP协议,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio

支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:blackerteam 或 webrtcorgcn)。

一 前言

RTP/RTCP协议是流媒体通信的基石。RTP协议定义流媒体数据在互联网上传输的数据包格式,而RTCP协议则负责可靠传输、流量控制和拥塞控制等服务质量保证。在WebRTC项目中,RTP/RTCP模块作为传输模块的一部分,负责对发送端采集到的媒体数据进行进行封包,然后交给上层网络模块发送;在接收端RTP/RTCP模块收到上层模块的数据包后,进行解包操作,最后把负载发送到解码模块。因此,RTP/RTCP 模块在WebRTC通信中发挥非常重要的作用。

本文在深入研究WebRTC源代码的基础上,以Video数据的发送和接收为例,力求用简洁语言描述RTP/RTCP模块的实现细节,为进一步深入掌握WebRTC打下良好基础。

二 RTP/RTCP协议概述

RTP协议是Internet上针对流媒体传输的基础协议,该协议详细说明在互联网上传输音视频的标准数据包格式。RTP协议本身只保证实时数据的传输,RTCP协议则负责流媒体的传输质量保证,提供流量控制和拥塞控制等服务。在RTP会话期间,各参与者周期性彼此发送RTCP报文。报文中包含各参与者数据发送和接收等统计信息,参与者可以据此动态控制流媒体传输质量。

RFC3550 [1]定义RTP/RTCP协议的基本内容,包括报文格式、传输规则等。除此之外,IETF还定义一系列扩展协议,包括RTP协议基于档次的扩展,和RTCP协议基于报文类型的扩展,等等。详细内容可参考文献[2]。

三 WebRTC线程关系和数据流

WebRTC对外提供两个线程:Signal和Worker,前者负责信令数据的处理和传输,后者负责媒体数据的处理和传输。在WebRTC内部,有一系列线程各司其职,相互协作完成数据流管线。下面以Video数据的处理流程为例,说明WebRTC内部的线程合作关系。

图1 WebRTC线程关系和数据管线

如图1所示,Capture线程从摄像头采集原始数据,得到VideoFrame;Capture线程是系统相关的,在Linux系统上可能是调用V4L2接口的线程,而在Mac系统上可能是调用AVFoundation框架的接口。接下来原始数据VideoFrame从Capture线程到达Worker线程,Worker线程起搬运工的作用,没有对数据做特别处理,而是转发到Encoder线程。Encoder线程调用具体的编码器(如VP8, H264)对原始数据VideoFrame进行编码,编码后的输出进一步进行RTP封包形成RTP数据包。然后RTP数据包发送到Pacer线程进行平滑发送,Pacer线程会把RTP数据包推送到Network线程。最终Network线程调用传输层系统函数把数据发送到网络。

在接收端,Network线程从网络接收字节流,接着Worker线程反序列化为RTP数据包,并在VCM模块进行组帧操作。Decoder线程对组帧完成的数据帧进行解码操作,解码后的原始数据VideoFrame会推送到IncomingVideoStream线程,该线程把VideoStream投放到render进行渲染显示。至此,一帧视频数据完成从采集到显示的完整过程。

在上述过程中,RTP数据包产生在发送端编码完成后,其编码输出被封装为RTP报文,然后经序列化发送到网络。在接收端由网络线程收到网络数据包后,经过反序列化还原成RTP报文,然后经过解包得到媒体数据负载,供解码器进行解码。RTP报文在发送和接收过程中,会执行一系列统计操作,统计结果作为数据源供构造RTCP报文之用。RTP报文构造、发送/接收统计和RTCP报文构造、解析反馈,是接下来分析的重点。

四 RTP报文发送和接收

RTP报文的构造和发送发生在编码器编码之后、网络层发送数据包之前,而接收和解包发生在网络层接收数据之后、解码器编码之前。本节详细分析这两部分的内容。

4.1 RTP报文构造和发送

图2描述发送端编码之后RTP报文的构造和发送过程,涉及三个线程:Encoder、Pacer和Network,分别负责编码和构造RTP报文,平滑发送和传输层发送。下面详细描述这三个线程的协同工作过程。

图2 RTP报文构造和发送

Encode线程调用编码器(比如VP8)对采集到的Raw VideoFrame进行编码,编码完成以后,其输出EncodedImage通过回调到达VideoSendStream::Encoded()函数,进而通过PayloadRouter路由到ModuleRtpRtcpImpl::SendOutgoingData()。接下来,该函数向下调用RtpSender::SendOutgoingData(),进而调用RtpSenderVideo::SendVideo()。该函数对EncodedImage进行打包,然后填充RTP头部构造RTP报文;如果配置了FEC,则进一步封装为FEC报文。最后返回RtpSender::SendToNetwork()进行下一步发送。

RtpSender::SendToNetwork()函数把报文存储到RTPPacketHistory结构中进行缓存。接下来如果开启PacedSending,则构造Packe发送到PacedSender进行排队,否则直接发送到网络层。

Pacer线程周期性从队列中获取Packet,然后调用PacedSender::SendPacket()进行发送,接下来经过ModuleRtpRtcpImpl到达RtpSender::TimeToSendPacket()。该函数首先从RtpPacketHistory缓存中拿到Packet的负载,然后调用PrepareAndSendPacket()函数:更新RtpHeader的相关域,统计延迟和数据包,调用SendPacketToNetwork()把报文发送到传输模块。

Network线程则调用传输层套接字执行数据发送操作。至此,发送端的RTP构造和发送流程完成。需要注意的是,在RtpSender中进行Rtp发送后,会统计RTP报文相关信息。这些信息作为RTCP构造SR/RR报文的数据来源,因此非常重要。

4.2 RTP报文接收和解析

在接收端,RTP报文的接收和解包操作主要在Worker线程中执行,RTP报文从Network线程拿到后,进入Worker线程,经过解包操作,进入VCM模块,由Decode线程进行解码,最终由Render线程进行渲染。下图3描述RTP报文在Worker线程中的处理流程。

图3 RTP报文接收和解析

RTP数据包经网络层到达Call对象,根据其SSRC找到对应的VideoReceiveStream,通过调用其DeliverRtp()函数到RtpStreamReceiver::DeliverRtp()。该函数首先解析数据包得到RTP头部信息,接下来执行三个操作:1.码率估计;2.继续发送数据包;3.接收统计。码率估计模块使用GCC算法估计码率,构造REMB报文,交给RtpRtcp模块发送回发送端。而接收统计则统计RTP接收信息,这些信息作为RTCP RR报文的数据来源。下面重点分析接下来的数据包发送流程。

RtpStreamReceiver::ReceivePacket()首先判断数据包是否是FEC报文,如果是则调用FecReceiver进行解包,否则直接调用RtpReceiver::IncomingRtpPacket()。该函数分析RTP报文得到通用的RTP头部描述结构,然后调用RtpReceiverVideo::ParseRtpPacket()进一步得到Video相关信息和负载,接着经过回调返回RtpStreamReceiver对象。该对象把Rtp描述信息和负载发送到VCM模块,继续接下来的JitterBuffer缓存和解码渲染操作。

RTP报文解包过程是封包的逆过程,重要的输出信息是RTP头部描述和媒体负载,这些信息是下一步JitterBuffer缓存和解码的基础。另外对RTP报文进行统计得到的信息则是RTCP RR报文的数据来源。

五 RTCP报文发送和接收

RTCP协议是RTP协议的控制下可以,负责流媒体的服务质量保证。比较常用的RTCP报文由发送端报告SR和接收端报告RR,分别包含数据发送统计信息和数据接收信息。这些信息对于流媒体质量保证非常重要,比如码率控制、负载反馈,等等。其他RTCP报文还有诸如SDES、BYE、SDES等,RFC3550对此有详细定义。

本节重点分析WebRTC内部RTCP报文的构造、发送、接收、解析、反馈等流程。需要再次强调的是,RTCP报文的数据源来自RTP报文发送和接收时的统计信息。在WebRTC内部,RTCP报文的发送采取周期性发送和及时发送相结合的策略:ModuleProcess线程周期性发送RTCP报文;而RtpSender则在每次发送RTP报文之前都判断是否需要发送RTCP报文;另外在接收端码率估计模块构造出REMB报文后,通过设置超时让ModuleProcess模块立即发送RTCP报文。

5.1 RTCP报文构造和发送

在发送端,RTCP以周期性发送为基准,辅以RTP报文发送时的及时发送和REMB报文的立即发送。发送过程主要包括Feedback信息获取、RTCP报文构造、序列化和发送。图4描述了RTCP报文的构造和发送过程。

图4 RTCP报文构造和发送

ModuleProcess线程周期性调用ModuleRtpRtcpImpl::Process()函数,该函数通过RTCPSender::TimeToSendRtcpReport()函数确定当前是否需要立即发送RTCP报文。若是,则首先从RTPSender::GetDataCounters()获取RTP发送统计信息,然后调用RTCPSender::SendRTCP(),接着是SendCompoundRTCP()发送RTCP组合报文。关于RTCP组合报文的定义,请参考文献[1]。

在SendCompoundRTCP()函数中,首先通过PrepareReport()确定将要发送何种类型的RTCP报文。然后针对每一种报文,调用其构造函数(如构造SR报文为BuildSR()函数),构造好的报文存储在PacketContainer容器中。最后调用SendPackets()进行发送。

接下来每种RTCP报文都会调用各自的序列化函数,把报文序列化为网络字节流。最后通过回调到达PacketContainer::OnPacketReady(),最终把字节流发送到传输层模块:即通过TransportAdapter到达BaseChannel,Network线程调用传输层套接字API发送数据到网络。

RTCP报文的构造和发送过程总体不是很复杂,最核心的操作就是获取数据源、构造报文、序列化和发送。相对来说构造报文和序列化比较繁琐,基于RFC定义的细节进行。

5.2 RTCP报文接收和解析

接收端的RTCP报文接收和解析过程如图5所示。

图5 RTCP报文接收和解析

在接收端,RTCP报文的接收流程和RTP一样,经过网络接收之后到达Call对象,进而通过SSRC找到VideoReceiveStream,继而到达RtpStreamReceiver。接下来RTCP报文的解析和反馈操作都在ModuleRtpRtcpImpl::IncomingRtcpPacket()函数中完成。该函数首先调用RTCPReceiver::IncomingRtcpPacket()解析RTCP报文,得到RTCPPacketInformation对象,然后调用 TriggerCallbacksFromRTCPPacket(),触发注册在此处的各路观察者执行回调操作。

RTCPReceiver::IncomingRtcpPacket()使用RTCPParser解析组合报文,针对每一种报文类型,调用对应的处理函数(如处理SDES的HandleSDES函数),反序列化后拿到报文的描述结构。最后所有报文综合在一起形成RTCPPacketInformation对象。该对象接下来作为参数调用TriggerCallbacksFromRTCPPacket()函数触发回调操作,如处理NACK的回调,处理SLI的回调,处理REMB的回调,等等。这些回调在各自模块控制流媒体数据的编码、发送、码率等服务质量保证,这也是RTCP报文最终起作用的地方。

至此,我们分析了RTCP报文发送和接收的整个流程。

六 总结

本文在深入分析WebRTC源代码的基础上,结合流程图描述出RTP/RTCP模块的实现流程,在关键问题上(如RTCP报文的数据来源)进行深入细致的研究。为进一步深入掌握WebRTC的实现原理和细节打下良好基础。

参考文献

[1] RFC3550 - RTP: A Transport Protocol for Real-Time Applications
https://www.ietf.org/rfc/rfc3550.txt

[2] 超越RFC3550 - RTP/RTCP协议族分析 : http://www.jianshu.com/p/e5e21aeb219f

时间: 2024-10-15 22:43:14

Android IOS WebRTC 音视频开发总结(八十六)-- WebRTC中RTP/RTCP协议实现分析的相关文章

Android IOS WebRTC 音视频开发总结(十六)

本节主要分享视频通话中android和ios上操作音频设备的方式,如调解音量大小,启用扬声器(本系列文章转载请说明出处,博客园RTC.Blacker). 先看看webrtc中处理音频设备代码的目录结构: 第一种方式就是直接调用Android或ObjectC的API,代码如下(WebRtcAudioTrack.java): 通过调用Android提供的AudioManager类来操作音频设备(webrtc是用C++写的,通过JNI来调用这些JAVA方法). 下面是IOS中操作音频设备的方式(aud

Android IOS WebRTC 音视频开发总结(七六)-- 探讨直播低延迟低流量的粉丝连麦技术

本文主要探讨基于WebRTC的P2P直播粉丝连麦技术 (作者:郝飞,亲加云CTO,编辑:dora),最早发表在[这里] 支持原创,转载必须注明出处,欢迎关注微信公众号blacker(微信ID:blackerteam  或 webrtcorgcn) 到目前为止,直播行业继续如预期的那样如火如荼的发展着,在先后竞争完延迟,高清,美 颜,秒开等功能后,最近各大直播平台比拼的一个热点就是连麦.什么是连麦? 简单??述 就是当主播直播期间,可以与其中某一个粉丝进行互动,并且其他粉丝能够观看到这个互动 过程

Android IOS WebRTC 音视频开发总结(十九)

折腾了一个多星期终于将kurento的环境搭建好(开发阶段的产品,有些BUG要自己解决),所以单独写篇文件来介绍. 下面开始介绍kurento,文章来自博客园RTC.Blacker,转载请说明出处. 一.kurento是什么? 搞视频会议就会涉及一对多.多对多.广播.转码.混音.合屏.录制,这就需要用到流媒体服务器,而kurento就具有这些功能. 他主要用来作为webrtc的流媒体服务器,因为BUG多,目前不适于商用,不过前景可期,具体说明见下图: 说明: 1.看到这里您可不要讲他的功能和IC

Android IOS WebRTC 音视频开发总结(十九)- kurento

转自:http://www.cnblogs.com/lingyunhu/p/4232348.html 折腾了一个多星期终于将kurento的环境搭建好(开发阶段的产品,有些BUG要自己解决),所以单独写篇文件来介绍. 下面开始介绍kurento,文章来自博客园RTC.Blacker,转载请说明出处. 一.kurento是什么? 搞视频会议就会涉及一对多.多对多.广播.转码.混音.合屏.录制,这就需要用到流媒体服务器,而kurento就具有这些功能. 他主要用来作为webrtc的流媒体服务器,因为

Android IOS WebRTC 音视频开发总结(十)

继续上一篇中未翻译完成的部分,主要包括下面三个部分: 1,扩展:WebRTC多方通话. 2,MCU Multipoint Control Unit. 2, 扩展:VOIP,电话,消息通讯. 注意:翻译的时候不是逐字逐句的,而是按照自己的理解翻译的,同时为了便于理解,也加入一些自己组织的语言. 转载请说明出处: http://www.cnblogs.com/lingyunhu. 英文来自:http://www.html5rocks.com/en/tutorials/webrtc/infrastru

Android IOS WebRTC 音视频开发总结(十二)

谈到音视频不得不谈谈对视频呈现的理解,为了让大家能有一个更好的理解,先看看android里面SurfaceView的原理,后续陆续分享其绘画原理. 说明:本文是转载的,转载自哪里我也不知道,貌似经过很多层转载了,在这里先对原创者表示谢意.  cnblogs RTC.Blacker 通过自定义View, 我们知道使用它可以做一些简单的动画效果.它通过不断循环的执行View.onDraw方法,每次执行都对内部显示的图形做一些调整,我们假设 onDraw方法每秒执行20次,这样就会形成一个20帧的补间

Android IOS WebRTC 音视频开发总结(十四)

这篇文章主要介绍XMPP与SIP,很多人容易混淆这两个概念,转载请说明出处, cnblogs_RTC.Blacker. 简介:XMPP和SIP都是应用层协议,主要用于互联网上发送语音和即时通讯. SIP在RFC 3621中定义,XMPP在RFC 3920中定义, XMPP是从即时通讯中演变而来,SIP是从VOIP中演变而来,XMPP为了会话协商添加了一个扩展叫做Jingle,SIP为了即时通讯业务添加了一个扩展叫做SIMPLE. SIP (Session Initiation Protocol)

Android IOS WebRTC 音视频开发总结(十八)

本文主要介绍上次碰到的某些机器上看不到视频的问题,文章来自博客园RTC.Blacker,转载请说明出处. 之前做的视频聊天App一直运行良好,前几天客户反馈说在三星9100. Android4.0.3上看不到自己的图像. 一翻查找后才发现是WebRTC底层的BUG,现已修复并反馈给社区了,下面主要分析BUG原因及解决方案. Bug主要原因: 设置摄像头参数时使用了不支持的参数,导致Camera.setParameters出错,导致后面的内存分配出错,视频流读取出错,最终导致看不到视频. Bug具

Android IOS WebRTC 音视频开发总结(二六)

本文主要是自己之前研究WebRTC代码结构时的一些资料(包括Android,iOS,PC),文章来自博客园RTC.Blacker,转载请说明出处. 1.WEBRTC模块:音频数据采集.发送.接收.播放调用过程: 2.WEBRTC模块:视频数据采集.发送.接收.播放调用过程: 3.libjingle模块: 3.1.底层包发送(通过注册transport来实现包的发送,逻辑基本上跟之前的一样) 3.2.视频包发送: 3.3.音频包发送: 3.4.收到视频包: 3.5.收到音频包: