关键词:
这里写目录标题
- 了解RTSP协议
- 使用Java程序编写RTSP客户端 访问 RTSP服务端,实现拉流
RTSP协议是什么
RTSP是一种基于文本的协议,用CRLF(回车换行)作为每一行的结束符,其好处是,在使用过程中可以方便地增加自定义参数,也方便抓包分析。从消息传送方向上来分,RTSP的报文有两类:请求报文和响应报文。请求报文是指从客户端向服务器发送的请求(也有少量从服务器向客户端发送的请求),响应报文是指从服务器到客户端的回应。
RTSP请求报文的常用方法与作用
一次基本的RTSP交互过程如下,C表示客户端,S表示服务端。
- OPTION请求
-> 响应 - DESCRIBE请求
-> 响应
如果响应无权限, 那么需要带上用户名密码 - SETUP请求
-> 响应 - PLAY请求
-> 响应
-> 流数据
-> 流数据
-> 流数据
报文实例:
1. OPTIONS
OPTIONS rtsp://39.170.35.150:1554/h264/ch0/1 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
RTSP/1.0 200 OK
CSeq: 2
Public: OPTIONS, DESCRIBE, PLAY, PAUSE, SETUP, TEARDOWN, SET_PARAMETER, GET_PARAMETER
Date: Sat, Mar 05 2022 15:39:55 GMT
2. DESCRIBE
DESCRIBE rtsp://39.170.35.150:1554/h264/ch0/1 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
Accept: application/sdp
RTSP/1.0 401 Unauthorized
CSeq: 3
WWW-Authenticate: Digest realm="2857be191e08", nonce="5b960b3d4673be2908666321f64d2bff", stale="FALSE"
WWW-Authenticate: Basic realm="2857be191e08"
Date: Sat, Mar 05 2022 15:39:55 GMT
发现无权, 使用用户名密码进行授权
DESCRIBE rtsp://39.170.35.150:1554/h264/ch0/1 RTSP/1.0
CSeq: 4
Authorization: Digest username="admin", realm="2857be191e08", nonce="5b960b3d4673be2908666321f64d2bff", uri="rtsp://39.170.35.150:1554/h264/ch0/1", response="3cfc28bcf70670c2120acc3b5d1357d3"
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
Accept: application/sdp
RTSP/1.0 200 OK
CSeq: 4
Content-Type: application/sdp
Content-Base: rtsp://39.170.35.150:1554/h264/ch0/1/
Content-Length: 569
v=0
o=- 1646494795935543 1646494795935543 IN IP4 10.2.144.4
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://39.170.35.150:1554/h264/ch0/1/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://39.170.35.150:1554/h264/ch0/1/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpWoHgCJ+WbgICAgQA==,aO48gA==
a=Media_header:MEDIAINFO=494D4B48010100000400000100000000000000000000000000000000000000000000000000000000;
a=appversion:1.0
4. SETUP
这里得URL后会带上control后的trackID
这里的Transport表示要使用的传输方式, TCP表示使用tcp传输, 也可以使用UDP
SETUP rtsp://39.170.35.150:1554/h264/ch0/1/trackID=1 RTSP/1.0
CSeq: 5
Authorization: Digest username="admin", realm="2857be191e08", nonce="5b960b3d4673be2908666321f64d2bff", uri="rtsp://39.170.35.150:1554/h264/ch0/1/", response="80324f6c8f797633475816843f329b61"
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
RTSP/1.0 200 OK
CSeq: 5
Session: 2007782907;timeout=60
Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=762c0e39;mode="play"
Date: Sat, Mar 05 2022 15:39:55 GMT
5. PLAY
PLAY rtsp://39.170.35.150:1554/h264/ch0/1/ RTSP/1.0
CSeq: 6
Authorization: Digest username="admin", realm="2857be191e08", nonce="5b960b3d4673be2908666321f64d2bff", uri="rtsp://39.170.35.150:1554/h264/ch0/1/", response="f88f707756441a9437f162d68ec5adbb"
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
Session: 2007782907
Range: npt=0.000-
RTSP/1.0 200 OK
CSeq: 6
Session: 2007782907
RTP-Info: url=rtsp://39.170.35.150:1554/h264/ch0/1/trackID=1;seq=40895;rtptime=1378373026
Date: Sat, Mar 05 2022 15:39:56 GMT
后续就会有二进制流进来.
Java实现简单的RTSP报文交换
RTSP端口默认为:1554
通过TCP对目标主机端口发起连接,然后使用RTSP格式的报文进行交换信息即可。
下面是报文拼凑的代码,使用建立TCP连接后安装RTSP规定的通讯顺序发送即可。
// 定义协议头和通用信息
private String transport = "RTP/AVP/TCP;unicast;interleaved=0-1";
private static final String VERSION = " RTSP/1.0";
private static final String RTSP_OK = "RTSP/1.0 200 OK";
private String address = "rtsp://admin:shinemo123@39.170.35.150:1554/h264/ch0/1"; // RTSP URI 包含目标ip和账号密码
private String sessionid; // RTSP 是有状态的,通讯成功后需要记录 sessionId
private void doTeardown()
StringBuilder sb = new StringBuilder();
sb.append("TEARDOWN ");
sb.append(this.address);
sb.append("/");
sb.append(VERSION);
sb.append(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32)");
sb.append(System.lineSeparator());
sb.append("Session: ");
sb.append(sessionid);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
send(sb.toString().getBytes());
private void doPlay()
StringBuilder sb = new StringBuilder();
sb.append("PLAY ");
sb.append(this.address);
sb.append(VERSION);
sb.append(System.lineSeparator());
sb.append("Session: ");
sb.append(sessionid);
sb.append(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("Range: npt=0.000-");
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("Transport: ");
sb.append(transport);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
send(sb.toString().getBytes());
private void doDescribe()
StringBuilder sb = new StringBuilder();
sb.append("DESCRIBE ").append(this.address).append(VERSION).append(System.lineSeparator());
sb.append("Cseq: ").append(seq++).append(System.lineSeparator());
sb.append("Accept: application/sdp").append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("Session: ");
sb.append(sessionid);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
send(sb.toString().getBytes());
完整的垃圾代码:
这里使用的是netty。
package loki.rtsp;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
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";
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 String transport = "RTP/AVP/TCP;unicast;interleaved=0-1";
/** *//** 线程是否结束的标志 */
private AtomicBoolean shutdown;
private int seq=2;
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("端口打开成功" + socketChannel.getLocalAddress() + " -> " + socketChannel.getRemoteAddress());
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();
// doPlay();
System.out.println("PLAY start");
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;
String temp =tmp.substring(tmp.indexOf("trackID"));
trackInfo = temp.split("\\r\\n")[0];
break;
case describe:
String tempSessionId = tmp.substring(tmp.indexOf("Session: ") + 9);
sessionid = tempSessionId.split("\\r\\n")[0];
sessionid = tempSessionId.split(";")[0];
sessionid = sessionid.trim();
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("User-Agent: RealMedia Player HelixDNAClient/10.0.0.11279 (win32)");
sb.append(System.lineSeparator());
sb.append("Session: ");
sb.append(sessionid);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Session: ");
sb.append(sessionid);
sb.append(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("Range: npt=0.000-");
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("Transport: ");
sb.append(transport);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
System.out.println(sb.toString());
send(sb.toString().getBytes());
private void doDescribe()
StringBuilder sb = new StringBuilder();
sb.append("DESCRIBE ").append(this.address).append(VERSION).append(System.lineSeparator());
sb.append("Cseq: ").append(seq++).append(System.lineSeparator());
sb.append("Accept: application/sdp").append(System.lineSeparator());
sb.append(System.lineSeparator());
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(System.lineSeparator());
sb.append("Cseq: ");
sb.append(seq++);
sb.append(System.lineSeparator());
sb.append("Session: ");
sb.append(sessionid);
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
send(sb.toString().getBytes());
System.out.println(sb.toString());
public static void main(String[] args)
try
// RTSPClient(InetSocketAddress remoteAddress,
// InetSocketAddress localAddress, String address)
// rtsp://admin:shinemo123@39.170.35.150:1554/h264/ch0/1
RTSPClient client = new RTSPClient(
new InetSocketAddress("39.170.35.150", 1554),
new InetSocketAddress("10.1.65.48", 0),
"rtsp://admin:shinemo123@39.170.35.150:1554/h264/ch0/1");
client.start();
catch (Exception e)
e.printStackTrace();
用 Java 实现 RTSP 媒体服务器
】用Java实现RTSP媒体服务器【英文标题】:ImplementingRTSPmediaserverinJava【发布时间】:2011-07-2107:43:07【问题描述】:我正在尝试在java中实现一个简单的RTSP服务器,它将使用android手机作为接收客户端。我已经在互联网上搜寻答案并... 查看详情
rtsp协议格式解析(代码片段)
...开发系列文章。rtsp协议开发指南rtsp协议格式解析rtsp协议报文解析-请求行解析rtsp协议报文解析-首部字段解析1RTSP与HTTP协议比较rtsp协议在语法及一些消息参数等方面与http协议类似,这里之所以引入http的相关内容,是为... 查看详情
关于rtsp-over-http
...概述RTSP-Over-HTTP的关键(同时也是全部内容)在于:让RTSP报文通过HTTP端口(即80端口)通信。我们知道RTSP的标准端口是554,但是由于各种不同的防火墙等安全策略配置的原因,客户端在访问554端口时可能存在限制,从而无法正... 查看详情
ffmpeg搭建一个简单的rtsp推流方案(代码片段)
文章目录ffmpeg搭建一个简单的rtsp推流方案基础环境要求开始demoffmpeg搭建一个简单的rtsp推流方案基础环境要求ffmpegffplay/VLCrtsp服务器开始首先我们必须要安装好ffmpeg,不管是源码编译还是直接安装网上都有很多教程,这里不多展开.... 查看详情
unity下如何实现rtmp或rtsp流播放和录制(代码片段)
...RTSP直播播放模块;iOS平台RTMP|RTSP直播播放模块。技术实现本文主要介绍流数据录制,实际上,录制相对直播播放来说,更简单一些,毕竟不需要解码 查看详情
ffmpeg代码实现流媒体推流(rtsp)
...务器,并不符合我的要求。接着我找到另一篇文章:ffmpeg实现H264压缩并且推流至RTSP,这篇文章只有图像编码,并没有音频编码,并且推流之后并没有播放成功。我综合上面两位大佬的思路,和查找一些资料实现了这个功能。RTSP... 查看详情
rtsp协议详解(代码片段)
...RTSP又称为“因特网录像机遥控协议”。1.1.RTSP协议简介要实现RTSP的控制功能,不仅要有协议,而且要有专门的媒体播放器(mediaplayer)和媒体服务器(mediaserver)。媒体服务器与媒体播放器的关系是服务器与客户的关系。媒体... 查看详情
live555编译
项目里面需要简单的rtsp服务器来实现视频预览等功能;rtsp本来不是太复杂的东西,github上有很多功能都比较完善的项目可以随便拿来用,但是测试过程中发现live555还是有性能上的一些差异;具体如下:环境hisi3516cv200sd卡上读取... 查看详情
使用 rtsp 在 java me 中实现视频流
】使用rtsp在javame中实现视频流【英文标题】:Implementvideostreaminginjavameusingrtsp【发布时间】:2012-08-2706:27:09【问题描述】:我想使用rtspurl实现videostreaminginjavame。在设备上测试代码时,我得到媒体异常声明PrefetchError-33。这是我的... 查看详情
浏览器播放rtsp视频流:3rtsp转webrtc播放(代码片段)
...tc,这样我们就可以结合我们之前的cgo+onvif+gSoap实现方案来获取rtsp流,并且可以根据已经实现的ptz、预置点等功能接口做更多的扩展。2.rtsp转webRTC如下是找到的一个比较合适的开源方案,前端使用了jQuery、bootstr... 查看详情
windows平台unitycamera场景实现轻量级rtsp服务和rtmp推送
...开发者对Unity平台下的直播体验提出了更高的要求。技术实现Unity平台下的RTMP推流、RTMP、RTSP播放前几年已经覆盖了Windows、Linux、Android、iOS平台。本文主要介绍Windows平台Unity环境下的轻量级RTSP服务。通过对外提供RTSP拉流URL的形... 查看详情
用easyrtspserver模拟摄像机rtsp流实现rtsp摄像机模拟器
在很早之前的一篇博客《基于EasyIPCamera实现的数字网络摄像机IPCamera的模拟器IPCRTSPSimulator》,我们描述了如何模拟一个摄像机的RTSP流,但当时的RTSP流模拟器还只能读取固定的.264文件,而这种文件是很难找到的,... 查看详情
华为海康大华摄像头编译rtsp转rtmp和httpflv(代码片段)
...rtmp流方案二通过Java+Nginx-http-flv-module+FFmpeg+Flv.js实现rtsp转flv在vue项目中(采用)项目需求要看摄像头实时画面谷歌浏 查看详情
easynvr分发rtsp不标准导致客户端检查报错是什么原因?(代码片段)
...进行简单配置后即可直接使用,无需自建服务器即可实现网页无插件直播。EasyNVR主要是接入RTSP协议的前端设备,并且支持分发包含RTSP在内的多种协议视频流。有客户反馈EasyNVR的rtsp分发流不标准,如下图抓包所示... 查看详情
RTSP 隧道 HTTP、FFMPEG
...将control_transport设置为RTSP_MODE_TUNNEL来支持它。我的问题很简单,我该如何用 查看详情
easynvr如何获取rtsp格式的视频流?三种获取rtsp地址方式介绍
...xff0c;放到其他平台使用。其实这个需求可以直接在平台内实现,EasyNVR中提供了获取RTSP流的功能,本文我们就介绍一下具体操作方法。首先最简单的方法是在EasyNVR视频播放页面F12可以查看到当前播放的信息 查看详情
用easyrtspserver模拟摄像机rtsp流实现rtsp摄像机模拟器
在很早之前的一篇博客《基于EasyIPCamera实现的数字网络摄像机IPCamera的模拟器IPCRTSPSimulator》,我们描述了如何模拟一个摄像机的RTSP流,但当时的RTSP流模拟器还只能读取固定的.264文件,而这种文件是很难找到的,... 查看详情
java示例代码_从pcap文件中筛选rtsp数据包
java示例代码_从pcap文件中筛选rtsp数据包 查看详情