Commit 652489b47ef582b3f492039ab7fd58c558c1ba36

Authored by ‘sxh’
2 parents 05cc0d14 319cdd21

Merge remote-tracking branch 'origin/wvp-28181-2.0' into wvp-28181-2.0

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
Showing 44 changed files with 644 additions and 269 deletions
README.md
@@ -27,7 +27,7 @@ wvp使用文档 [https://doc.wvp-pro.cn](https://doc.wvp-pro.cn) @@ -27,7 +27,7 @@ wvp使用文档 [https://doc.wvp-pro.cn](https://doc.wvp-pro.cn)
27 ZLM使用文档 [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit) 27 ZLM使用文档 [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
28 > wvp文档由gitee提供服务,如果遇到打不开请多刷新几次。 28 > wvp文档由gitee提供服务,如果遇到打不开请多刷新几次。
29 29
30 -# 社群地址 30 +# 付费社群
31 [![社群](doc/_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) 31 [![社群](doc/_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
32 > 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 32 > 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。
33 33
@@ -105,6 +105,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git @@ -105,6 +105,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
105 - [X] 支持打包可执行jar和war 105 - [X] 支持打包可执行jar和war
106 - [X] 支持跨域请求,支持前后端分离部署 106 - [X] 支持跨域请求,支持前后端分离部署
107 - [X] 支持Mysql,Postgresql,金仓等数据库 107 - [X] 支持Mysql,Postgresql,金仓等数据库
  108 +- [X] 支持Onvif(目前在onvif分支,需要安装onvif服务,服务请在知识星球获取)
108 109
109 # 授权协议 110 # 授权协议
110 本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 111 本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议
doc/README.md
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 - 完全开源,且使用MIT许可协议。保留版权的情况下可以用于商业项目。 14 - 完全开源,且使用MIT许可协议。保留版权的情况下可以用于商业项目。
15 - 支持多流媒体节点负载均衡。 15 - 支持多流媒体节点负载均衡。
16 16
17 -# 社群 17 +# 付费社群
18 [![社群](_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) 18 [![社群](_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
19 > 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 19 > 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。
20 20
@@ -62,16 +62,16 @@ @@ -62,16 +62,16 @@
62 - [X] 注册 62 - [X] 注册
63 - [X] 注销 63 - [X] 注销
64 - [X] 实时视音频点播 64 - [X] 实时视音频点播
65 -- [ ] 设备控制  
66 - - [ ] 云台控制 65 +- [X] 设备控制
  66 + - [X] 云台控制
67 - [ ] 远程启动 67 - [ ] 远程启动
68 - - [ ] 录像控制  
69 - - [ ] 报警布防/撤防  
70 - - [ ] 报警复位  
71 - - [ ] 强制关键帧  
72 - - [ ] 拉框放大  
73 - - [ ] 拉框缩小  
74 - - [ ] 看守位控制 68 + - [X] 录像控制
  69 + - [X] 报警布防/撤防
  70 + - [X] 报警复位
  71 + - [X] 强制关键帧
  72 + - [X] 拉框放大
  73 + - [X] 拉框缩小
  74 + - [X] 看守位控制
75 - [ ] 设备配置 75 - [ ] 设备配置
76 - [ ] 报警事件通知和分发 76 - [ ] 报警事件通知和分发
77 - [X] 设备目录订阅 77 - [X] 设备目录订阅
@@ -79,7 +79,7 @@ @@ -79,7 +79,7 @@
79 - [X] 设备目录查询 79 - [X] 设备目录查询
80 - [X] 设备状态查询 80 - [X] 设备状态查询
81 - [ ] 设备配置查询 81 - [ ] 设备配置查询
82 - - [ ] 设备预置位查询 82 + - [X] 设备预置位查询
83 - [X] 状态信息报送 83 - [X] 状态信息报送
84 - [X] 设备视音频文件检索 84 - [X] 设备视音频文件检索
85 - [X] 历史视音频的回放 85 - [X] 历史视音频的回放
@@ -87,7 +87,7 @@ @@ -87,7 +87,7 @@
87 - [x] 暂停 87 - [x] 暂停
88 - [x] 进/退 88 - [x] 进/退
89 - [x] 停止 89 - [x] 停止
90 -- [ ] 视音频文件下载 90 +- [X] 视音频文件下载
91 - [ ] ~~校时~~ 91 - [ ] ~~校时~~
92 - [X] 订阅和通知 92 - [X] 订阅和通知
93 - [X] 事件订阅 93 - [X] 事件订阅
doc/_media/1372762149.jpg 0 → 100644

126 KB

doc/_media/903207146.jpg 0 → 100644

131 KB

@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 11
12 <groupId>com.genersoft</groupId> 12 <groupId>com.genersoft</groupId>
13 <artifactId>wvp-pro</artifactId> 13 <artifactId>wvp-pro</artifactId>
14 - <version>2.6.8</version> 14 + <version>2.6.9</version>
15 <name>web video platform</name> 15 <name>web video platform</name>
16 <description>国标28181视频平台</description> 16 <description>国标28181视频平台</description>
17 <packaging>${project.packaging}</packaging> 17 <packaging>${project.packaging}</packaging>
sql/2.6.8升级2.6.9.sql
@@ -180,10 +180,6 @@ alter table device_mobile_position @@ -180,10 +180,6 @@ alter table device_mobile_position
180 change createTime create_time varchar(50) null; 180 change createTime create_time varchar(50) null;
181 181
182 alter table gb_stream 182 alter table gb_stream
183 - add constraint gb_stream_pk  
184 - primary key (gbStreamId);  
185 -  
186 -alter table gb_stream  
187 change gbStreamId gb_stream_id int auto_increment; 183 change gbStreamId gb_stream_id int auto_increment;
188 184
189 alter table gb_stream 185 alter table gb_stream
src/main/java/com/genersoft/iot/vmp/common/GeneralCallback.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common;
  2 +
  3 +public interface GeneralCallback<T>{
  4 + void run(int code, String msg, T data);
  5 +}
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -107,6 +107,11 @@ public class VideoManagerConstants { @@ -107,6 +107,11 @@ public class VideoManagerConstants {
107 public static final String VM_MSG_STREAM_PUSH_RESPONSE = "VM_MSG_STREAM_PUSH_RESPONSE"; 107 public static final String VM_MSG_STREAM_PUSH_RESPONSE = "VM_MSG_STREAM_PUSH_RESPONSE";
108 108
109 /** 109 /**
  110 + * redis 通知平台关闭推流
  111 + */
  112 + public static final String VM_MSG_STREAM_PUSH_CLOSE = "VM_MSG_STREAM_PUSH_CLOSE";
  113 +
  114 + /**
110 * redis 消息请求所有的在线通道 115 * redis 消息请求所有的在线通道
111 */ 116 */
112 public static final String VM_MSG_GET_ALL_ONLINE_REQUESTED = "VM_MSG_GET_ALL_ONLINE_REQUESTED"; 117 public static final String VM_MSG_GET_ALL_ONLINE_REQUESTED = "VM_MSG_GET_ALL_ONLINE_REQUESTED";
src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java
@@ -43,6 +43,9 @@ public class RedisMsgListenConfig { @@ -43,6 +43,9 @@ public class RedisMsgListenConfig {
43 @Autowired 43 @Autowired
44 private RedisPushStreamResponseListener redisPushStreamResponseListener; 44 private RedisPushStreamResponseListener redisPushStreamResponseListener;
45 45
  46 + @Autowired
  47 + private RedisCloseStreamMsgListener redisCloseStreamMsgListener;
  48 +
46 49
47 /** 50 /**
48 * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 51 * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
@@ -63,6 +66,7 @@ public class RedisMsgListenConfig { @@ -63,6 +66,7 @@ public class RedisMsgListenConfig {
63 container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE)); 66 container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
64 container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); 67 container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
65 container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); 68 container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));
  69 + container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE));
66 return container; 70 return container;
67 } 71 }
68 } 72 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import javax.sdp.SessionDescription;
  4 +
  5 +/**
  6 + * 28181 的SDP解析器
  7 + */
  8 +public class Gb28181Sdp {
  9 + private SessionDescription baseSdb;
  10 + private String ssrc;
  11 +
  12 + private String mediaDescription;
  13 +
  14 + public static Gb28181Sdp getInstance(SessionDescription baseSdb, String ssrc, String mediaDescription) {
  15 + Gb28181Sdp gb28181Sdp = new Gb28181Sdp();
  16 + gb28181Sdp.setBaseSdb(baseSdb);
  17 + gb28181Sdp.setSsrc(ssrc);
  18 + gb28181Sdp.setMediaDescription(mediaDescription);
  19 + return gb28181Sdp;
  20 + }
  21 +
  22 +
  23 + public SessionDescription getBaseSdb() {
  24 + return baseSdb;
  25 + }
  26 +
  27 + public void setBaseSdb(SessionDescription baseSdb) {
  28 + this.baseSdb = baseSdb;
  29 + }
  30 +
  31 + public String getSsrc() {
  32 + return ssrc;
  33 + }
  34 +
  35 + public void setSsrc(String ssrc) {
  36 + this.ssrc = ssrc;
  37 + }
  38 +
  39 + public String getMediaDescription() {
  40 + return mediaDescription;
  41 + }
  42 +
  43 + public void setMediaDescription(String mediaDescription) {
  44 + this.mediaDescription = mediaDescription;
  45 + }
  46 +}
src/main/java/com/genersoft/iot/vmp/gb28181/session/SSRCFactory.java
1 package com.genersoft.iot.vmp.gb28181.session; 1 package com.genersoft.iot.vmp.gb28181.session;
2 2
3 import com.genersoft.iot.vmp.conf.SipConfig; 3 import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.data.redis.core.StringRedisTemplate; 6 import org.springframework.data.redis.core.StringRedisTemplate;
6 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
@@ -31,10 +32,13 @@ public class SSRCFactory { @@ -31,10 +32,13 @@ public class SSRCFactory {
31 @Autowired 32 @Autowired
32 private SipConfig sipConfig; 33 private SipConfig sipConfig;
33 34
  35 + @Autowired
  36 + private UserSetting userSetting;
  37 +
34 38
35 public void initMediaServerSSRC(String mediaServerId, Set<String> usedSet) { 39 public void initMediaServerSSRC(String mediaServerId, Set<String> usedSet) {
36 String ssrcPrefix = sipConfig.getDomain().substring(3, 8); 40 String ssrcPrefix = sipConfig.getDomain().substring(3, 8);
37 - String redisKey = SSRC_INFO_KEY + mediaServerId; 41 + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
38 List<String> ssrcList = new ArrayList<>(); 42 List<String> ssrcList = new ArrayList<>();
39 for (int i = 1; i < MAX_STREAM_COUNT; i++) { 43 for (int i = 1; i < MAX_STREAM_COUNT; i++) {
40 String ssrc = String.format("%s%04d", ssrcPrefix, i); 44 String ssrc = String.format("%s%04d", ssrcPrefix, i);
@@ -77,7 +81,7 @@ public class SSRCFactory { @@ -77,7 +81,7 @@ public class SSRCFactory {
77 return; 81 return;
78 } 82 }
79 String sn = ssrc.substring(1); 83 String sn = ssrc.substring(1);
80 - String redisKey = SSRC_INFO_KEY + mediaServerId; 84 + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
81 redisTemplate.opsForSet().add(redisKey, sn); 85 redisTemplate.opsForSet().add(redisKey, sn);
82 } 86 }
83 87
@@ -86,7 +90,7 @@ public class SSRCFactory { @@ -86,7 +90,7 @@ public class SSRCFactory {
86 */ 90 */
87 private String getSN(String mediaServerId) { 91 private String getSN(String mediaServerId) {
88 String sn = null; 92 String sn = null;
89 - String redisKey = SSRC_INFO_KEY + mediaServerId; 93 + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
90 Long size = redisTemplate.opsForSet().size(redisKey); 94 Long size = redisTemplate.opsForSet().size(redisKey);
91 if (size == null || size == 0) { 95 if (size == null || size == 0) {
92 throw new RuntimeException("ssrc已经用完"); 96 throw new RuntimeException("ssrc已经用完");
@@ -113,20 +117,8 @@ public class SSRCFactory { @@ -113,20 +117,8 @@ public class SSRCFactory {
113 * @param mediaServerId 流媒体服务ID 117 * @param mediaServerId 流媒体服务ID
114 */ 118 */
115 public boolean hasMediaServerSSRC(String mediaServerId) { 119 public boolean hasMediaServerSSRC(String mediaServerId) {
116 - String redisKey = SSRC_INFO_KEY + mediaServerId; 120 + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
117 return redisTemplate.opsForSet().members(redisKey) != null; 121 return redisTemplate.opsForSet().members(redisKey) != null;
118 } 122 }
119 123
120 - /**  
121 - * 查询ssrc是否可用  
122 - *  
123 - * @param mediaServerId  
124 - * @param ssrc  
125 - * @return  
126 - */  
127 - public boolean checkSsrc(String mediaServerId, String ssrc) {  
128 - String sn = ssrc.substring(1);  
129 - String redisKey = SSRC_INFO_KEY + mediaServerId;  
130 - return redisTemplate.opsForSet().isMember(redisKey, sn) != null;  
131 - }  
132 } 124 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
@@ -39,11 +39,13 @@ public class DeferredResultHolder { @@ -39,11 +39,13 @@ public class DeferredResultHolder {
39 39
40 public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD"; 40 public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD";
41 41
  42 + public static final String CALLBACK_CMD_PROXY = "CALLBACK_PROXY";
  43 +
42 public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; 44 public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
43 45
44 public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL"; 46 public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL";
45 47
46 - public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; 48 + public static final String CALLBACK_CMD_MOBILE_POSITION = "CALLBACK_CMD_MOBILE_POSITION";
47 49
48 public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; 50 public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
49 51
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -54,8 +54,8 @@ public class SIPRequestHeaderPlarformProvider { @@ -54,8 +54,8 @@ public class SIPRequestHeaderPlarformProvider {
54 parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); 54 parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
55 //via 55 //via
56 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); 56 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
57 - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),  
58 - parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag()); 57 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(),
  58 + Integer.parseInt(parentPlatform.getDevicePort()), parentPlatform.getTransport(), SipUtils.getNewViaTag());
59 viaHeader.setRPort(); 59 viaHeader.setRPort();
60 viaHeaders.add(viaHeader); 60 viaHeaders.add(viaHeader);
61 //from 61 //from
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -472,7 +472,7 @@ public class SIPCommander implements ISIPCommander { @@ -472,7 +472,7 @@ public class SIPCommander implements ISIPCommander {
472 } 472 }
473 subscribe.removeSubscribe(hookSubscribe); 473 subscribe.removeSubscribe(hookSubscribe);
474 }); 474 });
475 - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc()); 475 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
476 476
477 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { 477 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
478 ResponseEvent responseEvent = (ResponseEvent) event.event; 478 ResponseEvent responseEvent = (ResponseEvent) event.event;
@@ -588,17 +588,13 @@ public class SIPCommander implements ISIPCommander { @@ -588,17 +588,13 @@ public class SIPCommander implements ISIPCommander {
588 }); 588 });
589 }); 589 });
590 590
591 - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc()); 591 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
592 592
593 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { 593 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
594 ResponseEvent responseEvent = (ResponseEvent) event.event; 594 ResponseEvent responseEvent = (ResponseEvent) event.event;
595 SIPResponse response = (SIPResponse) responseEvent.getResponse(); 595 SIPResponse response = (SIPResponse) responseEvent.getResponse();
596 String contentString =new String(response.getRawContent()); 596 String contentString =new String(response.getRawContent());
597 - int ssrcIndex = contentString.indexOf("y=");  
598 - String ssrc=ssrcInfo.getSsrc();  
599 - if (ssrcIndex >= 0) {  
600 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
601 - } 597 + String ssrc = SipUtils.getSsrcFromSdp(contentString);
602 streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); 598 streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD);
603 okEvent.response(event); 599 okEvent.response(event);
604 }); 600 });
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -241,18 +241,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -241,18 +241,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
241 // 解析sdp消息, 使用jainsip 自带的sdp解析方式 241 // 解析sdp消息, 使用jainsip 自带的sdp解析方式
242 String contentString = new String(request.getRawContent()); 242 String contentString = new String(request.getRawContent());
243 243
244 - // jainSip不支持y=字段, 移除以解析。  
245 - // 检查是否有y字段  
246 - int ssrcIndex = contentString.indexOf("y=");  
247 -  
248 - SessionDescription sdp;  
249 - if (ssrcIndex >= 0) {  
250 - //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段  
251 - String substring = contentString.substring(0, ssrcIndex);  
252 - sdp = SdpFactory.getInstance().createSessionDescription(substring);  
253 - } else {  
254 - sdp = SdpFactory.getInstance().createSessionDescription(contentString);  
255 - } 244 + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
  245 + SessionDescription sdp = gb28181Sdp.getBaseSdb();
256 String sessionName = sdp.getSessionName().getValue(); 246 String sessionName = sdp.getSessionName().getValue();
257 247
258 Long startTime = null; 248 Long startTime = null;
@@ -340,11 +330,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -340,11 +330,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
340 } 330 }
341 331
342 String ssrc; 332 String ssrc;
343 - if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) { 333 + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) {
344 // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 334 // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
345 ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); 335 ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
346 }else { 336 }else {
347 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); 337 + ssrc = gb28181Sdp.getSsrc();
348 } 338 }
349 String streamTypeStr = null; 339 String streamTypeStr = null;
350 if (mediaTransmissionTCP) { 340 if (mediaTransmissionTCP) {
@@ -513,11 +503,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -513,11 +503,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
513 } else if (gbStream != null) { 503 } else if (gbStream != null) {
514 504
515 String ssrc; 505 String ssrc;
516 - if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) { 506 + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) {
517 // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 507 // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
518 ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); 508 ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
519 }else { 509 }else {
520 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); 510 + ssrc = gb28181Sdp.getSsrc();
521 } 511 }
522 512
523 if("push".equals(gbStream.getStreamType())) { 513 if("push".equals(gbStream.getStreamType())) {
@@ -891,20 +881,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -891,20 +881,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
891 } 881 }
892 String contentString = new String(request.getRawContent()); 882 String contentString = new String(request.getRawContent());
893 // jainSip不支持y=字段, 移除移除以解析。 883 // jainSip不支持y=字段, 移除移除以解析。
894 - String substring = contentString;  
895 String ssrc = "0000000404"; 884 String ssrc = "0000000404";
896 - int ssrcIndex = contentString.indexOf("y=");  
897 - if (ssrcIndex > 0) {  
898 - substring = contentString.substring(0, ssrcIndex);  
899 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
900 - }  
901 - ssrcIndex = substring.indexOf("f=");  
902 - if (ssrcIndex > 0) {  
903 - substring = contentString.substring(0, ssrcIndex);  
904 - }  
905 - SessionDescription sdp = null; 885 +
906 try { 886 try {
907 - sdp = SdpFactory.getInstance().createSessionDescription(substring); 887 + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
  888 + SessionDescription sdp = gb28181Sdp.getBaseSdb();
908 // 获取支持的格式 889 // 获取支持的格式
909 Vector mediaDescriptions = sdp.getMediaDescriptions(true); 890 Vector mediaDescriptions = sdp.getMediaDescriptions(true);
910 // 查看是否支持PS 负载96 891 // 查看是否支持PS 负载96
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java
@@ -175,6 +175,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -175,6 +175,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
175 } 175 }
176 }else { 176 }else {
177 addChannelMap.put(channel.getChannelId(), channel); 177 addChannelMap.put(channel.getChannelId(), channel);
  178 + if (userSetting.getDeviceStatusNotify()) {
  179 + // 发送redis消息
  180 + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true);
  181 + }
  182 +
178 if (addChannelMap.keySet().size() > 300) { 183 if (addChannelMap.keySet().size() > 300) {
179 executeSaveForAdd(); 184 executeSaveForAdd();
180 } 185 }
@@ -185,6 +190,10 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -185,6 +190,10 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
185 // 删除 190 // 删除
186 logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); 191 logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
187 deleteChannelList.add(channel); 192 deleteChannelList.add(channel);
  193 + if (userSetting.getDeviceStatusNotify()) {
  194 + // 发送redis消息
  195 + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), false);
  196 + }
188 if (deleteChannelList.size() > 300) { 197 if (deleteChannelList.size() > 300) {
189 executeSaveForDelete(); 198 executeSaveForDelete();
190 } 199 }
@@ -205,6 +214,10 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @@ -205,6 +214,10 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
205 if (addChannelMap.keySet().size() > 300) { 214 if (addChannelMap.keySet().size() > 300) {
206 executeSaveForAdd(); 215 executeSaveForAdd();
207 } 216 }
  217 + if (userSetting.getDeviceStatusNotify()) {
  218 + // 发送redis消息
  219 + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true);
  220 + }
208 } 221 }
209 break; 222 break;
210 default: 223 default:
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
@@ -192,7 +192,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -192,7 +192,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
192 mobilePosition.setDeviceId(device.getDeviceId()); 192 mobilePosition.setDeviceId(device.getDeviceId());
193 mobilePosition.setChannelId(channelId); 193 mobilePosition.setChannelId(channelId);
194 String time = XmlUtil.getText(rootElement, "Time"); 194 String time = XmlUtil.getText(rootElement, "Time");
195 - mobilePosition.setTime(time); 195 + if (ObjectUtils.isEmpty(time)){
  196 + mobilePosition.setTime(DateUtil.getNow());
  197 + }else {
  198 + mobilePosition.setTime(SipUtils.parseTime(time));
  199 + }
  200 +
196 mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); 201 mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
197 mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); 202 mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
198 if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { 203 if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) {
@@ -237,7 +242,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -237,7 +242,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
237 242
238 // 发送redis消息。 通知位置信息的变化 243 // 发送redis消息。 通知位置信息的变化
239 JSONObject jsonObject = new JSONObject(); 244 JSONObject jsonObject = new JSONObject();
240 - jsonObject.put("time", time); 245 + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
241 jsonObject.put("serial", deviceId); 246 jsonObject.put("serial", deviceId);
242 jsonObject.put("code", channelId); 247 jsonObject.put("code", channelId);
243 jsonObject.put("longitude", mobilePosition.getLongitude()); 248 jsonObject.put("longitude", mobilePosition.getLongitude());
@@ -339,7 +344,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @@ -339,7 +344,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
339 storager.updateChannelPosition(deviceChannel); 344 storager.updateChannelPosition(deviceChannel);
340 // 发送redis消息。 通知位置信息的变化 345 // 发送redis消息。 通知位置信息的变化
341 JSONObject jsonObject = new JSONObject(); 346 JSONObject jsonObject = new JSONObject();
342 - jsonObject.put("time", mobilePosition.getTime()); 347 + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
343 jsonObject.put("serial", deviceChannel.getDeviceId()); 348 jsonObject.put("serial", deviceChannel.getDeviceId());
344 jsonObject.put("code", deviceChannel.getChannelId()); 349 jsonObject.put("code", deviceChannel.getChannelId());
345 jsonObject.put("longitude", mobilePosition.getLongitude()); 350 jsonObject.put("longitude", mobilePosition.getLongitude());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
@@ -164,7 +164,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @@ -164,7 +164,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
164 164
165 // 发送redis消息。 通知位置信息的变化 165 // 发送redis消息。 通知位置信息的变化
166 JSONObject jsonObject = new JSONObject(); 166 JSONObject jsonObject = new JSONObject();
167 - jsonObject.put("time", mobilePosition.getTime()); 167 + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
168 jsonObject.put("serial", deviceChannel.getDeviceId()); 168 jsonObject.put("serial", deviceChannel.getDeviceId());
169 jsonObject.put("code", deviceChannel.getChannelId()); 169 jsonObject.put("code", deviceChannel.getChannelId());
170 jsonObject.put("longitude", mobilePosition.getLongitude()); 170 jsonObject.put("longitude", mobilePosition.getLongitude());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
@@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; 8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
9 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; 9 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  10 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
10 import com.genersoft.iot.vmp.service.IDeviceChannelService; 11 import com.genersoft.iot.vmp.service.IDeviceChannelService;
11 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 12 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
12 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 13 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -95,7 +96,12 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen @@ -95,7 +96,12 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
95 } 96 }
96 mobilePosition.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); 97 mobilePosition.setDeviceId(sipMsgInfo.getDevice().getDeviceId());
97 mobilePosition.setChannelId(getText(rootElementAfterCharset, "DeviceID")); 98 mobilePosition.setChannelId(getText(rootElementAfterCharset, "DeviceID"));
98 - mobilePosition.setTime(getText(rootElementAfterCharset, "Time")); 99 + String time = getText(rootElementAfterCharset, "Time");
  100 + if (ObjectUtils.isEmpty(time)){
  101 + mobilePosition.setTime(DateUtil.getNow());
  102 + }else {
  103 + mobilePosition.setTime(SipUtils.parseTime(time));
  104 + }
99 mobilePosition.setLongitude(Double.parseDouble(getText(rootElementAfterCharset, "Longitude"))); 105 mobilePosition.setLongitude(Double.parseDouble(getText(rootElementAfterCharset, "Longitude")));
100 mobilePosition.setLatitude(Double.parseDouble(getText(rootElementAfterCharset, "Latitude"))); 106 mobilePosition.setLatitude(Double.parseDouble(getText(rootElementAfterCharset, "Latitude")));
101 if (NumericUtil.isDouble(getText(rootElementAfterCharset, "Speed"))) { 107 if (NumericUtil.isDouble(getText(rootElementAfterCharset, "Speed"))) {
@@ -138,7 +144,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen @@ -138,7 +144,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
138 144
139 // 发送redis消息。 通知位置信息的变化 145 // 发送redis消息。 通知位置信息的变化
140 JSONObject jsonObject = new JSONObject(); 146 JSONObject jsonObject = new JSONObject();
141 - jsonObject.put("time", mobilePosition.getTime()); 147 + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
142 jsonObject.put("serial", deviceChannel.getDeviceId()); 148 jsonObject.put("serial", deviceChannel.getDeviceId());
143 jsonObject.put("code", deviceChannel.getChannelId()); 149 jsonObject.put("code", deviceChannel.getChannelId());
144 jsonObject.put("longitude", mobilePosition.getLongitude()); 150 jsonObject.put("longitude", mobilePosition.getLongitude());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
@@ -2,17 +2,21 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon @@ -2,17 +2,21 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon
2 2
3 import com.alibaba.fastjson2.JSONObject; 3 import com.alibaba.fastjson2.JSONObject;
4 import com.genersoft.iot.vmp.conf.UserSetting; 4 import com.genersoft.iot.vmp.conf.UserSetting;
5 -import com.genersoft.iot.vmp.gb28181.bean.*; 5 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  6 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  7 +import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
  8 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  9 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
6 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
7 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
9 -import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;  
10 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; 14 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  15 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
11 import com.genersoft.iot.vmp.service.IDeviceChannelService; 16 import com.genersoft.iot.vmp.service.IDeviceChannelService;
12 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 17 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
13 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 18 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
14 import com.genersoft.iot.vmp.utils.DateUtil; 19 import com.genersoft.iot.vmp.utils.DateUtil;
15 -import com.genersoft.iot.vmp.utils.GpsUtil;  
16 import gov.nist.javax.sip.message.SIPRequest; 20 import gov.nist.javax.sip.message.SIPRequest;
17 import org.dom4j.DocumentException; 21 import org.dom4j.DocumentException;
18 import org.dom4j.Element; 22 import org.dom4j.Element;
@@ -56,6 +60,9 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar @@ -56,6 +60,9 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
56 @Autowired 60 @Autowired
57 private IDeviceChannelService deviceChannelService; 61 private IDeviceChannelService deviceChannelService;
58 62
  63 + @Autowired
  64 + private DeferredResultHolder resultHolder;
  65 +
59 @Override 66 @Override
60 public void afterPropertiesSet() throws Exception { 67 public void afterPropertiesSet() throws Exception {
61 responseMessageHandler.addHandler(cmdType, this); 68 responseMessageHandler.addHandler(cmdType, this);
@@ -83,7 +90,13 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar @@ -83,7 +90,13 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
83 } 90 }
84 mobilePosition.setDeviceId(device.getDeviceId()); 91 mobilePosition.setDeviceId(device.getDeviceId());
85 mobilePosition.setChannelId(getText(rootElement, "DeviceID")); 92 mobilePosition.setChannelId(getText(rootElement, "DeviceID"));
86 - mobilePosition.setTime(getText(rootElement, "Time")); 93 + //兼容ISO 8601格式时间
  94 + String time = getText(rootElement, "Time");
  95 + if (ObjectUtils.isEmpty(time)){
  96 + mobilePosition.setTime(DateUtil.getNow());
  97 + }else {
  98 + mobilePosition.setTime(SipUtils.parseTime(time));
  99 + }
87 mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude"))); 100 mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
88 mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude"))); 101 mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
89 if (NumericUtil.isDouble(getText(rootElement, "Speed"))) { 102 if (NumericUtil.isDouble(getText(rootElement, "Speed"))) {
@@ -121,11 +134,18 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar @@ -121,11 +134,18 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
121 if (userSetting.getSavePositionHistory()) { 134 if (userSetting.getSavePositionHistory()) {
122 storager.insertMobilePosition(mobilePosition); 135 storager.insertMobilePosition(mobilePosition);
123 } 136 }
  137 +
124 storager.updateChannelPosition(deviceChannel); 138 storager.updateChannelPosition(deviceChannel);
125 139
  140 + String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + device.getDeviceId();
  141 + RequestMessage msg = new RequestMessage();
  142 + msg.setKey(key);
  143 + msg.setData(mobilePosition);
  144 + resultHolder.invokeAllResult(msg);
  145 +
126 // 发送redis消息。 通知位置信息的变化 146 // 发送redis消息。 通知位置信息的变化
127 JSONObject jsonObject = new JSONObject(); 147 JSONObject jsonObject = new JSONObject();
128 - jsonObject.put("time", mobilePosition.getTime()); 148 + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
129 jsonObject.put("serial", deviceChannel.getDeviceId()); 149 jsonObject.put("serial", deviceChannel.getDeviceId());
130 jsonObject.put("code", deviceChannel.getChannelId()); 150 jsonObject.put("code", deviceChannel.getChannelId());
131 jsonObject.put("longitude", mobilePosition.getLongitude()); 151 jsonObject.put("longitude", mobilePosition.getLongitude());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
1 package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
2 2
3 import com.genersoft.iot.vmp.gb28181.SipLayer; 3 import com.genersoft.iot.vmp.gb28181.SipLayer;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp;
4 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; 5 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
5 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; 6 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
6 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
7 import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; 8 import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
  9 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
8 import gov.nist.javax.sip.ResponseEventExt; 10 import gov.nist.javax.sip.ResponseEventExt;
9 import gov.nist.javax.sip.message.SIPResponse; 11 import gov.nist.javax.sip.message.SIPResponse;
10 import org.slf4j.Logger; 12 import org.slf4j.Logger;
@@ -12,7 +14,6 @@ import org.slf4j.LoggerFactory; @@ -12,7 +14,6 @@ import org.slf4j.LoggerFactory;
12 import org.springframework.beans.factory.annotation.Autowired; 14 import org.springframework.beans.factory.annotation.Autowired;
13 import org.springframework.stereotype.Component; 15 import org.springframework.stereotype.Component;
14 16
15 -import javax.sdp.SdpFactory;  
16 import javax.sdp.SdpParseException; 17 import javax.sdp.SdpParseException;
17 import javax.sdp.SessionDescription; 18 import javax.sdp.SessionDescription;
18 import javax.sip.InvalidArgumentException; 19 import javax.sip.InvalidArgumentException;
@@ -79,18 +80,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { @@ -79,18 +80,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
79 ResponseEventExt event = (ResponseEventExt)evt; 80 ResponseEventExt event = (ResponseEventExt)evt;
80 81
81 String contentString = new String(response.getRawContent()); 82 String contentString = new String(response.getRawContent());
82 - // jainSip不支持y=字段, 移除以解析。  
83 - int ssrcIndex = contentString.indexOf("y=");  
84 - // 检查是否有y字段  
85 - SessionDescription sdp;  
86 - if (ssrcIndex >= 0) {  
87 - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段  
88 - String substring = contentString.substring(0, contentString.indexOf("y="));  
89 - sdp = SdpFactory.getInstance().createSessionDescription(substring);  
90 - } else {  
91 - sdp = SdpFactory.getInstance().createSessionDescription(contentString);  
92 - }  
93 - 83 + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
  84 + SessionDescription sdp = gb28181Sdp.getBaseSdb();
94 SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort()); 85 SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
95 Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response); 86 Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);
96 87
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
1 package com.genersoft.iot.vmp.gb28181.utils; 1 package com.genersoft.iot.vmp.gb28181.utils;
2 2
3 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; 3 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp;
4 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; 5 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
  6 +import com.genersoft.iot.vmp.utils.DateUtil;
5 import com.genersoft.iot.vmp.utils.GitUtil; 7 import com.genersoft.iot.vmp.utils.GitUtil;
6 import gov.nist.javax.sip.address.AddressImpl; 8 import gov.nist.javax.sip.address.AddressImpl;
7 import gov.nist.javax.sip.address.SipUri; 9 import gov.nist.javax.sip.address.SipUri;
8 import gov.nist.javax.sip.header.Subject; 10 import gov.nist.javax.sip.header.Subject;
9 import gov.nist.javax.sip.message.SIPRequest; 11 import gov.nist.javax.sip.message.SIPRequest;
  12 +import org.apache.commons.lang3.RandomStringUtils;
  13 +import org.slf4j.Logger;
  14 +import org.slf4j.LoggerFactory;
10 import org.springframework.util.ObjectUtils; 15 import org.springframework.util.ObjectUtils;
11 16
  17 +import javax.sdp.SdpFactory;
  18 +import javax.sdp.SdpParseException;
  19 +import javax.sdp.SessionDescription;
12 import javax.sip.PeerUnavailableException; 20 import javax.sip.PeerUnavailableException;
13 import javax.sip.SipFactory; 21 import javax.sip.SipFactory;
14 import javax.sip.header.FromHeader; 22 import javax.sip.header.FromHeader;
@@ -16,6 +24,8 @@ import javax.sip.header.Header; @@ -16,6 +24,8 @@ import javax.sip.header.Header;
16 import javax.sip.header.UserAgentHeader; 24 import javax.sip.header.UserAgentHeader;
17 import javax.sip.message.Request; 25 import javax.sip.message.Request;
18 import java.text.ParseException; 26 import java.text.ParseException;
  27 +import java.time.LocalDateTime;
  28 +import java.time.format.DateTimeParseException;
19 import java.util.ArrayList; 29 import java.util.ArrayList;
20 import java.util.List; 30 import java.util.List;
21 import java.util.UUID; 31 import java.util.UUID;
@@ -28,6 +38,8 @@ import java.util.UUID; @@ -28,6 +38,8 @@ import java.util.UUID;
28 */ 38 */
29 public class SipUtils { 39 public class SipUtils {
30 40
  41 + private final static Logger logger = LoggerFactory.getLogger(SipUtils.class);
  42 +
31 public static String getUserIdFromFromHeader(Request request) { 43 public static String getUserIdFromFromHeader(Request request) {
32 FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); 44 FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
33 return getUserIdFromFromHeader(fromHeader); 45 return getUserIdFromFromHeader(fromHeader);
@@ -51,7 +63,7 @@ public class SipUtils { @@ -51,7 +63,7 @@ public class SipUtils {
51 } 63 }
52 64
53 public static String getNewViaTag() { 65 public static String getNewViaTag() {
54 - return "z9hG4bK" + System.currentTimeMillis(); 66 + return "z9hG4bK" + RandomStringUtils.randomNumeric(10);
55 } 67 }
56 68
57 public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException { 69 public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException {
@@ -189,4 +201,67 @@ public class SipUtils { @@ -189,4 +201,67 @@ public class SipUtils {
189 } 201 }
190 return deviceChannel; 202 return deviceChannel;
191 } 203 }
  204 +
  205 + public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException {
  206 +
  207 + // jainSip不支持y= f=字段, 移除以解析。
  208 + int ssrcIndex = sdpStr.indexOf("y=");
  209 + int mediaDescriptionIndex = sdpStr.indexOf("f=");
  210 + // 检查是否有y字段
  211 + SessionDescription sdp;
  212 + String ssrc = null;
  213 + String mediaDescription = null;
  214 + if (mediaDescriptionIndex == 0 && ssrcIndex == 0) {
  215 + sdp = SdpFactory.getInstance().createSessionDescription(sdpStr);
  216 + }else {
  217 + String lines[] = sdpStr.split("\\r?\\n");
  218 + StringBuilder sdpBuffer = new StringBuilder();
  219 + for (String line : lines) {
  220 + if (line.trim().startsWith("y=")) {
  221 + ssrc = line.substring(2);
  222 + }else if (line.trim().startsWith("f=")) {
  223 + mediaDescription = line.substring(2);
  224 + }else {
  225 + sdpBuffer.append(line.trim()).append("\r\n");
  226 + }
  227 + }
  228 + sdp = SdpFactory.getInstance().createSessionDescription(sdpBuffer.toString());
  229 + }
  230 + return Gb28181Sdp.getInstance(sdp, ssrc, mediaDescription);
  231 + }
  232 +
  233 + public static String getSsrcFromSdp(String sdpStr) {
  234 +
  235 + // jainSip不支持y= f=字段, 移除以解析。
  236 + int ssrcIndex = sdpStr.indexOf("y=");
  237 + if (ssrcIndex == 0) {
  238 + return null;
  239 + }
  240 + String lines[] = sdpStr.split("\\r?\\n");
  241 + for (String line : lines) {
  242 + if (line.trim().startsWith("y=")) {
  243 + return line.substring(2);
  244 + }
  245 + }
  246 + return null;
  247 + }
  248 +
  249 + public static String parseTime(String timeStr) {
  250 + if (ObjectUtils.isEmpty(timeStr)){
  251 + return null;
  252 + }
  253 + System.out.println(timeStr);
  254 + LocalDateTime localDateTime;
  255 + try {
  256 + localDateTime = LocalDateTime.parse(timeStr);
  257 + }catch (DateTimeParseException e) {
  258 + try {
  259 + localDateTime = LocalDateTime.parse(timeStr, DateUtil.formatterISO8601);
  260 + }catch (DateTimeParseException e2) {
  261 + logger.error("[格式化时间] 无法格式化时间: {}", timeStr);
  262 + return null;
  263 + }
  264 + }
  265 + return localDateTime.format(DateUtil.formatterISO8601);
  266 + }
192 } 267 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -6,7 +6,9 @@ import com.alibaba.fastjson2.JSONObject; @@ -6,7 +6,9 @@ import com.alibaba.fastjson2.JSONObject;
6 import com.genersoft.iot.vmp.common.CommonCallback; 6 import com.genersoft.iot.vmp.common.CommonCallback;
7 import com.genersoft.iot.vmp.conf.UserSetting; 7 import com.genersoft.iot.vmp.conf.UserSetting;
8 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; 8 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
9 -import com.genersoft.iot.vmp.media.zlm.dto.*; 9 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;
  11 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
10 import org.slf4j.Logger; 12 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory; 13 import org.slf4j.LoggerFactory;
12 import org.springframework.beans.factory.annotation.Autowired; 14 import org.springframework.beans.factory.annotation.Autowired;
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java
@@ -20,28 +20,26 @@ public class StreamProxyItem extends GbStream { @@ -20,28 +20,26 @@ public class StreamProxyItem extends GbStream {
20 @Schema(description = "拉流地址") 20 @Schema(description = "拉流地址")
21 private String url; 21 private String url;
22 @Schema(description = "拉流地址") 22 @Schema(description = "拉流地址")
23 - private String src_url; 23 + private String srcUrl;
24 @Schema(description = "目标地址") 24 @Schema(description = "目标地址")
25 - private String dst_url; 25 + private String dstUrl;
26 @Schema(description = "超时时间") 26 @Schema(description = "超时时间")
27 - private int timeout_ms; 27 + private int timeoutMs;
28 @Schema(description = "ffmpeg模板KEY") 28 @Schema(description = "ffmpeg模板KEY")
29 - private String ffmpeg_cmd_key; 29 + private String ffmpegCmdKey;
30 @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") 30 @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播")
31 - private String rtp_type; 31 + private String rtpType;
32 @Schema(description = "是否启用") 32 @Schema(description = "是否启用")
33 private boolean enable; 33 private boolean enable;
34 @Schema(description = "是否启用音频") 34 @Schema(description = "是否启用音频")
35 - private boolean enable_audio; 35 + private boolean enableAudio;
36 @Schema(description = "是否启用MP4") 36 @Schema(description = "是否启用MP4")
37 - private boolean enable_mp4; 37 + private boolean enableMp4;
38 @Schema(description = "是否 无人观看时删除") 38 @Schema(description = "是否 无人观看时删除")
39 - private boolean enable_remove_none_reader; 39 + private boolean enableRemoveNoneReader;
40 40
41 @Schema(description = "是否 无人观看时自动停用") 41 @Schema(description = "是否 无人观看时自动停用")
42 - private boolean enable_disable_none_reader;  
43 - @Schema(description = "创建时间")  
44 - private String createTime; 42 + private boolean enableDisableNoneReader;
45 43
46 public String getType() { 44 public String getType() {
47 return type; 45 return type;
@@ -89,44 +87,44 @@ public class StreamProxyItem extends GbStream { @@ -89,44 +87,44 @@ public class StreamProxyItem extends GbStream {
89 this.url = url; 87 this.url = url;
90 } 88 }
91 89
92 - public String getSrc_url() {  
93 - return src_url; 90 + public String getSrcUrl() {
  91 + return srcUrl;
94 } 92 }
95 93
96 - public void setSrc_url(String src_url) {  
97 - this.src_url = src_url; 94 + public void setSrcUrl(String src_url) {
  95 + this.srcUrl = src_url;
98 } 96 }
99 97
100 - public String getDst_url() {  
101 - return dst_url; 98 + public String getDstUrl() {
  99 + return dstUrl;
102 } 100 }
103 101
104 - public void setDst_url(String dst_url) {  
105 - this.dst_url = dst_url; 102 + public void setDstUrl(String dst_url) {
  103 + this.dstUrl = dst_url;
106 } 104 }
107 105
108 - public int getTimeout_ms() {  
109 - return timeout_ms; 106 + public int getTimeoutMs() {
  107 + return timeoutMs;
110 } 108 }
111 109
112 - public void setTimeout_ms(int timeout_ms) {  
113 - this.timeout_ms = timeout_ms; 110 + public void setTimeoutMs(int timeout_ms) {
  111 + this.timeoutMs = timeout_ms;
114 } 112 }
115 113
116 - public String getFfmpeg_cmd_key() {  
117 - return ffmpeg_cmd_key; 114 + public String getFfmpegCmdKey() {
  115 + return ffmpegCmdKey;
118 } 116 }
119 117
120 - public void setFfmpeg_cmd_key(String ffmpeg_cmd_key) {  
121 - this.ffmpeg_cmd_key = ffmpeg_cmd_key; 118 + public void setFfmpegCmdKey(String ffmpeg_cmd_key) {
  119 + this.ffmpegCmdKey = ffmpeg_cmd_key;
122 } 120 }
123 121
124 - public String getRtp_type() {  
125 - return rtp_type; 122 + public String getRtpType() {
  123 + return rtpType;
126 } 124 }
127 125
128 - public void setRtp_type(String rtp_type) {  
129 - this.rtp_type = rtp_type; 126 + public void setRtpType(String rtp_type) {
  127 + this.rtpType = rtp_type;
130 } 128 }
131 129
132 public boolean isEnable() { 130 public boolean isEnable() {
@@ -137,45 +135,37 @@ public class StreamProxyItem extends GbStream { @@ -137,45 +135,37 @@ public class StreamProxyItem extends GbStream {
137 this.enable = enable; 135 this.enable = enable;
138 } 136 }
139 137
140 - public boolean isEnable_mp4() {  
141 - return enable_mp4; 138 + public boolean isEnableMp4() {
  139 + return enableMp4;
142 } 140 }
143 141
144 - public void setEnable_mp4(boolean enable_mp4) {  
145 - this.enable_mp4 = enable_mp4; 142 + public void setEnableMp4(boolean enable_mp4) {
  143 + this.enableMp4 = enable_mp4;
146 } 144 }
147 145
148 - @Override  
149 - public String getCreateTime() {  
150 - return createTime; 146 + public boolean isEnableRemoveNoneReader() {
  147 + return enableRemoveNoneReader;
151 } 148 }
152 149
153 - @Override  
154 - public void setCreateTime(String createTime) {  
155 - this.createTime = createTime; 150 + public void setEnableRemoveNoneReader(boolean enable_remove_none_reader) {
  151 + this.enableRemoveNoneReader = enable_remove_none_reader;
156 } 152 }
157 153
158 - public boolean isEnable_remove_none_reader() {  
159 - return enable_remove_none_reader; 154 + public boolean isEnableDisableNoneReader() {
  155 + return enableDisableNoneReader;
160 } 156 }
161 157
162 - public void setEnable_remove_none_reader(boolean enable_remove_none_reader) {  
163 - this.enable_remove_none_reader = enable_remove_none_reader; 158 + public void setEnableDisableNoneReader(boolean enable_disable_none_reader) {
  159 + this.enableDisableNoneReader = enable_disable_none_reader;
164 } 160 }
165 161
166 - public boolean isEnable_disable_none_reader() {  
167 - return enable_disable_none_reader; 162 + public boolean isEnableAudio() {
  163 + return enableAudio;
168 } 164 }
169 165
170 - public void setEnable_disable_none_reader(boolean enable_disable_none_reader) {  
171 - this.enable_disable_none_reader = enable_disable_none_reader; 166 + public void setEnableAudio(boolean enable_audio) {
  167 + this.enableAudio = enable_audio;
172 } 168 }
173 169
174 - public boolean isEnable_audio() {  
175 - return enable_audio;  
176 - }  
177 170
178 - public void setEnable_audio(boolean enable_audio) {  
179 - this.enable_audio = enable_audio;  
180 - }  
181 } 171 }
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
1 package com.genersoft.iot.vmp.service; 1 package com.genersoft.iot.vmp.service;
2 2
3 import com.alibaba.fastjson2.JSONObject; 3 import com.alibaba.fastjson2.JSONObject;
  4 +import com.genersoft.iot.vmp.common.GeneralCallback;
4 import com.genersoft.iot.vmp.common.StreamInfo; 5 import com.genersoft.iot.vmp.common.StreamInfo;
5 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 6 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
6 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 7 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
@@ -13,7 +14,7 @@ public interface IStreamProxyService { @@ -13,7 +14,7 @@ public interface IStreamProxyService {
13 * 保存视频代理 14 * 保存视频代理
14 * @param param 15 * @param param
15 */ 16 */
16 - StreamInfo save(StreamProxyItem param); 17 + void save(StreamProxyItem param, GeneralCallback<StreamInfo> callback);
17 18
18 /** 19 /**
19 * 添加视频代理到zlm 20 * 添加视频代理到zlm
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -187,7 +187,7 @@ public class DeviceServiceImpl implements IDeviceService { @@ -187,7 +187,7 @@ public class DeviceServiceImpl implements IDeviceService {
187 187
188 @Override 188 @Override
189 public void offline(String deviceId, String reason) { 189 public void offline(String deviceId, String reason) {
190 - logger.error("[设备离线],{}, device:{}", reason, deviceId); 190 + logger.warn("[设备离线],{}, device:{}", reason, deviceId);
191 Device device = deviceMapper.getDeviceByDeviceId(deviceId); 191 Device device = deviceMapper.getDeviceByDeviceId(deviceId);
192 if (device == null) { 192 if (device == null) {
193 return; 193 return;
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -418,7 +418,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -418,7 +418,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
418 } 418 }
419 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId(); 419 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
420 dynamicTask.stop(zlmKeepaliveKey); 420 dynamicTask.stop(zlmKeepaliveKey);
421 - dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000); 421 + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (serverItem.getHookAliveInterval().intValue() + 5) * 1000);
422 publisher.zlmOnlineEventPublish(serverItem.getId()); 422 publisher.zlmOnlineEventPublish(serverItem.getId());
423 423
424 logger.info("[ZLM] 连接成功 {} - {}:{} ", 424 logger.info("[ZLM] 连接成功 {} - {}:{} ",
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -2,12 +2,16 @@ package com.genersoft.iot.vmp.service.impl; @@ -2,12 +2,16 @@ package com.genersoft.iot.vmp.service.impl;
2 2
3 import com.alibaba.fastjson2.JSONArray; 3 import com.alibaba.fastjson2.JSONArray;
4 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.common.GeneralCallback;
5 import com.genersoft.iot.vmp.common.StreamInfo; 6 import com.genersoft.iot.vmp.common.StreamInfo;
6 import com.genersoft.iot.vmp.conf.UserSetting; 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 import com.genersoft.iot.vmp.conf.exception.ControllerException; 8 import com.genersoft.iot.vmp.conf.exception.ControllerException;
8 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 9 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
9 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; 10 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
10 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 11 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  12 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  13 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
11 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
12 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 16 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
13 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; 17 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
@@ -86,6 +90,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -86,6 +90,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
86 private IMediaServerService mediaServerService; 90 private IMediaServerService mediaServerService;
87 91
88 @Autowired 92 @Autowired
  93 + private ZlmHttpHookSubscribe hookSubscribe;
  94 +
  95 + @Autowired
89 DataSourceTransactionManager dataSourceTransactionManager; 96 DataSourceTransactionManager dataSourceTransactionManager;
90 97
91 @Autowired 98 @Autowired
@@ -93,7 +100,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -93,7 +100,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
93 100
94 101
95 @Override 102 @Override
96 - public StreamInfo save(StreamProxyItem param) { 103 + public void save(StreamProxyItem param, GeneralCallback<StreamInfo> callback) {
97 MediaServerItem mediaInfo; 104 MediaServerItem mediaInfo;
98 if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){ 105 if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){
99 mediaInfo = mediaServerService.getMediaServerForMinimumLoad(null); 106 mediaInfo = mediaServerService.getMediaServerForMinimumLoad(null);
@@ -104,10 +111,43 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -104,10 +111,43 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
104 logger.warn("保存代理未找到在线的ZLM..."); 111 logger.warn("保存代理未找到在线的ZLM...");
105 throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM"); 112 throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM");
106 } 113 }
107 - String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),  
108 - param.getStream() );  
109 - param.setDst_url(dstUrl);  
110 - StringBuffer resultMsg = new StringBuffer(); 114 + String dstUrl;
  115 + if ("ffmpeg".equalsIgnoreCase(param.getType())) {
  116 + JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaInfo);
  117 + if (jsonObject.getInteger("code") != 0) {
  118 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败");
  119 + }
  120 + JSONArray dataArray = jsonObject.getJSONArray("data");
  121 + JSONObject mediaServerConfig = dataArray.getJSONObject(0);
  122 + String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey());
  123 + String schema = getSchemaFromFFmpegCmd(ffmpegCmd);
  124 + if (schema == null) {
  125 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法从ffmpeg cmd中获取到输出格式");
  126 + }
  127 + int port;
  128 + String schemaForUri;
  129 + if (schema.equalsIgnoreCase("rtsp")) {
  130 + port = mediaInfo.getRtspPort();
  131 + schemaForUri = schema;
  132 + }else if (schema.equalsIgnoreCase("flv")) {
  133 + port = mediaInfo.getHttpPort();
  134 + schemaForUri = "http";
  135 + }else if (schema.equalsIgnoreCase("rtmp")) {
  136 + port = mediaInfo.getRtmpPort();
  137 + schemaForUri = schema;
  138 + }else {
  139 + port = mediaInfo.getRtmpPort();
  140 + schemaForUri = schema;
  141 + }
  142 +
  143 + dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(),
  144 + param.getStream());
  145 + }else {
  146 + dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),
  147 + param.getStream());
  148 + }
  149 + param.setDstUrl(dstUrl);
  150 + logger.info("[拉流代理] 输出地址为:{}", dstUrl);
111 param.setMediaServerId(mediaInfo.getId()); 151 param.setMediaServerId(mediaInfo.getId());
112 boolean saveResult; 152 boolean saveResult;
113 // 更新 153 // 更新
@@ -117,29 +157,60 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -117,29 +157,60 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
117 saveResult = addStreamProxy(param); 157 saveResult = addStreamProxy(param);
118 } 158 }
119 if (!saveResult) { 159 if (!saveResult) {
120 - throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败"); 160 + callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null);
  161 + return;
121 } 162 }
122 - StreamInfo resultForStreamInfo = null;  
123 - resultMsg.append("保存成功"); 163 +
  164 + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(param.getApp(), param.getStream(), true, "rtsp", mediaInfo.getId());
  165 + hookSubscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItem, response) -> {
  166 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
  167 + mediaInfo, param.getApp(), param.getStream(), null, null);
  168 + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
  169 + });
  170 +
124 if (param.isEnable()) { 171 if (param.isEnable()) {
125 JSONObject jsonObject = addStreamProxyToZlm(param); 172 JSONObject jsonObject = addStreamProxyToZlm(param);
126 - if (jsonObject == null || jsonObject.getInteger("code") != 0) {  
127 - resultMsg.append(", 但是启用失败,请检查流地址是否可用"); 173 + if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  174 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
  175 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
  176 + mediaInfo, param.getApp(), param.getStream(), null, null);
  177 + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
  178 + }else {
128 param.setEnable(false); 179 param.setEnable(false);
129 // 直接移除 180 // 直接移除
130 - if (param.isEnable_remove_none_reader()) { 181 + if (param.isEnableRemoveNoneReader()) {
131 del(param.getApp(), param.getStream()); 182 del(param.getApp(), param.getStream());
132 }else { 183 }else {
133 updateStreamProxy(param); 184 updateStreamProxy(param);
134 } 185 }
  186 + if (jsonObject == null){
  187 + callback.run(ErrorCode.ERROR100.getCode(), "记录已保存,启用失败", null);
  188 + return;
  189 + }else {
  190 + callback.run(ErrorCode.ERROR100.getCode(), jsonObject.getString("msg"), null);
  191 + return;
  192 + }
  193 + }
  194 + }
  195 + }
135 196
136 - }else {  
137 - resultForStreamInfo = mediaService.getStreamInfoByAppAndStream(  
138 - mediaInfo, param.getApp(), param.getStream(), null, null); 197 + private String getSchemaFromFFmpegCmd(String ffmpegCmd) {
  198 + ffmpegCmd = ffmpegCmd.replaceAll(" + ", " ");
  199 + String[] paramArray = ffmpegCmd.split(" ");
  200 + if (paramArray.length == 0) {
  201 + return null;
  202 + }
  203 + for (int i = 0; i < paramArray.length; i++) {
  204 + if (paramArray[i].equalsIgnoreCase("-f")) {
  205 + if (i + 1 < paramArray.length - 1) {
  206 + return paramArray[i+1];
  207 + }else {
  208 + return null;
  209 + }
139 210
140 } 211 }
141 } 212 }
142 - return resultForStreamInfo; 213 + return null;
143 } 214 }
144 215
145 /** 216 /**
@@ -228,11 +299,11 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -228,11 +299,11 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
228 } 299 }
229 if ("default".equals(param.getType())){ 300 if ("default".equals(param.getType())){
230 result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(), 301 result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(),
231 - param.isEnable_audio(), param.isEnable_mp4(), param.getRtp_type()); 302 + param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());
232 }else if ("ffmpeg".equals(param.getType())) { 303 }else if ("ffmpeg".equals(param.getType())) {
233 - result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrc_url(), param.getDst_url(),  
234 - param.getTimeout_ms() + "", param.isEnable_audio(), param.isEnable_mp4(),  
235 - param.getFfmpeg_cmd_key()); 304 + result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl(), param.getDstUrl(),
  305 + param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(),
  306 + param.getFfmpegCmdKey());
236 } 307 }
237 return result; 308 return result;
238 } 309 }
@@ -286,7 +357,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -286,7 +357,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
286 updateStreamProxy(streamProxy); 357 updateStreamProxy(streamProxy);
287 }else { 358 }else {
288 logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), 359 logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"),
289 - streamProxy.getSrc_url() == null? streamProxy.getUrl():streamProxy.getSrc_url()); 360 + streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl());
290 } 361 }
291 } 362 }
292 return result; 363 return result;
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -183,6 +183,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @@ -183,6 +183,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
183 183
184 @Override 184 @Override
185 public boolean stop(String app, String streamId) { 185 public boolean stop(String app, String streamId) {
  186 + logger.info("[推流 ] 停止流: {}/{}", app, streamId);
186 StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId); 187 StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId);
187 if (streamPushItem != null) { 188 if (streamPushItem != null) {
188 gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL); 189 gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL);
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.redisMsg;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.service.IStreamPushService;
  6 +import org.jetbrains.annotations.NotNull;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.beans.factory.annotation.Qualifier;
  11 +import org.springframework.data.redis.connection.Message;
  12 +import org.springframework.data.redis.connection.MessageListener;
  13 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  14 +import org.springframework.stereotype.Component;
  15 +
  16 +import java.util.concurrent.ConcurrentLinkedQueue;
  17 +
  18 +/**
  19 + * 接收来自redis的关闭流更新通知
  20 + * @author lin
  21 + */
  22 +@Component
  23 +public class RedisCloseStreamMsgListener implements MessageListener {
  24 +
  25 + private final static Logger logger = LoggerFactory.getLogger(RedisCloseStreamMsgListener.class);
  26 +
  27 +
  28 + @Autowired
  29 + private IStreamPushService pushService;
  30 +
  31 + private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
  32 +
  33 + @Qualifier("taskExecutor")
  34 + @Autowired
  35 + private ThreadPoolTaskExecutor taskExecutor;
  36 +
  37 + @Override
  38 + public void onMessage(@NotNull Message message, byte[] bytes) {
  39 + boolean isEmpty = taskQueue.isEmpty();
  40 + taskQueue.offer(message);
  41 + if (isEmpty) {
  42 + taskExecutor.execute(() -> {
  43 + while (!taskQueue.isEmpty()) {
  44 + Message msg = taskQueue.poll();
  45 + try {
  46 + JSONObject jsonObject = JSON.parseObject(msg.getBody());
  47 + String app = jsonObject.getString("app");
  48 + String stream = jsonObject.getString("stream");
  49 + pushService.stop(app, stream);
  50 +
  51 + }catch (Exception e) {
  52 + logger.warn("[REDIS的关闭推流通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  53 + logger.error("[REDIS的关闭推流通知] 异常内容: ", e);
  54 + }
  55 + }
  56 + });
  57 + }
  58 + }
  59 +}
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
@@ -202,4 +202,5 @@ public interface IRedisCatchStorage { @@ -202,4 +202,5 @@ public interface IRedisCatchStorage {
202 void removeAllDevice(); 202 void removeAllDevice();
203 203
204 void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online); 204 void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online);
  205 + void sendChannelAddOrDelete(String deviceId, String channelId, boolean add);
205 } 206 }
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
@@ -96,6 +96,6 @@ public interface ParentPlatformMapper { @@ -96,6 +96,6 @@ public interface ParentPlatformMapper {
96 96
97 @Select("select 'channel' as name, count(pgc.platform_id) count from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id where pgc.platform_id=#{platform_id} and dc.channel_id =#{gbId} " + 97 @Select("select 'channel' as name, count(pgc.platform_id) count from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id where pgc.platform_id=#{platform_id} and dc.channel_id =#{gbId} " +
98 "union " + 98 "union " +
99 - "select 'stream' as name, count(pgs.platform_id) count from wvp_platform_gb_stream pgs left join wvp_gb_stream gs on pgs.gb_stream_id = gs.gb_stream_id where pgs.platform_id=#{platform_id} and gs.gb_id #{gbId}") 99 + "select 'stream' as name, count(pgs.platform_id) count from wvp_platform_gb_stream pgs left join wvp_gb_stream gs on pgs.gb_stream_id = gs.gb_stream_id where pgs.platform_id=#{platform_id} and gs.gb_id =#{gbId}")
100 List<ChannelSourceInfo> getChannelSource(String platform_id, String gbId); 100 List<ChannelSourceInfo> getChannelSource(String platform_id, String gbId);
101 } 101 }
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java
@@ -12,7 +12,7 @@ import java.util.List; @@ -12,7 +12,7 @@ import java.util.List;
12 @Repository 12 @Repository
13 public interface PlatformCatalogMapper { 13 public interface PlatformCatalogMapper {
14 14
15 - @Insert("INSERT INTO platform_catalog (id, name, platform_id, parent_id, civil_code, business_group_id) VALUES" + 15 + @Insert("INSERT INTO wvp_platform_catalog (id, name, platform_id, parent_id, civil_code, business_group_id) VALUES" +
16 "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})") 16 "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})")
17 int add(PlatformCatalog platformCatalog); 17 int add(PlatformCatalog platformCatalog);
18 18
@@ -32,7 +32,7 @@ public interface PlatformCatalogMapper { @@ -32,7 +32,7 @@ public interface PlatformCatalogMapper {
32 PlatformCatalog select(String id); 32 PlatformCatalog select(String id);
33 33
34 @Update(value = {" <script>" + 34 @Update(value = {" <script>" +
35 - "UPDATE platform_catalog " + 35 + "UPDATE wvp_platform_catalog " +
36 "SET name=#{name}" + 36 "SET name=#{name}" +
37 "WHERE id=#{id}"+ 37 "WHERE id=#{id}"+
38 "</script>"}) 38 "</script>"})
@@ -41,11 +41,11 @@ public interface PlatformCatalogMapper { @@ -41,11 +41,11 @@ public interface PlatformCatalogMapper {
41 @Select("SELECT *, (SELECT COUNT(1) from wvp_platform_catalog where parent_id = pc.id) as children_count from wvp_platform_catalog pc WHERE pc.platform_id=#{platformId}") 41 @Select("SELECT *, (SELECT COUNT(1) from wvp_platform_catalog where parent_id = pc.id) as children_count from wvp_platform_catalog pc WHERE pc.platform_id=#{platformId}")
42 List<PlatformCatalog> selectByPlatForm(String platformId); 42 List<PlatformCatalog> selectByPlatForm(String platformId);
43 43
44 - @Select("SELECT pc.* FROM platform_catalog pc WHERE pc.id = (SELECT pp.catalog_id from wvp_platform pp WHERE pp.server_gb_id=#{platformId})") 44 + @Select("SELECT pc.* FROM wvp_platform_catalog pc WHERE pc.id = (SELECT pp.catalog_id from wvp_platform pp WHERE pp.server_gb_id=#{platformId})")
45 PlatformCatalog selectDefaultByPlatFormId(String platformId); 45 PlatformCatalog selectDefaultByPlatFormId(String platformId);
46 46
47 47
48 - @Select("SELECT pc.* FROM platform_catalog pc WHERE pc.id = #{id}") 48 + @Select("SELECT pc.* FROM wvp_platform_catalog pc WHERE pc.id = #{id}")
49 PlatformCatalog selectParentCatalog(String id); 49 PlatformCatalog selectParentCatalog(String id);
50 50
51 @Select("SELECT pc.id as channel_id, pc.name, pc.civil_code, pc.business_group_id,'1' as parental, pc.parent_id " + 51 @Select("SELECT pc.id as channel_id, pc.name, pc.civil_code, pc.business_group_id,'1' as parental, pc.parent_id " +
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
@@ -13,9 +13,9 @@ public interface StreamProxyMapper { @@ -13,9 +13,9 @@ public interface StreamProxyMapper {
13 13
14 @Insert("INSERT INTO wvp_stream_proxy (type, name, app, stream,media_server_id, url, src_url, dst_url, " + 14 @Insert("INSERT INTO wvp_stream_proxy (type, name, app, stream,media_server_id, url, src_url, dst_url, " +
15 "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" + 15 "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" +
16 - "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{src_url}, #{dst_url}, " +  
17 - "#{timeout_ms}, #{ffmpeg_cmd_key}, #{rtp_type}, #{enable_audio}, #{enable_mp4}, #{enable}, #{status}, " +  
18 - "#{enable_remove_none_reader}, #{enable_disable_none_reader}, #{createTime} )") 16 + "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " +
  17 + "#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, " +
  18 + "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )")
19 int add(StreamProxyItem streamProxyDto); 19 int add(StreamProxyItem streamProxyDto);
20 20
21 @Update("UPDATE wvp_stream_proxy " + 21 @Update("UPDATE wvp_stream_proxy " +
@@ -25,17 +25,17 @@ public interface StreamProxyMapper { @@ -25,17 +25,17 @@ public interface StreamProxyMapper {
25 "stream=#{stream}," + 25 "stream=#{stream}," +
26 "url=#{url}, " + 26 "url=#{url}, " +
27 "media_server_id=#{mediaServerId}, " + 27 "media_server_id=#{mediaServerId}, " +
28 - "src_url=#{src_url}," +  
29 - "dst_url=#{dst_url}, " +  
30 - "timeout_ms=#{timeout_ms}, " +  
31 - "ffmpeg_cmd_key=#{ffmpeg_cmd_key}, " +  
32 - "rtp_type=#{rtp_type}, " +  
33 - "enable_audio=#{enable_audio}, " + 28 + "src_url=#{srcUrl}," +
  29 + "dst_url=#{dstUrl}, " +
  30 + "timeout_ms=#{timeoutMs}, " +
  31 + "ffmpeg_cmd_key=#{ffmpegCmdKey}, " +
  32 + "rtp_type=#{rtpType}, " +
  33 + "enable_audio=#{enableAudio}, " +
34 "enable=#{enable}, " + 34 "enable=#{enable}, " +
35 "status=#{status}, " + 35 "status=#{status}, " +
36 - "enable_remove_none_reader=#{enable_remove_none_reader}, " +  
37 - "enable_disable_none_reader=#{enable_disable_none_reader}, " +  
38 - "enable_mp4=#{enable_mp4} " + 36 + "enable_remove_none_reader=#{enableRemoveNoneReader}, " +
  37 + "enable_disable_none_reader=#{enableDisableNoneReader}, " +
  38 + "enable_mp4=#{enableMp4} " +
39 "WHERE app=#{app} AND stream=#{stream}") 39 "WHERE app=#{app} AND stream=#{stream}")
40 int update(StreamProxyItem streamProxyDto); 40 int update(StreamProxyItem streamProxyDto);
41 41
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -596,18 +596,29 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -596,18 +596,29 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
596 @Override 596 @Override
597 public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) { 597 public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
598 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS; 598 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
599 - if (channelId == null) {  
600 - logger.info("[redis通知] 推送设备状态, {}-{}", deviceId, online);  
601 - }else {  
602 - logger.info("[redis通知] 推送通道状态, {}/{}-{}", deviceId, channelId, online); 599 + StringBuilder msg = new StringBuilder();
  600 + msg.append(deviceId);
  601 + if (channelId != null) {
  602 + msg.append(":").append(channelId);
603 } 603 }
  604 + msg.append(" ").append(online? "ON":"OFF");
  605 + logger.info("[redis通知] 推送状态-> {} ", msg);
  606 + // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
  607 + stringRedisTemplate.convertAndSend(key, msg.toString());
  608 + }
  609 +
  610 + @Override
  611 + public void sendChannelAddOrDelete(String deviceId, String channelId, boolean add) {
  612 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
  613 +
604 614
605 StringBuilder msg = new StringBuilder(); 615 StringBuilder msg = new StringBuilder();
606 msg.append(deviceId); 616 msg.append(deviceId);
607 if (channelId != null) { 617 if (channelId != null) {
608 msg.append(":").append(channelId); 618 msg.append(":").append(channelId);
609 } 619 }
610 - msg.append(" ").append(online? "ON":"OFF"); 620 + msg.append(" ").append(add? "ADD":"DELETE");
  621 + logger.info("[redis通知] 推送通道-> {}", msg);
611 // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号 622 // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
612 stringRedisTemplate.convertAndSend(key, msg.toString()); 623 stringRedisTemplate.convertAndSend(key, msg.toString());
613 } 624 }
src/main/java/com/genersoft/iot/vmp/vmanager/bean/ErrorCode.java
@@ -6,7 +6,7 @@ package com.genersoft.iot.vmp.vmanager.bean; @@ -6,7 +6,7 @@ package com.genersoft.iot.vmp.vmanager.bean;
6 public enum ErrorCode { 6 public enum ErrorCode {
7 SUCCESS(0, "成功"), 7 SUCCESS(0, "成功"),
8 ERROR100(100, "失败"), 8 ERROR100(100, "失败"),
9 - ERROR400(400, "参数不全或者错误"), 9 + ERROR400(400, "参数或方法错误"),
10 ERROR404(404, "资源未找到"), 10 ERROR404(404, "资源未找到"),
11 ERROR403(403, "无权限操作"), 11 ERROR403(403, "无权限操作"),
12 ERROR401(401, "请登录后重新请求"), 12 ERROR401(401, "请登录后重新请求"),
src/main/java/com/genersoft/iot/vmp/vmanager/bean/SnapPath.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.bean;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +
  5 +@Schema(description = "截图地址信息")
  6 +public class SnapPath {
  7 +
  8 + @Schema(description = "相对地址")
  9 + private String path;
  10 +
  11 + @Schema(description = "绝对地址")
  12 + private String absoluteFilePath;
  13 +
  14 + @Schema(description = "请求地址")
  15 + private String url;
  16 +
  17 +
  18 + public static SnapPath getInstance(String path, String absoluteFilePath, String url) {
  19 + SnapPath snapPath = new SnapPath();
  20 + snapPath.setPath(path);
  21 + snapPath.setAbsoluteFilePath(absoluteFilePath);
  22 + snapPath.setUrl(url);
  23 + return snapPath;
  24 + }
  25 +
  26 +
  27 + public String getPath() {
  28 + return path;
  29 + }
  30 +
  31 + public void setPath(String path) {
  32 + this.path = path;
  33 + }
  34 +
  35 + public String getAbsoluteFilePath() {
  36 + return absoluteFilePath;
  37 + }
  38 +
  39 + public void setAbsoluteFilePath(String absoluteFilePath) {
  40 + this.absoluteFilePath = absoluteFilePath;
  41 + }
  42 +
  43 + public String getUrl() {
  44 + return url;
  45 + }
  46 +
  47 + public void setUrl(String url) {
  48 + this.url = url;
  49 + }
  50 +}
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java
@@ -102,7 +102,7 @@ public class MobilePositionController { @@ -102,7 +102,7 @@ public class MobilePositionController {
102 public DeferredResult<MobilePosition> realTimePosition(@PathVariable String deviceId) { 102 public DeferredResult<MobilePosition> realTimePosition(@PathVariable String deviceId) {
103 Device device = storager.queryVideoDevice(deviceId); 103 Device device = storager.queryVideoDevice(deviceId);
104 String uuid = UUID.randomUUID().toString(); 104 String uuid = UUID.randomUUID().toString();
105 - String key = DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId; 105 + String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + deviceId;
106 try { 106 try {
107 cmder.mobilePostitionQuery(device, event -> { 107 cmder.mobilePostitionQuery(device, event -> {
108 RequestMessage msg = new RequestMessage(); 108 RequestMessage msg = new RequestMessage();
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -466,10 +466,12 @@ public class DeviceQuery { @@ -466,10 +466,12 @@ public class DeviceQuery {
466 @Operation(summary = "请求截图") 466 @Operation(summary = "请求截图")
467 @Parameter(name = "deviceId", description = "设备国标编号", required = true) 467 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
468 @Parameter(name = "channelId", description = "通道国标编号", required = true) 468 @Parameter(name = "channelId", description = "通道国标编号", required = true)
469 - public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) { 469 + @Parameter(name = "mark", description = "标识", required = false)
  470 + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) {
470 471
471 try { 472 try {
472 - final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath()); 473 +
  474 + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());
473 resp.setContentType(MediaType.IMAGE_PNG_VALUE); 475 resp.setContentType(MediaType.IMAGE_PNG_VALUE);
474 IOUtils.copy(in, resp.getOutputStream()); 476 IOUtils.copy(in, resp.getOutputStream());
475 } catch (IOException e) { 477 } catch (IOException e) {
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
1 package com.genersoft.iot.vmp.vmanager.streamProxy; 1 package com.genersoft.iot.vmp.vmanager.streamProxy;
2 2
3 import com.alibaba.fastjson2.JSONObject; 3 import com.alibaba.fastjson2.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.conf.UserSetting;
4 import com.genersoft.iot.vmp.conf.exception.ControllerException; 6 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
5 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 9 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
6 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 10 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
7 import com.genersoft.iot.vmp.service.IMediaServerService; 11 import com.genersoft.iot.vmp.service.IMediaServerService;
8 import com.genersoft.iot.vmp.service.IStreamProxyService; 12 import com.genersoft.iot.vmp.service.IStreamProxyService;
9 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 13 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
10 import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 14 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
  15 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
11 import com.github.pagehelper.PageInfo; 16 import com.github.pagehelper.PageInfo;
12 import io.swagger.v3.oas.annotations.Operation; 17 import io.swagger.v3.oas.annotations.Operation;
13 import io.swagger.v3.oas.annotations.Parameter; 18 import io.swagger.v3.oas.annotations.Parameter;
@@ -18,6 +23,9 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -18,6 +23,9 @@ import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.stereotype.Controller; 23 import org.springframework.stereotype.Controller;
19 import org.springframework.util.ObjectUtils; 24 import org.springframework.util.ObjectUtils;
20 import org.springframework.web.bind.annotation.*; 25 import org.springframework.web.bind.annotation.*;
  26 +import org.springframework.web.context.request.async.DeferredResult;
  27 +
  28 +import java.util.UUID;
21 29
22 @SuppressWarnings("rawtypes") 30 @SuppressWarnings("rawtypes")
23 /** 31 /**
@@ -37,6 +45,12 @@ public class StreamProxyController { @@ -37,6 +45,12 @@ public class StreamProxyController {
37 @Autowired 45 @Autowired
38 private IStreamProxyService streamProxyService; 46 private IStreamProxyService streamProxyService;
39 47
  48 + @Autowired
  49 + private DeferredResultHolder resultHolder;
  50 +
  51 + @Autowired
  52 + private UserSetting userSetting;
  53 +
40 54
41 @Operation(summary = "分页查询流代理") 55 @Operation(summary = "分页查询流代理")
42 @Parameter(name = "page", description = "当前页") 56 @Parameter(name = "page", description = "当前页")
@@ -58,7 +72,7 @@ public class StreamProxyController { @@ -58,7 +72,7 @@ public class StreamProxyController {
58 }) 72 })
59 @PostMapping(value = "/save") 73 @PostMapping(value = "/save")
60 @ResponseBody 74 @ResponseBody
61 - public StreamContent save(@RequestBody StreamProxyItem param){ 75 + public DeferredResult<Object> save(@RequestBody StreamProxyItem param){
62 logger.info("添加代理: " + JSONObject.toJSONString(param)); 76 logger.info("添加代理: " + JSONObject.toJSONString(param));
63 if (ObjectUtils.isEmpty(param.getMediaServerId())) { 77 if (ObjectUtils.isEmpty(param.getMediaServerId())) {
64 param.setMediaServerId("auto"); 78 param.setMediaServerId("auto");
@@ -69,7 +83,33 @@ public class StreamProxyController { @@ -69,7 +83,33 @@ public class StreamProxyController {
69 if (ObjectUtils.isEmpty(param.getGbId())) { 83 if (ObjectUtils.isEmpty(param.getGbId())) {
70 param.setGbId(null); 84 param.setGbId(null);
71 } 85 }
72 - return new StreamContent(streamProxyService.save(param)); 86 +
  87 + RequestMessage requestMessage = new RequestMessage();
  88 + String key = DeferredResultHolder.CALLBACK_CMD_PROXY + param.getApp() + param.getStream();
  89 + requestMessage.setKey(key);
  90 + String uuid = UUID.randomUUID().toString();
  91 + requestMessage.setId(uuid);
  92 + DeferredResult<Object> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  93 + // 录像查询以channelId作为deviceId查询
  94 + resultHolder.put(key, uuid, result);
  95 + result.onTimeout(()->{
  96 + WVPResult<StreamInfo> wvpResult = new WVPResult<>();
  97 + wvpResult.setCode(ErrorCode.ERROR100.getCode());
  98 + wvpResult.setMsg("超时");
  99 + requestMessage.setData(wvpResult);
  100 + resultHolder.invokeAllResult(requestMessage);
  101 + });
  102 +
  103 + streamProxyService.save(param, (code, msg, streamInfo) -> {
  104 + logger.info("[拉流代理] {}", code == ErrorCode.SUCCESS.getCode()? "成功":"失败: " + msg);
  105 + if (code == ErrorCode.SUCCESS.getCode()) {
  106 + requestMessage.setData(new StreamContent(streamInfo));
  107 + }else {
  108 + requestMessage.setData(WVPResult.fail(code, msg));
  109 + }
  110 + resultHolder.invokeAllResult(requestMessage);
  111 + });
  112 + return result;
73 } 113 }
74 114
75 @GetMapping(value = "/ffmpeg_cmd/list") 115 @GetMapping(value = "/ffmpeg_cmd/list")
src/main/resources/all-application.yml
@@ -43,10 +43,6 @@ spring: @@ -43,10 +43,6 @@ spring:
43 idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) 43 idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
44 max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) 44 max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
45 45
46 -# 修改为数据库字段下划线分隔直接对应java驼峰命名  
47 -mybatis:  
48 - configuration:  
49 - map-underscore-to-camel-case: true  
50 46
51 # 修改分页插件为 postgresql, 数据库类型为mysql不需要 47 # 修改分页插件为 postgresql, 数据库类型为mysql不需要
52 #pagehelper: 48 #pagehelper:
web_src/src/components/StreamProxyList.vue
@@ -22,8 +22,8 @@ @@ -22,8 +22,8 @@
22 {{scope.row.url}} 22 {{scope.row.url}}
23 </el-tag> 23 </el-tag>
24 <el-tag size="medium" v-if="scope.row.type != 'default'"> 24 <el-tag size="medium" v-if="scope.row.type != 'default'">
25 - <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="scope.row.src_url" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>  
26 - {{scope.row.src_url}} 25 + <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="scope.row.srcUrl" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
  26 + {{scope.row.srcUrl}}
27 </el-tag> 27 </el-tag>
28 </div> 28 </div>
29 </template> 29 </template>
@@ -58,25 +58,25 @@ @@ -58,25 +58,25 @@
58 <el-table-column label="音频" min-width="120" > 58 <el-table-column label="音频" min-width="120" >
59 <template slot-scope="scope"> 59 <template slot-scope="scope">
60 <div slot="reference" class="name-wrapper"> 60 <div slot="reference" class="name-wrapper">
61 - <el-tag size="medium" v-if="scope.row.enable_audio">已启用</el-tag>  
62 - <el-tag size="medium" type="info" v-if="!scope.row.enable_audio">未启用</el-tag> 61 + <el-tag size="medium" v-if="scope.row.enableAudio">已启用</el-tag>
  62 + <el-tag size="medium" type="info" v-if="!scope.row.enableAudio">未启用</el-tag>
63 </div> 63 </div>
64 </template> 64 </template>
65 </el-table-column> 65 </el-table-column>
66 <el-table-column label="录制" min-width="120" > 66 <el-table-column label="录制" min-width="120" >
67 <template slot-scope="scope"> 67 <template slot-scope="scope">
68 <div slot="reference" class="name-wrapper"> 68 <div slot="reference" class="name-wrapper">
69 - <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag>  
70 - <el-tag size="medium" type="info" v-if="!scope.row.enable_mp4">未启用</el-tag> 69 + <el-tag size="medium" v-if="scope.row.enableMp4">已启用</el-tag>
  70 + <el-tag size="medium" type="info" v-if="!scope.row.enableMp4">未启用</el-tag>
71 </div> 71 </div>
72 </template> 72 </template>
73 </el-table-column> 73 </el-table-column>
74 <el-table-column label="无人观看" min-width="160" > 74 <el-table-column label="无人观看" min-width="160" >
75 <template slot-scope="scope"> 75 <template slot-scope="scope">
76 <div slot="reference" class="name-wrapper"> 76 <div slot="reference" class="name-wrapper">
77 - <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">移除</el-tag>  
78 - <el-tag size="medium" v-if="scope.row.enable_disable_none_reader">停用</el-tag>  
79 - <el-tag size="medium" type="info" v-if="!scope.row.enable_remove_none_reader && !scope.row.enable_disable_none_reader">不做处理</el-tag> 77 + <el-tag size="medium" v-if="scope.row.enableRemoveNoneReader">移除</el-tag>
  78 + <el-tag size="medium" v-if="scope.row.enableDisableNoneReader">停用</el-tag>
  79 + <el-tag size="medium" type="info" v-if="!scope.row.enableRemoveNoneReader && !scope.row.enableDisableNoneReader">不做处理</el-tag>
80 </div> 80 </div>
81 </template> 81 </template>
82 </el-table-column> 82 </el-table-column>
@@ -197,7 +197,7 @@ @@ -197,7 +197,7 @@
197 this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{ 197 this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{
198 if (url != null) { 198 if (url != null) {
199 this.$refs.onvifEdit.close(); 199 this.$refs.onvifEdit.close();
200 - this.$refs.streamProxyEdit.openDialog({type: "default", url: url, src_url: url}, this.initData()) 200 + this.$refs.streamProxyEdit.openDialog({type: "default", url: url, srcUrl: url}, this.initData())
201 } 201 }
202 }) 202 })
203 }else { 203 }else {
@@ -245,18 +245,25 @@ @@ -245,18 +245,25 @@
245 }, 245 },
246 deleteStreamProxy: function(row){ 246 deleteStreamProxy: function(row){
247 let that = this; 247 let that = this;
248 - that.$axios({  
249 - method:"delete",  
250 - url:"/api/proxy/del",  
251 - params:{  
252 - app: row.app,  
253 - stream: row.stream  
254 - }  
255 - }).then((res)=>{  
256 - that.initData()  
257 - }).catch(function (error) {  
258 - console.log(error);  
259 - }); 248 + this.$confirm('确定删除此代理吗?', '提示', {
  249 + confirmButtonText: '确定',
  250 + cancelButtonText: '取消',
  251 + type: 'warning'
  252 + }).then(() => {
  253 + that.$axios({
  254 + method:"delete",
  255 + url:"/api/proxy/del",
  256 + params:{
  257 + app: row.app,
  258 + stream: row.stream
  259 + }
  260 + }).then((res)=>{
  261 + that.initData()
  262 + }).catch(function (error) {
  263 + console.log(error);
  264 + });
  265 + }).catch(() => {
  266 + });
260 }, 267 },
261 start: function(row){ 268 start: function(row){
262 this.stopUpdateList() 269 this.stopUpdateList()
web_src/src/components/dialog/StreamProxyEdit.vue
@@ -33,13 +33,13 @@ @@ -33,13 +33,13 @@
33 <el-form-item label="拉流地址" prop="url" v-if="proxyParam.type=='default'"> 33 <el-form-item label="拉流地址" prop="url" v-if="proxyParam.type=='default'">
34 <el-input v-model="proxyParam.url" clearable></el-input> 34 <el-input v-model="proxyParam.url" clearable></el-input>
35 </el-form-item> 35 </el-form-item>
36 - <el-form-item label="拉流地址" prop="src_url" v-if="proxyParam.type=='ffmpeg'">  
37 - <el-input v-model="proxyParam.src_url" clearable></el-input> 36 + <el-form-item label="拉流地址" prop="srcUrl" v-if="proxyParam.type=='ffmpeg'">
  37 + <el-input v-model="proxyParam.srcUrl" clearable></el-input>
38 </el-form-item> 38 </el-form-item>
39 - <el-form-item label="超时时间:毫秒" prop="timeout_ms" v-if="proxyParam.type=='ffmpeg'">  
40 - <el-input v-model="proxyParam.timeout_ms" clearable></el-input> 39 + <el-form-item label="超时时间:毫秒" prop="timeoutMs" v-if="proxyParam.type=='ffmpeg'">
  40 + <el-input v-model="proxyParam.timeoutMs" clearable></el-input>
41 </el-form-item> 41 </el-form-item>
42 - <el-form-item label="节点选择" prop="rtp_type"> 42 + <el-form-item label="节点选择" prop="rtpType">
43 <el-select 43 <el-select
44 v-model="proxyParam.mediaServerId" 44 v-model="proxyParam.mediaServerId"
45 @change="mediaServerIdChange" 45 @change="mediaServerIdChange"
@@ -54,10 +54,9 @@ @@ -54,10 +54,9 @@
54 </el-option> 54 </el-option>
55 </el-select> 55 </el-select>
56 </el-form-item> 56 </el-form-item>
57 - <el-form-item label="FFmpeg命令模板" prop="ffmpeg_cmd_key" v-if="proxyParam.type=='ffmpeg'">  
58 -<!-- <el-input v-model="proxyParam.ffmpeg_cmd_key" clearable></el-input>--> 57 + <el-form-item label="FFmpeg命令模板" prop="ffmpegCmdKey" v-if="proxyParam.type=='ffmpeg'">
59 <el-select 58 <el-select
60 - v-model="proxyParam.ffmpeg_cmd_key" 59 + v-model="proxyParam.ffmpegCmdKey"
61 style="width: 100%" 60 style="width: 100%"
62 placeholder="请选择FFmpeg命令模板" 61 placeholder="请选择FFmpeg命令模板"
63 > 62 >
@@ -72,9 +71,9 @@ @@ -72,9 +71,9 @@
72 <el-form-item label="国标编码" prop="gbId"> 71 <el-form-item label="国标编码" prop="gbId">
73 <el-input v-model="proxyParam.gbId" placeholder="设置国标编码可推送到国标" clearable></el-input> 72 <el-input v-model="proxyParam.gbId" placeholder="设置国标编码可推送到国标" clearable></el-input>
74 </el-form-item> 73 </el-form-item>
75 - <el-form-item label="拉流方式" prop="rtp_type" v-if="proxyParam.type=='default'"> 74 + <el-form-item label="拉流方式" prop="rtpType" v-if="proxyParam.type=='default'">
76 <el-select 75 <el-select
77 - v-model="proxyParam.rtp_type" 76 + v-model="proxyParam.rtpType"
78 style="width: 100%" 77 style="width: 100%"
79 placeholder="请选择拉流方式" 78 placeholder="请选择拉流方式"
80 > 79 >
@@ -83,10 +82,10 @@ @@ -83,10 +82,10 @@
83 <el-option label="组播" value="2"></el-option> 82 <el-option label="组播" value="2"></el-option>
84 </el-select> 83 </el-select>
85 </el-form-item> 84 </el-form-item>
86 - <el-form-item label="无人观看" prop="rtp_type" > 85 + <el-form-item label="无人观看" prop="rtpType" >
87 <el-select 86 <el-select
88 @change="noneReaderHandler" 87 @change="noneReaderHandler"
89 - v-model="proxyParam.none_reader" 88 + v-model="proxyParam.noneReader"
90 style="width: 100%" 89 style="width: 100%"
91 placeholder="请选择无人观看的处理方式" 90 placeholder="请选择无人观看的处理方式"
92 > 91 >
@@ -98,8 +97,8 @@ @@ -98,8 +97,8 @@
98 <el-form-item label="其他选项"> 97 <el-form-item label="其他选项">
99 <div style="float: left;"> 98 <div style="float: left;">
100 <el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox> 99 <el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox>
101 - <el-checkbox label="开启音频" v-model="proxyParam.enable_audio" ></el-checkbox>  
102 - <el-checkbox label="录制" v-model="proxyParam.enable_mp4" ></el-checkbox> 100 + <el-checkbox label="开启音频" v-model="proxyParam.enableAudio" ></el-checkbox>
  101 + <el-checkbox label="录制" v-model="proxyParam.enableMp4" ></el-checkbox>
103 </div> 102 </div>
104 103
105 </el-form-item> 104 </el-form-item>
@@ -155,17 +154,17 @@ export default { @@ -155,17 +154,17 @@ export default {
155 app: null, 154 app: null,
156 stream: null, 155 stream: null,
157 url: "", 156 url: "",
158 - src_url: null,  
159 - timeout_ms: null,  
160 - ffmpeg_cmd_key: null, 157 + srcUrl: null,
  158 + timeoutMs: null,
  159 + ffmpegCmdKey: null,
161 gbId: null, 160 gbId: null,
162 - rtp_type: null, 161 + rtpType: null,
163 enable: true, 162 enable: true,
164 - enable_audio: true,  
165 - enable_mp4: false,  
166 - none_reader: null,  
167 - enable_remove_none_reader: false,  
168 - enable_disable_none_reader: false, 163 + enableAudio: true,
  164 + enableMp4: false,
  165 + noneReader: null,
  166 + enableRemoveNoneReader: false,
  167 + enableDisableNoneReader: false,
169 platformGbId: null, 168 platformGbId: null,
170 mediaServerId: null, 169 mediaServerId: null,
171 }, 170 },
@@ -177,9 +176,9 @@ export default { @@ -177,9 +176,9 @@ export default {
177 app: [{ required: true, message: "请输入应用名", trigger: "blur" }], 176 app: [{ required: true, message: "请输入应用名", trigger: "blur" }],
178 stream: [{ required: true, message: "请输入流ID", trigger: "blur" }], 177 stream: [{ required: true, message: "请输入流ID", trigger: "blur" }],
179 url: [{ required: true, message: "请输入要代理的流", trigger: "blur" }], 178 url: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
180 - src_url: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],  
181 - timeout_ms: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],  
182 - ffmpeg_cmd_key: [{ required: false, message: "请输入FFmpeg命令参数模板(可选)", trigger: "blur" }], 179 + srcUrl: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
  180 + timeoutMs: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],
  181 + ffmpegCmdKey: [{ required: false, message: "请输入FFmpeg命令参数模板(可选)", trigger: "blur" }],
183 }, 182 },
184 }; 183 };
185 }, 184 },
@@ -189,7 +188,7 @@ export default { @@ -189,7 +188,7 @@ export default {
189 this.listChangeCallback = callback; 188 this.listChangeCallback = callback;
190 if (proxyParam != null) { 189 if (proxyParam != null) {
191 this.proxyParam = proxyParam; 190 this.proxyParam = proxyParam;
192 - this.proxyParam.none_reader = null; 191 + this.proxyParam.noneReader = null;
193 } 192 }
194 193
195 let that = this; 194 let that = this;
@@ -218,7 +217,7 @@ export default { @@ -218,7 +217,7 @@ export default {
218 } 217 }
219 }).then(function (res) { 218 }).then(function (res) {
220 that.ffmpegCmdList = res.data.data; 219 that.ffmpegCmdList = res.data.data;
221 - that.proxyParam.ffmpeg_cmd_key = Object.keys(res.data.data)[0]; 220 + that.proxyParam.ffmpegCmdKey = Object.keys(res.data.data)[0];
222 }).catch(function (error) { 221 }).catch(function (error) {
223 console.log(error); 222 console.log(error);
224 }); 223 });
@@ -275,15 +274,15 @@ export default { @@ -275,15 +274,15 @@ export default {
275 } 274 }
276 }, 275 },
277 noneReaderHandler: function() { 276 noneReaderHandler: function() {
278 - if (this.proxyParam.none_reader === null || this.proxyParam.none_reader === "0") {  
279 - this.proxyParam.enable_disable_none_reader = false;  
280 - this.proxyParam.enable_remove_none_reader = false;  
281 - }else if (this.proxyParam.none_reader === "1"){  
282 - this.proxyParam.enable_disable_none_reader = true;  
283 - this.proxyParam.enable_remove_none_reader = false;  
284 - }else if (this.proxyParam.none_reader ==="2"){  
285 - this.proxyParam.enable_disable_none_reader = false;  
286 - this.proxyParam.enable_remove_none_reader = true; 277 + if (this.proxyParam.noneReader === null || this.proxyParam.noneReader === "0") {
  278 + this.proxyParam.enableDisableNoneReader = false;
  279 + this.proxyParam.enableRemoveNoneReader = false;
  280 + }else if (this.proxyParam.noneReader === "1"){
  281 + this.proxyParam.enableDisableNoneReader = true;
  282 + this.proxyParam.enableRemoveNoneReader = false;
  283 + }else if (this.proxyParam.noneReader ==="2"){
  284 + this.proxyParam.enableDisableNoneReader = false;
  285 + this.proxyParam.enableRemoveNoneReader = true;
287 } 286 }
288 }, 287 },
289 }, 288 },
web_src/src/components/dialog/devicePlayer.vue
@@ -14,7 +14,6 @@ @@ -14,7 +14,6 @@
14 <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player> 14 <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
15 </el-tab-pane> 15 </el-tab-pane>
16 <el-tab-pane label="h265web">h265web敬请期待</el-tab-pane> 16 <el-tab-pane label="h265web">h265web敬请期待</el-tab-pane>
17 - <el-tab-pane label="wsPlayer">wsPlayer 敬请期待</el-tab-pane>  
18 </el-tabs> 17 </el-tabs>
19 <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer> 18 <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
20 <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player> 19 <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
@@ -451,7 +450,15 @@ export default { @@ -451,7 +450,15 @@ export default {
451 playFromStreamInfo: function (realHasAudio, streamInfo) { 450 playFromStreamInfo: function (realHasAudio, streamInfo) {
452 this.showVideoDialog = true; 451 this.showVideoDialog = true;
453 this.hasaudio = realHasAudio && this.hasaudio; 452 this.hasaudio = realHasAudio && this.hasaudio;
454 - this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo)) 453 + if (this.$refs[this.activePlayer]) {
  454 + this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
  455 + }else {
  456 + this.$nextTick(() => {
  457 + this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
  458 + });
  459 + }
  460 +
  461 +
455 }, 462 },
456 close: function () { 463 close: function () {
457 console.log('关闭视频'); 464 console.log('关闭视频');