RTSP协议详解
协议 详解 RTSP
2023-09-27 14:29:30 时间
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端口。
SIP(Session Initiation Protocol),是基于IP的一个应用层控制协议。由于SIP是基于纯文本的信令协议,可以管理不同接入网络上的会话等。会话可以是终端设备之间任何类型的通信,如视频会话、既时信息处理或协作会话。该协议不会定义或限制可使用的业务,传输、服务质量、计费、安全性等问题都由基本核心网络和其它协议处理。 (1)联系:sip和rtsp都是应用层的控制协议,负责一次通信过程的建立和控制和结束,不负责中间的传输部分。他们都是基于纯文本的信令协议,穿墙性能良好。支持tcp、udp,支持多方通信。他们都需要服务器支持,都支持会话中重定向。sip和rtsp 都使用sdp协议来传送媒体参数,使用rtp(rtcp)协议来传输媒体流。 (2)区别:rtsp是专门为流媒体制定的协议,在多个媒体流的时间同步方面比sip强大。rtsp还提供网络负载均衡的功能,减轻服务器压力和网络带宽要求。sip一般用来创建一次音频、视频通话(双向),而rtsp一般用来做视频点播、视频监控等(单向)。当然,从原理上讲,rtsp也可以做双向的视频通话。
RTSP的消息 RTSP的消息有两大类,一是请求消息(request),一是回应消息(response),两种消息的格式不同。 请求消息格式:
方法 URI RTSP版本 CR LF
消息头 CR LF CR LF
消息体 CR LF
/** *//** * IEvent.java 网络事件处理器,当Selector可以进行操作时,调用这个接口中的方法. * 2007-3-22 下午03:35:51 * @author sycheng * @version 1.0 public interface IEvent { /** *//** * 当channel得到connect事件时调用这个方法. * @param key * @throws IOException */ void connect(SelectionKey key) throws IOException; /** *//** * 当channel可读时调用这个方法. * @param key * @throws IOException */ void read(SelectionKey key) throws IOException; /** *//** * 当channel可写时调用这个方法. * @throws IOException */ void write() throws IOException; /** *//** * 当channel发生错误时调用. * @param e */ void error(Exception e);
InetSocketAddress localAddress, String address) { this.remoteAddress = remoteAddress; this.localAddress = localAddress; this.address = address; // 初始化缓冲区 sendBuf = ByteBuffer.allocateDirect(BUFFER_SIZE); receiveBuf = ByteBuffer.allocateDirect(BUFFER_SIZE); if (selector == null) { // 创建新的Selector try { selector = Selector.open(); } catch (final IOException e) { e.printStackTrace(); } } startup(); sysStatus = Status.init; shutdown=new AtomicBoolean(false); isSended=false; } public void startup() { try { // 打开通道 socketChannel = SocketChannel.open(); // 绑定到本地端口 socketChannel.socket().setSoTimeout(30000); socketChannel.configureBlocking(false); socketChannel.socket().bind(localAddress); if (socketChannel.connect(remoteAddress)) { System.out.println("开始建立连接:" + remoteAddress); } socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE, this); System.out.println("端口打开成功"); } catch (final IOException e1) { e1.printStackTrace(); } } public void send(byte[] out) { if (out == null || out.length 1) { return; } synchronized (sendBuf) { sendBuf.clear(); sendBuf.put(out); sendBuf.flip(); } // 发送出去 try { write(); isSended=true; } catch (final IOException e) { e.printStackTrace(); } } public void write() throws IOException { if (isConnected()) { try { socketChannel.write(sendBuf); } catch (final IOException e) { } } else { System.out.println("通道为空或者没有连接上"); } } public byte[] recieve() { if (isConnected()) { try { int len = 0; int readBytes = 0; synchronized (receiveBuf) { receiveBuf.clear(); try { while ((len = socketChannel.read(receiveBuf)) 0) { readBytes += len; } } finally { receiveBuf.flip(); } if (readBytes 0) { final byte[] tmp = new byte[readBytes]; receiveBuf.get(tmp); return tmp; } else { System.out.println("接收到数据为空,重新启动连接"); return null; } } } catch (final IOException e) { System.out.println("接收消息错误:"); } } else { System.out.println("端口没有连接"); } return null; } public boolean isConnected() { return socketChannel != null socketChannel.isConnected(); } private void select() { int n = 0; try { if (selector == null) { return; } n = selector.select(1000); } catch (final Exception e) { e.printStackTrace(); } // 如果select返回大于0,处理事件 if (n 0) { for (final Iterator SelectionKey i = selector.selectedKeys() .iterator(); i.hasNext();) { // 得到下一个Key final SelectionKey sk = i.next(); i.remove(); // 检查其是否还有效 if (!sk.isValid()) { continue; } // 处理事件 final IEvent handler = (IEvent) sk.attachment(); try { if (sk.isConnectable()) { handler.connect(sk); } else if (sk.isReadable()) { handler.read(sk); } else { // System.err.println("Ooops"); } } catch (final Exception e) { handler.error(e); sk.cancel(); } } } } public void shutdown() { if (isConnected()) { try { socketChannel.close(); System.out.println("端口关闭成功"); } catch (final IOException e) { System.out.println("端口关闭错误:"); } finally { socketChannel = null; } } else { System.out.println("通道为空或者没有连接"); } } @Override public void run() { // 启动主循环流程 while (!shutdown.get()) { try { if (isConnected() (!isSended)) { switch (sysStatus) { case init: doOption(); break; case options: doDescribe(); break; case describe: doSetup(); break; case setup: if(sessionid==null sessionid.length() 0){ System.out.println("setup还没有正常返回"); }else{ doPlay(); } break; case play: doPause(); break; case pause: doTeardown(); break; default: break; } } // do select select(); try { Thread.sleep(1000); } catch (final Exception e) { } } catch (final Exception e) { e.printStackTrace(); } } shutdown(); } public void connect(SelectionKey key) throws IOException { if (isConnected()) { return; } // 完成SocketChannel的连接 socketChannel.finishConnect(); while (!socketChannel.isConnected()) { try { Thread.sleep(300); } catch (final InterruptedException e) { e.printStackTrace(); } socketChannel.finishConnect(); } } public void error(Exception e) { e.printStackTrace(); } public void read(SelectionKey key) throws IOException { // 接收消息 final byte[] msg = recieve(); if (msg != null) { handle(msg); } else { key.cancel(); } } private void handle(byte[] msg) { String tmp = new String(msg); System.out.println("返回内容:"); System.out.println(tmp); if (tmp.startsWith(RTSP_OK)) { switch (sysStatus) { case init: sysStatus = Status.options; break; case options: sysStatus = Status.describe; trackInfo=tmp.substring(tmp.indexOf("trackID")); break; case describe: sessionid = tmp.substring(tmp.indexOf("Session: ") + 9, tmp .indexOf("Date:")); if(sessionid!=null sessionid.length() 0){ sysStatus = Status.setup; } break; case setup: sysStatus = Status.play; break; case play: sysStatus = Status.pause; break; case pause: sysStatus = Status.teardown; shutdown.set(true); break; case teardown: sysStatus = Status.init; break; default: break; } isSended=false; } else { System.out.println("返回错误:" + tmp); } } private void doTeardown() { StringBuilder sb = new StringBuilder(); sb.append("TEARDOWN "); sb.append(this.address); sb.append("/"); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32)/r/n"); sb.append("Session: "); sb.append(sessionid); sb.append("/r/n"); send(sb.toString().getBytes()); System.out.println(sb.toString()); } private void doPlay() { StringBuilder sb = new StringBuilder(); sb.append("PLAY "); sb.append(this.address); sb.append(VERSION); sb.append("Session: "); sb.append(sessionid); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doSetup() { StringBuilder sb = new StringBuilder(); sb.append("SETUP "); sb.append(this.address); sb.append("/"); sb.append(trackInfo); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doOption() { StringBuilder sb = new StringBuilder(); sb.append("OPTIONS "); sb.append(this.address.substring(0, address.lastIndexOf("/"))); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doDescribe() { StringBuilder sb = new StringBuilder(); sb.append("DESCRIBE "); sb.append(this.address); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doPause() { StringBuilder sb = new StringBuilder(); sb.append("PAUSE "); sb.append(this.address); sb.append("/"); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("Session: "); sb.append(sessionid); sb.append("/r/n"); send(sb.toString().getBytes()); System.out.println(sb.toString()); } public static void main(String[] args) { try { // RTSPClient(InetSocketAddress remoteAddress, // InetSocketAddress localAddress, String address) RTSPClient client = new RTSPClient( new InetSocketAddress("218.207.101.236", 554), new InetSocketAddress("192.168.2.28", 0), "rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp"); client.start(); } catch (Exception e) { e.printStackTrace(); } }
DESCRIBE rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0 Cseq: 2 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 2 Content-length: 421 Date: Mon, 03 Aug 2009 08:50:36 GMT Expires: Mon, 03 Aug 2009 08:50:36 GMT Content-Type: application/sdp x-Accept-Retransmit: our-retransmit x-Accept-Dynamic-Rate: 1 Content-Base: rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ v=0 o=MediaBox 127992 137813 IN IP4 0.0.0.0 s=RTSP Session i=Starv Box Live Cast c=IN IP4 218.207.101.236 t=0 0 a=range:npt=now- a=control:* m=video 0 RTP/AVP 96 b=AS:20 a=rtpmap:96 MP4V-ES/1000 a=fmtp:96 profile-level-id=8; config=000001b008000001b5090000010000000120008440fa282c2090a31f; decode_buf=12586 a=range:npt=now- a=framerate:5 a=framesize:96 176-144 a=cliprect:0,0,144,176 a=control:trackID=1 SETUP rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1 RTSP/1.0 Cseq: 3 Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 3 Session: 15470472221769 Date: Mon, 03 Aug 2009 08:50:36 GMT Expires: Mon, 03 Aug 2009 08:50:36 GMT Transport: RTP/AVP;UNICAST;mode=play;client_port=16264-16265;server_port=20080-20081 PLAY rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0 Session: 15470472221769 Cseq: 4 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 4 Session: 15470472221769 RTP-Info: url=rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1;seq=0;rtptime=0 PAUSE rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0 Cseq: 5 Session: 15470472221769 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 5 Session: 15470472221769 TEARDOWN rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0 Cseq: 6 User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32) Session: 15470472221769 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 6 Session: 15470472221769 Connection: Close 端口关闭成功
RTSP协议分析(二) 以下是某省IPTV的RTSP协商过程: DESCRIBE rtsp://118.122.89.27:554/live/ch10083121594790060557.sdp?playtype=1&boid=001&backupagent=118.
RTSP 协议分析 (一) RTSP 协议分析1.概述: RTSP(Real Time Streaming Protocol),实时流传输协议,是TCP/IP协议体系中的一个应用层协议,由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC标准。
rtsp协议详解 格式为:a=rtpmap: 净荷类型 编码名称 * 净荷类型0固定分配给了PCMU, * 净荷类型96对应的编码方案为G.726,为动态分配的。 * 净荷类型97对应的编码方式为自适应多速率宽带编码(AMR-WB),为动态分配的
SIP(Session Initiation Protocol),是基于IP的一个应用层控制协议。由于SIP是基于纯文本的信令协议,可以管理不同接入网络上的会话等。会话可以是终端设备之间任何类型的通信,如视频会话、既时信息处理或协作会话。该协议不会定义或限制可使用的业务,传输、服务质量、计费、安全性等问题都由基本核心网络和其它协议处理。 (1)联系:sip和rtsp都是应用层的控制协议,负责一次通信过程的建立和控制和结束,不负责中间的传输部分。他们都是基于纯文本的信令协议,穿墙性能良好。支持tcp、udp,支持多方通信。他们都需要服务器支持,都支持会话中重定向。sip和rtsp 都使用sdp协议来传送媒体参数,使用rtp(rtcp)协议来传输媒体流。 (2)区别:rtsp是专门为流媒体制定的协议,在多个媒体流的时间同步方面比sip强大。rtsp还提供网络负载均衡的功能,减轻服务器压力和网络带宽要求。sip一般用来创建一次音频、视频通话(双向),而rtsp一般用来做视频点播、视频监控等(单向)。当然,从原理上讲,rtsp也可以做双向的视频通话。
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客户端的JAVA实现
3.1 接口IEvent.java
接口IEvent.java的代码如下:
/** *//** * IEvent.java 网络事件处理器,当Selector可以进行操作时,调用这个接口中的方法. * 2007-3-22 下午03:35:51 * @author sycheng * @version 1.0 public interface IEvent { /** *//** * 当channel得到connect事件时调用这个方法. * @param key * @throws IOException */ void connect(SelectionKey key) throws IOException; /** *//** * 当channel可读时调用这个方法. * @param key * @throws IOException */ void read(SelectionKey key) throws IOException; /** *//** * 当channel可写时调用这个方法. * @throws IOException */ void write() throws IOException; /** *//** * 当channel发生错误时调用. * @param e */ void error(Exception e);
3.2 RTSP的测试类:RTSPClient.java
RTSP的测试类RTSPClient.java类的代码如下所示:
InetSocketAddress localAddress, String address) { this.remoteAddress = remoteAddress; this.localAddress = localAddress; this.address = address; // 初始化缓冲区 sendBuf = ByteBuffer.allocateDirect(BUFFER_SIZE); receiveBuf = ByteBuffer.allocateDirect(BUFFER_SIZE); if (selector == null) { // 创建新的Selector try { selector = Selector.open(); } catch (final IOException e) { e.printStackTrace(); } } startup(); sysStatus = Status.init; shutdown=new AtomicBoolean(false); isSended=false; } public void startup() { try { // 打开通道 socketChannel = SocketChannel.open(); // 绑定到本地端口 socketChannel.socket().setSoTimeout(30000); socketChannel.configureBlocking(false); socketChannel.socket().bind(localAddress); if (socketChannel.connect(remoteAddress)) { System.out.println("开始建立连接:" + remoteAddress); } socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE, this); System.out.println("端口打开成功"); } catch (final IOException e1) { e1.printStackTrace(); } } public void send(byte[] out) { if (out == null || out.length 1) { return; } synchronized (sendBuf) { sendBuf.clear(); sendBuf.put(out); sendBuf.flip(); } // 发送出去 try { write(); isSended=true; } catch (final IOException e) { e.printStackTrace(); } } public void write() throws IOException { if (isConnected()) { try { socketChannel.write(sendBuf); } catch (final IOException e) { } } else { System.out.println("通道为空或者没有连接上"); } } public byte[] recieve() { if (isConnected()) { try { int len = 0; int readBytes = 0; synchronized (receiveBuf) { receiveBuf.clear(); try { while ((len = socketChannel.read(receiveBuf)) 0) { readBytes += len; } } finally { receiveBuf.flip(); } if (readBytes 0) { final byte[] tmp = new byte[readBytes]; receiveBuf.get(tmp); return tmp; } else { System.out.println("接收到数据为空,重新启动连接"); return null; } } } catch (final IOException e) { System.out.println("接收消息错误:"); } } else { System.out.println("端口没有连接"); } return null; } public boolean isConnected() { return socketChannel != null socketChannel.isConnected(); } private void select() { int n = 0; try { if (selector == null) { return; } n = selector.select(1000); } catch (final Exception e) { e.printStackTrace(); } // 如果select返回大于0,处理事件 if (n 0) { for (final Iterator SelectionKey i = selector.selectedKeys() .iterator(); i.hasNext();) { // 得到下一个Key final SelectionKey sk = i.next(); i.remove(); // 检查其是否还有效 if (!sk.isValid()) { continue; } // 处理事件 final IEvent handler = (IEvent) sk.attachment(); try { if (sk.isConnectable()) { handler.connect(sk); } else if (sk.isReadable()) { handler.read(sk); } else { // System.err.println("Ooops"); } } catch (final Exception e) { handler.error(e); sk.cancel(); } } } } public void shutdown() { if (isConnected()) { try { socketChannel.close(); System.out.println("端口关闭成功"); } catch (final IOException e) { System.out.println("端口关闭错误:"); } finally { socketChannel = null; } } else { System.out.println("通道为空或者没有连接"); } } @Override public void run() { // 启动主循环流程 while (!shutdown.get()) { try { if (isConnected() (!isSended)) { switch (sysStatus) { case init: doOption(); break; case options: doDescribe(); break; case describe: doSetup(); break; case setup: if(sessionid==null sessionid.length() 0){ System.out.println("setup还没有正常返回"); }else{ doPlay(); } break; case play: doPause(); break; case pause: doTeardown(); break; default: break; } } // do select select(); try { Thread.sleep(1000); } catch (final Exception e) { } } catch (final Exception e) { e.printStackTrace(); } } shutdown(); } public void connect(SelectionKey key) throws IOException { if (isConnected()) { return; } // 完成SocketChannel的连接 socketChannel.finishConnect(); while (!socketChannel.isConnected()) { try { Thread.sleep(300); } catch (final InterruptedException e) { e.printStackTrace(); } socketChannel.finishConnect(); } } public void error(Exception e) { e.printStackTrace(); } public void read(SelectionKey key) throws IOException { // 接收消息 final byte[] msg = recieve(); if (msg != null) { handle(msg); } else { key.cancel(); } } private void handle(byte[] msg) { String tmp = new String(msg); System.out.println("返回内容:"); System.out.println(tmp); if (tmp.startsWith(RTSP_OK)) { switch (sysStatus) { case init: sysStatus = Status.options; break; case options: sysStatus = Status.describe; trackInfo=tmp.substring(tmp.indexOf("trackID")); break; case describe: sessionid = tmp.substring(tmp.indexOf("Session: ") + 9, tmp .indexOf("Date:")); if(sessionid!=null sessionid.length() 0){ sysStatus = Status.setup; } break; case setup: sysStatus = Status.play; break; case play: sysStatus = Status.pause; break; case pause: sysStatus = Status.teardown; shutdown.set(true); break; case teardown: sysStatus = Status.init; break; default: break; } isSended=false; } else { System.out.println("返回错误:" + tmp); } } private void doTeardown() { StringBuilder sb = new StringBuilder(); sb.append("TEARDOWN "); sb.append(this.address); sb.append("/"); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32)/r/n"); sb.append("Session: "); sb.append(sessionid); sb.append("/r/n"); send(sb.toString().getBytes()); System.out.println(sb.toString()); } private void doPlay() { StringBuilder sb = new StringBuilder(); sb.append("PLAY "); sb.append(this.address); sb.append(VERSION); sb.append("Session: "); sb.append(sessionid); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doSetup() { StringBuilder sb = new StringBuilder(); sb.append("SETUP "); sb.append(this.address); sb.append("/"); sb.append(trackInfo); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doOption() { StringBuilder sb = new StringBuilder(); sb.append("OPTIONS "); sb.append(this.address.substring(0, address.lastIndexOf("/"))); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doDescribe() { StringBuilder sb = new StringBuilder(); sb.append("DESCRIBE "); sb.append(this.address); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("/r/n"); System.out.println(sb.toString()); send(sb.toString().getBytes()); } private void doPause() { StringBuilder sb = new StringBuilder(); sb.append("PAUSE "); sb.append(this.address); sb.append("/"); sb.append(VERSION); sb.append("Cseq: "); sb.append(seq++); sb.append("/r/n"); sb.append("Session: "); sb.append(sessionid); sb.append("/r/n"); send(sb.toString().getBytes()); System.out.println(sb.toString()); } public static void main(String[] args) { try { // RTSPClient(InetSocketAddress remoteAddress, // InetSocketAddress localAddress, String address) RTSPClient client = new RTSPClient( new InetSocketAddress("218.207.101.236", 554), new InetSocketAddress("192.168.2.28", 0), "rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp"); client.start(); } catch (Exception e) { e.printStackTrace(); } }
其中:rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp为我在网上找到的一个rtsp的sdp地址,读者可自行更换,RTSP的默认端口为554.
3.3 运行结果
运行RTSPClient.java,运行结果如下所示:
DESCRIBE rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0 Cseq: 2 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 2 Content-length: 421 Date: Mon, 03 Aug 2009 08:50:36 GMT Expires: Mon, 03 Aug 2009 08:50:36 GMT Content-Type: application/sdp x-Accept-Retransmit: our-retransmit x-Accept-Dynamic-Rate: 1 Content-Base: rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ v=0 o=MediaBox 127992 137813 IN IP4 0.0.0.0 s=RTSP Session i=Starv Box Live Cast c=IN IP4 218.207.101.236 t=0 0 a=range:npt=now- a=control:* m=video 0 RTP/AVP 96 b=AS:20 a=rtpmap:96 MP4V-ES/1000 a=fmtp:96 profile-level-id=8; config=000001b008000001b5090000010000000120008440fa282c2090a31f; decode_buf=12586 a=range:npt=now- a=framerate:5 a=framesize:96 176-144 a=cliprect:0,0,144,176 a=control:trackID=1 SETUP rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1 RTSP/1.0 Cseq: 3 Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 3 Session: 15470472221769 Date: Mon, 03 Aug 2009 08:50:36 GMT Expires: Mon, 03 Aug 2009 08:50:36 GMT Transport: RTP/AVP;UNICAST;mode=play;client_port=16264-16265;server_port=20080-20081 PLAY rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0 Session: 15470472221769 Cseq: 4 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 4 Session: 15470472221769 RTP-Info: url=rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1;seq=0;rtptime=0 PAUSE rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0 Cseq: 5 Session: 15470472221769 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 5 Session: 15470472221769 TEARDOWN rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0 Cseq: 6 User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32) Session: 15470472221769 返回内容: RTSP/1.0 200 OK Server: PVSS/1.4.8 (Build/20090111; Platform/Win32; Release/StarValley; ) Cseq: 6 Session: 15470472221769 Connection: Close 端口关闭成功
RTSP协议分析(二) 以下是某省IPTV的RTSP协商过程: DESCRIBE rtsp://118.122.89.27:554/live/ch10083121594790060557.sdp?playtype=1&boid=001&backupagent=118.
RTSP 协议分析 (一) RTSP 协议分析1.概述: RTSP(Real Time Streaming Protocol),实时流传输协议,是TCP/IP协议体系中的一个应用层协议,由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC标准。
rtsp协议详解 格式为:a=rtpmap: 净荷类型 编码名称 * 净荷类型0固定分配给了PCMU, * 净荷类型96对应的编码方案为G.726,为动态分配的。 * 净荷类型97对应的编码方式为自适应多速率宽带编码(AMR-WB),为动态分配的
相关文章
- 软件测试网络基础——Web Http协议详解
- 网络协议之:socket协议详解之Socket和Stream Socket
- 【HTTP协议】---HTTP协议详解
- WebRTC进阶-SDP协议详解
- zookeeper的ZAB协议的原理以及底层源码实现超级详解
- 图文并茂详解 NAT 协议!
- TCP/IP协议与Http协议的区别详解
- AS2协议详解(三)
- 利用http协议put上传控制对方服务器
- HTTP协议详解之响应篇
- RabbitMQ与AMQP协议详解
- 亨通光电与联通网研院签署量子通信战略合作协议
- swift class protocol-限定协议只能由类实现
- 如何使用git通过ssh协议拉取gitee上的项目代码——如何正确的免密使用git
- 多网卡系统下如何使用tcp协议实现MPI的分布式多机运行(mpi的实现使用openmpi)
- 如何禁止DELETE、PUT、OPTIONS、TRACE、HEAD等协议访问应用程序 .
- 网络协议之:socket协议详解之Unix domain Socket
- Python 基于TCP协议通信的简单套接字编程
- 微软为怀俄明州数据中心签署最大的风力发电协议
- HTTP协议状态码详解(HTTP Status Code)(转)
- 5_3. 多路访问控制协议
- http协议详解
- TCP协议详解