Kaynağa Gözat

更新README

648540858 2 yıl önce
ebeveyn
işleme
4babf2b47b

+ 60 - 101
README.md

@@ -1,4 +1,4 @@
-![logo](https://raw.githubusercontent.com/648540858/wvp-GB28181-pro/wvp-28181-2.0/web_src/static/logo.png)
+![logo](doc/_media/logo.png)
 # 开箱即用的28181协议视频平台
 
 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit)
@@ -17,7 +17,7 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网
 # 应用场景:
 支持浏览器无插件播放摄像头视频。  
 支持摄像机、平台、NVR等设备接入。 
-支持国标级联。  
+支持国标级联。多平台级联。跨网视频预览。
 支持rtsp/rtmp等视频流转发到国标平台。  
 支持rtsp/rtmp等推流转发到国标平台。  
 
@@ -31,62 +31,49 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网
 https://gitee.com/pan648540858/wvp-GB28181-pro.git
 
 # 截图
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/101513_79632720_1018729.png "2022-03-04_09-51.png")
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/103025_5df016f9_1018729.png "2022-03-04_10-27.png")
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/101706_088fbafa_1018729.png "2022-03-04_09-52_1.png")
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/101756_3d662828_1018729.png "2022-03-04_10-00_1.png")
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/101823_19050c66_1018729.png "2022-03-04_10-12_1.png")
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/101848_e5a39557_1018729.png "2022-03-04_10-12_2.png")
-![build_1.png](https://images.gitee.com/uploads/images/2022/0304/101919_ee5b8c79_1018729.png "2022-03-04_10-13.png")
-
-# 1.0 基础特性  
-1. 视频预览;  
-2. 云台控制(方向、缩放控制);  
-3. 视频设备信息同步;   
-4. 离在线监控;  
-5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作);  
-6. 无人观看自动断流;    
-7. 支持UDP和TCP两种国标信令传输模式; 
-8. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署;   
-9. 支持平台接入, 针对大平台大量设备的情况进行优化;  
-10. 支持检索,通道筛选;  
-11. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;  
-12. 支持启用udp多端口模式, 提高udp模式下媒体传输性能;  
-13. 支持通道是否含有音频的设置;  
-14. 支持通道子目录查询;  
-15. 支持udp/tcp国标流传输模式;  
-16. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址  
-17. 支持国标网络校时  
-18. 支持公网部署, 支持wvp与zlm分开部署   
-19. 支持播放h265, g.711格式的流(需要将closeWaitRTPInfo设为false)
-20. 报警信息处理,支持向前端推送报警信息
-
-# 1.0 新支持特性  
-1. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署;   
-2. 支持平台接入, 针对大平台大量设备的情况进行优化;  
-3. 支持检索,通道筛选;  
-4. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;  
-5. 支持启用udp多端口模式, 提高udp模式下媒体传输性能;  
-6. 支持通道是否含有音频的设置;  
-7. 支持通道子目录查询;  
-8. 支持udp/tcp国标流传输模式;  
-9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址  
-10. 支持国标网络校时  
-11. 支持公网部署, 支持wvp与zlm分开部署   
-12. 支持播放h265, g.711格式的流   
-13. 支持固定流地址和自动点播,同时支持未点播时直接播放流地址,代码自动发起点播.  ( [查看WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E5%9B%BA%E5%AE%9A%E6%92%AD%E6%94%BE%E5%9C%B0%E5%9D%80%E4%B8%8E%E8%87%AA%E5%8A%A8%E7%82%B9%E6%92%AD))
-14. 报警信息处理,支持向前端推送报警信息
-15. 支持订阅与通知方法
-   -  [X] 移动位置订阅
-   -  [X] 移动位置通知处理
-   -  [X] 报警事件订阅
-   -  [X] 报警事件通知处理
-   -  [X] 设备目录订阅
-   -  [X] 设备目录通知处理
-16. 移动位置查询和显示,可通过配置文件设置移动位置历史是否存储
-
-# 2.0 支持特性
-- [X] 国标通道向上级联
+![index](doc/_media/index.png "index.png")
+![2](doc/_media/2.png "2.png")
+![3](doc/_media/3.png "3.png")
+![3-1](doc/_media/3-1.png "3-1.png")
+![3-2](doc/_media/3-2.png "3-2.png")
+![3-3](doc/_media/3-3.png "3-3.png")
+![build_1](https://images.gitee.com/uploads/images/2022/0304/101919_ee5b8c79_1018729.png "2022-03-04_10-13.png")
+
+# 功能特性 
+-  [X] 集成web界面
+-  [X] 兼容性良好
+-  [X] 支持电子地图,支持接入WGS84和GCJ02两种坐标系,并且自动转化为合适的坐标系进行展示和分发
+-  [X] 接入设备
+  -  [X] 视频预览
+  -  [X] 无限制接入路数,能接入多少设备只取决于你的服务器性能
+  -  [X] 云台控制,控制设备转向,拉近,拉远
+  -  [X] 预置位查询,使用与设置
+  -  [X] 查询NVR/IPC上的录像与播放,支持指定时间播放与下载
+  -  [X] 无人观看自动断流,节省流量
+  -  [X] 视频设备信息同步
+  -  [X] 离在线监控
+  -  [X] 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址
+  -  [X] 支持通过一个流地址直接观看摄像头,无需登录以及调用任何接口
+  -  [X] 支持UDP和TCP两种国标信令传输模式
+  -  [X] 支持UDP和TCP两种国标流传输模式
+  -  [X] 支持检索,通道筛选
+  -  [X] 支持通道子目录查询
+  -  [X] 支持过滤音频,防止杂音影响观看
+  -  [X] 支持国标网络校时
+  -  [X] 支持播放H264和H265
+  -  [X] 报警信息处理,支持向前端推送报警信息
+  -  [X] 支持订阅与通知方法
+    -  [X] 移动位置订阅
+    -  [X] 移动位置通知处理
+    -  [X] 报警事件订阅
+    -  [X] 报警事件通知处理
+    -  [X] 设备目录订阅
+    -  [X] 设备目录通知处理
+  -  [X] 移动位置查询和显示
+  - [X] 支持手动添加设备和给设备设置单独的密码
+-  [X] 支持平台对接接入
+-  [X] 支持国标级联
+  - [X] 国标通道向上级联
     - [X] WEB添加上级平台
     - [X] 注册
     - [X] 心跳保活
@@ -101,61 +88,33 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
     - [X] 目录订阅与通知
     - [X] 录像查看与播放
     - [X] GPS订阅与通知(直播推流)
-- [X] 支持手动添加设备和给设备设置单独的密码
-- [X] 添加RTSP视频
-- [X] 添加接口鉴权
-- [X] 添加RTMP视频
-- [X] 云端录像(需要部署单独服务配合使用)
-- [X] 多流媒体节点,自动选择负载最低的节点使用。
-- [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。
-- [X] 支持电子地图。
-- [X] 支持接入WGS84和GCJ02两种坐标系。
-
-[//]: # (# docker快速体验)
-
-[//]: # (目前作者的docker-compose因为时间有限维护不及时,这里提供第三方提供的供大家使用,维护不易,大家记得给这位小伙伴点个star。  )
-
-[//]: # (https://github.com/SaltFish001/wvp_pro_compose)
-
-[//]: # ([https://github.com/SaltFish001/wvp_pro_compose](https://github.com/SaltFish001/wvp_pro_compose))
-
-[//]: # (这是作者维护的一个镜像,可能存在不及时的问题。)
-
-[//]: # (```shell)
-
-[//]: # (docker pull 648540858/wvp_pro)
-
-[//]: # ()
-[//]: # (docker run  --env WVP_IP="你的IP" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro)
-
-[//]: # (```)
-
-[//]: # (docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro))
-
-# gitee同步仓库
-https://gitee.com/pan648540858/wvp-GB28181-pro.git  
-
-# 遇到问题
+-  [X] 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;  
+-  [X] 多流媒体节点,自动选择负载最低的节点使用。
+-  [X] 支持启用udp多端口模式, 提高udp模式下媒体传输性能;
+-  [X] 支持公网部署; 
+-  [X] 支持wvp与zlm分开部署,提升平台并发能力
+-  [X] 支持拉流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台
+-  [X] 支持推流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台
+-  [X] 支持推流鉴权
+-  [X] 支持接口鉴权
+-  [X] 云端录像,推流/代理/国标视频绝可以录制在云端服务器,支持预览和下载
+
+
+# 遇到问题如何解决
 国标最麻烦的地方在于设备的兼容性,所以需要大量的设备来测试,目前作者手里的设备有限,再加上作者水平有限,所以遇到问题在所难免;
 1. 查看wiki,仔细的阅读可以帮你避免几乎所有的问题
 2. 搜索issues,这里有大部分的答案
-3. 加QQ群,这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。
+3. 加QQ群(901799015),这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。
 4. 你可以请作者为你解答,但是我不是免费的。
 5. 你可以把遇到问题的设备寄给我,可以更容易的复现问题。
 
-
-# 合作
-目前很多打着合作的幌子来私聊的,其实大家大可不必,目前作者没有精力,你有问题可以付费找我解答,也可以提PR
-,如果对代码有建议可以提ISSUE;也可以加群一起聊聊。我们欢迎所有有兴趣参与到项目中来的人。
-
-
-
 # 使用帮助
 QQ群: 901799015, ZLM使用文档[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)  
 QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对你有帮助,欢迎star和提交pr。
 
 # 授权协议
 本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议
+
 # 致谢
 感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架,并在开发过程中给予支持与帮助。     
 感谢作者[dexter langhuihui](https://github.com/langhuihui) 开源这么好用的WEB播放器。     

BIN
doc/_media/2.png


BIN
doc/_media/3-1.png


BIN
doc/_media/3-2.png


BIN
doc/_media/3-3.png


BIN
doc/_media/3.png


BIN
doc/_media/index.png


+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java

@@ -70,7 +70,7 @@ public class VideoStreamSessionManager {
 			stream ="*";
 		}
 		String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream;
-		List<Object> scanResult = RedisUtil.scan(key);
+		List<Object> scanResult = RedisUtil.scan(key, 1);
 		if (scanResult.size() == 0) {
 			return null;
 		}

+ 9 - 9
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@@ -75,7 +75,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
     @Override
     public void resetAllSN() {
         String scanKey = VideoManagerConstants.SIP_SN_PREFIX  + userSetting.getServerId() + "_*";
-        List<Object> keys = RedisUtil.scan(scanKey);
+        List<Object> keys = RedisUtil.scan(scanKey, null);
         for (Object o : keys) {
             String key = (String) o;
             RedisUtil.set(key, 1);
@@ -129,7 +129,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
     }
     @Override
     public StreamInfo queryPlayByStreamId(String streamId) {
-        List<Object> playLeys = RedisUtil.scan(String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), streamId));
+        List<Object> playLeys = RedisUtil.scan(String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), streamId), 1);
         if (playLeys == null || playLeys.size() == 0) {
             return null;
         }
@@ -141,7 +141,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
         List<Object> playLeys = RedisUtil.scan(String.format("%S_%s_*_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
                 userSetting.getServerId(),
                 deviceId,
-                channelId));
+                channelId), 1);
         if (playLeys == null || playLeys.size() == 0) {
             return null;
         }
@@ -278,7 +278,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
                 stream,
                 callId
         );
-        List<Object> streamInfoScan = RedisUtil.scan(key);
+        List<Object> streamInfoScan = RedisUtil.scan(key, 1);
         if (streamInfoScan.size() > 0) {
             return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0));
         }else {
@@ -310,7 +310,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
                 stream,
                 callId
         );
-        List<Object> streamInfoScan = RedisUtil.scan(key);
+        List<Object> streamInfoScan = RedisUtil.scan(key, 1);
         return (String) streamInfoScan.get(0);
     }
 
@@ -399,7 +399,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
                 + channelId + "_"
                 + streamId + "_"
                 + callId;
-        List<Object> scan = RedisUtil.scan(key);
+        List<Object> scan = RedisUtil.scan(key, 1);
         if (scan.size() > 0) {
             return (SendRtpItem)RedisUtil.get((String)scan.get(0));
         }else {
@@ -521,7 +521,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
         String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
                 + userSetting.getServerId() + "_*_*_"
                 + channelId + "*_" + "*_";
-        List<Object> RtpStreams = RedisUtil.scan(key);
+        List<Object> RtpStreams = RedisUtil.scan(key, 1);
         if (RtpStreams.size() > 0) {
             return true;
         } else {
@@ -613,7 +613,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
                 stream,
                 callId
         );
-        List<Object> streamInfoScan = RedisUtil.scan(key);
+        List<Object> streamInfoScan = RedisUtil.scan(key, 1);
         if (streamInfoScan.size() > 0) {
             return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0));
         }else {
@@ -732,7 +732,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
         String scanKey = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX  + userSetting.getServerId() + "_*_" + app + "_" + streamId + "_" + mediaServerId;
 
         OnStreamChangedHookParam result = null;
-        List<Object> keys = RedisUtil.scan(scanKey);
+        List<Object> keys = RedisUtil.scan(scanKey, 1);
         if (keys.size() > 0) {
             String key = (String) keys.get(0);
             result = (OnStreamChangedHookParam)RedisUtil.get(key);

+ 18 - 6
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

@@ -1,14 +1,13 @@
 package com.genersoft.iot.vmp.utils.redis;
 
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-
 import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
-import gov.nist.javax.sip.stack.UDPMessageChannel;
 import org.springframework.data.redis.core.*;
 import org.springframework.util.CollectionUtils;
 
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
 /**    
  * Redis工具类
  * @author swwheihei
@@ -865,12 +864,16 @@ public class RedisUtil {
      * @param query 查询参数
      * @return
      */
-    public static List<Object> scan(String query) {
+    public static List<Object> scan(String query, Integer count) {
         if (redisTemplate == null) {
             redisTemplate = SpringBeanFactory.getBean("redisTemplate");
         }
         Set<String> resultKeys = (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
-            ScanOptions scanOptions = ScanOptions.scanOptions().match("*" + query + "*").count(1000).build();
+            ScanOptions.ScanOptionsBuilder match = ScanOptions.scanOptions().match("*" + query + "*");
+            if (count != null) {
+                match.count(count);
+            }
+            ScanOptions scanOptions = match.build();
             Cursor<byte[]> scan = connection.scan(scanOptions);
             Set<String> keys = new HashSet<>();
             while (scan.hasNext()) {
@@ -883,6 +886,15 @@ public class RedisUtil {
         return new ArrayList<>(resultKeys);
     }
 
+    /**
+     * 模糊查询
+     * @param query 查询参数
+     * @return
+     */
+    public static List<Object> scan(String query) {
+        return scan(query, null);
+    }
+
     //    ============================== 消息发送与订阅 ==============================
     public static void convertAndSend(String channel, JSONObject msg) {
         if (redisTemplate == null) {