Merge remote-tracking branch 'origin/wvp-28181-2.0' into commercial
# Conflicts: # sql/update.sql # src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java # src/main/java/com/genersoft/iot/vmp/service/IPlayService.java # src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java # web_src/src/components/dialog/deviceEdit.vue # web_src/src/components/dialog/devicePlayer.vue
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
#很久没维护了,已经与定前版本不匹配
|
||||
|
||||
|
||||
FROM ubuntu:20.04 AS build
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -107,6 +107,8 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
- [X] 云端录像(需要部署单独服务配合使用)
|
||||
- [X] 多流媒体节点,自动选择负载最低的节点使用。
|
||||
- [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。
|
||||
- [X] 支持电子地图。
|
||||
- [X] 支持接入WGS84和GCJ02两种坐标系。
|
||||
|
||||
[//]: # (# docker快速体验)
|
||||
|
||||
@@ -143,7 +145,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||
|
||||
# 合作
|
||||
目前很多打着合作的幌子来私聊的,其实大家大可不必,目前作者没有精力,你有问题可以付费找我解答,也可以提PR
|
||||
,如果对代码有建议可以提ISSUE;也可以加群一起聊聊。我们欢迎所有有兴趣但遇到项目中来的人。
|
||||
,如果对代码有建议可以提ISSUE;也可以加群一起聊聊。我们欢迎所有有兴趣参与到项目中来的人。
|
||||
|
||||
|
||||
|
||||
@@ -163,6 +165,7 @@ QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对
|
||||
[hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen)
|
||||
[chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb)
|
||||
[ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
|
||||
[mk1990](https://github.com/mk1990)
|
||||
|
||||
ps: 刚增加了这个名单,肯定遗漏了一些大佬,欢迎大佬联系我添加。
|
||||
|
||||
|
||||
9
pom.xml
9
pom.xml
@@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>com.genersoft</groupId>
|
||||
<artifactId>wvp-pro</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>2.3.1</version>
|
||||
<name>web video platform</name>
|
||||
<description>国标28181视频平台</description>
|
||||
|
||||
@@ -159,9 +159,10 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.73</version>
|
||||
<version>1.2.83</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- okhttp -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
@@ -180,9 +181,9 @@
|
||||
|
||||
<!-- okhttp-digest -->
|
||||
<dependency>
|
||||
<groupId>com.burgstaller</groupId>
|
||||
<groupId>io.github.rburgst</groupId>
|
||||
<artifactId>okhttp-digest</artifactId>
|
||||
<version>2.1</version>
|
||||
<version>2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
|
||||
|
||||
@@ -471,6 +471,7 @@ CREATE TABLE `stream_push` (
|
||||
`createStamp` bigint(20) DEFAULT NULL,
|
||||
`aliveSecond` int(11) DEFAULT NULL,
|
||||
`mediaServerId` varchar(50) DEFAULT NULL,
|
||||
`serverId` varchar(50) not NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `stream_push_pk` (`app`,`stream`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=300838 DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
alter table parent_platform
|
||||
add startOfflinePush int default 0 null;
|
||||
|
||||
alter table parent_platform
|
||||
add administrativeDivision varchar(50) not null;
|
||||
|
||||
alter table parent_platform
|
||||
add catalogGroup int default 1 null;
|
||||
|
||||
alter table device
|
||||
add audioChannelForReceive VARCHAR(50) null;
|
||||
|
||||
alter table device
|
||||
add audioChannelForSend VARCHAR(50) null;
|
||||
|
||||
alter table stream_push
|
||||
add serverId varchar(50) not null;
|
||||
alter table device
|
||||
add geoCoordSys varchar(50) not null;
|
||||
update device set device.geoCoordSys='WGS84';
|
||||
alter table device_channel
|
||||
add longitudeGcj02 double default null;
|
||||
alter table device_channel
|
||||
add latitudeGcj02 double default null;
|
||||
alter table device_channel
|
||||
add longitudeWgs84 double default null;
|
||||
alter table device_channel
|
||||
add latitudeWgs84 double default null;
|
||||
|
||||
|
||||
@@ -97,4 +97,5 @@ public class VideoManagerConstants {
|
||||
//************************** 第三方 ****************************************
|
||||
public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
|
||||
public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
|
||||
|
||||
}
|
||||
|
||||
@@ -103,12 +103,12 @@ public class DynamicTask {
|
||||
|
||||
public void stop(String key) {
|
||||
if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
|
||||
futureMap.get(key).cancel(true);
|
||||
Runnable runnable = runnableMap.get(key);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
// Runnable runnable = runnableMap.get(key);
|
||||
// if (runnable instanceof ISubscribeTask) {
|
||||
// ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
// subscribeTask.stop();
|
||||
// }
|
||||
futureMap.get(key).cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@Configuration("mediaConfig")
|
||||
public class MediaConfig{
|
||||
@@ -161,7 +165,18 @@ public class MediaConfig{
|
||||
if (StringUtils.isEmpty(sdpIp)){
|
||||
return ip;
|
||||
}else {
|
||||
return sdpIp;
|
||||
if (isValidIPAddress(sdpIp)) {
|
||||
return sdpIp;
|
||||
}else {
|
||||
// 按照域名解析
|
||||
String hostAddress = null;
|
||||
try {
|
||||
hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return hostAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,4 +226,11 @@ public class MediaConfig{
|
||||
return mediaServerItem;
|
||||
}
|
||||
|
||||
private boolean isValidIPAddress(String ipAddress) {
|
||||
if ((ipAddress != null) && (!ipAddress.isEmpty())) {
|
||||
return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGPSMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGpsMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisStreamMsgListener;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -47,11 +49,17 @@ public class RedisConfig extends CachingConfigurerSupport {
|
||||
private int poolMaxWait;
|
||||
|
||||
@Autowired
|
||||
private RedisGPSMsgListener redisGPSMsgListener;
|
||||
private RedisGpsMsgListener redisGPSMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisAlarmMsgListener redisAlarmMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisStreamMsgListener redisStreamMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
@Bean
|
||||
public JedisPool jedisPool() {
|
||||
if (StringUtils.isBlank(password)) {
|
||||
@@ -98,6 +106,8 @@ public class RedisConfig extends CachingConfigurerSupport {
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
|
||||
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
|
||||
container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
|
||||
container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY));
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置Spring Security
|
||||
* @author lin
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -132,15 +133,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
.anyRequest().authenticated()
|
||||
// 异常处理(权限拒绝、登录失效等)
|
||||
.and().exceptionHandling()
|
||||
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
|
||||
//匿名用户访问无权限资源时的异常处理
|
||||
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)
|
||||
// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
|
||||
// 登入
|
||||
.and().formLogin().permitAll()//允许所有用户
|
||||
.successHandler(loginSuccessHandler)//登录成功处理逻辑
|
||||
.failureHandler(loginFailureHandler)//登录失败处理逻辑
|
||||
// 登入 允许所有用户
|
||||
.and().formLogin().permitAll()
|
||||
//登录成功处理逻辑
|
||||
.successHandler(loginSuccessHandler)
|
||||
//登录失败处理逻辑
|
||||
.failureHandler(loginFailureHandler)
|
||||
// 登出
|
||||
.and().logout().logoutUrl("/api/user/logout").permitAll()//允许所有用户
|
||||
.logoutSuccessHandler(logoutHandler)//登出成功处理逻辑
|
||||
.and().logout().logoutUrl("/api/user/logout").permitAll()
|
||||
//登出成功处理逻辑
|
||||
.logoutSuccessHandler(logoutHandler)
|
||||
.deleteCookies("JSESSIONID")
|
||||
// 会话管理
|
||||
// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
/**
|
||||
* 国标设备/平台
|
||||
* @author lin
|
||||
*/
|
||||
public class Device {
|
||||
|
||||
/**
|
||||
@@ -127,7 +131,12 @@ public class Device {
|
||||
/**
|
||||
* 是否开启ssrc校验,默认关闭,开启可以防止串流
|
||||
*/
|
||||
private boolean ssrcCheck;
|
||||
private boolean ssrcCheck = true;
|
||||
|
||||
/**
|
||||
* 地理坐标系, 目前支持 WGS84,GCJ02 TODO CGCS2000
|
||||
*/
|
||||
private String geoCoordSys;
|
||||
|
||||
|
||||
public String getDeviceId() {
|
||||
@@ -322,4 +331,12 @@ public class Device {
|
||||
this.ssrcCheck = ssrcCheck;
|
||||
}
|
||||
|
||||
public String getGeoCoordSys() {
|
||||
return geoCoordSys;
|
||||
}
|
||||
|
||||
public void setGeoCoordSys(String geoCoordSys) {
|
||||
this.geoCoordSys = geoCoordSys;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -154,6 +154,26 @@ public class DeviceChannel {
|
||||
*/
|
||||
private double latitude;
|
||||
|
||||
/**
|
||||
* 经度 GCJ02
|
||||
*/
|
||||
private double longitudeGcj02;
|
||||
|
||||
/**
|
||||
* 纬度 GCJ02
|
||||
*/
|
||||
private double latitudeGcj02;
|
||||
|
||||
/**
|
||||
* 经度 WGS84
|
||||
*/
|
||||
private double longitudeWgs84;
|
||||
|
||||
/**
|
||||
* 纬度 WGS84
|
||||
*/
|
||||
private double latitudeWgs84;
|
||||
|
||||
/**
|
||||
* 子设备数
|
||||
*/
|
||||
@@ -407,6 +427,38 @@ public class DeviceChannel {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public double getLongitudeGcj02() {
|
||||
return longitudeGcj02;
|
||||
}
|
||||
|
||||
public void setLongitudeGcj02(double longitudeGcj02) {
|
||||
this.longitudeGcj02 = longitudeGcj02;
|
||||
}
|
||||
|
||||
public double getLatitudeGcj02() {
|
||||
return latitudeGcj02;
|
||||
}
|
||||
|
||||
public void setLatitudeGcj02(double latitudeGcj02) {
|
||||
this.latitudeGcj02 = latitudeGcj02;
|
||||
}
|
||||
|
||||
public double getLongitudeWgs84() {
|
||||
return longitudeWgs84;
|
||||
}
|
||||
|
||||
public void setLongitudeWgs84(double longitudeWgs84) {
|
||||
this.longitudeWgs84 = longitudeWgs84;
|
||||
}
|
||||
|
||||
public double getLatitudeWgs84() {
|
||||
return latitudeWgs84;
|
||||
}
|
||||
|
||||
public void setLatitudeWgs84(double latitudeWgs84) {
|
||||
this.latitudeWgs84 = latitudeWgs84;
|
||||
}
|
||||
|
||||
public int getSubCount() {
|
||||
return subCount;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import org.dom4j.Element;
|
||||
|
||||
import javax.sip.RequestEvent;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public class HandlerCatchData {
|
||||
private RequestEvent evt;
|
||||
private Device device;
|
||||
private Element rootElement;
|
||||
|
||||
public HandlerCatchData(RequestEvent evt, Device device, Element rootElement) {
|
||||
this.evt = evt;
|
||||
this.device = device;
|
||||
this.rootElement = rootElement;
|
||||
}
|
||||
|
||||
public RequestEvent getEvt() {
|
||||
return evt;
|
||||
}
|
||||
|
||||
public void setEvt(RequestEvent evt) {
|
||||
this.evt = evt;
|
||||
}
|
||||
|
||||
public Device getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
public void setDevice(Device device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public Element getRootElement() {
|
||||
return rootElement;
|
||||
}
|
||||
|
||||
public void setRootElement(Element rootElement) {
|
||||
this.rootElement = rootElement;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,11 @@ public class SendRtpItem {
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 使用的服务的ID
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* invite的callId
|
||||
*/
|
||||
@@ -259,4 +264,12 @@ public class SendRtpItem {
|
||||
public void setOnlyAudio(boolean onlyAudio) {
|
||||
this.onlyAudio = onlyAudio;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@@ -38,7 +39,6 @@ public class SubscribeHolder {
|
||||
catalogMap.put(platformId, subscribeInfo);
|
||||
// 添加订阅到期
|
||||
String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()),
|
||||
subscribeInfo.getExpires() * 1000);
|
||||
@@ -49,10 +49,17 @@ public class SubscribeHolder {
|
||||
}
|
||||
|
||||
public void removeCatalogSubscribe(String platformId) {
|
||||
|
||||
catalogMap.remove(platformId);
|
||||
String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
|
||||
Runnable runnable = dynamicTask.get(taskOverdueKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
|
||||
}
|
||||
|
||||
public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) {
|
||||
@@ -63,7 +70,6 @@ public class SubscribeHolder {
|
||||
storager, platformId, subscribeInfo.getSn(), key, this, dynamicTask),
|
||||
subscribeInfo.getGpsInterval() * 1000);
|
||||
String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.startDelay(taskOverdueKey, () -> {
|
||||
removeMobilePositionSubscribe(subscribeInfo.getId());
|
||||
@@ -81,6 +87,11 @@ public class SubscribeHolder {
|
||||
// 结束任务处理GPS定时推送
|
||||
dynamicTask.stop(key);
|
||||
String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
|
||||
Runnable runnable = dynamicTask.get(taskOverdueKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ public class SipSubscribe {
|
||||
this.type = "timeout";
|
||||
this.msg = "消息超时未回复";
|
||||
this.statusCode = -1024;
|
||||
this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();
|
||||
this.dialog = timeoutEvent.getClientTransaction().getDialog();
|
||||
this.callId = this.dialog != null?timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId(): null;
|
||||
}else if (event instanceof TransactionTerminatedEvent) {
|
||||
TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
|
||||
this.type = "transactionTerminated";
|
||||
@@ -109,8 +109,8 @@ public class SipSubscribe {
|
||||
this.type = "deviceNotFoundEvent";
|
||||
this.msg = "设备未找到";
|
||||
this.statusCode = -1024;
|
||||
this.callId = deviceNotFoundEvent.getDialog().getCallId().getCallId();
|
||||
this.dialog = deviceNotFoundEvent.getDialog();
|
||||
this.callId = this.dialog != null ?deviceNotFoundEvent.getDialog().getCallId().getCallId() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,9 @@ public class SipSubscribe {
|
||||
}
|
||||
|
||||
public void removeErrorSubscribe(String key) {
|
||||
if(key == null){
|
||||
return;
|
||||
}
|
||||
errorSubscribes.remove(key);
|
||||
errorTimeSubscribes.remove(key);
|
||||
}
|
||||
@@ -139,6 +142,9 @@ public class SipSubscribe {
|
||||
}
|
||||
|
||||
public void removeOkSubscribe(String key) {
|
||||
if(key == null){
|
||||
return;
|
||||
}
|
||||
okSubscribes.remove(key);
|
||||
okTimeSubscribes.remove(key);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
|
||||
subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
|
||||
|
||||
if (subscribe == null) {
|
||||
logger.info("发送订阅消息时发现订阅信息已经不存在");
|
||||
logger.info("发送订阅消息时发现订阅信息已经不存在: {}", event.getPlatformId());
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
|
||||
@@ -99,8 +99,8 @@ public class VideoStreamSessionManager {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public SIPDialog getDialogByCallId(String deviceId, String channelId, String callID){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callID, null);
|
||||
public SIPDialog getDialogByCallId(String deviceId, String channelId, String callId){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
|
||||
if (ssrcTransaction == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -108,11 +108,17 @@ public class VideoStreamSessionManager {
|
||||
if (dialogByteArray == null) {
|
||||
return null;
|
||||
}
|
||||
SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
|
||||
return dialog;
|
||||
return (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
|
||||
}
|
||||
|
||||
public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
|
||||
|
||||
if (StringUtils.isEmpty(deviceId)) {
|
||||
deviceId ="*";
|
||||
}
|
||||
if (StringUtils.isEmpty(channelId)) {
|
||||
channelId ="*";
|
||||
}
|
||||
if (StringUtils.isEmpty(callId)) {
|
||||
callId ="*";
|
||||
}
|
||||
@@ -179,7 +185,7 @@ public class VideoStreamSessionManager {
|
||||
|
||||
|
||||
public List<SsrcTransaction> getAllSsrc() {
|
||||
List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId() + "_" ));
|
||||
List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId()));
|
||||
List<SsrcTransaction> result= new ArrayList<>();
|
||||
for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
|
||||
String key = (String)ssrcTransactionKeys.get(i);
|
||||
|
||||
@@ -71,7 +71,9 @@ public class MobilePositionSubscribeHandlerTask implements ISubscribeTask {
|
||||
String gbId = gbStream.getGbId();
|
||||
GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId);
|
||||
if (gpsMsgInfo != null) { // 无最新位置不发送
|
||||
logger.info("无最新位置不发送");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("无最新位置不发送");
|
||||
}
|
||||
// 经纬度都为0不发送
|
||||
if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) {
|
||||
continue;
|
||||
|
||||
@@ -150,30 +150,24 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
|
||||
public void processTimeout(TimeoutEvent timeoutEvent) {
|
||||
logger.info("[消息发送超时]");
|
||||
ClientTransaction clientTransaction = timeoutEvent.getClientTransaction();
|
||||
eventPublisher.requestTimeOut(timeoutEvent);
|
||||
|
||||
if (clientTransaction != null) {
|
||||
logger.info("[发送错误订阅] clientTransaction != null");
|
||||
Request request = clientTransaction.getRequest();
|
||||
if (request != null) {
|
||||
logger.info("[发送错误订阅] request != null");
|
||||
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
|
||||
if (callIdHeader != null) {
|
||||
logger.info("[发送错误订阅]");
|
||||
SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
|
||||
SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent);
|
||||
subscribe.response(eventResult);
|
||||
sipSubscribe.removeOkSubscribe(callIdHeader.getCallId());
|
||||
sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout timeout = timeoutEvent.getTimeout();
|
||||
// ServerTransaction serverTransaction = timeoutEvent.getServerTransaction();
|
||||
// if (serverTransaction != null) {
|
||||
// Request request = serverTransaction.getRequest();
|
||||
// URI requestURI = request.getRequestURI();
|
||||
// Header header = request.getHeader(FromHeader.NAME);
|
||||
// }
|
||||
// if(timeoutProcessor != null) {
|
||||
// timeoutProcessor.process(timeoutEvent);
|
||||
// }
|
||||
eventPublisher.requestTimeOut(timeoutEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -149,6 +149,14 @@ public interface ISIPCommander {
|
||||
*/
|
||||
void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
|
||||
|
||||
/**
|
||||
* 回放控制
|
||||
* @param device
|
||||
* @param streamInfo
|
||||
* @param content
|
||||
*/
|
||||
void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
|
||||
|
||||
|
||||
/**
|
||||
* 语音广播
|
||||
|
||||
@@ -103,6 +103,14 @@ public interface ISIPCommanderForPlatform {
|
||||
*/
|
||||
boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo);
|
||||
|
||||
/**
|
||||
* 录像播放推送完成时发送MediaStatus消息
|
||||
* @param platform
|
||||
* @param sendRtpItem
|
||||
* @return
|
||||
*/
|
||||
boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem);
|
||||
|
||||
/**
|
||||
* 向发起点播的上级回复bye
|
||||
* @param platform 平台信息
|
||||
|
||||
@@ -32,7 +32,9 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sip.*;
|
||||
import javax.sip.address.Address;
|
||||
import javax.sip.address.SipURI;
|
||||
import javax.sip.address.URI;
|
||||
import javax.sip.header.*;
|
||||
import javax.sip.message.Request;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -708,22 +710,19 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
SIPDialog dialog;
|
||||
if (callId != null) {
|
||||
dialog = streamSession.getDialogByCallId(deviceId, channelId, callId);
|
||||
dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);
|
||||
}else {
|
||||
if (stream == null) {
|
||||
if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {
|
||||
return;
|
||||
}
|
||||
dialog = streamSession.getDialogByStream(deviceId, channelId, stream);
|
||||
}
|
||||
if (ssrcTransaction != null) {
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
|
||||
mediaServerService.closeRTPServer(deviceId, channelId, ssrcTransaction.getStream());
|
||||
streamSession.remove(deviceId, channelId, ssrcTransaction.getStream());
|
||||
dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
|
||||
}
|
||||
mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
|
||||
mediaServerService.closeRTPServer(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
|
||||
streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
|
||||
|
||||
if (dialog == null) {
|
||||
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId);
|
||||
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
|
||||
return;
|
||||
}
|
||||
SipStack sipStack = udpSipProvider.getSipStack();
|
||||
@@ -1456,12 +1455,20 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
Request request;
|
||||
if (dialog != null) {
|
||||
logger.info("发送移动位置订阅消息时 dialog的状态为: {}", dialog.getState());
|
||||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
|
||||
request = dialog.createRequest(Request.SUBSCRIBE);
|
||||
ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
|
||||
request.setExpires(expiresHeader);
|
||||
|
||||
request.setRequestURI(requestURI);
|
||||
|
||||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
||||
request.setContent(subscribePostitionXml.toString(), contentTypeHeader);
|
||||
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());
|
||||
request.addHeader(expireHeader);
|
||||
|
||||
CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
|
||||
cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
|
||||
request.removeHeader(CSeqHeader.NAME);
|
||||
request.addHeader(cSeqHeader);
|
||||
}else {
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
|
||||
@@ -1552,12 +1559,21 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
Request request;
|
||||
if (dialog != null) {
|
||||
logger.info("发送目录订阅消息时 dialog的状态为: {}", dialog.getState());
|
||||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
|
||||
request = dialog.createRequest(Request.SUBSCRIBE);
|
||||
ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
|
||||
request.setExpires(expiresHeader);
|
||||
|
||||
request.setRequestURI(requestURI);
|
||||
|
||||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
||||
request.setContent(cmdXml.toString(), contentTypeHeader);
|
||||
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());
|
||||
request.addHeader(expireHeader);
|
||||
|
||||
CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
|
||||
cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
|
||||
request.removeHeader(CSeqHeader.NAME);
|
||||
request.addHeader(cSeqHeader);
|
||||
|
||||
}else {
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
|
||||
@@ -1780,6 +1796,43 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
|
||||
try {
|
||||
Request request = headerProvider.createInfoRequest(device, streamInfo, content);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
logger.info(request.toString());
|
||||
ClientTransaction clientTransaction = null;
|
||||
if ("TCP".equals(device.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(request);
|
||||
} else if ("UDP".equals(device.getTransport())) {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(request);
|
||||
}
|
||||
CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
|
||||
if(errorEvent != null) {
|
||||
sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
|
||||
errorEvent.response(eventResult);
|
||||
sipSubscribe.removeErrorSubscribe(eventResult.callId);
|
||||
sipSubscribe.removeOkSubscribe(eventResult.callId);
|
||||
}));
|
||||
}
|
||||
|
||||
if(okEvent != null) {
|
||||
sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
|
||||
okEvent.response(eventResult);
|
||||
sipSubscribe.removeOkSubscribe(eventResult.callId);
|
||||
sipSubscribe.removeErrorSubscribe(eventResult.callId);
|
||||
});
|
||||
}
|
||||
clientTransaction.sendRequest();
|
||||
|
||||
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
|
||||
if (device == null) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.sip.address.SipURI;
|
||||
import javax.sip.header.*;
|
||||
import javax.sip.message.Request;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -276,8 +277,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n");
|
||||
catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
|
||||
catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
|
||||
catalogXml.append("<Longitude>" + channel.getLongitude() + "</Longitude>\r\n");
|
||||
catalogXml.append("<Latitude>" + channel.getLatitude() + "</Latitude>\r\n");
|
||||
catalogXml.append("<Longitude>" + channel.getLongitudeWgs84() + "</Longitude>\r\n");
|
||||
catalogXml.append("<Latitude>" + channel.getLatitudeWgs84() + "</Latitude>\r\n");
|
||||
catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
|
||||
catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
|
||||
catalogXml.append("<Info>\r\n");
|
||||
@@ -546,14 +547,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
}
|
||||
notifyRequest.addHeader(event);
|
||||
SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
|
||||
if (subscribeInfo.getTransaction() != null) {
|
||||
SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
|
||||
sipURI.setHost(request.getRemoteAddress().getHostAddress());
|
||||
sipURI.setPort(request.getRemotePort());
|
||||
}else {
|
||||
sipURI.setHost(parentPlatform.getServerIP());
|
||||
sipURI.setPort(parentPlatform.getServerPort());
|
||||
}
|
||||
sipURI.setHost(parentPlatform.getServerIP());
|
||||
sipURI.setPort(parentPlatform.getServerPort());
|
||||
|
||||
ClientTransaction transaction = null;
|
||||
if ("TCP".equals(parentPlatform.getTransport())) {
|
||||
@@ -750,6 +745,82 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) {
|
||||
if (sendRtpItem == null) {
|
||||
return false;
|
||||
}
|
||||
if (platform == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] dialogByteArray = sendRtpItem.getDialog();
|
||||
if (dialogByteArray == null) {
|
||||
return false;
|
||||
}
|
||||
try{
|
||||
SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
|
||||
SipStack sipStack;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
sipStack = tcpSipProvider.getSipStack();
|
||||
} else {
|
||||
sipStack = udpSipProvider.getSipStack();
|
||||
}
|
||||
SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
|
||||
if (dialog != sipDialog) {
|
||||
dialog = sipDialog;
|
||||
}
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
dialog.setSipProvider(tcpSipProvider);
|
||||
} else {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
}
|
||||
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
|
||||
SIPRequest messageRequest = (SIPRequest)dialog.createRequest(Request.MESSAGE);
|
||||
String characterSet = platform.getCharacterSet();
|
||||
StringBuffer mediaStatusXml = new StringBuffer(200);
|
||||
mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
|
||||
mediaStatusXml.append("<Notify>\r\n");
|
||||
mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
|
||||
mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
|
||||
mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
|
||||
mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
|
||||
mediaStatusXml.append("</Notify>\r\n");
|
||||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
||||
messageRequest.setContent(mediaStatusXml.toString(), contentTypeHeader);
|
||||
SipURI sipURI = (SipURI) messageRequest.getRequestURI();
|
||||
sipURI.setHost(platform.getServerIP());
|
||||
sipURI.setPort(platform.getServerPort());
|
||||
ClientTransaction clientTransaction;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(messageRequest);
|
||||
}else {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(messageRequest);
|
||||
}
|
||||
dialog.sendRequest(clientTransaction);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamByeCmd(ParentPlatform platform, String callId) {
|
||||
if (platform == null) {
|
||||
@@ -766,45 +837,51 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
byte[] dialogByteArray = sendRtpItem.getDialog();
|
||||
if (dialogByteArray != null) {
|
||||
SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
|
||||
SipStack sipStack = udpSipProvider.getSipStack();
|
||||
SipStack sipStack;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
sipStack = tcpSipProvider.getSipStack();
|
||||
} else {
|
||||
sipStack = udpSipProvider.getSipStack();
|
||||
}
|
||||
SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
|
||||
if (dialog != sipDialog) {
|
||||
dialog = sipDialog;
|
||||
} else {
|
||||
try {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
|
||||
byte[] transactionByteArray = sendRtpItem.getTransaction();
|
||||
ClientTransaction clientTransaction = (ClientTransaction) SerializeUtils.deSerialize(transactionByteArray);
|
||||
Request byeRequest = dialog.createRequest(Request.BYE);
|
||||
|
||||
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
|
||||
SIPRequest request = (SIPRequest) clientTransaction.getRequest();
|
||||
byeURI.setHost(request.getRemoteAddress().getHostAddress());
|
||||
byeURI.setPort(request.getRemotePort());
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
|
||||
} else if ("UDP".equals(platform.getTransport())) {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
|
||||
}
|
||||
dialog.sendRequest(clientTransaction);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
dialog.setSipProvider(tcpSipProvider);
|
||||
} else {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
}
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
|
||||
Request byeRequest = dialog.createRequest(Request.BYE);
|
||||
|
||||
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
|
||||
byeURI.setHost(platform.getServerIP());
|
||||
byeURI.setPort(platform.getServerPort());
|
||||
ClientTransaction clientTransaction;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
|
||||
} else {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
|
||||
}
|
||||
dialog.sendRequest(clientTransaction);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request;
|
||||
|
||||
import gov.nist.javax.sip.SipProviderImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* @description:处理接收IPCamera发来的SIP协议请求消息
|
||||
* @author: songww
|
||||
* @date: 2020年5月3日 下午4:42:22
|
||||
*/
|
||||
public abstract class SIPRequestProcessorAbstract {
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier(value="tcpSipProvider")
|
||||
private SipProviderImpl tcpSipProvider;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(value="udpSipProvider")
|
||||
private SipProviderImpl udpSipProvider;
|
||||
|
||||
}
|
||||
@@ -15,10 +15,13 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import com.genersoft.iot.vmp.utils.SerializeUtils;
|
||||
import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -46,7 +49,7 @@ import java.util.*;
|
||||
public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
|
||||
private String method = "ACK";
|
||||
private final String method = "ACK";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
@@ -84,6 +87,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
@Autowired
|
||||
private AudioBroadcastManager audioBroadcastManager;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
|
||||
/**
|
||||
* 处理 ACK请求
|
||||
@@ -159,60 +165,41 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
// 向上级平台
|
||||
commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
|
||||
}
|
||||
if (mediaInfo == null) {
|
||||
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
|
||||
sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(),
|
||||
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
|
||||
sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
|
||||
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, jsonObject->{
|
||||
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
|
||||
});
|
||||
}else {
|
||||
JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
|
||||
}
|
||||
|
||||
|
||||
// if (streamInfo == null) { // 流还没上来,对方就回复ack
|
||||
// logger.info("监听流以等待流上线1 rtp/{}", sendRtpItem.getStreamId());
|
||||
// // 监听流上线
|
||||
// // 添加订阅
|
||||
// JSONObject subscribeKey = new JSONObject();
|
||||
// subscribeKey.put("app", "rtp");
|
||||
// subscribeKey.put("stream", sendRtpItem.getStreamId());
|
||||
// subscribeKey.put("regist", true);
|
||||
// subscribeKey.put("schema", "rtmp");
|
||||
// subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());
|
||||
// subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
// (MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
// Map<String, Object> param = new HashMap<>();
|
||||
// param.put("vhost","__defaultVhost__");
|
||||
// param.put("app",json.getString("app"));
|
||||
// param.put("stream",json.getString("stream"));
|
||||
// param.put("ssrc", sendRtpItem.getSsrc());
|
||||
// param.put("dst_url",sendRtpItem.getIp());
|
||||
// param.put("dst_port", sendRtpItem.getPort());
|
||||
// param.put("is_udp", is_Udp);
|
||||
// param.put("src_port", sendRtpItem.getLocalPort());
|
||||
// zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// });
|
||||
// }else {
|
||||
// Map<String, Object> param = new HashMap<>();
|
||||
// param.put("vhost","__defaultVhost__");
|
||||
// param.put("app",streamInfo.getApp());
|
||||
// param.put("stream",streamInfo.getStream());
|
||||
// param.put("ssrc", sendRtpItem.getSsrc());
|
||||
// param.put("dst_url",sendRtpItem.getIp());
|
||||
// param.put("dst_port", sendRtpItem.getPort());
|
||||
// param.put("is_udp", is_Udp);
|
||||
// param.put("src_port", sendRtpItem.getLocalPort());
|
||||
//
|
||||
// JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// if (jsonObject.getInteger("code") != 0) {
|
||||
// logger.info("监听流以等待流上线2 {}/{}", streamInfo.getApp(), streamInfo.getStream());
|
||||
// // 监听流上线
|
||||
// // 添加订阅
|
||||
// JSONObject subscribeKey = new JSONObject();
|
||||
// subscribeKey.put("app", "rtp");
|
||||
// subscribeKey.put("stream", streamInfo.getStream());
|
||||
// subscribeKey.put("regist", true);
|
||||
// subscribeKey.put("schema", "rtmp");
|
||||
// subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());
|
||||
// subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
// (MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
// zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
|
||||
JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
|
||||
if (jsonObject == null) {
|
||||
logger.error("RTP推流失败: 请检查ZLM服务");
|
||||
} else if (jsonObject.getInteger("code") == 0) {
|
||||
logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
|
||||
byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
|
||||
sendRtpItem.setDialog(dialogByteArray);
|
||||
byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
|
||||
sendRtpItem.setTransaction(transactionByteArray);
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
} else {
|
||||
logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param));
|
||||
if (sendRtpItem.isOnlyAudio()) {
|
||||
// TODO 可能是语音对讲
|
||||
}else {
|
||||
// 向上级平台
|
||||
commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +114,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
|
||||
}
|
||||
if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
|
||||
MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
|
||||
messageForPushChannel.setType(0);
|
||||
messageForPushChannel.setGbId(sendRtpItem.getChannelId());
|
||||
messageForPushChannel.setApp(sendRtpItem.getApp());
|
||||
messageForPushChannel.setStream(sendRtpItem.getStreamId());
|
||||
messageForPushChannel.setMediaServerId(sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormId(sendRtpItem.getPlatformId());
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import javax.sip.RequestEvent;
|
||||
@Component
|
||||
public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||
|
||||
private String method = "CANCEL";
|
||||
private final String method = "CANCEL";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
@@ -18,6 +17,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Element;
|
||||
@@ -25,6 +25,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -35,6 +37,7 @@ import javax.sip.header.FromHeader;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* SIP命令类型: NOTIFY请求
|
||||
@@ -63,11 +66,19 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Autowired
|
||||
private EventPublisher publisher;
|
||||
|
||||
private String method = "NOTIFY";
|
||||
private final String method = "NOTIFY";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
private boolean taskQueueHandlerRun = false;
|
||||
|
||||
private final ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 添加消息处理的订阅
|
||||
@@ -77,23 +88,40 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
try {
|
||||
Element rootElement = getRootElement(evt);
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
|
||||
if (CmdType.CATALOG.equals(cmd)) {
|
||||
logger.info("接收到Catalog通知");
|
||||
processNotifyCatalogList(evt);
|
||||
} else if (CmdType.ALARM.equals(cmd)) {
|
||||
logger.info("接收到Alarm通知");
|
||||
processNotifyAlarm(evt);
|
||||
} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
|
||||
logger.info("接收到MobilePosition通知");
|
||||
processNotifyMobilePosition(evt);
|
||||
} else {
|
||||
logger.info("接收到消息:" + cmd);
|
||||
responseAck(evt, Response.OK);
|
||||
taskQueue.offer(new HandlerCatchData(evt, null, null));
|
||||
responseAck(evt, Response.OK);
|
||||
if (!taskQueueHandlerRun) {
|
||||
taskQueueHandlerRun = true;
|
||||
taskExecutor.execute(()-> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
try {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
Element rootElement = getRootElement(take.getEvt());
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
|
||||
if (CmdType.CATALOG.equals(cmd)) {
|
||||
logger.info("接收到Catalog通知");
|
||||
processNotifyCatalogList(take.getEvt());
|
||||
} else if (CmdType.ALARM.equals(cmd)) {
|
||||
logger.info("接收到Alarm通知");
|
||||
processNotifyAlarm(take.getEvt());
|
||||
} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
|
||||
logger.info("接收到MobilePosition通知");
|
||||
processNotifyMobilePosition(take.getEvt());
|
||||
} else {
|
||||
logger.info("接收到消息:" + cmd);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
taskQueueHandlerRun = false;
|
||||
});
|
||||
}
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
|
||||
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -166,8 +194,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
jsonObject.put("direction", mobilePosition.getDirection());
|
||||
jsonObject.put("speed", mobilePosition.getSpeed());
|
||||
redisCatchStorage.sendMobilePositionMsg(jsonObject);
|
||||
responseAck(evt, Response.OK);
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -188,6 +215,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
if (device == null) {
|
||||
logger.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId);
|
||||
return;
|
||||
}
|
||||
rootElement = getRootElement(evt, device.getCharset());
|
||||
@@ -195,7 +223,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
deviceAlarm.setDeviceId(deviceId);
|
||||
deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
|
||||
deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
|
||||
deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
|
||||
String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
|
||||
if (alarmTime == null) {
|
||||
logger.warn("[ NotifyAlarm ] AlarmTime cannot be null");
|
||||
return;
|
||||
}
|
||||
deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
|
||||
if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
|
||||
deviceAlarm.setAlarmDescription("");
|
||||
} else {
|
||||
@@ -212,7 +245,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
deviceAlarm.setLatitude(0.00);
|
||||
}
|
||||
logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId());
|
||||
if (deviceAlarm.getAlarmMethod().equals("4")) {
|
||||
if ("4".equals(deviceAlarm.getAlarmMethod())) {
|
||||
MobilePosition mobilePosition = new MobilePosition();
|
||||
mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
|
||||
mobilePosition.setTime(deviceAlarm.getAlarmTime());
|
||||
@@ -233,11 +266,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
// TODO: 需要实现存储报警信息、报警分类
|
||||
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
if (redisCatchStorage.deviceIsOnline(deviceId)) {
|
||||
publisher.deviceAlarmEventPublish(deviceAlarm);
|
||||
}
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -273,64 +305,60 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
continue;
|
||||
}
|
||||
Element eventElement = itemDevice.element("Event");
|
||||
DeviceChannel channel = XmlUtil.channelContentHander(itemDevice);
|
||||
String event;
|
||||
if (eventElement == null) {
|
||||
logger.warn("[收到 目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
|
||||
event = CatalogEvent.ADD;
|
||||
}else {
|
||||
event = eventElement.getText().toUpperCase();
|
||||
}
|
||||
DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device);
|
||||
channel.setDeviceId(device.getDeviceId());
|
||||
logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
|
||||
switch (eventElement.getText().toUpperCase()) {
|
||||
switch (event) {
|
||||
case CatalogEvent.ON:
|
||||
// 上线
|
||||
logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOnline(deviceId, channel.getChannelId());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.OFF :
|
||||
// 离线
|
||||
logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.VLOST:
|
||||
// 视频丢失
|
||||
logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.DEFECT:
|
||||
// 故障
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.ADD:
|
||||
// 增加
|
||||
logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.updateChannel(deviceId, channel);
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.DEL:
|
||||
// 删除
|
||||
logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.delChannel(deviceId, channel.getChannelId());
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.UPDATE:
|
||||
// 更新
|
||||
logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.updateChannel(deviceId, channel);
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
default:
|
||||
responseAck(evt, Response.BAD_REQUEST, "event not found");
|
||||
logger.warn("[ NotifyCatalog ] event not found : {}", event );
|
||||
|
||||
}
|
||||
// 转发变化信息
|
||||
eventPublisher.catalogEventPublish(null, channel, eventElement.getText().toUpperCase());
|
||||
eventPublisher.catalogEventPublish(null, channel, event);
|
||||
|
||||
}
|
||||
}
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import gov.nist.javax.sip.RequestEventExt;
|
||||
import gov.nist.javax.sip.address.AddressImpl;
|
||||
@@ -45,20 +41,11 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
|
||||
|
||||
public String method = "REGISTER";
|
||||
public final String method = "REGISTER";
|
||||
|
||||
@Autowired
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private EventPublisher publisher;
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
@@ -86,7 +73,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
|
||||
Response response = null;
|
||||
boolean passwordCorrect = false;
|
||||
// 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功
|
||||
// 注册标志
|
||||
boolean registerFlag = false;
|
||||
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
|
||||
AddressImpl address = (AddressImpl) fromHeader.getAddress();
|
||||
@@ -105,7 +92,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
// 校验密码是否正确
|
||||
passwordCorrect = StringUtils.isEmpty(sipConfig.getPassword()) ||
|
||||
new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, sipConfig.getPassword());
|
||||
// 未携带授权头或者密码错误 回复401
|
||||
|
||||
if (!passwordCorrect) {
|
||||
// 注册失败
|
||||
@@ -154,6 +140,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
device = new Device();
|
||||
device.setStreamMode("UDP");
|
||||
device.setCharset("GB2312");
|
||||
device.setGeoCoordSys("WGS84");
|
||||
device.setDeviceId(deviceId);
|
||||
}
|
||||
device.setIp(received);
|
||||
|
||||
@@ -82,7 +82,6 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
Request request = evt.getRequest();
|
||||
|
||||
try {
|
||||
Element rootElement = getRootElement(evt);
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
@@ -176,6 +175,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
}
|
||||
|
||||
private void processNotifyCatalogList(RequestEvent evt, Element rootElement) throws SipException {
|
||||
|
||||
System.out.println(evt.getRequest().toString());
|
||||
String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
|
||||
String deviceId = XmlUtil.getText(rootElement, "DeviceID");
|
||||
ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info;
|
||||
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.RequestEvent;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.header.*;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
|
||||
@Component
|
||||
public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class);
|
||||
|
||||
private final String method = "INFO";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private SipSubscribe sipSubscribe;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private SIPCommander cmder;
|
||||
|
||||
@Autowired
|
||||
private VideoStreamSessionManager sessionManager;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 添加消息处理的订阅
|
||||
sipProcessorObserver.addRequestProcessor(method, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
logger.debug("接收到消息:" + evt.getRequest());
|
||||
String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
|
||||
// 先从会话内查找
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
|
||||
if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
|
||||
deviceId = ssrcTransaction.getDeviceId();
|
||||
}
|
||||
// 查询设备是否存在
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
// 查询上级平台是否存在
|
||||
ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
|
||||
try {
|
||||
if (device != null && parentPlatform != null) {
|
||||
logger.warn("[重复]平台与设备编号重复:{}", deviceId);
|
||||
SIPRequest request = (SIPRequest) evt.getRequest();
|
||||
String hostAddress = request.getRemoteAddress().getHostAddress();
|
||||
int remotePort = request.getRemotePort();
|
||||
if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
|
||||
parentPlatform = null;
|
||||
}else {
|
||||
device = null;
|
||||
}
|
||||
}
|
||||
if (device == null && parentPlatform == null) {
|
||||
// 不存在则回复404
|
||||
responseAck(evt, Response.NOT_FOUND, "device "+ deviceId +" not found");
|
||||
logger.warn("[设备未找到 ]: {}", deviceId);
|
||||
if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
|
||||
SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
|
||||
sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
|
||||
};
|
||||
}else {
|
||||
ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME);
|
||||
String contentType = header.getContentType();
|
||||
String contentSubType = header.getContentSubType();
|
||||
if ("Application".equals(contentType) && "MANSRTSP".equals(contentSubType)) {
|
||||
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
|
||||
String streamId = sendRtpItem.getStreamId();
|
||||
StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
|
||||
if (null == streamInfo) {
|
||||
responseAck(evt, Response.NOT_FOUND, "stream " + streamId + " not found");
|
||||
return;
|
||||
}
|
||||
Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
|
||||
cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> {
|
||||
// 失败的回复
|
||||
try {
|
||||
responseAck(evt, eventResult.statusCode, eventResult.msg);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, eventResult -> {
|
||||
// 成功的回复
|
||||
try {
|
||||
responseAck(evt, eventResult.statusCode);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (SipException e) {
|
||||
logger.warn("SIP 回复错误", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
logger.warn("参数无效", e);
|
||||
} catch (ParseException e) {
|
||||
logger.warn("SIP回复时解析异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
|
||||
String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
|
||||
// 先从会话内查找
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, null, callIdHeader.getCallId());
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
|
||||
if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
|
||||
deviceId = ssrcTransaction.getDeviceId();
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.service.IDeviceAlarmService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -84,7 +86,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
deviceAlarm.setChannelId(channelId);
|
||||
deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
|
||||
deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
|
||||
deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
|
||||
String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
|
||||
if (alarmTime == null) {
|
||||
return;
|
||||
}
|
||||
deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
|
||||
String alarmDescription = getText(rootElement, "AlarmDescription");
|
||||
if (alarmDescription == null) {
|
||||
deviceAlarm.setAlarmDescription("");
|
||||
@@ -175,7 +181,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
deviceAlarm.setChannelId(channelId);
|
||||
deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
|
||||
deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
|
||||
deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
|
||||
String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
|
||||
if (alarmTime == null) {
|
||||
return;
|
||||
}
|
||||
deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
|
||||
String alarmDescription = getText(rootElement, "AlarmDescription");
|
||||
if (alarmDescription == null) {
|
||||
deviceAlarm.setAlarmDescription("");
|
||||
|
||||
@@ -64,16 +64,14 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
|
||||
device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
|
||||
}
|
||||
device.setKeepaliveTime(DateUtil.getNow());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
if (device.getOnline() == 1) {
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
deviceService.updateDevice(device);
|
||||
}else {
|
||||
// 对于已经离线的设备判断他的注册是否已经过期
|
||||
if (!deviceService.expire(device)){
|
||||
deviceService.online(device);
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
}
|
||||
}
|
||||
} catch (SipException e) {
|
||||
|
||||
@@ -3,11 +3,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,9 +41,18 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
|
||||
@Autowired
|
||||
private SIPCommander cmder;
|
||||
|
||||
@Autowired
|
||||
private SIPCommanderFroPlatform sipCommanderFroPlatform;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private VideoStreamSessionManager sessionManager;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
notifyMessageHandler.addHandler(cmdType, this);
|
||||
@@ -59,17 +73,32 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
|
||||
}
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
|
||||
String NotifyType =getText(rootElement, "NotifyType");
|
||||
if (NotifyType.equals("121")){
|
||||
if ("121".equals(NotifyType)){
|
||||
logger.info("[录像流]推送完毕,收到关流通知");
|
||||
String channelId =getText(rootElement, "DeviceID");
|
||||
// 查询是设备
|
||||
StreamInfo streamInfo = redisCatchStorage.queryDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
|
||||
// 设置进度100%
|
||||
streamInfo.setProgress(1);
|
||||
redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
|
||||
// TODO 如果级联播放,需要给上级发送此通知
|
||||
StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId());
|
||||
if (streamInfo != null) {
|
||||
// 设置进度100%
|
||||
streamInfo.setProgress(1);
|
||||
redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
|
||||
}
|
||||
|
||||
// 先从会话内查找
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
|
||||
if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
|
||||
cmder.streamByeCmd(device.getDeviceId(), ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
|
||||
|
||||
// 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
|
||||
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
|
||||
if (sendRtpItem != null) {
|
||||
ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
|
||||
if (parentPlatform == null) {
|
||||
logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId());
|
||||
return;
|
||||
}
|
||||
sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -31,6 +33,7 @@ import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@Component
|
||||
public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
|
||||
@@ -38,9 +41,13 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class);
|
||||
private final String cmdType = "Catalog";
|
||||
|
||||
private boolean taskQueueHandlerRun = false;
|
||||
|
||||
@Autowired
|
||||
private ResponseMessageHandler responseMessageHandler;
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@@ -63,6 +70,10 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
responseMessageHandler.addHandler(cmdType, this);
|
||||
@@ -70,68 +81,88 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
|
||||
@Override
|
||||
public void handForDevice(RequestEvent evt, Device device, Element element) {
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + device.getDeviceId();
|
||||
Element rootElement = null;
|
||||
taskQueue.offer(new HandlerCatchData(evt, device, element));
|
||||
// 回复200 OK
|
||||
try {
|
||||
rootElement = getRootElement(evt, device.getCharset());
|
||||
Element deviceListElement = rootElement.element("DeviceList");
|
||||
Element sumNumElement = rootElement.element("SumNum");
|
||||
Element snElement = rootElement.element("SN");
|
||||
if (snElement == null || sumNumElement == null || deviceListElement == null) {
|
||||
responseAck(evt, Response.BAD_REQUEST, "xml error");
|
||||
return;
|
||||
}
|
||||
int sumNum = Integer.parseInt(sumNumElement.getText());
|
||||
|
||||
if (sumNum == 0) {
|
||||
// 数据已经完整接收
|
||||
storager.cleanChannelsForDevice(device.getDeviceId());
|
||||
catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);
|
||||
}else {
|
||||
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
|
||||
if (deviceListIterator != null) {
|
||||
List<DeviceChannel> channelList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (deviceListIterator.hasNext()) {
|
||||
Element itemDevice = deviceListIterator.next();
|
||||
Element channelDeviceElement = itemDevice.element("DeviceID");
|
||||
if (channelDeviceElement == null) {
|
||||
continue;
|
||||
responseAck(evt, Response.OK);
|
||||
} catch (SipException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (!taskQueueHandlerRun) {
|
||||
taskQueueHandlerRun = true;
|
||||
taskExecutor.execute(()-> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + take.getDevice().getDeviceId();
|
||||
Element rootElement = null;
|
||||
try {
|
||||
rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
|
||||
Element deviceListElement = rootElement.element("DeviceList");
|
||||
Element sumNumElement = rootElement.element("SumNum");
|
||||
Element snElement = rootElement.element("SN");
|
||||
if (snElement == null || sumNumElement == null || deviceListElement == null) {
|
||||
responseAck(take.getEvt(), Response.BAD_REQUEST, "xml error");
|
||||
return;
|
||||
}
|
||||
//by brewswang
|
||||
// if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置
|
||||
// processNotifyMobilePosition(evt, itemDevice);
|
||||
// }
|
||||
DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice);
|
||||
deviceChannel.setDeviceId(device.getDeviceId());
|
||||
int sumNum = Integer.parseInt(sumNumElement.getText());
|
||||
|
||||
channelList.add(deviceChannel);
|
||||
}
|
||||
int sn = Integer.parseInt(snElement.getText());
|
||||
catalogDataCatch.put(device.getDeviceId(), sn, sumNum, device, channelList);
|
||||
logger.info("收到来自设备【{}】的通道: {}个,{}/{}", device.getDeviceId(), channelList.size(), catalogDataCatch.get(device.getDeviceId()) == null ? 0 :catalogDataCatch.get(device.getDeviceId()).size(), sumNum);
|
||||
if (catalogDataCatch.get(device.getDeviceId()).size() == sumNum) {
|
||||
// 数据已经完整接收
|
||||
boolean resetChannelsResult = storager.resetChannels(device.getDeviceId(), catalogDataCatch.get(device.getDeviceId()));
|
||||
if (!resetChannelsResult) {
|
||||
String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(device.getDeviceId()).size() + "条";
|
||||
catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), errorMsg);
|
||||
if (sumNum == 0) {
|
||||
// 数据已经完整接收
|
||||
storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
|
||||
}else {
|
||||
catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);
|
||||
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
|
||||
if (deviceListIterator != null) {
|
||||
List<DeviceChannel> channelList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (deviceListIterator.hasNext()) {
|
||||
Element itemDevice = deviceListIterator.next();
|
||||
Element channelDeviceElement = itemDevice.element("DeviceID");
|
||||
if (channelDeviceElement == null) {
|
||||
continue;
|
||||
}
|
||||
//by brewswang
|
||||
// if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置
|
||||
// processNotifyMobilePosition(evt, itemDevice);
|
||||
// }
|
||||
DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device);
|
||||
deviceChannel.setDeviceId(take.getDevice().getDeviceId());
|
||||
|
||||
channelList.add(deviceChannel);
|
||||
}
|
||||
int sn = Integer.parseInt(snElement.getText());
|
||||
catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
|
||||
logger.info("收到来自设备【{}】的通道: {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
|
||||
if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
|
||||
// 数据已经完整接收
|
||||
boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
|
||||
if (!resetChannelsResult) {
|
||||
String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条";
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg);
|
||||
}else {
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
taskQueueHandlerRun = false;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
|
||||
deviceService.offline(device.getDeviceId());
|
||||
}
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId);
|
||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
|
||||
msg.setData(json);
|
||||
deferredResultHolder.invokeAllResult(msg);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
@@ -19,6 +16,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -28,6 +27,9 @@ import javax.sip.SipException;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
|
||||
|
||||
@@ -38,10 +40,11 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
|
||||
public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class);
|
||||
public static volatile List<String> threadNameList = new ArrayList();
|
||||
private final String cmdType = "RecordInfo";
|
||||
private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private boolean taskQueueHandlerRun = false;
|
||||
@Autowired
|
||||
private ResponseMessageHandler responseMessageHandler;
|
||||
|
||||
@@ -51,11 +54,13 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
||||
@Autowired
|
||||
private DeferredResultHolder deferredResultHolder;
|
||||
|
||||
|
||||
|
||||
@Autowired
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
responseMessageHandler.addHandler(cmdType, this);
|
||||
@@ -67,67 +72,89 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
||||
// 回复200 OK
|
||||
try {
|
||||
responseAck(evt, Response.OK);
|
||||
taskQueue.offer(new HandlerCatchData(evt, device, rootElement));
|
||||
if (!taskQueueHandlerRun) {
|
||||
taskQueueHandlerRun = true;
|
||||
taskExecutor.execute(()->{
|
||||
try {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset());
|
||||
String sn = getText(rootElementForCharset, "SN");
|
||||
String channelId = getText(rootElementForCharset, "DeviceID");
|
||||
RecordInfo recordInfo = new RecordInfo();
|
||||
recordInfo.setChannelId(channelId);
|
||||
recordInfo.setDeviceId(take.getDevice().getDeviceId());
|
||||
recordInfo.setSn(sn);
|
||||
recordInfo.setName(getText(rootElementForCharset, "Name"));
|
||||
String sumNumStr = getText(rootElementForCharset, "SumNum");
|
||||
int sumNum = 0;
|
||||
if (!StringUtils.isEmpty(sumNumStr)) {
|
||||
sumNum = Integer.parseInt(sumNumStr);
|
||||
}
|
||||
recordInfo.setSumNum(sumNum);
|
||||
Element recordListElement = rootElementForCharset.element("RecordList");
|
||||
if (recordListElement == null || sumNum == 0) {
|
||||
logger.info("无录像数据");
|
||||
eventPublisher.recordEndEventPush(recordInfo);
|
||||
recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
|
||||
releaseRequest(take.getDevice().getDeviceId(), sn);
|
||||
} else {
|
||||
Iterator<Element> recordListIterator = recordListElement.elementIterator();
|
||||
if (recordListIterator != null) {
|
||||
List<RecordItem> recordList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (recordListIterator.hasNext()) {
|
||||
Element itemRecord = recordListIterator.next();
|
||||
Element recordElement = itemRecord.element("DeviceID");
|
||||
if (recordElement == null) {
|
||||
logger.info("记录为空,下一个...");
|
||||
continue;
|
||||
}
|
||||
RecordItem record = new RecordItem();
|
||||
record.setDeviceId(getText(itemRecord, "DeviceID"));
|
||||
record.setName(getText(itemRecord, "Name"));
|
||||
record.setFilePath(getText(itemRecord, "FilePath"));
|
||||
record.setFileSize(getText(itemRecord, "FileSize"));
|
||||
record.setAddress(getText(itemRecord, "Address"));
|
||||
|
||||
rootElement = getRootElement(evt, device.getCharset());
|
||||
String sn = getText(rootElement, "SN");
|
||||
String startTimeStr = getText(itemRecord, "StartTime");
|
||||
record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
|
||||
|
||||
String sumNumStr = getText(rootElement, "SumNum");
|
||||
int sumNum = 0;
|
||||
if (!StringUtils.isEmpty(sumNumStr)) {
|
||||
sumNum = Integer.parseInt(sumNumStr);
|
||||
}
|
||||
Element recordListElement = rootElement.element("RecordList");
|
||||
if (recordListElement == null || sumNum == 0) {
|
||||
logger.info("无录像数据");
|
||||
recordDataCatch.put(device.getDeviceId(), sn, sumNum, new ArrayList<>());
|
||||
releaseRequest(device.getDeviceId(), sn);
|
||||
} else {
|
||||
Iterator<Element> recordListIterator = recordListElement.elementIterator();
|
||||
if (recordListIterator != null) {
|
||||
List<RecordItem> recordList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (recordListIterator.hasNext()) {
|
||||
Element itemRecord = recordListIterator.next();
|
||||
Element recordElement = itemRecord.element("DeviceID");
|
||||
if (recordElement == null) {
|
||||
logger.info("记录为空,下一个...");
|
||||
continue;
|
||||
String endTimeStr = getText(itemRecord, "EndTime");
|
||||
record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
|
||||
|
||||
record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
|
||||
: Integer.parseInt(getText(itemRecord, "Secrecy")));
|
||||
record.setType(getText(itemRecord, "Type"));
|
||||
record.setRecorderId(getText(itemRecord, "RecorderID"));
|
||||
recordList.add(record);
|
||||
}
|
||||
recordInfo.setRecordList(recordList);
|
||||
// 发送消息,如果是上级查询此录像,则会通过这里通知给上级
|
||||
eventPublisher.recordEndEventPush(recordInfo);
|
||||
int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
|
||||
logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
|
||||
}
|
||||
|
||||
if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
|
||||
releaseRequest(take.getDevice().getDeviceId(), sn);
|
||||
}
|
||||
}
|
||||
}
|
||||
RecordItem record = new RecordItem();
|
||||
record.setDeviceId(getText(itemRecord, "DeviceID"));
|
||||
record.setName(getText(itemRecord, "Name"));
|
||||
record.setFilePath(getText(itemRecord, "FilePath"));
|
||||
record.setFileSize(getText(itemRecord, "FileSize"));
|
||||
record.setAddress(getText(itemRecord, "Address"));
|
||||
|
||||
String startTimeStr = getText(itemRecord, "StartTime");
|
||||
record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
|
||||
|
||||
String endTimeStr = getText(itemRecord, "EndTime");
|
||||
record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
|
||||
|
||||
record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
|
||||
: Integer.parseInt(getText(itemRecord, "Secrecy")));
|
||||
record.setType(getText(itemRecord, "Type"));
|
||||
record.setRecorderId(getText(itemRecord, "RecorderID"));
|
||||
recordList.add(record);
|
||||
taskQueueHandlerRun = false;
|
||||
}catch (DocumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
int count = recordDataCatch.put(device.getDeviceId(), sn, sumNum, recordList);
|
||||
logger.info("[国标录像], {}->{}: {}/{}", device.getDeviceId(), sn, count, sumNum);
|
||||
}
|
||||
|
||||
if (recordDataCatch.isComplete(device.getDeviceId(), sn)){
|
||||
releaseRequest(device.getDeviceId(), sn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.sip.ResponseEvent;
|
||||
@Component
|
||||
public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private String method = "BYE";
|
||||
private final String method = "BYE";
|
||||
|
||||
@Autowired
|
||||
private SipLayer sipLayer;
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.sip.ResponseEvent;
|
||||
@Component
|
||||
public class CancelResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private String method = "CANCEL";
|
||||
private final String method = "CANCEL";
|
||||
|
||||
@Autowired
|
||||
private SipLayer sipLayer;
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.text.ParseException;
|
||||
public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
|
||||
private String method = "INVITE";
|
||||
private final String method = "INVITE";
|
||||
|
||||
@Autowired
|
||||
private SipLayer sipLayer;
|
||||
|
||||
@@ -27,7 +27,7 @@ import javax.sip.message.Response;
|
||||
public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
|
||||
private String method = "REGISTER";
|
||||
private final String method = "REGISTER";
|
||||
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform sipCommanderForPlatform;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import org.dom4j.Attribute;
|
||||
import org.dom4j.Document;
|
||||
@@ -180,7 +181,7 @@ public class XmlUtil {
|
||||
return xml.getRootElement();
|
||||
}
|
||||
|
||||
public static DeviceChannel channelContentHander(Element itemDevice){
|
||||
public static DeviceChannel channelContentHander(Element itemDevice, Device device){
|
||||
Element channdelNameElement = itemDevice.element("Name");
|
||||
String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
|
||||
Element statusElement = itemDevice.element("Status");
|
||||
@@ -254,6 +255,8 @@ public class XmlUtil {
|
||||
}else if (deviceChannel.getChannelId().length() == 20) {
|
||||
if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织
|
||||
deviceChannel.setParentId(businessGroupID);
|
||||
}else if (Integer.parseInt(device.getDeviceId().substring(10, 13) )== 118) {//NVR 如果上级设备编号是NVR则直接将NVR的编号设置给通道的上级编号
|
||||
deviceChannel.setParentId(device.getDeviceId());
|
||||
}else if (deviceChannel.getCivilCode() != null) {
|
||||
// 设备, 无parentId的20位是使用CivilCode表示上级的设备,
|
||||
// 注:215 业务分组是需要有parentId的
|
||||
@@ -308,6 +311,31 @@ public class XmlUtil {
|
||||
} else {
|
||||
deviceChannel.setLatitude(0.00);
|
||||
}
|
||||
if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) {
|
||||
if ("WGS84".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeGcj02(position[0]);
|
||||
deviceChannel.setLatitudeGcj02(position[1]);
|
||||
}else if ("GCJ02".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeWgs84(position[0]);
|
||||
deviceChannel.setLatitudeWgs84(position[1]);
|
||||
}else {
|
||||
deviceChannel.setLongitudeGcj02(0.00);
|
||||
deviceChannel.setLatitudeGcj02(0.00);
|
||||
deviceChannel.setLongitudeWgs84(0.00);
|
||||
deviceChannel.setLatitudeWgs84(0.00);
|
||||
}
|
||||
}else {
|
||||
deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
|
||||
}
|
||||
if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) {
|
||||
//兼容INFO中的信息
|
||||
Element info = itemDevice.element("Info");
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.*;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
@@ -92,10 +91,9 @@ public class ZLMHttpHookListener {
|
||||
public ResponseEntity<String> onServerKeepalive(@RequestBody JSONObject json){
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[ ZLM HOOK ]on_server_keepalive API调用,参数:" + json.toString());
|
||||
logger.debug("[ ZLM HOOK ] on_server_keepalive API调用,参数:" + json.toString());
|
||||
}
|
||||
String mediaServerId = json.getString("mediaServerId");
|
||||
|
||||
List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive);
|
||||
if (subscribes != null && subscribes.size() > 0) {
|
||||
for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
|
||||
@@ -165,7 +163,6 @@ public class ZLMHttpHookListener {
|
||||
if (mediaInfo != null) {
|
||||
subscribe.response(mediaInfo, json);
|
||||
}
|
||||
|
||||
}
|
||||
JSONObject ret = new JSONObject();
|
||||
ret.put("code", 0);
|
||||
@@ -248,6 +245,23 @@ public class ZLMHttpHookListener {
|
||||
ret.put("msg", "success");
|
||||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
||||
}
|
||||
/**
|
||||
* 录制hls完成后通知事件;此事件对回复不敏感。
|
||||
*
|
||||
*/
|
||||
@ResponseBody
|
||||
@PostMapping(value = "/on_record_ts", produces = "application/json;charset=UTF-8")
|
||||
public ResponseEntity<String> onRecordTs(@RequestBody JSONObject json){
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[ ZLM HOOK ]on_record_ts API调用,参数:" + json.toString());
|
||||
}
|
||||
String mediaServerId = json.getString("mediaServerId");
|
||||
JSONObject ret = new JSONObject();
|
||||
ret.put("code", 0);
|
||||
ret.put("msg", "success");
|
||||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。
|
||||
@@ -383,21 +397,22 @@ public class ZLMHttpHookListener {
|
||||
if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|
||||
|| item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|
||||
|| item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
|
||||
streamPushItem = zlmMediaListManager.addPush(item);
|
||||
item.setSeverId(userSetting.getServerId());
|
||||
zlmMediaListManager.addPush(item);
|
||||
}
|
||||
|
||||
List<GbStream> gbStreams = new ArrayList<>();
|
||||
if (streamPushItem == null || streamPushItem.getGbId() == null) {
|
||||
GbStream gbStream = storager.getGbStream(app, streamId);
|
||||
gbStreams.add(gbStream);
|
||||
}else {
|
||||
if (streamPushItem.getGbId() != null) {
|
||||
gbStreams.add(streamPushItem);
|
||||
}
|
||||
}
|
||||
if (gbStreams.size() > 0) {
|
||||
// List<GbStream> gbStreams = new ArrayList<>();
|
||||
// if (streamPushItem == null || streamPushItem.getGbId() == null) {
|
||||
// GbStream gbStream = storager.getGbStream(app, streamId);
|
||||
// gbStreams.add(gbStream);
|
||||
// }else {
|
||||
// if (streamPushItem.getGbId() != null) {
|
||||
// gbStreams.add(streamPushItem);
|
||||
// }
|
||||
// }
|
||||
// if (gbStreams.size() > 0) {
|
||||
// eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON);
|
||||
}
|
||||
// }
|
||||
|
||||
}else {
|
||||
// 兼容流注销时类型从redis记录获取
|
||||
|
||||
@@ -24,6 +24,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class ZLMMediaListManager {
|
||||
|
||||
@@ -147,7 +150,6 @@ public class ZLMMediaListManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
// StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(transform.getApp(), transform.getStream());
|
||||
List<GbStream> gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId());
|
||||
if (gbStreamList != null && gbStreamList.size() == 1) {
|
||||
transform.setGbStreamId(gbStreamList.get(0).getGbStreamId());
|
||||
@@ -162,13 +164,12 @@ public class ZLMMediaListManager {
|
||||
}
|
||||
if (transform != null) {
|
||||
if (channelOnlineEvents.get(transform.getGbId()) != null) {
|
||||
channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream());
|
||||
channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream(), transform.getServerId());
|
||||
channelOnlineEvents.remove(transform.getGbId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
storager.updateMedia(transform);
|
||||
return transform;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -28,6 +29,9 @@ public class ZLMRESTfulUtils {
|
||||
|
||||
private OkHttpClient getClient(){
|
||||
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
|
||||
//todo 暂时写死超时时间 均为5s
|
||||
httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); //设置连接超时时间
|
||||
httpClientBuilder.readTimeout(5,TimeUnit.SECONDS); //设置读取超时时间
|
||||
if (logger.isDebugEnabled()) {
|
||||
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
|
||||
logger.debug("http请求参数:" + message);
|
||||
@@ -47,7 +51,10 @@ public class ZLMRESTfulUtils {
|
||||
return null;
|
||||
}
|
||||
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
|
||||
JSONObject responseJSON = null;
|
||||
JSONObject responseJSON = new JSONObject();
|
||||
//-2自定义流媒体 调用错误码
|
||||
responseJSON.put("code",-2);
|
||||
responseJSON.put("msg","流媒体调用失败");
|
||||
|
||||
FormBody.Builder builder = new FormBody.Builder();
|
||||
builder.add("secret",mediaServerItem.getSecret());
|
||||
@@ -78,11 +85,20 @@ public class ZLMRESTfulUtils {
|
||||
response.close();
|
||||
Objects.requireNonNull(response.body()).close();
|
||||
}
|
||||
} catch (ConnectException e) {
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
|
||||
logger.info("请检查media配置并确认ZLM已启动...");
|
||||
}catch (IOException e) {
|
||||
logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
|
||||
|
||||
if(e instanceof SocketTimeoutException){
|
||||
//读取超时超时异常
|
||||
logger.error(String.format("读取ZLM数据失败: %s, %s", url, e.getMessage()));
|
||||
}
|
||||
if(e instanceof ConnectException){
|
||||
//判断连接异常,我这里是报Failed to connect to 10.7.5.144
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", url, e.getMessage()));
|
||||
}
|
||||
|
||||
}catch (Exception e){
|
||||
logger.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage()));
|
||||
}
|
||||
}else {
|
||||
client.newCall(request).enqueue(new Callback(){
|
||||
@@ -105,8 +121,16 @@ public class ZLMRESTfulUtils {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
|
||||
logger.info("请检查media配置并确认ZLM已启动...");
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
|
||||
|
||||
if(e instanceof SocketTimeoutException){
|
||||
//读取超时超时异常
|
||||
logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
|
||||
}
|
||||
if(e instanceof ConnectException){
|
||||
//判断连接异常,我这里是报Failed to connect to 10.7.5.144
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -151,7 +175,7 @@ public class ZLMRESTfulUtils {
|
||||
}
|
||||
|
||||
}
|
||||
File snapFile = new File(targetPath + "/" + fileName);
|
||||
File snapFile = new File(targetPath + File.separator + fileName);
|
||||
FileOutputStream outStream = new FileOutputStream(snapFile);
|
||||
|
||||
outStream.write(Objects.requireNonNull(response.body()).bytes());
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import org.slf4j.Logger;
|
||||
@@ -20,6 +21,9 @@ public class ZLMRTPServerFactory {
|
||||
@Autowired
|
||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
private int[] portRangeArray = new int[2];
|
||||
|
||||
public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
|
||||
@@ -87,10 +91,15 @@ public class ZLMRTPServerFactory {
|
||||
int result = -1;
|
||||
// 查询此rtp server 是否已经存在
|
||||
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
|
||||
if (rtpInfo != null && rtpInfo.getInteger("code") == 0 && rtpInfo.getBoolean("exist")) {
|
||||
result = rtpInfo.getInteger("local_port");
|
||||
if(rtpInfo.getInteger("code") == 0){
|
||||
if (rtpInfo.getBoolean("exist")) {
|
||||
result = rtpInfo.getInteger("local_port");
|
||||
return result;
|
||||
}
|
||||
}else if(rtpInfo.getInteger("code") == -2){
|
||||
return result;
|
||||
}
|
||||
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
// 推流端口设置0则使用随机端口
|
||||
param.put("enable_tcp", 1);
|
||||
@@ -197,6 +206,7 @@ public class ZLMRTPServerFactory {
|
||||
sendRtpItem.setTcp(tcp);
|
||||
sendRtpItem.setApp("rtp");
|
||||
sendRtpItem.setLocalPort(localPort);
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
sendRtpItem.setMediaServerId(serverItem.getId());
|
||||
return sendRtpItem;
|
||||
}
|
||||
@@ -238,6 +248,7 @@ public class ZLMRTPServerFactory {
|
||||
sendRtpItem.setChannelId(channelId);
|
||||
sendRtpItem.setTcp(tcp);
|
||||
sendRtpItem.setLocalPort(localPort);
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
sendRtpItem.setMediaServerId(serverItem.getId());
|
||||
return sendRtpItem;
|
||||
}
|
||||
@@ -279,10 +290,10 @@ public class ZLMRTPServerFactory {
|
||||
*/
|
||||
public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
|
||||
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
|
||||
Integer code = mediaInfo.getInteger("code");
|
||||
if (mediaInfo == null) {
|
||||
return 0;
|
||||
}
|
||||
Integer code = mediaInfo.getInteger("code");
|
||||
if ( code < 0) {
|
||||
logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg"));
|
||||
return -1;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.genersoft.iot.vmp.media.zlm.dto;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public interface ChannelOnlineEvent {
|
||||
|
||||
void run(String app, String stream);
|
||||
void run(String app, String stream, String serverId);
|
||||
}
|
||||
|
||||
@@ -61,10 +61,15 @@ public class MediaItem {
|
||||
private String originUrl;
|
||||
|
||||
/**
|
||||
* 服务器id
|
||||
* 流媒体服务器id
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 服务器id
|
||||
*/
|
||||
private String severId;
|
||||
|
||||
/**
|
||||
* GMT unix系统时间戳,单位秒
|
||||
*/
|
||||
@@ -414,4 +419,12 @@ public class MediaItem {
|
||||
public void setStreamInfo(StreamInfo streamInfo) {
|
||||
this.streamInfo = streamInfo;
|
||||
}
|
||||
|
||||
public String getSeverId() {
|
||||
return severId;
|
||||
}
|
||||
|
||||
public void setSeverId(String severId) {
|
||||
this.severId = severId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 使用的服务ID
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
public String getVhost() {
|
||||
return vhost;
|
||||
}
|
||||
@@ -219,5 +224,13 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,13 +61,12 @@ public class ZLMKeepliveTimeoutListener extends RedisKeyExpirationEventMessageLi
|
||||
// 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||
JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
|
||||
if (mediaServerConfig == null) {
|
||||
publisher.zlmOfflineEventPublish(mediaServerId);
|
||||
}else {
|
||||
if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) {
|
||||
logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息", mediaServerId);
|
||||
// 添加zlm信息
|
||||
mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig);
|
||||
}else {
|
||||
publisher.zlmOfflineEventPublish(mediaServerId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ZLMStatusEventListener {
|
||||
logger.info("[ZLM] 上线 ID:" + event.getMediaServerId());
|
||||
streamPushService.zlmServerOnline(event.getMediaServerId());
|
||||
streamProxyService.zlmServerOnline(event.getMediaServerId());
|
||||
|
||||
playService.zlmServerOnline(event.getMediaServerId());
|
||||
}
|
||||
|
||||
@Async
|
||||
|
||||
@@ -42,6 +42,8 @@ public interface IPlayService {
|
||||
|
||||
StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
|
||||
|
||||
void zlmServerOnline(String mediaServerId);
|
||||
|
||||
void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event);
|
||||
void stopAudioBroadcast(String deviceId, String channelId);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ public class StreamGPSSubscribeTask {
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
|
||||
|
||||
@Scheduled(fixedRate = 30 * 1000) //每30秒执行一次
|
||||
public void execute(){
|
||||
List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo();
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 当上级平台
|
||||
* @author lin
|
||||
*/
|
||||
public class MessageForPushChannel {
|
||||
/**
|
||||
@@ -45,6 +48,20 @@ public class MessageForPushChannel {
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
public static MessageForPushChannel getInstance(int type, String app, String stream, String gbId,
|
||||
String platFormId, String platFormName, String serverId,
|
||||
String mediaServerId){
|
||||
MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
|
||||
messageForPushChannel.setType(type);
|
||||
messageForPushChannel.setGbId(gbId);
|
||||
messageForPushChannel.setApp(app);
|
||||
messageForPushChannel.setStream(stream);
|
||||
messageForPushChannel.setMediaServerId(mediaServerId);
|
||||
messageForPushChannel.setPlatFormId(platFormId);
|
||||
messageForPushChannel.setPlatFormName(platFormName);
|
||||
return messageForPushChannel;
|
||||
}
|
||||
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* redis消息:请求下级推送流信息
|
||||
* @author lin
|
||||
*/
|
||||
public class RequestPushStreamMsg {
|
||||
|
||||
|
||||
/**
|
||||
* 下级服务ID
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 流ID
|
||||
*/
|
||||
private String app;
|
||||
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
/**
|
||||
* 目标IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 目标端口
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* ssrc
|
||||
*/
|
||||
private String ssrc;
|
||||
|
||||
/**
|
||||
* 是否使用TCP方式
|
||||
*/
|
||||
private boolean tcp;
|
||||
|
||||
/**
|
||||
* 本地使用的端口
|
||||
*/
|
||||
private int srcPort;
|
||||
|
||||
/**
|
||||
* 发送时,rtp的pt(uint8_t),不传时默认为96
|
||||
*/
|
||||
private int pt;
|
||||
|
||||
/**
|
||||
* 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es;
|
||||
*/
|
||||
private boolean ps;
|
||||
|
||||
/**
|
||||
* 是否只有音频
|
||||
*/
|
||||
private boolean onlyAudio;
|
||||
|
||||
|
||||
public static RequestPushStreamMsg getInstance(String mediaServerId, String app, String stream, String ip, int port, String ssrc,
|
||||
boolean tcp, int srcPort, int pt, boolean ps, boolean onlyAudio) {
|
||||
RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg();
|
||||
requestPushStreamMsg.setMediaServerId(mediaServerId);
|
||||
requestPushStreamMsg.setApp(app);
|
||||
requestPushStreamMsg.setStream(stream);
|
||||
requestPushStreamMsg.setIp(ip);
|
||||
requestPushStreamMsg.setPort(port);
|
||||
requestPushStreamMsg.setSsrc(ssrc);
|
||||
requestPushStreamMsg.setTcp(tcp);
|
||||
requestPushStreamMsg.setSrcPort(srcPort);
|
||||
requestPushStreamMsg.setPt(pt);
|
||||
requestPushStreamMsg.setPs(ps);
|
||||
requestPushStreamMsg.setOnlyAudio(onlyAudio);
|
||||
return requestPushStreamMsg;
|
||||
}
|
||||
|
||||
public String getMediaServerId() {
|
||||
return mediaServerId;
|
||||
}
|
||||
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public String getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public void setApp(String app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public String getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void setStream(String stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getSsrc() {
|
||||
return ssrc;
|
||||
}
|
||||
|
||||
public void setSsrc(String ssrc) {
|
||||
this.ssrc = ssrc;
|
||||
}
|
||||
|
||||
public boolean isTcp() {
|
||||
return tcp;
|
||||
}
|
||||
|
||||
public void setTcp(boolean tcp) {
|
||||
this.tcp = tcp;
|
||||
}
|
||||
|
||||
public int getSrcPort() {
|
||||
return srcPort;
|
||||
}
|
||||
|
||||
public void setSrcPort(int srcPort) {
|
||||
this.srcPort = srcPort;
|
||||
}
|
||||
|
||||
public int getPt() {
|
||||
return pt;
|
||||
}
|
||||
|
||||
public void setPt(int pt) {
|
||||
this.pt = pt;
|
||||
}
|
||||
|
||||
public boolean isPs() {
|
||||
return ps;
|
||||
}
|
||||
|
||||
public void setPs(boolean ps) {
|
||||
this.ps = ps;
|
||||
}
|
||||
|
||||
public boolean isOnlyAudio() {
|
||||
return onlyAudio;
|
||||
}
|
||||
|
||||
public void setOnlyAudio(boolean onlyAudio) {
|
||||
this.onlyAudio = onlyAudio;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* redis消息:请求下级回复推送信息
|
||||
* @author lin
|
||||
*/
|
||||
public class RequestSendItemMsg {
|
||||
|
||||
/**
|
||||
* 下级服务ID
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* 下级服务ID
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 流ID
|
||||
*/
|
||||
private String app;
|
||||
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
/**
|
||||
* 目标IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 目标端口
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* ssrc
|
||||
*/
|
||||
private String ssrc;
|
||||
|
||||
/**
|
||||
* 平台国标编号
|
||||
*/
|
||||
private String platformId;
|
||||
|
||||
/**
|
||||
* 平台名称
|
||||
*/
|
||||
private String platformName;
|
||||
|
||||
/**
|
||||
* 通道ID
|
||||
*/
|
||||
private String channelId;
|
||||
|
||||
|
||||
/**
|
||||
* 是否使用TCP
|
||||
*/
|
||||
private Boolean isTcp;
|
||||
|
||||
|
||||
|
||||
|
||||
public static RequestSendItemMsg getInstance(String serverId, String mediaServerId, String app, String stream, String ip, int port,
|
||||
String ssrc, String platformId, String channelId, Boolean isTcp, String platformName) {
|
||||
RequestSendItemMsg requestSendItemMsg = new RequestSendItemMsg();
|
||||
requestSendItemMsg.setServerId(serverId);
|
||||
requestSendItemMsg.setMediaServerId(mediaServerId);
|
||||
requestSendItemMsg.setApp(app);
|
||||
requestSendItemMsg.setStream(stream);
|
||||
requestSendItemMsg.setIp(ip);
|
||||
requestSendItemMsg.setPort(port);
|
||||
requestSendItemMsg.setSsrc(ssrc);
|
||||
requestSendItemMsg.setPlatformId(platformId);
|
||||
requestSendItemMsg.setPlatformName(platformName);
|
||||
requestSendItemMsg.setChannelId(channelId);
|
||||
requestSendItemMsg.setTcp(isTcp);
|
||||
|
||||
return requestSendItemMsg;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public String getMediaServerId() {
|
||||
return mediaServerId;
|
||||
}
|
||||
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public String getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public void setApp(String app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public String getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void setStream(String stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getSsrc() {
|
||||
return ssrc;
|
||||
}
|
||||
|
||||
public void setSsrc(String ssrc) {
|
||||
this.ssrc = ssrc;
|
||||
}
|
||||
|
||||
public String getPlatformId() {
|
||||
return platformId;
|
||||
}
|
||||
|
||||
public void setPlatformId(String platformId) {
|
||||
this.platformId = platformId;
|
||||
}
|
||||
|
||||
public String getPlatformName() {
|
||||
return platformName;
|
||||
}
|
||||
|
||||
public void setPlatformName(String platformName) {
|
||||
this.platformName = platformName;
|
||||
}
|
||||
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public void setChannelId(String channelId) {
|
||||
this.channelId = channelId;
|
||||
}
|
||||
|
||||
public Boolean getTcp() {
|
||||
return isTcp;
|
||||
}
|
||||
|
||||
public void setTcp(Boolean tcp) {
|
||||
isTcp = tcp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
|
||||
/**
|
||||
* redis消息:下级回复推送信息
|
||||
* @author lin
|
||||
*/
|
||||
public class ResponseSendItemMsg {
|
||||
|
||||
private SendRtpItem sendRtpItem;
|
||||
|
||||
private MediaServerItem mediaServerItem;
|
||||
|
||||
public SendRtpItem getSendRtpItem() {
|
||||
return sendRtpItem;
|
||||
}
|
||||
|
||||
public void setSendRtpItem(SendRtpItem sendRtpItem) {
|
||||
this.sendRtpItem = sendRtpItem;
|
||||
}
|
||||
|
||||
public MediaServerItem getMediaServerItem() {
|
||||
return mediaServerItem;
|
||||
}
|
||||
|
||||
public void setMediaServerItem(MediaServerItem mediaServerItem) {
|
||||
this.mediaServerItem = mediaServerItem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public class WvpRedisMsg {
|
||||
|
||||
public static WvpRedisMsg getInstance(String fromId, String toId, String type, String cmd, String serial, String content){
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setFromId(fromId);
|
||||
wvpRedisMsg.setToId(toId);
|
||||
wvpRedisMsg.setType(type);
|
||||
wvpRedisMsg.setCmd(cmd);
|
||||
wvpRedisMsg.setSerial(serial);
|
||||
wvpRedisMsg.setContent(content);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
/**
|
||||
* req 请求, res 回复
|
||||
*/
|
||||
private String type;
|
||||
private String cmd;
|
||||
|
||||
/**
|
||||
* 消息的ID
|
||||
*/
|
||||
private String serial;
|
||||
private Object content;
|
||||
|
||||
private final static String requestTag = "req";
|
||||
private final static String responseTag = "res";
|
||||
|
||||
public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) {
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setType(requestTag);
|
||||
wvpRedisMsg.setFromId(fromId);
|
||||
wvpRedisMsg.setToId(toId);
|
||||
wvpRedisMsg.setCmd(cmd);
|
||||
wvpRedisMsg.setSerial(serial);
|
||||
wvpRedisMsg.setContent(content);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
public static WvpRedisMsg getResponseInstance() {
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setType(responseTag);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) {
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setType(responseTag);
|
||||
wvpRedisMsg.setFromId(fromId);
|
||||
wvpRedisMsg.setToId(toId);
|
||||
wvpRedisMsg.setCmd(cmd);
|
||||
wvpRedisMsg.setSerial(serial);
|
||||
wvpRedisMsg.setContent(content);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
public static boolean isRequest(WvpRedisMsg wvpRedisMsg) {
|
||||
return requestTag.equals(wvpRedisMsg.getType());
|
||||
}
|
||||
|
||||
public String getSerial() {
|
||||
return serial;
|
||||
}
|
||||
|
||||
public void setSerial(String serial) {
|
||||
this.serial = serial;
|
||||
}
|
||||
|
||||
public String getFromId() {
|
||||
return fromId;
|
||||
}
|
||||
|
||||
public void setFromId(String fromId) {
|
||||
this.fromId = fromId;
|
||||
}
|
||||
|
||||
public String getToId() {
|
||||
return toId;
|
||||
}
|
||||
|
||||
public void setToId(String toId) {
|
||||
this.toId = toId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getCmd() {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public void setCmd(String cmd) {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
public Object getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(Object content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
|
||||
public class WvpRedisMsgCmd {
|
||||
|
||||
public static final String GET_SEND_ITEM = "GetSendItem";
|
||||
public static final String REQUEST_PUSH_STREAM = "RequestPushStream";
|
||||
|
||||
}
|
||||
@@ -2,16 +2,21 @@ package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
|
||||
import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -49,6 +54,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
|
||||
@Autowired
|
||||
private DeviceChannelMapper deviceChannelMapper;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private ISIPCommander commander;
|
||||
|
||||
@@ -68,7 +79,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (deviceInRedis != null && deviceInDb == null) {
|
||||
// redis 存在脏数据
|
||||
redisCatchStorage.clearCatchByDeviceId(device.getDeviceId());
|
||||
|
||||
}
|
||||
device.setUpdateTime(now);
|
||||
device.setOnline(1);
|
||||
@@ -77,13 +87,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (device.getCreateTime() == null) {
|
||||
device.setCreateTime(now);
|
||||
logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId());
|
||||
deviceMapper.add(device);
|
||||
redisCatchStorage.updateDevice(device);
|
||||
commander.deviceInfoQuery(device);
|
||||
sync(device);
|
||||
deviceMapper.add(device);
|
||||
}else {
|
||||
deviceMapper.update(device);
|
||||
redisCatchStorage.updateDevice(device);
|
||||
}
|
||||
redisCatchStorage.updateDevice(device);
|
||||
|
||||
// 上线添加订阅
|
||||
if (device.getSubscribeCycleForCatalog() > 0) {
|
||||
// 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
|
||||
@@ -94,7 +106,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
// 刷新过期任务
|
||||
String registerExpireTaskKey = registerExpireTaskKeyPrefix + device.getDeviceId();
|
||||
dynamicTask.stop(registerExpireTaskKey);
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getExpires() * 1000);
|
||||
}
|
||||
|
||||
@@ -143,8 +154,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
|
||||
return false;
|
||||
}
|
||||
logger.info("移除目录订阅: {}", device.getDeviceId());
|
||||
dynamicTask.stop(device.getDeviceId() + "catalog");
|
||||
logger.info("[移除目录订阅]: {}", device.getDeviceId());
|
||||
String taskKey = device.getDeviceId() + "catalog";
|
||||
if (device.getOnline() == 1) {
|
||||
Runnable runnable = dynamicTask.get(taskKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
}
|
||||
dynamicTask.stop(taskKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -168,8 +187,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
|
||||
return false;
|
||||
}
|
||||
logger.info("移除移动位置订阅: {}", device.getDeviceId());
|
||||
dynamicTask.stop(device.getDeviceId() + "mobile_position");
|
||||
logger.info("[移除移动位置订阅]: {}", device.getDeviceId());
|
||||
String taskKey = device.getDeviceId() + "mobile_position";
|
||||
if (device.getOnline() == 1) {
|
||||
Runnable runnable = dynamicTask.get(taskKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
}
|
||||
dynamicTask.stop(taskKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -275,6 +302,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
removeMobilePositionSubscribe(deviceInStore);
|
||||
}
|
||||
}
|
||||
// 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标
|
||||
if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) {
|
||||
updateDeviceChannelGeoCoordSys(device);
|
||||
}
|
||||
|
||||
String now = DateUtil.getNow();
|
||||
device.setUpdateTime(now);
|
||||
@@ -282,6 +313,32 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
device.setUpdateTime(DateUtil.getNow());
|
||||
if (deviceMapper.update(device) > 0) {
|
||||
redisCatchStorage.updateDevice(device);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通道坐标系
|
||||
*/
|
||||
private void updateDeviceChannelGeoCoordSys(Device device) {
|
||||
List<DeviceChannel> deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId());
|
||||
if (deviceChannels.size() > 0) {
|
||||
for (DeviceChannel deviceChannel : deviceChannels) {
|
||||
if ("WGS84".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeGcj02(position[0]);
|
||||
deviceChannel.setLatitudeGcj02(position[1]);
|
||||
}else if ("GCJ02".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeWgs84(position[0]);
|
||||
deviceChannel.setLatitudeWgs84(position[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
storage.updateChannels(device.getDeviceId(), deviceChannels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,8 @@ public class GbStreamServiceImpl implements IGbStreamService {
|
||||
deviceChannel.setStatus(1);
|
||||
deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId);
|
||||
deviceChannel.setRegisterWay(1);
|
||||
if (catalogId.length() <= 10) { // 父节点是行政区划,则设置CivilCode使用此行政区划
|
||||
if (catalogId.length() > 0 && catalogId.length() <= 10) {
|
||||
// 父节点是行政区划,则设置CivilCode使用此行政区划
|
||||
deviceChannel.setCivilCode(catalogId);
|
||||
}else {
|
||||
deviceChannel.setCivilCode(platform.getAdministrativeDivision());
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
@@ -35,7 +36,9 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 媒体服务器节点管理
|
||||
@@ -189,6 +192,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
public void clearRTPServer(MediaServerItem mediaServerItem) {
|
||||
mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()));
|
||||
redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -229,11 +233,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
result.sort((serverItem1, serverItem2)->{
|
||||
int sortResult = 0;
|
||||
try {
|
||||
sortResult = DateUtil.format.parse(serverItem1.getCreateTime()).compareTo(DateUtil.format.parse(serverItem2.getCreateTime()));
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
|
||||
LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
|
||||
|
||||
sortResult = localDateTime1.compareTo(localDateTime2);
|
||||
return sortResult;
|
||||
});
|
||||
return result;
|
||||
@@ -495,14 +498,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
|
||||
param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s");
|
||||
param.put("hook.enable","1");
|
||||
param.put("hook.on_flow_report","");
|
||||
param.put("hook.on_flow_report",String.format("%s/on_flow_report", hookPrex));
|
||||
param.put("hook.on_play",String.format("%s/on_play", hookPrex));
|
||||
param.put("hook.on_http_access","");
|
||||
param.put("hook.on_http_access",String.format("%s/on_http_access", hookPrex));
|
||||
param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
|
||||
param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): "");
|
||||
param.put("hook.on_record_ts","");
|
||||
param.put("hook.on_rtsp_auth","");
|
||||
param.put("hook.on_rtsp_realm","");
|
||||
param.put("hook.on_record_ts",String.format("%s/on_record_ts", hookPrex));
|
||||
param.put("hook.on_rtsp_auth",String.format("%s/on_rtsp_auth", hookPrex));
|
||||
param.put("hook.on_rtsp_realm",String.format("%s/on_rtsp_realm", hookPrex));
|
||||
param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex));
|
||||
param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex));
|
||||
param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.IMediaService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.PlayBackResult;
|
||||
@@ -32,8 +34,6 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
|
||||
import com.genersoft.iot.vmp.service.IMediaService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -49,6 +49,7 @@ import javax.sip.SipException;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -74,9 +75,6 @@ public class PlayServiceImpl implements IPlayService {
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redis;
|
||||
|
||||
@Autowired
|
||||
private DeferredResultHolder resultHolder;
|
||||
|
||||
@@ -140,36 +138,19 @@ public class PlayServiceImpl implements IPlayService {
|
||||
result.onCompletion(()->{
|
||||
// 点播结束时调用截图接口
|
||||
// TODO 应该在上流时调用更好,结束也可能是错误结束
|
||||
try {
|
||||
String classPath = ResourceUtils.getURL("classpath:").getPath();
|
||||
// 兼容打包为jar的class路径
|
||||
if(classPath.contains("jar")) {
|
||||
classPath = classPath.substring(0, classPath.lastIndexOf("."));
|
||||
classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);
|
||||
String path = "static/static/snap/";
|
||||
String fileName = deviceId + "_" + channelId + ".jpg";
|
||||
ResponseEntity responseEntity = (ResponseEntity)result.getResult();
|
||||
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
|
||||
if (Objects.requireNonNull(wvpResult).getCode() == 0) {
|
||||
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
|
||||
String streamUrl = streamInfoForSuccess.getFmp4();
|
||||
// 请求截图
|
||||
logger.info("[请求截图]: " + fileName);
|
||||
zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
|
||||
}
|
||||
if (classPath.startsWith("file:")) {
|
||||
classPath = classPath.substring(classPath.indexOf(":") + 1);
|
||||
}
|
||||
String path = classPath + "static/static/snap/";
|
||||
// 兼容Windows系统路径(去除前面的“/”)
|
||||
if(System.getProperty("os.name").contains("indows")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
String fileName = deviceId + "_" + channelId + ".jpg";
|
||||
ResponseEntity responseEntity = (ResponseEntity)result.getResult();
|
||||
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
|
||||
if (Objects.requireNonNull(wvpResult).getCode() == 0) {
|
||||
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
|
||||
String streamUrl = streamInfoForSuccess.getFmp4();
|
||||
// 请求截图
|
||||
logger.info("[请求截图]: " + fileName);
|
||||
zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
if (streamInfo != null) {
|
||||
@@ -186,24 +167,33 @@ public class PlayServiceImpl implements IPlayService {
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
|
||||
|
||||
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
|
||||
if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
|
||||
if(rtpInfo.getInteger("code") == 0){
|
||||
if (rtpInfo.getBoolean("exist")) {
|
||||
|
||||
WVPResult wvpResult = new WVPResult();
|
||||
wvpResult.setCode(0);
|
||||
wvpResult.setMsg("success");
|
||||
wvpResult.setData(streamInfo);
|
||||
msg.setData(wvpResult);
|
||||
WVPResult wvpResult = new WVPResult();
|
||||
wvpResult.setCode(0);
|
||||
wvpResult.setMsg("success");
|
||||
wvpResult.setData(streamInfo);
|
||||
msg.setData(wvpResult);
|
||||
|
||||
resultHolder.invokeAllResult(msg);
|
||||
if (hookEvent != null) {
|
||||
hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
|
||||
resultHolder.invokeAllResult(msg);
|
||||
if (hookEvent != null) {
|
||||
hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
|
||||
}
|
||||
}else {
|
||||
redisCatchStorage.stopPlay(streamInfo);
|
||||
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
|
||||
streamInfo = null;
|
||||
}
|
||||
}else {
|
||||
//zlm连接失败
|
||||
redisCatchStorage.stopPlay(streamInfo);
|
||||
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
|
||||
streamInfo = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (streamInfo == null) {
|
||||
String streamId = null;
|
||||
@@ -256,33 +246,41 @@ public class PlayServiceImpl implements IPlayService {
|
||||
if (ssrcInfo == null) {
|
||||
ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);
|
||||
}
|
||||
|
||||
logger.info("[点播开始] deviceId: {}, channelId: {}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getSsrc() );
|
||||
// 超时处理
|
||||
String timeOutTaskKey = UUID.randomUUID().toString();
|
||||
SSRCInfo finalSsrcInfo = ssrcInfo;
|
||||
dynamicTask.startDelay( timeOutTaskKey,()->{
|
||||
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", device.getDeviceId(), channelId));
|
||||
|
||||
SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
|
||||
if (dialog != null) {
|
||||
logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
timeoutCallback.run(1, "收流超时");
|
||||
// 点播超时回复BYE 同时释放ssrc以及此次点播的资源
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null);
|
||||
}else {
|
||||
logger.info("[点播超时] 消息未响应 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
timeoutCallback.run(0, "点播超时");
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
|
||||
mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
|
||||
streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
|
||||
}
|
||||
}, userSetting.getPlayTimeout()*1000);
|
||||
}, userSetting.getPlayTimeout());
|
||||
final String ssrc = ssrcInfo.getSsrc();
|
||||
final String stream = ssrcInfo.getStream();
|
||||
//端口获取失败的ssrcInfo 没有必要发送点播指令
|
||||
if(ssrcInfo.getPort() <= 0){
|
||||
logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
|
||||
return;
|
||||
}
|
||||
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
|
||||
logger.info("收到订阅消息: " + response.toJSONString());
|
||||
dynamicTask.stop(timeOutTaskKey);
|
||||
// hook响应
|
||||
onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
|
||||
hookEvent.response(mediaServerItemInuse, response);
|
||||
logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
|
||||
}, (event) -> {
|
||||
ResponseEvent responseEvent = (ResponseEvent)event.event;
|
||||
String contentString = new String(responseEvent.getResponse().getRawContent());
|
||||
@@ -296,8 +294,10 @@ public class PlayServiceImpl implements IPlayService {
|
||||
if (ssrc.equals(ssrcInResponse)) {
|
||||
return;
|
||||
}
|
||||
logger.info("[SIP 消息] 收到invite 200, 发现下级自定义了ssrc 开启修正");
|
||||
logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
|
||||
if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
|
||||
logger.info("[SIP 消息] SSRC修正 {}->{}", ssrc, ssrcInResponse);
|
||||
|
||||
if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
|
||||
// ssrc 不可用
|
||||
// 释放ssrc
|
||||
@@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
|
||||
// 回复之前所有的点播请求
|
||||
playBackCallback.call(playBackResult);
|
||||
}, userSetting.getPlayTimeout()*1000);
|
||||
}, userSetting.getPlayTimeout());
|
||||
|
||||
cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
|
||||
(InviteStreamInfo inviteStreamInfo) -> {
|
||||
@@ -539,7 +539,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
|
||||
// 回复之前所有的点播请求
|
||||
hookCallBack.call(downloadResult);
|
||||
}, userSetting.getPlayTimeout()*1000);
|
||||
}, userSetting.getPlayTimeout());
|
||||
cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
|
||||
inviteStreamInfo -> {
|
||||
logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
|
||||
@@ -605,7 +605,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
|
||||
BigDecimal currentCount = new BigDecimal(duration/1000);
|
||||
BigDecimal totalCount = new BigDecimal(end-start);
|
||||
BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP);
|
||||
BigDecimal divide = currentCount.divide(totalCount,2, RoundingMode.HALF_UP);
|
||||
double process = divide.doubleValue();
|
||||
streamInfo.setProgress(process);
|
||||
}
|
||||
@@ -728,4 +728,9 @@ public class PlayServiceImpl implements IPlayService {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void zlmServerOnline(String mediaServerId) {
|
||||
// 似乎没啥需要做的
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.*;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* 监听下级发送推送信息,并发送国标推流消息上级
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisGbPlayMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class);
|
||||
|
||||
public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM";
|
||||
|
||||
/**
|
||||
* 流媒体不存在的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1;
|
||||
|
||||
/**
|
||||
* 离线的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_OFFLINE = -2;
|
||||
|
||||
/**
|
||||
* 超时的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_TIMEOUT = -3;
|
||||
|
||||
private Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
|
||||
private Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
|
||||
private Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redis;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
@Autowired
|
||||
private ZLMRTPServerFactory zlmrtpServerFactory;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager mediaListManager;
|
||||
|
||||
@Autowired
|
||||
private ZLMHttpHookSubscribe subscribe;
|
||||
|
||||
|
||||
public interface PlayMsgCallback{
|
||||
void handler(ResponseSendItemMsg responseSendItemMsg);
|
||||
}
|
||||
|
||||
public interface PlayMsgCallbackForStartSendRtpStream{
|
||||
void handler(JSONObject jsonObject);
|
||||
}
|
||||
|
||||
public interface PlayMsgErrorCallback{
|
||||
void handler(WVPResult wvpResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
JSONObject msgJSON = JSON.parseObject(message.getBody(), JSONObject.class);
|
||||
WvpRedisMsg wvpRedisMsg = JSON.toJavaObject(msgJSON, WvpRedisMsg.class);
|
||||
if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
|
||||
return;
|
||||
}
|
||||
if (WvpRedisMsg.isRequest(wvpRedisMsg)) {
|
||||
logger.info("[收到REDIS通知] 请求: {}", new String(message.getBody()));
|
||||
|
||||
switch (wvpRedisMsg.getCmd()){
|
||||
case WvpRedisMsgCmd.GET_SEND_ITEM:
|
||||
RequestSendItemMsg content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestSendItemMsg.class);
|
||||
requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
|
||||
RequestPushStreamMsg param = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestPushStreamMsg.class);;
|
||||
requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}else {
|
||||
logger.info("[收到REDIS通知] 回复: {}", new String(message.getBody()));
|
||||
switch (wvpRedisMsg.getCmd()){
|
||||
case WvpRedisMsgCmd.GET_SEND_ITEM:
|
||||
|
||||
WVPResult content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
|
||||
|
||||
String key = wvpRedisMsg.getSerial();
|
||||
switch (content.getCode()) {
|
||||
case 0:
|
||||
ResponseSendItemMsg responseSendItemMsg =JSON.toJavaObject((JSONObject)content.getData(), ResponseSendItemMsg.class);
|
||||
PlayMsgCallback playMsgCallback = callbacks.get(key);
|
||||
if (playMsgCallback != null) {
|
||||
callbacksForError.remove(key);
|
||||
playMsgCallback.handler(responseSendItemMsg);
|
||||
}
|
||||
break;
|
||||
case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
|
||||
case ERROR_CODE_OFFLINE:
|
||||
case ERROR_CODE_TIMEOUT:
|
||||
PlayMsgErrorCallback errorCallback = callbacksForError.get(key);
|
||||
if (errorCallback != null) {
|
||||
callbacks.remove(key);
|
||||
errorCallback.handler(content);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
|
||||
WVPResult wvpResult = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
|
||||
String serial = wvpRedisMsg.getSerial();
|
||||
switch (wvpResult.getCode()) {
|
||||
case 0:
|
||||
JSONObject jsonObject = (JSONObject)wvpResult.getData();
|
||||
PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial);
|
||||
if (playMsgCallback != null) {
|
||||
callbacksForError.remove(serial);
|
||||
playMsgCallback.handler(jsonObject);
|
||||
}
|
||||
break;
|
||||
case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
|
||||
case ERROR_CODE_OFFLINE:
|
||||
case ERROR_CODE_TIMEOUT:
|
||||
PlayMsgErrorCallback errorCallback = callbacksForError.get(serial);
|
||||
if (errorCallback != null) {
|
||||
callbacks.remove(serial);
|
||||
errorCallback.handler(wvpResult);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求推流的请求
|
||||
*/
|
||||
private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) {
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
|
||||
if (mediaInfo == null) {
|
||||
// TODO 回复错误
|
||||
return;
|
||||
}
|
||||
String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1";
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("vhost","__defaultVhost__");
|
||||
param.put("app",requestPushStreamMsg.getApp());
|
||||
param.put("stream",requestPushStreamMsg.getStream());
|
||||
param.put("ssrc", requestPushStreamMsg.getSsrc());
|
||||
param.put("dst_url",requestPushStreamMsg.getIp());
|
||||
param.put("dst_port", requestPushStreamMsg.getPort());
|
||||
param.put("is_udp", is_Udp);
|
||||
param.put("src_port", requestPushStreamMsg.getSrcPort());
|
||||
param.put("pt", requestPushStreamMsg.getPt());
|
||||
param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0");
|
||||
param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0");
|
||||
JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// 回复消息
|
||||
responsePushStream(jsonObject, fromId, serial);
|
||||
}
|
||||
|
||||
private void responsePushStream(JSONObject content, String toId, String serial) {
|
||||
|
||||
WVPResult<JSONObject> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
result.setData(content);
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
|
||||
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求sendItem的请求
|
||||
*/
|
||||
private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) {
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
|
||||
if (mediaServerItem == null) {
|
||||
logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId());
|
||||
|
||||
WVPResult<SendRtpItem> result = new WVPResult<>();
|
||||
result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND);
|
||||
result.setMsg("流媒体不存在");
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
|
||||
WvpRedisMsgCmd.GET_SEND_ITEM, serial, result);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
return;
|
||||
}
|
||||
// 确定流是否在线
|
||||
boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
|
||||
if (streamReady) {
|
||||
logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream());
|
||||
responseSendItem(mediaServerItem, content, toId, serial);
|
||||
}else {
|
||||
// 流已经离线
|
||||
// 发送redis消息以使设备上线
|
||||
logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream());
|
||||
|
||||
String taskKey = UUID.randomUUID().toString();
|
||||
// 设置超时
|
||||
dynamicTask.startDelay(taskKey, ()->{
|
||||
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream());
|
||||
WVPResult<SendRtpItem> result = new WVPResult<>();
|
||||
result.setCode(ERROR_CODE_TIMEOUT);
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
|
||||
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
|
||||
);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
|
||||
// 添加订阅
|
||||
JSONObject subscribeKey = new JSONObject();
|
||||
subscribeKey.put("app", content.getApp());
|
||||
subscribeKey.put("stream", content.getStream());
|
||||
subscribeKey.put("regist", true);
|
||||
subscribeKey.put("schema", "rtmp");
|
||||
subscribeKey.put("mediaServerId", mediaServerItem.getId());
|
||||
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
dynamicTask.stop(taskKey);
|
||||
responseSendItem(mediaServerItem, content, toId, serial);
|
||||
});
|
||||
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(),
|
||||
content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(),
|
||||
content.getMediaServerId());
|
||||
redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将获取到的sendItem发送出去
|
||||
*/
|
||||
private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
|
||||
SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
|
||||
content.getPort(), content.getSsrc(), content.getPlatformId(),
|
||||
content.getApp(), content.getStream(), content.getChannelId(),
|
||||
content.getTcp());
|
||||
|
||||
WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg();
|
||||
responseSendItemMsg.setSendRtpItem(sendRtpItem);
|
||||
responseSendItemMsg.setMediaServerItem(mediaServerItem);
|
||||
result.setData(responseSendItemMsg);
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
|
||||
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
|
||||
);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息要求下级生成推流信息
|
||||
* @param serverId 下级服务ID
|
||||
* @param app 应用名
|
||||
* @param stream 流ID
|
||||
* @param ip 目标IP
|
||||
* @param port 目标端口
|
||||
* @param ssrc ssrc
|
||||
* @param platformId 平台国标编号
|
||||
* @param channelId 通道ID
|
||||
* @param isTcp 是否使用TCP
|
||||
* @param callback 得到信息的回调
|
||||
*/
|
||||
public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc,
|
||||
String platformId, String channelId, boolean isTcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) {
|
||||
RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance(
|
||||
serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, platformName);
|
||||
requestSendItemMsg.setServerId(serverId);
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
|
||||
key, requestSendItemMsg);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject);
|
||||
callbacks.put(key, callback);
|
||||
callbacksForError.put(key, errorCallback);
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
callbacks.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
WVPResult<Object> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(ERROR_CODE_TIMEOUT);
|
||||
wvpResult.setMsg("timeout");
|
||||
errorCallback.handler(wvpResult);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求推流的消息
|
||||
* @param param 推流参数
|
||||
* @param callback 回调
|
||||
*/
|
||||
public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
|
||||
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject);
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
callbacksForStartSendRtpStream.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
callbacksForStartSendRtpStream.put(key, callback);
|
||||
callbacksForError.put(key, (wvpResult)->{
|
||||
logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg());
|
||||
callbacksForStartSendRtpStream.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
});
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -10,17 +11,23 @@ import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收来自redis的GPS更新通知
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisGPSMsgListener implements MessageListener {
|
||||
public class RedisGpsMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGPSMsgListener.class);
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class);
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
logger.info("收到来自REDIS的GPS通知: {}", new String(message.getBody()));
|
||||
public void onMessage(@NotNull Message message, byte[] bytes) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("收到来自REDIS的GPS通知: {}", new String(message.getBody()));
|
||||
}
|
||||
GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class);
|
||||
redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisStreamMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class);
|
||||
|
||||
@Autowired
|
||||
private ISIPCommander commander;
|
||||
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform commanderForPlatform;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
|
||||
JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class);
|
||||
if (steamMsgJson == null) {
|
||||
logger.warn("[REDIS的ALARM通知]消息解析失败");
|
||||
return;
|
||||
}
|
||||
String serverId = steamMsgJson.getString("serverId");
|
||||
|
||||
if (userSetting.getServerId().equals(serverId)) {
|
||||
// 自己发送的消息忽略即可
|
||||
return;
|
||||
}
|
||||
logger.info("[REDIS通知] 流变化: {}", new String(message.getBody()));
|
||||
String app = steamMsgJson.getString("app");
|
||||
String stream = steamMsgJson.getString("stream");
|
||||
boolean register = steamMsgJson.getBoolean("register");
|
||||
String mediaServerId = steamMsgJson.getString("mediaServerId");
|
||||
MediaItem mediaItem = new MediaItem();
|
||||
mediaItem.setSeverId(serverId);
|
||||
mediaItem.setApp(app);
|
||||
mediaItem.setStream(stream);
|
||||
mediaItem.setRegist(register);
|
||||
mediaItem.setMediaServerId(mediaServerId);
|
||||
mediaItem.setCreateStamp(System.currentTimeMillis()/1000);
|
||||
mediaItem.setAliveSecond(0L);
|
||||
mediaItem.setTotalReaderCount("0");
|
||||
mediaItem.setOriginType(0);
|
||||
mediaItem.setOriginTypeStr("0");
|
||||
mediaItem.setOriginTypeStr("unknown");
|
||||
|
||||
zlmMediaListManager.addPush(mediaItem);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
streamPushItem.setStatus(true);
|
||||
streamPushItem.setStreamType("push");
|
||||
streamPushItem.setVhost(item.getVhost());
|
||||
streamPushItem.setServerId(item.getSeverId());
|
||||
return streamPushItem;
|
||||
}
|
||||
|
||||
|
||||
@@ -356,6 +356,15 @@ public interface IVideoManagerStorage {
|
||||
int removeMedia(String app, String stream);
|
||||
|
||||
|
||||
/**
|
||||
* 获取但个推流
|
||||
* @param app
|
||||
* @param stream
|
||||
* @return
|
||||
*/
|
||||
StreamPushItem getMedia(String app, String stream);
|
||||
|
||||
|
||||
/**
|
||||
* 清空推流列表
|
||||
*/
|
||||
|
||||
@@ -17,10 +17,10 @@ public interface DeviceChannelMapper {
|
||||
|
||||
@Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " +
|
||||
"address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
|
||||
"ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
|
||||
"ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
|
||||
"VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," +
|
||||
"'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " +
|
||||
"'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude},'${createTime}', '${updateTime}')")
|
||||
"'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude}, ${longitudeGcj02}, ${latitudeGcj02}, ${longitudeWgs84}, ${latitudeWgs84},'${createTime}', '${updateTime}')")
|
||||
int add(DeviceChannel channel);
|
||||
|
||||
@Update(value = {" <script>" +
|
||||
@@ -50,6 +50,10 @@ public interface DeviceChannelMapper {
|
||||
"<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" +
|
||||
"<if test='longitude != null'>, longitude=${longitude}</if>" +
|
||||
"<if test='latitude != null'>, latitude=${latitude}</if>" +
|
||||
"<if test='longitudeGcj02 != null'>, longitudeGcj02=${longitudeGcj02}</if>" +
|
||||
"<if test='latitudeGcj02 != null'>, latitudeGcj02=${latitudeGcj02}</if>" +
|
||||
"<if test='longitudeWgs84 != null'>, longitudeWgs84=${longitudeWgs84}</if>" +
|
||||
"<if test='latitudeWgs84 != null'>, latitudeWgs84=${latitudeWgs84}</if>" +
|
||||
"WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+
|
||||
" </script>"})
|
||||
int update(DeviceChannel channel);
|
||||
@@ -67,7 +71,7 @@ public interface DeviceChannelMapper {
|
||||
" <if test='online == false' > AND dc.status=0</if>" +
|
||||
" <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
|
||||
" <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
|
||||
"GROUP BY dc.channelId " +
|
||||
"ORDER BY dc.channelId " +
|
||||
" </script>"})
|
||||
List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online);
|
||||
|
||||
@@ -138,7 +142,8 @@ public interface DeviceChannelMapper {
|
||||
"insert into device_channel " +
|
||||
"(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
|
||||
" address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
|
||||
" ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
|
||||
" ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " +
|
||||
" longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
|
||||
"values " +
|
||||
"<foreach collection='addChannels' index='index' item='item' separator=','> " +
|
||||
"('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " +
|
||||
@@ -146,7 +151,8 @@ public interface DeviceChannelMapper {
|
||||
"'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " +
|
||||
"'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " +
|
||||
"'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " +
|
||||
"'${item.streamId}', ${item.longitude}, ${item.latitude},'${item.createTime}', '${item.updateTime}')" +
|
||||
"'${item.streamId}', ${item.longitude}, ${item.latitude},${item.longitudeGcj02}, " +
|
||||
"${item.latitudeGcj02},${item.longitudeWgs84}, ${item.latitudeWgs84},'${item.createTime}', '${item.updateTime}')" +
|
||||
"</foreach> " +
|
||||
"ON DUPLICATE KEY UPDATE " +
|
||||
"updateTime=VALUES(updateTime), " +
|
||||
@@ -173,7 +179,11 @@ public interface DeviceChannelMapper {
|
||||
"status=VALUES(status), " +
|
||||
"streamId=VALUES(streamId), " +
|
||||
"longitude=VALUES(longitude), " +
|
||||
"latitude=VALUES(latitude)" +
|
||||
"latitude=VALUES(latitude), " +
|
||||
"longitudeGcj02=VALUES(longitudeGcj02), " +
|
||||
"latitudeGcj02=VALUES(latitudeGcj02), " +
|
||||
"longitudeWgs84=VALUES(longitudeWgs84), " +
|
||||
"latitudeWgs84=VALUES(latitudeWgs84) " +
|
||||
"</script>")
|
||||
int batchAdd(List<DeviceChannel> addChannels);
|
||||
|
||||
@@ -207,7 +217,11 @@ public interface DeviceChannelMapper {
|
||||
"<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" +
|
||||
"<if test='item.longitude != null'>, longitude=${item.longitude}</if>" +
|
||||
"<if test='item.latitude != null'>, latitude=${item.latitude}</if>" +
|
||||
"WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+
|
||||
"<if test='item.longitudeGcj02 != null'>, longitudeGcj02=${item.longitudeGcj02}</if>" +
|
||||
"<if test='item.latitudeGcj02 != null'>, latitudeGcj02=${item.latitudeGcj02}</if>" +
|
||||
"<if test='item.longitudeWgs84 != null'>, longitudeWgs84=${item.longitudeWgs84}</if>" +
|
||||
"<if test='item.latitudeWgs84 != null'>, latitudeWgs84=${item.latitudeWgs84}</if>" +
|
||||
"WHERE deviceId='${item.deviceId}' AND channelId='${item.channelId}'"+
|
||||
"</foreach>" +
|
||||
"</script>"})
|
||||
int batchUpdate(List<DeviceChannel> updateChannels);
|
||||
@@ -261,4 +275,6 @@ public interface DeviceChannelMapper {
|
||||
@Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0")
|
||||
List<DeviceChannel> getAllChannelInPlay();
|
||||
|
||||
@Select("select * from device_channel where longitude*latitude > 0 and deviceId = #{deviceId}")
|
||||
List<DeviceChannel> getAllChannelWithCoordinate(String deviceId);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"geoCoordSys," +
|
||||
"online" +
|
||||
") VALUES (" +
|
||||
"#{deviceId}," +
|
||||
@@ -61,6 +62,7 @@ public interface DeviceMapper {
|
||||
"#{mobilePositionSubmissionInterval}," +
|
||||
"#{subscribeCycleForAlarm}," +
|
||||
"#{ssrcCheck}," +
|
||||
"#{geoCoordSys}," +
|
||||
"#{online}" +
|
||||
")")
|
||||
int add(Device device);
|
||||
@@ -87,6 +89,7 @@ public interface DeviceMapper {
|
||||
"<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" +
|
||||
"<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" +
|
||||
"<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" +
|
||||
"<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
|
||||
"WHERE deviceId='${deviceId}'"+
|
||||
" </script>"})
|
||||
int update(Device device);
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.util.List;
|
||||
public interface StreamPushMapper {
|
||||
|
||||
@Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
|
||||
"createStamp, aliveSecond, mediaServerId) VALUES" +
|
||||
"createStamp, aliveSecond, mediaServerId, serverId) VALUES" +
|
||||
"('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
|
||||
"'${createStamp}', '${aliveSecond}', '${mediaServerId}' )")
|
||||
"'${createStamp}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' )")
|
||||
int add(StreamPushItem streamPushItem);
|
||||
|
||||
@Update("UPDATE stream_push " +
|
||||
|
||||
@@ -587,11 +587,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*";
|
||||
List<GPSMsgInfo> result = new ArrayList<>();
|
||||
List<Object> keys = redis.scan(scanKey);
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = (String) keys.get(i);
|
||||
for (Object o : keys) {
|
||||
String key = (String) o;
|
||||
GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key);
|
||||
if (!gpsMsgInfo.isStored()) { // 只取没有存过得
|
||||
result.add((GPSMsgInfo)redis.get(key));
|
||||
result.add((GPSMsgInfo) redis.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
@Override
|
||||
public void sendStreamPushRequestedMsg(MessageForPushChannel msg) {
|
||||
String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
|
||||
logger.info("[redis 推流被请求通知] {}: {}-{}", key, msg.getApp(), msg.getStream());
|
||||
logger.info("[redis 推流被请求通知] {}: {}/{}", key, msg.getApp(), msg.getStream());
|
||||
redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
@@ -195,7 +196,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
|
||||
@Override
|
||||
public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
|
||||
if (deviceChannelList == null) {
|
||||
if (CollectionUtils.isEmpty(deviceChannelList)) {
|
||||
return false;
|
||||
}
|
||||
List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay();
|
||||
@@ -246,6 +247,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
if (stringBuilder.length() > 0) {
|
||||
logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder);
|
||||
}
|
||||
if(CollectionUtils.isEmpty(channels)){
|
||||
logger.info("通道重设,数据为空={}" , deviceChannelList);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels);
|
||||
int limitCount = 300;
|
||||
@@ -315,6 +320,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
List<DeviceChannel> all;
|
||||
if (catalogUnderDevice != null && catalogUnderDevice) {
|
||||
all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online);
|
||||
// 海康设备的parentId是SIP id
|
||||
List<DeviceChannel> deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online);
|
||||
all.addAll(deviceChannels);
|
||||
}else {
|
||||
all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online);
|
||||
}
|
||||
@@ -876,6 +884,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
return streamPushMapper.del(app, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamPushItem getMedia(String app, String stream) {
|
||||
return streamPushMapper.selectOne(app, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearMediaList() {
|
||||
streamPushMapper.clear();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -18,35 +17,63 @@ import java.util.Locale;
|
||||
*/
|
||||
public class DateUtil {
|
||||
|
||||
private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
|
||||
/**
|
||||
* 兼容不规范的iso8601时间格式
|
||||
*/
|
||||
private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s";
|
||||
|
||||
public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault());
|
||||
public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault());
|
||||
/**
|
||||
* 用以输出标准的iso8601时间格式
|
||||
*/
|
||||
private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
|
||||
public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault());
|
||||
public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault());
|
||||
/**
|
||||
* wvp内部统一时间格式
|
||||
*/
|
||||
public static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String zoneStr = "Asia/Shanghai";
|
||||
|
||||
public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
|
||||
public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
|
||||
public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
|
||||
|
||||
public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
|
||||
|
||||
return formatterISO8601.format(formatter.parse(formatTime));
|
||||
}
|
||||
|
||||
public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
|
||||
return formatter.format(formatterISO8601.parse(formatTime));
|
||||
return formatter.format(formatterCompatibleISO8601.parse(formatTime));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* yyyy_MM_dd_HH_mm_ss 转时间戳
|
||||
* @param formatTime
|
||||
* @return
|
||||
*/
|
||||
public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) {
|
||||
TemporalAccessor temporalAccessor = formatter.parse(formatTime);
|
||||
Instant instant = Instant.from(temporalAccessor);
|
||||
return instant.getEpochSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间
|
||||
* @return
|
||||
*/
|
||||
public static String getNow() {
|
||||
LocalDateTime nowDateTime = LocalDateTime.now();
|
||||
return formatter.format(nowDateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式校验
|
||||
* @param timeStr 时间字符串
|
||||
* @param dateTimeFormatter 待校验的格式
|
||||
* @return
|
||||
*/
|
||||
public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) {
|
||||
try {
|
||||
LocalDate.parse(timeStr, dateTimeFormatter);
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -68,8 +69,8 @@ public class AlarmController {
|
||||
@ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="startTime", value = "开始时间" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="endTime", value = "结束时间" ,dataTypeClass = String.class),
|
||||
})
|
||||
public ResponseEntity<PageInfo<DeviceAlarm>> getAll(
|
||||
@RequestParam int page,
|
||||
@@ -98,14 +99,7 @@ public class AlarmController {
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (startTime != null) {
|
||||
DateUtil.format.parse(startTime);
|
||||
}
|
||||
if (endTime != null) {
|
||||
DateUtil.format.parse(endTime);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@@ -144,11 +138,7 @@ public class AlarmController {
|
||||
if (StringUtils.isEmpty(time)) {
|
||||
time = null;
|
||||
}
|
||||
try {
|
||||
if (time != null) {
|
||||
DateUtil.format.parse(time);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (!DateUtil.verification(time, DateUtil.formatter) ){
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
List<String> deviceIdList = null;
|
||||
@@ -189,7 +179,7 @@ public class AlarmController {
|
||||
deviceAlarm.setAlarmDescription("test");
|
||||
deviceAlarm.setAlarmMethod("1");
|
||||
deviceAlarm.setAlarmPriority("1");
|
||||
deviceAlarm.setAlarmTime(DateUtil.formatISO8601.format(System.currentTimeMillis()));
|
||||
deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
|
||||
deviceAlarm.setAlarmType("1");
|
||||
deviceAlarm.setLongitude(115.33333);
|
||||
deviceAlarm.setLatitude(39.33333);
|
||||
|
||||
@@ -21,16 +21,22 @@ import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.sip.DialogState;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
@Api(tags = "国标设备查询", value = "国标设备查询")
|
||||
@@ -200,6 +206,11 @@ public class DeviceQuery {
|
||||
Set<String> allKeys = dynamicTask.getAllKeys();
|
||||
for (String key : allKeys) {
|
||||
if (key.startsWith(deviceId)) {
|
||||
Runnable runnable = dynamicTask.get(key);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
dynamicTask.stop(key);
|
||||
}
|
||||
}
|
||||
@@ -306,12 +317,7 @@ public class DeviceQuery {
|
||||
public ResponseEntity<WVPResult<String>> updateDevice(Device device){
|
||||
|
||||
if (device != null && device.getDeviceId() != null) {
|
||||
|
||||
|
||||
// TODO 报警订阅相关的信息
|
||||
|
||||
deviceService.updateDevice(device);
|
||||
// cmder.deviceInfoQuery(device);
|
||||
}
|
||||
WVPResult<String> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
@@ -336,6 +342,11 @@ public class DeviceQuery {
|
||||
Device device = storager.queryVideoDevice(deviceId);
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId;
|
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
|
||||
if(device == null) {
|
||||
result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK));
|
||||
return result;
|
||||
}
|
||||
cmder.deviceStatusQuery(device, event -> {
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setId(uuid);
|
||||
@@ -343,7 +354,6 @@ public class DeviceQuery {
|
||||
msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));
|
||||
resultHolder.invokeResult(msg);
|
||||
});
|
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
|
||||
result.onTimeout(()->{
|
||||
logger.warn(String.format("获取设备状态超时"));
|
||||
// 释放rtpserver
|
||||
@@ -456,4 +466,17 @@ public class DeviceQuery {
|
||||
wvpResult.setData(dialogStateMap);
|
||||
return wvpResult;
|
||||
}
|
||||
|
||||
@GetMapping("/snap/{deviceId}/{channelId}")
|
||||
@ApiOperation(value = "请求截图", notes = "请求截图")
|
||||
public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) {
|
||||
|
||||
try {
|
||||
final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath());
|
||||
resp.setContentType(MediaType.IMAGE_PNG_VALUE);
|
||||
IOUtils.copy(in, resp.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class GBRecordController {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter)){
|
||||
WVPResult<RecordInfo> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(-1);
|
||||
wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss);
|
||||
wvpResult.setMsg("startTime error, format is " + DateUtil.PATTERN);
|
||||
|
||||
ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
|
||||
result.setResult(resultResponseEntity);
|
||||
@@ -81,7 +81,7 @@ public class GBRecordController {
|
||||
if (!DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
WVPResult<RecordInfo> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(-1);
|
||||
wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss);
|
||||
wvpResult.setMsg("endTime error, format is " + DateUtil.PATTERN);
|
||||
ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
|
||||
result.setResult(resultResponseEntity);
|
||||
return result;
|
||||
|
||||
@@ -76,14 +76,7 @@ public class LogController {
|
||||
logger.warn("自动记录日志功能已关闭,查询结果可能不完整。");
|
||||
}
|
||||
|
||||
try {
|
||||
if (startTime != null) {
|
||||
DateUtil.format.parse(startTime);
|
||||
}
|
||||
if (endTime != null) {
|
||||
DateUtil.format.parse(endTime);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,8 +146,8 @@ public class ApiDeviceController {
|
||||
// 2-基于口令的双向认证,
|
||||
// 3-基于数字证书的双向认证
|
||||
deviceJOSNChannel.put("Status", deviceChannel.getStatus());
|
||||
deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude());
|
||||
deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude());
|
||||
deviceJOSNChannel.put("Longitude", deviceChannel.getLongitudeWgs84());
|
||||
deviceJOSNChannel.put("Latitude", deviceChannel.getLatitudeWgs84());
|
||||
deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球,
|
||||
// 3 - 固定枪机, 4 - 遥控枪机
|
||||
deviceJOSNChannel.put("CustomPTZType", "");
|
||||
|
||||
@@ -32,7 +32,7 @@ spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
|
||||
username: root
|
||||
password: root123
|
||||
druid:
|
||||
|
||||
@@ -20,7 +20,7 @@ spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
|
||||
username: root
|
||||
password: 123456
|
||||
druid:
|
||||
|
||||
@@ -20,7 +20,7 @@ spring:
|
||||
datasource:
|
||||
# 使用mysql 打开23-28行注释, 删除29-36行
|
||||
name: wvp
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false
|
||||
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true
|
||||
username: root
|
||||
password: root
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
|
||||
@@ -8,6 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
@@ -64,8 +68,8 @@ class DeviceAlarmServiceImplTest {
|
||||
* * 7其他报警;可以为直接组合如12为电话报警或 设备报警-
|
||||
*/
|
||||
deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + "");
|
||||
Date date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00");
|
||||
deviceAlarm.setAlarmTime(DateUtil.format.format(date));
|
||||
Instant date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00");
|
||||
deviceAlarm.setAlarmTime(DateUtil.formatter.format(date));
|
||||
/**
|
||||
* 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情-
|
||||
*/
|
||||
@@ -85,17 +89,20 @@ class DeviceAlarmServiceImplTest {
|
||||
|
||||
|
||||
|
||||
private Date randomDate(String beginDate, String endDate) {
|
||||
private Instant randomDate(String beginDate, String endDate) {
|
||||
try {
|
||||
|
||||
Date start = DateUtil.format.parse(beginDate);//构造开始日期
|
||||
Date end = DateUtil.format.parse(endDate);//构造结束日期
|
||||
//构造开始日期
|
||||
LocalDateTime start = LocalDateTime.parse(beginDate, DateUtil.formatter);
|
||||
|
||||
//构造结束日期
|
||||
LocalDateTime end = LocalDateTime.parse(endDate, DateUtil.formatter);
|
||||
//getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
|
||||
if (start.getTime() >= end.getTime()) {
|
||||
if (start.isAfter(end)) {
|
||||
return null;
|
||||
}
|
||||
long date = random(start.getTime(), end.getTime());
|
||||
return new Date(date);
|
||||
long date = random(start.toInstant(ZoneOffset.of("+8")).toEpochMilli(), end.toInstant(ZoneOffset.of("+8")).toEpochMilli());
|
||||
return Instant.ofEpochMilli(date);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"postcss-url": "^7.2.1",
|
||||
"rimraf": "^2.6.0",
|
||||
"semver": "^5.3.0",
|
||||
"shelljs": "^0.7.6",
|
||||
"shelljs": "^0.8.5",
|
||||
"uglifyjs-webpack-plugin": "^1.1.1",
|
||||
"url-loader": "^0.5.8",
|
||||
"vue-loader": "^13.3.0",
|
||||
|
||||
@@ -76,7 +76,7 @@ body,
|
||||
line-height: 60px;
|
||||
}
|
||||
.el-main {
|
||||
background-color: #e9eef3;
|
||||
background-color: #f0f2f5;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
padding-top: 0px !important;
|
||||
@@ -101,4 +101,8 @@ body,
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
|
||||
}
|
||||
.table-header {
|
||||
color: #727272;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,19 +18,17 @@
|
||||
<div v-if="!recordDetail">
|
||||
|
||||
<!--设备列表-->
|
||||
<el-table :data="recordList" border style="width: 100%" :height="winHeight">
|
||||
<el-table-column prop="app" label="应用名" align="center">
|
||||
<el-table :data="recordList" style="width: 100%" :height="winHeight">
|
||||
<el-table-column prop="app" label="应用名" >
|
||||
</el-table-column>
|
||||
<el-table-column prop="stream" label="流ID" align="center">
|
||||
<el-table-column prop="stream" label="流ID" >
|
||||
</el-table-column>
|
||||
<el-table-column prop="time" label="时间" align="center">
|
||||
<el-table-column prop="time" label="时间" >
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="360" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="360" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button-group>
|
||||
<el-button size="mini" icon="el-icon-video-camera-solid" type="primary" @click="showRecordDetail(scope.row)">查看</el-button>
|
||||
<!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>-->
|
||||
</el-button-group>
|
||||
<el-button size="medium" icon="el-icon-folder-opened" type="text" @click="showRecordDetail(scope.row)">查看</el-button>
|
||||
<!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>-->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -7,34 +7,33 @@
|
||||
@click="getDeviceList()"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <devicePlayer ref="devicePlayer"></devicePlayer> -->
|
||||
<!--设备列表-->
|
||||
<el-table :data="deviceList" border style="width: 100%;font-size: 12px;" :height="winHeight">
|
||||
<el-table-column prop="name" label="名称" align="center">
|
||||
<el-table :data="deviceList" style="width: 100%;font-size: 12px;" :height="winHeight" header-row-class-name="table-header">
|
||||
<el-table-column prop="name" label="名称" min-width="160">
|
||||
</el-table-column>
|
||||
<el-table-column prop="deviceId" label="设备编号" width="180" align="center">
|
||||
<el-table-column prop="deviceId" label="设备编号" min-width="200" >
|
||||
</el-table-column>
|
||||
<el-table-column label="地址" width="180" align="center">
|
||||
<el-table-column label="地址" min-width="160" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium">{{ scope.row.hostAddress }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="manufacturer" label="厂家" align="center">
|
||||
<el-table-column prop="manufacturer" label="厂家" min-width="120" >
|
||||
</el-table-column>
|
||||
<el-table-column label="流传输模式" align="center" width="120">
|
||||
<el-table-column label="流传输模式" min-width="160" >
|
||||
<template slot-scope="scope">
|
||||
<el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择">
|
||||
<el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择" style="width: 120px">
|
||||
<el-option key="UDP" label="UDP" value="UDP"></el-option>
|
||||
<el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option>
|
||||
<el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="channelCount" label="通道数" align="center">
|
||||
<el-table-column prop="channelCount" label="通道数" min-width="120" >
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<el-table-column label="状态" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.online == 1">在线</el-tag>
|
||||
@@ -42,30 +41,32 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="keepaliveTime" label="最近心跳" align="center" width="140">
|
||||
<el-table-column prop="keepaliveTime" label="最近心跳" min-width="160" >
|
||||
</el-table-column>
|
||||
<el-table-column prop="registerTime" label="最近注册" align="center" width="140">
|
||||
</el-table-column>
|
||||
<el-table-column prop="updateTime" label="更新时间" align="center" width="140">
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" align="center" width="140">
|
||||
<el-table-column prop="registerTime" label="最近注册" min-width="160">
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="updateTime" label="更新时间" width="140">-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column prop="createTime" label="创建时间" width="140">-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<el-table-column label="操作" width="450" align="center" fixed="right">
|
||||
<el-table-column label="操作" min-width="450" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" v-if="scope.row.online!=0" icon="el-icon-refresh" @click="refDevice(scope.row)"
|
||||
<el-button type="text" size="medium" v-bind:disabled="scope.row.online==0" icon="el-icon-refresh" @click="refDevice(scope.row)"
|
||||
@mouseover="getTooltipContent(scope.row.deviceId)">刷新
|
||||
</el-button>
|
||||
<el-button-group>
|
||||
<el-button size="mini" icon="el-icon-video-camera-solid" v-bind:disabled="scope.row.online==0"
|
||||
type="primary" @click="showChannelList(scope.row)">通道
|
||||
</el-button>
|
||||
<el-button size="mini" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="primary"
|
||||
@click="showDevicePosition(scope.row)">定位
|
||||
</el-button>
|
||||
<el-button size="mini" icon="el-icon-edit" type="primary" @click="edit(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteDevice(scope.row)">删除</el-button>
|
||||
</el-button-group>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button type="text" size="medium" icon="el-icon-video-camera" v-bind:disabled="scope.row.online==0"
|
||||
@click="showChannelList(scope.row)">通道
|
||||
</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="text"
|
||||
@click="showDevicePosition(scope.row)">定位
|
||||
</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">编辑</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-delete" type="text" @click="deleteDevice(scope.row)" style="color: #f56c6c">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -347,4 +348,5 @@ export default {
|
||||
padding: 0.3rem;
|
||||
width: 14.4rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<span style="font-size: 16px">{{item.id}}</span>
|
||||
<el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">编辑</el-button>
|
||||
<el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">查看</el-button>
|
||||
<el-button icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button>
|
||||
<el-button v-if="!item.defaultServer" icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button>
|
||||
<div style="margin-top: 13px; line-height: 12px; ">
|
||||
<span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span>
|
||||
<span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span>
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
<div class="page-title">上级平台列表</div>
|
||||
<div class="page-header-btn">
|
||||
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addParentPlatform">添加</el-button>
|
||||
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--设备列表-->
|
||||
<el-table :data="platformList" border style="width: 100%" :height="winHeight">
|
||||
<el-table-column prop="name" label="名称" align="center"></el-table-column>
|
||||
<el-table-column prop="serverGBId" label="平台编号" align="center"></el-table-column>
|
||||
<el-table-column label="是否启用" width="120" align="center">
|
||||
<el-table :data="platformList" style="width: 100%" :height="winHeight">
|
||||
<el-table-column prop="name" label="名称" ></el-table-column>
|
||||
<el-table-column prop="serverGBId" label="平台编号" min-width="200"></el-table-column>
|
||||
<el-table-column label="是否启用" min-width="80" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.enable">已启用</el-tag>
|
||||
@@ -19,7 +20,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<el-table-column label="状态" min-width="80" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.status">在线</el-tag>
|
||||
@@ -27,17 +28,17 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="地址" width="180" align="center">
|
||||
<el-table-column label="地址" min-width="160" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium">{{ scope.row.serverIP}}:{{scope.row.serverPort }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deviceGBId" label="设备国标编号" width="200" align="center"></el-table-column>
|
||||
<el-table-column prop="transport" label="信令传输模式" width="120" align="center"></el-table-column>
|
||||
<el-table-column prop="channelCount" label="通道数" width="120" align="center"></el-table-column>
|
||||
<el-table-column label="订阅信息" width="240" align="center" fixed="right">
|
||||
<el-table-column prop="deviceGBId" label="设备国标编号" min-width="200" ></el-table-column>
|
||||
<el-table-column prop="transport" label="信令传输模式" min-width="120" ></el-table-column>
|
||||
<el-table-column prop="channelCount" label="通道数" min-width="120" ></el-table-column>
|
||||
<el-table-column label="订阅信息" min-width="120" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-on " ></i>
|
||||
<i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅" class="iconfont icon-gbaojings subscribe-off " ></i>
|
||||
@@ -48,11 +49,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="300" align="center" fixed="right">
|
||||
<el-table-column label="操作" min-width="240" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" icon="el-icon-edit" @click="editPlatform(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" icon="el-icon-share" type="primary" @click="chooseChannel(scope.row)">选择通道</el-button>
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" @click="deletePlatform(scope.row)">删除</el-button>
|
||||
<el-button size="medium" icon="el-icon-edit" type="text" @click="editPlatform(scope.row)">编辑</el-button>
|
||||
<el-button size="medium" icon="el-icon-share" type="text" @click="chooseChannel(scope.row)">选择通道</el-button>
|
||||
<el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deletePlatform(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -168,6 +169,9 @@ export default {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
},
|
||||
refresh: function (){
|
||||
this.initData();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,52 +34,54 @@
|
||||
<el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;"
|
||||
:disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除
|
||||
</el-button>
|
||||
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<devicePlayer ref="devicePlayer"></devicePlayer>
|
||||
<addStreamTOGB ref="addStreamTOGB"></addStreamTOGB>
|
||||
<el-table ref="pushListTable" :data="pushList" border style="width: 100%" :height="winHeight"
|
||||
<el-table ref="pushListTable" :data="pushList" style="width: 100%" :height="winHeight"
|
||||
@selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream">
|
||||
<el-table-column align="center" type="selection" :reserve-selection="true" width="55">
|
||||
<el-table-column type="selection" :reserve-selection="true" min-width="55">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="名称" align="center">
|
||||
<el-table-column prop="name" label="名称" min-width="200">
|
||||
</el-table-column>
|
||||
<el-table-column prop="app" label="APP" align="center">
|
||||
<el-table-column prop="app" label="APP" min-width="200">
|
||||
</el-table-column>
|
||||
<el-table-column prop="stream" label="流ID" align="center">
|
||||
<el-table-column prop="stream" label="流ID" min-width="200">
|
||||
</el-table-column>
|
||||
<el-table-column prop="gbId" label="国标编码" width="200" align="center">
|
||||
<el-table-column prop="gbId" label="国标编码" min-width="200" >
|
||||
</el-table-column>
|
||||
<el-table-column prop="mediaServerId" label="流媒体" width="200" align="center">
|
||||
<el-table-column prop="mediaServerId" label="流媒体" min-width="200" >
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" align="center" width="200">
|
||||
<el-table-column label="开始时间" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button-group>
|
||||
{{ dateFormat(parseInt(scope.row.createStamp)) }}
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="正在推流" align="center" width="100">
|
||||
<el-table-column label="正在推流" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '是' : '否' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="360" align="center" fixed="right">
|
||||
<el-table-column label="操作" min-width="360" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button-group>
|
||||
<el-button size="mini" icon="el-icon-video-play"
|
||||
v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"
|
||||
@click="playPush(scope.row)">播放
|
||||
</el-button>
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" @click="stopPush(scope.row)">移除</el-button>
|
||||
<el-button size="mini" icon="el-icon-position" type="primary" v-if="!!!scope.row.gbId"
|
||||
@click="addToGB(scope.row)">加入国标
|
||||
</el-button>
|
||||
<el-button size="mini" icon="el-icon-position" type="primary" v-if="!!scope.row.gbId"
|
||||
@click="removeFromGB(scope.row)">移出国标
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
<el-button size="medium" icon="el-icon-video-play"
|
||||
v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"
|
||||
@click="playPush(scope.row)" type="text">播放
|
||||
</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-delete" type="text" @click="stopPush(scope.row)" style="color: #f56c6c" >移除</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-position" type="text" v-if="!!!scope.row.gbId"
|
||||
@click="addToGB(scope.row)">加入国标
|
||||
</el-button>
|
||||
<el-divider v-if="!!!scope.row.gbId" direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-position" type="text" v-if="!!scope.row.gbId"
|
||||
@click="removeFromGB(scope.row)">移出国标
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -284,6 +286,9 @@ export default {
|
||||
handleSelectionChange: function (val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
refresh: function () {
|
||||
this.initData();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
<div class="page-header-btn">
|
||||
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button>
|
||||
<el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button>
|
||||
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<devicePlayer ref="devicePlayer"></devicePlayer>
|
||||
<el-table :data="streamProxyList" border style="width: 100%" :height="winHeight">
|
||||
<el-table-column prop="name" label="名称" align="center" show-overflow-tooltip/>
|
||||
<el-table-column prop="app" label="流应用名" align="center" show-overflow-tooltip/>
|
||||
<el-table-column prop="stream" label="流ID" align="center" show-overflow-tooltip/>
|
||||
<el-table-column label="流地址" width="400" align="center" show-overflow-tooltip >
|
||||
<el-table :data="streamProxyList" style="width: 100%" :height="winHeight">
|
||||
<el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip/>
|
||||
<el-table-column prop="app" label="流应用名" min-width="120" show-overflow-tooltip/>
|
||||
<el-table-column prop="stream" label="流ID" min-width="120" show-overflow-tooltip/>
|
||||
<el-table-column label="流地址" min-width="400" show-overflow-tooltip >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
|
||||
@@ -27,8 +28,8 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mediaServerId" label="流媒体" width="150" align="center"></el-table-column>
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<el-table-column prop="mediaServerId" label="流媒体" min-width="180" ></el-table-column>
|
||||
<el-table-column label="类型" width="100" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium">{{scope.row.type}}</el-tag>
|
||||
@@ -36,8 +37,8 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="gbId" label="国标编码" width="180" align="center" show-overflow-tooltip/>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<el-table-column prop="gbId" label="国标编码" min-width="180" show-overflow-tooltip/>
|
||||
<el-table-column label="状态" min-width="120" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.status">在线</el-tag>
|
||||
@@ -45,7 +46,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用" width="120" align="center">
|
||||
<el-table-column label="启用" min-width="120" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.enable">已启用</el-tag>
|
||||
@@ -53,8 +54,8 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" align="center" width="150" show-overflow-tooltip/>
|
||||
<el-table-column label="转HLS" width="120" align="center">
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="150" show-overflow-tooltip/>
|
||||
<el-table-column label="转HLS" min-width="120" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.enable_hls">已启用</el-tag>
|
||||
@@ -62,7 +63,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="MP4录制" width="120" align="center">
|
||||
<el-table-column label="MP4录制" min-width="120" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag>
|
||||
@@ -70,7 +71,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="无人观看自动删除" width="160" align="center">
|
||||
<el-table-column label="无人观看自动删除" min-width="160" >
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag>
|
||||
@@ -80,14 +81,15 @@
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column label="操作" width="360" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="360" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button-group>
|
||||
<el-button size="mini" icon="el-icon-video-play" v-if="scope.row.enable" @click="play(scope.row)">播放</el-button>
|
||||
<el-button size="mini" icon="el-icon-close" type="success" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button>
|
||||
<el-button size="mini" icon="el-icon-check" type="primary" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button>
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteStreamProxy(scope.row)">删除</el-button>
|
||||
</el-button-group>
|
||||
<el-button size="medium" icon="el-icon-video-play" type="text" v-if="scope.row.enable" @click="play(scope.row)">播放</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-switch-button" type="text" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-check" type="text" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button>
|
||||
<el-divider v-if="!scope.row.enable" direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deleteStreamProxy(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -305,8 +307,10 @@
|
||||
console.log(error);
|
||||
that.getListLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
refresh: function (){
|
||||
this.initData();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<div id="channelList" style="width: 100%">
|
||||
<div class="page-header">
|
||||
<div class="page-title">
|
||||
<el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem;" type="primary" @click="showDevice">
|
||||
返回
|
||||
</el-button>
|
||||
通道列表({{ parentChannelId == 0 ? deviceId : parentChannelId }})</div>
|
||||
<el-button icon="el-icon-back" size="mini" style="font-size: 20px; color: #000;" type="text" @click="showDevice" ></el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
通道列表
|
||||
</div>
|
||||
<div class="page-header-btn">
|
||||
搜索:
|
||||
<el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
|
||||
@@ -25,84 +25,85 @@
|
||||
<el-option label="在线" value="true"></el-option>
|
||||
<el-option label="离线" value="false"></el-option>
|
||||
</el-select>
|
||||
<el-checkbox size="mini" v-model="autoList" @change="autoListChange">
|
||||
自动刷新
|
||||
</el-checkbox>
|
||||
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer>
|
||||
<!--设备列表-->
|
||||
<el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%">
|
||||
<el-table-column prop="channelId" label="通道编号" width="200">
|
||||
<el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%" header-row-class-name="table-header">
|
||||
<el-table-column prop="channelId" label="通道编号" min-width="200">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="通道名称">
|
||||
<el-table-column prop="deviceId" label="设备编号" min-width="200">
|
||||
</el-table-column>
|
||||
<el-table-column label="快照" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<img style="max-height: 3rem;max-width: 4rem;"
|
||||
v-if="scope.row.subCount === 0 && scope.row.parental === 0"
|
||||
:id="scope.row.deviceId + '_' + scope.row.channelId"
|
||||
:src="getSnap(scope.row)"
|
||||
@error="getSnapErrorEvent($event.target.id)"
|
||||
alt="">
|
||||
<!-- <el-image-->
|
||||
<!-- :id="'snapImg_' + scope.row.deviceId + '_' + scope.row.channelId"-->
|
||||
<!-- :src="getSnap(scope.row)"-->
|
||||
<!-- @error="getSnapErrorEvent($event, scope.row)"-->
|
||||
<!-- :fit="'contain'">-->
|
||||
<!-- <div slot="error" class="image-slot">-->
|
||||
<!-- <i class="el-icon-picture-outline"></i>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-image>-->
|
||||
<el-table-column prop="name" label="通道名称" min-width="200">
|
||||
</el-table-column>
|
||||
<el-table-column label="快照" min-width="120">
|
||||
<template v-slot:default="scope">
|
||||
<el-image
|
||||
:src="getSnap(scope.row)"
|
||||
:preview-src-list="getBigSnap(scope.row)"
|
||||
@error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)"
|
||||
:fit="'contain'"
|
||||
style="width: 60px">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="subCount" label="子节点数">
|
||||
<el-table-column prop="subCount" label="子节点数" min-width="120">
|
||||
</el-table-column>
|
||||
<el-table-column prop="manufacture" label="厂家">
|
||||
<el-table-column prop="manufacture" label="厂家" min-width="120">
|
||||
</el-table-column>
|
||||
<el-table-column label="位置信息" align="center">
|
||||
<el-table-column label="位置信息" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.longitude }},{{ scope.row.latitude }}</span>
|
||||
<span v-if="scope.row.longitude*scope.row.latitude > 0">{{ scope.row.longitude }},<br>{{ scope.row.latitude }}</span>
|
||||
<span v-if="scope.row.longitude*scope.row.latitude === 0">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ptztypeText" label="云台类型"/>
|
||||
<el-table-column label="开启音频" align="center">
|
||||
<el-table-column prop="ptztypeText" label="云台类型" min-width="120"/>
|
||||
<el-table-column label="开启音频" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="180" align="center">
|
||||
<el-table-column label="状态" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<div slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium" v-if="scope.row.status == 1">开启</el-tag>
|
||||
<el-tag size="medium" type="info" v-if="scope.row.status == 0">关闭</el-tag>
|
||||
<el-tag size="medium" v-if="scope.row.status === 1">在线</el-tag>
|
||||
<el-tag size="medium" type="info" v-if="scope.row.status === 0">离线</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right">
|
||||
<el-table-column label="操作" min-width="280" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button-group>
|
||||
<!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> -->
|
||||
<el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button>
|
||||
<el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId"
|
||||
@click="stopDevicePush(scope.row)">停止
|
||||
</el-button>
|
||||
<el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
|
||||
@click="changeSubchannel(scope.row)">查看
|
||||
</el-button>
|
||||
<el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录像
|
||||
</el-button>
|
||||
<!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
|
||||
</el-button-group>
|
||||
<!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> -->
|
||||
<el-button size="medium" icon="el-icon-video-play" type="text" @click="sendDevicePush(scope.row)">播放</el-button>
|
||||
<el-button size="medium" icon="el-icon-switch-button" type="text" style="color: #f56c6c" v-if="!!scope.row.streamId"
|
||||
@click="stopDevicePush(scope.row)">停止
|
||||
</el-button>
|
||||
<el-divider direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-s-open" type="text" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
|
||||
@click="changeSubchannel(scope.row)">查看
|
||||
</el-button>
|
||||
<el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider>
|
||||
<el-button size="medium" icon="el-icon-video-camera" type="text" @click="queryRecords(scope.row)">设备录像
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination style="float: right" @size-change="handleSizeChange" @current-change="currentChange"
|
||||
:current-page="currentPage" :page-size="count" :page-sizes="[15, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next" :total="total">
|
||||
<el-pagination
|
||||
style="float: right"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="currentChange"
|
||||
:current-page="currentPage"
|
||||
:page-size="count"
|
||||
:page-sizes="[15, 25, 35, 50]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
@@ -111,6 +112,8 @@
|
||||
import devicePlayer from './dialog/devicePlayer.vue'
|
||||
import uiHeader from '../layout/UiHeader.vue'
|
||||
import moment from "moment";
|
||||
import DviceService from "./service/DeviceService";
|
||||
import DeviceService from "./service/DeviceService";
|
||||
|
||||
export default {
|
||||
name: 'channelList',
|
||||
@@ -120,6 +123,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deviceService: new DeviceService(),
|
||||
device: null,
|
||||
deviceId: this.$route.params.deviceId,
|
||||
parentChannelId: this.$route.params.parentChannelId,
|
||||
deviceChannelList: [],
|
||||
@@ -135,16 +140,21 @@ export default {
|
||||
total: 0,
|
||||
beforeUrl: "/deviceList",
|
||||
isLoging: false,
|
||||
autoList: true,
|
||||
loadSnap: {}
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initData();
|
||||
if (this.autoList) {
|
||||
this.updateLooper = setInterval(this.initData, 5000);
|
||||
if (this.deviceId) {
|
||||
this.deviceService.getDevice(this.deviceId, (result)=>{
|
||||
this.device = result;
|
||||
|
||||
}, (error)=>{
|
||||
console.log("获取设备信息失败")
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
this.initData();
|
||||
|
||||
},
|
||||
destroyed() {
|
||||
@@ -177,12 +187,8 @@ export default {
|
||||
})
|
||||
},
|
||||
handleSizeChange: function (val) {
|
||||
var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1`
|
||||
this.$router.push(url).then(() => {
|
||||
this.initParam();
|
||||
this.initData();
|
||||
})
|
||||
|
||||
this.count = val;
|
||||
this.getDeviceChannelList();
|
||||
},
|
||||
getDeviceChannelList: function () {
|
||||
let that = this;
|
||||
@@ -227,7 +233,7 @@ export default {
|
||||
setTimeout(() => {
|
||||
|
||||
let snapId = deviceId + "_" + channelId;
|
||||
that.loadSnap[snapId] = 0;
|
||||
that.loadSnap[deviceId + channelId] = 0;
|
||||
that.getSnapErrorEvent(snapId)
|
||||
}, 5000)
|
||||
that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
|
||||
@@ -269,19 +275,24 @@ export default {
|
||||
});
|
||||
},
|
||||
getSnap: function (row) {
|
||||
return '/static/snap/' + row.deviceId + '_' + row.channelId + '.jpg'
|
||||
let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId
|
||||
return url
|
||||
},
|
||||
getSnapErrorEvent: function (id) {
|
||||
getBigSnap: function (row) {
|
||||
return [this.getSnap(row)]
|
||||
},
|
||||
getSnapErrorEvent: function (deviceId, channelId) {
|
||||
|
||||
if (typeof (this.loadSnap[id]) != "undefined") {
|
||||
console.log("下载截图" + this.loadSnap[id])
|
||||
if (this.loadSnap[id] > 5) {
|
||||
delete this.loadSnap[id];
|
||||
if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") {
|
||||
console.log("下载截图" + this.loadSnap[deviceId + channelId])
|
||||
if (this.loadSnap[deviceId + channelId] > 5) {
|
||||
delete this.loadSnap[deviceId + channelId];
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.loadSnap[id]++
|
||||
document.getElementById(id).setAttribute("src", '/static/snap/' + id + '.jpg?' + new Date().getTime())
|
||||
let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + deviceId + '/' + channelId
|
||||
this.loadSnap[deviceId + channelId]++
|
||||
document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime())
|
||||
}, 1000)
|
||||
|
||||
}
|
||||
@@ -342,12 +353,8 @@ export default {
|
||||
console.log(JSON.stringify(res));
|
||||
});
|
||||
},
|
||||
autoListChange: function () {
|
||||
if (this.autoList) {
|
||||
this.updateLooper = setInterval(this.initData, 1500);
|
||||
} else {
|
||||
window.clearInterval(this.updateLooper);
|
||||
}
|
||||
refresh: function () {
|
||||
this.initData();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,22 +84,34 @@ export default {
|
||||
}else {
|
||||
resolve([])
|
||||
}
|
||||
}, (list)=>{
|
||||
console.log("设备加载完成")
|
||||
}, (error)=>{
|
||||
|
||||
})
|
||||
}
|
||||
if (node.level === 1) {
|
||||
this.deviceService.getAllChannel(true, true, node.data.id, (catalogData) => {
|
||||
this.deviceService.getAllChannel(false, true, node.data.id, (channelData) => {
|
||||
let data = catalogData.concat(channelData)
|
||||
this.channelDataHandler(data, resolve)
|
||||
let channelArray = []
|
||||
this.deviceService.getAllChannel(true, true, node.data.id, catalogData =>{
|
||||
channelArray = channelArray.concat(catalogData)
|
||||
this.channelDataHandler(channelArray, resolve)
|
||||
},(endCatalogData) => {
|
||||
this.deviceService.getAllChannel(false, true, node.data.id, channelData => {
|
||||
channelArray = channelArray.concat(channelData)
|
||||
this.channelDataHandler(channelArray, resolve)
|
||||
}, endChannelList => {
|
||||
|
||||
})
|
||||
})
|
||||
}else if (node.level > 1){
|
||||
let channelArray = []
|
||||
this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{
|
||||
channelArray = channelArray.concat(catalogData)
|
||||
this.channelDataHandler(channelArray, resolve)
|
||||
}, (endCatalogData)=>{
|
||||
this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{
|
||||
let data = catalogData.concat(channelData)
|
||||
this.channelDataHandler(data, resolve)
|
||||
channelArray = channelArray.concat(channelData)
|
||||
this.channelDataHandler(channelArray, resolve)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let jessibucaPlayer = {};
|
||||
export default {
|
||||
name: 'jessibuca',
|
||||
data() {
|
||||
return {
|
||||
jessibuca: null,
|
||||
playing: false,
|
||||
isNotMute: false,
|
||||
quieting: false,
|
||||
@@ -49,6 +49,7 @@ export default {
|
||||
window.onerror = (msg) => {
|
||||
// console.error(msg)
|
||||
};
|
||||
console.log(this._uid)
|
||||
let paramUrl = decodeURIComponent(this.$route.params.url)
|
||||
this.$nextTick(() => {
|
||||
this.updatePlayerDomSize()
|
||||
@@ -88,7 +89,7 @@ export default {
|
||||
let options = {};
|
||||
console.log("hasAudio " + this.hasAudio)
|
||||
|
||||
this.jessibuca = new window.Jessibuca(Object.assign(
|
||||
jessibucaPlayer[this._uid] = new window.Jessibuca(Object.assign(
|
||||
{
|
||||
container: this.$refs.container,
|
||||
videoBuffer: 0.2, // 最大缓冲时长,单位秒
|
||||
@@ -117,70 +118,70 @@ export default {
|
||||
},
|
||||
options
|
||||
));
|
||||
|
||||
let jessibuca = jessibucaPlayer[this._uid];
|
||||
let _this = this;
|
||||
this.jessibuca.on("load", function () {
|
||||
jessibuca.on("load", function () {
|
||||
console.log("on load init");
|
||||
});
|
||||
|
||||
this.jessibuca.on("log", function (msg) {
|
||||
jessibuca.on("log", function (msg) {
|
||||
console.log("on log", msg);
|
||||
});
|
||||
this.jessibuca.on("record", function (msg) {
|
||||
jessibuca.on("record", function (msg) {
|
||||
console.log("on record:", msg);
|
||||
});
|
||||
this.jessibuca.on("pause", function () {
|
||||
jessibuca.on("pause", function () {
|
||||
_this.playing = false;
|
||||
});
|
||||
this.jessibuca.on("play", function () {
|
||||
jessibuca.on("play", function () {
|
||||
_this.playing = true;
|
||||
});
|
||||
this.jessibuca.on("fullscreen", function (msg) {
|
||||
jessibuca.on("fullscreen", function (msg) {
|
||||
console.log("on fullscreen", msg);
|
||||
_this.fullscreen = msg
|
||||
});
|
||||
|
||||
this.jessibuca.on("mute", function (msg) {
|
||||
jessibuca.on("mute", function (msg) {
|
||||
console.log("on mute", msg);
|
||||
_this.isNotMute = !msg;
|
||||
});
|
||||
this.jessibuca.on("audioInfo", function (msg) {
|
||||
jessibuca.on("audioInfo", function (msg) {
|
||||
// console.log("audioInfo", msg);
|
||||
});
|
||||
|
||||
this.jessibuca.on("videoInfo", function (msg) {
|
||||
jessibuca.on("videoInfo", function (msg) {
|
||||
// this.videoInfo = msg;
|
||||
console.log("videoInfo", msg);
|
||||
|
||||
});
|
||||
|
||||
this.jessibuca.on("bps", function (bps) {
|
||||
jessibuca.on("bps", function (bps) {
|
||||
// console.log('bps', bps);
|
||||
|
||||
});
|
||||
let _ts = 0;
|
||||
this.jessibuca.on("timeUpdate", function (ts) {
|
||||
jessibuca.on("timeUpdate", function (ts) {
|
||||
// console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
|
||||
_ts = ts;
|
||||
});
|
||||
|
||||
this.jessibuca.on("videoInfo", function (info) {
|
||||
jessibuca.on("videoInfo", function (info) {
|
||||
console.log("videoInfo", info);
|
||||
});
|
||||
|
||||
this.jessibuca.on("error", function (error) {
|
||||
jessibuca.on("error", function (error) {
|
||||
console.log("error", error);
|
||||
});
|
||||
|
||||
this.jessibuca.on("timeout", function () {
|
||||
jessibuca.on("timeout", function () {
|
||||
console.log("timeout");
|
||||
});
|
||||
|
||||
this.jessibuca.on('start', function () {
|
||||
jessibuca.on('start', function () {
|
||||
console.log('start');
|
||||
})
|
||||
|
||||
this.jessibuca.on("performance", function (performance) {
|
||||
jessibuca.on("performance", function (performance) {
|
||||
let show = "卡顿";
|
||||
if (performance === 2) {
|
||||
show = "非常流畅";
|
||||
@@ -189,25 +190,25 @@ export default {
|
||||
}
|
||||
_this.performance = show;
|
||||
});
|
||||
this.jessibuca.on('buffer', function (buffer) {
|
||||
jessibuca.on('buffer', function (buffer) {
|
||||
// console.log('buffer', buffer);
|
||||
})
|
||||
|
||||
this.jessibuca.on('stats', function (stats) {
|
||||
jessibuca.on('stats', function (stats) {
|
||||
// console.log('stats', stats);
|
||||
})
|
||||
|
||||
this.jessibuca.on('kBps', function (kBps) {
|
||||
jessibuca.on('kBps', function (kBps) {
|
||||
_this.kBps = Math.round(kBps);
|
||||
});
|
||||
|
||||
// 显示时间戳 PTS
|
||||
this.jessibuca.on('videoFrame', function () {
|
||||
jessibuca.on('videoFrame', function () {
|
||||
|
||||
})
|
||||
|
||||
//
|
||||
this.jessibuca.on('metadata', function () {
|
||||
jessibuca.on('metadata', function () {
|
||||
|
||||
});
|
||||
},
|
||||
@@ -216,40 +217,40 @@ export default {
|
||||
},
|
||||
play: function (url) {
|
||||
console.log(url)
|
||||
if (this.jessibuca) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
this.destroy();
|
||||
}
|
||||
this.create();
|
||||
this.jessibuca.on("play", () => {
|
||||
jessibucaPlayer[this._uid].on("play", () => {
|
||||
this.playing = true;
|
||||
this.loaded = true;
|
||||
this.quieting = this.jessibuca.quieting;
|
||||
this.quieting = jessibuca.quieting;
|
||||
});
|
||||
if (this.jessibuca.hasLoaded()) {
|
||||
this.jessibuca.play(url);
|
||||
if (jessibucaPlayer[this._uid].hasLoaded()) {
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
} else {
|
||||
this.jessibuca.on("load", () => {
|
||||
jessibucaPlayer[this._uid].on("load", () => {
|
||||
console.log("load 播放")
|
||||
this.jessibuca.play(url);
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
});
|
||||
}
|
||||
},
|
||||
pause: function () {
|
||||
if (this.jessibuca) {
|
||||
this.jessibuca.pause();
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].pause();
|
||||
}
|
||||
this.playing = false;
|
||||
this.err = "";
|
||||
this.performance = "";
|
||||
},
|
||||
destroy: function () {
|
||||
if (this.jessibuca) {
|
||||
this.jessibuca.destroy();
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].destroy();
|
||||
}
|
||||
if (document.getElementById("buttonsBox") == null) {
|
||||
this.$refs.container.appendChild(this.btnDom)
|
||||
}
|
||||
this.jessibuca = null;
|
||||
jessibucaPlayer[this._uid] = null;
|
||||
this.playing = false;
|
||||
this.err = "";
|
||||
this.performance = "";
|
||||
@@ -262,7 +263,7 @@ export default {
|
||||
},
|
||||
fullscreenSwich: function () {
|
||||
let isFull = this.isFullscreen()
|
||||
this.jessibuca.setFullscreen(!isFull)
|
||||
jessibucaPlayer[this._uid].setFullscreen(!isFull)
|
||||
this.fullscreen = !isFull;
|
||||
},
|
||||
isFullscreen: function () {
|
||||
@@ -273,8 +274,8 @@ export default {
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.jessibuca) {
|
||||
this.jessibuca.destroy();
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].destroy();
|
||||
}
|
||||
this.playing = false;
|
||||
this.loaded = false;
|
||||
|
||||
@@ -235,10 +235,8 @@
|
||||
<el-table-column prop="local_ip" label="本地"></el-table-column>
|
||||
<el-table-column prop="typeid" label="类型"></el-table-column>
|
||||
<el-table-column align="right">
|
||||
<template slot="header" slot-scope="scope">
|
||||
<el-button icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<template v-slot:default="scope">
|
||||
<el-button size="mini" icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button>
|
||||
<el-button @click.native.prevent="deleteRow(scope.$index, allSessionData)" type="text" size="small">移除
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
<el-form-item label="语音发送通道" prop="name">
|
||||
<el-input v-model="form.audioChannelForReceive" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="地理坐标系" prop="geoCoordSys" >
|
||||
<el-select v-model="form.geoCoordSys" style="float: left; width: 100%" >
|
||||
<el-option key="WGS84" label="WGS84" value="WGS84"></el-option>
|
||||
<el-option key="GCJ02" label="GCJ02" value="GCJ02"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" >
|
||||
<el-input v-model="form.subscribeCycleForCatalog" clearable ></el-input>
|
||||
</el-form-item>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user