实时传输协议RTP
##1. RTP 实时传输协议(Real-time Transport Protocol,RTP)是一个网络传输协议,它是由IETF的多媒体传输工作小组1996年在RFC 1889中公布的。RTP协议详细说明了通过互联网上传递音频和视频的标准数据包格式。
每一个RTP数据报都由头部(Header)和载体(Payload)两个部分组成,其中头部前12个字节(96 bit)的含义是固定的,而负载则可以是音频或者视频数据。
- Ver.(2 bits)是目前协定的版本号码,目前版号是 2。
- P(1 bit)是用于RTP 封包(packet)结束点的预留空间,视封包是否需要多余的填塞空间。
- X(1 bit)是否在使用延伸空间于封包之中。.
- CC(4 bits)包含了 CSRC 数目用于修正标头(fixed header).
- M (1 bit) 是用于应用等级以及其原型(profile)的定义。如果不为零表示目前的资料有特别的程序解译。
- PT(7 bits)是指payload的格式并决定将如何去由应用程式加以解译。
- SSRC 是同步化来源。
实时传输控制协议(Real-time Transport Control Protocol 或RTP Control Protocol或简写RTCP)是实时传输协议的一个兄弟协议。RTCP为RTP媒体流提供信道外控制。RTCP本身并不传输数据,主要用来和RTP一起协作将要发送的音频和视频数据打包发送。RTCP的主要功能是为RTP所提供的服务质量提供反馈,保证流媒体发送实时同步。
RTP的同步控制是通过RTCP协议的Sender Report和Receiver Report实现。
- Sender Report:发送端报告,所谓发送端是指发出RTP数据报的应用程序或者终端,发送端同时也可以是接收端。
- Receiver Report:接收端报告,所谓接收端是指仅接收但不发送RTP数据报的应用程序或者终端。
##2. 基于sipdroid完成RTP包解析播放 sipdroid默认建立链接之后会判断当服务器是否是sipdroid官方提供的服务器,如果不是,则只会建立音频通话,不建立视频通话。注释掉该判断语句,发现它实现了发送RTP流媒体功能,通过与linphone的通话,能够在linphone端看到视频信息。然而,无法播放linphone发送的视频信息。又通过查看代码,发现播放视频的方法是通过RTSP接收。
if (Receiver.call_state == UserAgent.UA_STATE_INCALL && socket == null
&& Receiver.engine(mContext).getLocalVideo() != 0
&& Receiver.engine(mContext).getRemoteVideo() != 0 && PreferenceManager.getDefaultSharedPreferences(this).getString(org.sipdroid.sipua.ui.Settings.PREF_SERVER, org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER).equals(org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER)))
mVideoFrame
.setVideoURI(Uri.parse("rtsp://"
+ Receiver.engine(mContext).getRemoteAddr() + "/"
+ Receiver.engine(mContext).getRemoteVideo()
+ "/sipdroid"));
sipdroid播放视频流还需要建立RTSP会话。而linphone直接发送的是RTP包,所以sipdroid无法播放linphone的视频。在此之前一直使用WireShark分析RTSP建立,结果就是没找到建立过程。后来看源码才发现。。。上面提到的代码分别是在包org.sipdroid.sipua.ui
内的类CallScreen
的函数onResume
中和类VideoCamera
的函数onResume
中。
但是现在依然有疑问,上面的代码是通过直接请求远程客户端建立RTSP请求,通过MediaPlayer播放。并没有关系到服务器,那么RTSP是怎么建立的?而且,它专门指定要自己的服务器才能去建立视频连接,既然RTSP建立又没有关系到服务器,这个逻辑又是为什么?而且这两个疑问又互相矛盾,如果大家有想法,不妨屈尊指导一下学渣
抛开上面的问题不管,只要自行进行视频解码,还是可以播放的。通过从rtp_socket读取包,发现每次都能读取到1414字节的数据。应该可以证实是从linphone传来的数据,包大小也正常。下面开始着手解包和播放。
整个RTP包的实现就是基于UDP进行传输的。也可以从代码的实现验证这一点,RtpSocket类内部拥有UDPDatagramPacket
数据报。RTP可以认为是为UDP加了12字节协议包。类RtpPacket提供自动解析的能力。下面是解析后的结果和协议头的对比。
# | RTP协议 | 抓取到的内容 | 描述 | |||||
1 | Version | 2 | 版本号,为2 | |||||
2 | hasPadding | false | 没有加塞空间 | |||||
3 | hasExtension | false | 没有额外空间 | |||||
4 | CscrCount | 0 | Cscr数目 | |||||
5 | hasMarker | false | 没有特别程序解释 | |||||
6 | PayloadType | 103 | Payload的格式 h263-1988 | |||||
7 | SequenceNumber | 325 | ||||||
8 | Timestamp | 468900 | ||||||
9 | Sscr | 488792334 | ||||||
10 | CscrList | |||||||
11 | Payload | |||||||
12 | ||||||||
13 |
##3. RTP解包
由于前期调研的疏忽,给自己挖了一个坑。绕了好多弯路。前期通过查看论文,了解到目前流行的方法都是通过RFC 3984进行开发。通过RTP发送H264格式的视频。H264格式的视频数据由分隔符00000001
、NAL和VCL组成。进行RTP封包就是去掉分隔符进行发送,解包的时候再加上分隔符即可。还看了C++和Java实现的RTP解包方法,愣是和sipdroid的封包方法对不上。后来才意识到,我看的是H264的,而sipdroid用的是H263。
经过调研,决定使用libsteaming基于RTSP开发。Android支持H264的编码和解码,但是没有直接提供编码和解码接口。编码只能使用MediaRecorder编码到文件当中,而解码更是只能依靠MediaPlayer播放文件时才能被调用解码器。所以使用libstreaming编码,直接使用MediaPlayer进行RTSP播放即可。如果要自行封装RTP包的话,还需要跨平台编译H264的解码器。
##乱续、同步
参考文献
- 【1】RTMP/RTP/RTSP/RTCP的区别 - FrankieWang008的专栏
- 【2】RFC3984 RTP Payload Format for H.264 Video
- 【3】鲍轩. 基于Android手机音视频监控的软件研发与同步实现[D]. 杭州电子科技大学, 2013年10月.
- 【4】fly2700. FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法. http://www.cnweblog.com/fly2700/archive/2012/02/23/319718.html
- 【5】jasonhwang. Wireshark Lua: 一个从RTP抓包里导出H.264 Payload,变成264裸码流文件(xxx.264)的Wireshark插件. http://blog.csdn.net/jasonhwang/article/details/7359095
- 【6】mcodec, H264解码器源码(Android 1.6 版)[EB/OL], http://www.cnblogs.com/mcodec/articles/1780598.html.
- 【7】熊荣海,刘立柱. H263码流的RTP封装的研究与实现.
原文链接:实时传输协议RTP,转载请注明来源!
–EOF–