diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md index cd306403..1a9d58b4 100644 --- a/doc/_content/introduction/compile.md +++ b/doc/_content/introduction/compile.md @@ -69,7 +69,7 @@ git clone https://github.com/648540858/wvp-GB28181-pro.git ### 5.2 编译前端页面 ```shell script cd wvp-GB28181-pro/web_src/ -npm --registry=https://registry.npm.taobao.org install +npm --registry=https://registry.npmmirror.com install npm run build ``` 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 45cd57be..99fc074e 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -139,4 +139,15 @@ 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_"; + /** + * Redis Const + * 设备录像信息结果前缀 + */ + public static final String REDIS_RECORD_INFO_RES_PRE = "GB_RECORD_INFO_RES_"; + /** + * Redis Const + * 设备录像信息结果前缀 + */ + public static final String REDIS_RECORD_INFO_RES_COUNT_PRE = "GB_RECORD_INFO_RES_COUNT:"; + } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java index d7382e07..af14f6a5 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.utils.DateUtil; +import org.junit.jupiter.api.Order; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -14,6 +15,7 @@ import java.util.regex.Pattern; @Configuration("mediaConfig") +@Order(0) public class MediaConfig{ private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java index c4086b9e..d24380a3 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; +import org.junit.jupiter.api.Order; import org.mitre.dsmiley.httpproxy.ProxyServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +26,7 @@ import java.net.ConnectException; */ @SuppressWarnings(value = {"rawtypes", "unchecked"}) @Configuration +@Order(1) public class ProxyServletConfig { private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java index 69947fa8..f32c250a 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java @@ -1,12 +1,14 @@ package com.genersoft.iot.vmp.conf; +import org.junit.jupiter.api.Order; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; @Component @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true) +@Order(0) public class SipConfig { private String ip; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java index 15e38aea..b3f144a7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java @@ -18,7 +18,7 @@ import java.util.List; * @author lin */ @Component -@Order(value=3) +@Order(value=13) public class SipPlatformRunner implements CommandLineRunner { @Autowired diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java index 2cc74959..b3ae3efe 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java @@ -1,14 +1,11 @@ package com.genersoft.iot.vmp.conf; -import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; -import io.swagger.v3.oas.models.media.StringSchema; -import io.swagger.v3.oas.models.parameters.HeaderParameter; +import org.junit.jupiter.api.Order; import org.springdoc.core.GroupedOpenApi; -import org.springdoc.core.SpringDocConfigProperties; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -17,6 +14,7 @@ import org.springframework.context.annotation.Configuration; * @author lin */ @Configuration +@Order(1) public class SpringDocConfig { @Value("${doc.enabled: true}") diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java index 7377702f..2f9921c7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.conf; +import org.junit.jupiter.api.Order; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; @@ -12,6 +13,7 @@ import java.util.concurrent.ThreadPoolExecutor; * @author lin */ @Configuration +@Order(1) @EnableAsync(proxyTargetClass = true) public class ThreadPoolTaskConfig { diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index 81fbf740..811f7e96 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.conf; +import org.junit.jupiter.api.Order; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -11,6 +12,7 @@ import java.util.List; */ @Component @ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) +@Order(0) public class UserSetting { private Boolean savePositionHistory = Boolean.FALSE; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java index a24ade41..902e41b7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java @@ -1,10 +1,12 @@ package com.genersoft.iot.vmp.conf; +import org.junit.jupiter.api.Order; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "version") +@Order(0) public class VersionConfig { private String version; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java index 1eca1319..2b181812 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java @@ -3,18 +3,18 @@ package com.genersoft.iot.vmp.conf.redis; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.service.redisMsg.*; +import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.serializer.StringRedisSerializer; -import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; - /** * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 @@ -23,6 +23,7 @@ import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; * */ @Configuration +@Order(value=1) public class RedisConfig extends CachingConfigurerSupport { @Autowired diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index 0d3a7d6e..cce0d11c 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.conf.security; import com.genersoft.iot.vmp.conf.UserSetting; +import org.junit.jupiter.api.Order; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -25,6 +26,7 @@ import java.util.List; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) +@Order(1) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index cfac446d..d452771e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Component -@Order(value=1) +@Order(value=10) public class SipLayer implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java index d9e89fa2..91f12d5b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java @@ -109,6 +109,30 @@ public class AudioBroadcastCatch { return sipTransactionInfo; } + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { this.sipTransactionInfo = sipTransactionInfo; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java index 451c589c..cdba4454 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java @@ -28,7 +28,7 @@ import java.util.Map; * @author lin */ @Component -@Order(value=4) +@Order(value=14) public class SipRunner implements CommandLineRunner { @Autowired @@ -69,6 +69,26 @@ public class SipRunner implements CommandLineRunner { // 重置cseq计数 redisCatchStorage.resetAllCSEQ(); // 清理redis + // 清理数据库不存在但是redis中存在的数据 + List devicesInDb = deviceService.getAll(); + if (devicesInDb.size() == 0) { + redisCatchStorage.removeAllDevice(); + }else { + List devicesInRedis = redisCatchStorage.getAllDevices(); + if (devicesInRedis.size() > 0) { + Map deviceMapInDb = new HashMap<>(); + devicesInDb.parallelStream().forEach(device -> { + deviceMapInDb.put(device.getDeviceId(), device); + }); + devicesInRedis.parallelStream().forEach(device -> { + if (deviceMapInDb.get(device.getDeviceId()) == null) { + redisCatchStorage.removeDevice(device.getDeviceId()); + } + }); + } + } + + // 查找国标推流 List sendRtpItems = redisCatchStorage.queryAllSendRTPServer(); if (sendRtpItems.size() > 0) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java index e5d0343c..b917c3d6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java @@ -235,6 +235,25 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { }else { catalogXml.append("
" + channel.getAddress() + "
\r\n"); } + catalogXml.append("" + channel.getBlock() + "\r\n"); + catalogXml.append("" + channel.getSafetyWay() + "\r\n"); + catalogXml.append("" + channel.getCertNum() + "\r\n"); + catalogXml.append("" + channel.getCertifiable() + "\r\n"); + catalogXml.append("" + channel.getErrCode() + "\r\n"); + catalogXml.append("" + channel.getEndTime() + "\r\n"); + catalogXml.append("" + channel.getSecrecy() + "\r\n"); + catalogXml.append("" + channel.getIpAddress() + "\r\n"); + catalogXml.append("" + channel.getPort() + "\r\n"); + catalogXml.append("" + channel.getPort() + "\r\n"); + catalogXml.append("" + (channel.getStatus() == 1?"ON":"OFF") + "\r\n"); + catalogXml.append("" + + (channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude()) + + "\r\n"); + catalogXml.append("" + + (channel.getLatitudeWgs84() != 0? channel.getLatitudeWgs84():channel.getLatitude()) + + "\r\n"); + + } } catalogXml.append("\r\n"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index 6f3e6c09..73693280 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -360,7 +360,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements return; } String username = sdp.getOrigin().getUsername(); - String addressStr = sdp.getOrigin().getAddress(); + String addressStr = sdp.getConnection().getAddress(); logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc); Device device = null; @@ -903,8 +903,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) Device device = redisCatchStorage.getDevice(requesterId); - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId); - if (audioBroadcastCatch == null) { + AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(requesterId, channelId); + if (broadcastCatch == null) { logger.warn("来自设备的Invite请求非语音广播,已忽略,requesterId: {}/{}", requesterId, channelId); try { responseAck(request, Response.FORBIDDEN); @@ -915,13 +915,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements } if (device != null) { logger.info("收到设备" + requesterId + "的语音广播Invite请求"); - String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + audioBroadcastCatch.getChannelId(); + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + broadcastCatch.getChannelId(); dynamicTask.stop(key); try { responseAck(request, Response.TRYING); } catch (SipException | InvalidArgumentException | ParseException e) { logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); return; } String contentString = new String(request.getRawContent()); @@ -977,28 +977,18 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 } catch (SipException | InvalidArgumentException | ParseException e) { logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); return; } return; } - String addressStr = sdp.getOrigin().getAddress(); + String addressStr = sdp.getConnection().getAddress(); logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, ssrc, mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP"); - MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); - if (mediaServerItem == null) { - logger.warn("未找到可用的zlm"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); - } - return; - } + MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem(); SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, - device.getDeviceId(), audioBroadcastCatch.getChannelId(), + device.getDeviceId(), broadcastCatch.getChannelId(), mediaTransmissionTCP, false); if (sendRtpItem == null) { @@ -1007,22 +997,20 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements responseAck(request, Response.BUSY_HERE); } catch (SipException | InvalidArgumentException | ParseException e) { logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); return; } return; } - String app = "broadcast"; - String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId(); CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); sendRtpItem.setPlayType(InviteStreamType.BROADCAST); sendRtpItem.setCallId(callIdHeader.getCallId()); sendRtpItem.setPlatformId(requesterId); sendRtpItem.setStatus(1); - sendRtpItem.setApp(app); - sendRtpItem.setStreamId(stream); + sendRtpItem.setApp(broadcastCatch.getApp()); + sendRtpItem.setStreamId(broadcastCatch.getStream()); sendRtpItem.setPt(8); sendRtpItem.setUsePs(false); sendRtpItem.setRtcp(false); @@ -1034,23 +1022,22 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements redisCatchStorage.updateSendRTPSever(sendRtpItem); - - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream); + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream()); if (streamReady) { sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); }else { - logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", app, stream); + logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream()); try { responseAck(request, Response.GONE); } catch (SipException | InvalidArgumentException | ParseException e) { logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); return; } - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); } } catch (SdpException e) { logger.error("[SDP解析异常]", e); - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); } } else { logger.warn("来自无效设备/平台的请求"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java index 94646bb4..a468926f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java @@ -38,6 +38,7 @@ import javax.sip.header.FromHeader; import javax.sip.message.Response; import java.text.ParseException; import java.util.Iterator; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; /** @@ -154,6 +155,17 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements Element deviceIdElement = rootElement.element("DeviceID"); String channelId = deviceIdElement.getTextTrim().toString(); Device device = redisCatchStorage.getDevice(deviceId); + + if (device == null) { + // 根据通道id查询设备Id + List deviceList = deviceChannelService.getDeviceByChannelId(channelId); + if (deviceList.size() > 0) { + device = deviceList.get(0); + }else { + logger.warn("[mobilePosition移动位置Notify] 未找到通道{}所属的设备", channelId); + return; + } + } if (device != null) { if (!ObjectUtils.isEmpty(device.getName())) { mobilePosition.setDeviceName(device.getName()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java index 2effe112..49277637 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java @@ -1,6 +1,5 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.genersoft.iot.vmp.conf.ServiceInfo; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; @@ -95,7 +94,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen // } // } - System.out.println(ServiceInfo.getServerPort()); +// System.out.println(ServiceInfo.getServerPort()); SIPRequest request = (SIPRequest)evt.getRequest(); Response response = null; boolean passwordCorrect = false; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java index 2129ee12..119b5625 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.session.CatalogDataCatch; 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.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; @@ -108,6 +109,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp continue; } DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null); + deviceChannel = SipUtils.updateGps(deviceChannel, device.getGeoCoordSys()); deviceChannel.setDeviceId(take.getDevice().getDeviceId()); channelList.add(deviceChannel); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java index 8b4ae2e1..6d8d8f56 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java @@ -1,16 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; +import com.genersoft.iot.vmp.common.VideoManagerConstants; 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; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; 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.response.ResponseMessageHandler; import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.utils.UJson; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; import gov.nist.javax.sip.message.SIPRequest; -import org.dom4j.DocumentException; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,11 +27,9 @@ import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; @@ -48,9 +47,6 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @Autowired private ResponseMessageHandler responseMessageHandler; - @Autowired - private RecordDataCatch recordDataCatch; - @Autowired private DeferredResultHolder deferredResultHolder; @@ -61,6 +57,8 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @Autowired private ThreadPoolTaskExecutor taskExecutor; + private Long recordInfoTtl = 1800L; + @Override public void afterPropertiesSet() throws Exception { responseMessageHandler.addHandler(cmdType, this); @@ -68,93 +66,93 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - boolean isEmpty = taskQueue.isEmpty(); try { // 回复200 OK responseAck((SIPRequest) evt.getRequest(), Response.OK); }catch (SipException | InvalidArgumentException | ParseException e) { logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); } - taskQueue.offer(new HandlerCatchData(evt, device, rootElement)); - if (isEmpty) { - taskExecutor.execute(()->{ - while (!taskQueue.isEmpty()) { - try { - HandlerCatchData take = taskQueue.poll(); - Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset()); - if (rootElement == null) { - logger.warn("[ 国标录像 ] content cannot be null, {}", evt.getRequest()); - continue; - } - 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 (!ObjectUtils.isEmpty(sumNumStr)) { - sumNum = Integer.parseInt(sumNumStr); - } - recordInfo.setSumNum(sumNum); - Element recordListElement = rootElementForCharset.element("RecordList"); - if (recordListElement == null || sumNum == 0) { - logger.info("无录像数据"); - int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, new ArrayList<>()); - recordInfo.setCount(count); - eventPublisher.recordEndEventPush(recordInfo); - releaseRequest(take.getDevice().getDeviceId(), sn); - } else { - Iterator recordListIterator = recordListElement.elementIterator(); - if (recordListIterator != null) { - List 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")); + taskExecutor.execute(()->{ + try { - 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); - } - recordInfo.setRecordList(recordList); - int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, recordList);recordInfo.setCount(count); - logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum); - // 发送消息,如果是上级查询此录像,则会通过这里通知给上级 - eventPublisher.recordEndEventPush(recordInfo); - } - if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){ - releaseRequest(take.getDevice().getDeviceId(), sn); + String sn = getText(rootElement, "SN"); + String channelId = getText(rootElement, "DeviceID"); + RecordInfo recordInfo = new RecordInfo(); + recordInfo.setChannelId(channelId); + recordInfo.setDeviceId(device.getDeviceId()); + recordInfo.setSn(sn); + recordInfo.setName(getText(rootElement, "Name")); + String sumNumStr = getText(rootElement, "SumNum"); + int sumNum = 0; + if (!ObjectUtils.isEmpty(sumNumStr)) { + sumNum = Integer.parseInt(sumNumStr); + } + recordInfo.setSumNum(sumNum); + Element recordListElement = rootElement.element("RecordList"); + if (recordListElement == null || sumNum == 0) { + logger.info("无录像数据"); + recordInfo.setCount(sumNum); + eventPublisher.recordEndEventPush(recordInfo); + releaseRequest(device.getDeviceId(), sn,recordInfo); + } else { + Iterator recordListIterator = recordListElement.elementIterator(); + if (recordListIterator != null) { + List 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")); + + 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); } - } catch (DocumentException e) { - logger.error("xml解析异常: ", e); - } catch (Exception e) { - logger.warn("[国标录像] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + Map map = recordList.stream() + .filter(record -> record.getDeviceId() != null) + .collect(Collectors.toMap(record -> record.getStartTime()+ record.getEndTime(), UJson::writeJson)); + // 获取任务结果数据 + String resKey = VideoManagerConstants.REDIS_RECORD_INFO_RES_PRE + channelId + sn; + RedisUtil.hmset(resKey, map, recordInfoTtl); + String resCountKey = VideoManagerConstants.REDIS_RECORD_INFO_RES_COUNT_PRE + channelId + sn; + long incr = RedisUtil.incr(resCountKey, map.size()); + RedisUtil.expire(resCountKey, recordInfoTtl); + recordInfo.setRecordList(recordList); + recordInfo.setCount(Math.toIntExact(incr)); + eventPublisher.recordEndEventPush(recordInfo); + if (incr < sumNum) { + return; + } + // 已接收完成 + List resList = RedisUtil.hmget(resKey).values().stream().map(e -> UJson.readJson(e.toString(), RecordItem.class)).collect(Collectors.toList()); + if (resList.size() < sumNum) { + return; + } + recordInfo.setRecordList(resList); + releaseRequest(device.getDeviceId(), sn,recordInfo); } } - }); - } + } catch (Exception e) { + logger.error("[国标录像] 发现未处理的异常, "+e.getMessage(), e); + } + }); } @Override @@ -162,15 +160,14 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent } - public void releaseRequest(String deviceId, String sn){ + public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){ String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; // 对数据进行排序 - Collections.sort(recordDataCatch.getRecordInfo(deviceId, sn).getRecordList()); + Collections.sort(recordInfo.getRecordList()); RequestMessage msg = new RequestMessage(); msg.setKey(key); - msg.setData(recordDataCatch.getRecordInfo(deviceId, sn)); + msg.setData(recordInfo); deferredResultHolder.invokeAllResult(msg); - recordDataCatch.remove(deviceId, sn); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java index 16fa6d83..7feac173 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java @@ -65,8 +65,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { */ @Override public void process(ResponseEvent evt ){ + logger.debug("接收到消息:" + evt.getResponse()); try { - SIPResponse response = (SIPResponse)evt.getResponse(); int statusCode = response.getStatusCode(); // trying不会回复 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java index 45c5a90a..98d7dd3e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.gb28181.utils; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.utils.GitUtil; import gov.nist.javax.sip.address.AddressImpl; @@ -168,4 +169,37 @@ public class SipUtils { return new RemoteAddressInfo(remoteAddress, remotePort); } + + public static DeviceChannel updateGps(DeviceChannel deviceChannel, String geoCoordSys) { + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { + + if (geoCoordSys == null) { + geoCoordSys = "WGS84"; + } + if ("WGS84".equals(geoCoordSys)) { + 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(geoCoordSys)) { + 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()); + } + return deviceChannel; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java index 0ea6d877..d51ab9f5 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java @@ -1,11 +1,9 @@ package com.genersoft.iot.vmp.gb28181.utils; -import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.TreeType; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.utils.DateUtil; import org.dom4j.Attribute; @@ -400,6 +398,7 @@ public class XmlUtil { } else { deviceChannel.setLatitude(0.00); } + deviceChannel.setGpsTime(DateUtil.getNow()); @@ -414,6 +413,7 @@ public class XmlUtil { } else { deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); } + return deviceChannel; } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 26940c1e..16e7e62e 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -282,12 +282,12 @@ public class ZLMHttpHookListener { taskExecutor.execute(() -> { ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); if (subscribe != null) { + if (mediaInfo != null) { subscribe.response(mediaInfo, json); } } // 流消失移除redis play - List tracks = param.getTracks(); if (param.isRegist()) { if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index a4b4cb7e..afbbafa8 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -25,7 +25,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @Component -@Order(value=2) +@Order(value=12) public class ZLMRunner implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java index 31e568a3..c192dd5a 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java @@ -46,4 +46,14 @@ public interface IDeviceChannelService { * @return */ List queryAllChannelList(String platformId); + + /** + * 数据位置信息格式处理 + */ + boolean updateAllGps(Device device); + + /** + * 查询通道所属的设备 + */ + List getDeviceByChannelId(String channelId); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java index b87c9a73..072519bd 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java @@ -163,4 +163,8 @@ public interface IDeviceService { */ ResourceBaceInfo getOverview(); + /** + * 获取所有设备 + */ + List getAll(); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java index 01aa51cd..a8b67451 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java @@ -67,6 +67,8 @@ public interface IPlayService { void stopAudioBroadcast(String deviceId, String channelId); + void audioBroadcastCmd(Device device, String channelId, int timeout, MediaServerItem mediaServerItem, String sourceApp, String sourceStream, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; + void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java index 880b6971..336082f5 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java @@ -19,6 +19,7 @@ import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * @author lin @@ -176,5 +177,36 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { return channelMapper.queryChannelListInAll(null, null, null, platformId, null); } + @Override + public boolean updateAllGps(Device device) { + List deviceChannels = channelMapper.getChannelsWithoutTransform(device.getDeviceId()); + List result = new CopyOnWriteArrayList<>(); + if (deviceChannels.size() == 0) { + return true; + } + String now = DateUtil.getNow(); + deviceChannels.parallelStream().forEach(deviceChannel -> { + deviceChannel.setUpdateTime(now); + result.add(updateGps(deviceChannel, device)); + }); + int limitCount = 300; + if (result.size() > limitCount) { + for (int i = 0; i < result.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > result.size()) { + toIndex = result.size(); + } + channelMapper.batchUpdate(result.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdate(result); + } + return true; + } + + @Override + public List getDeviceByChannelId(String channelId) { + return channelMapper.getDeviceByChannelId(channelId); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java index 1c57d0b2..a4309dec 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -655,4 +655,9 @@ public class DeviceServiceImpl implements IDeviceService { public ResourceBaceInfo getOverview() { return deviceMapper.getOverview(); } + + @Override + public List getAll() { + return deviceMapper.getAll(); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index f2825a5a..16b0e9db 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -1027,8 +1027,8 @@ public class PlayServiceImpl implements IPlayService { SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { // 查询流是否存在,不存在则认为是异常状态 - MediaServerItem mediaServerItemInUse = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItemInUse, sendRtpItem.getApp(), sendRtpItem.getStreamId()); + MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStreamId()); if (streamReady) { logger.warn("语音广播已经开启: {}", channelId); event.call("语音广播已经开启"); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java index b98f1886..6540e3e3 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java @@ -366,7 +366,7 @@ public class StreamPushServiceImpl implements IStreamPushService { // 存储数据到stream_push表 streamPushMapper.addAll(streamPushItems); List streamPushItemForGbStream = streamPushItems.stream() - .filter(streamPushItem-> streamPushItem.getId() != null) + .filter(streamPushItem-> streamPushItem.getGbId() != null) .collect(Collectors.toList()); // 存储数据到gb_stream表, id会返回到streamPushItemForGbStream里 if (streamPushItemForGbStream.size() > 0) { diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index 15b6d807..1e10469f 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -258,4 +258,7 @@ public interface IRedisCatchStorage { List queryAllSendRTPServer(); + List getAllDevices(); + + void removeAllDevice(); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java index 0b0a7d91..938f669b 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java @@ -56,7 +56,7 @@ public interface IVideoManagerStorage { * @param count 每页数量 * @return */ - public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count); + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count); public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List channelIds); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java index 8da1a09b..83f93343 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.storager.dao; +import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; @@ -357,4 +358,21 @@ public interface DeviceChannelMapper { @Select("select count(1) as total, sum(status) as online from device_channel") ResourceBaceInfo getOverview(); + + @Select("select channelId" + + ", deviceId" + + ", latitude" + + ", longitude" + + ", latitudeWgs84" + + ", longitudeWgs84" + + ", latitudeGcj02" + + ", longitudeGcj02 " + + "from device_channel where deviceId = #{deviceId} " + + "and latitude != 0 " + + "and longitude != 0 " + + "and (latitudeGcj02 = 0 or latitudeWgs84 = 0 or longitudeWgs84 = 0 or longitudeGcj02 = 0)") + List getChannelsWithoutTransform(String deviceId); + + @Select("select de.* from device de left join device_channel dc on de.deviceId = dc.deviceId where dc.channelId=#{channelId}") + List getDeviceByChannelId(String channelId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java index 8143d35a..0aed8207 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java @@ -280,4 +280,6 @@ public interface DeviceMapper { @Select("select count(1) as total, sum(online) as online from device") ResourceBaceInfo getOverview(); + @Select("select * from device") + List getAll(); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index e997e4d5..4f229d78 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -664,6 +664,31 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { RedisUtil.del(key); } + @Override + public void removeAllDevice() { + String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(scanKey); + for (Object key : keys) { + RedisUtil.del((String) key); + } + } + + @Override + public List getAllDevices() { + String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(scanKey); + for (Object o : keys) { + String key = (String) o; + Device device = JsonUtil.redisJsonToObject(key, Device.class); + if (Objects.nonNull(device)) { // 只取没有存过得 + result.add(JsonUtil.redisJsonToObject(key, Device.class)); + } + } + + return result; + } + @Override public Device getDevice(String deviceId) { String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java index 655a54c5..ab98994c 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java @@ -226,8 +226,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { if (allChannelMap.containsKey(deviceChannel.getChannelId())) { deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); + deviceChannel.setUpdateTime(DateUtil.getNow()); updateChannels.add(deviceChannel); }else { + deviceChannel.setCreateTime(DateUtil.getNow()); addChannels.add(deviceChannel); } if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { diff --git a/src/main/java/com/genersoft/iot/vmp/utils/UJson.java b/src/main/java/com/genersoft/iot/vmp/utils/UJson.java new file mode 100644 index 00000000..a2d40339 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/UJson.java @@ -0,0 +1,150 @@ +package com.genersoft.iot.vmp.utils; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * @author gaofuwang + * @version 1.0 + * @date 2022/3/11 10:17 + */ +public class UJson { + + private static Logger logger = LoggerFactory.getLogger(UJson.class); + public static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + + static { + JSON_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); + } + + private ObjectNode node; + + public UJson(){ + this.node = JSON_MAPPER.createObjectNode(); + } + + public UJson(String json){ + if(StringUtils.isBlank(json)){ + this.node = JSON_MAPPER.createObjectNode(); + }else{ + try { + this.node = JSON_MAPPER.readValue(json, ObjectNode.class); + }catch (Exception e){ + logger.error(e.getMessage(), e); + this.node = JSON_MAPPER.createObjectNode(); + } + } + } + + public UJson(ObjectNode node){ + this.node = node; + } + + public String asText(String key){ + JsonNode jsonNode = node.get(key); + if(Objects.isNull(jsonNode)){ + return ""; + } + return jsonNode.asText(); + } + + public String asText(String key, String defaultVal){ + JsonNode jsonNode = node.get(key); + if(Objects.isNull(jsonNode)){ + return ""; + } + return jsonNode.asText(defaultVal); + } + + public UJson put(String key, String value){ + this.node.put(key, value); + return this; + } + + public UJson put(String key, Integer value){ + this.node.put(key, value); + return this; + } + + public static UJson json(){ + return new UJson(); + } + + public static UJson json(String json){ + return new UJson(json); + } + + public static T readJson(String json, Class clazz){ + if(StringUtils.isBlank(json)){ + return null; + } + try { + return JSON_MAPPER.readValue(json, clazz); + }catch (Exception e){ + logger.error(e.getMessage(), e); + return null; + } + } + + public static String writeJson(Object object) { + try{ + return JSON_MAPPER.writeValueAsString(object); + }catch (Exception e){ + logger.error(e.getMessage(), e); + return ""; + } + } + + @Override + public String toString() { + return node.toString(); + } + + public int asInt(String key, int defValue) { + JsonNode jsonNode = this.node.get(key); + if(Objects.isNull(jsonNode)){ + return defValue; + } + return jsonNode.asInt(defValue); + } + + public UJson getSon(String key) { + JsonNode sonNode = this.node.get(key); + if(Objects.isNull(sonNode)){ + return new UJson(); + } + return new UJson((ObjectNode) sonNode); + } + + public UJson set(String key, ObjectNode sonNode) { + this.node.set(key, sonNode); + return this; + } + + public UJson set(String key, UJson sonNode) { + this.node.set(key, sonNode.node); + return this; + } + + public Iterator> fields() { + return this.node.fields(); + } + + public ObjectNode getNode() { + return this.node; + } + + public UJson setAll(UJson json) { + this.node.setAll(json.node); + return this; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java index 50152cda..a50553d4 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java @@ -238,7 +238,7 @@ public class RedisUtil { * @param time 时间 * @return true / false */ - public static boolean hmset(String key, Map map, long time) { + public static boolean hmset(String key, Map map, long time) { if (redisTemplate == null) { redisTemplate = SpringBeanFactory.getBean("redisTemplate"); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java index e7c24aae..e577baa2 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java @@ -1,43 +1,96 @@ package com.genersoft.iot.vmp.vmanager.bean; import com.genersoft.iot.vmp.common.StreamInfo; +import io.swagger.v3.oas.annotations.media.Schema; +@Schema(description = "流信息") public class StreamContent { + @Schema(description = "应用名") private String app; + + @Schema(description = "流ID") private String stream; + @Schema(description = "IP") private String ip; + @Schema(description = "HTTP-FLV流地址") private String flv; + @Schema(description = "HTTPS-FLV流地址") private String https_flv; + + @Schema(description = "Websocket-FLV流地址") private String ws_flv; + + @Schema(description = "Websockets-FLV流地址") private String wss_flv; + + @Schema(description = "HTTP-FMP4流地址") private String fmp4; + + @Schema(description = "HTTPS-FMP4流地址") private String https_fmp4; + + @Schema(description = "Websocket-FMP4流地址") private String ws_fmp4; + + @Schema(description = "Websockets-FMP4流地址") private String wss_fmp4; + + @Schema(description = "HLS流地址") private String hls; + + @Schema(description = "HTTPS-HLS流地址") private String https_hls; + + @Schema(description = "Websocket-HLS流地址") private String ws_hls; + + @Schema(description = "Websockets-HLS流地址") private String wss_hls; + + @Schema(description = "HTTP-TS流地址") private String ts; + + @Schema(description = "HTTPS-TS流地址") private String https_ts; + + @Schema(description = "Websocket-TS流地址") private String ws_ts; + + @Schema(description = "Websockets-TS流地址") private String wss_ts; + + @Schema(description = "RTMP流地址") private String rtmp; + + @Schema(description = "RTMPS流地址") private String rtmps; + + @Schema(description = "RTSP流地址") private String rtsp; + + @Schema(description = "RTSPS流地址") private String rtsps; + + @Schema(description = "RTC流地址") private String rtc; + @Schema(description = "RTCS流地址") private String rtcs; + + @Schema(description = "流媒体ID") private String mediaServerId; + + @Schema(description = "流编码信息") private Object tracks; + @Schema(description = "开始时间") private String startTime; + @Schema(description = "结束时间") private String endTime; private double progress; diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java index f399f300..305d488a 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java @@ -1,39 +1,30 @@ package com.genersoft.iot.vmp.vmanager.gb28181.MobilePosition; -import java.text.ParseException; -import java.util.List; -import java.util.UUID; - import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.util.StringUtil; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; import javax.sip.InvalidArgumentException; import javax.sip.SipException; +import java.text.ParseException; +import java.util.List; +import java.util.UUID; /** * 位置信息管理 @@ -58,6 +49,9 @@ public class MobilePositionController { @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceChannelService deviceChannelService; + /** * 查询历史轨迹 * @param deviceId 设备ID @@ -162,4 +156,24 @@ public class MobilePositionController { throw new ControllerException(ErrorCode.ERROR100); } } + + /** + * 数据位置信息格式处理 + * @param deviceId 设备ID + * @return true = 命令发送成功 + */ + @Operation(summary = "数据位置信息格式处理") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/transform/{deviceId}") + public void positionTransform(@PathVariable String deviceId) { + + Device device = deviceService.getDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); + } + boolean result = deviceChannelService.updateAllGps(device); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java index 11bc6211..c15d771a 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java @@ -123,7 +123,7 @@ public class DeviceQuery { @Parameter(name = "online", description = "是否在线") @Parameter(name = "channelType", description = "设备/子目录-> false/true") @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录") - public PageInfo channels(@PathVariable String deviceId, + public PageInfo channels(@PathVariable String deviceId, int page, int count, @RequestParam(required = false) String query, @RequestParam(required = false) Boolean online, @@ -223,7 +223,7 @@ public class DeviceQuery { @Parameter(name = "online", description = "是否在线") @Parameter(name = "channelType", description = "设备/子目录-> false/true") @GetMapping("/sub_channels/{deviceId}/{channelId}/channels") - public PageInfo subChannels(@PathVariable String deviceId, + public PageInfo subChannels(@PathVariable String deviceId, @PathVariable String channelId, int page, int count, @@ -237,8 +237,7 @@ public class DeviceQuery { return deviceChannelPageResult; } - PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); - return pageResult; + return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); } /** diff --git a/web_src/src/components/CloudRecordDetail.vue b/web_src/src/components/CloudRecordDetail.vue index d76101a2..8b3f4c80 100644 --- a/web_src/src/components/CloudRecordDetail.vue +++ b/web_src/src/components/CloudRecordDetail.vue @@ -115,7 +115,7 @@ props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'], data() { return { - basePath: `${this.mediaServerPath}/record`, + basePath: `${this.mediaServerPath}`, dateFilesObj: [], detailFiles: [], chooseDate: null,