RTSP协议详解

时间:2023-03-09 17:59:06
RTSP协议详解
    RTSP(Real Time Streaming Protocol)是由Real Network和Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议。RTSP对流媒体提供了诸如暂停,快进等控制,而它本身并不传输数据,RTSP的作用相当于流媒体服务器的远程控制。服 务器端可以自行选择使用TCP或UDP来传送串流内容,它的语法和运作跟HTTP 1.1类似,但并不特别强调时间同步,所以比较能容忍网络延迟。而且允许同时多个串流需求控制(Multicast),除了可以降低服务器端的网络用量, 还可以支持多方视频会议(Video  onference)。 因为与HTTP1.1的运作方式相似,所以代理服务器《Proxy》的快取功能《Cache》也同样适用于RTSP,并因RTSP具有重新导向功能,可视 实际负载情况来转换提供服务的服务器,以避免过大的负载集中于同一服务器而造成延迟。

rtsp和http的区别和联系

    (1)联系:两者都用纯文本来发送消息,且rtsp协议的语法也和HTTP类似。Rtsp一开始这样设计,也是为了能够兼容使用以前写的HTTP协议分析代码
    (2)区别:rtsp
是有状态的,不同的是RTSP的命令需要知道现在正处于一个什么状态,也就是说rtsp的命令总是按照顺序来发送,某个命令总在另外一个命令之前要发送。
Rtsp不管处于什么状态都不会去断掉连接。,而http则不保存状态,协议在发送一个命令以后,连接就会断开,且命令之间是没有依赖性的。rtsp协议使用554端口,http使用80端口。
rtsp和sip的区别和联系
SIP(Session
Initiation
Protocol),是基于IP的一个应用层控制协议。由于SIP是基于纯文本的信令协议,可以管理不同接入网络上的会话等。会话可以是终端设备之间任何
类型的通信,如视频会话、既时信息处理或协作会话。该协议不会定义或限制可使用的业务,传输、服务质量、计费、安全性等问题都由基本核心网络和其它协议处
理。

(1)联系:sip和rtsp都是应用层的控制协议,负责一次通信过程的建立和控制和结束,不负责中间的传输部分。他们都是基于纯文本的信令协议,穿墙性
能良好。支持tcp、udp,支持多方通信。他们都需要服务器支持,都支持会话中重定向。sip和rtsp
都使用sdp协议来传送媒体参数,使用rtp(rtcp)协议来传输媒体流。

(2)区别:rtsp是专门为流媒*定的协议,在多个媒体流的时间同步方面比sip强大。rtsp还提供网络负载均衡的功能,减轻服务器压力和网络带宽
要求。sip一般用来创建一次音频、视频通话(双向),而rtsp一般用来做视频点播、视频监控等(单向)。当然,从原理上讲,rtsp也可以做双向的视
频通话。

RTSP和RTP(rtcp)的关系
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表示成功,解释是与状态码对应的文本解释。

状态码由三位数组成,表示方法执行的结果,定义如下:

1XX:保留,将来使用;

2XX:成功,操作被接收、理解、接受(received,understand,accepted);

3XX:重定向,要完成操作必须进行进一步操作;

4XX:客户端出错,请求有语法错误或无法实现;

5XX:服务器出错,服务器无法实现合法的请求。

RTSP的方法
rtsp中定义的方法有:OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, SCALE, GET_PARAMETER ,SET_PARAMETER 
1.OPTION
目的是得到服务器提供的可用方法:
OPTIONS rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 1         //每个消息都有序号来标记,第一个包通常是option请求消息
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
服务器的回应信息包括提供的一些方法,例如:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 1         //每个回应消息的cseq数值和请求消息的cseq相对应
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, SCALE, GET_PARAMETER //服务器提供的可用的方法
2.DESCRIBE
C向S发起DESCRIBE请求,为了得到会话描述信息(SDP):
DESCRIBE rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 2
token:
Accept: application/sdp
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
服务器回应一些对此会话的描述信息(sdp):
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 2
x-prev-url: rtsp://192.168.20.136:5000
x-next-url: rtsp://192.168.20.136:5000
x-Accept-Retransmit: our-retransmit
x-Accept-Dynamic-Rate: 1
Cache-Control: must-revalidate
Last-Modified: Fri, 10 Nov 2006 12:34:38 GMT
Date: Fri, 10 Nov 2006 12:34:38 GMT
Expires: Fri, 10 Nov 2006 12:34:38 GMT
Content-Base: rtsp://192.168.20.136:5000/xxx666/
Content-Length: 344
Content-Type: application/sdp
v=0        //以下都是sdp信息
o=OnewaveUServerNG 1451516402 1025358037 IN IP4 192.168.20.136
s=/xxx666
u=http:///
e=admin@
c=IN IP4 0.0.0.0
t=0 0
a=isma-compliance:1,1.0,1
a=range:npt=0-
m=video 0 RTP/AVP 96    //m表示媒体描述,下面是对会话中视频通道的媒体描述
a=rtpmap:96 MP4V-ES/90000
a=fmtp:96 profile-level-id=245;config=000001B0F5000001B509000001000000012000C888B0E0E0FA62D089028307
a=control:trackID=0//trackID=0表示视频流用的是通道0
3.SETUP
客户端提醒服务器建立会话,并确定传输模式:
SETUP rtsp://192.168.20.136:5000/xxx666/trackID=0 RTSP/1.0    
CSeq: 3
Transport: RTP/AVP/TCP;unicast;interleaved=0-1      
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
//uri
中带有trackID=0,表示对该通道进行设置。Transport参数设置了传输模式,包的结构。接下来的数据包头部第二个字节位置就是
interleaved,它的值是每个通道都不同的,trackID=0的interleaved值有两个0或1,0表示rtp包,1表示rtcp包,接
受端根据interleaved的值来区别是哪种数据包。
服务器回应信息:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 3
Session: 6310936469860791894     //服务器回应的会话标识符
Cache-Control: no-cache
Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=6B8B4567
4.PLAY
客户端发送播放请求:
PLAY rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 4
Session: 6310936469860791894
Range: npt=0.000-      //设置播放时间的范围
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
服务器回应信息:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 4
Session: 6310936469860791894
Range: npt=0.000000-
RTP-Info: url=trackID=0;seq=17040;rtptime=1467265309     
//seq和rtptime都是rtp包中的信息
5.TEARDOWN
客户端发起关闭请求:
TEARDOWN rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 5
Session: 6310936469860791894
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
服务器回应:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 5
Session: 6310936469860791894
Connection: Close
以上方法都是交互过程中最为常用的,其它还有一些重要的方法如get/set_parameter,pause,redirect等等
ps:
sdp的格式
v=<version>
o=<username> <session id> <version> <network type> <address type> <address>
s=<session name>
i=<session description>
u=<URI>
e=<email address>
p=<phone number>
c=<network type> <address type> <connection address>
b=<modifier>:<bandwidth-value>
t=<start time> <stop time>
r=<repeat interval> <active duration> <list of offsets from start-time>
z=<adjustment time> <offset> <adjustment time> <offset> ....
k=<method>
k=<method>:<encryption key>
a=<attribute>
a=<attribute>:<value>
m=<media> <port> <transport> <fmt list>
v = (协议版本)
o = (所有者/创建者和会话标识符)
s = (会话名称)
i = * (会话信息)
u = * (URI 描述)
e = * (Email 地址)
p = * (电话号码)
c = * (连接信息)
b = * (带宽信息)
z = * (时间区域调整)
k = * (加密密钥)
a = * (0 个或多个会话属性行)
时间描述:
t = (会话活动时间)
r = * (0或多次重复次数)
媒体描述:
m = (媒体名称和传输地址)
i = * (媒体标题)
c = * (连接信息 — 如果包含在会话层则该字段可选)
b = * (带宽信息)
k = * (加密密钥)
a = * (0 个或多个媒体属性行)
RTSP客户端的JAVA实现


3.1  接口IEvent.java 

接口IEvent.java的代码如下:

  1. package com.amigo.rtsp;
  2. import java.io.IOException;
  3. import java.nio.channels.SelectionKey;
  4. /** *//**
  5. * IEvent.java 网络事件处理器,当Selector可以进行操作时,调用这个接口中的方法.
  6. * 2007-3-22 下午03:35:51
  7. * @author sycheng
  8. * @version 1.0
  9. */
  10. public interface IEvent {
  11. /** *//**
  12. * 当channel得到connect事件时调用这个方法.
  13. * @param key
  14. * @throws IOException
  15. */
  16. void connect(SelectionKey key) throws IOException;
  17. /** *//**
  18. * 当channel可读时调用这个方法.
  19. * @param key
  20. * @throws IOException
  21. */
  22. void read(SelectionKey key) throws IOException;
  23. /** *//**
  24. * 当channel可写时调用这个方法.
  25. * @throws IOException
  26. */
  27. void write() throws IOException;
  28. /** *//**
  29. * 当channel发生错误时调用.
  30. * @param e
  31. */
  32. void error(Exception e);
  33. }

3.2  RTSP的测试类:RTSPClient.java
        RTSP的测试类RTSPClient.java类的代码如下所示:

  1. package com.amigo.rtsp;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.nio.ByteBuffer;
  5. import java.nio.channels.SelectionKey;
  6. import java.nio.channels.Selector;
  7. import java.nio.channels.SocketChannel;
  8. import java.util.Iterator;
  9. import java.util.concurrent.atomic.AtomicBoolean;
  10. public class RTSPClient extends Thread implements IEvent {
  11. private static final String VERSION = " RTSP/1.0/r/n";
  12. private static final String RTSP_OK = "RTSP/1.0 200 OK";
  13. /** *//** 远程地址 */
  14. private final InetSocketAddress remoteAddress;
  15. /** *//** * 本地地址 */
  16. private final InetSocketAddress localAddress;
  17. /** *//** * 连接通道 */
  18. private SocketChannel socketChannel;
  19. /** *//** 发送缓冲区 */
  20. private final ByteBuffer sendBuf;
  21. /** *//** 接收缓冲区 */
  22. private final ByteBuffer receiveBuf;
  23. private static final int BUFFER_SIZE = 8192;
  24. /** *//** 端口选择器 */
  25. private Selector selector;
  26. private String address;
  27. private Status sysStatus;
  28. private String sessionid;
  29. /** *//** 线程是否结束的标志 */
  30. private AtomicBoolean shutdown;
  31. private int seq=1;
  32. private boolean isSended;
  33. private String trackInfo;
  34. private enum Status {
  35. init, options, describe, setup, play, pause, teardown
  36. }
  37. public RTSPClient(InetSocketAddress remoteAddress,
  38. InetSocketAddress localAddress, String address) {
  39. this.remoteAddress = remoteAddress;
  40. this.localAddress = localAddress;
  41. this.address = address;
  42. // 初始化缓冲区
  43. sendBuf = ByteBuffer.allocateDirect(BUFFER_SIZE);
  44. receiveBuf = ByteBuffer.allocateDirect(BUFFER_SIZE);
  45. if (selector == null) {
  46. // 创建新的Selector
  47. try {
  48. selector = Selector.open();
  49. } catch (final IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. startup();
  54. sysStatus = Status.init;
  55. shutdown=new AtomicBoolean(false);
  56. isSended=false;
  57. }
  58. public void startup() {
  59. try {
  60. // 打开通道
  61. socketChannel = SocketChannel.open();
  62. // 绑定到本地端口
  63. socketChannel.socket().setSoTimeout(30000);
  64. socketChannel.configureBlocking(false);
  65. socketChannel.socket().bind(localAddress);
  66. if (socketChannel.connect(remoteAddress)) {
  67. System.out.println("开始建立连接:" + remoteAddress);
  68. }
  69. socketChannel.register(selector, SelectionKey.OP_CONNECT
  70. | SelectionKey.OP_READ | SelectionKey.OP_WRITE, this);
  71. System.out.println("端口打开成功");
  72. } catch (final IOException e1) {
  73. e1.printStackTrace();
  74. }
  75. }
  76. public void send(byte[] out) {
  77. if (out == null || out.length < 1) {
  78. return;
  79. }
  80. synchronized (sendBuf) {
  81. sendBuf.clear();
  82. sendBuf.put(out);
  83. sendBuf.flip();
  84. }
  85. // 发送出去
  86. try {
  87. write();
  88. isSended=true;
  89. } catch (final IOException e) {
  90. e.printStackTrace();
  91. }
  92. }
  93. public void write() throws IOException {
  94. if (isConnected()) {
  95. try {
  96. socketChannel.write(sendBuf);
  97. } catch (final IOException e) {
  98. }
  99. } else {
  100. System.out.println("通道为空或者没有连接上");
  101. }
  102. }
  103. public byte[] recieve() {
  104. if (isConnected()) {
  105. try {
  106. int len = 0;
  107. int readBytes = 0;
  108. synchronized (receiveBuf) {
  109. receiveBuf.clear();
  110. try {
  111. while ((len = socketChannel.read(receiveBuf)) > 0) {
  112. readBytes += len;
  113. }
  114. } finally {
  115. receiveBuf.flip();
  116. }
  117. if (readBytes > 0) {
  118. final byte[] tmp = new byte[readBytes];
  119. receiveBuf.get(tmp);
  120. return tmp;
  121. } else {
  122. System.out.println("接收到数据为空,重新启动连接");
  123. return null;
  124. }
  125. }
  126. } catch (final IOException e) {
  127. System.out.println("接收消息错误:");
  128. }
  129. } else {
  130. System.out.println("端口没有连接");
  131. }
  132. return null;
  133. }
  134. public boolean isConnected() {
  135. return socketChannel != null && socketChannel.isConnected();
  136. }
  137. private void select() {
  138. int n = 0;
  139. try {
  140. if (selector == null) {
  141. return;
  142. }
  143. n = selector.select(1000);
  144. } catch (final Exception e) {
  145. e.printStackTrace();
  146. }
  147. // 如果select返回大于0,处理事件
  148. if (n > 0) {
  149. for (final Iterator<SelectionKey> i = selector.selectedKeys()
  150. .iterator(); i.hasNext();) {
  151. // 得到下一个Key
  152. final SelectionKey sk = i.next();
  153. i.remove();
  154. // 检查其是否还有效
  155. if (!sk.isValid()) {
  156. continue;
  157. }
  158. // 处理事件
  159. final IEvent handler = (IEvent) sk.attachment();
  160. try {
  161. if (sk.isConnectable()) {
  162. handler.connect(sk);
  163. } else if (sk.isReadable()) {
  164. handler.read(sk);
  165. } else {
  166. // System.err.println("Ooops");
  167. }
  168. } catch (final Exception e) {
  169. handler.error(e);
  170. sk.cancel();
  171. }
  172. }
  173. }
  174. }
  175. public void shutdown() {
  176. if (isConnected()) {
  177. try {
  178. socketChannel.close();
  179. System.out.println("端口关闭成功");
  180. } catch (final IOException e) {
  181. System.out.println("端口关闭错误:");
  182. } finally {
  183. socketChannel = null;
  184. }
  185. } else {
  186. System.out.println("通道为空或者没有连接");
  187. }
  188. }
  189. @Override
  190. public void run() {
  191. // 启动主循环流程
  192. while (!shutdown.get()) {
  193. try {
  194. if (isConnected()&&(!isSended)) {
  195. switch (sysStatus) {
  196. case init:
  197. doOption();
  198. break;
  199. case options:
  200. doDescribe();
  201. break;
  202. case describe:
  203. doSetup();
  204. break;
  205. case setup:
  206. if(sessionid==null&&sessionid.length()>0){
  207. System.out.println("setup还没有正常返回");
  208. }else{
  209. doPlay();
  210. }
  211. break;
  212. case play:
  213. doPause();
  214. break;
  215. case pause:
  216. doTeardown();
  217. break;
  218. default:
  219. break;
  220. }
  221. }
  222. // do select
  223. select();
  224. try {
  225. Thread.sleep(1000);
  226. } catch (final Exception e) {
  227. }
  228. } catch (final Exception e) {
  229. e.printStackTrace();
  230. }
  231. }
  232. shutdown();
  233. }
  234. public void connect(SelectionKey key) throws IOException {
  235. if (isConnected()) {
  236. return;
  237. }
  238. // 完成SocketChannel的连接
  239. socketChannel.finishConnect();
  240. while (!socketChannel.isConnected()) {
  241. try {
  242. Thread.sleep(300);
  243. } catch (final InterruptedException e) {
  244. e.printStackTrace();
  245. }
  246. socketChannel.finishConnect();
  247. }
  248. }
  249. public void error(Exception e) {
  250. e.printStackTrace();
  251. }
  252. public void read(SelectionKey key) throws IOException {
  253. // 接收消息
  254. final byte[] msg = recieve();
  255. if (msg != null) {
  256. handle(msg);
  257. } else {
  258. key.cancel();
  259. }
  260. }
  261. private void handle(byte[] msg) {
  262. String tmp = new String(msg);
  263. System.out.println("返回内容:");
  264. System.out.println(tmp);
  265. if (tmp.startsWith(RTSP_OK)) {
  266. switch (sysStatus) {
  267. case init:
  268. sysStatus = Status.options;
  269. break;
  270. case options:
  271. sysStatus = Status.describe;
  272. trackInfo=tmp.substring(tmp.indexOf("trackID"));
  273. break;
  274. case describe:
  275. sessionid = tmp.substring(tmp.indexOf("Session: ") + 9, tmp
  276. .indexOf("Date:"));
  277. if(sessionid!=null&&sessionid.length()>0){
  278. sysStatus = Status.setup;
  279. }
  280. break;
  281. case setup:
  282. sysStatus = Status.play;
  283. break;
  284. case play:
  285. sysStatus = Status.pause;
  286. break;
  287. case pause:
  288. sysStatus = Status.teardown;
  289. shutdown.set(true);
  290. break;
  291. case teardown:
  292. sysStatus = Status.init;
  293. break;
  294. default:
  295. break;
  296. }
  297. isSended=false;
  298. } else {
  299. System.out.println("返回错误:" + tmp);
  300. }
  301. }
  302. private void doTeardown() {
  303. StringBuilder sb = new StringBuilder();
  304. sb.append("TEARDOWN ");
  305. sb.append(this.address);
  306. sb.append("/");
  307. sb.append(VERSION);
  308. sb.append("Cseq: ");
  309. sb.append(seq++);
  310. sb.append("/r/n");
  311. sb.append("User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32)/r/n");
  312. sb.append("Session: ");
  313. sb.append(sessionid);
  314. sb.append("/r/n");
  315. send(sb.toString().getBytes());
  316. System.out.println(sb.toString());
  317. }
  318. private void doPlay() {
  319. StringBuilder sb = new StringBuilder();
  320. sb.append("PLAY ");
  321. sb.append(this.address);
  322. sb.append(VERSION);
  323. sb.append("Session: ");
  324. sb.append(sessionid);
  325. sb.append("Cseq: ");
  326. sb.append(seq++);
  327. sb.append("/r/n");
  328. sb.append("/r/n");
  329. System.out.println(sb.toString());
  330. send(sb.toString().getBytes());
  331. }
  332. private void doSetup() {
  333. StringBuilder sb = new StringBuilder();
  334. sb.append("SETUP ");
  335. sb.append(this.address);
  336. sb.append("/");
  337. sb.append(trackInfo);
  338. sb.append(VERSION);
  339. sb.append("Cseq: ");
  340. sb.append(seq++);
  341. sb.append("/r/n");
  342. sb.append("Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play/r/n");
  343. sb.append("/r/n");
  344. System.out.println(sb.toString());
  345. send(sb.toString().getBytes());
  346. }
  347. private void doOption() {
  348. StringBuilder sb = new StringBuilder();
  349. sb.append("OPTIONS ");
  350. sb.append(this.address.substring(0, address.lastIndexOf("/")));
  351. sb.append(VERSION);
  352. sb.append("Cseq: ");
  353. sb.append(seq++);
  354. sb.append("/r/n");
  355. sb.append("/r/n");
  356. System.out.println(sb.toString());
  357. send(sb.toString().getBytes());
  358. }
  359. private void doDescribe() {
  360. StringBuilder sb = new StringBuilder();
  361. sb.append("DESCRIBE ");
  362. sb.append(this.address);
  363. sb.append(VERSION);
  364. sb.append("Cseq: ");
  365. sb.append(seq++);
  366. sb.append("/r/n");
  367. sb.append("/r/n");
  368. System.out.println(sb.toString());
  369. send(sb.toString().getBytes());
  370. }
  371. private void doPause() {
  372. StringBuilder sb = new StringBuilder();
  373. sb.append("PAUSE ");
  374. sb.append(this.address);
  375. sb.append("/");
  376. sb.append(VERSION);
  377. sb.append("Cseq: ");
  378. sb.append(seq++);
  379. sb.append("/r/n");
  380. sb.append("Session: ");
  381. sb.append(sessionid);
  382. sb.append("/r/n");
  383. send(sb.toString().getBytes());
  384. System.out.println(sb.toString());
  385. }
  386. public static void main(String[] args) {
  387. try {
  388. // RTSPClient(InetSocketAddress remoteAddress,
  389. // InetSocketAddress localAddress, String address)
  390. RTSPClient client = new RTSPClient(
  391. new InetSocketAddress("218.207.101.236", 554),
  392. new InetSocketAddress("192.168.2.28", 0),
  393. "rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp");
  394. client.start();
  395. } catch (Exception e) {
  396. e.printStackTrace();
  397. }
  398. }
  399. }

其中:rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp为我在网上找到的一个rtsp的sdp地址,读者可自行更换,RTSP的默认端口为554.
3.3  运行结果
       运行RTSPClient.java,运行结果如下所示:

  1. 端口打开成功
  2. OPTIONS rtsp://218.207.101.236:554/mobile/3/67A451E937422331 RTSP/1.0
  3. Cseq: 1
  4. 返回内容:
  5. RTSP/1.0 200 OK
  6. Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; )
  7. Cseq: 1
  8. Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD
  9. DESCRIBE rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0
  10. Cseq: 2
  11. 返回内容:
  12. RTSP/1.0 200 OK
  13. Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; )
  14. Cseq: 2
  15. Content-length: 421
  16. Date: Mon, 03 Aug 2009 08:50:36 GMT
  17. Expires: Mon, 03 Aug 2009 08:50:36 GMT
  18. Content-Type: application/sdp
  19. x-Accept-Retransmit: our-retransmit
  20. x-Accept-Dynamic-Rate: 1
  21. Content-Base: rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/
  22. v=0
  23. o=MediaBox 127992 137813 IN IP4 0.0.0.0
  24. s=RTSP Session
  25. i=Starv Box Live Cast
  26. c=IN IP4 218.207.101.236
  27. t=0 0
  28. a=range:npt=now-
  29. a=control:*
  30. m=video 0 RTP/AVP 96
  31. b=AS:20
  32. a=rtpmap:96 MP4V-ES/1000
  33. a=fmtp:96 profile-level-id=8; config=000001b008000001b5090000010000000120008440fa282c2090a31f; decode_buf=12586
  34. a=range:npt=now-
  35. a=framerate:5
  36. a=framesize:96 176-144
  37. a=cliprect:0,0,144,176
  38. a=control:trackID=1
  39. SETUP rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1
  40. RTSP/1.0
  41. Cseq: 3
  42. Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play
  43. 返回内容:
  44. RTSP/1.0 200 OK
  45. Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; )
  46. Cseq: 3
  47. Session: 15470472221769
  48. Date: Mon, 03 Aug 2009 08:50:36 GMT
  49. Expires: Mon, 03 Aug 2009 08:50:36 GMT
  50. Transport: RTP/AVP;UNICAST;mode=play;client_port=16264-16265;server_port=20080-20081
  51. PLAY rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0
  52. Session: 15470472221769
  53. Cseq: 4
  54. 返回内容:
  55. RTSP/1.0 200 OK
  56. Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; )
  57. Cseq: 4
  58. Session: 15470472221769
  59. RTP-Info: url=rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1;seq=0;rtptime=0
  60. PAUSE rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0
  61. Cseq: 5
  62. Session: 15470472221769
  63. 返回内容:
  64. RTSP/1.0 200 OK
  65. Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; )
  66. Cseq: 5
  67. Session: 15470472221769
  68. TEARDOWN rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0
  69. Cseq: 6
  70. User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32)
  71. Session: 15470472221769
  72. 返回内容:
  73. RTSP/1.0 200 OK
  74. Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; )
  75. Cseq: 6
  76. Session: 15470472221769
  77. Connection: Close
  78. 端口关闭成功

对照运行结果,读者可以熟悉RTSP的常用命令.