Merge branch 'main' into 级联

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
结构优化
648540858 2023-02-22 18:14:53 +08:00
commit 420dbce900
45 changed files with 596 additions and 159 deletions

View File

@ -69,7 +69,7 @@ git clone https://github.com/648540858/wvp-GB28181-pro.git
### 5.2 编译前端页面 ### 5.2 编译前端页面
```shell script ```shell script
cd wvp-GB28181-pro/web_src/ cd wvp-GB28181-pro/web_src/
npm --registry=https://registry.npm.taobao.org install npm --registry=https://registry.npmmirror.com install
npm run build npm run build
``` ```
编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败

View File

@ -139,4 +139,15 @@ public class VideoManagerConstants {
public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; 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:";
} }

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.DateUtil;
import org.junit.jupiter.api.Order;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -14,6 +15,7 @@ import java.util.regex.Pattern;
@Configuration("mediaConfig") @Configuration("mediaConfig")
@Order(0)
public class MediaConfig{ public class MediaConfig{
private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class); private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class);

View File

@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.junit.jupiter.api.Order;
import org.mitre.dsmiley.httpproxy.ProxyServlet; import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -25,6 +26,7 @@ import java.net.ConnectException;
*/ */
@SuppressWarnings(value = {"rawtypes", "unchecked"}) @SuppressWarnings(value = {"rawtypes", "unchecked"})
@Configuration @Configuration
@Order(1)
public class ProxyServletConfig { public class ProxyServletConfig {
private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);

View File

@ -1,12 +1,14 @@
package com.genersoft.iot.vmp.conf; package com.genersoft.iot.vmp.conf;
import org.junit.jupiter.api.Order;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@Component @Component
@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true) @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
@Order(0)
public class SipConfig { public class SipConfig {
private String ip; private String ip;

View File

@ -18,7 +18,7 @@ import java.util.List;
* @author lin * @author lin
*/ */
@Component @Component
@Order(value=3) @Order(value=13)
public class SipPlatformRunner implements CommandLineRunner { public class SipPlatformRunner implements CommandLineRunner {
@Autowired @Autowired

View File

@ -1,14 +1,11 @@
package com.genersoft.iot.vmp.conf; 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.OpenAPI;
import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.StringSchema; import org.junit.jupiter.api.Order;
import io.swagger.v3.oas.models.parameters.HeaderParameter;
import org.springdoc.core.GroupedOpenApi; import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.SpringDocConfigProperties;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -17,6 +14,7 @@ import org.springframework.context.annotation.Configuration;
* @author lin * @author lin
*/ */
@Configuration @Configuration
@Order(1)
public class SpringDocConfig { public class SpringDocConfig {
@Value("${doc.enabled: true}") @Value("${doc.enabled: true}")

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.conf; package com.genersoft.iot.vmp.conf;
import org.junit.jupiter.api.Order;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
@ -12,6 +13,7 @@ import java.util.concurrent.ThreadPoolExecutor;
* @author lin * @author lin
*/ */
@Configuration @Configuration
@Order(1)
@EnableAsync(proxyTargetClass = true) @EnableAsync(proxyTargetClass = true)
public class ThreadPoolTaskConfig { public class ThreadPoolTaskConfig {

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.conf; package com.genersoft.iot.vmp.conf;
import org.junit.jupiter.api.Order;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -11,6 +12,7 @@ import java.util.List;
*/ */
@Component @Component
@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) @ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true)
@Order(0)
public class UserSetting { public class UserSetting {
private Boolean savePositionHistory = Boolean.FALSE; private Boolean savePositionHistory = Boolean.FALSE;

View File

@ -1,10 +1,12 @@
package com.genersoft.iot.vmp.conf; package com.genersoft.iot.vmp.conf;
import org.junit.jupiter.api.Order;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@ConfigurationProperties(prefix = "version") @ConfigurationProperties(prefix = "version")
@Order(0)
public class VersionConfig { public class VersionConfig {
private String version; private String version;

View File

@ -3,18 +3,18 @@ package com.genersoft.iot.vmp.conf.redis;
import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.service.redisMsg.*; 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.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
/** /**
* @description:Redis使spring-data-redisapplication.ymlredis * @description:Redis使spring-data-redisapplication.ymlredis
@ -23,6 +23,7 @@ import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
* *
*/ */
@Configuration @Configuration
@Order(value=1)
public class RedisConfig extends CachingConfigurerSupport { public class RedisConfig extends CachingConfigurerSupport {
@Autowired @Autowired

View File

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.conf.security; package com.genersoft.iot.vmp.conf.security;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import org.junit.jupiter.api.Order;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -25,6 +26,7 @@ import java.util.List;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);

View File

@ -19,7 +19,7 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Component @Component
@Order(value=1) @Order(value=10)
public class SipLayer implements CommandLineRunner { public class SipLayer implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);

View File

@ -109,6 +109,30 @@ public class AudioBroadcastCatch {
return sipTransactionInfo; 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) { public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
this.sipTransactionInfo = sipTransactionInfo; this.sipTransactionInfo = sipTransactionInfo;
} }

View File

@ -28,7 +28,7 @@ import java.util.Map;
* @author lin * @author lin
*/ */
@Component @Component
@Order(value=4) @Order(value=14)
public class SipRunner implements CommandLineRunner { public class SipRunner implements CommandLineRunner {
@Autowired @Autowired
@ -69,6 +69,26 @@ public class SipRunner implements CommandLineRunner {
// 重置cseq计数 // 重置cseq计数
redisCatchStorage.resetAllCSEQ(); redisCatchStorage.resetAllCSEQ();
// 清理redis // 清理redis
// 清理数据库不存在但是redis中存在的数据
List<Device> devicesInDb = deviceService.getAll();
if (devicesInDb.size() == 0) {
redisCatchStorage.removeAllDevice();
}else {
List<Device> devicesInRedis = redisCatchStorage.getAllDevices();
if (devicesInRedis.size() > 0) {
Map<String, Device> 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<SendRtpItem> sendRtpItems = redisCatchStorage.queryAllSendRTPServer(); List<SendRtpItem> sendRtpItems = redisCatchStorage.queryAllSendRTPServer();
if (sendRtpItems.size() > 0) { if (sendRtpItems.size() > 0) {

View File

@ -235,6 +235,25 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}else { }else {
catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n"); catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
} }
catalogXml.append("<Block>" + channel.getBlock() + "</Block>\r\n");
catalogXml.append("<SafetyWay>" + channel.getSafetyWay() + "</SafetyWay>\r\n");
catalogXml.append("<CertNum>" + channel.getCertNum() + "</CertNum>\r\n");
catalogXml.append("<Certifiable>" + channel.getCertifiable() + "</Certifiable>\r\n");
catalogXml.append("<ErrCode>" + channel.getErrCode() + "</ErrCode>\r\n");
catalogXml.append("<EndTime>" + channel.getEndTime() + "</EndTime>\r\n");
catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
catalogXml.append("<Password>" + channel.getPort() + "</Password>\r\n");
catalogXml.append("<Status>" + (channel.getStatus() == 1?"ON":"OFF") + "</Status>\r\n");
catalogXml.append("<Longitude>" +
(channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude())
+ "</Longitude>\r\n");
catalogXml.append("<Latitude>" +
(channel.getLatitudeWgs84() != 0? channel.getLatitudeWgs84():channel.getLatitude())
+ "</Latitude>\r\n");
} }
} }
catalogXml.append("</Item>\r\n"); catalogXml.append("</Item>\r\n");

View File

@ -360,7 +360,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
return; return;
} }
String username = sdp.getOrigin().getUsername(); String username = sdp.getOrigin().getUsername();
String addressStr = sdp.getOrigin().getAddress(); String addressStr = sdp.getConnection().getAddress();
logger.info("[上级点播]用户:{} 通道:{}, 地址:{}:{} ssrc{}", username, channelId, addressStr, port, ssrc); logger.info("[上级点播]用户:{} 通道:{}, 地址:{}:{} ssrc{}", username, channelId, addressStr, port, ssrc);
Device device = null; Device device = null;
@ -903,8 +903,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
Device device = redisCatchStorage.getDevice(requesterId); Device device = redisCatchStorage.getDevice(requesterId);
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId); AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(requesterId, channelId);
if (audioBroadcastCatch == null) { if (broadcastCatch == null) {
logger.warn("来自设备的Invite请求非语音广播已忽略requesterId {}/{}", requesterId, channelId); logger.warn("来自设备的Invite请求非语音广播已忽略requesterId {}/{}", requesterId, channelId);
try { try {
responseAck(request, Response.FORBIDDEN); responseAck(request, Response.FORBIDDEN);
@ -915,13 +915,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} }
if (device != null) { if (device != null) {
logger.info("收到设备" + requesterId + "的语音广播Invite请求"); 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); dynamicTask.stop(key);
try { try {
responseAck(request, Response.TRYING); responseAck(request, Response.TRYING);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage());
playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId());
return; return;
} }
String contentString = new String(request.getRawContent()); String contentString = new String(request.getRawContent());
@ -977,28 +977,18 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式发415 responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式发415
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage());
playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId());
return; return;
} }
return; return;
} }
String addressStr = sdp.getOrigin().getAddress(); String addressStr = sdp.getConnection().getAddress();
logger.info("设备{}请求语音流,地址:{}:{}ssrc{}, {}", requesterId, addressStr, port, ssrc, logger.info("设备{}请求语音流,地址:{}:{}ssrc{}, {}", requesterId, addressStr, port, ssrc,
mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP"); mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP");
MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem();
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;
}
SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
device.getDeviceId(), audioBroadcastCatch.getChannelId(), device.getDeviceId(), broadcastCatch.getChannelId(),
mediaTransmissionTCP, false); mediaTransmissionTCP, false);
if (sendRtpItem == null) { if (sendRtpItem == null) {
@ -1007,22 +997,20 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
responseAck(request, Response.BUSY_HERE); responseAck(request, Response.BUSY_HERE);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId());
return; return;
} }
return; return;
} }
String app = "broadcast";
String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId();
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
sendRtpItem.setPlayType(InviteStreamType.BROADCAST); sendRtpItem.setPlayType(InviteStreamType.BROADCAST);
sendRtpItem.setCallId(callIdHeader.getCallId()); sendRtpItem.setCallId(callIdHeader.getCallId());
sendRtpItem.setPlatformId(requesterId); sendRtpItem.setPlatformId(requesterId);
sendRtpItem.setStatus(1); sendRtpItem.setStatus(1);
sendRtpItem.setApp(app); sendRtpItem.setApp(broadcastCatch.getApp());
sendRtpItem.setStreamId(stream); sendRtpItem.setStreamId(broadcastCatch.getStream());
sendRtpItem.setPt(8); sendRtpItem.setPt(8);
sendRtpItem.setUsePs(false); sendRtpItem.setUsePs(false);
sendRtpItem.setRtcp(false); sendRtpItem.setRtcp(false);
@ -1034,23 +1022,22 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream());
Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream);
if (streamReady) { if (streamReady) {
sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc);
}else { }else {
logger.warn("[语音通话] 未发现待推送的流,app={},stream={}", app, stream); logger.warn("[语音通话] 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream());
try { try {
responseAck(request, Response.GONE); responseAck(request, Response.GONE);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 语音通话 回复410失败 {}", e.getMessage()); logger.error("[命令发送失败] 语音通话 回复410失败 {}", e.getMessage());
return; return;
} }
playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId());
} }
} catch (SdpException e) { } catch (SdpException e) {
logger.error("[SDP解析异常]", e); logger.error("[SDP解析异常]", e);
playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId());
} }
} else { } else {
logger.warn("来自无效设备/平台的请求"); logger.warn("来自无效设备/平台的请求");

View File

@ -38,6 +38,7 @@ import javax.sip.header.FromHeader;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.text.ParseException; import java.text.ParseException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
/** /**
@ -154,6 +155,17 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
Element deviceIdElement = rootElement.element("DeviceID"); Element deviceIdElement = rootElement.element("DeviceID");
String channelId = deviceIdElement.getTextTrim().toString(); String channelId = deviceIdElement.getTextTrim().toString();
Device device = redisCatchStorage.getDevice(deviceId); Device device = redisCatchStorage.getDevice(deviceId);
if (device == null) {
// 根据通道id查询设备Id
List<Device> deviceList = deviceChannelService.getDeviceByChannelId(channelId);
if (deviceList.size() > 0) {
device = deviceList.get(0);
}else {
logger.warn("[mobilePosition移动位置Notify] 未找到通道{}所属的设备", channelId);
return;
}
}
if (device != null) { if (device != null) {
if (!ObjectUtils.isEmpty(device.getName())) { if (!ObjectUtils.isEmpty(device.getName())) {
mobilePosition.setDeviceName(device.getName()); mobilePosition.setDeviceName(device.getName());

View File

@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; 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.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; 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(); SIPRequest request = (SIPRequest)evt.getRequest();
Response response = null; Response response = null;
boolean passwordCorrect = false; boolean passwordCorrect = false;

View File

@ -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.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 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.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.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPRequest;
@ -108,6 +109,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
continue; continue;
} }
DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null); DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null);
deviceChannel = SipUtils.updateGps(deviceChannel, device.getGeoCoordSys());
deviceChannel.setDeviceId(take.getDevice().getDeviceId()); deviceChannel.setDeviceId(take.getDevice().getDeviceId());
channelList.add(deviceChannel); channelList.add(deviceChannel);

View File

@ -1,16 +1,17 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; 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.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 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.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; 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.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; 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.transmit.event.request.impl.message.response.ResponseMessageHandler;
import com.genersoft.iot.vmp.utils.DateUtil; 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 gov.nist.javax.sip.message.SIPRequest;
import org.dom4j.DocumentException;
import org.dom4j.Element; import org.dom4j.Element;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -26,11 +27,9 @@ import javax.sip.RequestEvent;
import javax.sip.SipException; import javax.sip.SipException;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
@ -48,9 +47,6 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
@Autowired @Autowired
private ResponseMessageHandler responseMessageHandler; private ResponseMessageHandler responseMessageHandler;
@Autowired
private RecordDataCatch recordDataCatch;
@Autowired @Autowired
private DeferredResultHolder deferredResultHolder; private DeferredResultHolder deferredResultHolder;
@ -61,6 +57,8 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
@Autowired @Autowired
private ThreadPoolTaskExecutor taskExecutor; private ThreadPoolTaskExecutor taskExecutor;
private Long recordInfoTtl = 1800L;
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
responseMessageHandler.addHandler(cmdType, this); responseMessageHandler.addHandler(cmdType, this);
@ -68,93 +66,93 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
@Override @Override
public void handForDevice(RequestEvent evt, Device device, Element rootElement) { public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
boolean isEmpty = taskQueue.isEmpty();
try { try {
// 回复200 OK // 回复200 OK
responseAck((SIPRequest) evt.getRequest(), Response.OK); responseAck((SIPRequest) evt.getRequest(), Response.OK);
}catch (SipException | InvalidArgumentException | ParseException e) { }catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage());
} }
taskQueue.offer(new HandlerCatchData(evt, device, rootElement)); taskExecutor.execute(()->{
if (isEmpty) { try {
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<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"));
String startTimeStr = getText(itemRecord, "StartTime"); String sn = getText(rootElement, "SN");
record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr)); String channelId = getText(rootElement, "DeviceID");
RecordInfo recordInfo = new RecordInfo();
String endTimeStr = getText(itemRecord, "EndTime"); recordInfo.setChannelId(channelId);
record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr)); recordInfo.setDeviceId(device.getDeviceId());
recordInfo.setSn(sn);
record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 recordInfo.setName(getText(rootElement, "Name"));
: Integer.parseInt(getText(itemRecord, "Secrecy"))); String sumNumStr = getText(rootElement, "SumNum");
record.setType(getText(itemRecord, "Type")); int sumNum = 0;
record.setRecorderId(getText(itemRecord, "RecorderID")); if (!ObjectUtils.isEmpty(sumNumStr)) {
recordList.add(record); sumNum = Integer.parseInt(sumNumStr);
} }
recordInfo.setRecordList(recordList); recordInfo.setSumNum(sumNum);
int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, recordList);recordInfo.setCount(count); Element recordListElement = rootElement.element("RecordList");
logger.info("[国标录像] {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum); if (recordListElement == null || sumNum == 0) {
// 发送消息,如果是上级查询此录像,则会通过这里通知给上级 logger.info("无录像数据");
eventPublisher.recordEndEventPush(recordInfo); recordInfo.setCount(sumNum);
} eventPublisher.recordEndEventPush(recordInfo);
if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){ releaseRequest(device.getDeviceId(), sn,recordInfo);
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"));
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) { Map<String, String> map = recordList.stream()
logger.error("xml解析异常 ", e); .filter(record -> record.getDeviceId() != null)
} catch (Exception e) { .collect(Collectors.toMap(record -> record.getStartTime()+ record.getEndTime(), UJson::writeJson));
logger.warn("[国标录像] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); // 获取任务结果数据
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<RecordItem> 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 @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; String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
// 对数据进行排序 // 对数据进行排序
Collections.sort(recordDataCatch.getRecordInfo(deviceId, sn).getRecordList()); Collections.sort(recordInfo.getRecordList());
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
msg.setKey(key); msg.setKey(key);
msg.setData(recordDataCatch.getRecordInfo(deviceId, sn)); msg.setData(recordInfo);
deferredResultHolder.invokeAllResult(msg); deferredResultHolder.invokeAllResult(msg);
recordDataCatch.remove(deviceId, sn);
} }
} }

View File

@ -65,8 +65,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
*/ */
@Override @Override
public void process(ResponseEvent evt ){ public void process(ResponseEvent evt ){
logger.debug("接收到消息:" + evt.getResponse());
try { try {
SIPResponse response = (SIPResponse)evt.getResponse(); SIPResponse response = (SIPResponse)evt.getResponse();
int statusCode = response.getStatusCode(); int statusCode = response.getStatusCode();
// trying不会回复 // trying不会回复

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.utils; 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.gb28181.bean.RemoteAddressInfo;
import com.genersoft.iot.vmp.utils.GitUtil; import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.AddressImpl;
@ -168,4 +169,37 @@ public class SipUtils {
return new RemoteAddressInfo(remoteAddress, remotePort); 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;
}
} }

View File

@ -1,11 +1,9 @@
package com.genersoft.iot.vmp.gb28181.utils; package com.genersoft.iot.vmp.gb28181.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; 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.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.DateUtil;
import org.dom4j.Attribute; import org.dom4j.Attribute;
@ -400,6 +398,7 @@ public class XmlUtil {
} else { } else {
deviceChannel.setLatitude(0.00); deviceChannel.setLatitude(0.00);
} }
deviceChannel.setGpsTime(DateUtil.getNow()); deviceChannel.setGpsTime(DateUtil.getNow());
@ -414,6 +413,7 @@ public class XmlUtil {
} else { } else {
deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
} }
return deviceChannel; return deviceChannel;
} }

View File

@ -282,12 +282,12 @@ public class ZLMHttpHookListener {
taskExecutor.execute(() -> { taskExecutor.execute(() -> {
ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
if (subscribe != null) { if (subscribe != null) {
if (mediaInfo != null) { if (mediaInfo != null) {
subscribe.response(mediaInfo, json); subscribe.response(mediaInfo, json);
} }
} }
// 流消失移除redis play // 流消失移除redis play
List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
if (param.isRegist()) { if (param.isRegist()) {
if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal() || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()

View File

@ -25,7 +25,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Component @Component
@Order(value=2) @Order(value=12)
public class ZLMRunner implements CommandLineRunner { public class ZLMRunner implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);

View File

@ -46,4 +46,14 @@ public interface IDeviceChannelService {
* @return * @return
*/ */
List<ChannelReduce> queryAllChannelList(String platformId); List<ChannelReduce> queryAllChannelList(String platformId);
/**
*
*/
boolean updateAllGps(Device device);
/**
*
*/
List<Device> getDeviceByChannelId(String channelId);
} }

View File

@ -163,4 +163,8 @@ public interface IDeviceService {
*/ */
ResourceBaceInfo getOverview(); ResourceBaceInfo getOverview();
/**
*
*/
List<Device> getAll();
} }

View File

@ -67,6 +67,8 @@ public interface IPlayService {
void stopAudioBroadcast(String deviceId, String channelId); 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 pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException;
void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException;

View File

@ -19,6 +19,7 @@ import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* @author lin * @author lin
@ -176,5 +177,36 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
return channelMapper.queryChannelListInAll(null, null, null, platformId, null); return channelMapper.queryChannelListInAll(null, null, null, platformId, null);
} }
@Override
public boolean updateAllGps(Device device) {
List<DeviceChannel> deviceChannels = channelMapper.getChannelsWithoutTransform(device.getDeviceId());
List<DeviceChannel> 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<Device> getDeviceByChannelId(String channelId) {
return channelMapper.getDeviceByChannelId(channelId);
}
} }

View File

@ -655,4 +655,9 @@ public class DeviceServiceImpl implements IDeviceService {
public ResourceBaceInfo getOverview() { public ResourceBaceInfo getOverview() {
return deviceMapper.getOverview(); return deviceMapper.getOverview();
} }
@Override
public List<Device> getAll() {
return deviceMapper.getAll();
}
} }

View File

@ -1027,8 +1027,8 @@ public class PlayServiceImpl implements IPlayService {
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) {
// 查询流是否存在,不存在则认为是异常状态 // 查询流是否存在,不存在则认为是异常状态
MediaServerItem mediaServerItemInUse = mediaServerService.getOne(sendRtpItem.getMediaServerId()); MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItemInUse, sendRtpItem.getApp(), sendRtpItem.getStreamId()); Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStreamId());
if (streamReady) { if (streamReady) {
logger.warn("语音广播已经开启: {}", channelId); logger.warn("语音广播已经开启: {}", channelId);
event.call("语音广播已经开启"); event.call("语音广播已经开启");

View File

@ -366,7 +366,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
// 存储数据到stream_push表 // 存储数据到stream_push表
streamPushMapper.addAll(streamPushItems); streamPushMapper.addAll(streamPushItems);
List<StreamPushItem> streamPushItemForGbStream = streamPushItems.stream() List<StreamPushItem> streamPushItemForGbStream = streamPushItems.stream()
.filter(streamPushItem-> streamPushItem.getId() != null) .filter(streamPushItem-> streamPushItem.getGbId() != null)
.collect(Collectors.toList()); .collect(Collectors.toList());
// 存储数据到gb_stream表 id会返回到streamPushItemForGbStream里 // 存储数据到gb_stream表 id会返回到streamPushItemForGbStream里
if (streamPushItemForGbStream.size() > 0) { if (streamPushItemForGbStream.size() > 0) {

View File

@ -258,4 +258,7 @@ public interface IRedisCatchStorage {
List<SendRtpItem> queryAllSendRTPServer(); List<SendRtpItem> queryAllSendRTPServer();
List<Device> getAllDevices();
void removeAllDevice();
} }

View File

@ -56,7 +56,7 @@ public interface IVideoManagerStorage {
* @param count * @param count
* @return * @return
*/ */
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count); public PageInfo<DeviceChannel> queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count);
public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds); public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.storager.dao; 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.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; 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") @Select("select count(1) as total, sum(status) as online from device_channel")
ResourceBaceInfo getOverview(); 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<DeviceChannel> getChannelsWithoutTransform(String deviceId);
@Select("select de.* from device de left join device_channel dc on de.deviceId = dc.deviceId where dc.channelId=#{channelId}")
List<Device> getDeviceByChannelId(String channelId);
} }

View File

@ -280,4 +280,6 @@ public interface DeviceMapper {
@Select("select count(1) as total, sum(online) as online from device") @Select("select count(1) as total, sum(online) as online from device")
ResourceBaceInfo getOverview(); ResourceBaceInfo getOverview();
@Select("select * from device")
List<Device> getAll();
} }

View File

@ -664,6 +664,31 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
RedisUtil.del(key); RedisUtil.del(key);
} }
@Override
public void removeAllDevice() {
String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*";
List<Object> keys = RedisUtil.scan(scanKey);
for (Object key : keys) {
RedisUtil.del((String) key);
}
}
@Override
public List<Device> getAllDevices() {
String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*";
List<Device> result = new ArrayList<>();
List<Object> 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 @Override
public Device getDevice(String deviceId) { public Device getDevice(String deviceId) {
String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId;

View File

@ -226,8 +226,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
if (allChannelMap.containsKey(deviceChannel.getChannelId())) { if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
deviceChannel.setUpdateTime(DateUtil.getNow());
updateChannels.add(deviceChannel); updateChannels.add(deviceChannel);
}else { }else {
deviceChannel.setCreateTime(DateUtil.getNow());
addChannels.add(deviceChannel); addChannels.add(deviceChannel);
} }
if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {

View File

@ -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> T readJson(String json, Class<T> 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<Map.Entry<String, JsonNode>> fields() {
return this.node.fields();
}
public ObjectNode getNode() {
return this.node;
}
public UJson setAll(UJson json) {
this.node.setAll(json.node);
return this;
}
}

View File

@ -238,7 +238,7 @@ public class RedisUtil {
* @param time * @param time
* @return true / false * @return true / false
*/ */
public static boolean hmset(String key, Map<Object, Object> map, long time) { public static boolean hmset(String key, Map<?, ?> map, long time) {
if (redisTemplate == null) { if (redisTemplate == null) {
redisTemplate = SpringBeanFactory.getBean("redisTemplate"); redisTemplate = SpringBeanFactory.getBean("redisTemplate");
} }

View File

@ -1,43 +1,96 @@
package com.genersoft.iot.vmp.vmanager.bean; package com.genersoft.iot.vmp.vmanager.bean;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "流信息")
public class StreamContent { public class StreamContent {
@Schema(description = "应用名")
private String app; private String app;
@Schema(description = "流ID")
private String stream; private String stream;
@Schema(description = "IP")
private String ip; private String ip;
@Schema(description = "HTTP-FLV流地址")
private String flv; private String flv;
@Schema(description = "HTTPS-FLV流地址")
private String https_flv; private String https_flv;
@Schema(description = "Websocket-FLV流地址")
private String ws_flv; private String ws_flv;
@Schema(description = "Websockets-FLV流地址")
private String wss_flv; private String wss_flv;
@Schema(description = "HTTP-FMP4流地址")
private String fmp4; private String fmp4;
@Schema(description = "HTTPS-FMP4流地址")
private String https_fmp4; private String https_fmp4;
@Schema(description = "Websocket-FMP4流地址")
private String ws_fmp4; private String ws_fmp4;
@Schema(description = "Websockets-FMP4流地址")
private String wss_fmp4; private String wss_fmp4;
@Schema(description = "HLS流地址")
private String hls; private String hls;
@Schema(description = "HTTPS-HLS流地址")
private String https_hls; private String https_hls;
@Schema(description = "Websocket-HLS流地址")
private String ws_hls; private String ws_hls;
@Schema(description = "Websockets-HLS流地址")
private String wss_hls; private String wss_hls;
@Schema(description = "HTTP-TS流地址")
private String ts; private String ts;
@Schema(description = "HTTPS-TS流地址")
private String https_ts; private String https_ts;
@Schema(description = "Websocket-TS流地址")
private String ws_ts; private String ws_ts;
@Schema(description = "Websockets-TS流地址")
private String wss_ts; private String wss_ts;
@Schema(description = "RTMP流地址")
private String rtmp; private String rtmp;
@Schema(description = "RTMPS流地址")
private String rtmps; private String rtmps;
@Schema(description = "RTSP流地址")
private String rtsp; private String rtsp;
@Schema(description = "RTSPS流地址")
private String rtsps; private String rtsps;
@Schema(description = "RTC流地址")
private String rtc; private String rtc;
@Schema(description = "RTCS流地址")
private String rtcs; private String rtcs;
@Schema(description = "流媒体ID")
private String mediaServerId; private String mediaServerId;
@Schema(description = "流编码信息")
private Object tracks; private Object tracks;
@Schema(description = "开始时间")
private String startTime; private String startTime;
@Schema(description = "结束时间")
private String endTime; private String endTime;
private double progress; private double progress;

View File

@ -1,39 +1,30 @@
package com.genersoft.iot.vmp.vmanager.gb28181.MobilePosition; 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.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; 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.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 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.service.IDeviceService;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.util.StringUtil; import com.github.pagehelper.util.StringUtil;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*;
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.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.SipException; import javax.sip.SipException;
import java.text.ParseException;
import java.util.List;
import java.util.UUID;
/** /**
* *
@ -58,6 +49,9 @@ public class MobilePositionController {
@Autowired @Autowired
private IDeviceService deviceService; private IDeviceService deviceService;
@Autowired
private IDeviceChannelService deviceChannelService;
/** /**
* *
* @param deviceId ID * @param deviceId ID
@ -162,4 +156,24 @@ public class MobilePositionController {
throw new ControllerException(ErrorCode.ERROR100); 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);
}
}
} }

View File

@ -123,7 +123,7 @@ public class DeviceQuery {
@Parameter(name = "online", description = "是否在线") @Parameter(name = "online", description = "是否在线")
@Parameter(name = "channelType", description = "设备/子目录-> false/true") @Parameter(name = "channelType", description = "设备/子目录-> false/true")
@Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录") @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录")
public PageInfo channels(@PathVariable String deviceId, public PageInfo<DeviceChannel> channels(@PathVariable String deviceId,
int page, int count, int page, int count,
@RequestParam(required = false) String query, @RequestParam(required = false) String query,
@RequestParam(required = false) Boolean online, @RequestParam(required = false) Boolean online,
@ -223,7 +223,7 @@ public class DeviceQuery {
@Parameter(name = "online", description = "是否在线") @Parameter(name = "online", description = "是否在线")
@Parameter(name = "channelType", description = "设备/子目录-> false/true") @Parameter(name = "channelType", description = "设备/子目录-> false/true")
@GetMapping("/sub_channels/{deviceId}/{channelId}/channels") @GetMapping("/sub_channels/{deviceId}/{channelId}/channels")
public PageInfo subChannels(@PathVariable String deviceId, public PageInfo<DeviceChannel> subChannels(@PathVariable String deviceId,
@PathVariable String channelId, @PathVariable String channelId,
int page, int page,
int count, int count,
@ -237,8 +237,7 @@ public class DeviceQuery {
return deviceChannelPageResult; return deviceChannelPageResult;
} }
PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count);
return pageResult;
} }
/** /**

View File

@ -115,7 +115,7 @@
props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'], props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'],
data() { data() {
return { return {
basePath: `${this.mediaServerPath}/record`, basePath: `${this.mediaServerPath}`,
dateFilesObj: [], dateFilesObj: [],
detailFiles: [], detailFiles: [],
chooseDate: null, chooseDate: null,