文章目录
- 前言
- 摄像头预览
-
- 1.什么是rtsp
-
- 1.1 了解海康威视rtsp的url规范
- 1.2 下载(VLC media player)[VLC](https://www.videolan.org/)多媒体播放器
- 2.用FFmpeg+Nginx-rtmp推流
-
- 2.1 使用FFmpeg工具+Nginx-rtmp模块
- 2.2 下载FFmpeg
- 2.3 在Java里面操作FFmpeg
- 2.4 在Nginx中配置rtmp模块
- 2.5 得到rtmp流
- 2.6 关闭推流
- 3.用FFmpeg+Nginx-FLV推流
-
- 3.1 Nginx中配置flv模块
- 3.2 返回的http地址
- 4.后续优化
-
- 4.1 前端解决方案
- 4.2 后端解决方案
前言
本文是之前文章的一篇完善文,如果你是才接触海康威视摄像头的二次开发请先阅读入门篇
摄像头预览
1.什么是rtsp
在实现摄像头的预览的时候需要搞懂什么是rtsp。
rtsp是一种实时流传输协议(Real Time Streaming Protocol,RTSP),主要使用TCP和UDP完成数据的传输。
1.1 了解海康威视rtsp的url规范
【老版本】URL规定:
rtsp://username:password@[ipaddress]/[videotype]/ch[number]/[streamtype]
注:VLC可以支持解析URL里的用户名密码,实际发给设备的RTSP请求不支持带用户名密码。
详细描述:
举例说明:
通道01主码流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch01/main/av_stream
通道01子码流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch01/sub/av_stream
通道01第3码流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch01/stream3/av_stream
IP通道01的主码流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch33/main/av_stream
IP通道01的子码流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch33/sub/av_stream
零通道主码流(零通道无子码流):
rtsp://admin:test1234@172.6.22.106:554/h264/ch0/main/av_stream
注:老版本URL,64路以下的NVR的IP通道的通道号从33开始,64路以及以上路数的NVR的IP通道的通道号从1开始。
【新版本】URL规定:
rtsp://username:password@[address]:[port]/Streaming/Channels/[id](?parm1=value1&parm2-=value2…)
注:VLC可以支持解析URL里的用户名密码,实际发给设备的RTSP请求不支持带用户名密码。
详细描述:
举例说明:
通道01主码流:
rtsp://admin:abc12345@172.6.22.234:554/Streaming/Channels/101?transportmode=unicast
通道01子码流:
rtsp://admin:abc12345@172.6.22.234:554/Streaming/Channels/102?transportmode=unicast(单播)
rtsp://admin:abc12345@172.6.22.106:554/Streaming/Channels/102?transportmode=multicast (多播)
rtsp://admin:abc12345@172.6.22.106:554/Streaming/Channels/102 (?后面可省略,默认单播)
通道01第3码流:
rtsp://admin:abc12345@172.6.22.234:554/Streaming/Channels/103?transportmode=unicast
零通道主码流(零通道无子码流):
rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/001
注:新版本URL,通道号全部按顺序从1开始。
针对上面的单播多播做一个解释
单播:用网络技术的术语来描述就是“单播”,此时信息的接收和传递只在两个节点之间进行,
网络节点之间的通信就好像是人们之间的对话一样。如果一个人对另外一个人说话。
多播:多播也称为“组播”:将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
大概了解了rtsp和海康威视的url规范之后我们来拼接一个可以播放的地址。
1.2 下载(VLC media player)VLC多媒体播放器
能成功播放就说明成功了
注:没有成功的可以检查一下摄像头的状态和rtsp地址
2.用FFmpeg+Nginx-rtmp推流
2.1 使用FFmpeg工具+Nginx-rtmp模块
上面我们说到了rtsp,但是这种格式前端是不能直接播放的。
所以我们需要使用FFmpeg把流进行转换,再用Nginx-rtmp模块把流推到我们要播放的地址。
逻辑比较简单,但是这里需要用到2个插件。
注:用这种方式推出来的流是rtmp格式的流,需要Flash插件,后面写一个不需要Flash插件的
这种方式的好处是延迟低可能只有0.2延迟左右,缺点是需要Flash的插件支持。
2.2 下载FFmpeg
下载稳定版的
下载解压到指定目录
配置ffmpeg的环境变量
计算机右键属性——>高级系统设置——>环境变量——>系统变量——>Path——>放入ffmpeg解压之后的目录的bin文件夹地址
输入下面的命令
ffmpeg -version
2.3 在Java里面操作FFmpeg
这里需要下载一个三方集成的jar包,来操作和执行FFmpeg命令。
阿里云盘地址:https://www.aliyundrive.com/s/zjEKF198tNE
提取码:2vn4
这里是下载解压之后的存放jar包的位置,jar包中配置文件的路径是作者的FFmpeg的安装路径。
注:可以更改源码自行编译jar包。
或者在自己的Springboot项目中建一个loadFFmpeg.properties配置文件来覆盖
这里也是建议把这个jar包打进自己的maven仓库,然后引入这个jar包。
注:这三个方法需要注意,后面会用
因为这个第三方包默认是以udp推流的,所以需要修改源码中的CommandAssemblyImpl类
2.4 在Nginx中配置rtmp模块
这里有一个源码地址,需要自己编译(有能力的可以在windows自行编译)
https://github.com/arut/nginx-rtmp-module
这个是已经编译好的(但是版本不是最新的)
https://github.com/illuspas/nginx-rtmp-win32
打开文件夹找到配置文件
重点注意这里的配置
2.5 得到rtmp流
复习一下前面的流程
rtsp流——>ffmpeg+Nginx-rtmp——>rtmp流——>前端播放
下面的方法会涉及到SDK的一些接口,没看过的可以看之前的入门篇
之前的入门篇讲过,在需要执行操作的时候都需要登录摄像头。
所以这里的这个获取通道号的时候前面是需要登录的,方法中的lUserID就是登录接口的返回值
注:通道号可以理解为相机的标识,用来区分调用的是哪一个摄像头
/**
* 获取相机通道号
*/
public int getChannelNumber() {
//获取IP接入配置参数
IntByReference ibrBytesReturned = new IntByReference(0);
boolean bRet;
int iChannelNum = -1;
ipparacfg = new HCNetSDK.NET_DVR_IPPARACFG();
ipparacfg.write();
Pointer lpIpParaConfig = ipparacfg.getPointer();
bRet = hcNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_IPPARACFG,
0, lpIpParaConfig, ipparacfg.size(), ibrBytesReturned);
ipparacfg.read();
String devices = "";
if (!bRet) {
//设备不支持,则表示没有IP通道
for (int iChannum = 0; iChannum < deviceiInfo.byChanNum; iChannum++) {
devices = "Camera" + (iChannum + deviceiInfo.byStartChan);
}
} else {
for (int iChannum = 0; iChannum < HCNetSDK.MAX_IP_CHANNEL; iChannum++) {
if (ipparacfg.struIPChanInfo[iChannum].byEnable == 1) {
devices = "IPCamera" + (iChannum + deviceiInfo.byStartChan);
}
}
}
if (StringUtils.isNotEmpty(devices)) {
//Camara开头表示模拟通道
if (devices.charAt(0) == 'C') {
//子字符串中获取通道号
iChannelNum = Integer.parseInt(devices.substring(6));
} else {
//IPCamara开头表示IP通道
if (devices.charAt(0) == 'I') {
//子字符创中获取通道号,IP通道号要加32
iChannelNum = Integer.parseInt(devices.substring(8)) + 32;
} else {
log.info("获取通道号失败");
}
}
}
return iChannelNum;
}
推流的方法
private static final FFmpegManager manager =new FFmpegManagerImpl();
/**
* 开始推流
* @param appName 进程名称,为相机ip去"."
* @param account 登录相机账号
* @param password 密码
* @param ip 相机ip
* @param channelNumber 相机的通道号
* @return String appName
*/
public static String startPlugFlow(String appName, String account, String password, String ip, int channelNumber) {
//如果进程存在,则直接返回进程名
if (MinitorUtil.taskerIsRun(appName)) {
return appName;
}
Map<String, String> map = new HashMap<>(10);
//尝试用TCP方式 进程名
map.put("appName", appName);
// 自定义的参数,需要修改ffmpeg源代码
map.put("rtspTransport", "tcp");
//组装rtsp流
map.put("input", "rtsp://" + account + ":" + password + "@" + ip + "/Streaming/Channels" + channelNumber);
//rtmp流.live为nginx-rtmp的配置
map.put("output", "rtmp://" + "本机ip" + ":1935/live/");
map.put("codec", "copy");
//只推元码流
map.put("twoPart", "1");
map.put("fmt", "flv");
// 执行任务,就是appName,如果执行失败返回为null
return manager.start(map);
}
注:上面这个方法执行之前需要启动Nginx,控制台会打印开始推流的命令,
命令执行无误并一直输出红色的推流信息,说明是成功的。
执行完命令之后组装rtmp的url地址
String rtmpurl="rtmp://" + "本机ip" + ":1935/live/" + appName);
这个地址就是最后的rtmp地址了,同样的使用VLC播放器进行测试。
能够正确播放成功说明是没有问题的。
2.6 关闭推流
3.用FFmpeg+Nginx-FLV推流
3.1 Nginx中配置flv模块
阿里云盘地址:https://www.aliyundrive.com/s/oK9SgRquVvp
提取码:gy20
这里也是一个编译好的模块了。
大概方式和上一种是差不多的
private static final FFmpegManager manager =new FFmpegManagerImpl();
/**
* 开始推流
* @param appName 进程名称,为相机ip去"."
* @param account 登录相机账号
* @param password 密码
* @param ip ip 相机ip
* @param nginxIp nginxIp nginx的ip
* @param nginxPort nginxIp nginx的端口
* @param channelNumber 相机的通道号
* @return String appName
*/
public static String startPlugFlow(String appName, String account, String password, String ip,
String nginxIp, String nginxPort, int channelNumber) {
//如果进程存在,则直接返回进程名
if (MinitorUtil.taskerIsRun(appName)) {
return appName;
}
Map<String, String> map = new HashMap<>(10);
//尝试用TCP方式 进程名
map.put("appName", appName);
// 自定义的参数,需要修改ffmpeg源代码
map.put("rtspTransport", "tcp");
//组装rtsp流
map.put("input", "rtsp://" + account + ":" + password + "@" + ip + "/Streaming/Channels" + channelNumber);
//rtmp流.live为nginx-rtmp的配置
map.put("output", "rtmp://" + nginxIp + ":" + nginxPort + "/live/");
map.put("codec", "copy");
//只推元码流
map.put("twoPart", "1");
map.put("fmt", "flv");
// 执行任务,就是appName,如果执行失败返回为null
return manager.start(map);
}
注:上面这个方法执行之前需要启动Nginx,控制台会打印开始推流的命令,
命令执行无误并一直输出红色的推流信息,说明是成功的。
3.2 返回的http地址
执行完命令之后组装rtmp的url地址
String httpUrl="https://" + "nginxIp" + ":" + "nginxPort" + "/live?port=1935&app=live&stream=" + appName);
成功推流
成功播放
关闭推流:这里的流程和上面是一样的
注:这种方式的优点是不需要Flash插件就能播放
但是存在的问题是延迟较大接近8s左右的延迟和丢包的风险。
4.后续优化
4.1 前端解决方案
前端下载海康威视的前端开发包
4.2 后端解决方案
- 优化FFmpeg的命令和方法。更新并编译更高Nginx的版本。
- 使用WVP+流媒体服务器实现推流。