添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
RTSP实例

一. 参考资料
1. 《RTSP简单命令》: http://blog.csdn.net/feidragon319/archive/2007/08/14/1742357.aspx
2. http://bbs.21eic.com/dispbbs.asp?boardid=15&Id=22948
3. 《RTSP客户端的Java实现》:http://hi.baidu.com/ssyuan/blog/item/566df6defac1dc5094ee37eb.html

二. RTSP的常用命令与解释
其中C是客户端,S是服务端。
2.1  OPTIONS
C->S:       OPTION request //询问S有哪些方法可用
S->C:       OPTION response //S回应信息中包括提供的所有可用方法
使用举例:
客户端到服务端:

Java代码
  • OPTIONS rtsp: //218.207.101.236:554/mobile/3/67A451E937422331 RTSP/1.0
  • Cseq: 1
  • 服务端对OPTIONS的回应:

    Java代码
  • RTSP/ 1.0 200 OK
  • Server: PVSS/ 1.4 . 8 (Build/ 20090111 ; Platform/Win32; Release/StarValley; )
  • Cseq: 1
  • Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD
  • 2.2  DESCRIBE

    C->S:      DESCRIBE request //要求得到S提供的媒体初始化描述信息
    S->C:      DESCRIBE response //S回应媒体初始化描述信息,主要是sdp

    使用举例:
    客户端到服务端:

    Java代码
  • DESCRIBE
  • Java代码
  • rtsp: //218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0
  • Cseq: 2
  • 服务端对OPTIONS的回应:

    Java代码
  • 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 : 21 : 33 GMT
  • Expires: Mon, 03 Aug 2009 08 : 21 : 33 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/
  • 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
  • 2.3  SETUP
    C->S:        SETUP request //设置会话的属性,以及传输模式,提醒S建立会话
    S->C:        SETUP response //S建立会话,返回会话标识符,以及会话相关信息
    客户端到服务端的请求举例:

    Java代码
  • 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
  • 服务端对客户端的回应举例:

    Java代码
  • RTSP/ 1.0 200 OK
  • Server: PVSS/ 1.4 . 8 (Build/ 20090111 ;Platform/Win32;Release/StarValley; )
  • Cseq: 3
  • Session: 26633092229589
  • Date: Mon, 03 Aug 2009 08 : 21 : 33 GMT
  • Expires: Mon, 03 Aug 2009 08 : 21 : 33 GMT
  • Transport: RTP/AVP;UNICAST;mode=play;client_port= 16264 - 16265 ;server_port= 20026 - 20027
  • 2.4  PLAY
    C->S:      PLAY request //C请求播放
    S->C:      PLAY response //S回应该请求的信息
    客户端到服务端的请求举例:

    Java代码
  • PLAY rtsp: //218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0
  • Session: 26633092229589
  • Cseq: 4
  • 服务端对客户端的回应举例:

    Java代码
  • RTSP/ 1.0 200 OK
  • Server: PVSS/ 1.4 . 8 (Build/ 20090111 ; Platform/Win32; Release/StarValley; )
  • Cseq: 4
  • Session: 26633092229589
  • RTP-Info: url=rtsp: //218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1;seq=0;rtptime=0
  • 2.5  PAUSE
    C->S:      PAUSE request //C请求暂停播放
    S->C:      PAUSE response //S回应该请求的信息
    客户端到服务端的请求举例:

    Java代码
  • PAUSE rtsp: //218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0
  • Cseq: 5
  • Session: 26633092229589
  • 服务端对客户端的回应举例:

    Java代码
  • RTSP/ 1.0 200 OK
  • Server: PVSS/ 1.4 . 8 (Build/ 20090111 ; Platform/Win32; Release/StarValley; )
  • Cseq: 5
  • Session: 26633092229589
  • 2.6  TEARDOWN
    C->S:        TEARDOWN request //C请求关闭会话
    S->C:        TEARDOWN response //S回应该请求
    客户端到服务端的请求举例:

    Java代码
  • 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: 26633092229589
  • 服务端对客户端的回应举例:

    Java代码
  • RTSP/ 1.0 200 OK
  • Server: PVSS/ 1.4 . 8 (Build/ 20090111 ; Platform/Win32; Release/StarValley; )
  • Cseq: 6
  • Session: 26633092229589
  • Connection: Close
  • 三. RTSP客户端的Java实现
    3.1  接口IEvent.java

    接口IEvent.java的代码如下:

    Java代码
  • package com.amigo.rtsp;
  • import java.io.IOException;
  • import java.nio.channels.SelectionKey;
  • /** */ /**
  • * 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类的代码如下所示:

    Java代码
  • package com.amigo.rtsp;
  • import java.io.IOException;
  • import java.net.InetSocketAddress;
  • import java.nio.ByteBuffer;
  • import java.nio.channels.SelectionKey;
  • import java.nio.channels.Selector;
  • import java.nio.channels.SocketChannel;
  • import java.util.Iterator;
  • import java.util.concurrent.atomic.AtomicBoolean;
  • public class RTSPClient extends Thread implements IEvent {
  • private static final String VERSION = " RTSP/1.0/r/n" ;
  • private static final String RTSP_OK = "RTSP/1.0 200 OK" ;
  • /** */ /** 远程地址 */
  • private final InetSocketAddress remoteAddress;
  • /** */ /** * 本地地址 */
  • private final InetSocketAddress localAddress;
  • /** */ /** * 连接通道 */
  • private SocketChannel socketChannel;
  • /** */ /** 发送缓冲区 */
  • private final ByteBuffer sendBuf;
  • /** */ /** 接收缓冲区 */
  • private final ByteBuffer receiveBuf;
  • private static final int BUFFER_SIZE = 8192 ;
  • /** */ /** 端口选择器 */
  • private Selector selector;
  • private String address;
  • private Status sysStatus;
  • private String sessionid;
  • /** */ /** 线程是否结束的标志 */
  • private AtomicBoolean shutdown;
  • private int seq= 1 ;
  • private boolean isSended;
  • private String trackInfo;
  • private enum Status {
  • init, options, describe, setup, play, pause, teardown
  • public RTSPClient(InetSocketAddress remoteAddress,
  • 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,运行结果如下所示:

    Java代码
  • 端口打开成功
  • OPTIONS rtsp: //218.207.101.236:554/mobile/3/67A451E937422331 RTSP/1.0
  • Cseq: 1
  • 返回内容:
  • RTSP/ 1.0 200 OK
  • Server: PVSS/ 1.4 . 8 (Build/ 20090111 ; Platform/Win32; Release/StarValley; )
  • Cseq: 1
  • Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD
  • 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/
  • 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的常用命令.