背景:

我在最近的项目中遇到了使用Android的MediaPlayer来进行RTSP播放的场景。但对于RTSP这种流媒体协议,其实Android原生的播放器支持得不是很好,所以有许多需要修改的地方。

本文主要简单介绍RTSP协议及其在MediaPlayer中的层级,后续会记录下在项目中遇到的具体情况及对应的修改。

RTSP播放器架构

播放器的架构很清晰,
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 。
在下面的交互流程中的例子中,会简单介绍这些方法的使用。

3 SDP各个参数简单介绍

注: "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.0Accept: application/sdpCSeq: 2RTSP/1.0 200 OKServer: ZXUSS100 1.0Cache-Control: no-cacheContent-Base: rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/Content-Length: 267Content-Type: application/sdpCSeq: 2Date: Thu, 10 May 2018 06:48:32 GMTExpires: Thu, 10 May 2018 06:48:32 GMTv=0o=- 0 0 IN IP4 61.149.64.212s=ZMSS RTSP Serverc=IN IP4 239.2.1.232/16 b=AS:2500 t=0 0a=control:*a=range:clock=20180503T064832.00Z-20180510T064832.00Zm=video 8000 RTP/AVP 33a=rtpmap:33 MP2T/90000a=control:trackID=2a=3GPP-Adaptation-Support:5

###4、RTSP交互流程###
一次基本的RTSP操作过程如下:

  1. 客户端连接到流服务器并发送一个RTSP描述命令(DESCRIBE)
  2. 流服务器通过一个SDP描述来进行反馈,反馈信息包括流数量、媒体类型等信息
  3. 客户端再分析该SDP描述,并为会话中的每一个流发送一个RTSP建立命令(SETUP),RTSP建立命令告诉服务器客户端用于接收媒体数据的端口
  4. 客户端发送一个播放命令(PLAY),服务器就开始在TCP/UDP上传送媒体流(RTP包)到客户端
  5. 在播放过程中客户端还可以向服务器发送命令来控制快进、快退和暂停等
  6. 最后,客户端可发送一个终止命令(TERADOWN)来结束流媒体会话

例子如下:

/*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.0CSeq: 1RTSP/1.0 200 OKPublic: DESCRIBE,OPTIONS,SETUP,TEARDOWN,PLAY,PAUSE,GET_PARAMETER,SET_PARAMETERServer: ZXUSS100 1.0 CSeq: 1/*DESCRIBE 主要功能:从服务器获取流媒体文件格式信息,从服务器获取流媒体文件传输信息关键字段:Content-Type:一般是SDPContent-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.0Accept: application/sdpCSeq: 2RTSP/1.0 200 OKServer: ZXUSS100 1.0Cache-Control: no-cacheContent-Base: rtsp://61.149.64.212:554/live/ch11091521323921117877.sdp/Content-Length: 267Content-Type: application/sdpCSeq: 2Date: Thu, 10 May 2018 06:48:32 GMTExpires: Thu, 10 May 2018 06:48:32 GMTv=0o=- 0 0 IN IP4 61.149.64.212s=ZMSS RTSP Serverc=IN IP4 239.2.1.232/16 b=AS:2500 t=0 0a=control:*a=range:clock=20180503T064832.00Z-20180510T064832.00Zm=video 8000 RTP/AVP 33a=rtpmap:33 MP2T/90000a=control:trackID=2a=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.0Transport: MP2T/RTP/UDP;unicast;client_port=5000-5001CSeq: 3RTSP/1.0 200 OKServer: ZXUSS100 1.0CSeq: 3Date: Thu, 10 May 2018 06:48:32 GMTExpires: Thu, 10 May 2018 06:48:32 GMTSession: 65536595Transport: 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.0Range: clock=20180510T060000.00Z-Scale: 1.0CSeq: 4Session: 65536595RTSP/1.0 200 OKServer: ZXUSS100 1.0CSeq: 4Range: clock=20180510T060000.00Z-20180510T064832.93ZScale: 1.0Session: 65536595RTP-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.0CSeq: 5Session: 65536595RTSP/1.0 200 OKServer: ZXUSS100 1.0CSeq: 5Session: 65536595

总结

本文介绍了一个相对简单清晰的RTSP交互实例,但是在实际情况中,因为项目使用的是FFmpeg的RTSP来进行拉流的,特别是整合到MediaPlayer中使用,还是有许多情况需要考虑并完善的:

1、SETUP阶段的协议选择:载流协议是UDP还是TCP,是否使用RTP承载,尝试一种载流协议不支持后的切换流程
2、重定向的完善:正常来说,一般在SETUP阶段后,就不会再有重定向了,因为这样需要重新断开再连接,但是实际使用中有些服务器是会在PLAY阶段去重定向的
3、NAT穿透: 使用UDP载流时,处于内网的客户端该如何进行NAT的打洞,以让服务器能将数据传送给客户端
4、与MediaPlayer的整合:PLAY中使用绝对时间描述时,该如何与MediaPlayer整合
5、媒体服务器的选择:即上面例子中提到的情况

这些是之前遇到的部分问题,我将在后续文章中说明这些问题。

更多相关文章

  1. Android平台上优秀的开源项目
  2. GitHub 优秀的 Android(安卓)开源项目
  3. haproxy根据客户端浏览器进行跳转
  4. 【Android】文件读写操作(含SDCard的读写)
  5. 【安卓笔记】android客户端与服务端交互的三种方式
  6. 【Android(安卓)应用开发】GitHub 优秀的 Android(安卓)开源项目
  7. android手机客户端上传文件,java servlet服务器端接收并保存到服
  8. 【Android(安卓)volley】Android库Volley的使用介绍
  9. android获取经纬度和地方名称

随机推荐

  1. 微服务初级设计指南
  2. 基于角色的访问控制
  3. java并发编程CAS机制原理分析
  4. HTTPS 项目实战指南
  5. 基于数据的访问控制
  6. android通过Location API显示地址信息的
  7. Web API 文档生成工具 apidoc
  8. 你应该要理解的java并发关键字volatile详
  9. 状态机在移动端项目中的使用
  10. 「一周答疑」2018年的第12周