diff --git a/doc/_content/ability/device.md b/doc/_content/ability/device.md index ceed545e..134f7add 100644 --- a/doc/_content/ability/device.md +++ b/doc/_content/ability/device.md @@ -3,9 +3,6 @@ 设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息,只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md), 主要有以下字段需要配置: -- sip->ip -本机IP,不要使用127.0.0.1/0.0.0.0, 除非你对项目及其熟悉 - - sip->port 28181服务监听的端口 diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md index 66693bd9..b5dbef05 100644 --- a/doc/_content/introduction/compile.md +++ b/doc/_content/introduction/compile.md @@ -44,7 +44,7 @@ window环境,以windows10为例: 这里依然是参考网上教程,自行安装吧。 ## 4 编译ZLMediaKit -参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki),截取一下关键步骤: +参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki),如果需要使用语音对讲功能,请参考[zlm启用webrtc编译指南](https://github.com/ZLMediaKit/ZLMediaKit/wiki/zlm%E5%90%AF%E7%94%A8webrtc%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97),开启zlm的webrtc功能。截取一下关键步骤: ```bash # 国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit diff --git a/doc/_content/introduction/config.md b/doc/_content/introduction/config.md index 29f00a4b..359aac9e 100644 --- a/doc/_content/introduction/config.md +++ b/doc/_content/introduction/config.md @@ -14,20 +14,11 @@ WVP-PRO通过调用ZLMediaKit的RESTful接口实现对ZLMediaKit行为的控制; 基于spring boot的开发方式,配置文件的加载是很灵活的。默认在src/main/resources/application.yml,部分配置项是可选,你不需要全部配置在配置文件中, 完全的配置说明可以参看all-application.yml。 ### 1.1 默认加载配置文件方式 -使用maven打包后的jar包里,已经存在了配置文件,但是每次打开jar包修改配置文件或者修改后再打包都是比较麻烦的,所以大家可通过指定配置文件路径来加载指定位置的配置文件。 +使用maven打包后的target里,已经存在了配置文件,默认加载配置文件为application.yml,查看内容发现其中spring.profiles.active配置的内容,将入配置的值为dev,那么具体加载的配置文件就是application-dev.yml,如果配置的值找不到对应的配置文件,修改值为dev。 ```shell cd wvp-GB28181-pro/target -java -jar wvp-pro-*.jar --spring.config.location=../src/main/resources/application.yml +java -jar wvp-pro-*.jar ``` -### 1.2 迁移配置文件以方便启动 -由于配置文件的命令比较长,所以为了启动方便通常我会把配置文件放到jar包的同级目录,类似这样, -移除jar包内/BOOT-INF/classes/下所有以application开头的文件,使用解压缩工具打开jar即可,不需要解压出来。 -```shell -cd wvp-GB28181-pro/target -mv ../src/main/resources/application-dev.yml application.yml -java -jar wvp-pro-*.jar -``` -这也是我自己最常用的方式。 ## 2 配置WVP-PRO wvp支持多种数据库,包括Mysql,Postgresql,金仓等,配置任选一种即可。 ### 2.1 数据库配置 @@ -37,27 +28,28 @@ wvp支持多种数据库,包括Mysql,Postgresql,金仓等,配置任选 数据库名称以wvp为例 ```yaml spring: - datasource: - 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&allowPublicKeyRetrieval=true - username: root - password: 12345678 - -mybatis: - configuration: - map-underscore-to-camel-case: true + 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 + username: root + password: root123 ``` #### 2.1.3 Postgresql数据库配置 数据库名称以wvp为例 ```yaml spring: - datasource: - type: com.zaxxer.hikari.HikariDataSource - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true - username: root - password: 12345678 + dynamic: + primary: master + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: 12345678 pagehelper: helper-dialect: postgresql @@ -66,12 +58,14 @@ pagehelper: 数据库名称以wvp为例 ```yaml spring: - datasource: - type: com.zaxxer.hikari.HikariDataSource - driver-class-name: com.kingbase8.Driver - url: jdbc:kingbase8://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=utf8 - username: root - password: 12345678 + dynamic: + primary: master + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.kingbase8.Driver + url: jdbc:kingbase8://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=utf8 + username: root + password: 12345678 pagehelper: helper-dialect: postgresql @@ -88,8 +82,6 @@ server: ```yaml # 作为28181服务器的配置 sip: - # [必须修改] 本机的IP - ip: 192.168.1.3 # [可选] 28181服务监听的端口 port: 5060 # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) @@ -106,58 +98,42 @@ sip: ```yaml #zlm 默认服务器配置 media: - # ZLMediaKit的服务ID,必须配置 - id: FQ3TF8yT83wh5Wvz - # [必须修改] zlm服务器的内网IP,sdp-ip与stream-ip使用默认值的情况下,这里不要使用127.0.0.1/0.0.0.0 - ip: 192.168.1.3 - # [必须修改] zlm服务器的http.port - http-port: 6080 - # [可选] zlm服务器的hook.admin_params=secret - secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc - # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 - rtp: - # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 - enable: true - # [可选] 在此范围内选择端口用于媒体流传输, - port-range: 30000,30500 # 端口范围 - # [可选] 国标级联在此范围内选择端口发送媒体流, - send-port-range: 30000,30500 # 端口范围 - # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 - record-assist-port: 18081 + id: zlmediakit-local + # [必须修改] zlm服务器的内网IP + ip: 172.19.128.50 + # [可选] 有公网IP就配置公网IP, 不可用域名 + wan_ip: + # [必须修改] zlm服务器的http.port + http-port: 9092 + # [可选] zlm服务器访问WVP所使用的IP, 默认使用127.0.0.1,zlm和wvp没有部署在同一台服务器时必须配置 + hook-ip: 172.19.128.50 + # [必选选] zlm服务器的hook.admin_params=secret + secret: TWSYFgYJOQWB4ftgeYut8DW4wbs7pQnj + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 + port-range: 30000,35000 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 40000,40300 # 端口范围 ``` ### 2.4 个性化定制信息配置 ```yaml # [根据业务需求配置] user-settings: - # [可选] 服务ID,不写则为000000 - server-id: - # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true - auto-apply-play: false - # [可选] 部分设备需要扩展SDP,需要打开此设置 - senior-sdp: false - # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) - save-position-history: false - # 点播等待超时时间,单位:毫秒 - play-timeout: 3000 - # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 - wait-track: false - # 是否开启接口鉴权 - interface-authentication: true - # 自动配置redis 可以过期事件 - redis-config: true - # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 - interface-authentication-excludes: - - /api/v1/** - # 推流直播是否录制 - record-push-live: true - # 国标是否录制 - record-sip: true - # 是否将日志存储进数据库 - logInDatabase: true - # 第三方匹配,用于从stream钟获取有效信息 - thirdPartyGBIdReg: [\s\S]* + # 点播/录像回放 等待超时时间,单位:毫秒 + play-timeout: 180000 + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: true + # 推流直播是否录制 + record-push-live: true + # 国标是否录制 + record-sip: true + # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + stream-on-demand: true ``` - +更多完整的配置信息参考all-application.yml文件,需要那个配置项,复制到正在使用的配置文件中对应的文件即可。 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。 接下来[部署到服务器](./_content/introduction/deployment.md), 如果你只是本地运行直接在本地运行即可。 diff --git a/doc/_content/introduction/deployment.md b/doc/_content/introduction/deployment.md index 3883842a..f11fe7f2 100644 --- a/doc/_content/introduction/deployment.md +++ b/doc/_content/introduction/deployment.md @@ -19,7 +19,7 @@ 3. 测试环境部署建议所有服务部署在一台主机,关闭防火墙,减少因网络出现问题的可能; 4. 生产环境按需开放端口,但是建议修改默认端口,尤其是5060端口,易受到攻击; -5. zlm使用docker部署的情况,要求端口映射一致,比如映射5060,应将外部端口也映射为5060端口; +5. zlm使用docker部署的情况,请使用host模式,或者端口映射一致,比如映射5060,应将外部端口也映射为5060端口; 6. zlm与wvp会保持高频率的通信,所以不要去将wvp与zlm分属在两个网络,比如wvp在内网,zlm却在公网的情况。 7. 启动服务,以linux为例 **启动WVP-PRO** @@ -36,19 +36,9 @@ nohup ./MediaServer -d -m 3 & ### 前后端分离部署 前后端部署目前在最新的版本已经支持,请使用3月15日之后的版本部署 前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。 -前后端分离部署最大的问题是跨域的解决,之前版本使用cookie完成登录流程,而cookie是不可以在复杂跨域中使用的。所以当前版本使用JWT生成的TOKEN作为认证凭据, -部署前端后需要在wvp中配置前端访问的地址以完成跨域流程。 +WVP默认开启全部接口支持跨域。部署前端文件到WEB容器,并将访问的地址设置为WVP的地址即可。 **配置前端服务器** -1. 假如你的服务有公网域名为xxx.com,公网IP为11.11.11.11, 那么你可以在wvp中这样配置: -```yaml -user-settings: - # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 - allowed-origins: - - http://xxx.com:8008 - - http://11.11.11.11:8008 -``` -配置不是必须的,你使用哪个ip/域名访问就配置哪个即可。修改配置后重启wvp以使配置生效。 -2. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址 +1. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址 ```javascript window.baseUrl = "http://xxx.com:18080" ``` diff --git a/doc/_sidebar.md b/doc/_sidebar.md index bdf59552..09a675df 100644 --- a/doc/_sidebar.md +++ b/doc/_sidebar.md @@ -1,7 +1,6 @@ * **编译与部署** - * [测试](_content/introduction/test.md) * [编译](_content/introduction/compile.md) * [配置](_content/introduction/config.md) * [部署](_content/introduction/deployment.md) diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java index eac7c113..0770172d 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -27,19 +27,19 @@ public class MediaConfig{ @Value("${media.ip}") private String ip; - @Value("${media.hook-ip:}") - private String hookIp; + @Value("${media.wan_ip}") + private String wanIp; - @Value("${sip.ip}") - private String sipIp; + @Value("${media.hook-ip:127.0.0.1}") + private String hookIp; @Value("${sip.domain}") private String sipDomain; - @Value("${media.sdp-ip:${media.ip}}") + @Value("${media.sdp-ip:${media.wan_ip}}") private String sdpIp; - @Value("${media.stream-ip:${media.ip}}") + @Value("${media.stream-ip:${media.wan_ip}}") private String streamIp; @Value("${media.http-port:0}") @@ -111,20 +111,7 @@ public class MediaConfig{ } public String getHookIp() { - if (ObjectUtils.isEmpty(hookIp)){ - return sipIp; - }else { - return hookIp; - } - - } - - public String getSipIp() { - if (sipIp == null) { - return this.ip; - }else { - return sipIp; - } + return hookIp; } public int getHttpPort() { diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java index 6a6c54f9..4b85e0da 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java @@ -98,9 +98,6 @@ public class SipConfig { } public String getShowIp() { - if (this.showIp == null) { - return this.ip; - } return showIp; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index 96253d6e..007c72a3 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -74,6 +74,8 @@ public class UserSetting { private boolean registerKeepIntDialog = false; + private int gbDeviceOnline = 1; + public Boolean getSavePositionHistory() { return savePositionHistory; } @@ -325,4 +327,12 @@ public class UserSetting { public void setDocEnable(Boolean docEnable) { this.docEnable = docEnable; } + + public int getGbDeviceOnline() { + return gbDeviceOnline; + } + + public void setGbDeviceOnline(int gbDeviceOnline) { + this.gbDeviceOnline = gbDeviceOnline; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java index c5862550..229aeabb 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java @@ -22,7 +22,7 @@ public class WVPTimerTask { @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 public void execute(){ JSONObject jsonObject = new JSONObject(); - jsonObject.put("ip", sipConfig.getIp()); + jsonObject.put("ip", sipConfig.getShowIp()); jsonObject.put("port", serverPort); redisCatchStorage.updateWVPInfo(jsonObject, 3); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index 56663f18..13735306 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -16,6 +16,9 @@ import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import javax.sip.*; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -40,15 +43,46 @@ public class SipLayer implements CommandLineRunner { @Override public void run(String... args) { List monitorIps = new ArrayList<>(); - // 使用逗号分割多个ip - String separator = ","; - if (sipConfig.getIp().indexOf(separator) > 0) { - String[] split = sipConfig.getIp().split(separator); - monitorIps.addAll(Arrays.asList(split)); + if (ObjectUtils.isEmpty(sipConfig.getIp())) { + try { + // 获得本机的所有网络接口 + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + while (nifs.hasMoreElements()) { + NetworkInterface nif = nifs.nextElement(); + // 获得与该网络接口绑定的 IP 地址,一般只有一个 + Enumeration addresses = nif.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = addresses.nextElement(); + if (addr instanceof Inet4Address) { + if (addr.getHostAddress().equals("127.0.0.1")){ + continue; + } + if (nif.getName().startsWith("docker")) { + continue; + } + logger.error("[自动配置SIP监听网卡] 网卡接口地址: {}", addr.getHostAddress());// 只关心 IPv4 地址 + monitorIps.add(addr.getHostAddress()); + } + } + } + }catch (Exception e) { + logger.error("[读取网卡信息失败]", e); + } + if (monitorIps.isEmpty()) { + logger.error("[自动配置SIP监听网卡信息失败], 请手动配置SIP.IP后重新启动"); + System.exit(1); + } }else { - monitorIps.add(sipConfig.getIp()); + // 使用逗号分割多个ip + String separator = ","; + if (sipConfig.getIp().indexOf(separator) > 0) { + String[] split = sipConfig.getIp().split(separator); + monitorIps.addAll(Arrays.asList(split)); + }else { + monitorIps.add(sipConfig.getIp()); + } } - + sipConfig.setShowIp(String.join(",", monitorIps)); SipFactory.getInstance().setPathName("gov.nist"); if (monitorIps.size() > 0) { for (String monitorIp : monitorIps) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java index 6557bbaa..a15de224 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java @@ -4,7 +4,6 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; -import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -58,12 +57,19 @@ public class SubscribeHolder { dynamicTask.stop(taskOverdueKey); } - public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) { + public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo, Runnable gpsTask) { mobilePositionMap.put(platformId, subscribeInfo); String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetting.getServerId() + "MobilePosition_" + platformId; // 添加任务处理GPS定时推送 - dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(platformId), - subscribeInfo.getGpsInterval() * 1000); + + int cycleForCatalog; + if (subscribeInfo.getGpsInterval() <= 0) { + cycleForCatalog = 5; + }else { + cycleForCatalog = subscribeInfo.getGpsInterval(); + } + dynamicTask.startCron(key, gpsTask, + cycleForCatalog * 1000); String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; if (subscribeInfo.getExpires() > 0) { // 添加任务处理订阅过期 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java index 299d59ac..b87684a8 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java @@ -79,7 +79,7 @@ public class EventPublisher { // 数据去重 Set gbIdSet = new HashSet<>(); for (DeviceChannel deviceChannel : deviceChannels) { - if (!gbIdSet.contains(deviceChannel.getChannelId())) { + if (deviceChannel != null && deviceChannel.getChannelId() != null && !gbIdSet.contains(deviceChannel.getChannelId())) { gbIdSet.add(deviceChannel.getChannelId()); channels.add(deviceChannel); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java deleted file mode 100755 index a4512f35..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.task.impl; - -import com.genersoft.iot.vmp.common.CommonCallback; -import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; -import com.genersoft.iot.vmp.service.IPlatformService; -import com.genersoft.iot.vmp.utils.SpringBeanFactory; - -/** - * 向已经订阅(移动位置)的上级发送MobilePosition消息 - * @author lin - */ -public class MobilePositionSubscribeHandlerTask implements ISubscribeTask { - - - private IPlatformService platformService; - private String platformId; - - - public MobilePositionSubscribeHandlerTask(String platformId) { - this.platformService = SpringBeanFactory.getBean("platformServiceImpl"); - this.platformId = platformId; - } - - @Override - public void run() { - platformService.sendNotifyMobilePosition(this.platformId); - } - - @Override - public void stop(CommonCallback callback) { - - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java index dd30fea5..8787ed1f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java @@ -351,38 +351,6 @@ public class SIPRequestHeaderProvider { request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); - return request; - } - public Request createBroadcastMessageRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { - Request request = null; - // sipuri - SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); - // via - ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag); - viaHeader.setRPort(); - viaHeaders.add(viaHeader); - // from - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); - Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); - // to - SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); - Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); - - // Forwards - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); - // ceq - CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); - - ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); - - request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, - toHeader, viaHeaders, maxForwards, contentTypeHeader, content); - - request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); - return request; } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 247dd8e2..e27343ce 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -1230,6 +1230,8 @@ public class SIPCommander implements ISIPCommander { subscribePostitionXml.append("" + device.getDeviceId() + "\r\n"); if (device.getSubscribeCycleForMobilePosition() > 0) { subscribePostitionXml.append("" + device.getMobilePositionSubmissionInterval() + "\r\n"); + }else { + subscribePostitionXml.append("5\r\n"); } subscribePostitionXml.append("\r\n"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java index 3bb6842b..d4df0ae5 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java @@ -13,7 +13,6 @@ import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.service.IPlayService; -import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; @@ -108,19 +107,16 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) { WVPResult wvpResult = redisRpcService.startSendRtp(sendRtpItem.getRedisKey(), sendRtpItem); if (wvpResult.getCode() == 0) { - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(), - sendRtpItem.getChannelId(), parentPlatform.getServerGBId(), parentPlatform.getName(), userSetting.getServerId(), - sendRtpItem.getMediaServerId()); - messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); - redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel); + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, parentPlatform); } } else { try { if (sendRtpItem.isTcpActive()) { - mediaServerService.startSendRtpPassive(mediaInfo, parentPlatform, sendRtpItem, null); + mediaServerService.startSendRtpPassive(mediaInfo,sendRtpItem, null); } else { - mediaServerService.startSendRtp(mediaInfo, parentPlatform, sendRtpItem); + mediaServerService.startSendRtp(mediaInfo, sendRtpItem); } + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, parentPlatform); }catch (ControllerException e) { logger.error("RTP推流失败: {}", e.getMessage()); playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader); @@ -142,9 +138,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In } try { if (sendRtpItem.isTcpActive()) { - mediaServerService.startSendRtpPassive(mediaInfo, null, sendRtpItem, null); + mediaServerService.startSendRtpPassive(mediaInfo, sendRtpItem, null); } else { - mediaServerService.startSendRtp(mediaInfo, null, sendRtpItem); + mediaServerService.startSendRtp(mediaInfo, sendRtpItem); } }catch (ControllerException e) { logger.error("RTP推流失败: {}", e.getMessage()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index e3a25ec8..de1a9290 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -466,7 +466,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements if (sendRtpItem.isTcpActive()) { MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); try { - mediaServerService.startSendRtpPassive(mediaServer, platform, sendRtpItem, 5); + mediaServerService.startSendRtpPassive(mediaServer, sendRtpItem, 5); + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, platform); }catch (ControllerException e) {} } } catch (SipException | InvalidArgumentException | ParseException e) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java index 1580bafb..c392a129 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java @@ -147,7 +147,9 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme subscribeHolder.removeMobilePositionSubscribe(platformId); }else { subscribeInfo.setResponse(response); - subscribeHolder.putMobilePositionSubscribe(platformId, subscribeInfo); + subscribeHolder.putMobilePositionSubscribe(platformId, subscribeInfo, ()->{ + platformService.sendNotifyMobilePosition(platformId); + }); } } catch (SipException | InvalidArgumentException | ParseException e) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java index 46f9642c..bb1b3792 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java @@ -152,7 +152,7 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp }else { // 发流 try { - mediaServerService.startSendRtp(hookData.getMediaServer(),null, sendRtpItem); + mediaServerService.startSendRtp(hookData.getMediaServer(), sendRtpItem); }catch (ControllerException e) { logger.info("[语音喊话] 推流失败, 结果: {}", e.getMessage()); return; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java index 034e24f9..bfc42f5a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -62,7 +62,10 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp } SIPRequest request = (SIPRequest) evt.getRequest(); logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); - + if (userSetting.getGbDeviceOnline() == 0 && !device.isOnLine()) { + logger.warn("[收到心跳] 设备离线,心跳不进行回复, device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + return; + } // 回复200 OK try { responseAck(request, Response.OK); @@ -101,9 +104,10 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp if (device.isOnLine()) { deviceService.updateDevice(device); }else { - // 对于已经离线的设备判断他的注册是否已经过期 - if (!deviceService.expire(device)){ - device.setOnLine(false); + if (userSetting.getGbDeviceOnline() == 1) { + // 对于已经离线的设备判断他的注册是否已经过期 + device.setOnLine(true); + device.setRegisterTime(DateUtil.getNow()); deviceService.online(device, null); } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java index fb9de3f8..ccd2828e 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java @@ -1,9 +1,9 @@ package com.genersoft.iot.vmp.media; import com.genersoft.iot.vmp.conf.MediaConfig; +import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.bean.MediaServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaServer.java b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaServer.java index bd55fc73..b6c150e7 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaServer.java +++ b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaServer.java @@ -16,7 +16,7 @@ public class MediaServer { private String ip; @Schema(description = "hook使用的IP(zlm访问WVP使用的IP)") - private String hookIp; + private String hookIp = "127.0.0.1"; @Schema(description = "SDP IP") private String sdpIp; diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java index b2f1ce65..a56c7b07 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.media.service; import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; @@ -141,9 +140,9 @@ public interface IMediaServerService { Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId); - void startSendRtpPassive(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout); + void startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout); - void startSendRtp(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem); + void startSendRtp(MediaServer mediaServer, SendRtpItem sendRtpItem); SendRtpItem createSendRtpItem(MediaServer mediaServerItem, String addressStr, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean mediaTransmissionTCP, boolean rtcp); diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java index c29da4f4..6973bf09 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java @@ -7,8 +7,6 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; -import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; import com.genersoft.iot.vmp.media.bean.MediaInfo; @@ -24,7 +22,6 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType; import com.genersoft.iot.vmp.service.IInviteStreamService; import com.genersoft.iot.vmp.service.bean.MediaServerLoad; -import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; @@ -827,18 +824,17 @@ public class MediaServerServiceImpl implements IMediaServerService { } @Override - public void startSendRtpPassive(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout) { + public void startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { logger.info("[startSendRtpPassive] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); } mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout); - sendPlatformStartPlayMsg(platform, sendRtpItem); } @Override - public void startSendRtp(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem) { + public void startSendRtp(MediaServer mediaServer, SendRtpItem sendRtpItem) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { logger.info("[startSendRtpStream] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); @@ -847,21 +843,6 @@ public class MediaServerServiceImpl implements IMediaServerService { logger.info("[开始推流] rtp/{}, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); mediaNodeServerService.startSendRtpStream(mediaServer, sendRtpItem); - if (platform != null) { - sendPlatformStartPlayMsg(platform, sendRtpItem); - } - - - } - - private void sendPlatformStartPlayMsg(ParentPlatform platform, SendRtpItem sendRtpItem) { - if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform != null) { - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(), - sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(), - sendRtpItem.getMediaServerId()); - messageForPushChannel.setPlatFormIndex(platform.getId()); - redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel); - } } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 738d3f80..636b4bf1 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -190,7 +190,15 @@ public class ZLMHttpHookListener { } } - + /** + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 + */ +// @ResponseBody +// @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") +// public HookResult onStreamChanged(@RequestBody JSONObject param) { +// System.out.println(11); +// return HookResult.SUCCESS(); +// } /** * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 */ diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java index 977fded7..b9ffbe21 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java @@ -8,15 +8,14 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.media.bean.MediaInfo; -import com.genersoft.iot.vmp.media.service.IMediaNodeServerService; import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.service.IMediaNodeServerService; import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; @@ -36,9 +35,6 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { @Autowired private ZLMServerFactory zlmServerFactory; - @Value("${sip.ip}") - private String sipIp; - @Override public int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) { return zlmServerFactory.createRTPServer(mediaServer, streamId, ssrc, port, onlyAuto, reUsePort, tcpMode); @@ -120,7 +116,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { mediaServer.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); mediaServer.setStreamIp(ip); - mediaServer.setHookIp(sipIp.split(",")[0]); + mediaServer.setHookIp("127.0.0.1"); mediaServer.setSdpIp(ip); mediaServer.setType("zlm"); return mediaServer; diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index 285b9e6b..5d5f1c31 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -96,7 +96,6 @@ public class ZLMRESTfulUtils { if (callback == null) { try { Response response = client.newCall(request).execute(); - if (response.isSuccessful()) { ResponseBody responseBody = response.body(); if (responseBody != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index b46b5fc4..2b331fb5 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -389,7 +389,7 @@ public class PlayServiceImpl implements IPlayService { }, userSetting.getPlayTimeout()); try { - mediaServerService.startSendRtpPassive(mediaServerItem, null, sendRtpItem, userSetting.getPlayTimeout() * 1000); + mediaServerService.startSendRtpPassive(mediaServerItem, sendRtpItem, userSetting.getPlayTimeout() * 1000); }catch (ControllerException e) { mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); logger.info("[语音对讲]失败 deviceId: {}, channelId: {}", device.getDeviceId(), channelId); @@ -1003,6 +1003,7 @@ public class PlayServiceImpl implements IPlayService { dynamicTask.stop(downLoadTimeOutTaskKey); callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg), null); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); inviteStreamService.removeInviteInfo(inviteInfo); }; @@ -1413,10 +1414,11 @@ public class PlayServiceImpl implements IPlayService { if (mediaInfo != null) { try { if (sendRtpItem.isTcpActive()) { - mediaServerService.startSendRtpPassive(mediaInfo, platform, sendRtpItem, null); + mediaServerService.startSendRtpPassive(mediaInfo, sendRtpItem, null); } else { - mediaServerService.startSendRtp(mediaInfo, platform, sendRtpItem); + mediaServerService.startSendRtp(mediaInfo, sendRtpItem); } + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, platform); }catch (ControllerException e) { logger.error("RTP推流失败: {}", e.getMessage()); startSendRtpStreamFailHand(sendRtpItem, platform, callIdHeader); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java index d3f69d67..920228df 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; @@ -531,7 +532,16 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override public int updateStatus(boolean status, String app, String stream) { - return streamProxyMapper.updateStatus(app, stream, status); + // 状态变化时推送到国标上级 + StreamProxyItem streamProxyItem = streamProxyMapper.selectOne(app, stream); + if (streamProxyItem == null) { + return 0; + } + int result = streamProxyMapper.updateStatus(app, stream, status); + if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { + gbStreamService.sendCatalogMsg(streamProxyItem, status?CatalogEvent.ON:CatalogEvent.OFF); + } + return result; } private void syncPullStream(String mediaServerId){ diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java index 4e12670e..adb23186 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java @@ -209,7 +209,7 @@ public class RedisRpcController { return response; } try { - mediaServerService.startSendRtp(mediaServer, null, sendRtpItem); + mediaServerService.startSendRtp(mediaServer, sendRtpItem); }catch (ControllerException exception) { logger.info("[redis-rpc] 发流失败: {}/{}, 目标地址: {}:{}, {}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), exception.getMsg()); WVPResult wvpResult = WVPResult.fail(exception.getCode(), exception.getMsg()); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index d87c3423..2347f502 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -208,7 +208,7 @@ public interface IRedisCatchStorage { void sendChannelAddOrDelete(String deviceId, String channelId, boolean add); - void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel); + void sendPlatformStartPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform); void sendPlatformStopPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index a7c5f72c..b743cfc6 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -656,10 +656,16 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { } @Override - public void sendPlatformStartPlayMsg(MessageForPushChannel msg) { - String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY; - logger.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); - redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + public void sendPlatformStartPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform) { + if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform != null) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(), + sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(), + sendRtpItem.getMediaServerId()); + messageForPushChannel.setPlatFormIndex(platform.getId()); + String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY; + logger.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId()); + redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel)); + } } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java index 9dd4c3d8..8a2618bb 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java @@ -322,6 +322,9 @@ public class DeviceQuery { public void updateDevice(Device device){ if (device != null && device.getDeviceId() != null) { + if (device.getSubscribeCycleForMobilePosition() > 0 && device.getMobilePositionSubmissionInterval() <= 0) { + device.setMobilePositionSubmissionInterval(5); + } deviceService.updateCustomDevice(device); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java index 97803f98..debc3b2f 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java @@ -89,7 +89,7 @@ public class PlatformController { @GetMapping("/server_config") public JSONObject serverConfig() { JSONObject result = new JSONObject(); - result.put("deviceIp", sipConfig.getIp()); + result.put("deviceIp", sipConfig.getShowIp()); result.put("devicePort", sipConfig.getPort()); result.put("username", sipConfig.getId()); result.put("password", sipConfig.getPassword()); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java index 74e155b4..a587d5df 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java @@ -1,19 +1,17 @@ package com.genersoft.iot.vmp.vmanager.ps; -import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.media.event.hook.Hook; -import com.genersoft.iot.vmp.media.event.hook.HookType; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; -import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; 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.service.IMediaServerService; +import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -210,7 +208,7 @@ public class PsController { SendRtpItem sendRtpItem = SendRtpItem.getInstance(app, stream, ssrc, dstIp, dstPort, !isUdp, sendInfo.getSendLocalPort(), null); Boolean streamReady = mediaServerService.isStreamReady(mediaServer, app, stream); if (streamReady) { - mediaServerService.startSendRtp(mediaServer, null, sendRtpItem); + mediaServerService.startSendRtp(mediaServer, sendRtpItem); logger.info("[第三方PS服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItem); redisTemplate.opsForValue().set(key, sendInfo); }else { @@ -235,7 +233,7 @@ public class PsController { } catch (InterruptedException e) { throw new RuntimeException(e); } - mediaServerService.startSendRtp(mediaServer, null, sendRtpItem); + mediaServerService.startSendRtp(mediaServer, sendRtpItem); logger.info("[第三方PS服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItem); redisTemplate.opsForValue().set(key, finalSendInfo); hookSubscribe.removeSubscribe(hook); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java index e08f769a..7832e373 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java @@ -1,18 +1,17 @@ package com.genersoft.iot.vmp.vmanager.rtp; -import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.media.event.hook.Hook; -import com.genersoft.iot.vmp.media.event.hook.HookType; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; 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.service.IMediaServerService; +import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -31,9 +30,7 @@ import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -247,12 +244,12 @@ public class RtpController { Boolean streamReady = mediaServerService.isStreamReady(mediaServer, app, stream); if (streamReady) { if (sendRtpItemForVideo != null) { - mediaServerService.startSendRtp(mediaServer, null, sendRtpItemForVideo); + mediaServerService.startSendRtp(mediaServer, sendRtpItemForVideo); logger.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItemForVideo); redisTemplate.opsForValue().set(key, sendInfo); } if(sendRtpItemForAudio != null) { - mediaServerService.startSendRtp(mediaServer, null, sendRtpItemForAudio); + mediaServerService.startSendRtp(mediaServer, sendRtpItemForAudio); logger.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, sendRtpItemForAudio); redisTemplate.opsForValue().set(key, sendInfo); } @@ -279,12 +276,12 @@ public class RtpController { throw new RuntimeException(e); } if (sendRtpItemForVideo != null) { - mediaServerService.startSendRtp(mediaServer, null, sendRtpItemForVideo); + mediaServerService.startSendRtp(mediaServer, sendRtpItemForVideo); logger.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItemForVideo); redisTemplate.opsForValue().set(key, finalSendInfo); } if(sendRtpItemForAudio != null) { - mediaServerService.startSendRtp(mediaServer, null, sendRtpItemForAudio); + mediaServerService.startSendRtp(mediaServer, sendRtpItemForAudio); logger.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, sendRtpItemForAudio); redisTemplate.opsForValue().set(key, finalSendInfo); } diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java index 3457cf99..e0973b96 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java @@ -40,7 +40,7 @@ public class ApiController { result.put("Server",""); result.put("SIPSerial", sipConfig.getId()); result.put("SIPRealm", sipConfig.getDomain()); - result.put("SIPHost", sipConfig.getIp()); + result.put("SIPHost", sipConfig.getShowIp()); result.put("SIPPort", sipConfig.getPort()); result.put("ChannelCount","1000"); result.put("VersionType",""); diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index f2fd504a..31db6807 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/all-application.yml @@ -139,12 +139,14 @@ media: id: # [必须修改] zlm服务器的内网IP ip: 192.168.0.100 + # [可选] 有公网IP就配置公网IP, 不可用域名 + wan_ip: # [可选] 返回流地址时的ip,置空使用 media.ip stream-ip: # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip sdp-ip: - # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip - hook-ip: + # [可选] zlm服务器访问WVP所使用的IP, 默认使用127.0.0.1,zlm和wvp没有部署在同一台服务器时必须配置 + hook-ip: 172.19.128.50 # [必须修改] zlm服务器的http.port http-port: 80 # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 @@ -247,6 +249,10 @@ user-settings: allowed-origins: - http://localhost:8008 - http://192.168.1.3:8008 + # 国标设备离线后的上线策略, + # 0: 国标标准实现,设备离线后不回复心跳,直到设备重新注册上线, + # 1(默认): 对于离线设备,收到心跳就把设备设置为上线,并更新注册时间为上次这次心跳的时间。防止过期时间判断异常 + gb-device-online: 0 # 关闭在线文档(生产环境建议关闭) springdoc: diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e61ac0b3..93c8af3b 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -58,11 +58,6 @@ server: # 作为28181服务器的配置 sip: - # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, - # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 - # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 - # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 - ip: 172.19.128.50 # [可选] 28181服务监听的端口 port: 8116 # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) @@ -82,18 +77,14 @@ media: id: zlmediakit-local # [必须修改] zlm服务器的内网IP ip: 172.19.128.50 + # [可选] 有公网IP就配置公网IP, 不可用域名 + wan_ip: # [必须修改] zlm服务器的http.port http-port: 9092 - # [可选] 返回流地址时的ip,置空使用 media.ip - stream-ip: 172.19.128.50 - # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip - sdp-ip: 172.19.128.50 - # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip + # [可选] zlm服务器访问WVP所使用的IP, 默认使用127.0.0.1,zlm和wvp没有部署在同一台服务器时必须配置 hook-ip: 172.19.128.50 - # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 - http-ssl-port: 1443 - # [可选] zlm服务器的hook.admin_params=secret - secret: 10000 + # [必选选] zlm服务器的hook.admin_params=secret + secret: TWSYFgYJOQWB4ftgeYut8DW4wbs7pQnj # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 rtp: # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 @@ -102,14 +93,16 @@ media: port-range: 50000,50300 # 端口范围 # [可选] 国标级联在此范围内选择端口发送媒体流, send-port-range: 50000,50300 # 端口范围 - # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 - record-assist-port: 18081 # [根据业务需求配置] user-settings: # 点播/录像回放 等待超时时间,单位:毫秒 play-timeout: 180000 # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true auto-apply-play: true - # 设备/通道状态变化时发送消息 - device-status-notify: true + # 推流直播是否录制 + record-push-live: true + # 国标是否录制 + record-sip: true + # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + stream-on-demand: true diff --git a/web_src/src/components/dialog/deviceEdit.vue b/web_src/src/components/dialog/deviceEdit.vue index d8e0b43b..d8092fae 100755 --- a/web_src/src/components/dialog/deviceEdit.vue +++ b/web_src/src/components/dialog/deviceEdit.vue @@ -128,6 +128,9 @@ export default { this.form.subscribeCycleForCatalog = this.form.subscribeCycleForCatalog||0 this.form.subscribeCycleForMobilePosition = this.form.subscribeCycleForMobilePosition||0 this.form.mobilePositionSubmissionInterval = this.form.mobilePositionSubmissionInterval||0 + if (this.form.mobilePositionSubmissionInterval === 0) { + this.form.mobilePositionSubmissionInterval = 5 + } this.$axios({ method: 'post', url:`/api/device/query/device/${this.isEdit?'update':'add'}/`,