Merge branch 'master' into dev/1078-newUI
# Conflicts: # src/main/resources/application.yml
50
README.md
@@ -12,12 +12,13 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网
|
||||
|
||||
流媒体服务基于@夏楚 ZLMediaKit [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
|
||||
播放器使用@dexter jessibuca [https://github.com/langhuihui/jessibuca/tree/v3](https://github.com/langhuihui/jessibuca/tree/v3)
|
||||
前端页面基于@Kyle MediaServerUI [https://gitee.com/kkkkk5G/MediaServerUI](https://gitee.com/kkkkk5G/MediaServerUI) 进行修改.
|
||||
播放器使用@Numberwolf-Yanlong h265web.js [https://github.com/numberwolf/h265web.js](https://github.com/numberwolf/h265web.js)
|
||||
前端页面基于vue-admin-template构建 [https://github.com/PanJiaChen/vue-admin-template?tab=readme-ov-file](https://github.com/PanJiaChen/vue-admin-template?tab=readme-ov-file)
|
||||
|
||||
# 应用场景:
|
||||
支持浏览器无插件播放摄像头视频。
|
||||
支持国标设备(摄像机、平台、NVR等)设备接入
|
||||
支持非国标(onvif, rtsp, rtmp,直播设备等等)设备接入,充分利旧。
|
||||
支持rtsp, rtmp,直播设备设备接入,充分利旧。
|
||||
支持国标级联。多平台级联。跨网视频预览。
|
||||
支持跨网网闸平台互联。
|
||||
|
||||
@@ -29,19 +30,31 @@ ZLM使用文档 [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZL
|
||||
# 付费社群
|
||||
[](https://t.zsxq.com/0d8VAD3Dm)
|
||||
> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接自行推出,星球会直接退款给大家。
|
||||
> 星球还提供了基于主线master分支的打包, 会随时更新。
|
||||
|
||||
# gitee同步仓库
|
||||
> 星球还提供了包括闭源的全功能试用包, 会随时更新。
|
||||
|
||||
# gitee仓库
|
||||
https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
|
||||
# 截图
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<table>
|
||||
<tr>
|
||||
<td ><center><img src="doc/_media/1.png" >登录页面 </center></td>
|
||||
<td ><center><img src="doc/_media/2.png" >首页</center></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td ><center><img src="doc/_media/3.png" >分屏播放 </center></td>
|
||||
<td ><center><img src="doc/_media/4.png" >国标设备列表</center></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td ><center><img src="doc/_media/5.png" >行政区划管理 </center></td>
|
||||
<td ><center><img src="doc/_media/8.png" >业务分组管理</center></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td ><center><img src="doc/_media/6.png" >录制计划</center></td>
|
||||
<td ><center><img src="doc/_media/7.png" >平台信息</center></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# 功能特性
|
||||
- [X] 集成web界面
|
||||
@@ -117,10 +130,10 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
- [X] 支持国标信令集群
|
||||
|
||||
|
||||
# 非开源的内容
|
||||
- [X] ONVIF设备的接入,支持点播,云台控制,国标级联点播,自动点播。试用安装包以及使用教程: [知识星球](https://t.zsxq.com/10WAnH2MP),没有使用时间限制,需要源码可以星球私信我或者邮箱联系。
|
||||
# 闭源内容
|
||||
- [X] ONVIF设备的接入,支持点播,云台控制,国标级联点播,自动点播。
|
||||
- [X] 支持部标1078+808协议,支持点播,云台控制,录像回放,位置上报,自动点播。
|
||||
- [X] 支持国标28181-2022协议,支持巡航轨迹查询,PTZ精准控制,存储卡格式化,设备软件升级,OSD配置,h265+aac,支持辅码流,录像倒放等。具体的功能列表可在[知识星球](https://t.zsxq.com/18GXkpkqs)查看,试用安装包: [知识星球](https://t.zsxq.com/UJ6V3),没有使用时间限制,需要源码可以星球私信我或者邮箱联系。
|
||||
- [X] 支持国标28181-2022协议,支持巡航轨迹查询,PTZ精准控制,存储卡格式化,设备软件升级,OSD配置,h265+aac,支持辅码流,录像倒放等。
|
||||
|
||||
|
||||
# 授权协议
|
||||
@@ -131,19 +144,14 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
[知识星球](https://t.zsxq.com/0d8VAD3Dm)专栏列表:,
|
||||
- [使用入门系列一:WVP-PRO能做什么](https://t.zsxq.com/0dLguVoSp)
|
||||
|
||||
有偿技术支持,请发送邮件到648540858@qq.com
|
||||
有偿技术支持,一对一开发辅导,闭源内容合作请发送邮件到648540858@qq.com咨询
|
||||
|
||||
# 致谢
|
||||
感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架,并在开发过程中给予支持与帮助。
|
||||
感谢作者[dexter langhuihui](https://github.com/langhuihui) 开源这么好用的WEB播放器。
|
||||
感谢作者[Kyle](https://gitee.com/kkkkk5G) 开源了好用的前端页面
|
||||
感谢作者[dexter langhuihui](https://github.com/langhuihui)和[Numberwolf-Yanlong](https://github.com/numberwolf/h265web.js) 开源这么好用的WEB播放器。
|
||||
感谢各位大佬的赞助以及对项目的指正与帮助。包括但不限于代码贡献、问题反馈、资金捐赠等各种方式的支持!以下排名不分先后:
|
||||
[lawrencehj](https://github.com/lawrencehj) [Smallwhitepig](https://github.com/Smallwhitepig) [swwhaha](https://github.com/swwheihei)
|
||||
[hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen)
|
||||
[chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb)
|
||||
[ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
|
||||
[mk1990](https://github.com/mk1990) [SaltFish001](https://github.com/SaltFish001)
|
||||
|
||||
同时感谢JetBrains对开源项目的支持,本项目使用IntelliJ IDEA开发与调试:
|
||||
|
||||

|
||||
|
||||
115
bin/wvp.sh
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
function log() {
|
||||
message="[Polaris Log]: $1 "
|
||||
case "$1" in
|
||||
*"Fail"* | *"Error"* | *"请使用 root 或 sudo 权限运行此脚本"*)
|
||||
echo -e "${RED}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
*"Success"*)
|
||||
echo -e "${GREEN}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
*"Ignore"* | *"Jump"*)
|
||||
echo -e "${YELLOW}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
*)
|
||||
echo -e "${BLUE}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
esac
|
||||
}
|
||||
echo
|
||||
cat <<EOF
|
||||
██████╗ ██████╗ ██╗ █████╗ ██████╗ ██╗███████╗
|
||||
██╔══██╗██╔═══██╗██║ ██╔══██╗██╔══██╗██║██╔════╝
|
||||
██████╔╝██║ ██║██║ ███████║██████╔╝██║███████╗
|
||||
██╔═══╝ ██║ ██║██║ ██╔══██║██╔══██╗██║╚════██║
|
||||
██║ ╚██████╔╝███████╗██║ ██║██║ ██║██║███████║
|
||||
╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝
|
||||
|
||||
EOF
|
||||
|
||||
#配置jdk的路径
|
||||
export JAVA_HOME=/usr/local/java/jdk1.8.0_202 #此处为JDK路径
|
||||
export JRE_HOME=${JAVA_HOME}/jre
|
||||
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
|
||||
export PATH=${JAVA_HOME}/bin:$PATH
|
||||
|
||||
# WVP-pro defines
|
||||
AppName=wvp-pro-2.7.2-05131055.jar
|
||||
AppHome="/root/polaris/wvp/"
|
||||
# JVM参数
|
||||
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
|
||||
|
||||
function start() {
|
||||
log "======================= 开启流媒体服务 ======================="
|
||||
log "AppName: $AppName"
|
||||
log "AppHome: $AppHome"
|
||||
log "Success:流媒体服务开启成功"
|
||||
}
|
||||
|
||||
function stop() {
|
||||
log "======================= 停止流媒体服务 ======================="
|
||||
|
||||
PID=""
|
||||
query() {
|
||||
PID=$(ps -ef | grep java | grep $AppName | grep -v grep | awk '{print $2}')
|
||||
}
|
||||
query
|
||||
if [ x"$PID" != x"" ]; then
|
||||
log "进程PID: $PID"
|
||||
kill -TERM $PID
|
||||
log "$AppName (pid:$PID) exiting..."
|
||||
while [ x"$PID" != x"" ]; do
|
||||
sleep 1
|
||||
query
|
||||
done
|
||||
log "Success:$AppName exited."
|
||||
else
|
||||
log "Jump:进程不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
function status() {
|
||||
log "======================= 运行状态 ======================="
|
||||
log ""
|
||||
|
||||
PID=$(ps -ef | grep java | grep $AppName | grep -v grep | wc -l)
|
||||
if [ $PID != 0 ]; then
|
||||
log "进程PID: $PID"
|
||||
log "$AppName is running..."
|
||||
else
|
||||
log "$AppName is not running..."
|
||||
fi
|
||||
log ""
|
||||
log "========================================================"
|
||||
}
|
||||
|
||||
function restart() {
|
||||
stop
|
||||
sleep 3
|
||||
start
|
||||
}
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
*) ;;
|
||||
|
||||
esac
|
||||
|
||||
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 214 KiB |
@@ -8,15 +8,18 @@
|
||||
|
||||
左侧树结构为行政区划结构, 通过数据鼠标右键可以操作,包括: 刷新节点,新建节点,编辑节点,删除节点,添加设备(
|
||||
可以将某个国标设备下的通道全部添加道某一个节点下),移除设备(可以将某个国标设备下的通道全部从这个节点移除)
|
||||
右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作, 添加状态选择"未添加"可以进行添加操作,选择"
|
||||
已添加"进行移除操作
|
||||

|
||||
右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作。
|
||||
选择左侧的节点后,可以点击右侧的“添加通道”, 选择需要的通道添加到改节点下,如果找不到通道, 可以选择“异常挂载通道”,点击清理后重新回来选择。
|
||||

|
||||
|
||||
## 业务分组
|
||||
|
||||
左侧树结构为业务分组结构, 通过数据鼠标右键可以操作,包括: 刷新节点,新建节点,编辑节点,删除节点,添加设备(
|
||||
可以将某个国标设备下的通道全部添加道某一个节点下),移除设备(可以将某个国标设备下的通道全部从这个节点移除)
|
||||
业务分组下不能挂载设备,所以没有选择该节点的单选框.
|
||||
右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作, 添加状态选择"未添加"可以进行添加操作,选择"
|
||||
已添加"进行移除操作
|
||||

|
||||
右侧为通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作。
|
||||
|
||||
选择左侧的节点后,可以点击右侧的“添加通道”, 选择需要的通道添加到改节点下。
|
||||
如果找不到通道, 可以选择“异常挂载通道”,点击清理后重新回来选择。
|
||||
注意,根资源组下的那一级为业务分组类型不可以直接挂载设备,需要继续建立节点,后续的节点的都是虚拟组织类型, 就可以挂载通道了。
|
||||

|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# 云端录像
|
||||
|
||||

|
||||

|
||||
云端录像是对录制在zlm服务下的录像文件的管理,录像的文件路径默认在ZLM/www/record下。
|
||||
|
||||
- 国标设备是否录像: 可以再WVP的配置中user-settings.record-sip设置为true那么每次点播以及录像回放都会录像;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# 节点管理
|
||||
|
||||

|
||||

|
||||
|
||||
WVP支持单个WVP多个ZLM的方案来扩展WVP的视频并发能力,并发点播是因为带宽和性能的原因,单个ZLM节点能支持的路数有限,所以WVP增加了ZLM集群来扩展并发并且保证ZLM的高可用。
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 180 KiB |
@@ -87,9 +87,9 @@ git clone https://github.com/648540858/wvp-GB28181-pro.git
|
||||
### 5.2 编译前端页面
|
||||
|
||||
```shell script
|
||||
cd wvp-GB28181-pro/web_src/
|
||||
cd wvp-GB28181-pro/web/
|
||||
npm --registry=https://registry.npmmirror.com install
|
||||
npm run build
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
编译如果报错, 一般都是网络问题, 导致的依赖包下载失败
|
||||
|
||||
@@ -44,10 +44,7 @@ wvp支持多种数据库,包括Mysql,Postgresql,金仓等,配置任选
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
dynamic:
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
|
||||
@@ -61,8 +58,6 @@ spring:
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
dynamic:
|
||||
primary: master
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: org.postgresql.Driver
|
||||
@@ -80,8 +75,6 @@ pagehelper:
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
dynamic:
|
||||
primary: master
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.kingbase8.Driver
|
||||
@@ -89,6 +82,7 @@ spring:
|
||||
username: root
|
||||
password: 12345678
|
||||
|
||||
|
||||
pagehelper:
|
||||
helper-dialect: postgresql
|
||||
```
|
||||
|
||||
@@ -41,19 +41,7 @@ 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)
|
||||
前端基于 [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template/blob/master/README-zh.md) 构建, 参考这儿即可。
|
||||
|
||||
### 默认账号和密码
|
||||
|
||||
|
||||
BIN
doc/_media/1.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
doc/_media/2.png
|
Before Width: | Height: | Size: 938 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 920 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
BIN
doc/_media/3.png
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 1.0 MiB |
BIN
doc/_media/4.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
doc/_media/5.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
doc/_media/6.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
doc/_media/7.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
doc/_media/8.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 96 KiB |
@@ -10,7 +10,7 @@
|
||||
* [推流列表](_content/ability/push.md)
|
||||
* [拉流代理](_content/ability/proxy.md)
|
||||
* [云端录像](_content/ability/cloud_record.md)
|
||||
* [节点管理](_content/ability/node_manger.md)
|
||||
* [节点管理](_content/ability/node_manager.md)
|
||||
* [通道管理](_content/ability/channel.md)
|
||||
* [国标级联](_content/ability/cascade2.md)
|
||||
* **流程与原理**
|
||||
|
||||
45
docker/build.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
cd wvp-GB28181-pro/web_src && \
|
||||
npm install && \
|
||||
npm run build
|
||||
|
||||
cd ../../
|
||||
mkdir -p ./nginx/dist
|
||||
cp -r wvp-GB28181-pro/src/main/resources/static/* ./nginx/dist
|
||||
|
||||
echo "构建ZLM容器"
|
||||
cd ./media/
|
||||
chmod +x ./build.sh
|
||||
./build.sh
|
||||
cd ../
|
||||
|
||||
echo "构建数据库容器"
|
||||
cd ./mysql/
|
||||
chmod +x ./build.sh
|
||||
./build.sh
|
||||
cd ../
|
||||
|
||||
echo "构建Redis容器"
|
||||
cd ./redis/
|
||||
chmod +x ./build.sh
|
||||
./build.sh
|
||||
cd ../
|
||||
|
||||
echo "构建WVP容器"
|
||||
cd ./wvp/
|
||||
chmod +x ./build.sh
|
||||
./build.sh
|
||||
cd ../
|
||||
|
||||
echo "构建Nginx容器"
|
||||
cd ./nginx/
|
||||
chmod +x ./build.sh
|
||||
./build.sh
|
||||
cd ../
|
||||
|
||||
./push.sh
|
||||
@@ -1,49 +1,125 @@
|
||||
version: '3'
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
volumes:
|
||||
- ./redis/redis.conf:/etc/redis/redis_default.conf
|
||||
- ./redis/data/:/data
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
command: redis-server /etc/redis/redis_default.conf --appendonly yes
|
||||
wvp:
|
||||
build:
|
||||
context: ./wvp
|
||||
args:
|
||||
gitUrl: "https://gitee.com/pan648540858"
|
||||
zlmGitUrl: "https://gitee.com/xia-chu/ZLMediaKit"
|
||||
restart: always
|
||||
polaris-redis:
|
||||
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-redis:latest
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
networks:
|
||||
- media-net
|
||||
ports:
|
||||
- "5060:5060"
|
||||
- "5060:5060/udp"
|
||||
- "18080:18080"
|
||||
- "80:80"
|
||||
- "10000:10000/tcp"
|
||||
- "10000:10000/udp"
|
||||
- "30000-30500:30000-30500/tcp"
|
||||
- "30000-30500:30000-30500/udp"
|
||||
- 6379:6379
|
||||
volumes:
|
||||
- ./video:/opt/media/www/record/
|
||||
- ./logs/wvp:/opt/wvp/logs/
|
||||
- ./logs/assist:/opt/assist/logs/
|
||||
- ./logs/media:/opt/media/log/
|
||||
- ./redis/conf/redis.conf:/opt/polaris/redis/redis.conf
|
||||
- ./volumes/redis/data/:/data
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
# [必须修改] 本机的IP
|
||||
WVP_HOST: 172.18.0.61
|
||||
WVP_PWD: aseqw_+hiy123
|
||||
WVP_DOMAIN: 6101130049
|
||||
WVP_ID: 61011300490000000001
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_DB: 6
|
||||
REDIS_PWD: root
|
||||
ASSIST_JVM_CONFIG: -Xms128m -Xmx256m
|
||||
WVP_JVM_CONFIG: -Xms128m -Xmx256m
|
||||
ASSIST_CONFIG:
|
||||
WVP_CONFIG:
|
||||
command: redis-server /opt/polaris/redis/redis.conf --appendonly yes
|
||||
|
||||
polaris-mysql:
|
||||
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-mysql:latest
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306" ]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
networks:
|
||||
- media-net
|
||||
environment:
|
||||
MYSQL_DATABASE: wvp
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: root
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
- ./mysql/conf:/etc/mysql/conf.d
|
||||
- ./logs/mysql:/logs
|
||||
- ./volumes/mysql/data:/var/lib/mysql
|
||||
command: [
|
||||
'mysqld',
|
||||
'--default-authentication-plugin=mysql_native_password',
|
||||
'--innodb-buffer-pool-size=80M',
|
||||
'--character-set-server=utf8mb4',
|
||||
'--collation-server=utf8mb4_general_ci',
|
||||
'--default-time-zone=+8:00',
|
||||
'--lower-case-table-names=1'
|
||||
]
|
||||
|
||||
polaris-media:
|
||||
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-media:latest
|
||||
restart: always
|
||||
networks:
|
||||
- media-net
|
||||
ports:
|
||||
- "10935:10935"
|
||||
- "5540:5540"
|
||||
- "6080:6080"
|
||||
volumes:
|
||||
- ./volumes/video:/opt/media/www/record/
|
||||
- ./logs/media:/opt/media/log/
|
||||
|
||||
polaris-wvp:
|
||||
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-wvp:latest
|
||||
restart: always
|
||||
networks:
|
||||
- media-net
|
||||
ports:
|
||||
- "18978:18978"
|
||||
- "8116:8116/udp"
|
||||
- "8116:8116/tcp"
|
||||
depends_on:
|
||||
- redis
|
||||
- polaris-redis
|
||||
- polaris-mysql
|
||||
- polaris-media
|
||||
links:
|
||||
- polaris-redis
|
||||
- polaris-mysql
|
||||
- polaris-media
|
||||
volumes:
|
||||
- ./wvp/wvp/:/opt/wvp/wvp/
|
||||
- ./logs/wvp:/opt/wvp/logs/
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
# 本机的IP
|
||||
SIP_HOST: 127.0.0.1
|
||||
STREAM_HOST: 127.0.0.1
|
||||
ZLM_HOST: polaris-media
|
||||
ZLM_PORT: 6080
|
||||
ZLM_SERCERT: su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
|
||||
REDIS_HOST: polaris-redis
|
||||
REDIS_PORT: 6379
|
||||
DATABASE_HOST: polaris-mysql
|
||||
DATABASE_PORT: 3306
|
||||
DATABASE_USER: wvp
|
||||
DATABASE_PASSWORD: wvp
|
||||
# 前端跨域配置,nginx容器所在物理机IP
|
||||
NGINX_HOST: http://127.0.0.1:8080
|
||||
|
||||
polaris-nginx:
|
||||
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-nginx:latest
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- polaris-wvp
|
||||
links:
|
||||
- polaris-wvp
|
||||
environment:
|
||||
WVP_HOST: polaris-wvp
|
||||
WVP_PORT: 18978
|
||||
volumes:
|
||||
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./logs/nginx:/var/log/nginx
|
||||
networks:
|
||||
- media-net
|
||||
|
||||
networks:
|
||||
media-net:
|
||||
driver: bridge
|
||||
5
docker/docker-upgrade.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
docker compose down
|
||||
docker compose up -d --remove-orphans
|
||||
91
docker/media/Dockerfile
Normal file
@@ -0,0 +1,91 @@
|
||||
FROM ubuntu:20.04 AS build
|
||||
|
||||
#shell,rtmp,rtsp,rtsps,http,rtp
|
||||
EXPOSE 10935/tcp
|
||||
EXPOSE 5540/tcp
|
||||
EXPOSE 6080/tcp
|
||||
EXPOSE 10000/udp
|
||||
EXPOSE 10000/tcp
|
||||
EXPOSE 8000/udp
|
||||
EXPOSE 8000/tcp
|
||||
EXPOSE 9000/udp
|
||||
|
||||
# ADD sources.list /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
curl \
|
||||
vim \
|
||||
wget \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
libssl-dev \
|
||||
gcc \
|
||||
g++ \
|
||||
gdb && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean -y && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /opt/media
|
||||
WORKDIR /opt/media
|
||||
RUN git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit && \
|
||||
cd ZLMediaKit && git submodule update --init
|
||||
|
||||
# 3rdpart init
|
||||
WORKDIR /opt/media/ZLMediaKit/3rdpart
|
||||
RUN wget https://polaris-tian-generic.pkg.coding.net/qt/dependencies/openssl-1.1.1k.tar.gz?version=latest -O openssl-1.1.1k.tar.gz && \
|
||||
tar -xvzf openssl-1.1.1k.tar.gz && \
|
||||
cd openssl-1.1.1k && ./config shared --openssldir=/usr/local/openssl --prefix=/usr/local/openssl && \
|
||||
make && make install && \
|
||||
echo "/usr/local/lib64/" >> /etc/ld.so.conf && \
|
||||
echo "/usr/local/openssl/lib" >> /etc/ld.so.conf && \
|
||||
ldconfig && \
|
||||
ln -s /usr/local/openssl/bin/openssl /usr/local/bin/openssl
|
||||
|
||||
WORKDIR /opt/media/ZLMediaKit/3rdpart
|
||||
RUN wget https://github.com/cisco/libsrtp/archive/v2.3.0.tar.gz -O libsrtp-2.3.0.tar.gz && \
|
||||
tar xfv libsrtp-2.3.0.tar.gz && \
|
||||
mv libsrtp-2.3.0 libsrtp && \
|
||||
cd libsrtp && ./configure --enable-openssl --with-openssl-dir=/usr/local/openssl && make -j $(nproc) && make install
|
||||
|
||||
|
||||
WORKDIR /opt/media/ZLMediaKit/build
|
||||
RUN cmake .. -DENABLE_WEBRTC=true -DOPENSSL_ROOT_DIR=/usr/local/openssl -DOPENSSL_LIBRARIES=/usr/local/openssl/lib && \
|
||||
cmake --build . --target MediaServer
|
||||
COPY config.ini /opt/media/ZLMediaKit/release/linux/Debug/
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
apt-get install -y --no-install-recommends \
|
||||
vim \
|
||||
wget \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
curl \
|
||||
libssl-dev \
|
||||
ffmpeg \
|
||||
gcc \
|
||||
g++ \
|
||||
gdb && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean -y && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
|
||||
&& echo $TZ > /etc/timezone && \
|
||||
mkdir -p /opt/media/bin/www
|
||||
|
||||
WORKDIR /opt/media/bin/
|
||||
COPY --from=build /opt/media/ZLMediaKit/release/linux/Debug/MediaServer /opt/media/ZLMediaKit/default.pem /opt/media/bin/
|
||||
COPY --from=build /opt/media/ZLMediaKit/release/linux/Debug/config.ini /opt/media/conf/
|
||||
COPY --from=build /opt/media/ZLMediaKit/www/ /opt/media/bin/www/
|
||||
ENV PATH /opt/media/bin:$PATH
|
||||
CMD ["./MediaServer","-s", "default.pem", "-c", "../conf/config.ini", "-l","0"]
|
||||
8
docker/media/build.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
docker build -t polaris-media:${version} .
|
||||
docker tag polaris-media:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-media:${version}
|
||||
docker tag polaris-media:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-media:latest
|
||||
196
docker/media/config.ini
Normal file
@@ -0,0 +1,196 @@
|
||||
; auto-generated by mINI class {
|
||||
|
||||
[api]
|
||||
apiDebug=1
|
||||
defaultSnap=./www/logo.png
|
||||
downloadRoot=./www;
|
||||
secret=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
|
||||
snapRoot=./www/snap/
|
||||
|
||||
[cluster]
|
||||
origin_url=
|
||||
retry_count=3
|
||||
timeout_sec=15
|
||||
|
||||
[ffmpeg]
|
||||
bin=/usr/bin/ffmpeg
|
||||
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
|
||||
log=./ffmpeg/ffmpeg.log
|
||||
restart_sec=0
|
||||
snap=%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s
|
||||
|
||||
[general]
|
||||
broadcast_player_count_changed=0
|
||||
check_nvidia_dev=1
|
||||
enableVhost=0
|
||||
enable_ffmpeg_log=0
|
||||
flowThreshold=1024
|
||||
listen_ip=::
|
||||
maxStreamWaitMS=15000
|
||||
mediaServerId=polaris
|
||||
mergeWriteMS=0
|
||||
resetWhenRePlay=1
|
||||
streamNoneReaderDelayMS=20000
|
||||
unready_frame_cache=100
|
||||
wait_add_track_ms=3000
|
||||
wait_audio_track_data_ms=1000
|
||||
wait_track_ready_ms=10000
|
||||
|
||||
[hls]
|
||||
broadcastRecordTs=0
|
||||
deleteDelaySec=10
|
||||
fastRegister=0
|
||||
fileBufSize=65536
|
||||
segDelay=0
|
||||
segDur=2
|
||||
segKeep=0
|
||||
segNum=3
|
||||
segRetain=5
|
||||
|
||||
[hook]
|
||||
alive_interval=10.0
|
||||
enable=1
|
||||
on_flow_report=
|
||||
on_http_access=
|
||||
on_play=
|
||||
on_publish=
|
||||
on_record_mp4=
|
||||
on_record_ts=
|
||||
on_rtp_server_timeout=
|
||||
on_rtsp_auth=
|
||||
on_rtsp_realm=
|
||||
on_send_rtp_stopped=
|
||||
on_server_exited=
|
||||
on_server_keepalive=
|
||||
on_server_started=
|
||||
on_shell_login=
|
||||
on_stream_changed=
|
||||
on_stream_none_reader=
|
||||
on_stream_not_found=
|
||||
retry=1
|
||||
retry_delay=3.0
|
||||
stream_changed_schemas=rtsp/rtmp/fmp4/ts/hls/hls.fmp4
|
||||
timeoutSec=30
|
||||
|
||||
[http]
|
||||
allow_cross_domains=1
|
||||
allow_ip_range=
|
||||
charSet=utf-8
|
||||
dirMenu=1
|
||||
forbidCacheSuffix=
|
||||
forwarded_ip_header=
|
||||
keepAliveSecond=30
|
||||
maxReqSize=40960
|
||||
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit(git hash:8ccb4e9/%aI,branch:master,build time:2024-11-07T10:34:19)</center></body></html>
|
||||
port=6080
|
||||
rootPath=./www
|
||||
sendBufSize=65536
|
||||
sslport=4443
|
||||
virtualPath=
|
||||
|
||||
[multicast]
|
||||
addrMax=239.255.255.255
|
||||
addrMin=239.0.0.0
|
||||
udpTTL=64
|
||||
|
||||
[protocol]
|
||||
add_mute_audio=1
|
||||
auto_close=0
|
||||
continue_push_ms=3000
|
||||
enable_audio=1
|
||||
enable_fmp4=1
|
||||
enable_hls=1
|
||||
enable_hls_fmp4=0
|
||||
enable_mp4=0
|
||||
enable_rtmp=1
|
||||
enable_rtsp=1
|
||||
enable_ts=1
|
||||
fmp4_demand=0
|
||||
hls_demand=0
|
||||
hls_save_path=./www
|
||||
modify_stamp=2
|
||||
mp4_as_player=0
|
||||
mp4_max_second=3600
|
||||
mp4_save_path=/home
|
||||
paced_sender_ms=0
|
||||
rtmp_demand=0
|
||||
rtsp_demand=0
|
||||
ts_demand=0
|
||||
|
||||
[record]
|
||||
appName=record
|
||||
enableFmp4=0
|
||||
fastStart=0
|
||||
fileBufSize=65536
|
||||
fileRepeat=0
|
||||
sampleMS=500
|
||||
|
||||
[rtc]
|
||||
datachannel_echo=0
|
||||
externIP=
|
||||
maxRtpCacheMS=5000
|
||||
maxRtpCacheSize=2048
|
||||
max_bitrate=0
|
||||
min_bitrate=0
|
||||
nackIntervalRatio=1.0
|
||||
nackMaxCount=15
|
||||
nackMaxMS=3000
|
||||
nackMaxSize=2048
|
||||
nackRtpSize=8
|
||||
port=8000
|
||||
preferredCodecA=PCMA,PCMU,opus,mpeg4-generic
|
||||
preferredCodecV=H264,H265,AV1,VP9,VP8
|
||||
rembBitRate=0
|
||||
start_bitrate=0
|
||||
tcpPort=8000
|
||||
timeoutSec=30
|
||||
|
||||
[rtmp]
|
||||
directProxy=1
|
||||
enhanced=0
|
||||
handshakeSecond=15
|
||||
keepAliveSecond=15
|
||||
port=10935
|
||||
sslport=0
|
||||
|
||||
[rtp]
|
||||
audioMtuSize=600
|
||||
h264_stap_a=1
|
||||
lowLatency=0
|
||||
rtpMaxSize=10
|
||||
videoMtuSize=1400
|
||||
|
||||
[rtp_proxy]
|
||||
dumpDir=
|
||||
gop_cache=1
|
||||
h264_pt=98
|
||||
h265_pt=99
|
||||
opus_pt=100
|
||||
port=10000
|
||||
port_range=30000-30500
|
||||
ps_pt=96
|
||||
rtp_g711_dur_ms=100
|
||||
timeoutSec=15
|
||||
udp_recv_socket_buffer=4194304
|
||||
|
||||
[rtsp]
|
||||
authBasic=0
|
||||
directProxy=1
|
||||
handshakeSecond=15
|
||||
keepAliveSecond=15
|
||||
lowLatency=0
|
||||
port=5540
|
||||
rtpTransportType=-1
|
||||
sslport=0
|
||||
|
||||
[shell]
|
||||
maxReqSize=1024
|
||||
port=0
|
||||
|
||||
[srt]
|
||||
latencyMul=4
|
||||
pktBufSize=8192
|
||||
port=9000
|
||||
timeoutSec=5
|
||||
|
||||
; } ---
|
||||
3
docker/mysql/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM mysql:8.0.32
|
||||
|
||||
ADD ./db/*.sql /docker-entrypoint-initdb.d/
|
||||
8
docker/mysql/build.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
docker build -t polaris-mysql:${version} .
|
||||
docker tag polaris-mysql:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-mysql:${version}
|
||||
docker tag polaris-mysql:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-mysql:latest
|
||||
3
docker/mysql/db/privileges.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
use mysql;
|
||||
grant all privileges on wvp.* to 'ylcx'@'%';
|
||||
flush privileges;
|
||||
769
docker/mysql/db/wvp.sql
Normal file
@@ -0,0 +1,769 @@
|
||||
/*建库*/
|
||||
DROP DATABASE IF EXISTS `wvp`;
|
||||
|
||||
CREATE DATABASE `wvp` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
USE `wvp`;
|
||||
|
||||
/*建表*/
|
||||
drop table IF EXISTS wvp_device;
|
||||
create table IF NOT EXISTS wvp_device
|
||||
(
|
||||
id serial primary key,
|
||||
device_id character varying(50) not null,
|
||||
name character varying(255),
|
||||
manufacturer character varying(255),
|
||||
model character varying(255),
|
||||
firmware character varying(255),
|
||||
transport character varying(50),
|
||||
stream_mode character varying(50),
|
||||
on_line bool default false,
|
||||
register_time character varying(50),
|
||||
keepalive_time character varying(50),
|
||||
ip character varying(50),
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
port integer,
|
||||
expires integer,
|
||||
subscribe_cycle_for_catalog integer DEFAULT 0,
|
||||
subscribe_cycle_for_mobile_position integer DEFAULT 0,
|
||||
mobile_position_submission_interval integer DEFAULT 5,
|
||||
subscribe_cycle_for_alarm integer DEFAULT 0,
|
||||
host_address character varying(50),
|
||||
charset character varying(50),
|
||||
ssrc_check bool default false,
|
||||
geo_coord_sys character varying(50),
|
||||
media_server_id character varying(50) default 'auto',
|
||||
custom_name character varying(255),
|
||||
sdp_ip character varying(50),
|
||||
local_ip character varying(50),
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
heart_beat_interval integer,
|
||||
heart_beat_count integer,
|
||||
position_capability integer,
|
||||
broadcast_push_after_ack bool default false,
|
||||
server_id character varying(50),
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_device_alarm;
|
||||
create table IF NOT EXISTS wvp_device_alarm
|
||||
(
|
||||
id serial primary key,
|
||||
device_id character varying(50) not null,
|
||||
channel_id character varying(50) not null,
|
||||
alarm_priority character varying(50),
|
||||
alarm_method character varying(50),
|
||||
alarm_time character varying(50),
|
||||
alarm_description character varying(255),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
alarm_type character varying(50),
|
||||
create_time character varying(50) not null
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_device_mobile_position;
|
||||
create table IF NOT EXISTS wvp_device_mobile_position
|
||||
(
|
||||
id serial primary key,
|
||||
device_id character varying(50) not null,
|
||||
channel_id character varying(50) not null,
|
||||
device_name character varying(255),
|
||||
time character varying(50),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
altitude double precision,
|
||||
speed double precision,
|
||||
direction double precision,
|
||||
report_source character varying(50),
|
||||
create_time character varying(50)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_device_channel;
|
||||
create table IF NOT EXISTS wvp_device_channel
|
||||
(
|
||||
id serial primary key,
|
||||
device_id character varying(50),
|
||||
name character varying(255),
|
||||
manufacturer character varying(50),
|
||||
model character varying(50),
|
||||
owner character varying(50),
|
||||
civil_code character varying(50),
|
||||
block character varying(50),
|
||||
address character varying(50),
|
||||
parental integer,
|
||||
parent_id character varying(50),
|
||||
safety_way integer,
|
||||
register_way integer,
|
||||
cert_num character varying(50),
|
||||
certifiable integer,
|
||||
err_code integer,
|
||||
end_time character varying(50),
|
||||
secrecy integer,
|
||||
ip_address character varying(50),
|
||||
port integer,
|
||||
password character varying(255),
|
||||
status character varying(50),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
ptz_type integer,
|
||||
position_type integer,
|
||||
room_type integer,
|
||||
use_type integer,
|
||||
supply_light_type integer,
|
||||
direction_type integer,
|
||||
resolution character varying(255),
|
||||
business_group_id character varying(255),
|
||||
download_speed character varying(255),
|
||||
svc_space_support_mod integer,
|
||||
svc_time_support_mode integer,
|
||||
create_time character varying(50) not null,
|
||||
update_time character varying(50) not null,
|
||||
sub_count integer,
|
||||
stream_id character varying(255),
|
||||
has_audio bool default false,
|
||||
gps_time character varying(50),
|
||||
stream_identification character varying(50),
|
||||
channel_type int default 0 not null,
|
||||
gb_device_id character varying(50),
|
||||
gb_name character varying(255),
|
||||
gb_manufacturer character varying(255),
|
||||
gb_model character varying(255),
|
||||
gb_owner character varying(255),
|
||||
gb_civil_code character varying(255),
|
||||
gb_block character varying(255),
|
||||
gb_address character varying(255),
|
||||
gb_parental integer,
|
||||
gb_parent_id character varying(255),
|
||||
gb_safety_way integer,
|
||||
gb_register_way integer,
|
||||
gb_cert_num character varying(50),
|
||||
gb_certifiable integer,
|
||||
gb_err_code integer,
|
||||
gb_end_time character varying(50),
|
||||
gb_secrecy integer,
|
||||
gb_ip_address character varying(50),
|
||||
gb_port integer,
|
||||
gb_password character varying(50),
|
||||
gb_status character varying(50),
|
||||
gb_longitude double,
|
||||
gb_latitude double,
|
||||
gb_business_group_id character varying(50),
|
||||
gb_ptz_type integer,
|
||||
gb_position_type integer,
|
||||
gb_room_type integer,
|
||||
gb_use_type integer,
|
||||
gb_supply_light_type integer,
|
||||
gb_direction_type integer,
|
||||
gb_resolution character varying(255),
|
||||
gb_download_speed character varying(255),
|
||||
gb_svc_space_support_mod integer,
|
||||
gb_svc_time_support_mode integer,
|
||||
record_plan_id integer,
|
||||
data_type integer not null,
|
||||
data_device_id integer not null,
|
||||
gps_speed double precision,
|
||||
gps_altitude double precision,
|
||||
gps_direction double precision,
|
||||
index (data_type),
|
||||
index (data_device_id),
|
||||
constraint uk_wvp_unique_channel unique (gb_device_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_media_server;
|
||||
create table IF NOT EXISTS wvp_media_server
|
||||
(
|
||||
id character varying(255) primary key,
|
||||
ip character varying(50),
|
||||
hook_ip character varying(50),
|
||||
sdp_ip character varying(50),
|
||||
stream_ip character varying(50),
|
||||
http_port integer,
|
||||
http_ssl_port integer,
|
||||
rtmp_port integer,
|
||||
rtmp_ssl_port integer,
|
||||
rtp_proxy_port integer,
|
||||
rtsp_port integer,
|
||||
rtsp_ssl_port integer,
|
||||
flv_port integer,
|
||||
flv_ssl_port integer,
|
||||
ws_flv_port integer,
|
||||
ws_flv_ssl_port integer,
|
||||
auto_config bool default false,
|
||||
secret character varying(50),
|
||||
type character varying(50) default 'zlm',
|
||||
rtp_enable bool default false,
|
||||
rtp_port_range character varying(50),
|
||||
send_rtp_port_range character varying(50),
|
||||
record_assist_port integer,
|
||||
default_server bool default false,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
hook_alive_interval integer,
|
||||
record_path character varying(255),
|
||||
record_day integer default 7,
|
||||
transcode_suffix character varying(255),
|
||||
server_id character varying(50),
|
||||
constraint uk_media_server_unique_ip_http_port unique (ip, http_port, server_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_platform;
|
||||
create table IF NOT EXISTS wvp_platform
|
||||
(
|
||||
id serial primary key,
|
||||
enable bool default false,
|
||||
name character varying(255),
|
||||
server_gb_id character varying(50),
|
||||
server_gb_domain character varying(50),
|
||||
server_ip character varying(50),
|
||||
server_port integer,
|
||||
device_gb_id character varying(50),
|
||||
device_ip character varying(50),
|
||||
device_port character varying(50),
|
||||
username character varying(255),
|
||||
password character varying(50),
|
||||
expires character varying(50),
|
||||
keep_timeout character varying(50),
|
||||
transport character varying(50),
|
||||
civil_code character varying(50),
|
||||
manufacturer character varying(255),
|
||||
model character varying(255),
|
||||
address character varying(255),
|
||||
character_set character varying(50),
|
||||
ptz bool default false,
|
||||
rtcp bool default false,
|
||||
status bool default false,
|
||||
catalog_group integer,
|
||||
register_way integer,
|
||||
secrecy integer,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
as_message_channel bool default false,
|
||||
catalog_with_platform integer default 1,
|
||||
catalog_with_group integer default 1,
|
||||
catalog_with_region integer default 1,
|
||||
auto_push_channel bool default true,
|
||||
send_stream_ip character varying(50),
|
||||
server_id character varying(50),
|
||||
constraint uk_platform_unique_server_gb_id unique (server_gb_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_platform_channel;
|
||||
create table IF NOT EXISTS wvp_platform_channel
|
||||
(
|
||||
id serial primary key,
|
||||
platform_id integer,
|
||||
device_channel_id integer,
|
||||
custom_device_id character varying(50),
|
||||
custom_name character varying(255),
|
||||
custom_manufacturer character varying(50),
|
||||
custom_model character varying(50),
|
||||
custom_owner character varying(50),
|
||||
custom_civil_code character varying(50),
|
||||
custom_block character varying(50),
|
||||
custom_address character varying(50),
|
||||
custom_parental integer,
|
||||
custom_parent_id character varying(50),
|
||||
custom_safety_way integer,
|
||||
custom_register_way integer,
|
||||
custom_cert_num character varying(50),
|
||||
custom_certifiable integer,
|
||||
custom_err_code integer,
|
||||
custom_end_time character varying(50),
|
||||
custom_secrecy integer,
|
||||
custom_ip_address character varying(50),
|
||||
custom_port integer,
|
||||
custom_password character varying(255),
|
||||
custom_status character varying(50),
|
||||
custom_longitude double precision,
|
||||
custom_latitude double precision,
|
||||
custom_ptz_type integer,
|
||||
custom_position_type integer,
|
||||
custom_room_type integer,
|
||||
custom_use_type integer,
|
||||
custom_supply_light_type integer,
|
||||
custom_direction_type integer,
|
||||
custom_resolution character varying(255),
|
||||
custom_business_group_id character varying(255),
|
||||
custom_download_speed character varying(255),
|
||||
custom_svc_space_support_mod integer,
|
||||
custom_svc_time_support_mode integer,
|
||||
constraint uk_platform_gb_channel_platform_id_catalog_id_device_channel_id unique (platform_id, device_channel_id),
|
||||
constraint uk_platform_gb_channel_device_id unique (custom_device_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_platform_group;
|
||||
create table IF NOT EXISTS wvp_platform_group
|
||||
(
|
||||
id serial primary key,
|
||||
platform_id integer,
|
||||
group_id integer,
|
||||
constraint uk_wvp_platform_group_platform_id_group_id unique (platform_id, group_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_platform_region;
|
||||
create table IF NOT EXISTS wvp_platform_region
|
||||
(
|
||||
id serial primary key,
|
||||
platform_id integer,
|
||||
region_id integer,
|
||||
constraint uk_wvp_platform_region_platform_id_group_id unique (platform_id, region_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_stream_proxy;
|
||||
create table IF NOT EXISTS wvp_stream_proxy
|
||||
(
|
||||
id serial primary key,
|
||||
type character varying(50),
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
src_url character varying(255),
|
||||
timeout integer,
|
||||
ffmpeg_cmd_key character varying(255),
|
||||
rtsp_type character varying(50),
|
||||
media_server_id character varying(50),
|
||||
enable_audio bool default false,
|
||||
enable_mp4 bool default false,
|
||||
pulling bool default false,
|
||||
enable bool default false,
|
||||
enable_remove_none_reader bool default false,
|
||||
create_time character varying(50),
|
||||
name character varying(255),
|
||||
update_time character varying(50),
|
||||
stream_key character varying(255),
|
||||
server_id character varying(50),
|
||||
enable_disable_none_reader bool default false,
|
||||
relates_media_server_id character varying(50),
|
||||
constraint uk_stream_proxy_app_stream unique (app, stream)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_stream_push;
|
||||
create table IF NOT EXISTS wvp_stream_push
|
||||
(
|
||||
id serial primary key,
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
create_time character varying(50),
|
||||
media_server_id character varying(50),
|
||||
server_id character varying(50),
|
||||
push_time character varying(50),
|
||||
status bool default false,
|
||||
update_time character varying(50),
|
||||
pushing bool default false,
|
||||
self bool default false,
|
||||
start_offline_push bool default true,
|
||||
constraint uk_stream_push_app_stream unique (app, stream)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_cloud_record;
|
||||
create table IF NOT EXISTS wvp_cloud_record
|
||||
(
|
||||
id serial primary key,
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
call_id character varying(255),
|
||||
start_time bigint,
|
||||
end_time bigint,
|
||||
media_server_id character varying(50),
|
||||
server_id character varying(50),
|
||||
file_name character varying(255),
|
||||
folder character varying(500),
|
||||
file_path character varying(500),
|
||||
collect bool default false,
|
||||
file_size bigint,
|
||||
time_len bigint
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_user;
|
||||
create table IF NOT EXISTS wvp_user
|
||||
(
|
||||
id serial primary key,
|
||||
username character varying(255),
|
||||
password character varying(255),
|
||||
role_id integer,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
push_key character varying(50),
|
||||
constraint uk_user_username unique (username)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_user_role;
|
||||
create table IF NOT EXISTS wvp_user_role
|
||||
(
|
||||
id serial primary key,
|
||||
name character varying(50),
|
||||
authority character varying(50),
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
|
||||
drop table IF EXISTS wvp_user_api_key;
|
||||
create table IF NOT EXISTS wvp_user_api_key
|
||||
(
|
||||
id serial primary key,
|
||||
user_id bigint,
|
||||
app character varying(255),
|
||||
api_key text,
|
||||
expired_at bigint,
|
||||
remark character varying(255),
|
||||
enable bool default true,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
|
||||
/*初始数据*/
|
||||
INSERT INTO wvp_user
|
||||
VALUES (1, 'admin', '21232f297a57a5a743894a0e4a801fc3', 1, '2021-04-13 14:14:57', '2021-04-13 14:14:57',
|
||||
'3e80d1762a324d5b0ff636e0bd16f1e3');
|
||||
INSERT INTO wvp_user_role
|
||||
VALUES (1, 'admin', '0', '2021-04-13 14:14:57', '2021-04-13 14:14:57');
|
||||
|
||||
drop table IF EXISTS wvp_common_group;
|
||||
create table IF NOT EXISTS wvp_common_group
|
||||
(
|
||||
id serial primary key,
|
||||
device_id varchar(50) NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
parent_id int,
|
||||
parent_device_id varchar(50) DEFAULT NULL,
|
||||
business_group varchar(50) NOT NULL,
|
||||
create_time varchar(50) NOT NULL,
|
||||
update_time varchar(50) NOT NULL,
|
||||
civil_code varchar(50) default null,
|
||||
constraint uk_common_group_device_platform unique (device_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_common_region;
|
||||
create table IF NOT EXISTS wvp_common_region
|
||||
(
|
||||
id serial primary key,
|
||||
device_id varchar(50) NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
parent_id int,
|
||||
parent_device_id varchar(50) DEFAULT NULL,
|
||||
create_time varchar(50) NOT NULL,
|
||||
update_time varchar(50) NOT NULL,
|
||||
constraint uk_common_region_device_id unique (device_id)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_record_plan;
|
||||
create table IF NOT EXISTS wvp_record_plan
|
||||
(
|
||||
id serial primary key,
|
||||
snap bool default false,
|
||||
name varchar(255) NOT NULL,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
drop table IF EXISTS wvp_record_plan_item;
|
||||
create table IF NOT EXISTS wvp_record_plan_item
|
||||
(
|
||||
id serial primary key,
|
||||
start int,
|
||||
stop int,
|
||||
week_day int,
|
||||
plan_id int,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
/*
|
||||
* 20240528
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20240528`()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'transcode_suffix')
|
||||
THEN
|
||||
ALTER TABLE wvp_media_server ADD transcode_suffix character varying(255);
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'type')
|
||||
THEN
|
||||
alter table wvp_media_server
|
||||
add type character varying(50) default 'zlm';
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'flv_port')
|
||||
THEN
|
||||
alter table wvp_media_server add flv_port integer;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'flv_ssl_port')
|
||||
THEN
|
||||
alter table wvp_media_server add flv_ssl_port integer;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'ws_flv_port')
|
||||
THEN
|
||||
alter table wvp_media_server add ws_flv_port integer;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'ws_flv_ssl_port')
|
||||
THEN
|
||||
alter table wvp_media_server add ws_flv_ssl_port integer;
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20240528();
|
||||
DROP PROCEDURE wvp_20240528;
|
||||
DELIMITER ;
|
||||
|
||||
create table IF NOT EXISTS wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
app character varying(255) ,
|
||||
api_key text,
|
||||
expired_at bigint,
|
||||
remark character varying(255),
|
||||
enable bool default true,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
/*
|
||||
* 20241222
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20241222`()
|
||||
BEGIN
|
||||
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'uk_wvp_device_channel_unique_device_channel')
|
||||
THEN
|
||||
alter table wvp_device_channel drop index uk_wvp_device_channel_unique_device_channel;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'uk_wvp_unique_stream_push_id')
|
||||
THEN
|
||||
alter table wvp_device_channel drop index uk_wvp_unique_stream_push_id;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'uk_wvp_unique_stream_proxy_id')
|
||||
THEN
|
||||
alter table wvp_device_channel drop index uk_wvp_unique_stream_proxy_id;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'data_type')
|
||||
THEN
|
||||
alter table wvp_device_channel add data_type integer not null;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'data_device_id')
|
||||
THEN
|
||||
alter table wvp_device_channel add data_device_id integer not null;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'device_db_id')
|
||||
THEN
|
||||
update wvp_device_channel wdc INNER JOIN
|
||||
(SELECT id, device_db_id from wvp_device_channel where device_db_id is not null ) ct on ct.id = wdc.id
|
||||
set wdc.data_type = 1, wdc.data_device_id = ct.device_db_id where wdc.device_db_id is not null;
|
||||
alter table wvp_device_channel drop device_db_id;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'stream_push_id')
|
||||
THEN
|
||||
update wvp_device_channel wdc INNER JOIN
|
||||
(SELECT id, stream_push_id from wvp_device_channel where stream_push_id is not null ) ct on ct.id = wdc.id
|
||||
set wdc.data_type = 2, wdc.data_device_id = ct.stream_push_id where wdc.stream_push_id is not null;
|
||||
alter table wvp_device_channel drop stream_push_id;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'stream_proxy_id')
|
||||
THEN
|
||||
update wvp_device_channel wdc INNER JOIN
|
||||
(SELECT id, stream_proxy_id from wvp_device_channel where stream_proxy_id is not null ) ct on ct.id = wdc.id
|
||||
set wdc.data_type = 3, wdc.data_device_id = ct.stream_proxy_id where wdc.stream_proxy_id is not null;
|
||||
alter table wvp_device_channel drop stream_proxy_id;
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20241222();
|
||||
DROP PROCEDURE wvp_20241222;
|
||||
DELIMITER ;
|
||||
/*
|
||||
* 20241231
|
||||
*/
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE `wvp_20241231`()
|
||||
BEGIN
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_stream_proxy' and column_name = 'relates_media_server_id')
|
||||
THEN
|
||||
alter table wvp_stream_proxy add relates_media_server_id character varying(50);
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20241231();
|
||||
DROP PROCEDURE wvp_20241231;
|
||||
DELIMITER ;
|
||||
/*
|
||||
* 20250111
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20250111`()
|
||||
BEGIN
|
||||
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and INDEX_NAME = 'uk_stream_push_app_stream_path')
|
||||
THEN
|
||||
alter table wvp_cloud_record drop index uk_stream_push_app_stream_path ;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and column_name = 'folder')
|
||||
THEN
|
||||
alter table wvp_cloud_record modify folder varchar(500) null;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and column_name = 'file_path')
|
||||
THEN
|
||||
alter table wvp_cloud_record modify file_path varchar(500) null;
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20250111();
|
||||
DROP PROCEDURE wvp_20250111;
|
||||
DELIMITER ;
|
||||
|
||||
/*
|
||||
* 20250211
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20250211`()
|
||||
BEGIN
|
||||
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'keepalive_interval_time')
|
||||
THEN
|
||||
alter table wvp_device change keepalive_interval_time heart_beat_interval integer after as_message_channel;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'heart_beat_count')
|
||||
THEN
|
||||
alter table wvp_device add heart_beat_count integer;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'position_capability')
|
||||
THEN
|
||||
alter table wvp_device add position_capability integer;
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20250211();
|
||||
DROP PROCEDURE wvp_20250211;
|
||||
DELIMITER ;
|
||||
|
||||
/**
|
||||
* 20250312
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20250312`()
|
||||
BEGIN
|
||||
DECLARE serverId VARCHAR(32) DEFAULT '你的服务ID';
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'server_id')
|
||||
THEN
|
||||
alter table wvp_device add server_id character varying(50);
|
||||
update wvp_device set server_id = serverId;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'server_id')
|
||||
THEN
|
||||
alter table wvp_media_server add server_id character varying(50);
|
||||
update wvp_media_server set server_id = serverId;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_stream_proxy' and column_name = 'server_id')
|
||||
THEN
|
||||
alter table wvp_stream_proxy add server_id character varying(50);
|
||||
update wvp_stream_proxy set server_id = serverId;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and column_name = 'server_id')
|
||||
THEN
|
||||
alter table wvp_cloud_record add server_id character varying(50);
|
||||
update wvp_cloud_record set server_id = serverId;
|
||||
END IF;
|
||||
|
||||
IF not EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_platform' and column_name = 'server_id')
|
||||
THEN
|
||||
alter table wvp_platform add server_id character varying(50);
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20250312();
|
||||
DROP PROCEDURE wvp_20250312;
|
||||
DELIMITER ;
|
||||
|
||||
/*
|
||||
* 20250319
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20250319`()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'gps_speed')
|
||||
THEN
|
||||
alter table wvp_device_channel add gps_speed double precision;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'gps_altitude')
|
||||
THEN
|
||||
alter table wvp_device_channel add gps_altitude double precision;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'gps_direction')
|
||||
THEN
|
||||
alter table wvp_device_channel add gps_direction double precision;
|
||||
END IF;
|
||||
END; //
|
||||
call wvp_20250319();
|
||||
DROP PROCEDURE wvp_20250319;
|
||||
DELIMITER ;
|
||||
|
||||
/*
|
||||
* 20250402
|
||||
*/
|
||||
DELIMITER // -- 重定义分隔符避免分号冲突
|
||||
CREATE PROCEDURE `wvp_20250402`()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'data_type')
|
||||
THEN
|
||||
create index data_type on wvp_device_channel (data_type);
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT column_name FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'data_device_id')
|
||||
THEN
|
||||
create index data_device_id on wvp_device_channel (data_device_id);
|
||||
END IF;
|
||||
|
||||
END; //
|
||||
call wvp_20250402();
|
||||
DROP PROCEDURE wvp_20250402;
|
||||
DELIMITER ;
|
||||
|
||||
|
||||
|
||||
19
docker/nginx/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
RUN apk add --no-cache bash
|
||||
|
||||
ARG TZ=Asia/Shanghai
|
||||
RUN \
|
||||
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||
apk update && \
|
||||
apk add tzdata
|
||||
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||
echo '${TZ}' > /etc/timezone
|
||||
|
||||
RUN rm -rf /etc/nginx/conf.d/*
|
||||
RUN mkdir /opt/dist
|
||||
COPY ./dist /opt/dist
|
||||
COPY ./conf/nginx.conf /etc/nginx/conf.d
|
||||
|
||||
CMD ["nginx","-g","daemon off;"]
|
||||
|
||||
11
docker/nginx/build.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
rm ./dist/static/js/config.js
|
||||
cp ./config.js ./dist/static/js/
|
||||
|
||||
docker build -t polaris-nginx:${version} .
|
||||
docker tag polaris-nginx:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-nginx:${version}
|
||||
docker tag polaris-nginx:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-nginx:latest
|
||||
55
docker/nginx/conf/nginx.conf
Normal file
@@ -0,0 +1,55 @@
|
||||
#user nobody;
|
||||
worker_processes 1;
|
||||
|
||||
#error_log logs/error.log;
|
||||
#error_log logs/error.log notice;
|
||||
#error_log logs/error.log info;
|
||||
|
||||
#pid logs/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
#keepalive_timeout 0;
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /opt/dist;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /record_proxy/{
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_pass http://polaris-wvp:18978/;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_pass http://polaris-wvp:18978;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
docker/nginx/config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
window.baseUrl = "http://10.10.1.124:18978"
|
||||
|
||||
// map组件全局参数, 注释此内容可以关闭地图功能
|
||||
window.mapParam = {
|
||||
// 开启/关闭地图功能
|
||||
enable: true,
|
||||
// 坐标系 GCJ-02 WGS-84,
|
||||
coordinateSystem: "GCJ-02",
|
||||
// 地图瓦片地址
|
||||
tilesUrl: "http://webrd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
|
||||
// 瓦片大小
|
||||
tileSize: 256,
|
||||
// 默认层级
|
||||
zoom:10,
|
||||
// 默认地图中心点
|
||||
center:[116.41020, 39.915119],
|
||||
// 地图最大层级
|
||||
maxZoom:18,
|
||||
// 地图最小层级
|
||||
minZoom: 3
|
||||
}
|
||||
15
docker/push.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-media:latest
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-mysql:latest
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-redis:latest
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-wvp:latest
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-nginx:latest
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-media:${version}
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-mysql:${version}
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-redis:${version}
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-wvp:${version}
|
||||
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-nginx:${version}
|
||||
5
docker/redis/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM redis
|
||||
|
||||
RUN mkdir -p /opt/polaris/redis
|
||||
WORKDIR /opt/polaris/redis
|
||||
COPY ./conf/redis.conf /opt/polaris/redis/redis.conf
|
||||
8
docker/redis/build.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
docker build -t polaris-redis:${version} .
|
||||
docker tag polaris-redis:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-redis:${version}
|
||||
docker tag polaris-redis:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-redis:latest
|
||||
2
docker/redis/conf/redis.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
#requirepass root
|
||||
bind 0.0.0.0
|
||||
@@ -1,2 +0,0 @@
|
||||
requirepass root
|
||||
bind 0.0.0.0
|
||||
@@ -1,81 +1,64 @@
|
||||
FROM ubuntu:20.04 as build
|
||||
FROM ubuntu:20.04 AS build
|
||||
ARG Platfrom=amd64
|
||||
ARG JDK_NAME
|
||||
|
||||
ARG gitUrl="https://gitee.com/pan648540858"
|
||||
ARG zlmGitUrl="https://gitee.com/xia-chu/ZLMediaKit"
|
||||
EXPOSE 18978/tcp
|
||||
EXPOSE 8116/tcp
|
||||
EXPOSE 8116/udp
|
||||
EXPOSE 8080/tcp
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive &&\
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends openjdk-11-jre git maven nodejs npm build-essential \
|
||||
cmake ca-certificates openssl ffmpeg &&\
|
||||
mkdir -p /opt/wvp/config /opt/wvp/heapdump /opt/wvp/config /opt/assist/config /opt/assist/heapdump /opt/media/www/record
|
||||
|
||||
RUN cd /home && \
|
||||
git clone "${gitUrl}/maven.git" && \
|
||||
cp maven/settings.xml /usr/share/maven/conf/
|
||||
|
||||
RUN cd /home && \
|
||||
git clone "${gitUrl}/wvp-GB28181-pro.git"
|
||||
RUN cd /home/wvp-GB28181-pro/web_src && \
|
||||
npm install && \
|
||||
npm run build
|
||||
RUN cd /home/wvp-GB28181-pro && \
|
||||
mvn clean package -Dmaven.test.skip=true && \
|
||||
cp /home/wvp-GB28181-pro/target/*.jar /opt/wvp/ && \
|
||||
cp /home/wvp-GB28181-pro/src/main/resources/application-docker.yml /opt/wvp/config/application.yml
|
||||
|
||||
RUN cd /home && \
|
||||
git clone "${gitUrl}/wvp-pro-assist.git"
|
||||
RUN cd /home/wvp-pro-assist && \
|
||||
git reset --hard 58f1a79136a55a7cd1593c95b56ddadcc2225b61 && \
|
||||
mvn clean package -Dmaven.test.skip=true && \
|
||||
cp /home/wvp-pro-assist/target/*.jar /opt/assist/ && \
|
||||
cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml
|
||||
|
||||
RUN cd /home && \
|
||||
git clone --depth=1 "${zlmGitUrl}"
|
||||
RUN cd /home/ZLMediaKit && \
|
||||
git submodule update --init --recursive && \
|
||||
mkdir -p build release/linux/Release/ &&\
|
||||
cd build && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .. && \
|
||||
make -j4 && \
|
||||
rm -rf ../release/linux/Release/config.ini && \
|
||||
cp -r ../release/linux/Release/* /opt/media
|
||||
|
||||
RUN cd /opt/wvp && \
|
||||
echo '#!/bin/bash' > run.sh && \
|
||||
echo 'echo ${WVP_IP}' >> run.sh && \
|
||||
echo 'echo ${WVP_CONFIG}' >> run.sh && \
|
||||
echo 'cd /opt/assist' >> run.sh && \
|
||||
echo 'nohup java ${ASSIST_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/assist/heapdump/ -jar *.jar --spring.config.location=/opt/assist/config/application.yml --userSettings.record=/opt/media/www/record/ --media.record-assist-port=18081 ${ASSIST_CONFIG} &' >> run.sh && \
|
||||
echo 'nohup /opt/media/MediaServer -d -m 3 &' >> run.sh && \
|
||||
echo 'cd /opt/wvp' >> run.sh && \
|
||||
echo 'java ${WVP_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/wvp/heapdump/ -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \
|
||||
chmod +x run.sh
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
EXPOSE 18080/tcp
|
||||
EXPOSE 5060/tcp
|
||||
EXPOSE 5060/udp
|
||||
EXPOSE 6379/tcp
|
||||
EXPOSE 18081/tcp
|
||||
EXPOSE 80/tcp
|
||||
EXPOSE 1935/tcp
|
||||
EXPOSE 554/tcp
|
||||
EXPOSE 554/udp
|
||||
EXPOSE 30000-30500/tcp
|
||||
EXPOSE 30000-30500/udp
|
||||
|
||||
ENV LC_ALL zh_CN.UTF-8
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive &&\
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends openjdk-11-jre ca-certificates ffmpeg language-pack-zh-hans && \
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
apt-get install -y --no-install-recommends \
|
||||
wget \
|
||||
cmake \
|
||||
maven \
|
||||
git \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
curl \
|
||||
libpcre3 \
|
||||
libpcre3-dev \
|
||||
zlib1g-dev \
|
||||
openssl \
|
||||
libssl-dev \
|
||||
gdb && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean -y && \
|
||||
rm -rf /var/lib/apt/lists/*dic
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=build /opt /opt
|
||||
# install jdk1.8
|
||||
RUN mkdir -p /opt/download
|
||||
WORKDIR /opt/download
|
||||
RUN if [ "$Platfrom" = "arm64" ]; \
|
||||
then \
|
||||
wget https://polaris-tian-generic.pkg.coding.net/qt/autopliot/jdk-8u411-linux-aarch64.tar.gz?version=latest --no-check-certificate -O jdk-8.tar.gz && \
|
||||
tar -zxvf /opt/download/jdk-8.tar.gz -C /usr/local/ --transform 's/jdk1.8.0_411/java/' && \
|
||||
rm /opt/download/jdk-8.tar.gz; \
|
||||
else \
|
||||
wget https://polaris-tian-generic.pkg.coding.net/qt/autopliot/jdk-8u202-linux-x64.tar.gz?version=latest --no-check-certificate -O jdk-8.tar.gz && \
|
||||
tar -zxvf /opt/download/jdk-8.tar.gz -C /usr/local/ --transform 's/jdk1.8.0_202/java/' && \
|
||||
rm /opt/download/jdk-8.tar.gz; \
|
||||
fi
|
||||
|
||||
ENV JAVA_HOME /usr/local/java/
|
||||
ENV JRE_HOME ${JAVA_HOME}/jre
|
||||
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
|
||||
ENV PATH ${JAVA_HOME}/bin:$PATH
|
||||
|
||||
RUN java -version && javac -version
|
||||
|
||||
RUN mkdir -p /opt/wvp
|
||||
WORKDIR /opt/wvp
|
||||
CMD ["sh", "run.sh"]
|
||||
COPY ./wvp /opt/wvp
|
||||
|
||||
WORKDIR /home
|
||||
RUN cd /home && \
|
||||
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
|
||||
RUN cd /home/wvp-GB28181-pro && \
|
||||
mvn clean package -Dmaven.test.skip=true && \
|
||||
cp /home/wvp-GB28181-pro/target/*.jar /opt/wvp/wvp.jar
|
||||
|
||||
WORKDIR /opt/wvp
|
||||
ENTRYPOINT ["java", "-Xms512m", "-Xmx1024m", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/opt/ylcx/", "-jar", "wvp.jar", "--spring.config.location=/opt/ylcx/wvp/application.yml"]
|
||||
8
docker/wvp/build.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
|
||||
version=2.7.3
|
||||
|
||||
docker build -t polaris-wvp:${version} .
|
||||
docker tag polaris-wvp:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-wvp:${version}
|
||||
docker tag polaris-wvp:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-wvp:latest
|
||||
105
docker/wvp/wvp/application-base.yml
Executable file
@@ -0,0 +1,105 @@
|
||||
spring:
|
||||
# 设置接口超时时间
|
||||
mvc:
|
||||
async:
|
||||
request-timeout: 20000
|
||||
thymeleaf:
|
||||
cache: false
|
||||
# [可选]上传文件大小限制
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 100MB
|
||||
# REDIS数据库配置
|
||||
redis:
|
||||
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
|
||||
host: 127.0.0.1
|
||||
# [必须修改] 端口号
|
||||
port: 6379
|
||||
# [可选] 数据库 DB
|
||||
database: 1
|
||||
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
||||
password:
|
||||
# [可选] 超时时间
|
||||
timeout: 30000
|
||||
# mysql数据源
|
||||
datasource:
|
||||
dynamic:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
|
||||
username: root
|
||||
password: root
|
||||
#[可选] 监听的HTTP端口, 网页和接口调用都是这个端口
|
||||
server:
|
||||
port: 18978
|
||||
ssl:
|
||||
# [可选] 是否开启HTTPS访问
|
||||
enabled: false
|
||||
# 作为28181服务器的配置
|
||||
sip:
|
||||
# [必须修改] 本机的IP
|
||||
ip: 127.0.0.1
|
||||
# [可选]
|
||||
port: 8116
|
||||
# [可选]
|
||||
domain: 3402000000
|
||||
# [可选]
|
||||
id: 34020000002000000001
|
||||
password:
|
||||
alarm: true
|
||||
|
||||
# 默认服务器配置
|
||||
media:
|
||||
id: polaris
|
||||
# [必须修改]内网IP
|
||||
ip: 127.0.0.1
|
||||
http-port: 6080
|
||||
# [可选] 返回流地址时的ip,置空使用 media.ip
|
||||
stream-ip: 127.0.0.1
|
||||
# [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip
|
||||
sdp-ip: 127.0.0.1
|
||||
# [可选] Hook IP, 默认使用sip.ip
|
||||
hook-ip: 127.0.0.1
|
||||
# [可选] sslport
|
||||
http-ssl-port: 4443
|
||||
rtp-proxy-port: 10000
|
||||
rtmp-port: 10935
|
||||
rtmp-ssl-port: 41935
|
||||
rtsp-port: 5540
|
||||
rtsp-ssl-port: 45540
|
||||
# [可选]
|
||||
secret: su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
|
||||
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
|
||||
rtp:
|
||||
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
|
||||
enable: false
|
||||
# [可选]
|
||||
port-range: 30000,30500
|
||||
# [可选]
|
||||
send-port-range: 50502,50506
|
||||
|
||||
record-path: /opt/media/record
|
||||
record-day: 7
|
||||
record-assist-port: 0
|
||||
user-settings:
|
||||
auto-apply-play: true
|
||||
play-timeout: 30000
|
||||
wait-track: false
|
||||
record-push-live: false
|
||||
record-sip: false
|
||||
stream-on-demand: true
|
||||
interface-authentication: false
|
||||
broadcast-for-platform: TCP-PASSIVE
|
||||
push-stream-after-ack: true
|
||||
send-to-platforms-when-id-lost: true
|
||||
interface-authentication-excludes:
|
||||
- /api/**
|
||||
push-authority: false
|
||||
allowed-origins:
|
||||
- http://localhost:8080
|
||||
- http://127.0.0.1:8080
|
||||
- http://0.0.0.0:8080
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
|
||||
106
docker/wvp/wvp/application-docker.yml
Normal file
@@ -0,0 +1,106 @@
|
||||
spring:
|
||||
# 设置接口超时时间
|
||||
mvc:
|
||||
async:
|
||||
request-timeout: 20000
|
||||
thymeleaf:
|
||||
cache: false
|
||||
# [可选]上传文件大小限制
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 100MB
|
||||
# REDIS数据库配置
|
||||
redis:
|
||||
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
|
||||
host: ${REDIS_HOST:127.0.0.1}
|
||||
# [必须修改] 端口号
|
||||
port: ${REDIS_PORT:6379}
|
||||
# [可选] 数据库 DB
|
||||
database: 1
|
||||
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
||||
password:
|
||||
# [可选] 超时时间
|
||||
timeout: 30000
|
||||
# mysql数据源
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://${DATABASE_HOST:127.0.0.1}:${DATABASE_PORT:3306}/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true
|
||||
username: ${DATABASE_USER:root}
|
||||
password: ${DATABASE_PASSWORD:root}
|
||||
|
||||
#[可选] 监听的HTTP端口, 网页和接口调用都是这个端口
|
||||
server:
|
||||
port: 18978
|
||||
ssl:
|
||||
# [可选] 是否开启HTTPS访问
|
||||
enabled: false
|
||||
# 作为28181服务器的配置
|
||||
sip:
|
||||
# [必须修改] 本机的IP
|
||||
ip: ${SIP_HOST:127.0.0.1}
|
||||
# [可选]
|
||||
port: 8116
|
||||
# [可选]
|
||||
domain: 3402000000
|
||||
# [可选]
|
||||
id: 34020000002000000001
|
||||
password:
|
||||
alarm: true
|
||||
|
||||
# 默认服务器配置
|
||||
media:
|
||||
id: polaris
|
||||
# [必须修改] ZLM 内网IP与端口
|
||||
ip: ${ZLM_HOST:127.0.0.1}
|
||||
http-port: ${ZLM_PORT:6080}
|
||||
# [可选] 返回流地址时的ip,置空使用 media.ip
|
||||
stream-ip: ${STREAM_HOST:127.0.0.1}
|
||||
# [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip
|
||||
sdp-ip: ${SIP_HOST:127.0.0.1}
|
||||
# [可选] Hook IP, 默认使用sip.ip
|
||||
hook-ip: ${SIP_HOST:127.0.0.1}
|
||||
# [可选] sslport
|
||||
http-ssl-port: 4443
|
||||
rtp-proxy-port: 10000
|
||||
rtmp-port: 10935
|
||||
rtmp-ssl-port: 41935
|
||||
rtsp-port: 5540
|
||||
rtsp-ssl-port: 45540
|
||||
# [可选]
|
||||
secret: ${ZLM_SERCERT}
|
||||
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
|
||||
rtp:
|
||||
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
|
||||
enable: false
|
||||
# [可选]
|
||||
port-range: 30000,30500
|
||||
# [可选]
|
||||
send-port-range: 50502,50506
|
||||
|
||||
record-path: /opt/media/record
|
||||
record-day: 7
|
||||
record-assist-port: 0
|
||||
user-settings:
|
||||
auto-apply-play: true
|
||||
play-timeout: 30000
|
||||
wait-track: false
|
||||
record-push-live: false
|
||||
record-sip: false
|
||||
stream-on-demand: true
|
||||
interface-authentication: false
|
||||
broadcast-for-platform: TCP-PASSIVE
|
||||
push-stream-after-ack: true
|
||||
send-to-platforms-when-id-lost: true
|
||||
interface-authentication-excludes:
|
||||
- /api/**
|
||||
push-authority: false
|
||||
allowed-origins:
|
||||
- http://localhost:8080
|
||||
- http://127.0.0.1:8080
|
||||
- http://0.0.0.0:8080
|
||||
- ${NGINX_HOST}
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
|
||||
5
docker/wvp/wvp/application.yml
Executable file
@@ -0,0 +1,5 @@
|
||||
spring:
|
||||
application:
|
||||
name: wvp
|
||||
profiles:
|
||||
active: docker
|
||||
57
install.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#! /bin/sh
|
||||
|
||||
WORD_DIR=$(cd $(dirname $0); pwd)
|
||||
SERVICE_NAME="wvp"
|
||||
|
||||
# 检查是否为 root 用户
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "提示: 建议使用 root 用户执行此脚本,否则可能权限不足!"
|
||||
read -p "继续?(y/n) " -n 1 -r
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
|
||||
# 当前目录直接搜索(不含子目录)
|
||||
jar_files=(*.jar)
|
||||
|
||||
if [ ${#jar_files[@]} -eq 0 ]; then
|
||||
echo "当前目录无 JAR 文件!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 遍历结果
|
||||
for jar in "${jar_files[@]}"; do
|
||||
echo "找到 JAR 文件: $jar"
|
||||
done
|
||||
|
||||
# 写文件
|
||||
# 生成 Systemd 服务文件内容
|
||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
cat << EOF | sudo tee "$SERVICE_FILE" > /dev/null
|
||||
[Unit]
|
||||
Description=${SERVICE_NAME}
|
||||
After=syslog.target
|
||||
|
||||
[Service]
|
||||
User=$USER
|
||||
WorkingDirectory=${WORD_DIR}
|
||||
ExecStart=java -jar ${jar_files}
|
||||
SuccessExitStatus=143
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
Environment=SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# 重载 Systemd 并启动服务
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable "$SERVICE_NAME"
|
||||
sudo systemctl start "$SERVICE_NAME"
|
||||
|
||||
# 验证服务状态
|
||||
echo "服务已安装!执行以下命令查看状态:"
|
||||
echo "sudo systemctl status $SERVICE_NAME"
|
||||
11
pom.xml
@@ -187,7 +187,6 @@
|
||||
<version>1.4.6</version>
|
||||
</dependency>
|
||||
|
||||
<!--在线文档 -->
|
||||
<!--在线文档 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
@@ -200,13 +199,6 @@
|
||||
<version>1.6.10</version>
|
||||
</dependency>
|
||||
|
||||
<!--在线文档 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>1.6.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-springdoc-ui</artifactId>
|
||||
@@ -402,6 +394,7 @@
|
||||
<version>2.7.2</version>
|
||||
<configuration>
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
<executable>true</executable>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -452,6 +445,7 @@
|
||||
<exclude>**/application.yml</exclude>
|
||||
<exclude>**/application-*.yml</exclude>
|
||||
<exclude>**/local.jks</exclude>
|
||||
<exclude>**/install.sh</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -471,6 +465,7 @@
|
||||
<includes>
|
||||
<include>application.yml</include>
|
||||
<include>application-*.yml</include>
|
||||
<include>install.sh</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
122
run.sh
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/bin/bash
|
||||
# JDK路径
|
||||
export JAVA_HOME=/usr/local/java/jdk1.8.0_202
|
||||
export JRE_HOME=${JAVA_HOME}/jre
|
||||
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
|
||||
export PATH=${JAVA_HOME}/bin:$PATH
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
function log() {
|
||||
message="[Polaris Log]: $1 "
|
||||
case "$1" in
|
||||
*"失败"* | *"错误"* | *"请使用 root 或 sudo 权限运行此脚本"*)
|
||||
echo -e "${RED}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
*"成功"*)
|
||||
echo -e "${GREEN}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
*"忽略"* | *"跳过"*)
|
||||
echo -e "${YELLOW}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
*)
|
||||
echo -e "${BLUE}${message}${NC}" 2>&1 | tee -a
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
echo
|
||||
cat <<EOF
|
||||
██████╗ ██████╗ ██╗ █████╗ ██████╗ ██╗███████╗
|
||||
██╔══██╗██╔═══██╗██║ ██╔══██╗██╔══██╗██║██╔════╝
|
||||
██████╔╝██║ ██║██║ ███████║██████╔╝██║███████╗
|
||||
██╔═══╝ ██║ ██║██║ ██╔══██║██╔══██╗██║╚════██║
|
||||
██║ ╚██████╔╝███████╗██║ ██║██║ ██║██║███████║
|
||||
╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝
|
||||
|
||||
EOF
|
||||
|
||||
# WVP-pro defines
|
||||
AppName=wvp-pro-2.7.18-11211115.jar
|
||||
AppHome="/root/polaris/wvp/"
|
||||
# JVM参数
|
||||
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
|
||||
|
||||
function start() {
|
||||
log "======================= 开启流媒体服务 ======================="
|
||||
log "AppHome: $AppHome"
|
||||
cd $AppHome
|
||||
log "AppName: $AppName"
|
||||
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
|
||||
if [x"$PID" != x""]; then
|
||||
log "$AppName is running..."
|
||||
else
|
||||
nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 &
|
||||
fi
|
||||
log "流媒体服务开启成功"
|
||||
}
|
||||
|
||||
function stop() {
|
||||
log "======================= 停止流媒体服务 ======================="
|
||||
|
||||
PID=""
|
||||
query() {
|
||||
PID=$(ps -ef | grep java | grep $AppName | grep -v grep | awk '{print $2}')
|
||||
}
|
||||
query
|
||||
if [ x"$PID" != x"" ]; then
|
||||
log "进程PID: $PID"
|
||||
kill -TERM $PID
|
||||
log "$AppName (pid:$PID) exiting..."
|
||||
while [ x"$PID" != x"" ]; do
|
||||
sleep 1
|
||||
query
|
||||
done
|
||||
log "成功:$AppName exited."
|
||||
else
|
||||
log "忽略:进程不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
function status() {
|
||||
log "======================= 运行状态 ======================="
|
||||
log ""
|
||||
|
||||
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
|
||||
if [ $PID != 0 ]; then
|
||||
log "进程PID: $PID"
|
||||
log "$AppName is running..."
|
||||
else
|
||||
log "$AppName is not running..."
|
||||
fi
|
||||
log ""
|
||||
log "========================================================"
|
||||
}
|
||||
|
||||
function restart() {
|
||||
stop
|
||||
sleep 3
|
||||
start
|
||||
}
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
*) ;;
|
||||
|
||||
esac
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
@Data
|
||||
public class StatisticsInfo {
|
||||
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 分支
|
||||
*/
|
||||
private String branch;
|
||||
|
||||
/**
|
||||
* git提交版本ID
|
||||
*/
|
||||
private String gitCommitId;
|
||||
|
||||
/**
|
||||
* git地址
|
||||
*/
|
||||
private String gitUrl;
|
||||
|
||||
/**
|
||||
* 构建版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 操作系统名称
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 是否是docker环境
|
||||
*/
|
||||
private Boolean docker;
|
||||
|
||||
/**
|
||||
* 架构
|
||||
*/
|
||||
private String arch;
|
||||
|
||||
/**
|
||||
* jdk版本
|
||||
*/
|
||||
private String jdkVersion;
|
||||
|
||||
/**
|
||||
* redis版本
|
||||
*/
|
||||
private String redisVersion;
|
||||
|
||||
/**
|
||||
* sql数据库版本
|
||||
*/
|
||||
private String sqlVersion;
|
||||
|
||||
/**
|
||||
* sql数据库类型, mysql/postgresql/金仓等
|
||||
*/
|
||||
private String sqlType;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String time;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatisticsInfo{" +
|
||||
"id=" + id +
|
||||
", deviceId='" + deviceId + '\'' +
|
||||
", branch='" + branch + '\'' +
|
||||
", gitCommitId='" + gitCommitId + '\'' +
|
||||
", gitUrl='" + gitUrl + '\'' +
|
||||
", version='" + version + '\'' +
|
||||
", osName='" + osName + '\'' +
|
||||
", docker=" + docker +
|
||||
", arch='" + arch + '\'' +
|
||||
", jdkVersion='" + jdkVersion + '\'' +
|
||||
", redisVersion='" + redisVersion + '\'' +
|
||||
", sqlVersion='" + sqlVersion + '\'' +
|
||||
", sqlType='" + sqlType + '\'' +
|
||||
", time='" + time + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.nio.file.Files;
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Order(value=14)
|
||||
@Order(value=15)
|
||||
public class CivilCodeFileConf implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
@@ -59,11 +60,14 @@ public class CloudRecordTimer {
|
||||
// TODO 后续可以删除空了的过期日期文件夹
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
|
||||
try {
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
}
|
||||
}catch (ControllerException ignored) {}
|
||||
|
||||
}
|
||||
result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@ public class SipConfig {
|
||||
|
||||
private boolean alarm = false;
|
||||
|
||||
private long timeout = 500;
|
||||
private long timeout = 1000;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class SpringDocConfig {
|
||||
Contact contact = new Contact();
|
||||
contact.setName("pan");
|
||||
contact.setEmail("648540858@qq.com");
|
||||
|
||||
return new OpenAPI()
|
||||
.components(new Components()
|
||||
.addSecuritySchemes(JwtUtils.HEADER, new SecurityScheme()
|
||||
@@ -37,7 +38,11 @@ public class SpringDocConfig {
|
||||
.bearerFormat("JWT")))
|
||||
.info(new Info().title("WVP-PRO 接口文档")
|
||||
.contact(contact)
|
||||
.description("开箱即用的28181协议视频平台")
|
||||
.description("开箱即用的28181协议视频平台。 <br/>" +
|
||||
"1. 打开http://127.0.0.1:18080/doc.html#/1.%20全部/用户管理/login_1" +
|
||||
" 登录成功后返回AccessToken。 <br/>" +
|
||||
"2. 填写到AccessToken到参数值 http://127.0.0.1:18080/doc.html#/Authorize/1.%20全部 <br/>" +
|
||||
"后续接口就可以直接测试了")
|
||||
.version("v3.1.0")
|
||||
.license(new License().name("Apache 2.0").url("http://springdoc.org")));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.common.StatisticsInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
@Order(value=100)
|
||||
@Slf4j
|
||||
public class StatisticsInfoTask implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private GitUtil gitUtil;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
try {
|
||||
StatisticsInfo statisticsInfo = new StatisticsInfo();
|
||||
statisticsInfo.setDeviceId(SystemInfoUtils.getHardwareId());
|
||||
statisticsInfo.setBranch(gitUtil.getBranch());
|
||||
statisticsInfo.setGitCommitId(gitUtil.getGitCommitId());
|
||||
statisticsInfo.setGitUrl(gitUtil.getGitUrl());
|
||||
statisticsInfo.setVersion(gitUtil.getBuildVersion());
|
||||
|
||||
statisticsInfo.setOsName(System.getProperty("os.name"));
|
||||
statisticsInfo.setArch(System.getProperty("os.arch"));
|
||||
statisticsInfo.setJdkVersion(System.getProperty("java.version"));
|
||||
|
||||
statisticsInfo.setDocker(new File("/.dockerenv").exists());
|
||||
try {
|
||||
statisticsInfo.setRedisVersion(getRedisVersion());
|
||||
}catch (Exception ignored) {}
|
||||
try {
|
||||
DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
|
||||
statisticsInfo.setSqlVersion(metaData.getDatabaseProductVersion());
|
||||
statisticsInfo.setSqlType(metaData.getDriverName());
|
||||
}catch (Exception ignored) {}
|
||||
statisticsInfo.setTime(DateUtil.getNow());
|
||||
sendPost(statisticsInfo);
|
||||
|
||||
|
||||
}catch (Exception e) {
|
||||
log.error("[获取信息失败] ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRedisVersion() {
|
||||
if (redisTemplate.getConnectionFactory() == null) {
|
||||
return null;
|
||||
}
|
||||
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
|
||||
if (connection.info() == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.info().getProperty("redis_version");
|
||||
}
|
||||
|
||||
public void sendPost(StatisticsInfo statisticsInfo) {
|
||||
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
|
||||
OkHttpClient client = httpClientBuilder.build();
|
||||
|
||||
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(statisticsInfo));
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.post(requestBodyJson)
|
||||
.url("http://api.wvp-pro.cn:136/api/statistics/ping")
|
||||
// .url("http://127.0.0.1:11236/api/statistics/ping")
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.build();
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
response.close();
|
||||
Objects.requireNonNull(response.body()).close();
|
||||
|
||||
}catch (Exception ignored){}
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ public class UserSetting {
|
||||
/**
|
||||
* 登录超时时间(分钟),
|
||||
*/
|
||||
private long loginTimeout = 30;
|
||||
private long loginTimeout = 60;
|
||||
|
||||
/**
|
||||
* jwk文件路径,若不指定则使用resources目录下的jwk.json
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.message.Response;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -97,7 +97,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
// return;
|
||||
default:
|
||||
}
|
||||
|
||||
// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
|
||||
User user = new User();
|
||||
user.setId(jwtUser.getUserId());
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
@@ -24,7 +23,6 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -58,33 +56,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链
|
||||
**/
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
if (userSetting.getInterfaceAuthentication()) {
|
||||
ArrayList<String> matchers = new ArrayList<>();
|
||||
matchers.add("/");
|
||||
matchers.add("/#/**");
|
||||
matchers.add("/static/**");
|
||||
matchers.add("/swagger-ui.html");
|
||||
matchers.add("/swagger-ui/");
|
||||
matchers.add("/index.html");
|
||||
matchers.add("/doc.html");
|
||||
matchers.add("/webjars/**");
|
||||
matchers.add("/swagger-resources/**");
|
||||
matchers.add("/v3/api-docs/**");
|
||||
matchers.add("/js/**");
|
||||
matchers.add("/api/device/query/snap/**");
|
||||
matchers.add("/record_proxy/*/**");
|
||||
matchers.add("/api/emit");
|
||||
matchers.add("/favicon.ico");
|
||||
// 可以直接访问的静态数据
|
||||
web.ignoring().antMatchers(matchers.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置认证方式
|
||||
*
|
||||
@@ -105,15 +76,36 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
List<String> defaultExcludes = new ArrayList<>();
|
||||
defaultExcludes.add("/");
|
||||
defaultExcludes.add("/#/**");
|
||||
defaultExcludes.add("/static/**");
|
||||
|
||||
List<String> defaultExcludes = userSetting.getInterfaceAuthenticationExcludes();
|
||||
defaultExcludes.add("/swagger-ui.html");
|
||||
defaultExcludes.add("/swagger-ui/**");
|
||||
defaultExcludes.add("/swagger-resources/**");
|
||||
defaultExcludes.add("/doc.html");
|
||||
defaultExcludes.add("/doc.html#/**");
|
||||
defaultExcludes.add("/v3/api-docs/**");
|
||||
|
||||
defaultExcludes.add("/index.html");
|
||||
defaultExcludes.add("/webjars/**");
|
||||
|
||||
defaultExcludes.add("/js/**");
|
||||
defaultExcludes.add("/api/device/query/snap/**");
|
||||
defaultExcludes.add("/record_proxy/*/**");
|
||||
defaultExcludes.add("/api/emit");
|
||||
defaultExcludes.add("/favicon.ico");
|
||||
defaultExcludes.add("/api/user/login");
|
||||
defaultExcludes.add("/index/hook/**");
|
||||
defaultExcludes.add("/api/device/query/snap/**");
|
||||
defaultExcludes.add("/index/hook/abl/**");
|
||||
defaultExcludes.add("/swagger-ui/**");
|
||||
defaultExcludes.add("/doc.html#/**");
|
||||
// defaultExcludes.add("/channel/log");
|
||||
|
||||
|
||||
|
||||
if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) {
|
||||
defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes());
|
||||
}
|
||||
|
||||
http.headers().contentTypeOptions().disable()
|
||||
.and().cors().configurationSource(configurationSource())
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.conf.security.dto;
|
||||
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.Role;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.security.core.CredentialsContainer;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
@@ -19,8 +21,13 @@ public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
*/
|
||||
private User user;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String accessToken;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
@@ -104,11 +111,4 @@ public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
return user.getPushKey();
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 业务分组
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(description = "业务分组树")
|
||||
public class GroupTree extends Group{
|
||||
|
||||
@@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 区域
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(description = "区域树")
|
||||
public class RegionTree extends Region {
|
||||
|
||||
@@ -224,16 +224,19 @@ public class DeviceQuery {
|
||||
"UDP(udp传输),TCP-ACTIVE(tcp主动模式),TCP-PASSIVE(tcp被动模式)", required = true)
|
||||
@PostMapping("/transport/{deviceId}/{streamMode}")
|
||||
public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){
|
||||
Assert.isTrue(streamMode.equalsIgnoreCase("UDP")
|
||||
|| streamMode.equalsIgnoreCase("TCP-ACTIVE")
|
||||
|| streamMode.equalsIgnoreCase("TCP-PASSIVE"), "数据流传输模式, 取值:UDP/TCP-ACTIVE/TCP-PASSIVE");
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
device.setStreamMode(streamMode);
|
||||
device.setStreamMode(streamMode.toUpperCase());
|
||||
deviceService.updateCustomDevice(device);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "device", description = "设备", required = true)
|
||||
@PostMapping("/device/add/")
|
||||
public void addDevice(Device device){
|
||||
@PostMapping("/device/add")
|
||||
public void addDevice(@RequestBody Device device){
|
||||
|
||||
if (device == null || device.getDeviceId() == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
@@ -250,8 +253,8 @@ public class DeviceQuery {
|
||||
|
||||
@Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "device", description = "设备", required = true)
|
||||
@PostMapping("/device/update/")
|
||||
public void updateDevice(Device device){
|
||||
@PostMapping("/device/update")
|
||||
public void updateDevice(@RequestBody Device device){
|
||||
if (device == null || device.getDeviceId() == null || device.getId() <= 0) {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -215,7 +215,7 @@ public class PlaybackController {
|
||||
|
||||
@Operation(summary = "回放倍速播放", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "streamId", description = "回放流ID", required = true)
|
||||
@Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4", required = true)
|
||||
@Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4、8", required = true)
|
||||
@GetMapping("/speed/{streamId}/{speed}")
|
||||
public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) {
|
||||
log.info("playSpeed: "+streamId+", "+speed);
|
||||
@@ -225,10 +225,6 @@ public class PlaybackController {
|
||||
log.warn("streamId不存在!");
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
|
||||
}
|
||||
if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) {
|
||||
log.warn("不支持的speed: " + speed);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)");
|
||||
}
|
||||
Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId());
|
||||
DeviceChannel channel = channelService.getOneById(inviteInfo.getChannelId());
|
||||
try {
|
||||
|
||||
@@ -381,7 +381,7 @@ public interface DeviceMapper {
|
||||
" OR device_id LIKE concat('%',#{query},'%') escape '/' " +
|
||||
" OR ip LIKE concat('%',#{query},'%') escape '/')" +
|
||||
"</if> " +
|
||||
" order by create_time desc "+
|
||||
" order by create_time desc, device_id " +
|
||||
" </script>")
|
||||
List<Device> getDeviceList(@Param("dataType") Integer dataType, @Param("query") String query, @Param("status") Boolean status);
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ public class DeviceChannelProvider {
|
||||
}
|
||||
sqlBuild.append(" )");
|
||||
}
|
||||
sqlBuild.append("ORDER BY device_id");
|
||||
return sqlBuild.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
@@ -41,9 +40,6 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.message.Response;
|
||||
@@ -117,6 +113,9 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
|
||||
|
||||
@Override
|
||||
public int updateChannels(Device device, List<DeviceChannel> channels) {
|
||||
if (CollectionUtils.isEmpty(channels)) {
|
||||
return 0;
|
||||
}
|
||||
List<DeviceChannel> addChannels = new ArrayList<>();
|
||||
List<DeviceChannel> updateChannels = new ArrayList<>();
|
||||
HashMap<String, DeviceChannel> channelsInStore = new HashMap<>();
|
||||
@@ -442,7 +441,7 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
|
||||
deviceMobilePositionMapper.insertNewPosition(mobilePosition);
|
||||
}
|
||||
|
||||
if (deviceChannel.getDeviceId().equals(deviceChannel.getDeviceId())) {
|
||||
if (deviceChannel.getDeviceId().equals(device.getDeviceId())) {
|
||||
deviceChannel.setDeviceId(null);
|
||||
}
|
||||
if (deviceChannel.getGpsTime() == null) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.common.InviteSessionType;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.exception.ServiceException;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.common.enums.ChannelDataType;
|
||||
@@ -145,6 +146,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
|
||||
deviceChannelPlayService.play(channel, record, callback);
|
||||
} catch (PlayException e) {
|
||||
callback.run(e.getCode(), e.getMsg(), null);
|
||||
} catch (ControllerException e) {
|
||||
log.error("[点播失败] {}({}), {}", channel.getGbName(), channel.getGbDeviceId(), e.getMsg());
|
||||
callback.run(Response.BUSY_HERE, "busy here", null);
|
||||
} catch (Exception e) {
|
||||
log.error("[点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e);
|
||||
callback.run(Response.BUSY_HERE, "busy here", null);
|
||||
|
||||
@@ -669,7 +669,6 @@ public class PlatformServiceImpl implements IPlatformService {
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
|
||||
mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
|
||||
sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream());
|
||||
mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
|
||||
}
|
||||
}
|
||||
}, userSetting.getPlayTimeout());
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.IReceiveRtpServerService;
|
||||
import com.genersoft.iot.vmp.service.ISendRtpServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.*;
|
||||
@@ -1047,8 +1048,8 @@ public class PlayServiceImpl implements IPlayService {
|
||||
null);
|
||||
return;
|
||||
}
|
||||
log.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}({}), SSRC校验:{}",
|
||||
device.getDeviceId(), channel.getDeviceId(), downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(),
|
||||
log.info("[录像下载] deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}({}), SSRC校验:{}",
|
||||
device.getDeviceId(), channel.getDeviceId(), startTime, endTime, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(),
|
||||
ssrcInfo.getSsrc(), String.format("%08x", Long.parseLong(ssrcInfo.getSsrc())).toUpperCase(),
|
||||
device.isSsrcCheck());
|
||||
|
||||
@@ -1723,7 +1724,14 @@ public class PlayServiceImpl implements IPlayService {
|
||||
throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error");
|
||||
}
|
||||
DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId());
|
||||
play(device, deviceChannel, callback);
|
||||
|
||||
MediaServer mediaServerItem = getNewMediaServerItem(device);
|
||||
if (mediaServerItem == null) {
|
||||
log.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", device.getDeviceId(), deviceChannel.getDeviceId());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
|
||||
}
|
||||
play(mediaServerItem, device, deviceChannel, null, record, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ public class MediaInfo {
|
||||
private Integer audioChannels;
|
||||
@Schema(description = "音频采样率")
|
||||
private Integer audioSampleRate;
|
||||
@Schema(description = "音频采样率")
|
||||
@Schema(description = "时长")
|
||||
private Long duration;
|
||||
@Schema(description = "在线")
|
||||
private Boolean online;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.genersoft.iot.vmp.media.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RecordInfo {
|
||||
private String fileName;
|
||||
private String filePath;
|
||||
@@ -24,70 +26,6 @@ public class RecordInfo {
|
||||
return recordInfo;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public double getTimeLen() {
|
||||
return timeLen;
|
||||
}
|
||||
|
||||
public void setTimeLen(double timeLen) {
|
||||
this.timeLen = timeLen;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(String params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecordInfo{" +
|
||||
|
||||
@@ -70,4 +70,9 @@ public interface IMediaNodeServerService {
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath);
|
||||
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema);
|
||||
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema);
|
||||
}
|
||||
|
||||
@@ -159,4 +159,10 @@ public interface IMediaServerService {
|
||||
int createRTPServer(MediaServer mediaServerItem, String streamId, long ssrc, Integer port, boolean onlyAuto, boolean disableAudio, boolean reUsePort, Integer tcpMode);
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
StreamInfo loadMP4File(MediaServer mediaServer, String app, String stream, String datePath);
|
||||
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema);
|
||||
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema);
|
||||
}
|
||||
|
||||
@@ -311,6 +311,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
if (mediaServerInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaServerInDataBase.getId())) {
|
||||
ssrcFactory.initMediaServerSSRC(mediaServerInDataBase.getId(),null);
|
||||
}
|
||||
if (mediaSerItem.getSecret() != null && !mediaServerInDataBase.getSecret().equals(mediaSerItem.getSecret())) {
|
||||
mediaServerInDataBase.setSecret(mediaSerItem.getSecret());
|
||||
}
|
||||
mediaServerInDataBase.setSecret(mediaSerItem.getSecret());
|
||||
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId();
|
||||
redisTemplate.opsForHash().put(key, mediaServerInDataBase.getId(), mediaServerInDataBase);
|
||||
if (mediaServerInDataBase.isStatus()) {
|
||||
@@ -966,4 +970,35 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
mediaNodeServerService.stopProxy(mediaServer, streamKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamInfo loadMP4File(MediaServer mediaServer, String app, String stream, String datePath) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[loadMP4File] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.loadMP4File(mediaServer, app, stream, datePath);
|
||||
return getStreamInfoByAppAndStream(mediaServer, app, stream, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[seekRecordStamp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.seekRecordStamp(mediaServer, app, stream, stamp, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[setRecordSpeed] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@ public class ZLMHttpHookListener {
|
||||
@ResponseBody
|
||||
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
|
||||
public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
|
||||
|
||||
MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
|
||||
if (mediaServer == null) {
|
||||
return HookResult.SUCCESS();
|
||||
@@ -190,6 +189,8 @@ public class ZLMHttpHookListener {
|
||||
|
||||
JSONObject ret = new JSONObject();
|
||||
boolean close = mediaService.closeStreamOnNoneReader(param.getMediaServerId(), param.getApp(), param.getStream(), param.getSchema());
|
||||
log.info("[ZLM HOOK]流无人观看是否触发关闭:{}, {}->{}->{}/{}", close, param.getMediaServerId(), param.getSchema(),
|
||||
param.getApp(), param.getStream());
|
||||
ret.put("code", 0);
|
||||
ret.put("close", close);
|
||||
return ret;
|
||||
|
||||
@@ -169,7 +169,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
return true;
|
||||
}else {
|
||||
log.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject);
|
||||
return false;
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "删除磁盘文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,4 +549,37 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.loadMP4File(mediaServer, app, stream, datePath);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
if (jsonObject.getInteger("code") != 0) {
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.seekRecordStamp(mediaServer, app, stream, stamp, schema);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
if (jsonObject.getInteger("code") != 0) {
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
if (jsonObject.getInteger("code") != 0) {
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public class ZLMRESTfulUtils {
|
||||
|
||||
private OkHttpClient client;
|
||||
|
||||
|
||||
public interface RequestCallback{
|
||||
void run(JSONObject response);
|
||||
}
|
||||
@@ -415,4 +416,34 @@ public class ZLMRESTfulUtils {
|
||||
param.put("name", fileName);
|
||||
return sendPost(mediaServerItem, "deleteRecordDirectory",param, null);
|
||||
}
|
||||
|
||||
public JSONObject loadMP4File(MediaServer mediaServer, String app, String stream, String datePath) {
|
||||
Map<String, Object> param = new HashMap<>(1);
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
param.put("file_path", datePath);
|
||||
param.put("file_repeat", "0");
|
||||
return sendPost(mediaServer, "loadMP4File",param, null);
|
||||
}
|
||||
|
||||
public JSONObject setRecordSpeed(MediaServer mediaServer, String app, String stream, int speed, String schema) {
|
||||
Map<String, Object> param = new HashMap<>(1);
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
param.put("speed", speed);
|
||||
param.put("schema", schema);
|
||||
return sendPost(mediaServer, "setRecordSpeed",param, null);
|
||||
}
|
||||
|
||||
public JSONObject seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
Map<String, Object> param = new HashMap<>(1);
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
param.put("stamp", stamp);
|
||||
param.put("schema", schema);
|
||||
return sendPost(mediaServer, "seekRecordStamp",param, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.genersoft.iot.vmp.gb28181.service;
|
||||
package com.genersoft.iot.vmp.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 云端录像管理
|
||||
@@ -17,7 +20,7 @@ public interface ICloudRecordService {
|
||||
/**
|
||||
* 分页回去云端录像列表
|
||||
*/
|
||||
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId);
|
||||
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, Boolean ascOrder);
|
||||
|
||||
/**
|
||||
* 获取所有的日期
|
||||
@@ -52,4 +55,15 @@ public interface ICloudRecordService {
|
||||
DownloadFileInfo getPlayUrlPath(Integer recordId);
|
||||
|
||||
List<CloudRecordItem> getAllList(String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, List<Integer> ids);
|
||||
|
||||
/**
|
||||
* 加载录像文件,形成录像流
|
||||
*/
|
||||
void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback);
|
||||
|
||||
void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema);
|
||||
|
||||
void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema);
|
||||
|
||||
void deleteFileByIds(Set<Integer> ids);
|
||||
}
|
||||
@@ -79,7 +79,7 @@ public class CloudRecordItem {
|
||||
/**
|
||||
* 文件时长
|
||||
*/
|
||||
private long timeLen;
|
||||
private double timeLen;
|
||||
|
||||
/**
|
||||
* 所属服务ID
|
||||
@@ -96,7 +96,7 @@ public class CloudRecordItem {
|
||||
cloudRecordItem.setFileSize(param.getRecordInfo().getFileSize());
|
||||
cloudRecordItem.setFilePath(param.getRecordInfo().getFilePath());
|
||||
cloudRecordItem.setMediaServerId(param.getMediaServer().getId());
|
||||
cloudRecordItem.setTimeLen((long) param.getRecordInfo().getTimeLen() * 1000);
|
||||
cloudRecordItem.setTimeLen(param.getRecordInfo().getTimeLen() * 1000);
|
||||
cloudRecordItem.setEndTime((param.getRecordInfo().getStartTime() + (long)param.getRecordInfo().getTimeLen()) * 1000);
|
||||
Map<String, String> paramsMap = MediaServerUtils.urlParamToMap(param.getRecordInfo().getParams());
|
||||
if (paramsMap.get("callId") != null) {
|
||||
|
||||
@@ -2,16 +2,22 @@ package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.hook.Hook;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookType;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
|
||||
@@ -28,12 +34,10 @@ import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -57,9 +61,12 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
@Autowired
|
||||
private IRedisRpcPlayService redisRpcPlayService;
|
||||
|
||||
@Autowired
|
||||
private HookSubscribe subscribe;
|
||||
|
||||
@Override
|
||||
public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime,
|
||||
String endTime, List<MediaServer> mediaServerItems, String callId) {
|
||||
String endTime, List<MediaServer> mediaServerItems, String callId, Boolean ascOrder) {
|
||||
// 开始时间和结束时间在数据库中都是以秒为单位的
|
||||
Long startTimeStamp = null;
|
||||
Long endTimeStamp = null;
|
||||
@@ -84,7 +91,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
.replaceAll("_", "/_");
|
||||
}
|
||||
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, null);
|
||||
callId, mediaServerItems, null, ascOrder);
|
||||
return new PageInfo<>(all);
|
||||
}
|
||||
|
||||
@@ -100,7 +107,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
|
||||
long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
|
||||
endTimeStamp, null, mediaServerItems, null);
|
||||
endTimeStamp, null, mediaServerItems, null, null);
|
||||
if (cloudRecordItemList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@@ -213,7 +220,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
}
|
||||
|
||||
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, null);
|
||||
callId, mediaServerItems, null, null);
|
||||
if (all.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
|
||||
}
|
||||
@@ -273,6 +280,96 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
|
||||
}
|
||||
return cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, ids);
|
||||
callId, mediaServerItems, ids, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback) {
|
||||
long startTimestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(date + " 00:00:00");
|
||||
long endTimestamp = startTimestamp + 24 * 60 * 60 * 1000;
|
||||
|
||||
List<CloudRecordItem> recordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimestamp, endTimestamp, null, null, null, false);
|
||||
if (recordItemList.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "此时间无录像");
|
||||
}
|
||||
String mediaServerId = recordItemList.get(0).getMediaServerId();
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
String buildApp = "mp4_record";
|
||||
String buildStream = app + "_" + stream + "_" + date;
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, buildApp, buildStream);
|
||||
if (mediaInfo != null) {
|
||||
if (callback != null) {
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, mediaInfo, null);
|
||||
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, buildApp, buildStream, mediaServerId);
|
||||
subscribe.addSubscribe(hook, (hookData) -> {
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, hookData.getMediaInfo(), null);
|
||||
if (callback != null) {
|
||||
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||
}
|
||||
});
|
||||
String dateDir = recordItemList.get(0).getFilePath().substring(0, recordItemList.get(0).getFilePath().lastIndexOf("/"));
|
||||
mediaServerService.loadMP4File(mediaServer, buildApp, buildStream, dateDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
mediaServerService.seekRecordStamp(mediaServer, app, stream, seek, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
mediaServerService.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteFileByIds(Set<Integer> ids) {
|
||||
log.info("[删除录像文件] ids: {}", ids.toArray());
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordByIds(ids);
|
||||
if (cloudRecordItemList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<CloudRecordItem> cloudRecordItemIdListForDelete = new ArrayList<>();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
|
||||
MediaServer mediaServer = mediaServerService.getOne(cloudRecordItem.getMediaServerId());
|
||||
try {
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServer, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
cloudRecordItemIdListForDelete.add(cloudRecordItem);
|
||||
}
|
||||
}catch (ControllerException e) {
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
stringBuilder.append(cloudRecordItem.getFileName());
|
||||
}
|
||||
|
||||
}
|
||||
if (!cloudRecordItemIdListForDelete.isEmpty()) {
|
||||
cloudRecordServiceMapper.deleteList(cloudRecordItemIdListForDelete);
|
||||
}
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.append(" 删除失败");
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), stringBuilder.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,8 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
|
||||
|
||||
Map<Integer, StreamInfo> recordStreamMap = new HashMap<>();
|
||||
|
||||
@Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES)
|
||||
@Scheduled(fixedRate = 1, timeUnit = TimeUnit.MINUTES)
|
||||
public void execution() {
|
||||
log.info("[录制计划] 执行");
|
||||
// 查询现在需要录像的通道Id
|
||||
List<Integer> startChannelIdList = queryCurrentChannelRecord();
|
||||
|
||||
@@ -133,7 +132,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
|
||||
// 获取当前时间在一周内的序号, 数据库存储的从第几个30分钟开始, 0-47, 包括首尾
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
int week = now.getDayOfWeek().getValue();
|
||||
int index = now.getHour() * 2 + (now.getMinute() > 30?1:0);
|
||||
int index = now.getHour() * 60 + now.getMinute();
|
||||
|
||||
// 查询现在需要录像的通道Id
|
||||
return recordPlanMapper.queryRecordIng(week, index);
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
|
||||
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.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcController;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcMapping;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
public interface CloudRecordServiceMapper {
|
||||
@@ -55,12 +56,13 @@ public interface CloudRecordServiceMapper {
|
||||
" <if test= 'ids != null ' > and id in " +
|
||||
" <foreach collection='ids' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
|
||||
" </if>" +
|
||||
" order by start_time desc" +
|
||||
" <if test= 'ascOrder != null and ascOrder == true'> order by start_time asc</if>" +
|
||||
" <if test= 'ascOrder == null or ascOrder == false'> order by start_time desc</if>" +
|
||||
" </script>")
|
||||
List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
|
||||
@Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
|
||||
@Param("callId")String callId, List<MediaServer> mediaServerItemList,
|
||||
List<Integer> ids);
|
||||
List<Integer> ids, @Param("ascOrder") Boolean ascOrder);
|
||||
|
||||
|
||||
@Select(" <script>" +
|
||||
@@ -124,4 +126,26 @@ public interface CloudRecordServiceMapper {
|
||||
"where id = #{id}" +
|
||||
" </script>")
|
||||
CloudRecordItem queryOne(@Param("id") Integer id);
|
||||
|
||||
@Select(" <script>" +
|
||||
"select media_server_id " +
|
||||
" from wvp_cloud_record " +
|
||||
" where 0 = 0" +
|
||||
" <if test= 'app != null '> and app=#{app}</if>" +
|
||||
" <if test= 'stream != null '> and stream=#{stream}</if>" +
|
||||
" <if test= 'startTimeStamp != null '> and end_time >= #{startTimeStamp}</if>" +
|
||||
" <if test= 'endTimeStamp != null '> and start_time <= #{endTimeStamp}</if>" +
|
||||
" group by media_server_id" +
|
||||
" </script>")
|
||||
List<String> queryMediaServerId(@Param("app") String app,
|
||||
@Param("stream") String stream,
|
||||
@Param("startTimeStamp")Long startTimeStamp,
|
||||
@Param("endTimeStamp")Long endTimeStamp);
|
||||
|
||||
@Select(" <script>" +
|
||||
"select * " +
|
||||
" from wvp_cloud_record where id in " +
|
||||
" <foreach collection='ids' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
|
||||
" </script>")
|
||||
List<CloudRecordItem> queryRecordByIds(Set<Integer> ids);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
@@ -123,16 +124,13 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
public void onApplicationEvent(MediaDepartureEvent event) {
|
||||
|
||||
// 兼容流注销时类型从redis记录获取
|
||||
MediaInfo mediaInfo = redisCatchStorage.getStreamInfo(
|
||||
event.getApp(), event.getStream(), event.getMediaServer().getId());
|
||||
MediaInfo mediaInfo = redisCatchStorage.getPushListItem(event.getApp(), event.getStream());
|
||||
|
||||
if (mediaInfo != null) {
|
||||
log.info("[推流信息] 查询到redis存在推流缓存, 开始清理,{}/{}", event.getApp(), event.getStream());
|
||||
String type = OriginType.values()[mediaInfo.getOriginType()].getType();
|
||||
redisCatchStorage.removeStream(event.getMediaServer().getId(), type, event.getApp(), event.getStream());
|
||||
if ("PUSH".equalsIgnoreCase(type)) {
|
||||
// 冗余数据,自己系统中自用
|
||||
redisCatchStorage.removePushListItem(event.getApp(), event.getStream(), event.getMediaServer().getId());
|
||||
}
|
||||
if (type != null) {
|
||||
// 发送流变化redis消息
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("serverId", userSetting.getServerId());
|
||||
@@ -142,7 +140,6 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
jsonObject.put("mediaServerId", event.getMediaServer().getId());
|
||||
redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
|
||||
}
|
||||
}
|
||||
StreamPush streamPush = getPush(event.getApp(), event.getStream());
|
||||
if (streamPush == null) {
|
||||
return;
|
||||
@@ -576,13 +573,13 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
if (streamPushList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<CommonGBChannel> commonGBChannelList = new ArrayList<>();
|
||||
Set<Integer> channelIds = new HashSet<>();
|
||||
streamPushList.stream().forEach(streamPush -> {
|
||||
if (streamPush.getGbDeviceId() != null) {
|
||||
commonGBChannelList.add(streamPush.buildCommonGBChannel());
|
||||
channelIds.add(streamPush.getGbId());
|
||||
}
|
||||
});
|
||||
streamPushMapper.batchDel(streamPushList);
|
||||
gbChannelService.delete(ids);
|
||||
gbChannelService.delete(channelIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
@@ -9,6 +10,7 @@ import oshi.hardware.NetworkIF;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -142,4 +144,19 @@ public class SystemInfoUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getHardwareId(){
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
HardwareAbstractionLayer hardware = systemInfo.getHardware();
|
||||
// CPU ID
|
||||
String cpuId = hardware.getProcessor().getProcessorIdentifier().getProcessorID();
|
||||
// 主板序号
|
||||
String serialNumber = hardware.getComputerSystem().getSerialNumber();
|
||||
|
||||
return DigestUtils.md5DigestAsHex(
|
||||
(
|
||||
DigestUtils.md5DigestAsHex(cpuId.getBytes(StandardCharsets.UTF_8)) +
|
||||
DigestUtils.md5DigestAsHex(serialNumber.getBytes(StandardCharsets.UTF_8))
|
||||
).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package com.genersoft.iot.vmp.vmanager.cloudRecord;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
|
||||
import com.genersoft.iot.vmp.streamPush.bean.BatchRemoveParam;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.genersoft.iot.vmp.vmanager.cloudRecord.bean.CloudRecordUrl;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -20,12 +27,15 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
@@ -46,6 +56,9 @@ public class CloudRecordController {
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/date/list")
|
||||
@@ -55,7 +68,12 @@ public class CloudRecordController {
|
||||
@Parameter(name = "year", description = "年,置空则查询当年", required = false)
|
||||
@Parameter(name = "month", description = "月,置空则查询当月", required = false)
|
||||
@Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部", required = false)
|
||||
public List<String> openRtpServer(@RequestParam(required = true) String app, @RequestParam(required = true) String stream, @RequestParam(required = false) Integer year, @RequestParam(required = false) Integer month, @RequestParam(required = false) String mediaServerId
|
||||
public List<String> openRtpServer(
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = false) Integer year,
|
||||
@RequestParam(required = false) Integer month,
|
||||
@RequestParam(required = false) String mediaServerId
|
||||
|
||||
) {
|
||||
log.info("[云端录像] 查询存在云端录像的日期 app->{}, stream->{}, mediaServerId->{}, year->{}, month->{}", app, stream, mediaServerId, year, month);
|
||||
@@ -96,7 +114,17 @@ public class CloudRecordController {
|
||||
@Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
|
||||
@Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false)
|
||||
@Parameter(name = "callId", description = "每次录像的唯一标识,置空则查询全部流媒体", required = false)
|
||||
public PageInfo<CloudRecordItem> openRtpServer(@RequestParam(required = false) String query, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam int page, @RequestParam int count, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String callId
|
||||
@Parameter(name = "ascOrder", description = "是否升序排序, 升序: true, 降序: false", required = false)
|
||||
public PageInfo<CloudRecordItem> openRtpServer(@RequestParam(required = false) String query,
|
||||
@RequestParam(required = false) String app,
|
||||
@RequestParam(required = false) String stream,
|
||||
@RequestParam int page,
|
||||
@RequestParam int count,
|
||||
@RequestParam(required = false) String startTime,
|
||||
@RequestParam(required = false) String endTime,
|
||||
@RequestParam(required = false) String mediaServerId,
|
||||
@RequestParam(required = false) String callId,
|
||||
@RequestParam(required = false) Boolean ascOrder
|
||||
|
||||
) {
|
||||
log.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId);
|
||||
@@ -130,7 +158,7 @@ public class CloudRecordController {
|
||||
if (callId != null && ObjectUtils.isEmpty(callId.trim())) {
|
||||
callId = null;
|
||||
}
|
||||
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
|
||||
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId, ascOrder);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@@ -220,6 +248,113 @@ public class CloudRecordController {
|
||||
return cloudRecordService.getPlayUrlPath(recordId);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/loadRecord")
|
||||
@Operation(summary = "加载录像文件形成播放地址")
|
||||
@Parameter(name = "app", description = "应用名", required = true)
|
||||
@Parameter(name = "stream", description = "流ID", required = true)
|
||||
@Parameter(name = "date", description = "日期, 例如 2025-04-10", required = true)
|
||||
public DeferredResult<WVPResult<StreamContent>> loadRecord(
|
||||
HttpServletRequest request,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) String date
|
||||
) {
|
||||
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>();
|
||||
|
||||
result.onTimeout(()->{
|
||||
log.info("[加载录像文件超时] app={}, stream={}, date={}", app, stream, date);
|
||||
WVPResult<StreamContent> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(ErrorCode.ERROR100.getCode());
|
||||
wvpResult.setMsg("加载录像文件超时");
|
||||
result.setResult(wvpResult);
|
||||
});
|
||||
|
||||
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
||||
|
||||
WVPResult<StreamContent> wvpResult = new WVPResult<>();
|
||||
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
||||
wvpResult.setCode(ErrorCode.SUCCESS.getCode());
|
||||
wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
|
||||
|
||||
if (streamInfo != null) {
|
||||
if (userSetting.getUseSourceIpAsStreamIp()) {
|
||||
streamInfo=streamInfo.clone();//深拷贝
|
||||
String host;
|
||||
try {
|
||||
URL url=new URL(request.getRequestURL().toString());
|
||||
host=url.getHost();
|
||||
} catch (MalformedURLException e) {
|
||||
host=request.getLocalAddr();
|
||||
}
|
||||
streamInfo.changeStreamIp(host);
|
||||
}
|
||||
if (!org.springframework.util.ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
||||
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
||||
}
|
||||
wvpResult.setData(new StreamContent(streamInfo));
|
||||
}else {
|
||||
wvpResult.setCode(code);
|
||||
wvpResult.setMsg(msg);
|
||||
}
|
||||
}else {
|
||||
wvpResult.setCode(code);
|
||||
wvpResult.setMsg(msg);
|
||||
}
|
||||
result.setResult(wvpResult);
|
||||
};
|
||||
|
||||
cloudRecordService.loadRecord(app, stream, date, callback);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/seek")
|
||||
@Operation(summary = "定位录像播放到制定位置")
|
||||
@Parameter(name = "mediaServerId", description = "使用的节点Id", required = true)
|
||||
@Parameter(name = "stream", description = "流ID", required = true)
|
||||
@Parameter(name = "seek", description = "要定位的时间位置,从录像开始的时间算起", required = true)
|
||||
public void seekRecord(
|
||||
@RequestParam(required = true) String mediaServerId,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) Double seek,
|
||||
@RequestParam(required = false) String schema
|
||||
) {
|
||||
if (schema == null) {
|
||||
schema = "ts";
|
||||
}
|
||||
cloudRecordService.seekRecord(mediaServerId, app, stream, seek, schema);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/speed")
|
||||
@Operation(summary = "设置录像播放速度")
|
||||
@Parameter(name = "mediaServerId", description = "使用的节点Id", required = true)
|
||||
@Parameter(name = "stream", description = "流ID", required = true)
|
||||
@Parameter(name = "speed", description = "要设置的录像倍速", required = true)
|
||||
public void setRecordSpeed(
|
||||
@RequestParam(required = true) String mediaServerId,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) Integer speed,
|
||||
@RequestParam(required = false) String schema
|
||||
) {
|
||||
if (schema == null) {
|
||||
schema = "ts";
|
||||
}
|
||||
|
||||
cloudRecordService.setRecordSpeed(mediaServerId, app, stream, speed, schema);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除录像文件")
|
||||
@Parameter(name = "ids", description = "文件ID集合", required = true)
|
||||
public void deleteFileByIds(@RequestBody BatchRemoveParam ids) {
|
||||
cloudRecordService.deleteFileByIds(ids.getIds());
|
||||
}
|
||||
|
||||
/************************* 以下这些接口只适合wvp和zlm部署在同一台服务器的情况,且wvp只有一个zlm节点的情况 ***************************************/
|
||||
|
||||
/**
|
||||
@@ -282,7 +417,7 @@ public class CloudRecordController {
|
||||
try {
|
||||
ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss((long)cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
File file = new File(cloudRecordItem.getFilePath());
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
continue;
|
||||
@@ -366,7 +501,7 @@ public class CloudRecordController {
|
||||
if (remoteHost == null) {
|
||||
remoteHost = request.getScheme() + "://" + request.getLocalAddr() + ":" + (request.getScheme().equals("https") ? mediaServer.getHttpSSlPort() : mediaServer.getHttpPort());
|
||||
}
|
||||
PageInfo<CloudRecordItem> cloudRecordItemPageInfo = cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
|
||||
PageInfo<CloudRecordItem> cloudRecordItemPageInfo = cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId, null);
|
||||
PageInfo<CloudRecordUrl> cloudRecordUrlPageInfo = new PageInfo<>();
|
||||
if (!ObjectUtils.isEmpty(cloudRecordItemPageInfo)) {
|
||||
cloudRecordUrlPageInfo.setPageNum(cloudRecordItemPageInfo.getPageNum());
|
||||
@@ -391,7 +526,7 @@ public class CloudRecordController {
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
CloudRecordUrl cloudRecordUrl = new CloudRecordUrl();
|
||||
cloudRecordUrl.setId(cloudRecordItem.getId());
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()));
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss((long)cloudRecordItem.getStartTime()));
|
||||
cloudRecordUrl.setPlayUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath());
|
||||
cloudRecordUrlList.add(cloudRecordUrl);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
package com.genersoft.iot.vmp.vmanager.log;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.ILogService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.LogFileInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.cloudRecord.bean.CloudRecordUrl;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
@@ -23,24 +12,17 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Tag(name = "日志文件查询接口")
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -41,13 +42,14 @@ import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.hardware.NetworkIF;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Tag(name = "服务控制")
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/server")
|
||||
public class ServerController {
|
||||
@@ -80,6 +82,7 @@ public class ServerController {
|
||||
@Value("${server.port}")
|
||||
private int serverPort;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@@ -176,33 +179,14 @@ public class ServerController {
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "重启服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@GetMapping(value = "/restart")
|
||||
@Operation(summary = "关闭服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@GetMapping(value = "/shutdown")
|
||||
@ResponseBody
|
||||
public void restart() {
|
||||
// taskExecutor.execute(()-> {
|
||||
// try {
|
||||
// Thread.sleep(3000);
|
||||
// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
|
||||
// SipStackImpl stack = (SipStackImpl) up.getSipStack();
|
||||
// stack.stop();
|
||||
// Iterator listener = stack.getListeningPoints();
|
||||
// while (listener.hasNext()) {
|
||||
// stack.deleteListeningPoint((ListeningPoint) listener.next());
|
||||
// }
|
||||
// Iterator providers = stack.getSipProviders();
|
||||
// while (providers.hasNext()) {
|
||||
// stack.deleteSipProvider((SipProvider) providers.next());
|
||||
// }
|
||||
// VManageBootstrap.restart();
|
||||
// } catch (InterruptedException | ObjectInUseException e) {
|
||||
// throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
|
||||
// }
|
||||
// });
|
||||
public void shutdown() {
|
||||
log.info("正在关闭服务。。。");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
@Operation(summary = "获取系统配置信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@GetMapping(value = "/system/configInfo")
|
||||
@ResponseBody
|
||||
@@ -293,7 +277,7 @@ public class ServerController {
|
||||
@GetMapping(value = "/info")
|
||||
@ResponseBody
|
||||
@Operation(summary = "获取系统信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
public Map<String, Map<String, String>> getInfo() {
|
||||
public Map<String, Map<String, String>> getInfo(HttpServletRequest request) {
|
||||
Map<String, Map<String, String>> result = new LinkedHashMap<>();
|
||||
Map<String, String> hardwareMap = new LinkedHashMap<>();
|
||||
result.put("硬件信息", hardwareMap);
|
||||
@@ -341,6 +325,12 @@ public class ServerController {
|
||||
platformMap.put("GIT版本", version.getGIT_Revision_SHORT());
|
||||
platformMap.put("DOCKER环境", new File("/.dockerenv").exists()?"是":"否");
|
||||
|
||||
Map<String, String> docmap = new LinkedHashMap<>();
|
||||
result.put("文档地址", docmap);
|
||||
docmap.put("部署文档", "https://doc.wvp-pro.cn");
|
||||
docmap.put("接口文档", String.format("%s://%s:%s/doc.html", request.getScheme(), request.getServerName(), request.getServerPort()));
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,9 +63,9 @@ public class UserApiKeyController {
|
||||
|
||||
Long expirationTime = null;
|
||||
if (expiresAt != null) {
|
||||
long timestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
|
||||
expirationTime = (timestamp - System.currentTimeMillis()) / (60 * 1000);
|
||||
if (expirationTime < 0) {
|
||||
expirationTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
|
||||
long difference = (expirationTime - System.currentTimeMillis()) / (60 * 1000);
|
||||
if (difference < 0) {
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "过期时间不能早于当前时间");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.genersoft.iot.vmp.vmanager.user;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.SecurityUtils;
|
||||
@@ -42,6 +43,9 @@ public class UserController {
|
||||
@Autowired
|
||||
private IRoleService roleService;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@GetMapping("/login")
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "登录", description = "登录成功后返回AccessToken, 可以从返回值获取到也可以从响应头中获取到," +
|
||||
@@ -62,6 +66,7 @@ public class UserController {
|
||||
String jwt = JwtUtils.createToken(username);
|
||||
response.setHeader(JwtUtils.getHeader(), jwt);
|
||||
user.setAccessToken(jwt);
|
||||
user.setServerId(userSetting.getServerId());
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -37,10 +37,9 @@ public class ApiControlController {
|
||||
* @param channel 通道序号
|
||||
* @param code 通道编号
|
||||
* @param speed 速度(0~255) 默认值: 129
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/ptz")
|
||||
private void list(String serial,String command,
|
||||
private void ptz(String serial,String command,
|
||||
@RequestParam(required = false)Integer channel,
|
||||
@RequestParam(required = false)String code,
|
||||
@RequestParam(required = false)Integer speed){
|
||||
@@ -55,7 +54,7 @@ public class ApiControlController {
|
||||
if (device == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到");
|
||||
}
|
||||
int cmdCode = 0;
|
||||
int cmdCode = -1;
|
||||
switch (command){
|
||||
case "left":
|
||||
cmdCode = 2;
|
||||
@@ -93,6 +92,9 @@ public class ApiControlController {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (cmdCode == -1) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未识别的指令:" + command);
|
||||
}
|
||||
// 默认值 50
|
||||
try {
|
||||
cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed);
|
||||
@@ -110,7 +112,6 @@ public class ApiControlController {
|
||||
* @param command 控制指令 允许值: set, goto, remove
|
||||
* @param preset 预置位编号(1~255)
|
||||
* @param name 预置位名称, command=set 时有效
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/preset")
|
||||
private void list(String serial,String command,
|
||||
|
||||
57
src/main/resources/install.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#! /bin/sh
|
||||
|
||||
WORD_DIR=$(cd $(dirname $0); pwd)
|
||||
SERVICE_NAME="wvp"
|
||||
|
||||
# 检查是否为 root 用户
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "提示: 建议使用 root 用户执行此脚本,否则可能权限不足!"
|
||||
read -p "继续?(y/n) " -n 1 -r
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
|
||||
# 当前目录直接搜索(不含子目录)
|
||||
jar_files=(*.jar)
|
||||
|
||||
if [ ${#jar_files[@]} -eq 0 ]; then
|
||||
echo "当前目录无 JAR 文件!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 遍历结果
|
||||
for jar in "${jar_files[@]}"; do
|
||||
echo "找到 JAR 文件: $jar"
|
||||
done
|
||||
|
||||
# 写文件
|
||||
# 生成 Systemd 服务文件内容
|
||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
cat << EOF | sudo tee "$SERVICE_FILE" > /dev/null
|
||||
[Unit]
|
||||
Description=${SERVICE_NAME}
|
||||
After=syslog.target
|
||||
|
||||
[Service]
|
||||
User=$USER
|
||||
WorkingDirectory=${WORD_DIR}
|
||||
ExecStart=java -jar ${jar_files}
|
||||
SuccessExitStatus=143
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
Environment=SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# 重载 Systemd 并启动服务
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable "$SERVICE_NAME"
|
||||
sudo systemctl start "$SERVICE_NAME"
|
||||
|
||||
# 验证服务状态
|
||||
echo "服务已安装!执行以下命令查看状态:"
|
||||
echo "sudo systemctl status $SERVICE_NAME"
|
||||