diff --git a/README.md b/README.md index 1c5f868ba..1c131bff4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![logo](https://raw.githubusercontent.com/648540858/wvp-GB28181-pro/wvp-28181-2.0/web_src/static/logo.png) -# 开箱即用的的28181协议视频平台 +# 开箱即用的28181协议视频平台 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit) [![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是一个基于GB28181-2016标准实现的开箱即用的网 旨在打造一个易配置,易使用,便于维护的28181国标信令系统, 依托优秀的开源流媒体服务框架ZLMediaKit, 实现一个完整易用GB28181平台. # 部署文档 -[https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki) +[doc.wvp-pro.cn](https://doc.wvp-pro.cn) # gitee同步仓库 https://gitee.com/pan648540858/wvp-GB28181-pro.git diff --git a/bootstrap.sh b/bootstrap.sh old mode 100755 new mode 100644 diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 000000000..635a4a530 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,102 @@ +# 介绍 + +> 开箱即用的28181协议视频平台 + +# 概述 +- WVP-PRO基于GB/T 28181-2016标准实现的流媒体平台,依托优秀的开源流媒体服务[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit),提供完善丰富的功能。 +- GB/T 28181-2016 中文标准名称是《公共安全视频监控联网系统信息传输、交换、控制技术要求》是监控领域的国家标准。大量应用于政府视频平台。 +- 通过28181协议你可以将IPC摄像头接入平台,可以观看也可以使用28181/rtsp/rtmp/flv等协议将视频流分发到其他平台。 + +# 特性 +- 实现标准的28181信令,兼容常见的品牌设备,比如海康、大华、宇视等品牌的IPC、NVR以及平台。 +- 支持将国标设备级联到其他国标平台,也支持将不支持国标的设备的图像或者直播推送到其他国标平台 +- 前端完善,自带完整前端页面,无需二次开发可直接部署使用。 +- 完全开源,且使用MIT许可协议。保留版权的情况下可以用于商业项目。 +- 支持多流媒体节点负载均衡。 + +# 我们实现了哪些国标功能 +**作为上级平台** +- [X] 注册 +- [X] 注销 +- [X] 实时视音频点播 +- [X] 设备控制 + - [X] 云台控制 + - [ ] 远程启动 + - [ ] 录像控制 + - [ ] 报警布防/撤防 + - [ ] 报警复位 + - [X] 强制关键帧 + - [ ] 拉框放大 + - [ ] 拉框缩小 + - [ ] 看守位控制 + - [ ] 设备配置 +- [X] 报警事件通知和分发 +- [X] 设备目录订阅 +- [X] 网络设备信息查询 + - [X] 设备目录查询 + - [X] 设备状态查询 + - [ ] 设备配置查询 + - [ ] 设备预置位查询 +- [X] 状态信息报送 +- [X] 设备视音频文件检索 +- [X] 历史视音频的回放 + - [X] 播放 + - [X] 暂停 + - [X] 进/退 + - [X] 停止 +- [X] 视音频文件下载 +- [X] 校时 +- [X] 订阅和通知 + - [X] 事件订阅 + - [X] 移动设备位置订阅 + - [X] 报警订阅 + - [X] 目录订阅 +- [ ] 语音广播 +- [ ] 语音对讲 + +**作为下级平台** +- [X] 注册 +- [X] 注销 +- [X] 实时视音频点播 +- [ ] 设备控制 + - [ ] 云台控制 + - [ ] 远程启动 + - [ ] 录像控制 + - [ ] 报警布防/撤防 + - [ ] 报警复位 + - [ ] 强制关键帧 + - [ ] 拉框放大 + - [ ] 拉框缩小 + - [ ] 看守位控制 + - [ ] 设备配置 +- [ ] 报警事件通知和分发 +- [X] 设备目录订阅 +- [X] 网络设备信息查询 + - [X] 设备目录查询 + - [X] 设备状态查询 + - [ ] 设备配置查询 + - [ ] 设备预置位查询 +- [X] 状态信息报送 +- [X] 设备视音频文件检索 +- [X] 历史视音频的回放 + - [X] 播放 + - [x] 暂停 + - [x] 进/退 + - [x] 停止 +- [ ] 视音频文件下载 +- [ ] ~~校时~~ +- [X] 订阅和通知 + - [X] 事件订阅 + - [X] 移动设备位置订阅 + - [ ] 报警订阅 + - [X] 目录订阅 +- [ ] 语音广播 +- [ ] 语音对讲 + + + + +# 社区 +代码目前托管在GitHub和Gitee,Gitee目前作为加速仓库使用,不接受issue。 +GitHub: [https://github.com/648540858/wvp-GB28181-pro](https://github.com/648540858/wvp-GB28181-pro) +Gitee: [https://gitee.com/pan648540858/wvp-GB28181-pro](https://gitee.com/pan648540858/wvp-GB28181-pro) \ No newline at end of file diff --git a/doc/_content/ability/_media/cascade1.png b/doc/_content/ability/_media/cascade1.png new file mode 100644 index 000000000..9ba8280b7 Binary files /dev/null and b/doc/_content/ability/_media/cascade1.png differ diff --git a/doc/_content/ability/_media/cascade2.png b/doc/_content/ability/_media/cascade2.png new file mode 100644 index 000000000..4dd62cf26 Binary files /dev/null and b/doc/_content/ability/_media/cascade2.png differ diff --git a/doc/_content/ability/_media/cascade3.png b/doc/_content/ability/_media/cascade3.png new file mode 100644 index 000000000..f95f5d5ac Binary files /dev/null and b/doc/_content/ability/_media/cascade3.png differ diff --git a/doc/_content/ability/_media/cascade4.png b/doc/_content/ability/_media/cascade4.png new file mode 100644 index 000000000..9db85b55e Binary files /dev/null and b/doc/_content/ability/_media/cascade4.png differ diff --git a/doc/_content/ability/_media/img.png b/doc/_content/ability/_media/img.png new file mode 100644 index 000000000..6a0c550d9 Binary files /dev/null and b/doc/_content/ability/_media/img.png differ diff --git a/doc/_content/ability/_media/img_1.png b/doc/_content/ability/_media/img_1.png new file mode 100644 index 000000000..31995c3cb Binary files /dev/null and b/doc/_content/ability/_media/img_1.png differ diff --git a/doc/_content/ability/_media/img_10.png b/doc/_content/ability/_media/img_10.png new file mode 100644 index 000000000..030502d0b Binary files /dev/null and b/doc/_content/ability/_media/img_10.png differ diff --git a/doc/_content/ability/_media/img_11.png b/doc/_content/ability/_media/img_11.png new file mode 100644 index 000000000..cb0f3d519 Binary files /dev/null and b/doc/_content/ability/_media/img_11.png differ diff --git a/doc/_content/ability/_media/img_12.png b/doc/_content/ability/_media/img_12.png new file mode 100644 index 000000000..d6fe87770 Binary files /dev/null and b/doc/_content/ability/_media/img_12.png differ diff --git a/doc/_content/ability/_media/img_13.png b/doc/_content/ability/_media/img_13.png new file mode 100644 index 000000000..6be1128f2 Binary files /dev/null and b/doc/_content/ability/_media/img_13.png differ diff --git a/doc/_content/ability/_media/img_14.png b/doc/_content/ability/_media/img_14.png new file mode 100644 index 000000000..247120453 Binary files /dev/null and b/doc/_content/ability/_media/img_14.png differ diff --git a/doc/_content/ability/_media/img_15.png b/doc/_content/ability/_media/img_15.png new file mode 100644 index 000000000..f167811d3 Binary files /dev/null and b/doc/_content/ability/_media/img_15.png differ diff --git a/doc/_content/ability/_media/img_16.png b/doc/_content/ability/_media/img_16.png new file mode 100644 index 000000000..f7ce9e7aa Binary files /dev/null and b/doc/_content/ability/_media/img_16.png differ diff --git a/doc/_content/ability/_media/img_17.png b/doc/_content/ability/_media/img_17.png new file mode 100644 index 000000000..02b303e36 Binary files /dev/null and b/doc/_content/ability/_media/img_17.png differ diff --git a/doc/_content/ability/_media/img_18.png b/doc/_content/ability/_media/img_18.png new file mode 100644 index 000000000..5ca4faf9d Binary files /dev/null and b/doc/_content/ability/_media/img_18.png differ diff --git a/doc/_content/ability/_media/img_2.png b/doc/_content/ability/_media/img_2.png new file mode 100644 index 000000000..f9f2f55f9 Binary files /dev/null and b/doc/_content/ability/_media/img_2.png differ diff --git a/doc/_content/ability/_media/img_3.png b/doc/_content/ability/_media/img_3.png new file mode 100644 index 000000000..efe688cfd Binary files /dev/null and b/doc/_content/ability/_media/img_3.png differ diff --git a/doc/_content/ability/_media/img_4.png b/doc/_content/ability/_media/img_4.png new file mode 100644 index 000000000..f548cec77 Binary files /dev/null and b/doc/_content/ability/_media/img_4.png differ diff --git a/doc/_content/ability/_media/img_5.png b/doc/_content/ability/_media/img_5.png new file mode 100644 index 000000000..6959c7800 Binary files /dev/null and b/doc/_content/ability/_media/img_5.png differ diff --git a/doc/_content/ability/_media/img_6.png b/doc/_content/ability/_media/img_6.png new file mode 100644 index 000000000..04c42bc14 Binary files /dev/null and b/doc/_content/ability/_media/img_6.png differ diff --git a/doc/_content/ability/_media/img_7.png b/doc/_content/ability/_media/img_7.png new file mode 100644 index 000000000..1a8edbf02 Binary files /dev/null and b/doc/_content/ability/_media/img_7.png differ diff --git a/doc/_content/ability/_media/img_8.png b/doc/_content/ability/_media/img_8.png new file mode 100644 index 000000000..02fa66f12 Binary files /dev/null and b/doc/_content/ability/_media/img_8.png differ diff --git a/doc/_content/ability/_media/img_9.png b/doc/_content/ability/_media/img_9.png new file mode 100644 index 000000000..708e901a3 Binary files /dev/null and b/doc/_content/ability/_media/img_9.png differ diff --git a/doc/_content/ability/auto_play.md b/doc/_content/ability/auto_play.md new file mode 100644 index 000000000..5259e0b52 --- /dev/null +++ b/doc/_content/ability/auto_play.md @@ -0,0 +1,2 @@ + +# 自动点播 diff --git a/doc/_content/ability/cascade.md b/doc/_content/ability/cascade.md new file mode 100644 index 000000000..7942bda4b --- /dev/null +++ b/doc/_content/ability/cascade.md @@ -0,0 +1,34 @@ + +# 国标级联的使用 +国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。 +## 1 接入平台 +### 1.1 wvp-pro +#### 1.1.1 wvp-pro管理页面点击添加 + ![cascade1](_media/cascade1.png) +#### 1.1.2 填入wvp-pro上级平台信息 + ![cascade1](_media/img_4.png) + ![cascade1](_media/img_5.png) +#### 1.1.3 编辑wvp-pro上级设备信息,开启订阅 + ![cascade1](_media/img_6.png) +### 1.2 大华平台 +### 1.3 海康平台 +### 1.4 liveGBS +#### 1.4.1. wvp-pro管理页面点击添加 + ![添加](_media/cascade1.png) +#### 1.4.2. 填入liveGBS平台信息 + ![填入liveGBS平台信息1](_media/cascade2.png) + ![填入liveGBS平台信息2](_media/cascade3.png) +#### 1.4.3. 编辑liveGBS设备信息,开启目录订阅 + ![cascade1](_media/cascade4.png) +#### 1.4.4. 编辑liveGBS设备信息,开启GPS订阅 + ![cascade1](_media/img_7.png) + +## 2 添加目录与通道 +1. 级联平台添加目录信息 + ![cascade1](_media/img_1.png) +2. 为目录添加通道 + ![cascade1](_media/img_2.png) +3. 设置默认流目录 +如果需要后续自动生成的流信息都在某一个节点下,可以在对应节点右键设置为默认 + ![cascade1](_media/img_3.png) + diff --git a/doc/_content/ability/cascade2.md b/doc/_content/ability/cascade2.md new file mode 100644 index 000000000..7bd6c7929 --- /dev/null +++ b/doc/_content/ability/cascade2.md @@ -0,0 +1,18 @@ + +# 国标级联的使用 +国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。 +## 添加上级平台 +在国标级联页面点击“添加”按钮,以推送到上级WVP为例子,参看[接入设备](./_content/ability/device.md) +![cascade17](_media/img_17.png) +点击保存可以在上级的国标通道列表看到新增加的设备; +国标级联列表出现了级联的这个平台;同时状态显示为在线,如果状态为离线那么可能是你的服务信息配置有误或者网络不通。 +订阅信息列有三个图标,表示上级开启订阅,从左到右依次是:报警订阅,目录订阅,移动位置订阅。 +## 推送通道 +点击你要推送的平台的“选择通道”按钮。 +![cascade18](_media/img_18.png) +- **页面结构** + - 左侧为目录结构 + 选择未分配,则右侧显示待分配的通道,可以点击“添加按钮”,在弹窗中选择要放置的位置,保存后即可添加通道成功 + 选择其他的目录可以看到已经分配在这个目录下的通道,可以对其进行删除后重新在未分配中去分配。 + - 右侧为数据展示以及操作 + 国标通道栏内为来自其他国标设备/平台的通道;直播流通道为来自推流/拉流代理的通道。 \ No newline at end of file diff --git a/doc/_content/ability/cloud_record.md b/doc/_content/ability/cloud_record.md new file mode 100644 index 000000000..4ac4f8f12 --- /dev/null +++ b/doc/_content/ability/cloud_record.md @@ -0,0 +1,8 @@ + +# 云端录像 +云端录像是对录制在zlm服务下的录像文件的管理,录像的文件路径默认在ZLM/www/record下,使用云端录像功能必须部署wvp-pro-assist,主要通过调用wvp-pro-assist的接口完成各种功能。 +如果你需要24小时的录像,目前有一个这种方案,可以参考[7*24不间断录像](./_content/ability/continuous_recording.md)。 +1. 云段录像支持录像文件的查看,播放(可能因为编码的原因导致无法播放); +2. 支持录像的下载; +3. 支持录像的合并下载; +功能没有太多特殊的地方就不一一介绍了,大家自行体验吧。 diff --git a/doc/_content/ability/continuous_recording.md b/doc/_content/ability/continuous_recording.md new file mode 100644 index 000000000..516830fde --- /dev/null +++ b/doc/_content/ability/continuous_recording.md @@ -0,0 +1,14 @@ + +# 7*24不间断录像 + +目前如果要实现不间断录像如果只是关闭无人观看停止推流是不够的,设备可能经历断网,重启,都会导致录像的中断,目前给大家提供一种可用的临时方案。 + +**原理:** wvp支持使用流地址自动点播,即你拿到一个流地址直接去播放,即使设备处于未点播状态,wvp会自动帮你点播;ZLM +的拉流代理成功后会无限重试,只要流一恢复就可以拉起来,基于这两个原理。 +**方案如下:** +1. wvp的配置中user-settings->auto-apply-play设置为团true,开启自动点播; +2. 点击你要录像的通道,点击播放页面左下角的“更多地址”,点击rtsp,此时复制了rtsp地址到剪贴板; +3. 在拉流代理中添加一路流,地址填写你复制的地址,启用成功即可。 +**前提:** +1. wvp使用多端口收流,不然你无法得到一个固定的流地址,也就无法实现自动点播。 + diff --git a/doc/_content/ability/device.md b/doc/_content/ability/device.md new file mode 100644 index 000000000..ceed545ea --- /dev/null +++ b/doc/_content/ability/device.md @@ -0,0 +1,36 @@ + +# 接入设备 +设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息,只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md), +主要有以下字段需要配置: + +- sip->ip +本机IP,不要使用127.0.0.1/0.0.0.0, 除非你对项目及其熟悉 + +- sip->port +28181服务监听的端口 + +- sip->domain +domain宜采用ID统一编码的前十位编码。 + +- sip->id +28181服务ID + +- sip->password +28181服务密码 + +- 配置信息在如下位置 + +![_media/img_16.png](_media/img_16.png) +*** +## 大华摄像头 +![_media/img_10.png](_media/img_10.png) +## 大华NVR +![_media/img_11.png](_media/img_11.png) +## 艾科威视摄像头 +![_media/img_15.png](_media/img_15.png) +## 水星摄像头 +![_media/img_12.png](_media/img_12.png) +## 海康摄像头 +![_media/img_9.png](_media/img_9.png) + +[设备使用](_content/ability/device_use.md) diff --git a/doc/_content/ability/device_use.md b/doc/_content/ability/device_use.md new file mode 100644 index 000000000..c406b3bbb --- /dev/null +++ b/doc/_content/ability/device_use.md @@ -0,0 +1,35 @@ + +# 设备使用 +### 更新设备通道 + 点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png)即可看到通道数量的变化;如果通道数量仍未0,那么可能时对方尚未推送通道给你。 +### 查看设备通道 + 点击列表末尾的“通道”按钮, +### 查看设备定位 + 点击列表末尾的“定位”按钮,即可跳转到地图页面看到设备的位置 +### 编辑设备在WVP中一些功能 +点击列表末尾的“编辑”按钮,即可在打开的弹窗中对设备功能进行修改 +- 设备名称 + 如何未能从设备里读取到设备名称或者需要自己重命名,那么可以修改此选项。 +- 字符集 + 修改读取设备数据时使用的字符集,默认为GB2312,但是GB2312收录的汉字不全,所以有时候回遇到乱码,可以修改为UTF-8来解决。 +- 地理坐标系 + 展示此设备定位信息时使用的设用什么坐标系来解析经纬度,一般不用修改,如果遇到定位不准,可以修改尝试修改此选项解决。 +- 目录结构 + 展示设备的通道信息时,使用设备作为树形结构的依据,国标28181定义了两种树形结构,详情查看[国标28181的树形结构](./_content/theory/channel_tree.md); +- 目录订阅 + 填写订阅周期即可对设备开启目录订阅,设备如果支持目录订阅那么设备在通道信息发生变化时就会通知WVP哪些通道发生了那些变化,包括通道增加/删除/更新/上线/下线/视频丢失/故障。0为取消订阅。 + 一般NVR和平台对接可以开启此选项,直接接摄像机开启此选项意义不大。 +- 移动位置订阅 + 对设备开启移动位置订阅,设备如果支持目录订阅那么设备位置发生变化时会通知到WVP,一般执法记录仪可以开启此选项,对固定位置的设备意义不大。 +- SSRC校验 + 为了解决部分设备出现的串流问题,可以打开此选项。ZLM会严格按照给定的ssrc处理视频流。部分设备流信息不标准,开启可能导致无法点播。 +### 删除设备 + 可以删除WVP中的设备信息,如果设备28181配置未更改,那么设备在下一次注册后仍然会注册上来。 +### 点播视频 + 进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面 +### 设备录像 + 进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。 +### 云台控制 + 可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。 +### 获取视频的播放器地址 + 视频点播成功后在实时视频页面,点击“更多地址”可以看到所有的播放地址,地址是否可以播放与你是否完整编译启用zlm功能有关,更与网络有关。 \ No newline at end of file diff --git a/doc/_content/ability/gis.md b/doc/_content/ability/gis.md new file mode 100644 index 000000000..8796dba05 --- /dev/null +++ b/doc/_content/ability/gis.md @@ -0,0 +1,37 @@ + +# 电子地图 +WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息,电子地图基于开源的地图引擎openlayers开发。 +### 查看设备定位 +1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面; +2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。 +3. 单击通道信息可以定位到具体的通道 + + +### 查询设备轨迹 +查询轨迹需要提前配置save-position-history选项开启轨迹信息的保存,目前WVP此处未支持分库分表,对于大数据量的轨迹信息无法胜任,有需求请自行二次开发或者定制开发。 +在电子地图页面在设备上右键点击“查询轨迹”获取设备轨迹信息。 + +PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。 + +### 更换底图以及底图配置 +目前WVP支持使用了更换底图,配置文件在web_src/static/js/mapConfig.js,请修改后重新编译前端文件。 +```javascript +window.mapParam = { + // 开启/关闭地图功能 + enable: true, + // 坐标系 GCJ-02 WGS-84, + coordinateSystem: "GCJ-02", + // 地图瓦片地址 + tilesUrl: "http://webrd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8", + // 瓦片大小 + tileSize: 256, + // 默认层级 + zoom:10, + // 默认地图中心点 + center:[116.41020, 39.915119], + // 地图最大层级 + maxZoom:18, + // 地图最小层级 + minZoom: 3 +} +``` diff --git a/doc/_content/ability/node_manger.md b/doc/_content/ability/node_manger.md new file mode 100644 index 000000000..1b4aef324 --- /dev/null +++ b/doc/_content/ability/node_manger.md @@ -0,0 +1,9 @@ + +# 节点管理 +WVP支持单个WVP多个ZLM的方案来扩展WVP的视频并发能力,并发点播是因为带宽和性能的原因,单个ZLM节点能支持的路数有限,所以WVP增加了ZLM集群来扩展并发并且保证ZLM的高可用。 +## 默认节点 +WVP中为了保证功能的完整性,ZLM节点至少要有一个默认节点,这个节点不是在管理页面添加的,而是在WVP的配置文件中配置的,这个节点不可在页面删除。每次启动会自动从配置文件中读取配置写入数据库备用。 +## 新增节点 +启动你要添加的zlm节点,然后点击“添加节点”按钮输入zlm的ip, http端口,SECRET。点击测试测试完成则开始对节点进行详细的设置,如果你的zlm是使用docker启动的,可能存在zlm使用的端口与宿主机端口不一致的情况,需要在这里一一配置。 +## wvp使用多个节点的原理 +wvp会把连接的节点统一记录在redis中,并记录zlm的负载情况,当新的请求到来时,会取出负载最低的那个zlm进行使用。以此保证节点负载均衡。 diff --git a/doc/_content/ability/online_doc.md b/doc/_content/ability/online_doc.md new file mode 100644 index 000000000..55ecb2680 --- /dev/null +++ b/doc/_content/ability/online_doc.md @@ -0,0 +1,2 @@ + +# 在线文档 diff --git a/doc/_content/ability/proxy.md b/doc/_content/ability/proxy.md new file mode 100644 index 000000000..74e632d81 --- /dev/null +++ b/doc/_content/ability/proxy.md @@ -0,0 +1,24 @@ + +# 拉流代理 +不是所有的摄像机都支持国标或者推流的,但是这些设备可以得到一个视频播放地址,通常为rtsp协议, +以大华为例: +```text +rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0 +``` +可以得到这样一个流地址,可以直接用vlc进行播放,此时我们可以通过拉流代理功能将这个设备推送给其他国标平台了。 +流程如下: +```plantuml +@startuml +"摄像机" <- "ZLMediaKit": 1. 流去流信息到ZLM +"ZLMediaKit" -> "WVP-PRO": 2. 收到hook通知得到流信息 +"上级国标平台" -> "WVP-PRO": 3. 点播这路视频 +"WVP-PRO" -> "ZLMediaKit": 4. 通知推流到上级国标平台 +@enduml +``` +## 添加代理 +拉流代理支持两种方式: +1. ZLM中直接代理流,支持RTSP/RTMP,不支持转码; +2. 借助ffmpeg完成拉转,可以通过修改ffmpeg拉转参数完成转码。 +点击页面的“添加代理”,安装提示操作即可,保存并启用成功后,可以在国标级联中[添加通道推送给上级平台](./_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) + +PS: ffmpeg默认模板不需修改,需要修改参数自行去ZLM配置文件中添加一个即可。 diff --git a/doc/_content/ability/push.md b/doc/_content/ability/push.md new file mode 100644 index 000000000..ef501826e --- /dev/null +++ b/doc/_content/ability/push.md @@ -0,0 +1,41 @@ + +# 推流列表 +## 功能说明 + +WVP支持三种图像输入方式,直播,[拉流代理](_content/ability/proxy.md),[国标](_content/ability/device.md),直播设备接入流程如下 +```plantuml +@startuml +"直播设备" -> "ZLMediaKit": 1. 发起推流 +"ZLMediaKit" -> "WVP-PRO": 2. 收到hook通知得到流信息 +"上级国标平台" -> "WVP-PRO": 3. 点播这路视频 +"WVP-PRO" -> "ZLMediaKit": 4. 通知推流到上级国标平台 +@enduml +``` +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) +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) + +## 推拉流鉴权规则 +为了保护服务器的WVP默认开启推流鉴权(目前不支持关闭此功能) + +### 推流规则 +推流时需要携带推流鉴权的签名sign,sign=md5(pushKey),pushKey来自用户表,每个用户会有一个不同的pushKey. +例如app=test,stream=live,pushKey=1000,ip=192.168.1.4, port=10554 那么推流地址为: +``` +rtsp://192.168.1.4:10554/test/live?sign=a9b7ba70783b617e9998dc4dd82eb3c5 +``` +支持推流时自定义播放鉴权Id,参数名为callId,此时sign=md5(callId_pushKey) +例如app=test,stream=live,pushKey=1000,callId=12345678, ip=192.168.1.4, port=10554 那么推流地址为: +``` +rtsp://192.168.1.4:10554/test/live?callId=12345678&sign=c8e6e01dde2d60c66dcea8d2498ffef1 +``` +### 播放规则 +默认情况播放不需要鉴权,但是如果推流时携带了callId,那么播放时必须携带callId +例如app=test,stream=live,无callId, ip=192.168.1.4, port=10554 那么播放地址为: +``` +rtsp://192.168.1.4:10554/test/live +``` +例如app=test,stream=live,callId=12345678, ip=192.168.1.4, port=10554 那么播放地址为: +``` +rtsp://192.168.1.4:10554/test/live?callId=12345678 +``` + diff --git a/doc/_content/ability/user.md b/doc/_content/ability/user.md new file mode 100644 index 000000000..0c472386a --- /dev/null +++ b/doc/_content/ability/user.md @@ -0,0 +1,2 @@ + +# 用户管理 diff --git a/doc/_content/about_doc.md b/doc/_content/about_doc.md new file mode 100644 index 000000000..f727264e9 --- /dev/null +++ b/doc/_content/about_doc.md @@ -0,0 +1,5 @@ + + +# 关于本文档 +本文档开源在gitee上,[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git),如果文档出现任何错误或者不易理解的语句,请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档,让更多的人可以使用到这个开源的视频平台。 + diff --git a/doc/_content/disclaimers.md b/doc/_content/disclaimers.md new file mode 100644 index 000000000..94412ecdc --- /dev/null +++ b/doc/_content/disclaimers.md @@ -0,0 +1,2 @@ +# 免责声明 +WVP-PRO自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 \ No newline at end of file diff --git a/doc/_content/donation.md b/doc/_content/donation.md new file mode 100644 index 000000000..959eee819 --- /dev/null +++ b/doc/_content/donation.md @@ -0,0 +1,7 @@ +# 捐赠 +项目目前仍在积极开发。大家的捐赠以及start可以让我看到大家的支持于关注。更加有动力把项目维护下去。 + +
+ + +
\ No newline at end of file diff --git a/doc/_content/introduction/_media/img.png b/doc/_content/introduction/_media/img.png new file mode 100644 index 000000000..48f8d8e48 Binary files /dev/null and b/doc/_content/introduction/_media/img.png differ diff --git a/doc/_content/introduction/_media/img_1.png b/doc/_content/introduction/_media/img_1.png new file mode 100644 index 000000000..aad1859d0 Binary files /dev/null and b/doc/_content/introduction/_media/img_1.png differ diff --git a/doc/_content/introduction/_media/img_2.png b/doc/_content/introduction/_media/img_2.png new file mode 100644 index 000000000..e78690000 Binary files /dev/null and b/doc/_content/introduction/_media/img_2.png differ diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md new file mode 100644 index 000000000..b208685b5 --- /dev/null +++ b/doc/_content/introduction/compile.md @@ -0,0 +1,95 @@ + +# 编译 +WVP-PRO不只是实现了国标28181的协议,本身也是一个完整的视频平台。所以对于新手来说,你可能需要一些耐心来完成。遇到问题不要焦躁,你可以 +1. 百度 +2. 如果身边有熟悉java的朋友,可以咨询下朋友; +3. 来群里(901799015)咨询群友; +4. 向作者发送邮件648540858@qq.com; +5. 作者远程支持(有偿)。 + 如果这些仍不能解决你的问题,那么你可能需要与作者我一起合作完成这个项目,解决你遇到的问题。 + + +WVP-PRO使用Spring boot开发,maven管理依赖。对于熟悉spring开发的朋友是很容易进行编译部署以及运行的。 +下面将提供一种通用方法方便大家运行项目。 +## 1 服务介绍 +| 服务 | 作用 | 是否必须 | +|----------------|------------------------------------------|-------------------------| +| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 | +| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现,以及各种视频流格式的分发支持 | 是 | +| wvp-pro-assist | wvp的辅助录像程序,也可单独跟zlm一起使用,提供录像控制,录像合并下载接口 | 否(不安装只是影响云端录像功能和国标录像下载) | + +## 2 安装依赖 +| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 | +|--------|------------|-------------|--------|--------| +| jdk | >=1.8 | 运行与编译java代码 | 是 | 是 | +| maven | >=3.3 | 管理java代码依赖 | 否 | 否 | +| git || 下载/更新/提交代码 | 否 | 否 | +| nodejs || 编译于运行前端文件 | 否 | 否 | +| npm || 管理前端文件依赖 | 否 | 否 | + +如果你是一个新手,建议你使用linux或者macOS平台。windows不推荐。 + +ubuntu环境,以ubuntu 18为例: +``` bash +apt-get install -y openjdk-11-jre git maven nodejs npm +``` +centos环境,以centos 8为例: +```bash +yum install -y java-1.8.0-openjdk.x86_64 git maven nodejs npm +``` +window环境,以windows10为例: +```bash +这里不细说了,百度或者谷歌一搜一大把,基本都是下一步下一步,然后配置环境变量。 +``` +## 3 安装mysql以及redis +这里依然是参考网上教程,自行安装吧。 + +## 4 编译ZLMediaKit +参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki),截取一下关键步骤: +```bash +# 国内用户推荐从同步镜像网站gitee下载 +git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit +cd ZLMediaKit +# 千万不要忘记执行这句命令 +git submodule update --init +``` +## 5 编译WVP-PRO +### 5.1 可以通过git克隆,也可以在项目下载点击下载 +![点击下载](_media/img_1.png) +![点击下载](_media/img_2.png) +从gitee克隆 +```bash +git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git +``` +从github克隆 +```bash +git clone https://github.com/648540858/wvp-GB28181-pro.git +``` + +### 5.2 编译前端页面 +```shell script +cd wvp-GB28181-pro/web_src/ +npm --registry=https://registry.npm.taobao.org install +npm run build +``` +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 +编译完成后在src/main/resources下出现static目录 +**编译完成一般是这个样子,中间没有报红的错误信息** +![编译成功](_media/img.png) + +### 5.3 打包项目, 生成可执行jar +```bash +cd wvp-GB28181-pro +mvn package +``` +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 +编译完成后在target目录下出现wvp-pro-***.jar。 +接下来[配置服务](./_content/introduction/config.md) + + + + + + + + diff --git a/doc/_content/introduction/config.md b/doc/_content/introduction/config.md new file mode 100644 index 000000000..feb7487c0 --- /dev/null +++ b/doc/_content/introduction/config.md @@ -0,0 +1,119 @@ + +# 配置 +对于首次测试或者新手同学,我建议在局域网测试,并且关闭服务器与客户机的防火墙测试。建议部署在linux进行测试。 + +```plantuml +@startuml +"WVP-PRO" -> "ZLMediaKit": RESTful 接口 +"WVP-PRO" <-- "ZLMediaKit": Web Hook 接口 +@enduml +``` +WVP-PRO通过调用ZLMediaKit的RESTful接口实现对ZLMediaKit行为的控制; ZLMediaKit通过Web Hook 接口把消息通知WVP-PRO。通过这种方式,实现了两者的互通。 +对于最简单的配置,你不需要修改ZLMediaKit的任何默认配置。你只需要在WVP-PRO中配置的ZLMediaKit信息即可 +## 1 WVP配置文件位置 +基于spring boot的开发方式,配置文件的加载是很灵活的。默认在src/main/resources/application.yml,部分配置项是可选,你不需要全部配置在配置文件中, +完全的配置说明可以参看all-application.yml。 +### 1.1 默认加载配置文件方式 +使用maven打包后的jar包里,已经存在了配置文件,但是每次打开jar包修改配置文件或者修改后再打包都是比较麻烦的,所以大家可通过指定配置文件路径来加载指定位置的配置文件。 +```shell +cd wvp-GB28181-pro/target +java -jar wvp-pro-*.jar --spring.config.location=../src/main/resources/application.yml +``` +### 1.2 迁移配置文件以方便启动 +由于配置文件的命令比较长,所以为了启动方便通常我会把配置文件放到jar包的同级目录,类似这样, +移除jar包内/BOOT-INF/classes/下所有以application开头的文件,使用解压缩工具打开jar即可,不需要解压出来。 +```shell +cd wvp-GB28181-pro/target +mv ../src/main/resources/application-dev.yml application.yml +java -jar wvp-pro-*.jar +``` +这也是我自己最常用的方式。 +## 2 配置WVP-PRO +### 2.1 Mysql数据库配置 +首先你需要创建一个名为wvp(也可使用其他名字)的数据库,并使用sql/mysql.sql导入数据库,初始化数据库结构。 +在application-dev.yml中配置(使用1.2方式的是在jar包的同级目录的application.yml)配置数据库连接,包括数据库连接信息,密码。 +### 2.2 Redis数据库配置 +配置wvp中的redis连接信息,建议wvp自己单独使用一个db。 +### 2.3 配置服务启动端口(可直接使用默认配置) +```yaml +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 +``` +### 2.4 配置28181相关信息(可直接使用默认配置) +```yaml +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP + ip: 192.168.1.3 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 3402000000 + # [可选] + id: 34020000002000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: 12345678 +``` +### 2.5 配置ZLMediaKit连接信息 +```yaml +#zlm 默认服务器配置 +media: + # ZLMediaKit的服务ID,必须配置 + id: FQ3TF8yT83wh5Wvz + # [必须修改] zlm服务器的内网IP,sdp-ip与stream-ip使用默认值的情况下,这里不要使用127.0.0.1/0.0.0.0 + ip: 192.168.1.3 + # [必须修改] zlm服务器的http.port + http-port: 6080 + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, + port-range: 30000,30500 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 18081 +``` +### 2.4 个性化定制信息配置 +```yaml +# [根据业务需求配置] +user-settings: + # [可选] 服务ID,不写则为000000 + server-id: + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: false + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播等待超时时间,单位:毫秒 + play-timeout: 3000 + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 + wait-track: false + # 是否开启接口鉴权 + interface-authentication: true + # 自动配置redis 可以过期事件 + redis-config: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: true + # 国标是否录制 + record-sip: true + # 是否将日志存储进数据库 + logInDatebase: true + # 第三方匹配,用于从stream钟获取有效信息 + thirdPartyGBIdReg: [\s\S]* +``` + + +如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。 +接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。 \ No newline at end of file diff --git a/doc/_content/introduction/deployment.md b/doc/_content/introduction/deployment.md new file mode 100644 index 000000000..cdc1568e0 --- /dev/null +++ b/doc/_content/introduction/deployment.md @@ -0,0 +1,36 @@ + + +# 部署 +**请仔细阅读以下内容** +1. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与zlm部署在同一台主机; +2. 需要开放的端口 +| 服务 | 端口 | 类型 | 必选 | +|-----|:-------------------------|-------------|-------| +| wvp | server.port | tcp | 是 | +| wvp | sip.port | udp and tcp | 是 | +| zlm | http.port | tcp | 是 | +| zlm | http.sslport | tcp | 否 | +| zlm | rtmp.port | tcp | 否 | +| zlm | rtmp.sslport | tcp | 否 | +| zlm | rtsp.port | udp and tcp | 否 | +| zlm | rtsp.sslport | udp and tcp | 否 | +| zlm | rtp_proxy.port | udp and tcp | 单端口开放 | +| zlm | rtp.port-range(在wvp中配置) | udp and tcp | 多端口开放 | + +3. 测试环境部署建议所有服务部署在一台主机,关闭防火墙,减少因网络出现问题的可能; +4. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与zlm部署在同一台主机; +5. 生产环境按需开放端口,但是建议修改默认端口,尤其是5060端口,易受到攻击; +6. zlm使用docker部署的情况,要求端口映射一致,比如映射5060,应将外部端口也映射为5060端口; +7. 启动服务,以linux为例 +**启动WVP-PRO** +```shell +nohup java -jar java -jar wvp-pro-*.jar & +``` + +**启动ZLM** +```shell +nohup ./MediaServer -d -m 3 & +``` + +[接入设备](./_content/ability/device.md) + diff --git a/doc/_content/qa/_media/img.png b/doc/_content/qa/_media/img.png new file mode 100644 index 000000000..d6c29d71c Binary files /dev/null and b/doc/_content/qa/_media/img.png differ diff --git a/doc/_content/qa/_media/img_1.png b/doc/_content/qa/_media/img_1.png new file mode 100644 index 000000000..5b74d95b3 Binary files /dev/null and b/doc/_content/qa/_media/img_1.png differ diff --git a/doc/_content/qa/_media/img_2.png b/doc/_content/qa/_media/img_2.png new file mode 100644 index 000000000..4aaa7fed9 Binary files /dev/null and b/doc/_content/qa/_media/img_2.png differ diff --git a/doc/_content/qa/_media/img_3.png b/doc/_content/qa/_media/img_3.png new file mode 100644 index 000000000..27f8a96a7 Binary files /dev/null and b/doc/_content/qa/_media/img_3.png differ diff --git a/doc/_content/qa/_media/img_4.png b/doc/_content/qa/_media/img_4.png new file mode 100644 index 000000000..aa3b88eef Binary files /dev/null and b/doc/_content/qa/_media/img_4.png differ diff --git a/doc/_content/qa/_media/img_5.png b/doc/_content/qa/_media/img_5.png new file mode 100644 index 000000000..76e6fafda Binary files /dev/null and b/doc/_content/qa/_media/img_5.png differ diff --git a/doc/_content/qa/bug.md b/doc/_content/qa/bug.md new file mode 100644 index 000000000..f452161a4 --- /dev/null +++ b/doc/_content/qa/bug.md @@ -0,0 +1,19 @@ + +# 反馈bug +代码是在不断的完善的,不断修改会修复旧的问题也有可能引入新的问题,所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥,咱们就事论事就好了。 +## 如何反馈 +1. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试; +2. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复; +3. 你可以来我的QQ群里,询问群友看看是否遇到了同样的问题; +4. 你可以私聊我的QQ,如果我有时间我会给你答复,但是除非你有明确的复现步骤或者修复方案,否则你可能等不到我的答复。 + +## 如何快速解决BUG +目前解决BUG有三种方式: +1. 作者验证以及修复; +2. 热心开发者提来的PR; +3. 使用运维手段屏蔽BUG的影响。 + +- 对于第一种:详细的复现步骤,完整的抓包文件,有条理的错误分析都可以帮助作者复现问题,进而解决问题。解决问题往往不是最难的,复现才是。 +- 对于第二种:如果你是开发者,你已经发现了造成BUG的原因以及知道如何正确的修复,那么我很希望你PR,SRS的大佬经常说的,开源不是一个人的事。所以你的参与就是最大的鼓励。 +- 对于第三种:如果你有一个有经验的运维伙伴,那么部分问题是可以通过运维的手段暂时屏蔽的,在等待修复的这段时间了以保证项目的运行。 + diff --git a/doc/_content/qa/development.md b/doc/_content/qa/development.md new file mode 100644 index 000000000..fec7b70cf --- /dev/null +++ b/doc/_content/qa/development.md @@ -0,0 +1,15 @@ + +# 参与到开发中来 +非常欢迎有兴趣的小伙伴一起来维护这个项目 +## 与开发有关的信息 +- 开发语言:后端java + 前端vue; +- jdk版本: 1.8; +- 作者自用开发ide: jetbrains intellij idea; +- nodejs/npm版本:v10.19.0/6.14.4; +- 后端使用Spring boot框架开发; +- 项目大量使用了异步操作; +- 跟代码学流程需要参考28181文档,只看代码你会很懵的; +- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的 + +## 提交代码 +大家可以通过fork项目的方式提交自己的代码,然后提交PR,我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约**”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。 \ No newline at end of file diff --git a/doc/_content/qa/img.png b/doc/_content/qa/img.png new file mode 100644 index 000000000..d6c29d71c Binary files /dev/null and b/doc/_content/qa/img.png differ diff --git a/doc/_content/qa/play_error.md b/doc/_content/qa/play_error.md new file mode 100644 index 000000000..8c9e75906 --- /dev/null +++ b/doc/_content/qa/play_error.md @@ -0,0 +1,57 @@ + +# 点播错误 +排查点播错误你首先要清除[点播的基本流程](_content/theory/play.md),一般的流程如下: +```plantuml +@startuml +"WEB用户" -> "WVP-PRO": 1. 发起点播请求 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体) +"设备" --> "WVP-PRO": 3. 200OK(携带SDP消息体) +"设备" <-- "WVP-PRO": 4. Ack +"设备" -> "ZLMediaKit": 5. 发送实时流 +"WVP-PRO" <- "ZLMediaKit": 6. 流改变事件 +"WEB用户" <-- "WVP-PRO": 7. 回复流播放地址(携带流地址) +"WVP-PRO" <- "ZLMediaKit": 8. 无人观看事件 +"设备" <- "WVP-PRO": 9 Bye消息 +"设备" --> "WVP-PRO": 10 200OK +@enduml +``` +针对几种常见的错误,我们来分析一下,也方便大家对号入座解决常见的问题 +## 点播收到错误码 +这个错误一般表现为点击"播放"按钮后很快得到一个错误。 +1. **400错误码** +出现400错误玛时一般是这样的流程是这样的 +```plantuml +@startuml +"WEB用户" -> "WVP-PRO": 1. 发起点播请求 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体) +"设备" --> "WVP-PRO": 3. 400错误 +@enduml +``` +此时通常是设备认为WVP发送了错误的消息给它,它认为消息不全或者错误所以直接返回400错误,此时我们需要[抓包](_content/skill/tcpdump.md)来分析是否缺失了内容,也可以直接联系对方询问为什么返回了400。 +WVP不能保证兼容所有的设备,有些实现不规范的设备可能在对接时就会出现上述问题,你可以联系作者帮忙对接。 +2. **500错误码** +500或者大于500小于600的错误码一般多是设备内部出了问题,解决方式有两个,第一种直接联系设备/平台客服寻求解决;第二种,如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我,我来尝试解决。 + + +## 点播超时 +点播超时的情况大致分为两种:点播超时和收流超时 +1. **点播超时** +点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体)”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因: +> 1. 设备内部错误,未能回复消息 +> 2. 网络原因消息未到到达设备 + +大部分时候是原因2,所以遇到这个错误我们首先要排查我们我的网路,如果你是公网部署,那么也可能时心跳周期太长,导致的路由NAT失效,WVP的消息无法通道原来的IP端口号发送给设备。 + +2. **收流超时** +收流超时可能发生在流程中的5和6,可能的原因有: +> 1. 设备发送了流但是发送到了错误的ip和端口上,而这个信息是在invite消息的sdp中指定的,就是流程2Invite(携带SDP消息体)中,而这个错误很可能来自你的配置错误,比如你设置了127.0.0.1导致设备网127.0.0.1上发流,或者是你WVP在公网,但是你给设备了一个内网ip,导致设备无法把流发送过来; +> 2. 设备内部错误未发送流; +> 2. 设备发送了流,但是流无法识别,可能存在于流不规范和网络很差的情况下; +> 3. 设备发送了流,zlm也收到了,但是zlm无法通过hook通知到wvp,此时原因是你可以检查zlm的配置文件中的hook配置,看看是否无法从zlm连接到wvp; +> 4. 设备发送了流,但是开启SSRC校验,设备的流不够规范采用错误的ssrc,导致zlm选择丢弃; + +针对这些可能的错误原因我建议的排查顺序: +- 关闭ssrc校验; +- 查看zlm配置的hook是否可以连接到zlm; +- 查看zlm日志是否有流注册; +- 抓包查看流的信息,看看流是否正常发送,甚至可以导出发送原始流,用vlc播放,看看是否可以播放。 diff --git a/doc/_content/qa/regiser_error.md b/doc/_content/qa/regiser_error.md new file mode 100644 index 000000000..d18459c54 --- /dev/null +++ b/doc/_content/qa/regiser_error.md @@ -0,0 +1,8 @@ + +# 设备注册不上来的解决办法 +一般的原因有两个 +1. 信息填写错误,比如密码错误; +2. 网络不通导致注册消息无法发送到WVP; + + +遇到问题首先仔细校验填写信息,例如海康可能需要勾选鉴权才可以输入密码。网络问题请自行测试。 \ No newline at end of file diff --git a/doc/_content/qa/start_error.md b/doc/_content/qa/start_error.md new file mode 100644 index 000000000..1dd533f2c --- /dev/null +++ b/doc/_content/qa/start_error.md @@ -0,0 +1,24 @@ + +# 启动时报错 +启动时的报错大部分时候是因为你的配置有问题,比如mysql没连接上,redis没连接上,18080/15060端口占用了,这些都会导致启动是报错,修改配置配置之后都可以解决; +下面我整理的一些常见的错误,大家可以先对号入座的简单排查下。 +> **常见错误** + +![_media/img.png](_media/img.png) +**错误原因:** redis配置错误,可能原因: redis未启动/ip错误/端口错误/网络不通 +--- +![_media/img_1.png](_media/img_1.png) +**错误原因:** redis配置错误,可能原因: 密码错误 +--- +![_media/img_2.png](_media/img_2.png) +**错误原因:** mysql配置错误,可能原因: mysql未启动/ip错误/端口错误/网络不通 +--- +![_media/img_3.png](_media/img_3.png) +**错误原因:** mysql配置错误,可能原因: 用户名/密码错误 +--- +![_media/img_4.png](_media/img_4.png) +**错误原因:** SIP配置错误,可能原因: SIP端口被占用 +--- +![_media/img_5.png](_media/img_5.png) +**错误原因:** WVP Tomcat端口配置错误,可能原因: server.port端口被占用 +--- \ No newline at end of file diff --git a/doc/_content/skill/_media/img.png b/doc/_content/skill/_media/img.png new file mode 100644 index 000000000..a9bc95fcd Binary files /dev/null and b/doc/_content/skill/_media/img.png differ diff --git a/doc/_content/skill/_media/img_1.png b/doc/_content/skill/_media/img_1.png new file mode 100644 index 000000000..e08e4e1e0 Binary files /dev/null and b/doc/_content/skill/_media/img_1.png differ diff --git a/doc/_content/skill/_media/img_2.png b/doc/_content/skill/_media/img_2.png new file mode 100644 index 000000000..2af0ecc6f Binary files /dev/null and b/doc/_content/skill/_media/img_2.png differ diff --git a/doc/_content/skill/tcpdump.md b/doc/_content/skill/tcpdump.md new file mode 100644 index 000000000..7dd270de8 --- /dev/null +++ b/doc/_content/skill/tcpdump.md @@ -0,0 +1,62 @@ + +# 抓包 +如果说对于网络编程,有什么工具是必会的,我觉得抓包肯定是其中之一了。作为GB/T 28181调试过程中最重要的手段,我觉得如果你真对他有兴趣,或者系统遇到问题可以最快的得到解决,那么抓包你就一定要学会了。 + +## 抓包工具的选择 +### 1. Wireshark +在具备图形界面的系统上,比如windows,linux发行版ubuntu,opensuse等,我一般直接使用Wireshark直接进行抓包,也方便进行内容的查看。 +### 2. Tcpdump +在使用命令行的系统,比如linux服务器,我一般使用Tcpdump进行抓包,无需额外安装,系统一般自带,抓包的到的文件,可以使用Wireshark打开,在图形界面下方便查看内容。 + +## 工具安装 +Wireshark的安装很简单,根据提示一步步点击就好了,在linux需要解决权限的问题,如果和我一样使用图形界面的linux发行版的话,可以参看如下步骤; windows的小伙伴直接略过即可 +```shell +# 1. 添加wireshark用户组 +sudo groupadd wireshark +# 2. 将dumpcap更改为wireshark用户组 +sudo chgrp wireshark /usr/bin/dumpcap +# 3. 让wireshark用户组有root权限使用dumpcap +sudo chmod 4755 /usr/bin/dumpcap +# 4. 将需要使用的用户名加入wireshark用户组 +sudo gpasswd -a $USER wireshark +``` +tcpdump一般linux都是自带,无需安装,可以这样验证;显示版本信息即是已安装 +```shell +tcpdump --version +``` +## 开始抓包 +### 使用Wireshark +在28181中我一般只关注sip包和rtp包,所以我一般是直接过滤sip和rtp,可以输入框输入 `sip or rtp`这样即可,如果设备来源比较多还可以加上ip和端口号的过滤`(sip or rtp )and ip.addr==192.168.1.3 and udp.port==5060` +详细的过滤规则可以自行百度,我可以提供一些常用的给大家参考 +![img.png](_media/img.png) +**只过滤SIP:** +```shell +sip +``` +**只获取rtp数据:** +```shell +rtp +``` +**默认方式:** +```shell +sip or rtp +``` +**过滤IP:** +```shell + sip and ip.addr==192.168.1.3 +``` +**过滤端口:** +```shell + sip and udp.port==5060 +``` +输入命令开启抓包后,此时可以进行操作,比如点播,录像回访等,操作完成回到Wireshark点击红色的停止即可,需要保存文件可以点击`文件->导出特定分组`导出过滤后的数据,也可以直接`文件->另存为`保存未过滤的数据。 +### 使用tcpdump +对于服务器抓包,为了得到足够完整的数据,我一般会要求直接抓取网卡数据而不过滤,如下: +抓取网卡首先需要获取网卡名,在linux我一般使用`ip addr`获取网卡信息,如下所示: +![img_1.png](_media/img_1.png) +```shell +sudo tcpdump -i wlp3s0 -w demo.pcap +``` +![img_2.png](_media/img_2.png) +命令行会停留在这个位置,此时可以进行操作,比如点播,录像回放等,操作完成回到命令行使用`Ctrl+C`结束命令行,在当前目录下得到demo.pcap,将这个文件下载到图形界面操作系统里,即可使用Wireshark查看了 +更多的操作可以参考: [https://www.cnblogs.com/jiujuan/p/9017495.html](https://www.cnblogs.com/jiujuan/p/9017495.html) diff --git a/doc/_content/theory/_media/img.png b/doc/_content/theory/_media/img.png new file mode 100644 index 000000000..ecf62e9f1 Binary files /dev/null and b/doc/_content/theory/_media/img.png differ diff --git a/doc/_content/theory/_media/img_1.png b/doc/_content/theory/_media/img_1.png new file mode 100644 index 000000000..2dc8cc8ad Binary files /dev/null and b/doc/_content/theory/_media/img_1.png differ diff --git a/doc/_content/theory/_media/img_2.png b/doc/_content/theory/_media/img_2.png new file mode 100644 index 000000000..7e2ddde4e Binary files /dev/null and b/doc/_content/theory/_media/img_2.png differ diff --git a/doc/_content/theory/_media/img_3.png b/doc/_content/theory/_media/img_3.png new file mode 100644 index 000000000..5fc5ef410 Binary files /dev/null and b/doc/_content/theory/_media/img_3.png differ diff --git a/doc/_content/theory/_media/img_4.png b/doc/_content/theory/_media/img_4.png new file mode 100644 index 000000000..d5df7cebd Binary files /dev/null and b/doc/_content/theory/_media/img_4.png differ diff --git a/doc/_content/theory/_media/img_5.png b/doc/_content/theory/_media/img_5.png new file mode 100644 index 000000000..47daffcdf Binary files /dev/null and b/doc/_content/theory/_media/img_5.png differ diff --git a/doc/_content/theory/_media/img_6.png b/doc/_content/theory/_media/img_6.png new file mode 100644 index 000000000..6c67ef472 Binary files /dev/null and b/doc/_content/theory/_media/img_6.png differ diff --git a/doc/_content/theory/_media/img_7.png b/doc/_content/theory/_media/img_7.png new file mode 100644 index 000000000..fc204aaaa Binary files /dev/null and b/doc/_content/theory/_media/img_7.png differ diff --git a/doc/_content/theory/_media/img_8.png b/doc/_content/theory/_media/img_8.png new file mode 100644 index 000000000..9b436417c Binary files /dev/null and b/doc/_content/theory/_media/img_8.png differ diff --git a/doc/_content/theory/_media/img_9.png b/doc/_content/theory/_media/img_9.png new file mode 100644 index 000000000..c3aa1ff65 Binary files /dev/null and b/doc/_content/theory/_media/img_9.png differ diff --git a/doc/_content/theory/channel_tree.md b/doc/_content/theory/channel_tree.md new file mode 100644 index 000000000..c4c1b09ee --- /dev/null +++ b/doc/_content/theory/channel_tree.md @@ -0,0 +1,14 @@ + + +# 通道的树形结构 + +国标28181规定了两种组织设备树的方式 +1. **行政区划** + 行政区划模式下主要是以行政区划作为目录节点例如:河北省->邯郸市->广平县 + ![_media/img_8.png](_media/img_8.png) +2. **业务分组** + 业务分组主要自定义的目录树的一种组织形式,但是对定义的目录的国标编号有一定的要求。 + 第一级别需要是业务分组类型,即国标编码中的11、12、13是215,例如:65010200002150000001; + 业务分组下是虚拟组织,即国标编码中的11、12、13是216,例如:65010200002160000002。 + 虚拟组织下不可是业务分组,虚拟组织下可以继续添加虚拟组织。 + ![_media/img_9.png](_media/img_9.png) diff --git a/doc/_content/theory/code.md b/doc/_content/theory/code.md new file mode 100644 index 000000000..d5a892e2a --- /dev/null +++ b/doc/_content/theory/code.md @@ -0,0 +1,25 @@ + + +# 统一编码规则 +## D.1 编码规则 A +>  编码规则 A 由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十 +>进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。 +>  编码规则 A 的详细说明见表 D.1。其中,中心编码指用户或设备所归属的监控中心的编码,按照监控中心所在地的行政区划代码确定, +> 当不是基层单位时空余位为0。行政区划代码采用 GB/T2260— 2007规定的行政区划代码表示。行业编码是指用户或设备所归属的行业,行业编码对照表见 D.3。 +> 类型编码指定了设备或用户的具体类型,其中的前端设备包含公安系统和非公安系统的前端设备,终端用 户包含公安系统和非公安系统的终端用户。 +![img_7.png](_media/img_7.png) +![img_1.png](_media/img_1.png) +![img_2.png](_media/img_2.png) + + +## D.2 编码规则 B +>  编码规则 B由中心编码(8位)、行业编码(2位)、序号(4位)和类型编码(2位)四个码段构成,即系 +>统编码 =中心编码 + 行业编码 +序号+类型编码。编码规则 B的详细说明见表 D.2。 +![img_3.png](_media/img_3.png) +![img_4.png](_media/img_4.png) + + +## D.3 行业编码对照表 +>  行业编码对照表见表 D.3。 +![img_5.png](_media/img_5.png) +![img_6.png](_media/img_6.png) \ No newline at end of file diff --git a/doc/_content/theory/img.png b/doc/_content/theory/img.png new file mode 100644 index 000000000..9b436417c Binary files /dev/null and b/doc/_content/theory/img.png differ diff --git a/doc/_content/theory/play.md b/doc/_content/theory/play.md new file mode 100644 index 000000000..fbfdcc661 --- /dev/null +++ b/doc/_content/theory/play.md @@ -0,0 +1,33 @@ + + +# 点播流程 +> 以下为WVP-PRO点播流程。点播成功前的任何一个环节出现问题都可能出现点播超时,这也是排查点播超时的依据。 + +```plantuml +@startuml +"WEB用户" -> "WVP-PRO": 1. 发起点播请求 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体) +"设备" --> "WVP-PRO": 3. 200OK(携带SDP消息体) +"设备" <-- "WVP-PRO": 4. Ack +"设备" -> "ZLMediaKit": 5. 发送实时流 +"WVP-PRO" <- "ZLMediaKit": 6. 流改变事件 +"WEB用户" <-- "WVP-PRO": 7. 回复流播放地址(携带流地址) +"WVP-PRO" <- "ZLMediaKit": 8. 无人观看事件 +"设备" <- "WVP-PRO": 9 Bye消息 +"设备" --> "WVP-PRO": 10 200OK +@enduml +``` + + +## 注册流程描述如下: +1. 用户从网页或调用接口发起点播请求; +2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、 + 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。 +3. 摄像机向WVP-PRO回复200OK,消息体中描述了媒体流发送者发送媒体流的IP、端口、媒体格式、SSRC字段等内容。 +4. WVP-PRO向设备回复Ack, 会话建立成功。 +5. 设备向ZLMediaKit发送实时流。 +6. ZLMediaKit向WVP-PRO发送流改变事件。 +7. WVP-PRO向WEB用户回复播放地址。 +8. ZLMediaKit向WVP发送流无人观看事件。 +9. WVP-PRO向设备回复Bye, 结束会话。 +10. 设备回复200OK,会话结束成功。 diff --git a/doc/_content/theory/register.md b/doc/_content/theory/register.md new file mode 100644 index 000000000..fad9589fe --- /dev/null +++ b/doc/_content/theory/register.md @@ -0,0 +1,21 @@ + + +# 注册流程 +WVP-PRO目前仅支持国标中描述的基本注册流程,也是最常用的, +> 基本注册即采用IETFRFC3261规定的基于数字摘要的挑战应答式安全技术进行注册. + +```plantuml +@startuml +"设备" -> "WVP-PRO": 1. Register +"设备" <-- "WVP-PRO": 2. 401 Unauthorized +"设备" -> "WVP-PRO": 3. Register +"设备" <-- "WVP-PRO": 4. 200 OK +@enduml +``` + + +> 注册流程描述如下: +> 1. 摄像机向WVP-PRO服务器发送 Register请求; +> 2. WVP-PRO向摄像机发送响应401,并在响应的消息头 WWW_Authenticate字段中给出适合摄像机的认证体制和参数; +> 3. 摄像机重新向WVP-PRO发送 Register请求,在请求的 Authorization字段给出信任书, 包含认证信息; +> 4. WVP-PRO对请求进行验证,如果检查出 摄像机身份合法,向摄像机发送成功响应 200OK,如果身份不合法则发送拒绝服务应答。 diff --git a/doc/_coverpage.md b/doc/_coverpage.md new file mode 100644 index 000000000..eb3524f9a --- /dev/null +++ b/doc/_coverpage.md @@ -0,0 +1,17 @@ + +![logo](_media/logo-mini.png) + +# WVP-PRO 2.0 + +> 开箱即用的28181协议视频平台。 + +- 基于GB/T28181-2016标准信令实现,兼容GB/T28181-2011。 +- 自带完整前端页面,开箱即用。 +- 完全开源,且使用MIT许可协议。可以在保留版权信息的基础上商用。 + +[GitHub](https://github.com/648540858/wvp-GB28181-pro) +[Gitee](https://gitee.com/pan648540858/wvp-GB28181-pro) + + + +[//]: # ([comment]: <> (![color](#f0f0f0))) diff --git a/doc/_media/favicon.ico b/doc/_media/favicon.ico new file mode 100644 index 000000000..bc5f8e662 Binary files /dev/null and b/doc/_media/favicon.ico differ diff --git a/doc/_media/logo-mini.png b/doc/_media/logo-mini.png new file mode 100644 index 000000000..cc8078db3 Binary files /dev/null and b/doc/_media/logo-mini.png differ diff --git a/doc/_media/logo.png b/doc/_media/logo.png new file mode 100644 index 000000000..c5da2d4b9 Binary files /dev/null and b/doc/_media/logo.png differ diff --git a/doc/_media/weixin.jpg b/doc/_media/weixin.jpg new file mode 100644 index 000000000..eda1260ec Binary files /dev/null and b/doc/_media/weixin.jpg differ diff --git a/doc/_media/zhifubao.jpg b/doc/_media/zhifubao.jpg new file mode 100644 index 000000000..973996b79 Binary files /dev/null and b/doc/_media/zhifubao.jpg differ diff --git a/doc/_navbar.md b/doc/_navbar.md new file mode 100644 index 000000000..22ac93ae0 --- /dev/null +++ b/doc/_navbar.md @@ -0,0 +1 @@ + diff --git a/doc/_sidebar.md b/doc/_sidebar.md new file mode 100644 index 000000000..eb0c96c01 --- /dev/null +++ b/doc/_sidebar.md @@ -0,0 +1,33 @@ + + +* **编译与部署** + * [编译](_content/introduction/compile.md) + * [配置](_content/introduction/config.md) + * [部署](_content/introduction/deployment.md) +* **功能与使用** + * [接入设备](_content/ability/device.md) + * [设备使用](_content/ability/device_use.md) + * [国标级联](_content/ability/cascade2.md) + * [推流列表](_content/ability/push.md) + * [拉流代理](_content/ability/proxy.md) + * [电子地图](_content/ability/gis.md) + * [节点管理](_content/ability/node_manger.md) + * [云端录像](_content/ability/cloud_record.md) + * [不间断录像](_content/ability/continuous_recording.md) +* **流程与原理** + * [统一编码规则](_content/theory/code.md) + * [树形结构](_content/theory/channel_tree.md) + * [注册流程](_content/theory/register.md) + * [点播流程](_content/theory/play.md) +* **必备技巧** + * [抓包](_content/skill/tcpdump.md) + +* **常见问答** + - [如何反馈BUG](_content/qa/bug.md) + - [如何参与开发](_content/qa/development.md) + - [启动报错的解决办法](_content/qa/start_error.md) + - [设备注册不上来的解决办法](_content/qa/regiser_error.md) + - [点播超时/报错的解决办法](_content/qa/play_error.md) +* [**免责声明**](_content/disclaimers.md) +* [**捐赠**](_content/donation.md) +* [**关于本文档**](_content/about_doc.md) diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 000000000..1048d9294 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,59 @@ + + + + + WVP-PRO文档 + + + + + + + + + +
加载中
+ + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 12091242e..41be5675f 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,13 @@ org.springframework.boot spring-boot-starter-data-redis + + + + io.lettuce + lettuce-core + + org.springframework.boot @@ -75,6 +82,12 @@ org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.4 + + + com.zaxxer + HikariCP + + org.springframework.boot @@ -84,7 +97,6 @@ redis.clients jedis - ${jedis-version} @@ -265,14 +277,14 @@ - + org.apache.maven.plugins diff --git a/sql/mysql.sql b/sql/mysql.sql index 9d2e1174e..e49499021 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -2,55 +2,60 @@ -- -- Host: 127.0.0.1 Database: wvp2 -- ------------------------------------------------------ --- Server version 8.0.29-0ubuntu0.22.04.2 +-- Server version 8.0.29-0ubuntu0.22.04.3 -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS = @@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION = @@COLLATION_CONNECTION */; /*!50503 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +/*!40103 SET @OLD_TIME_ZONE = @@TIME_ZONE */; +/*!40103 SET TIME_ZONE = ' + 00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS = @@UNIQUE_CHECKS, UNIQUE_CHECKS = 0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS = @@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0 */; +/*!40101 SET @OLD_SQL_MODE = @@SQL_MODE, SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES = @@SQL_NOTES, SQL_NOTES = 0 */; -- -- Table structure for table `device` -- DROP TABLE IF EXISTS `device`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `device` ( - `id` int NOT NULL AUTO_INCREMENT, - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `manufacturer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `firmware` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `port` int NOT NULL, - `expires` int NOT NULL, - `subscribeCycleForCatalog` int NOT NULL, - `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `subscribeCycleForMobilePosition` int DEFAULT NULL, - `mobilePositionSubmissionInterval` int DEFAULT '5', - `subscribeCycleForAlarm` int DEFAULT NULL, - `ssrcCheck` int DEFAULT '0', - `geoCoordSys` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, - `treeType` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `device_deviceId_uindex` (`deviceId`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `device` +( + `id` int NOT NULL AUTO_INCREMENT, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `manufacturer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `firmware` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `port` int NOT NULL, + `expires` int NOT NULL, + `subscribeCycleForCatalog` int NOT NULL, + `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `subscribeCycleForMobilePosition` int DEFAULT NULL, + `mobilePositionSubmissionInterval` int DEFAULT '5 ', + `subscribeCycleForAlarm` int DEFAULT NULL, + `ssrcCheck` int DEFAULT '0 ', + `geoCoordSys` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, + `treeType` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `device_deviceId_uindex` (`deviceId`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 53 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -58,8 +63,10 @@ CREATE TABLE `device` ( -- LOCK TABLES `device` WRITE; -/*!40000 ALTER TABLE `device` DISABLE KEYS */; -/*!40000 ALTER TABLE `device` ENABLE KEYS */; +/*!40000 ALTER TABLE `device` + DISABLE KEYS */; +/*!40000 ALTER TABLE `device` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -67,22 +74,26 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `device_alarm`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `device_alarm` ( - `id` int NOT NULL AUTO_INCREMENT, - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `alarmPriority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `alarmMethod` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `alarmTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `alarmDescription` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `longitude` double DEFAULT NULL, - `latitude` double DEFAULT NULL, - `alarmType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `device_alarm` +( + `id` int NOT NULL AUTO_INCREMENT, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `alarmPriority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `alarmMethod` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `alarmTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `alarmDescription` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `longitude` double DEFAULT NULL, + `latitude` double DEFAULT NULL, + `alarmType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -90,8 +101,10 @@ CREATE TABLE `device_alarm` ( -- LOCK TABLES `device_alarm` WRITE; -/*!40000 ALTER TABLE `device_alarm` DISABLE KEYS */; -/*!40000 ALTER TABLE `device_alarm` ENABLE KEYS */; +/*!40000 ALTER TABLE `device_alarm` + DISABLE KEYS */; +/*!40000 ALTER TABLE `device_alarm` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -99,50 +112,55 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `device_channel`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `device_channel` ( - `id` int NOT NULL AUTO_INCREMENT, - `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `manufacture` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `owner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `civilCode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `block` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `safetyWay` int DEFAULT NULL, - `registerWay` int DEFAULT NULL, - `certNum` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `certifiable` int DEFAULT NULL, - `errCode` int DEFAULT NULL, - `endTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `secrecy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `ipAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `port` int DEFAULT NULL, - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `PTZType` int DEFAULT NULL, - `status` int DEFAULT NULL, - `longitude` double DEFAULT NULL, - `latitude` double DEFAULT NULL, - `streamId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `parental` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `hasAudio` bit(1) DEFAULT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `subCount` int DEFAULT '0', - `longitudeGcj02` double DEFAULT NULL, - `latitudeGcj02` double DEFAULT NULL, - `longitudeWgs84` double DEFAULT NULL, - `latitudeWgs84` double DEFAULT NULL, - `businessGroupId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `device_channel_id_uindex` (`id`) USING BTREE, - UNIQUE KEY `device_channel_pk` (`channelId`,`deviceId`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=19331 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `device_channel` +( + `id` int NOT NULL AUTO_INCREMENT, + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `manufacture` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `owner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `civilCode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `block` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `safetyWay` int DEFAULT NULL, + `registerWay` int DEFAULT NULL, + `certNum` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `certifiable` int DEFAULT NULL, + `errCode` int DEFAULT NULL, + `endTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `secrecy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ipAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `port` int DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `PTZType` int DEFAULT NULL, + `status` int DEFAULT NULL, + `longitude` double DEFAULT NULL, + `latitude` double DEFAULT NULL, + `streamId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `parental` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `hasAudio` bit(1) DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `subCount` int DEFAULT '0 ', + `longitudeGcj02` double DEFAULT NULL, + `latitudeGcj02` double DEFAULT NULL, + `longitudeWgs84` double DEFAULT NULL, + `latitudeWgs84` double DEFAULT NULL, + `businessGroupId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `device_channel_id_uindex` (`id`) USING BTREE, + UNIQUE KEY `device_channel_pk` (`channelId`, `deviceId`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 19496 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -150,8 +168,10 @@ CREATE TABLE `device_channel` ( -- LOCK TABLES `device_channel` WRITE; -/*!40000 ALTER TABLE `device_channel` DISABLE KEYS */; -/*!40000 ALTER TABLE `device_channel` ENABLE KEYS */; +/*!40000 ALTER TABLE `device_channel` + DISABLE KEYS */; +/*!40000 ALTER TABLE `device_channel` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -159,27 +179,32 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `device_mobile_position`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `device_mobile_position` ( - `id` int NOT NULL AUTO_INCREMENT, - `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `channelId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - `deviceName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `time` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `longitude` double NOT NULL, - `latitude` double NOT NULL, - `altitude` double DEFAULT NULL, - `speed` double DEFAULT NULL, - `direction` double DEFAULT NULL, - `reportSource` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `longitudeGcj02` double DEFAULT NULL, - `latitudeGcj02` double DEFAULT NULL, - `longitudeWgs84` double DEFAULT NULL, - `latitudeWgs84` double DEFAULT NULL, - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=6751 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `device_mobile_position` +( + `id` int NOT NULL AUTO_INCREMENT, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `channelId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `deviceName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `time` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `longitude` double NOT NULL, + `latitude` double NOT NULL, + `altitude` double DEFAULT NULL, + `speed` double DEFAULT NULL, + `direction` double DEFAULT NULL, + `reportSource` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `longitudeGcj02` double DEFAULT NULL, + `latitudeGcj02` double DEFAULT NULL, + `longitudeWgs84` double DEFAULT NULL, + `latitudeWgs84` double DEFAULT NULL, + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 6956 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -187,8 +212,10 @@ CREATE TABLE `device_mobile_position` ( -- LOCK TABLES `device_mobile_position` WRITE; -/*!40000 ALTER TABLE `device_mobile_position` DISABLE KEYS */; -/*!40000 ALTER TABLE `device_mobile_position` ENABLE KEYS */; +/*!40000 ALTER TABLE `device_mobile_position` + DISABLE KEYS */; +/*!40000 ALTER TABLE `device_mobile_position` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -196,24 +223,29 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `gb_stream`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `gb_stream` ( - `gbStreamId` int NOT NULL AUTO_INCREMENT, - `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `gbId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `longitude` double DEFAULT NULL, - `latitude` double DEFAULT NULL, - `streamType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`gbStreamId`) USING BTREE, - UNIQUE KEY `app` (`app`,`stream`) USING BTREE, - UNIQUE KEY `gbId` (`gbId`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=301681 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `gb_stream` +( + `gbStreamId` int NOT NULL AUTO_INCREMENT, + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `gbId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `longitude` double DEFAULT NULL, + `latitude` double DEFAULT NULL, + `streamType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `gpsTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`gbStreamId`) USING BTREE, + UNIQUE KEY `app` (`app`, `stream`) USING BTREE, + UNIQUE KEY `gbId` (`gbId`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 301754 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -221,8 +253,10 @@ CREATE TABLE `gb_stream` ( -- LOCK TABLES `gb_stream` WRITE; -/*!40000 ALTER TABLE `gb_stream` DISABLE KEYS */; -/*!40000 ALTER TABLE `gb_stream` ENABLE KEYS */; +/*!40000 ALTER TABLE `gb_stream` + DISABLE KEYS */; +/*!40000 ALTER TABLE `gb_stream` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -230,20 +264,25 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `log`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `log` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `result` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `timing` bigint NOT NULL, - `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=34997 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `log` +( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `result` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `timing` bigint NOT NULL, + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 42703 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -251,8 +290,10 @@ CREATE TABLE `log` ( -- LOCK TABLES `log` WRITE; -/*!40000 ALTER TABLE `log` DISABLE KEYS */; -/*!40000 ALTER TABLE `log` ENABLE KEYS */; +/*!40000 ALTER TABLE `log` + DISABLE KEYS */; +/*!40000 ALTER TABLE `log` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -260,35 +301,39 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `media_server`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `media_server` ( - `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `hookIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `sdpIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `streamIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `httpPort` int NOT NULL, - `httpSSlPort` int NOT NULL, - `rtmpPort` int NOT NULL, - `rtmpSSlPort` int NOT NULL, - `rtpProxyPort` int NOT NULL, - `rtspPort` int NOT NULL, - `rtspSSLPort` int NOT NULL, - `autoConfig` int NOT NULL, - `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `streamNoneReaderDelayMS` int NOT NULL, - `rtpEnable` int NOT NULL, - `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `recordAssistPort` int NOT NULL, - `defaultServer` int NOT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `hookAliveInterval` int NOT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `media_server_i` (`ip`,`httpPort`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `media_server` +( + `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `hookIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `sdpIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `streamIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `httpPort` int NOT NULL, + `httpSSlPort` int NOT NULL, + `rtmpPort` int NOT NULL, + `rtmpSSlPort` int NOT NULL, + `rtpProxyPort` int NOT NULL, + `rtspPort` int NOT NULL, + `rtspSSLPort` int NOT NULL, + `autoConfig` int NOT NULL, + `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `streamNoneReaderDelayMS` int NOT NULL, + `rtpEnable` int NOT NULL, + `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `recordAssistPort` int NOT NULL, + `defaultServer` int NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `hookAliveInterval` int NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `media_server_i` (`ip`, `httpPort`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -296,8 +341,10 @@ CREATE TABLE `media_server` ( -- LOCK TABLES `media_server` WRITE; -/*!40000 ALTER TABLE `media_server` DISABLE KEYS */; -/*!40000 ALTER TABLE `media_server` ENABLE KEYS */; +/*!40000 ALTER TABLE `media_server` + DISABLE KEYS */; +/*!40000 ALTER TABLE `media_server` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -305,39 +352,44 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `parent_platform`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `parent_platform` ( - `id` int NOT NULL AUTO_INCREMENT, - `enable` int DEFAULT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `serverGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `serverGBDomain` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `serverIP` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `serverPort` int DEFAULT NULL, - `deviceGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `deviceIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `devicePort` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `expires` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `keepTimeout` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `characterSet` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `ptz` int DEFAULT NULL, - `rtcp` int DEFAULT NULL, - `status` bit(1) DEFAULT NULL, - `shareAllLiveStream` int DEFAULT NULL, - `startOfflinePush` int DEFAULT '0', - `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `catalogGroup` int DEFAULT '1', - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `parent_platform_id_uindex` (`id`) USING BTREE, - UNIQUE KEY `parent_platform_pk` (`serverGBId`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `parent_platform` +( + `id` int NOT NULL AUTO_INCREMENT, + `enable` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `serverGBDomain` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverIP` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverPort` int DEFAULT NULL, + `deviceGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deviceIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `devicePort` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `expires` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `keepTimeout` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `characterSet` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `ptz` int DEFAULT NULL, + `rtcp` int DEFAULT NULL, + `status` bit(1) DEFAULT NULL, + `startOfflinePush` int DEFAULT '0 ', + `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `catalogGroup` int DEFAULT '1 ', + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `treeType` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `parent_platform_id_uindex` (`id`) USING BTREE, + UNIQUE KEY `parent_platform_pk` (`serverGBId`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 40 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -345,8 +397,10 @@ CREATE TABLE `parent_platform` ( -- LOCK TABLES `parent_platform` WRITE; -/*!40000 ALTER TABLE `parent_platform` DISABLE KEYS */; -/*!40000 ALTER TABLE `parent_platform` ENABLE KEYS */; +/*!40000 ALTER TABLE `parent_platform` + DISABLE KEYS */; +/*!40000 ALTER TABLE `parent_platform` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -354,15 +408,21 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `platform_catalog`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `platform_catalog` ( - `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `platform_catalog` +( + `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `civilCode` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `businessGroupId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -370,8 +430,10 @@ CREATE TABLE `platform_catalog` ( -- LOCK TABLES `platform_catalog` WRITE; -/*!40000 ALTER TABLE `platform_catalog` DISABLE KEYS */; -/*!40000 ALTER TABLE `platform_catalog` ENABLE KEYS */; +/*!40000 ALTER TABLE `platform_catalog` + DISABLE KEYS */; +/*!40000 ALTER TABLE `platform_catalog` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -379,15 +441,20 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `platform_gb_channel`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `platform_gb_channel` ( - `id` int NOT NULL AUTO_INCREMENT, - `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `deviceChannelId` int NOT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=4889 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `platform_gb_channel` +( + `id` int NOT NULL AUTO_INCREMENT, + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deviceChannelId` int NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 4915 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -395,8 +462,10 @@ CREATE TABLE `platform_gb_channel` ( -- LOCK TABLES `platform_gb_channel` WRITE; -/*!40000 ALTER TABLE `platform_gb_channel` DISABLE KEYS */; -/*!40000 ALTER TABLE `platform_gb_channel` ENABLE KEYS */; +/*!40000 ALTER TABLE `platform_gb_channel` + DISABLE KEYS */; +/*!40000 ALTER TABLE `platform_gb_channel` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -404,16 +473,21 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `platform_gb_stream`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `platform_gb_stream` ( - `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `gbStreamId` int NOT NULL, - `id` int NOT NULL AUTO_INCREMENT, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `platform_gb_stream_pk` (`platformId`,`catalogId`,`gbStreamId`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=302077 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `platform_gb_stream` +( + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `gbStreamId` int NOT NULL, + `id` int NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `platform_gb_stream_pk` (`platformId`, `catalogId`, `gbStreamId`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 302149 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -421,8 +495,10 @@ CREATE TABLE `platform_gb_stream` ( -- LOCK TABLES `platform_gb_stream` WRITE; -/*!40000 ALTER TABLE `platform_gb_stream` DISABLE KEYS */; -/*!40000 ALTER TABLE `platform_gb_stream` ENABLE KEYS */; +/*!40000 ALTER TABLE `platform_gb_stream` + DISABLE KEYS */; +/*!40000 ALTER TABLE `platform_gb_stream` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -430,31 +506,36 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `stream_proxy`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `stream_proxy` ( - `id` int NOT NULL AUTO_INCREMENT, - `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `src_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `dst_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `timeout_ms` int DEFAULT NULL, - `ffmpeg_cmd_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `rtp_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `enable_hls` bit(1) DEFAULT NULL, - `enable_mp4` bit(1) DEFAULT NULL, - `enable` bit(1) NOT NULL, - `status` bit(1) NOT NULL, - `enable_remove_none_reader` bit(1) NOT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `stream_proxy_pk` (`app`,`stream`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `stream_proxy` +( + `id` int NOT NULL AUTO_INCREMENT, + `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `src_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `dst_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `timeout_ms` int DEFAULT NULL, + `ffmpeg_cmd_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `rtp_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `enable_hls` bit(1) DEFAULT NULL, + `enable_mp4` bit(1) DEFAULT NULL, + `enable` bit(1) NOT NULL, + `status` bit(1) NOT NULL, + `enable_remove_none_reader` bit(1) NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `stream_proxy_pk` (`app`, `stream`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 66 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -462,8 +543,10 @@ CREATE TABLE `stream_proxy` ( -- LOCK TABLES `stream_proxy` WRITE; -/*!40000 ALTER TABLE `stream_proxy` DISABLE KEYS */; -/*!40000 ALTER TABLE `stream_proxy` ENABLE KEYS */; +/*!40000 ALTER TABLE `stream_proxy` + DISABLE KEYS */; +/*!40000 ALTER TABLE `stream_proxy` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -471,25 +554,32 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `stream_push`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `stream_push` ( - `id` int NOT NULL AUTO_INCREMENT, - `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `totalReaderCount` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `originType` int DEFAULT NULL, - `originTypeStr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - `aliveSecond` int DEFAULT NULL, - `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `serverId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, - `pushTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - `status` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `stream_push_pk` (`app`,`stream`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=305315 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `stream_push` +( + `id` int NOT NULL AUTO_INCREMENT, + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `totalReaderCount` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `originType` int DEFAULT NULL, + `originTypeStr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `aliveSecond` int DEFAULT NULL, + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, + `pushTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `updateTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` int DEFAULT NULL, + `pushIng` int DEFAULT NULL, + `self` int DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `stream_push_pk` (`app`, `stream`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 305415 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -497,8 +587,10 @@ CREATE TABLE `stream_push` ( -- LOCK TABLES `stream_push` WRITE; -/*!40000 ALTER TABLE `stream_push` DISABLE KEYS */; -/*!40000 ALTER TABLE `stream_push` ENABLE KEYS */; +/*!40000 ALTER TABLE `stream_push` + DISABLE KEYS */; +/*!40000 ALTER TABLE `stream_push` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -506,19 +598,24 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `user`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `user` ( - `id` int NOT NULL AUTO_INCREMENT, - `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `roleId` int NOT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `pushKey` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `user_username_uindex` (`username`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `user` +( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `roleId` int NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `pushKey` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `user_username_uindex` (`username`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 2 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -526,9 +623,13 @@ CREATE TABLE `user` ( -- LOCK TABLES `user` WRITE; -/*!40000 ALTER TABLE `user` DISABLE KEYS */; -INSERT INTO `user` VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021 - 04 - 13 14:14:57','2021 - 04 - 13 14:14:57','453df297a57a5a7438934sda801fc3'); -/*!40000 ALTER TABLE `user` ENABLE KEYS */; +/*!40000 ALTER TABLE `user` + DISABLE KEYS */; +INSERT INTO `user` +VALUES (1, 'admin', '21232f297a57a5a743894a0e4a801fc3', 1, '2021 - 04 - 13 14:14:57', '2021 - 04 - 13 14:14:57', + '01685cb9573ae25ec6c52142402da7c5'); +/*!40000 ALTER TABLE `user` + ENABLE KEYS */; UNLOCK TABLES; -- @@ -536,16 +637,21 @@ UNLOCK TABLES; -- DROP TABLE IF EXISTS `user_role`; -/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `user_role` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE `user_role` +( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 2 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -553,18 +659,21 @@ CREATE TABLE `user_role` ( -- LOCK TABLES `user_role` WRITE; -/*!40000 ALTER TABLE `user_role` DISABLE KEYS */; -INSERT INTO `user_role` VALUES (1,'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57'); -/*!40000 ALTER TABLE `user_role` ENABLE KEYS */; +/*!40000 ALTER TABLE `user_role` + DISABLE KEYS */; +INSERT INTO `user_role` +VALUES (1, 'admin', '0 ', '2021 - 04 - 13 14:14:57', '2021 - 04 - 13 14:14:57'); +/*!40000 ALTER TABLE `user_role` + ENABLE KEYS */; UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +/*!40103 SET TIME_ZONE = @OLD_TIME_ZONE */; -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE = @OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS = @OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS = @OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT = @OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS = @OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION = @OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES = @OLD_SQL_NOTES */; --- Dump completed on 2022-07-11 17:32:51 +-- Dump completed on 2022-07-27 14:51:08 diff --git a/sql/update.sql b/sql/update.sql index f3ca46572..c04cd9260 100644 --- a/sql/update.sql +++ b/sql/update.sql @@ -70,6 +70,8 @@ alter table stream_push add status int DEFAULT NULL; alter table stream_push add updateTime varchar(50) default null; +alter table stream_push + add pushIng int DEFAULT NULL; alter table stream_push change createStamp createTime varchar(50) default null; @@ -78,4 +80,20 @@ alter table gb_stream alter table user add pushKey varchar(50) default null; +update user set pushKey='453df297a57a5a7438934sda801fc3' where id=1; + +alter table parent_platform + add treeType varchar(50) not null; +update parent_platform set parent_platform.treeType='BusinessGroup'; +alter table parent_platform drop shareAllLiveStream; + +alter table platform_catalog + add civilCode varchar(50) default null; +alter table platform_catalog + add businessGroupId varchar(50) default null; + +/********************* ADD ***************************/ +alter table stream_push + add self int DEFAULT NULL; + diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index 8ff2efb8a..3bac82d6f 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -11,7 +11,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; import springfox.documentation.oas.annotations.EnableOpenApi; /** - * + * 启动类 */ @ServletComponentScan("com.genersoft.iot.vmp.conf") @SpringBootApplication diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 702e35790..40a73521c 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -104,6 +104,10 @@ public class VideoManagerConstants { // 设备状态订阅的通知 public static final String VM_MSG_SUBSCRIBE_DEVICE_STATUS = "device"; + + + + //************************** 第三方 **************************************** public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java index ec1f9bac6..6a862ae3a 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java @@ -1,10 +1,7 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener; -import com.genersoft.iot.vmp.service.impl.RedisGpsMsgListener; -import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener; -import com.genersoft.iot.vmp.service.impl.RedisStreamMsgListener; +import com.genersoft.iot.vmp.service.impl.*; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -60,6 +57,9 @@ public class RedisConfig extends CachingConfigurerSupport { @Autowired private RedisGbPlayMsgListener redisGbPlayMsgListener; + @Autowired + private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener; + @Bean public JedisPool jedisPool() { if (StringUtils.isBlank(password)) { @@ -108,6 +108,7 @@ public class RedisConfig extends CachingConfigurerSupport { container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE)); container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH")); container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY)); + container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE)); return container; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index c0fcf01f0..d28ddebc8 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -1,6 +1,5 @@ package com.genersoft.iot.vmp.conf; -import io.swagger.models.auth.In; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java index 63569ef16..509a1e038 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java @@ -1,9 +1,7 @@ package com.genersoft.iot.vmp.conf.security; -import com.genersoft.iot.vmp.conf.security.dto.LoginUser; -import com.genersoft.iot.vmp.service.IUserService; -import com.genersoft.iot.vmp.storager.dao.dto.User; -import com.github.xiaoymin.knife4j.core.util.StrUtil; +import java.time.LocalDateTime; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -12,7 +10,10 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; +import com.alibaba.excel.util.StringUtils; +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.service.IUserService; +import com.genersoft.iot.vmp.storager.dao.dto.User; /** * 用户登录认证逻辑 @@ -27,7 +28,7 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - if (StrUtil.isBlank(username)) { + if (StringUtils.isBlank(username)) { logger.info("登录用户:{} 不存在", username); throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java index 8a96d356c..965d7f2af 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java @@ -3,8 +3,14 @@ package com.genersoft.iot.vmp.gb28181.bean; import java.time.Instant; import java.util.List; +/** + * @author lin + */ public class CatalogData { - private int sn; // 命令序列号 + /** + * 命令序列号 + */ + private int sn; private int total; private List channelList; private Instant lastTime; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java index 4377282d4..61e777ad1 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java @@ -104,11 +104,6 @@ public class ParentPlatform { */ private int channelCount; - /** - * 共享所有的直播流 - */ - private boolean shareAllLiveStream; - /** * 默认目录Id,自动添加的通道多放在这个目录下 */ @@ -154,6 +149,11 @@ public class ParentPlatform { */ private String createTime; + /** + * 树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGroup + */ + private String treeType; + public Integer getId() { return id; } @@ -314,15 +314,6 @@ public class ParentPlatform { this.channelCount = channelCount; } - - public boolean isShareAllLiveStream() { - return shareAllLiveStream; - } - - public void setShareAllLiveStream(boolean shareAllLiveStream) { - this.shareAllLiveStream = shareAllLiveStream; - } - public String getCatalogId() { return catalogId; } @@ -394,4 +385,12 @@ public class ParentPlatform { public void setCreateTime(String createTime) { this.createTime = createTime; } + + public String getTreeType() { + return treeType; + } + + public void setTreeType(String treeType) { + this.treeType = treeType; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java index 065971ddf..58a9cbb0d 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java @@ -1,12 +1,28 @@ package com.genersoft.iot.vmp.gb28181.bean; +/** + * 国标级联-目录 + * @author lin + */ public class PlatformCatalog { private String id; private String name; private String platformId; private String parentId; - private int childrenCount; // 子节点数 - private int type; // 0 目录, 1 国标通道, 2 直播流 + + private String civilCode; + + private String businessGroupId; + + /** + * 子节点数 + */ + private int childrenCount; + + /** + * 0 目录, 1 国标通道, 2 直播流 + */ + private int type; public String getId() { return id; @@ -68,4 +84,19 @@ public class PlatformCatalog { this.type = 2; } + public String getCivilCode() { + return civilCode; + } + + public void setCivilCode(String civilCode) { + this.civilCode = civilCode; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java index 2ae50e697..f3a197ca9 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java @@ -2,12 +2,15 @@ package com.genersoft.iot.vmp.gb28181.bean; /** * 摄像机同步状态 + * @author lin */ public class SyncStatus { private int total; private int current; private String errorMsg; + private boolean syncIng; + public int getTotal() { return total; } @@ -31,4 +34,12 @@ public class SyncStatus { public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } + + public boolean isSyncIng() { + return syncIng; + } + + public void setSyncIng(boolean syncIng) { + this.syncIng = syncIng; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java new file mode 100644 index 000000000..bb684e1a6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java @@ -0,0 +1,10 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 目录结构类型 + * @author lin + */ +public class TreeType { + public static final String BUSINESS_GROUP = "BusinessGroup"; + public static final String CIVIL_CODE = "CivilCode"; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java index 14ed76a28..56bdeb58b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java @@ -65,7 +65,6 @@ public class PlatformNotRegisterEventLister implements ApplicationListener sendRtpItems = redisCatchStorage.querySendRTPServer(event.getPlatformGbID()); - logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流size", sendRtpItems.size()); if (sendRtpItems != null && sendRtpItems.size() > 0) { logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流", event.getPlatformGbID()); for (SendRtpItem sendRtpItem : sendRtpItems) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java index c035b8087..5c064f756 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java @@ -11,13 +11,40 @@ public class CatalogEvent extends ApplicationEvent { super(source); } - public static final String ON = "ON"; // 上线 - public static final String OFF = "OFF"; // 离线 - public static final String VLOST = "VLOST"; // 视频丢失 - public static final String DEFECT = "DEFECT"; // 故障 - public static final String ADD = "ADD"; // 增加 - public static final String DEL = "DEL"; // 删除 - public static final String UPDATE = "UPDATE"; // 更新 + /** + * 上线 + */ + public static final String ON = "ON"; + + /** + * 离线 + */ + public static final String OFF = "OFF"; + + /** + * 视频丢失 + */ + public static final String VLOST = "VLOST"; + + /** + * 故障 + */ + public static final String DEFECT = "DEFECT"; + + /** + * 增加 + */ + public static final String ADD = "ADD"; + + /** + * 删除 + */ + public static final String DEL = "DEL"; + + /** + * 更新 + */ + public static final String UPDATE = "UPDATE"; private List deviceChannels; private List gbStreams; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java index e38733d5b..224fa1e68 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java @@ -58,17 +58,16 @@ public class CatalogEventLister implements ApplicationListener { ParentPlatform parentPlatform = null; Map> parentPlatformMap = new HashMap<>(); - if (event.getPlatformId() != null) { + if (!StringUtils.isEmpty(event.getPlatformId())) { + subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId()); + if (subscribe == null) { + return; + } parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformId()); if (parentPlatform != null && !parentPlatform.isStatus()) { return; } - subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId()); - if (subscribe == null) { - logger.info("发送订阅消息时发现订阅信息已经不存在: {}", event.getPlatformId()); - return; - } }else { // 获取所用订阅 List platforms = subscribeHolder.getAllCatalogSubscribePlatform(); @@ -144,11 +143,8 @@ public class CatalogEventLister implements ApplicationListener { } if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ for (GbStream gbStream : event.getGbStreams()) { - DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform); - if (deviceChannelByStream.getParentId().length() <= 10) { // 父节点是行政区划,则设置CivilCode使用此行政区划 - deviceChannelByStream.setCivilCode(deviceChannelByStream.getParentId()); - } - deviceChannelList.add(deviceChannelByStream); + deviceChannelList.add( + gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform)); } } if (deviceChannelList.size() > 0) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java index 7ed3c1169..732a87a3c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java @@ -84,6 +84,11 @@ public class CatalogDataCatch { syncStatus.setCurrent(catalogData.getChannelList().size()); syncStatus.setTotal(catalogData.getTotal()); syncStatus.setErrorMsg(catalogData.getErrorMsg()); + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { + syncStatus.setSyncIng(false); + }else { + syncStatus.setSyncIng(true); + } return syncStatus; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java index 66b57fec8..7edee4dd7 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java @@ -57,23 +57,17 @@ public class MobilePositionSubscribeHandlerTask implements ISubscribeTask { SubscribeInfo subscribe = subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()); if (subscribe != null) { -// if (!parentPlatform.isStatus()) { -// logger.info("发送订阅时发现平台已经离线:{}", platformId); -// return; -// } // TODO 暂时只处理视频流的回复,后续增加对国标设备的支持 - List gbStreams = storager.queryGbStreamListInPlatform(platform.getServerGBId()); + List gbStreams = storager.queryGbStreamListInPlatform(platform.getServerGBId()); if (gbStreams.size() == 0) { logger.info("发送订阅时发现平台已经没有关联的直播流:{}", platform.getServerGBId()); return; } - for (GbStream gbStream : gbStreams) { - String gbId = gbStream.getGbId(); + for (DeviceChannel deviceChannel : gbStreams) { + String gbId = deviceChannel.getChannelId(); GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId); - if (gpsMsgInfo != null) { // 无最新位置不发送 - if (logger.isDebugEnabled()) { - logger.debug("无最新位置不发送"); - } + // 无最新位置不发送 + if (gpsMsgInfo != null) { // 经纬度都为0不发送 if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) { continue; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java index cf1f7cf6c..e43e59b9c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java @@ -39,9 +39,9 @@ public class MobilePositionSubscribeTask implements ISubscribeTask { dynamicTask.stop(taskKey); } sipCommander.mobilePositionSubscribe(device, dialog, eventResult -> { -// if (eventResult.dialog != null || eventResult.dialog.getState().equals(DialogState.CONFIRMED)) { -// dialog = eventResult.dialog; -// } + if (eventResult.dialog != null || eventResult.dialog.getState().equals(DialogState.CONFIRMED)) { + dialog = eventResult.dialog; + } ResponseEvent event = (ResponseEvent) eventResult.event; if (event.getResponse().getRawContent() != null) { // 成功 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 832f7165d..96b92e9de 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/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; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.HookType; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; @@ -348,25 +351,19 @@ public class SIPCommander implements ISIPCommander { @Override public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) { - String streamId = ssrcInfo.getStream(); + String stream = ssrcInfo.getStream(); try { if (device == null) { return; } String streamMode = device.getStreamMode().toUpperCase(); - logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", streamId); - subscribeKey.put("regist", true); - subscribeKey.put("schema", "rtmp"); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtmp", mediaServerItem.getId()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ if (event != null) { event.response(mediaServerItemInUse, json); + subscribe.removeSubscribe(hookSubscribe); } }); // @@ -440,7 +437,7 @@ public class SIPCommander implements ISIPCommander { errorEvent.response(e); }), e ->{ // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 - streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play); + streamSession.put(device.getDeviceId(), channelId ,"play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play); streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog); okEvent.response(e); }); @@ -530,21 +527,14 @@ public class SIPCommander implements ISIPCommander { CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId(); - + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtmp", mediaServerItem.getId()); // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", ssrcInfo.getStream()); - subscribeKey.put("regist", true); - subscribeKey.put("schema", "rtmp"); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ if (hookEvent != null) { InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()); hookEvent.call(inviteStreamInfo); } + subscribe.removeSubscribe(hookSubscribe); }); Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc()); @@ -643,21 +633,15 @@ public class SIPCommander implements ISIPCommander { CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId(); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId()); // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", ssrcInfo.getStream()); - subscribeKey.put("regist", true); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream())); - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); - subscribeKey.put("regist", false); - subscribeKey.put("schema", "rtmp"); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("regist", false); + hookSubscribe.getContent().put("schema", "rtmp"); // 添加流注销的订阅,注销了后向设备发送bye - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{ ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId()); if (transaction != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java index 2d4913eb4..c87b55423 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java @@ -257,37 +257,37 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { catalogXml.append("\r\n"); if (channels.size() > 0) { for (DeviceChannel channel : channels) { + if (parentPlatform.getServerGBId().equals(channel.getParentId())) { + channel.setParentId(parentPlatform.getDeviceGBId()); + } catalogXml.append("\r\n"); + // 行政区划分组只需要这两项就可以 catalogXml.append("" + channel.getChannelId() + "\r\n"); catalogXml.append("" + channel.getName() + "\r\n"); - catalogXml.append("" + channel.getParental() + "\r\n"); if (channel.getParentId() != null) { + // 业务分组加上这一项即可,提高兼容性, catalogXml.append("" + channel.getParentId() + "\r\n"); } - if (channel.getChannelId().length() == 20) { - if (Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织增加BusinessGroupID字段 - catalogXml.append("" + channel.getParentId() + "\r\n"); - } + if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { + // 虚拟组织增加BusinessGroupID字段 + catalogXml.append("" + channel.getParentId() + "\r\n"); + } + catalogXml.append("" + channel.getParental() + "\r\n"); + if (channel.getParental() == 0) { + // 通道项 catalogXml.append("" + channel.getManufacture() + "\r\n"); + catalogXml.append("" + channel.getSecrecy() + "\r\n"); catalogXml.append("" + channel.getRegisterWay() + "\r\n"); - catalogXml.append("" + (channel.getStatus() == 0?"OFF":"ON") + "\r\n"); - if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下字段 - catalogXml.append("" + channel.getSecrecy() + "\r\n"); + catalogXml.append("" + (channel.getStatus() == 0 ? "OFF" : "ON") + "\r\n"); + + if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 catalogXml.append("" + channel.getModel() + "\r\n"); - catalogXml.append("" + channel.getOwner() + "\r\n"); + catalogXml.append(" " + channel.getOwner()+ "\r\n"); catalogXml.append("" + channel.getCivilCode() + "\r\n"); catalogXml.append("
" + channel.getAddress() + "
\r\n"); - catalogXml.append("" + channel.getLongitudeWgs84() + "\r\n"); - catalogXml.append("" + channel.getLatitudeWgs84() + "\r\n"); - catalogXml.append("" + channel.getIpAddress() + "\r\n"); - catalogXml.append("" + channel.getPort() + "\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("" + channel.getPTZType() + "\r\n"); - catalogXml.append("\r\n"); } + } - - catalogXml.append("
\r\n"); } } @@ -592,27 +592,35 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { channel.setParentId(parentPlatform.getDeviceGBId()); } catalogXml.append("\r\n"); + // 行政区划分组只需要这两项就可以 catalogXml.append("" + channel.getChannelId() + "\r\n"); catalogXml.append("" + channel.getName() + "\r\n"); - catalogXml.append("" + channel.getManufacture() + "\r\n"); - catalogXml.append("" + channel.getParental() + "\r\n"); if (channel.getParentId() != null) { + // 业务分组加上这一项即可,提高兼容性, catalogXml.append("" + channel.getParentId() + "\r\n"); } - catalogXml.append("" + channel.getSecrecy() + "\r\n"); - catalogXml.append("" + channel.getRegisterWay() + "\r\n"); - catalogXml.append("" + (channel.getStatus() == 0 ? "OFF" : "ON") + "\r\n"); - if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织增加BusinessGroupID字段 + if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { + // 虚拟组织增加BusinessGroupID字段 catalogXml.append("" + channel.getParentId() + "\r\n"); } - if (channel.getChannelType() == 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 - catalogXml.append("" + channel.getModel() + "\r\n"); - catalogXml.append("0\r\n"); - catalogXml.append("CivilCode\r\n"); - catalogXml.append("
" + channel.getAddress() + "
\r\n"); - } - if (!"presence".equals(subscribeInfo.getEventType())) { - catalogXml.append("" + type + "\r\n"); + catalogXml.append("" + channel.getParental() + "\r\n"); + if (channel.getParental() == 0) { + // 通道项 + catalogXml.append("" + channel.getManufacture() + "\r\n"); + catalogXml.append("" + channel.getSecrecy() + "\r\n"); + catalogXml.append("" + channel.getRegisterWay() + "\r\n"); + catalogXml.append("" + (channel.getStatus() == 0 ? "OFF" : "ON") + "\r\n"); + + if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 + catalogXml.append("" + channel.getModel() + "\r\n"); + catalogXml.append(" " + channel.getOwner()+ "\r\n"); + catalogXml.append("" + channel.getCivilCode() + "\r\n"); + catalogXml.append("
" + channel.getAddress() + "
\r\n"); + } + if (!"presence".equals(subscribeInfo.getEventType())) { + catalogXml.append("" + type + "\r\n"); + } + } catalogXml.append("
\r\n"); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java index 12083edad..9c457885d 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java +++ b/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 SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); if (ssrcTransactionForPlay != null){ SIPDialog dialogForPlay = (SIPDialog) SerializeUtils.deSerialize(ssrcTransactionForPlay.getDialog()); - if (dialogForPlay.getCallId().equals(callIdHeader.getCallId())){ + if (dialogForPlay.getCallId().getCallId().equals(callIdHeader.getCallId())){ // 释放ssrc MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId()); if (mediaServerItem != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index 40232195d..5e6e8d745 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/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; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamPushService; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.SSRCInfo; @@ -80,6 +81,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @Autowired private IStreamPushService streamPushService; + @Autowired + private IStreamProxyService streamProxyService; + @Autowired private IRedisCatchStorage redisCatchStorage; @@ -94,7 +98,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @Autowired private ISIPCommander commander; - + @Autowired private AudioBroadcastManager audioBroadcastManager; @@ -153,10 +157,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令 try { Request request = evt.getRequest(); - SipURI sipUri = (SipURI) request.getRequestURI(); - //从subject读取channelId,不再从request-line读取。 有些平台request-line是平台国标编码,不是设备国标编码。 - //String channelId = sipURI.getUser(); - String channelId = SipUtils.getChannelIdFromHeader(request); + String channelId = SipUtils.getChannelIdFromRequest(request); String requesterId = SipUtils.getUserIdFromFromHeader(request); CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); if (requesterId == null || channelId == null) { @@ -178,6 +179,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements MediaServerItem mediaServerItem = null; StreamPushItem streamPushItem = null; + StreamProxyItem proxyByAppAndStream =null; // 不是通道可能是直播流 if (channel != null && gbStream == null) { if (channel.getStatus() == 0) { @@ -211,6 +213,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements responseAck(evt, Response.GONE); return; } + }else if("proxy".equals(gbStream.getStreamType())){ + proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream()); + if (proxyByAppAndStream == null) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + responseAck(evt, Response.GONE); + return; + } } } responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中 @@ -452,18 +461,35 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements } } } else if (gbStream != null) { - if (streamPushItem.isStatus()) { - // 在线状态 - pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, - mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); - } else { - // 不在线 拉起 - notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, - mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + if("push".equals(gbStream.getStreamType())) { + if (streamPushItem != null && streamPushItem.isPushIng()) { + // 推流状态 + pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } else { + // 未推流 拉起 + notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + }else if ("proxy".equals(gbStream.getStreamType())){ + if(null != proxyByAppAndStream &&proxyByAppAndStream.isStatus()){ + pushProxyStream(evt, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }else{ + //开启代理拉流 + boolean start1 = streamProxyService.start(gbStream.getApp(), gbStream.getStream()); + if(start1) { + pushProxyStream(evt, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }else{ + //失败后通知 + notifyStreamOnline(evt, gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + } - } - } } catch (SipException | InvalidArgumentException | ParseException e) { @@ -480,13 +506,45 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements /** * 安排推流 */ + private void pushProxyStream(RequestEvent evt, GbStream gbStream, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, + mediaTransmissionTCP); + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + responseAck(evt, Response.BUSY_HERE); + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog()); + sendRtpItem.setDialog(dialogByteArray); + byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction()); + sendRtpItem.setTransaction(transactionByteArray); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + sendStreamAck(mediaServerItem, sendRtpItem, platform, evt); + + } + + } private void pushStream(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, CallIdHeader callIdHeader, MediaServerItem mediaServerItem, int port, Boolean tcpActive, boolean mediaTransmissionTCP, String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException { // 推流 - if (streamPushItem.getServerId().equals(userSetting.getServerId())) { + if (streamPushItem.isSelf()) { Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); if (streamReady) { // 自平台内容 @@ -525,7 +583,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements } } - /** * 通知流上线 */ @@ -535,7 +592,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException { if ("proxy".equals(gbStream.getStreamType())) { // TODO 控制启用以使设备上线 - logger.info("[ app={}, stream={} ]通道离线,启用流后开始推流", gbStream.getApp(), gbStream.getStream()); + logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream()); responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline"); } else if ("push".equals(gbStream.getStreamType())) { if (!platform.isStartOfflinePush()) { @@ -543,7 +600,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements return; } // 发送redis消息以使设备上线 - logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream()); + logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream()); MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(), @@ -553,7 +610,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements dynamicTask.startDelay(callIdHeader.getCallId(), () -> { logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream()); try { - mediaListManager.removedChannelOnlineEventLister(gbStream.getGbId()); + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); responseAck(evt, Response.REQUEST_TIMEOUT); // 超时 } catch (SipException e) { e.printStackTrace(); @@ -568,7 +625,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements Boolean finalTcpActive = tcpActive; // 添加在本机上线的通知 - mediaListManager.addChannelOnlineEventLister(gbStream.getGbId(), (app, stream, serverId) -> { + mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> { dynamicTask.stop(callIdHeader.getCallId()); if (serverId.equals(userSetting.getServerId())) { SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, @@ -656,7 +713,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements // 离线 // 查询是否在本机上线了 StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream()); - if (currentStreamPushItem.isStatus()) { + if (currentStreamPushItem.isPushIng()) { // 在线状态 pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java index 376d74b02..4ce30a2a6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java +++ b/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; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; @@ -71,6 +72,9 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @Autowired private SIPProcessorObserver sipProcessorObserver; + @Autowired + private IDeviceChannelService deviceChannelService; + private boolean taskQueueHandlerRun = false; private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @@ -88,39 +92,36 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @Override public void process(RequestEvent evt) { try { - taskQueue.offer(new HandlerCatchData(evt, null, null)); responseAck(evt, Response.OK); if (!taskQueueHandlerRun) { taskQueueHandlerRun = true; taskExecutor.execute(()-> { - while (!taskQueue.isEmpty()) { - try { - HandlerCatchData take = taskQueue.poll(); - Element rootElement = getRootElement(take.getEvt()); - String cmd = XmlUtil.getText(rootElement, "CmdType"); + while (!taskQueue.isEmpty()) { + try { + HandlerCatchData take = taskQueue.poll(); + Element rootElement = getRootElement(take.getEvt()); + String cmd = XmlUtil.getText(rootElement, "CmdType"); - if (CmdType.CATALOG.equals(cmd)) { - logger.info("接收到Catalog通知"); - processNotifyCatalogList(take.getEvt()); - } else if (CmdType.ALARM.equals(cmd)) { - logger.info("接收到Alarm通知"); - processNotifyAlarm(take.getEvt()); - } else if (CmdType.MOBILE_POSITION.equals(cmd)) { - logger.info("接收到MobilePosition通知"); - processNotifyMobilePosition(take.getEvt()); - } else { - logger.info("接收到消息:" + cmd); - } - } catch (DocumentException e) { - throw new RuntimeException(e); - } + if (CmdType.CATALOG.equals(cmd)) { + logger.info("接收到Catalog通知"); + processNotifyCatalogList(take.getEvt()); + } else if (CmdType.ALARM.equals(cmd)) { + logger.info("接收到Alarm通知"); + processNotifyAlarm(take.getEvt()); + } else if (CmdType.MOBILE_POSITION.equals(cmd)) { + logger.info("接收到MobilePosition通知"); + processNotifyMobilePosition(take.getEvt()); + } else { + logger.info("接收到消息:" + cmd); } - taskQueueHandlerRun = false; - }); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + } + taskQueueHandlerRun = false; + }); } - - } catch (SipException | InvalidArgumentException | ParseException e) { e.printStackTrace(); } @@ -170,31 +171,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } else { mobilePosition.setAltitude(0.0); } - logger.info("[收到 移动位置订阅]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), + logger.info("[收到移动位置订阅通知]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), mobilePosition.getLongitude(), mobilePosition.getLatitude()); mobilePosition.setReportSource("Mobile Position"); - // 默认来源坐标系为WGS-84处理 - if ("WGS84".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude()); - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeGcj02(position[0]); - mobilePosition.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude()); - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeWgs84(position[0]); - mobilePosition.setLatitudeWgs84(position[1]); - }else { - mobilePosition.setLongitudeGcj02(0.00); - mobilePosition.setLatitudeGcj02(0.00); - mobilePosition.setLongitudeWgs84(0.00); - mobilePosition.setLatitudeWgs84(0.00); - } - if (userSetting.getSavePositionHistory()) { - storager.insertMobilePosition(mobilePosition); - } + // 更新device channel 的经纬度 DeviceChannel deviceChannel = new DeviceChannel(); @@ -202,11 +182,18 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements deviceChannel.setChannelId(channelId); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84()); - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84()); - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02()); - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02()); deviceChannel.setGpsTime(mobilePosition.getTime()); + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } + storager.updateChannelPosition(deviceChannel); // 发送redis消息。 通知位置信息的变化 JSONObject jsonObject = new JSONObject(); @@ -281,38 +268,28 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements mobilePosition.setLongitude(deviceAlarm.getLongitude()); mobilePosition.setLatitude(deviceAlarm.getLatitude()); mobilePosition.setReportSource("GPS Alarm"); - if ("WGS84".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude()); - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeGcj02(position[0]); - mobilePosition.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude()); - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeWgs84(position[0]); - mobilePosition.setLatitudeWgs84(position[1]); - }else { - mobilePosition.setLongitudeGcj02(0.00); - mobilePosition.setLatitudeGcj02(0.00); - mobilePosition.setLongitudeWgs84(0.00); - mobilePosition.setLatitudeWgs84(0.00); - } - if (userSetting.getSavePositionHistory()) { - storager.insertMobilePosition(mobilePosition); - } + + + // 更新device channel 的经纬度 DeviceChannel deviceChannel = new DeviceChannel(); deviceChannel.setDeviceId(device.getDeviceId()); deviceChannel.setChannelId(channelId); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84()); - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84()); - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02()); - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02()); deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } + storager.updateChannelPosition(deviceChannel); } // TODO: 需要实现存储报警信息、报警分类 @@ -338,7 +315,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements Device device = redisCatchStorage.getDevice(deviceId); if (device == null || device.getOnline() == 0) { - logger.warn("[收到 目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); return; } Element rootElement = getRootElement(evt, device.getCharset()); @@ -359,28 +336,28 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements Element eventElement = itemDevice.element("Event"); String event; if (eventElement == null) { - logger.warn("[收到 目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); event = CatalogEvent.ADD; }else { event = eventElement.getText().toUpperCase(); } - DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device); + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event); channel.setDeviceId(device.getDeviceId()); - logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); switch (event) { case CatalogEvent.ON: // 上线 - logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId()); + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.deviceChannelOnline(deviceId, channel.getChannelId()); break; case CatalogEvent.OFF : // 离线 - logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId()); + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.deviceChannelOffline(deviceId, channel.getChannelId()); break; case CatalogEvent.VLOST: // 视频丢失 - logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId()); + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.deviceChannelOffline(deviceId, channel.getChannelId()); break; case CatalogEvent.DEFECT: @@ -388,18 +365,18 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements break; case CatalogEvent.ADD: // 增加 - logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId()); - storager.updateChannel(deviceId, channel); + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); break; case CatalogEvent.DEL: // 删除 - logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId()); + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.delChannel(deviceId, channel.getChannelId()); break; case CatalogEvent.UPDATE: // 更新 - logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId()); - storager.updateChannel(deviceId, channel); + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); break; default: logger.warn("[ NotifyCatalog ] event not found : {}", event ); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java index 622cf0739..351b79cf7 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java @@ -143,6 +143,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen device.setGeoCoordSys("WGS84"); device.setTreeType("CivilCode"); device.setDeviceId(deviceId); + device.setOnline(0); } device.setIp(received); device.setPort(rPort); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java index cd858896c..e004b89f7 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java @@ -27,6 +27,7 @@ import javax.sip.SipException; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; import javax.sip.header.CallIdHeader; +import javax.sip.message.Request; import javax.sip.message.Response; import java.text.ParseException; import java.util.Map; @@ -68,22 +69,23 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement @Override public void process(RequestEvent evt) { + SIPRequest sipRequest = (SIPRequest)evt.getRequest(); logger.debug("接收到消息:" + evt.getRequest()); String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); - CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); + CallIdHeader callIdHeader = sipRequest.getCallIdHeader(); // 先从会话内查找 SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); - if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + if (ssrcTransaction != null) { deviceId = ssrcTransaction.getDeviceId(); } // 查询设备是否存在 - CSeqHeader cseqHeader = (CSeqHeader) evt.getRequest().getHeader(CSeqHeader.NAME); - String method = cseqHeader.getMethod(); Device device = redisCatchStorage.getDevice(deviceId); // 查询上级平台是否存在 ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId); try { if (device != null && parentPlatform != null) { + logger.warn("[重复]平台与设备编号重复:{}", deviceId); SIPRequest request = (SIPRequest) evt.getRequest(); String hostAddress = request.getRemoteAddress().getHostAddress(); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java index c546057ee..c8be504ca 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java +++ b/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; /** * 命令类型: 通知命令 * 命令类型: 状态信息(心跳)报送, 报警通知, 媒体通知, 移动设备位置数据,语音广播通知(TODO), 设备预置位(TODO) + * @author lin */ @Component public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java index 8dfd23312..20316e7ee 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java +++ b/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; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import com.genersoft.iot.vmp.service.IDeviceAlarmService; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; @@ -58,6 +59,9 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Autowired private IDeviceAlarmService deviceAlarmService; + @Autowired + private IDeviceChannelService deviceChannelService; + @Override public void afterPropertiesSet() throws Exception { notifyMessageHandler.addHandler(cmdType, this); @@ -65,7 +69,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - logger.info("收到来自设备[{}]的报警通知", device.getDeviceId()); + logger.info("[收到报警通知]设备:{}", device.getDeviceId()); // 回复200 OK try { responseAck(evt, Response.OK); @@ -119,38 +123,26 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme mobilePosition.setLongitude(deviceAlarm.getLongitude()); mobilePosition.setLatitude(deviceAlarm.getLatitude()); mobilePosition.setReportSource("GPS Alarm"); - if ("WGS84".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude()); - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeGcj02(position[0]); - mobilePosition.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude()); - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeWgs84(position[0]); - mobilePosition.setLatitudeWgs84(position[1]); - }else { - mobilePosition.setLongitudeGcj02(0.00); - mobilePosition.setLatitudeGcj02(0.00); - mobilePosition.setLongitudeWgs84(0.00); - mobilePosition.setLatitudeWgs84(0.00); - } - if (userSetting.getSavePositionHistory()) { - storager.insertMobilePosition(mobilePosition); - } + + // 更新device channel 的经纬度 DeviceChannel deviceChannel = new DeviceChannel(); deviceChannel.setDeviceId(device.getDeviceId()); deviceChannel.setChannelId(channelId); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84()); - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84()); - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02()); - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02()); deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } storager.updateChannelPosition(deviceChannel); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java index d714ee4fc..0b27855fd 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java +++ b/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 // 准备回复通道信息 List deviceChannels = storage.queryChannelListInParentPlatform(parentPlatform.getServerGBId()); // 查询关联的直播通道 - List gbStreams = storage.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); + List gbStreams = storage.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); + // 回复目录信息 + List catalogs = storage.queryCatalogInPlatform(parentPlatform.getServerGBId()); List allChannels = new ArrayList<>(); - // 回复目录信息 - List catalogs = storage.queryCatalogInPlatform(parentPlatform.getServerGBId()); if (catalogs.size() > 0) { - for (PlatformCatalog catalog : catalogs) { - if (catalog.getParentId().equals(catalog.getPlatformId())) { - catalog.setParentId(parentPlatform.getDeviceGBId()); - } - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(catalog.getId()); - deviceChannel.setName(catalog.getName()); - deviceChannel.setLongitude(0.0); - deviceChannel.setLatitude(0.0); - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setStatus(1); - deviceChannel.setParental(1); - deviceChannel.setParentId(catalog.getParentId()); - deviceChannel.setRegisterWay(1); - if (catalog.getParentId() != null && catalog.getParentId().length() <= 10) { - deviceChannel.setCivilCode(catalog.getParentId()); - }else { - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - } - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setSecrecy("0"); - allChannels.add(deviceChannel); - } + allChannels.addAll(catalogs); } // 回复级联的通道 if (deviceChannels.size() > 0) { - for (DeviceChannelInPlatform channel : deviceChannels) { - if (channel.getCatalogId().equals(parentPlatform.getServerGBId())) { - channel.setCatalogId(parentPlatform.getDeviceGBId()); - } - DeviceChannel deviceChannel = storage.queryChannel(channel.getDeviceId(), channel.getChannelId()); - deviceChannel.setParental(0); - deviceChannel.setParentId(channel.getCatalogId()); - if (channel.getCatalogId() != null && channel.getCatalogId().length() <= 10) { - channel.setCivilCode(channel.getCatalogId()); - }else { - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - } - - allChannels.add(deviceChannel); - } + allChannels.addAll(deviceChannels); } // 回复直播的通道 if (gbStreams.size() > 0) { - for (GbStream gbStream : gbStreams) { - if (gbStream.getCatalogId().equals(parentPlatform.getServerGBId())) { - gbStream.setCatalogId(null); - } - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannel.setName(gbStream.getName()); - deviceChannel.setLongitude(gbStream.getLongitude()); - deviceChannel.setLatitude(gbStream.getLatitude()); - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); -// deviceChannel.setStatus(gbStream.isStatus()?1:0); - deviceChannel.setStatus(1); - deviceChannel.setParentId(gbStream.getCatalogId()); - deviceChannel.setRegisterWay(1); - if (gbStream.getCatalogId() != null && gbStream.getCatalogId().length() <= 10) { - deviceChannel.setCivilCode(gbStream.getCatalogId()); - }else { - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - } - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setParental(0); - deviceChannel.setSecrecy("0"); - allChannels.add(deviceChannel); - } + allChannels.addAll(gbStreams); } if (allChannels.size() > 0) { cmderFroPlatform.catalogQuery(allChannels, parentPlatform, sn, fromHeader.getTag()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java index aa91556f4..2402494cc 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java +++ b/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 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.GpsUtil; @@ -42,6 +43,9 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen @Autowired private IVideoManagerStorage storager; + @Autowired + private IDeviceChannelService deviceChannelService; + @Override public void afterPropertiesSet() throws Exception { notifyMessageHandler.addHandler(cmdType, this); @@ -79,38 +83,26 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen mobilePosition.setAltitude(0.0); } mobilePosition.setReportSource("Mobile Position"); - if ("WGS84".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude()); - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeGcj02(position[0]); - mobilePosition.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude()); - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeWgs84(position[0]); - mobilePosition.setLatitudeWgs84(position[1]); - }else { - mobilePosition.setLongitudeGcj02(0.00); - mobilePosition.setLatitudeGcj02(0.00); - mobilePosition.setLongitudeWgs84(0.00); - mobilePosition.setLatitudeWgs84(0.00); - } - if (userSetting.getSavePositionHistory()) { - storager.insertMobilePosition(mobilePosition); - } + + // 更新device channel 的经纬度 DeviceChannel deviceChannel = new DeviceChannel(); deviceChannel.setDeviceId(device.getDeviceId()); deviceChannel.setChannelId(mobilePosition.getChannelId()); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84()); - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84()); - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02()); - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02()); deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } storager.updateChannelPosition(deviceChannel); //回复 200 OK responseAck(evt, Response.OK); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java index 0e9796198..7f6f81775 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java +++ b/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 Element snElement = rootElement.element("SN"); String sn = snElement.getText(); // 准备回复通道信息 - List deviceChannelInPlatforms = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId()); + List deviceChannelInPlatforms = storager.queryChannelWithCatalog(parentPlatform.getServerGBId()); // 查询关联的直播通道 - List gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); + List gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); // 回复目录信息 - List catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId()); + List catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId()); List allChannels = new ArrayList<>(); + if (catalogs.size() > 0) { - for (PlatformCatalog catalog : catalogs) { - if (catalog.getParentId().equals(catalog.getPlatformId())) { - catalog.setParentId(parentPlatform.getDeviceGBId()); - } - DeviceChannel deviceChannel = new DeviceChannel(); - // 通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 - deviceChannel.setChannelType(2); - deviceChannel.setChannelId(catalog.getId()); - deviceChannel.setName(catalog.getName()); - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setStatus(1); - deviceChannel.setParental(1); - deviceChannel.setParentId(catalog.getParentId()); - deviceChannel.setRegisterWay(1); - if (catalog.getParentId() != null && catalog.getParentId().length() < 10) { - deviceChannel.setCivilCode(catalog.getParentId()); - }else { - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - } - allChannels.add(deviceChannel); - } + allChannels.addAll(catalogs); } // 回复级联的通道 if (deviceChannelInPlatforms.size() > 0) { - for (DeviceChannelInPlatform channel : deviceChannelInPlatforms) { - if (channel.getCatalogId().equals(parentPlatform.getServerGBId())) { - channel.setCatalogId(parentPlatform.getDeviceGBId()); - } - DeviceChannel deviceChannel = storage.queryChannel(channel.getDeviceId(), channel.getChannelId()); - // 通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 - deviceChannel.setChannelType(0); - deviceChannel.setParental(0); - deviceChannel.setParentId(channel.getCatalogId()); - if (channel.getCatalogId() != null && channel.getCatalogId().length() < 10) { - deviceChannel.setCivilCode(channel.getCatalogId()); - }else { - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - } - allChannels.add(deviceChannel); - } + allChannels.addAll(deviceChannelInPlatforms); } // 回复直播的通道 if (gbStreams.size() > 0) { - for (GbStream gbStream : gbStreams) { - if (gbStream.getCatalogId().equals(parentPlatform.getServerGBId())) { - gbStream.setCatalogId(null); - } - DeviceChannel deviceChannel = new DeviceChannel(); - // 通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 - deviceChannel.setChannelType(1); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannel.setName(gbStream.getName()); - deviceChannel.setLongitude(gbStream.getLongitude()); - deviceChannel.setLatitude(gbStream.getLatitude()); - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); -// deviceChannel.setStatus(gbStream.isStatus()?1:0); - deviceChannel.setStatus(1); - deviceChannel.setParentId(gbStream.getCatalogId()); - deviceChannel.setRegisterWay(1); - if (gbStream.getCatalogId() != null && gbStream.getCatalogId().length() < 10) { - deviceChannel.setCivilCode(gbStream.getCatalogId()); - }else { - deviceChannel.setCivilCode(parentPlatform.getAdministrativeDivision()); - } - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setParental(0); - deviceChannel.setSecrecy("0"); - allChannels.add(deviceChannel); - } + allChannels.addAll(gbStreams); } if (allChannels.size() > 0) { cmderFroPlatform.catalogQuery(allChannels, parentPlatform, sn, fromHeader.getTag()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java index 8234cb152..19183e07e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java +++ b/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 @Override public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { - logger.info("接收到DeviceInfo查询消息"); + logger.info("[DeviceInfo查询]消息"); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); try { // 回复200 OK diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java index 2766474e1..ff71a922c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java +++ b/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 int sumNum = Integer.parseInt(sumNumElement.getText()); if (sumNum == 0) { + logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); // 数据已经完整接收 storager.cleanChannelsForDevice(take.getDevice().getDeviceId()); catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); @@ -125,18 +126,14 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp if (channelDeviceElement == null) { continue; } - //by brewswang - // if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置 - // processNotifyMobilePosition(evt, itemDevice); - // } - DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device); + DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null); deviceChannel.setDeviceId(take.getDevice().getDeviceId()); channelList.add(deviceChannel); } int sn = Integer.parseInt(snElement.getText()); catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList); - logger.info("收到来自设备【{}】的通道: {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum); + logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum); if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) { // 数据已经完整接收 boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId())); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java index eb2c7b8f2..f5d04be99 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java +++ b/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 device.setStreamMode("UDP"); } deviceService.updateDevice(device); -// storager.updateDevice(device); RequestMessage msg = new RequestMessage(); msg.setKey(key); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java index e562fa45d..4c723abd5 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java +++ b/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 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.GpsUtil; @@ -42,6 +43,9 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar @Autowired private IVideoManagerStorage storager; + @Autowired + private IDeviceChannelService deviceChannelService; + @Override public void afterPropertiesSet() throws Exception { responseMessageHandler.addHandler(cmdType, this); @@ -79,38 +83,25 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar mobilePosition.setAltitude(0.0); } mobilePosition.setReportSource("Mobile Position"); - if ("WGS84".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeWgs84(mobilePosition.getLongitude()); - mobilePosition.setLatitudeWgs84(mobilePosition.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeGcj02(position[0]); - mobilePosition.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - mobilePosition.setLongitudeGcj02(mobilePosition.getLongitude()); - mobilePosition.setLatitudeGcj02(mobilePosition.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(mobilePosition.getLongitude(), mobilePosition.getLatitude()); - mobilePosition.setLongitudeWgs84(position[0]); - mobilePosition.setLatitudeWgs84(position[1]); - }else { - mobilePosition.setLongitudeGcj02(0.00); - mobilePosition.setLatitudeGcj02(0.00); - mobilePosition.setLongitudeWgs84(0.00); - mobilePosition.setLatitudeWgs84(0.00); - } - if (userSetting.getSavePositionHistory()) { - storager.insertMobilePosition(mobilePosition); - } + // 更新device channel 的经纬度 DeviceChannel deviceChannel = new DeviceChannel(); deviceChannel.setDeviceId(device.getDeviceId()); deviceChannel.setChannelId(mobilePosition.getChannelId()); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setLongitudeWgs84(mobilePosition.getLongitudeWgs84()); - deviceChannel.setLatitudeWgs84(mobilePosition.getLatitudeWgs84()); - deviceChannel.setLongitudeGcj02(mobilePosition.getLongitudeGcj02()); - deviceChannel.setLatitudeGcj02(mobilePosition.getLatitudeGcj02()); deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } storager.updateChannelPosition(deviceChannel); //回复 200 OK responseAck(evt, Response.OK); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java index f3a9f65f2..a48dd203b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java @@ -26,7 +26,7 @@ import javax.sip.message.Response; @Component public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { - private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); + private final Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); private final String method = "REGISTER"; @Autowired @@ -69,11 +69,11 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformGBId); if (parentPlatformCatch == null) { - logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台缓存信息未查询到!!!", platformGBId, response.getStatusCode())); + logger.warn(String.format("[收到注册/注销%S请求]平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(),platformGBId)); return; } String action = parentPlatformCatch.getParentPlatform().getExpires().equals("0") ? "注销" : "注册"; - logger.info(String.format("收到 %s %s的%S响应", platformGBId, action, response.getStatusCode() )); + logger.info(String.format("[%s %S响应]%s ", action, response.getStatusCode(), platformGBId )); ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform(); if (parentPlatform == null) { logger.warn(String.format("收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformGBId, action, response.getStatusCode())); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java index 35ec6404f..44e133720 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java @@ -23,9 +23,10 @@ public class SipUtils { /** * 从subject读取channelId * */ - public static String getChannelIdFromHeader(Request request) { + public static String getChannelIdFromRequest(Request request) { Header subject = request.getHeader("subject"); if (subject == null) { + // 如果缺失subject return null; } return ((Subject) subject).getSubject().split(":")[0]; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java index 72206d87a..ced97efe8 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java @@ -4,6 +4,8 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.TreeType; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.utils.DateUtil; import org.dom4j.Attribute; import org.dom4j.Document; @@ -29,7 +31,7 @@ public class XmlUtil { /** * 日志服务 */ - private static Logger LOG = LoggerFactory.getLogger(XmlUtil.class); + private static Logger logger = LoggerFactory.getLogger(XmlUtil.class); /** * 解析XML为Document对象 @@ -46,7 +48,7 @@ public class XmlUtil { try { document = saxReader.read(sr); } catch (DocumentException e) { - LOG.error("解析失败", e); + logger.error("解析失败", e); } return null == document ? null : document.getRootElement(); } @@ -182,47 +184,69 @@ public class XmlUtil { return xml.getRootElement(); } - public static DeviceChannel channelContentHander(Element itemDevice, Device device){ - Element channdelNameElement = itemDevice.element("Name"); - String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : ""; - Element statusElement = itemDevice.element("Status"); - String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON"; + private enum ChannelType{ + CivilCode, BusinessGroup,VirtualOrganization,Other + } + + public static DeviceChannel channelContentHander(Element itemDevice, Device device, String event){ DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setName(channelName); + deviceChannel.setDeviceId(device.getDeviceId()); Element channdelIdElement = itemDevice.element("DeviceID"); - String channelId = channdelIdElement != null ? channdelIdElement.getTextTrim().toString() : ""; - deviceChannel.setChannelId(channelId); - // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 - if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) { - deviceChannel.setStatus(1); + if (channdelIdElement == null) { + logger.warn("解析Catalog消息时发现缺少 DeviceID"); + return null; } - if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { - deviceChannel.setStatus(0); + String channelId = channdelIdElement.getTextTrim(); + if (StringUtils.isEmpty(channelId)) { + logger.warn("解析Catalog消息时发现缺少 DeviceID"); + return null; + } + deviceChannel.setChannelId(channelId); + if (event != null && !event.equals(CatalogEvent.ADD) && !event.equals(CatalogEvent.UPDATE)) { + // 除了ADD和update情况下需要识别全部内容, + return deviceChannel; } - deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); - deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); - deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); - deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode")); - deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); - deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); - String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID"); - if (XmlUtil.getText(itemDevice, "Parental") == null - || XmlUtil.getText(itemDevice, "Parental").equals("")) { - if (deviceChannel.getChannelId().length() <= 10 - || (deviceChannel.getChannelId().length() == 20 && ( - Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 215 - || Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216 - ) - ) - ) { - deviceChannel.setParental(1); - }else { - deviceChannel.setParental(0); + ChannelType channelType = ChannelType.Other; + if (channelId.length() <= 8) { + channelType = ChannelType.CivilCode; + }else { + if (channelId.length() == 20) { + int code = Integer.parseInt(channelId.substring(10, 13)); + switch (code){ + case 215: + channelType = ChannelType.BusinessGroup; + break; + case 216: + channelType = ChannelType.VirtualOrganization; + break; + default: + break; + + } } - } else { - // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 - deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0); + } + + Element channdelNameElement = itemDevice.element("Name"); + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim() : ""; + deviceChannel.setName(channelName); + + String civilCode = XmlUtil.getText(itemDevice, "CivilCode"); + deviceChannel.setCivilCode(civilCode); + if (channelType == ChannelType.CivilCode && civilCode == null) { + deviceChannel.setParental(1); + // 行政区划如果没有传递具体值,则推测一个 + if (channelId.length() > 2) { + deviceChannel.setCivilCode(channelId.substring(0, channelId.length() - 2)); + } + } + if (channelType.equals(ChannelType.CivilCode)) { + // 行政区划其他字段没必要识别了,默认在线即可 + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + return deviceChannel; } /** * 行政区划展示设备树与业务分组展示设备树是两种不同的模式 @@ -230,7 +254,17 @@ public class XmlUtil { * 河北省 * --> 石家庄市 * --> 摄像头 - * --> 正定县 + *String parentId = XmlUtil.getText(itemDevice, "ParentID"); + if (parentId != null) { + if (parentId.contains("/")) { + String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1); + String businessGroup = parentId.substring(0, parentId.indexOf("/")); + deviceChannel.setParentId(lastParentId); + }else { + deviceChannel.setParentId(parentId); + } + } + deviceCh --> 正定县 * --> 摄像头 * --> 摄像头 * @@ -243,59 +277,88 @@ public class XmlUtil { * --> 摄像头 */ String parentId = XmlUtil.getText(itemDevice, "ParentID"); + String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID"); if (parentId != null) { if (parentId.contains("/")) { String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1); + if (businessGroupID == null) { + businessGroupID = parentId.substring(0, parentId.indexOf("/")); + } deviceChannel.setParentId(lastParentId); }else { deviceChannel.setParentId(parentId); } } deviceChannel.setBusinessGroupId(businessGroupID); + if (channelType.equals(ChannelType.BusinessGroup) || channelType.equals(ChannelType.VirtualOrganization)) { + // 业务分组和虚拟组织 其他字段没必要识别了,默认在线即可 + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + return deviceChannel; + } -// else { -// if (deviceChannel.getChannelId().length() <= 10) { // 此时为行政区划, 上下级行政区划使用DeviceId关联 -// deviceChannel.setParentId(deviceChannel.getChannelId().substring(0, deviceChannel.getChannelId().length() - 2)); -// }else if (deviceChannel.getChannelId().length() == 20) { -// if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织 -// deviceChannel.setBusinessGroupId(businessGroupID); -// }else if (Integer.parseInt(device.getDeviceId().substring(10, 13) )== 118) {//NVR 如果上级设备编号是NVR则直接将NVR的编号设置给通道的上级编号 -// deviceChannel.setParentId(device.getDeviceId()); -// }else if (deviceChannel.getCivilCode() != null) { -// // 设备, 无parentId的20位是使用CivilCode表示上级的设备, -// // 注:215 业务分组是需要有parentId的 -// deviceChannel.setParentId(deviceChannel.getCivilCode()); -// } -// }else { -// deviceChannel.setParentId(deviceChannel.getDeviceId()); -// } -// } + Element statusElement = itemDevice.element("Status"); - if (XmlUtil.getText(itemDevice, "SafetyWay") == null - || XmlUtil.getText(itemDevice, "SafetyWay") == "") { + if (statusElement != null) { + String status = statusElement.getTextTrim().trim(); + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) { + deviceChannel.setStatus(1); + } + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { + deviceChannel.setStatus(0); + } + }else { + deviceChannel.setStatus(1); + } + // 识别自带的目录标识 + String parental = XmlUtil.getText(itemDevice, "Parental"); + // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 + if (!StringUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) { + deviceChannel.setParental(0); + }else { + deviceChannel.setParental(1); + } + + + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); + + String safetyWay = XmlUtil.getText(itemDevice, "SafetyWay"); + if (StringUtils.isEmpty(safetyWay)) { deviceChannel.setSafetyWay(0); } else { - deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay"))); + deviceChannel.setSafetyWay(Integer.parseInt(safetyWay)); } - if (XmlUtil.getText(itemDevice, "RegisterWay") == null - || XmlUtil.getText(itemDevice, "RegisterWay") == "") { + + String registerWay = XmlUtil.getText(itemDevice, "RegisterWay"); + if (StringUtils.isEmpty(registerWay)) { deviceChannel.setRegisterWay(1); } else { - deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay"))); + deviceChannel.setRegisterWay(Integer.parseInt(registerWay)); } - deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); + if (XmlUtil.getText(itemDevice, "Certifiable") == null || XmlUtil.getText(itemDevice, "Certifiable") == "") { deviceChannel.setCertifiable(0); } else { deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable"))); } + if (XmlUtil.getText(itemDevice, "ErrCode") == null || XmlUtil.getText(itemDevice, "ErrCode") == "") { deviceChannel.setErrCode(0); } else { deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode"))); } + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime")); deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy")); deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress")); @@ -304,43 +367,23 @@ public class XmlUtil { } else { deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port"))); } - deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) { - deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude"))); + + + String longitude = XmlUtil.getText(itemDevice, "Longitude"); + if (NumericUtil.isDouble(longitude)) { + deviceChannel.setLongitude(Double.parseDouble(longitude)); } else { deviceChannel.setLongitude(0.00); } - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) { - deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude"))); + String latitude = XmlUtil.getText(itemDevice, "Latitude"); + if (NumericUtil.isDouble(latitude)) { + deviceChannel.setLatitude(Double.parseDouble(latitude)); } else { deviceChannel.setLatitude(0.00); } deviceChannel.setGpsTime(DateUtil.getNow()); - if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { - if ("WGS84".equals(device.getGeoCoordSys())) { - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeGcj02(position[0]); - deviceChannel.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(position[0]); - deviceChannel.setLatitudeWgs84(position[1]); - }else { - deviceChannel.setLongitudeGcj02(0.00); - deviceChannel.setLatitudeGcj02(0.00); - deviceChannel.setLongitudeWgs84(0.00); - deviceChannel.setLatitudeWgs84(0.00); - } - }else { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - } + + if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) { //兼容INFO中的信息 Element info = itemDevice.element("Info"); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java index 249ec03c7..c8f90402d 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java @@ -136,4 +136,12 @@ public class AssistRESTfulUtils { return sendGet(mediaServerItem, "api/record/file/duration",param, callback); } + public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){ + Map param = new HashMap<>(); + param.put("app",app); + param.put("stream",stream); + param.put("callId",callId); + return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback); + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 95c035f57..0dc6bd387 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -87,6 +87,9 @@ public class ZLMHttpHookListener { @Autowired private VideoStreamSessionManager sessionManager; + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + /** * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 * @@ -99,12 +102,13 @@ public class ZLMHttpHookListener { logger.debug("[ ZLM HOOK ] on_server_keepalive API调用,参数:" + json.toString()); } String mediaServerId = json.getString("mediaServerId"); - List subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive); + List subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive); if (subscribes != null && subscribes.size() > 0) { for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { subscribe.response(null, json); } } + mediaServerService.updateMediaServerKeepalive(mediaServerId, json.getJSONObject("data")); JSONObject ret = new JSONObject(); ret.put("code", 0); @@ -164,7 +168,7 @@ public class ZLMHttpHookListener { logger.debug("[ ZLM HOOK ]on_play API调用,参数:" + JSON.toJSONString(param)); } String mediaServerId = param.getMediaServerId(); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json); + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json); if (subscribe != null ) { MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); if (mediaInfo != null) { @@ -200,7 +204,9 @@ public class ZLMHttpHookListener { logger.info("[ ZLM HOOK ]on_publish API调用,参数:" + json.toString()); JSONObject ret = new JSONObject(); - if (!"rtp".equals(param.getApp()) && !"broadcast".equals(param.getApp())) { + String mediaServerId = json.getString("mediaServerId"); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (!"rtp".equals(param.getApp())) { // 推流鉴权 if (param.getParams() == null) { logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)"); @@ -231,6 +237,12 @@ public class ZLMHttpHookListener { streamAuthorityInfo.setSign(sign); // 鉴权通过 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); + // 通知assist新的callId + if (mediaInfo != null) { + assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null); + } + }else { + zlmMediaListManager.sendStreamEvent(param.getApp(),param.getStream(), param.getMediaServerId()); } ret.put("code", 0); @@ -240,10 +252,9 @@ public class ZLMHttpHookListener { ret.put("enable_audio", true); } - String mediaServerId = json.getString("mediaServerId"); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); + + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); if (subscribe != null) { - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); if (mediaInfo != null) { subscribe.response(mediaInfo, json); }else { @@ -270,10 +281,12 @@ public class ZLMHttpHookListener { ret.put("mp4_max_second", 10); ret.put("enable_mp4", true); ret.put("enable_audio", true); + } } + return new ResponseEntity(ret.toString(), HttpStatus.OK); } @@ -364,7 +377,7 @@ public class ZLMHttpHookListener { logger.debug("[ ZLM HOOK ]on_shell_login API调用,参数:" + json.toString()); } String mediaServerId = json.getString("mediaServerId"); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json); + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_shell_login, json); if (subscribe != null ) { MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); if (mediaInfo != null) { @@ -390,7 +403,7 @@ public class ZLMHttpHookListener { logger.info("[ ZLM HOOK ]on_stream_changed API调用,参数:" + JSONObject.toJSONString(item)); String mediaServerId = item.getMediaServerId(); JSONObject json = (JSONObject) JSON.toJSON(item); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json); + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); if (subscribe != null ) { MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); if (mediaInfo != null) { @@ -438,7 +451,6 @@ public class ZLMHttpHookListener { redisCatchStorage.stopPlayback(streamInfo.getDeviceID(), streamInfo.getChannelId(), streamInfo.getStream(), null); } - } }else { if (!"rtp".equals(app)){ @@ -451,7 +463,6 @@ public class ZLMHttpHookListener { StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, tracks, streamAuthorityInfo.getCallId()); item.setStreamInfo(streamInfoByAppAndStream); - redisCatchStorage.addStream(mediaServerItem, type, app, stream, item); if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal() || item.getOriginType() == OriginType.RTMP_PUSH.ordinal() @@ -459,20 +470,6 @@ public class ZLMHttpHookListener { item.setSeverId(userSetting.getServerId()); zlmMediaListManager.addPush(item); } - -// List gbStreams = new ArrayList<>(); -// if (streamPushItem == null || streamPushItem.getGbId() == null) { -// GbStream gbStream = storager.getGbStream(app, streamId); -// gbStreams.add(gbStream); -// }else { -// if (streamPushItem.getGbId() != null) { -// gbStreams.add(streamPushItem); -// } -// } -// if (gbStreams.size() > 0) { -// eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON); -// } - }else { // 兼容流注销时类型从redis记录获取 MediaItem mediaItem = redisCatchStorage.getStreamInfo(app, stream, mediaServerId); @@ -616,16 +613,21 @@ public class ZLMHttpHookListener { } String remoteAddr = request.getRemoteAddr(); jsonObject.put("ip", remoteAddr); - List subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started); + List subscribes = this.subscribe.getSubscribes(HookType.on_server_started); if (subscribes != null && subscribes.size() > 0) { for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { subscribe.response(null, jsonObject); } } + + ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(jsonObject, ZLMServerConfig.class); + if (zlmServerConfig !=null ) { + mediaServerService.zlmServerOnline(zlmServerConfig); + } JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); + return new ResponseEntity<>(ret.toString(),HttpStatus.OK); } private Map urlParamToMap(String params) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java index ffd8ec90f..57b6d81f9 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java @@ -1,12 +1,16 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.media.zlm.dto.HookType; +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; /** * @description:针对 ZLMediaServer的hook事件订阅 @@ -16,51 +20,39 @@ import java.util.concurrent.ConcurrentHashMap; @Component public class ZLMHttpHookSubscribe { - public enum HookType{ - on_flow_report, - on_http_access, - on_play, - on_publish, - on_record_mp4, - on_rtsp_auth, - on_rtsp_realm, - on_shell_login, - on_stream_changed, - on_stream_none_reader, - on_stream_not_found, - on_server_started, - on_server_keepalive - } - @FunctionalInterface public interface Event{ void response(MediaServerItem mediaServerItem, JSONObject response); } - private Map> allSubscribes = new ConcurrentHashMap<>(); + private Map> allSubscribes = new ConcurrentHashMap<>(); - public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) { - allSubscribes.computeIfAbsent(type, k -> new ConcurrentHashMap<>()).put(hookResponse, event); + public void addSubscribe(IHookSubscribe hookSubscribe, ZLMHttpHookSubscribe.Event event) { + if (hookSubscribe.getExpires() == null) { + // 默认5分钟过期 + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5)); + hookSubscribe.setExpires(expiresInstant); + } + allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event); } - public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) { + public ZLMHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) { ZLMHttpHookSubscribe.Event event= null; - Map eventMap = allSubscribes.get(type); + Map eventMap = allSubscribes.get(type); if (eventMap == null) { return null; } - for (JSONObject key : eventMap.keySet()) { + for (IHookSubscribe key : eventMap.keySet()) { Boolean result = null; - for (String s : key.keySet()) { + for (String s : key.getContent().keySet()) { if (result == null) { - result = key.getString(s).equals(hookResponse.getString(s)); + result = key.getContent().getString(s).equals(hookResponse.getString(s)); }else { - if (key.getString(s) == null) { + if (key.getContent().getString(s) == null) { continue; } - result = result && key.getString(s).equals(hookResponse.getString(s)); + result = result && key.getContent().getString(s).equals(hookResponse.getString(s)); } - } if (null != result && result) { event = eventMap.get(key); @@ -69,26 +61,30 @@ public class ZLMHttpHookSubscribe { return event; } - public void removeSubscribe(HookType type, JSONObject hookResponse) { - Map eventMap = allSubscribes.get(type); + public void removeSubscribe(IHookSubscribe hookSubscribe) { + Map eventMap = allSubscribes.get(hookSubscribe.getHookType()); if (eventMap == null) { return; } - Set> entries = eventMap.entrySet(); + Set> entries = eventMap.entrySet(); if (entries.size() > 0) { - List> entriesToRemove = new ArrayList<>(); - for (Map.Entry entry : entries) { - JSONObject key = entry.getKey(); + List> entriesToRemove = new ArrayList<>(); + for (Map.Entry entry : entries) { + JSONObject content = entry.getKey().getContent(); + if (content == null || content.size() == 0) { + entriesToRemove.add(entry); + continue; + } Boolean result = null; - for (String s : key.keySet()) { + for (String s : content.keySet()) { if (result == null) { - result = key.getString(s).equals(hookResponse.getString(s)); + result = content.getString(s).equals(hookSubscribe.getContent().getString(s)); }else { - if (key.getString(s) == null) { + if (content.getString(s) == null) { continue; } - result = result && key.getString(s).equals(hookResponse.getString(s)); + result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s)); } } if (null != result && result){ @@ -97,7 +93,7 @@ public class ZLMHttpHookSubscribe { } if (!CollectionUtils.isEmpty(entriesToRemove)) { - for (Map.Entry entry : entriesToRemove) { + for (Map.Entry entry : entriesToRemove) { entries.remove(entry); } } @@ -111,17 +107,25 @@ public class ZLMHttpHookSubscribe { * @return */ public List getSubscribes(HookType type) { - // ZLMHttpHookSubscribe.Event event= null; - Map eventMap = allSubscribes.get(type); + Map eventMap = allSubscribes.get(type); if (eventMap == null) { return null; } List result = new ArrayList<>(); - for (JSONObject key : eventMap.keySet()) { + for (IHookSubscribe key : eventMap.keySet()) { result.add(eventMap.get(key)); } return result; } + public List getAll(){ + ArrayList result = new ArrayList<>(); + Collection> values = allSubscribes.values(); + for (Map value : values) { + result.addAll(value.keySet()); + } + return result; + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java index 693dda16d..a8b4a8d97 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.media.zlm.dto.*; +import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamPushService; import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; @@ -63,125 +64,49 @@ public class ZLMMediaListManager { @Autowired private UserSetting userSetting; - private Map channelOnlineEvents = new ConcurrentHashMap<>(); + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + @Autowired + private IMediaServerService mediaServerService; - public void updateMediaList(MediaServerItem mediaServerItem) { - storager.clearMediaList(); - - // 使用异步的当时更新媒体流列表 - zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ - if (mediaList == null) { - return; - } - String dataStr = mediaList.getString("data"); - - Integer code = mediaList.getInteger("code"); - Map result = new HashMap<>(); - List streamPushItems = null; - // 获取所有的国标关联 -// List gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId()); - if (code == 0 ) { - if (dataStr != null) { - streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem); - } - }else { - logger.warn("更新视频流失败,错误code: " + code); - } - - if (streamPushItems != null) { - storager.updateMediaList(streamPushItems); - for (StreamPushItem streamPushItem : streamPushItems) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("app", streamPushItem.getApp()); - jsonObject.put("stream", streamPushItem.getStream()); - jsonObject.put("mediaServerId", mediaServerItem.getId()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject, - (MediaServerItem mediaServerItemInuse, JSONObject response)->{ - updateMedia(mediaServerItem, response.getString("app"), response.getString("stream")); - } - ); - } - } - })); - - } - - public void addMedia(MediaServerItem mediaServerItem, String app, String streamId) { - //使用异步更新推流 - updateMedia(mediaServerItem, app, streamId); - } + private Map channelOnPublishEvents = new ConcurrentHashMap<>(); public StreamPushItem addPush(MediaItem mediaItem) { // 查找此直播流是否存在redis预设gbId StreamPushItem transform = streamPushService.transform(mediaItem); StreamPushItem pushInDb = streamPushService.getPush(mediaItem.getApp(), mediaItem.getStream()); + transform.setPushIng(mediaItem.isRegist()); transform.setUpdateTime(DateUtil.getNow()); transform.setPushTime(DateUtil.getNow()); + transform.setSelf(userSetting.getServerId().equals(mediaItem.getSeverId())); if (pushInDb == null) { transform.setCreateTime(DateUtil.getNow()); streamPushMapper.add(transform); }else { streamPushMapper.update(transform); - - -// if (!StringUtils.isEmpty(pushInDb.getGbId())) { -// List gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId()); -// if (gbStreamList != null && gbStreamList.size() == 1) { -// transform.setGbStreamId(gbStreamList.get(0).getGbStreamId()); -// transform.setPlatformId(gbStreamList.get(0).getPlatformId()); -// transform.setCatalogId(gbStreamList.get(0).getCatalogId()); -// transform.setGbId(gbStreamList.get(0).getGbId()); -// gbStreamMapper.update(transform); -// streamPushMapper.del(gbStreamList.get(0).getApp(), gbStreamList.get(0).getStream()); -// }else { -// transform.setCreateTime(DateUtil.getNow()); -// transform.setUpdateTime(DateUtil.getNow()); -// gbStreamMapper.add(transform); -// } - // 通知通道上线 -// if (transform != null) { -// if (channelOnlineEvents.get(transform.getGbId()) != null) { -// channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream(), transform.getServerId()); -// channelOnlineEvents.remove(transform.getGbId()); -// } -// } -// } + gbStreamMapper.updateMediaServer(mediaItem.getApp(), mediaItem.getStream(), mediaItem.getMediaServerId()); + } + if (transform != null) { + if (getChannelOnlineEventLister(transform.getApp(), transform.getStream()) != null) { + getChannelOnlineEventLister(transform.getApp(), transform.getStream()).run(transform.getApp(), transform.getStream(), transform.getServerId()); + removedChannelOnlineEventLister(transform.getApp(), transform.getStream()); + } } - - - return transform; } - - public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) { - //使用异步更新推流 - zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{ - - if (json == null) { - return; + public void sendStreamEvent(String app, String stream, String mediaServerId) { + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + // 查看推流状态 + if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) { + if (getChannelOnlineEventLister(app, stream) != null) { + getChannelOnlineEventLister(app, stream).run(app, stream, mediaServerId); + removedChannelOnlineEventLister(app, stream); } - String dataStr = json.getString("data"); - - Integer code = json.getInteger("code"); - Map result = new HashMap<>(); - List streamPushItems = null; - if (code == 0 ) { - if (dataStr != null) { - streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem); - } - }else { - logger.warn("更新视频流失败,错误code: " + code); - } - - if (streamPushItems != null && streamPushItems.size() == 1) { - storager.updateMedia(streamPushItems.get(0)); - } - }); + } } - public int removeMedia(String app, String streamId) { // 查找是否关联了国标, 关联了不删除, 置为离线 GbStream gbStream = gbStreamMapper.selectOne(app, streamId); @@ -189,48 +114,21 @@ public class ZLMMediaListManager { if (gbStream == null) { result = storager.removeMedia(app, streamId); }else { - // TODO 暂不设置为离线 result =storager.mediaOffline(app, streamId); } return result; } - public void addChannelOnlineEventLister(String key, ChannelOnlineEvent callback) { - this.channelOnlineEvents.put(key,callback); + public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) { + this.channelOnPublishEvents.put(app + "_" + stream, callback); } - public void removedChannelOnlineEventLister(String key) { - this.channelOnlineEvents.remove(key); + public void removedChannelOnlineEventLister(String app, String stream) { + this.channelOnPublishEvents.remove(app + "_" + stream); } + public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) { + return this.channelOnPublishEvents.get(app + "_" + stream); + } - -// public void clearAllSessions() { -// logger.info("清空所有国标相关的session"); -// JSONObject allSessionJSON = zlmresTfulUtils.getAllSession(); -// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); -// HashSet allLocalPorts = new HashSet(); -// if (allSessionJSON.getInteger("code") == 0) { -// JSONArray data = allSessionJSON.getJSONArray("data"); -// if (data.size() > 0) { -// for (int i = 0; i < data.size(); i++) { -// JSONObject sessionJOSN = data.getJSONObject(i); -// Integer local_port = sessionJOSN.getInteger("local_port"); -// if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){ -// allLocalPorts.add(sessionJOSN.getInteger("local_port") + ""); -// } -// } -// } -// } -// if (allLocalPorts.size() > 0) { -// List result = new ArrayList<>(allLocalPorts); -// String localPortSStr = String.join(",", result); -// zlmresTfulUtils.kickSessions(localPortSStr); -// } -// } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java index 60e79f785..6c3ad907c 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java @@ -87,7 +87,7 @@ public class ZLMRTPServerFactory { return result; } - public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc) { + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port) { int result = -1; // 查询此rtp server 是否已经存在 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId); @@ -105,7 +105,11 @@ public class ZLMRTPServerFactory { param.put("enable_tcp", 1); param.put("stream_id", streamId); // 推流端口设置0则使用随机端口 - param.put("port", 0); + if (port == null) { + param.put("port", 0); + }else { + param.put("port", port); + } param.put("ssrc", ssrc); JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); @@ -280,8 +284,10 @@ public class ZLMRTPServerFactory { * 查询待转推的流是否就绪 */ public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId); - return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online") != null && mediaInfo.getBoolean("online")); + JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId); + return (mediaInfo.getInteger("code") == 0 + && mediaInfo.getJSONArray("data") != null + && mediaInfo.getJSONArray("data").size() > 0); } /** diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index 138af7ae4..b24d0a1a4 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -6,22 +6,22 @@ import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForServerStarted; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; +import java.time.Instant; import java.util.*; +import java.util.concurrent.TimeUnit; @Component @Order(value=1) @@ -37,18 +37,12 @@ public class ZLMRunner implements CommandLineRunner { @Autowired private ZLMHttpHookSubscribe hookSubscribe; - @Autowired - private IStreamProxyService streamProxyService; - @Autowired private EventPublisher publisher; @Autowired private IMediaServerService mediaServerService; - @Autowired - private IRedisCatchStorage redisCatchStorage; - @Autowired private MediaConfig mediaConfig; @@ -67,26 +61,24 @@ public class ZLMRunner implements CommandLineRunner { mediaServerService.updateToDatabase(mediaSerItem); } mediaServerService.syncCatchFromDatabase(); + HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started(); +// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.SECONDS.toSeconds(60)); +// hookSubscribeForStreamChange.setExpires(expiresInstant); // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,new JSONObject(), + hookSubscribe.addSubscribe(hookSubscribeForServerStarted, (MediaServerItem mediaServerItem, JSONObject response)->{ ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); if (zlmServerConfig !=null ) { if (startGetMedia != null) { startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } } - mediaServerService.zlmServerOnline(zlmServerConfig); } }); - // 订阅 zlm保活事件, 当zlm离线时做业务的处理 - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_keepalive,new JSONObject(), - (MediaServerItem mediaServerItem, JSONObject response)->{ - String mediaServerId = response.getString("mediaServerId"); - if (mediaServerId !=null ) { - mediaServerService.updateMediaServerKeepalive(mediaServerId, response.getJSONObject("data")); - } - }); + // 获取zlm信息 logger.info("[zlm] 等待默认zlm中..."); @@ -125,6 +117,9 @@ public class ZLMRunner implements CommandLineRunner { zlmServerConfigFirst.setIp(mediaServerItem.getIp()); zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort()); startGetMedia.remove(mediaServerItem.getId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } mediaServerService.zlmServerOnline(zlmServerConfigFirst); }else { logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接", @@ -139,6 +134,9 @@ public class ZLMRunner implements CommandLineRunner { zlmServerConfig.setIp(mediaServerItem.getIp()); zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort()); startGetMedia.remove(mediaServerItem.getId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } mediaServerService.zlmServerOnline(zlmServerConfig); } }, 2000); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java new file mode 100644 index 000000000..92172f3a9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java @@ -0,0 +1,33 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + + +import com.alibaba.fastjson.JSONObject; + +/** + * hook 订阅工厂 + * @author lin + */ +public class HookSubscribeFactory { + + public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) { + HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange(); + JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject(); + subscribeKey.put("app", app); + subscribeKey.put("stream", stream); + subscribeKey.put("regist", regist); + if (scheam != null) { + subscribeKey.put("schema", scheam); + } + subscribeKey.put("mediaServerId", mediaServerId); + hookSubscribe.setContent(subscribeKey); + + return hookSubscribe; + } + + public static HookSubscribeForServerStarted on_server_started() { + HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); + hookSubscribe.setContent(new JSONObject()); + + return hookSubscribe; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java new file mode 100644 index 000000000..0b781e6a0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-流变化 + * @author lin + */ +public class HookSubscribeForServerStarted implements IHookSubscribe{ + + private HookType hookType = HookType.on_server_started; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java new file mode 100644 index 000000000..d5b2fb81d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java @@ -0,0 +1,43 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-流变化 + * @author lin + */ +public class HookSubscribeForStreamChange implements IHookSubscribe{ + + private HookType hookType = HookType.on_stream_changed; + + private JSONObject content; + + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java new file mode 100644 index 000000000..797ab81b4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +/** + * hook类型 + * @author lin + */ + +public enum HookType { + + on_flow_report, + on_http_access, + on_play, + on_publish, + on_record_mp4, + on_rtsp_auth, + on_rtsp_realm, + on_shell_login, + on_stream_changed, + on_stream_none_reader, + on_stream_not_found, + on_server_started, + on_server_keepalive +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java new file mode 100644 index 000000000..5f2ca3380 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson.JSONObject; + +import java.time.Instant; + +/** + * zlm hook事件的参数 + * @author lin + */ +public interface IHookSubscribe { + + /** + * 获取hook类型 + * @return hook类型 + */ + HookType getHookType(); + + /** + * 获取hook的具体内容 + * @return hook的具体内容 + */ + JSONObject getContent(); + + /** + * 设置过期时间 + * @param instant 过期时间 + */ + void setExpires(Instant instant); + + /** + * 获取过期时间 + * @return 过期时间 + */ + Instant getExpires(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java index 8abac5b07..96cbfbd8c 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java @@ -4,6 +4,9 @@ import com.genersoft.iot.vmp.common.StreamInfo; import java.util.List; +/** + * @author lin + */ public class MediaItem { /** @@ -21,6 +24,11 @@ public class MediaItem { */ private String stream; + /** + * 推流鉴权Id + */ + private String callId; + /** * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv */ @@ -427,4 +435,12 @@ public class MediaItem { public void setSeverId(String severId) { this.severId = severId; } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java index 91fa6198f..ff63f6fd6 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java @@ -103,6 +103,18 @@ public class StreamPushItem extends GbStream implements Comparable channels); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java index abdde6de1..0a392060b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java @@ -18,7 +18,7 @@ public interface IGbStreamService { * @param count * @return */ - PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId,String query,Boolean pushing,String mediaServerId); + PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId,String query,String mediaServerId); /** diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java index 1a9c8c787..03dbd0b31 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java @@ -47,6 +47,8 @@ public interface IMediaServerService { SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback); + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port); + void closeRTPServer(String deviceId, String channelId, String ssrc); void clearRTPServer(MediaServerItem mediaServerItem); diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java new file mode 100644 index 000000000..739e6b864 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java @@ -0,0 +1,22 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; + +import java.util.List; + +/** + * 平台关联通道管理 + * @author lin + */ +public interface IPlatformChannelService { + + /** + * 更新目录下的通道 + * @param platformId 平台编号 + * @param channelReduces 通道信息 + * @param catalogId 目录编号 + * @return + */ + int updateChannelForGB(String platformId, List channelReduces, String catalogId); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java index ac1000065..1dd74ee28 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java @@ -101,4 +101,9 @@ public interface IStreamProxyService { void zlmServerOffline(String mediaServerId); void clean(); + + /** + * 更新代理流 + */ + boolean updateStreamProxy(StreamProxyItem streamProxyItem); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java index acf0d2746..b95ec4862 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto; import com.github.pagehelper.PageInfo; @@ -44,31 +45,59 @@ public interface IStreamPushService { * 停止一路推流 * @param app 应用名 * @param streamId 流ID - * @return */ boolean stop(String app, String streamId); /** * 新的节点加入 - * @param mediaServerId - * @return */ void zlmServerOnline(String mediaServerId); /** * 节点离线 - * @param mediaServerId - * @return */ void zlmServerOffline(String mediaServerId); + /** + * 清空 + */ void clean(); + boolean saveToRandomGB(); + /** + * 批量添加 + */ void batchAdd(List streamPushExcelDtoList); + /** + * 中止多个推流 + */ boolean batchStop(List streamPushItems); + /** + * 导入时批量增加 + */ void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll); + + /** + * 全部离线 + */ + void allStreamOffline(); + + /** + * 推流离线 + */ + void offline(List offlineStreams); + + /** + * 推流上线 + */ + void online(List onlineStreams); + + /** + * 增加推流 + */ + boolean add(StreamPushItem stream); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java index e362605b3..7e2a8395b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.service; import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.github.pagehelper.PageInfo; import java.util.List; @@ -21,4 +22,8 @@ public interface IUserService { int updateUsers(User user); boolean checkPushAuthority(String callId, String sign); + + PageInfo getUsers(int page, int count); + + int changePushKey(int id, String pushKey); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java deleted file mode 100644 index 9d15c1f45..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.genersoft.iot.vmp.service; - -import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.util.List; - - -/** - * 定时查找redis中的GPS推送消息,并保存到对应的流中 - */ -@Component -public class StreamGPSSubscribeTask { - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private IVideoManagerStorage storager; - - - @Scheduled(fixedRate = 30 * 1000) //每30秒执行一次 - public void execute(){ - List gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo(); - if (gpsMsgInfo.size() > 0) { - storager.updateStreamGPS(gpsMsgInfo); - for (GPSMsgInfo msgInfo : gpsMsgInfo) { - msgInfo.setStored(true); - redisCatchStorage.updateGpsMsgInfo(msgInfo); - } - } - - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java index ead291c06..8c55986ea 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java @@ -17,7 +17,6 @@ public class DeviceAlarmServiceImpl implements IDeviceAlarmService { @Autowired private DeviceAlarmMapper deviceAlarmMapper; - @Override public PageInfo getAllAlarm(int page, int count, String deviceId, String alarmPriority, String alarmMethod, String alarmType, String startTime, String endTime) { PageHelper.startPage(page, count); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java new file mode 100644 index 000000000..99ad3dee5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java @@ -0,0 +1,165 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; +import com.genersoft.iot.vmp.service.IDeviceChannelService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.storager.dao.DeviceMapper; +import com.genersoft.iot.vmp.utils.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * @author lin + */ +@Service +public class DeviceChannelServiceImpl implements IDeviceChannelService { + + private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private DeviceChannelMapper channelMapper; + + @Autowired + private DeviceMapper deviceMapper; + + @Override + public DeviceChannel updateGps(DeviceChannel deviceChannel, Device device) { + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { + if (device == null) { + device = deviceMapper.getDeviceByDeviceId(deviceChannel.getDeviceId()); + } + + if ("WGS84".equals(device.getGeoCoordSys())) { + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeGcj02(position[0]); + deviceChannel.setLatitudeGcj02(position[1]); + }else if ("GCJ02".equals(device.getGeoCoordSys())) { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(position[0]); + deviceChannel.setLatitudeWgs84(position[1]); + }else { + deviceChannel.setLongitudeGcj02(0.00); + deviceChannel.setLatitudeGcj02(0.00); + deviceChannel.setLongitudeWgs84(0.00); + deviceChannel.setLatitudeWgs84(0.00); + } + }else { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + } + return deviceChannel; + } + + @Override + public void updateChannel(String deviceId, DeviceChannel channel) { + String channelId = channel.getChannelId(); + channel.setDeviceId(deviceId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); + if (streamInfo != null) { + channel.setStreamId(streamInfo.getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + DeviceChannel deviceChannel = channelMapper.queryChannel(deviceId, channelId); + channel = updateGps(channel, null); + if (deviceChannel == null) { + channel.setCreateTime(now); + channelMapper.add(channel); + }else { + channelMapper.update(channel); + } + channelMapper.updateChannelSubCount(deviceId,channel.getParentId()); + } + + @Override + public int updateChannels(String deviceId, List channels) { + List addChannels = new ArrayList<>(); + List updateChannels = new ArrayList<>(); + HashMap channelsInStore = new HashMap<>(); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (channels != null && channels.size() > 0) { + List channelList = channelMapper.queryChannels(deviceId, null, null, null, null); + if (channelList.size() == 0) { + for (DeviceChannel channel : channels) { + channel.setDeviceId(deviceId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); + if (streamInfo != null) { + channel.setStreamId(streamInfo.getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel.setCreateTime(now); + channel = updateGps(channel, device); + addChannels.add(channel); + } + }else { + for (DeviceChannel deviceChannel : channelList) { + channelsInStore.put(deviceChannel.getChannelId(), deviceChannel); + } + for (DeviceChannel channel : channels) { + channel.setDeviceId(deviceId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); + if (streamInfo != null) { + channel.setStreamId(streamInfo.getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel = updateGps(channel, device); + if (channelsInStore.get(channel.getChannelId()) != null) { + updateChannels.add(channel); + }else { + addChannels.add(channel); + channel.setCreateTime(now); + } + } + } + int limitCount = 300; + if (addChannels.size() > 0) { + if (addChannels.size() > limitCount) { + for (int i = 0; i < addChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > addChannels.size()) { + toIndex = addChannels.size(); + } + channelMapper.batchAdd(addChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchAdd(addChannels); + } + } + if (updateChannels.size() > 0) { + if (updateChannels.size() > limitCount) { + for (int i = 0; i < updateChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > updateChannels.size()) { + toIndex = updateChannels.size(); + } + channelMapper.batchUpdate(updateChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdate(updateChannels); + } + } + } + return addChannels.size() + updateChannels.size(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java index 9d376fb05..66f678a2c 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; @@ -55,6 +56,9 @@ public class DeviceServiceImpl implements IDeviceService { @Autowired private DeviceMapper deviceMapper; + @Autowired + private IDeviceChannelService deviceChannelService; + @Autowired private DeviceChannelMapper deviceChannelMapper; @@ -82,10 +86,10 @@ public class DeviceServiceImpl implements IDeviceService { redisCatchStorage.clearCatchByDeviceId(device.getDeviceId()); } device.setUpdateTime(now); - device.setOnline(1); - // 第一次上线 + // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询 if (device.getCreateTime() == null) { + device.setOnline(1); device.setCreateTime(now); logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); deviceMapper.add(device); @@ -93,8 +97,19 @@ public class DeviceServiceImpl implements IDeviceService { commander.deviceInfoQuery(device); sync(device); }else { - deviceMapper.update(device); - redisCatchStorage.updateDevice(device); + if(device.getOnline() == 0){ + device.setOnline(1); + device.setCreateTime(now); + logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + deviceMapper.update(device); + redisCatchStorage.updateDevice(device); + commander.deviceInfoQuery(device); + sync(device); + }else { + deviceMapper.update(device); + redisCatchStorage.updateDevice(device); + } + } // 上线添加订阅 @@ -121,6 +136,8 @@ public class DeviceServiceImpl implements IDeviceService { device.setOnline(0); redisCatchStorage.updateDevice(device); deviceMapper.update(device); + //进行通道离线 + deviceChannelMapper.offlineByDeviceId(deviceId); // 离线释放所有ssrc List ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); if (ssrcTransactions != null && ssrcTransactions.size() > 0) { @@ -143,7 +160,7 @@ public class DeviceServiceImpl implements IDeviceService { logger.info("[添加目录订阅] 设备{}", device.getDeviceId()); // 添加目录订阅 CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask); - // 提前开始刷新订阅 + // 刷新订阅 int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30); // 设置最小值为30 dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000); @@ -178,8 +195,8 @@ public class DeviceServiceImpl implements IDeviceService { MobilePositionSubscribeTask mobilePositionSubscribeTask = new MobilePositionSubscribeTask(device, sipCommander, dynamicTask); // 设置最小值为30 int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30); - // 提前开始刷新订阅 - dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog -1 ) * 1000); + // 刷新订阅 + dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog) * 1000); return true; } @@ -324,23 +341,12 @@ public class DeviceServiceImpl implements IDeviceService { private void updateDeviceChannelGeoCoordSys(Device device) { List deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId()); if (deviceChannels.size() > 0) { + List deviceChannelsForStore = new ArrayList<>(); for (DeviceChannel deviceChannel : deviceChannels) { - if ("WGS84".equals(device.getGeoCoordSys())) { - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeGcj02(position[0]); - deviceChannel.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(position[0]); - deviceChannel.setLatitudeWgs84(position[1]); - } + deviceChannelsForStore.add(deviceChannelService.updateGps(deviceChannel, device)); } + deviceChannelService.updateChannels(device.getDeviceId(), deviceChannelsForStore); } - storage.updateChannels(device.getDeviceId(), deviceChannels); } @@ -352,11 +358,11 @@ public class DeviceServiceImpl implements IDeviceService { } if (parentId == null || parentId.equals(deviceId)) { // 字根节点开始查询 - List rootNodes = getRootNodes(deviceId, "CivilCode".equals(device.getTreeType()), true, !onlyCatalog); + List rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), true, !onlyCatalog); return transportChannelsToTree(rootNodes, ""); } - if ("CivilCode".equals(device.getTreeType())) { + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) { if (parentId.length()%2 != 0) { return null; } @@ -386,7 +392,7 @@ public class DeviceServiceImpl implements IDeviceService { } // 使用业务分组展示树 - if ("BusinessGroup".equals(device.getTreeType())) { + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) { if (parentId.length() < 14 ) { return null; } @@ -406,11 +412,11 @@ public class DeviceServiceImpl implements IDeviceService { } if (parentId == null || parentId.equals(deviceId)) { // 字根节点开始查询 - List rootNodes = getRootNodes(deviceId, "CivilCode".equals(device.getTreeType()), false, true); + List rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), false, true); return rootNodes; } - if ("CivilCode".equals(device.getTreeType())) { + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) { if (parentId.length()%2 != 0) { return null; } @@ -431,7 +437,7 @@ public class DeviceServiceImpl implements IDeviceService { } // 使用业务分组展示树 - if ("BusinessGroup".equals(device.getTreeType())) { + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) { if (parentId.length() < 14 ) { return null; } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java index 4a965a0b2..8734882fe 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java @@ -1,14 +1,13 @@ package com.genersoft.iot.vmp.service.impl; import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper; import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; import com.genersoft.iot.vmp.service.IGbStreamService; import com.github.pagehelper.PageHelper; @@ -46,15 +45,15 @@ public class GbStreamServiceImpl implements IGbStreamService { private ParentPlatformMapper platformMapper; @Autowired - private SipConfig sipConfig; + private PlatformCatalogMapper catalogMapper; @Autowired private EventPublisher eventPublisher; @Override - public PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId, String query, Boolean pushing, String mediaServerId) { + public PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId, String query, String mediaServerId) { PageHelper.startPage(page, count); - List all = gbStreamMapper.selectAll(platFormId, catalogId, query, pushing, mediaServerId); + List all = gbStreamMapper.selectAll(platFormId, catalogId, query, mediaServerId); return new PageInfo<>(all); } @@ -102,16 +101,25 @@ public class GbStreamServiceImpl implements IGbStreamService { deviceChannel.setLatitude(gbStream.getLatitude()); deviceChannel.setDeviceId(platform.getDeviceGBId()); deviceChannel.setManufacture("wvp-pro"); -// deviceChannel.setStatus(gbStream.isStatus()?1:0); - deviceChannel.setStatus(1); - deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId); + deviceChannel.setStatus(gbStream.isStatus()?1:0); + deviceChannel.setRegisterWay(1); - if (catalogId.length() > 0 && catalogId.length() <= 10) { - // 父节点是行政区划,则设置CivilCode使用此行政区划 + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){ deviceChannel.setCivilCode(catalogId); - }else { - deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){ + PlatformCatalog catalog = catalogMapper.select(catalogId); + if (catalog == null) { + deviceChannel.setParentId(platform.getDeviceGBId()); + deviceChannel.setBusinessGroupId(null); + }else { + deviceChannel.setParentId(catalog.getId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + } + } + deviceChannel.setModel("live"); deviceChannel.setOwner("wvp-pro"); deviceChannel.setParental(0); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java index 12853972e..b1c6a9a52 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java @@ -1,5 +1,26 @@ package com.genersoft.iot.vmp.service.impl; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.genersoft.iot.vmp.media.zlm.ZLMRunner; +import com.genersoft.iot.vmp.service.IStreamProxyService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.StringUtils; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -14,25 +35,16 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.redis.JedisUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import okhttp3.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.stereotype.Service; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.util.StringUtils; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; import java.time.LocalDateTime; import java.util.*; @@ -47,6 +59,9 @@ public class MediaServerServiceImpl implements IMediaServerService { @Autowired private SipConfig sipConfig; + @Autowired + private ZLMRunner zlmRunner; + @Value("${server.ssl.enabled:false}") private boolean sslEnabled; @@ -120,7 +135,7 @@ public class MediaServerServiceImpl implements IMediaServerService { } @Override - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback) { + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) { if (mediaServerItem == null || mediaServerItem.getId() == null) { return null; } @@ -148,13 +163,18 @@ public class MediaServerServiceImpl implements IMediaServerService { } int rtpServerPort = mediaServerItem.getRtpProxyPort(); if (mediaServerItem.isRtpEnable()) { - rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0); + rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port); } redisUtil.set(key, mediaServerItem); return new SSRCInfo(rtpServerPort, ssrc, streamId); } } + @Override + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) { + return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null); + } + @Override public void closeRTPServer(String deviceId, String channelId, String stream) { String mediaServerId = streamSession.getMediaServerId(deviceId, channelId, stream); @@ -271,7 +291,13 @@ public class MediaServerServiceImpl implements IMediaServerService { return null; } String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId; - return (MediaServerItem)redisUtil.get(key); + MediaServerItem serverItem=(MediaServerItem)redisUtil.get(key); + if(null==serverItem){ + //zlm服务不在线,启动重连 + reloadZlm(); + serverItem=(MediaServerItem)redisUtil.get(key); + } + return serverItem; } @Override @@ -351,14 +377,15 @@ public class MediaServerServiceImpl implements IMediaServerService { */ @Override public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { - logger.info("[ZLM] 正在连接 : {} -> {}:{}", - zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); if (serverItem == null) { logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); logger.warn("请检查ZLM的配置是否与WVP的一致"); return; + }else { + logger.info("[ZLM] 正在连接 : {} -> {}:{}", + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); } serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval()); if (serverItem.getHttpPort() == 0) { @@ -463,8 +490,13 @@ public class MediaServerServiceImpl implements IMediaServerService { String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) { - logger.info("获取负载最低的节点时无在线节点"); - return null; + logger.info("获取负载最低的节点时无在线节点,启动重连机制"); + //启动重连 + reloadZlm(); + if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) { + logger.info("获取负载最低的节点时无在线节点"); + return null; + } } // 获取分数最低的,及并发最低的 @@ -595,9 +627,6 @@ public class MediaServerServiceImpl implements IMediaServerService { boolean result = false; OkHttpClient client = new OkHttpClient(); String url = String.format("http://%s:%s/index/api/record", ip, port); - - FormBody.Builder builder = new FormBody.Builder(); - Request request = new Request.Builder() .get() .url(url) @@ -629,9 +658,14 @@ public class MediaServerServiceImpl implements IMediaServerService { MediaServerItem mediaServerItem = getOne(mediaServerId); if (mediaServerItem == null) { // zlm连接重试 - - logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息"); - return; + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息,尝试重连zlm"); + reloadZlm(); + mediaServerItem = getOne(mediaServerId); + if (mediaServerItem == null) { + // zlm连接重试 + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息"); + return; + } } String key = VideoManagerConstants.MEDIA_SERVER_KEEPALIVE_PREFIX + userSetting.getServerId() + "_" + mediaServerId; int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2; @@ -648,10 +682,18 @@ public class MediaServerServiceImpl implements IMediaServerService { mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem); } for (MediaServerItem mediaServerItem : allInCatch) { - if (mediaServerItemMap.get(mediaServerItem) == null) { + if (!mediaServerItemMap.containsKey(mediaServerItem.getId())) { delete(mediaServerItem.getId()); } } } + public void reloadZlm(){ + try { + zlmRunner.run(); + Thread.sleep(500);//延迟0.5秒缓冲时间 + } catch (Exception e) { + logger.warn("尝试重连zlm失败!",e); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java index e1f54d4d5..5a277d785 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java @@ -67,9 +67,9 @@ public class MediaServiceImpl implements IMediaService { JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); JSONArray tracks = mediaJSON.getJSONArray("tracks"); if (authority) { - streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null, streamAuthorityInfo.getCallId(), true); + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,streamAuthorityInfo.getCallId(), true); }else { - streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null); + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null); } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java new file mode 100644 index 000000000..d0245502c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java @@ -0,0 +1,106 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; +import com.genersoft.iot.vmp.gb28181.bean.TreeType; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.service.IPlatformChannelService; +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformChannelMapper; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author lin + */ +@Service +public class PlatformChannelServiceImpl implements IPlatformChannelService { + + private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class); + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private PlatformCatalogMapper catalogManager; + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + EventPublisher eventPublisher; + + @Override + public int updateChannelForGB(String platformId, List channelReduces, String catalogId) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + logger.warn("更新级联通道信息时未找到平台{}的信息", platformId); + return 0; + } + Map deviceAndChannels = new HashMap<>(); + for (ChannelReduce channelReduce : channelReduces) { + channelReduce.setCatalogId(catalogId); + deviceAndChannels.put(channelReduce.getId(), channelReduce); + } + List deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet()); + // 查询当前已经存在的 + List channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces); + if (deviceAndChannelList != null) { + deviceAndChannelList.removeAll(channelIds); + } + for (Integer channelId : channelIds) { + deviceAndChannels.remove(channelId); + } + List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values()); + // 对剩下的数据进行存储 + int result = 0; + if (channelReducesToAdd.size() > 0) { + result = platformChannelMapper.addChannels(platformId, channelReducesToAdd); + // TODO 后续给平台增加控制开关以控制是否响应目录订阅 + List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform); + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); + } + + return result; + } + + private List getDeviceChannelListByChannelReduceList(List channelReduces, String catalogId, ParentPlatform platform) { + List deviceChannelList = new ArrayList<>(); + if (channelReduces.size() > 0){ + PlatformCatalog catalog = catalogManager.select(catalogId); + if (catalog == null && !catalogId.equals(platform.getServerGBId())) { + logger.warn("未查询到目录{}的信息", catalogId); + return null; + } + for (ChannelReduce channelReduce : channelReduces) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); + deviceChannel.setParental(0); + deviceChannelList.add(deviceChannel); + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){ + deviceChannel.setCivilCode(catalogId); + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){ + deviceChannel.setParentId(catalogId); + if (catalog != null) { + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + } + } + } + } + return deviceChannelList; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index a3b40bb12..5ae01031a 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -1,5 +1,21 @@ package com.genersoft.iot.vmp.service.impl; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import javax.sip.ResponseEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.async.DeferredResult; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -7,7 +23,13 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback; +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; @@ -16,6 +38,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.HookType; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; @@ -32,9 +57,11 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; +import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; + import gov.nist.javax.sip.stack.SIPDialog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -313,16 +340,10 @@ public class PlayServiceImpl implements IPlayService { // 单端口模式streamId也有变化,需要重新设置监听 if (!mediaServerItem.isRtpEnable()) { // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", stream); - subscribeKey.put("regist", true); - subscribeKey.put("schema", "rtmp"); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed,subscribeKey); - subscribeKey.put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject response)->{ + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtmp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{ logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); dynamicTask.stop(timeOutTaskKey); // hook响应 @@ -333,7 +354,7 @@ public class PlayServiceImpl implements IPlayService { // 关闭rtp server mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); // 重新开启ssrc server - mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false); + mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, finalSsrcInfo.getPort()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java index 638ea41d5..a4fa63579 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java @@ -8,6 +8,9 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.HookType; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.bean.*; @@ -270,14 +273,9 @@ public class RedisGbPlayMsgListener implements MessageListener { }, userSetting.getPlatformPlayTimeout()); // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", content.getApp()); - subscribeKey.put("stream", content.getStream()); - subscribeKey.put("regist", true); - subscribeKey.put("schema", "rtmp"); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(content.getApp(), content.getStream(), true, "rtmp", mediaServerItem.getId()); + + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ dynamicTask.stop(taskKey); responseSendItem(mediaServerItem, content, toId, serial); }); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java index be2140108..4e94d68ad 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java @@ -1,16 +1,24 @@ package com.genersoft.iot.vmp.service.impl; import com.alibaba.fastjson.JSON; +import com.genersoft.iot.vmp.gb28181.bean.HandlerCatchData; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + /** * 接收来自redis的GPS更新通知 * @author lin @@ -20,12 +28,50 @@ public class RedisGpsMsgListener implements MessageListener { private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class); + private boolean taskQueueHandlerRun = false; + @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private IVideoManagerStorage storager; + + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override public void onMessage(@NotNull Message message, byte[] bytes) { - GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class); - redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); + taskQueue.offer(message); + if (!taskQueueHandlerRun) { + taskQueueHandlerRun = true; + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class); + // 只是放入redis缓存起来 + redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); + } + taskQueueHandlerRun = false; + }); + } + } + + /** + * 定时将经纬度更新到数据库 + */ + @Scheduled(fixedRate = 2 * 1000) //每2秒执行一次 + public void execute(){ + List gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo(); + if (gpsMsgInfo.size() > 0) { + storager.updateStreamGPS(gpsMsgInfo); + for (GPSMsgInfo msgInfo : gpsMsgInfo) { + msgInfo.setStored(true); + redisCatchStorage.updateGpsMsgInfo(msgInfo); + } + } } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisPushStreamStatusMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisPushStreamStatusMsgListener.java new file mode 100644 index 000000000..50e894a62 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisPushStreamStatusMsgListener.java @@ -0,0 +1,115 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; +import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.service.bean.PushStreamStatusChangeFromRedisDto; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 接收redis发送的推流设备上线下线通知 + * @author lin + */ +@Component +public class RedisPushStreamStatusMsgListener implements MessageListener, ApplicationRunner { + + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusMsgListener.class); + + private boolean taskQueueHandlerRun = false; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private DynamicTask dynamicTask; + + + + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + // TODO 增加队列 + logger.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody())); + taskQueue.offer(message); + + if (!taskQueueHandlerRun) { + taskQueueHandlerRun = true; + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + PushStreamStatusChangeFromRedisDto statusChangeFromPushStream = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); + if (statusChangeFromPushStream == null) { + logger.warn("[REDIS消息]推流设备状态变化消息解析失败"); + return; + } + // 取消定时任务 + dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED); + if (statusChangeFromPushStream.isSetAllOffline()) { + // 所有设备离线 + streamPushService.allStreamOffline(); + } + if (statusChangeFromPushStream.getOfflineStreams() != null + && statusChangeFromPushStream.getOfflineStreams().size() > 0) { + // 更新部分设备离线 + streamPushService.offline(statusChangeFromPushStream.getOfflineStreams()); + } + if (statusChangeFromPushStream.getOnlineStreams() != null && + statusChangeFromPushStream.getOnlineStreams().size() > 0) { + // 更新部分设备上线 + streamPushService.online(statusChangeFromPushStream.getOnlineStreams()); + } + } + taskQueueHandlerRun = false; + }); + } + } + + @Override + public void run(ApplicationArguments args) throws Exception { + // 启动时设置所有推流通道离线,发起查询请求 + redisCatchStorage.sendStreamPushRequestedMsgForStatus(); + dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{ + logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); + // 五秒收不到请求就设置通道离线,然后通知上级离线 + streamPushService.allStreamOffline(); + }, 5000); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java index 07fffdcc1..fe024c076 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java @@ -3,16 +3,12 @@ package com.genersoft.iot.vmp.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; + import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.utils.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +18,7 @@ import org.springframework.stereotype.Component; /** + * 接收其他wvp发送流变化通知 * @author lin */ @Component @@ -49,7 +46,7 @@ public class RedisStreamMsgListener implements MessageListener { JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class); if (steamMsgJson == null) { - logger.warn("[REDIS的ALARM通知]消息解析失败"); + logger.warn("[收到redis 流变化]消息解析失败"); return; } String serverId = steamMsgJson.getString("serverId"); @@ -58,7 +55,7 @@ public class RedisStreamMsgListener implements MessageListener { // 自己发送的消息忽略即可 return; } - logger.info("[REDIS通知] 流变化: {}", new String(message.getBody())); + logger.info("[收到redis 流变化]: {}", new String(message.getBody())); String app = steamMsgJson.getString("app"); String stream = steamMsgJson.getString("stream"); boolean register = steamMsgJson.getBoolean("register"); @@ -75,9 +72,10 @@ public class RedisStreamMsgListener implements MessageListener { mediaItem.setOriginType(0); mediaItem.setOriginTypeStr("0"); mediaItem.setOriginTypeStr("unknown"); - - zlmMediaListManager.addPush(mediaItem); - - + if (register) { + zlmMediaListManager.addPush(mediaItem); + }else { + zlmMediaListManager.removeMedia(app, stream); + } } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java index b9bb96b1b..40c37c23a 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java @@ -3,10 +3,10 @@ package com.genersoft.iot.vmp.service.impl; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.TreeType; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; @@ -23,14 +23,19 @@ import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper; import com.genersoft.iot.vmp.service.IStreamProxyService; +import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.PageInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; import org.springframework.util.StringUtils; +import java.net.InetAddress; import java.util.*; /** @@ -48,7 +53,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { private IMediaService mediaService; @Autowired - private ZLMRESTfulUtils zlmresTfulUtils;; + private ZLMRESTfulUtils zlmresTfulUtils; @Autowired private StreamProxyMapper streamProxyMapper; @@ -62,9 +67,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Autowired private UserSetting userSetting; - @Autowired - private SipConfig sipConfig; - @Autowired private GbStreamMapper gbStreamMapper; @@ -83,6 +85,12 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Autowired private IMediaServerService mediaServerService; + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + @Override public WVPResult save(StreamProxyItem param) { @@ -99,6 +107,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { wvpResult.setMsg("保存失败"); return wvpResult; } + String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), param.getStream() ); param.setDst_url(dstUrl); @@ -108,9 +117,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { boolean saveResult; // 更新 if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { - saveResult = videoManagerStorager.updateStreamProxy(param); + saveResult = updateStreamProxy(param); }else { // 新增 - saveResult = videoManagerStorager.addStreamProxy(param); + saveResult = addStreamProxy(param); } if (saveResult) { result.append("保存成功"); @@ -124,7 +133,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { if (param.isEnable_remove_none_reader()) { del(param.getApp(), param.getStream()); }else { - videoManagerStorager.updateStreamProxy(param); + updateStreamProxy(param); } }else { @@ -147,27 +156,81 @@ public class StreamProxyServiceImpl implements IStreamProxyService { result.append(", 关联国标平台[ " + param.getPlatformGbId() + " ]失败"); } } - if (!StringUtils.isEmpty(param.getGbId())) { - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - param.setPlatformId(parentPlatform.getServerGBId()); - param.setCatalogId(parentPlatform.getCatalogId()); - String stream = param.getStream(); - StreamProxyItem streamProxyItems = platformGbStreamMapper.selectOne(param.getApp(), stream, parentPlatform.getServerGBId()); - if (streamProxyItems == null) { - platformGbStreamMapper.add(param); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), param, CatalogEvent.ADD); - } - } - } - } - wvpResult.setMsg(result.toString()); return wvpResult; } + /** + * 新增代理流 + * @param streamProxyItem + * @return + */ + private boolean addStreamProxy(StreamProxyItem streamProxyItem) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + streamProxyItem.setStreamType("proxy"); + streamProxyItem.setStatus(true); + String now = DateUtil.getNow(); + streamProxyItem.setCreateTime(now); + try { + if (streamProxyMapper.add(streamProxyItem) > 0) { + if (!StringUtils.isEmpty(streamProxyItem.getGbId())) { + if (gbStreamMapper.add(streamProxyItem) < 0) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + }else { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + result = true; + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }catch (Exception e) { + logger.error("向数据库添加流代理失败:", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + + + return result; + } + + /** + * 更新代理流 + * @param streamProxyItem + * @return + */ + @Override + public boolean updateStreamProxy(StreamProxyItem streamProxyItem) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + streamProxyItem.setStreamType("proxy"); + try { + if (streamProxyMapper.update(streamProxyItem) > 0) { + if (!StringUtils.isEmpty(streamProxyItem.getGbId())) { + if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + } else { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + result = true; + }catch (Exception e) { + e.printStackTrace(); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + @Override public JSONObject addStreamProxyToZlm(StreamProxyItem param) { JSONObject result = null; @@ -239,7 +302,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { if (jsonObject.getInteger("code") == 0) { result = true; streamProxy.setEnable(true); - videoManagerStorager.updateStreamProxy(streamProxy); + updateStreamProxy(streamProxy); } } return result; @@ -253,7 +316,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto); if (jsonObject != null && jsonObject.getInteger("code") == 0) { streamProxyDto.setEnable(false); - result = videoManagerStorager.updateStreamProxy(streamProxyDto); + result = updateStreamProxy(streamProxyDto); } } return result; diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java index 837e135c9..6c6c04b11 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; +import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; @@ -13,6 +14,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.*; import com.genersoft.iot.vmp.service.IGbStreamService; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.*; import com.genersoft.iot.vmp.utils.DateUtil; @@ -21,7 +23,10 @@ import com.github.pagehelper.PageInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; import org.springframework.util.StringUtils; import java.util.*; @@ -68,6 +73,16 @@ public class StreamPushServiceImpl implements IStreamPushService { @Autowired private IMediaServerService mediaServerService; + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + private MediaConfig mediaConfig; + + @Override public List handleJSON(String jsonData, MediaServerItem mediaServerItem) { if (jsonData == null) { @@ -132,30 +147,9 @@ public class StreamPushServiceImpl implements IStreamPushService { stream.setStreamType("push"); stream.setStatus(true); stream.setCreateTime(DateUtil.getNow()); + stream.setStreamType("push"); + stream.setMediaServerId(mediaConfig.getId()); int add = gbStreamMapper.add(stream); - - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - stream.setCatalogId(parentPlatform.getCatalogId()); - stream.setPlatformId(parentPlatform.getServerGBId()); - String streamId = stream.getStream(); - StreamProxyItem streamProxyItem = platformGbStreamMapper.selectOne(stream.getApp(), streamId, parentPlatform.getServerGBId()); - if (streamProxyItem == null) { - platformGbStreamMapper.add(stream); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD); - }else { - if (!streamProxyItem.getGbId().equals(stream.getGbId())) { - // 此流使用另一个国标Id已经与该平台关联,移除此记录 - platformGbStreamMapper.delByAppAndStreamAndPlatform(stream.getApp(), streamId, parentPlatform.getServerGBId()); - platformGbStreamMapper.add(stream); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD); - } - } - } - } - return add > 0; } @@ -181,7 +175,6 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override public StreamPushItem getPush(String app, String streamId) { - return streamPushMapper.selectOne(app, streamId); } @@ -345,31 +338,6 @@ public class StreamPushServiceImpl implements IStreamPushService { public void batchAdd(List streamPushItems) { streamPushMapper.addAll(streamPushItems); gbStreamMapper.batchAdd(streamPushItems); - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (StreamPushItem stream : streamPushItems) { - for (ParentPlatform parentPlatform : parentPlatforms) { - stream.setCatalogId(parentPlatform.getCatalogId()); - stream.setPlatformId(parentPlatform.getServerGBId()); - String streamId = stream.getStream(); - StreamProxyItem streamProxyItem = platformGbStreamMapper.selectOne(stream.getApp(), streamId, parentPlatform.getServerGBId()); - if (streamProxyItem == null) { - platformGbStreamMapper.add(stream); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD); - }else { - if (!streamProxyItem.getGbId().equals(stream.getGbId())) { - // 此流使用另一个国标Id已经与该平台关联,移除此记录 - platformGbStreamMapper.delByAppAndStreamAndPlatform(stream.getApp(), streamId, parentPlatform.getServerGBId()); - platformGbStreamMapper.add(stream); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.ADD); - stream.setGbId(streamProxyItem.getGbId()); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), stream, CatalogEvent.DEL); - } - } - } - } - } } @Override @@ -481,4 +449,58 @@ public class StreamPushServiceImpl implements IStreamPushService { } return true; } + + @Override + public void allStreamOffline() { + List onlinePushers = streamPushMapper.getOnlinePusherForGb(); + if (onlinePushers.size() == 0) { + return; + } + streamPushMapper.setAllStreamOffline(); + + // 发送通知 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF); + } + + @Override + public void offline(List offlineStreams) { + // 更新部分设备离线 + List onlinePushers = streamPushMapper.getOnlinePusherForGbInList(offlineStreams); + streamPushMapper.offline(offlineStreams); + // 发送通知 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF); + } + + @Override + public void online(List onlineStreams) { + // 更新部分设备上线streamPushService + List onlinePushers = streamPushMapper.getOfflinePusherForGbInList(onlineStreams); + streamPushMapper.online(onlineStreams); + // 发送通知 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.ON); + } + + @Override + public boolean add(StreamPushItem stream) { + stream.setUpdateTime(DateUtil.getNow()); + stream.setCreateTime(DateUtil.getNow()); + stream.setServerId(userSetting.getServerId()); + + // 放在事务内执行 + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + try { + int addStreamResult = streamPushMapper.add(stream); + if (!StringUtils.isEmpty(stream.getGbId())) { + stream.setStreamType("push"); + gbStreamMapper.add(stream); + } + dataSourceTransactionManager.commit(transactionStatus); + result = true; + }catch (Exception e) { + logger.error("批量移除流与平台的关系时错误", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java index 45cbcf6e9..0baad9664 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java @@ -14,38 +14,60 @@ import java.util.*; public class StreamPushUploadFileHandler extends AnalysisEventListener { - // 错误数据的回调,用于将错误数据发送给页面 + /** + * 错误数据的回调,用于将错误数据发送给页面 + */ private ErrorDataHandler errorDataHandler; - // 推流的业务类用于存储数据 + /** + * 推流的业务类用于存储数据 + */ private IStreamPushService pushService; - // 默认流媒体节点ID + /** + * 默认流媒体节点ID + */ private String defaultMediaServerId; - // 用于存储不加过滤的所有数据 + /** + * 用于存储不加过滤的所有数据 + */ private List streamPushItems = new ArrayList<>(); - // 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ private Map streamPushItemForSave = new HashMap<>(); - // 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表 + /** + * 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表 + */ private Map> streamPushItemsForPlatform = new HashMap<>(); - // 用于判断文件是否存在重复的app+Stream+平台ID + /** + * 用于判断文件是否存在重复的app+Stream+平台ID + */ private Set streamPushStreamSet = new HashSet<>(); - // 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 + /** + * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 + */ private BiMap gBMap = HashBiMap.create(); - // 记录错误的APP+Stream + /** + * 记录错误的APP+Stream + */ private List errorStreamList = new ArrayList<>(); - // 记录错误的国标ID + /** + * 记录错误的国标ID + */ private List errorGBList = new ArrayList<>(); - // 读取数量计数器 + /** + * 读取数量计数器 + */ private int loadedSize = 0; public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java index 01d91a55e..f5dc7b0fe 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java @@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.service.impl; import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.UserMapper; import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -11,7 +13,7 @@ import java.util.List; @Service public class UserServiceImpl implements IUserService { - + @Autowired private UserMapper userMapper; @@ -64,4 +66,16 @@ public class UserServiceImpl implements IUserService { return userMapper.checkPushAuthorityByCallIdAndSign(callId, sign).size() > 0; } } + + @Override + public PageInfo getUsers(int page, int count) { + PageHelper.startPage(page, count); + List users = userMapper.getUsers(); + return new PageInfo<>(users); + } + + @Override + public int changePushKey(int id, String pushKey) { + return userMapper.changePushKey(id,pushKey); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index 79e6b26a9..b9811da1e 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -233,4 +233,9 @@ public interface IRedisCatchStorage { * @return */ StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream); + + /** + * 发送redis消息 查询所有推流设备的状态 + */ + void sendStreamPushRequestedMsgForStatus(); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java index 96d4d958f..b2c1e1b97 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java @@ -26,22 +26,6 @@ public interface IVideoManagerStorage { */ public boolean exists(String deviceId); - /** - * 添加设备通道 - * - * @param deviceId 设备id - * @param channel 通道 - */ - public void updateChannel(String deviceId, DeviceChannel channel); - - /** - * 批量添加设备通道 - * - * @param deviceId 设备id - * @param channels 多个通道 - */ - public int updateChannels(String deviceId, List channels); - /** * 开始播放 * @param deviceId 设备id @@ -224,13 +208,6 @@ public interface IVideoManagerStorage { List queryChannelListInParentPlatform(String platformId); - /** - * 更新上级平台的通道信息 - * @param platformId - * @param channelReduces - * @return - */ - int updateChannelForGB(String platformId, List channelReduces, String catalogId); /** * 移除上级平台的通道信息 @@ -276,20 +253,6 @@ public interface IVideoManagerStorage { */ public int clearMobilePositionsByDeviceId(String deviceId); - /** - * 新增代理流 - * @param streamProxyDto - * @return - */ - public boolean addStreamProxy(StreamProxyItem streamProxyDto); - - /** - * 更新代理流 - * @param streamProxyDto - * @return - */ - public boolean updateStreamProxy(StreamProxyItem streamProxyDto); - /** * 移除代理流 * @param app @@ -334,19 +297,7 @@ public interface IVideoManagerStorage { * @param platformId * @return */ - List queryGbStreamListInPlatform(String platformId); - - /** - * 批量更新推流列表 - * @param streamPushItems - */ - void updateMediaList(List streamPushItems); - - /** - * 更新单个推流 - * @param streamPushItem - */ - void updateMedia(StreamPushItem streamPushItem); + List queryGbStreamListInPlatform(String platformId); /** * 移除单个推流 @@ -355,21 +306,6 @@ public interface IVideoManagerStorage { */ int removeMedia(String app, String stream); - - /** - * 获取但个推流 - * @param app - * @param stream - * @return - */ - StreamPushItem getMedia(String app, String stream); - - - /** - * 清空推流列表 - */ - void clearMediaList(); - /** * 设置流离线 */ @@ -445,7 +381,7 @@ public interface IVideoManagerStorage { int setDefaultCatalog(String platformId, String catalogId); - List queryCatalogInPlatform(String serverGBId); + List queryCatalogInPlatform(String serverGBId); int delRelation(PlatformCatalog platformCatalog); @@ -466,4 +402,8 @@ public interface IVideoManagerStorage { List getChannelSource(String platformId, String gbId); void updateChannelPosition(DeviceChannel deviceChannel); + + void cleanContentForPlatform(String serverGBId); + + List queryChannelWithCatalog(String serverGBId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java index ffa5cf15d..bcebb943f 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -140,6 +140,9 @@ public interface DeviceChannelMapper { @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) void offline(String deviceId, String channelId); + @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId}"}) + void offlineByDeviceId(String deviceId); + @Update(value = {"UPDATE device_channel SET status=1 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) void online(String deviceId, String channelId); @@ -329,5 +332,13 @@ public interface DeviceChannelMapper { @Select("select * from device_channel where deviceId=#{deviceId} and SUBSTRING(channelId, 11, 3)=#{typeCode}") List getBusinessGroups(String deviceId, String typeCode); - + @Select("select dc.id, dc.channelId, dc.deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " + + " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " + + " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " + + " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " + + " from device_channel dc" + + " left join platform_gb_channel pgc on dc.id = pgc.deviceChannelId" + + " left join platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" + + " where pgc.platformId=#{serverGBId}") + List queryChannelWithCatalog(String serverGBId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java index e8d23a8b4..7ed6b5ab0 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.storager.dao; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; @@ -15,10 +16,10 @@ import java.util.List; public interface GbStreamMapper { @Insert("REPLACE INTO gb_stream (app, stream, gbId, name, " + - "longitude, latitude, streamType, mediaServerId, status, createTime) VALUES" + + "longitude, latitude, streamType, mediaServerId, createTime) VALUES" + "('${app}', '${stream}', '${gbId}', '${name}', " + "'${longitude}', '${latitude}', '${streamType}', " + - "'${mediaServerId}', ${status}, '${createTime}')") + "'${mediaServerId}', '${createTime}')") @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") int add(GbStream gbStream); @@ -30,8 +31,7 @@ public interface GbStreamMapper { "streamType=#{streamType}," + "longitude=#{longitude}, " + "latitude=#{latitude}," + - "mediaServerId=#{mediaServerId}," + - "status=${status} " + + "mediaServerId=#{mediaServerId}" + "WHERE app=#{app} AND stream=#{stream}") int updateByAppAndStream(GbStream gbStream); @@ -43,8 +43,7 @@ public interface GbStreamMapper { "streamType=#{streamType}," + "longitude=#{longitude}, " + "latitude=#{latitude}," + - "mediaServerId=#{mediaServerId}," + - "status=${status} " + + "mediaServerId=#{mediaServerId}" + "WHERE gbStreamId=#{gbStreamId}") int update(GbStream gbStream); @@ -60,12 +59,10 @@ public interface GbStreamMapper { " AND gs.gbStreamId not in" + "(select pgs.gbStreamId from platform_gb_stream pgs where pgs.platformId = #{platformId}) " + " AND (gs.app LIKE '%${query}%' OR gs.stream LIKE '%${query}%' OR gs.gbId LIKE '%${query}%' OR gs.name LIKE '%${query}%') " + - " AND gs.status=1" + - " AND gs.status=0" + " AND gs.mediaServerId=#{mediaServerId} " + " order by gs.gbStreamId asc " + "") - List selectAll(String platformId, String catalogId, String query, Boolean pushing, String mediaServerId); + List selectAll(String platformId, String catalogId, String query, String mediaServerId); @Select("SELECT * FROM gb_stream WHERE app=#{app} AND stream=#{stream}") GbStream selectOne(String app, String stream); @@ -78,10 +75,18 @@ public interface GbStreamMapper { "WHERE gs.gbId = '${gbId}' AND pgs.platformId = '${platformId}'") GbStream queryStreamInPlatform(String platformId, String gbId); - @Select("SELECT gs.*, pgs.platformId as platformId, pgs.catalogId as catalogId FROM gb_stream gs " + - "LEFT JOIN platform_gb_stream pgs ON gs.gbStreamId = pgs.gbStreamId " + - "WHERE pgs.platformId = #{platformId}") - List queryGbStreamListInPlatform(String platformId); + @Select("select gt.gbId as channelId, gt.name, 'wvp-pro' as manufacture, st.status, gt.longitude, gt.latitude, pc.id as parentId," + + " '1' as registerWay, pc.civilCode, 'live' as model, 'wvp-pro' as owner, '0' as parental,'0' as secrecy" + + " from gb_stream gt " + + " left join (" + + " select sp.status, sp.app, sp.stream from stream_push sp" + + " union all" + + " select spxy.status, spxy.app, spxy.stream from stream_proxy spxy" + + " ) st on st.app = gt.app and st.stream = gt.stream" + + " left join platform_gb_stream pgs on gt.gbStreamId = pgs.gbStreamId" + + " left join platform_catalog pc on pgs.catalogId = pc.id and pgs.platformId = pc.platformId" + + " where pgs.platformId=#{platformId}") + List queryGbStreamListInPlatform(String platformId); @Select("SELECT gs.* FROM gb_stream gs LEFT JOIN platform_gb_stream pgs " + @@ -110,12 +115,12 @@ public interface GbStreamMapper { @Insert("") @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") @@ -138,4 +143,9 @@ public interface GbStreamMapper { "" + "") List selectAllForAppAndStream(List streamPushItems); + + @Update("UPDATE gb_stream " + + "SET mediaServerId=#{mediaServerId}" + + "WHERE app=#{app} AND stream=#{stream}") + void updateMediaServer(String app, String stream, String mediaServerId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java index b57e103c1..554354a8c 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java @@ -16,10 +16,10 @@ public interface ParentPlatformMapper { @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " + " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " + - " status, shareAllLiveStream, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime) " + + " status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " + " VALUES (${enable}, '${name}', '${serverGBId}', '${serverGBDomain}', '${serverIP}', ${serverPort}, '${deviceGBId}', '${deviceIp}', " + " '${devicePort}', '${username}', '${password}', '${expires}', '${keepTimeout}', '${transport}', '${characterSet}', ${ptz}, ${rtcp}, " + - " ${status}, ${shareAllLiveStream}, ${startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime})") + " ${status}, ${startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})") int addParentPlatform(ParentPlatform parentPlatform); @Update("UPDATE parent_platform " + @@ -41,12 +41,12 @@ public interface ParentPlatformMapper { "ptz=#{ptz}, " + "rtcp=#{rtcp}, " + "status=#{status}, " + - "shareAllLiveStream=#{shareAllLiveStream}, " + "startOfflinePush=${startOfflinePush}, " + "catalogGroup=#{catalogGroup}, " + "administrativeDivision=#{administrativeDivision}, " + "createTime=#{createTime}, " + "updateTime=#{updateTime}, " + + "treeType=#{treeType}, " + "catalogId=#{catalogId} " + "WHERE id=#{id}") int updateParentPlatform(ParentPlatform parentPlatform); @@ -83,9 +83,6 @@ public interface ParentPlatformMapper { @Update("UPDATE parent_platform SET status=#{online} WHERE serverGBId=#{platformGbID}" ) int updateParentPlatformStatus(String platformGbID, boolean online); - @Select("SELECT * FROM parent_platform WHERE shareAllLiveStream=true") - List selectAllAhareAllLiveStream(); - @Update(value = {" "}) int update(StreamPushItem streamPushItem); @@ -87,10 +91,11 @@ public interface StreamPushMapper { @Insert("") @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") @@ -113,8 +118,54 @@ public interface StreamPushMapper { "WHERE app=#{app} AND stream=#{stream}") int updateStatus(String app, String stream, boolean status); + @Update("UPDATE stream_push " + + "SET pushIng=${pushIng} " + + "WHERE app=#{app} AND stream=#{stream}") + int updatePushStatus(String app, String stream, boolean pushIng); + @Update("UPDATE stream_push " + "SET status=#{status} " + "WHERE mediaServerId=#{mediaServerId}") void updateStatusByMediaServerId(String mediaServerId, boolean status); + + + @Select("") + List getOnlinePusherForGbInList(List offlineStreams); + + @Update("") + void offline(List offlineStreams); + + @Select("") + List getOfflinePusherForGbInList(List onlineStreams); + + @Update("") + void online(List onlineStreams); + + @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") + List getOnlinePusherForGb(); + + @Update("UPDATE stream_push SET status=0") + void setAllStreamOffline(); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java index 5ed0a57de..c7a44fd2c 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java @@ -55,4 +55,11 @@ public interface UserMapper { @Select("select * from user where md5(pushKey) = '${sign}'") List checkPushAuthorityByCallId(String sign); + + @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") + @ResultMap(value="roleMap") + List getUsers(); + + @Update("update user set pushKey=#{pushKey} where id=#{id}") + int changePushKey(int id, String pushKey); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index d27984796..31294a082 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -479,13 +479,18 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void sendStreamChangeMsg(String type, JSONObject jsonObject) { String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; - logger.debug("[redis 流变化事件] {}: {}", key, jsonObject.toString()); + logger.info("[redis 流变化事件] {}: {}", key, jsonObject.toString()); redis.convertAndSend(key, jsonObject); } @Override public void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, MediaItem mediaItem) { + // 查找是否使用了callID + StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId); String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); + if (streamAuthorityInfo != null) { + mediaItem.setCallId(streamAuthorityInfo.getCallId()); + } redis.set(key, mediaItem); } @@ -683,21 +688,21 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void sendMobilePositionMsg(JSONObject jsonObject) { String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_MOBILE_POSITION; - logger.info("[redis 移动位置订阅通知] {}: {}", key, jsonObject.toString()); + logger.info("[redis发送通知]移动位置 {}: {}", key, jsonObject.toString()); redis.convertAndSend(key, jsonObject); } @Override public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; - logger.info("[redis 推流被请求通知] {}: {}/{}", key, msg.getApp(), msg.getStream()); + logger.info("[redis发送通知]推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream()); redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); } @Override public void sendAlarmMsg(AlarmChannelMessage msg) { String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM; - logger.info("[redis 报警通知] {}: {}", key, JSON.toJSON(msg)); + logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg)); redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); } @@ -707,4 +712,12 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { } + @Override + public void sendStreamPushRequestedMsgForStatus() { + String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED; + logger.info("[redis通知]获取所有推流设备的状态"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(key, key); + redis.convertAndSend(key, jsonObject); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java index c4d1c9e6b..82f09479b 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java @@ -48,12 +48,13 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @Autowired SipConfig sipConfig; - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; @Autowired TransactionDefinition transactionDefinition; + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + @Autowired private DeviceMapper deviceMapper; @@ -104,96 +105,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { return deviceMapper.getDeviceByDeviceId(deviceId) != null; } - @Override - public synchronized void updateChannel(String deviceId, DeviceChannel channel) { - String channelId = channel.getChannelId(); - channel.setDeviceId(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - if (streamInfo != null) { - channel.setStreamId(streamInfo.getStream()); - } - String now = DateUtil.getNow(); - channel.setUpdateTime(now); - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); - if (deviceChannel == null) { - channel.setCreateTime(now); - deviceChannelMapper.add(channel); - }else { - deviceChannelMapper.update(channel); - } - deviceChannelMapper.updateChannelSubCount(deviceId,channel.getParentId()); - } - - @Override - public int updateChannels(String deviceId, List channels) { - List addChannels = new ArrayList<>(); - List updateChannels = new ArrayList<>(); - HashMap channelsInStore = new HashMap<>(); - if (channels != null && channels.size() > 0) { - List channelList = deviceChannelMapper.queryChannels(deviceId, null, null, null, null); - if (channelList.size() == 0) { - for (DeviceChannel channel : channels) { - channel.setDeviceId(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); - if (streamInfo != null) { - channel.setStreamId(streamInfo.getStream()); - } - String now = DateUtil.getNow(); - channel.setUpdateTime(now); - channel.setCreateTime(now); - addChannels.add(channel); - } - }else { - for (DeviceChannel deviceChannel : channelList) { - channelsInStore.put(deviceChannel.getChannelId(), deviceChannel); - } - for (DeviceChannel channel : channels) { - channel.setDeviceId(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); - if (streamInfo != null) { - channel.setStreamId(streamInfo.getStream()); - } - String now = DateUtil.getNow(); - channel.setUpdateTime(now); - if (channelsInStore.get(channel.getChannelId()) != null) { - updateChannels.add(channel); - }else { - addChannels.add(channel); - channel.setCreateTime(now); - } - } - } - int limitCount = 300; - if (addChannels.size() > 0) { - if (addChannels.size() > limitCount) { - for (int i = 0; i < addChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > addChannels.size()) { - toIndex = addChannels.size(); - } - deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)); - } - }else { - deviceChannelMapper.batchAdd(addChannels); - } - } - if (updateChannels.size() > 0) { - if (updateChannels.size() > limitCount) { - for (int i = 0; i < updateChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > updateChannels.size()) { - toIndex = updateChannels.size(); - } - deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)); - } - }else { - deviceChannelMapper.batchUpdate(updateChannels); - } - } - } - return addChannels.size() + updateChannels.size(); - } - @Override public boolean resetChannels(String deviceId, List deviceChannelList) { if (CollectionUtils.isEmpty(deviceChannelList)) { @@ -532,20 +443,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { // 更新缓存 parentPlatformCatch.setParentPlatform(parentPlatform); redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - if (parentPlatform.isEnable()) { - // 共享所有视频流,需要将现有视频流添加到此平台 - List gbStreams = gbStreamMapper.queryStreamNotInPlatform(); - if (gbStreams.size() > 0) { - for (GbStream gbStream : gbStreams) { - gbStream.setCatalogId(parentPlatform.getCatalogId()); - } - if (parentPlatform.isShareAllLiveStream()) { - gbStreamService.addPlatformInfo(gbStreams, parentPlatform.getServerGBId(), parentPlatform.getCatalogId()); - }else { - gbStreamService.delPlatformInfo(parentPlatform.getServerGBId(), gbStreams); - } - } - } return result > 0; } @@ -596,36 +493,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { return deviceChannelMapper.queryChannelByPlatformId(platformId); } - @Override - public int updateChannelForGB(String platformId, List channelReduces, String catalogId) { - - Map deviceAndChannels = new HashMap<>(); - for (ChannelReduce channelReduce : channelReduces) { - channelReduce.setCatalogId(catalogId); - deviceAndChannels.put(channelReduce.getId(), channelReduce); - } - List deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet()); - // 查询当前已经存在的 - List channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces); - if (deviceAndChannelList != null) { - deviceAndChannelList.removeAll(channelIds); - } - for (Integer channelId : channelIds) { - deviceAndChannels.remove(channelId); - } - List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values()); - // 对剩下的数据进行存储 - int result = 0; - if (channelReducesToAdd.size() > 0) { - result = platformChannelMapper.addChannels(platformId, channelReducesToAdd); - // TODO 后续给平台增加控制开关以控制是否响应目录订阅 - List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId); - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); - } - - return result; - } - @Override public int delChannelForGB(String platformId, List channelReduces) { @@ -701,77 +568,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); } - /** - * 新增代理流 - * @param streamProxyItem - * @return - */ - @Override - public boolean addStreamProxy(StreamProxyItem streamProxyItem) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - streamProxyItem.setStreamType("proxy"); - streamProxyItem.setStatus(true); - String now = DateUtil.getNow(); - streamProxyItem.setCreateTime(now); - try { - if (streamProxyMapper.add(streamProxyItem) > 0) { - if (!StringUtils.isEmpty(streamProxyItem.getGbId())) { - if (gbStreamMapper.add(streamProxyItem) < 0) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - } - }else { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - result = true; - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - }catch (Exception e) { - logger.error("向数据库添加流代理失败:", e); - dataSourceTransactionManager.rollback(transactionStatus); - } - - - return result; - } - - /** - * 更新代理流 - * @param streamProxyItem - * @return - */ - @Override - public boolean updateStreamProxy(StreamProxyItem streamProxyItem) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - streamProxyItem.setStreamType("proxy"); - try { - if (streamProxyMapper.update(streamProxyItem) > 0) { - if (!StringUtils.isEmpty(streamProxyItem.getGbId())) { - if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - } - } else { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - result = true; - }catch (Exception e) { - e.printStackTrace(); - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } /** * 移除代理流 @@ -824,7 +620,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { * @return */ @Override - public List queryGbStreamListInPlatform(String platformId) { + public List queryGbStreamListInPlatform(String platformId) { return gbStreamMapper.queryGbStreamListInPlatform(platformId); } @@ -839,65 +635,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { return streamProxyMapper.selectOne(app, stream); } - @Override - public void updateMediaList(List streamPushItems) { - if (streamPushItems == null || streamPushItems.size() == 0) { - return; - } - logger.info("updateMediaList: " + streamPushItems.size()); - streamPushMapper.addAll(streamPushItems); - // TODO 待优化 - for (int i = 0; i < streamPushItems.size(); i++) { - int onlineResult = mediaOnline(streamPushItems.get(i).getApp(), streamPushItems.get(i).getStream()); - if (onlineResult > 0) { - // 发送上线通知 - eventPublisher.catalogEventPublishForStream(null, streamPushItems.get(i), CatalogEvent.ON); - } - } - } - - - - @Override - public void updateMedia(StreamPushItem streamPushItem) { - streamPushMapper.del(streamPushItem.getApp(), streamPushItem.getStream()); - streamPushMapper.add(streamPushItem); - mediaOffline(streamPushItem.getApp(), streamPushItem.getStream()); - - if(!StringUtils.isEmpty(streamPushItem.getGbId() )){ - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - StreamProxyItem streamProxyItem = platformGbStreamMapper.selectOne(streamPushItem.getApp(), streamPushItem.getStream(), - parentPlatform.getServerGBId()); - if (streamProxyItem == null) { - streamPushItem.setCatalogId(parentPlatform.getCatalogId()); - streamPushItem.setPlatformId(parentPlatform.getServerGBId()); - platformGbStreamMapper.add(streamPushItem); - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), streamPushItem, CatalogEvent.ADD); - } - } - } - } - - } - @Override public int removeMedia(String app, String stream) { return streamPushMapper.del(app, stream); } - @Override - public StreamPushItem getMedia(String app, String stream) { - return streamPushMapper.selectOne(app, stream); - } - - @Override - public void clearMediaList() { - streamPushMapper.clear(); - } - @Override public int mediaOffline(String app, String stream) { GbStream gbStream = gbStreamMapper.selectOne(app, stream); @@ -905,18 +647,19 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { if ("proxy".equals(gbStream.getStreamType())) { result = streamProxyMapper.updateStatus(app, stream, false); }else { - result = streamPushMapper.updateStatus(app, stream, false); + result = streamPushMapper.updatePushStatus(app, stream, false); } return result; } + @Override public int mediaOnline(String app, String stream) { GbStream gbStream = gbStreamMapper.selectOne(app, stream); int result; if ("proxy".equals(gbStream.getStreamType())) { result = streamProxyMapper.updateStatus(app, stream, true); }else { - result = streamPushMapper.updateStatus(app, stream, true); + result = streamPushMapper.updatePushStatus(app, stream, true); } return result; } @@ -954,6 +697,29 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @Override public int addCatalog(PlatformCatalog platformCatalog) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformCatalog.getPlatformId()); + if (platform == null) { + return 0; + } + if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) { + if (platformCatalog.getPlatformId().equals(platformCatalog.getParentId())) { + // 第一层节点 + platformCatalog.setBusinessGroupId(platformCatalog.getId()); + platformCatalog.setParentId(platform.getDeviceGBId()); + }else { + // 获取顶层的 + PlatformCatalog topCatalog = getTopCatalog(platformCatalog.getParentId(), platform.getDeviceGBId()); + platformCatalog.setBusinessGroupId(topCatalog.getId()); + } + } + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)) { + platformCatalog.setCivilCode(platformCatalog.getId()); + if (platformCatalog.getPlatformId().equals(platformCatalog.getParentId())) { + // 第一层节点 + platformCatalog.setParentId(platform.getDeviceGBId()); + } + } + int result = catalogMapper.add(platformCatalog); if (result > 0) { DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog); @@ -962,6 +728,15 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { return result; } + private PlatformCatalog getTopCatalog(String id, String platformId) { + PlatformCatalog catalog = catalogMapper.selectParentCatalog(id); + if (catalog.getParentId().equals(platformId)) { + return catalog; + }else { + return getTopCatalog(catalog.getParentId(), platformId); + } + } + @Override public PlatformCatalog getCatalog(String id) { return catalogMapper.select(id); @@ -1032,8 +807,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { } @Override - public List queryCatalogInPlatform(String platformId) { - return catalogMapper.selectByPlatForm(platformId); + public List queryCatalogInPlatform(String platformId) { + return catalogMapper.queryCatalogInPlatform(platformId); } @Override @@ -1076,20 +851,24 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { } private DeviceChannel getDeviceChannelByCatalog(PlatformCatalog catalog) { - ParentPlatform parentPlatByServerGBId = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId()); + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId()); DeviceChannel deviceChannel = new DeviceChannel(); deviceChannel.setChannelId(catalog.getId()); deviceChannel.setName(catalog.getName()); deviceChannel.setLongitude(0.0); deviceChannel.setLatitude(0.0); - deviceChannel.setDeviceId(parentPlatByServerGBId.getDeviceGBId()); + deviceChannel.setDeviceId(platform.getDeviceGBId()); deviceChannel.setManufacture("wvp-pro"); deviceChannel.setStatus(1); deviceChannel.setParental(1); - deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setRegisterWay(1); // 行政区划应该是Domain的前八位 - deviceChannel.setCivilCode(parentPlatByServerGBId.getAdministrativeDivision()); + if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) { + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + } + deviceChannel.setModel("live"); deviceChannel.setOwner("wvp-pro"); deviceChannel.setSecrecy("0"); @@ -1151,4 +930,27 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { deviceChannelMapper.updatePosition(deviceChannel); } + + @Override + public void cleanContentForPlatform(String serverGBId) { +// List catalogList = catalogMapper.selectByPlatForm(serverGBId); +// if (catalogList.size() > 0) { +// int result = catalogMapper.delByPlatformId(serverGBId); +// if (result > 0) { +// List deviceChannels = new ArrayList<>(); +// for (PlatformCatalog catalog : catalogList) { +// deviceChannels.add(getDeviceChannelByCatalog(catalog)); +// } +// eventPublisher.catalogEventPublish(serverGBId, deviceChannels, CatalogEvent.DEL); +// } +// } + catalogMapper.delByPlatformId(serverGBId); + platformChannelMapper.delByPlatformId(serverGBId); + platformGbStreamMapper.delByPlatformId(serverGBId); + } + + @Override + public List queryChannelWithCatalog(String serverGBId) { + return deviceChannelMapper.queryChannelWithCatalog(serverGBId); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java index 91ed22c22..db937d680 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java @@ -12,6 +12,7 @@ public class WVPResult { this.data = data; } + private int code; private String msg; private T data; diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java index 767e9c40e..29f9d8847 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/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; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; @@ -52,6 +53,9 @@ public class DeviceQuery { @Autowired private IVideoManagerStorage storager; + @Autowired + private IDeviceChannelService deviceChannelService; + @Autowired private IRedisCatchStorage redisCatchStorage; @@ -280,7 +284,7 @@ public class DeviceQuery { }) @PostMapping("/channel/update/{deviceId}") public ResponseEntity updateChannel(@PathVariable String deviceId,DeviceChannel channel){ - storager.updateChannel(deviceId, channel); + deviceChannelService.updateChannel(deviceId, channel); return new ResponseEntity<>(null,HttpStatus.OK); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java index 300546f98..fba050b0b 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java @@ -44,7 +44,6 @@ public class GbStreamController { @ApiImplicitParam(name = "platformId", value = "平台ID", required = true , dataTypeClass = String.class), @ApiImplicitParam(name = "catalogId", value = "目录ID", required = false , dataTypeClass = String.class), @ApiImplicitParam(name="query", value = "查询内容", required = false , dataTypeClass = String.class), - @ApiImplicitParam(name="pushing", value = "是否正在推流", required = false , dataTypeClass = Boolean.class), @ApiImplicitParam(name="mediaServerId", value = "流媒体ID", required = false , dataTypeClass = String.class), }) @@ -55,7 +54,6 @@ public class GbStreamController { @RequestParam(required = true)String platformId, @RequestParam(required = false)String catalogId, @RequestParam(required = false)String query, - @RequestParam(required = false)Boolean pushing, @RequestParam(required = false)String mediaServerId){ if (StringUtils.isEmpty(catalogId)) { catalogId = null; @@ -69,7 +67,7 @@ public class GbStreamController { // catalogId 为null 查询未在平台下分配的数据 // catalogId 不为null 查询平台下这个,目录下的通道 - return gbStreamService.getAll(page, count, platformId, catalogId, query, pushing, mediaServerId); + return gbStreamService.getAll(page, count, platformId, catalogId, query, mediaServerId); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java index 94fe8df2f..48973f9c2 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java +++ b/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; import com.genersoft.iot.vmp.media.zlm.dto.OnPublishHookParam; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamPushService; import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; @@ -37,6 +38,8 @@ public class MediaController { @Autowired private IMediaService mediaService; + @Autowired + private IStreamProxyService streamProxyService; /** @@ -95,8 +98,30 @@ public class MediaController { result.setMsg("scccess"); result.setData(streamInfo); }else { - result.setCode(-1); - result.setMsg("fail"); + //获取流失败,重启拉流后重试一次 + streamProxyService.stop(app,stream); + boolean start = streamProxyService.start(app, stream); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { + String host = request.getHeader("Host"); + String localAddr = host.split(":")[0]; + logger.info("使用{}作为返回流的ip", localAddr); + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); + }else { + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + } + if (streamInfo != null){ + result.setCode(0); + result.setMsg("scccess"); + result.setData(streamInfo); + }else { + result.setCode(-1); + result.setMsg("fail"); + } } return result; } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java index 44de6c9e2..7a2269345 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java @@ -8,7 +8,9 @@ import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; +import com.genersoft.iot.vmp.gb28181.bean.TreeType; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.service.IPlatformChannelService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; @@ -48,6 +50,9 @@ public class PlatformController { @Autowired private IVideoManagerStorage storager; + @Autowired + private IPlatformChannelService platformChannelService; + @Autowired private IRedisCatchStorage redisCatchStorage; @@ -236,6 +241,12 @@ public class PlatformController { parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase()); ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId()); parentPlatform.setUpdateTime(DateUtil.getNow()); + if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) { + // 目录结构发生变化,清空之前的关联关系 + logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId()); + storager.cleanContentForPlatform(parentPlatform.getServerGBId()); + + } boolean updateResult = storager.updateParentPlatform(parentPlatform); if (updateResult) { @@ -256,6 +267,8 @@ public class PlatformController { } } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销 commanderForPlatform.unregister(parentPlatformOld, null, null); + // 停止订阅相关的定时任务 + subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); } wvpResult.setCode(0); wvpResult.setMsg("success"); @@ -405,7 +418,7 @@ public class PlatformController { if (logger.isDebugEnabled()) { logger.debug("给上级平台添加国标通道API调用"); } - int result = storager.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId()); + int result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId()); return new ResponseEntity<>(String.valueOf(result > 0), HttpStatus.OK); } @@ -451,13 +464,20 @@ public class PlatformController { if (logger.isDebugEnabled()) { logger.debug("查询目录,platformId: {}, parentId: {}", platformId, parentId); } + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null) { + return new ResponseEntity<>(new WVPResult<>(400, "平台未找到", null), HttpStatus.OK); + } + if (platformId.equals(parentId)) { + parentId = platform.getDeviceGBId(); + } List platformCatalogList = storager.getChildrenCatalogByPlatform(platformId, parentId); - // 查询下属的国标通道 -// List catalogsForChannel = storager.queryChannelInParentPlatformAndCatalog(platformId, parentId); - // 查询下属的直播流通道 -// List catalogsForStream = storager.queryStreamInParentPlatformAndCatalog(platformId, parentId); -// platformCatalogList.addAll(catalogsForChannel); -// platformCatalogList.addAll(catalogsForStream); +// if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) { +// platformCatalogList = storager.getChildrenCatalogByPlatform(platformId, parentId); +// }else { +// +// } + WVPResult> result = new WVPResult<>(); result.setCode(0); result.setMsg("success"); @@ -485,7 +505,6 @@ public class PlatformController { PlatformCatalog platformCatalogInStore = storager.getCatalog(platformCatalog.getId()); WVPResult> result = new WVPResult<>(); - if (platformCatalogInStore != null) { result.setCode(-1); result.setMsg(platformCatalog.getId() + " already exists"); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java index faed2c826..2311d4bee 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java @@ -8,6 +8,8 @@ import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.VersionInfo; +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.utils.SpringBeanFactory; @@ -38,7 +40,7 @@ import java.util.Set; public class ServerController { @Autowired - private ConfigurableApplicationContext context; + private ZLMHttpHookSubscribe zlmHttpHookSubscribe; @Autowired private IMediaServerService mediaServerService; @@ -254,6 +256,18 @@ public class ServerController { return result; } + @ApiOperation("获取当前所有hook") + @GetMapping(value = "/hooks") + @ResponseBody + public WVPResult> getHooks(){ + WVPResult> result = new WVPResult<>(); + result.setCode(0); + result.setMsg("success"); + List all = zlmHttpHookSubscribe.getAll(); + result.setData(all); + return result; + } + // @ApiOperation("当前进行中的动态任务") // @GetMapping(value = "/dynamicTask") // @ResponseBody diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java index 300f95215..c978ae67d 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java @@ -4,6 +4,7 @@ import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelReader; import com.alibaba.excel.read.metadata.ReadSheet; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.security.SecurityUtils; import com.genersoft.iot.vmp.conf.security.dto.LoginUser; import com.genersoft.iot.vmp.gb28181.bean.GbStream; @@ -63,6 +64,9 @@ public class StreamPushController { @Autowired private IMediaService mediaService; + @Autowired + private UserSetting userSetting; + @ApiOperation("推流列表查询") @ApiImplicitParams({ @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), @@ -260,29 +264,63 @@ public class StreamPushController { }) @GetMapping(value = "/getPlayUrl") @ResponseBody - public WVPResult getPlayUrl(HttpServletRequest request, @RequestParam String app, - @RequestParam String stream, - @RequestParam(required = false) String mediaServerId){ + public WVPResult getPlayUrl(@RequestParam String app,@RequestParam String stream, + @RequestParam(required = false) String mediaServerId){ boolean authority = false; // 是否登陆用户, 登陆用户返回完整信息 LoginUser userInfo = SecurityUtils.getUserInfo(); if (userInfo!= null) { authority = true; } - - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); - WVPResult result = new WVPResult<>(); + StreamPushItem push = streamPushService.getPush(app, stream); + if (push != null && !push.isSelf()) { + result.setCode(-1); + result.setMsg("来自其他平台的推流信息"); + return result; + } + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); if (streamInfo != null){ result.setCode(0); - result.setMsg("scccess"); + result.setMsg("success"); result.setData(streamInfo); }else { result.setCode(-1); - result.setMsg("fail"); + result.setMsg("获取播放地址失败"); } + return result; } + /** + * 获取推流播放地址 + * @param stream 推流信息 + * @return + */ + @ApiOperation("获取推流播放地址") + @ApiImplicitParams({ + @ApiImplicitParam(name = "stream", value = "推流信息", dataTypeClass = StreamPushItem.class), + }) + @PostMapping(value = "/add") + @ResponseBody + public WVPResult add(@RequestBody StreamPushItem stream){ + if (StringUtils.isEmpty(stream.getGbId())) { + return new WVPResult<>(400, "国标ID不可为空", null); + } + if (StringUtils.isEmpty(stream.getApp()) && StringUtils.isEmpty(stream.getStream())) { + return new WVPResult<>(400, "app或stream不可为空", null); + } + stream.setStatus(false); + stream.setPushIng(false); + stream.setAliveSecond(0L); + stream.setTotalReaderCount("0"); + boolean result = streamPushService.add(stream); + + if (result) { + return new WVPResult<>(0, "success", null); + }else { + return new WVPResult<>(-1, "fail", null); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java index 152122d28..442832bf5 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.storager.dao.dto.Role; import com.genersoft.iot.vmp.storager.dao.dto.User; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.github.pagehelper.PageInfo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -123,7 +124,8 @@ public class UserController { User user = new User(); user.setUsername(username); user.setPassword(DigestUtils.md5DigestAsHex(password.getBytes())); - + //新增用户的pushKey的生成规则为md5(时间戳+用户名) + user.setPushKey(DigestUtils.md5DigestAsHex((System.currentTimeMillis()+password).getBytes())); Role role = roleService.getRoleById(roleId); if (role == null) { @@ -137,6 +139,7 @@ public class UserController { user.setUpdateTime(DateUtil.getNow()); int addResult = userService.addUser(user); + result.setCode(addResult > 0 ? 0 : -1); result.setMsg(addResult > 0 ? "success" : "fail"); result.setData(addResult); @@ -177,4 +180,68 @@ public class UserController { result.setData(allUsers); return new ResponseEntity<>(result, HttpStatus.OK); } + + /** + * 分页查询用户 + * + * @param page 当前页 + * @param count 每页查询数量 + * @return 分页用户列表 + */ + @ApiOperation("分页查询用户") + @ApiImplicitParams({ + @ApiImplicitParam(name = "page", value = "当前页", required = true, dataTypeClass = Integer.class), + @ApiImplicitParam(name = "count", value = "每页查询数量", required = true, dataTypeClass = Integer.class), + }) + @GetMapping("/users") + public PageInfo users(int page, int count) { + return userService.getUsers(page, count); + } + + @ApiOperation("修改pushkey") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", required = true, value = "用户Id", dataTypeClass = Integer.class), + @ApiImplicitParam(name = "pushKey", required = true, value = "新的pushKey", dataTypeClass = String.class), + }) + @RequestMapping("/changePushKey") + public ResponseEntity> changePushKey(@RequestParam Integer userId,@RequestParam String pushKey) { + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + WVPResult result = new WVPResult<>(); + if (currenRoleId != 1) { + // 只用角色id为0才可以删除和添加用户 + result.setCode(-1); + result.setMsg("用户无权限"); + return new ResponseEntity<>(result, HttpStatus.FORBIDDEN); + } + int resetPushKeyResult = userService.changePushKey(userId,pushKey); + + result.setCode(resetPushKeyResult > 0 ? 0 : -1); + result.setMsg(resetPushKeyResult > 0 ? "success" : "fail"); + return new ResponseEntity<>(result, HttpStatus.OK); + } + + @ApiOperation("管理员修改普通用户密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "adminId", required = true, value = "管理员id", dataTypeClass = String.class), + @ApiImplicitParam(name = "userId", required = true, value = "用户id", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", required = true, value = "新密码(未md5加密的密码)", dataTypeClass = String.class), + }) + @PostMapping("/changePasswordForAdmin") + public String changePasswordForAdmin(@RequestParam int userId, @RequestParam String password) { + // 获取当前登录用户id + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo == null) { + return "fail"; + } + Role role = userInfo.getRole(); + if (role != null && role.getId() == 1) { + boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes())); + if (result) { + return "success"; + } + } + + return "fail"; + } } diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index ef14c242c..b4ec9ef24 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/all-application.yml @@ -149,7 +149,7 @@ media: enable: true # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 port-range: 30000,30500 # 端口范围 - # [可选] 国标级联在此范围内选择端口发送媒体流, + # [可选] 国标级联在此范围内选择端口发送媒体流 send-port-range: 30000,30500 # 端口范围 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 record-assist-port: 0 diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue index d823659b3..4ade7444d 100644 --- a/web_src/src/components/Login.vue +++ b/web_src/src/components/Login.vue @@ -86,7 +86,7 @@ export default { }).then(function (res) { console.log(JSON.stringify(res)); if (res.data.code == 0 && res.data.msg == "success") { - that.$cookies.set("session", {"username": that.username}) ; + that.$cookies.set("session", {"username": that.username,"roleId":res.data.data.role.id}) ; //登录成功后 that.cancelEnterkeyDefaultAction(); that.$router.push('/'); diff --git a/web_src/src/components/ParentPlatformList.vue b/web_src/src/components/ParentPlatformList.vue index 3ae0b6571..23bebdbd4 100644 --- a/web_src/src/components/ParentPlatformList.vue +++ b/web_src/src/components/ParentPlatformList.vue @@ -143,7 +143,7 @@ export default { }); }, chooseChannel: function(platform) { - this.$refs.chooseChannelDialog.openDialog(platform.serverGBId, platform.name, platform.catalogId, this.initData) + this.$refs.chooseChannelDialog.openDialog(platform.serverGBId, platform.name, platform.catalogId, platform.treeType, this.initData) }, initData: function() { this.getPlatformList(); diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue index 0253c1e3e..6b4707e32 100644 --- a/web_src/src/components/PushVideoList.vue +++ b/web_src/src/components/PushVideoList.vue @@ -34,6 +34,8 @@ 批量移除 + 添加通道 + @@ -56,20 +58,25 @@ + + +