Merge branch 'master' into 重构/1078

# Conflicts:
#	pom.xml
#	src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonGBChannel.java
#	src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java
#	src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java
#	src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java
#	src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java
#	src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java
#	src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java
#	src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/utils/CivilCodeUtil.java
#	src/main/resources/application.yml
#	web_src/src/layout/UiHeader.vue
#	web_src/src/router/index.js
This commit is contained in:
648540858 2024-10-18 13:55:23 +08:00
commit 277f2db137
428 changed files with 26104 additions and 22272 deletions

View File

@ -16,7 +16,7 @@ assignees: ''
- 5. 是否使用https
- 6. 方便的话提供下使用的设备品牌或平台
- 7. 你做过哪些尝试
- 8. 代码更新时间
- 8. 代码更新时间(旧版本请更新最新版本代码测试)
**内容描述:**

View File

@ -39,7 +39,6 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
![3](doc/_media/3.png "3.png")
![3-1](doc/_media/3-1.png "3-1.png")
![3-2](doc/_media/3-2.png "3-2.png")
![3-3](doc/_media/3-3.png "3-3.png")
![build_1](https://images.gitee.com/uploads/images/2022/0304/101919_ee5b8c79_1018729.png "2022-03-04_10-13.png")
# 功能特性

View File

@ -3,11 +3,15 @@
> 开箱即用的28181协议视频平台
# 概述
- WVP-PRO基于GB/T 28181-2016标准实现的流媒体平台依托优秀的开源流媒体服务[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit),提供完善丰富的功能。
- 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以及平台。
- 支持将国标设备级联到其他国标平台,也支持将不支持国标的设备的图像或者直播推送到其他国标平台
- 前端完善,自带完整前端页面,无需二次开发可直接部署使用。
@ -15,92 +19,94 @@
- 支持多流媒体节点负载均衡。
# 付费社群
[![社群](_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。
# 我们实现了哪些国标功能
**作为上级平台**
- [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] 播放
- [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] 设备状态查询
- [ ] 设备配置查询
- [X] 设备预置位查询
- [X] 状态信息报送
- [X] 设备视音频文件检索
- [X] 历史视音频的回放
- [X] 播放
- [x] 暂停
- [x] 进/退
- [x] 停止
- [X] 播放
- [x] 暂停
- [x] 进/退
- [x] 停止
- [X] 视音频文件下载
- [ ] ~~校时~~
- [X] 订阅和通知
- [X] 事件订阅
- [X] 移动设备位置订阅
- [ ] 报警订阅
- [X] 目录订阅
- [X] 事件订阅
- [X] 移动设备位置订阅
- [ ] 报警订阅
- [X] 目录订阅
- [X] 语音广播
- [X] 语音喊话
# 社区
代码目前托管在GitHub和GiteeGitee目前作为加速仓库使用不接受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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,2 +1,3 @@
<!-- 自动点播 -->
# 自动点播

View File

@ -1,34 +1,56 @@
<!-- 国标级联的使用 -->
# 国标级联的使用
国标28181不同平台之间支持两种连接方式平级和上下级WVP目前支持向上级级联。
## 1 接入平台
### 1.1 wvp-pro
#### 1.1.1 wvp-pro管理页面点击添加
![cascade1](_media/cascade1.png)
![cascade1](_media/cascade1.png)
#### 1.1.2 填入wvp-pro上级平台信息
![cascade1](_media/img_4.png)
![cascade1](_media/img_5.png)
![cascade1](_media/img_4.png)
![cascade1](_media/img_5.png)
#### 1.1.3 编辑wvp-pro上级设备信息开启订阅
![cascade1](_media/img_6.png)
![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)
#### 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)

View File

@ -1,18 +1,71 @@
<!-- 国标级联的使用 -->
# 国标级联的使用
国标28181不同平台之间支持两种连接方式平级和上下级WVP目前支持向上级级联。
## 添加上级平台
在国标级联页面点击“添加”按钮以推送到上级WVP为例子参看[接入设备](./_content/ability/device.md)
![cascade17](_media/img_17.png)
点击保存可以在上级的国标通道列表看到新增加的设备;
1. 名称
上级平台看到的下级平台名称;
2. 本地IP
本地连接上级使用的具体哪个网卡;
3. SIP认证用户名
可以设置为与"设备国标编号"一致;
4. 注册周期
间隔多久发起一次注册,单位秒;
5. 心跳周期
间隔多久发送一次心跳,一般上级平台三次收不到心跳就会认为下级离线了, 所以建议{心跳周期}x3 < 注册周期;
6. SDP发流IP
调用媒体节点发送视频流给上级时,使用的本地IP;
7. 信令传输
信令传输模式,支持udp和TCP,没有特殊需求,默认UDP即可;
8. 目录分组
上级发送"CATALOG"消息查询通道信息,每一条消息中携带几条通道信息,默认为1,增大该值,可以加快通道发送速度;
9. 字符集
发送给上级"MESSAGE"消息中的消息体使用的编码格式,国标28181-2016默认为GB2312;
10. 行政区划
如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的行政区划信息
11. 平台厂商
如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的平台厂商信息
12. 平台型号
如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的平台型号信息
13. 平台安装地址
如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的平台安装地址信息
14. 其他选项
- RTCP保活
在上级的流传输模式为UDP时,因为UDP的无状态特性,会无法知道上级是否在正常收流,启用RTCP保活时,就可以主动发送RTCP消息确认上级是否在正常收流,
异常情况下,可以下级主动停止发流;
- 消息通道
支持通过报警消息给上级级WVP推送消息,消息内容由redis消息发送给wvp,wvp编辑成报警消息发送给上级;
- 主动推送通道
WVP模拟一条目录订阅信息,然后在共享通道变化时,发送CATAOLOG事件给上级,通知具体的通道变化,
目前支持的状态有: 状态改变事件 ON:上线,OFF:离线,VLOST:视频丢失,DEFECT:故障,ADD:增加,DEL:删除,UPDATE:更新;
- 推送平台信息
勾选此项,上级收到的通道信息中会多出一个平台信息的通道.内容在平台的编辑中修改;
- 推送分组信息
勾选此项,如果你共享的通道分配了具体的业务分组以及虚拟组织,那么上级收到的通道中会包括业务分组以及虚拟组织节点信息;
- 推送行政区划
勾选此项,如果你共享的通道分配了具体的行政区划,那么上级收到的通道中会包括行政区划信息;
国标级联列表出现了级联的这个平台;同时状态显示为在线,如果状态为离线那么可能是你的服务信息配置有误或者网络不通。
订阅信息列有三个图标,表示上级开启订阅,从左到右依次是:报警订阅,目录订阅,移动位置订阅。
## 推送通道
点击你要推送的平台的“选择通道”按钮。
## 通道共享
点击你要推送的平台的“通道共享”按钮。
![cascade18](_media/img_18.png)
- **页面结构**
- 左侧为目录结构
选择未分配,则右侧显示待分配的通道,可以点击“添加按钮”,在弹窗中选择要放置的位置,保存后即可添加通道成功
选择其他的目录可以看到已经分配在这个目录下的通道,可以对其进行删除后重新在未分配中去分配。
- 右侧为数据展示以及操作
国标通道栏内为来自其他国标设备/平台的通道;直播流通道为来自推流/拉流代理的通道。
1. 添加状态选择"未共享"可以将具体的通道共享给上级;
2. 添加状态选择"已共享"可以看到已经共享的通道,并且支持为这个通道在这个平台设备专门的名称和编号;
3. 点击"按设备添加"可以将某个国标设备下的所有通道共享给上级;
4. 点击"按设备移除"可以将某个国标设备下的所有通道取消共享给上级;
5. 点击"全部添加"可以将所有通道共享给上级;
6. 点击"全部移除"可以将所有通道共享给上级;
## 推送通道
WVP会将所有通道信息按照目录订阅消息通知形式,发送ADD事件给上级.

View File

@ -0,0 +1,22 @@
# 通道管理
通道管理为了对已经分配国标编号的通道进行统一的行政区划和业务分组管理,国标中对于组织结构有两种表示方式,一种是按照行政区划,一种是业务分组+虚拟组织的方式.
行政区划结构固定,比如: 北京/市辖区/昌平区, 通道可以挂载道何一级行政区划下. 业务分组比较灵活, 可以按照自己的随意取名,
但是通道只能放在业务分组下的虚拟组织里,不能放在业务分组下.
## 行政区划
左侧树结构为行政区划结构, 通过数据鼠标右键可以操作,包括: 刷新节点,新建节点,编辑节点,删除节点,添加设备(
可以将某个国标设备下的通道全部添加道某一个节点下),移除设备(可以将某个国标设备下的通道全部从这个节点移除)
右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作, 添加状态选择"未添加"可以进行添加操作,选择"
已添加"进行移除操作
![刷新](_media/img_21.png)
## 业务分组
左侧树结构为业务分组结构, 通过数据鼠标右键可以操作,包括: 刷新节点,新建节点,编辑节点,删除节点,添加设备(
可以将某个国标设备下的通道全部添加道某一个节点下),移除设备(可以将某个国标设备下的通道全部从这个节点移除)
业务分组下不能挂载设备,所以没有选择该节点的单选框.
右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作, 添加状态选择"未添加"可以进行添加操作,选择"
已添加"进行移除操作
![刷新](_media/img_22.png)

View File

@ -1,8 +1,13 @@
<!-- 云端录像 -->
# 云端录像
![云端录像](_media/img_23.png)
云端录像是对录制在zlm服务下的录像文件的管理录像的文件路径默认在ZLM/www/record下。
如果你需要24小时的录像目前有一个这种方案可以参考[7*24不间断录像](./_content/ability/continuous_recording.md)。
1. 云段录像支持录像文件的查看,播放(可能因为编码的原因导致无法播放);
2. 支持录像的下载;
3. 支持录像的合并下载;
功能没有太多特殊的地方就不一一介绍了,大家自行体验吧。
- 国标设备是否录像: 可以再WVP的配置中user-settings.record-sip设置为true那么每次点播以及录像回放都会录像;
- 推流设备是否录像: 可以再WVP的配置中user-settings.record-push-live设置为true;
- 拉流代理的是否录像: 在添加和编辑拉流代理时可以指定, 每次点播都会进行录像
- 录像文件存储路径配置: 可以修改media.record-path来修改录像路径,但是如果有旧的录像文件,请不要迁移,因为数据库记录了每一个录像的绝对路径,一旦修改会造成找到文件,无法定时移除以及播放
- 录像保存时间: 可以修改media.record-day来修改录像保存时间,单位是天;

View File

@ -1,9 +1,13 @@
# 语音对讲
## 流程和原理
语音对讲在国标28181-2016中分为broadcast广播和talk对讲两种模式broadcast模式是从服务端把音频传送到设备端是单向的
需要结合点播视频来实现双向对讲talk模式支持双向不过wvp只处理了和broadcast一样的把音频传递设备这样两种模式可以使用一样的逻辑处理即可。
不同的设备对于两种模式的支持不同且通常差异很大不同的设备对同一个设备的支持也有一些不同所以语音对讲中的兼容和适配也是问题最多的。talk模式因为在国标28181-2022中已经移除所以这里不再讨论它了。
### 1. broadcast模式流程
```plantuml
@startuml
"WVP-PRO" -> "设备": 语音广播通知
@ -16,24 +20,33 @@
"ZLMediaKit" -> "设备": 向设备发送语音流
@enduml
```
与点播的流程不同的是这里的invite消息是由设备发送给wvp的wvp按照invite协商的方式给设备推送语音流所有对讲的使用那种方式UDP/TCP被动/TCP主动传输语音流由设备决定
## 使用条件与限制
因为invite消息是由设备发送给wvp的这决定了发送语音流的方式这也就决定了有的设备不能用于公网对讲比如大部分的海康设备只支持udp方式收流(目前新版的海康设备已经在着手解决这个问题)那么wvp发流时只能按照sdp中指定的ip端口发流所以如果wvp在公网设备在内网中那么wvp无法连接设备提供的IP发流也就失败了。
因为invite消息是由设备发送给wvp的这决定了发送语音流的方式这也就决定了有的设备不能用于公网对讲比如大部分的海康设备只支持udp方式收流(
目前新版的海康设备已经在着手解决这个问题)那么wvp发流时只能按照sdp中指定的ip端口发流所以如果wvp在公网设备在内网中那么wvp无法连接设备提供的IP发流也就失败了。
与海康不同的大华以及很多执法记录仪厂商是支持tcp主动方式取流的这样是可以实现公网对讲的。
## 使用ffmpeg快速测试
由于浏览器对于音频的采集需要网页支持https才可以所以如果想要实现网页音频对讲那么你必须给wvp和zlm配置证书以使用https。
测试阶段如果只是想测试功能可以用ffmpeg来模拟语音流推送到wvp后可以实现把音频文件推送到摄像头。
测试命令格式如下:
```shell
ffmpeg -re -i {音频文件} -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://{zlm的IP}:{zlm的RTSP端口}/broadcast/{设备国标编号}_{通道国标编号}?sign={md5(pushKey)}'
```
例如
```shell
ffmpeg -re -i test.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://192.168.1.3:22554/broadcast/34020000001320000001_34020000001320000001?sign=41db35390ddad33f83944f44b8b75ded'
```
测试流程如下:
```plantuml
@startuml
"FFMPEG" -> "ZLMediaKit": 推流到zlm
@ -44,33 +57,48 @@ ffmpeg -re -i test.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://192.168.1
"ZLMediaKit" -> "设备": 向设备推流
@enduml
```
如果听到设备播放你推送的音频,那么意味着调用成功,此过程推流即可需要调用任何接口
## 生产环境网页发起语音对讲
生产环境下使用语音对讲如果是自己的客户端设备那么直接上面的ffmpeg测试方式按照固定格式推流到zlm即可。
对于WEB程序主要是局域网和公网的区别两个原因
1. 很多设备不支持公网对讲
2. 公网和局域网获取证书实现https支持的方式不同
### 公网使用
公网你可以直接使用证书厂商或者云服务器厂商提供的证书,这是很方便的。
### 局域网使用
局域网你需要为wvp和zlm生成自签名证书这里我推荐一种生成自签名证书相对方便的方式,
此方式为linux下的一种方式。
下载证书生成工具:
[https://github.com/FiloSottile/mkcert/releases/tag/v1.4.4](https://github.com/FiloSottile/mkcert/releases/tag/v1.4.4)
安装此工具, 进入解压的工具目录,执行
```shell
./mkcert-v1.4.4-linux-amd64 -install
```
生成pem证书
```shell
./mkcert-v1.4.4-linux-amd64 局域网IP 局域网IP2 局域网IP3
```
你会得到两文件*-key.pem和*.pem, 此文件配置到wvp后既可实现证书的加载
生成zlm使用的证书
```shell
cat *.pem *-key.pem> ./zlm.pem
```
得到的文件就是可以给zlm使用的证书
zlm下使用证书有两种方式
1. 替换zlm下的default.pem, 即删除此文件并把zlm.pem重命名为default.pem
2. 在启动zlm的使用添加 `-s zlm.pem`

View File

@ -1,14 +1,16 @@
<!-- 7*24不间断录像 -->
# 7*24不间断录像
目前如果要实现不间断录像如果只是关闭无人观看停止推流是不够的,设备可能经历断网,重启,都会导致录像的中断,目前给大家提供一种可用的临时方案。
目前如果要实现不间断录像如果只是关闭无人观看停止推流是不够的,设备可能经历断网,重启,都会导致录像的中断,目前给大家提供一种可用的临时方案。
**原理:** wvp支持使用流地址自动点播即你拿到一个流地址直接去播放即使设备处于未点播状态wvp会自动帮你点播ZLM
的拉流代理成功后会无限重试,只要流一恢复就可以拉起来,基于这两个原理。
**方案如下:**
1. wvp的配置中user-settings->auto-apply-play设置为团true,开启自动点播;
2. 点击你要录像的通道点击播放页面左下角的“更多地址”点击rtsp此时复制了rtsp地址到剪贴板
3. 在拉流代理中添加一路流,地址填写你复制的地址,启用成功即可。
**前提:**
**前提:**
1. wvp使用多端口收流不然你无法得到一个固定的流地址也就无法实现自动点播。

View File

@ -1,33 +1,68 @@
<!-- 接入设备 -->
# 接入设备
设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md)
主要有以下字段需要配置:
## 国标28181设备
设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->
国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md)
主要有以下字段需要配置:
- sip->port
28181服务监听的端口
28181服务监听的端口
- sip->domain
domain宜采用ID统一编码的前十位编码。
domain宜采用ID统一编码的前十位编码。
- sip->id
28181服务ID
28181服务ID
- sip->password
28181服务密码
28181服务密码
- 配置信息在如下位置
- 配置信息在如下位置
![_media/img_16.png](_media/img_16.png)
***
## 大华摄像头
### 1. 大华摄像头
![_media/img_10.png](_media/img_10.png)
## 大华NVR
### 2. 大华NVR
![_media/img_11.png](_media/img_11.png)
## 艾科威视摄像头
### 3. 艾科威视摄像头
![_media/img_15.png](_media/img_15.png)
## 水星摄像头
### 4. 水星摄像头
![_media/img_12.png](_media/img_12.png)
## 海康摄像头
### 5. 海康摄像头
![_media/img_9.png](_media/img_9.png)
## 直播推流设备
这里以obs推流为例,很多无人机也是一样的,设置下推流地址就可以接入了
1. 从wvp获取推流地址, 选择节点管理菜单,查看要推流的节点;
![_media/img_19.png](_media/img_19.png)
2. 拼接推流地址
得到的rtsp地址就是: rtsp://{流IP}:{RTSP PORT}/{app}/{stream}
得到的rtmp地址就是: rtsp://{流IP}:{RTMP PORT}/{app}/{stream}
其中流IP是设备可以连接到zlm的IP,端口是对应协议的端口号, app和stream自己定义就可以.
3. 增加推流鉴权信息
wvp默认开启推流鉴权,拼接好的地址是不能直接推送的,会被返回鉴权失败,参考[推流规则](_content/ability/push?id=推流规则)
4. 推流成功后可以再推流列表中看到推流设备,可以播放
此方式只支持设备实时流的播放,无其他功能, 推流信息在推流结束后会自动移除,在列表里就看不到了,如果需要推流信息需要为设备配置国标编号,这样才可以作为wvp的一个永久通道存在.
## 接入非国标IPC设备或者其他流地址形式的设备
这类设备的接入主要通过拉流代理的方式接入,原理就是zlm主动像播放器一样拉取这个流缓存在自己服务器供其他人播放.可以解决源设备并发访问能力差的问题.
在拉流代理/添加代理后可以直接播放, 拉流代理也是同样只支持播放当前配置的流.
[设备使用](_content/ability/device_use.md)

View File

@ -1,21 +1,30 @@
<!-- 设备使用 -->
# 设备使用
### 更新设备通道
点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png)即可看到通道数量的变化如果通道数量仍未0,那么可能时对方尚未推送通道给你。
### 查看设备通道
点击列表末尾的“通道”按钮,
### 查看设备定位
点击列表末尾的“定位”按钮,即可跳转到地图页面看到设备的位置
### 编辑设备在WVP中一些功能
# 国标设备
### 更新设备通道
点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png)
即可看到通道数量的变化如果通道数量仍未0,那么可能时对方尚未推送通道给你。
### 查看设备通道
点击列表末尾的“通道”按钮,
### 编辑设备
点击列表末尾的“编辑”按钮,即可在打开的弹窗中对设备功能进行修改
- 设备名称
如何未能从设备里读取到设备名称或者需要自己重命名,那么可以修改此选项。
- 密码
支持为设备配置独立的密码.
- 收流IP
如果你需要设备从指定的网络地址接入视频流,那么可以配置此IP,设备将会发流到这个IP,比如多网卡接入的服务器.或者存在网络映射的情况.
- 流媒体ID
固定设备使用的流媒体ID,默认根据负载自动分配.
- 字符集
修改读取设备数据时使用的字符集默认为GB2312,但是GB2312收录的汉字不全所以有时候回遇到乱码可以修改为UTF-8来解决。
- 地理坐标系
展示此设备定位信息时使用的设用什么坐标系来解析经纬度,一般不用修改,如果遇到定位不准,可以修改尝试修改此选项解决。
- 目录结构
展示设备的通道信息时使用设备作为树形结构的依据国标28181定义了两种树形结构详情查看[国标28181的树形结构](./_content/theory/channel_tree.md)
- 目录订阅
填写订阅周期即可对设备开启目录订阅设备如果支持目录订阅那么设备在通道信息发生变化时就会通知WVP哪些通道发生了那些变化包括通道增加/删除/更新/上线/下线/视频丢失/故障。0为取消订阅。
一般NVR和平台对接可以开启此选项直接接摄像机开启此选项意义不大。
@ -23,13 +32,31 @@
对设备开启移动位置订阅设备如果支持目录订阅那么设备位置发生变化时会通知到WVP,一般执法记录仪可以开启此选项,对固定位置的设备意义不大。
- SSRC校验
为了解决部分设备出现的串流问题可以打开此选项。ZLM会严格按照给定的ssrc处理视频流。部分设备流信息不标准开启可能导致无法点播。
### 删除设备
可以删除WVP中的设备信息如果设备28181配置未更改那么设备在下一次注册后仍然会注册上来。
### 点播视频
进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面
### 设备录像
进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。
### 云台控制
可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。
### 获取视频的播放器地址
视频点播成功后在实时视频页面点击“更多地址”可以看到所有的播放地址地址是否可以播放与你是否完整编译启用zlm功能有关更与网络有关。
- 作为消息通道
wvp支持通过报警消息给下级WVP互相推送消息,消息内容由redis消息发送给wvp,wvp编辑成报警消息发送给下级
- 收到ACK后发流
语音对讲策略: 不同的设备对于语音对讲的收流时机要求不一,勾选后会在收到设备发送的ack后再开始发流,不勾选则在回复200OK后开始发流,目前已知大华设备不勾选,海康需要勾选.
### 删除设备
可以删除WVP中的设备信息如果设备28181配置未更改那么设备在下一次注册后仍然会注册上来。
### 点播视频
进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面
### 设备录像
进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。
### 云台控制
可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。
### 获取视频的播放器地址
视频点播成功后在实时视频页面点击“更多地址”可以看到所有的播放地址地址是否可以播放与你是否完整编译启用zlm功能有关更与网络有关。
### 语音对讲
[语音对讲](_content/ability/continuous_broadcast.md)

View File

@ -1,20 +1,26 @@
<!-- 电子地图 -->
# 电子地图
WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息电子地图基于开源的地图引擎openlayers开发。
### 查看设备定位
1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面;
2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。
3. 单击通道信息可以定位到具体的通道
# 电子地图
WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息电子地图基于开源的地图引擎openlayers开发。
### 查看设备定位
1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面;
2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。
3. 单击通道信息可以定位到具体的通道
### 查询设备轨迹
查询轨迹需要提前配置save-position-history选项开启轨迹信息的保存目前WVP此处未支持分库分表对于大数据量的轨迹信息无法胜任有需求请自行二次开发或者定制开发。
在电子地图页面在设备上右键点击“查询轨迹”获取设备轨迹信息。
PS 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。
### 更换底图以及底图配置
目前WVP支持使用了更换底图配置文件在web_src/static/js/config.js请修改后重新编译前端文件。
```javascript
window.mapParam = {
// 开启/关闭地图功能

View File

@ -1,9 +1,20 @@
<!-- 节点管理 -->
# 节点管理
![节点管理](_media/img_23.png)
WVP支持单个WVP多个ZLM的方案来扩展WVP的视频并发能力并发点播是因为带宽和性能的原因单个ZLM节点能支持的路数有限所以WVP增加了ZLM集群来扩展并发并且保证ZLM的高可用。
## 默认节点
WVP中为了保证功能的完整性ZLM节点至少要有一个默认节点这个节点不是在管理页面添加的而是在WVP的配置文件中配置的这个节点不可在页面删除。每次启动会自动从配置文件中读取配置写入数据库备用。
## 新增节点
启动你要添加的zlm节点然后点击“添加节点”按钮输入zlm的ip http端口SECRET。点击测试测试完成则开始对节点进行详细的设置如果你的zlm是使用docker启动的可能存在zlm使用的端口与宿主机端口不一致的情况需要在这里一一配置。
启动你要添加的zlm节点然后点击“添加节点”按钮输入zlm的ip
http端口SECRET。点击测试测试完成则开始对节点进行详细的设置如果你的zlm是使用docker启动的可能存在zlm使用的端口与宿主机端口不一致的情况需要在这里一一配置。
## wvp使用多个节点的原理
wvp会把连接的节点统一记录在redis中并记录zlm的负载情况当新的请求到来时会取出负载最低的那个zlm进行使用。以此保证节点负载均衡。

View File

@ -1,2 +1,3 @@
<!-- 在线文档 -->
# 在线文档

View File

@ -1,12 +1,17 @@
<!-- 拉流代理 -->
# 拉流代理
不是所有的摄像机都支持国标或者推流的但是这些设备可以得到一个视频播放地址通常为rtsp协议
以大华为例:
```text
rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0
```
可以得到这样一个流地址可以直接用vlc进行播放此时我们可以通过拉流代理功能将这个设备推送给其他国标平台了。
流程如下:
```plantuml
@startuml
"摄像机" <- "ZLMediaKit": 1. 流去流信息到ZLM
@ -15,10 +20,13 @@ rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0
"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配置文件中添加一个即可。
`PS ffmpeg默认模板不需修改需要修改`参数自行去ZLM配置文件中添加一个即可。

View File

@ -1,8 +1,11 @@
<!-- 推流列表 -->
# 推流列表
## 功能说明
WVP支持三种图像输入方式直播[拉流代理](_content/ability/proxy.md)[国标](_content/ability/device.md),直播设备接入流程如下
```plantuml
@startuml
"直播设备" -> "ZLMediaKit": 1. 发起推流
@ -11,30 +14,41 @@ WVP支持三种图像输入方式直播[拉流代理](_content/ability/pro
"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)
1. 默认情况下WVP收到推流信息后列表中出现这条推流信息如果你需要共享推流信息到其他国标平台,那么你需要编辑/国标通道配置,配置国标编码.
2. WVP也支持推流前导入大量通道直接推送给上级点击“下载模板”按钮根据示例修改模板后点击“通道导入”按钮导入通道数据.
## 推拉流鉴权规则
为了保护服务器的WVP默认开启推流鉴权目前不支持关闭此功能
### 推流规则
推流时需要携带推流鉴权的签名signsign=md5(pushKey),pushKey来自用户表每个用户会有一个不同的pushKey.
例如app=teststream=livepushKey=1000ip=192.168.1.4, port=10554 那么推流地址为:
```
rtsp://192.168.1.4:10554/test/live?sign=a9b7ba70783b617e9998dc4dd82eb3c5
```
支持推流时自定义播放鉴权Id参数名为callId此时sign=md5(callId_pushKey)
例如app=teststream=livepushKey=1000callId=12345678, ip=192.168.1.4, port=10554 那么推流地址为:
```
rtsp://192.168.1.4:10554/test/live?callId=12345678&sign=c8e6e01dde2d60c66dcea8d2498ffef1
```
### 播放规则
默认情况播放不需要鉴权但是如果推流时携带了callId那么播放时必须携带callId
例如app=teststream=live无callId, ip=192.168.1.4, port=10554 那么播放地址为:
```
rtsp://192.168.1.4:10554/test/live
```
例如app=teststream=livecallId=12345678, ip=192.168.1.4, port=10554 那么播放地址为:
```
rtsp://192.168.1.4:10554/test/live?callId=12345678
```

View File

@ -1,2 +1,3 @@
<!-- 用户管理 -->
# 用户管理

View File

@ -1,5 +1,7 @@
<!-- 关于本文档 -->
# 关于本文档
本文档开源在gitee上[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git),如果文档出现任何错误或者不易理解的语句请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档让更多的人可以使用到这个开源的视频平台。
本文档开源在gitee上[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git)
,如果文档出现任何错误或者不易理解的语句请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档让更多的人可以使用到这个开源的视频平台。

View File

@ -1,6 +1,7 @@
# 原理图
## 使用ffmpeg测试语音对讲原理
```plantuml
@startuml
"FFMPEG" -> "ZLMediaKit": 推流到zlm
@ -13,6 +14,7 @@
```
## 使用网页测试语音对讲原理
```plantuml
@startuml
"前端页面" -> "WVP-PRO": 请求推流地址

View File

@ -1,2 +1,5 @@
# 免责声明
WVP-PRO自有代码使用宽松的MIT协议在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议
WVP-PRO自有代码使用宽松的MIT协议在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。
但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。
在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -1,46 +1,63 @@
<!-- 编译 -->
# 编译
WVP-PRO不只是实现了国标28181的协议本身也是一个完整的视频平台。所以对于新手来说你可能需要一些耐心来完成。遇到问题不要焦躁你可以
1. 百度
2. 加入星球体提问;[知识星球](https://t.zsxq.com/0d8VAD3Dm)
3. 向作者发送邮件648540858@qq.com,寻求技术支持(有偿);
WVP-PRO使用Spring boot开发maven管理依赖。对于熟悉spring开发的朋友是很容易进行编译部署以及运行的。
下面将提供一种通用方法方便大家运行项目。
## 1 服务介绍
| 服务 | 作用 | 是否必须 |
|----------------|------------------------------------------|-------------------------|
| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 |
| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现以及各种视频流格式的分发支持 | 是 |
| 服务 | 作用 | 是否必须 |
|------------|------------------------------------------|------|
| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 |
| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现以及各种视频流格式的分发支持 | 是 |
## 2 安装依赖
| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 |
|--------|------------|-------------|--------|--------|
| jdk | >=1.8 | 运行与编译java代码 | 是 | 是 |
| maven | >=3.3 | 管理java代码依赖 | 否 | 否 |
| git || 下载/更新/提交代码 | 否 | 否 |
| nodejs || 编译于运行前端文件 | 否 | 否 |
| npm || 管理前端文件依赖 | 否 | 否 |
| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 |
|--------|-------|-------------|--------|--------|
| 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),如果需要使用语音对讲功能,请参考[zlm启用webrtc编译指南](https://github.com/ZLMediaKit/ZLMediaKit/wiki/zlm%E5%90%AF%E7%94%A8webrtc%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97)开启zlm的webrtc功能。截取一下关键步骤
参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki)
,如果需要使用语音对讲功能,请参考[zlm启用webrtc编译指南](https://github.com/ZLMediaKit/ZLMediaKit/wiki/zlm%E5%90%AF%E7%94%A8webrtc%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97)
开启zlm的webrtc功能。截取一下关键步骤
```bash
# 国内用户推荐从同步镜像网站gitee下载
git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit
@ -48,40 +65,52 @@ 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.npmmirror.com install
npm run build
```
编译如果报错, 一般都是网络问题, 导致的依赖包下载失败
编译完成后在src/main/resources下出现static目录
**编译完成一般是这个样子,中间没有报红的错误信息**
![编译成功](_media/img.png)
### 5.3 生成可执行jar
```bash
cd wvp-GB28181-pro
mvn package
```
### 5.4 生成war
```bash
cd wvp-GB28181-pro
mvn package -P war
```
编译如果报错, 一般都是网络问题, 导致的依赖包下载失败
编译完成后在target目录下出现wvp-pro-***.jar/wvp-pro-***.war。
接下来[配置服务](./_content/introduction/config.md)

View File

@ -1,5 +1,7 @@
<!-- 配置 -->
# 配置
对于首次测试或者新手同学我建议在局域网测试并且关闭服务器与客户机的防火墙测试。建议部署在linux进行测试。
```plantuml
@ -8,24 +10,38 @@
"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。
完全的配置说明可以参看"src/main/resources/配置详情.yml"。
### 1.1 默认加载配置文件方式
使用maven打包后的target里已经存在了配置文件默认加载配置文件为application.yml查看内容发现其中spring.profiles.active配置的内容将入配置的值为dev那么具体加载的配置文件就是application-dev.yml如果配置的值找不到对应的配置文件修改值为dev。
```shell
cd wvp-GB28181-pro/target
java -jar wvp-pro-*.jar
```
## 2 配置WVP-PRO
wvp支持多种数据库包括MysqlPostgresql金仓等配置任选一种即可。
### 2.1 数据库配置
#### 2.1.1 初始化数据库
#### 2.1.1 初始化数据库
首先使用创建数据库然后使用sql/初始化.sql初始化数据库如果是从旧版升级上来的使用升级sql更新。
#### 2.1.2 Mysql数据库配置
#### 2.1.2 Mysql数据库配置
数据库名称以wvp为例
```yaml
spring:
dynamic:
@ -38,24 +54,30 @@ spring:
username: root
password: root123
```
#### 2.1.3 Postgresql数据库配置
#### 2.1.3 Postgresql数据库配置
数据库名称以wvp为例
```yaml
spring:
dynamic:
primary: master
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: 12345678
primary: master
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: 12345678
pagehelper:
helper-dialect: postgresql
```
#### 2.1.4 金仓数据库配置
#### 2.1.4 金仓数据库配置
数据库名称以wvp为例
```yaml
spring:
dynamic:
@ -70,31 +92,39 @@ spring:
pagehelper:
helper-dialect: postgresql
```
### 2.2 Redis数据库配置
配置wvp中的redis连接信息建议wvp自己单独使用一个db。
### 2.3 配置服务启动端口(可直接使用默认配置)
```yaml
# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18080
port: 18080
```
### 2.4 配置28181相关信息可直接使用默认配置
```yaml
# 作为28181服务器的配置
sip:
# [可选] 28181服务监听的端口
port: 5060
# 根据国标6.1.2中规定domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码由省级、市级、区级、基层编号组成参照GB/T 2260-2007
# 后两位为行业编码定义参照附录D.3
# 3701020049标识山东济南历下区 信息行业接入
# [可选]
domain: 3402000000
# [可选]
id: 34020000002000000001
# [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
password: 12345678
# [可选] 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:
@ -118,7 +148,9 @@ media:
# [可选] 国标级联在此范围内选择端口发送媒体流,
send-port-range: 40000,40300 # 端口范围
```
### 2.4 个性化定制信息配置
### 2.4 策略配置
```yaml
# [根据业务需求配置]
user-settings:
@ -133,7 +165,8 @@ user-settings:
# 国标点播 按需拉流, true有人观看拉流无人观看释放 false拉起后不自动释放
stream-on-demand: true
```
更多完整的配置信息参考all-application.yml文件需要那个配置项复制到正在使用的配置文件中对应的文件即可。
更多完整的配置信息参考"src/main/resources/配置详情.yml"文件,需要那个配置项,复制到正在使用的配置文件中对应的文件即可。
如果配置信息无误你可以启动zlm再启动wvp来测试了启动成功的话你可以在wvp的日志下看到zlm已连接的提示。
接下来[部署到服务器](./_content/introduction/deployment.md) 如果你只是本地运行直接在本地运行即可。

View File

@ -1,47 +1,63 @@
<!-- 部署 -->
# 部署
**请仔细阅读以下内容**
1. WVP-PRO与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 | 多端口开放 |
| 服务 | 端口 | 类型 | 必选 |
|-----|:-------------------------|-------------|-------|
| 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. 生产环境按需开放端口但是建议修改默认端口尤其是5060端口易受到攻击;
5. zlm使用docker部署的情况请使用host模式或者端口映射一致比如映射5060,应将外部端口也映射为5060端口;
6. zlm与wvp会保持高频率的通信所以不要去将wvp与zlm分属在两个网络比如wvp在内网zlm却在公网的情况。
7. 启动服务以linux为例
**启动WVP-PRO**
**启动WVP-PRO**
```shell
nohup java -jar wvp-pro-*.jar &
```
**war包**
下载Tomcat后将war包放入webapps中启动Tomcat以解压war包停止Tomcat后删除ROOT目录以及war包将解压后的war包目录重命名为ROOT将配置文件中的Server.port配置为与Tomcat端口一致
然后启动Tomcat。
**启动ZLM**
```shell
nohup ./MediaServer -d -m 3 &
```
### 前后端分离部署
前后端部署目前在最新的版本已经支持请使用3月15日之后的版本部署
前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。
WVP默认开启全部接口支持跨域。部署前端文件到WEB容器并将访问的地址设置为WVP的地址即可。
**配置前端服务器**
1. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址也就是wvp服务的地址
```javascript
window.baseUrl = "http://xxx.com:18080"
```
`这里的地址是需要客户电脑能访问到的,因为请求是客户端电脑发起,与代理不同`
[接入设备](./_content/ability/device.md)
### 默认账号和密码
部署完毕后,可以通过访问 ip加端口的方式访问 WVP WVP的默认登录账号和密码均为 admin。

View File

@ -1,12 +1,18 @@
<!-- 反馈bug -->
# 反馈bug
代码是在不断的完善的不断修改会修复旧的问题也有可能引入新的问题所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥咱们就事论事就好了。
## 如何反馈
1. 在知识星球提问。
2. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试;
3. 可以在github提ISSUE我几乎每天都会去看issue你的问题我会尽快给予答复;
> 有偿支持可以给我发邮件, 648540858@qq.com
## 社群
[![社群](../../_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。

View File

@ -1,7 +1,11 @@
<!-- 参与开发 -->
# 参与到开发中来
非常欢迎有兴趣的小伙伴一起来维护这个项目
## 与开发有关的信息
- 开发语言后端java + 前端vue
- jdk版本 1.8
- 作者自用开发ide jetbrains intellij idea
@ -9,7 +13,9 @@
- 后端使用Spring boot框架开发
- 项目大量使用了异步操作;
- 跟代码学流程需要参考28181文档只看代码你会很懵的
- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的
- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的
## 提交代码
大家可以通过fork项目的方式提交自己的代码然后提交PR我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约**”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。
大家可以通过fork项目的方式提交自己的代码然后提交PR我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约**
”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。

View File

@ -1,6 +1,9 @@
<!-- 点播错误 -->
# 点播错误
排查点播错误你首先要清楚[点播的基本流程](_content/theory/play.md),一般的流程如下:
```plantuml
@startuml
"WEB用户" -> "WVP-PRO": 1. 发起点播请求
@ -15,11 +18,16 @@
"设备" --> "WVP-PRO": 10 200OK
@enduml
```
针对几种常见的错误,我们来分析一下,也方便大家对号入座解决常见的问题
## 点播收到错误码
这个错误一般表现为点击"播放"按钮后很快得到一个错误。
1. **400错误码**
出现400错误玛时一般是这样的流程是这样的
出现400错误玛时一般是这样的流程是这样的
```plantuml
@startuml
"WEB用户" -> "WVP-PRO": 1. 发起点播请求
@ -27,30 +35,39 @@
"设备" --> "WVP-PRO": 3. 400错误
@enduml
```
此时通常是设备认为WVP发送了错误的消息给它它认为消息不全或者错误所以直接返回400错误此时我们需要[抓包](_content/skill/tcpdump.md)来分析是否缺失了内容也可以直接联系对方询问为什么返回了400。
WVP不能保证兼容所有的设备有些实现不规范的设备可能在对接时就会出现上述问题你可以联系作者帮忙对接。
2. **500错误码**
500或者大于500小于600的错误码一般多是设备内部出了问题解决方式有两个第一种直接联系设备/平台客服寻求解决第二种如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我我来尝试解决。
此时通常是设备认为WVP发送了错误的消息给它它认为消息不全或者错误所以直接返回400错误此时我们需要[抓包](_content/skill/tcpdump.md)
来分析是否缺失了内容也可以直接联系对方询问为什么返回了400。
WVP不能保证兼容所有的设备有些实现不规范的设备可能在对接时就会出现上述问题你可以联系作者帮忙对接。
2. **500错误码**
500或者大于500小于600的错误码一般多是设备内部出了问题解决方式有两个第一种直接联系设备/平台客服寻求解决第二种如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我我来尝试解决。
## 点播超时
点播超时的情况大致分为两种:点播超时和收流超时
1. **点播超时**
点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体)”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因:
点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体)
”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因:
> 1. 设备内部错误,未能回复消息
> 2. 网络原因消息未到到达设备
> 2. 网络原因消息未到到达设备
大部分时候是原因2所以遇到这个错误我们首先要排查我们我的网路如果你是公网部署那么也可能时心跳周期太长导致的路由NAT失效WVP的消息无法通道原来的IP端口号发送给设备。
2. **收流超时**
收流超时可能发生在流程中的5和6,可能的原因有:
> 1. 设备发送了流但是发送到了错误的ip和端口上而这个信息是在invite消息的sdp中指定的就是流程2Invite(携带SDP消息体)中而这个错误很可能来自你的配置错误比如你设置了127.0.0.1导致设备网127.0.0.1上发流或者是你WVP在公网但是你给设备了一个内网ip导致设备无法把流发送过来
收流超时可能发生在流程中的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日志是否有流注册

View File

@ -1,8 +1,10 @@
<!-- 设备注册不上来的解决办法 -->
# 设备注册不上来的解决办法
一般的原因有两个
1. 信息填写错误,比如密码错误;
2. 网络不通导致注册消息无法发送到WVP
# 设备注册不上来的解决办法
一般的原因有两个
1. 信息填写错误,比如密码错误;
2. 网络不通导致注册消息无法发送到WVP
遇到问题首先仔细校验填写信息,例如海康可能需要勾选鉴权才可以输入密码。网络问题请自行测试。

View File

@ -1,17 +1,19 @@
<!-- 启动时报错 -->
# 启动时报错
启动时的报错大部分时候是因为你的配置有问题比如mysql没连接上redis没连接上18080/15060端口占用了这些都会导致启动是报错修改配置配置之后都可以解决
下面我整理的一些常见的错误,大家可以先对号入座的简单排查下。
> **常见错误**
> **常见错误**
![_media/img.png](_media/img.png)
**错误原因:** redis配置错误可能原因 redis未启动/ip错误/端口错误/网络不通
**错误原因:** redis配置错误可能原因 redis未启动/ip错误/端口错误/网络不通
---
![_media/img_1.png](_media/img_1.png)
**错误原因:** redis配置错误可能原因 密码错误
---
![_media/img_2.png](_media/img_2.png)
**错误原因:** mysql配置错误可能原因 mysql未启动/ip错误/端口错误/网络不通
**错误原因:** mysql配置错误可能原因 mysql未启动/ip错误/端口错误/网络不通
---
![_media/img_3.png](_media/img_3.png)
**错误原因:** mysql配置错误可能原因 用户名/密码错误

View File

@ -1,15 +1,25 @@
<!-- 抓包 -->
# 抓包
如果说对于网络编程有什么工具是必会的我觉得抓包肯定是其中之一了。作为GB/T 28181调试过程中最重要的手段我觉得如果你真对他有兴趣或者系统遇到问题可以最快的得到解决那么抓包你就一定要学会了。
如果说对于网络编程有什么工具是必会的我觉得抓包肯定是其中之一了。作为GB/T
28181调试过程中最重要的手段我觉得如果你真对他有兴趣或者系统遇到问题可以最快的得到解决那么抓包你就一定要学会了。
## 抓包工具的选择
### 1. Wireshark
在具备图形界面的系统上比如windowslinux发行版ubuntuopensuse等我一般直接使用Wireshark直接进行抓包也方便进行内容的查看。
### 2. Tcpdump
在使用命令行的系统比如linux服务器我一般使用Tcpdump进行抓包无需额外安装系统一般自带抓包的到的文件可以使用Wireshark打开在图形界面下方便查看内容。
## 工具安装
Wireshark的安装很简单根据提示一步步点击就好了在linux需要解决权限的问题如果和我一样使用图形界面的linux发行版的话可以参看如下步骤; windows的小伙伴直接略过即可
Wireshark的安装很简单根据提示一步步点击就好了在linux需要解决权限的问题如果和我一样使用图形界面的linux发行版的话可以参看如下步骤;
windows的小伙伴直接略过即可
```shell
# 1. 添加wireshark用户组
sudo groupadd wireshark
@ -20,43 +30,65 @@ 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`
在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点击红色的停止即可需要保存文件可以点击`文件->导出特定分组`导出过滤后的数据,也可以直接`文件->另存为`保存未过滤的数据。
输入命令开启抓包后此时可以进行操作比如点播录像回访等操作完成回到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查看了
命令行会停留在这个位置,此时可以进行操作,比如点播,录像回放等,操作完成回到命令行使用`Ctrl+C`
结束命令行在当前目录下得到demo.pcap将这个文件下载到图形界面操作系统里即可使用Wireshark查看了
更多的操作可以参考: [https://www.cnblogs.com/jiujuan/p/9017495.html](https://www.cnblogs.com/jiujuan/p/9017495.html)

View File

@ -1,6 +1,7 @@
<!-- 点播流程 -->
# 点播流程
> 以下为WVP-PRO级联语音喊话流程。
```plantuml
@ -31,8 +32,8 @@
@enduml
```
## 注册流程描述如下:
## 注册流程描述如下:
1. 用户从网页或调用接口发起点播请求;
2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、
接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播y字段描述SSRC值,f字段描述媒体参数。

View File

@ -1,14 +0,0 @@
<!-- 通道的树形结构 -->
# 通道的树形结构
国标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)

View File

@ -1,25 +1,29 @@
<!-- 统一编码规则 -->
# 统一编码规则
## D.1 编码规则 A
>&emsp;&emsp;编码规则 A 由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十
>进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。
>&emsp;&emsp;编码规则 A 的详细说明见表 D.1。其中,中心编码指用户或设备所归属的监控中心的编码,按照监控中心所在地的行政区划代码确定,
> 当不是基层单位时空余位为0。行政区划代码采用 GB/T2260— 2007规定的行政区划代码表示。行业编码是指用户或设备所归属的行业,行业编码对照表见 D.3。
> 类型编码指定了设备或用户的具体类型,其中的前端设备包含公安系统和非公安系统的前端设备,终端用 户包含公安系统和非公安系统的终端用户。
> &emsp;&emsp;编码规则 A 由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十
> 进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。
> &emsp;&emsp;编码规则 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)
![img_2.png](_media/img_2.png)
## D.2 编码规则 B
>&emsp;&emsp;编码规则 B由中心编码(8位)、行业编码(2位)、序号(4位)和类型编码(2位)四个码段构成,即系
>统编码 =中心编码 + 行业编码 +序号+类型编码。编码规则 B的详细说明见表 D.2。
> &emsp;&emsp;编码规则 B由中心编码(8位)、行业编码(2位)、序号(4位)和类型编码(2位)四个码段构成,即系
> 统编码 =中心编码 + 行业编码 +序号+类型编码。编码规则 B的详细说明见表 D.2。
![img_3.png](_media/img_3.png)
![img_4.png](_media/img_4.png)
## D.3 行业编码对照表
>&emsp;&emsp;行业编码对照表见表 D.3。
> &emsp;&emsp;行业编码对照表见表 D.3。
![img_5.png](_media/img_5.png)
![img_6.png](_media/img_6.png)

View File

@ -1,6 +1,7 @@
<!-- 点播流程 -->
# 点播流程
> 以下为WVP-PRO点播流程。点播成功前的任何一个环节出现问题都可能出现点播超时这也是排查点播超时的依据。
```plantuml
@ -18,11 +19,11 @@
@enduml
```
## 注册流程描述如下:
## 注册流程描述如下:
1. 用户从网页或调用接口发起点播请求;
1. 用户从网页或调用接口发起点播请求;
2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、
接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播y字段描述SSRC值,f字段描述媒体参数。
接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播y字段描述SSRC值,f字段描述媒体参数。
3. 摄像机向WVP-PRO回复200OK消息体中描述了媒体流发送者发送媒体流的IP、端口、媒体格式、SSRC字段等内容。
4. WVP-PRO向设备回复Ack 会话建立成功。
5. 设备向ZLMediaKit发送实时流。

View File

@ -1,6 +1,7 @@
<!-- 注册流程 -->
# 注册流程
WVP-PRO目前仅支持国标中描述的基本注册流程也是最常用的
> 基本注册即采用IETFRFC3261规定的基于数字摘要的挑战应答式安全技术进行注册.
@ -13,9 +14,8 @@ WVP-PRO目前仅支持国标中描述的基本注册流程也是最常用的
@enduml
```
> 注册流程描述如下:
> 1. 摄像机向WVP-PRO服务器发送 Register请求;
> 注册流程描述如下:
> 1. 摄像机向WVP-PRO服务器发送 Register请求;
> 2. WVP-PRO向摄像机发送响应401,并在响应的消息头 WWW_Authenticate字段中给出适合摄像机的认证体制和参数;
> 3. 摄像机重新向WVP-PRO发送 Register请求,在请求的 Authorization字段给出信任书, 包含认证信息;
> 4. WVP-PRO对请求进行验证,如果检查出 摄像机身份合法,向摄像机发送成功响应 200OK,如果身份不合法则发送拒绝服务应答。

View File

@ -5,8 +5,8 @@
> 开箱即用的28181协议视频平台。
- 基于GB/T28181-2016标准信令实现兼容GB/T28181-2011。
- 自带完整前端页面,开箱即用。
- 基于GB/T28181-2016标准信令实现兼容GB/T28181-2011。
- 自带完整前端页面,开箱即用。
- 完全开源且使用MIT许可协议。可以在保留版权信息的基础上商用。
[GitHub](https://github.com/648540858/wvp-GB28181-pro)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 938 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 KiB

After

Width:  |  Height:  |  Size: 920 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -1,34 +1,32 @@
<!-- 侧边栏 -->
* **编译与部署**
* [编译](_content/introduction/compile.md)
* [配置](_content/introduction/config.md)
* [部署](_content/introduction/deployment.md)
* [编译](_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/ability/continuous_broadcast.md)
* [接入设备](_content/ability/device.md)
* [国标设备](_content/ability/device_use.md)
* [推流列表](_content/ability/push.md)
* [拉流代理](_content/ability/proxy.md)
* [云端录像](_content/ability/cloud_record.md)
* [节点管理](_content/ability/node_manger.md)
* [通道管理](_content/ability/channel.md)
* [国标级联](_content/ability/cascade2.md)
* **流程与原理**
* [统一编码规则](_content/theory/code.md)
* [树形结构](_content/theory/channel_tree.md)
* [注册流程](_content/theory/register.md)
* [点播流程](_content/theory/play.md)
* [级联语音喊话流程](_content/theory/broadcast_cascade.md)
* [统一编码规则](_content/theory/code.md)
* [注册流程](_content/theory/register.md)
* [点播流程](_content/theory/play.md)
* [级联语音喊话流程](_content/theory/broadcast_cascade.md)
* [语音对讲](_content/ability/continuous_broadcast.md)
* **必备技巧**
* [抓包](_content/skill/tcpdump.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)
- [如何反馈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/about_doc.md)

View File

@ -1,59 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WVP-PRO文档</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="icon" href="_media/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="./lib/css/vue.css">
<!-- <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/dark.css">-->
<style>
.cover{
background: linear-gradient(to left bottom, hsl(82, 100%, 85%) 0%,hsl(199, 100%, 85%) 100%) !important;
}
</style>
<meta charset="UTF-8">
<title>WVP-PRO文档</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="icon" href="_media/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" href="./lib/css/vue.css">
<!-- <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/dark.css">-->
<style>
.cover {
background: linear-gradient(to left bottom, hsl(82, 100%, 85%) 0%, hsl(199, 100%, 85%) 100%) !important;
}
</style>
</head>
<body>
<div id="app">加载中</div>
<script>
<div id="app">加载中</div>
<script>
window.$docsify = {
name: 'WVP-RPO使用文档',
repo: 'https://github.com/648540858/wvp-GB28181-pro',
loadSidebar: true, // 开启侧边栏
loadNavbar: true, // 开启导航栏
coverpage: true, // 开启封面
name: 'WVP-RPO使用文档',
repo: 'https://github.com/648540858/wvp-GB28181-pro',
loadSidebar: true, // 开启侧边栏
loadNavbar: true, // 开启导航栏
coverpage: true, // 开启封面
subMaxLevel: 3,
plantuml: {
skin: 'default',
},
search: {
maxAge: 86400000, // 过期时间,单位毫秒,默认一天
paths: 'auto', // or 'auto'
placeholder: '搜索',
noData: '找不到结果',
// 搜索标题的最大层级, 1 - 6
depth: 4,
hideOtherSidebarContent: false, // 是否隐藏其他侧边栏内容
},
copyCode: {
buttonText : '复制',
errorText : '错误',
successText: '已复制'
},
// disqus: 'shortname'
subMaxLevel: 3,
plantuml: {
skin: 'default',
},
search: {
maxAge: 86400000, // 过期时间,单位毫秒,默认一天
paths: 'auto', // or 'auto'
placeholder: '搜索',
noData: '找不到结果',
// 搜索标题的最大层级, 1 - 6
depth: 4,
hideOtherSidebarContent: false, // 是否隐藏其他侧边栏内容
},
copyCode: {
buttonText: '复制',
errorText: '错误',
successText: '已复制'
},
// disqus: 'shortname'
}
</script>
<!-- Docsify v4 -->
<script src="./lib/js/docsify@4.js"></script>
<script src="./lib/js/docsify-plantuml.min.js"></script>
<script src="./lib/js/search.min.js"></script>
<script src="./lib/js/zoom-image.min.js"></script>
<script src="./lib/js/docsify-copy-code.min.js"></script>
</script>
<!-- Docsify v4 -->
<script src="./lib/js/docsify@4.js"></script>
<script src="./lib/js/docsify-plantuml.min.js"></script>
<script src="./lib/js/search.min.js"></script>
<script src="./lib/js/zoom-image.min.js"></script>
<script src="./lib/js/docsify-copy-code.min.js"></script>
</script>
</script>
<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/disqus.min.js"></script>-->
</body>
</html>

21
pom.xml
View File

@ -11,7 +11,7 @@
<groupId>com.genersoft</groupId>
<artifactId>wvp-pro</artifactId>
<version>2.7.2</version>
<version>2.7.3</version>
<name>web video platform</name>
<description>国标28181视频平台</description>
<packaging>${project.packaging}</packaging>
@ -133,6 +133,14 @@
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 数据库监控页面 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.23</version>
</dependency>
<!-- mysql数据库 -->
<dependency>
<groupId>com.mysql</groupId>
@ -344,13 +352,13 @@
<artifactId>ftpserver-core</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.ftpserver</groupId>
<artifactId>ftplet-api</artifactId>
<version>1.2.0</version>
</dependency>
<!-- 自动化生成代码工具 -->
<dependency>
<groupId>org.projectlombok</groupId>
@ -359,6 +367,13 @@
<scope>provided</scope>
</dependency>
<!-- 自动化生成代码工具 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -415,7 +430,7 @@
<version>3.3.0</version>
<configuration>
<excludes>
<exclude>**/all-application.yml</exclude>
<exclude>**/配置详情.yml</exclude>
<exclude>**/application.yml</exclude>
<exclude>**/application-*.yml</exclude>
<exclude>**/local.jks</exclude>

View File

@ -2,8 +2,7 @@ package com.genersoft.iot.vmp;
import com.genersoft.iot.vmp.utils.GitUtil;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@ -26,19 +25,22 @@ import java.util.Collections;
@SpringBootApplication
@EnableScheduling
@EnableCaching
@Slf4j
public class VManageBootstrap extends SpringBootServletInitializer {
private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
private static String[] args;
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
VManageBootstrap.args = args;
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
GitUtil gitUtil1 = SpringBeanFactory.getBean("gitUtil");
logger.info("构建版本: {}", gitUtil1.getBuildVersion());
logger.info("构建时间: {}", gitUtil1.getBuildDate());
logger.info("GIT最后提交时间 {}", gitUtil1.getCommitTime());
GitUtil gitUtil = SpringBeanFactory.getBean("gitUtil");
if (gitUtil == null) {
log.info("获取版本信息失败");
}else {
log.info("构建版本: {}", gitUtil.getBuildVersion());
log.info("构建时间: {}", gitUtil.getBuildDate());
log.info("GIT最后提交时间 {}", gitUtil.getCommitTime());
}
}
// 项目重启
public static void restart() {

View File

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.common;
import org.springframework.util.ObjectUtils;
public class CivilCodePo {
private String code;
@ -12,7 +14,9 @@ public class CivilCodePo {
CivilCodePo civilCodePo = new CivilCodePo();
civilCodePo.setCode(infoArray[0]);
civilCodePo.setName(infoArray[1]);
civilCodePo.setParentCode(infoArray[2]);
if (!ObjectUtils.isEmpty(infoArray[2])) {
civilCodePo.setParentCode(infoArray[2]);
}
return civilCodePo;
}

View File

@ -1,191 +0,0 @@
package com.genersoft.iot.vmp.common;
import io.swagger.v3.oas.annotations.media.Schema;
public class CommonGbChannel {
/**
* 国标字段归属
*/
@Schema(description = "归属")
private String owner;
/**
* 国标字段行政区划
*/
@Schema(description = "行政区划")
private String civilCode;
/**
* 国标字段安装地址
*/
@Schema(description = "安装地址")
private String address;
/**
* 国标字段经度
*/
@Schema(description = "经度")
private Double longitude;
/**
* 国标字段纬度
*/
@Schema(description = "纬度")
private Double latitude;
/**
* 国标字段摄像机类型:
* 1-球机;
* 2-半球;
* 3-固定枪机;
* 4-遥控枪机
*/
@Schema(description = "摄像机类型")
private Integer ptzType;
/**
* 国标字段摄像机位置类型扩展
* 1-省际检查站
* 2-党政机关
* 3-车站码头
* 4-中心广场
* 5-体育场馆
* 6-商业中心
* 7-宗教场所
* 8-校园周边
* 9-治安复杂区域
* 10-交通干线
*/
@Schema(description = "摄像机位置类型扩展")
private Integer positionType;
/**
* 国标字段安装位置室外室内属性
* 1-室外
* 2-室内
*/
@Schema(description = "安装位置室外、室内属性")
private Integer roomType;
/**
* 国标字段用途
* 1-治安
* 2-交通
* 3-重点
*/
@Schema(description = "用途")
private Integer useType;
/**
* 国标字段补光属性
* 1-无补光
* 2-红外补光
* 3-白光补光
*/
@Schema(description = "补光属性")
private Integer supplyLightType;
/**
* 摄像机监视方位属性
* 1-
* 2-西
* 3-
* 4-
* 5-东南
* 6-东北
* 7-西南
* 8-西北
*
*/
@Schema(description = "方位")
private Integer directionType;
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getCivilCode() {
return civilCode;
}
public void setCivilCode(String civilCode) {
this.civilCode = civilCode;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public Integer getPtzType() {
return ptzType;
}
public void setPtzType(Integer ptzType) {
this.ptzType = ptzType;
}
public Integer getPositionType() {
return positionType;
}
public void setPositionType(Integer positionType) {
this.positionType = positionType;
}
public Integer getRoomType() {
return roomType;
}
public void setRoomType(Integer roomType) {
this.roomType = roomType;
}
public Integer getUseType() {
return useType;
}
public void setUseType(Integer useType) {
this.useType = useType;
}
public Integer getSupplyLightType() {
return supplyLightType;
}
public void setSupplyLightType(Integer supplyLightType) {
this.supplyLightType = supplyLightType;
}
public Integer getDirectionType() {
return directionType;
}
public void setDirectionType(Integer directionType) {
this.directionType = directionType;
}
}

View File

@ -1,15 +1,17 @@
package com.genersoft.iot.vmp.common;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import lombok.Data;
/**
* 记录每次发送invite消息的状态
*/
@Data
public class InviteInfo {
private String deviceId;
private String channelId;
private Integer channelId;
private String stream;
@ -28,7 +30,7 @@ public class InviteInfo {
private StreamInfo streamInfo;
public static InviteInfo getInviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo,
public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo,
String receiveIp, Integer receivePort, String streamMode,
InviteSessionType type, InviteSessionStatus status) {
InviteInfo inviteInfo = new InviteInfo();
@ -44,84 +46,4 @@ public class InviteInfo {
return inviteInfo;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public InviteSessionType getType() {
return type;
}
public void setType(InviteSessionType type) {
this.type = type;
}
public InviteSessionStatus getStatus() {
return status;
}
public void setStatus(InviteSessionStatus status) {
this.status = status;
}
public StreamInfo getStreamInfo() {
return streamInfo;
}
public void setStreamInfo(StreamInfo streamInfo) {
this.streamInfo = streamInfo;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public SSRCInfo getSsrcInfo() {
return ssrcInfo;
}
public void setSsrcInfo(SSRCInfo ssrcInfo) {
this.ssrcInfo = ssrcInfo;
}
public String getReceiveIp() {
return receiveIp;
}
public void setReceiveIp(String receiveIp) {
this.receiveIp = receiveIp;
}
public Integer getReceivePort() {
return receivePort;
}
public void setReceivePort(Integer receivePort) {
this.receivePort = receivePort;
}
public String getStreamMode() {
return streamMode;
}
public void setStreamMode(String streamMode) {
this.streamMode = streamMode;
}
}

View File

@ -1,12 +1,14 @@
package com.genersoft.iot.vmp.common;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Objects;
@Schema(description = "流信息")
public class StreamInfo implements Serializable, Cloneable{
@ -15,9 +17,9 @@ public class StreamInfo implements Serializable, Cloneable{
@Schema(description = "流ID")
private String stream;
@Schema(description = "设备编号")
private String deviceID;
@Schema(description = "通道编号")
private String channelId;
private String deviceId;
@Schema(description = "通道ID")
private Integer channelId;
@Schema(description = "IP")
private String ip;
@ -68,8 +70,8 @@ public class StreamInfo implements Serializable, Cloneable{
@Schema(description = "RTCS流地址")
private StreamURL rtcs;
@Schema(description = "流媒体ID")
private String mediaServerId;
@Schema(description = "流媒体节点")
private MediaServer mediaServer;
@Schema(description = "流编码信息")
private MediaInfo mediaInfo;
@Schema(description = "开始时间")
@ -80,6 +82,8 @@ public class StreamInfo implements Serializable, Cloneable{
private double progress;
@Schema(description = "文件下载地址(录像下载使用)")
private DownloadFileInfo downLoadFilePath;
@Schema(description = "点播请求的callId")
private String callId;
@Schema(description = "是否暂停(录像回放使用)")
private boolean pause;
@ -90,6 +94,9 @@ public class StreamInfo implements Serializable, Cloneable{
@Schema(description = "转码后的视频流")
private StreamInfo transcodeStream;
@Schema(description = "使用的WVP ID")
private String serverId;
public void setFlv(StreamURL flv) {
this.flv = flv;
}
@ -352,19 +359,19 @@ public class StreamInfo implements Serializable, Cloneable{
this.app = app;
}
public String getDeviceID() {
return deviceID;
public String getDeviceId() {
return deviceId;
}
public void setDeviceID(String deviceID) {
this.deviceID = deviceID;
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getChannelId() {
public Integer getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
public void setChannelId(Integer channelId) {
this.channelId = channelId;
}
@ -480,12 +487,12 @@ public class StreamInfo implements Serializable, Cloneable{
return rtcs;
}
public String getMediaServerId() {
return mediaServerId;
public MediaServer getMediaServer() {
return mediaServer;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
public void setMediaServer(MediaServer mediaServer) {
this.mediaServer = mediaServer;
}
public MediaInfo getMediaInfo() {
@ -646,4 +653,20 @@ public class StreamInfo implements Serializable, Cloneable{
public void setOriginType(int originType) {
this.originType = originType;
}
public String getServerId() {
return serverId;
}
public void setServerId(String serverId) {
this.serverId = serverId;
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
}

View File

@ -26,7 +26,7 @@ public class VideoManagerConstants {
public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_";
// TODO 此处多了一个_暂不修改
public static final String INVITE_PREFIX = "VMP_INVITE";
public static final String INVITE_PREFIX = "VMP_INVITE_INFO";
public static final String PLAYER_PREFIX = "VMP_INVITE_PLAY_";
public static final String PLAY_BLACK_PREFIX = "VMP_INVITE_PLAYBACK_";
public static final String DOWNLOAD_PREFIX = "VMP_INVITE_DOWNLOAD_";
@ -39,7 +39,10 @@ public class VideoManagerConstants {
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_";
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_";
public static final String SEND_RTP_INFO = "VMP_SEND_RTP_INFO:";
public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID";
public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM";
public static final String SEND_RTP_INFO_CHANNEL = "VMP_SEND_RTP_INFO:CHANNEL";
public static final String EVENT_ONLINE_REGISTER = "1";
@ -51,7 +54,9 @@ public class VideoManagerConstants {
public static final String MEDIA_SSRC_USED_PREFIX = "VMP_MEDIA_USED_SSRC_";
public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_";
public static final String SIP_INVITE_SESSION = "VMP_SIP_INVITE_SESSION:";
public static final String SIP_INVITE_SESSION_CALL_ID = SIP_INVITE_SESSION + "CALL_ID" + ":";
public static final String SIP_INVITE_SESSION_STREAM = SIP_INVITE_SESSION + "STREAM" + ":";
public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY_";

View File

@ -5,9 +5,8 @@ import com.genersoft.iot.vmp.conf.security.SecurityUtils;
import com.genersoft.iot.vmp.service.ILogService;
import com.genersoft.iot.vmp.storager.dao.dto.LogDto;
import com.genersoft.iot.vmp.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
@ -24,13 +23,11 @@ import java.io.IOException;
/**
* @author lin
*/
@Slf4j
@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
@Component
public class ApiAccessFilter extends OncePerRequestFilter {
private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class);
@Autowired
private UserSetting userSetting;

View File

@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.common.CivilCodePo;
import com.genersoft.iot.vmp.utils.CivilCodeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
@ -17,18 +16,15 @@ import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
* 启动时读取行政区划表
*/
@Slf4j
@Configuration
@Order(value=14)
public class CivilCodeFileConf implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(CivilCodeFileConf.class);
@Autowired
@Lazy
private UserSetting userSetting;
@ -36,7 +32,7 @@ public class CivilCodeFileConf implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
if (ObjectUtils.isEmpty(userSetting.getCivilCodeFile())) {
logger.warn("[行政区划] 文件未设置,可能造成目录刷新结果不完整");
log.warn("[行政区划] 文件未设置,可能造成目录刷新结果不完整");
return;
}
InputStream inputStream;
@ -44,7 +40,7 @@ public class CivilCodeFileConf implements CommandLineRunner {
String filePath = userSetting.getCivilCodeFile().substring("classpath:".length());
ClassPathResource civilCodeFile = new ClassPathResource(filePath);
if (!civilCodeFile.exists()) {
logger.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile());
log.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile());
return;
}
inputStream = civilCodeFile.getInputStream();
@ -52,7 +48,7 @@ public class CivilCodeFileConf implements CommandLineRunner {
}else {
File civilCodeFile = new File(userSetting.getCivilCodeFile());
if (!civilCodeFile.exists()) {
logger.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile());
log.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile());
return;
}
inputStream = Files.newInputStream(civilCodeFile.toPath());
@ -61,7 +57,6 @@ public class CivilCodeFileConf implements CommandLineRunner {
BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream));
int index = -1;
String line;
List<CivilCodePo> civilCodePoList = new ArrayList<>();
while ((line = inputStreamReader.readLine()) != null) {
index ++;
if (index == 0) {
@ -69,15 +64,14 @@ public class CivilCodeFileConf implements CommandLineRunner {
}
String[] infoArray = line.split(",");
CivilCodePo civilCodePo = CivilCodePo.getInstance(infoArray);
civilCodePoList.add(civilCodePo);
CivilCodeUtil.INSTANCE.add(civilCodePo);
}
CivilCodeUtil.INSTANCE.add(civilCodePoList);
inputStreamReader.close();
inputStream.close();
if (civilCodePoList.isEmpty()) {
logger.warn("[行政区划] 文件内容为空,可能造成目录刷新结果不完整");
if (CivilCodeUtil.INSTANCE.isEmpty()) {
log.warn("[行政区划] 文件内容为空,可能造成目录刷新结果不完整");
}else {
logger.info("[行政区划] 加载成功,共加载数据{}条", civilCodePoList.size());
log.info("[行政区划] 加载成功,共加载数据{}条", CivilCodeUtil.INSTANCE.size());
}
}
}

View File

@ -5,8 +5,7 @@ import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@ -19,11 +18,10 @@ import java.util.List;
/**
* 录像文件定时删除
*/
@Slf4j
@Component
public class CloudRecordTimer {
private final static Logger logger = LoggerFactory.getLogger(CloudRecordTimer.class);
@Autowired
private IMediaServerService mediaServerService;
@ -36,7 +34,7 @@ public class CloudRecordTimer {
// @Scheduled(fixedRate = 10000) //每五秒执行一次方便测试
@Scheduled(cron = "0 0 0 * * ?") //每天的0点执行
public void execute(){
logger.info("[录像文件定时清理] 开始清理过期录像文件");
log.info("[录像文件定时清理] 开始清理过期录像文件");
// 获取配置了assist的流媒体节点
List<MediaServer> mediaServerItemList = mediaServerService.getAllOnline();
if (mediaServerItemList.isEmpty()) {
@ -64,12 +62,12 @@ public class CloudRecordTimer {
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
if (deleteResult) {
logger.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
}
}
result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
}
}
logger.info("[录像文件定时清理] 共清理{}个过期录像文件", result);
log.info("[录像文件定时清理] 共清理{}个过期录像文件", result);
}
}

View File

@ -1,8 +1,7 @@
package com.genersoft.iot.vmp.conf;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
@ -20,11 +19,10 @@ import java.util.concurrent.TimeUnit;
* 动态定时任务
* @author lin
*/
@Slf4j
@Component
public class DynamicTask {
private final Logger logger = LoggerFactory.getLogger(DynamicTask.class);
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
private final Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
@ -53,9 +51,9 @@ public class DynamicTask {
ScheduledFuture<?> future = futureMap.get(key);
if (future != null) {
if (future.isCancelled()) {
logger.debug("任务【{}】已存在但是关闭状态!!!", key);
log.debug("任务【{}】已存在但是关闭状态!!!", key);
} else {
logger.debug("任务【{}】已存在且已启动!!!", key);
log.debug("任务【{}】已存在且已启动!!!", key);
return;
}
}
@ -65,9 +63,9 @@ public class DynamicTask {
if (future != null){
futureMap.put(key, future);
runnableMap.put(key, task);
logger.debug("任务【{}】启动成功!!!", key);
log.debug("任务【{}】启动成功!!!", key);
}else {
logger.debug("任务【{}】启动失败!!!", key);
log.debug("任务【{}】启动失败!!!", key);
}
}
@ -90,9 +88,9 @@ public class DynamicTask {
ScheduledFuture future = futureMap.get(key);
if (future != null) {
if (future.isCancelled()) {
logger.debug("任务【{}】已存在但是关闭状态!!!", key);
log.debug("任务【{}】已存在但是关闭状态!!!", key);
} else {
logger.debug("任务【{}】已存在且已启动!!!", key);
log.debug("任务【{}】已存在且已启动!!!", key);
return;
}
}
@ -101,9 +99,9 @@ public class DynamicTask {
if (future != null){
futureMap.put(key, future);
runnableMap.put(key, task);
logger.debug("任务【{}】启动成功!!!", key);
log.debug("任务【{}】启动成功!!!", key);
}else {
logger.debug("任务【{}】启动失败!!!", key);
log.debug("任务【{}】启动失败!!!", key);
}
}

View File

@ -3,8 +3,7 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
@ -16,11 +15,10 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 默认异常处理
* @param e 异常
@ -29,7 +27,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public WVPResult<String> exceptionHandler(Exception e) {
logger.error("[全局异常] ", e);
log.error("[全局异常] ", e);
return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
}
@ -54,6 +52,16 @@ public class GlobalExceptionHandler {
public WVPResult<String> exceptionHandler(HttpRequestMethodNotSupportedException e) {
return WVPResult.fail(ErrorCode.ERROR400);
}
/**
* 断言异常处理
* @param e 异常
* @return 统一返回结果
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.OK)
public WVPResult<String> exceptionHandler(IllegalArgumentException e) {
return WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage());
}
/**
@ -62,7 +70,7 @@ public class GlobalExceptionHandler {
* @return 统一返回结果
*/
@ExceptionHandler(ControllerException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<WVPResult<String>> exceptionHandler(ControllerException e) {
return new ResponseEntity<>(WVPResult.fail(e.getCode(), e.getMsg()), HttpStatus.OK);
}

View File

@ -1,12 +1,9 @@
package com.genersoft.iot.vmp.conf;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
@ -15,6 +12,8 @@ import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.LinkedHashMap;
/**
* 全局统一返回结果
* @author lin
@ -52,6 +51,13 @@ public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {
return JSON.toJSONString(WVPResult.success(body));
}
if (body instanceof LinkedHashMap) {
LinkedHashMap<String, Object> bodyMap = (LinkedHashMap<String, Object>) body;
if (bodyMap.get("status") != null && (Integer)bodyMap.get("status") != 200) {
return body;
}
}
return WVPResult.success(body);
}

View File

@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@ -13,13 +12,11 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;
@Slf4j
@Configuration("mediaConfig")
@Order(0)
public class MediaConfig{
private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class);
// 修改必须配置不再支持自动获取
@Value("${media.id}")
private String id;
@ -179,7 +176,7 @@ public class MediaConfig{
try {
hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
} catch (UnknownHostException e) {
logger.error("[获取SDP IP]: 域名解析失败");
log.error("[获取SDP IP]: 域名解析失败");
}
return hostAddress;
}

View File

@ -2,18 +2,17 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.springframework.core.annotation.Order;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
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.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.util.ObjectUtils;
import javax.servlet.ServletException;
@ -28,10 +27,9 @@ import java.net.ConnectException;
@SuppressWarnings(value = {"rawtypes", "unchecked"})
@Configuration
@Order(1)
@Slf4j
public class ProxyServletConfig {
private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
@Autowired
private IMediaServerService mediaServerService;
@ -44,7 +42,7 @@ public class ProxyServletConfig {
servletRegistrationBean.setName("zlm_Proxy");
servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080");
servletRegistrationBean.addUrlMappings();
if (logger.isDebugEnabled()) {
if (log.isDebugEnabled()) {
servletRegistrationBean.addInitParameter("log", "true");
}
return servletRegistrationBean;
@ -85,15 +83,15 @@ public class ProxyServletConfig {
try {
super.handleRequestException(proxyRequest, proxyResonse, e);
} catch (ServletException servletException) {
logger.error("zlm 代理失败: ", e);
log.error("zlm 代理失败: ", e);
} catch (IOException ioException) {
if (ioException instanceof ConnectException) {
logger.error("zlm 连接失败");
log.error("zlm 连接失败");
} else {
logger.error("zlm 代理失败: ", e);
log.error("zlm 代理失败: ", e);
}
} catch (RuntimeException exception){
logger.error("zlm 代理失败: ", e);
log.error("zlm 代理失败: ", e);
}
}
@ -154,7 +152,7 @@ public class ProxyServletConfig {
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
logger.error("[ZLM服务访问代理]错误处理url信息时未找到流媒体信息=>{}", requestURI);
log.error("[ZLM服务访问代理]错误处理url信息时未找到流媒体信息=>{}", requestURI);
return url;
}
if (!ObjectUtils.isEmpty(mediaInfo.getId())) {
@ -170,7 +168,7 @@ public class ProxyServletConfig {
servletRegistrationBean.setName("record_proxy");
servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:18081");
servletRegistrationBean.addUrlMappings();
if (logger.isDebugEnabled()) {
if (log.isDebugEnabled()) {
servletRegistrationBean.addInitParameter("log", "true");
}
return servletRegistrationBean;
@ -214,10 +212,10 @@ public class ProxyServletConfig {
try {
super.handleRequestException(proxyRequest, proxyResponse, e);
} catch (ServletException servletException) {
logger.error("录像服务 代理失败: ", e);
log.error("录像服务 代理失败: ", e);
} catch (IOException ioException) {
if (ioException instanceof ConnectException) {
logger.error("录像服务 连接失败");
log.error("录像服务 连接失败");
// }else if (ioException instanceof ClientAbortException) {
// /**
// * TODO 使用这个代理库实现代理在遇到代理视频文件时如果是206结果会遇到报错蛋市目前功能正常
@ -225,10 +223,10 @@ public class ProxyServletConfig {
// */
}else {
logger.error("录像服务 代理失败: ", e);
log.error("录像服务 代理失败: ", e);
}
} catch (RuntimeException exception){
logger.error("录像服务 代理失败: ", e);
log.error("录像服务 代理失败: ", e);
}
}
@ -290,7 +288,7 @@ public class ProxyServletConfig {
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
logger.error("[录像服务访问代理]错误处理url信息时未找到流媒体信息=>{}", requestURI);
log.error("[录像服务访问代理]错误处理url信息时未找到流媒体信息=>{}", requestURI);
return url;
}
if (!ObjectUtils.isEmpty(mediaInfo.getId())) {

View File

@ -1,16 +1,14 @@
package com.genersoft.iot.vmp.conf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ServiceInfo implements ApplicationListener<WebServerInitializedEvent> {
private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class);
private static int serverPort;
public static int getServerPort() {
@ -21,7 +19,7 @@ public class ServiceInfo implements ApplicationListener<WebServerInitializedEven
public void onApplicationEvent(WebServerInitializedEvent event) {
// 项目启动获取启动的端口号
ServiceInfo.serverPort = event.getWebServer().getPort();
logger.info("项目启动获取启动的端口号: " + ServiceInfo.serverPort);
log.info("项目启动获取启动的端口号: " + ServiceInfo.serverPort);
}
public void setServerPort(int serverPort) {

View File

@ -1,13 +1,15 @@
package com.genersoft.iot.vmp.conf;
import org.springframework.core.annotation.Order;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
@Order(0)
@Data
public class SipConfig {
private String ip;
@ -26,82 +28,7 @@ public class SipConfig {
Integer registerTimeInterval = 120;
private boolean alarm;
private boolean alarm = false;
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(Integer port) {
this.port = port;
}
public void setDomain(String domain) {
this.domain = domain;
}
public void setId(String id) {
this.id = id;
}
public void setPassword(String password) {
this.password = password;
}
public void setPtzSpeed(Integer ptzSpeed) {
this.ptzSpeed = ptzSpeed;
}
public void setRegisterTimeInterval(Integer registerTimeInterval) {
this.registerTimeInterval = registerTimeInterval;
}
public String getIp() {
return ip;
}
public Integer getPort() {
return port;
}
public String getDomain() {
return domain;
}
public String getId() {
return id;
}
public String getPassword() {
return password;
}
public Integer getPtzSpeed() {
return ptzSpeed;
}
public Integer getRegisterTimeInterval() {
return registerTimeInterval;
}
public boolean isAlarm() {
return alarm;
}
public void setAlarm(boolean alarm) {
this.alarm = alarm;
}
public String getShowIp() {
return showIp;
}
public void setShowIp(String showIp) {
this.showIp = showIp;
}
private long timeout = 15;
}

View File

@ -1,14 +1,11 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatch;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.service.IPlatformService;
import com.genersoft.iot.vmp.service.impl.PlatformServiceImpl;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
@ -20,13 +17,11 @@ import java.util.List;
* 系统启动时控制上级平台重新注册
* @author lin
*/
@Slf4j
@Component
@Order(value=13)
public class SipPlatformRunner implements CommandLineRunner {
@Autowired
private IVideoManagerStorage storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@ -36,37 +31,38 @@ public class SipPlatformRunner implements CommandLineRunner {
@Autowired
private ISIPCommanderForPlatform sipCommanderForPlatform;
private final static Logger logger = LoggerFactory.getLogger(PlatformServiceImpl.class);
@Override
public void run(String... args) throws Exception {
// 获取所有启用的平台
List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
List<Platform> parentPlatforms = platformService.queryEnablePlatformList();
for (ParentPlatform parentPlatform : parentPlatforms) {
for (Platform platform : parentPlatforms) {
ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId());
// 更新缓存
ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
parentPlatformCatch.setParentPlatform(parentPlatform);
parentPlatformCatch.setId(parentPlatform.getServerGBId());
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
if (parentPlatformCatchOld != null) {
PlatformCatch platformCatch = new PlatformCatch();
platformCatch.setPlatform(platform);
platformCatch.setId(platform.getServerGBId());
redisCatchStorage.updatePlatformCatchInfo(platformCatch);
if (platformCatchOld != null) {
// 取消订阅
try {
sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
platformService.login(parentPlatform);
log.info("[平台主动注销] {}({})", platform.getName(), platform.getServerGBId());
sipCommanderForPlatform.unregister(platform, platformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
platformService.login(platform);
});
} catch (Exception e) {
logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
platformService.offline(parentPlatform, true);
log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
platformService.offline(platform, true);
continue;
}
}else {
platformService.login(platform);
}
// 设置所有平台离线
platformService.offline(parentPlatform, false);
// 设置平台离线
platformService.offline(platform, false);
}
}
}

View File

@ -1,10 +1,8 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd.AlarmQueryMessageHandler;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@ -15,11 +13,10 @@ import java.util.Map;
/**
* 获取系统信息写入redis
*/
@Slf4j
@Component
public class SystemInfoTimerTask {
private Logger logger = LoggerFactory.getLogger(SystemInfoTimerTask.class);
@Autowired
private IRedisCatchStorage redisCatchStorage;
@ -35,7 +32,7 @@ public class SystemInfoTimerTask {
List<Map<String, Object>> diskInfo =SystemInfoUtils.getDiskInfo();
redisCatchStorage.addDiskInfo(diskInfo);
} catch (InterruptedException e) {
logger.error("[获取系统信息失败] {}", e.getMessage());
log.error("[获取系统信息失败] {}", e.getMessage());
}
}

View File

@ -31,7 +31,7 @@ public class UserSetting {
private Boolean recordSip = Boolean.TRUE;
private Boolean logInDatabase = Boolean.TRUE;
private Boolean logInDatabase = Boolean.FALSE;
private Boolean usePushingAsStatus = Boolean.FALSE;

View File

@ -32,7 +32,7 @@ public class RedisMsgListenConfig {
private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener;
@Autowired
private RedisPushStreamStatusListMsgListener redisPushStreamListMsgListener;
private RedisPushStreamListMsgListener pushStreamListMsgListener;
@Autowired
@ -61,7 +61,7 @@ public class RedisMsgListenConfig {
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
container.addMessageListener(pushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE));
container.addMessageListener(redisRpcConfig, new PatternTopic(RedisRpcConfig.REDIS_REQUEST_CHANNEL_KEY));
container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));

View File

@ -7,8 +7,7 @@ import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
import com.genersoft.iot.vmp.service.redisMsg.control.RedisRpcController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.Message;
@ -26,11 +25,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class RedisRpcConfig implements MessageListener {
private final static Logger logger = LoggerFactory.getLogger(RedisRpcConfig.class);
public final static String REDIS_REQUEST_CHANNEL_KEY = "WVP_REDIS_REQUEST_CHANNEL_KEY";
private final Random random = new Random();
@ -65,10 +63,10 @@ public class RedisRpcConfig implements MessageListener {
} else if (redisRpcMessage.getResponse() != null){
handlerResponse(redisRpcMessage.getResponse());
} else {
logger.error("[redis rpc 解析失败] {}", JSON.toJSONString(redisRpcMessage));
log.error("[redis rpc 解析失败] {}", JSON.toJSONString(redisRpcMessage));
}
} catch (Exception e) {
logger.error("[redis rpc 解析异常] ", e);
log.error("[redis rpc 解析异常] ", e);
}
}
});
@ -79,7 +77,7 @@ public class RedisRpcConfig implements MessageListener {
if (userSetting.getServerId().equals(response.getToId())) {
return;
}
logger.info("[redis-rpc] << {}", response);
log.info("[redis-rpc] << {}", response);
response(response);
}
@ -88,7 +86,7 @@ public class RedisRpcConfig implements MessageListener {
if (userSetting.getServerId().equals(request.getFromId())) {
return;
}
logger.info("[redis-rpc] << {}", request);
log.info("[redis-rpc] << {}", request);
Method method = getMethod(request.getUri());
// 没有携带目标ID的可以理解为哪个wvp有结果就哪个回复携带目标ID但是如果是不存在的uri则直接回复404
if (userSetting.getServerId().equals(request.getToId())) {
@ -113,7 +111,7 @@ public class RedisRpcConfig implements MessageListener {
}
}
}catch (InvocationTargetException | IllegalAccessException e) {
logger.error("[redis rpc ] 处理请求失败 ", e);
log.error("[redis rpc ] 处理请求失败 ", e);
}
}
@ -130,7 +128,7 @@ public class RedisRpcConfig implements MessageListener {
}
private void sendResponse(RedisRpcResponse response){
logger.info("[redis-rpc] >> {}", response);
log.info("[redis-rpc] >> {}", response);
response.setToId(userSetting.getServerId());
RedisRpcMessage message = new RedisRpcMessage();
message.setResponse(response);
@ -138,7 +136,7 @@ public class RedisRpcConfig implements MessageListener {
}
private void sendRequest(RedisRpcRequest request){
logger.info("[redis-rpc] >> {}", request);
log.info("[redis-rpc] >> {}", request);
RedisRpcMessage message = new RedisRpcMessage();
message.setRequest(request);
redisTemplate.convertAndSend(REDIS_REQUEST_CHANNEL_KEY, message);
@ -156,7 +154,7 @@ public class RedisRpcConfig implements MessageListener {
sendRequest(request);
return subscribe.poll(timeOut, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.warn("[redis rpc timeout] uri: {}, sn: {}", request.getUri(), request.getSn(), e);
log.warn("[redis rpc timeout] uri: {}, sn: {}", request.getUri(), request.getSn(), e);
} finally {
this.unsubscribe(request.getSn());
}
@ -176,7 +174,7 @@ public class RedisRpcConfig implements MessageListener {
try {
return queue.offer(response, 2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.error("{}", e.getMessage(), e);
log.error("{}", e.getMessage(), e);
}
}else if (callback != null) {
callback.run(response);

View File

@ -4,8 +4,7 @@ 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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
@ -17,25 +16,24 @@ import java.time.LocalDateTime;
/**
* 用户登录认证逻辑
*/
@Slf4j
@Component
public class DefaultUserDetailsServiceImpl implements UserDetailsService {
private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
@Autowired
private IUserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isBlank(username)) {
logger.info("登录用户:{} 不存在", username);
log.info("登录用户:{} 不存在", username);
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
}
// 查出密码
User user = userService.getUserByUsername(username);
if (user == null) {
logger.info("登录用户:{} 不存在", username);
log.info("登录用户:{} 不存在", username);
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
}
String password = SecurityUtils.encryptPassword(user.getPassword());

View File

@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.service.IUserApiKeyService;
import com.genersoft.iot.vmp.service.IUserService;
import com.genersoft.iot.vmp.storager.dao.dto.User;
import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
import lombok.extern.slf4j.Slf4j;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwk.RsaJsonWebKey;
@ -18,8 +19,6 @@ import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@ -32,11 +31,10 @@ import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
public class JwtUtils implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
public static final String HEADER = "access-token";
public static final String API_KEY_HEADER = "api-key";
@ -75,7 +73,7 @@ public class JwtUtils implements InitializingBean {
try {
rsaJsonWebKey = generateRsaJsonWebKey();
} catch (JoseException e) {
logger.error("生成RsaJsonWebKey报错。", e);
log.error("生成RsaJsonWebKey报错。", e);
}
}
@ -145,7 +143,7 @@ public class JwtUtils implements InitializingBean {
//get token
return jws.getCompactSerialization();
} catch (JoseException e) {
logger.error("[Token生成失败] {}", e.getMessage());
log.error("[Token生成失败] {}", e.getMessage());
}
return null;
}
@ -217,7 +215,7 @@ public class JwtUtils implements InitializingBean {
}
return jwtUser;
} catch (Exception e) {
logger.error("[Token解析失败] {}", e.getMessage());
log.error("[Token解析失败] {}", e.getMessage());
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
return jwtUser;
}

View File

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.conf.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
@ -14,14 +13,13 @@ import java.io.IOException;
/**
* 退出登录成功
*/
@Slf4j
@Component
public class LogoutHandler implements LogoutSuccessHandler {
private final static Logger logger = LoggerFactory.getLogger(LogoutHandler.class);
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
String username = request.getParameter("username");
logger.info("[退出登录成功] - [{}]", username);
log.info("[退出登录成功] - [{}]", username);
}
}

View File

@ -1,8 +1,7 @@
package com.genersoft.iot.vmp.conf.security;
import com.genersoft.iot.vmp.conf.UserSetting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -36,10 +35,9 @@ import java.util.Collections;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(1)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
@Autowired
private UserSetting userSetting;

View File

@ -7,8 +7,7 @@ import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties;
import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.SipStackImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
@ -22,12 +21,11 @@ import java.net.NetworkInterface;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
@Order(value=10)
public class SipLayer implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
@Autowired
private SipConfig sipConfig;
@ -60,16 +58,16 @@ public class SipLayer implements CommandLineRunner {
if (nif.getName().startsWith("docker")) {
continue;
}
logger.info("[自动配置SIP监听网卡] 网卡接口地址: {}", addr.getHostAddress());// 只关心 IPv4 地址
log.info("[自动配置SIP监听网卡] 网卡接口地址: {}", addr.getHostAddress());// 只关心 IPv4 地址
monitorIps.add(addr.getHostAddress());
}
}
}
}catch (Exception e) {
logger.error("[读取网卡信息失败]", e);
log.error("[读取网卡信息失败]", e);
}
if (monitorIps.isEmpty()) {
logger.error("[自动配置SIP监听网卡信息失败] 请手动配置SIP.IP后重新启动");
log.error("[自动配置SIP监听网卡信息失败] 请手动配置SIP.IP后重新启动");
System.exit(1);
}
}else {
@ -82,7 +80,9 @@ public class SipLayer implements CommandLineRunner {
monitorIps.add(sipConfig.getIp());
}
}
sipConfig.setShowIp(String.join(",", monitorIps));
if (ObjectUtils.isEmpty(sipConfig.getShowIp())){
sipConfig.setShowIp(String.join(",", monitorIps));
}
SipFactory.getInstance().setPathName("gov.nist");
if (monitorIps.size() > 0) {
for (String monitorIp : monitorIps) {
@ -100,7 +100,7 @@ public class SipLayer implements CommandLineRunner {
sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties("GB28181_SIP", userSetting.getSipLog()));
sipStack.setMessageParserFactory(new GbStringMsgParserFactory());
} catch (PeerUnavailableException e) {
logger.error("[SIP SERVER] SIP服务启动失败 监听地址{}失败,请检查ip是否正确", monitorIp);
log.error("[SIP SERVER] SIP服务启动失败 监听地址{}失败,请检查ip是否正确", monitorIp);
return;
}
@ -111,12 +111,12 @@ public class SipLayer implements CommandLineRunner {
tcpSipProvider.setDialogErrorsAutomaticallyHandled();
tcpSipProvider.addSipListener(sipProcessorObserver);
tcpSipProviderMap.put(monitorIp, tcpSipProvider);
logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port);
log.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException
| TooManyListenersException
| ObjectInUseException
| InvalidArgumentException e) {
logger.error("[SIP SERVER] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
log.error("[SIP SERVER] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
, monitorIp, port);
}
@ -125,15 +125,15 @@ public class SipLayer implements CommandLineRunner {
SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(sipProcessorObserver);
udpSipProvider.setDialogErrorsAutomaticallyHandled();
udpSipProviderMap.put(monitorIp, udpSipProvider);
logger.info("[SIP SERVER] udp://{}:{} 启动成功", monitorIp, port);
log.info("[SIP SERVER] udp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException
| TooManyListenersException
| ObjectInUseException
| InvalidArgumentException e) {
logger.error("[SIP SERVER] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
log.error("[SIP SERVER] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
, monitorIp, port);
}
}

View File

@ -26,8 +26,7 @@
package com.genersoft.iot.vmp.gb28181.auth;
import gov.nist.core.InternalErrorHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import javax.sip.address.URI;
import javax.sip.header.AuthorizationHeader;
@ -46,19 +45,14 @@ import java.util.Random;
* @author M. Ranganathan
* @author Marc Bednarek
*/
@Slf4j
public class DigestServerAuthenticationHelper {
private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class);
private MessageDigest messageDigest;
public static final String DEFAULT_ALGORITHM = "MD5";
public static final String DEFAULT_SCHEME = "Digest";
/** to hex converter */
private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
@ -205,17 +199,17 @@ public class DigestServerAuthenticationHelper {
byte mdbytes[] = messageDigest.digest(A1.getBytes());
String HA1 = toHexString(mdbytes);
logger.debug("A1: " + A1);
logger.debug("A2: " + A2);
log.debug("A1: " + A1);
log.debug("A2: " + A2);
mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
logger.debug("HA1: " + HA1);
logger.debug("HA2: " + HA2);
log.debug("HA1: " + HA1);
log.debug("HA2: " + HA2);
// String cnonce = authHeader.getCNonce();
logger.debug("nonce: " + nonce);
logger.debug("nc: " + ncStr);
logger.debug("cnonce: " + cnonce);
logger.debug("qop: " + qop);
log.debug("nonce: " + nonce);
log.debug("nc: " + ncStr);
log.debug("cnonce: " + cnonce);
log.debug("qop: " + qop);
String KD = HA1 + ":" + nonce;
if (qop != null && qop.equalsIgnoreCase("auth") ) {
@ -228,12 +222,12 @@ public class DigestServerAuthenticationHelper {
KD += ":" + qop;
}
KD += ":" + HA2;
logger.debug("KD: " + KD);
log.debug("KD: " + KD);
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
logger.debug("mdString: " + mdString);
log.debug("mdString: " + mdString);
String response = authHeader.getResponse();
logger.debug("response: " + response);
log.debug("response: " + response);
return mdString.equals(response);
}

View File

@ -1,20 +1,22 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.controller.bean.AudioBroadcastEvent;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
import gov.nist.javax.sip.message.SIPResponse;
import lombok.Data;
/**
* 缓存语音广播的状态
* @author lin
*/
@Data
public class AudioBroadcastCatch {
public AudioBroadcastCatch(
String deviceId,
String channelId,
Integer channelId,
MediaServer mediaServerItem,
String app,
String stream,
@ -43,7 +45,7 @@ public class AudioBroadcastCatch {
/**
* 通道编号
*/
private String channelId;
private Integer channelId;
/**
* 流媒体信息
@ -81,79 +83,7 @@ public class AudioBroadcastCatch {
private AudioBroadcastEvent event;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public AudioBroadcastCatchStatus getStatus() {
return status;
}
public void setStatus(AudioBroadcastCatchStatus status) {
this.status = status;
}
public SipTransactionInfo getSipTransactionInfo() {
return sipTransactionInfo;
}
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public boolean isFromPlatform() {
return isFromPlatform;
}
public void setFromPlatform(boolean fromPlatform) {
isFromPlatform = fromPlatform;
}
public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
this.sipTransactionInfo = sipTransactionInfo;
}
public AudioBroadcastEvent getEvent() {
return event;
}
public void setEvent(AudioBroadcastEvent event) {
this.event = event;
}
public void setSipTransactionInfoByRequset(SIPResponse sipResponse) {
public void setSipTransactionInfoByRequest(SIPResponse sipResponse) {
this.sipTransactionInfo = new SipTransactionInfo(sipResponse);
}
}

View File

@ -0,0 +1,38 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import java.lang.reflect.InvocationTargetException;
@Data
@Slf4j
@EqualsAndHashCode(callSuper = true)
public class CatalogChannelEvent extends DeviceChannel{
private String event;
private DeviceChannel channel;
public static CatalogChannelEvent decode(Element element) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
Element eventElement = element.element("Event");
CatalogChannelEvent catalogChannelEvent = new CatalogChannelEvent();
if (eventElement != null) {
catalogChannelEvent.setEvent(eventElement.getText());
}else {
catalogChannelEvent.setEvent(CatalogEvent.ADD);
}
DeviceChannel deviceChannel;
if (CatalogEvent.ADD.equalsIgnoreCase(catalogChannelEvent.getEvent()) ||
CatalogEvent.UPDATE.equalsIgnoreCase(catalogChannelEvent.getEvent()) ){
deviceChannel = DeviceChannel.decode(element);
}else {
deviceChannel = DeviceChannel.decodeWithOnlyDeviceId(element);
}
catalogChannelEvent.setChannel(deviceChannel);
return catalogChannelEvent;
}
}

View File

@ -1,11 +1,14 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
import java.time.Instant;
import java.util.List;
/**
* @author lin
*/
@Data
public class CatalogData {
/**
* 命令序列号
@ -13,6 +16,8 @@ public class CatalogData {
private int sn;
private int total;
private List<DeviceChannel> channelList;
private List<Region> regionListList;
private List<Group> groupListListList;
private Instant lastTime;
private Device device;
private String errorMsg;
@ -21,61 +26,4 @@ public class CatalogData {
ready, runIng, end
}
private CatalogDataStatus status;
public int getSn() {
return sn;
}
public void setSn(int sn) {
this.sn = sn;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List<DeviceChannel> getChannelList() {
return channelList;
}
public void setChannelList(List<DeviceChannel> channelList) {
this.channelList = channelList;
}
public Instant getLastTime() {
return lastTime;
}
public void setLastTime(Instant lastTime) {
this.lastTime = lastTime;
}
public Device getDevice() {
return device;
}
public void setDevice(Device device) {
this.device = device;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public CatalogDataStatus getStatus() {
return status;
}
public void setStatus(CatalogDataStatus status) {
this.status = status;
}
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -7,8 +8,8 @@ import lombok.Data;
@Schema(description = "国标通道")
public class CommonGBChannel {
@Schema(description = "国标-数据库自增Id")
private Integer gbId;
@Schema(description = "国标-数据库自增ID")
private int gbId;
@Schema(description = "国标-编码")
private String gbDeviceId;
@ -36,7 +37,7 @@ public class CommonGBChannel {
private String gbAddress;
@Schema(description = "国标-是否有子设备")
private Boolean gbParental;
private Integer gbParental;
@Schema(description = "国标-父节点ID")
private String gbParentId;
@ -50,7 +51,7 @@ public class CommonGBChannel {
// 2016
@Schema(description = "国标-证书序列号")
private Integer gbCertNum;
private String gbCertNum;
// 2016
@Schema(description = "国标-证书有效标识")
@ -62,11 +63,7 @@ public class CommonGBChannel {
// 2016
@Schema(description = "国标-证书终止有效期(有证书且证书无效的设备必选)")
private Integer gbEndTime;
// 2022
@Schema(description = "国标-摄像机安全能力等级代码")
private String gbSecurityLevelCode;
private String gbEndTime;
@Schema(description = "国标-保密属性(必选)缺省为0;0-不涉密,1-涉密")
private Integer gbSecrecy;
@ -81,12 +78,12 @@ public class CommonGBChannel {
private String gbPassword;
@Schema(description = "国标-设备状态")
private Boolean gbStatus;
private String gbStatus;
@Schema(description = "国标-经度 WGS-84坐标系")
private Double gbLongitude;
@Schema(description = "国标-,纬度 WGS-84坐标系")
@Schema(description = "国标-纬度 WGS-84坐标系")
private Double gbLatitude;
@Schema(description = "国标-虚拟组织所属的业务分组ID")
@ -95,11 +92,10 @@ public class CommonGBChannel {
@Schema(description = "国标-摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;7-多目设备的分割通道")
private Integer gbPtzType;
@Schema(description = "国标-摄像机光电成像类型。1-可见光成像;2-热成像;3-雷达成像;4-X光成像;5-深度光场成像;9-其他。可多值,")
private String gbPhotoelectricImagingTyp;
@Schema(description = "国标-摄像机采集部位类型")
private String gbCapturePositionType;
// 2016
@Schema(description = "-摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、6-商业中心、7-宗教场所、" +
"8-校园周边、9-治安复杂区域、10-交通干线。当目录项为摄像机时可选。")
private Integer gbPositionType;
@Schema(description = "国标-摄像机安装位置室外、室内属性。1-室外、2-室内。")
private Integer gbRoomType;
@ -118,10 +114,6 @@ public class CommonGBChannel {
@Schema(description = "国标-摄像机支持的分辨率,可多值")
private String gbResolution;
// 2022
@Schema(description = "国标-摄像机支持的码流编号列表,用于实时点播时指定码流编号(可选)")
private String gbStreamNumberList;
@Schema(description = "国标-下载倍速(可选),可多值")
private String gbDownloadSpeed;
@ -131,70 +123,256 @@ public class CommonGBChannel {
@Schema(description = "国标-时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)")
private Integer gbSvcTimeSupportMode;
// 2022
@Schema(description = "国标- SSVC增强层与基本层比例能力 ")
private String gbSsvcRatioSupportList;
@Schema(description = "关联的国标设备数据库ID")
private Integer gbDeviceDbId;
// 2022
@Schema(description = "国标-移动采集设备类型(仅移动采集设备适用,必选);1-移动机器人载摄像机;2-执法记录仪;3-移动单兵设备;" +
"4-车载视频记录设备;5-无人机载摄像机;9-其他")
private Integer gbMobileDeviceType;
@Schema(description = "关联的推流Id流来源是推流时有效")
private Integer streamPushId;
// 2022
@Schema(description = "国标-摄像机水平视场角(可选),取值范围大于0度小于等于360度")
private Double gbHorizontalFieldAngle;
@Schema(description = "关联的拉流代理Id流来源是拉流代理时有效")
private Integer streamProxyId;
// 2022
@Schema(description = "国标-摄像机竖直视场角(可选),取值范围大于0度小于等于360度 ")
private Double gbVerticalFieldAngle;
@Schema(description = "创建时间")
private String createTime;
// 2022
@Schema(description = "国标-摄像机可视距离(可选),单位:米")
private Double gbMaxViewDistance;
@Schema(description = "更新时间")
private String updateTime;
// 2022
@Schema(description = "国标-基层组织编码(必选,非基层建设时为“000000”)")
private String gbGrassrootsCode;
public String encode(String serverDeviceId) {
return encode(null, serverDeviceId);
}
// 2022
@Schema(description = "国标-监控点位类型(当为摄像机时必选),1-一类视频监控点;2-二类视频监控点;3-三类视频监控点;9-其他点位。")
private Integer gbPoType;
public String encode(String event,String serverDeviceId) {
String content;
if (event == null) {
return getFullContent(null, serverDeviceId);
}
switch (event) {
case CatalogEvent.DEL:
case CatalogEvent.DEFECT:
case CatalogEvent.VLOST:
content = "<Item>\n" +
"<DeviceID>" + this.getGbDeviceId() + "</DeviceID>\n" +
"<Event>" + event + "</Event>\n" +
"</Item>\n";
break;
case CatalogEvent.ON:
case CatalogEvent.OFF:
content = "<Item>\n" +
"<DeviceID>" + this.getGbDeviceId() + "</DeviceID>\n" +
"<Event>" + event + "</Event>\r\n" +
"</Item>\n";
break;
case CatalogEvent.ADD:
case CatalogEvent.UPDATE:
content = getFullContent(event, serverDeviceId);
break;
default:
content = null;
break;
}
return content;
}
// 2022
@Schema(description = "国标-点位俗称")
private String gbPoCommonName;
private String getFullContent(String event, String serverDeviceId) {
StringBuilder content = new StringBuilder();
// 行政区划目录项
content.append("<Item>\n")
.append("<DeviceID>" + this.getGbDeviceId() + "</DeviceID>\n")
.append("<Name>" + this.getGbName() + "</Name>\n");
// 2022
@Schema(description = "国标-设备MAC地址(可选),用“XX-XX-XX-XX-XX-XX”格式表达")
private String gbMac;
// 2022
@Schema(description = "国标-摄像机卡口功能类型,01-人脸卡口;02-人员卡口;03-机动车卡口;04-非机动车卡口;05-物品卡口;99-其他")
private String gbFunctionType;
if (this.getGbDeviceId().length() > 8) {
// 2022
@Schema(description = "国标-摄像机视频编码格式")
private String gbEncodeType;
String type = this.getGbDeviceId().substring(10, 13);
if (type.equals("200")) {
// 业务分组目录项
if (this.getGbManufacturer() != null) {
content.append("<Manufacturer>" + this.getGbManufacturer() + "</Manufacturer>\n");
}
if (this.getGbModel() != null) {
content.append("<Model>" + this.getGbModel() + "</Model>\n");
}
if (this.getGbOwner() != null) {
content.append("<Owner>" + this.getGbOwner() + "</Owner>\n");
}
if (this.getGbCivilCode() != null) {
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
}
if (this.getGbAddress() != null) {
content.append("<Address>" + this.getGbAddress() + "</Address>\n");
}
if (this.getGbRegisterWay() != null) {
content.append("<RegisterWay>" + this.getGbRegisterWay() + "</RegisterWay>\n");
}
if (this.getGbSecrecy() != null) {
content.append("<Secrecy>" + this.getGbSecrecy() + "</Secrecy>\n");
}
} else if (type.equals("215")) {
// 业务分组
if (this.getGbCivilCode() != null) {
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
}
content.append("<ParentID>" + serverDeviceId + "</ParentID>\n");
} else if (type.equals("216")) {
// 虚拟组织目录项
if (this.getGbCivilCode() != null) {
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
}
if (this.getGbParentId() != null) {
content.append("<ParentID>" + this.getGbParentId() + "</ParentID>\n");
}
content.append("<BusinessGroupID>" + this.getGbBusinessGroupId() + "</BusinessGroupID>\n");
} else {
if (this.getGbManufacturer() != null) {
content.append("<Manufacturer>" + this.getGbManufacturer() + "</Manufacturer>\n");
}
if (this.getGbModel() != null) {
content.append("<Model>" + this.getGbModel() + "</Model>\n");
}
if (this.getGbOwner() != null) {
content.append("<Owner>" + this.getGbOwner() + "</Owner>\n");
}
if (this.getGbCivilCode() != null) {
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
}
if (this.getGbAddress() != null) {
content.append("<Address>" + this.getGbAddress() + "</Address>\n");
}
if (this.getGbRegisterWay() != null) {
content.append("<RegisterWay>" + this.getGbRegisterWay() + "</RegisterWay>\n");
}
if (this.getGbSecrecy() != null) {
content.append("<Secrecy>" + this.getGbSecrecy() + "</Secrecy>\n");
}
if (this.getGbParentId() != null) {
content.append("<ParentID>" + this.getGbParentId() + "</ParentID>\n");
}
if (this.getGbParental() != null) {
content.append("<Parental>" + this.getGbParental() + "</Parental>\n");
}
if (this.getGbSafetyWay() != null) {
content.append("<SafetyWay>" + this.getGbSafetyWay() + "</SafetyWay>\n");
}
if (this.getGbRegisterWay() != null) {
content.append("<RegisterWay>" + this.getGbRegisterWay() + "</RegisterWay>\n");
}
if (this.getGbCertNum() != null) {
content.append("<CertNum>" + this.getGbCertNum() + "</CertNum>\n");
}
if (this.getGbCertifiable() != null) {
content.append("<Certifiable>" + this.getGbCertifiable() + "</Certifiable>\n");
}
if (this.getGbErrCode() != null) {
content.append("<ErrCode>" + this.getGbErrCode() + "</ErrCode>\n");
}
if (this.getGbEndTime() != null) {
content.append("<EndTime>" + this.getGbEndTime() + "</EndTime>\n");
}
if (this.getGbSecrecy() != null) {
content.append("<Secrecy>" + this.getGbSecrecy() + "</Secrecy>\n");
}
if (this.getGbIpAddress() != null) {
content.append("<IPAddress>" + this.getGbIpAddress() + "</IPAddress>\n");
}
if (this.getGbPort() != null) {
content.append("<Port>" + this.getGbPort() + "</Port>\n");
}
if (this.getGbPassword() != null) {
content.append("<Password>" + this.getGbPassword() + "</Password>\n");
}
if (this.getGbStatus() != null) {
content.append("<Status>" + this.getGbStatus() + "</Status>\n");
}
if (this.getGbLongitude() != null) {
content.append("<Longitude>" + this.getGbLongitude() + "</Longitude>\n");
}
if (this.getGbLatitude() != null) {
content.append("<Latitude>" + this.getGbLatitude() + "</Latitude>\n");
}
content.append("<Info>\n");
// 2022
@Schema(description = "国标-摄像机安装使用时间")
private String gbInstallTime;
if (this.getGbPtzType() != null) {
content.append(" <PTZType>" + this.getGbPtzType() + "</PTZType>\n");
}
if (this.getGbPositionType() != null) {
content.append(" <PositionType>" + this.getGbPositionType() + "</PositionType>\n");
}
if (this.getGbRoomType() != null) {
content.append(" <RoomType>" + this.getGbRoomType() + "</RoomType>\n");
}
if (this.getGbUseType() != null) {
content.append(" <UseType>" + this.getGbUseType() + "</UseType>\n");
}
if (this.getGbSupplyLightType() != null) {
content.append(" <SupplyLightType>" + this.getGbSupplyLightType() + "</SupplyLightType>\n");
}
if (this.getGbDirectionType() != null) {
content.append(" <DirectionType>" + this.getGbDirectionType() + "</DirectionType>\n");
}
if (this.getGbResolution() != null) {
content.append(" <Resolution>" + this.getGbResolution() + "</Resolution>\n");
}
if (this.getGbBusinessGroupId() != null) {
content.append(" <BusinessGroupID>" + this.getGbBusinessGroupId() + "</BusinessGroupID>\n");
}
if (this.getGbDownloadSpeed() != null) {
content.append(" <DownloadSpeed>" + this.getGbDownloadSpeed() + "</DownloadSpeed>\n");
}
if (this.getGbSvcSpaceSupportMod() != null) {
content.append(" <SVCSpaceSupportMode>" + this.getGbSvcSpaceSupportMod() + "</SVCSpaceSupportMode>\n");
}
if (this.getGbSvcTimeSupportMode() != null) {
content.append(" <SVCTimeSupportMode>" + this.getGbSvcTimeSupportMode() + "</SVCTimeSupportMode>\n");
}
content.append("</Info>\n");
}
}
if (event != null) {
content.append("<Event>" + event + "</Event>\n");
}
content.append("</Item>\n");
return content.toString();
}
// 2022
@Schema(description = "国标-摄像机所属管理单位名称")
private String gbManagementUnit;
public static CommonGBChannel build(Group group) {
GbCode gbCode = GbCode.decode(group.getDeviceId());
CommonGBChannel channel = new CommonGBChannel();
if (gbCode.getTypeCode().equals("215")) {
// 业务分组
channel.setGbName(group.getName());
channel.setGbDeviceId(group.getDeviceId());
channel.setGbCivilCode(group.getCivilCode());
} else {
// 虚拟组织
channel.setGbName(group.getName());
channel.setGbDeviceId(group.getDeviceId());
channel.setGbParentId(group.getParentDeviceId());
channel.setGbBusinessGroupId(group.getBusinessGroup());
channel.setGbCivilCode(group.getCivilCode());
}
return channel;
}
// 2022
@Schema(description = "国标-摄像机所属管理单位联系人的联系方式(电话号码,可多值,用英文半角“/”分割)")
private String gbContactInfo;
// 2022
@Schema(description = "国标-录像保存天数(可选)")
private Integer gbRecordSaveDays;
// 2022
@Schema(description = "国标-国民经济行业分类代码(可选)")
private String gbIndustrialClassification;
public static CommonGBChannel build(Platform platform) {
CommonGBChannel commonGBChannel = new CommonGBChannel();
commonGBChannel.setGbDeviceId(platform.getDeviceGBId());
commonGBChannel.setGbName(platform.getName());
commonGBChannel.setGbManufacturer(platform.getManufacturer());
commonGBChannel.setGbModel(platform.getModel());
commonGBChannel.setGbCivilCode(platform.getCivilCode());
commonGBChannel.setGbAddress(platform.getAddress());
commonGBChannel.setGbRegisterWay(platform.getRegisterWay());
commonGBChannel.setGbSecrecy(platform.getSecrecy());
commonGBChannel.setGbStatus(platform.isStatus() ? "ON" : "OFF");
return commonGBChannel;
}
public static CommonGBChannel build(Region region) {
CommonGBChannel commonGBChannel = new CommonGBChannel();
commonGBChannel.setGbDeviceId(region.getDeviceId());
commonGBChannel.setGbName(region.getName());
return commonGBChannel;
}
}

View File

@ -2,14 +2,19 @@ package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 国标设备/平台
* @author lin
*/
@Data
@Schema(description = "国标设备/平台")
public class Device {
@Schema(description = "数据库自增ID")
private int id;
/**
* 设备国标编号
*/
@ -168,7 +173,7 @@ public class Device {
private boolean ssrcCheck = false;
/**
* 地理坐标系 目前支持 WGS84,GCJ02
* 地理坐标系 目前支持 WGS84,GCJ02, 此字段保留暂无用
*/
@Schema(description = "地理坐标系, 目前支持 WGS84,GCJ02")
private String geoCoordSys;
@ -190,273 +195,4 @@ public class Device {
@Schema(description = "控制语音对讲流程释放收到ACK后发流")
private boolean broadcastPushAfterAck;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getFirmware() {
return firmware;
}
public void setFirmware(String firmware) {
this.firmware = firmware;
}
public String getTransport() {
return transport;
}
public void setTransport(String transport) {
this.transport = transport;
}
public String getStreamMode() {
return streamMode;
}
public Integer getStreamModeForParam() {
if (streamMode == null) {
return 0;
}
if (streamMode.equalsIgnoreCase("UDP")) {
return 0;
}else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) {
return 1;
}else if (streamMode.equalsIgnoreCase("TCP-ACTIVE")) {
return 2;
}
return 0;
}
public void setStreamMode(String streamMode) {
this.streamMode = streamMode;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getHostAddress() {
return hostAddress;
}
public void setHostAddress(String hostAddress) {
this.hostAddress = hostAddress;
}
public boolean isOnLine() {
return onLine;
}
public void setOnLine(boolean onLine) {
this.onLine = onLine;
}
public int getChannelCount() {
return channelCount;
}
public void setChannelCount(int channelCount) {
this.channelCount = channelCount;
}
public String getRegisterTime() {
return registerTime;
}
public void setRegisterTime(String registerTime) {
this.registerTime = registerTime;
}
public String getKeepaliveTime() {
return keepaliveTime;
}
public void setKeepaliveTime(String keepaliveTime) {
this.keepaliveTime = keepaliveTime;
}
public int getExpires() {
return expires;
}
public void setExpires(int expires) {
this.expires = expires;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public int getSubscribeCycleForCatalog() {
return subscribeCycleForCatalog;
}
public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) {
this.subscribeCycleForCatalog = subscribeCycleForCatalog;
}
public int getSubscribeCycleForMobilePosition() {
return subscribeCycleForMobilePosition;
}
public void setSubscribeCycleForMobilePosition(int subscribeCycleForMobilePosition) {
this.subscribeCycleForMobilePosition = subscribeCycleForMobilePosition;
}
public int getMobilePositionSubmissionInterval() {
return mobilePositionSubmissionInterval;
}
public void setMobilePositionSubmissionInterval(int mobilePositionSubmissionInterval) {
this.mobilePositionSubmissionInterval = mobilePositionSubmissionInterval;
}
public int getSubscribeCycleForAlarm() {
return subscribeCycleForAlarm;
}
public void setSubscribeCycleForAlarm(int subscribeCycleForAlarm) {
this.subscribeCycleForAlarm = subscribeCycleForAlarm;
}
public boolean isSsrcCheck() {
return ssrcCheck;
}
public void setSsrcCheck(boolean ssrcCheck) {
this.ssrcCheck = ssrcCheck;
}
public String getGeoCoordSys() {
return geoCoordSys;
}
public void setGeoCoordSys(String geoCoordSys) {
this.geoCoordSys = geoCoordSys;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSdpIp() {
return sdpIp;
}
public void setSdpIp(String sdpIp) {
this.sdpIp = sdpIp;
}
public String getLocalIp() {
return localIp;
}
public void setLocalIp(String localIp) {
this.localIp = localIp;
}
public int getKeepaliveIntervalTime() {
return keepaliveIntervalTime;
}
public void setKeepaliveIntervalTime(int keepaliveIntervalTime) {
this.keepaliveIntervalTime = keepaliveIntervalTime;
}
public boolean isAsMessageChannel() {
return asMessageChannel;
}
public void setAsMessageChannel(boolean asMessageChannel) {
this.asMessageChannel = asMessageChannel;
}
public SipTransactionInfo getSipTransactionInfo() {
return sipTransactionInfo;
}
public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
this.sipTransactionInfo = sipTransactionInfo;
}
public boolean isBroadcastPushAfterAck() {
return broadcastPushAfterAck;
}
public void setBroadcastPushAfterAck(boolean broadcastPushAfterAck) {
this.broadcastPushAfterAck = broadcastPushAfterAck;
}
}

View File

@ -1,260 +1,186 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.utils.MessageElementForCatalog;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.InvocationTargetException;
@Data
@Slf4j
@Schema(description = "通道信息")
public class DeviceChannel {
@EqualsAndHashCode(callSuper = true)
public class DeviceChannel extends CommonGBChannel {
/**
* 数据库自增ID
*/
@Schema(description = "数据库自增ID")
private int id;
/**
* 通道国标编号
*/
@Schema(description = "通道国标编号")
private String channelId;
@Schema(description = "设备的数据库自增ID")
private Integer deviceDbId;
/**
* 设备国标编号
*/
@Schema(description = "设备国标编号")
@MessageElementForCatalog("DeviceID")
@Schema(description = "编码")
private String deviceId;
/**
* 通道名
*/
@MessageElementForCatalog("Name")
@Schema(description = "名称")
private String name;
/**
* 生产厂商
*/
@Schema(description = "生产厂商")
private String manufacture;
/**
* 型号
*/
@Schema(description = "型号")
@MessageElementForCatalog("Manufacturer")
@Schema(description = "设备厂商")
private String manufacturer;
@MessageElementForCatalog("Model")
@Schema(description = "设备型号")
private String model;
/**
* 设备归属
*/
// 2016
@MessageElementForCatalog("Owner")
@Schema(description = "设备归属")
private String owner;
/**
* 行政区域
*/
@MessageElementForCatalog("CivilCode")
@Schema(description = "行政区域")
private String civilCode;
/**
* 警区
*/
@MessageElementForCatalog("Block")
@Schema(description = "警区")
private String block;
/**
* 安装地址
*/
@MessageElementForCatalog("Address")
@Schema(description = "安装地址")
private String address;
/**
* 是否有子设备 1有, 0没有
*/
@Schema(description = "是否有子设备 1有, 0没有")
private int parental;
/**
* 父级id
*/
@Schema(description = "父级id")
@MessageElementForCatalog("Parental")
@Schema(description = "是否有子设备(必选)1有,0没有")
private Integer parental;
@MessageElementForCatalog("ParentID")
@Schema(description = "父节点ID")
private String parentId;
/**
* 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式
*/
@Schema(description = "信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式")
private int safetyWay;
/**
* 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 ; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式
*/
@Schema(description = "注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式")
private int registerWay;
/**
* 证书序列号
*/
// 2016
@MessageElementForCatalog("SafetyWay")
@Schema(description = "信令安全模式")
private Integer safetyWay;
@MessageElementForCatalog("RegisterWay")
@Schema(description = "注册方式")
private Integer registerWay;
// 2016
@MessageElementForCatalog("CertNum")
@Schema(description = "证书序列号")
private String certNum;
/**
* 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效
*/
@Schema(description = "证书有效标识 缺省为0;证书有效标识:0:无效1: 有效")
private int certifiable;
/**
* 证书无效原因码
*/
@Schema(description = "证书无效原因码")
private int errCode;
/**
* 证书终止有效期
*/
@Schema(description = "证书终止有效期")
// 2016
@MessageElementForCatalog("Certifiable")
@Schema(description = "证书有效标识, 缺省为0;证书有效标识:0:无效 1:有效")
private Integer certifiable;
// 2016
@MessageElementForCatalog("ErrCode")
@Schema(description = "无效原因码(有证书且证书无效的设备必选)")
private Integer errCode;
// 2016
@MessageElementForCatalog("EndTime")
@Schema(description = "证书终止有效期(有证书且证书无效的设备必选)")
private String endTime;
/**
* 保密属性 缺省为0; 0:不涉密, 1:涉密
*/
@Schema(description = "保密属性 缺省为0; 0:不涉密, 1:涉密")
private String secrecy;
/**
* IP地址
*/
@Schema(description = "IP地址")
@MessageElementForCatalog("Secrecy")
@Schema(description = "保密属性(必选)缺省为0;0-不涉密,1-涉密")
private Integer secrecy;
@MessageElementForCatalog("IPAddress")
@Schema(description = "设备/系统IPv4/IPv6地址")
private String ipAddress;
/**
* 端口号
*/
@Schema(description = "端口号")
private int port;
/**
* 密码
*/
@Schema(description = "密码")
@MessageElementForCatalog("Port")
@Schema(description = "设备/系统端口")
private Integer port;
@MessageElementForCatalog("Password")
@Schema(description = "设备口令")
private String password;
/**
* 云台类型
*/
@Schema(description = "云台类型")
private int ptzType;
@MessageElementForCatalog("Status")
@Schema(description = "设备状态")
private String status;
@MessageElementForCatalog("Longitude")
@Schema(description = "经度 WGS-84坐标系")
private Double longitude;
@MessageElementForCatalog("Latitude")
@Schema(description = ",纬度 WGS-84坐标系")
private Double latitude;
@MessageElementForCatalog("Info.PTZType")
@Schema(description = "摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;7-多目设备的分割通道")
private Integer ptzType;
@MessageElementForCatalog("Info.PositionType")
@Schema(description = "摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、" +
"6-商业中心、7-宗教场所、8-校园周边、9-治安复杂区域、10-交通干线")
private Integer positionType;
@MessageElementForCatalog("Info.RoomType")
@Schema(description = "摄像机安装位置室外、室内属性。1-室外、2-室内。")
private Integer roomType;
@MessageElementForCatalog("Info.UseType")
@Schema(description = "用途属性, 1-治安、2-交通、3-重点。")
private Integer useType;
@MessageElementForCatalog("Info.SupplyLightType")
@Schema(description = "摄像机补光属性。1-无补光;2-红外补光;3-白光补光;4-激光补光;9-其他")
private Integer supplyLightType;
@MessageElementForCatalog("Info.DirectionType")
@Schema(description = "摄像机监视方位(光轴方向)属性。1-东(西向东)、2-西(东向西)、3-南(北向南)、4-北(南向北)、" +
"5-东南(西北到东南)、6-东北(西南到东北)、7-西南(东北到西南)、8-西北(东南到西北)")
private Integer directionType;
@MessageElementForCatalog("Info.Resolution")
@Schema(description = "摄像机支持的分辨率,可多值")
private String resolution;
@MessageElementForCatalog({"BusinessGroupID","Info.BusinessGroupID"})
@Schema(description = "虚拟组织所属的业务分组ID")
private String businessGroupId;
@MessageElementForCatalog("Info.DownloadSpeed")
@Schema(description = "下载倍速(可选),可多值")
private String downloadSpeed;
@MessageElementForCatalog("Info.SVCSpaceSupportMode")
@Schema(description = "空域编码能力,取值0-不支持;1-1级增强(1个增强层);2-2级增强(2个增强层);3-3级增强(3个增强层)")
private Integer svcSpaceSupportMod;
@MessageElementForCatalog("Info.SVCTimeSupportMode")
@Schema(description = "时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)")
private Integer svcTimeSupportMode;
/**
* 云台类型描述字符串
*/
@Schema(description = "云台类型描述字符串")
private String ptzTypeText;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private String createTime;
/**
* 更新时间
*/
@Schema(description = "更新时间")
private String updateTime;
/**
* 在线/离线
* 1在线,0离线
* 默认在线
* 信令:
* <Status>ON</Status>
* <Status>OFF</Status>
* 遇到过NVR下的IPC下发信令可以推流 但是 Status 响应 OFF
*/
@Schema(description = "在线/离线, 1在线,0离线")
private boolean status;
/**
* 经度
*/
@Schema(description = "经度")
private double longitude;
/**
* 纬度
*/
@Schema(description = "纬度")
private double latitude;
/**
* 经度
*/
@Schema(description = "自定义经度")
private double customLongitude;
/**
* 纬度
*/
@Schema(description = "自定义纬度")
private double customLatitude;
/**
* 经度 GCJ02
*/
@Schema(description = "GCJ02坐标系经度")
private double longitudeGcj02;
/**
* 纬度 GCJ02
*/
@Schema(description = "GCJ02坐标系纬度")
private double latitudeGcj02;
/**
* 经度 WGS84
*/
@Schema(description = "WGS84坐标系经度")
private double longitudeWgs84;
/**
* 纬度 WGS84
*/
@Schema(description = "WGS84坐标系纬度")
private double latitudeWgs84;
/**
* 子设备数
*/
@Schema(description = "子设备数")
private int subCount;
/**
* 流唯一编号存在表示正在直播
*/
@Schema(description = "流唯一编号,存在表示正在直播")
private String streamId;
/**
* 是否含有音频
*/
@Schema(description = "是否含有音频")
private Boolean hasAudio;
private boolean hasAudio;
/**
* 标记通道的类型0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划
*/
@Schema(description = "标记通道的类型0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划")
private int channelType;
/**
* 业务分组
*/
@Schema(description = "业务分组")
private String businessGroupId;
/**
* GPS的更新时间
*/
@Schema(description = "GPS的更新时间")
private String gpsTime;
@ -262,21 +188,8 @@ public class DeviceChannel {
"用于选择码流时组成码流标识。默认为null不设置。可选值: stream/streamnumber/streamprofile/streamMode")
private String streamIdentification;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
@Schema(description = "通道类型, 默认0, 0 普通通道1 行政区划 2 业务分组/虚拟组织")
private int channelType;
public void setPtzType(int ptzType) {
this.ptzType = ptzType;
@ -296,322 +209,40 @@ public class DeviceChannel {
case 4:
this.ptzTypeText = "遥控枪机";
break;
case 5:
this.ptzTypeText = "遥控半球";
break;
case 6:
this.ptzTypeText = "多目设备的全景/拼接通道";
break;
case 7:
this.ptzTypeText = "多目设备的分割通道";
break;
}
}
public String getChannelId() {
return channelId;
public static DeviceChannel decode(Element element) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
DeviceChannel deviceChannel = XmlUtil.elementDecode(element, DeviceChannel.class);
if(deviceChannel.getCivilCode() != null ) {
if (ObjectUtils.isEmpty(deviceChannel.getCivilCode())
|| deviceChannel.getCivilCode().length() > 8 ){
deviceChannel.setCivilCode(null);
}
// 此处对于不在wvp缓存中的行政区划,默认直接存储.保证即使出现wvp的行政区划缓存过老,也可以通过用户自主创建的方式正常使用系统
}
GbCode gbCode = GbCode.decode(deviceChannel.getDeviceId());
if (gbCode != null && "138".equals(gbCode.getTypeCode())) {
deviceChannel.setHasAudio(true);
}
return deviceChannel;
}
public static DeviceChannel decodeWithOnlyDeviceId(Element element) {
Element deviceElement = element.element("DeviceID");
DeviceChannel deviceChannel = new DeviceChannel();
deviceChannel.setDeviceId(deviceElement.getText());
return deviceChannel;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getManufacture() {
return manufacture;
}
public void setManufacture(String manufacture) {
this.manufacture = manufacture;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getCivilCode() {
return civilCode;
}
public void setCivilCode(String civilCode) {
this.civilCode = civilCode;
}
public String getBlock() {
return block;
}
public void setBlock(String block) {
this.block = block;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getParental() {
return parental;
}
public void setParental(int parental) {
this.parental = parental;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public int getSafetyWay() {
return safetyWay;
}
public void setSafetyWay(int safetyWay) {
this.safetyWay = safetyWay;
}
public int getRegisterWay() {
return registerWay;
}
public void setRegisterWay(int registerWay) {
this.registerWay = registerWay;
}
public String getCertNum() {
return certNum;
}
public void setCertNum(String certNum) {
this.certNum = certNum;
}
public int getCertifiable() {
return certifiable;
}
public void setCertifiable(int certifiable) {
this.certifiable = certifiable;
}
public int getErrCode() {
return errCode;
}
public void setErrCode(int errCode) {
this.errCode = errCode;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public String getSecrecy() {
return secrecy;
}
public void setSecrecy(String secrecy) {
this.secrecy = secrecy;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getPtzType() {
return ptzType;
}
public String getPtzTypeText() {
return ptzTypeText;
}
public void setPtzTypeText(String ptzTypeText) {
this.ptzTypeText = ptzTypeText;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitudeGcj02() {
return longitudeGcj02;
}
public void setLongitudeGcj02(double longitudeGcj02) {
this.longitudeGcj02 = longitudeGcj02;
}
public double getLatitudeGcj02() {
return latitudeGcj02;
}
public void setLatitudeGcj02(double latitudeGcj02) {
this.latitudeGcj02 = latitudeGcj02;
}
public double getLongitudeWgs84() {
return longitudeWgs84;
}
public void setLongitudeWgs84(double longitudeWgs84) {
this.longitudeWgs84 = longitudeWgs84;
}
public double getLatitudeWgs84() {
return latitudeWgs84;
}
public void setLatitudeWgs84(double latitudeWgs84) {
this.latitudeWgs84 = latitudeWgs84;
}
public int getSubCount() {
return subCount;
}
public void setSubCount(int subCount) {
this.subCount = subCount;
}
public Boolean getHasAudio() {
return hasAudio;
}
public void setHasAudio(Boolean hasAudio) {
this.hasAudio = hasAudio;
}
public String getStreamId() {
return streamId;
}
public void setStreamId(String streamId) {
this.streamId = streamId;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public int getChannelType() {
return channelType;
}
public void setChannelType(int channelType) {
this.channelType = channelType;
}
public String getBusinessGroupId() {
return businessGroupId;
}
public void setBusinessGroupId(String businessGroupId) {
this.businessGroupId = businessGroupId;
}
public String getGpsTime() {
return gpsTime;
}
public void setGpsTime(String gpsTime) {
this.gpsTime = gpsTime;
}
public String getStreamIdentification() {
return streamIdentification;
}
public void setStreamIdentification(String streamIdentification) {
this.streamIdentification = streamIdentification;
}
public double getCustomLongitude() {
return customLongitude;
}
public void setCustomLongitude(double customLongitude) {
this.customLongitude = customLongitude;
}
public double getCustomLatitude() {
return customLatitude;
}
public void setCustomLatitude(double customLatitude) {
this.customLatitude = customLatitude;
}
}

View File

@ -0,0 +1,58 @@
package com.genersoft.iot.vmp.gb28181.bean;
import org.jetbrains.annotations.NotNull;
public class DeviceType implements Comparable<DeviceType>{
/**
* 编号
*/
private String name;
/**
* 名称
*/
private String code;
/**
* 归属名称
*/
private String ownerName;
public static DeviceType getInstance(DeviceTypeEnum typeEnum) {
DeviceType deviceType = new DeviceType();
deviceType.setName(typeEnum.getName());
deviceType.setCode(typeEnum.getCode());
deviceType.setOwnerName(typeEnum.getOwnerName());
return deviceType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getOwnerName() {
return ownerName;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
@Override
public int compareTo(@NotNull DeviceType deviceType) {
return Integer.compare(Integer.parseInt(this.code), Integer.parseInt(deviceType.getCode()));
}
}

View File

@ -0,0 +1,98 @@
package com.genersoft.iot.vmp.gb28181.bean;
/**
* 收录行业编码
*/
public enum DeviceTypeEnum {
DVR("111", "DVR编码", "前端主设备"),
VIDEO_SERVER("112", "视频服务器编码", "前端主设备"),
ENCODER("113", "编码器编码", "前端主设备"),
DECODER("114", "解码器编码", "前端主设备"),
VIDEO_SWITCHING_MATRIX("115", "视频切换矩阵编码", "前端主设备"),
AUDIO_SWITCHING_MATRIX("116", "音频切换矩阵编码", "前端主设备"),
ALARM_CONTROLLER("117", "报警控制器编码", "前端主设备"),
NVR("118", "网络视频录像机NVR编码", "前端主设备"),
RESERVE("119", "预留", "前端主设备"),
ONLINE_VIDEO_IMAGE_INFORMATION_ACQUISITION_SYSTEM("120", "在线视频图像信息采集系统编码", "前端主设备"),
VIDEO_CHECKPOINT("121", "视频卡口编码", "前端主设备"),
MULTI_CAMERA_DEVICE("122", "多目设备编码", "前端主设备"),
PARKING_LOT_ENTRANCE_AND_EXIT_CONTROL_EQUIPMENT("123", "停车场出入口控制设备编码", "前端主设备"),
PERSONNEL_ACCESS_CONTROL_EQUIPMENT("124", "人员出入口控制设备编码", "前端主设备"),
SECURITY_INSPECTION_EQUIPMENT("125", "安检设备编码", "前端主设备"),
HVR("130", "混合硬盘录像机HVR编码", "前端主设备"),
CAMERA("131", "摄像机编码", "前端外围设备"),
IPC("132", "网络摄像机IPC/在线视频图像信息采集设备编码", "前端外围设备"),
MONITOR("133", "显示器编码", "前端外围设备"),
ALARM_INPUT_DEVICE("134", "报警输入设备编码(如红外、烟感、门禁等报警设备)", "前端外围设备"),
ALARM_OUTPUT_DEVICE("135", "报警输出设备编码(如警灯、警铃等设备)", "前端外围设备"),
VOICE_INPUT_DEVICE("136", "语音输入设备编码", "前端外围设备"),
VOICE_OUTPUT_DEVICE("137", "语音输出设备", "前端外围设备"),
MOBILE_TRANSMISSION_EQUIPMENT("138", "移动传输设备编码", "前端外围设备"),
OTHER_PERIPHERAL_DEVICES("139", "其他外围设备编码", "前端外围设备"),
ALARM_OUTPUT_DEVICE2("140", "报警输出设备编码(如继电器或触发器控制的设备)", "前端外围设备"),
BARRIER_GATE("141", "道闸(控制车辆通行)", "前端外围设备"),
SMART_DOOR("142", "智能门(控制人员通行)", "前端外围设备"),
VOUCHER_RECOGNITION_UNIT("143", "凭证识别单元", "前端外围设备"),
CENTRAL_SIGNALING_CONTROL_SERVER("200", "中心信令控制服务器编码", "平台设备"),
WEB_APPLICATION_SERVER("201", "Web应用服务器编码", "平台设备"),
PROXY_SERVER("203", "代理服务器编码", "平台设备"),
SECURITY_SERVER("204", "安全服务器编码", "平台设备"),
ALARM_SERVER("205", "报警服务器编码", "平台设备"),
DATABASE_SERVER("206", "数据库服务器编码", "平台设备"),
GIS_SERVER("207", "GIS服务器编码", "平台设备"),
MANAGER_SERVER("208", "管理服务器编码", "平台设备"),
ACCESS_GATEWAY("209", "接入网关编码", "平台设备"),
MEDIA_STORAGE_SERVER("210", "媒体存储服务器编码", "平台设备"),
SIGNALING_SECURITY_ROUTING_GATEWAY("211", "信令安全路由网关编码", "平台设备"),
BUSINESS_GROUP("215", "业务分组编码", "平台设备"),
VIRTUAL_ORGANIZATION("216", "虚拟组织编码", "平台设备"),
CENTRAL_USER("300", "中心用户", "中心用户"),
END_USER("400", "终端用户", "终端用户"),
VIDEO_IMAGE_INFORMATION_SYNTHESIS("500", "视频图像信息综合应用平台", "平台外接服务器"),
VIDEO_IMAGE_INFORMATION_OPERATION_AND_MAINTENANCE_MANAGEMENT("501", "视频图像信息运维管理平台", "平台外接服务器"),
VIDEO_IMAGE_ANALYSIS("502", "视频图像分析系统", "平台外接服务器"),
VIDEO_IMAGE_INFORMATION_DATABASE("503", "视频图像信息数据库", "平台外接服务器"),
VIDEO_IMAGE_ANALYSIS_EQUIPMENT("505", "视频图像分析设备", "平台外接服务器"),
;
/**
* 编号
*/
private final String name;
/**
* 名称
*/
private String code;
/**
* 归属名称
*/
private String ownerName;
DeviceTypeEnum(String code, String name, String ownerName) {
this.name = name;
this.code = code;
this.ownerName = ownerName;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getOwnerName() {
return ownerName;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
}

View File

@ -1,9 +1,7 @@
package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.core.CommonLogger;
import gov.nist.core.Host;
import gov.nist.core.HostNameParser;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.SIPConstants;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.GenericURI;
@ -14,16 +12,16 @@ import gov.nist.javax.sip.message.SIPMessage;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.parser.*;
import lombok.extern.slf4j.Slf4j;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
@Slf4j
public class GBStringMsgParser implements MessageParser {
protected static boolean computeContentLengthFromMessage = false;
private static StackLogger logger = CommonLogger.getLogger(StringMsgParser.class);
/**
* @since v0.9
*/
@ -60,8 +58,8 @@ public class GBStringMsgParser implements MessageParser {
}
catch (ArrayIndexOutOfBoundsException e) {
// Array contains only control char, return null.
if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
logger.logDebug("handled only control char so returning null");
if (log.isDebugEnabled()) {
log.debug("handled only control char so returning null");
}
return null;
}

View File

@ -0,0 +1,48 @@
package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 国标编码对象
*/
@Data
@Schema(description = "国标编码对象")
public class GbCode {
@Schema(description = "中心编码,由监控中心所在地的行政区划代码确定,符合GB/T2260—2007的要求")
private String centerCode;
@Schema(description = "行业编码")
private String industryCode;
@Schema(description = "类型编码")
private String typeCode;
@Schema(description = "网络标识")
private String netCode;
@Schema(description = "序号")
private String sn;
/**
* 解析国标编号
*/
public static GbCode decode(String code){
if (code == null || code.trim().length() != 20) {
return null;
}
code = code.trim();
GbCode gbCode = new GbCode();
gbCode.setCenterCode(code.substring(0, 8));
gbCode.setIndustryCode(code.substring(8, 10));
gbCode.setTypeCode(code.substring(10, 13));
gbCode.setNetCode(code.substring(13, 14));
gbCode.setSn(code.substring(14));
return gbCode;
}
public String ecode(){
return centerCode + industryCode + typeCode + netCode + sn;
}
}

View File

@ -0,0 +1,93 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.utils.DateUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
/**
* 业务分组
*/
@Data
@Schema(description = "业务分组")
public class Group implements Comparable<Group>{
/**
* 数据库自增ID
*/
@Schema(description = "数据库自增ID")
private int id;
/**
* 区域国标编号
*/
@Schema(description = "区域国标编号")
private String deviceId;
/**
* 区域名称
*/
@Schema(description = "区域名称")
private String name;
/**
* 父分组ID
*/
@Schema(description = "父分组ID")
private Integer parentId;
/**
* 父区域国标ID
*/
@Schema(description = "父区域国标ID")
private String parentDeviceId;
/**
* 所属的业务分组国标编号
*/
@Schema(description = "所属的业务分组国标编号")
private String businessGroup;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private String createTime;
/**
* 更新时间
*/
@Schema(description = "更新时间")
private String updateTime;
/**
* 行政区划
*/
@Schema(description = "行政区划")
private String civilCode;
public static Group getInstance(DeviceChannel channel) {
GbCode gbCode = GbCode.decode(channel.getDeviceId());
if (gbCode == null || (!gbCode.getTypeCode().equals("215") && !gbCode.getTypeCode().equals("216"))) {
return null;
}
Group group = new Group();
group.setName(channel.getName());
group.setDeviceId(channel.getDeviceId());
group.setCreateTime(DateUtil.getNow());
group.setUpdateTime(DateUtil.getNow());
if (gbCode.getTypeCode().equals("215")) {
group.setBusinessGroup(channel.getDeviceId());
}else if (gbCode.getTypeCode().equals("216")) {
group.setBusinessGroup(channel.getBusinessGroupId());
group.setParentDeviceId(channel.getParentId());
}
if (group.getBusinessGroup() == null) {
return null;
}
return group;
}
@Override
public int compareTo(@NotNull Group region) {
return Integer.compare(Integer.parseInt(this.deviceId), Integer.parseInt(region.getDeviceId()));
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 业务分组
*/
@Data
@Schema(description = "业务分组树")
public class GroupTree extends Group{
@Schema(description = "树节点ID")
private String treeId;
@Schema(description = "是否有子节点")
private boolean isLeaf;
@Schema(description = "类型, 行政区划:0 摄像头: 1")
private int type;
}

View File

@ -0,0 +1,59 @@
package com.genersoft.iot.vmp.gb28181.bean;
import org.jetbrains.annotations.NotNull;
public class IndustryCodeType implements Comparable<IndustryCodeType>{
/**
* 接入类型码
*/
private String name;
/**
* 名称
*/
private String code;
/**
* 备注
*/
private String notes;
public static IndustryCodeType getInstance(IndustryCodeTypeEnum typeEnum) {
IndustryCodeType industryCodeType = new IndustryCodeType();
industryCodeType.setName(typeEnum.getName());
industryCodeType.setCode(typeEnum.getCode());
industryCodeType.setNotes(typeEnum.getNotes());
return industryCodeType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
@Override
public int compareTo(@NotNull IndustryCodeType industryCodeType) {
return Integer.compare(Integer.parseInt(this.code), Integer.parseInt(industryCodeType.getCode()));
}
}

View File

@ -0,0 +1,55 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
/**
* 收录行业编码
*/
public enum IndustryCodeTypeEnum {
SOCIAL_SECURITY_ROAD("00", "社会治安路面接入", "包括城市路面、商业街、公共区域、重点区域"),
SOCIAL_SECURITY_COMMUNITY("01", "社会治安社区接入", "包括社区、楼宇、网吧等"),
SOCIAL_SECURITY__INTERNAL("02", "社会治安内部接入 ", "包括公安办公楼、留置室等"),
SOCIAL_SECURITY_OTHER("03", "社会治安其他接入", ""),
TRAFFIC_ROAD("04", "交通路面接入 ", "包括城市主要干道、国道、高速交通状况监视"),
TRAFFIC_BAYONET("05", "交通卡口接入", "包括交叉路口、“电子警察”、关口、收费站等"),
TRAFFIC_INTERNAL("06", "交通内部接入", "包括交管办公楼等"),
TRAFFIC_OTHER("07", "交通其他接入", ""),
CITY_MANAGEMENT("08", "城市管理接入", ""),
HEALTH_ENVIRONMENTAL_PROTECTION("09", "卫生环保接入", ""),
COMMODITY_INSPECTION_CUSTOMHOUSE("10", "商检海关接入", ""),
EDUCATION_SECTOR("11", "教育部门接入", ""),
CIVIL_AVIATION("12", "民航接入", ""),
RAILWAY("13", "铁路接入", ""),
SHIPPING("14", "航运接入", ""),
AGRICULTURE_FORESTRY_ANIMAL_HUSBANDRY_FISHING("40", "农、林、牧、渔业接入", ""),
MINING("41", "采矿业接入", ""),
MANUFACTURING_INDUSTRY("42", "制造业接入", ""),
ELECTRICITY_HEAT_GAS_AND_WATER_PRODUCTION_AND_SUPPLY("43", "电力、热力、燃气及水生产和供应业接入", ""),
CONSTRUCTION("44", "建筑业接入", ""),
WHOLESALE_AND_RETAIL("45", "批发和零售业接入", ""),
;
/**
* 接入类型码
*/
@Getter
private String name;
/**
* 名称
*/
@Getter
private String code;
/**
* 备注
*/
@Getter
private String notes;
IndustryCodeTypeEnum(String code, String name, String notes) {
this.name = name;
this.code = code;
this.notes = notes;
}
}

View File

@ -0,0 +1,14 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
@Data
public class InviteDecodeException extends RuntimeException{
private int code;
private String msg;
public InviteDecodeException(int code, String msg) {
this.code = code;
this.msg = msg;
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
// 从INVITE消息中解析需要的信息
@Data
public class InviteInfo {
private String requesterId;
private String targetChannelId;
private String sourceChannelId;
private String sessionName;
private String ssrc;
private boolean tcp;
private boolean tcpActive;
private String callId;
private Long startTime;
private Long stopTime;
private String downloadSpeed;
private String ip;
private int port;
}

View File

@ -1,11 +1,14 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
/**
* @description: 移动位置bean
* @author: lawrencehj
* @date: 2021年1月23日
*/
@Data
public class MobilePosition {
/**
* 设备Id
@ -15,7 +18,7 @@ public class MobilePosition {
/**
* 通道Id
*/
private String channelId;
private Integer channelId;
/**
* 设备名称
@ -56,150 +59,8 @@ public class MobilePosition {
* 位置信息上报来源Mobile PositionGPS Alarm
*/
private String reportSource;
/**
* 国内坐标系经度坐标
*/
private double longitudeGcj02;
/**
* 国内坐标系纬度坐标
*/
private double latitudeGcj02;
/**
* 国内坐标系经度坐标
*/
private double longitudeWgs84;
/**
* 国内坐标系纬度坐标
*/
private double latitudeWgs84;
/**
* 创建时间
*/
private String createTime;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getAltitude() {
return altitude;
}
public void setAltitude(double altitude) {
this.altitude = altitude;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public double getDirection() {
return direction;
}
public void setDirection(double direction) {
this.direction = direction;
}
public String getReportSource() {
return reportSource;
}
public void setReportSource(String reportSource) {
this.reportSource = reportSource;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public double getLongitudeGcj02() {
return longitudeGcj02;
}
public void setLongitudeGcj02(double longitudeGcj02) {
this.longitudeGcj02 = longitudeGcj02;
}
public double getLatitudeGcj02() {
return latitudeGcj02;
}
public void setLatitudeGcj02(double latitudeGcj02) {
this.latitudeGcj02 = latitudeGcj02;
}
public double getLongitudeWgs84() {
return longitudeWgs84;
}
public void setLongitudeWgs84(double longitudeWgs84) {
this.longitudeWgs84 = longitudeWgs84;
}
public double getLatitudeWgs84() {
return latitudeWgs84;
}
public void setLatitudeWgs84(double latitudeWgs84) {
this.latitudeWgs84 = latitudeWgs84;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,45 @@
package com.genersoft.iot.vmp.gb28181.bean;
import org.jetbrains.annotations.NotNull;
public class NetworkIdentificationType implements Comparable<NetworkIdentificationType>{
/**
* 接入类型码
*/
private String name;
/**
* 名称
*/
private String code;
public static NetworkIdentificationType getInstance(NetworkIdentificationTypeEnum typeEnum) {
NetworkIdentificationType industryCodeType = new NetworkIdentificationType();
industryCodeType.setName(typeEnum.getName());
industryCodeType.setCode(typeEnum.getCode());
return industryCodeType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public int compareTo(@NotNull NetworkIdentificationType networkIdentificationType) {
return Integer.compare(Integer.parseInt(this.code), Integer.parseInt(networkIdentificationType.getCode()));
}
}

View File

@ -0,0 +1,51 @@
package com.genersoft.iot.vmp.gb28181.bean;
/**
* 收录行业编码
*/
public enum NetworkIdentificationTypeEnum {
PUBLIC_SECURITY_VIDEO_TRANSMISSION_NETWORK("0", "公安视频传输网"),
PUBLIC_SECURITY_VIDEO_TRANSMISSION_NETWORK2("1", "公安视频传输网"),
INDUSTRY_SPECIFIC_NETWORK("2", "行业专网"),
POLITICAL_AND_LEGAL_INFORMATION_NETWORK("3", "政法信息网"),
PUBLIC_SECURITY_MOBILE_INFORMATION_NETWORK("4", "公安移动信息网"),
PUBLIC_SECURITY_INFORMATION_NETWORK("5", "公安信息网"),
ELECTRONIC_GOVERNMENT_EXTRANET("6", "电子政务外网"),
PUBLIC_NETWORKS_SUCH_AS_THE_INTERNET("7", "互联网等公共网络"),
Dedicated_Line("8", "专线"),
RESERVE("9", "预留"),
;
/**
* 接入类型码
*/
private String name;
/**
* 名称
*/
private String code;
NetworkIdentificationTypeEnum(String code, String name) {
this.name = name;
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@ -0,0 +1,38 @@
package com.genersoft.iot.vmp.gb28181.bean;
public class NotifyCatalogChannel {
private Type type;
private DeviceChannel channel;
public enum Type {
ADD, DELETE, UPDATE, STATUS_CHANGED
}
public static NotifyCatalogChannel getInstance(Type type, DeviceChannel channel) {
NotifyCatalogChannel notifyCatalogChannel = new NotifyCatalogChannel();
notifyCatalogChannel.setType(type);
notifyCatalogChannel.setChannel(channel);
return notifyCatalogChannel;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public DeviceChannel getChannel() {
return channel;
}
public void setChannel(DeviceChannel channel) {
this.channel = channel;
}
}

View File

@ -0,0 +1,12 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.media.event.hook.HookData;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import lombok.Data;
@Data
public class OpenRTPServerResult {
private SSRCInfo ssrcInfo;
private HookData hookData;
}

View File

@ -1,450 +0,0 @@
package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* @author lin
*/
@Schema(description = "平台信息")
public class ParentPlatform {
/**
* id
*/
@Schema(description = "ID(数据库中)")
private Integer id;
/**
* 是否启用
*/
@Schema(description = "是否启用")
private boolean enable;
/**
* 名称
*/
@Schema(description = "名称")
private String name;
/**
* SIP服务国标编码
*/
@Schema(description = "SIP服务国标编码")
private String serverGBId;
/**
* SIP服务国标域
*/
@Schema(description = "SIP服务国标域")
private String serverGBDomain;
/**
* SIP服务IP
*/
@Schema(description = "SIP服务IP")
private String serverIP;
/**
* SIP服务端口
*/
@Schema(description = "SIP服务端口")
private int serverPort;
/**
* 设备国标编号
*/
@Schema(description = "设备国标编号")
private String deviceGBId;
/**
* 设备ip
*/
@Schema(description = "设备ip")
private String deviceIp;
/**
* 设备端口
*/
@Schema(description = "设备端口")
private int devicePort;
/**
* SIP认证用户名(默认使用设备国标编号)
*/
@Schema(description = "SIP认证用户名(默认使用设备国标编号)")
private String username;
/**
* SIP认证密码
*/
@Schema(description = "SIP认证密码")
private String password;
/**
* 注册周期 ()
*/
@Schema(description = "注册周期 (秒)")
private int expires;
/**
* 心跳周期()
*/
@Schema(description = "心跳周期(秒)")
private int keepTimeout;
/**
* 传输协议
* UDP/TCP
*/
@Schema(description = "传输协议")
private String transport;
/**
* 字符集
*/
@Schema(description = "字符集")
private String characterSet;
/**
* 允许云台控制
*/
@Schema(description = "允许云台控制")
private boolean ptz;
/**
* RTCP流保活
*/
@Schema(description = "RTCP流保活")
private boolean rtcp;
/**
* 在线状态
*/
@Schema(description = "在线状态")
private boolean status;
/**
* 在线状态
*/
@Schema(description = "在线状态")
private int channelCount;
/**
* 默认目录Id,自动添加的通道多放在这个目录下
*/
@Schema(description = "默认目录Id,自动添加的通道多放在这个目录下")
private String catalogId;
/**
* 已被订阅目录信息
*/
@Schema(description = "已被订阅目录信息")
private boolean catalogSubscribe;
/**
* 已被订阅报警信息
*/
@Schema(description = "已被订阅报警信息")
private boolean alarmSubscribe;
/**
* 已被订阅移动位置信息
*/
@Schema(description = "已被订阅移动位置信息")
private boolean mobilePositionSubscribe;
/**
* 点播未推流的设备时是否使用redis通知拉起
*/
@Schema(description = "点播未推流的设备时是否使用redis通知拉起")
private boolean startOfflinePush;
/**
* 目录分组-每次向上级发送通道信息时单个包携带的通道数量取值1,2,4,8
*/
@Schema(description = "目录分组-每次向上级发送通道信息时单个包携带的通道数量取值1,2,4,8")
private int catalogGroup;
/**
* 行政区划
*/
@Schema(description = "行政区划")
private String administrativeDivision;
/**
* 更新时间
*/
@Schema(description = "更新时间")
private String updateTime;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private String createTime;
@Schema(description = "是否作为消息通道")
private boolean asMessageChannel;
@Schema(description = "是否作为消息通道")
private boolean autoPushChannel;
@Schema(description = "点播回复200OK使用次IP")
private String sendStreamIp;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getServerGBId() {
return serverGBId;
}
public void setServerGBId(String serverGBId) {
this.serverGBId = serverGBId;
}
public String getServerGBDomain() {
return serverGBDomain;
}
public void setServerGBDomain(String serverGBDomain) {
this.serverGBDomain = serverGBDomain;
}
public String getServerIP() {
return serverIP;
}
public void setServerIP(String serverIP) {
this.serverIP = serverIP;
}
public int getServerPort() {
return serverPort;
}
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
public String getDeviceGBId() {
return deviceGBId;
}
public void setDeviceGBId(String deviceGBId) {
this.deviceGBId = deviceGBId;
}
public String getDeviceIp() {
return deviceIp;
}
public void setDeviceIp(String deviceIp) {
this.deviceIp = deviceIp;
}
public int getDevicePort() {
return devicePort;
}
public void setDevicePort(int devicePort) {
this.devicePort = devicePort;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getExpires() {
return expires;
}
public void setExpires(int expires) {
this.expires = expires;
}
public int getKeepTimeout() {
return keepTimeout;
}
public void setKeepTimeout(int keepTimeout) {
this.keepTimeout = keepTimeout;
}
public String getTransport() {
return transport;
}
public void setTransport(String transport) {
this.transport = transport;
}
public String getCharacterSet() {
return characterSet;
}
public void setCharacterSet(String characterSet) {
this.characterSet = characterSet;
}
public boolean isPtz() {
return ptz;
}
public void setPtz(boolean ptz) {
this.ptz = ptz;
}
public boolean isRtcp() {
return rtcp;
}
public void setRtcp(boolean rtcp) {
this.rtcp = rtcp;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public int getChannelCount() {
return channelCount;
}
public void setChannelCount(int channelCount) {
this.channelCount = channelCount;
}
public String getCatalogId() {
return catalogId;
}
public void setCatalogId(String catalogId) {
this.catalogId = catalogId;
}
public boolean isCatalogSubscribe() {
return catalogSubscribe;
}
public void setCatalogSubscribe(boolean catalogSubscribe) {
this.catalogSubscribe = catalogSubscribe;
}
public boolean isAlarmSubscribe() {
return alarmSubscribe;
}
public void setAlarmSubscribe(boolean alarmSubscribe) {
this.alarmSubscribe = alarmSubscribe;
}
public boolean isMobilePositionSubscribe() {
return mobilePositionSubscribe;
}
public void setMobilePositionSubscribe(boolean mobilePositionSubscribe) {
this.mobilePositionSubscribe = mobilePositionSubscribe;
}
public boolean isStartOfflinePush() {
return startOfflinePush;
}
public void setStartOfflinePush(boolean startOfflinePush) {
this.startOfflinePush = startOfflinePush;
}
public int getCatalogGroup() {
return catalogGroup;
}
public void setCatalogGroup(int catalogGroup) {
this.catalogGroup = catalogGroup;
}
public String getAdministrativeDivision() {
return administrativeDivision;
}
public void setAdministrativeDivision(String administrativeDivision) {
this.administrativeDivision = administrativeDivision;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public boolean isAsMessageChannel() {
return asMessageChannel;
}
public void setAsMessageChannel(boolean asMessageChannel) {
this.asMessageChannel = asMessageChannel;
}
public boolean isAutoPushChannel() {
return autoPushChannel;
}
public void setAutoPushChannel(boolean autoPushChannel) {
this.autoPushChannel = autoPushChannel;
}
public String getSendStreamIp() {
return sendStreamIp;
}
public void setSendStreamIp(String sendStreamIp) {
this.sendStreamIp = sendStreamIp;
}
}

View File

@ -1,68 +0,0 @@
package com.genersoft.iot.vmp.gb28181.bean;
public class ParentPlatformCatch {
private String id;
/**
* 心跳未回复次数
*/
private int keepAliveReply;
// 注册未回复次数
private int registerAliveReply;
private String callId;
private ParentPlatform parentPlatform;
private SipTransactionInfo sipTransactionInfo;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getKeepAliveReply() {
return keepAliveReply;
}
public void setKeepAliveReply(int keepAliveReply) {
this.keepAliveReply = keepAliveReply;
}
public int getRegisterAliveReply() {
return registerAliveReply;
}
public void setRegisterAliveReply(int registerAliveReply) {
this.registerAliveReply = registerAliveReply;
}
public ParentPlatform getParentPlatform() {
return parentPlatform;
}
public void setParentPlatform(ParentPlatform parentPlatform) {
this.parentPlatform = parentPlatform;
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
public SipTransactionInfo getSipTransactionInfo() {
return sipTransactionInfo;
}
public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
this.sipTransactionInfo = sipTransactionInfo;
}
}

View File

@ -0,0 +1,130 @@
package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author lin
*/
@Data
@Schema(description = "平台信息")
public class Platform {
@Schema(description = "ID(数据库中)")
private Integer id;
@Schema(description = "是否启用")
private boolean enable;
@Schema(description = "名称")
private String name;
@Schema(description = "SIP服务国标编码")
private String serverGBId;
@Schema(description = "SIP服务国标域")
private String serverGBDomain;
@Schema(description = "SIP服务IP")
private String serverIp;
@Schema(description = "SIP服务端口")
private int serverPort;
@Schema(description = "设备国标编号")
private String deviceGBId;
@Schema(description = "设备ip")
private String deviceIp;
@Schema(description = "设备端口")
private int devicePort;
@Schema(description = "SIP认证用户名(默认使用设备国标编号)")
private String username;
@Schema(description = "SIP认证密码")
private String password;
@Schema(description = "注册周期 (秒)")
private int expires;
@Schema(description = "心跳周期(秒)")
private int keepTimeout;
@Schema(description = "传输协议")
private String transport;
@Schema(description = "字符集")
private String characterSet;
@Schema(description = "允许云台控制")
private boolean ptz;
@Schema(description = "RTCP流保活")
private boolean rtcp;
@Schema(description = "在线状态")
private boolean status;
@Schema(description = "通道数量")
private int channelCount;
@Schema(description = "已被订阅目录信息")
private boolean catalogSubscribe;
@Schema(description = "已被订阅报警信息")
private boolean alarmSubscribe;
@Schema(description = "已被订阅移动位置信息")
private boolean mobilePositionSubscribe;
@Schema(description = "目录分组-每次向上级发送通道信息时单个包携带的通道数量取值1,2,4,8")
private int catalogGroup;
@Schema(description = "更新时间")
private String updateTime;
@Schema(description = "创建时间")
private String createTime;
@Schema(description = "是否作为消息通道")
private boolean asMessageChannel;
@Schema(description = "点播回复200OK使用的IP")
private String sendStreamIp;
@Schema(description = "是否自动推送通道变化")
private Boolean autoPushChannel;
@Schema(description = "目录信息包含平台信息, 0关闭1打开")
private int catalogWithPlatform;
@Schema(description = "目录信息包含分组信息, 0关闭1打开")
private int catalogWithGroup;
@Schema(description = "目录信息包含行政区划, 0关闭1打开")
private int catalogWithRegion;
@Schema(description = "行政区划")
private String civilCode;
@Schema(description = "平台厂商")
private String manufacturer;
@Schema(description = "平台型号")
private String model;
@Schema(description = "平台安装地址")
private String address;
@Schema(description = "注册方式必选缺省为1 " +
"1-符合IETF RFC 3261标准的认证注册模式" +
"2-基于口令的双向认证注册模式;" +
"3-基于数字证书的双向认证注册模式(高安全级别要求)" +
"4-基于数字证书的单向认证注册模式(高安全级别要求)")
private int registerWay = 1;
@Schema(description = "保密属性必选缺省为00-不涉密1-涉密")
private int secrecy = 0;
}

View File

@ -0,0 +1,24 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
@Data
public class PlatformCatch {
private String id;
/**
* 心跳未回复次数
*/
private int keepAliveReply;
// 注册未回复次数
private int registerAliveReply;
private String callId;
private Platform platform;
private SipTransactionInfo sipTransactionInfo;
}

Some files were not shown because too many files have changed in this diff Show More