原文来自百度文库
androidrtsp流媒体播放介绍
rtsp协议介绍:
该协议用于C/S模型,是一个基于文本的协议,用于在客户端和服务器端建立和协商实时流会话。
实时流协议(RTSP)是应用级协议,控制实时数据的发送。RTSP提供了一个可扩展框架,使实时数据,如音频与视频,的受控、点播成为可能。数据源包括现场数据与存储在剪辑中数据。该协议目的在于控制多个数据发送连接,为选择发送通道,如UDP、组播UDP与TCP,提供途径,并为选择基于RTP上发送机制提供方法。
实时流协议(RTSP)建立并控制一个或几个时间同步的连续流媒体。尽管连续媒体流与控制流交换是可能的,通常它本身并不发送连续流。换言之,RTSP充当多媒体服务器的网络远程控制。RTSP连接没有绑定到传输层连接,如TCP。在RTSP连接期间,RTSP用户可打开或关闭多个对服务器的可传输连接以发出RTSP请求。此外,可使用无连接传输协议,如UDP。RTSP流控制的流可能用到RTP,但RTSP操作并不依赖用于携带连续媒体的传输机制。
协议支持的操作如下:
(1)从媒体服务器上检索媒体:用户可通过HTTP或其它方法提交一个演示描述。如演示是组播,演示式就包含用于连续媒体的的组播地址和端口。如演示仅通过单播发送给用户,用户为了安全应提供目的地址。
(2)媒体服务器邀请进入会议:媒体服务器可被邀请参加正进行的会议,或回放媒体,或记录其中一部分,或全部。这种模式在分布式教育应用上很有用,会议中几方可轮流按远程控制按钮。
(3)将媒体加到现成讲座中:如服务器告诉用户可获得附加媒体内容,对现场讲座显得尤其有用。如HTTP/1.1中类似,RTSP请求可由代理、通道与缓存处理。
RFC2326 Real Time Streaming Protocol April 1998
method direction object requirement
DESCRIBE C->S P,S recommended
ANNOUNCE C->S, S->C P,S optional
GET_PARAMETER C->S, S->C P,S optional
OPTIONS C->S, S->C P,S required
(S->C: optional)
PAUSE C->S P,S recommended
PLAY C->S P,S required
RECORD C->S P,S optional
REDIRECT S->C P,S optional
SETUP C->S S required
SET_PARAMETER C->S, S->C P,S optional
TEARDOWN C->S P,S required
RTSP命令的状态转换表
Example:
C->S: OPTIONS * RTSP/1.0
CSeq: 1
S->C: RTSP/1.0 200 OK
CSeq: 1
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE
Example:
C->S:DESCRIBE rtsp://server.example.com/fizzle/foo RTSP/1.0
CSeq:312
Accept: application/sdp,application/rtsl, pplication/mheg
S->C:RTSP/1.0 200 OK
CSeq: 312
Date: 23 Jan 1997 15:35:06 GMT
Content-Type: application/sdp
Content-Length: 376
v=0
o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
[email protected] (MarkHandley)
c=IN IP4 224.2.17.12/127
t=2873397496 2873404696
a=recvonly
m=audio 3456 RTP/AVP 0
m=video 2232 RTP/AVP 31
m=whiteboard 32416 UDP WB
a=orient:portrait
C->S:SETUP rtsp://example.com/foo/bar/baz.rm RTSP/1.0
CSeq: 302
Transport: RTP/AVP;unicast;client_port=4588-4589
S->C:RTSP/1.0 200 OK
CSeq: 302
Date: 23 Jan 1997 15:35:06 GMT
Session: 47112344
Transport: RTP/AVP;unicast;
client_port=4588-4589;server_port=6256-6257
C->S:PLAY rtsp://audio.example.com/audio RTSP/1.0
CSeq: 835
Session: 12345678
Range: npt=10-15
C->S:PLAY rtsp://audio.example.com/audio RTSP/1.0
CSeq: 836
Session: 12345678
Range: npt=20-25
C->S:PLAY rtsp://audio.example.com/audio RTSP/1.0
CSeq: 837
Session: 12345678
Range: npt=30-
S->C:RTSP/1.0 200 OK
CSeq: 835
Date: 23 Jan 1997 15:35:06 GMT
C->S:TEARDOWN rtsp://example.com/fizzle/foo RTSP/1.0
CSeq: 892
Session: 12345678
S->C: RTSP/1.0 200 OK
CSeq: 892
二、RTP协议介绍:
RTP报文格式
RTP报文由两部分组成:报头和有效载荷。RTP报头格式如图6.7所示,其中:
V:RTP协议的版本号,占2位,当前协议版本号为2。
P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头。
CC:CSRC计数器,占4位,指示CSRC 标识符的个数。
M: 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
同步信源(SSRC)标识符:占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
特约信源(CSRC)标识符:每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。
PT: 有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等。
序列号:占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。接收者通过序列号来检测报文丢失情况,重新排序报文,恢复数据。
时戳(Timestamp):占32位,时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
V |
P |
X |
CC |
M |
PT |
序列号 |
时戳 |
||||||
同步信源(SSRC)标识符 |
||||||
特约信源(CSRC)标识符 |
||||||
··· |
这里的同步信源是指产生媒体流的信源,它通过RTP报头中的一个32位数字SSRC标识符来标识,而不依赖于网络地址,接收者将根据SSRC标识符来区分不同的信源,进行RTP报文的分组。特约信源是指当混合器接收到一个或多个同步信源的RTP报文后,经过混合处理产生一个新的组合RTP报文,并把混合器作为组合RTP报文的SSRC,而将原来所有的SSRC都作为CSRC传送给接收者,使接收者知道组成组合报文的各个SSRC。
在发送端,上层应用程序以分组形式将编码后的媒体数据传给RTP通信模块,作为RTP报文的有效载荷,RTP通信模块将根据上层应用提供的参数在有效载荷前添加RTP报头,形成RTP报文,通过Socket接口选择UDP协议发送出去。
在接收端,RTP通信模块通过Socket接口接收到RTP报文后,将RTP报头分离出来作相应处理,再将RTP报文的有效载荷作为数据分组传递给上层应用。
三、Androidrtsp播放的设计:
Rtsp播放涉及到rtsp与rtp/rtcp两个协议,rtsp用于控制流的交互,rtp用于数据流的包的封装;在android里MyHandle做为一个引擎控制这两路流,ARTSPConnection用于rtsp的处理,ARTPConnection用于接收rtp数据,ARTPAssembler的子类负责对rtp数据解析。
解析完的数据上传到MyHandle的缓冲中,RTSPSource从缓冲列表里读取数据给解码器使用。
数据流图
NuPlayer::RTSPSource
MyHandler
注:黑色的是tcp方式,蓝色的udp方式来接收。
下面以h264的流的rtp封装的方式来说明rtp协议对音视频数据的封装,rfc3984定义对h264的打包方式,对h264的流的解析与打包都必须遵守这个协议,h264的数据以nal单元为单位打包, H.264Payload 格式定义了三种不同的基本的负载(Payload)结构.接收端可能通过 RTPPayload 的第一个字节来识别它们.这一个字节类似 NALU头的格式,而这个头结构的 NAL单元类型字段则指出了代表的是哪一种结构,
这个字节的结构如下,可以看出它和 H.264的 NALU头结构是一样的.
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
1. 单一NAL单元模式
即一个 RTP包仅由一个完整的 NALU组成.这种情况下 RTPNAL 头类型字段和原始的H.264的
NALU 头类型字段是一样的.
2.组合封包模式
即可能是由多个 NAL单元组成一个 RTP包.分别有4种组合方式:STAP-A, STAP-B, MTAP16, MTAP24.
那么这里的类型值分别是24,25, 26 以及 27.
3.分片封包模式
用于把一个 NALU单元封装成多个 RTP包.存在两种类型 FU-A和 FU-B.类型值分别是 28和 29.
在android的代码处理了在AAVCAssembler::addNALUnit里面处理了单一NAL单元,STAP-A和FU-A三种包。这个类将会把接收的数据包处理成上层解码器能解码帧的数据上传给上层。
四、时间戳同步:
对于rtsp的流媒体播放,时间多戳同步是十分重要的,根据RTP规范,不同的RTP媒体流是分开传输的,且使用各自独立的时间戳进行同步。假设在一次视频点播中,传输两路RTP媒体流,一路视频,一路音频。根据视频帧时间戳,可以实现视频流内同步,这很好理解,通过视频帧时间戳可以计算出相邻视频帧的时间间隔,也就是视频帧之间的相对时间关系很容易通过时间戳来确定,按照这个间隔去呈现视频,就可以获得较好的效果。同理,音频流也可以实现自身的同步。那么音频和视频这两路媒体间如何实现同步呢?我们只使用音视频的RTP时间戳,看能否实现媒体间的同步。音视频的RTP时间戳的增长速率一般是不同的,但没关系,知道了具体的单位后,两者是可以通过单位换算联系起来的。
把音视频被映射到同一个时间轴上,音频和视频帧间的相对关系很清楚。慢着,RTP规范要求时间戳的初始值应该是一个随机值,那么假设音频帧时间戳的初始值是随机值1234,视频帧时间戳的初始值是随机值5678,看起来应该是下面这样:
这么做合适吗?我们把音频帧时间戳1234和视频帧时间戳5678对应到绝对时间轴的0上,我们这么做的理由是什么?你可能会说,因为那是第一个音频帧和第一个视频帧,所以可以对应到同一个点上,在第一幅图中我们就是这么做的,把音频帧时间戳0和视频帧时间戳0对应到绝对时间轴的0上。但是RTP规范并没有规定第一个视频帧的时间戳和第一个音频帧的时间戳必须或者应该对应到绝对时间轴的同一个点上,从整个RTP规范中不能直接得出这样的结论,也推导不出这样的结论。上图所做的转换是不正确的,为什么呢?因为在做转换时,隐含了一个假设,我们想当然地认为这个假设是成立的。这个假设就是第一个视频帧和第一个音频帧的时间戳应该对应到同一个点上,即无论它们时间戳是多少,都应该在同一时间播放。仅仅使用RTP时间戳是无法实现媒体间同步的,根本的原因是音频时间轴和视频时间轴是完全独立的,通过音频帧和视频帧的时间戳,无法确定一个视频帧和一个音频帧的相对时间关系,也就是无法把它们都准确定位在绝对时间轴上,只能准确定位一个。
在rtp的规范里,要实现RTP媒体间同步,需要借助于RTCP,在RTCP的SR包中,包含有
在androidrtsp的原版的代码里所以也是通过rtcp来的ntp-rtp时间对来同步的,由于ntp是服务器的时间跟本机时间是一定的误差,所以这个时间值本身就没有了价值,它需要两个时间对的差值才能来计算同步的时间戳,android的计算方式如下:
uint64_tARTPSource::RTP2NTP(uint32_t rtpTime) const {
CHECK_EQ(mNumTimes,2u);
returnmNTPTime[0] + (double)(mNTPTime[1] - mNTPTime[0])
*((double)rtpTime - (double)mRTPTime[0])
/(double)(mRTPTime[1] - mRTPTime[0]);
}
现在我们来分析这样的一种情况,当我们接收的两个rtcp的ntp-rtp时间对相差的时间很大或者没有收到两个以上的ntp-rtp时间对,那么我们就无法计算出同步的时间戳,音视频也就无法定位到ntp上无法同步,很不幸我们调试过程中遇到了这种情况,在我面前的如何解决这个问题?于是我们回到前面的同步的方法,鉴于目前所有的流媒体服务端都是同时发送音视频数据的,所以我们可以不依赖于rtcp直接来同步。目前我们rtsp使用了这种方式来同步。从效果来看这种同步机制是简单可靠的。
参考文档:
http://www.faqs.org/rfcs/rfc2326.html
http://linfengdu.blog.163.com/blog/static/11771073201091461511795/
http://www.cppblog.com/czanyou/archive/2009/12/25/67940.html