RTSP实例

本文详细介绍了RTSP协议的常用命令及其交互过程,并提供了一个简单的RTSP客户端Java实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
现在位置: 主页 > JAVA博客 > 文章内容

RTSP实例

收藏 发布 更新日期:2009-08-06 点击: 8

一. 参考资料
1. 《RTSP简单命令》:http://blog.youkuaiyun.com/feidragon319/archive/2007/08/14/1742357.aspx
2. http://bbs.21eic.com/dispbbs.asp?boardid=15%26amp;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回应信息中包括提供的所有可用方法
使用举例:
客户端到服务端:
OPTIONS rtsp: // 218.207.101.236:554/mobile/3/67A451E937422331 RTSP/1.0
Cseq: 1 服务端对OPTIONS的回应:
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

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

DESCRIBE rtsp: // 218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0
Cseq: 2

服务端对OPTIONS的回应:

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/

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

2.3 SETUP
C->S: SETUP request //设置会话的属性,以及传输模式,提醒S建立会话
S->C: SETUP response //S建立会话,返回会话标识符,以及会话相关信息
客户端到服务端的请求举例:

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:
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回应该请求的信息
客户端到服务端的请求举例:

PLAY rtsp: // 218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp RTSP/1.0
Session: 26633092229589
Cseq:
4

服务端对客户端的回应举例:

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回应该请求的信息
客户端到服务端的请求举例:

PAUSE rtsp: // 218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/ RTSP/1.0
Cseq: 5
Session:
26633092229589

服务端对客户端的回应举例:

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回应该请求
客户端到服务端的请求举例:

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

服务端对客户端的回应举例:

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的代码如下:
package com.amigo.rtsp;

import java.io.IOException;
import java.nio.channels.SelectionKey;

/**
* IEvent.java 网络事件处理器,当Selector可以进行操作时,调用这个接口中的方法.
* 2007-3-22 下午03:35:51
*
%26#64;author sycheng
*
%26#64;version 1.0
*/

public interface IEvent {
/**
* 当channel得到connect事件时调用这个方法.
*
%26#64;param key
*
%26#64;throws IOException
*/

void connect(SelectionKey key) throws IOException;

/**
* 当channel可读时调用这个方法.
*
%26#64;param key
*
%26#64;throws IOException
*/

void read(SelectionKey key) throws IOException;

/**
* 当channel可写时调用这个方法.
*
%26#64;throws IOException
*/

void write() throws IOException;

/**
* 当channel发生错误时调用.
*
%26#64;param e
*/

void error(Exception e);
}

3.2 RTSP的测试类:RTSPClient.java
RTSP的测试类RTSPClient.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 %26amp;%26amp; 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(
" 通道为空或者没有连接 " );
}

}


%26#64;Override
public void run() {
// 启动主循环流程
while ( ! shutdown.get()) {
try {
if (isConnected() %26amp;%26amp; ( ! isSended)) {
switch (sysStatus) {
case init:
doOption();
break ;
case options:
doDescribe();
break ;
case describe:
doSetup();
break ;
case setup:
if (sessionid == null %26amp;%26amp; 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 %26amp;%26amp; 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,运行结果如下所示:
端口打开成功
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/

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的常用命令.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值