Commit 8b0ff3767b23f9479a0b3ebe8e343ed6470bd194

Authored by 648540858
2 parents cfed8606 a88518f8

Merge branch 'wvp-28181-2.0'

# Conflicts:
#	sql/update.sql
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
Showing 204 changed files with 4644 additions and 1894 deletions
README.md
1 1 ![logo](https://raw.githubusercontent.com/648540858/wvp-GB28181-pro/wvp-28181-2.0/web_src/static/logo.png)
2   -# 寮绠卞嵆鐢ㄧ殑8181鍗忚瑙嗛骞冲彴
  2 +# 寮绠卞嵆鐢ㄧ殑28181鍗忚瑙嗛骞冲彴
3 3  
4 4 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit)
5 5 [![license](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/xia-chu/ZLMediaKit/blob/master/LICENSE)
... ... @@ -25,7 +25,7 @@ WEB VIDEO PLATFORM鏄竴涓熀浜嶨B28181-2016鏍囧噯瀹炵幇鐨勫紑绠卞嵆鐢ㄧ殑缃戠
25 25 鏃ㄥ湪鎵撻犱竴涓槗閰嶇疆,鏄撲娇鐢,渚夸簬缁存姢鐨8181鍥芥爣淇′护绯荤粺, 渚濇墭浼樼鐨勫紑婧愭祦濯掍綋鏈嶅姟妗嗘灦ZLMediaKit, 瀹炵幇涓涓畬鏁存槗鐢℅B28181骞冲彴.
26 26  
27 27 # 閮ㄧ讲鏂囨。
28   -[https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki)
  28 +[doc.wvp-pro.cn](https://doc.wvp-pro.cn)
29 29  
30 30 # gitee鍚屾浠撳簱
31 31 https://gitee.com/pan648540858/wvp-GB28181-pro.git
... ...
bootstrap.sh 100755 → 100644
doc/README.md 0 → 100644
  1 +# 介绍
  2 +
  3 +> 开箱即用的28181协议视频平台
  4 +
  5 +# 概述
  6 +- WVP-PRO基于GB/T 28181-2016标准实现的流媒体平台,依托优秀的开源流媒体服务[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit),提供完善丰富的功能。
  7 +- GB/T 28181-2016 中文标准名称是《公共安全视频监控联网系统信息传输、交换、控制技术要求》是监控领域的国家标准。大量应用于政府视频平台。
  8 +- 通过28181协议你可以将IPC摄像头接入平台,可以观看也可以使用28181/rtsp/rtmp/flv等协议将视频流分发到其他平台。
  9 +
  10 +# 特性
  11 +- 实现标准的28181信令,兼容常见的品牌设备,比如海康、大华、宇视等品牌的IPC、NVR以及平台。
  12 +- 支持将国标设备级联到其他国标平台,也支持将不支持国标的设备的图像或者直播推送到其他国标平台
  13 +- 前端完善,自带完整前端页面,无需二次开发可直接部署使用。
  14 +- 完全开源,且使用MIT许可协议。保留版权的情况下可以用于商业项目。
  15 +- 支持多流媒体节点负载均衡。
  16 +
  17 +# 我们实现了哪些国标功能
  18 +**作为上级平台**
  19 +- [X] 注册
  20 +- [X] 注销
  21 +- [X] 实时视音频点播
  22 +- [X] 设备控制
  23 + - [X] 云台控制
  24 + - [ ] 远程启动
  25 + - [ ] 录像控制
  26 + - [ ] 报警布防/撤防
  27 + - [ ] 报警复位
  28 + - [X] 强制关键帧
  29 + - [ ] 拉框放大
  30 + - [ ] 拉框缩小
  31 + - [ ] 看守位控制
  32 + - [ ] 设备配置
  33 +- [X] 报警事件通知和分发
  34 +- [X] 设备目录订阅
  35 +- [X] 网络设备信息查询
  36 + - [X] 设备目录查询
  37 + - [X] 设备状态查询
  38 + - [ ] 设备配置查询
  39 + - [ ] 设备预置位查询
  40 +- [X] 状态信息报送
  41 +- [X] 设备视音频文件检索
  42 +- [X] 历史视音频的回放
  43 + - [X] 播放
  44 + - [X] 暂停
  45 + - [X] 进/退
  46 + - [X] 停止
  47 +- [X] 视音频文件下载
  48 +- [X] 校时
  49 +- [X] 订阅和通知
  50 + - [X] 事件订阅
  51 + - [X] 移动设备位置订阅
  52 + - [X] 报警订阅
  53 + - [X] 目录订阅
  54 +- [ ] 语音广播
  55 +- [ ] 语音对讲
  56 +
  57 +**作为下级平台**
  58 +- [X] 注册
  59 +- [X] 注销
  60 +- [X] 实时视音频点播
  61 +- [ ] 设备控制
  62 + - [ ] 云台控制
  63 + - [ ] 远程启动
  64 + - [ ] 录像控制
  65 + - [ ] 报警布防/撤防
  66 + - [ ] 报警复位
  67 + - [ ] 强制关键帧
  68 + - [ ] 拉框放大
  69 + - [ ] 拉框缩小
  70 + - [ ] 看守位控制
  71 + - [ ] 设备配置
  72 +- [ ] 报警事件通知和分发
  73 +- [X] 设备目录订阅
  74 +- [X] 网络设备信息查询
  75 + - [X] 设备目录查询
  76 + - [X] 设备状态查询
  77 + - [ ] 设备配置查询
  78 + - [ ] 设备预置位查询
  79 +- [X] 状态信息报送
  80 +- [X] 设备视音频文件检索
  81 +- [X] 历史视音频的回放
  82 + - [X] 播放
  83 + - [x] 暂停
  84 + - [x] 进/退
  85 + - [x] 停止
  86 +- [ ] 视音频文件下载
  87 +- [ ] ~~校时~~
  88 +- [X] 订阅和通知
  89 + - [X] 事件订阅
  90 + - [X] 移动设备位置订阅
  91 + - [ ] 报警订阅
  92 + - [X] 目录订阅
  93 +- [ ] 语音广播
  94 +- [ ] 语音对讲
  95 +
  96 +
  97 +
  98 +
  99 +# 社区
  100 +代码目前托管在GitHub和Gitee,Gitee目前作为加速仓库使用,不接受issue。
  101 +GitHub: [https://github.com/648540858/wvp-GB28181-pro](https://github.com/648540858/wvp-GB28181-pro)
  102 +Gitee: [https://gitee.com/pan648540858/wvp-GB28181-pro](https://gitee.com/pan648540858/wvp-GB28181-pro)
0 103 \ No newline at end of file
... ...
doc/_content/ability/_media/cascade1.png 0 → 100644

37.4 KB

doc/_content/ability/_media/cascade2.png 0 → 100644

162 KB

doc/_content/ability/_media/cascade3.png 0 → 100644

129 KB

doc/_content/ability/_media/cascade4.png 0 → 100644

119 KB

doc/_content/ability/_media/img.png 0 → 100644

13.1 KB

doc/_content/ability/_media/img_1.png 0 → 100644

17.1 KB

doc/_content/ability/_media/img_10.png 0 → 100644

65.1 KB

doc/_content/ability/_media/img_11.png 0 → 100644

80.5 KB

doc/_content/ability/_media/img_12.png 0 → 100644

66.2 KB

doc/_content/ability/_media/img_13.png 0 → 100644

58.1 KB

doc/_content/ability/_media/img_14.png 0 → 100644

692 Bytes

doc/_content/ability/_media/img_15.png 0 → 100644

98.9 KB

doc/_content/ability/_media/img_16.png 0 → 100644

41.5 KB

doc/_content/ability/_media/img_17.png 0 → 100644

56.3 KB

doc/_content/ability/_media/img_18.png 0 → 100644

64.6 KB

doc/_content/ability/_media/img_2.png 0 → 100644

41.1 KB

doc/_content/ability/_media/img_3.png 0 → 100644

16.2 KB

doc/_content/ability/_media/img_4.png 0 → 100644

136 KB

doc/_content/ability/_media/img_5.png 0 → 100644

152 KB

doc/_content/ability/_media/img_6.png 0 → 100644

62.2 KB

doc/_content/ability/_media/img_7.png 0 → 100644

46.3 KB

doc/_content/ability/_media/img_8.png 0 → 100644

43.8 KB

doc/_content/ability/_media/img_9.png 0 → 100644

80.1 KB

doc/_content/ability/auto_play.md 0 → 100644
  1 +<!-- 自动点播 -->
  2 +# 自动点播
... ...
doc/_content/ability/cascade.md 0 → 100644
  1 +<!-- 国标级联的使用 -->
  2 +# 国标级联的使用
  3 +国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。
  4 +## 1 接入平台
  5 +### 1.1 wvp-pro
  6 +#### 1.1.1 wvp-pro管理页面点击添加
  7 + ![cascade1](_media/cascade1.png)
  8 +#### 1.1.2 填入wvp-pro上级平台信息
  9 + ![cascade1](_media/img_4.png)
  10 + ![cascade1](_media/img_5.png)
  11 +#### 1.1.3 编辑wvp-pro上级设备信息,开启订阅
  12 + ![cascade1](_media/img_6.png)
  13 +### 1.2 大华平台
  14 +### 1.3 海康平台
  15 +### 1.4 liveGBS
  16 +#### 1.4.1. wvp-pro管理页面点击添加
  17 + ![添加](_media/cascade1.png)
  18 +#### 1.4.2. 填入liveGBS平台信息
  19 + ![填入liveGBS平台信息1](_media/cascade2.png)
  20 + ![填入liveGBS平台信息2](_media/cascade3.png)
  21 +#### 1.4.3. 编辑liveGBS设备信息,开启目录订阅
  22 + ![cascade1](_media/cascade4.png)
  23 +#### 1.4.4. 编辑liveGBS设备信息,开启GPS订阅
  24 + ![cascade1](_media/img_7.png)
  25 +
  26 +## 2 添加目录与通道
  27 +1. 级联平台添加目录信息
  28 + ![cascade1](_media/img_1.png)
  29 +2. 为目录添加通道
  30 + ![cascade1](_media/img_2.png)
  31 +3. 设置默认流目录
  32 +如果需要后续自动生成的流信息都在某一个节点下,可以在对应节点右键设置为默认
  33 + ![cascade1](_media/img_3.png)
  34 +
... ...
doc/_content/ability/cascade2.md 0 → 100644
  1 +<!-- 国标级联的使用 -->
  2 +# 国标级联的使用
  3 +国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。
  4 +## 添加上级平台
  5 +在国标级联页面点击“添加”按钮,以推送到上级WVP为例子,参看[接入设备](./_content/ability/device.md)
  6 +![cascade17](_media/img_17.png)
  7 +点击保存可以在上级的国标通道列表看到新增加的设备;
  8 +国标级联列表出现了级联的这个平台;同时状态显示为在线,如果状态为离线那么可能是你的服务信息配置有误或者网络不通。
  9 +订阅信息列有三个图标,表示上级开启订阅,从左到右依次是:报警订阅,目录订阅,移动位置订阅。
  10 +## 推送通道
  11 +点击你要推送的平台的“选择通道”按钮。
  12 +![cascade18](_media/img_18.png)
  13 +- **页面结构**
  14 + - 左侧为目录结构
  15 + 选择未分配,则右侧显示待分配的通道,可以点击“添加按钮”,在弹窗中选择要放置的位置,保存后即可添加通道成功
  16 + 选择其他的目录可以看到已经分配在这个目录下的通道,可以对其进行删除后重新在未分配中去分配。
  17 + - 右侧为数据展示以及操作
  18 + 国标通道栏内为来自其他国标设备/平台的通道;直播流通道为来自推流/拉流代理的通道。
0 19 \ No newline at end of file
... ...
doc/_content/ability/cloud_record.md 0 → 100644
  1 +<!-- 云端录像 -->
  2 +# 云端录像
  3 +云端录像是对录制在zlm服务下的录像文件的管理,录像的文件路径默认在ZLM/www/record下,使用云端录像功能必须部署wvp-pro-assist,主要通过调用wvp-pro-assist的接口完成各种功能。
  4 +如果你需要24小时的录像,目前有一个这种方案,可以参考[7*24不间断录像](./_content/ability/continuous_recording.md)。
  5 +1. 云段录像支持录像文件的查看,播放(可能因为编码的原因导致无法播放);
  6 +2. 支持录像的下载;
  7 +3. 支持录像的合并下载;
  8 +功能没有太多特殊的地方就不一一介绍了,大家自行体验吧。
... ...
doc/_content/ability/continuous_recording.md 0 → 100644
  1 +<!-- 7*24不间断录像 -->
  2 +# 7*24不间断录像
  3 +
  4 +目前如果要实现不间断录像如果只是关闭无人观看停止推流是不够的,设备可能经历断网,重启,都会导致录像的中断,目前给大家提供一种可用的临时方案。
  5 +
  6 +**原理:** wvp支持使用流地址自动点播,即你拿到一个流地址直接去播放,即使设备处于未点播状态,wvp会自动帮你点播;ZLM
  7 +的拉流代理成功后会无限重试,只要流一恢复就可以拉起来,基于这两个原理。
  8 +**方案如下:**
  9 +1. wvp的配置中user-settings->auto-apply-play设置为团true,开启自动点播;
  10 +2. 点击你要录像的通道,点击播放页面左下角的“更多地址”,点击rtsp,此时复制了rtsp地址到剪贴板;
  11 +3. 在拉流代理中添加一路流,地址填写你复制的地址,启用成功即可。
  12 +**前提:**
  13 +1. wvp使用多端口收流,不然你无法得到一个固定的流地址,也就无法实现自动点播。
  14 +
... ...
doc/_content/ability/device.md 0 → 100644
  1 +<!-- 接入设备 -->
  2 +# 接入设备
  3 +设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息,只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md),
  4 +主要有以下字段需要配置:
  5 +
  6 +- sip->ip
  7 +本机IP,不要使用127.0.0.1/0.0.0.0, 除非你对项目及其熟悉
  8 +
  9 +- sip->port
  10 +28181服务监听的端口
  11 +
  12 +- sip->domain
  13 +domain宜采用ID统一编码的前十位编码。
  14 +
  15 +- sip->id
  16 +28181服务ID
  17 +
  18 +- sip->password
  19 +28181服务密码
  20 +
  21 +- 配置信息在如下位置
  22 +
  23 +![_media/img_16.png](_media/img_16.png)
  24 +***
  25 +## 大华摄像头
  26 +![_media/img_10.png](_media/img_10.png)
  27 +## 大华NVR
  28 +![_media/img_11.png](_media/img_11.png)
  29 +## 艾科威视摄像头
  30 +![_media/img_15.png](_media/img_15.png)
  31 +## 水星摄像头
  32 +![_media/img_12.png](_media/img_12.png)
  33 +## 海康摄像头
  34 +![_media/img_9.png](_media/img_9.png)
  35 +
  36 +[设备使用](_content/ability/device_use.md)
... ...
doc/_content/ability/device_use.md 0 → 100644
  1 +<!-- 设备使用 -->
  2 +# 设备使用
  3 +### 更新设备通道
  4 + 点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png)即可看到通道数量的变化;如果通道数量仍未0,那么可能时对方尚未推送通道给你。
  5 +### 查看设备通道
  6 + 点击列表末尾的“通道”按钮,
  7 +### 查看设备定位
  8 + 点击列表末尾的“定位”按钮,即可跳转到地图页面看到设备的位置
  9 +### 编辑设备在WVP中一些功能
  10 +点击列表末尾的“编辑”按钮,即可在打开的弹窗中对设备功能进行修改
  11 +- 设备名称
  12 + 如何未能从设备里读取到设备名称或者需要自己重命名,那么可以修改此选项。
  13 +- 字符集
  14 + 修改读取设备数据时使用的字符集,默认为GB2312,但是GB2312收录的汉字不全,所以有时候回遇到乱码,可以修改为UTF-8来解决。
  15 +- 地理坐标系
  16 + 展示此设备定位信息时使用的设用什么坐标系来解析经纬度,一般不用修改,如果遇到定位不准,可以修改尝试修改此选项解决。
  17 +- 目录结构
  18 + 展示设备的通道信息时,使用设备作为树形结构的依据,国标28181定义了两种树形结构,详情查看[国标28181的树形结构](./_content/theory/channel_tree.md);
  19 +- 目录订阅
  20 + 填写订阅周期即可对设备开启目录订阅,设备如果支持目录订阅那么设备在通道信息发生变化时就会通知WVP哪些通道发生了那些变化,包括通道增加/删除/更新/上线/下线/视频丢失/故障。0为取消订阅。
  21 + 一般NVR和平台对接可以开启此选项,直接接摄像机开启此选项意义不大。
  22 +- 移动位置订阅
  23 + 对设备开启移动位置订阅,设备如果支持目录订阅那么设备位置发生变化时会通知到WVP,一般执法记录仪可以开启此选项,对固定位置的设备意义不大。
  24 +- SSRC校验
  25 + 为了解决部分设备出现的串流问题,可以打开此选项。ZLM会严格按照给定的ssrc处理视频流。部分设备流信息不标准,开启可能导致无法点播。
  26 +### 删除设备
  27 + 可以删除WVP中的设备信息,如果设备28181配置未更改,那么设备在下一次注册后仍然会注册上来。
  28 +### 点播视频
  29 + 进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面
  30 +### 设备录像
  31 + 进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。
  32 +### 云台控制
  33 + 可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。
  34 +### 获取视频的播放器地址
  35 + 视频点播成功后在实时视频页面,点击“更多地址”可以看到所有的播放地址,地址是否可以播放与你是否完整编译启用zlm功能有关,更与网络有关。
0 36 \ No newline at end of file
... ...
doc/_content/ability/gis.md 0 → 100644
  1 +<!-- 电子地图 -->
  2 +# 电子地图
  3 +WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息,电子地图基于开源的地图引擎openlayers开发。
  4 +### 查看设备定位
  5 +1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面;
  6 +2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。
  7 +3. 单击通道信息可以定位到具体的通道
  8 +
  9 +
  10 +### 查询设备轨迹
  11 +查询轨迹需要提前配置save-position-history选项开启轨迹信息的保存,目前WVP此处未支持分库分表,对于大数据量的轨迹信息无法胜任,有需求请自行二次开发或者定制开发。
  12 +在电子地图页面在设备上右键点击“查询轨迹”获取设备轨迹信息。
  13 +
  14 +PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。
  15 +
  16 +### 更换底图以及底图配置
  17 +目前WVP支持使用了更换底图,配置文件在web_src/static/js/mapConfig.js,请修改后重新编译前端文件。
  18 +```javascript
  19 +window.mapParam = {
  20 + // 开启/关闭地图功能
  21 + enable: true,
  22 + // 坐标系 GCJ-02 WGS-84,
  23 + coordinateSystem: "GCJ-02",
  24 + // 地图瓦片地址
  25 + tilesUrl: "http://webrd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
  26 + // 瓦片大小
  27 + tileSize: 256,
  28 + // 默认层级
  29 + zoom:10,
  30 + // 默认地图中心点
  31 + center:[116.41020, 39.915119],
  32 + // 地图最大层级
  33 + maxZoom:18,
  34 + // 地图最小层级
  35 + minZoom: 3
  36 +}
  37 +```
... ...
doc/_content/ability/node_manger.md 0 → 100644
  1 +<!-- 节点管理 -->
  2 +# 节点管理
  3 +WVP支持单个WVP多个ZLM的方案来扩展WVP的视频并发能力,并发点播是因为带宽和性能的原因,单个ZLM节点能支持的路数有限,所以WVP增加了ZLM集群来扩展并发并且保证ZLM的高可用。
  4 +## 默认节点
  5 +WVP中为了保证功能的完整性,ZLM节点至少要有一个默认节点,这个节点不是在管理页面添加的,而是在WVP的配置文件中配置的,这个节点不可在页面删除。每次启动会自动从配置文件中读取配置写入数据库备用。
  6 +## 新增节点
  7 +启动你要添加的zlm节点,然后点击“添加节点”按钮输入zlm的ip, http端口,SECRET。点击测试测试完成则开始对节点进行详细的设置,如果你的zlm是使用docker启动的,可能存在zlm使用的端口与宿主机端口不一致的情况,需要在这里一一配置。
  8 +## wvp使用多个节点的原理
  9 +wvp会把连接的节点统一记录在redis中,并记录zlm的负载情况,当新的请求到来时,会取出负载最低的那个zlm进行使用。以此保证节点负载均衡。
... ...
doc/_content/ability/online_doc.md 0 → 100644
  1 +<!-- 在线文档 -->
  2 +# 在线文档
... ...
doc/_content/ability/proxy.md 0 → 100644
  1 +<!-- 拉流代理 -->
  2 +# 拉流代理
  3 +不是所有的摄像机都支持国标或者推流的,但是这些设备可以得到一个视频播放地址,通常为rtsp协议,
  4 +以大华为例:
  5 +```text
  6 +rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0
  7 +```
  8 +可以得到这样一个流地址,可以直接用vlc进行播放,此时我们可以通过拉流代理功能将这个设备推送给其他国标平台了。
  9 +流程如下:
  10 +```plantuml
  11 +@startuml
  12 +"摄像机" <- "ZLMediaKit": 1. 流去流信息到ZLM
  13 +"ZLMediaKit" -> "WVP-PRO": 2. 收到hook通知得到流信息
  14 +"上级国标平台" -> "WVP-PRO": 3. 点播这路视频
  15 +"WVP-PRO" -> "ZLMediaKit": 4. 通知推流到上级国标平台
  16 +@enduml
  17 +```
  18 +## 添加代理
  19 +拉流代理支持两种方式:
  20 +1. ZLM中直接代理流,支持RTSP/RTMP,不支持转码;
  21 +2. 借助ffmpeg完成拉转,可以通过修改ffmpeg拉转参数完成转码。
  22 +点击页面的“添加代理”,安装提示操作即可,保存并启用成功后,可以在国标级联中[添加通道推送给上级平台](./_content/ability/cascade?id=_2-%e6%b7%bb%e5%8a%a0%e7%9b%ae%e5%bd%95%e4%b8%8e%e9%80%9a%e9%81%93)
  23 +
  24 +PS: ffmpeg默认模板不需修改,需要修改参数自行去ZLM配置文件中添加一个即可。
... ...
doc/_content/ability/push.md 0 → 100644
  1 +<!-- 推流列表 -->
  2 +# 推流列表
  3 +## 功能说明
  4 +
  5 +WVP支持三种图像输入方式,直播,[拉流代理](_content/ability/proxy.md),[国标](_content/ability/device.md),直播设备接入流程如下
  6 +```plantuml
  7 +@startuml
  8 +"直播设备" -> "ZLMediaKit": 1. 发起推流
  9 +"ZLMediaKit" -> "WVP-PRO": 2. 收到hook通知得到流信息
  10 +"上级国标平台" -> "WVP-PRO": 3. 点播这路视频
  11 +"WVP-PRO" -> "ZLMediaKit": 4. 通知推流到上级国标平台
  12 +@enduml
  13 +```
  14 +1. 默认情况下WVP收到推流信息后,列表中出现这条推流信息,此时你可以点击“加入国标”按钮为此路推流配置名称以及国标编号,只有有国标编号的推流才可以添加到级联平台,保存成功后可以在国标级联中[添加通道推送给上级平台](_content/ability/cascade?id=_2-%e6%b7%bb%e5%8a%a0%e7%9b%ae%e5%bd%95%e4%b8%8e%e9%80%9a%e9%81%93)
  15 +2. WVP也支持推流前导入大量通道直接推送给上级,点击“下载模板”按钮,根据示例修改模板后,点击“通道导入”按钮导入通道数据,保存成功后可以在国标级联中[添加通道推送给上级平台](_content/ability/cascade?id=_2-%e6%b7%bb%e5%8a%a0%e7%9b%ae%e5%bd%95%e4%b8%8e%e9%80%9a%e9%81%93)
  16 +
  17 +## 推拉流鉴权规则
  18 +为了保护服务器的WVP默认开启推流鉴权(目前不支持关闭此功能)
  19 +
  20 +### 推流规则
  21 +推流时需要携带推流鉴权的签名sign,sign=md5(pushKey),pushKey来自用户表,每个用户会有一个不同的pushKey.
  22 +例如app=test,stream=live,pushKey=1000,ip=192.168.1.4, port=10554 那么推流地址为:
  23 +```
  24 +rtsp://192.168.1.4:10554/test/live?sign=a9b7ba70783b617e9998dc4dd82eb3c5
  25 +```
  26 +支持推流时自定义播放鉴权Id,参数名为callId,此时sign=md5(callId_pushKey)
  27 +例如app=test,stream=live,pushKey=1000,callId=12345678, ip=192.168.1.4, port=10554 那么推流地址为:
  28 +```
  29 +rtsp://192.168.1.4:10554/test/live?callId=12345678&sign=c8e6e01dde2d60c66dcea8d2498ffef1
  30 +```
  31 +### 播放规则
  32 +默认情况播放不需要鉴权,但是如果推流时携带了callId,那么播放时必须携带callId
  33 +例如app=test,stream=live,无callId, ip=192.168.1.4, port=10554 那么播放地址为:
  34 +```
  35 +rtsp://192.168.1.4:10554/test/live
  36 +```
  37 +例如app=test,stream=live,callId=12345678, ip=192.168.1.4, port=10554 那么播放地址为:
  38 +```
  39 +rtsp://192.168.1.4:10554/test/live?callId=12345678
  40 +```
  41 +
... ...
doc/_content/ability/user.md 0 → 100644
  1 +<!-- 用户管理 -->
  2 +# 用户管理
... ...
doc/_content/about_doc.md 0 → 100644
  1 +<!-- 关于本文档 -->
  2 +
  3 +# 关于本文档
  4 +本文档开源在gitee上,[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git),如果文档出现任何错误或者不易理解的语句,请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档,让更多的人可以使用到这个开源的视频平台。
  5 +
... ...
doc/_content/disclaimers.md 0 → 100644
  1 +# 免责声明
  2 +WVP-PRO自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议
0 3 \ No newline at end of file
... ...
doc/_content/donation.md 0 → 100644
  1 +# 捐赠
  2 +项目目前仍在积极开发。大家的捐赠以及start可以让我看到大家的支持于关注。更加有动力把项目维护下去。
  3 +
  4 +<div align="left">
  5 +<img src=_media/weixin.jpg style="height: 500px; border: 1px solid #e2e2e2"/>
  6 +<img src=_media/zhifubao.jpg style="height: 500px; border: 1px solid #e2e2e2; margin-left: 20px"/>
  7 +</div>
0 8 \ No newline at end of file
... ...
doc/_content/introduction/_media/img.png 0 → 100644

112 KB

doc/_content/introduction/_media/img_1.png 0 → 100644

150 KB

doc/_content/introduction/_media/img_2.png 0 → 100644

206 KB

doc/_content/introduction/compile.md 0 → 100644
  1 +<!-- 编译 -->
  2 +# 编译
  3 +WVP-PRO不只是实现了国标28181的协议,本身也是一个完整的视频平台。所以对于新手来说,你可能需要一些耐心来完成。遇到问题不要焦躁,你可以
  4 +1. 百度
  5 +2. 如果身边有熟悉java的朋友,可以咨询下朋友;
  6 +3. 来群里(901799015)咨询群友;
  7 +4. 向作者发送邮件648540858@qq.com;
  8 +5. 作者远程支持(有偿)。
  9 + 如果这些仍不能解决你的问题,那么你可能需要与作者我一起合作完成这个项目,解决你遇到的问题。
  10 +
  11 +
  12 +WVP-PRO使用Spring boot开发,maven管理依赖。对于熟悉spring开发的朋友是很容易进行编译部署以及运行的。
  13 +下面将提供一种通用方法方便大家运行项目。
  14 +## 1 服务介绍
  15 +| 服务 | 作用 | 是否必须 |
  16 +|----------------|------------------------------------------|-------------------------|
  17 +| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 |
  18 +| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现,以及各种视频流格式的分发支持 | 是 |
  19 +| wvp-pro-assist | wvp的辅助录像程序,也可单独跟zlm一起使用,提供录像控制,录像合并下载接口 | 否(不安装只是影响云端录像功能和国标录像下载) |
  20 +
  21 +## 2 安装依赖
  22 +| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 |
  23 +|--------|------------|-------------|--------|--------|
  24 +| jdk | >=1.8 | 运行与编译java代码 | 是 | 是 |
  25 +| maven | >=3.3 | 管理java代码依赖 | 否 | 否 |
  26 +| git || 下载/更新/提交代码 | 否 | 否 |
  27 +| nodejs || 编译于运行前端文件 | 否 | 否 |
  28 +| npm || 管理前端文件依赖 | 否 | 否 |
  29 +
  30 +如果你是一个新手,建议你使用linux或者macOS平台。windows不推荐。
  31 +
  32 +ubuntu环境,以ubuntu 18为例:
  33 +``` bash
  34 +apt-get install -y openjdk-11-jre git maven nodejs npm
  35 +```
  36 +centos环境,以centos 8为例:
  37 +```bash
  38 +yum install -y java-1.8.0-openjdk.x86_64 git maven nodejs npm
  39 +```
  40 +window环境,以windows10为例:
  41 +```bash
  42 +这里不细说了,百度或者谷歌一搜一大把,基本都是下一步下一步,然后配置环境变量。
  43 +```
  44 +## 3 安装mysql以及redis
  45 +这里依然是参考网上教程,自行安装吧。
  46 +
  47 +## 4 编译ZLMediaKit
  48 +参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki),截取一下关键步骤:
  49 +```bash
  50 +# 国内用户推荐从同步镜像网站gitee下载
  51 +git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit
  52 +cd ZLMediaKit
  53 +# 千万不要忘记执行这句命令
  54 +git submodule update --init
  55 +```
  56 +## 5 编译WVP-PRO
  57 +### 5.1 可以通过git克隆,也可以在项目下载点击下载
  58 +![点击下载](_media/img_1.png)
  59 +![点击下载](_media/img_2.png)
  60 +从gitee克隆
  61 +```bash
  62 +git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git
  63 +```
  64 +从github克隆
  65 +```bash
  66 +git clone https://github.com/648540858/wvp-GB28181-pro.git
  67 +```
  68 +
  69 +### 5.2 编译前端页面
  70 +```shell script
  71 +cd wvp-GB28181-pro/web_src/
  72 +npm --registry=https://registry.npm.taobao.org install
  73 +npm run build
  74 +```
  75 +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败
  76 +编译完成后在src/main/resources下出现static目录
  77 +**编译完成一般是这个样子,中间没有报红的错误信息**
  78 +![编译成功](_media/img.png)
  79 +
  80 +### 5.3 打包项目, 生成可执行jar
  81 +```bash
  82 +cd wvp-GB28181-pro
  83 +mvn package
  84 +```
  85 +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败
  86 +编译完成后在target目录下出现wvp-pro-***.jar。
  87 +接下来[配置服务](./_content/introduction/config.md)
  88 +
  89 +
  90 +
  91 +
  92 +
  93 +
  94 +
  95 +
... ...
doc/_content/introduction/config.md 0 → 100644
  1 +<!-- 配置 -->
  2 +# 配置
  3 +对于首次测试或者新手同学,我建议在局域网测试,并且关闭服务器与客户机的防火墙测试。建议部署在linux进行测试。
  4 +
  5 +```plantuml
  6 +@startuml
  7 +"WVP-PRO" -> "ZLMediaKit": RESTful 接口
  8 +"WVP-PRO" <-- "ZLMediaKit": Web Hook 接口
  9 +@enduml
  10 +```
  11 +WVP-PRO通过调用ZLMediaKit的RESTful接口实现对ZLMediaKit行为的控制; ZLMediaKit通过Web Hook 接口把消息通知WVP-PRO。通过这种方式,实现了两者的互通。
  12 +对于最简单的配置,你不需要修改ZLMediaKit的任何默认配置。你只需要在WVP-PRO中配置的ZLMediaKit信息即可
  13 +## 1 WVP配置文件位置
  14 +基于spring boot的开发方式,配置文件的加载是很灵活的。默认在src/main/resources/application.yml,部分配置项是可选,你不需要全部配置在配置文件中,
  15 +完全的配置说明可以参看all-application.yml。
  16 +### 1.1 默认加载配置文件方式
  17 +使用maven打包后的jar包里,已经存在了配置文件,但是每次打开jar包修改配置文件或者修改后再打包都是比较麻烦的,所以大家可通过指定配置文件路径来加载指定位置的配置文件。
  18 +```shell
  19 +cd wvp-GB28181-pro/target
  20 +java -jar wvp-pro-*.jar --spring.config.location=../src/main/resources/application.yml
  21 +```
  22 +### 1.2 迁移配置文件以方便启动
  23 +由于配置文件的命令比较长,所以为了启动方便通常我会把配置文件放到jar包的同级目录,类似这样,
  24 +移除jar包内/BOOT-INF/classes/下所有以application开头的文件,使用解压缩工具打开jar即可,不需要解压出来。
  25 +```shell
  26 +cd wvp-GB28181-pro/target
  27 +mv ../src/main/resources/application-dev.yml application.yml
  28 +java -jar wvp-pro-*.jar
  29 +```
  30 +这也是我自己最常用的方式。
  31 +## 2 配置WVP-PRO
  32 +### 2.1 Mysql数据库配置
  33 +首先你需要创建一个名为wvp(也可使用其他名字)的数据库,并使用sql/mysql.sql导入数据库,初始化数据库结构。
  34 +在application-dev.yml中配置(使用1.2方式的是在jar包的同级目录的application.yml)配置数据库连接,包括数据库连接信息,密码。
  35 +### 2.2 Redis数据库配置
  36 +配置wvp中的redis连接信息,建议wvp自己单独使用一个db。
  37 +### 2.3 配置服务启动端口(可直接使用默认配置)
  38 +```yaml
  39 +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
  40 +server:
  41 + port: 18080
  42 +```
  43 +### 2.4 配置28181相关信息(可直接使用默认配置)
  44 +```yaml
  45 +# 作为28181服务器的配置
  46 +sip:
  47 + # [必须修改] 本机的IP
  48 + ip: 192.168.1.3
  49 + # [可选] 28181服务监听的端口
  50 + port: 5060
  51 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
  52 + # 后两位为行业编码,定义参照附录D.3
  53 + # 3701020049标识山东济南历下区 信息行业接入
  54 + # [可选]
  55 + domain: 3402000000
  56 + # [可选]
  57 + id: 34020000002000000001
  58 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
  59 + password: 12345678
  60 +```
  61 +### 2.5 配置ZLMediaKit连接信息
  62 +```yaml
  63 +#zlm 默认服务器配置
  64 +media:
  65 + # ZLMediaKit的服务ID,必须配置
  66 + id: FQ3TF8yT83wh5Wvz
  67 + # [必须修改] zlm服务器的内网IP,sdp-ip与stream-ip使用默认值的情况下,这里不要使用127.0.0.1/0.0.0.0
  68 + ip: 192.168.1.3
  69 + # [必须修改] zlm服务器的http.port
  70 + http-port: 6080
  71 + # [可选] zlm服务器的hook.admin_params=secret
  72 + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
  73 + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
  74 + rtp:
  75 + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
  76 + enable: true
  77 + # [可选] 在此范围内选择端口用于媒体流传输,
  78 + port-range: 30000,30500 # 端口范围
  79 + # [可选] 国标级联在此范围内选择端口发送媒体流,
  80 + send-port-range: 30000,30500 # 端口范围
  81 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
  82 + record-assist-port: 18081
  83 +```
  84 +### 2.4 个性化定制信息配置
  85 +```yaml
  86 +# [根据业务需求配置]
  87 +user-settings:
  88 + # [可选] 服务ID,不写则为000000
  89 + server-id:
  90 + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true
  91 + auto-apply-play: false
  92 + # [可选] 部分设备需要扩展SDP,需要打开此设置
  93 + senior-sdp: false
  94 + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认)
  95 + save-position-history: false
  96 + # 点播等待超时时间,单位:毫秒
  97 + play-timeout: 3000
  98 + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播
  99 + wait-track: false
  100 + # 是否开启接口鉴权
  101 + interface-authentication: true
  102 + # 自动配置redis 可以过期事件
  103 + redis-config: true
  104 + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录
  105 + interface-authentication-excludes:
  106 + - /api/v1/**
  107 + # 推流直播是否录制
  108 + record-push-live: true
  109 + # 国标是否录制
  110 + record-sip: true
  111 + # 是否将日志存储进数据库
  112 + logInDatebase: true
  113 + # 第三方匹配,用于从stream钟获取有效信息
  114 + thirdPartyGBIdReg: [\s\S]*
  115 +```
  116 +
  117 +
  118 +如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。
  119 +接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。
0 120 \ No newline at end of file
... ...
doc/_content/introduction/deployment.md 0 → 100644
  1 +<!-- 部署 -->
  2 +
  3 +# 部署
  4 +**请仔细阅读以下内容**
  5 +1. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与zlm部署在同一台主机;
  6 +2. 需要开放的端口
  7 +| 服务 | 端口 | 类型 | 必选 |
  8 +|-----|:-------------------------|-------------|-------|
  9 +| wvp | server.port | tcp | 是 |
  10 +| wvp | sip.port | udp and tcp | 是 |
  11 +| zlm | http.port | tcp | 是 |
  12 +| zlm | http.sslport | tcp | 否 |
  13 +| zlm | rtmp.port | tcp | 否 |
  14 +| zlm | rtmp.sslport | tcp | 否 |
  15 +| zlm | rtsp.port | udp and tcp | 否 |
  16 +| zlm | rtsp.sslport | udp and tcp | 否 |
  17 +| zlm | rtp_proxy.port | udp and tcp | 单端口开放 |
  18 +| zlm | rtp.port-range(在wvp中配置) | udp and tcp | 多端口开放 |
  19 +
  20 +3. 测试环境部署建议所有服务部署在一台主机,关闭防火墙,减少因网络出现问题的可能;
  21 +4. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与zlm部署在同一台主机;
  22 +5. 生产环境按需开放端口,但是建议修改默认端口,尤其是5060端口,易受到攻击;
  23 +6. zlm使用docker部署的情况,要求端口映射一致,比如映射5060,应将外部端口也映射为5060端口;
  24 +7. 启动服务,以linux为例
  25 +**启动WVP-PRO**
  26 +```shell
  27 +nohup java -jar java -jar wvp-pro-*.jar &
  28 +```
  29 +
  30 +**启动ZLM**
  31 +```shell
  32 +nohup ./MediaServer -d -m 3 &
  33 +```
  34 +
  35 +[接入设备](./_content/ability/device.md)
  36 +
... ...
doc/_content/qa/_media/img.png 0 → 100644

139 KB

doc/_content/qa/_media/img_1.png 0 → 100644

117 KB

doc/_content/qa/_media/img_2.png 0 → 100644

72.3 KB

doc/_content/qa/_media/img_3.png 0 → 100644

90.2 KB

doc/_content/qa/_media/img_4.png 0 → 100644

48 KB

doc/_content/qa/_media/img_5.png 0 → 100644

149 KB

doc/_content/qa/bug.md 0 → 100644
  1 +<!-- 反馈bug -->
  2 +# 反馈bug
  3 +代码是在不断的完善的,不断修改会修复旧的问题也有可能引入新的问题,所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥,咱们就事论事就好了。
  4 +## 如何反馈
  5 +1. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试;
  6 +2. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复;
  7 +3. 你可以来我的QQ群里,询问群友看看是否遇到了同样的问题;
  8 +4. 你可以私聊我的QQ,如果我有时间我会给你答复,但是除非你有明确的复现步骤或者修复方案,否则你可能等不到我的答复。
  9 +
  10 +## 如何快速解决BUG
  11 +目前解决BUG有三种方式:
  12 +1. 作者验证以及修复;
  13 +2. 热心开发者提来的PR;
  14 +3. 使用运维手段屏蔽BUG的影响。
  15 +
  16 +- 对于第一种:详细的复现步骤,完整的抓包文件,有条理的错误分析都可以帮助作者复现问题,进而解决问题。解决问题往往不是最难的,复现才是。
  17 +- 对于第二种:如果你是开发者,你已经发现了造成BUG的原因以及知道如何正确的修复,那么我很希望你PR,SRS的大佬经常说的,开源不是一个人的事。所以你的参与就是最大的鼓励。
  18 +- 对于第三种:如果你有一个有经验的运维伙伴,那么部分问题是可以通过运维的手段暂时屏蔽的,在等待修复的这段时间了以保证项目的运行。
  19 +
... ...
doc/_content/qa/development.md 0 → 100644
  1 +<!-- 参与开发 -->
  2 +# 参与到开发中来
  3 +非常欢迎有兴趣的小伙伴一起来维护这个项目
  4 +## 与开发有关的信息
  5 +- 开发语言:后端java + 前端vue;
  6 +- jdk版本: 1.8;
  7 +- 作者自用开发ide: jetbrains intellij idea;
  8 +- nodejs/npm版本:v10.19.0/6.14.4;
  9 +- 后端使用Spring boot框架开发;
  10 +- 项目大量使用了异步操作;
  11 +- 跟代码学流程需要参考28181文档,只看代码你会很懵的;
  12 +- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的
  13 +
  14 +## 提交代码
  15 +大家可以通过fork项目的方式提交自己的代码,然后提交PR,我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约**”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。
0 16 \ No newline at end of file
... ...
doc/_content/qa/img.png 0 → 100644

139 KB

doc/_content/qa/play_error.md 0 → 100644
  1 +<!-- 点播错误 -->
  2 +# 点播错误
  3 +排查点播错误你首先要清除[点播的基本流程](_content/theory/play.md),一般的流程如下:
  4 +```plantuml
  5 +@startuml
  6 +"WEB用户" -> "WVP-PRO": 1. 发起点播请求
  7 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体)
  8 +"设备" --> "WVP-PRO": 3. 200OK(携带SDP消息体)
  9 +"设备" <-- "WVP-PRO": 4. Ack
  10 +"设备" -> "ZLMediaKit": 5. 发送实时流
  11 +"WVP-PRO" <- "ZLMediaKit": 6. 流改变事件
  12 +"WEB用户" <-- "WVP-PRO": 7. 回复流播放地址(携带流地址)
  13 +"WVP-PRO" <- "ZLMediaKit": 8. 无人观看事件
  14 +"设备" <- "WVP-PRO": 9 Bye消息
  15 +"设备" --> "WVP-PRO": 10 200OK
  16 +@enduml
  17 +```
  18 +针对几种常见的错误,我们来分析一下,也方便大家对号入座解决常见的问题
  19 +## 点播收到错误码
  20 +这个错误一般表现为点击"播放"按钮后很快得到一个错误。
  21 +1. **400错误码**
  22 +出现400错误玛时一般是这样的流程是这样的
  23 +```plantuml
  24 +@startuml
  25 +"WEB用户" -> "WVP-PRO": 1. 发起点播请求
  26 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体)
  27 +"设备" --> "WVP-PRO": 3. 400错误
  28 +@enduml
  29 +```
  30 +此时通常是设备认为WVP发送了错误的消息给它,它认为消息不全或者错误所以直接返回400错误,此时我们需要[抓包](_content/skill/tcpdump.md)来分析是否缺失了内容,也可以直接联系对方询问为什么返回了400。
  31 +WVP不能保证兼容所有的设备,有些实现不规范的设备可能在对接时就会出现上述问题,你可以联系作者帮忙对接。
  32 +2. **500错误码**
  33 +500或者大于500小于600的错误码一般多是设备内部出了问题,解决方式有两个,第一种直接联系设备/平台客服寻求解决;第二种,如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我,我来尝试解决。
  34 +
  35 +
  36 +## 点播超时
  37 +点播超时的情况大致分为两种:点播超时和收流超时
  38 +1. **点播超时**
  39 +点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体)”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因:
  40 +> 1. 设备内部错误,未能回复消息
  41 +> 2. 网络原因消息未到到达设备
  42 +
  43 +大部分时候是原因2,所以遇到这个错误我们首先要排查我们我的网路,如果你是公网部署,那么也可能时心跳周期太长,导致的路由NAT失效,WVP的消息无法通道原来的IP端口号发送给设备。
  44 +
  45 +2. **收流超时**
  46 +收流超时可能发生在流程中的5和6,可能的原因有:
  47 +> 1. 设备发送了流但是发送到了错误的ip和端口上,而这个信息是在invite消息的sdp中指定的,就是流程2Invite(携带SDP消息体)中,而这个错误很可能来自你的配置错误,比如你设置了127.0.0.1导致设备网127.0.0.1上发流,或者是你WVP在公网,但是你给设备了一个内网ip,导致设备无法把流发送过来;
  48 +> 2. 设备内部错误未发送流;
  49 +> 2. 设备发送了流,但是流无法识别,可能存在于流不规范和网络很差的情况下;
  50 +> 3. 设备发送了流,zlm也收到了,但是zlm无法通过hook通知到wvp,此时原因是你可以检查zlm的配置文件中的hook配置,看看是否无法从zlm连接到wvp;
  51 +> 4. 设备发送了流,但是开启SSRC校验,设备的流不够规范采用错误的ssrc,导致zlm选择丢弃;
  52 +
  53 +针对这些可能的错误原因我建议的排查顺序:
  54 +- 关闭ssrc校验;
  55 +- 查看zlm配置的hook是否可以连接到zlm;
  56 +- 查看zlm日志是否有流注册;
  57 +- 抓包查看流的信息,看看流是否正常发送,甚至可以导出发送原始流,用vlc播放,看看是否可以播放。
... ...
doc/_content/qa/regiser_error.md 0 → 100644
  1 +<!-- 设备注册不上来的解决办法 -->
  2 +# 设备注册不上来的解决办法
  3 +一般的原因有两个
  4 +1. 信息填写错误,比如密码错误;
  5 +2. 网络不通导致注册消息无法发送到WVP;
  6 +
  7 +
  8 +遇到问题首先仔细校验填写信息,例如海康可能需要勾选鉴权才可以输入密码。网络问题请自行测试。
0 9 \ No newline at end of file
... ...
doc/_content/qa/start_error.md 0 → 100644
  1 +<!-- 启动时报错 -->
  2 +# 启动时报错
  3 +启动时的报错大部分时候是因为你的配置有问题,比如mysql没连接上,redis没连接上,18080/15060端口占用了,这些都会导致启动是报错,修改配置配置之后都可以解决;
  4 +下面我整理的一些常见的错误,大家可以先对号入座的简单排查下。
  5 +> **常见错误**
  6 +
  7 +![_media/img.png](_media/img.png)
  8 +**错误原因:** redis配置错误,可能原因: redis未启动/ip错误/端口错误/网络不通
  9 +---
  10 +![_media/img_1.png](_media/img_1.png)
  11 +**错误原因:** redis配置错误,可能原因: 密码错误
  12 +---
  13 +![_media/img_2.png](_media/img_2.png)
  14 +**错误原因:** mysql配置错误,可能原因: mysql未启动/ip错误/端口错误/网络不通
  15 +---
  16 +![_media/img_3.png](_media/img_3.png)
  17 +**错误原因:** mysql配置错误,可能原因: 用户名/密码错误
  18 +---
  19 +![_media/img_4.png](_media/img_4.png)
  20 +**错误原因:** SIP配置错误,可能原因: SIP端口被占用
  21 +---
  22 +![_media/img_5.png](_media/img_5.png)
  23 +**错误原因:** WVP Tomcat端口配置错误,可能原因: server.port端口被占用
  24 +---
0 25 \ No newline at end of file
... ...
doc/_content/skill/_media/img.png 0 → 100644

43.8 KB

doc/_content/skill/_media/img_1.png 0 → 100644

105 KB

doc/_content/skill/_media/img_2.png 0 → 100644

15.9 KB

doc/_content/skill/tcpdump.md 0 → 100644
  1 +<!-- 抓包 -->
  2 +# 抓包
  3 +如果说对于网络编程,有什么工具是必会的,我觉得抓包肯定是其中之一了。作为GB/T 28181调试过程中最重要的手段,我觉得如果你真对他有兴趣,或者系统遇到问题可以最快的得到解决,那么抓包你就一定要学会了。
  4 +
  5 +## 抓包工具的选择
  6 +### 1. Wireshark
  7 +在具备图形界面的系统上,比如windows,linux发行版ubuntu,opensuse等,我一般直接使用Wireshark直接进行抓包,也方便进行内容的查看。
  8 +### 2. Tcpdump
  9 +在使用命令行的系统,比如linux服务器,我一般使用Tcpdump进行抓包,无需额外安装,系统一般自带,抓包的到的文件,可以使用Wireshark打开,在图形界面下方便查看内容。
  10 +
  11 +## 工具安装
  12 +Wireshark的安装很简单,根据提示一步步点击就好了,在linux需要解决权限的问题,如果和我一样使用图形界面的linux发行版的话,可以参看如下步骤; windows的小伙伴直接略过即可
  13 +```shell
  14 +# 1. 添加wireshark用户组
  15 +sudo groupadd wireshark
  16 +# 2. 将dumpcap更改为wireshark用户组
  17 +sudo chgrp wireshark /usr/bin/dumpcap
  18 +# 3. 让wireshark用户组有root权限使用dumpcap
  19 +sudo chmod 4755 /usr/bin/dumpcap
  20 +# 4. 将需要使用的用户名加入wireshark用户组
  21 +sudo gpasswd -a $USER wireshark
  22 +```
  23 +tcpdump一般linux都是自带,无需安装,可以这样验证;显示版本信息即是已安装
  24 +```shell
  25 +tcpdump --version
  26 +```
  27 +## 开始抓包
  28 +### 使用Wireshark
  29 +在28181中我一般只关注sip包和rtp包,所以我一般是直接过滤sip和rtp,可以输入框输入 `sip or rtp`这样即可,如果设备来源比较多还可以加上ip和端口号的过滤`(sip or rtp )and ip.addr==192.168.1.3 and udp.port==5060`
  30 +详细的过滤规则可以自行百度,我可以提供一些常用的给大家参考
  31 +![img.png](_media/img.png)
  32 +**只过滤SIP:**
  33 +```shell
  34 +sip
  35 +```
  36 +**只获取rtp数据:**
  37 +```shell
  38 +rtp
  39 +```
  40 +**默认方式:**
  41 +```shell
  42 +sip or rtp
  43 +```
  44 +**过滤IP:**
  45 +```shell
  46 + sip and ip.addr==192.168.1.3
  47 +```
  48 +**过滤端口:**
  49 +```shell
  50 + sip and udp.port==5060
  51 +```
  52 +输入命令开启抓包后,此时可以进行操作,比如点播,录像回访等,操作完成回到Wireshark点击红色的停止即可,需要保存文件可以点击`文件->导出特定分组`导出过滤后的数据,也可以直接`文件->另存为`保存未过滤的数据。
  53 +### 使用tcpdump
  54 +对于服务器抓包,为了得到足够完整的数据,我一般会要求直接抓取网卡数据而不过滤,如下:
  55 +抓取网卡首先需要获取网卡名,在linux我一般使用`ip addr`获取网卡信息,如下所示:
  56 +![img_1.png](_media/img_1.png)
  57 +```shell
  58 +sudo tcpdump -i wlp3s0 -w demo.pcap
  59 +```
  60 +![img_2.png](_media/img_2.png)
  61 +命令行会停留在这个位置,此时可以进行操作,比如点播,录像回放等,操作完成回到命令行使用`Ctrl+C`结束命令行,在当前目录下得到demo.pcap,将这个文件下载到图形界面操作系统里,即可使用Wireshark查看了
  62 +更多的操作可以参考: [https://www.cnblogs.com/jiujuan/p/9017495.html](https://www.cnblogs.com/jiujuan/p/9017495.html)
... ...
doc/_content/theory/_media/img.png 0 → 100644

96.4 KB

doc/_content/theory/_media/img_1.png 0 → 100644

105 KB

doc/_content/theory/_media/img_2.png 0 → 100644

16.8 KB

doc/_content/theory/_media/img_3.png 0 → 100644

82.8 KB

doc/_content/theory/_media/img_4.png 0 → 100644

45.6 KB

doc/_content/theory/_media/img_5.png 0 → 100644

59.5 KB

doc/_content/theory/_media/img_6.png 0 → 100644

51.7 KB

doc/_content/theory/_media/img_7.png 0 → 100644

80 KB

doc/_content/theory/_media/img_8.png 0 → 100644

23.7 KB

doc/_content/theory/_media/img_9.png 0 → 100644

28 KB

doc/_content/theory/channel_tree.md 0 → 100644
  1 +<!-- 通道的树形结构 -->
  2 +
  3 +# 通道的树形结构
  4 +
  5 +国标28181规定了两种组织设备树的方式
  6 +1. **行政区划**
  7 + 行政区划模式下主要是以行政区划作为目录节点例如:河北省->邯郸市->广平县
  8 + ![_media/img_8.png](_media/img_8.png)
  9 +2. **业务分组**
  10 + 业务分组主要自定义的目录树的一种组织形式,但是对定义的目录的国标编号有一定的要求。
  11 + 第一级别需要是业务分组类型,即国标编码中的11、12、13是215,例如:65010200002150000001;
  12 + 业务分组下是虚拟组织,即国标编码中的11、12、13是216,例如:65010200002160000002。
  13 + 虚拟组织下不可是业务分组,虚拟组织下可以继续添加虚拟组织。
  14 + ![_media/img_9.png](_media/img_9.png)
... ...
doc/_content/theory/code.md 0 → 100644
  1 +<!-- 统一编码规则 -->
  2 +
  3 +# 统一编码规则
  4 +## D.1 编码规则 A
  5 +>&emsp;&emsp;编码规则 A 由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十
  6 +>进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。
  7 +>&emsp;&emsp;编码规则 A 的详细说明见表 D.1。其中,中心编码指用户或设备所归属的监控中心的编码,按照监控中心所在地的行政区划代码确定,
  8 +> 当不是基层单位时空余位为0。行政区划代码采用 GB/T2260— 2007规定的行政区划代码表示。行业编码是指用户或设备所归属的行业,行业编码对照表见 D.3。
  9 +> 类型编码指定了设备或用户的具体类型,其中的前端设备包含公安系统和非公安系统的前端设备,终端用 户包含公安系统和非公安系统的终端用户。
  10 +![img_7.png](_media/img_7.png)
  11 +![img_1.png](_media/img_1.png)
  12 +![img_2.png](_media/img_2.png)
  13 +
  14 +
  15 +## D.2 编码规则 B
  16 +>&emsp;&emsp;编码规则 B由中心编码(8位)、行业编码(2位)、序号(4位)和类型编码(2位)四个码段构成,即系
  17 +>统编码 =中心编码 + 行业编码 +序号+类型编码。编码规则 B的详细说明见表 D.2。
  18 +![img_3.png](_media/img_3.png)
  19 +![img_4.png](_media/img_4.png)
  20 +
  21 +
  22 +## D.3 行业编码对照表
  23 +>&emsp;&emsp;行业编码对照表见表 D.3。
  24 +![img_5.png](_media/img_5.png)
  25 +![img_6.png](_media/img_6.png)
0 26 \ No newline at end of file
... ...
doc/_content/theory/img.png 0 → 100644

23.7 KB

doc/_content/theory/play.md 0 → 100644
  1 +<!-- 点播流程 -->
  2 +
  3 +# 点播流程
  4 +> 以下为WVP-PRO点播流程。点播成功前的任何一个环节出现问题都可能出现点播超时,这也是排查点播超时的依据。
  5 +
  6 +```plantuml
  7 +@startuml
  8 +"WEB用户" -> "WVP-PRO": 1. 发起点播请求
  9 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体)
  10 +"设备" --> "WVP-PRO": 3. 200OK(携带SDP消息体)
  11 +"设备" <-- "WVP-PRO": 4. Ack
  12 +"设备" -> "ZLMediaKit": 5. 发送实时流
  13 +"WVP-PRO" <- "ZLMediaKit": 6. 流改变事件
  14 +"WEB用户" <-- "WVP-PRO": 7. 回复流播放地址(携带流地址)
  15 +"WVP-PRO" <- "ZLMediaKit": 8. 无人观看事件
  16 +"设备" <- "WVP-PRO": 9 Bye消息
  17 +"设备" --> "WVP-PRO": 10 200OK
  18 +@enduml
  19 +```
  20 +
  21 +
  22 +## 注册流程描述如下:
  23 +1. 用户从网页或调用接口发起点播请求;
  24 +2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、
  25 + 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。
  26 +3. 摄像机向WVP-PRO回复200OK,消息体中描述了媒体流发送者发送媒体流的IP、端口、媒体格式、SSRC字段等内容。
  27 +4. WVP-PRO向设备回复Ack, 会话建立成功。
  28 +5. 设备向ZLMediaKit发送实时流。
  29 +6. ZLMediaKit向WVP-PRO发送流改变事件。
  30 +7. WVP-PRO向WEB用户回复播放地址。
  31 +8. ZLMediaKit向WVP发送流无人观看事件。
  32 +9. WVP-PRO向设备回复Bye, 结束会话。
  33 +10. 设备回复200OK,会话结束成功。
... ...
doc/_content/theory/register.md 0 → 100644
  1 +<!-- 注册流程 -->
  2 +
  3 +# 注册流程
  4 +WVP-PRO目前仅支持国标中描述的基本注册流程,也是最常用的,
  5 +> 基本注册即采用IETFRFC3261规定的基于数字摘要的挑战应答式安全技术进行注册.
  6 +
  7 +```plantuml
  8 +@startuml
  9 +"设备" -> "WVP-PRO": 1. Register
  10 +"设备" <-- "WVP-PRO": 2. 401 Unauthorized
  11 +"设备" -> "WVP-PRO": 3. Register
  12 +"设备" <-- "WVP-PRO": 4. 200 OK
  13 +@enduml
  14 +```
  15 +
  16 +
  17 +> 注册流程描述如下:
  18 +> 1. 摄像机向WVP-PRO服务器发送 Register请求;
  19 +> 2. WVP-PRO向摄像机发送响应401,并在响应的消息头 WWW_Authenticate字段中给出适合摄像机的认证体制和参数;
  20 +> 3. 摄像机重新向WVP-PRO发送 Register请求,在请求的 Authorization字段给出信任书, 包含认证信息;
  21 +> 4. WVP-PRO对请求进行验证,如果检查出 摄像机身份合法,向摄像机发送成功响应 200OK,如果身份不合法则发送拒绝服务应答。
... ...
doc/_coverpage.md 0 → 100644
  1 +<!-- 封面 -->
  2 +![logo](_media/logo-mini.png)
  3 +
  4 +# WVP-PRO <small>2.0</small>
  5 +
  6 +> 开箱即用的28181协议视频平台。
  7 +
  8 +- 基于GB/T28181-2016标准信令实现,兼容GB/T28181-2011。
  9 +- 自带完整前端页面,开箱即用。
  10 +- 完全开源,且使用MIT许可协议。可以在保留版权信息的基础上商用。
  11 +
  12 +[GitHub](https://github.com/648540858/wvp-GB28181-pro)
  13 +[Gitee](https://gitee.com/pan648540858/wvp-GB28181-pro)
  14 +
  15 +<!-- 背景色 -->
  16 +
  17 +[//]: # ([comment]: <> &#40;![color]&#40;#f0f0f0&#41;&#41;)
... ...
doc/_media/favicon.ico 0 → 100644
No preview for this file type
doc/_media/logo-mini.png 0 → 100644

22.4 KB

doc/_media/logo.png 0 → 100644

65.5 KB

doc/_media/weixin.jpg 0 → 100644

87.1 KB

doc/_media/zhifubao.jpg 0 → 100644

421 KB

doc/_navbar.md 0 → 100644
  1 +<!-- 导航栏 -->
... ...
doc/_sidebar.md 0 → 100644
  1 +<!-- 侧边栏 -->
  2 +
  3 +* **编译与部署**
  4 + * [编译](_content/introduction/compile.md)
  5 + * [配置](_content/introduction/config.md)
  6 + * [部署](_content/introduction/deployment.md)
  7 +* **功能与使用**
  8 + * [接入设备](_content/ability/device.md)
  9 + * [设备使用](_content/ability/device_use.md)
  10 + * [国标级联](_content/ability/cascade2.md)
  11 + * [推流列表](_content/ability/push.md)
  12 + * [拉流代理](_content/ability/proxy.md)
  13 + * [电子地图](_content/ability/gis.md)
  14 + * [节点管理](_content/ability/node_manger.md)
  15 + * [云端录像](_content/ability/cloud_record.md)
  16 + * [不间断录像](_content/ability/continuous_recording.md)
  17 +* **流程与原理**
  18 + * [统一编码规则](_content/theory/code.md)
  19 + * [树形结构](_content/theory/channel_tree.md)
  20 + * [注册流程](_content/theory/register.md)
  21 + * [点播流程](_content/theory/play.md)
  22 +* **必备技巧**
  23 + * [抓包](_content/skill/tcpdump.md)
  24 +
  25 +* **常见问答**
  26 + - [如何反馈BUG](_content/qa/bug.md)
  27 + - [如何参与开发](_content/qa/development.md)
  28 + - [启动报错的解决办法](_content/qa/start_error.md)
  29 + - [设备注册不上来的解决办法](_content/qa/regiser_error.md)
  30 + - [点播超时/报错的解决办法](_content/qa/play_error.md)
  31 +* [**免责声明**](_content/disclaimers.md)
  32 +* [**捐赠**](_content/donation.md)
  33 +* [**关于本文档**](_content/about_doc.md)
... ...
doc/index.html 0 → 100644
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 +<head>
  4 + <meta charset="UTF-8">
  5 + <title>WVP-PRO文档</title>
  6 + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  7 + <meta name="description" content="Description">
  8 + <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
  9 + <link rel="icon" href="_media/favicon.ico" type="image/x-icon" />
  10 + <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
  11 +<!-- <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/dark.css">-->
  12 + <style>
  13 + .cover{
  14 + background: linear-gradient(to left bottom, hsl(82, 100%, 85%) 0%,hsl(199, 100%, 85%) 100%) !important;
  15 + }
  16 + </style>
  17 +</head>
  18 +<body>
  19 + <div id="app">加载中</div>
  20 + <script>
  21 + window.$docsify = {
  22 + name: 'WVP-RPO使用文档',
  23 + repo: 'https://github.com/648540858/wvp-GB28181-pro',
  24 + loadSidebar: true, // 开启侧边栏
  25 + loadNavbar: true, // 开启导航栏
  26 + coverpage: true, // 开启封面
  27 +
  28 + subMaxLevel: 3,
  29 + plantuml: {
  30 + skin: 'default',
  31 + },
  32 + search: {
  33 + maxAge: 86400000, // 过期时间,单位毫秒,默认一天
  34 + paths: 'auto', // or 'auto'
  35 + placeholder: '搜索',
  36 + noData: '找不到结果',
  37 + // 搜索标题的最大层级, 1 - 6
  38 + depth: 4,
  39 + hideOtherSidebarContent: false, // 是否隐藏其他侧边栏内容
  40 + },
  41 + copyCode: {
  42 + buttonText : '复制',
  43 + errorText : '错误',
  44 + successText: '已复制'
  45 + },
  46 + // disqus: 'shortname'
  47 + }
  48 + </script>
  49 + <!-- Docsify v4 -->
  50 + <script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
  51 + <script src="//unpkg.com/docsify-plantuml/dist/docsify-plantuml.min.js"></script>
  52 + <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
  53 + <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.min.js"></script>
  54 + <script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
  55 +
  56 + </script>
  57 +<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/disqus.min.js"></script>-->
  58 +</body>
  59 +</html>
... ...
... ... @@ -61,6 +61,13 @@
61 61 <dependency>
62 62 <groupId>org.springframework.boot</groupId>
63 63 <artifactId>spring-boot-starter-data-redis</artifactId>
  64 + <exclusions>
  65 + <!-- 去掉 Lettuce 的依赖, Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->
  66 + <exclusion>
  67 + <groupId>io.lettuce</groupId>
  68 + <artifactId>lettuce-core</artifactId>
  69 + </exclusion>
  70 + </exclusions>
64 71 </dependency>
65 72 <dependency>
66 73 <groupId>org.springframework.boot</groupId>
... ... @@ -75,6 +82,12 @@
75 82 <groupId>org.mybatis.spring.boot</groupId>
76 83 <artifactId>mybatis-spring-boot-starter</artifactId>
77 84 <version>2.1.4</version>
  85 + <exclusions>
  86 + <exclusion>
  87 + <groupId>com.zaxxer</groupId>
  88 + <artifactId>HikariCP</artifactId>
  89 + </exclusion>
  90 + </exclusions>
78 91 </dependency>
79 92 <dependency>
80 93 <groupId>org.springframework.boot</groupId>
... ... @@ -84,7 +97,6 @@
84 97 <dependency>
85 98 <groupId>redis.clients</groupId>
86 99 <artifactId>jedis</artifactId>
87   - <version>${jedis-version}</version>
88 100 </dependency>
89 101  
90 102 <!-- druid数据库连接池 -->
... ... @@ -265,14 +277,14 @@
265 277 </configuration>
266 278 </plugin>
267 279  
268   - <plugin>
  280 + <!-- <plugin>
269 281 <groupId>pl.project13.maven</groupId>
270 282 <artifactId>git-commit-id-plugin</artifactId>
271 283 <version>3.0.1</version>
272 284 <configuration>
273 285 <offline>true</offline>
274 286 </configuration>
275   - </plugin>
  287 + </plugin>-->
276 288  
277 289 <plugin>
278 290 <groupId>org.apache.maven.plugins</groupId>
... ...
sql/mysql.sql
... ... @@ -2,55 +2,60 @@
2 2 --
3 3 -- Host: 127.0.0.1 Database: wvp2
4 4 -- ------------------------------------------------------
5   --- Server version 8.0.29-0ubuntu0.22.04.2
  5 +-- Server version 8.0.29-0ubuntu0.22.04.3
6 6  
7   -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8   -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9   -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
  7 +/*!40101 SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT */;
  8 +/*!40101 SET @OLD_CHARACTER_SET_RESULTS = @@CHARACTER_SET_RESULTS */;
  9 +/*!40101 SET @OLD_COLLATION_CONNECTION = @@COLLATION_CONNECTION */;
10 10 /*!50503 SET NAMES utf8mb4 */;
11   -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12   -/*!40103 SET TIME_ZONE='+00:00' */;
13   -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14   -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15   -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16   -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
  11 +/*!40103 SET @OLD_TIME_ZONE = @@TIME_ZONE */;
  12 +/*!40103 SET TIME_ZONE = ' + 00:00' */;
  13 +/*!40014 SET @OLD_UNIQUE_CHECKS = @@UNIQUE_CHECKS, UNIQUE_CHECKS = 0 */;
  14 +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS = @@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0 */;
  15 +/*!40101 SET @OLD_SQL_MODE = @@SQL_MODE, SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO' */;
  16 +/*!40111 SET @OLD_SQL_NOTES = @@SQL_NOTES, SQL_NOTES = 0 */;
17 17  
18 18 --
19 19 -- Table structure for table `device`
20 20 --
21 21  
22 22 DROP TABLE IF EXISTS `device`;
23   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  23 +/*!40101 SET @saved_cs_client = @@character_set_client */;
24 24 /*!50503 SET character_set_client = utf8mb4 */;
25   -CREATE TABLE `device` (
26   - `id` int NOT NULL AUTO_INCREMENT,
27   - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
28   - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
29   - `manufacturer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
30   - `model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
31   - `firmware` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
32   - `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
33   - `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
34   - `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
35   - `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
36   - `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
37   - `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
38   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
39   - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
40   - `port` int NOT NULL,
41   - `expires` int NOT NULL,
42   - `subscribeCycleForCatalog` int NOT NULL,
43   - `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
44   - `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
45   - `subscribeCycleForMobilePosition` int DEFAULT NULL,
46   - `mobilePositionSubmissionInterval` int DEFAULT '5',
47   - `subscribeCycleForAlarm` int DEFAULT NULL,
48   - `ssrcCheck` int DEFAULT '0',
49   - `geoCoordSys` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
50   - `treeType` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
51   - PRIMARY KEY (`id`) USING BTREE,
52   - UNIQUE KEY `device_deviceId_uindex` (`deviceId`) USING BTREE
53   -) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  25 +CREATE TABLE `device`
  26 +(
  27 + `id` int NOT NULL AUTO_INCREMENT,
  28 + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  29 + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  30 + `manufacturer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  31 + `model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  32 + `firmware` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  33 + `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  34 + `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  35 + `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  36 + `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  37 + `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  38 + `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  39 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  40 + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  41 + `port` int NOT NULL,
  42 + `expires` int NOT NULL,
  43 + `subscribeCycleForCatalog` int NOT NULL,
  44 + `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  45 + `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  46 + `subscribeCycleForMobilePosition` int DEFAULT NULL,
  47 + `mobilePositionSubmissionInterval` int DEFAULT '5 ',
  48 + `subscribeCycleForAlarm` int DEFAULT NULL,
  49 + `ssrcCheck` int DEFAULT '0 ',
  50 + `geoCoordSys` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
  51 + `treeType` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
  52 + PRIMARY KEY (`id`) USING BTREE,
  53 + UNIQUE KEY `device_deviceId_uindex` (`deviceId`) USING BTREE
  54 +) ENGINE = InnoDB
  55 + AUTO_INCREMENT = 53
  56 + DEFAULT CHARSET = utf8mb4
  57 + COLLATE = utf8mb4_general_ci
  58 + ROW_FORMAT = DYNAMIC;
54 59 /*!40101 SET character_set_client = @saved_cs_client */;
55 60  
56 61 --
... ... @@ -58,8 +63,10 @@ CREATE TABLE `device` (
58 63 --
59 64  
60 65 LOCK TABLES `device` WRITE;
61   -/*!40000 ALTER TABLE `device` DISABLE KEYS */;
62   -/*!40000 ALTER TABLE `device` ENABLE KEYS */;
  66 +/*!40000 ALTER TABLE `device`
  67 + DISABLE KEYS */;
  68 +/*!40000 ALTER TABLE `device`
  69 + ENABLE KEYS */;
63 70 UNLOCK TABLES;
64 71  
65 72 --
... ... @@ -67,22 +74,26 @@ UNLOCK TABLES;
67 74 --
68 75  
69 76 DROP TABLE IF EXISTS `device_alarm`;
70   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  77 +/*!40101 SET @saved_cs_client = @@character_set_client */;
71 78 /*!50503 SET character_set_client = utf8mb4 */;
72   -CREATE TABLE `device_alarm` (
73   - `id` int NOT NULL AUTO_INCREMENT,
74   - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
75   - `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
76   - `alarmPriority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
77   - `alarmMethod` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
78   - `alarmTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
79   - `alarmDescription` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
80   - `longitude` double DEFAULT NULL,
81   - `latitude` double DEFAULT NULL,
82   - `alarmType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
83   - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
84   - PRIMARY KEY (`id`) USING BTREE
85   -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  79 +CREATE TABLE `device_alarm`
  80 +(
  81 + `id` int NOT NULL AUTO_INCREMENT,
  82 + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  83 + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  84 + `alarmPriority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  85 + `alarmMethod` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  86 + `alarmTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  87 + `alarmDescription` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  88 + `longitude` double DEFAULT NULL,
  89 + `latitude` double DEFAULT NULL,
  90 + `alarmType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  91 + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  92 + PRIMARY KEY (`id`) USING BTREE
  93 +) ENGINE = InnoDB
  94 + DEFAULT CHARSET = utf8mb4
  95 + COLLATE = utf8mb4_general_ci
  96 + ROW_FORMAT = DYNAMIC;
86 97 /*!40101 SET character_set_client = @saved_cs_client */;
87 98  
88 99 --
... ... @@ -90,8 +101,10 @@ CREATE TABLE `device_alarm` (
90 101 --
91 102  
92 103 LOCK TABLES `device_alarm` WRITE;
93   -/*!40000 ALTER TABLE `device_alarm` DISABLE KEYS */;
94   -/*!40000 ALTER TABLE `device_alarm` ENABLE KEYS */;
  104 +/*!40000 ALTER TABLE `device_alarm`
  105 + DISABLE KEYS */;
  106 +/*!40000 ALTER TABLE `device_alarm`
  107 + ENABLE KEYS */;
95 108 UNLOCK TABLES;
96 109  
97 110 --
... ... @@ -99,50 +112,55 @@ UNLOCK TABLES;
99 112 --
100 113  
101 114 DROP TABLE IF EXISTS `device_channel`;
102   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  115 +/*!40101 SET @saved_cs_client = @@character_set_client */;
103 116 /*!50503 SET character_set_client = utf8mb4 */;
104   -CREATE TABLE `device_channel` (
105   - `id` int NOT NULL AUTO_INCREMENT,
106   - `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
107   - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
108   - `manufacture` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
109   - `model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
110   - `owner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
111   - `civilCode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
112   - `block` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
113   - `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
114   - `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
115   - `safetyWay` int DEFAULT NULL,
116   - `registerWay` int DEFAULT NULL,
117   - `certNum` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
118   - `certifiable` int DEFAULT NULL,
119   - `errCode` int DEFAULT NULL,
120   - `endTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
121   - `secrecy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
122   - `ipAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
123   - `port` int DEFAULT NULL,
124   - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
125   - `PTZType` int DEFAULT NULL,
126   - `status` int DEFAULT NULL,
127   - `longitude` double DEFAULT NULL,
128   - `latitude` double DEFAULT NULL,
129   - `streamId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
130   - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
131   - `parental` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
132   - `hasAudio` bit(1) DEFAULT NULL,
133   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
134   - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
135   - `subCount` int DEFAULT '0',
136   - `longitudeGcj02` double DEFAULT NULL,
137   - `latitudeGcj02` double DEFAULT NULL,
138   - `longitudeWgs84` double DEFAULT NULL,
139   - `latitudeWgs84` double DEFAULT NULL,
140   - `businessGroupId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
141   - `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
142   - PRIMARY KEY (`id`) USING BTREE,
143   - UNIQUE KEY `device_channel_id_uindex` (`id`) USING BTREE,
144   - UNIQUE KEY `device_channel_pk` (`channelId`,`deviceId`) USING BTREE
145   -) ENGINE=InnoDB AUTO_INCREMENT=19331 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  117 +CREATE TABLE `device_channel`
  118 +(
  119 + `id` int NOT NULL AUTO_INCREMENT,
  120 + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  121 + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  122 + `manufacture` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  123 + `model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  124 + `owner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  125 + `civilCode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  126 + `block` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  127 + `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  128 + `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  129 + `safetyWay` int DEFAULT NULL,
  130 + `registerWay` int DEFAULT NULL,
  131 + `certNum` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  132 + `certifiable` int DEFAULT NULL,
  133 + `errCode` int DEFAULT NULL,
  134 + `endTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  135 + `secrecy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  136 + `ipAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  137 + `port` int DEFAULT NULL,
  138 + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  139 + `PTZType` int DEFAULT NULL,
  140 + `status` int DEFAULT NULL,
  141 + `longitude` double DEFAULT NULL,
  142 + `latitude` double DEFAULT NULL,
  143 + `streamId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  144 + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  145 + `parental` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  146 + `hasAudio` bit(1) DEFAULT NULL,
  147 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  148 + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  149 + `subCount` int DEFAULT '0 ',
  150 + `longitudeGcj02` double DEFAULT NULL,
  151 + `latitudeGcj02` double DEFAULT NULL,
  152 + `longitudeWgs84` double DEFAULT NULL,
  153 + `latitudeWgs84` double DEFAULT NULL,
  154 + `businessGroupId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  155 + `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  156 + PRIMARY KEY (`id`) USING BTREE,
  157 + UNIQUE KEY `device_channel_id_uindex` (`id`) USING BTREE,
  158 + UNIQUE KEY `device_channel_pk` (`channelId`, `deviceId`) USING BTREE
  159 +) ENGINE = InnoDB
  160 + AUTO_INCREMENT = 19496
  161 + DEFAULT CHARSET = utf8mb4
  162 + COLLATE = utf8mb4_general_ci
  163 + ROW_FORMAT = DYNAMIC;
146 164 /*!40101 SET character_set_client = @saved_cs_client */;
147 165  
148 166 --
... ... @@ -150,8 +168,10 @@ CREATE TABLE `device_channel` (
150 168 --
151 169  
152 170 LOCK TABLES `device_channel` WRITE;
153   -/*!40000 ALTER TABLE `device_channel` DISABLE KEYS */;
154   -/*!40000 ALTER TABLE `device_channel` ENABLE KEYS */;
  171 +/*!40000 ALTER TABLE `device_channel`
  172 + DISABLE KEYS */;
  173 +/*!40000 ALTER TABLE `device_channel`
  174 + ENABLE KEYS */;
155 175 UNLOCK TABLES;
156 176  
157 177 --
... ... @@ -159,27 +179,32 @@ UNLOCK TABLES;
159 179 --
160 180  
161 181 DROP TABLE IF EXISTS `device_mobile_position`;
162   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  182 +/*!40101 SET @saved_cs_client = @@character_set_client */;
163 183 /*!50503 SET character_set_client = utf8mb4 */;
164   -CREATE TABLE `device_mobile_position` (
165   - `id` int NOT NULL AUTO_INCREMENT,
166   - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
167   - `channelId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
168   - `deviceName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
169   - `time` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
170   - `longitude` double NOT NULL,
171   - `latitude` double NOT NULL,
172   - `altitude` double DEFAULT NULL,
173   - `speed` double DEFAULT NULL,
174   - `direction` double DEFAULT NULL,
175   - `reportSource` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
176   - `longitudeGcj02` double DEFAULT NULL,
177   - `latitudeGcj02` double DEFAULT NULL,
178   - `longitudeWgs84` double DEFAULT NULL,
179   - `latitudeWgs84` double DEFAULT NULL,
180   - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
181   - PRIMARY KEY (`id`) USING BTREE
182   -) ENGINE=InnoDB AUTO_INCREMENT=6751 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  184 +CREATE TABLE `device_mobile_position`
  185 +(
  186 + `id` int NOT NULL AUTO_INCREMENT,
  187 + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  188 + `channelId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  189 + `deviceName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  190 + `time` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  191 + `longitude` double NOT NULL,
  192 + `latitude` double NOT NULL,
  193 + `altitude` double DEFAULT NULL,
  194 + `speed` double DEFAULT NULL,
  195 + `direction` double DEFAULT NULL,
  196 + `reportSource` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  197 + `longitudeGcj02` double DEFAULT NULL,
  198 + `latitudeGcj02` double DEFAULT NULL,
  199 + `longitudeWgs84` double DEFAULT NULL,
  200 + `latitudeWgs84` double DEFAULT NULL,
  201 + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  202 + PRIMARY KEY (`id`) USING BTREE
  203 +) ENGINE = InnoDB
  204 + AUTO_INCREMENT = 6956
  205 + DEFAULT CHARSET = utf8mb4
  206 + COLLATE = utf8mb4_general_ci
  207 + ROW_FORMAT = DYNAMIC;
183 208 /*!40101 SET character_set_client = @saved_cs_client */;
184 209  
185 210 --
... ... @@ -187,8 +212,10 @@ CREATE TABLE `device_mobile_position` (
187 212 --
188 213  
189 214 LOCK TABLES `device_mobile_position` WRITE;
190   -/*!40000 ALTER TABLE `device_mobile_position` DISABLE KEYS */;
191   -/*!40000 ALTER TABLE `device_mobile_position` ENABLE KEYS */;
  215 +/*!40000 ALTER TABLE `device_mobile_position`
  216 + DISABLE KEYS */;
  217 +/*!40000 ALTER TABLE `device_mobile_position`
  218 + ENABLE KEYS */;
192 219 UNLOCK TABLES;
193 220  
194 221 --
... ... @@ -196,24 +223,29 @@ UNLOCK TABLES;
196 223 --
197 224  
198 225 DROP TABLE IF EXISTS `gb_stream`;
199   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  226 +/*!40101 SET @saved_cs_client = @@character_set_client */;
200 227 /*!50503 SET character_set_client = utf8mb4 */;
201   -CREATE TABLE `gb_stream` (
202   - `gbStreamId` int NOT NULL AUTO_INCREMENT,
203   - `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
204   - `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
205   - `gbId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
206   - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
207   - `longitude` double DEFAULT NULL,
208   - `latitude` double DEFAULT NULL,
209   - `streamType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
210   - `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
211   - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
212   - `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
213   - PRIMARY KEY (`gbStreamId`) USING BTREE,
214   - UNIQUE KEY `app` (`app`,`stream`) USING BTREE,
215   - UNIQUE KEY `gbId` (`gbId`) USING BTREE
216   -) ENGINE=InnoDB AUTO_INCREMENT=301681 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  228 +CREATE TABLE `gb_stream`
  229 +(
  230 + `gbStreamId` int NOT NULL AUTO_INCREMENT,
  231 + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  232 + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  233 + `gbId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  234 + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  235 + `longitude` double DEFAULT NULL,
  236 + `latitude` double DEFAULT NULL,
  237 + `streamType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  238 + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  239 + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  240 + `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  241 + PRIMARY KEY (`gbStreamId`) USING BTREE,
  242 + UNIQUE KEY `app` (`app`, `stream`) USING BTREE,
  243 + UNIQUE KEY `gbId` (`gbId`) USING BTREE
  244 +) ENGINE = InnoDB
  245 + AUTO_INCREMENT = 301754
  246 + DEFAULT CHARSET = utf8mb4
  247 + COLLATE = utf8mb4_general_ci
  248 + ROW_FORMAT = DYNAMIC;
217 249 /*!40101 SET character_set_client = @saved_cs_client */;
218 250  
219 251 --
... ... @@ -221,8 +253,10 @@ CREATE TABLE `gb_stream` (
221 253 --
222 254  
223 255 LOCK TABLES `gb_stream` WRITE;
224   -/*!40000 ALTER TABLE `gb_stream` DISABLE KEYS */;
225   -/*!40000 ALTER TABLE `gb_stream` ENABLE KEYS */;
  256 +/*!40000 ALTER TABLE `gb_stream`
  257 + DISABLE KEYS */;
  258 +/*!40000 ALTER TABLE `gb_stream`
  259 + ENABLE KEYS */;
226 260 UNLOCK TABLES;
227 261  
228 262 --
... ... @@ -230,20 +264,25 @@ UNLOCK TABLES;
230 264 --
231 265  
232 266 DROP TABLE IF EXISTS `log`;
233   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  267 +/*!40101 SET @saved_cs_client = @@character_set_client */;
234 268 /*!50503 SET character_set_client = utf8mb4 */;
235   -CREATE TABLE `log` (
236   - `id` int NOT NULL AUTO_INCREMENT,
237   - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
238   - `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
239   - `uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
240   - `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
241   - `result` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
242   - `timing` bigint NOT NULL,
243   - `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
244   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
245   - PRIMARY KEY (`id`) USING BTREE
246   -) ENGINE=InnoDB AUTO_INCREMENT=34997 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  269 +CREATE TABLE `log`
  270 +(
  271 + `id` int NOT NULL AUTO_INCREMENT,
  272 + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  273 + `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  274 + `uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  275 + `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  276 + `result` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  277 + `timing` bigint NOT NULL,
  278 + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  279 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  280 + PRIMARY KEY (`id`) USING BTREE
  281 +) ENGINE = InnoDB
  282 + AUTO_INCREMENT = 42703
  283 + DEFAULT CHARSET = utf8mb4
  284 + COLLATE = utf8mb4_general_ci
  285 + ROW_FORMAT = DYNAMIC;
247 286 /*!40101 SET character_set_client = @saved_cs_client */;
248 287  
249 288 --
... ... @@ -251,8 +290,10 @@ CREATE TABLE `log` (
251 290 --
252 291  
253 292 LOCK TABLES `log` WRITE;
254   -/*!40000 ALTER TABLE `log` DISABLE KEYS */;
255   -/*!40000 ALTER TABLE `log` ENABLE KEYS */;
  293 +/*!40000 ALTER TABLE `log`
  294 + DISABLE KEYS */;
  295 +/*!40000 ALTER TABLE `log`
  296 + ENABLE KEYS */;
256 297 UNLOCK TABLES;
257 298  
258 299 --
... ... @@ -260,35 +301,39 @@ UNLOCK TABLES;
260 301 --
261 302  
262 303 DROP TABLE IF EXISTS `media_server`;
263   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  304 +/*!40101 SET @saved_cs_client = @@character_set_client */;
264 305 /*!50503 SET character_set_client = utf8mb4 */;
265   -CREATE TABLE `media_server` (
266   - `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
267   - `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
268   - `hookIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
269   - `sdpIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
270   - `streamIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
271   - `httpPort` int NOT NULL,
272   - `httpSSlPort` int NOT NULL,
273   - `rtmpPort` int NOT NULL,
274   - `rtmpSSlPort` int NOT NULL,
275   - `rtpProxyPort` int NOT NULL,
276   - `rtspPort` int NOT NULL,
277   - `rtspSSLPort` int NOT NULL,
278   - `autoConfig` int NOT NULL,
279   - `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
280   - `streamNoneReaderDelayMS` int NOT NULL,
281   - `rtpEnable` int NOT NULL,
282   - `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
283   - `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
284   - `recordAssistPort` int NOT NULL,
285   - `defaultServer` int NOT NULL,
286   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
287   - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
288   - `hookAliveInterval` int NOT NULL,
289   - PRIMARY KEY (`id`) USING BTREE,
290   - UNIQUE KEY `media_server_i` (`ip`,`httpPort`) USING BTREE
291   -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  306 +CREATE TABLE `media_server`
  307 +(
  308 + `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  309 + `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  310 + `hookIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  311 + `sdpIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  312 + `streamIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  313 + `httpPort` int NOT NULL,
  314 + `httpSSlPort` int NOT NULL,
  315 + `rtmpPort` int NOT NULL,
  316 + `rtmpSSlPort` int NOT NULL,
  317 + `rtpProxyPort` int NOT NULL,
  318 + `rtspPort` int NOT NULL,
  319 + `rtspSSLPort` int NOT NULL,
  320 + `autoConfig` int NOT NULL,
  321 + `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  322 + `streamNoneReaderDelayMS` int NOT NULL,
  323 + `rtpEnable` int NOT NULL,
  324 + `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  325 + `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  326 + `recordAssistPort` int NOT NULL,
  327 + `defaultServer` int NOT NULL,
  328 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  329 + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  330 + `hookAliveInterval` int NOT NULL,
  331 + PRIMARY KEY (`id`) USING BTREE,
  332 + UNIQUE KEY `media_server_i` (`ip`, `httpPort`) USING BTREE
  333 +) ENGINE = InnoDB
  334 + DEFAULT CHARSET = utf8mb4
  335 + COLLATE = utf8mb4_general_ci
  336 + ROW_FORMAT = DYNAMIC;
292 337 /*!40101 SET character_set_client = @saved_cs_client */;
293 338  
294 339 --
... ... @@ -296,8 +341,10 @@ CREATE TABLE `media_server` (
296 341 --
297 342  
298 343 LOCK TABLES `media_server` WRITE;
299   -/*!40000 ALTER TABLE `media_server` DISABLE KEYS */;
300   -/*!40000 ALTER TABLE `media_server` ENABLE KEYS */;
  344 +/*!40000 ALTER TABLE `media_server`
  345 + DISABLE KEYS */;
  346 +/*!40000 ALTER TABLE `media_server`
  347 + ENABLE KEYS */;
301 348 UNLOCK TABLES;
302 349  
303 350 --
... ... @@ -305,39 +352,44 @@ UNLOCK TABLES;
305 352 --
306 353  
307 354 DROP TABLE IF EXISTS `parent_platform`;
308   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  355 +/*!40101 SET @saved_cs_client = @@character_set_client */;
309 356 /*!50503 SET character_set_client = utf8mb4 */;
310   -CREATE TABLE `parent_platform` (
311   - `id` int NOT NULL AUTO_INCREMENT,
312   - `enable` int DEFAULT NULL,
313   - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
314   - `serverGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
315   - `serverGBDomain` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
316   - `serverIP` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
317   - `serverPort` int DEFAULT NULL,
318   - `deviceGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
319   - `deviceIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
320   - `devicePort` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
321   - `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
322   - `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
323   - `expires` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
324   - `keepTimeout` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
325   - `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
326   - `characterSet` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
327   - `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
328   - `ptz` int DEFAULT NULL,
329   - `rtcp` int DEFAULT NULL,
330   - `status` bit(1) DEFAULT NULL,
331   - `shareAllLiveStream` int DEFAULT NULL,
332   - `startOfflinePush` int DEFAULT '0',
333   - `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
334   - `catalogGroup` int DEFAULT '1',
335   - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
336   - `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
337   - PRIMARY KEY (`id`) USING BTREE,
338   - UNIQUE KEY `parent_platform_id_uindex` (`id`) USING BTREE,
339   - UNIQUE KEY `parent_platform_pk` (`serverGBId`) USING BTREE
340   -) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  357 +CREATE TABLE `parent_platform`
  358 +(
  359 + `id` int NOT NULL AUTO_INCREMENT,
  360 + `enable` int DEFAULT NULL,
  361 + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  362 + `serverGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  363 + `serverGBDomain` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  364 + `serverIP` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  365 + `serverPort` int DEFAULT NULL,
  366 + `deviceGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  367 + `deviceIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  368 + `devicePort` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  369 + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  370 + `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  371 + `expires` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  372 + `keepTimeout` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  373 + `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  374 + `characterSet` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  375 + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  376 + `ptz` int DEFAULT NULL,
  377 + `rtcp` int DEFAULT NULL,
  378 + `status` bit(1) DEFAULT NULL,
  379 + `startOfflinePush` int DEFAULT '0 ',
  380 + `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  381 + `catalogGroup` int DEFAULT '1 ',
  382 + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  383 + `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  384 + `treeType` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
  385 + PRIMARY KEY (`id`) USING BTREE,
  386 + UNIQUE KEY `parent_platform_id_uindex` (`id`) USING BTREE,
  387 + UNIQUE KEY `parent_platform_pk` (`serverGBId`) USING BTREE
  388 +) ENGINE = InnoDB
  389 + AUTO_INCREMENT = 40
  390 + DEFAULT CHARSET = utf8mb4
  391 + COLLATE = utf8mb4_general_ci
  392 + ROW_FORMAT = DYNAMIC;
341 393 /*!40101 SET character_set_client = @saved_cs_client */;
342 394  
343 395 --
... ... @@ -345,8 +397,10 @@ CREATE TABLE `parent_platform` (
345 397 --
346 398  
347 399 LOCK TABLES `parent_platform` WRITE;
348   -/*!40000 ALTER TABLE `parent_platform` DISABLE KEYS */;
349   -/*!40000 ALTER TABLE `parent_platform` ENABLE KEYS */;
  400 +/*!40000 ALTER TABLE `parent_platform`
  401 + DISABLE KEYS */;
  402 +/*!40000 ALTER TABLE `parent_platform`
  403 + ENABLE KEYS */;
350 404 UNLOCK TABLES;
351 405  
352 406 --
... ... @@ -354,15 +408,21 @@ UNLOCK TABLES;
354 408 --
355 409  
356 410 DROP TABLE IF EXISTS `platform_catalog`;
357   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  411 +/*!40101 SET @saved_cs_client = @@character_set_client */;
358 412 /*!50503 SET character_set_client = utf8mb4 */;
359   -CREATE TABLE `platform_catalog` (
360   - `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
361   - `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
362   - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
363   - `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
364   - PRIMARY KEY (`id`) USING BTREE
365   -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  413 +CREATE TABLE `platform_catalog`
  414 +(
  415 + `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  416 + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  417 + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  418 + `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  419 + `civilCode` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  420 + `businessGroupId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  421 + PRIMARY KEY (`id`) USING BTREE
  422 +) ENGINE = InnoDB
  423 + DEFAULT CHARSET = utf8mb4
  424 + COLLATE = utf8mb4_general_ci
  425 + ROW_FORMAT = DYNAMIC;
366 426 /*!40101 SET character_set_client = @saved_cs_client */;
367 427  
368 428 --
... ... @@ -370,8 +430,10 @@ CREATE TABLE `platform_catalog` (
370 430 --
371 431  
372 432 LOCK TABLES `platform_catalog` WRITE;
373   -/*!40000 ALTER TABLE `platform_catalog` DISABLE KEYS */;
374   -/*!40000 ALTER TABLE `platform_catalog` ENABLE KEYS */;
  433 +/*!40000 ALTER TABLE `platform_catalog`
  434 + DISABLE KEYS */;
  435 +/*!40000 ALTER TABLE `platform_catalog`
  436 + ENABLE KEYS */;
375 437 UNLOCK TABLES;
376 438  
377 439 --
... ... @@ -379,15 +441,20 @@ UNLOCK TABLES;
379 441 --
380 442  
381 443 DROP TABLE IF EXISTS `platform_gb_channel`;
382   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  444 +/*!40101 SET @saved_cs_client = @@character_set_client */;
383 445 /*!50503 SET character_set_client = utf8mb4 */;
384   -CREATE TABLE `platform_gb_channel` (
385   - `id` int NOT NULL AUTO_INCREMENT,
386   - `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
387   - `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
388   - `deviceChannelId` int NOT NULL,
389   - PRIMARY KEY (`id`) USING BTREE
390   -) ENGINE=InnoDB AUTO_INCREMENT=4889 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  446 +CREATE TABLE `platform_gb_channel`
  447 +(
  448 + `id` int NOT NULL AUTO_INCREMENT,
  449 + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  450 + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  451 + `deviceChannelId` int NOT NULL,
  452 + PRIMARY KEY (`id`) USING BTREE
  453 +) ENGINE = InnoDB
  454 + AUTO_INCREMENT = 4915
  455 + DEFAULT CHARSET = utf8mb4
  456 + COLLATE = utf8mb4_general_ci
  457 + ROW_FORMAT = DYNAMIC;
391 458 /*!40101 SET character_set_client = @saved_cs_client */;
392 459  
393 460 --
... ... @@ -395,8 +462,10 @@ CREATE TABLE `platform_gb_channel` (
395 462 --
396 463  
397 464 LOCK TABLES `platform_gb_channel` WRITE;
398   -/*!40000 ALTER TABLE `platform_gb_channel` DISABLE KEYS */;
399   -/*!40000 ALTER TABLE `platform_gb_channel` ENABLE KEYS */;
  465 +/*!40000 ALTER TABLE `platform_gb_channel`
  466 + DISABLE KEYS */;
  467 +/*!40000 ALTER TABLE `platform_gb_channel`
  468 + ENABLE KEYS */;
400 469 UNLOCK TABLES;
401 470  
402 471 --
... ... @@ -404,16 +473,21 @@ UNLOCK TABLES;
404 473 --
405 474  
406 475 DROP TABLE IF EXISTS `platform_gb_stream`;
407   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  476 +/*!40101 SET @saved_cs_client = @@character_set_client */;
408 477 /*!50503 SET character_set_client = utf8mb4 */;
409   -CREATE TABLE `platform_gb_stream` (
410   - `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
411   - `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
412   - `gbStreamId` int NOT NULL,
413   - `id` int NOT NULL AUTO_INCREMENT,
414   - PRIMARY KEY (`id`) USING BTREE,
415   - UNIQUE KEY `platform_gb_stream_pk` (`platformId`,`catalogId`,`gbStreamId`) USING BTREE
416   -) ENGINE=InnoDB AUTO_INCREMENT=302077 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  478 +CREATE TABLE `platform_gb_stream`
  479 +(
  480 + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  481 + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  482 + `gbStreamId` int NOT NULL,
  483 + `id` int NOT NULL AUTO_INCREMENT,
  484 + PRIMARY KEY (`id`) USING BTREE,
  485 + UNIQUE KEY `platform_gb_stream_pk` (`platformId`, `catalogId`, `gbStreamId`) USING BTREE
  486 +) ENGINE = InnoDB
  487 + AUTO_INCREMENT = 302149
  488 + DEFAULT CHARSET = utf8mb4
  489 + COLLATE = utf8mb4_general_ci
  490 + ROW_FORMAT = DYNAMIC;
417 491 /*!40101 SET character_set_client = @saved_cs_client */;
418 492  
419 493 --
... ... @@ -421,8 +495,10 @@ CREATE TABLE `platform_gb_stream` (
421 495 --
422 496  
423 497 LOCK TABLES `platform_gb_stream` WRITE;
424   -/*!40000 ALTER TABLE `platform_gb_stream` DISABLE KEYS */;
425   -/*!40000 ALTER TABLE `platform_gb_stream` ENABLE KEYS */;
  498 +/*!40000 ALTER TABLE `platform_gb_stream`
  499 + DISABLE KEYS */;
  500 +/*!40000 ALTER TABLE `platform_gb_stream`
  501 + ENABLE KEYS */;
426 502 UNLOCK TABLES;
427 503  
428 504 --
... ... @@ -430,31 +506,36 @@ UNLOCK TABLES;
430 506 --
431 507  
432 508 DROP TABLE IF EXISTS `stream_proxy`;
433   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  509 +/*!40101 SET @saved_cs_client = @@character_set_client */;
434 510 /*!50503 SET character_set_client = utf8mb4 */;
435   -CREATE TABLE `stream_proxy` (
436   - `id` int NOT NULL AUTO_INCREMENT,
437   - `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
438   - `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
439   - `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
440   - `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
441   - `src_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
442   - `dst_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
443   - `timeout_ms` int DEFAULT NULL,
444   - `ffmpeg_cmd_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
445   - `rtp_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
446   - `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
447   - `enable_hls` bit(1) DEFAULT NULL,
448   - `enable_mp4` bit(1) DEFAULT NULL,
449   - `enable` bit(1) NOT NULL,
450   - `status` bit(1) NOT NULL,
451   - `enable_remove_none_reader` bit(1) NOT NULL,
452   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
453   - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
454   - `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
455   - PRIMARY KEY (`id`) USING BTREE,
456   - UNIQUE KEY `stream_proxy_pk` (`app`,`stream`) USING BTREE
457   -) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  511 +CREATE TABLE `stream_proxy`
  512 +(
  513 + `id` int NOT NULL AUTO_INCREMENT,
  514 + `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  515 + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  516 + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  517 + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  518 + `src_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  519 + `dst_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  520 + `timeout_ms` int DEFAULT NULL,
  521 + `ffmpeg_cmd_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  522 + `rtp_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  523 + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  524 + `enable_hls` bit(1) DEFAULT NULL,
  525 + `enable_mp4` bit(1) DEFAULT NULL,
  526 + `enable` bit(1) NOT NULL,
  527 + `status` bit(1) NOT NULL,
  528 + `enable_remove_none_reader` bit(1) NOT NULL,
  529 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  530 + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  531 + `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  532 + PRIMARY KEY (`id`) USING BTREE,
  533 + UNIQUE KEY `stream_proxy_pk` (`app`, `stream`) USING BTREE
  534 +) ENGINE = InnoDB
  535 + AUTO_INCREMENT = 66
  536 + DEFAULT CHARSET = utf8mb4
  537 + COLLATE = utf8mb4_general_ci
  538 + ROW_FORMAT = DYNAMIC;
458 539 /*!40101 SET character_set_client = @saved_cs_client */;
459 540  
460 541 --
... ... @@ -462,8 +543,10 @@ CREATE TABLE `stream_proxy` (
462 543 --
463 544  
464 545 LOCK TABLES `stream_proxy` WRITE;
465   -/*!40000 ALTER TABLE `stream_proxy` DISABLE KEYS */;
466   -/*!40000 ALTER TABLE `stream_proxy` ENABLE KEYS */;
  546 +/*!40000 ALTER TABLE `stream_proxy`
  547 + DISABLE KEYS */;
  548 +/*!40000 ALTER TABLE `stream_proxy`
  549 + ENABLE KEYS */;
467 550 UNLOCK TABLES;
468 551  
469 552 --
... ... @@ -471,25 +554,32 @@ UNLOCK TABLES;
471 554 --
472 555  
473 556 DROP TABLE IF EXISTS `stream_push`;
474   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  557 +/*!40101 SET @saved_cs_client = @@character_set_client */;
475 558 /*!50503 SET character_set_client = utf8mb4 */;
476   -CREATE TABLE `stream_push` (
477   - `id` int NOT NULL AUTO_INCREMENT,
478   - `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
479   - `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
480   - `totalReaderCount` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
481   - `originType` int DEFAULT NULL,
482   - `originTypeStr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
483   - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
484   - `aliveSecond` int DEFAULT NULL,
485   - `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
486   - `serverId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
487   - `pushTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
488   - `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
489   - `status` int DEFAULT NULL,
490   - PRIMARY KEY (`id`) USING BTREE,
491   - UNIQUE KEY `stream_push_pk` (`app`,`stream`) USING BTREE
492   -) ENGINE=InnoDB AUTO_INCREMENT=305315 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  559 +CREATE TABLE `stream_push`
  560 +(
  561 + `id` int NOT NULL AUTO_INCREMENT,
  562 + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  563 + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  564 + `totalReaderCount` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  565 + `originType` int DEFAULT NULL,
  566 + `originTypeStr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  567 + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  568 + `aliveSecond` int DEFAULT NULL,
  569 + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  570 + `serverId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
  571 + `pushTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  572 + `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  573 + `status` int DEFAULT NULL,
  574 + `pushIng` int DEFAULT NULL,
  575 + `self` int DEFAULT NULL,
  576 + PRIMARY KEY (`id`) USING BTREE,
  577 + UNIQUE KEY `stream_push_pk` (`app`, `stream`) USING BTREE
  578 +) ENGINE = InnoDB
  579 + AUTO_INCREMENT = 305415
  580 + DEFAULT CHARSET = utf8mb4
  581 + COLLATE = utf8mb4_general_ci
  582 + ROW_FORMAT = DYNAMIC;
493 583 /*!40101 SET character_set_client = @saved_cs_client */;
494 584  
495 585 --
... ... @@ -497,8 +587,10 @@ CREATE TABLE `stream_push` (
497 587 --
498 588  
499 589 LOCK TABLES `stream_push` WRITE;
500   -/*!40000 ALTER TABLE `stream_push` DISABLE KEYS */;
501   -/*!40000 ALTER TABLE `stream_push` ENABLE KEYS */;
  590 +/*!40000 ALTER TABLE `stream_push`
  591 + DISABLE KEYS */;
  592 +/*!40000 ALTER TABLE `stream_push`
  593 + ENABLE KEYS */;
502 594 UNLOCK TABLES;
503 595  
504 596 --
... ... @@ -506,19 +598,24 @@ UNLOCK TABLES;
506 598 --
507 599  
508 600 DROP TABLE IF EXISTS `user`;
509   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  601 +/*!40101 SET @saved_cs_client = @@character_set_client */;
510 602 /*!50503 SET character_set_client = utf8mb4 */;
511   -CREATE TABLE `user` (
512   - `id` int NOT NULL AUTO_INCREMENT,
513   - `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
514   - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
515   - `roleId` int NOT NULL,
516   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
517   - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
518   - `pushKey` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
519   - PRIMARY KEY (`id`) USING BTREE,
520   - UNIQUE KEY `user_username_uindex` (`username`) USING BTREE
521   -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  603 +CREATE TABLE `user`
  604 +(
  605 + `id` int NOT NULL AUTO_INCREMENT,
  606 + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  607 + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  608 + `roleId` int NOT NULL,
  609 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  610 + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  611 + `pushKey` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
  612 + PRIMARY KEY (`id`) USING BTREE,
  613 + UNIQUE KEY `user_username_uindex` (`username`) USING BTREE
  614 +) ENGINE = InnoDB
  615 + AUTO_INCREMENT = 2
  616 + DEFAULT CHARSET = utf8mb4
  617 + COLLATE = utf8mb4_general_ci
  618 + ROW_FORMAT = DYNAMIC;
522 619 /*!40101 SET character_set_client = @saved_cs_client */;
523 620  
524 621 --
... ... @@ -526,9 +623,13 @@ CREATE TABLE `user` (
526 623 --
527 624  
528 625 LOCK TABLES `user` WRITE;
529   -/*!40000 ALTER TABLE `user` DISABLE KEYS */;
530   -INSERT INTO `user` VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021 - 04 - 13 14:14:57','2021 - 04 - 13 14:14:57','453df297a57a5a7438934sda801fc3');
531   -/*!40000 ALTER TABLE `user` ENABLE KEYS */;
  626 +/*!40000 ALTER TABLE `user`
  627 + DISABLE KEYS */;
  628 +INSERT INTO `user`
  629 +VALUES (1, 'admin', '21232f297a57a5a743894a0e4a801fc3', 1, '2021 - 04 - 13 14:14:57', '2021 - 04 - 13 14:14:57',
  630 + '01685cb9573ae25ec6c52142402da7c5');
  631 +/*!40000 ALTER TABLE `user`
  632 + ENABLE KEYS */;
532 633 UNLOCK TABLES;
533 634  
534 635 --
... ... @@ -536,16 +637,21 @@ UNLOCK TABLES;
536 637 --
537 638  
538 639 DROP TABLE IF EXISTS `user_role`;
539   -/*!40101 SET @saved_cs_client = @@character_set_client */;
  640 +/*!40101 SET @saved_cs_client = @@character_set_client */;
540 641 /*!50503 SET character_set_client = utf8mb4 */;
541   -CREATE TABLE `user_role` (
542   - `id` int NOT NULL AUTO_INCREMENT,
543   - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
544   - `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
545   - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
546   - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
547   - PRIMARY KEY (`id`) USING BTREE
548   -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
  642 +CREATE TABLE `user_role`
  643 +(
  644 + `id` int NOT NULL AUTO_INCREMENT,
  645 + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  646 + `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  647 + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  648 + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  649 + PRIMARY KEY (`id`) USING BTREE
  650 +) ENGINE = InnoDB
  651 + AUTO_INCREMENT = 2
  652 + DEFAULT CHARSET = utf8mb4
  653 + COLLATE = utf8mb4_general_ci
  654 + ROW_FORMAT = DYNAMIC;
549 655 /*!40101 SET character_set_client = @saved_cs_client */;
550 656  
551 657 --
... ... @@ -553,18 +659,21 @@ CREATE TABLE `user_role` (
553 659 --
554 660  
555 661 LOCK TABLES `user_role` WRITE;
556   -/*!40000 ALTER TABLE `user_role` DISABLE KEYS */;
557   -INSERT INTO `user_role` VALUES (1,'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');
558   -/*!40000 ALTER TABLE `user_role` ENABLE KEYS */;
  662 +/*!40000 ALTER TABLE `user_role`
  663 + DISABLE KEYS */;
  664 +INSERT INTO `user_role`
  665 +VALUES (1, 'admin', '0 ', '2021 - 04 - 13 14:14:57', '2021 - 04 - 13 14:14:57');
  666 +/*!40000 ALTER TABLE `user_role`
  667 + ENABLE KEYS */;
559 668 UNLOCK TABLES;
560   -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
  669 +/*!40103 SET TIME_ZONE = @OLD_TIME_ZONE */;
561 670  
562   -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
563   -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
564   -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
565   -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
566   -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
567   -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
568   -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
  671 +/*!40101 SET SQL_MODE = @OLD_SQL_MODE */;
  672 +/*!40014 SET FOREIGN_KEY_CHECKS = @OLD_FOREIGN_KEY_CHECKS */;
  673 +/*!40014 SET UNIQUE_CHECKS = @OLD_UNIQUE_CHECKS */;
  674 +/*!40101 SET CHARACTER_SET_CLIENT = @OLD_CHARACTER_SET_CLIENT */;
  675 +/*!40101 SET CHARACTER_SET_RESULTS = @OLD_CHARACTER_SET_RESULTS */;
  676 +/*!40101 SET COLLATION_CONNECTION = @OLD_COLLATION_CONNECTION */;
  677 +/*!40111 SET SQL_NOTES = @OLD_SQL_NOTES */;
569 678  
570   --- Dump completed on 2022-07-11 17:32:51
  679 +-- Dump completed on 2022-07-27 14:51:08
... ...
sql/update.sql
... ... @@ -71,6 +71,8 @@ alter table stream_push
71 71 alter table stream_push
72 72 add updateTime varchar(50) default null;
73 73 alter table stream_push
  74 + add pushIng int DEFAULT NULL;
  75 +alter table stream_push
74 76 change createStamp createTime varchar(50) default null;
75 77  
76 78 alter table gb_stream
... ... @@ -78,4 +80,20 @@ alter table gb_stream
78 80  
79 81 alter table user
80 82 add pushKey varchar(50) default null;
  83 +update user set pushKey='453df297a57a5a7438934sda801fc3' where id=1;
  84 +
  85 +alter table parent_platform
  86 + add treeType varchar(50) not null;
  87 +update parent_platform set parent_platform.treeType='BusinessGroup';
  88 +alter table parent_platform drop shareAllLiveStream;
  89 +
  90 +alter table platform_catalog
  91 + add civilCode varchar(50) default null;
  92 +alter table platform_catalog
  93 + add businessGroupId varchar(50) default null;
  94 +
  95 +/********************* ADD ***************************/
  96 +alter table stream_push
  97 + add self int DEFAULT NULL;
  98 +
81 99  
... ...
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
... ... @@ -11,7 +11,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
11 11 import springfox.documentation.oas.annotations.EnableOpenApi;
12 12  
13 13 /**
14   - *
  14 + * 启动类
15 15 */
16 16 @ServletComponentScan("com.genersoft.iot.vmp.conf")
17 17 @SpringBootApplication
... ...
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
... ... @@ -104,6 +104,10 @@ public class VideoManagerConstants {
104 104 // 设备状态订阅的通知
105 105 public static final String VM_MSG_SUBSCRIBE_DEVICE_STATUS = "device";
106 106  
  107 +
  108 +
  109 +
  110 +
107 111 //************************** 第三方 ****************************************
108 112 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
109 113 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
... ...
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
1 1 package com.genersoft.iot.vmp.conf;
2 2  
3 3 import com.genersoft.iot.vmp.common.VideoManagerConstants;
4   -import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener;
5   -import com.genersoft.iot.vmp.service.impl.RedisGpsMsgListener;
6   -import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
7   -import com.genersoft.iot.vmp.service.impl.RedisStreamMsgListener;
  4 +import com.genersoft.iot.vmp.service.impl.*;
8 5 import org.apache.commons.lang3.StringUtils;
9 6 import org.springframework.beans.factory.annotation.Autowired;
10 7 import org.springframework.beans.factory.annotation.Value;
... ... @@ -60,6 +57,9 @@ public class RedisConfig extends CachingConfigurerSupport {
60 57 @Autowired
61 58 private RedisGbPlayMsgListener redisGbPlayMsgListener;
62 59  
  60 + @Autowired
  61 + private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener;
  62 +
63 63 @Bean
64 64 public JedisPool jedisPool() {
65 65 if (StringUtils.isBlank(password)) {
... ... @@ -108,6 +108,7 @@ public class RedisConfig extends CachingConfigurerSupport {
108 108 container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
109 109 container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
110 110 container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY));
  111 + container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
111 112 return container;
112 113 }
113 114  
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
1 1 package com.genersoft.iot.vmp.conf;
2 2  
3   -import io.swagger.models.auth.In;
4 3 import org.springframework.boot.context.properties.ConfigurationProperties;
5 4 import org.springframework.stereotype.Component;
6 5  
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
1 1 package com.genersoft.iot.vmp.conf.security;
2 2  
3   -import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
4   -import com.genersoft.iot.vmp.service.IUserService;
5   -import com.genersoft.iot.vmp.storager.dao.dto.User;
6   -import com.github.xiaoymin.knife4j.core.util.StrUtil;
  3 +import java.time.LocalDateTime;
  4 +
7 5 import org.slf4j.Logger;
8 6 import org.slf4j.LoggerFactory;
9 7 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -12,7 +10,10 @@ import org.springframework.security.core.userdetails.UserDetailsService;
12 10 import org.springframework.security.core.userdetails.UsernameNotFoundException;
13 11 import org.springframework.stereotype.Component;
14 12  
15   -import java.time.LocalDateTime;
  13 +import com.alibaba.excel.util.StringUtils;
  14 +import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
  15 +import com.genersoft.iot.vmp.service.IUserService;
  16 +import com.genersoft.iot.vmp.storager.dao.dto.User;
16 17  
17 18 /**
18 19 * 用户登录认证逻辑
... ... @@ -27,7 +28,7 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService {
27 28  
28 29 @Override
29 30 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
30   - if (StrUtil.isBlank(username)) {
  31 + if (StringUtils.isBlank(username)) {
31 32 logger.info("登录用户:{} 不存在", username);
32 33 throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
33 34 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java
... ... @@ -3,8 +3,14 @@ package com.genersoft.iot.vmp.gb28181.bean;
3 3 import java.time.Instant;
4 4 import java.util.List;
5 5  
  6 +/**
  7 + * @author lin
  8 + */
6 9 public class CatalogData {
7   - private int sn; // 命令序列号
  10 + /**
  11 + * 命令序列号
  12 + */
  13 + private int sn;
8 14 private int total;
9 15 private List<DeviceChannel> channelList;
10 16 private Instant lastTime;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
... ... @@ -105,11 +105,6 @@ public class ParentPlatform {
105 105 private int channelCount;
106 106  
107 107 /**
108   - * 共享所有的直播流
109   - */
110   - private boolean shareAllLiveStream;
111   -
112   - /**
113 108 * 默认目录Id,自动添加的通道多放在这个目录下
114 109 */
115 110 private String catalogId;
... ... @@ -154,6 +149,11 @@ public class ParentPlatform {
154 149 */
155 150 private String createTime;
156 151  
  152 + /**
  153 + * 树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGroup
  154 + */
  155 + private String treeType;
  156 +
157 157 public Integer getId() {
158 158 return id;
159 159 }
... ... @@ -314,15 +314,6 @@ public class ParentPlatform {
314 314 this.channelCount = channelCount;
315 315 }
316 316  
317   -
318   - public boolean isShareAllLiveStream() {
319   - return shareAllLiveStream;
320   - }
321   -
322   - public void setShareAllLiveStream(boolean shareAllLiveStream) {
323   - this.shareAllLiveStream = shareAllLiveStream;
324   - }
325   -
326 317 public String getCatalogId() {
327 318 return catalogId;
328 319 }
... ... @@ -394,4 +385,12 @@ public class ParentPlatform {
394 385 public void setCreateTime(String createTime) {
395 386 this.createTime = createTime;
396 387 }
  388 +
  389 + public String getTreeType() {
  390 + return treeType;
  391 + }
  392 +
  393 + public void setTreeType(String treeType) {
  394 + this.treeType = treeType;
  395 + }
397 396 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java
1 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
  3 +/**
  4 + * 国标级联-目录
  5 + * @author lin
  6 + */
3 7 public class PlatformCatalog {
4 8 private String id;
5 9 private String name;
6 10 private String platformId;
7 11 private String parentId;
8   - private int childrenCount; // 子节点数
9   - private int type; // 0 目录, 1 国标通道, 2 直播流
  12 +
  13 + private String civilCode;
  14 +
  15 + private String businessGroupId;
  16 +
  17 + /**
  18 + * 子节点数
  19 + */
  20 + private int childrenCount;
  21 +
  22 + /**
  23 + * 0 目录, 1 国标通道, 2 直播流
  24 + */
  25 + private int type;
10 26  
11 27 public String getId() {
12 28 return id;
... ... @@ -68,4 +84,19 @@ public class PlatformCatalog {
68 84 this.type = 2;
69 85 }
70 86  
  87 + public String getCivilCode() {
  88 + return civilCode;
  89 + }
  90 +
  91 + public void setCivilCode(String civilCode) {
  92 + this.civilCode = civilCode;
  93 + }
  94 +
  95 + public String getBusinessGroupId() {
  96 + return businessGroupId;
  97 + }
  98 +
  99 + public void setBusinessGroupId(String businessGroupId) {
  100 + this.businessGroupId = businessGroupId;
  101 + }
71 102 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java
... ... @@ -2,12 +2,15 @@ package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3 3 /**
4 4 * 摄像机同步状态
  5 + * @author lin
5 6 */
6 7 public class SyncStatus {
7 8 private int total;
8 9 private int current;
9 10 private String errorMsg;
10 11  
  12 + private boolean syncIng;
  13 +
11 14 public int getTotal() {
12 15 return total;
13 16 }
... ... @@ -31,4 +34,12 @@ public class SyncStatus {
31 34 public void setErrorMsg(String errorMsg) {
32 35 this.errorMsg = errorMsg;
33 36 }
  37 +
  38 + public boolean isSyncIng() {
  39 + return syncIng;
  40 + }
  41 +
  42 + public void setSyncIng(boolean syncIng) {
  43 + this.syncIng = syncIng;
  44 + }
34 45 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +/**
  4 + * 目录结构类型
  5 + * @author lin
  6 + */
  7 +public class TreeType {
  8 + public static final String BUSINESS_GROUP = "BusinessGroup";
  9 + public static final String CIVIL_CODE = "CivilCode";
  10 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
... ... @@ -65,7 +65,6 @@ public class PlatformNotRegisterEventLister implements ApplicationListener&lt;Platf
65 65 }
66 66 // 查询是否有推流, 如果有则都停止
67 67 List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(event.getPlatformGbID());
68   - logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流size", sendRtpItems.size());
69 68 if (sendRtpItems != null && sendRtpItems.size() > 0) {
70 69 logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流", event.getPlatformGbID());
71 70 for (SendRtpItem sendRtpItem : sendRtpItems) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java
... ... @@ -11,13 +11,40 @@ public class CatalogEvent extends ApplicationEvent {
11 11 super(source);
12 12 }
13 13  
14   - public static final String ON = "ON"; // 上线
15   - public static final String OFF = "OFF"; // 离线
16   - public static final String VLOST = "VLOST"; // 视频丢失
17   - public static final String DEFECT = "DEFECT"; // 故障
18   - public static final String ADD = "ADD"; // 增加
19   - public static final String DEL = "DEL"; // 删除
20   - public static final String UPDATE = "UPDATE"; // 更新
  14 + /**
  15 + * 上线
  16 + */
  17 + public static final String ON = "ON";
  18 +
  19 + /**
  20 + * 离线
  21 + */
  22 + public static final String OFF = "OFF";
  23 +
  24 + /**
  25 + * 视频丢失
  26 + */
  27 + public static final String VLOST = "VLOST";
  28 +
  29 + /**
  30 + * 故障
  31 + */
  32 + public static final String DEFECT = "DEFECT";
  33 +
  34 + /**
  35 + * 增加
  36 + */
  37 + public static final String ADD = "ADD";
  38 +
  39 + /**
  40 + * 删除
  41 + */
  42 + public static final String DEL = "DEL";
  43 +
  44 + /**
  45 + * 更新
  46 + */
  47 + public static final String UPDATE = "UPDATE";
21 48  
22 49 private List<DeviceChannel> deviceChannels;
23 50 private List<GbStream> gbStreams;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
... ... @@ -58,17 +58,16 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
58 58 ParentPlatform parentPlatform = null;
59 59  
60 60 Map<String, List<ParentPlatform>> parentPlatformMap = new HashMap<>();
61   - if (event.getPlatformId() != null) {
  61 + if (!StringUtils.isEmpty(event.getPlatformId())) {
  62 + subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
  63 + if (subscribe == null) {
  64 + return;
  65 + }
62 66 parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformId());
63 67 if (parentPlatform != null && !parentPlatform.isStatus()) {
64 68 return;
65 69 }
66   - subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
67 70  
68   - if (subscribe == null) {
69   - logger.info("发送订阅消息时发现订阅信息已经不存在: {}", event.getPlatformId());
70   - return;
71   - }
72 71 }else {
73 72 // 获取所用订阅
74 73 List<String> platforms = subscribeHolder.getAllCatalogSubscribePlatform();
... ... @@ -144,11 +143,8 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
144 143 }
145 144 if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
146 145 for (GbStream gbStream : event.getGbStreams()) {
147   - DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform);
148   - if (deviceChannelByStream.getParentId().length() <= 10) { // 父节点是行政区划,则设置CivilCode使用此行政区划
149   - deviceChannelByStream.setCivilCode(deviceChannelByStream.getParentId());
150   - }
151   - deviceChannelList.add(deviceChannelByStream);
  146 + deviceChannelList.add(
  147 + gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform));
152 148 }
153 149 }
154 150 if (deviceChannelList.size() > 0) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
... ... @@ -84,6 +84,11 @@ public class CatalogDataCatch {
84 84 syncStatus.setCurrent(catalogData.getChannelList().size());
85 85 syncStatus.setTotal(catalogData.getTotal());
86 86 syncStatus.setErrorMsg(catalogData.getErrorMsg());
  87 + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) {
  88 + syncStatus.setSyncIng(false);
  89 + }else {
  90 + syncStatus.setSyncIng(true);
  91 + }
87 92 return syncStatus;
88 93 }
89 94  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java
... ... @@ -57,23 +57,17 @@ public class MobilePositionSubscribeHandlerTask implements ISubscribeTask {
57 57 SubscribeInfo subscribe = subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId());
58 58 if (subscribe != null) {
59 59  
60   -// if (!parentPlatform.isStatus()) {
61   -// logger.info("发送订阅时发现平台已经离线:{}", platformId);
62   -// return;
63   -// }
64 60 // TODO 暂时只处理视频流的回复,后续增加对国标设备的支持
65   - List<GbStream> gbStreams = storager.queryGbStreamListInPlatform(platform.getServerGBId());
  61 + List<DeviceChannel> gbStreams = storager.queryGbStreamListInPlatform(platform.getServerGBId());
66 62 if (gbStreams.size() == 0) {
67 63 logger.info("发送订阅时发现平台已经没有关联的直播流:{}", platform.getServerGBId());
68 64 return;
69 65 }
70   - for (GbStream gbStream : gbStreams) {
71   - String gbId = gbStream.getGbId();
  66 + for (DeviceChannel deviceChannel : gbStreams) {
  67 + String gbId = deviceChannel.getChannelId();
72 68 GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId);
73   - if (gpsMsgInfo != null) { // 无最新位置不发送
74   - if (logger.isDebugEnabled()) {
75   - logger.debug("无最新位置不发送");
76   - }
  69 + // 无最新位置不发送
  70 + if (gpsMsgInfo != null) {
77 71 // 经纬度都为0不发送
78 72 if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) {
79 73 continue;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java
... ... @@ -39,9 +39,9 @@ public class MobilePositionSubscribeTask implements ISubscribeTask {
39 39 dynamicTask.stop(taskKey);
40 40 }
41 41 sipCommander.mobilePositionSubscribe(device, dialog, eventResult -> {
42   -// if (eventResult.dialog != null || eventResult.dialog.getState().equals(DialogState.CONFIRMED)) {
43   -// dialog = eventResult.dialog;
44   -// }
  42 + if (eventResult.dialog != null || eventResult.dialog.getState().equals(DialogState.CONFIRMED)) {
  43 + dialog = eventResult.dialog;
  44 + }
45 45 ResponseEvent event = (ResponseEvent) eventResult.event;
46 46 if (event.getResponse().getRawContent() != null) {
47 47 // 成功
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -10,6 +10,9 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
10 10 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
11 11 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
12 12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  13 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  15 +import com.genersoft.iot.vmp.media.zlm.dto.HookType;
13 16 import com.genersoft.iot.vmp.utils.DateUtil;
14 17 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
15 18 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
... ... @@ -348,25 +351,19 @@ public class SIPCommander implements ISIPCommander {
348 351 @Override
349 352 public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
350 353 ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
351   - String streamId = ssrcInfo.getStream();
  354 + String stream = ssrcInfo.getStream();
352 355 try {
353 356 if (device == null) {
354 357 return;
355 358 }
356 359 String streamMode = device.getStreamMode().toUpperCase();
357 360  
358   - logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
359   - // 添加订阅
360   - JSONObject subscribeKey = new JSONObject();
361   - subscribeKey.put("app", "rtp");
362   - subscribeKey.put("stream", streamId);
363   - subscribeKey.put("regist", true);
364   - subscribeKey.put("schema", "rtmp");
365   - subscribeKey.put("mediaServerId", mediaServerItem.getId());
366   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
367   - (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  361 + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  362 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtmp", mediaServerItem.getId());
  363 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
368 364 if (event != null) {
369 365 event.response(mediaServerItemInUse, json);
  366 + subscribe.removeSubscribe(hookSubscribe);
370 367 }
371 368 });
372 369 //
... ... @@ -440,7 +437,7 @@ public class SIPCommander implements ISIPCommander {
440 437 errorEvent.response(e);
441 438 }), e ->{
442 439 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
443   - streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
  440 + streamSession.put(device.getDeviceId(), channelId ,"play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
444 441 streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
445 442 okEvent.response(e);
446 443 });
... ... @@ -530,21 +527,14 @@ public class SIPCommander implements ISIPCommander {
530 527  
531 528 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
532 529 : udpSipProvider.getNewCallId();
533   -
  530 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtmp", mediaServerItem.getId());
534 531 // 添加订阅
535   - JSONObject subscribeKey = new JSONObject();
536   - subscribeKey.put("app", "rtp");
537   - subscribeKey.put("stream", ssrcInfo.getStream());
538   - subscribeKey.put("regist", true);
539   - subscribeKey.put("schema", "rtmp");
540   - subscribeKey.put("mediaServerId", mediaServerItem.getId());
541   - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey);
542   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
543   - (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  532 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
544 533 if (hookEvent != null) {
545 534 InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream());
546 535 hookEvent.call(inviteStreamInfo);
547 536 }
  537 + subscribe.removeSubscribe(hookSubscribe);
548 538 });
549 539 Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
550 540  
... ... @@ -643,21 +633,15 @@ public class SIPCommander implements ISIPCommander {
643 633 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
644 634 : udpSipProvider.getNewCallId();
645 635  
  636 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
646 637 // 添加订阅
647   - JSONObject subscribeKey = new JSONObject();
648   - subscribeKey.put("app", "rtp");
649   - subscribeKey.put("stream", ssrcInfo.getStream());
650   - subscribeKey.put("regist", true);
651   - subscribeKey.put("mediaServerId", mediaServerItem.getId());
652   - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
653   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
654   - (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  638 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
655 639 hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
656   - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
657   - subscribeKey.put("regist", false);
658   - subscribeKey.put("schema", "rtmp");
  640 + subscribe.removeSubscribe(hookSubscribe);
  641 + hookSubscribe.getContent().put("regist", false);
  642 + hookSubscribe.getContent().put("schema", "rtmp");
659 643 // 添加流注销的订阅,注销了后向设备发送bye
660   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
  644 + subscribe.addSubscribe(hookSubscribe,
661 645 (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{
662 646 ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
663 647 if (transaction != null) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
... ... @@ -257,37 +257,37 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
257 257 catalogXml.append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
258 258 if (channels.size() > 0) {
259 259 for (DeviceChannel channel : channels) {
  260 + if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
  261 + channel.setParentId(parentPlatform.getDeviceGBId());
  262 + }
260 263 catalogXml.append("<Item>\r\n");
  264 + // 行政区划分组只需要这两项就可以
261 265 catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
262 266 catalogXml.append("<Name>" + channel.getName() + "</Name>\r\n");
263   - catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
264 267 if (channel.getParentId() != null) {
  268 + // 业务分组加上这一项即可,提高兼容性,
265 269 catalogXml.append("<ParentID>" + channel.getParentId() + "</ParentID>\r\n");
266 270 }
267   - if (channel.getChannelId().length() == 20) {
268   - if (Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织增加BusinessGroupID字段
269   - catalogXml.append("<BusinessGroupID>" + channel.getParentId() + "</BusinessGroupID>\r\n");
270   - }
  271 + if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) {
  272 + // 虚拟组织增加BusinessGroupID字段
  273 + catalogXml.append("<BusinessGroupID>" + channel.getParentId() + "</BusinessGroupID>\r\n");
  274 + }
  275 + catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
  276 + if (channel.getParental() == 0) {
  277 + // 通道项
271 278 catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
  279 + catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
272 280 catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
273   - catalogXml.append("<Status>" + (channel.getStatus() == 0?"OFF":"ON") + "</Status>\r\n");
274   - if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下字段
275   - catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
  281 + catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
  282 +
  283 + if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
276 284 catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
277   - catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n");
  285 + catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");
278 286 catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
279 287 catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
280   - catalogXml.append("<Longitude>" + channel.getLongitudeWgs84() + "</Longitude>\r\n");
281   - catalogXml.append("<Latitude>" + channel.getLatitudeWgs84() + "</Latitude>\r\n");
282   - catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
283   - catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
284   - catalogXml.append("<Info>\r\n");
285   - catalogXml.append("<PTZType>" + channel.getPTZType() + "</PTZType>\r\n");
286   - catalogXml.append("</Info>\r\n");
287 288 }
288   - }
289   -
290 289  
  290 + }
291 291 catalogXml.append("</Item>\r\n");
292 292 }
293 293 }
... ... @@ -592,27 +592,35 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
592 592 channel.setParentId(parentPlatform.getDeviceGBId());
593 593 }
594 594 catalogXml.append("<Item>\r\n");
  595 + // 行政区划分组只需要这两项就可以
595 596 catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
596 597 catalogXml.append("<Name>" + channel.getName() + "</Name>\r\n");
597   - catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
598   - catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
599 598 if (channel.getParentId() != null) {
  599 + // 业务分组加上这一项即可,提高兼容性,
600 600 catalogXml.append("<ParentID>" + channel.getParentId() + "</ParentID>\r\n");
601 601 }
602   - catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
603   - catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
604   - catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
605   - if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织增加BusinessGroupID字段
  602 + if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) {
  603 + // 虚拟组织增加BusinessGroupID字段
606 604 catalogXml.append("<BusinessGroupID>" + channel.getParentId() + "</BusinessGroupID>\r\n");
607 605 }
608   - if (channel.getChannelType() == 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
609   - catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
610   - catalogXml.append("<Owner>0</Owner>\r\n");
611   - catalogXml.append("<CivilCode>CivilCode</CivilCode>\r\n");
612   - catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
613   - }
614   - if (!"presence".equals(subscribeInfo.getEventType())) {
615   - catalogXml.append("<Event>" + type + "</Event>\r\n");
  606 + catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
  607 + if (channel.getParental() == 0) {
  608 + // 通道项
  609 + catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
  610 + catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
  611 + catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
  612 + catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
  613 +
  614 + if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
  615 + catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
  616 + catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");
  617 + catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
  618 + catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
  619 + }
  620 + if (!"presence".equals(subscribeInfo.getEventType())) {
  621 + catalogXml.append("<Event>" + type + "</Event>\r\n");
  622 + }
  623 +
616 624 }
617 625 catalogXml.append("</Item>\r\n");
618 626 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
... ... @@ -133,7 +133,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
133 133 SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
134 134 if (ssrcTransactionForPlay != null){
135 135 SIPDialog dialogForPlay = (SIPDialog) SerializeUtils.deSerialize(ssrcTransactionForPlay.getDialog());
136   - if (dialogForPlay.getCallId().equals(callIdHeader.getCallId())){
  136 + if (dialogForPlay.getCallId().getCallId().equals(callIdHeader.getCallId())){
137 137 // 释放ssrc
138 138 MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId());
139 139 if (mediaServerItem != null) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
... ... @@ -30,6 +30,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
30 30 import com.genersoft.iot.vmp.service.IMediaServerService;
31 31 import com.genersoft.iot.vmp.service.IMediaService;
32 32 import com.genersoft.iot.vmp.service.IPlayService;
  33 +import com.genersoft.iot.vmp.service.IStreamProxyService;
33 34 import com.genersoft.iot.vmp.service.IStreamPushService;
34 35 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
35 36 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
... ... @@ -81,6 +82,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
81 82 private IStreamPushService streamPushService;
82 83  
83 84 @Autowired
  85 + private IStreamProxyService streamProxyService;
  86 +
  87 + @Autowired
84 88 private IRedisCatchStorage redisCatchStorage;
85 89  
86 90 @Autowired
... ... @@ -94,7 +98,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
94 98  
95 99 @Autowired
96 100 private ISIPCommander commander;
97   -
  101 +
98 102 @Autowired
99 103 private AudioBroadcastManager audioBroadcastManager;
100 104  
... ... @@ -153,10 +157,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
153 157 // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
154 158 try {
155 159 Request request = evt.getRequest();
156   - SipURI sipUri = (SipURI) request.getRequestURI();
157   - //从subject读取channelId,不再从request-line读取。 有些平台request-line是平台国标编码,不是设备国标编码。
158   - //String channelId = sipURI.getUser();
159   - String channelId = SipUtils.getChannelIdFromHeader(request);
  160 + String channelId = SipUtils.getChannelIdFromRequest(request);
160 161 String requesterId = SipUtils.getUserIdFromFromHeader(request);
161 162 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
162 163 if (requesterId == null || channelId == null) {
... ... @@ -178,6 +179,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
178 179  
179 180 MediaServerItem mediaServerItem = null;
180 181 StreamPushItem streamPushItem = null;
  182 + StreamProxyItem proxyByAppAndStream =null;
181 183 // 不是通道可能是直播流
182 184 if (channel != null && gbStream == null) {
183 185 if (channel.getStatus() == 0) {
... ... @@ -211,6 +213,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
211 213 responseAck(evt, Response.GONE);
212 214 return;
213 215 }
  216 + }else if("proxy".equals(gbStream.getStreamType())){
  217 + proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream());
  218 + if (proxyByAppAndStream == null) {
  219 + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
  220 + responseAck(evt, Response.GONE);
  221 + return;
  222 + }
214 223 }
215 224 }
216 225 responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
... ... @@ -452,18 +461,35 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
452 461 }
453 462 }
454 463 } else if (gbStream != null) {
455   - if (streamPushItem.isStatus()) {
456   - // 在线状态
457   - pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
458   - mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
459   - } else {
460   - // 不在线 拉起
461   - notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
462   - mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
463   - }
  464 + if("push".equals(gbStream.getStreamType())) {
  465 + if (streamPushItem != null && streamPushItem.isPushIng()) {
  466 + // 推流状态
  467 + pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  468 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  469 + } else {
  470 + // 未推流 拉起
  471 + notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  472 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  473 + }
  474 + }else if ("proxy".equals(gbStream.getStreamType())){
  475 + if(null != proxyByAppAndStream &&proxyByAppAndStream.isStatus()){
  476 + pushProxyStream(evt, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
  477 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  478 + }else{
  479 + //开启代理拉流
  480 + boolean start1 = streamProxyService.start(gbStream.getApp(), gbStream.getStream());
  481 + if(start1) {
  482 + pushProxyStream(evt, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
  483 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  484 + }else{
  485 + //失败后通知
  486 + notifyStreamOnline(evt, gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive,
  487 + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
  488 + }
  489 + }
464 490  
  491 + }
465 492 }
466   -
467 493 }
468 494  
469 495 } catch (SipException | InvalidArgumentException | ParseException e) {
... ... @@ -480,13 +506,45 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
480 506 /**
481 507 * 安排推流
482 508 */
  509 + private void pushProxyStream(RequestEvent evt, GbStream gbStream, ParentPlatform platform,
  510 + CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
  511 + int port, Boolean tcpActive, boolean mediaTransmissionTCP,
  512 + String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
  513 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
  514 + if (streamReady) {
  515 + // 自平台内容
  516 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  517 + gbStream.getApp(), gbStream.getStream(), channelId,
  518 + mediaTransmissionTCP);
  519 +
  520 + if (sendRtpItem == null) {
  521 + logger.warn("服务器端口资源不足");
  522 + responseAck(evt, Response.BUSY_HERE);
  523 + return;
  524 + }
  525 + if (tcpActive != null) {
  526 + sendRtpItem.setTcpActive(tcpActive);
  527 + }
  528 + sendRtpItem.setPlayType(InviteStreamType.PUSH);
  529 + // 写入redis, 超时时回复
  530 + sendRtpItem.setStatus(1);
  531 + sendRtpItem.setCallId(callIdHeader.getCallId());
  532 + byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
  533 + sendRtpItem.setDialog(dialogByteArray);
  534 + byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
  535 + sendRtpItem.setTransaction(transactionByteArray);
  536 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  537 + sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);
  538 +
  539 + }
483 540  
  541 + }
484 542 private void pushStream(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
485 543 CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
486 544 int port, Boolean tcpActive, boolean mediaTransmissionTCP,
487 545 String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
488 546 // 推流
489   - if (streamPushItem.getServerId().equals(userSetting.getServerId())) {
  547 + if (streamPushItem.isSelf()) {
490 548 Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
491 549 if (streamReady) {
492 550 // 自平台内容
... ... @@ -525,7 +583,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
525 583 }
526 584  
527 585 }
528   -
529 586 /**
530 587 * 通知流上线
531 588 */
... ... @@ -535,7 +592,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
535 592 String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
536 593 if ("proxy".equals(gbStream.getStreamType())) {
537 594 // TODO 控制启用以使设备上线
538   - logger.info("[ app={}, stream={} ]通道离线,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
  595 + logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
539 596 responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
540 597 } else if ("push".equals(gbStream.getStreamType())) {
541 598 if (!platform.isStartOfflinePush()) {
... ... @@ -543,7 +600,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
543 600 return;
544 601 }
545 602 // 发送redis消息以使设备上线
546   - logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream());
  603 + logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream());
547 604  
548 605 MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1,
549 606 gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(),
... ... @@ -553,7 +610,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
553 610 dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
554 611 logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
555 612 try {
556   - mediaListManager.removedChannelOnlineEventLister(gbStream.getGbId());
  613 + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
557 614 responseAck(evt, Response.REQUEST_TIMEOUT); // 超时
558 615 } catch (SipException e) {
559 616 e.printStackTrace();
... ... @@ -568,7 +625,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
568 625 Boolean finalTcpActive = tcpActive;
569 626  
570 627 // 添加在本机上线的通知
571   - mediaListManager.addChannelOnlineEventLister(gbStream.getGbId(), (app, stream, serverId) -> {
  628 + mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> {
572 629 dynamicTask.stop(callIdHeader.getCallId());
573 630 if (serverId.equals(userSetting.getServerId())) {
574 631 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
... ... @@ -656,7 +713,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
656 713 // 离线
657 714 // 查询是否在本机上线了
658 715 StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
659   - if (currentStreamPushItem.isStatus()) {
  716 + if (currentStreamPushItem.isPushIng()) {
660 717 // 在线状态
661 718 pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
662 719 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
... ... @@ -15,6 +15,7 @@ import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
15 15 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
16 16 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
17 17 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  18 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
18 19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
19 20 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
20 21 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -71,6 +72,9 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
71 72 @Autowired
72 73 private SIPProcessorObserver sipProcessorObserver;
73 74  
  75 + @Autowired
  76 + private IDeviceChannelService deviceChannelService;
  77 +
74 78 private boolean taskQueueHandlerRun = false;
75 79  
76 80 private final ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
... ... @@ -88,39 +92,36 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
88 92 @Override
89 93 public void process(RequestEvent evt) {
90 94 try {
91   -
92 95 taskQueue.offer(new HandlerCatchData(evt, null, null));
93 96 responseAck(evt, Response.OK);
94 97 if (!taskQueueHandlerRun) {
95 98 taskQueueHandlerRun = true;
96 99 taskExecutor.execute(()-> {
97   - while (!taskQueue.isEmpty()) {
98   - try {
99   - HandlerCatchData take = taskQueue.poll();
100   - Element rootElement = getRootElement(take.getEvt());
101   - String cmd = XmlUtil.getText(rootElement, "CmdType");
102   -
103   - if (CmdType.CATALOG.equals(cmd)) {
104   - logger.info("接收到Catalog通知");
105   - processNotifyCatalogList(take.getEvt());
106   - } else if (CmdType.ALARM.equals(cmd)) {
107   - logger.info("接收到Alarm通知");
108   - processNotifyAlarm(take.getEvt());
109   - } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
110   - logger.info("接收到MobilePosition通知");
111   - processNotifyMobilePosition(take.getEvt());
112   - } else {
113   - logger.info("接收到消息:" + cmd);
114   - }
115   - } catch (DocumentException e) {
116   - throw new RuntimeException(e);
117   - }
  100 + while (!taskQueue.isEmpty()) {
  101 + try {
  102 + HandlerCatchData take = taskQueue.poll();
  103 + Element rootElement = getRootElement(take.getEvt());
  104 + String cmd = XmlUtil.getText(rootElement, "CmdType");
  105 +
  106 + if (CmdType.CATALOG.equals(cmd)) {
  107 + logger.info("接收到Catalog通知");
  108 + processNotifyCatalogList(take.getEvt());
  109 + } else if (CmdType.ALARM.equals(cmd)) {
  110 + logger.info("接收到Alarm通知");
  111 + processNotifyAlarm(take.getEvt());
  112 + } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
  113 + logger.info("接收到MobilePosition通知");
  114 + processNotifyMobilePosition(take.getEvt());
  115 + } else {
  116 + logger.info("接收到消息:" + cmd);
118 117 }
119   - taskQueueHandlerRun = false;
120   - });
  118 + } catch (DocumentException e) {
  119 + throw new RuntimeException(e);
  120 + }
  121 + }
  122 + taskQueueHandlerRun = false;
  123 + });
121 124 }
122   -
123   -
124 125 } catch (SipException | InvalidArgumentException | ParseException e) {
125 126 e.printStackTrace();
126 127 }
... ... @@ -170,31 +171,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
170 171 } else {
171 172 mobilePosition.setAltitude(0.0);
172 173 }
173   - logger.info("[收到 移动位置订阅]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(),
  174 + logger.info("[收到移动位置订阅通知]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(),
174 175 mobilePosition.getLongitude(), mobilePosition.getLatitude());
175 176 mobilePosition.setReportSource("Mobile Position");
176   - // 默认来源坐标系为WGS-84处理
177   - if ("WGS84".equals(device.getGeoCoordSys())) {
178   - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude());
179   - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude());
180   - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
181   - mobilePosition.setLongitudeGcj02(position[0]);
182   - mobilePosition.setLatitudeGcj02(position[1]);
183   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
184   - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude());
185   - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude());
186   - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude());
187   - mobilePosition.setLongitudeWgs84(position[0]);
188   - mobilePosition.setLatitudeWgs84(position[1]);
189   - }else {
190   - mobilePosition.setLongitudeGcj02(0.00);
191   - mobilePosition.setLatitudeGcj02(0.00);
192   - mobilePosition.setLongitudeWgs84(0.00);
193   - mobilePosition.setLatitudeWgs84(0.00);
194   - }
195   - if (userSetting.getSavePositionHistory()) {
196   - storager.insertMobilePosition(mobilePosition);
197   - }
  177 +
198 178  
199 179 // 更新device channel 的经纬度
200 180 DeviceChannel deviceChannel = new DeviceChannel();
... ... @@ -202,11 +182,18 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
202 182 deviceChannel.setChannelId(channelId);
203 183 deviceChannel.setLongitude(mobilePosition.getLongitude());
204 184 deviceChannel.setLatitude(mobilePosition.getLatitude());
205   - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84());
206   - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84());
207   - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02());
208   - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02());
209 185 deviceChannel.setGpsTime(mobilePosition.getTime());
  186 + deviceChannel = deviceChannelService.updateGps(deviceChannel, device);
  187 +
  188 + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84());
  189 + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84());
  190 + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02());
  191 + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02());
  192 +
  193 + if (userSetting.getSavePositionHistory()) {
  194 + storager.insertMobilePosition(mobilePosition);
  195 + }
  196 +
210 197 storager.updateChannelPosition(deviceChannel);
211 198 // 发送redis消息。 通知位置信息的变化
212 199 JSONObject jsonObject = new JSONObject();
... ... @@ -281,38 +268,28 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
281 268 mobilePosition.setLongitude(deviceAlarm.getLongitude());
282 269 mobilePosition.setLatitude(deviceAlarm.getLatitude());
283 270 mobilePosition.setReportSource("GPS Alarm");
284   - if ("WGS84".equals(device.getGeoCoordSys())) {
285   - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude());
286   - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude());
287   - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
288   - mobilePosition.setLongitudeGcj02(position[0]);
289   - mobilePosition.setLatitudeGcj02(position[1]);
290   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
291   - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude());
292   - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude());
293   - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude());
294   - mobilePosition.setLongitudeWgs84(position[0]);
295   - mobilePosition.setLatitudeWgs84(position[1]);
296   - }else {
297   - mobilePosition.setLongitudeGcj02(0.00);
298   - mobilePosition.setLatitudeGcj02(0.00);
299   - mobilePosition.setLongitudeWgs84(0.00);
300   - mobilePosition.setLatitudeWgs84(0.00);
301   - }
302   - if (userSetting.getSavePositionHistory()) {
303   - storager.insertMobilePosition(mobilePosition);
304   - }
  271 +
  272 +
  273 +
305 274 // 更新device channel 的经纬度
306 275 DeviceChannel deviceChannel = new DeviceChannel();
307 276 deviceChannel.setDeviceId(device.getDeviceId());
308 277 deviceChannel.setChannelId(channelId);
309 278 deviceChannel.setLongitude(mobilePosition.getLongitude());
310 279 deviceChannel.setLatitude(mobilePosition.getLatitude());
311   - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84());
312   - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84());
313   - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02());
314   - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02());
315 280 deviceChannel.setGpsTime(mobilePosition.getTime());
  281 +
  282 + deviceChannel = deviceChannelService.updateGps(deviceChannel, device);
  283 +
  284 + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84());
  285 + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84());
  286 + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02());
  287 + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02());
  288 +
  289 + if (userSetting.getSavePositionHistory()) {
  290 + storager.insertMobilePosition(mobilePosition);
  291 + }
  292 +
316 293 storager.updateChannelPosition(deviceChannel);
317 294 }
318 295 // TODO: 需要实现存储报警信息、报警分类
... ... @@ -338,7 +315,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
338 315  
339 316 Device device = redisCatchStorage.getDevice(deviceId);
340 317 if (device == null || device.getOnline() == 0) {
341   - logger.warn("[收到 目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" ));
  318 + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" ));
342 319 return;
343 320 }
344 321 Element rootElement = getRootElement(evt, device.getCharset());
... ... @@ -359,28 +336,28 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
359 336 Element eventElement = itemDevice.element("Event");
360 337 String event;
361 338 if (eventElement == null) {
362   - logger.warn("[收到 目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
  339 + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
363 340 event = CatalogEvent.ADD;
364 341 }else {
365 342 event = eventElement.getText().toUpperCase();
366 343 }
367   - DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device);
  344 + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event);
368 345 channel.setDeviceId(device.getDeviceId());
369   - logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
  346 + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
370 347 switch (event) {
371 348 case CatalogEvent.ON:
372 349 // 上线
373   - logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId());
  350 + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
374 351 storager.deviceChannelOnline(deviceId, channel.getChannelId());
375 352 break;
376 353 case CatalogEvent.OFF :
377 354 // 离线
378   - logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId());
  355 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
379 356 storager.deviceChannelOffline(deviceId, channel.getChannelId());
380 357 break;
381 358 case CatalogEvent.VLOST:
382 359 // 视频丢失
383   - logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId());
  360 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
384 361 storager.deviceChannelOffline(deviceId, channel.getChannelId());
385 362 break;
386 363 case CatalogEvent.DEFECT:
... ... @@ -388,18 +365,18 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
388 365 break;
389 366 case CatalogEvent.ADD:
390 367 // 增加
391   - logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId());
392   - storager.updateChannel(deviceId, channel);
  368 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  369 + deviceChannelService.updateChannel(deviceId, channel);
393 370 break;
394 371 case CatalogEvent.DEL:
395 372 // 删除
396   - logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId());
  373 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
397 374 storager.delChannel(deviceId, channel.getChannelId());
398 375 break;
399 376 case CatalogEvent.UPDATE:
400 377 // 更新
401   - logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId());
402   - storager.updateChannel(deviceId, channel);
  378 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  379 + deviceChannelService.updateChannel(deviceId, channel);
403 380 break;
404 381 default:
405 382 logger.warn("[ NotifyCatalog ] event not found : {}", event );
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
... ... @@ -143,6 +143,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
143 143 device.setGeoCoordSys("WGS84");
144 144 device.setTreeType("CivilCode");
145 145 device.setDeviceId(deviceId);
  146 + device.setOnline(0);
146 147 }
147 148 device.setIp(received);
148 149 device.setPort(rPort);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
... ... @@ -27,6 +27,7 @@ import javax.sip.SipException;
27 27 import javax.sip.address.SipURI;
28 28 import javax.sip.header.CSeqHeader;
29 29 import javax.sip.header.CallIdHeader;
  30 +import javax.sip.message.Request;
30 31 import javax.sip.message.Response;
31 32 import java.text.ParseException;
32 33 import java.util.Map;
... ... @@ -68,22 +69,23 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
68 69  
69 70 @Override
70 71 public void process(RequestEvent evt) {
  72 + SIPRequest sipRequest = (SIPRequest)evt.getRequest();
71 73 logger.debug("接收到消息:" + evt.getRequest());
72 74 String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
73   - CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
  75 + CallIdHeader callIdHeader = sipRequest.getCallIdHeader();
74 76 // 先从会话内查找
75 77 SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
76   - if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
  78 + // 兼容海康 媒体通知 消息from字段不是设备ID的问题
  79 + if (ssrcTransaction != null) {
77 80 deviceId = ssrcTransaction.getDeviceId();
78 81 }
79 82 // 查询设备是否存在
80   - CSeqHeader cseqHeader = (CSeqHeader) evt.getRequest().getHeader(CSeqHeader.NAME);
81   - String method = cseqHeader.getMethod();
82 83 Device device = redisCatchStorage.getDevice(deviceId);
83 84 // 查询上级平台是否存在
84 85 ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
85 86 try {
86 87 if (device != null && parentPlatform != null) {
  88 +
87 89 logger.warn("[重复]平台与设备编号重复:{}", deviceId);
88 90 SIPRequest request = (SIPRequest) evt.getRequest();
89 91 String hostAddress = request.getRemoteAddress().getHostAddress();
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java
... ... @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component;
9 9 /**
10 10 * 命令类型: 通知命令
11 11 * 命令类型: 状态信息(心跳)报送, 报警通知, 媒体通知, 移动设备位置数据,语音广播通知(TODO), 设备预置位(TODO)
  12 + * @author lin
12 13 */
13 14 @Component
14 15 public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
... ... @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
11 11 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
12 12 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
13 13 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
  14 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
14 15 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
15 16 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
16 17 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -58,6 +59,9 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
58 59 @Autowired
59 60 private IDeviceAlarmService deviceAlarmService;
60 61  
  62 + @Autowired
  63 + private IDeviceChannelService deviceChannelService;
  64 +
61 65 @Override
62 66 public void afterPropertiesSet() throws Exception {
63 67 notifyMessageHandler.addHandler(cmdType, this);
... ... @@ -65,7 +69,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
65 69  
66 70 @Override
67 71 public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
68   - logger.info("收到来自设备[{}]的报警通知", device.getDeviceId());
  72 + logger.info("[收到报警通知]设备:{}", device.getDeviceId());
69 73 // 回复200 OK
70 74 try {
71 75 responseAck(evt, Response.OK);
... ... @@ -119,38 +123,26 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
119 123 mobilePosition.setLongitude(deviceAlarm.getLongitude());
120 124 mobilePosition.setLatitude(deviceAlarm.getLatitude());
121 125 mobilePosition.setReportSource("GPS Alarm");
122   - if ("WGS84".equals(device.getGeoCoordSys())) {
123   - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude());
124   - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude());
125   - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
126   - mobilePosition.setLongitudeGcj02(position[0]);
127   - mobilePosition.setLatitudeGcj02(position[1]);
128   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
129   - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude());
130   - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude());
131   - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude());
132   - mobilePosition.setLongitudeWgs84(position[0]);
133   - mobilePosition.setLatitudeWgs84(position[1]);
134   - }else {
135   - mobilePosition.setLongitudeGcj02(0.00);
136   - mobilePosition.setLatitudeGcj02(0.00);
137   - mobilePosition.setLongitudeWgs84(0.00);
138   - mobilePosition.setLatitudeWgs84(0.00);
139   - }
140   - if (userSetting.getSavePositionHistory()) {
141   - storager.insertMobilePosition(mobilePosition);
142   - }
  126 +
  127 +
143 128 // 更新device channel 的经纬度
144 129 DeviceChannel deviceChannel = new DeviceChannel();
145 130 deviceChannel.setDeviceId(device.getDeviceId());
146 131 deviceChannel.setChannelId(channelId);
147 132 deviceChannel.setLongitude(mobilePosition.getLongitude());
148 133 deviceChannel.setLatitude(mobilePosition.getLatitude());
149   - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84());
150   - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84());
151   - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02());
152   - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02());
153 134 deviceChannel.setGpsTime(mobilePosition.getTime());
  135 +
  136 + deviceChannel = deviceChannelService.updateGps(deviceChannel, device);
  137 +
  138 + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84());
  139 + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84());
  140 + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02());
  141 + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02());
  142 +
  143 + if (userSetting.getSavePositionHistory()) {
  144 + storager.insertMobilePosition(mobilePosition);
  145 + }
154 146 storager.updateChannelPosition(deviceChannel);
155 147 }
156 148 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java
... ... @@ -58,85 +58,21 @@ public class CatalogNotifyMessageHandler extends SIPRequestProcessorParent imple
58 58 // 准备回复通道信息
59 59 List<DeviceChannelInPlatform> deviceChannels = storage.queryChannelListInParentPlatform(parentPlatform.getServerGBId());
60 60 // 查询关联的直播通道
61   - List<GbStream> gbStreams = storage.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
  61 + List<DeviceChannel> gbStreams = storage.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
  62 + // 回复目录信息
  63 + List<DeviceChannel> catalogs = storage.queryCatalogInPlatform(parentPlatform.getServerGBId());
62 64  
63 65 List<DeviceChannel> allChannels = new ArrayList<>();
64   - // 回复目录信息
65   - List<PlatformCatalog> catalogs = storage.queryCatalogInPlatform(parentPlatform.getServerGBId());
66 66 if (catalogs.size() > 0) {
67   - for (PlatformCatalog catalog : catalogs) {
68   - if (catalog.getParentId().equals(catalog.getPlatformId())) {
69   - catalog.setParentId(parentPlatform.getDeviceGBId());
70   - }
71   - DeviceChannel deviceChannel = new DeviceChannel();
72   - deviceChannel.setChannelId(catalog.getId());
73   - deviceChannel.setName(catalog.getName());
74   - deviceChannel.setLongitude(0.0);
75   - deviceChannel.setLatitude(0.0);
76   - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
77   - deviceChannel.setManufacture("wvp-pro");
78   - deviceChannel.setStatus(1);
79   - deviceChannel.setParental(1);
80   - deviceChannel.setParentId(catalog.getParentId());
81   - deviceChannel.setRegisterWay(1);
82   - if (catalog.getParentId() != null && catalog.getParentId().length() <= 10) {
83   - deviceChannel.setCivilCode(catalog.getParentId());
84   - }else {
85   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
86   - }
87   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
88   - deviceChannel.setModel("live");
89   - deviceChannel.setOwner("wvp-pro");
90   - deviceChannel.setSecrecy("0");
91   - allChannels.add(deviceChannel);
92   - }
  67 + allChannels.addAll(catalogs);
93 68 }
94 69 // 回复级联的通道
95 70 if (deviceChannels.size() > 0) {
96   - for (DeviceChannelInPlatform channel : deviceChannels) {
97   - if (channel.getCatalogId().equals(parentPlatform.getServerGBId())) {
98   - channel.setCatalogId(parentPlatform.getDeviceGBId());
99   - }
100   - DeviceChannel deviceChannel = storage.queryChannel(channel.getDeviceId(), channel.getChannelId());
101   - deviceChannel.setParental(0);
102   - deviceChannel.setParentId(channel.getCatalogId());
103   - if (channel.getCatalogId() != null && channel.getCatalogId().length() <= 10) {
104   - channel.setCivilCode(channel.getCatalogId());
105   - }else {
106   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
107   - }
108   -
109   - allChannels.add(deviceChannel);
110   - }
  71 + allChannels.addAll(deviceChannels);
111 72 }
112 73 // 回复直播的通道
113 74 if (gbStreams.size() > 0) {
114   - for (GbStream gbStream : gbStreams) {
115   - if (gbStream.getCatalogId().equals(parentPlatform.getServerGBId())) {
116   - gbStream.setCatalogId(null);
117   - }
118   - DeviceChannel deviceChannel = new DeviceChannel();
119   - deviceChannel.setChannelId(gbStream.getGbId());
120   - deviceChannel.setName(gbStream.getName());
121   - deviceChannel.setLongitude(gbStream.getLongitude());
122   - deviceChannel.setLatitude(gbStream.getLatitude());
123   - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
124   - deviceChannel.setManufacture("wvp-pro");
125   -// deviceChannel.setStatus(gbStream.isStatus()?1:0);
126   - deviceChannel.setStatus(1);
127   - deviceChannel.setParentId(gbStream.getCatalogId());
128   - deviceChannel.setRegisterWay(1);
129   - if (gbStream.getCatalogId() != null && gbStream.getCatalogId().length() <= 10) {
130   - deviceChannel.setCivilCode(gbStream.getCatalogId());
131   - }else {
132   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
133   - }
134   - deviceChannel.setModel("live");
135   - deviceChannel.setOwner("wvp-pro");
136   - deviceChannel.setParental(0);
137   - deviceChannel.setSecrecy("0");
138   - allChannels.add(deviceChannel);
139   - }
  75 + allChannels.addAll(gbStreams);
140 76 }
141 77 if (allChannels.size() > 0) {
142 78 cmderFroPlatform.catalogQuery(allChannels, parentPlatform, sn, fromHeader.getTag());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
... ... @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
7 7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
8 8 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
9 9 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  10 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
10 11 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
11 12 import com.genersoft.iot.vmp.utils.DateUtil;
12 13 import com.genersoft.iot.vmp.utils.GpsUtil;
... ... @@ -42,6 +43,9 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
42 43 @Autowired
43 44 private IVideoManagerStorage storager;
44 45  
  46 + @Autowired
  47 + private IDeviceChannelService deviceChannelService;
  48 +
45 49 @Override
46 50 public void afterPropertiesSet() throws Exception {
47 51 notifyMessageHandler.addHandler(cmdType, this);
... ... @@ -79,38 +83,26 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
79 83 mobilePosition.setAltitude(0.0);
80 84 }
81 85 mobilePosition.setReportSource("Mobile Position");
82   - if ("WGS84".equals(device.getGeoCoordSys())) {
83   - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude());
84   - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude());
85   - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
86   - mobilePosition.setLongitudeGcj02(position[0]);
87   - mobilePosition.setLatitudeGcj02(position[1]);
88   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
89   - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude());
90   - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude());
91   - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude());
92   - mobilePosition.setLongitudeWgs84(position[0]);
93   - mobilePosition.setLatitudeWgs84(position[1]);
94   - }else {
95   - mobilePosition.setLongitudeGcj02(0.00);
96   - mobilePosition.setLatitudeGcj02(0.00);
97   - mobilePosition.setLongitudeWgs84(0.00);
98   - mobilePosition.setLatitudeWgs84(0.00);
99   - }
100   - if (userSetting.getSavePositionHistory()) {
101   - storager.insertMobilePosition(mobilePosition);
102   - }
  86 +
  87 +
103 88 // 更新device channel 的经纬度
104 89 DeviceChannel deviceChannel = new DeviceChannel();
105 90 deviceChannel.setDeviceId(device.getDeviceId());
106 91 deviceChannel.setChannelId(mobilePosition.getChannelId());
107 92 deviceChannel.setLongitude(mobilePosition.getLongitude());
108 93 deviceChannel.setLatitude(mobilePosition.getLatitude());
109   - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84());
110   - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84());
111   - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02());
112   - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02());
113 94 deviceChannel.setGpsTime(mobilePosition.getTime());
  95 +
  96 + deviceChannel = deviceChannelService.updateGps(deviceChannel, device);
  97 +
  98 + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84());
  99 + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84());
  100 + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02());
  101 + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02());
  102 +
  103 + if (userSetting.getSavePositionHistory()) {
  104 + storager.insertMobilePosition(mobilePosition);
  105 + }
114 106 storager.updateChannelPosition(deviceChannel);
115 107 //回复 200 OK
116 108 responseAck(evt, Response.OK);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
... ... @@ -70,86 +70,24 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
70 70 Element snElement = rootElement.element("SN");
71 71 String sn = snElement.getText();
72 72 // 准备回复通道信息
73   - List<DeviceChannelInPlatform> deviceChannelInPlatforms = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId());
  73 + List<DeviceChannel> deviceChannelInPlatforms = storager.queryChannelWithCatalog(parentPlatform.getServerGBId());
74 74 // 查询关联的直播通道
75   - List<GbStream> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
  75 + List<DeviceChannel> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
76 76 // 回复目录信息
77   - List<PlatformCatalog> catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId());
  77 + List<DeviceChannel> catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId());
78 78  
79 79 List<DeviceChannel> allChannels = new ArrayList<>();
  80 +
80 81 if (catalogs.size() > 0) {
81   - for (PlatformCatalog catalog : catalogs) {
82   - if (catalog.getParentId().equals(catalog.getPlatformId())) {
83   - catalog.setParentId(parentPlatform.getDeviceGBId());
84   - }
85   - DeviceChannel deviceChannel = new DeviceChannel();
86   - // 通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划
87   - deviceChannel.setChannelType(2);
88   - deviceChannel.setChannelId(catalog.getId());
89   - deviceChannel.setName(catalog.getName());
90   - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
91   - deviceChannel.setManufacture("wvp-pro");
92   - deviceChannel.setStatus(1);
93   - deviceChannel.setParental(1);
94   - deviceChannel.setParentId(catalog.getParentId());
95   - deviceChannel.setRegisterWay(1);
96   - if (catalog.getParentId() != null && catalog.getParentId().length() < 10) {
97   - deviceChannel.setCivilCode(catalog.getParentId());
98   - }else {
99   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
100   - }
101   - allChannels.add(deviceChannel);
102   - }
  82 + allChannels.addAll(catalogs);
103 83 }
104 84 // 回复级联的通道
105 85 if (deviceChannelInPlatforms.size() > 0) {
106   - for (DeviceChannelInPlatform channel : deviceChannelInPlatforms) {
107   - if (channel.getCatalogId().equals(parentPlatform.getServerGBId())) {
108   - channel.setCatalogId(parentPlatform.getDeviceGBId());
109   - }
110   - DeviceChannel deviceChannel = storage.queryChannel(channel.getDeviceId(), channel.getChannelId());
111   - // 通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划
112   - deviceChannel.setChannelType(0);
113   - deviceChannel.setParental(0);
114   - deviceChannel.setParentId(channel.getCatalogId());
115   - if (channel.getCatalogId() != null && channel.getCatalogId().length() < 10) {
116   - deviceChannel.setCivilCode(channel.getCatalogId());
117   - }else {
118   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
119   - }
120   - allChannels.add(deviceChannel);
121   - }
  86 + allChannels.addAll(deviceChannelInPlatforms);
122 87 }
123 88 // 回复直播的通道
124 89 if (gbStreams.size() > 0) {
125   - for (GbStream gbStream : gbStreams) {
126   - if (gbStream.getCatalogId().equals(parentPlatform.getServerGBId())) {
127   - gbStream.setCatalogId(null);
128   - }
129   - DeviceChannel deviceChannel = new DeviceChannel();
130   - // 通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划
131   - deviceChannel.setChannelType(1);
132   - deviceChannel.setChannelId(gbStream.getGbId());
133   - deviceChannel.setName(gbStream.getName());
134   - deviceChannel.setLongitude(gbStream.getLongitude());
135   - deviceChannel.setLatitude(gbStream.getLatitude());
136   - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
137   - deviceChannel.setManufacture("wvp-pro");
138   -// deviceChannel.setStatus(gbStream.isStatus()?1:0);
139   - deviceChannel.setStatus(1);
140   - deviceChannel.setParentId(gbStream.getCatalogId());
141   - deviceChannel.setRegisterWay(1);
142   - if (gbStream.getCatalogId() != null && gbStream.getCatalogId().length() < 10) {
143   - deviceChannel.setCivilCode(gbStream.getCatalogId());
144   - }else {
145   - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision());
146   - }
147   - deviceChannel.setModel("live");
148   - deviceChannel.setOwner("wvp-pro");
149   - deviceChannel.setParental(0);
150   - deviceChannel.setSecrecy("0");
151   - allChannels.add(deviceChannel);
152   - }
  90 + allChannels.addAll(gbStreams);
153 91 }
154 92 if (allChannels.size() > 0) {
155 93 cmderFroPlatform.catalogQuery(allChannels, parentPlatform, sn, fromHeader.getTag());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
... ... @@ -44,7 +44,7 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
44 44  
45 45 @Override
46 46 public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
47   - logger.info("接收到DeviceInfo查询消息");
  47 + logger.info("[DeviceInfo查询]消息");
48 48 FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
49 49 try {
50 50 // 回复200 OK
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
... ... @@ -111,6 +111,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
111 111 int sumNum = Integer.parseInt(sumNumElement.getText());
112 112  
113 113 if (sumNum == 0) {
  114 + logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId());
114 115 // 数据已经完整接收
115 116 storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
116 117 catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
... ... @@ -125,18 +126,14 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
125 126 if (channelDeviceElement == null) {
126 127 continue;
127 128 }
128   - //by brewswang
129   - // if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置
130   - // processNotifyMobilePosition(evt, itemDevice);
131   - // }
132   - DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device);
  129 + DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null);
133 130 deviceChannel.setDeviceId(take.getDevice().getDeviceId());
134 131  
135 132 channelList.add(deviceChannel);
136 133 }
137 134 int sn = Integer.parseInt(snElement.getText());
138 135 catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
139   - logger.info("收到来自设备【{}】的通道: {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
  136 + logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
140 137 if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
141 138 // 数据已经完整接收
142 139 boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
... ... @@ -87,7 +87,6 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent
87 87 device.setStreamMode("UDP");
88 88 }
89 89 deviceService.updateDevice(device);
90   -// storager.updateDevice(device);
91 90  
92 91 RequestMessage msg = new RequestMessage();
93 92 msg.setKey(key);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
... ... @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
7 7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
8 8 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
9 9 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  10 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
10 11 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
11 12 import com.genersoft.iot.vmp.utils.DateUtil;
12 13 import com.genersoft.iot.vmp.utils.GpsUtil;
... ... @@ -42,6 +43,9 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
42 43 @Autowired
43 44 private IVideoManagerStorage storager;
44 45  
  46 + @Autowired
  47 + private IDeviceChannelService deviceChannelService;
  48 +
45 49 @Override
46 50 public void afterPropertiesSet() throws Exception {
47 51 responseMessageHandler.addHandler(cmdType, this);
... ... @@ -79,38 +83,25 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
79 83 mobilePosition.setAltitude(0.0);
80 84 }
81 85 mobilePosition.setReportSource("Mobile Position");
82   - if ("WGS84".equals(device.getGeoCoordSys())) {
83   - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude());
84   - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude());
85   - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
86   - mobilePosition.setLongitudeGcj02(position[0]);
87   - mobilePosition.setLatitudeGcj02(position[1]);
88   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
89   - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude());
90   - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude());
91   - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude());
92   - mobilePosition.setLongitudeWgs84(position[0]);
93   - mobilePosition.setLatitudeWgs84(position[1]);
94   - }else {
95   - mobilePosition.setLongitudeGcj02(0.00);
96   - mobilePosition.setLatitudeGcj02(0.00);
97   - mobilePosition.setLongitudeWgs84(0.00);
98   - mobilePosition.setLatitudeWgs84(0.00);
99   - }
100   - if (userSetting.getSavePositionHistory()) {
101   - storager.insertMobilePosition(mobilePosition);
102   - }
  86 +
103 87 // 更新device channel 的经纬度
104 88 DeviceChannel deviceChannel = new DeviceChannel();
105 89 deviceChannel.setDeviceId(device.getDeviceId());
106 90 deviceChannel.setChannelId(mobilePosition.getChannelId());
107 91 deviceChannel.setLongitude(mobilePosition.getLongitude());
108 92 deviceChannel.setLatitude(mobilePosition.getLatitude());
109   - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84());
110   - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84());
111   - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02());
112   - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02());
113 93 deviceChannel.setGpsTime(mobilePosition.getTime());
  94 +
  95 + deviceChannel = deviceChannelService.updateGps(deviceChannel, device);
  96 +
  97 + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84());
  98 + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84());
  99 + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02());
  100 + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02());
  101 +
  102 + if (userSetting.getSavePositionHistory()) {
  103 + storager.insertMobilePosition(mobilePosition);
  104 + }
114 105 storager.updateChannelPosition(deviceChannel);
115 106 //回复 200 OK
116 107 responseAck(evt, Response.OK);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
... ... @@ -26,7 +26,7 @@ import javax.sip.message.Response;
26 26 @Component
27 27 public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
28 28  
29   - private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
  29 + private final Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
30 30 private final String method = "REGISTER";
31 31  
32 32 @Autowired
... ... @@ -69,11 +69,11 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
69 69  
70 70 ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformGBId);
71 71 if (parentPlatformCatch == null) {
72   - logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台缓存信息未查询到!!!", platformGBId, response.getStatusCode()));
  72 + logger.warn(String.format("[收到注册/注销%S请求]平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(),platformGBId));
73 73 return;
74 74 }
75 75 String action = parentPlatformCatch.getParentPlatform().getExpires().equals("0") ? "注销" : "注册";
76   - logger.info(String.format("收到 %s %s的%S响应", platformGBId, action, response.getStatusCode() ));
  76 + logger.info(String.format("[%s %S响应]%s ", action, response.getStatusCode(), platformGBId ));
77 77 ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform();
78 78 if (parentPlatform == null) {
79 79 logger.warn(String.format("收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformGBId, action, response.getStatusCode()));
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
... ... @@ -23,9 +23,10 @@ public class SipUtils {
23 23 /**
24 24 * 从subject读取channelId
25 25 * */
26   - public static String getChannelIdFromHeader(Request request) {
  26 + public static String getChannelIdFromRequest(Request request) {
27 27 Header subject = request.getHeader("subject");
28 28 if (subject == null) {
  29 + // 如果缺失subject
29 30 return null;
30 31 }
31 32 return ((Subject) subject).getSubject().split(":")[0];
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
... ... @@ -4,6 +4,8 @@ import com.alibaba.fastjson.JSONArray;
4 4 import com.alibaba.fastjson.JSONObject;
5 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 6 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  7 +import com.genersoft.iot.vmp.gb28181.bean.TreeType;
  8 +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
7 9 import com.genersoft.iot.vmp.utils.DateUtil;
8 10 import org.dom4j.Attribute;
9 11 import org.dom4j.Document;
... ... @@ -29,7 +31,7 @@ public class XmlUtil {
29 31 /**
30 32 * 日志服务
31 33 */
32   - private static Logger LOG = LoggerFactory.getLogger(XmlUtil.class);
  34 + private static Logger logger = LoggerFactory.getLogger(XmlUtil.class);
33 35  
34 36 /**
35 37 * 解析XML为Document对象
... ... @@ -46,7 +48,7 @@ public class XmlUtil {
46 48 try {
47 49 document = saxReader.read(sr);
48 50 } catch (DocumentException e) {
49   - LOG.error("解析失败", e);
  51 + logger.error("解析失败", e);
50 52 }
51 53 return null == document ? null : document.getRootElement();
52 54 }
... ... @@ -182,47 +184,69 @@ public class XmlUtil {
182 184 return xml.getRootElement();
183 185 }
184 186  
185   - public static DeviceChannel channelContentHander(Element itemDevice, Device device){
186   - Element channdelNameElement = itemDevice.element("Name");
187   - String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
188   - Element statusElement = itemDevice.element("Status");
189   - String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
  187 + private enum ChannelType{
  188 + CivilCode, BusinessGroup,VirtualOrganization,Other
  189 + }
  190 +
  191 + public static DeviceChannel channelContentHander(Element itemDevice, Device device, String event){
190 192 DeviceChannel deviceChannel = new DeviceChannel();
191   - deviceChannel.setName(channelName);
  193 + deviceChannel.setDeviceId(device.getDeviceId());
192 194 Element channdelIdElement = itemDevice.element("DeviceID");
193   - String channelId = channdelIdElement != null ? channdelIdElement.getTextTrim().toString() : "";
  195 + if (channdelIdElement == null) {
  196 + logger.warn("解析Catalog消息时发现缺少 DeviceID");
  197 + return null;
  198 + }
  199 + String channelId = channdelIdElement.getTextTrim();
  200 + if (StringUtils.isEmpty(channelId)) {
  201 + logger.warn("解析Catalog消息时发现缺少 DeviceID");
  202 + return null;
  203 + }
194 204 deviceChannel.setChannelId(channelId);
195   - // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
196   - if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) {
197   - deviceChannel.setStatus(1);
  205 + if (event != null && !event.equals(CatalogEvent.ADD) && !event.equals(CatalogEvent.UPDATE)) {
  206 + // 除了ADD和update情况下需要识别全部内容,
  207 + return deviceChannel;
198 208 }
199   - if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
200   - deviceChannel.setStatus(0);
  209 +
  210 + ChannelType channelType = ChannelType.Other;
  211 + if (channelId.length() <= 8) {
  212 + channelType = ChannelType.CivilCode;
  213 + }else {
  214 + if (channelId.length() == 20) {
  215 + int code = Integer.parseInt(channelId.substring(10, 13));
  216 + switch (code){
  217 + case 215:
  218 + channelType = ChannelType.BusinessGroup;
  219 + break;
  220 + case 216:
  221 + channelType = ChannelType.VirtualOrganization;
  222 + break;
  223 + default:
  224 + break;
  225 +
  226 + }
  227 + }
201 228 }
202 229  
203   - deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
204   - deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
205   - deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
206   - deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
207   - deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
208   - deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
209   - String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID");
210   - if (XmlUtil.getText(itemDevice, "Parental") == null
211   - || XmlUtil.getText(itemDevice, "Parental").equals("")) {
212   - if (deviceChannel.getChannelId().length() <= 10
213   - || (deviceChannel.getChannelId().length() == 20 && (
214   - Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 215
215   - || Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216
216   - )
217   - )
218   - ) {
219   - deviceChannel.setParental(1);
220   - }else {
221   - deviceChannel.setParental(0);
  230 + Element channdelNameElement = itemDevice.element("Name");
  231 + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim() : "";
  232 + deviceChannel.setName(channelName);
  233 +
  234 + String civilCode = XmlUtil.getText(itemDevice, "CivilCode");
  235 + deviceChannel.setCivilCode(civilCode);
  236 + if (channelType == ChannelType.CivilCode && civilCode == null) {
  237 + deviceChannel.setParental(1);
  238 + // 行政区划如果没有传递具体值,则推测一个
  239 + if (channelId.length() > 2) {
  240 + deviceChannel.setCivilCode(channelId.substring(0, channelId.length() - 2));
222 241 }
223   - } else {
224   - // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1
225   - deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0);
  242 + }
  243 + if (channelType.equals(ChannelType.CivilCode)) {
  244 + // 行政区划其他字段没必要识别了,默认在线即可
  245 + deviceChannel.setStatus(1);
  246 + deviceChannel.setParental(1);
  247 + deviceChannel.setCreateTime(DateUtil.getNow());
  248 + deviceChannel.setUpdateTime(DateUtil.getNow());
  249 + return deviceChannel;
226 250 }
227 251 /**
228 252 * 行政区划展示设备树与业务分组展示设备树是两种不同的模式
... ... @@ -230,7 +254,17 @@ public class XmlUtil {
230 254 * 河北省
231 255 * --> 石家庄市
232 256 * --> 摄像头
233   - * --> 正定县
  257 + *String parentId = XmlUtil.getText(itemDevice, "ParentID");
  258 + if (parentId != null) {
  259 + if (parentId.contains("/")) {
  260 + String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1);
  261 + String businessGroup = parentId.substring(0, parentId.indexOf("/"));
  262 + deviceChannel.setParentId(lastParentId);
  263 + }else {
  264 + deviceChannel.setParentId(parentId);
  265 + }
  266 + }
  267 + deviceCh --> 正定县
234 268 * --> 摄像头
235 269 * --> 摄像头
236 270 *
... ... @@ -243,59 +277,88 @@ public class XmlUtil {
243 277 * --> 摄像头
244 278 */
245 279 String parentId = XmlUtil.getText(itemDevice, "ParentID");
  280 + String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID");
246 281 if (parentId != null) {
247 282 if (parentId.contains("/")) {
248 283 String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1);
  284 + if (businessGroupID == null) {
  285 + businessGroupID = parentId.substring(0, parentId.indexOf("/"));
  286 + }
249 287 deviceChannel.setParentId(lastParentId);
250 288 }else {
251 289 deviceChannel.setParentId(parentId);
252 290 }
253 291 }
254 292 deviceChannel.setBusinessGroupId(businessGroupID);
  293 + if (channelType.equals(ChannelType.BusinessGroup) || channelType.equals(ChannelType.VirtualOrganization)) {
  294 + // 业务分组和虚拟组织 其他字段没必要识别了,默认在线即可
  295 + deviceChannel.setStatus(1);
  296 + deviceChannel.setParental(1);
  297 + deviceChannel.setCreateTime(DateUtil.getNow());
  298 + deviceChannel.setUpdateTime(DateUtil.getNow());
  299 + return deviceChannel;
  300 + }
255 301  
256   -// else {
257   -// if (deviceChannel.getChannelId().length() <= 10) { // 此时为行政区划, 上下级行政区划使用DeviceId关联
258   -// deviceChannel.setParentId(deviceChannel.getChannelId().substring(0, deviceChannel.getChannelId().length() - 2));
259   -// }else if (deviceChannel.getChannelId().length() == 20) {
260   -// if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织
261   -// deviceChannel.setBusinessGroupId(businessGroupID);
262   -// }else if (Integer.parseInt(device.getDeviceId().substring(10, 13) )== 118) {//NVR 如果上级设备编号是NVR则直接将NVR的编号设置给通道的上级编号
263   -// deviceChannel.setParentId(device.getDeviceId());
264   -// }else if (deviceChannel.getCivilCode() != null) {
265   -// // 设备, 无parentId的20位是使用CivilCode表示上级的设备,
266   -// // 注:215 业务分组是需要有parentId的
267   -// deviceChannel.setParentId(deviceChannel.getCivilCode());
268   -// }
269   -// }else {
270   -// deviceChannel.setParentId(deviceChannel.getDeviceId());
271   -// }
272   -// }
273   -
274   - if (XmlUtil.getText(itemDevice, "SafetyWay") == null
275   - || XmlUtil.getText(itemDevice, "SafetyWay") == "") {
  302 + Element statusElement = itemDevice.element("Status");
  303 +
  304 + if (statusElement != null) {
  305 + String status = statusElement.getTextTrim().trim();
  306 + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
  307 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) {
  308 + deviceChannel.setStatus(1);
  309 + }
  310 + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
  311 + deviceChannel.setStatus(0);
  312 + }
  313 + }else {
  314 + deviceChannel.setStatus(1);
  315 + }
  316 + // 识别自带的目录标识
  317 + String parental = XmlUtil.getText(itemDevice, "Parental");
  318 + // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1
  319 + if (!StringUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) {
  320 + deviceChannel.setParental(0);
  321 + }else {
  322 + deviceChannel.setParental(1);
  323 + }
  324 +
  325 +
  326 + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
  327 + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
  328 + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
  329 + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
  330 + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
  331 + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
  332 + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
  333 +
  334 + String safetyWay = XmlUtil.getText(itemDevice, "SafetyWay");
  335 + if (StringUtils.isEmpty(safetyWay)) {
276 336 deviceChannel.setSafetyWay(0);
277 337 } else {
278   - deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
  338 + deviceChannel.setSafetyWay(Integer.parseInt(safetyWay));
279 339 }
280   - if (XmlUtil.getText(itemDevice, "RegisterWay") == null
281   - || XmlUtil.getText(itemDevice, "RegisterWay") == "") {
  340 +
  341 + String registerWay = XmlUtil.getText(itemDevice, "RegisterWay");
  342 + if (StringUtils.isEmpty(registerWay)) {
282 343 deviceChannel.setRegisterWay(1);
283 344 } else {
284   - deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
  345 + deviceChannel.setRegisterWay(Integer.parseInt(registerWay));
285 346 }
286   - deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
  347 +
287 348 if (XmlUtil.getText(itemDevice, "Certifiable") == null
288 349 || XmlUtil.getText(itemDevice, "Certifiable") == "") {
289 350 deviceChannel.setCertifiable(0);
290 351 } else {
291 352 deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
292 353 }
  354 +
293 355 if (XmlUtil.getText(itemDevice, "ErrCode") == null
294 356 || XmlUtil.getText(itemDevice, "ErrCode") == "") {
295 357 deviceChannel.setErrCode(0);
296 358 } else {
297 359 deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
298 360 }
  361 +
299 362 deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
300 363 deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
301 364 deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
... ... @@ -304,43 +367,23 @@ public class XmlUtil {
304 367 } else {
305 368 deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
306 369 }
307   - deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
308   - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
309   - deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
  370 +
  371 +
  372 + String longitude = XmlUtil.getText(itemDevice, "Longitude");
  373 + if (NumericUtil.isDouble(longitude)) {
  374 + deviceChannel.setLongitude(Double.parseDouble(longitude));
310 375 } else {
311 376 deviceChannel.setLongitude(0.00);
312 377 }
313   - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) {
314   - deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
  378 + String latitude = XmlUtil.getText(itemDevice, "Latitude");
  379 + if (NumericUtil.isDouble(latitude)) {
  380 + deviceChannel.setLatitude(Double.parseDouble(latitude));
315 381 } else {
316 382 deviceChannel.setLatitude(0.00);
317 383 }
318 384 deviceChannel.setGpsTime(DateUtil.getNow());
319   - if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) {
320   - if ("WGS84".equals(device.getGeoCoordSys())) {
321   - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
322   - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
323   - Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
324   - deviceChannel.setLongitudeGcj02(position[0]);
325   - deviceChannel.setLatitudeGcj02(position[1]);
326   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
327   - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
328   - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
329   - Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
330   - deviceChannel.setLongitudeWgs84(position[0]);
331   - deviceChannel.setLatitudeWgs84(position[1]);
332   - }else {
333   - deviceChannel.setLongitudeGcj02(0.00);
334   - deviceChannel.setLatitudeGcj02(0.00);
335   - deviceChannel.setLongitudeWgs84(0.00);
336   - deviceChannel.setLatitudeWgs84(0.00);
337   - }
338   - }else {
339   - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
340   - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
341   - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
342   - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
343   - }
  385 +
  386 +
344 387 if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) {
345 388 //兼容INFO中的信息
346 389 Element info = itemDevice.element("Info");
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
... ... @@ -136,4 +136,12 @@ public class AssistRESTfulUtils {
136 136 return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
137 137 }
138 138  
  139 + public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
  140 + Map<String, Object> param = new HashMap<>();
  141 + param.put("app",app);
  142 + param.put("stream",stream);
  143 + param.put("callId",callId);
  144 + return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback);
  145 + }
  146 +
139 147 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -87,6 +87,9 @@ public class ZLMHttpHookListener {
87 87 @Autowired
88 88 private VideoStreamSessionManager sessionManager;
89 89  
  90 + @Autowired
  91 + private AssistRESTfulUtils assistRESTfulUtils;
  92 +
90 93 /**
91 94 * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
92 95 *
... ... @@ -99,12 +102,13 @@ public class ZLMHttpHookListener {
99 102 logger.debug("[ ZLM HOOK ] on_server_keepalive API调用,参数:" + json.toString());
100 103 }
101 104 String mediaServerId = json.getString("mediaServerId");
102   - List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive);
  105 + List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
103 106 if (subscribes != null && subscribes.size() > 0) {
104 107 for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
105 108 subscribe.response(null, json);
106 109 }
107 110 }
  111 + mediaServerService.updateMediaServerKeepalive(mediaServerId, json.getJSONObject("data"));
108 112  
109 113 JSONObject ret = new JSONObject();
110 114 ret.put("code", 0);
... ... @@ -164,7 +168,7 @@ public class ZLMHttpHookListener {
164 168 logger.debug("[ ZLM HOOK ]on_play API调用,参数:" + JSON.toJSONString(param));
165 169 }
166 170 String mediaServerId = param.getMediaServerId();
167   - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json);
  171 + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
168 172 if (subscribe != null ) {
169 173 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
170 174 if (mediaInfo != null) {
... ... @@ -200,7 +204,9 @@ public class ZLMHttpHookListener {
200 204  
201 205 logger.info("[ ZLM HOOK ]on_publish API调用,参数:" + json.toString());
202 206 JSONObject ret = new JSONObject();
203   - if (!"rtp".equals(param.getApp()) && !"broadcast".equals(param.getApp())) {
  207 + String mediaServerId = json.getString("mediaServerId");
  208 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  209 + if (!"rtp".equals(param.getApp())) {
204 210 // 推流鉴权
205 211 if (param.getParams() == null) {
206 212 logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
... ... @@ -231,6 +237,12 @@ public class ZLMHttpHookListener {
231 237 streamAuthorityInfo.setSign(sign);
232 238 // 鉴权通过
233 239 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  240 + // 通知assist新的callId
  241 + if (mediaInfo != null) {
  242 + assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
  243 + }
  244 + }else {
  245 + zlmMediaListManager.sendStreamEvent(param.getApp(),param.getStream(), param.getMediaServerId());
234 246 }
235 247  
236 248 ret.put("code", 0);
... ... @@ -240,10 +252,9 @@ public class ZLMHttpHookListener {
240 252 ret.put("enable_audio", true);
241 253 }
242 254  
243   - String mediaServerId = json.getString("mediaServerId");
244   - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
  255 +
  256 + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
245 257 if (subscribe != null) {
246   - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
247 258 if (mediaInfo != null) {
248 259 subscribe.response(mediaInfo, json);
249 260 }else {
... ... @@ -270,10 +281,12 @@ public class ZLMHttpHookListener {
270 281 ret.put("mp4_max_second", 10);
271 282 ret.put("enable_mp4", true);
272 283 ret.put("enable_audio", true);
  284 +
273 285 }
274 286 }
275 287  
276 288  
  289 +
277 290 return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
278 291 }
279 292  
... ... @@ -364,7 +377,7 @@ public class ZLMHttpHookListener {
364 377 logger.debug("[ ZLM HOOK ]on_shell_login API调用,参数:" + json.toString());
365 378 }
366 379 String mediaServerId = json.getString("mediaServerId");
367   - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json);
  380 + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_shell_login, json);
368 381 if (subscribe != null ) {
369 382 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
370 383 if (mediaInfo != null) {
... ... @@ -390,7 +403,7 @@ public class ZLMHttpHookListener {
390 403 logger.info("[ ZLM HOOK ]on_stream_changed API调用,参数:" + JSONObject.toJSONString(item));
391 404 String mediaServerId = item.getMediaServerId();
392 405 JSONObject json = (JSONObject) JSON.toJSON(item);
393   - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
  406 + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
394 407 if (subscribe != null ) {
395 408 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
396 409 if (mediaInfo != null) {
... ... @@ -438,7 +451,6 @@ public class ZLMHttpHookListener {
438 451 redisCatchStorage.stopPlayback(streamInfo.getDeviceID(), streamInfo.getChannelId(),
439 452 streamInfo.getStream(), null);
440 453 }
441   -
442 454 }
443 455 }else {
444 456 if (!"rtp".equals(app)){
... ... @@ -451,7 +463,6 @@ public class ZLMHttpHookListener {
451 463 StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
452 464 app, stream, tracks, streamAuthorityInfo.getCallId());
453 465 item.setStreamInfo(streamInfoByAppAndStream);
454   -
455 466 redisCatchStorage.addStream(mediaServerItem, type, app, stream, item);
456 467 if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
457 468 || item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
... ... @@ -459,20 +470,6 @@ public class ZLMHttpHookListener {
459 470 item.setSeverId(userSetting.getServerId());
460 471 zlmMediaListManager.addPush(item);
461 472 }
462   -
463   -// List<GbStream> gbStreams = new ArrayList<>();
464   -// if (streamPushItem == null || streamPushItem.getGbId() == null) {
465   -// GbStream gbStream = storager.getGbStream(app, streamId);
466   -// gbStreams.add(gbStream);
467   -// }else {
468   -// if (streamPushItem.getGbId() != null) {
469   -// gbStreams.add(streamPushItem);
470   -// }
471   -// }
472   -// if (gbStreams.size() > 0) {
473   -// eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON);
474   -// }
475   -
476 473 }else {
477 474 // 兼容流注销时类型从redis记录获取
478 475 MediaItem mediaItem = redisCatchStorage.getStreamInfo(app, stream, mediaServerId);
... ... @@ -616,16 +613,21 @@ public class ZLMHttpHookListener {
616 613 }
617 614 String remoteAddr = request.getRemoteAddr();
618 615 jsonObject.put("ip", remoteAddr);
619   - List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started);
  616 + List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
620 617 if (subscribes != null && subscribes.size() > 0) {
621 618 for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
622 619 subscribe.response(null, jsonObject);
623 620 }
624 621 }
  622 +
  623 + ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(jsonObject, ZLMServerConfig.class);
  624 + if (zlmServerConfig !=null ) {
  625 + mediaServerService.zlmServerOnline(zlmServerConfig);
  626 + }
625 627 JSONObject ret = new JSONObject();
626 628 ret.put("code", 0);
627 629 ret.put("msg", "success");
628   - return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
  630 + return new ResponseEntity<>(ret.toString(),HttpStatus.OK);
629 631 }
630 632  
631 633 private Map<String, String> urlParamToMap(String params) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
1 1 package com.genersoft.iot.vmp.media.zlm;
2 2  
3 3 import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.media.zlm.dto.HookType;
  5 +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe;
4 6 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
5 7 import org.springframework.stereotype.Component;
6 8 import org.springframework.util.CollectionUtils;
7 9  
  10 +import java.time.Instant;
8 11 import java.util.*;
9 12 import java.util.concurrent.ConcurrentHashMap;
  13 +import java.util.concurrent.TimeUnit;
10 14  
11 15 /**
12 16 * @description:针对 ZLMediaServer的hook事件订阅
... ... @@ -16,51 +20,39 @@ import java.util.concurrent.ConcurrentHashMap;
16 20 @Component
17 21 public class ZLMHttpHookSubscribe {
18 22  
19   - public enum HookType{
20   - on_flow_report,
21   - on_http_access,
22   - on_play,
23   - on_publish,
24   - on_record_mp4,
25   - on_rtsp_auth,
26   - on_rtsp_realm,
27   - on_shell_login,
28   - on_stream_changed,
29   - on_stream_none_reader,
30   - on_stream_not_found,
31   - on_server_started,
32   - on_server_keepalive
33   - }
34   -
35 23 @FunctionalInterface
36 24 public interface Event{
37 25 void response(MediaServerItem mediaServerItem, JSONObject response);
38 26 }
39 27  
40   - private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
  28 + private Map<HookType, Map<IHookSubscribe, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
41 29  
42   - public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) {
43   - allSubscribes.computeIfAbsent(type, k -> new ConcurrentHashMap<>()).put(hookResponse, event);
  30 + public void addSubscribe(IHookSubscribe hookSubscribe, ZLMHttpHookSubscribe.Event event) {
  31 + if (hookSubscribe.getExpires() == null) {
  32 + // 默认5分钟过期
  33 + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5));
  34 + hookSubscribe.setExpires(expiresInstant);
  35 + }
  36 + allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event);
44 37 }
45 38  
46   - public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) {
  39 + public ZLMHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) {
47 40 ZLMHttpHookSubscribe.Event event= null;
48   - Map<JSONObject, Event> eventMap = allSubscribes.get(type);
  41 + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type);
49 42 if (eventMap == null) {
50 43 return null;
51 44 }
52   - for (JSONObject key : eventMap.keySet()) {
  45 + for (IHookSubscribe key : eventMap.keySet()) {
53 46 Boolean result = null;
54   - for (String s : key.keySet()) {
  47 + for (String s : key.getContent().keySet()) {
55 48 if (result == null) {
56   - result = key.getString(s).equals(hookResponse.getString(s));
  49 + result = key.getContent().getString(s).equals(hookResponse.getString(s));
57 50 }else {
58   - if (key.getString(s) == null) {
  51 + if (key.getContent().getString(s) == null) {
59 52 continue;
60 53 }
61   - result = result && key.getString(s).equals(hookResponse.getString(s));
  54 + result = result && key.getContent().getString(s).equals(hookResponse.getString(s));
62 55 }
63   -
64 56 }
65 57 if (null != result && result) {
66 58 event = eventMap.get(key);
... ... @@ -69,26 +61,30 @@ public class ZLMHttpHookSubscribe {
69 61 return event;
70 62 }
71 63  
72   - public void removeSubscribe(HookType type, JSONObject hookResponse) {
73   - Map<JSONObject, Event> eventMap = allSubscribes.get(type);
  64 + public void removeSubscribe(IHookSubscribe hookSubscribe) {
  65 + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(hookSubscribe.getHookType());
74 66 if (eventMap == null) {
75 67 return;
76 68 }
77 69  
78   - Set<Map.Entry<JSONObject, Event>> entries = eventMap.entrySet();
  70 + Set<Map.Entry<IHookSubscribe, Event>> entries = eventMap.entrySet();
79 71 if (entries.size() > 0) {
80   - List<Map.Entry<JSONObject, ZLMHttpHookSubscribe.Event>> entriesToRemove = new ArrayList<>();
81   - for (Map.Entry<JSONObject, ZLMHttpHookSubscribe.Event> entry : entries) {
82   - JSONObject key = entry.getKey();
  72 + List<Map.Entry<IHookSubscribe, ZLMHttpHookSubscribe.Event>> entriesToRemove = new ArrayList<>();
  73 + for (Map.Entry<IHookSubscribe, ZLMHttpHookSubscribe.Event> entry : entries) {
  74 + JSONObject content = entry.getKey().getContent();
  75 + if (content == null || content.size() == 0) {
  76 + entriesToRemove.add(entry);
  77 + continue;
  78 + }
83 79 Boolean result = null;
84   - for (String s : key.keySet()) {
  80 + for (String s : content.keySet()) {
85 81 if (result == null) {
86   - result = key.getString(s).equals(hookResponse.getString(s));
  82 + result = content.getString(s).equals(hookSubscribe.getContent().getString(s));
87 83 }else {
88   - if (key.getString(s) == null) {
  84 + if (content.getString(s) == null) {
89 85 continue;
90 86 }
91   - result = result && key.getString(s).equals(hookResponse.getString(s));
  87 + result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s));
92 88 }
93 89 }
94 90 if (null != result && result){
... ... @@ -97,7 +93,7 @@ public class ZLMHttpHookSubscribe {
97 93 }
98 94  
99 95 if (!CollectionUtils.isEmpty(entriesToRemove)) {
100   - for (Map.Entry<JSONObject, ZLMHttpHookSubscribe.Event> entry : entriesToRemove) {
  96 + for (Map.Entry<IHookSubscribe, ZLMHttpHookSubscribe.Event> entry : entriesToRemove) {
101 97 entries.remove(entry);
102 98 }
103 99 }
... ... @@ -111,17 +107,25 @@ public class ZLMHttpHookSubscribe {
111 107 * @return
112 108 */
113 109 public List<ZLMHttpHookSubscribe.Event> getSubscribes(HookType type) {
114   - // ZLMHttpHookSubscribe.Event event= null;
115   - Map<JSONObject, Event> eventMap = allSubscribes.get(type);
  110 + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type);
116 111 if (eventMap == null) {
117 112 return null;
118 113 }
119 114 List<ZLMHttpHookSubscribe.Event> result = new ArrayList<>();
120   - for (JSONObject key : eventMap.keySet()) {
  115 + for (IHookSubscribe key : eventMap.keySet()) {
121 116 result.add(eventMap.get(key));
122 117 }
123 118 return result;
124 119 }
125 120  
  121 + public List<IHookSubscribe> getAll(){
  122 + ArrayList<IHookSubscribe> result = new ArrayList<>();
  123 + Collection<Map<IHookSubscribe, Event>> values = allSubscribes.values();
  124 + for (Map<IHookSubscribe, Event> value : values) {
  125 + result.addAll(value.keySet());
  126 + }
  127 + return result;
  128 + }
  129 +
126 130  
127 131 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
... ... @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
4 4 import com.genersoft.iot.vmp.conf.UserSetting;
5 5 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
6 6 import com.genersoft.iot.vmp.media.zlm.dto.*;
  7 +import com.genersoft.iot.vmp.service.IMediaServerService;
7 8 import com.genersoft.iot.vmp.service.IStreamProxyService;
8 9 import com.genersoft.iot.vmp.service.IStreamPushService;
9 10 import com.genersoft.iot.vmp.service.bean.ThirdPartyGB;
... ... @@ -63,125 +64,49 @@ public class ZLMMediaListManager {
63 64 @Autowired
64 65 private UserSetting userSetting;
65 66  
66   - private Map<String, ChannelOnlineEvent> channelOnlineEvents = new ConcurrentHashMap<>();
67   -
68   -
69   - public void updateMediaList(MediaServerItem mediaServerItem) {
70   - storager.clearMediaList();
71   -
72   - // 使用异步的当时更新媒体流列表
73   - zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{
74   - if (mediaList == null) {
75   - return;
76   - }
77   - String dataStr = mediaList.getString("data");
78   -
79   - Integer code = mediaList.getInteger("code");
80   - Map<String, StreamPushItem> result = new HashMap<>();
81   - List<StreamPushItem> streamPushItems = null;
82   - // 获取所有的国标关联
83   -// List<GbStream> gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId());
84   - if (code == 0 ) {
85   - if (dataStr != null) {
86   - streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
87   - }
88   - }else {
89   - logger.warn("更新视频流失败,错误code: " + code);
90   - }
91   -
92   - if (streamPushItems != null) {
93   - storager.updateMediaList(streamPushItems);
94   - for (StreamPushItem streamPushItem : streamPushItems) {
95   - JSONObject jsonObject = new JSONObject();
96   - jsonObject.put("app", streamPushItem.getApp());
97   - jsonObject.put("stream", streamPushItem.getStream());
98   - jsonObject.put("mediaServerId", mediaServerItem.getId());
99   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,
100   - (MediaServerItem mediaServerItemInuse, JSONObject response)->{
101   - updateMedia(mediaServerItem, response.getString("app"), response.getString("stream"));
102   - }
103   - );
104   - }
105   - }
106   - }));
  67 + @Autowired
  68 + private ZLMRTPServerFactory zlmrtpServerFactory;
107 69  
108   - }
  70 + @Autowired
  71 + private IMediaServerService mediaServerService;
109 72  
110   - public void addMedia(MediaServerItem mediaServerItem, String app, String streamId) {
111   - //使用异步更新推流
112   - updateMedia(mediaServerItem, app, streamId);
113   - }
  73 + private Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
114 74  
115 75 public StreamPushItem addPush(MediaItem mediaItem) {
116 76 // 查找此直播流是否存在redis预设gbId
117 77 StreamPushItem transform = streamPushService.transform(mediaItem);
118 78 StreamPushItem pushInDb = streamPushService.getPush(mediaItem.getApp(), mediaItem.getStream());
  79 + transform.setPushIng(mediaItem.isRegist());
119 80 transform.setUpdateTime(DateUtil.getNow());
120 81 transform.setPushTime(DateUtil.getNow());
  82 + transform.setSelf(userSetting.getServerId().equals(mediaItem.getSeverId()));
121 83 if (pushInDb == null) {
122 84 transform.setCreateTime(DateUtil.getNow());
123 85 streamPushMapper.add(transform);
124 86 }else {
125 87 streamPushMapper.update(transform);
126   -
127   -
128   -// if (!StringUtils.isEmpty(pushInDb.getGbId())) {
129   -// List<GbStream> gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId());
130   -// if (gbStreamList != null && gbStreamList.size() == 1) {
131   -// transform.setGbStreamId(gbStreamList.get(0).getGbStreamId());
132   -// transform.setPlatformId(gbStreamList.get(0).getPlatformId());
133   -// transform.setCatalogId(gbStreamList.get(0).getCatalogId());
134   -// transform.setGbId(gbStreamList.get(0).getGbId());
135   -// gbStreamMapper.update(transform);
136   -// streamPushMapper.del(gbStreamList.get(0).getApp(), gbStreamList.get(0).getStream());
137   -// }else {
138   -// transform.setCreateTime(DateUtil.getNow());
139   -// transform.setUpdateTime(DateUtil.getNow());
140   -// gbStreamMapper.add(transform);
141   -// }
142   - // 通知通道上线
143   -// if (transform != null) {
144   -// if (channelOnlineEvents.get(transform.getGbId()) != null) {
145   -// channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream(), transform.getServerId());
146   -// channelOnlineEvents.remove(transform.getGbId());
147   -// }
148   -// }
149   -// }
  88 + gbStreamMapper.updateMediaServer(mediaItem.getApp(), mediaItem.getStream(), mediaItem.getMediaServerId());
  89 + }
  90 + if (transform != null) {
  91 + if (getChannelOnlineEventLister(transform.getApp(), transform.getStream()) != null) {
  92 + getChannelOnlineEventLister(transform.getApp(), transform.getStream()).run(transform.getApp(), transform.getStream(), transform.getServerId());
  93 + removedChannelOnlineEventLister(transform.getApp(), transform.getStream());
  94 + }
150 95 }
151   -
152   -
153   -
154 96 return transform;
155 97 }
156 98  
157   -
158   - public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) {
159   - //使用异步更新推流
160   - zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{
161   -
162   - if (json == null) {
163   - return;
164   - }
165   - String dataStr = json.getString("data");
166   -
167   - Integer code = json.getInteger("code");
168   - Map<String, StreamPushItem> result = new HashMap<>();
169   - List<StreamPushItem> streamPushItems = null;
170   - if (code == 0 ) {
171   - if (dataStr != null) {
172   - streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
173   - }
174   - }else {
175   - logger.warn("更新视频流失败,错误code: " + code);
  99 + public void sendStreamEvent(String app, String stream, String mediaServerId) {
  100 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  101 + // 查看推流状态
  102 + if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) {
  103 + if (getChannelOnlineEventLister(app, stream) != null) {
  104 + getChannelOnlineEventLister(app, stream).run(app, stream, mediaServerId);
  105 + removedChannelOnlineEventLister(app, stream);
176 106 }
177   -
178   - if (streamPushItems != null && streamPushItems.size() == 1) {
179   - storager.updateMedia(streamPushItems.get(0));
180   - }
181   - });
  107 + }
182 108 }
183 109  
184   -
185 110 public int removeMedia(String app, String streamId) {
186 111 // 查找是否关联了国标, 关联了不删除, 置为离线
187 112 GbStream gbStream = gbStreamMapper.selectOne(app, streamId);
... ... @@ -189,48 +114,21 @@ public class ZLMMediaListManager {
189 114 if (gbStream == null) {
190 115 result = storager.removeMedia(app, streamId);
191 116 }else {
192   - // TODO 暂不设置为离线
193 117 result =storager.mediaOffline(app, streamId);
194 118 }
195 119 return result;
196 120 }
197 121  
198   - public void addChannelOnlineEventLister(String key, ChannelOnlineEvent callback) {
199   - this.channelOnlineEvents.put(key,callback);
  122 + public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) {
  123 + this.channelOnPublishEvents.put(app + "_" + stream, callback);
200 124 }
201 125  
202   - public void removedChannelOnlineEventLister(String key) {
203   - this.channelOnlineEvents.remove(key);
  126 + public void removedChannelOnlineEventLister(String app, String stream) {
  127 + this.channelOnPublishEvents.remove(app + "_" + stream);
204 128 }
205 129  
  130 + public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) {
  131 + return this.channelOnPublishEvents.get(app + "_" + stream);
  132 + }
206 133  
207   -
208   -// public void clearAllSessions() {
209   -// logger.info("清空所有国标相关的session");
210   -// JSONObject allSessionJSON = zlmresTfulUtils.getAllSession();
211   -// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
212   -// HashSet<String> allLocalPorts = new HashSet();
213   -// if (allSessionJSON.getInteger("code") == 0) {
214   -// JSONArray data = allSessionJSON.getJSONArray("data");
215   -// if (data.size() > 0) {
216   -// for (int i = 0; i < data.size(); i++) {
217   -// JSONObject sessionJOSN = data.getJSONObject(i);
218   -// Integer local_port = sessionJOSN.getInteger("local_port");
219   -// if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) &&
220   -// !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) &&
221   -// !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) &&
222   -// !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) &&
223   -// !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) &&
224   -// !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){
225   -// allLocalPorts.add(sessionJOSN.getInteger("local_port") + "");
226   -// }
227   -// }
228   -// }
229   -// }
230   -// if (allLocalPorts.size() > 0) {
231   -// List<String> result = new ArrayList<>(allLocalPorts);
232   -// String localPortSStr = String.join(",", result);
233   -// zlmresTfulUtils.kickSessions(localPortSStr);
234   -// }
235   -// }
236 134 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
... ... @@ -87,7 +87,7 @@ public class ZLMRTPServerFactory {
87 87 return result;
88 88 }
89 89  
90   - public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc) {
  90 + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port) {
91 91 int result = -1;
92 92 // 查询此rtp server 是否已经存在
93 93 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
... ... @@ -105,7 +105,11 @@ public class ZLMRTPServerFactory {
105 105 param.put("enable_tcp", 1);
106 106 param.put("stream_id", streamId);
107 107 // 推流端口设置0则使用随机端口
108   - param.put("port", 0);
  108 + if (port == null) {
  109 + param.put("port", 0);
  110 + }else {
  111 + param.put("port", port);
  112 + }
109 113 param.put("ssrc", ssrc);
110 114 JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
111 115  
... ... @@ -280,8 +284,10 @@ public class ZLMRTPServerFactory {
280 284 * 查询待转推的流是否就绪
281 285 */
282 286 public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
283   - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId);
284   - return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online") != null && mediaInfo.getBoolean("online"));
  287 + JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
  288 + return (mediaInfo.getInteger("code") == 0
  289 + && mediaInfo.getJSONArray("data") != null
  290 + && mediaInfo.getJSONArray("data").size() > 0);
285 291 }
286 292  
287 293 /**
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
... ... @@ -6,22 +6,22 @@ import com.alibaba.fastjson.JSONObject;
6 6 import com.genersoft.iot.vmp.conf.DynamicTask;
7 7 import com.genersoft.iot.vmp.conf.MediaConfig;
8 8 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  9 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForServerStarted;
  11 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
9 12 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
10 13 import com.genersoft.iot.vmp.service.IMediaServerService;
11   -import com.genersoft.iot.vmp.service.IStreamProxyService;
12   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
13 14 import org.slf4j.Logger;
14 15 import org.slf4j.LoggerFactory;
15 16 import org.springframework.beans.factory.annotation.Autowired;
16   -import org.springframework.beans.factory.annotation.Qualifier;
17 17 import org.springframework.boot.CommandLineRunner;
18 18 import org.springframework.core.annotation.Order;
19 19 import org.springframework.scheduling.annotation.Async;
20   -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
21 20 import org.springframework.stereotype.Component;
22   -import org.springframework.util.StringUtils;
23 21  
  22 +import java.time.Instant;
24 23 import java.util.*;
  24 +import java.util.concurrent.TimeUnit;
25 25  
26 26 @Component
27 27 @Order(value=1)
... ... @@ -38,18 +38,12 @@ public class ZLMRunner implements CommandLineRunner {
38 38 private ZLMHttpHookSubscribe hookSubscribe;
39 39  
40 40 @Autowired
41   - private IStreamProxyService streamProxyService;
42   -
43   - @Autowired
44 41 private EventPublisher publisher;
45 42  
46 43 @Autowired
47 44 private IMediaServerService mediaServerService;
48 45  
49 46 @Autowired
50   - private IRedisCatchStorage redisCatchStorage;
51   -
52   - @Autowired
53 47 private MediaConfig mediaConfig;
54 48  
55 49 @Autowired
... ... @@ -67,26 +61,24 @@ public class ZLMRunner implements CommandLineRunner {
67 61 mediaServerService.updateToDatabase(mediaSerItem);
68 62 }
69 63 mediaServerService.syncCatchFromDatabase();
  64 + HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started();
  65 +// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.SECONDS.toSeconds(60));
  66 +// hookSubscribeForStreamChange.setExpires(expiresInstant);
70 67 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
71   - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,new JSONObject(),
  68 + hookSubscribe.addSubscribe(hookSubscribeForServerStarted,
72 69 (MediaServerItem mediaServerItem, JSONObject response)->{
73 70 ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class);
74 71 if (zlmServerConfig !=null ) {
75 72 if (startGetMedia != null) {
76 73 startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
  74 + if (startGetMedia.size() == 0) {
  75 + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
  76 + }
77 77 }
78   - mediaServerService.zlmServerOnline(zlmServerConfig);
79 78 }
80 79 });
81 80  
82   - // 订阅 zlm保活事件, 当zlm离线时做业务的处理
83   - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_keepalive,new JSONObject(),
84   - (MediaServerItem mediaServerItem, JSONObject response)->{
85   - String mediaServerId = response.getString("mediaServerId");
86   - if (mediaServerId !=null ) {
87   - mediaServerService.updateMediaServerKeepalive(mediaServerId, response.getJSONObject("data"));
88   - }
89   - });
  81 +
90 82  
91 83 // 获取zlm信息
92 84 logger.info("[zlm] 等待默认zlm中...");
... ... @@ -125,6 +117,9 @@ public class ZLMRunner implements CommandLineRunner {
125 117 zlmServerConfigFirst.setIp(mediaServerItem.getIp());
126 118 zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort());
127 119 startGetMedia.remove(mediaServerItem.getId());
  120 + if (startGetMedia.size() == 0) {
  121 + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
  122 + }
128 123 mediaServerService.zlmServerOnline(zlmServerConfigFirst);
129 124 }else {
130 125 logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接",
... ... @@ -139,6 +134,9 @@ public class ZLMRunner implements CommandLineRunner {
139 134 zlmServerConfig.setIp(mediaServerItem.getIp());
140 135 zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
141 136 startGetMedia.remove(mediaServerItem.getId());
  137 + if (startGetMedia.size() == 0) {
  138 + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
  139 + }
142 140 mediaServerService.zlmServerOnline(zlmServerConfig);
143 141 }
144 142 }, 2000);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +
  4 +import com.alibaba.fastjson.JSONObject;
  5 +
  6 +/**
  7 + * hook 订阅工厂
  8 + * @author lin
  9 + */
  10 +public class HookSubscribeFactory {
  11 +
  12 + public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) {
  13 + HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange();
  14 + JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject();
  15 + subscribeKey.put("app", app);
  16 + subscribeKey.put("stream", stream);
  17 + subscribeKey.put("regist", regist);
  18 + if (scheam != null) {
  19 + subscribeKey.put("schema", scheam);
  20 + }
  21 + subscribeKey.put("mediaServerId", mediaServerId);
  22 + hookSubscribe.setContent(subscribeKey);
  23 +
  24 + return hookSubscribe;
  25 + }
  26 +
  27 + public static HookSubscribeForServerStarted on_server_started() {
  28 + HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted();
  29 + hookSubscribe.setContent(new JSONObject());
  30 +
  31 + return hookSubscribe;
  32 + }
  33 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.alibaba.fastjson.annotation.JSONField;
  5 +
  6 +import java.time.Instant;
  7 +
  8 +/**
  9 + * hook订阅-流变化
  10 + * @author lin
  11 + */
  12 +public class HookSubscribeForServerStarted implements IHookSubscribe{
  13 +
  14 + private HookType hookType = HookType.on_server_started;
  15 +
  16 + private JSONObject content;
  17 +
  18 + @JSONField(format="yyyy-MM-dd HH:mm:ss")
  19 + private Instant expires;
  20 +
  21 + @Override
  22 + public HookType getHookType() {
  23 + return hookType;
  24 + }
  25 +
  26 + @Override
  27 + public JSONObject getContent() {
  28 + return content;
  29 + }
  30 +
  31 + public void setContent(JSONObject content) {
  32 + this.content = content;
  33 + }
  34 +
  35 + @Override
  36 + public Instant getExpires() {
  37 + return expires;
  38 + }
  39 +
  40 + @Override
  41 + public void setExpires(Instant expires) {
  42 + this.expires = expires;
  43 + }
  44 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.alibaba.fastjson.annotation.JSONField;
  5 +
  6 +import java.time.Instant;
  7 +
  8 +/**
  9 + * hook订阅-流变化
  10 + * @author lin
  11 + */
  12 +public class HookSubscribeForStreamChange implements IHookSubscribe{
  13 +
  14 + private HookType hookType = HookType.on_stream_changed;
  15 +
  16 + private JSONObject content;
  17 +
  18 + private Instant expires;
  19 +
  20 + @Override
  21 + public HookType getHookType() {
  22 + return hookType;
  23 + }
  24 +
  25 + @Override
  26 + public JSONObject getContent() {
  27 + return content;
  28 + }
  29 +
  30 + public void setContent(JSONObject content) {
  31 + this.content = content;
  32 + }
  33 +
  34 + @Override
  35 + public Instant getExpires() {
  36 + return expires;
  37 + }
  38 +
  39 + @Override
  40 + public void setExpires(Instant expires) {
  41 + this.expires = expires;
  42 + }
  43 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +/**
  4 + * hook类型
  5 + * @author lin
  6 + */
  7 +
  8 +public enum HookType {
  9 +
  10 + on_flow_report,
  11 + on_http_access,
  12 + on_play,
  13 + on_publish,
  14 + on_record_mp4,
  15 + on_rtsp_auth,
  16 + on_rtsp_realm,
  17 + on_shell_login,
  18 + on_stream_changed,
  19 + on_stream_none_reader,
  20 + on_stream_not_found,
  21 + on_server_started,
  22 + on_server_keepalive
  23 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +
  5 +import java.time.Instant;
  6 +
  7 +/**
  8 + * zlm hook事件的参数
  9 + * @author lin
  10 + */
  11 +public interface IHookSubscribe {
  12 +
  13 + /**
  14 + * 获取hook类型
  15 + * @return hook类型
  16 + */
  17 + HookType getHookType();
  18 +
  19 + /**
  20 + * 获取hook的具体内容
  21 + * @return hook的具体内容
  22 + */
  23 + JSONObject getContent();
  24 +
  25 + /**
  26 + * 设置过期时间
  27 + * @param instant 过期时间
  28 + */
  29 + void setExpires(Instant instant);
  30 +
  31 + /**
  32 + * 获取过期时间
  33 + * @return 过期时间
  34 + */
  35 + Instant getExpires();
  36 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java
... ... @@ -4,6 +4,9 @@ import com.genersoft.iot.vmp.common.StreamInfo;
4 4  
5 5 import java.util.List;
6 6  
  7 +/**
  8 + * @author lin
  9 + */
7 10 public class MediaItem {
8 11  
9 12 /**
... ... @@ -22,6 +25,11 @@ public class MediaItem {
22 25 private String stream;
23 26  
24 27 /**
  28 + * 推流鉴权Id
  29 + */
  30 + private String callId;
  31 +
  32 + /**
25 33 * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv
26 34 */
27 35 private String totalReaderCount;
... ... @@ -427,4 +435,12 @@ public class MediaItem {
427 435 public void setSeverId(String severId) {
428 436 this.severId = severId;
429 437 }
  438 +
  439 + public String getCallId() {
  440 + return callId;
  441 + }
  442 +
  443 + public void setCallId(String callId) {
  444 + this.callId = callId;
  445 + }
430 446 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
... ... @@ -103,6 +103,18 @@ public class StreamPushItem extends GbStream implements Comparable&lt;StreamPushIte
103 103 */
104 104 private String createTime;
105 105  
  106 + /**
  107 + * 是否正在推流
  108 + */
  109 + private boolean pushIng;
  110 +
  111 + /**
  112 + * 是否自己平台的推流
  113 + */
  114 + private boolean self;
  115 +
  116 +
  117 +
106 118 public String getVhost() {
107 119 return vhost;
108 120 }
... ... @@ -277,5 +289,21 @@ public class StreamPushItem extends GbStream implements Comparable&lt;StreamPushIte
277 289 public void setCreateTime(String createTime) {
278 290 this.createTime = createTime;
279 291 }
  292 +
  293 + public boolean isPushIng() {
  294 + return pushIng;
  295 + }
  296 +
  297 + public void setPushIng(boolean pushIng) {
  298 + this.pushIng = pushIng;
  299 + }
  300 +
  301 + public boolean isSelf() {
  302 + return self;
  303 + }
  304 +
  305 + public void setSelf(boolean self) {
  306 + this.self = self;
  307 + }
280 308 }
281 309  
... ...
src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  4 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  5 +
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * 国标通道业务类
  10 + * @author lin
  11 + */
  12 +public interface IDeviceChannelService {
  13 +
  14 + /**
  15 + * 更新gps信息
  16 + */
  17 + DeviceChannel updateGps(DeviceChannel deviceChannel, Device device);
  18 +
  19 + /**
  20 + * 添加设备通道
  21 + *
  22 + * @param deviceId 设备id
  23 + * @param channel 通道
  24 + */
  25 + void updateChannel(String deviceId, DeviceChannel channel);
  26 +
  27 + /**
  28 + * 批量添加设备通道
  29 + *
  30 + * @param deviceId 设备id
  31 + * @param channels 多个通道
  32 + */
  33 + int updateChannels(String deviceId, List<DeviceChannel> channels);
  34 +
  35 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java
... ... @@ -18,7 +18,7 @@ public interface IGbStreamService {
18 18 * @param count
19 19 * @return
20 20 */
21   - PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId, String catalogId,String query,Boolean pushing,String mediaServerId);
  21 + PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId, String catalogId,String query,String mediaServerId);
22 22  
23 23  
24 24 /**
... ...
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
... ... @@ -47,6 +47,8 @@ public interface IMediaServerService {
47 47  
48 48 SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback);
49 49  
  50 + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port);
  51 +
50 52 void closeRTPServer(String deviceId, String channelId, String ssrc);
51 53  
52 54 void clearRTPServer(MediaServerItem mediaServerItem);
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * 平台关联通道管理
  9 + * @author lin
  10 + */
  11 +public interface IPlatformChannelService {
  12 +
  13 + /**
  14 + * 更新目录下的通道
  15 + * @param platformId 平台编号
  16 + * @param channelReduces 通道信息
  17 + * @param catalogId 目录编号
  18 + * @return
  19 + */
  20 + int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces, String catalogId);
  21 +
  22 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
... ... @@ -101,4 +101,9 @@ public interface IStreamProxyService {
101 101 void zlmServerOffline(String mediaServerId);
102 102  
103 103 void clean();
  104 +
  105 + /**
  106 + * 更新代理流
  107 + */
  108 + boolean updateStreamProxy(StreamProxyItem streamProxyItem);
104 109 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
5 5 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
6 6 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
7 7 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
  8 +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
8 9 import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto;
9 10 import com.github.pagehelper.PageInfo;
10 11  
... ... @@ -44,31 +45,59 @@ public interface IStreamPushService {
44 45 * 停止一路推流
45 46 * @param app 应用名
46 47 * @param streamId 流ID
47   - * @return
48 48 */
49 49 boolean stop(String app, String streamId);
50 50  
51 51 /**
52 52 * 新的节点加入
53   - * @param mediaServerId
54   - * @return
55 53 */
56 54 void zlmServerOnline(String mediaServerId);
57 55  
58 56 /**
59 57 * 节点离线
60   - * @param mediaServerId
61   - * @return
62 58 */
63 59 void zlmServerOffline(String mediaServerId);
64 60  
  61 + /**
  62 + * 清空
  63 + */
65 64 void clean();
66 65  
  66 +
67 67 boolean saveToRandomGB();
68 68  
  69 + /**
  70 + * 批量添加
  71 + */
69 72 void batchAdd(List<StreamPushItem> streamPushExcelDtoList);
70 73  
  74 + /**
  75 + * 中止多个推流
  76 + */
71 77 boolean batchStop(List<GbStream> streamPushItems);
72 78  
  79 + /**
  80 + * 导入时批量增加
  81 + */
73 82 void batchAddForUpload(List<StreamPushItem> streamPushItems, Map<String, List<String[]>> streamPushItemsForAll);
  83 +
  84 + /**
  85 + * 全部离线
  86 + */
  87 + void allStreamOffline();
  88 +
  89 + /**
  90 + * 推流离线
  91 + */
  92 + void offline(List<StreamPushItemFromRedis> offlineStreams);
  93 +
  94 + /**
  95 + * 推流上线
  96 + */
  97 + void online(List<StreamPushItemFromRedis> onlineStreams);
  98 +
  99 + /**
  100 + * 增加推流
  101 + */
  102 + boolean add(StreamPushItem stream);
74 103 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IUserService.java
1 1 package com.genersoft.iot.vmp.service;
2 2  
3 3 import com.genersoft.iot.vmp.storager.dao.dto.User;
  4 +import com.github.pagehelper.PageInfo;
4 5  
5 6 import java.util.List;
6 7  
... ... @@ -21,4 +22,8 @@ public interface IUserService {
21 22 int updateUsers(User user);
22 23  
23 24 boolean checkPushAuthority(String callId, String sign);
  25 +
  26 + PageInfo<User> getUsers(int page, int count);
  27 +
  28 + int changePushKey(int id, String pushKey);
24 29 }
... ...
src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.service;
2   -
3   -import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
4   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
5   -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
6   -import org.springframework.beans.factory.annotation.Autowired;
7   -import org.springframework.scheduling.annotation.Scheduled;
8   -import org.springframework.stereotype.Component;
9   -
10   -import java.util.List;
11   -
12   -
13   -/**
14   - * 定时查找redis中的GPS推送消息,并保存到对应的流中
15   - */
16   -@Component
17   -public class StreamGPSSubscribeTask {
18   -
19   - @Autowired
20   - private IRedisCatchStorage redisCatchStorage;
21   -
22   - @Autowired
23   - private IVideoManagerStorage storager;
24   -
25   -
26   - @Scheduled(fixedRate = 30 * 1000) //每30秒执行一次
27   - public void execute(){
28   - List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo();
29   - if (gpsMsgInfo.size() > 0) {
30   - storager.updateStreamGPS(gpsMsgInfo);
31   - for (GPSMsgInfo msgInfo : gpsMsgInfo) {
32   - msgInfo.setStored(true);
33   - redisCatchStorage.updateGpsMsgInfo(msgInfo);
34   - }
35   - }
36   -
37   - }
38   -}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java
... ... @@ -17,7 +17,6 @@ public class DeviceAlarmServiceImpl implements IDeviceAlarmService {
17 17 @Autowired
18 18 private DeviceAlarmMapper deviceAlarmMapper;
19 19  
20   -
21 20 @Override
22 21 public PageInfo<DeviceAlarm> getAllAlarm(int page, int count, String deviceId, String alarmPriority, String alarmMethod, String alarmType, String startTime, String endTime) {
23 22 PageHelper.startPage(page, count);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  6 +import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
  7 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
  8 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  9 +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
  10 +import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
  11 +import com.genersoft.iot.vmp.utils.DateUtil;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +import org.springframework.beans.factory.annotation.Autowired;
  15 +import org.springframework.stereotype.Service;
  16 +
  17 +import java.util.ArrayList;
  18 +import java.util.HashMap;
  19 +import java.util.List;
  20 +
  21 +/**
  22 + * @author lin
  23 + */
  24 +@Service
  25 +public class DeviceChannelServiceImpl implements IDeviceChannelService {
  26 +
  27 + private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class);
  28 +
  29 + @Autowired
  30 + private IRedisCatchStorage redisCatchStorage;
  31 +
  32 + @Autowired
  33 + private DeviceChannelMapper channelMapper;
  34 +
  35 + @Autowired
  36 + private DeviceMapper deviceMapper;
  37 +
  38 + @Override
  39 + public DeviceChannel updateGps(DeviceChannel deviceChannel, Device device) {
  40 + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) {
  41 + if (device == null) {
  42 + device = deviceMapper.getDeviceByDeviceId(deviceChannel.getDeviceId());
  43 + }
  44 +
  45 + if ("WGS84".equals(device.getGeoCoordSys())) {
  46 + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
  47 + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
  48 + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
  49 + deviceChannel.setLongitudeGcj02(position[0]);
  50 + deviceChannel.setLatitudeGcj02(position[1]);
  51 + }else if ("GCJ02".equals(device.getGeoCoordSys())) {
  52 + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
  53 + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
  54 + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
  55 + deviceChannel.setLongitudeWgs84(position[0]);
  56 + deviceChannel.setLatitudeWgs84(position[1]);
  57 + }else {
  58 + deviceChannel.setLongitudeGcj02(0.00);
  59 + deviceChannel.setLatitudeGcj02(0.00);
  60 + deviceChannel.setLongitudeWgs84(0.00);
  61 + deviceChannel.setLatitudeWgs84(0.00);
  62 + }
  63 + }else {
  64 + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
  65 + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
  66 + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
  67 + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
  68 + }
  69 + return deviceChannel;
  70 + }
  71 +
  72 + @Override
  73 + public void updateChannel(String deviceId, DeviceChannel channel) {
  74 + String channelId = channel.getChannelId();
  75 + channel.setDeviceId(deviceId);
  76 + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
  77 + if (streamInfo != null) {
  78 + channel.setStreamId(streamInfo.getStream());
  79 + }
  80 + String now = DateUtil.getNow();
  81 + channel.setUpdateTime(now);
  82 + DeviceChannel deviceChannel = channelMapper.queryChannel(deviceId, channelId);
  83 + channel = updateGps(channel, null);
  84 + if (deviceChannel == null) {
  85 + channel.setCreateTime(now);
  86 + channelMapper.add(channel);
  87 + }else {
  88 + channelMapper.update(channel);
  89 + }
  90 + channelMapper.updateChannelSubCount(deviceId,channel.getParentId());
  91 + }
  92 +
  93 + @Override
  94 + public int updateChannels(String deviceId, List<DeviceChannel> channels) {
  95 + List<DeviceChannel> addChannels = new ArrayList<>();
  96 + List<DeviceChannel> updateChannels = new ArrayList<>();
  97 + HashMap<String, DeviceChannel> channelsInStore = new HashMap<>();
  98 + Device device = deviceMapper.getDeviceByDeviceId(deviceId);
  99 + if (channels != null && channels.size() > 0) {
  100 + List<DeviceChannel> channelList = channelMapper.queryChannels(deviceId, null, null, null, null);
  101 + if (channelList.size() == 0) {
  102 + for (DeviceChannel channel : channels) {
  103 + channel.setDeviceId(deviceId);
  104 + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
  105 + if (streamInfo != null) {
  106 + channel.setStreamId(streamInfo.getStream());
  107 + }
  108 + String now = DateUtil.getNow();
  109 + channel.setUpdateTime(now);
  110 + channel.setCreateTime(now);
  111 + channel = updateGps(channel, device);
  112 + addChannels.add(channel);
  113 + }
  114 + }else {
  115 + for (DeviceChannel deviceChannel : channelList) {
  116 + channelsInStore.put(deviceChannel.getChannelId(), deviceChannel);
  117 + }
  118 + for (DeviceChannel channel : channels) {
  119 + channel.setDeviceId(deviceId);
  120 + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
  121 + if (streamInfo != null) {
  122 + channel.setStreamId(streamInfo.getStream());
  123 + }
  124 + String now = DateUtil.getNow();
  125 + channel.setUpdateTime(now);
  126 + channel = updateGps(channel, device);
  127 + if (channelsInStore.get(channel.getChannelId()) != null) {
  128 + updateChannels.add(channel);
  129 + }else {
  130 + addChannels.add(channel);
  131 + channel.setCreateTime(now);
  132 + }
  133 + }
  134 + }
  135 + int limitCount = 300;
  136 + if (addChannels.size() > 0) {
  137 + if (addChannels.size() > limitCount) {
  138 + for (int i = 0; i < addChannels.size(); i += limitCount) {
  139 + int toIndex = i + limitCount;
  140 + if (i + limitCount > addChannels.size()) {
  141 + toIndex = addChannels.size();
  142 + }
  143 + channelMapper.batchAdd(addChannels.subList(i, toIndex));
  144 + }
  145 + }else {
  146 + channelMapper.batchAdd(addChannels);
  147 + }
  148 + }
  149 + if (updateChannels.size() > 0) {
  150 + if (updateChannels.size() > limitCount) {
  151 + for (int i = 0; i < updateChannels.size(); i += limitCount) {
  152 + int toIndex = i + limitCount;
  153 + if (i + limitCount > updateChannels.size()) {
  154 + toIndex = updateChannels.size();
  155 + }
  156 + channelMapper.batchUpdate(updateChannels.subList(i, toIndex));
  157 + }
  158 + }else {
  159 + channelMapper.batchUpdate(updateChannels);
  160 + }
  161 + }
  162 + }
  163 + return addChannels.size() + updateChannels.size();
  164 + }
  165 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
... ... @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
7 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
8 8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
9 9 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
  10 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
10 11 import com.genersoft.iot.vmp.service.IDeviceService;
11 12 import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
12 13 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
... ... @@ -56,6 +57,9 @@ public class DeviceServiceImpl implements IDeviceService {
56 57 private DeviceMapper deviceMapper;
57 58  
58 59 @Autowired
  60 + private IDeviceChannelService deviceChannelService;
  61 +
  62 + @Autowired
59 63 private DeviceChannelMapper deviceChannelMapper;
60 64  
61 65 @Autowired
... ... @@ -82,10 +86,10 @@ public class DeviceServiceImpl implements IDeviceService {
82 86 redisCatchStorage.clearCatchByDeviceId(device.getDeviceId());
83 87 }
84 88 device.setUpdateTime(now);
85   - device.setOnline(1);
86 89  
87   - // 第一次上线
  90 + // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
88 91 if (device.getCreateTime() == null) {
  92 + device.setOnline(1);
89 93 device.setCreateTime(now);
90 94 logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId());
91 95 deviceMapper.add(device);
... ... @@ -93,8 +97,19 @@ public class DeviceServiceImpl implements IDeviceService {
93 97 commander.deviceInfoQuery(device);
94 98 sync(device);
95 99 }else {
96   - deviceMapper.update(device);
97   - redisCatchStorage.updateDevice(device);
  100 + if(device.getOnline() == 0){
  101 + device.setOnline(1);
  102 + device.setCreateTime(now);
  103 + logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId());
  104 + deviceMapper.update(device);
  105 + redisCatchStorage.updateDevice(device);
  106 + commander.deviceInfoQuery(device);
  107 + sync(device);
  108 + }else {
  109 + deviceMapper.update(device);
  110 + redisCatchStorage.updateDevice(device);
  111 + }
  112 +
98 113 }
99 114  
100 115 // 上线添加订阅
... ... @@ -121,6 +136,8 @@ public class DeviceServiceImpl implements IDeviceService {
121 136 device.setOnline(0);
122 137 redisCatchStorage.updateDevice(device);
123 138 deviceMapper.update(device);
  139 + //进行通道离线
  140 + deviceChannelMapper.offlineByDeviceId(deviceId);
124 141 // 离线释放所有ssrc
125 142 List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null);
126 143 if (ssrcTransactions != null && ssrcTransactions.size() > 0) {
... ... @@ -143,7 +160,7 @@ public class DeviceServiceImpl implements IDeviceService {
143 160 logger.info("[添加目录订阅] 设备{}", device.getDeviceId());
144 161 // 添加目录订阅
145 162 CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask);
146   - // 提前开始刷新订阅
  163 + // 刷新订阅
147 164 int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30);
148 165 // 设置最小值为30
149 166 dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000);
... ... @@ -178,8 +195,8 @@ public class DeviceServiceImpl implements IDeviceService {
178 195 MobilePositionSubscribeTask mobilePositionSubscribeTask = new MobilePositionSubscribeTask(device, sipCommander, dynamicTask);
179 196 // 设置最小值为30
180 197 int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30);
181   - // 提前开始刷新订阅
182   - dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog -1 ) * 1000);
  198 + // 刷新订阅
  199 + dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog) * 1000);
183 200 return true;
184 201 }
185 202  
... ... @@ -324,23 +341,12 @@ public class DeviceServiceImpl implements IDeviceService {
324 341 private void updateDeviceChannelGeoCoordSys(Device device) {
325 342 List<DeviceChannel> deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId());
326 343 if (deviceChannels.size() > 0) {
  344 + List<DeviceChannel> deviceChannelsForStore = new ArrayList<>();
327 345 for (DeviceChannel deviceChannel : deviceChannels) {
328   - if ("WGS84".equals(device.getGeoCoordSys())) {
329   - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
330   - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
331   - Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
332   - deviceChannel.setLongitudeGcj02(position[0]);
333   - deviceChannel.setLatitudeGcj02(position[1]);
334   - }else if ("GCJ02".equals(device.getGeoCoordSys())) {
335   - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
336   - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
337   - Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
338   - deviceChannel.setLongitudeWgs84(position[0]);
339   - deviceChannel.setLatitudeWgs84(position[1]);
340   - }
  346 + deviceChannelsForStore.add(deviceChannelService.updateGps(deviceChannel, device));
341 347 }
  348 + deviceChannelService.updateChannels(device.getDeviceId(), deviceChannelsForStore);
342 349 }
343   - storage.updateChannels(device.getDeviceId(), deviceChannels);
344 350 }
345 351  
346 352  
... ... @@ -352,11 +358,11 @@ public class DeviceServiceImpl implements IDeviceService {
352 358 }
353 359 if (parentId == null || parentId.equals(deviceId)) {
354 360 // 字根节点开始查询
355   - List<DeviceChannel> rootNodes = getRootNodes(deviceId, "CivilCode".equals(device.getTreeType()), true, !onlyCatalog);
  361 + List<DeviceChannel> rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), true, !onlyCatalog);
356 362 return transportChannelsToTree(rootNodes, "");
357 363 }
358 364  
359   - if ("CivilCode".equals(device.getTreeType())) {
  365 + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) {
360 366 if (parentId.length()%2 != 0) {
361 367 return null;
362 368 }
... ... @@ -386,7 +392,7 @@ public class DeviceServiceImpl implements IDeviceService {
386 392  
387 393 }
388 394 // 使用业务分组展示树
389   - if ("BusinessGroup".equals(device.getTreeType())) {
  395 + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) {
390 396 if (parentId.length() < 14 ) {
391 397 return null;
392 398 }
... ... @@ -406,11 +412,11 @@ public class DeviceServiceImpl implements IDeviceService {
406 412 }
407 413 if (parentId == null || parentId.equals(deviceId)) {
408 414 // 字根节点开始查询
409   - List<DeviceChannel> rootNodes = getRootNodes(deviceId, "CivilCode".equals(device.getTreeType()), false, true);
  415 + List<DeviceChannel> rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), false, true);
410 416 return rootNodes;
411 417 }
412 418  
413   - if ("CivilCode".equals(device.getTreeType())) {
  419 + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) {
414 420 if (parentId.length()%2 != 0) {
415 421 return null;
416 422 }
... ... @@ -431,7 +437,7 @@ public class DeviceServiceImpl implements IDeviceService {
431 437  
432 438 }
433 439 // 使用业务分组展示树
434   - if ("BusinessGroup".equals(device.getTreeType())) {
  440 + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) {
435 441 if (parentId.length() < 14 ) {
436 442 return null;
437 443 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
3 3 import com.genersoft.iot.vmp.conf.SipConfig;
4   -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
5   -import com.genersoft.iot.vmp.gb28181.bean.GbStream;
6   -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  4 +import com.genersoft.iot.vmp.gb28181.bean.*;
7 5 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
8 6 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
9 7 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
10 8 import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
11 9 import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
  10 +import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper;
12 11 import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
13 12 import com.genersoft.iot.vmp.service.IGbStreamService;
14 13 import com.github.pagehelper.PageHelper;
... ... @@ -46,15 +45,15 @@ public class GbStreamServiceImpl implements IGbStreamService {
46 45 private ParentPlatformMapper platformMapper;
47 46  
48 47 @Autowired
49   - private SipConfig sipConfig;
  48 + private PlatformCatalogMapper catalogMapper;
50 49  
51 50 @Autowired
52 51 private EventPublisher eventPublisher;
53 52  
54 53 @Override
55   - public PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId, String catalogId, String query, Boolean pushing, String mediaServerId) {
  54 + public PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId, String catalogId, String query, String mediaServerId) {
56 55 PageHelper.startPage(page, count);
57   - List<GbStream> all = gbStreamMapper.selectAll(platFormId, catalogId, query, pushing, mediaServerId);
  56 + List<GbStream> all = gbStreamMapper.selectAll(platFormId, catalogId, query, mediaServerId);
58 57 return new PageInfo<>(all);
59 58 }
60 59  
... ... @@ -102,16 +101,25 @@ public class GbStreamServiceImpl implements IGbStreamService {
102 101 deviceChannel.setLatitude(gbStream.getLatitude());
103 102 deviceChannel.setDeviceId(platform.getDeviceGBId());
104 103 deviceChannel.setManufacture("wvp-pro");
105   -// deviceChannel.setStatus(gbStream.isStatus()?1:0);
106   - deviceChannel.setStatus(1);
107   - deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId);
  104 + deviceChannel.setStatus(gbStream.isStatus()?1:0);
  105 +
108 106 deviceChannel.setRegisterWay(1);
109   - if (catalogId.length() > 0 && catalogId.length() <= 10) {
110   - // 父节点是行政区划,则设置CivilCode使用此行政区划
  107 + deviceChannel.setCivilCode(platform.getAdministrativeDivision());
  108 +
  109 + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){
111 110 deviceChannel.setCivilCode(catalogId);
112   - }else {
113   - deviceChannel.setCivilCode(platform.getAdministrativeDivision());
  111 + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){
  112 + PlatformCatalog catalog = catalogMapper.select(catalogId);
  113 + if (catalog == null) {
  114 + deviceChannel.setParentId(platform.getDeviceGBId());
  115 + deviceChannel.setBusinessGroupId(null);
  116 + }else {
  117 + deviceChannel.setParentId(catalog.getId());
  118 + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId());
  119 + }
  120 +
114 121 }
  122 +
115 123 deviceChannel.setModel("live");
116 124 deviceChannel.setOwner("wvp-pro");
117 125 deviceChannel.setParental(0);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
  3 +import java.time.LocalDateTime;
  4 +import java.util.ArrayList;
  5 +import java.util.Collections;
  6 +import java.util.HashMap;
  7 +import java.util.List;
  8 +import java.util.Map;
  9 +import java.util.Set;
  10 +
  11 +import com.genersoft.iot.vmp.media.zlm.ZLMRunner;
  12 +import com.genersoft.iot.vmp.service.IStreamProxyService;
  13 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
  16 +import org.springframework.beans.factory.annotation.Autowired;
  17 +import org.springframework.beans.factory.annotation.Value;
  18 +import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.transaction.TransactionDefinition;
  21 +import org.springframework.transaction.TransactionStatus;
  22 +import org.springframework.util.StringUtils;
  23 +
3 24 import com.alibaba.fastjson.JSON;
4 25 import com.alibaba.fastjson.JSONArray;
5 26 import com.alibaba.fastjson.JSONObject;
... ... @@ -14,25 +35,16 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
14 35 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
15 36 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 37 import com.genersoft.iot.vmp.service.IMediaServerService;
17   -import com.genersoft.iot.vmp.service.IStreamProxyService;
18 38 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
19   -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
20 39 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
21 40 import com.genersoft.iot.vmp.utils.DateUtil;
22 41 import com.genersoft.iot.vmp.utils.redis.JedisUtil;
23 42 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
24 43 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
25   -import okhttp3.*;
26   -import org.slf4j.Logger;
27   -import org.slf4j.LoggerFactory;
28   -import org.springframework.beans.factory.annotation.Autowired;
29   -import org.springframework.beans.factory.annotation.Value;
30   -import org.springframework.jdbc.datasource.DataSourceTransactionManager;
31   -import org.springframework.stereotype.Service;
32   -import org.springframework.transaction.TransactionDefinition;
33   -import org.springframework.transaction.TransactionStatus;
34   -import org.springframework.util.StringUtils;
35 44  
  45 +import okhttp3.OkHttpClient;
  46 +import okhttp3.Request;
  47 +import okhttp3.Response;
36 48 import java.time.LocalDateTime;
37 49 import java.util.*;
38 50  
... ... @@ -47,6 +59,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
47 59 @Autowired
48 60 private SipConfig sipConfig;
49 61  
  62 + @Autowired
  63 + private ZLMRunner zlmRunner;
  64 +
50 65 @Value("${server.ssl.enabled:false}")
51 66 private boolean sslEnabled;
52 67  
... ... @@ -120,7 +135,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
120 135 }
121 136  
122 137 @Override
123   - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback) {
  138 + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) {
124 139 if (mediaServerItem == null || mediaServerItem.getId() == null) {
125 140 return null;
126 141 }
... ... @@ -148,7 +163,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
148 163 }
149 164 int rtpServerPort = mediaServerItem.getRtpProxyPort();
150 165 if (mediaServerItem.isRtpEnable()) {
151   - rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0);
  166 + rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port);
152 167 }
153 168 redisUtil.set(key, mediaServerItem);
154 169 return new SSRCInfo(rtpServerPort, ssrc, streamId);
... ... @@ -156,6 +171,11 @@ public class MediaServerServiceImpl implements IMediaServerService {
156 171 }
157 172  
158 173 @Override
  174 + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) {
  175 + return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null);
  176 + }
  177 +
  178 + @Override
159 179 public void closeRTPServer(String deviceId, String channelId, String stream) {
160 180 String mediaServerId = streamSession.getMediaServerId(deviceId, channelId, stream);
161 181 String ssrc = streamSession.getSSRC(deviceId, channelId, stream);
... ... @@ -271,7 +291,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
271 291 return null;
272 292 }
273 293 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId;
274   - return (MediaServerItem)redisUtil.get(key);
  294 + MediaServerItem serverItem=(MediaServerItem)redisUtil.get(key);
  295 + if(null==serverItem){
  296 + //zlm服务不在线,启动重连
  297 + reloadZlm();
  298 + serverItem=(MediaServerItem)redisUtil.get(key);
  299 + }
  300 + return serverItem;
275 301 }
276 302  
277 303 @Override
... ... @@ -351,14 +377,15 @@ public class MediaServerServiceImpl implements IMediaServerService {
351 377 */
352 378 @Override
353 379 public void zlmServerOnline(ZLMServerConfig zlmServerConfig) {
354   - logger.info("[ZLM] 正在连接 : {} -> {}:{}",
355   - zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
356 380  
357 381 MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId());
358 382 if (serverItem == null) {
359 383 logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() );
360 384 logger.warn("请检查ZLM的<general.mediaServerId>配置是否与WVP的<media.id>一致");
361 385 return;
  386 + }else {
  387 + logger.info("[ZLM] 正在连接 : {} -> {}:{}",
  388 + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
362 389 }
363 390 serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval());
364 391 if (serverItem.getHttpPort() == 0) {
... ... @@ -463,8 +490,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
463 490 String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
464 491  
465 492 if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) {
466   - logger.info("获取负载最低的节点时无在线节点");
467   - return null;
  493 + logger.info("获取负载最低的节点时无在线节点,启动重连机制");
  494 + //启动重连
  495 + reloadZlm();
  496 + if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) {
  497 + logger.info("获取负载最低的节点时无在线节点");
  498 + return null;
  499 + }
468 500 }
469 501  
470 502 // 获取分数最低的,及并发最低的
... ... @@ -595,9 +627,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
595 627 boolean result = false;
596 628 OkHttpClient client = new OkHttpClient();
597 629 String url = String.format("http://%s:%s/index/api/record", ip, port);
598   -
599   - FormBody.Builder builder = new FormBody.Builder();
600   -
601 630 Request request = new Request.Builder()
602 631 .get()
603 632 .url(url)
... ... @@ -629,9 +658,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
629 658 MediaServerItem mediaServerItem = getOne(mediaServerId);
630 659 if (mediaServerItem == null) {
631 660 // zlm连接重试
632   -
633   - logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息");
634   - return;
  661 + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息,尝试重连zlm");
  662 + reloadZlm();
  663 + mediaServerItem = getOne(mediaServerId);
  664 + if (mediaServerItem == null) {
  665 + // zlm连接重试
  666 + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息");
  667 + return;
  668 + }
635 669 }
636 670 String key = VideoManagerConstants.MEDIA_SERVER_KEEPALIVE_PREFIX + userSetting.getServerId() + "_" + mediaServerId;
637 671 int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2;
... ... @@ -648,10 +682,18 @@ public class MediaServerServiceImpl implements IMediaServerService {
648 682 mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem);
649 683 }
650 684 for (MediaServerItem mediaServerItem : allInCatch) {
651   - if (mediaServerItemMap.get(mediaServerItem) == null) {
  685 + if (!mediaServerItemMap.containsKey(mediaServerItem.getId())) {
652 686 delete(mediaServerItem.getId());
653 687 }
654 688 }
655 689 }
656 690  
  691 + public void reloadZlm(){
  692 + try {
  693 + zlmRunner.run();
  694 + Thread.sleep(500);//延迟0.5秒缓冲时间
  695 + } catch (Exception e) {
  696 + logger.warn("尝试重连zlm失败!",e);
  697 + }
  698 + }
657 699 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
... ... @@ -67,9 +67,9 @@ public class MediaServiceImpl implements IMediaService {
67 67 JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class);
68 68 JSONArray tracks = mediaJSON.getJSONArray("tracks");
69 69 if (authority) {
70   - streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null, streamAuthorityInfo.getCallId(), true);
  70 + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,streamAuthorityInfo.getCallId(), true);
71 71 }else {
72   - streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
  72 + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null);
73 73 }
74 74  
75 75 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  4 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  5 +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
  6 +import com.genersoft.iot.vmp.gb28181.bean.TreeType;
  7 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  8 +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  9 +import com.genersoft.iot.vmp.service.IPlatformChannelService;
  10 +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
  11 +import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
  12 +import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper;
  13 +import com.genersoft.iot.vmp.storager.dao.PlatformChannelMapper;
  14 +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
  15 +import org.slf4j.Logger;
  16 +import org.slf4j.LoggerFactory;
  17 +import org.springframework.beans.factory.annotation.Autowired;
  18 +import org.springframework.stereotype.Service;
  19 +
  20 +import java.util.ArrayList;
  21 +import java.util.HashMap;
  22 +import java.util.List;
  23 +import java.util.Map;
  24 +
  25 +/**
  26 + * @author lin
  27 + */
  28 +@Service
  29 +public class PlatformChannelServiceImpl implements IPlatformChannelService {
  30 +
  31 + private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class);
  32 +
  33 + @Autowired
  34 + private PlatformChannelMapper platformChannelMapper;
  35 +
  36 + @Autowired
  37 + private DeviceChannelMapper deviceChannelMapper;
  38 +
  39 + @Autowired
  40 + private PlatformCatalogMapper catalogManager;
  41 +
  42 + @Autowired
  43 + private ParentPlatformMapper platformMapper;
  44 +
  45 + @Autowired
  46 + EventPublisher eventPublisher;
  47 +
  48 + @Override
  49 + public int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces, String catalogId) {
  50 + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId);
  51 + if (platform == null) {
  52 + logger.warn("更新级联通道信息时未找到平台{}的信息", platformId);
  53 + return 0;
  54 + }
  55 + Map<Integer, ChannelReduce> deviceAndChannels = new HashMap<>();
  56 + for (ChannelReduce channelReduce : channelReduces) {
  57 + channelReduce.setCatalogId(catalogId);
  58 + deviceAndChannels.put(channelReduce.getId(), channelReduce);
  59 + }
  60 + List<Integer> deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet());
  61 + // 查询当前已经存在的
  62 + List<Integer> channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces);
  63 + if (deviceAndChannelList != null) {
  64 + deviceAndChannelList.removeAll(channelIds);
  65 + }
  66 + for (Integer channelId : channelIds) {
  67 + deviceAndChannels.remove(channelId);
  68 + }
  69 + List<ChannelReduce> channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
  70 + // 对剩下的数据进行存储
  71 + int result = 0;
  72 + if (channelReducesToAdd.size() > 0) {
  73 + result = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
  74 + // TODO 后续给平台增加控制开关以控制是否响应目录订阅
  75 + List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
  76 + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
  77 + }
  78 +
  79 + return result;
  80 + }
  81 +
  82 + private List<DeviceChannel> getDeviceChannelListByChannelReduceList(List<ChannelReduce> channelReduces, String catalogId, ParentPlatform platform) {
  83 + List<DeviceChannel> deviceChannelList = new ArrayList<>();
  84 + if (channelReduces.size() > 0){
  85 + PlatformCatalog catalog = catalogManager.select(catalogId);
  86 + if (catalog == null && !catalogId.equals(platform.getServerGBId())) {
  87 + logger.warn("未查询到目录{}的信息", catalogId);
  88 + return null;
  89 + }
  90 + for (ChannelReduce channelReduce : channelReduces) {
  91 + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
  92 + deviceChannel.setParental(0);
  93 + deviceChannelList.add(deviceChannel);
  94 + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){
  95 + deviceChannel.setCivilCode(catalogId);
  96 + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){
  97 + deviceChannel.setParentId(catalogId);
  98 + if (catalog != null) {
  99 + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId());
  100 + }
  101 + }
  102 + }
  103 + }
  104 + return deviceChannelList;
  105 + }
  106 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
  3 +import java.math.BigDecimal;
  4 +import java.math.RoundingMode;
  5 +import java.util.List;
  6 +import java.util.Objects;
  7 +import java.util.UUID;
  8 +
  9 +import javax.sip.ResponseEvent;
  10 +
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.http.HttpStatus;
  15 +import org.springframework.http.ResponseEntity;
  16 +import org.springframework.stereotype.Service;
  17 +import org.springframework.web.context.request.async.DeferredResult;
  18 +
3 19 import com.alibaba.fastjson.JSON;
4 20 import com.alibaba.fastjson.JSONArray;
5 21 import com.alibaba.fastjson.JSONObject;
... ... @@ -7,7 +23,13 @@ import com.genersoft.iot.vmp.common.StreamInfo;
7 23 import com.genersoft.iot.vmp.conf.DynamicTask;
8 24 import com.genersoft.iot.vmp.conf.SipConfig;
9 25 import com.genersoft.iot.vmp.conf.UserSetting;
10   -import com.genersoft.iot.vmp.gb28181.bean.*;
  26 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  27 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  28 +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
  29 +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
  30 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  31 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  32 +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
11 33 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
12 34 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
13 35 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
... ... @@ -16,6 +38,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
16 38 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
17 39 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
18 40 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  41 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  42 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  43 +import com.genersoft.iot.vmp.media.zlm.dto.HookType;
19 44 import com.genersoft.iot.vmp.utils.DateUtil;
20 45 import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
21 46 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
... ... @@ -32,9 +57,11 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
32 57 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
33 58 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
34 59 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
  60 +import com.genersoft.iot.vmp.utils.DateUtil;
35 61 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
36 62 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
37 63 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
  64 +
38 65 import gov.nist.javax.sip.stack.SIPDialog;
39 66 import org.slf4j.Logger;
40 67 import org.slf4j.LoggerFactory;
... ... @@ -313,16 +340,10 @@ public class PlayServiceImpl implements IPlayService {
313 340 // 单端口模式streamId也有变化,需要重新设置监听
314 341 if (!mediaServerItem.isRtpEnable()) {
315 342 // 添加订阅
316   - JSONObject subscribeKey = new JSONObject();
317   - subscribeKey.put("app", "rtp");
318   - subscribeKey.put("stream", stream);
319   - subscribeKey.put("regist", true);
320   - subscribeKey.put("schema", "rtmp");
321   - subscribeKey.put("mediaServerId", mediaServerItem.getId());
322   - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed,subscribeKey);
323   - subscribeKey.put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
324   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
325   - (MediaServerItem mediaServerItemInUse, JSONObject response)->{
  343 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtmp", mediaServerItem.getId());
  344 + subscribe.removeSubscribe(hookSubscribe);
  345 + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
  346 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{
326 347 logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
327 348 dynamicTask.stop(timeOutTaskKey);
328 349 // hook响应
... ... @@ -333,7 +354,7 @@ public class PlayServiceImpl implements IPlayService {
333 354 // 关闭rtp server
334 355 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
335 356 // 重新开启ssrc server
336   - mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false);
  357 + mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, finalSsrcInfo.getPort());
337 358  
338 359 }
339 360 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java
... ... @@ -8,6 +8,9 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
8 8 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
9 9 import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
10 10 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  11 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  12 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  13 +import com.genersoft.iot.vmp.media.zlm.dto.HookType;
11 14 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
12 15 import com.genersoft.iot.vmp.service.IMediaServerService;
13 16 import com.genersoft.iot.vmp.service.bean.*;
... ... @@ -270,14 +273,9 @@ public class RedisGbPlayMsgListener implements MessageListener {
270 273 }, userSetting.getPlatformPlayTimeout());
271 274  
272 275 // 添加订阅
273   - JSONObject subscribeKey = new JSONObject();
274   - subscribeKey.put("app", content.getApp());
275   - subscribeKey.put("stream", content.getStream());
276   - subscribeKey.put("regist", true);
277   - subscribeKey.put("schema", "rtmp");
278   - subscribeKey.put("mediaServerId", mediaServerItem.getId());
279   - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
280   - (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  276 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(content.getApp(), content.getStream(), true, "rtmp", mediaServerItem.getId());
  277 +
  278 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
281 279 dynamicTask.stop(taskKey);
282 280 responseSendItem(mediaServerItem, content, toId, serial);
283 281 });
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
3 3 import com.alibaba.fastjson.JSON;
  4 +import com.genersoft.iot.vmp.gb28181.bean.HandlerCatchData;
4 5 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
5 6 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  7 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
6 8 import org.jetbrains.annotations.NotNull;
7 9 import org.slf4j.Logger;
8 10 import org.slf4j.LoggerFactory;
9 11 import org.springframework.beans.factory.annotation.Autowired;
  12 +import org.springframework.beans.factory.annotation.Qualifier;
10 13 import org.springframework.data.redis.connection.Message;
11 14 import org.springframework.data.redis.connection.MessageListener;
  15 +import org.springframework.scheduling.annotation.Scheduled;
  16 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
12 17 import org.springframework.stereotype.Component;
13 18  
  19 +import java.util.List;
  20 +import java.util.concurrent.ConcurrentLinkedQueue;
  21 +
14 22 /**
15 23 * 接收来自redis的GPS更新通知
16 24 * @author lin
... ... @@ -20,12 +28,50 @@ public class RedisGpsMsgListener implements MessageListener {
20 28  
21 29 private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class);
22 30  
  31 + private boolean taskQueueHandlerRun = false;
  32 +
23 33 @Autowired
24 34 private IRedisCatchStorage redisCatchStorage;
25 35  
  36 + @Autowired
  37 + private IVideoManagerStorage storager;
  38 +
  39 + private final ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
  40 +
  41 + @Qualifier("taskExecutor")
  42 + @Autowired
  43 + private ThreadPoolTaskExecutor taskExecutor;
  44 +
  45 +
26 46 @Override
27 47 public void onMessage(@NotNull Message message, byte[] bytes) {
28   - GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class);
29   - redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
  48 + taskQueue.offer(message);
  49 + if (!taskQueueHandlerRun) {
  50 + taskQueueHandlerRun = true;
  51 + taskExecutor.execute(() -> {
  52 + while (!taskQueue.isEmpty()) {
  53 + Message msg = taskQueue.poll();
  54 + GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class);
  55 + // 只是放入redis缓存起来
  56 + redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
  57 + }
  58 + taskQueueHandlerRun = false;
  59 + });
  60 + }
  61 + }
  62 +
  63 + /**
  64 + * 定时将经纬度更新到数据库
  65 + */
  66 + @Scheduled(fixedRate = 2 * 1000) //每2秒执行一次
  67 + public void execute(){
  68 + List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo();
  69 + if (gpsMsgInfo.size() > 0) {
  70 + storager.updateStreamGPS(gpsMsgInfo);
  71 + for (GPSMsgInfo msgInfo : gpsMsgInfo) {
  72 + msgInfo.setStored(true);
  73 + redisCatchStorage.updateGpsMsgInfo(msgInfo);
  74 + }
  75 + }
30 76 }
31 77 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/RedisPushStreamStatusMsgListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  6 +import com.genersoft.iot.vmp.conf.DynamicTask;
  7 +import com.genersoft.iot.vmp.conf.UserSetting;
  8 +import com.genersoft.iot.vmp.gb28181.bean.GbStream;
  9 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  10 +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  12 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
  13 +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
  15 +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
  16 +import com.genersoft.iot.vmp.service.IStreamPushService;
  17 +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
  18 +import com.genersoft.iot.vmp.service.bean.PushStreamStatusChangeFromRedisDto;
  19 +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
  20 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  21 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  22 +import org.slf4j.Logger;
  23 +import org.slf4j.LoggerFactory;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.beans.factory.annotation.Qualifier;
  26 +import org.springframework.boot.ApplicationArguments;
  27 +import org.springframework.boot.ApplicationRunner;
  28 +import org.springframework.data.redis.connection.Message;
  29 +import org.springframework.data.redis.connection.MessageListener;
  30 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  31 +import org.springframework.stereotype.Component;
  32 +
  33 +import java.util.ArrayList;
  34 +import java.util.List;
  35 +import java.util.concurrent.ConcurrentLinkedQueue;
  36 +
  37 +
  38 +/**
  39 + * 接收redis发送的推流设备上线下线通知
  40 + * @author lin
  41 + */
  42 +@Component
  43 +public class RedisPushStreamStatusMsgListener implements MessageListener, ApplicationRunner {
  44 +
  45 + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusMsgListener.class);
  46 +
  47 + private boolean taskQueueHandlerRun = false;
  48 +
  49 + @Autowired
  50 + private IRedisCatchStorage redisCatchStorage;
  51 +
  52 + @Autowired
  53 + private IStreamPushService streamPushService;
  54 +
  55 + @Autowired
  56 + private DynamicTask dynamicTask;
  57 +
  58 +
  59 +
  60 + private final ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
  61 +
  62 + @Qualifier("taskExecutor")
  63 + @Autowired
  64 + private ThreadPoolTaskExecutor taskExecutor;
  65 +
  66 + @Override
  67 + public void onMessage(Message message, byte[] bytes) {
  68 + // TODO 增加队列
  69 + logger.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody()));
  70 + taskQueue.offer(message);
  71 +
  72 + if (!taskQueueHandlerRun) {
  73 + taskQueueHandlerRun = true;
  74 + taskExecutor.execute(() -> {
  75 + while (!taskQueue.isEmpty()) {
  76 + Message msg = taskQueue.poll();
  77 + PushStreamStatusChangeFromRedisDto statusChangeFromPushStream = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class);
  78 + if (statusChangeFromPushStream == null) {
  79 + logger.warn("[REDIS消息]推流设备状态变化消息解析失败");
  80 + return;
  81 + }
  82 + // 取消定时任务
  83 + dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED);
  84 + if (statusChangeFromPushStream.isSetAllOffline()) {
  85 + // 所有设备离线
  86 + streamPushService.allStreamOffline();
  87 + }
  88 + if (statusChangeFromPushStream.getOfflineStreams() != null
  89 + && statusChangeFromPushStream.getOfflineStreams().size() > 0) {
  90 + // 更新部分设备离线
  91 + streamPushService.offline(statusChangeFromPushStream.getOfflineStreams());
  92 + }
  93 + if (statusChangeFromPushStream.getOnlineStreams() != null &&
  94 + statusChangeFromPushStream.getOnlineStreams().size() > 0) {
  95 + // 更新部分设备上线
  96 + streamPushService.online(statusChangeFromPushStream.getOnlineStreams());
  97 + }
  98 + }
  99 + taskQueueHandlerRun = false;
  100 + });
  101 + }
  102 + }
  103 +
  104 + @Override
  105 + public void run(ApplicationArguments args) throws Exception {
  106 + // 启动时设置所有推流通道离线,发起查询请求
  107 + redisCatchStorage.sendStreamPushRequestedMsgForStatus();
  108 + dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{
  109 + logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线");
  110 + // 五秒收不到请求就设置通道离线,然后通知上级离线
  111 + streamPushService.allStreamOffline();
  112 + }, 5000);
  113 + }
  114 +
  115 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java
... ... @@ -3,16 +3,12 @@ package com.genersoft.iot.vmp.service.impl;
3 3 import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONObject;
5 5 import com.genersoft.iot.vmp.conf.UserSetting;
6   -import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
7   -import com.genersoft.iot.vmp.gb28181.bean.Device;
8   -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
9   -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  6 +
10 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
11 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
12 9 import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
13 10 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
14 11 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
15   -import com.genersoft.iot.vmp.utils.DateUtil;
16 12 import org.slf4j.Logger;
17 13 import org.slf4j.LoggerFactory;
18 14 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -22,6 +18,7 @@ import org.springframework.stereotype.Component;
22 18  
23 19  
24 20 /**
  21 + * 接收其他wvp发送流变化通知
25 22 * @author lin
26 23 */
27 24 @Component
... ... @@ -49,7 +46,7 @@ public class RedisStreamMsgListener implements MessageListener {
49 46  
50 47 JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class);
51 48 if (steamMsgJson == null) {
52   - logger.warn("[REDIS的ALARM通知]消息解析失败");
  49 + logger.warn("[收到redis 流变化]消息解析失败");
53 50 return;
54 51 }
55 52 String serverId = steamMsgJson.getString("serverId");
... ... @@ -58,7 +55,7 @@ public class RedisStreamMsgListener implements MessageListener {
58 55 // 自己发送的消息忽略即可
59 56 return;
60 57 }
61   - logger.info("[REDIS通知] 流变化: {}", new String(message.getBody()));
  58 + logger.info("[收到redis 流变化]: {}", new String(message.getBody()));
62 59 String app = steamMsgJson.getString("app");
63 60 String stream = steamMsgJson.getString("stream");
64 61 boolean register = steamMsgJson.getBoolean("register");
... ... @@ -75,9 +72,10 @@ public class RedisStreamMsgListener implements MessageListener {
75 72 mediaItem.setOriginType(0);
76 73 mediaItem.setOriginTypeStr("0");
77 74 mediaItem.setOriginTypeStr("unknown");
78   -
79   - zlmMediaListManager.addPush(mediaItem);
80   -
81   -
  75 + if (register) {
  76 + zlmMediaListManager.addPush(mediaItem);
  77 + }else {
  78 + zlmMediaListManager.removeMedia(app, stream);
  79 + }
82 80 }
83 81 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
... ... @@ -3,10 +3,10 @@ package com.genersoft.iot.vmp.service.impl;
3 3 import com.alibaba.fastjson.JSONArray;
4 4 import com.alibaba.fastjson.JSONObject;
5 5 import com.genersoft.iot.vmp.common.StreamInfo;
6   -import com.genersoft.iot.vmp.conf.SipConfig;
7 6 import com.genersoft.iot.vmp.conf.UserSetting;
8 7 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
9 8 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  9 +import com.genersoft.iot.vmp.gb28181.bean.TreeType;
10 10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
11 11 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
12 12 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
... ... @@ -23,14 +23,19 @@ import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
23 23 import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
24 24 import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper;
25 25 import com.genersoft.iot.vmp.service.IStreamProxyService;
  26 +import com.genersoft.iot.vmp.utils.DateUtil;
26 27 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
27 28 import com.github.pagehelper.PageInfo;
28 29 import org.slf4j.Logger;
29 30 import org.slf4j.LoggerFactory;
30 31 import org.springframework.beans.factory.annotation.Autowired;
  32 +import org.springframework.jdbc.datasource.DataSourceTransactionManager;
31 33 import org.springframework.stereotype.Service;
  34 +import org.springframework.transaction.TransactionDefinition;
  35 +import org.springframework.transaction.TransactionStatus;
32 36 import org.springframework.util.StringUtils;
33 37  
  38 +import java.net.InetAddress;
34 39 import java.util.*;
35 40  
36 41 /**
... ... @@ -48,7 +53,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
48 53 private IMediaService mediaService;
49 54  
50 55 @Autowired
51   - private ZLMRESTfulUtils zlmresTfulUtils;;
  56 + private ZLMRESTfulUtils zlmresTfulUtils;
52 57  
53 58 @Autowired
54 59 private StreamProxyMapper streamProxyMapper;
... ... @@ -63,9 +68,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
63 68 private UserSetting userSetting;
64 69  
65 70 @Autowired
66   - private SipConfig sipConfig;
67   -
68   - @Autowired
69 71 private GbStreamMapper gbStreamMapper;
70 72  
71 73 @Autowired
... ... @@ -83,6 +85,12 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
83 85 @Autowired
84 86 private IMediaServerService mediaServerService;
85 87  
  88 + @Autowired
  89 + DataSourceTransactionManager dataSourceTransactionManager;
  90 +
  91 + @Autowired
  92 + TransactionDefinition transactionDefinition;
  93 +
86 94  
87 95 @Override
88 96 public WVPResult<StreamInfo> save(StreamProxyItem param) {
... ... @@ -99,6 +107,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
99 107 wvpResult.setMsg("保存失败");
100 108 return wvpResult;
101 109 }
  110 +
102 111 String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),
103 112 param.getStream() );
104 113 param.setDst_url(dstUrl);
... ... @@ -108,9 +117,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
108 117 boolean saveResult;
109 118 // 更新
110 119 if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) {
111   - saveResult = videoManagerStorager.updateStreamProxy(param);
  120 + saveResult = updateStreamProxy(param);
112 121 }else { // 新增
113   - saveResult = videoManagerStorager.addStreamProxy(param);
  122 + saveResult = addStreamProxy(param);
114 123 }
115 124 if (saveResult) {
116 125 result.append("保存成功");
... ... @@ -124,7 +133,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
124 133 if (param.isEnable_remove_none_reader()) {
125 134 del(param.getApp(), param.getStream());
126 135 }else {
127   - videoManagerStorager.updateStreamProxy(param);
  136 + updateStreamProxy(param);
128 137 }
129 138  
130 139 }else {
... ... @@ -147,25 +156,79 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
147 156 result.append(", 关联国标平台[ " + param.getPlatformGbId() + " ]失败");
148 157 }
149 158 }
150   - if (!StringUtils.isEmpty(param.getGbId())) {
151   - // 查找开启了全部直播流共享的上级平台
152   - List<ParentPlatform> parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream();
153   - if (parentPlatforms.size() > 0) {
154   - for (ParentPlatform parentPlatform : parentPlatforms) {
155   - param.setPlatformId(parentPlatform.getServerGBId());
156   - param.setCatalogId(parentPlatform.getCatalogId());
157   - String stream = param.getStream();
158   - StreamProxyItem streamProxyItems = platformGbStreamMapper.selectOne(param.getApp(), stream, parentPlatform.getServerGBId());
159   - if (streamProxyItems == null) {
160   - platformGbStreamMapper.add(param);
161   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), param, CatalogEvent.ADD);
  159 + wvpResult.setMsg(result.toString());
  160 + return wvpResult;
  161 + }
  162 +
  163 + /**
  164 + * 新增代理流
  165 + * @param streamProxyItem
  166 + * @return
  167 + */
  168 + private boolean addStreamProxy(StreamProxyItem streamProxyItem) {
  169 + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
  170 + boolean result = false;
  171 + streamProxyItem.setStreamType("proxy");
  172 + streamProxyItem.setStatus(true);
  173 + String now = DateUtil.getNow();
  174 + streamProxyItem.setCreateTime(now);
  175 + try {
  176 + if (streamProxyMapper.add(streamProxyItem) > 0) {
  177 + if (!StringUtils.isEmpty(streamProxyItem.getGbId())) {
  178 + if (gbStreamMapper.add(streamProxyItem) < 0) {
  179 + //事务回滚
  180 + dataSourceTransactionManager.rollback(transactionStatus);
  181 + return false;
162 182 }
163 183 }
  184 + }else {
  185 + //事务回滚
  186 + dataSourceTransactionManager.rollback(transactionStatus);
  187 + return false;
164 188 }
  189 + result = true;
  190 + dataSourceTransactionManager.commit(transactionStatus); //手动提交
  191 + }catch (Exception e) {
  192 + logger.error("向数据库添加流代理失败:", e);
  193 + dataSourceTransactionManager.rollback(transactionStatus);
165 194 }
166 195  
167   - wvpResult.setMsg(result.toString());
168   - return wvpResult;
  196 +
  197 + return result;
  198 + }
  199 +
  200 + /**
  201 + * 更新代理流
  202 + * @param streamProxyItem
  203 + * @return
  204 + */
  205 + @Override
  206 + public boolean updateStreamProxy(StreamProxyItem streamProxyItem) {
  207 + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
  208 + boolean result = false;
  209 + streamProxyItem.setStreamType("proxy");
  210 + try {
  211 + if (streamProxyMapper.update(streamProxyItem) > 0) {
  212 + if (!StringUtils.isEmpty(streamProxyItem.getGbId())) {
  213 + if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) {
  214 + //事务回滚
  215 + dataSourceTransactionManager.rollback(transactionStatus);
  216 + return false;
  217 + }
  218 + }
  219 + } else {
  220 + //事务回滚
  221 + dataSourceTransactionManager.rollback(transactionStatus);
  222 + return false;
  223 + }
  224 +
  225 + dataSourceTransactionManager.commit(transactionStatus); //手动提交
  226 + result = true;
  227 + }catch (Exception e) {
  228 + e.printStackTrace();
  229 + dataSourceTransactionManager.rollback(transactionStatus);
  230 + }
  231 + return result;
169 232 }
170 233  
171 234 @Override
... ... @@ -239,7 +302,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
239 302 if (jsonObject.getInteger("code") == 0) {
240 303 result = true;
241 304 streamProxy.setEnable(true);
242   - videoManagerStorager.updateStreamProxy(streamProxy);
  305 + updateStreamProxy(streamProxy);
243 306 }
244 307 }
245 308 return result;
... ... @@ -253,7 +316,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
253 316 JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto);
254 317 if (jsonObject != null && jsonObject.getInteger("code") == 0) {
255 318 streamProxyDto.setEnable(false);
256   - result = videoManagerStorager.updateStreamProxy(streamProxyDto);
  319 + result = updateStreamProxy(streamProxyDto);
257 320 }
258 321 }
259 322 return result;
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
... ... @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import com.alibaba.fastjson.TypeReference;
  7 +import com.genersoft.iot.vmp.conf.MediaConfig;
7 8 import com.genersoft.iot.vmp.conf.UserSetting;
8 9 import com.genersoft.iot.vmp.gb28181.bean.*;
9 10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
... ... @@ -13,6 +14,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.*;
13 14 import com.genersoft.iot.vmp.service.IGbStreamService;
14 15 import com.genersoft.iot.vmp.service.IMediaServerService;
15 16 import com.genersoft.iot.vmp.service.IStreamPushService;
  17 +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
16 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
17 19 import com.genersoft.iot.vmp.storager.dao.*;
18 20 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -21,7 +23,10 @@ import com.github.pagehelper.PageInfo;
21 23 import org.slf4j.Logger;
22 24 import org.slf4j.LoggerFactory;
23 25 import org.springframework.beans.factory.annotation.Autowired;
  26 +import org.springframework.jdbc.datasource.DataSourceTransactionManager;
24 27 import org.springframework.stereotype.Service;
  28 +import org.springframework.transaction.TransactionDefinition;
  29 +import org.springframework.transaction.TransactionStatus;
25 30 import org.springframework.util.StringUtils;
26 31  
27 32 import java.util.*;
... ... @@ -68,6 +73,16 @@ public class StreamPushServiceImpl implements IStreamPushService {
68 73 @Autowired
69 74 private IMediaServerService mediaServerService;
70 75  
  76 + @Autowired
  77 + DataSourceTransactionManager dataSourceTransactionManager;
  78 +
  79 + @Autowired
  80 + TransactionDefinition transactionDefinition;
  81 +
  82 + @Autowired
  83 + private MediaConfig mediaConfig;
  84 +
  85 +
71 86 @Override
72 87 public List<StreamPushItem> handleJSON(String jsonData, MediaServerItem mediaServerItem) {
73 88 if (jsonData == null) {
... ... @@ -132,30 +147,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
132 147 stream.setStreamType("push");
133 148 stream.setStatus(true);
134 149 stream.setCreateTime(DateUtil.getNow());
  150 + stream.setStreamType("push");
  151 + stream.setMediaServerId(mediaConfig.getId());
135 152 int add = gbStreamMapper.add(stream);
136   -
137   - // 查找开启了全部直播流共享的上级平台
138   - List<ParentPlatform> parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream();
139   - if (parentPlatforms.size() > 0) {
140   - for (ParentPlatform parentPlatform : parentPlatforms) {
141   - stream.setCatalogId(parentPlatform.getCatalogId());
142   - stream.setPlatformId(parentPlatform.getServerGBId());
143   - String streamId = stream.getStream();
144   - StreamProxyItem streamProxyItem = platformGbStreamMapper.selectOne(stream.getApp(), streamId, parentPlatform.getServerGBId());
145   - if (streamProxyItem == null) {
146   - platformGbStreamMapper.add(stream);
147   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD);
148   - }else {
149   - if (!streamProxyItem.getGbId().equals(stream.getGbId())) {
150   - // 此流使用另一个国标Id已经与该平台关联,移除此记录
151   - platformGbStreamMapper.delByAppAndStreamAndPlatform(stream.getApp(), streamId, parentPlatform.getServerGBId());
152   - platformGbStreamMapper.add(stream);
153   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD);
154   - }
155   - }
156   - }
157   - }
158   -
159 153 return add > 0;
160 154 }
161 155  
... ... @@ -181,7 +175,6 @@ public class StreamPushServiceImpl implements IStreamPushService {
181 175  
182 176 @Override
183 177 public StreamPushItem getPush(String app, String streamId) {
184   -
185 178 return streamPushMapper.selectOne(app, streamId);
186 179 }
187 180  
... ... @@ -345,31 +338,6 @@ public class StreamPushServiceImpl implements IStreamPushService {
345 338 public void batchAdd(List<StreamPushItem> streamPushItems) {
346 339 streamPushMapper.addAll(streamPushItems);
347 340 gbStreamMapper.batchAdd(streamPushItems);
348   - // 查找开启了全部直播流共享的上级平台
349   - List<ParentPlatform> parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream();
350   - if (parentPlatforms.size() > 0) {
351   - for (StreamPushItem stream : streamPushItems) {
352   - for (ParentPlatform parentPlatform : parentPlatforms) {
353   - stream.setCatalogId(parentPlatform.getCatalogId());
354   - stream.setPlatformId(parentPlatform.getServerGBId());
355   - String streamId = stream.getStream();
356   - StreamProxyItem streamProxyItem = platformGbStreamMapper.selectOne(stream.getApp(), streamId, parentPlatform.getServerGBId());
357   - if (streamProxyItem == null) {
358   - platformGbStreamMapper.add(stream);
359   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD);
360   - }else {
361   - if (!streamProxyItem.getGbId().equals(stream.getGbId())) {
362   - // 此流使用另一个国标Id已经与该平台关联,移除此记录
363   - platformGbStreamMapper.delByAppAndStreamAndPlatform(stream.getApp(), streamId, parentPlatform.getServerGBId());
364   - platformGbStreamMapper.add(stream);
365   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD);
366   - stream.setGbId(streamProxyItem.getGbId());
367   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.DEL);
368   - }
369   - }
370   - }
371   - }
372   - }
373 341 }
374 342  
375 343 @Override
... ... @@ -481,4 +449,58 @@ public class StreamPushServiceImpl implements IStreamPushService {
481 449 }
482 450 return true;
483 451 }
  452 +
  453 + @Override
  454 + public void allStreamOffline() {
  455 + List<GbStream> onlinePushers = streamPushMapper.getOnlinePusherForGb();
  456 + if (onlinePushers.size() == 0) {
  457 + return;
  458 + }
  459 + streamPushMapper.setAllStreamOffline();
  460 +
  461 + // 发送通知
  462 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF);
  463 + }
  464 +
  465 + @Override
  466 + public void offline(List<StreamPushItemFromRedis> offlineStreams) {
  467 + // 更新部分设备离线
  468 + List<GbStream> onlinePushers = streamPushMapper.getOnlinePusherForGbInList(offlineStreams);
  469 + streamPushMapper.offline(offlineStreams);
  470 + // 发送通知
  471 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF);
  472 + }
  473 +
  474 + @Override
  475 + public void online(List<StreamPushItemFromRedis> onlineStreams) {
  476 + // 更新部分设备上线streamPushService
  477 + List<GbStream> onlinePushers = streamPushMapper.getOfflinePusherForGbInList(onlineStreams);
  478 + streamPushMapper.online(onlineStreams);
  479 + // 发送通知
  480 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.ON);
  481 + }
  482 +
  483 + @Override
  484 + public boolean add(StreamPushItem stream) {
  485 + stream.setUpdateTime(DateUtil.getNow());
  486 + stream.setCreateTime(DateUtil.getNow());
  487 + stream.setServerId(userSetting.getServerId());
  488 +
  489 + // 放在事务内执行
  490 + boolean result = false;
  491 + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
  492 + try {
  493 + int addStreamResult = streamPushMapper.add(stream);
  494 + if (!StringUtils.isEmpty(stream.getGbId())) {
  495 + stream.setStreamType("push");
  496 + gbStreamMapper.add(stream);
  497 + }
  498 + dataSourceTransactionManager.commit(transactionStatus);
  499 + result = true;
  500 + }catch (Exception e) {
  501 + logger.error("批量移除流与平台的关系时错误", e);
  502 + dataSourceTransactionManager.rollback(transactionStatus);
  503 + }
  504 + return result;
  505 + }
484 506 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java
... ... @@ -14,38 +14,60 @@ import java.util.*;
14 14  
15 15 public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPushExcelDto> {
16 16  
17   - // 错误数据的回调,用于将错误数据发送给页面
  17 + /**
  18 + * 错误数据的回调,用于将错误数据发送给页面
  19 + */
18 20 private ErrorDataHandler errorDataHandler;
19 21  
20   - // 推流的业务类用于存储数据
  22 + /**
  23 + * 推流的业务类用于存储数据
  24 + */
21 25 private IStreamPushService pushService;
22 26  
23   - // 默认流媒体节点ID
  27 + /**
  28 + * 默认流媒体节点ID
  29 + */
24 30 private String defaultMediaServerId;
25 31  
26   - // 用于存储不加过滤的所有数据
  32 + /**
  33 + * 用于存储不加过滤的所有数据
  34 + */
27 35 private List<StreamPushItem> streamPushItems = new ArrayList<>();
28 36  
29   - // 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表
  37 + /**
  38 + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表
  39 + */
30 40 private Map<String,StreamPushItem> streamPushItemForSave = new HashMap<>();
31 41  
32   - // 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表
  42 + /**
  43 + * 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表
  44 + */
33 45 private Map<String, List<String[]>> streamPushItemsForPlatform = new HashMap<>();
34 46  
35   - // 用于判断文件是否存在重复的app+Stream+平台ID
  47 + /**
  48 + * 用于判断文件是否存在重复的app+Stream+平台ID
  49 + */
36 50 private Set<String> streamPushStreamSet = new HashSet<>();
37 51  
38   - // 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应
  52 + /**
  53 + * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应
  54 + */
39 55 private BiMap<String,String> gBMap = HashBiMap.create();
40 56  
41   - // 记录错误的APP+Stream
  57 + /**
  58 + * 记录错误的APP+Stream
  59 + */
42 60 private List<String> errorStreamList = new ArrayList<>();
43 61  
44 62  
45   - // 记录错误的国标ID
  63 + /**
  64 + * 记录错误的国标ID
  65 + */
46 66 private List<String> errorGBList = new ArrayList<>();
47 67  
48   - // 读取数量计数器
  68 + /**
  69 + * 读取数量计数器
  70 + */
49 71 private int loadedSize = 0;
50 72  
51 73 public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java
... ... @@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.service.impl;
3 3 import com.genersoft.iot.vmp.service.IUserService;
4 4 import com.genersoft.iot.vmp.storager.dao.UserMapper;
5 5 import com.genersoft.iot.vmp.storager.dao.dto.User;
  6 +import com.github.pagehelper.PageHelper;
  7 +import com.github.pagehelper.PageInfo;
6 8 import org.springframework.beans.factory.annotation.Autowired;
7 9 import org.springframework.stereotype.Service;
8 10 import org.springframework.util.StringUtils;
... ... @@ -11,7 +13,7 @@ import java.util.List;
11 13  
12 14 @Service
13 15 public class UserServiceImpl implements IUserService {
14   -
  16 +
15 17 @Autowired
16 18 private UserMapper userMapper;
17 19  
... ... @@ -64,4 +66,16 @@ public class UserServiceImpl implements IUserService {
64 66 return userMapper.checkPushAuthorityByCallIdAndSign(callId, sign).size() > 0;
65 67 }
66 68 }
  69 +
  70 + @Override
  71 + public PageInfo<User> getUsers(int page, int count) {
  72 + PageHelper.startPage(page, count);
  73 + List<User> users = userMapper.getUsers();
  74 + return new PageInfo<>(users);
  75 + }
  76 +
  77 + @Override
  78 + public int changePushKey(int id, String pushKey) {
  79 + return userMapper.changePushKey(id,pushKey);
  80 + }
67 81 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
... ... @@ -233,4 +233,9 @@ public interface IRedisCatchStorage {
233 233 * @return
234 234 */
235 235 StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream);
  236 +
  237 + /**
  238 + * 发送redis消息 查询所有推流设备的状态
  239 + */
  240 + void sendStreamPushRequestedMsgForStatus();
236 241 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
... ... @@ -27,22 +27,6 @@ public interface IVideoManagerStorage {
27 27 public boolean exists(String deviceId);
28 28  
29 29 /**
30   - * 添加设备通道
31   - *
32   - * @param deviceId 设备id
33   - * @param channel 通道
34   - */
35   - public void updateChannel(String deviceId, DeviceChannel channel);
36   -
37   - /**
38   - * 批量添加设备通道
39   - *
40   - * @param deviceId 设备id
41   - * @param channels 多个通道
42   - */
43   - public int updateChannels(String deviceId, List<DeviceChannel> channels);
44   -
45   - /**
46 30 * 开始播放
47 31 * @param deviceId 设备id
48 32 * @param channelId 通道ID
... ... @@ -224,13 +208,6 @@ public interface IVideoManagerStorage {
224 208 List<DeviceChannelInPlatform> queryChannelListInParentPlatform(String platformId);
225 209  
226 210  
227   - /**
228   - * 更新上级平台的通道信息
229   - * @param platformId
230   - * @param channelReduces
231   - * @return
232   - */
233   - int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces, String catalogId);
234 211  
235 212 /**
236 213 * 移除上级平台的通道信息
... ... @@ -277,20 +254,6 @@ public interface IVideoManagerStorage {
277 254 public int clearMobilePositionsByDeviceId(String deviceId);
278 255  
279 256 /**
280   - * 新增代理流
281   - * @param streamProxyDto
282   - * @return
283   - */
284   - public boolean addStreamProxy(StreamProxyItem streamProxyDto);
285   -
286   - /**
287   - * 更新代理流
288   - * @param streamProxyDto
289   - * @return
290   - */
291   - public boolean updateStreamProxy(StreamProxyItem streamProxyDto);
292   -
293   - /**
294 257 * 移除代理流
295 258 * @param app
296 259 * @param stream
... ... @@ -334,19 +297,7 @@ public interface IVideoManagerStorage {
334 297 * @param platformId
335 298 * @return
336 299 */
337   - List<GbStream> queryGbStreamListInPlatform(String platformId);
338   -
339   - /**
340   - * 批量更新推流列表
341   - * @param streamPushItems
342   - */
343   - void updateMediaList(List<StreamPushItem> streamPushItems);
344   -
345   - /**
346   - * 更新单个推流
347   - * @param streamPushItem
348   - */
349   - void updateMedia(StreamPushItem streamPushItem);
  300 + List<DeviceChannel> queryGbStreamListInPlatform(String platformId);
350 301  
351 302 /**
352 303 * 移除单个推流
... ... @@ -355,21 +306,6 @@ public interface IVideoManagerStorage {
355 306 */
356 307 int removeMedia(String app, String stream);
357 308  
358   -
359   - /**
360   - * 获取但个推流
361   - * @param app
362   - * @param stream
363   - * @return
364   - */
365   - StreamPushItem getMedia(String app, String stream);
366   -
367   -
368   - /**
369   - * 清空推流列表
370   - */
371   - void clearMediaList();
372   -
373 309 /**
374 310 * 设置流离线
375 311 */
... ... @@ -445,7 +381,7 @@ public interface IVideoManagerStorage {
445 381  
446 382 int setDefaultCatalog(String platformId, String catalogId);
447 383  
448   - List<PlatformCatalog> queryCatalogInPlatform(String serverGBId);
  384 + List<DeviceChannel> queryCatalogInPlatform(String serverGBId);
449 385  
450 386 int delRelation(PlatformCatalog platformCatalog);
451 387  
... ... @@ -466,4 +402,8 @@ public interface IVideoManagerStorage {
466 402 List<ChannelSourceInfo> getChannelSource(String platformId, String gbId);
467 403  
468 404 void updateChannelPosition(DeviceChannel deviceChannel);
  405 +
  406 + void cleanContentForPlatform(String serverGBId);
  407 +
  408 + List<DeviceChannel> queryChannelWithCatalog(String serverGBId);
469 409 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
... ... @@ -140,6 +140,9 @@ public interface DeviceChannelMapper {
140 140 @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"})
141 141 void offline(String deviceId, String channelId);
142 142  
  143 + @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId}"})
  144 + void offlineByDeviceId(String deviceId);
  145 +
143 146 @Update(value = {"UPDATE device_channel SET status=1 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"})
144 147 void online(String deviceId, String channelId);
145 148  
... ... @@ -329,5 +332,13 @@ public interface DeviceChannelMapper {
329 332 @Select("select * from device_channel where deviceId=#{deviceId} and SUBSTRING(channelId, 11, 3)=#{typeCode}")
330 333 List<DeviceChannel> getBusinessGroups(String deviceId, String typeCode);
331 334  
332   -
  335 + @Select("select dc.id, dc.channelId, dc.deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " +
  336 + " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " +
  337 + " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " +
  338 + " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " +
  339 + " from device_channel dc" +
  340 + " left join platform_gb_channel pgc on dc.id = pgc.deviceChannelId" +
  341 + " left join platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" +
  342 + " where pgc.platformId=#{serverGBId}")
  343 + List<DeviceChannel> queryChannelWithCatalog(String serverGBId);
333 344 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java
1 1 package com.genersoft.iot.vmp.storager.dao;
2 2  
  3 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
3 4 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
4 5 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
5 6 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
... ... @@ -15,10 +16,10 @@ import java.util.List;
15 16 public interface GbStreamMapper {
16 17  
17 18 @Insert("REPLACE INTO gb_stream (app, stream, gbId, name, " +
18   - "longitude, latitude, streamType, mediaServerId, status, createTime) VALUES" +
  19 + "longitude, latitude, streamType, mediaServerId, createTime) VALUES" +
19 20 "('${app}', '${stream}', '${gbId}', '${name}', " +
20 21 "'${longitude}', '${latitude}', '${streamType}', " +
21   - "'${mediaServerId}', ${status}, '${createTime}')")
  22 + "'${mediaServerId}', '${createTime}')")
22 23 @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId")
23 24 int add(GbStream gbStream);
24 25  
... ... @@ -30,8 +31,7 @@ public interface GbStreamMapper {
30 31 "streamType=#{streamType}," +
31 32 "longitude=#{longitude}, " +
32 33 "latitude=#{latitude}," +
33   - "mediaServerId=#{mediaServerId}," +
34   - "status=${status} " +
  34 + "mediaServerId=#{mediaServerId}" +
35 35 "WHERE app=#{app} AND stream=#{stream}")
36 36 int updateByAppAndStream(GbStream gbStream);
37 37  
... ... @@ -43,8 +43,7 @@ public interface GbStreamMapper {
43 43 "streamType=#{streamType}," +
44 44 "longitude=#{longitude}, " +
45 45 "latitude=#{latitude}," +
46   - "mediaServerId=#{mediaServerId}," +
47   - "status=${status} " +
  46 + "mediaServerId=#{mediaServerId}" +
48 47 "WHERE gbStreamId=#{gbStreamId}")
49 48 int update(GbStream gbStream);
50 49  
... ... @@ -60,12 +59,10 @@ public interface GbStreamMapper {
60 59 " <if test='catalogId == null'> AND gs.gbStreamId not in" +
61 60 "(select pgs.gbStreamId from platform_gb_stream pgs where pgs.platformId = #{platformId}) </if> " +
62 61 " <if test='query != null'> AND (gs.app LIKE '%${query}%' OR gs.stream LIKE '%${query}%' OR gs.gbId LIKE '%${query}%' OR gs.name LIKE '%${query}%')</if> " +
63   - " <if test='pushing == true' > AND gs.status=1</if>" +
64   - " <if test='pushing == false' > AND gs.status=0</if>" +
65 62 " <if test='mediaServerId != null' > AND gs.mediaServerId=#{mediaServerId} </if>" +
66 63 " order by gs.gbStreamId asc " +
67 64 "</script>")
68   - List<GbStream> selectAll(String platformId, String catalogId, String query, Boolean pushing, String mediaServerId);
  65 + List<GbStream> selectAll(String platformId, String catalogId, String query, String mediaServerId);
69 66  
70 67 @Select("SELECT * FROM gb_stream WHERE app=#{app} AND stream=#{stream}")
71 68 GbStream selectOne(String app, String stream);
... ... @@ -78,10 +75,18 @@ public interface GbStreamMapper {
78 75 "WHERE gs.gbId = '${gbId}' AND pgs.platformId = '${platformId}'")
79 76 GbStream queryStreamInPlatform(String platformId, String gbId);
80 77  
81   - @Select("SELECT gs.*, pgs.platformId as platformId, pgs.catalogId as catalogId FROM gb_stream gs " +
82   - "LEFT JOIN platform_gb_stream pgs ON gs.gbStreamId = pgs.gbStreamId " +
83   - "WHERE pgs.platformId = #{platformId}")
84   - List<GbStream> queryGbStreamListInPlatform(String platformId);
  78 + @Select("select gt.gbId as channelId, gt.name, 'wvp-pro' as manufacture, st.status, gt.longitude, gt.latitude, pc.id as parentId," +
  79 + " '1' as registerWay, pc.civilCode, 'live' as model, 'wvp-pro' as owner, '0' as parental,'0' as secrecy" +
  80 + " from gb_stream gt " +
  81 + " left join (" +
  82 + " select sp.status, sp.app, sp.stream from stream_push sp" +
  83 + " union all" +
  84 + " select spxy.status, spxy.app, spxy.stream from stream_proxy spxy" +
  85 + " ) st on st.app = gt.app and st.stream = gt.stream" +
  86 + " left join platform_gb_stream pgs on gt.gbStreamId = pgs.gbStreamId" +
  87 + " left join platform_catalog pc on pgs.catalogId = pc.id and pgs.platformId = pc.platformId" +
  88 + " where pgs.platformId=#{platformId}")
  89 + List<DeviceChannel> queryGbStreamListInPlatform(String platformId);
85 90  
86 91  
87 92 @Select("SELECT gs.* FROM gb_stream gs LEFT JOIN platform_gb_stream pgs " +
... ... @@ -110,12 +115,12 @@ public interface GbStreamMapper {
110 115 @Insert("<script> " +
111 116 "INSERT IGNORE into gb_stream " +
112 117 "(app, stream, gbId, name, " +
113   - "longitude, latitude, streamType, mediaServerId, status, createTime)" +
  118 + "longitude, latitude, streamType, mediaServerId, createTime)" +
114 119 "values " +
115 120 "<foreach collection='subList' index='index' item='item' separator=','> " +
116 121 "('${item.app}', '${item.stream}', '${item.gbId}', '${item.name}', " +
117 122 "'${item.longitude}', '${item.latitude}', '${item.streamType}', " +
118   - "'${item.mediaServerId}', ${item.status}, '${item.createTime}') "+
  123 + "'${item.mediaServerId}', '${item.createTime}') "+
119 124 "</foreach> " +
120 125 "</script>")
121 126 @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId")
... ... @@ -138,4 +143,9 @@ public interface GbStreamMapper {
138 143 "</foreach>" +
139 144 "</script>")
140 145 List<GbStream> selectAllForAppAndStream(List<StreamPushItem> streamPushItems);
  146 +
  147 + @Update("UPDATE gb_stream " +
  148 + "SET mediaServerId=#{mediaServerId}" +
  149 + "WHERE app=#{app} AND stream=#{stream}")
  150 + void updateMediaServer(String app, String stream, String mediaServerId);
141 151 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
... ... @@ -16,10 +16,10 @@ public interface ParentPlatformMapper {
16 16  
17 17 @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " +
18 18 " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " +
19   - " status, shareAllLiveStream, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime) " +
  19 + " status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " +
20 20 " VALUES (${enable}, '${name}', '${serverGBId}', '${serverGBDomain}', '${serverIP}', ${serverPort}, '${deviceGBId}', '${deviceIp}', " +
21 21 " '${devicePort}', '${username}', '${password}', '${expires}', '${keepTimeout}', '${transport}', '${characterSet}', ${ptz}, ${rtcp}, " +
22   - " ${status}, ${shareAllLiveStream}, ${startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime})")
  22 + " ${status}, ${startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})")
23 23 int addParentPlatform(ParentPlatform parentPlatform);
24 24  
25 25 @Update("UPDATE parent_platform " +
... ... @@ -41,12 +41,12 @@ public interface ParentPlatformMapper {
41 41 "ptz=#{ptz}, " +
42 42 "rtcp=#{rtcp}, " +
43 43 "status=#{status}, " +
44   - "shareAllLiveStream=#{shareAllLiveStream}, " +
45 44 "startOfflinePush=${startOfflinePush}, " +
46 45 "catalogGroup=#{catalogGroup}, " +
47 46 "administrativeDivision=#{administrativeDivision}, " +
48 47 "createTime=#{createTime}, " +
49 48 "updateTime=#{updateTime}, " +
  49 + "treeType=#{treeType}, " +
50 50 "catalogId=#{catalogId} " +
51 51 "WHERE id=#{id}")
52 52 int updateParentPlatform(ParentPlatform parentPlatform);
... ... @@ -83,9 +83,6 @@ public interface ParentPlatformMapper {
83 83 @Update("UPDATE parent_platform SET status=#{online} WHERE serverGBId=#{platformGbID}" )
84 84 int updateParentPlatformStatus(String platformGbID, boolean online);
85 85  
86   - @Select("SELECT * FROM parent_platform WHERE shareAllLiveStream=true")
87   - List<ParentPlatform> selectAllAhareAllLiveStream();
88   -
89 86 @Update(value = {" <script>" +
90 87 "UPDATE parent_platform " +
91 88 "SET catalogId=#{catalogId}, updateTime=#{updateTime}" +
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java
1 1 package com.genersoft.iot.vmp.storager.dao;
2 2  
  3 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
3 4 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
4 5 import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
5 6 import com.genersoft.iot.vmp.gb28181.bean.PlatformGbStream;
... ... @@ -14,8 +15,8 @@ import java.util.List;
14 15 @Repository
15 16 public interface PlatformCatalogMapper {
16 17  
17   - @Insert("INSERT INTO platform_catalog (id, name, platformId, parentId) VALUES" +
18   - "(#{id}, #{name}, #{platformId}, #{parentId})")
  18 + @Insert("INSERT INTO platform_catalog (id, name, platformId, parentId, civilCode, businessGroupId) VALUES" +
  19 + "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})")
19 20 int add(PlatformCatalog platformCatalog);
20 21  
21 22 @Delete("DELETE FROM platform_catalog WHERE id=#{id}")
... ... @@ -44,4 +45,12 @@ public interface PlatformCatalogMapper {
44 45  
45 46 @Select("SELECT pc.* FROM platform_catalog pc WHERE pc.id = (SELECT pp.catalogId from parent_platform pp WHERE pp.serverGBId=#{platformId})")
46 47 PlatformCatalog selectDefaultByPlatFormId(String platformId);
  48 +
  49 +
  50 + @Select("SELECT pc.* FROM platform_catalog pc WHERE pc.id = #{id}")
  51 + PlatformCatalog selectParentCatalog(String id);
  52 +
  53 + @Select("SELECT pc.id as channelId, pc.name, pc.civilCode, pc.businessGroupId,'1' as parental, pc.parentId " +
  54 + " FROM platform_catalog pc WHERE pc.platformId=#{platformId}")
  55 + List<DeviceChannel> queryCatalogInPlatform(String platformId);
47 56 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.storager.dao;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
4 4 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
  5 +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
5 6 import org.apache.ibatis.annotations.*;
6 7 // import org.omg.PortableInterceptor.INACTIVE;
7 8 import org.springframework.stereotype.Repository;
... ... @@ -14,9 +15,10 @@ import java.util.List;
14 15 public interface StreamPushMapper {
15 16  
16 17 @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
17   - "pushTime, aliveSecond, mediaServerId, serverId, updateTime, createTime) VALUES" +
  18 + "pushTime, aliveSecond, mediaServerId, serverId, updateTime, createTime, pushIng, self) VALUES" +
18 19 "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
19   - "'${pushTime}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' , '${updateTime}' , '${createTime}' )")
  20 + "'${pushTime}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' , '${updateTime}' , '${createTime}', " +
  21 + "${pushIng}, ${self} )")
20 22 int add(StreamPushItem streamPushItem);
21 23  
22 24  
... ... @@ -29,6 +31,8 @@ public interface StreamPushMapper {
29 31 "<if test=\"originTypeStr != null\">, originTypeStr='${originTypeStr}'</if>" +
30 32 "<if test=\"pushTime != null\">, pushTime='${pushTime}'</if>" +
31 33 "<if test=\"aliveSecond != null\">, aliveSecond='${aliveSecond}'</if>" +
  34 + "<if test=\"pushIng != null\">, pushIng=${pushIng}</if>" +
  35 + "<if test=\"self != null\">, self=${self}</if>" +
32 36 "WHERE app=#{app} AND stream=#{stream}"+
33 37 " </script>"})
34 38 int update(StreamPushItem streamPushItem);
... ... @@ -87,10 +91,11 @@ public interface StreamPushMapper {
87 91  
88 92 @Insert("<script>" +
89 93 "Insert IGNORE INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
90   - "createTime, aliveSecond, mediaServerId) " +
  94 + "createTime, aliveSecond, mediaServerId, status, pushIng) " +
91 95 "VALUES <foreach collection='streamPushItems' item='item' index='index' separator=','>" +
92 96 "( '${item.app}', '${item.stream}', '${item.totalReaderCount}', #{item.originType}, " +
93   - "'${item.originTypeStr}',#{item.createTime}, #{item.aliveSecond}, '${item.mediaServerId}' )" +
  97 + "'${item.originTypeStr}',#{item.createTime}, #{item.aliveSecond}, '${item.mediaServerId}', ${item.status} ," +
  98 + " ${item.pushIng} )" +
94 99 " </foreach>" +
95 100 "</script>")
96 101 @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
... ... @@ -114,7 +119,53 @@ public interface StreamPushMapper {
114 119 int updateStatus(String app, String stream, boolean status);
115 120  
116 121 @Update("UPDATE stream_push " +
  122 + "SET pushIng=${pushIng} " +
  123 + "WHERE app=#{app} AND stream=#{stream}")
  124 + int updatePushStatus(String app, String stream, boolean pushIng);
  125 +
  126 + @Update("UPDATE stream_push " +
117 127 "SET status=#{status} " +
118 128 "WHERE mediaServerId=#{mediaServerId}")
119 129 void updateStatusByMediaServerId(String mediaServerId, boolean status);
  130 +
  131 +
  132 + @Select("<script> "+
  133 + "SELECT gs.* FROM stream_push sp left join gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream " +
  134 + "where sp.status = 1 and (gs.app, gs.stream) in (" +
  135 + "<foreach collection='offlineStreams' item='item' separator=','>" +
  136 + "(#{item.app}, #{item.stream}) " +
  137 + "</foreach>" +
  138 + ")</script>")
  139 + List<GbStream> getOnlinePusherForGbInList(List<StreamPushItemFromRedis> offlineStreams);
  140 +
  141 + @Update("<script> "+
  142 + "UPDATE stream_push SET status=0 where (app, stream) in (" +
  143 + "<foreach collection='offlineStreams' item='item' separator=','>" +
  144 + "(#{item.app}, #{item.stream}) " +
  145 + "</foreach>" +
  146 + ")</script>")
  147 + void offline(List<StreamPushItemFromRedis> offlineStreams);
  148 +
  149 + @Select("<script> "+
  150 + "SELECT * FROM stream_push sp left join gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream " +
  151 + "where sp.status = 0 and (gs.app, gs.stream) in (" +
  152 + "<foreach collection='onlineStreams' item='item' separator=','>" +
  153 + "(#{item.app}, #{item.stream}) " +
  154 + "</foreach>" +
  155 + ") </script>")
  156 + List<GbStream> getOfflinePusherForGbInList(List<StreamPushItemFromRedis> onlineStreams);
  157 +
  158 + @Update("<script> "+
  159 + "UPDATE stream_push SET status=1 where (app, stream) in (" +
  160 + "<foreach collection='onlineStreams' item='item' separator=','>" +
  161 + "(#{item.app}, #{item.stream}) " +
  162 + "</foreach>" +
  163 + ")</script>")
  164 + void online(List<StreamPushItemFromRedis> onlineStreams);
  165 +
  166 + @Select("SELECT gs.* FROM stream_push sp left join gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream where sp.status = 1")
  167 + List<GbStream> getOnlinePusherForGb();
  168 +
  169 + @Update("UPDATE stream_push SET status=0")
  170 + void setAllStreamOffline();
120 171 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java
... ... @@ -55,4 +55,11 @@ public interface UserMapper {
55 55  
56 56 @Select("select * from user where md5(pushKey) = '${sign}'")
57 57 List<User> checkPushAuthorityByCallId(String sign);
  58 +
  59 + @Select("select u.id, u.username,u.pushKey,u.roleId, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM user u join user_role r on u.roleId=r.id")
  60 + @ResultMap(value="roleMap")
  61 + List<User> getUsers();
  62 +
  63 + @Update("update user set pushKey=#{pushKey} where id=#{id}")
  64 + int changePushKey(int id, String pushKey);
58 65 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -479,13 +479,18 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
479 479 @Override
480 480 public void sendStreamChangeMsg(String type, JSONObject jsonObject) {
481 481 String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type;
482   - logger.debug("[redis 流变化事件] {}: {}", key, jsonObject.toString());
  482 + logger.info("[redis 流变化事件] {}: {}", key, jsonObject.toString());
483 483 redis.convertAndSend(key, jsonObject);
484 484 }
485 485  
486 486 @Override
487 487 public void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, MediaItem mediaItem) {
  488 + // 查找是否使用了callID
  489 + StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId);
488 490 String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId();
  491 + if (streamAuthorityInfo != null) {
  492 + mediaItem.setCallId(streamAuthorityInfo.getCallId());
  493 + }
489 494 redis.set(key, mediaItem);
490 495 }
491 496  
... ... @@ -683,21 +688,21 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
683 688 @Override
684 689 public void sendMobilePositionMsg(JSONObject jsonObject) {
685 690 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_MOBILE_POSITION;
686   - logger.info("[redis 移动位置订阅通知] {}: {}", key, jsonObject.toString());
  691 + logger.info("[redis发送通知]移动位置 {}: {}", key, jsonObject.toString());
687 692 redis.convertAndSend(key, jsonObject);
688 693 }
689 694  
690 695 @Override
691 696 public void sendStreamPushRequestedMsg(MessageForPushChannel msg) {
692 697 String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
693   - logger.info("[redis 推流被请求通知] {}: {}/{}", key, msg.getApp(), msg.getStream());
  698 + logger.info("[redis发送通知]推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream());
694 699 redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
695 700 }
696 701  
697 702 @Override
698 703 public void sendAlarmMsg(AlarmChannelMessage msg) {
699 704 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
700   - logger.info("[redis 报警通知] {}: {}", key, JSON.toJSON(msg));
  705 + logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
701 706 redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
702 707 }
703 708  
... ... @@ -707,4 +712,12 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
707 712 }
708 713  
709 714  
  715 + @Override
  716 + public void sendStreamPushRequestedMsgForStatus() {
  717 + String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED;
  718 + logger.info("[redis通知]获取所有推流设备的状态");
  719 + JSONObject jsonObject = new JSONObject();
  720 + jsonObject.put(key, key);
  721 + redis.convertAndSend(key, jsonObject);
  722 + }
710 723 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
... ... @@ -48,13 +48,14 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
48 48 @Autowired
49 49 SipConfig sipConfig;
50 50  
51   - @Autowired
52   - DataSourceTransactionManager dataSourceTransactionManager;
53 51  
54 52 @Autowired
55 53 TransactionDefinition transactionDefinition;
56 54  
57 55 @Autowired
  56 + DataSourceTransactionManager dataSourceTransactionManager;
  57 +
  58 + @Autowired
58 59 private DeviceMapper deviceMapper;
59 60  
60 61 @Autowired
... ... @@ -105,96 +106,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
105 106 }
106 107  
107 108 @Override
108   - public synchronized void updateChannel(String deviceId, DeviceChannel channel) {
109   - String channelId = channel.getChannelId();
110   - channel.setDeviceId(deviceId);
111   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
112   - if (streamInfo != null) {
113   - channel.setStreamId(streamInfo.getStream());
114   - }
115   - String now = DateUtil.getNow();
116   - channel.setUpdateTime(now);
117   - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId);
118   - if (deviceChannel == null) {
119   - channel.setCreateTime(now);
120   - deviceChannelMapper.add(channel);
121   - }else {
122   - deviceChannelMapper.update(channel);
123   - }
124   - deviceChannelMapper.updateChannelSubCount(deviceId,channel.getParentId());
125   - }
126   -
127   - @Override
128   - public int updateChannels(String deviceId, List<DeviceChannel> channels) {
129   - List<DeviceChannel> addChannels = new ArrayList<>();
130   - List<DeviceChannel> updateChannels = new ArrayList<>();
131   - HashMap<String, DeviceChannel> channelsInStore = new HashMap<>();
132   - if (channels != null && channels.size() > 0) {
133   - List<DeviceChannel> channelList = deviceChannelMapper.queryChannels(deviceId, null, null, null, null);
134   - if (channelList.size() == 0) {
135   - for (DeviceChannel channel : channels) {
136   - channel.setDeviceId(deviceId);
137   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
138   - if (streamInfo != null) {
139   - channel.setStreamId(streamInfo.getStream());
140   - }
141   - String now = DateUtil.getNow();
142   - channel.setUpdateTime(now);
143   - channel.setCreateTime(now);
144   - addChannels.add(channel);
145   - }
146   - }else {
147   - for (DeviceChannel deviceChannel : channelList) {
148   - channelsInStore.put(deviceChannel.getChannelId(), deviceChannel);
149   - }
150   - for (DeviceChannel channel : channels) {
151   - channel.setDeviceId(deviceId);
152   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
153   - if (streamInfo != null) {
154   - channel.setStreamId(streamInfo.getStream());
155   - }
156   - String now = DateUtil.getNow();
157   - channel.setUpdateTime(now);
158   - if (channelsInStore.get(channel.getChannelId()) != null) {
159   - updateChannels.add(channel);
160   - }else {
161   - addChannels.add(channel);
162   - channel.setCreateTime(now);
163   - }
164   - }
165   - }
166   - int limitCount = 300;
167   - if (addChannels.size() > 0) {
168   - if (addChannels.size() > limitCount) {
169   - for (int i = 0; i < addChannels.size(); i += limitCount) {
170   - int toIndex = i + limitCount;
171   - if (i + limitCount > addChannels.size()) {
172   - toIndex = addChannels.size();
173   - }
174   - deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex));
175   - }
176   - }else {
177   - deviceChannelMapper.batchAdd(addChannels);
178   - }
179   - }
180   - if (updateChannels.size() > 0) {
181   - if (updateChannels.size() > limitCount) {
182   - for (int i = 0; i < updateChannels.size(); i += limitCount) {
183   - int toIndex = i + limitCount;
184   - if (i + limitCount > updateChannels.size()) {
185   - toIndex = updateChannels.size();
186   - }
187   - deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex));
188   - }
189   - }else {
190   - deviceChannelMapper.batchUpdate(updateChannels);
191   - }
192   - }
193   - }
194   - return addChannels.size() + updateChannels.size();
195   - }
196   -
197   - @Override
198 109 public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
199 110 if (CollectionUtils.isEmpty(deviceChannelList)) {
200 111 return false;
... ... @@ -532,20 +443,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
532 443 // 更新缓存
533 444 parentPlatformCatch.setParentPlatform(parentPlatform);
534 445 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
535   - if (parentPlatform.isEnable()) {
536   - // 共享所有视频流,需要将现有视频流添加到此平台
537   - List<GbStream> gbStreams = gbStreamMapper.queryStreamNotInPlatform();
538   - if (gbStreams.size() > 0) {
539   - for (GbStream gbStream : gbStreams) {
540   - gbStream.setCatalogId(parentPlatform.getCatalogId());
541   - }
542   - if (parentPlatform.isShareAllLiveStream()) {
543   - gbStreamService.addPlatformInfo(gbStreams, parentPlatform.getServerGBId(), parentPlatform.getCatalogId());
544   - }else {
545   - gbStreamService.delPlatformInfo(parentPlatform.getServerGBId(), gbStreams);
546   - }
547   - }
548   - }
549 446  
550 447 return result > 0;
551 448 }
... ... @@ -596,36 +493,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
596 493 return deviceChannelMapper.queryChannelByPlatformId(platformId);
597 494 }
598 495  
599   - @Override
600   - public int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces, String catalogId) {
601   -
602   - Map<Integer, ChannelReduce> deviceAndChannels = new HashMap<>();
603   - for (ChannelReduce channelReduce : channelReduces) {
604   - channelReduce.setCatalogId(catalogId);
605   - deviceAndChannels.put(channelReduce.getId(), channelReduce);
606   - }
607   - List<Integer> deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet());
608   - // 查询当前已经存在的
609   - List<Integer> channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces);
610   - if (deviceAndChannelList != null) {
611   - deviceAndChannelList.removeAll(channelIds);
612   - }
613   - for (Integer channelId : channelIds) {
614   - deviceAndChannels.remove(channelId);
615   - }
616   - List<ChannelReduce> channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
617   - // 对剩下的数据进行存储
618   - int result = 0;
619   - if (channelReducesToAdd.size() > 0) {
620   - result = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
621   - // TODO 后续给平台增加控制开关以控制是否响应目录订阅
622   - List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId);
623   - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
624   - }
625   -
626   - return result;
627   - }
628   -
629 496  
630 497 @Override
631 498 public int delChannelForGB(String platformId, List<ChannelReduce> channelReduces) {
... ... @@ -701,77 +568,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
701 568 return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId);
702 569 }
703 570  
704   - /**
705   - * 新增代理流
706   - * @param streamProxyItem
707   - * @return
708   - */
709   - @Override
710   - public boolean addStreamProxy(StreamProxyItem streamProxyItem) {
711   - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
712   - boolean result = false;
713   - streamProxyItem.setStreamType("proxy");
714   - streamProxyItem.setStatus(true);
715   - String now = DateUtil.getNow();
716   - streamProxyItem.setCreateTime(now);
717   - try {
718   - if (streamProxyMapper.add(streamProxyItem) > 0) {
719   - if (!StringUtils.isEmpty(streamProxyItem.getGbId())) {
720   - if (gbStreamMapper.add(streamProxyItem) < 0) {
721   - //事务回滚
722   - dataSourceTransactionManager.rollback(transactionStatus);
723   - return false;
724   - }
725   - }
726   - }else {
727   - //事务回滚
728   - dataSourceTransactionManager.rollback(transactionStatus);
729   - return false;
730   - }
731   - result = true;
732   - dataSourceTransactionManager.commit(transactionStatus); //手动提交
733   - }catch (Exception e) {
734   - logger.error("向数据库添加流代理失败:", e);
735   - dataSourceTransactionManager.rollback(transactionStatus);
736   - }
737   -
738   -
739   - return result;
740   - }
741   -
742   - /**
743   - * 更新代理流
744   - * @param streamProxyItem
745   - * @return
746   - */
747   - @Override
748   - public boolean updateStreamProxy(StreamProxyItem streamProxyItem) {
749   - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
750   - boolean result = false;
751   - streamProxyItem.setStreamType("proxy");
752   - try {
753   - if (streamProxyMapper.update(streamProxyItem) > 0) {
754   - if (!StringUtils.isEmpty(streamProxyItem.getGbId())) {
755   - if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) {
756   - //事务回滚
757   - dataSourceTransactionManager.rollback(transactionStatus);
758   - return false;
759   - }
760   - }
761   - } else {
762   - //事务回滚
763   - dataSourceTransactionManager.rollback(transactionStatus);
764   - return false;
765   - }
766   -
767   - dataSourceTransactionManager.commit(transactionStatus); //手动提交
768   - result = true;
769   - }catch (Exception e) {
770   - e.printStackTrace();
771   - dataSourceTransactionManager.rollback(transactionStatus);
772   - }
773   - return result;
774   - }
775 571  
776 572 /**
777 573 * 移除代理流
... ... @@ -824,7 +620,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
824 620 * @return
825 621 */
826 622 @Override
827   - public List<GbStream> queryGbStreamListInPlatform(String platformId) {
  623 + public List<DeviceChannel> queryGbStreamListInPlatform(String platformId) {
828 624 return gbStreamMapper.queryGbStreamListInPlatform(platformId);
829 625 }
830 626  
... ... @@ -840,83 +636,30 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
840 636 }
841 637  
842 638 @Override
843   - public void updateMediaList(List<StreamPushItem> streamPushItems) {
844   - if (streamPushItems == null || streamPushItems.size() == 0) {
845   - return;
846   - }
847   - logger.info("updateMediaList: " + streamPushItems.size());
848   - streamPushMapper.addAll(streamPushItems);
849   - // TODO 待优化
850   - for (int i = 0; i < streamPushItems.size(); i++) {
851   - int onlineResult = mediaOnline(streamPushItems.get(i).getApp(), streamPushItems.get(i).getStream());
852   - if (onlineResult > 0) {
853   - // 发送上线通知
854   - eventPublisher.catalogEventPublishForStream(null, streamPushItems.get(i), CatalogEvent.ON);
855   - }
856   - }
857   - }
858   -
859   -
860   -
861   - @Override
862   - public void updateMedia(StreamPushItem streamPushItem) {
863   - streamPushMapper.del(streamPushItem.getApp(), streamPushItem.getStream());
864   - streamPushMapper.add(streamPushItem);
865   - mediaOffline(streamPushItem.getApp(), streamPushItem.getStream());
866   -
867   - if(!StringUtils.isEmpty(streamPushItem.getGbId() )){
868   - // 查找开启了全部直播流共享的上级平台
869   - List<ParentPlatform> parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream();
870   - if (parentPlatforms.size() > 0) {
871   - for (ParentPlatform parentPlatform : parentPlatforms) {
872   - StreamProxyItem streamProxyItem = platformGbStreamMapper.selectOne(streamPushItem.getApp(), streamPushItem.getStream(),
873   - parentPlatform.getServerGBId());
874   - if (streamProxyItem == null) {
875   - streamPushItem.setCatalogId(parentPlatform.getCatalogId());
876   - streamPushItem.setPlatformId(parentPlatform.getServerGBId());
877   - platformGbStreamMapper.add(streamPushItem);
878   - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), streamPushItem, CatalogEvent.ADD);
879   - }
880   - }
881   - }
882   - }
883   -
884   - }
885   -
886   - @Override
887 639 public int removeMedia(String app, String stream) {
888 640 return streamPushMapper.del(app, stream);
889 641 }
890 642  
891 643 @Override
892   - public StreamPushItem getMedia(String app, String stream) {
893   - return streamPushMapper.selectOne(app, stream);
894   - }
895   -
896   - @Override
897   - public void clearMediaList() {
898   - streamPushMapper.clear();
899   - }
900   -
901   - @Override
902 644 public int mediaOffline(String app, String stream) {
903 645 GbStream gbStream = gbStreamMapper.selectOne(app, stream);
904 646 int result;
905 647 if ("proxy".equals(gbStream.getStreamType())) {
906 648 result = streamProxyMapper.updateStatus(app, stream, false);
907 649 }else {
908   - result = streamPushMapper.updateStatus(app, stream, false);
  650 + result = streamPushMapper.updatePushStatus(app, stream, false);
909 651 }
910 652 return result;
911 653 }
912 654  
  655 + @Override
913 656 public int mediaOnline(String app, String stream) {
914 657 GbStream gbStream = gbStreamMapper.selectOne(app, stream);
915 658 int result;
916 659 if ("proxy".equals(gbStream.getStreamType())) {
917 660 result = streamProxyMapper.updateStatus(app, stream, true);
918 661 }else {
919   - result = streamPushMapper.updateStatus(app, stream, true);
  662 + result = streamPushMapper.updatePushStatus(app, stream, true);
920 663 }
921 664 return result;
922 665 }
... ... @@ -954,6 +697,29 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
954 697  
955 698 @Override
956 699 public int addCatalog(PlatformCatalog platformCatalog) {
  700 + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformCatalog.getPlatformId());
  701 + if (platform == null) {
  702 + return 0;
  703 + }
  704 + if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) {
  705 + if (platformCatalog.getPlatformId().equals(platformCatalog.getParentId())) {
  706 + // 第一层节点
  707 + platformCatalog.setBusinessGroupId(platformCatalog.getId());
  708 + platformCatalog.setParentId(platform.getDeviceGBId());
  709 + }else {
  710 + // 获取顶层的
  711 + PlatformCatalog topCatalog = getTopCatalog(platformCatalog.getParentId(), platform.getDeviceGBId());
  712 + platformCatalog.setBusinessGroupId(topCatalog.getId());
  713 + }
  714 + }
  715 + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)) {
  716 + platformCatalog.setCivilCode(platformCatalog.getId());
  717 + if (platformCatalog.getPlatformId().equals(platformCatalog.getParentId())) {
  718 + // 第一层节点
  719 + platformCatalog.setParentId(platform.getDeviceGBId());
  720 + }
  721 + }
  722 +
957 723 int result = catalogMapper.add(platformCatalog);
958 724 if (result > 0) {
959 725 DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog);
... ... @@ -962,6 +728,15 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
962 728 return result;
963 729 }
964 730  
  731 + private PlatformCatalog getTopCatalog(String id, String platformId) {
  732 + PlatformCatalog catalog = catalogMapper.selectParentCatalog(id);
  733 + if (catalog.getParentId().equals(platformId)) {
  734 + return catalog;
  735 + }else {
  736 + return getTopCatalog(catalog.getParentId(), platformId);
  737 + }
  738 + }
  739 +
965 740 @Override
966 741 public PlatformCatalog getCatalog(String id) {
967 742 return catalogMapper.select(id);
... ... @@ -1032,8 +807,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
1032 807 }
1033 808  
1034 809 @Override
1035   - public List<PlatformCatalog> queryCatalogInPlatform(String platformId) {
1036   - return catalogMapper.selectByPlatForm(platformId);
  810 + public List<DeviceChannel> queryCatalogInPlatform(String platformId) {
  811 + return catalogMapper.queryCatalogInPlatform(platformId);
1037 812 }
1038 813  
1039 814 @Override
... ... @@ -1076,20 +851,24 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
1076 851 }
1077 852  
1078 853 private DeviceChannel getDeviceChannelByCatalog(PlatformCatalog catalog) {
1079   - ParentPlatform parentPlatByServerGBId = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId());
  854 + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId());
1080 855 DeviceChannel deviceChannel = new DeviceChannel();
1081 856 deviceChannel.setChannelId(catalog.getId());
1082 857 deviceChannel.setName(catalog.getName());
1083 858 deviceChannel.setLongitude(0.0);
1084 859 deviceChannel.setLatitude(0.0);
1085   - deviceChannel.setDeviceId(parentPlatByServerGBId.getDeviceGBId());
  860 + deviceChannel.setDeviceId(platform.getDeviceGBId());
1086 861 deviceChannel.setManufacture("wvp-pro");
1087 862 deviceChannel.setStatus(1);
1088 863 deviceChannel.setParental(1);
1089   - deviceChannel.setParentId(catalog.getParentId());
  864 +
1090 865 deviceChannel.setRegisterWay(1);
1091 866 // 行政区划应该是Domain的前八位
1092   - deviceChannel.setCivilCode(parentPlatByServerGBId.getAdministrativeDivision());
  867 + if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) {
  868 + deviceChannel.setParentId(catalog.getParentId());
  869 + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId());
  870 + }
  871 +
1093 872 deviceChannel.setModel("live");
1094 873 deviceChannel.setOwner("wvp-pro");
1095 874 deviceChannel.setSecrecy("0");
... ... @@ -1151,4 +930,27 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
1151 930  
1152 931 deviceChannelMapper.updatePosition(deviceChannel);
1153 932 }
  933 +
  934 + @Override
  935 + public void cleanContentForPlatform(String serverGBId) {
  936 +// List<PlatformCatalog> catalogList = catalogMapper.selectByPlatForm(serverGBId);
  937 +// if (catalogList.size() > 0) {
  938 +// int result = catalogMapper.delByPlatformId(serverGBId);
  939 +// if (result > 0) {
  940 +// List<DeviceChannel> deviceChannels = new ArrayList<>();
  941 +// for (PlatformCatalog catalog : catalogList) {
  942 +// deviceChannels.add(getDeviceChannelByCatalog(catalog));
  943 +// }
  944 +// eventPublisher.catalogEventPublish(serverGBId, deviceChannels, CatalogEvent.DEL);
  945 +// }
  946 +// }
  947 + catalogMapper.delByPlatformId(serverGBId);
  948 + platformChannelMapper.delByPlatformId(serverGBId);
  949 + platformGbStreamMapper.delByPlatformId(serverGBId);
  950 + }
  951 +
  952 + @Override
  953 + public List<DeviceChannel> queryChannelWithCatalog(String serverGBId) {
  954 + return deviceChannelMapper.queryChannelWithCatalog(serverGBId);
  955 + }
1154 956 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
... ... @@ -12,6 +12,7 @@ public class WVPResult&lt;T&gt; {
12 12 this.data = data;
13 13 }
14 14  
  15 +
15 16 private int code;
16 17 private String msg;
17 18 private T data;
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
... ... @@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
12 12 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
13 13 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
14 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  15 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
15 16 import com.genersoft.iot.vmp.service.IDeviceService;
16 17 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
17 18 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
... ... @@ -53,6 +54,9 @@ public class DeviceQuery {
53 54 private IVideoManagerStorage storager;
54 55  
55 56 @Autowired
  57 + private IDeviceChannelService deviceChannelService;
  58 +
  59 + @Autowired
56 60 private IRedisCatchStorage redisCatchStorage;
57 61  
58 62 @Autowired
... ... @@ -280,7 +284,7 @@ public class DeviceQuery {
280 284 })
281 285 @PostMapping("/channel/update/{deviceId}")
282 286 public ResponseEntity<PageInfo> updateChannel(@PathVariable String deviceId,DeviceChannel channel){
283   - storager.updateChannel(deviceId, channel);
  287 + deviceChannelService.updateChannel(deviceId, channel);
284 288 return new ResponseEntity<>(null,HttpStatus.OK);
285 289 }
286 290  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
... ... @@ -44,7 +44,6 @@ public class GbStreamController {
44 44 @ApiImplicitParam(name = "platformId", value = "平台ID", required = true , dataTypeClass = String.class),
45 45 @ApiImplicitParam(name = "catalogId", value = "目录ID", required = false , dataTypeClass = String.class),
46 46 @ApiImplicitParam(name="query", value = "查询内容", required = false , dataTypeClass = String.class),
47   - @ApiImplicitParam(name="pushing", value = "是否正在推流", required = false , dataTypeClass = Boolean.class),
48 47 @ApiImplicitParam(name="mediaServerId", value = "流媒体ID", required = false , dataTypeClass = String.class),
49 48  
50 49 })
... ... @@ -55,7 +54,6 @@ public class GbStreamController {
55 54 @RequestParam(required = true)String platformId,
56 55 @RequestParam(required = false)String catalogId,
57 56 @RequestParam(required = false)String query,
58   - @RequestParam(required = false)Boolean pushing,
59 57 @RequestParam(required = false)String mediaServerId){
60 58 if (StringUtils.isEmpty(catalogId)) {
61 59 catalogId = null;
... ... @@ -69,7 +67,7 @@ public class GbStreamController {
69 67  
70 68 // catalogId 为null 查询未在平台下分配的数据
71 69 // catalogId 不为null 查询平台下这个,目录下的通道
72   - return gbStreamService.getAll(page, count, platformId, catalogId, query, pushing, mediaServerId);
  70 + return gbStreamService.getAll(page, count, platformId, catalogId, query, mediaServerId);
73 71 }
74 72  
75 73  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java
... ... @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
6 6 import com.genersoft.iot.vmp.media.zlm.dto.OnPublishHookParam;
7 7 import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
8 8 import com.genersoft.iot.vmp.service.IMediaServerService;
  9 +import com.genersoft.iot.vmp.service.IStreamProxyService;
9 10 import com.genersoft.iot.vmp.service.IStreamPushService;
10 11 import com.genersoft.iot.vmp.service.IMediaService;
11 12 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
... ... @@ -37,6 +38,8 @@ public class MediaController {
37 38  
38 39 @Autowired
39 40 private IMediaService mediaService;
  41 + @Autowired
  42 + private IStreamProxyService streamProxyService;
40 43  
41 44  
42 45 /**
... ... @@ -95,8 +98,30 @@ public class MediaController {
95 98 result.setMsg("scccess");
96 99 result.setData(streamInfo);
97 100 }else {
98   - result.setCode(-1);
99   - result.setMsg("fail");
  101 + //获取流失败,重启拉流后重试一次
  102 + streamProxyService.stop(app,stream);
  103 + boolean start = streamProxyService.start(app, stream);
  104 + try {
  105 + Thread.sleep(1000);
  106 + } catch (InterruptedException e) {
  107 + e.printStackTrace();
  108 + }
  109 + if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) {
  110 + String host = request.getHeader("Host");
  111 + String localAddr = host.split(":")[0];
  112 + logger.info("使用{}作为返回流的ip", localAddr);
  113 + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority);
  114 + }else {
  115 + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority);
  116 + }
  117 + if (streamInfo != null){
  118 + result.setCode(0);
  119 + result.setMsg("scccess");
  120 + result.setData(streamInfo);
  121 + }else {
  122 + result.setCode(-1);
  123 + result.setMsg("fail");
  124 + }
100 125 }
101 126 return result;
102 127 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
... ... @@ -8,7 +8,9 @@ import com.genersoft.iot.vmp.conf.UserSetting;
8 8 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
9 9 import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
10 10 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
  11 +import com.genersoft.iot.vmp.gb28181.bean.TreeType;
11 12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
  13 +import com.genersoft.iot.vmp.service.IPlatformChannelService;
12 14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
13 15 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
14 16 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -49,6 +51,9 @@ public class PlatformController {
49 51 private IVideoManagerStorage storager;
50 52  
51 53 @Autowired
  54 + private IPlatformChannelService platformChannelService;
  55 +
  56 + @Autowired
52 57 private IRedisCatchStorage redisCatchStorage;
53 58  
54 59 @Autowired
... ... @@ -236,6 +241,12 @@ public class PlatformController {
236 241 parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
237 242 ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId());
238 243 parentPlatform.setUpdateTime(DateUtil.getNow());
  244 + if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
  245 + // 目录结构发生变化,清空之前的关联关系
  246 + logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
  247 + storager.cleanContentForPlatform(parentPlatform.getServerGBId());
  248 +
  249 + }
239 250 boolean updateResult = storager.updateParentPlatform(parentPlatform);
240 251  
241 252 if (updateResult) {
... ... @@ -256,6 +267,8 @@ public class PlatformController {
256 267 }
257 268 } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销
258 269 commanderForPlatform.unregister(parentPlatformOld, null, null);
  270 + // 停止订阅相关的定时任务
  271 + subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
259 272 }
260 273 wvpResult.setCode(0);
261 274 wvpResult.setMsg("success");
... ... @@ -405,7 +418,7 @@ public class PlatformController {
405 418 if (logger.isDebugEnabled()) {
406 419 logger.debug("给上级平台添加国标通道API调用");
407 420 }
408   - int result = storager.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId());
  421 + int result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId());
409 422  
410 423 return new ResponseEntity<>(String.valueOf(result > 0), HttpStatus.OK);
411 424 }
... ... @@ -451,13 +464,20 @@ public class PlatformController {
451 464 if (logger.isDebugEnabled()) {
452 465 logger.debug("查询目录,platformId: {}, parentId: {}", platformId, parentId);
453 466 }
  467 + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  468 + if (platform == null) {
  469 + return new ResponseEntity<>(new WVPResult<>(400, "平台未找到", null), HttpStatus.OK);
  470 + }
  471 + if (platformId.equals(parentId)) {
  472 + parentId = platform.getDeviceGBId();
  473 + }
454 474 List<PlatformCatalog> platformCatalogList = storager.getChildrenCatalogByPlatform(platformId, parentId);
455   - // 查询下属的国标通道
456   -// List<PlatformCatalog> catalogsForChannel = storager.queryChannelInParentPlatformAndCatalog(platformId, parentId);
457   - // 查询下属的直播流通道
458   -// List<PlatformCatalog> catalogsForStream = storager.queryStreamInParentPlatformAndCatalog(platformId, parentId);
459   -// platformCatalogList.addAll(catalogsForChannel);
460   -// platformCatalogList.addAll(catalogsForStream);
  475 +// if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) {
  476 +// platformCatalogList = storager.getChildrenCatalogByPlatform(platformId, parentId);
  477 +// }else {
  478 +//
  479 +// }
  480 +
461 481 WVPResult<List<PlatformCatalog>> result = new WVPResult<>();
462 482 result.setCode(0);
463 483 result.setMsg("success");
... ... @@ -485,7 +505,6 @@ public class PlatformController {
485 505 PlatformCatalog platformCatalogInStore = storager.getCatalog(platformCatalog.getId());
486 506 WVPResult<List<PlatformCatalog>> result = new WVPResult<>();
487 507  
488   -
489 508 if (platformCatalogInStore != null) {
490 509 result.setCode(-1);
491 510 result.setMsg(platformCatalog.getId() + " already exists");
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
... ... @@ -8,6 +8,8 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
8 8 import com.genersoft.iot.vmp.conf.SipConfig;
9 9 import com.genersoft.iot.vmp.conf.UserSetting;
10 10 import com.genersoft.iot.vmp.conf.VersionInfo;
  11 +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
  12 +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe;
11 13 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
12 14 import com.genersoft.iot.vmp.service.IMediaServerService;
13 15 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
... ... @@ -38,7 +40,7 @@ import java.util.Set;
38 40 public class ServerController {
39 41  
40 42 @Autowired
41   - private ConfigurableApplicationContext context;
  43 + private ZLMHttpHookSubscribe zlmHttpHookSubscribe;
42 44  
43 45 @Autowired
44 46 private IMediaServerService mediaServerService;
... ... @@ -254,6 +256,18 @@ public class ServerController {
254 256 return result;
255 257 }
256 258  
  259 + @ApiOperation("获取当前所有hook")
  260 + @GetMapping(value = "/hooks")
  261 + @ResponseBody
  262 + public WVPResult<List<IHookSubscribe>> getHooks(){
  263 + WVPResult<List<IHookSubscribe>> result = new WVPResult<>();
  264 + result.setCode(0);
  265 + result.setMsg("success");
  266 + List<IHookSubscribe> all = zlmHttpHookSubscribe.getAll();
  267 + result.setData(all);
  268 + return result;
  269 + }
  270 +
257 271 // @ApiOperation("当前进行中的动态任务")
258 272 // @GetMapping(value = "/dynamicTask")
259 273 // @ResponseBody
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java
... ... @@ -4,6 +4,7 @@ import com.alibaba.excel.EasyExcel;
4 4 import com.alibaba.excel.ExcelReader;
5 5 import com.alibaba.excel.read.metadata.ReadSheet;
6 6 import com.genersoft.iot.vmp.common.StreamInfo;
  7 +import com.genersoft.iot.vmp.conf.UserSetting;
7 8 import com.genersoft.iot.vmp.conf.security.SecurityUtils;
8 9 import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
9 10 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
... ... @@ -63,6 +64,9 @@ public class StreamPushController {
63 64 @Autowired
64 65 private IMediaService mediaService;
65 66  
  67 + @Autowired
  68 + private UserSetting userSetting;
  69 +
66 70 @ApiOperation("推流列表查询")
67 71 @ApiImplicitParams({
68 72 @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
... ... @@ -260,29 +264,63 @@ public class StreamPushController {
260 264 })
261 265 @GetMapping(value = "/getPlayUrl")
262 266 @ResponseBody
263   - public WVPResult<StreamInfo> getPlayUrl(HttpServletRequest request, @RequestParam String app,
264   - @RequestParam String stream,
265   - @RequestParam(required = false) String mediaServerId){
  267 + public WVPResult<StreamInfo> getPlayUrl(@RequestParam String app,@RequestParam String stream,
  268 + @RequestParam(required = false) String mediaServerId){
266 269 boolean authority = false;
267 270 // 是否登陆用户, 登陆用户返回完整信息
268 271 LoginUser userInfo = SecurityUtils.getUserInfo();
269 272 if (userInfo!= null) {
270 273 authority = true;
271 274 }
272   -
273   - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority);
274   -
275 275 WVPResult<StreamInfo> result = new WVPResult<>();
  276 + StreamPushItem push = streamPushService.getPush(app, stream);
  277 + if (push != null && !push.isSelf()) {
  278 + result.setCode(-1);
  279 + result.setMsg("来自其他平台的推流信息");
  280 + return result;
  281 + }
  282 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority);
276 283 if (streamInfo != null){
277 284 result.setCode(0);
278   - result.setMsg("scccess");
  285 + result.setMsg("success");
279 286 result.setData(streamInfo);
280 287 }else {
281 288 result.setCode(-1);
282   - result.setMsg("fail");
  289 + result.setMsg("获取播放地址失败");
283 290 }
  291 +
284 292 return result;
285 293 }
286 294  
  295 + /**
  296 + * 获取推流播放地址
  297 + * @param stream 推流信息
  298 + * @return
  299 + */
  300 + @ApiOperation("获取推流播放地址")
  301 + @ApiImplicitParams({
  302 + @ApiImplicitParam(name = "stream", value = "推流信息", dataTypeClass = StreamPushItem.class),
  303 + })
  304 + @PostMapping(value = "/add")
  305 + @ResponseBody
  306 + public WVPResult<StreamInfo> add(@RequestBody StreamPushItem stream){
  307 + if (StringUtils.isEmpty(stream.getGbId())) {
  308 +
  309 + return new WVPResult<>(400, "国标ID不可为空", null);
  310 + }
  311 + if (StringUtils.isEmpty(stream.getApp()) && StringUtils.isEmpty(stream.getStream())) {
  312 + return new WVPResult<>(400, "app或stream不可为空", null);
  313 + }
  314 + stream.setStatus(false);
  315 + stream.setPushIng(false);
  316 + stream.setAliveSecond(0L);
  317 + stream.setTotalReaderCount("0");
  318 + boolean result = streamPushService.add(stream);
287 319  
  320 + if (result) {
  321 + return new WVPResult<>(0, "success", null);
  322 + }else {
  323 + return new WVPResult<>(-1, "fail", null);
  324 + }
  325 + }
288 326 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
... ... @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.storager.dao.dto.Role;
8 8 import com.genersoft.iot.vmp.storager.dao.dto.User;
9 9 import com.genersoft.iot.vmp.utils.DateUtil;
10 10 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  11 +import com.github.pagehelper.PageInfo;
11 12 import io.swagger.annotations.Api;
12 13 import io.swagger.annotations.ApiImplicitParam;
13 14 import io.swagger.annotations.ApiImplicitParams;
... ... @@ -123,7 +124,8 @@ public class UserController {
123 124 User user = new User();
124 125 user.setUsername(username);
125 126 user.setPassword(DigestUtils.md5DigestAsHex(password.getBytes()));
126   -
  127 + //新增用户的pushKey的生成规则为md5(时间戳+用户名)
  128 + user.setPushKey(DigestUtils.md5DigestAsHex((System.currentTimeMillis()+password).getBytes()));
127 129 Role role = roleService.getRoleById(roleId);
128 130  
129 131 if (role == null) {
... ... @@ -137,6 +139,7 @@ public class UserController {
137 139 user.setUpdateTime(DateUtil.getNow());
138 140 int addResult = userService.addUser(user);
139 141  
  142 +
140 143 result.setCode(addResult > 0 ? 0 : -1);
141 144 result.setMsg(addResult > 0 ? "success" : "fail");
142 145 result.setData(addResult);
... ... @@ -177,4 +180,68 @@ public class UserController {
177 180 result.setData(allUsers);
178 181 return new ResponseEntity<>(result, HttpStatus.OK);
179 182 }
  183 +
  184 + /**
  185 + * 分页查询用户
  186 + *
  187 + * @param page 当前页
  188 + * @param count 每页查询数量
  189 + * @return 分页用户列表
  190 + */
  191 + @ApiOperation("分页查询用户")
  192 + @ApiImplicitParams({
  193 + @ApiImplicitParam(name = "page", value = "当前页", required = true, dataTypeClass = Integer.class),
  194 + @ApiImplicitParam(name = "count", value = "每页查询数量", required = true, dataTypeClass = Integer.class),
  195 + })
  196 + @GetMapping("/users")
  197 + public PageInfo<User> users(int page, int count) {
  198 + return userService.getUsers(page, count);
  199 + }
  200 +
  201 + @ApiOperation("修改pushkey")
  202 + @ApiImplicitParams({
  203 + @ApiImplicitParam(name = "userId", required = true, value = "用户Id", dataTypeClass = Integer.class),
  204 + @ApiImplicitParam(name = "pushKey", required = true, value = "新的pushKey", dataTypeClass = String.class),
  205 + })
  206 + @RequestMapping("/changePushKey")
  207 + public ResponseEntity<WVPResult<String>> changePushKey(@RequestParam Integer userId,@RequestParam String pushKey) {
  208 + // 获取当前登录用户id
  209 + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId();
  210 + WVPResult<String> result = new WVPResult<>();
  211 + if (currenRoleId != 1) {
  212 + // 只用角色id为0才可以删除和添加用户
  213 + result.setCode(-1);
  214 + result.setMsg("用户无权限");
  215 + return new ResponseEntity<>(result, HttpStatus.FORBIDDEN);
  216 + }
  217 + int resetPushKeyResult = userService.changePushKey(userId,pushKey);
  218 +
  219 + result.setCode(resetPushKeyResult > 0 ? 0 : -1);
  220 + result.setMsg(resetPushKeyResult > 0 ? "success" : "fail");
  221 + return new ResponseEntity<>(result, HttpStatus.OK);
  222 + }
  223 +
  224 + @ApiOperation("管理员修改普通用户密码")
  225 + @ApiImplicitParams({
  226 + @ApiImplicitParam(name = "adminId", required = true, value = "管理员id", dataTypeClass = String.class),
  227 + @ApiImplicitParam(name = "userId", required = true, value = "用户id", dataTypeClass = String.class),
  228 + @ApiImplicitParam(name = "password", required = true, value = "新密码(未md5加密的密码)", dataTypeClass = String.class),
  229 + })
  230 + @PostMapping("/changePasswordForAdmin")
  231 + public String changePasswordForAdmin(@RequestParam int userId, @RequestParam String password) {
  232 + // 获取当前登录用户id
  233 + LoginUser userInfo = SecurityUtils.getUserInfo();
  234 + if (userInfo == null) {
  235 + return "fail";
  236 + }
  237 + Role role = userInfo.getRole();
  238 + if (role != null && role.getId() == 1) {
  239 + boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes()));
  240 + if (result) {
  241 + return "success";
  242 + }
  243 + }
  244 +
  245 + return "fail";
  246 + }
180 247 }
... ...
src/main/resources/all-application.yml
... ... @@ -149,7 +149,7 @@ media:
149 149 enable: true
150 150 # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功
151 151 port-range: 30000,30500 # 端口范围
152   - # [可选] 国标级联在此范围内选择端口发送媒体流,
  152 + # [可选] 国标级联在此范围内选择端口发送媒体流
153 153 send-port-range: 30000,30500 # 端口范围
154 154 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
155 155 record-assist-port: 0
... ...
web_src/src/components/Login.vue
... ... @@ -86,7 +86,7 @@ export default {
86 86 }).then(function (res) {
87 87 console.log(JSON.stringify(res));
88 88 if (res.data.code == 0 && res.data.msg == "success") {
89   - that.$cookies.set("session", {"username": that.username}) ;
  89 + that.$cookies.set("session", {"username": that.username,"roleId":res.data.data.role.id}) ;
90 90 //登录成功后
91 91 that.cancelEnterkeyDefaultAction();
92 92 that.$router.push('/');
... ...
web_src/src/components/ParentPlatformList.vue
... ... @@ -143,7 +143,7 @@ export default {
143 143 });
144 144 },
145 145 chooseChannel: function(platform) {
146   - this.$refs.chooseChannelDialog.openDialog(platform.serverGBId, platform.name, platform.catalogId, this.initData)
  146 + this.$refs.chooseChannelDialog.openDialog(platform.serverGBId, platform.name, platform.catalogId, platform.treeType, this.initData)
147 147 },
148 148 initData: function() {
149 149 this.getPlatformList();
... ...
web_src/src/components/PushVideoList.vue
... ... @@ -34,6 +34,8 @@
34 34 <el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;"
35 35 :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除
36 36 </el-button>
  37 + <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStream">添加通道
  38 + </el-button>
37 39 <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
38 40 </div>
39 41 </div>
... ... @@ -56,20 +58,25 @@
56 58 <el-table-column label="开始时间" min-width="200">
57 59 <template slot-scope="scope">
58 60 <el-button-group>
59   - {{ dateFormat(parseInt(scope.row.createStamp)) }}
  61 + {{ scope.row.pushTime == null? "-":scope.row.pushTime }}
60 62 </el-button-group>
61 63 </template>
62 64 </el-table-column>
63 65 <el-table-column label="正在推流" min-width="100">
64 66 <template slot-scope="scope">
65   - {{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '是' : '否' }}
  67 + {{scope.row.pushIng ? '是' : '否' }}
  68 + </template>
  69 + </el-table-column>
  70 + <el-table-column label="本平台推流" min-width="100">
  71 + <template slot-scope="scope">
  72 + {{scope.row.pushIng && !!scope.row.self ? '是' : '否' }}
66 73 </template>
67 74 </el-table-column>
68 75  
69 76 <el-table-column label="操作" min-width="360" fixed="right">
70 77 <template slot-scope="scope">
71 78 <el-button size="medium" icon="el-icon-video-play"
72   - v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"
  79 + v-if="scope.row.pushIng === true"
73 80 @click="playPush(scope.row)" type="text">播放
74 81 </el-button>
75 82 <el-divider direction="vertical"></el-divider>
... ... @@ -103,7 +110,7 @@
103 110 <script>
104 111 import streamProxyEdit from './dialog/StreamProxyEdit.vue'
105 112 import devicePlayer from './dialog/devicePlayer.vue'
106   -import addStreamTOGB from './dialog/addStreamTOGB.vue'
  113 +import addStreamTOGB from './dialog/pushStreamEdit.vue'
107 114 import uiHeader from '../layout/UiHeader.vue'
108 115 import importChannel from './dialog/importChannel.vue'
109 116 import MediaServer from './service/MediaServer'
... ... @@ -195,10 +202,15 @@ export default {
195 202 }
196 203 }).then(function (res) {
197 204 that.getListLoading = false;
198   - that.$refs.devicePlayer.openDialog("streamPlay", null, null, {
199   - streamInfo: res.data.data,
200   - hasAudio: true
201   - });
  205 + if (res.data.code === 0 ) {
  206 + that.$refs.devicePlayer.openDialog("streamPlay", null, null, {
  207 + streamInfo: res.data.data,
  208 + hasAudio: true
  209 + });
  210 + }else {
  211 + that.$message.error(res.data.msg);
  212 + }
  213 +
202 214 }).catch(function (error) {
203 215 console.error(error);
204 216 that.getListLoading = false;
... ... @@ -242,24 +254,14 @@ export default {
242 254 console.error(error);
243 255 });
244 256 },
245   - dateFormat: function (/** timestamp=0 **/) {
246   - let ts = arguments[0] || 0;
247   - let t, y, m, d, h, i, s;
248   - t = ts ? new Date(ts) : new Date();
249   - y = t.getFullYear();
250   - m = t.getMonth() + 1;
251   - d = t.getDate();
252   - h = t.getHours();
253   - i = t.getMinutes();
254   - s = t.getSeconds();
255   - // 可根据需要在这里定义时间格式
256   - return y + '-' + (m < 10 ? '0' + m : m) + '-' + (d < 10 ? '0' + d : d) + ' ' + (h < 10 ? '0' + h : h) + ':' + (i < 10 ? '0' + i : i) + ':' + (s < 10 ? '0' + s : s);
257   - },
258 257 importChannel: function () {
259 258 this.$refs.importChannel.openDialog(() => {
260 259  
261 260 })
262 261 },
  262 + addStream: function (){
  263 + this.$refs.addStreamTOGB.openDialog(null, this.initData);
  264 + },
263 265 batchDel: function () {
264 266 this.$confirm(`确定删除选中的${this.multipleSelection.length}个通道?`, '提示', {
265 267 confirmButtonText: '确定',
... ...
web_src/src/components/UserManager.vue 0 → 100644
  1 +<template>
  2 +
  3 + <div id="app" style="width: 100%">
  4 + <div class="page-header">
  5 +
  6 + <div class="page-title">用户列表</div>
  7 + <div class="page-header-btn">
  8 + <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addUser">
  9 + 添加用户
  10 + </el-button>
  11 +
  12 + </div>
  13 + </div>
  14 + <!--用户列表-->
  15 + <el-table :data="userList" style="width: 100%;font-size: 12px;" :height="winHeight"
  16 + header-row-class-name="table-header">
  17 + <el-table-column prop="username" label="用户名" min-width="160"/>
  18 + <el-table-column prop="pushKey" label="pushkey" min-width="160"/>
  19 + <el-table-column prop="role.name" label="类型" min-width="160"/>
  20 + <el-table-column label="操作" min-width="450" fixed="right">
  21 + <template slot-scope="scope">
  22 + <el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">修改密码</el-button>
  23 + <el-divider direction="vertical"></el-divider>
  24 + <el-button size="medium" icon="el-icon-edit" type="text" @click="changePushKey(scope.row)">修改pushkey</el-button>
  25 + <el-divider direction="vertical"></el-divider>
  26 + <el-button size="medium" icon="el-icon-delete" type="text" @click="deleteUser(scope.row)"
  27 + style="color: #f56c6c">删除
  28 + </el-button>
  29 + </template>
  30 + </el-table-column>
  31 + </el-table>
  32 + <changePasswordForAdmin ref="changePasswordForAdmin"></changePasswordForAdmin>
  33 + <changePushKey ref="changePushKey"></changePushKey>
  34 + <addUser ref="addUser"></addUser>
  35 + <el-pagination
  36 + style="float: right"
  37 + @size-change="handleSizeChange"
  38 + @current-change="currentChange"
  39 + :current-page="currentPage"
  40 + :page-size="count"
  41 + :page-sizes="[15, 25, 35, 50]"
  42 + layout="total, sizes, prev, pager, next"
  43 + :total="total">
  44 + </el-pagination>
  45 + </div>
  46 +</template>
  47 +
  48 +<script>
  49 +import uiHeader from '../layout/UiHeader.vue'
  50 +import changePasswordForAdmin from './dialog/changePasswordForAdmin.vue'
  51 +import changePushKey from './dialog/changePushKey.vue'
  52 +import addUser from '../components/dialog/addUser.vue'
  53 +
  54 +export default {
  55 + name: 'userManager',
  56 + components: {
  57 + uiHeader,
  58 + changePasswordForAdmin,
  59 + changePushKey,
  60 + addUser
  61 + },
  62 + data() {
  63 + return {
  64 + userList: [], //设备列表
  65 + currentUser: {}, //当前操作设备对象
  66 +
  67 + videoComponentList: [],
  68 + updateLooper: 0, //数据刷新轮训标志
  69 + currentUserLenth: 0,
  70 + winHeight: window.innerHeight - 200,
  71 + currentPage: 1,
  72 + count: 15,
  73 + total: 0,
  74 + getUserListLoading: false
  75 + };
  76 + },
  77 + mounted() {
  78 + this.initData();
  79 + this.updateLooper = setInterval(this.initData, 10000);
  80 + },
  81 + destroyed() {
  82 + this.$destroy('videojs');
  83 + clearTimeout(this.updateLooper);
  84 + },
  85 + methods: {
  86 + initData: function () {
  87 + this.getUserList();
  88 + },
  89 + currentChange: function (val) {
  90 + this.currentPage = val;
  91 + this.getUserList();
  92 + },
  93 + handleSizeChange: function (val) {
  94 + this.count = val;
  95 + this.getUserList();
  96 + },
  97 + getUserList: function () {
  98 + let that = this;
  99 + this.getUserListLoading = true;
  100 + this.$axios({
  101 + method: 'get',
  102 + url: `/api/user/users`,
  103 + params: {
  104 + page: that.currentPage,
  105 + count: that.count
  106 + }
  107 + }).then(function (res) {
  108 + that.total = res.data.total;
  109 + that.userList = res.data.list;
  110 + that.getUserListLoading = false;
  111 + }).catch(function (error) {
  112 + that.getUserListLoading = false;
  113 + });
  114 +
  115 + },
  116 + edit: function (row) {
  117 + this.$refs.changePasswordForAdmin.openDialog(row, () => {
  118 + this.$refs.changePasswordForAdmin.close();
  119 + this.$message({
  120 + showClose: true,
  121 + message: "密码修改成功",
  122 + type: "success",
  123 + });
  124 + setTimeout(this.getUserList, 200)
  125 +
  126 + })
  127 + },
  128 + deleteUser: function (row) {
  129 + let msg = "确定删除此用户?"
  130 + if (row.online !== 0) {
  131 + msg = "<strong>确定删除此用户?</strong>"
  132 + }
  133 + this.$confirm(msg, '提示', {
  134 + dangerouslyUseHTMLString: true,
  135 + confirmButtonText: '确定',
  136 + cancelButtonText: '取消',
  137 + center: true,
  138 + type: 'warning'
  139 + }).then(() => {
  140 + this.$axios({
  141 + method: 'delete',
  142 + url: `/api/user/delete?id=${row.id}`
  143 + }).then((res) => {
  144 + this.getUserList();
  145 + }).catch((error) => {
  146 + console.error(error);
  147 + });
  148 + }).catch(() => {
  149 +
  150 + });
  151 +
  152 +
  153 + },
  154 +
  155 + changePushKey: function (row) {
  156 + this.$refs.changePushKey.openDialog(row, () => {
  157 + this.$refs.changePushKey.close();
  158 + this.$message({
  159 + showClose: true,
  160 + message: "pushKey修改成功",
  161 + type: "success",
  162 + });
  163 + setTimeout(this.getUserList, 200)
  164 +
  165 + })
  166 + },
  167 + addUser: function () {
  168 + // this.$refs.addUser.openDialog()
  169 + this.$refs.addUser.openDialog( () => {
  170 + this.$refs.addUser.close();
  171 + this.$message({
  172 + showClose: true,
  173 + message: "用户添加成功",
  174 + type: "success",
  175 + });
  176 + setTimeout(this.getUserList, 200)
  177 +
  178 + })
  179 + }
  180 + }
  181 +}
  182 +</script>
  183 +<style>
  184 +.videoList {
  185 + display: flex;
  186 + flex-wrap: wrap;
  187 + align-content: flex-start;
  188 +}
  189 +
  190 +.video-item {
  191 + position: relative;
  192 + width: 15rem;
  193 + height: 10rem;
  194 + margin-right: 1rem;
  195 + background-color: #000000;
  196 +}
  197 +
  198 +.video-item-img {
  199 + position: absolute;
  200 + top: 0;
  201 + bottom: 0;
  202 + left: 0;
  203 + right: 0;
  204 + margin: auto;
  205 + width: 100%;
  206 + height: 100%;
  207 +}
  208 +
  209 +.video-item-img:after {
  210 + content: "";
  211 + display: inline-block;
  212 + position: absolute;
  213 + z-index: 2;
  214 + top: 0;
  215 + bottom: 0;
  216 + left: 0;
  217 + right: 0;
  218 + margin: auto;
  219 + width: 3rem;
  220 + height: 3rem;
  221 + background-image: url("../assets/loading.png");
  222 + background-size: cover;
  223 + background-color: #000000;
  224 +}
  225 +
  226 +.video-item-title {
  227 + position: absolute;
  228 + bottom: 0;
  229 + color: #000000;
  230 + background-color: #ffffff;
  231 + line-height: 1.5rem;
  232 + padding: 0.3rem;
  233 + width: 14.4rem;
  234 +}
  235 +
  236 +</style>
... ...
web_src/src/components/channelList.vue
... ... @@ -32,7 +32,7 @@
32 32 <el-button v-if="!showTree" icon="iconfont icon-tree" circle size="mini" @click="switchTree()"></el-button>
33 33 </div>
34 34 </div>
35   - <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer>
  35 + <devicePlayer ref="devicePlayer" ></devicePlayer>
36 36 <el-container v-loading="isLoging" style="height: 82vh;">
37 37 <el-aside width="auto" style="height: 82vh; background-color: #ffffff; overflow: auto" v-if="showTree" >
38 38 <DeviceTree ref="deviceTree" :device="device" :onlyCatalog="true" :clickEvent="treeNodeClickEvent" ></DeviceTree>
... ... @@ -124,7 +124,6 @@
124 124 import devicePlayer from './dialog/devicePlayer.vue'
125 125 import uiHeader from '../layout/UiHeader.vue'
126 126 import moment from "moment";
127   -import DviceService from "./service/DeviceService";
128 127 import DeviceService from "./service/DeviceService";
129 128 import DeviceTree from "./common/DeviceTree";
130 129  
... ... @@ -318,7 +317,7 @@ export default {
318 317 changeSubchannel(itemData) {
319 318 this.beforeUrl = this.$router.currentRoute.path;
320 319  
321   - var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}/${this.$router.currentRoute.params.count}/1`
  320 + var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}`
322 321 this.$router.push(url).then(() => {
323 322 this.searchSrt = "";
324 323 this.channelType = "";
... ...
web_src/src/components/common/jessibuca.vue
... ... @@ -5,8 +5,8 @@
5 5 <i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i>
6 6 <i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i>
7 7 <i class="iconfont icon-stop jessibuca-btn" @click="destroy"></i>
8   - <i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="jessibuca.mute()"></i>
9   - <i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="jessibuca.cancelMute()"></i>
  8 + <i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="mute()"></i>
  9 + <i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="cancelMute()"></i>
10 10 </div>
11 11 <div class="buttons-box-right">
12 12 <span class="jessibuca-btn">{{ kBps }} kb/s</span>
... ... @@ -243,6 +243,16 @@ export default {
243 243 this.err = "";
244 244 this.performance = "";
245 245 },
  246 + mute: function () {
  247 + if (jessibucaPlayer[this._uid]) {
  248 + jessibucaPlayer[this._uid].mute();
  249 + }
  250 + },
  251 + cancelMute: function () {
  252 + if (jessibucaPlayer[this._uid]) {
  253 + jessibucaPlayer[this._uid].cancelMute();
  254 + }
  255 + },
246 256 destroy: function () {
247 257 if (jessibucaPlayer[this._uid]) {
248 258 jessibucaPlayer[this._uid].destroy();
... ...
web_src/src/components/control.vue
... ... @@ -324,7 +324,7 @@ export default {
324 324 */
325 325 getThreadsLoad: function () {
326 326 let that = this;
327   - if (that.mediaServerChoose != null) {
  327 + if (!!that.mediaServerChoose) {
328 328 this.$axios({
329 329 method: 'get',
330 330 url: '/zlm/' + that.mediaServerChoose + '/index/api/getThreadsLoad'
... ... @@ -375,7 +375,7 @@ export default {
375 375 },
376 376 getLoadCount: function () {
377 377 let that = this;
378   - if (that.mediaServerChoose != null) {
  378 + if (!!that.mediaServerChoose) {
379 379 that.mediaServer.getMediaServer(that.mediaServerChoose, (data) => {
380 380 if (data.code == 0) {
381 381 that.loadCount = data.data.count
... ... @@ -473,42 +473,46 @@ export default {
473 473 },
474 474  
475 475 getAllSession: function () {
476   - let that = this;
477   - that.allSessionData = [];
478   - this.$axios({
479   - method: 'get',
480   - url: '/zlm/' + that.mediaServerChoose + '/index/api/getAllSession'
481   - }).then(function (res) {
482   - res.data.data.forEach(item => {
483   - let data = {
484   - peer_ip: item.peer_ip,
485   - local_ip: item.local_ip,
486   - typeid: item.typeid,
487   - id: item.id
488   - };
489   - that.allSessionData.push(data);
  476 + this.allSessionData = [];
  477 + if (!!this.mediaServerChoose) {
  478 + this.$axios({
  479 + method: 'get',
  480 + url: '/zlm/' + this.mediaServerChoose + '/index/api/getAllSession'
  481 + }).then((res)=> {
  482 + res.data.data.forEach(item => {
  483 + let data = {
  484 + peer_ip: item.peer_ip,
  485 + local_ip: item.local_ip,
  486 + typeid: item.typeid,
  487 + id: item.id
  488 + };
  489 + this.allSessionData.push(data);
  490 + });
490 491 });
491   - });
  492 + }
  493 +
492 494 },
493 495 getServerConfig: function () {
494   - let that = this;
495   - this.$axios({
496   - method: 'get',
497   - url: '/zlm/' + that.mediaServerChoose + '/index/api/getServerConfig'
498   - }).then(function (res) {
499   - let info = res.data.data[0];
500   - let serverInfo = {}
501   - for (let i = 0; i < Object.keys(info).length; i++) {
502   - let key = Object.keys(info)[i];
503   - let group = key.substring(0, key.indexOf("."))
504   - let itemKey = key.substring(key.indexOf(".") + 1)
505   - if (!serverInfo[group]) serverInfo[group] = {}
506   - serverInfo[group][itemKey] = info[key]
507   - }
  496 + if (!!this.mediaServerChoose) {
  497 + this.$axios({
  498 + method: 'get',
  499 + url: '/zlm/' + this.mediaServerChoose + '/index/api/getServerConfig'
  500 + }).then((res)=> {
  501 + let info = res.data.data[0];
  502 + let serverInfo = {}
  503 + for (let i = 0; i < Object.keys(info).length; i++) {
  504 + let key = Object.keys(info)[i];
  505 + let group = key.substring(0, key.indexOf("."))
  506 + let itemKey = key.substring(key.indexOf(".") + 1)
  507 + if (!serverInfo[group]) serverInfo[group] = {}
  508 + serverInfo[group][itemKey] = info[key]
  509 + }
  510 +
  511 + this.serverConfig = serverInfo;
  512 + this.visible = true;
  513 + });
  514 + }
508 515  
509   - that.serverConfig = serverInfo;
510   - that.visible = true;
511   - });
512 516 },
513 517 getWVPServerConfig: function () {
514 518 let that = this;
... ... @@ -531,6 +535,14 @@ export default {
531 535 },
532 536 reStartServer: function () {
533 537 let that = this;
  538 + if (!!!this.mediaServerChoose) {
  539 + this.$message({
  540 + type: 'info',
  541 + message: '未选择节点'
  542 + });
  543 + return;
  544 + }
  545 +
534 546 this.$confirm('此操作将重启媒体服务器, 是否继续?', '提示', {
535 547 confirmButtonText: '确定',
536 548 cancelButtonText: '取消',
... ... @@ -571,17 +583,19 @@ export default {
571 583 console.log(JSON.stringify(tabledata[index]));
572 584 },
573 585 deleteSession: function (id) {
574   - let that = this;
575   - this.$axios({
576   - method: 'get',
577   - url: '/zlm/' + that.mediaServerChoose + '/index/api/kick_session?id=' + id
578   - }).then(function (res) {
579   - that.getAllSession();
580   - that.$message({
581   - type: 'success',
582   - message: '删除成功!'
  586 + if (!!this.mediaServerChoose) {
  587 + this.$axios({
  588 + method: 'get',
  589 + url: '/zlm/' + this.mediaServerChoose + '/index/api/kick_session?id=' + id
  590 + }).then((res)=>{
  591 + this.getAllSession();
  592 + this.$message({
  593 + type: 'success',
  594 + message: '删除成功!'
  595 + });
583 596 });
584   - });
  597 + }
  598 +
585 599 },
586 600 getNameFromKey: function (key) {
587 601 let nameData = {
... ...
web_src/src/components/dialog/SyncChannelProgress.vue
... ... @@ -63,34 +63,39 @@ export default {
63 63 }
64 64  
65 65 if (res.data.data != null) {
66   - if (res.data.data.total == 0) {
67   - if (res.data.data.errorMsg !== null ){
68   - this.msg = res.data.data.errorMsg;
69   - this.syncStatus = "exception"
70   - }else {
71   - this.msg = `等待同步中`;
72   - this.timmer = setTimeout(this.getProgress, 300)
73   - }
74   - }else {
75   - if (res.data.data.total == res.data.data.current) {
76   - this.syncStatus = "success"
77   - this.percentage = 100;
78   - this.msg = '同步成功';
79   - }else {
  66 + if (res.data.syncIng) {
  67 + if (res.data.data.total == 0) {
80 68 if (res.data.data.errorMsg !== null ){
81 69 this.msg = res.data.data.errorMsg;
82 70 this.syncStatus = "exception"
83 71 }else {
84   - this.total = res.data.data.total;
85   - this.current = res.data.data.current;
86   - this.percentage = Math.floor(Number(res.data.data.current)/Number(res.data.data.total)* 10000)/100;
87   - this.msg = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
  72 + this.msg = `等待同步中`;
88 73 this.timmer = setTimeout(this.getProgress, 300)
89 74 }
  75 + }else {
  76 + if (res.data.data.total == res.data.data.current) {
  77 + this.syncStatus = "success"
  78 + this.percentage = 100;
  79 + this.msg = '同步成功';
  80 + }else {
  81 + if (res.data.data.errorMsg !== null ){
  82 + this.msg = res.data.data.errorMsg;
  83 + this.syncStatus = "exception"
  84 + }else {
  85 + this.total = res.data.data.total;
  86 + this.current = res.data.data.current;
  87 + this.percentage = Math.floor(Number(res.data.data.current)/Number(res.data.data.total)* 10000)/100;
  88 + this.msg = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
  89 + this.timmer = setTimeout(this.getProgress, 300)
  90 + }
  91 + }
90 92 }
  93 + }else {
  94 + this.syncStatus = "success"
  95 + this.percentage = 100;
  96 + this.msg = '同步成功';
91 97 }
92 98 }
93   -
94 99 }else {
95 100 if (this.syncFlag) {
96 101 this.syncStatus = "success"
... ...
web_src/src/components/dialog/addUser.vue 0 → 100644
  1 +<template>
  2 + <div id="addUser" v-loading="isLoging">
  3 + <el-dialog
  4 + title="添加用户"
  5 + width="40%"
  6 + top="2rem"
  7 + :close-on-click-modal="false"
  8 + :visible.sync="showDialog"
  9 + :destroy-on-close="true"
  10 + @close="close()"
  11 + >
  12 + <div id="shared" style="margin-right: 20px;">
  13 + <el-form ref="passwordForm" :rules="rules" status-icon label-width="80px">
  14 + <el-form-item label="用户名" prop="username">
  15 + <el-input v-model="username" autocomplete="off"></el-input>
  16 + </el-form-item>
  17 + <el-form-item label="用户类型" prop="roleId" >
  18 + <el-select v-model="roleId" placeholder="请选择" style="width: 100%">
  19 + <el-option
  20 + v-for="item in options"
  21 + :key="item.id"
  22 + :label="item.name"
  23 + :value="item.id">
  24 + </el-option>
  25 + </el-select>
  26 + </el-form-item>
  27 + <el-form-item label="密码" prop="password">
  28 + <el-input v-model="password" autocomplete="off"></el-input>
  29 + </el-form-item>
  30 + <el-form-item label="确认密码" prop="confirmPassword">
  31 + <el-input v-model="confirmPassword" autocomplete="off"></el-input>
  32 + </el-form-item>
  33 +
  34 + <el-form-item>
  35 + <div style="float: right;">
  36 + <el-button type="primary" @click="onSubmit">保存</el-button>
  37 + <el-button @click="close">取消</el-button>
  38 + </div>
  39 + </el-form-item>
  40 + </el-form>
  41 + </div>
  42 + </el-dialog>
  43 + </div>
  44 +</template>
  45 +
  46 +<script>
  47 +
  48 +export default {
  49 + name: "addUser",
  50 + props: {},
  51 + computed: {},
  52 + created() {
  53 + this.getAllRole();
  54 + },
  55 + data() {
  56 + let validatePass1 = (rule, value, callback) => {
  57 + if (value === '') {
  58 + callback(new Error('请输入新密码'));
  59 + } else {
  60 + if (this.confirmPassword !== '') {
  61 + this.$refs.passwordForm.validateField('confirmPassword');
  62 + }
  63 + callback();
  64 + }
  65 + };
  66 + let validatePass2 = (rule, value, callback) => {
  67 + if (this.confirmPassword === '') {
  68 + callback(new Error('请再次输入密码'));
  69 + } else if (this.confirmPassword !== this.password) {
  70 + callback(new Error('两次输入密码不一致!'));
  71 + } else {
  72 + callback();
  73 + }
  74 + };
  75 + return {
  76 + value:"",
  77 + options: [],
  78 + loading: false,
  79 + username: null,
  80 + password: null,
  81 + roleId: null,
  82 + confirmPassword: null,
  83 + listChangeCallback: null,
  84 + showDialog: false,
  85 + isLoging: false,
  86 + rules: {
  87 + newPassword: [{required: true, validator: validatePass1, trigger: "blur"}, {
  88 + pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,20}$/,
  89 + message: "密码长度在8-20位之间,由字母+数字+特殊字符组成",
  90 + },],
  91 + confirmPassword: [{required: true, validator: validatePass2, trigger: "blur"}],
  92 + },
  93 + };
  94 + },
  95 + methods: {
  96 + openDialog: function (callback) {
  97 + this.listChangeCallback = callback;
  98 + this.showDialog = true;
  99 + },
  100 + onSubmit: function () {
  101 + this.$axios({
  102 + method: 'post',
  103 + url: "/api/user/add",
  104 + params: {
  105 + username: this.username,
  106 + password: this.password,
  107 + roleId: this.roleId
  108 + }
  109 + }).then((res) => {
  110 + if (res.data.code === 0) {
  111 + this.$message({
  112 + showClose: true,
  113 + message: '添加成功',
  114 + type: 'success',
  115 +
  116 + });
  117 + this.showDialog = false;
  118 + this.listChangeCallback()
  119 +
  120 + } else {
  121 + this.$message({
  122 + showClose: true,
  123 + message: res.data.msg,
  124 + type: 'error'
  125 + });
  126 + }
  127 + }).catch((error) => {
  128 + console.error(error)
  129 + });
  130 + },
  131 + close: function () {
  132 + this.showDialog = false;
  133 + this.password = null;
  134 + this.confirmPassword = null;
  135 + this.username = null;
  136 + this.roleId = null;
  137 + },
  138 + getAllRole:function () {
  139 +
  140 + this.$axios({
  141 + method: 'get',
  142 + url: "/api/role/all"
  143 + }).then((res) => {
  144 + this.loading = true;
  145 + console.info(res)
  146 + res.data
  147 + console.info(res.data.code)
  148 + if (res.data.code === 0) {
  149 + console.info(res.data.data)
  150 + this.options=res.data.data
  151 +
  152 + }
  153 + }).catch((error) => {
  154 + console.error(error)
  155 + });
  156 + }
  157 + },
  158 +};
  159 +</script>
... ...
web_src/src/components/dialog/catalogEdit.vue
... ... @@ -49,11 +49,42 @@ export default {
49 49 props: ['platformId'],
50 50 created() {},
51 51 data() {
  52 + let checkId = (rule, value, callback) => {
  53 + console.log("checkId")
  54 + console.log(this.treeType)
  55 + console.log(rule)
  56 + console.log(value)
  57 + console.log(value.length)
  58 + console.log(this.level)
  59 + if (!value) {
  60 + return callback(new Error('编号不能为空'));
  61 + }
  62 + if (this.treeType === "BusinessGroup" && value.length !== 20) {
  63 + return callback(new Error('编号必须由20位数字组成'));
  64 + }
  65 + if (this.treeType === "CivilCode" && value.length <= 8 && value.length%2 !== 0) {
  66 + return callback(new Error('行政区划必须是八位以下的偶数个数字组成'));
  67 + }
  68 + if (this.treeType === "BusinessGroup") {
  69 + let catalogType = value.substring(10, 13);
  70 + console.log(catalogType)
  71 + // 216 为虚拟组织 215 为业务分组;目录第一级必须为业务分组, 业务分组下为虚拟组织,虚拟组织下可以有其他虚拟组织
  72 + if (this.level === 1 && catalogType !== "215") {
  73 + return callback(new Error('业务分组模式下第一层目录的编号11到13位必须为215'));
  74 + }
  75 + if (this.level > 1 && catalogType !== "216") {
  76 + return callback(new Error('业务分组模式下第一层以下目录的编号11到13位必须为216'));
  77 + }
  78 + }
  79 + callback();
  80 + }
52 81 return {
53 82 submitCallback: null,
54 83 showDialog: false,
55 84 isLoging: false,
56 85 isEdit: false,
  86 + treeType: null,
  87 + level: 0,
57 88 form: {
58 89 id: null,
59 90 name: null,
... ... @@ -62,12 +93,12 @@ export default {
62 93 },
63 94 rules: {
64 95 name: [{ required: true, message: "请输入名称", trigger: "blur" }],
65   - id: [{ required: true, message: "请输入ID", trigger: "blur" }]
  96 + id: [{ required: true, trigger: "blur",validator: checkId }]
66 97 },
67 98 };
68 99 },
69 100 methods: {
70   - openDialog: function (isEdit, id, name, parentId, callback) {
  101 + openDialog: function (isEdit, id, name, parentId, treeType, level, callback) {
71 102 console.log("parentId: " + parentId)
72 103 console.log(this.form)
73 104 this.isEdit = isEdit;
... ... @@ -77,6 +108,8 @@ export default {
77 108 this.form.parentId = parentId;
78 109 this.showDialog = true;
79 110 this.submitCallback = callback;
  111 + this.treeType = treeType;
  112 + this.level = level;
80 113 },
81 114 onSubmit: function () {
82 115 console.log("onSubmit");
... ...
web_src/src/components/dialog/changePasswordForAdmin.vue 0 → 100644
  1 +<template>
  2 + <div id="changePassword" v-loading="isLoging">
  3 + <el-dialog
  4 + title="修改密码"
  5 + width="40%"
  6 + top="2rem"
  7 + :close-on-click-modal="false"
  8 + :visible.sync="showDialog"
  9 + :destroy-on-close="true"
  10 + @close="close()"
  11 + >
  12 + <div id="shared" style="margin-right: 20px;">
  13 + <el-form ref="passwordForm" :rules="rules" status-icon label-width="80px">
  14 + <el-form-item label="新密码" prop="newPassword" >
  15 + <el-input v-model="newPassword" autocomplete="off"></el-input>
  16 + </el-form-item>
  17 + <el-form-item label="确认密码" prop="confirmPassword">
  18 + <el-input v-model="confirmPassword" autocomplete="off"></el-input>
  19 + </el-form-item>
  20 +
  21 + <el-form-item>
  22 + <div style="float: right;">
  23 + <el-button type="primary" @click="onSubmit">保存</el-button>
  24 + <el-button @click="close">取消</el-button>
  25 + </div>
  26 + </el-form-item>
  27 + </el-form>
  28 + </div>
  29 + </el-dialog>
  30 + </div>
  31 +</template>
  32 +
  33 +<script>
  34 +export default {
  35 + name: "changePasswordForAdmin",
  36 + props: {},
  37 + computed: {},
  38 + created() {},
  39 + data() {
  40 + let validatePass1 = (rule, value, callback) => {
  41 + if (value === '') {
  42 + callback(new Error('请输入新密码'));
  43 + } else {
  44 + if (this.confirmPassword !== '') {
  45 + this.$refs.passwordForm.validateField('confirmPassword');
  46 + }
  47 + callback();
  48 + }
  49 + };
  50 + let validatePass2 = (rule, value, callback) => {
  51 + if (this.confirmPassword === '') {
  52 + callback(new Error('请再次输入密码'));
  53 + } else if (this.confirmPassword !== this.newPassword) {
  54 + callback(new Error('两次输入密码不一致!'));
  55 + } else {
  56 + callback();
  57 + }
  58 + };
  59 + return {
  60 + newPassword: null,
  61 + confirmPassword: null,
  62 + userId: null,
  63 + showDialog: false,
  64 + isLoging: false,
  65 + listChangeCallback: null,
  66 + form: {},
  67 + rules: {
  68 + newPassword: [{ required: true, validator: validatePass1, trigger: "blur" }, {
  69 + pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,20}$/,
  70 + message: "密码长度在8-20位之间,由字母+数字+特殊字符组成",
  71 + },],
  72 + confirmPassword: [{ required: true, validator: validatePass2, trigger: "blur" }],
  73 + },
  74 + };
  75 + },
  76 + methods: {
  77 + openDialog: function (row, callback) {
  78 + console.log(row)
  79 + this.showDialog = true;
  80 + this.listChangeCallback = callback;
  81 + if (row != null) {
  82 + this.form = row;
  83 + }
  84 + },
  85 + onSubmit: function () {
  86 + this.$axios({
  87 + method: 'post',
  88 + url:"/api/user/changePasswordForAdmin",
  89 + params: {
  90 + password: this.newPassword,
  91 + userId: this.form.id,
  92 + }
  93 + }).then((res)=> {
  94 + if (res.data === "success"){
  95 + this.$message({
  96 + showClose: true,
  97 + message: '修改成功',
  98 + type: 'success'
  99 + });
  100 + this.showDialog = false;
  101 + }else {
  102 + this.$message({
  103 + showClose: true,
  104 + message: '修改密码失败,是否已登录(接口鉴权关闭无法修改密码)',
  105 + type: 'error'
  106 + });
  107 + }
  108 + }).catch((error)=> {
  109 + console.error(error)
  110 + });
  111 + },
  112 + close: function () {
  113 + this.showDialog = false;
  114 + this.newPassword = null;
  115 + this.confirmPassword = null;
  116 + this.userId=null;
  117 + this.adminId=null;
  118 + },
  119 + },
  120 +};
  121 +</script>
... ...
web_src/src/components/dialog/changePushKey.vue 0 → 100644
  1 +<template>
  2 + <div id="changepushKey" v-loading="isLoging">
  3 + <el-dialog
  4 + title="修改pushKey"
  5 + width="42%"
  6 + top="2rem"
  7 + :close-on-click-modal="false"
  8 + :visible.sync="showDialog"
  9 + :destroy-on-close="true"
  10 + @close="close()"
  11 + >
  12 + <div id="shared" style="margin-right: 18px;">
  13 + <el-form ref="pushKeyForm" :rules="rules" status-icon label-width="86px">
  14 + <el-form-item label="新pushKey" prop="newPushKey" >
  15 + <el-input v-model="newPushKey" autocomplete="off"></el-input>
  16 + </el-form-item>
  17 + <el-form-item>
  18 + <div style="float: right;">
  19 + <el-button type="primary" @click="onSubmit">保存</el-button>
  20 + <el-button @click="close">取消</el-button>
  21 + </div>
  22 + </el-form-item>
  23 + </el-form>
  24 + </div>
  25 + </el-dialog>
  26 + </div>
  27 +</template>
  28 +
  29 +<script>
  30 +export default {
  31 + name: "changePushKey",
  32 + props: {},
  33 + computed: {},
  34 + created() {},
  35 + data() {
  36 + let validatePass1 = (rule, value, callback) => {
  37 + if (value === '') {
  38 + callback(new Error('请输入新pushKey'));
  39 + } else {
  40 + callback();
  41 + }
  42 + };
  43 + return {
  44 + newPushKey: null,
  45 + confirmpushKey: null,
  46 + userId: null,
  47 + showDialog: false,
  48 + isLoging: false,
  49 + listChangeCallback: null,
  50 + form: {},
  51 + rules: {
  52 + newpushKey: [{ required: true, validator: validatePass1, trigger: "blur" }],
  53 + },
  54 + };
  55 + },
  56 + methods: {
  57 + openDialog: function (row, callback) {
  58 + console.log(row)
  59 + this.showDialog = true;
  60 + this.listChangeCallback = callback;
  61 + if (row != null) {
  62 + this.form = row;
  63 + }
  64 + },
  65 + onSubmit: function () {
  66 + this.$axios({
  67 + method: 'post',
  68 + url:"/api/user/changePushKey",
  69 + params: {
  70 + pushKey: this.newPushKey,
  71 + userId: this.form.id,
  72 + }
  73 + }).then((res)=> {
  74 + console.log(res.data)
  75 + if (res.data.msg === "success"){
  76 + this.$message({
  77 + showClose: true,
  78 + message: '修改成功',
  79 + type: 'success'
  80 + });
  81 + this.showDialog = false;
  82 + this.listChangeCallback();
  83 + }else {
  84 + this.$message({
  85 + showClose: true,
  86 + message: '修改pushKey失败,是否已登录(接口鉴权关闭无法修改pushKey)',
  87 + type: 'error'
  88 + });
  89 + }
  90 + }).catch((error)=> {
  91 + console.error(error)
  92 + });
  93 + },
  94 + close: function () {
  95 + this.showDialog = false;
  96 + this.newpushKey = null;
  97 + this.userId=null;
  98 + this.adminId=null;
  99 + },
  100 + },
  101 +};
  102 +</script>
... ...
web_src/src/components/dialog/chooseChannel.vue
... ... @@ -8,7 +8,7 @@
8 8 <el-tab-pane label="目录结构" name="catalog">
9 9 <el-container>
10 10 <el-main v-bind:style="{backgroundColor: '#FFF', maxHeight: winHeight + 'px'}">
11   - <chooseChannelForCatalog ref="chooseChannelForCatalog" :platformId=platformId :platformName=platformName :defaultCatalogId=defaultCatalogId :catalogIdChange="catalogIdChange" ></chooseChannelForCatalog>
  11 + <chooseChannelForCatalog ref="chooseChannelForCatalog" :platformId=platformId :platformName=platformName :defaultCatalogId=defaultCatalogId :catalogIdChange="catalogIdChange" :treeType=treeType ></chooseChannelForCatalog>
12 12 </el-main>
13 13 </el-container>
14 14 </el-tab-pane>
... ... @@ -66,18 +66,20 @@ export default {
66 66 platformName: "",
67 67 defaultCatalogId: "",
68 68 showDialog: false,
  69 + treeType: null,
69 70 chooseData: {},
70 71 winHeight: window.innerHeight - 250,
71 72  
72 73 };
73 74 },
74 75 methods: {
75   - openDialog(platformId, platformName, defaultCatalogId, closeCallback) {
  76 + openDialog(platformId, platformName, defaultCatalogId, treeType, closeCallback) {
76 77 this.platformId = platformId
77 78 this.platformName = platformName
78 79 this.defaultCatalogId = defaultCatalogId
79 80 this.showDialog = true
80 81 this.closeCallback = closeCallback
  82 + this.treeType = treeType
81 83 },
82 84 tabClick (tab, event){
83 85  
... ...
web_src/src/components/dialog/chooseChannelForCatalog.vue
... ... @@ -38,7 +38,7 @@
38 38 import catalogEdit from './catalogEdit.vue'
39 39 export default {
40 40 name: 'chooseChannelForCatalog',
41   - props: ['platformId', 'platformName', 'defaultCatalogId', 'catalogIdChange'],
  41 + props: ['platformId', 'platformName', 'defaultCatalogId', 'catalogIdChange', 'treeType'],
42 42 created() {
43 43 this.chooseId = this.defaultCatalogId;
44 44 this.defaultCatalogIdSign = this.defaultCatalogId;
... ... @@ -102,8 +102,9 @@ export default {
102 102 },
103 103 addCatalog: function (parentId, node){
104 104 let that = this;
  105 + console.log(this.treeType)
105 106 // 打开添加弹窗
106   - that.$refs.catalogEdit.openDialog(false, null, null, parentId, ()=>{
  107 + that.$refs.catalogEdit.openDialog(false, null, null, parentId, this.treeType, node.level, ()=>{
107 108 node.loaded = false
108 109 node.expand();
109 110 });
... ...
web_src/src/components/dialog/chooseChannelForStream.vue
... ... @@ -174,7 +174,6 @@ export default {
174 174 page: that.currentPage,
175 175 count: that.count,
176 176 query: that.searchSrt,
177   - pushing: that.pushing,
178 177 platformId: that.platformId,
179 178 catalogId: that.catalogId,
180 179 mediaServerId: that.mediaServerId
... ...
web_src/src/components/dialog/devicePlayer.vue
... ... @@ -388,7 +388,7 @@ export default {
388 388 url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId
389 389 }).then(function (res) {
390 390 that.tracksLoading = false;
391   - if (res.data.code == 0 && res.data.online) {
  391 + if (res.data.code == 0 && res.data.tracks) {
392 392 that.tracks = res.data.tracks;
393 393 }else{
394 394 that.tracksNotLoaded = true;
... ...
web_src/src/components/dialog/platformEdit.vue
... ... @@ -78,6 +78,12 @@
78 78 <el-option label="8" value="8"></el-option>
79 79 </el-select>
80 80 </el-form-item>
  81 + <el-form-item label="目录结构" prop="treeType" >
  82 + <el-select v-model="platform.treeType" style="width: 100%" >
  83 + <el-option key="WGS84" label="行政区划" value="CivilCode"></el-option>
  84 + <el-option key="GCJ02" label="业务分组" value="BusinessGroup"></el-option>
  85 + </el-select>
  86 + </el-form-item>
81 87 <el-form-item label="字符集" prop="characterSet">
82 88 <el-select
83 89 v-model="platform.characterSet"
... ... @@ -91,7 +97,6 @@
91 97 <el-form-item label="其他选项">
92 98 <el-checkbox label="启用" v-model="platform.enable" @change="checkExpires"></el-checkbox>
93 99 <el-checkbox label="云台控制" v-model="platform.ptz"></el-checkbox>
94   - <el-checkbox label="共享所有直播流" v-model="platform.shareAllLiveStream"></el-checkbox>
95 100 <el-checkbox label="拉起离线推流" v-model="platform.startOfflinePush"></el-checkbox>
96 101 </el-form-item>
97 102 <el-form-item>
... ... @@ -153,10 +158,10 @@ export default {
153 158 keepTimeout: 60,
154 159 transport: "UDP",
155 160 characterSet: "GB2312",
156   - shareAllLiveStream: false,
157 161 startOfflinePush: false,
158 162 catalogGroup: 1,
159 163 administrativeDivision: null,
  164 + treeType: "BusinessGroup",
160 165 },
161 166 rules: {
162 167 name: [{ required: true, message: "请输入平台名称", trigger: "blur" }],
... ... @@ -194,6 +199,7 @@ export default {
194 199 that.platform.devicePort = res.data.devicePort;
195 200 that.platform.username = res.data.username;
196 201 that.platform.password = res.data.password;
  202 + that.platform.treeType = "BusinessGroup";
197 203 that.platform.administrativeDivision = res.data.username.substr(0, 6);
198 204 }).catch(function (error) {
199 205 console.log(error);
... ... @@ -217,11 +223,11 @@ export default {
217 223 this.platform.keepTimeout = platform.keepTimeout;
218 224 this.platform.transport = platform.transport;
219 225 this.platform.characterSet = platform.characterSet;
220   - this.platform.shareAllLiveStream = platform.shareAllLiveStream;
221 226 this.platform.catalogId = platform.catalogId;
222 227 this.platform.startOfflinePush = platform.startOfflinePush;
223 228 this.platform.catalogGroup = platform.catalogGroup;
224 229 this.platform.administrativeDivision = platform.administrativeDivision;
  230 + this.platform.treeType = platform.treeType;
225 231 this.onSubmit_text = "保存";
226 232 this.saveUrl = "/api/platform/save";
227 233 }
... ... @@ -242,32 +248,49 @@ export default {
242 248  
243 249 },
244 250 onSubmit: function () {
  251 + if (this.onSubmit_text === "保存") {
  252 + this.$confirm("修改目录结构会导致关联目录与通道数据被清空", '提示', {
  253 + dangerouslyUseHTMLString: true,
  254 + confirmButtonText: '确定',
  255 + cancelButtonText: '取消',
  256 + center: true,
  257 + type: 'warning'
  258 + }).then(() => {
  259 + this.saveForm()
  260 + }).catch(() => {
  261 +
  262 + });
  263 + }else {
  264 + this.saveForm()
  265 + }
  266 + },
  267 + saveForm: function (){
245 268 var that = this;
246 269 that.$axios({
247 270 method: 'post',
248 271 url: this.saveUrl,
249 272 data: that.platform
250 273 }).then(function (res) {
251   - if (res.data.code === 0) {
252   - that.$message({
253   - showClose: true,
254   - message: "保存成功",
255   - type: "success",
256   - });
257   - that.showDialog = false;
258   - if (that.listChangeCallback != null) {
259   - that.listChangeCallback();
260   - }
261   - }else {
262   - that.$message({
263   - showClose: true,
264   - message: res.data.msg,
265   - type: "error",
266   - });
  274 + if (res.data.code === 0) {
  275 + that.$message({
  276 + showClose: true,
  277 + message: "保存成功",
  278 + type: "success",
  279 + });
  280 + that.showDialog = false;
  281 + if (that.listChangeCallback != null) {
  282 + that.listChangeCallback();
267 283 }
268   - }).catch(function (error) {
269   - console.log(error);
270   - });
  284 + }else {
  285 + that.$message({
  286 + showClose: true,
  287 + message: res.data.msg,
  288 + type: "error",
  289 + });
  290 + }
  291 + }).catch(function (error) {
  292 + console.log(error);
  293 + });
271 294 },
272 295 close: function () {
273 296 this.showDialog = false;
... ... @@ -293,7 +316,7 @@ export default {
293 316 keepTimeout: 60,
294 317 transport: "UDP",
295 318 characterSet: "GB2312",
296   - shareAllLiveStream: false,
  319 + treeType: "BusinessGroup",
297 320 startOfflinePush: false,
298 321 catalogGroup: 1,
299 322 }
... ...
web_src/src/components/dialog/addStreamTOGB.vue renamed to web_src/src/components/dialog/pushStreamEdit.vue
... ... @@ -15,20 +15,25 @@
15 15 <el-input v-model="proxyParam.name" clearable></el-input>
16 16 </el-form-item>
17 17 <el-form-item label="流应用名" prop="app">
18   - <el-input v-model="proxyParam.app" clearable :disabled="true"></el-input>
  18 + <el-input v-model="proxyParam.app" clearable :disabled="edit"></el-input>
19 19 </el-form-item>
20 20 <el-form-item label="流ID" prop="stream">
21   - <el-input v-model="proxyParam.stream" clearable :disabled="true"></el-input>
  21 + <el-input v-model="proxyParam.stream" clearable :disabled="edit"></el-input>
22 22 </el-form-item>
23 23 <el-form-item label="国标编码" prop="gbId">
24 24 <el-input v-model="proxyParam.gbId" placeholder="设置国标编码可推送到国标" clearable></el-input>
25 25 </el-form-item>
  26 + <el-form-item label="经度" prop="longitude" v-if="proxyParam.gbId">
  27 + <el-input v-model="proxyParam.longitude" placeholder="经度" clearable></el-input>
  28 + </el-form-item>
  29 + <el-form-item label="纬度" prop="latitude" v-if="proxyParam.gbId">
  30 + <el-input v-model="proxyParam.latitude" placeholder="经度" clearable></el-input>
  31 + </el-form-item>
26 32 <el-form-item>
27 33 <div style="float: right;">
28 34 <el-button type="primary" @click="onSubmit">保存</el-button>
29 35 <el-button @click="close">取消</el-button>
30 36 </div>
31   -
32 37 </el-form-item>
33 38 </el-form>
34 39 </div>
... ... @@ -38,7 +43,7 @@
38 43  
39 44 <script>
40 45 export default {
41   - name: "streamProxyEdit",
  46 + name: "pushStreamEdit",
42 47 props: {},
43 48 computed: {},
44 49 created() {},
... ... @@ -63,13 +68,15 @@ export default {
63 68 listChangeCallback: null,
64 69 showDialog: false,
65 70 isLoging: false,
  71 + edit: false,
66 72 proxyParam: {
67 73 name: null,
68 74 app: null,
69 75 stream: null,
70 76 gbId: null,
  77 + longitude: null,
  78 + latitude: null,
71 79 },
72   -
73 80 rules: {
74 81 name: [{ required: true, message: "请输入名称", trigger: "blur" }],
75 82 app: [{ required: true, message: "请输入应用名", trigger: "blur" }],
... ... @@ -84,30 +91,63 @@ export default {
84 91 this.listChangeCallback = callback;
85 92 if (proxyParam != null) {
86 93 this.proxyParam = proxyParam;
87   - }
  94 + this.edit = true
  95 + }else{
  96 + this.proxyParam= {
  97 + name: null,
  98 + app: null,
  99 + stream: null,
  100 + gbId: null,
  101 + longitude: null,
  102 + latitude: null,
  103 + }
  104 + this.edit = false
  105 + }
88 106 },
89 107 onSubmit: function () {
90 108 console.log("onSubmit");
91   - var that = this;
92   - that.$axios({
93   - method:"post",
94   - url:`/api/push/save_to_gb`,
95   - data: that.proxyParam
96   - }).then(function (res) {
  109 + if (this.edit) {
  110 + this.$axios({
  111 + method:"post",
  112 + url:`/api/push/save_to_gb`,
  113 + data: this.proxyParam
  114 + }).then( (res) => {
97 115 if (res.data == "success") {
98   - that.$message({
  116 + this.$message({
99 117 showClose: true,
100 118 message: "保存成功",
101 119 type: "success",
102 120 });
103   - that.showDialog = false;
104   - if (that.listChangeCallback != null) {
105   - that.listChangeCallback();
  121 + this.showDialog = false;
  122 + if (this.listChangeCallback != null) {
  123 + this.listChangeCallback();
106 124 }
107 125 }
108   - }).catch(function (error) {
  126 + }).catch((error)=> {
109 127 console.log(error);
110   - });
  128 + });
  129 + }else {
  130 + this.$axios({
  131 + method:"post",
  132 + url:`/api/push/add`,
  133 + data: this.proxyParam
  134 + }).then( (res) => {
  135 + if (res.data.code === 0) {
  136 + this.$message({
  137 + showClose: true,
  138 + message: "保存成功",
  139 + type: "success",
  140 + });
  141 + this.showDialog = false;
  142 + if (this.listChangeCallback != null) {
  143 + this.listChangeCallback();
  144 + }
  145 + }
  146 + }).catch((error)=> {
  147 + console.log(error);
  148 + });
  149 + }
  150 +
111 151 },
112 152 close: function () {
113 153 console.log("关闭加入GB");
... ... @@ -131,6 +171,9 @@ export default {
131 171 if (this.platform.enable && this.platform.expires == "0") {
132 172 this.platform.expires = "300";
133 173 }
  174 + },
  175 + handleNodeClick: function (node){
  176 +
134 177 }
135 178 },
136 179 };
... ...
web_src/src/layout/UiHeader.vue
... ... @@ -13,6 +13,7 @@
13 13 <el-menu-item index="/cloudRecord">云端录像</el-menu-item>
14 14 <el-menu-item index="/mediaServerManger">节点管理</el-menu-item>
15 15 <el-menu-item index="/parentPlatformList/15/1">国标级联</el-menu-item>
  16 + <el-menu-item v-if="editUser" index="/userManager">用户管理</el-menu-item>
16 17  
17 18 <!-- <el-submenu index="/setting">-->
18 19 <!-- <template slot="title">系统设置</template>-->
... ... @@ -47,9 +48,11 @@ export default {
47 48 alarmNotify: false,
48 49 sseSource: null,
49 50 activeIndex: this.$route.path,
  51 + editUser: this.$cookies.get("session").roleId==1
50 52 };
51 53 },
52 54 created() {
  55 + console.log(this.$cookies.get("session"))
53 56 if (this.$route.path.startsWith("/channelList")) {
54 57 this.activeIndex = "/deviceList"
55 58 }
... ...
web_src/src/router/index.js
... ... @@ -17,6 +17,7 @@ import sip from &#39;../components/setting/Sip.vue&#39;
17 17 import media from '../components/setting/Media.vue'
18 18 import live from '../components/live.vue'
19 19 import deviceTree from '../components/common/DeviceTree.vue'
  20 +import userManager from '../components/UserManager.vue'
20 21  
21 22 import wasmPlayer from '../components/common/jessibuca.vue'
22 23 import rtcPlayer from '../components/dialog/rtcPlayer.vue'
... ... @@ -103,6 +104,11 @@ export default new VueRouter({
103 104 name: 'map',
104 105 component: map,
105 106 },
  107 + {
  108 + path: '/userManager',
  109 + name: 'userManager',
  110 + component: userManager,
  111 + }
106 112 ]
107 113 },
108 114 {
... ...
web_src/static/file/推流通道导入.zip
No preview for this file type
web_src/static/js/jessibuca/decoder.wasm 100755 → 100644
No preview for this file type