我在最近的项目中遇到了使用Android的MediaPlayer来进行RTSP播放的场景。但对于RTSP这种流媒体协议,其实Android原生的播放器支持得不是很好,所以有许多需要修改的地方。
本文主要简单介绍RTSP协议及其在MediaPlayer中的层级,后续会记录下在项目中遇到的具体情况及对应的修改。
播放器的架构很清晰,
apk–>MediaPlayer->media_server–>厂商自己的Player(和NuPalyer/StagefrightPlayer一个层级)–>FFmpeg
如下图。
因为走Android MediaPlayer的流程,拉流的部分是使用FFmpeg实现的,所以FFmpeg是最核心的部分,主要的修改,也即是针对FFmpeg里RTSP部分的修改,以适配项目的特殊性。
##RTSP协议简介##
###1、简介###
RTSP属于应用层协议,被用于控制媒体流的传输,它为多媒体服务扮演“网络远程控制”的角色,对流媒体提供了诸如暂停,快进等控制,而它本身并不传输数据。
因为RTSP的作用相当于流媒体服务器的远程控制,所以客户端需要和服务器进行命令交互,以到达建立/释放连接及远程控制的目的。命令连接基于TCP,一般使用554端口。而数据传输可以选择TCP或UDP来传送,这个需要看服务端的支持情况及客户端的选择。
RTSP负责建立和控制会话,RTP/TCP负责多媒体的传输,RTCP配合RTP做控制和流量统计,他们是合作的关系。
###2、RTSP的消息###
RTSP的消息有两大类,一是请求消息(request),一是回应消息(response),两种消息的格式不同。
请求消息格式:
方法 URI RTSP版本 CR LF
消息头 CR LF CR LF
消息体 CR LF
其中方法包括OPTIONS、SETUP、PLAY、TEARDOWN等待,URI是接收方(服务端)的地址,例如:rtsp://192.168.22.136:5000/v0,每行后面的CR LF表示回车换行,需要接收端有相应的解析,最后一个消息头需要有两个CR LF。
回应消息格式:
RTSP版本 状态码 解释 CR LF
消息头 CR LF CR LF
消息体 CR LF
其中RTSP版本一般都是RTSP/1.0,状态码是一个数值,200表示成功,解释是与状态码对应的文本解释。
状态码和HTTP差不多,由三位数组成,表示方法执行的结果,定义如下:
1XX:保留,将来使用;
2XX:成功,操作被接收、理解、接受(received,understand,accepted);
3XX:重定向,要完成操作必须进行进一步操作;
4XX:客户端出错,请求有语法错误或无法实现;
5XX:服务器出错,服务器无法实现合法的请求。
RTSP中定义的方法有:OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, SCALE, GET_PARAMETER ,SET_PARAMETER 。
在下面的交互流程中的例子中,会简单介绍这些方法的使用。
注: "a="字段描述节目时长。直播用 clock=表示,描述直播时移时长;点播用 ntp=表示 ,描述点播节目时长。具体参见 rfc 2326。
举例如下:
直播:直播时移时长为 1 小时
a=range:clock=20100817T000742.55Z-20100818T000742.55Z
点播: a=range:npt=0-246.655
注: "c="字段表示连接描述,可使用组播地址;如果是组播地址,字段包含了媒体流的目的地址。也就是说,通过单播 rtsp 获取当前直播频道的组播地址,然后加入这个组播播放。具体参见 rfc 2326。例如: c=IN IP4 225.61.100.28/16
注: "m"字段表示负载类型(PT): RTP 信息包中的有效载荷域(Payload Type Field)的长度为 7位,因此 RTP 可支持 128 种不同的有效载荷类型。具体参见 rfc 3551。
SDP例子
DESCRIBE rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp?playtype=1&boid=001&backupagent=61.149.64.212:554&clienttype=1&time=20180510144608+08&life=172800&ifpricereqsnd=1&vcdnid=001&userid=09-20170912003-01&mediaid=ch11091521323921117877&ctype=5&TSTVTimeLife=604800&contname=&authid=0&UserLiveType=1&stbid=801100c8bb1d8e97&nodelevel=3&terminalflag=1&bitrate=3 RTSP/1.0
Accept: application/sdp
CSeq: 2
RTSP/1.0 200 OK
Server: ZXUSS100 1.0
Cache-Control: no-cache
Content-Base: rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/
Content-Length: 267
Content-Type: application/sdp
CSeq: 2
Date: Thu, 10 May 2018 06:48:32 GMT
Expires: Thu, 10 May 2018 06:48:32 GMT
v=0
o=- 0 0 IN IP4 61.149.64.212
s=ZMSS RTSP Server
c=IN IP4 239.2.1.232/16
b=AS:2500
t=0 0
a=control:*
a=range:clock=20180503T064832.00Z-20180510T064832.00Z
m=video 8000 RTP/AVP 33
a=rtpmap:33 MP2T/90000
a=control:trackID=2
a=3GPP-Adaptation-Support:5
###4、RTSP交互流程###
一次基本的RTSP操作过程如下:
例子如下:
/*OPTIONS 主要功能:获取服务器/客户端支持的能力集,
关键字段:
Public:服务器支持的命令,在此例子中是用于获取服务器支持的命令,可以看到服务器回复我们支持DESCRIBE,OPTIONS,SETUP,TEARDOWN,PLAY,PAUSE,GET_PARAMETER,SET_PARAMETER
特殊说明:在一个RTSP交互流程中OPTIONS方法并不是必须的,前提是你知道服务器支持哪些命令
*/
OPTIONS rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp?playtype=1&boid=001&backupagent=61.149.64.212:554&clienttype=1&time=20180510144608+08&life=172800&ifpricereqsnd=1&vcdnid=001&userid=09-20170912003-01&mediaid=ch11091521323921117877&ctype=5&TSTVTimeLife=604800&contname=&authid=0&UserLiveType=1&stbid=801100c8bb1d8e97&nodelevel=3&terminalflag=1&bitrate=3 RTSP/1.0
CSeq: 1
RTSP/1.0 200 OK
Public: DESCRIBE,OPTIONS,SETUP,TEARDOWN,PLAY,PAUSE,GET_PARAMETER,SET_PARAMETER
Server: ZXUSS100 1.0 CSeq: 1
/*
DESCRIBE 主要功能:从服务器获取流媒体文件格式信息,从服务器获取流媒体文件传输信息
关键字段:
Content-Type:一般是SDP
Content-length:一般是SDP的长度
特殊说明:媒体信息通过SDP协议给出,例如这个例子中服务器回复range:clock=20180503T064832.00Z-20180510T064832.00Z,告诉客户端服务器的可时移range,并且是clock(绝对时间描述),也就是PLAY阶段请求的时间是ISO 8601时间戳标准
*/
DESCRIBE rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp?playtype=1&boid=001&backupagent=61.149.64.212:554&clienttype=1&time=20180510144608+08&life=172800&ifpricereqsnd=1&vcdnid=001&userid=09-20170912003-01&mediaid=ch11091521323921117877&ctype=5&TSTVTimeLife=604800&contname=&authid=0&UserLiveType=1&stbid=801100c8bb1d8e97&nodelevel=3&terminalflag=1&bitrate=3 RTSP/1.0
Accept: application/sdp
CSeq: 2
RTSP/1.0 200 OK
Server: ZXUSS100 1.0
Cache-Control: no-cache
Content-Base: rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/
Content-Length: 267
Content-Type: application/sdp
CSeq: 2
Date: Thu, 10 May 2018 06:48:32 GMT
Expires: Thu, 10 May 2018 06:48:32 GMT
v=0
o=- 0 0 IN IP4 61.149.64.212
s=ZMSS RTSP Server
c=IN IP4 239.2.1.232/16
b=AS:2500
t=0 0
a=control:*
a=range:clock=20180503T064832.00Z-20180510T064832.00Z
m=video 8000 RTP/AVP 33
a=rtpmap:33 MP2T/90000
a=control:trackID=2
a=3GPP-Adaptation-Support:5
/*
SETUP 主要功能:与服务器协商流媒体传输方式,此过程中,建立RTP通道
关键字段:
Transport:指明服务器支持的传输方式及传输端口,地址等等信息。
在例子中参数MP2T/RTP/UDP指明服务器传输媒体数据将使用UDP传输;参数server_port=8000-8001;source=239.2.1.232指明了服务器的地址和端口号;destination=192.168.1.4指明客户端地址,这个例子中为内网地址,对于这种情况需要做NAT穿透。
特殊说明:需要注意的是,媒体服务器的地址source=239.2.1.232,为组播地址,需要加入RTP组播来拉流。(但在此例子中,我试过使用该地址拉流也没数据)
*/
SETUP rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/trackID=2 RTSP/1.0
Transport: MP2T/RTP/UDP;unicast;client_port=5000-5001
CSeq: 3
RTSP/1.0 200 OK
Server: ZXUSS100 1.0
CSeq: 3
Date: Thu, 10 May 2018 06:48:32 GMT
Expires: Thu, 10 May 2018 06:48:32 GMT
Session: 65536595
Transport: MP2T/RTP/UDP;destination=192.168.1.4;client_port=5000-5001;server_port=8000-8001;source=239.2.1.232
/*
PLAY 主要功能:与服务器协商流媒体播放
关键字段:
Range:播放时间支持两种格式,Range: npt=0.0-end或者Range:clock=20100318T021919.35Z-20100318T031919.80Z
方法1 位置描述,相对时间描述——npt(normalplay time)
•beginning 节目起始点
•now 当前播放点
•end 节目结束点
•相对时间 媒体的相对时间
方法2 时间描述,绝对时间描述——clock,ISO 8601时间戳标准
•直接用数字形式表示与起始点的时间
Scale:播放速度 例如1倍速,Scale: 1.0
特殊说明:RTP-Info是前段回复我们的媒体服务器信息,可以看到这里和之前setup阶段前端回复的source地址并不一样。
(这才是媒体服务器的地址,这地方的冲突,可能是服务器写的不标准)
*/
PLAY rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/ RTSP/1.0
Range: clock=20180510T060000.00Z-
Scale: 1.0
CSeq: 4
Session: 65536595
RTSP/1.0 200 OK
Server: ZXUSS100 1.0
CSeq: 4
Range: clock=20180510T060000.00Z-20180510T064832.93Z
Scale: 1.0
Session: 65536595
RTP-Info: url=rtsp://61.149.64.132:12370/live/ch11091521323921117877.sdp/trackID=2;seq=0;rtptime=841899578
/*
GET_PARAMETER 主要功能:1、从服务器获取参数,目前主要获取时间范围;2、保持RTSP连接(发送空的GET_PARAMETER)
在此例子中,是第二种用途,keep-alive
*/
GET_PARAMETER rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/ RTSP/1.0
CSeq: 5
Session: 65536595
RTSP/1.0 200 OK
Server: ZXUSS100 1.0
CSeq: 5
Session: 65536595
本文介绍了一个相对简单清晰的RTSP交互实例,但是在实际情况中,因为项目使用的是FFmpeg的RTSP来进行拉流的,特别是整合到MediaPlayer中使用,还是有许多情况需要考虑并完善的:
1、SETUP阶段的协议选择:载流协议是UDP还是TCP,是否使用RTP承载,尝试一种载流协议不支持后的切换流程
2、重定向的完善:正常来说,一般在SETUP阶段后,就不会再有重定向了,因为这样需要重新断开再连接,但是实际使用中有些服务器是会在PLAY阶段去重定向的
3、NAT穿透: 使用UDP载流时,处于内网的客户端该如何进行NAT的打洞,以让服务器能将数据传送给客户端
4、与MediaPlayer的整合:PLAY中使用绝对时间描述时,该如何与MediaPlayer整合
5、媒体服务器的选择:即上面例子中提到的情况
这些是之前遇到的部分问题,我将在后续文章中说明这些问题。