Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	pom.xml
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
#	src/main/resources/all-application.yml
#	src/main/resources/application-dev.yml
This commit is contained in:
648540858
2024-02-07 20:30:37 +08:00
144 changed files with 6070 additions and 28330 deletions

View File

@@ -1,8 +1,8 @@
package com.genersoft.iot.vmp.gb28181.conf;
import gov.nist.core.StackLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;
import org.springframework.stereotype.Component;
import java.util.Properties;
@@ -10,100 +10,132 @@ import java.util.Properties;
@Component
public class StackLoggerImpl implements StackLogger {
private final static Logger logger = LoggerFactory.getLogger(StackLoggerImpl.class);
/**
* 完全限定类名(Fully Qualified Class Name),用于定位日志位置
*/
private static final String FQCN = StackLoggerImpl.class.getName();
@Override
public void logStackTrace() {
/**
* 获取栈中类信息(以便底层日志记录系统能够提取正确的位置信息(方法名、行号))
* @return LocationAwareLogger
*/
private static LocationAwareLogger getLocationAwareLogger() {
return (LocationAwareLogger) LoggerFactory.getLogger(new Throwable().getStackTrace()[4].getClassName());
}
}
@Override
public void logStackTrace(int traceLevel) {
System.out.println("traceLevel: " + traceLevel);
}
/**
* 封装打印日志的位置信息
* @param level 日志级别
* @param message 日志事件的消息
*/
private static void log(int level, String message) {
LocationAwareLogger locationAwareLogger = getLocationAwareLogger();
locationAwareLogger.log(null, FQCN, level, message, null, null);
}
@Override
public int getLineCount() {
return 0;
}
/**
* 封装打印日志的位置信息
* @param level 日志级别
* @param message 日志事件的消息
*/
private static void log(int level, String message, Throwable throwable) {
LocationAwareLogger locationAwareLogger = getLocationAwareLogger();
locationAwareLogger.log(null, FQCN, level, message, null, throwable);
}
@Override
public void logException(Throwable ex) {
@Override
public void logStackTrace() {
}
}
@Override
public void logDebug(String message) {
// logger.debug(message);
}
@Override
public void logStackTrace(int traceLevel) {
System.out.println("traceLevel: " + traceLevel);
}
@Override
public void logDebug(String message, Exception ex) {
// logger.debug(message);
}
@Override
public int getLineCount() {
return 0;
}
@Override
public void logTrace(String message) {
logger.trace(message);
}
@Override
public void logException(Throwable ex) {
@Override
public void logFatalError(String message) {
// logger.error(message);
}
}
@Override
public void logError(String message) {
// logger.error(message);
}
@Override
public void logDebug(String message) {
log(LocationAwareLogger.INFO_INT, message);
}
@Override
public boolean isLoggingEnabled() {
return true;
}
@Override
public void logDebug(String message, Exception ex) {
log(LocationAwareLogger.INFO_INT, message, ex);
}
@Override
public boolean isLoggingEnabled(int logLevel) {
return true;
}
@Override
public void logTrace(String message) {
log(LocationAwareLogger.INFO_INT, message);
}
@Override
public void logError(String message, Exception ex) {
// logger.error(message);
}
@Override
public void logFatalError(String message) {
log(LocationAwareLogger.INFO_INT, message);
}
@Override
public void logWarning(String message) {
logger.warn(message);
}
@Override
public void logError(String message) {
log(LocationAwareLogger.INFO_INT, message);
}
@Override
public void logInfo(String message) {
logger.info(message);
}
@Override
public boolean isLoggingEnabled() {
return true;
}
@Override
public void disableLogging() {
@Override
public boolean isLoggingEnabled(int logLevel) {
return true;
}
}
@Override
public void logError(String message, Exception ex) {
log(LocationAwareLogger.INFO_INT, message, ex);
}
@Override
public void enableLogging() {
@Override
public void logWarning(String message) {
log(LocationAwareLogger.INFO_INT, message);
}
}
@Override
public void logInfo(String message) {
log(LocationAwareLogger.INFO_INT, message);
}
@Override
public void setBuildTimeStamp(String buildTimeStamp) {
@Override
public void disableLogging() {
}
}
@Override
public void setStackProperties(Properties stackProperties) {
@Override
public void enableLogging() {
}
}
@Override
public String getLoggerName() {
return null;
}
@Override
public void setBuildTimeStamp(String buildTimeStamp) {
}
@Override
public void setStackProperties(Properties stackProperties) {
}
@Override
public String getLoggerName() {
return null;
}
}

View File

@@ -1,55 +1,68 @@
package com.genersoft.iot.vmp.gb28181.event.alarm;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 报警事件监听
* @author: lawrencehj
* @data: 2021-01-20
* 报警事件监听器.
*
* @author lawrencehj
* @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>
* @since 2021/01/20
*/
@Component
public class AlarmEventListener implements ApplicationListener<AlarmEvent> {
private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class);
private static final Logger logger = LoggerFactory.getLogger(AlarmEventListener.class);
private static Map<String, SseEmitter> sseEmitters = new Hashtable<>();
private static final Map<String, PrintWriter> SSE_CACHE = new ConcurrentHashMap<>();
public void addSseEmitters(String browserId, SseEmitter sseEmitter) {
sseEmitters.put(browserId, sseEmitter);
public void addSseEmitter(String browserId, PrintWriter writer) {
SSE_CACHE.put(browserId, writer);
logger.info("SSE 在线数量: {}", SSE_CACHE.size());
}
public void removeSseEmitter(String browserId, PrintWriter writer) {
SSE_CACHE.remove(browserId, writer);
logger.info("SSE 在线数量: {}", SSE_CACHE.size());
}
@Override
public void onApplicationEvent(AlarmEvent event) {
public void onApplicationEvent(@NotNull AlarmEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("设备报警事件触发deviceId" + event.getAlarmInfo().getDeviceId() + ", "
+ event.getAlarmInfo().getAlarmDescription());
logger.debug("设备报警事件触发, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription());
}
String msg = "<strong>设备编码:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
+ "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
+ "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>"
+ "<br><strong>报警位置:</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>"
+ ", <i>" + event.getAlarmInfo().getLatitude() + "</i>";
for (Iterator<Map.Entry<String, SseEmitter>> it = sseEmitters.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, SseEmitter> emitter = it.next();
logger.info("推送到SSE连接浏览器ID: " + emitter.getKey());
String msg = "<strong>设备编号:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
+ "<br><strong>通道编号:</strong> <i>" + event.getAlarmInfo().getChannelId() + "</i>"
+ "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
+ "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>";
for (Iterator<Map.Entry<String, PrintWriter>> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, PrintWriter> response = it.next();
logger.info("推送到 SSE 连接, 浏览器 ID: {}", response.getKey());
try {
emitter.getValue().send(msg);
} catch (IOException | IllegalStateException e) {
if (logger.isDebugEnabled()) {
logger.debug("SSE连接已关闭");
PrintWriter writer = response.getValue();
if (writer.checkError()) {
it.remove();
continue;
}
// 移除已关闭的连接
String sseMsg = "event:message\n" +
"data:" + msg + "\n" +
"\n";
writer.write(sseMsg);
writer.flush();
} catch (Exception e) {
it.remove();
}
}

View File

@@ -35,7 +35,7 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
int sumNum = event.getRecordInfo().getSumNum();
logger.info("录像查询完成事件触发deviceId{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(),
event.getRecordInfo().getChannelId(), count,sumNum);
if (handlerMap.size() > 0) {
if (!handlerMap.isEmpty()) {
RecordEndEventHandler handler = handlerMap.get(deviceId + channelId);
if (handler !=null){
handler.handler(event.getRecordInfo());
@@ -43,6 +43,9 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
handlerMap.remove(deviceId + channelId);
}
}
}else {
logger.info("录像查询完成事件触发, 但是订阅为空取消发送deviceId{}, channelId: {}",
event.getRecordInfo().getDeviceId(), event.getRecordInfo().getChannelId());
}
}
@@ -53,6 +56,7 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
* @param recordEndEventHandler
*/
public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
logger.info("录像查询事件添加监听deviceId{}, channelId: {}", device, channelId);
handlerMap.put(device + channelId, recordEndEventHandler);
}
/**
@@ -61,6 +65,7 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
* @param channelId
*/
public void delEndEventHandler(String device, String channelId) {
logger.info("录像查询事件移除监听deviceId{}, channelId: {}", device, channelId);
handlerMap.remove(device + channelId);
}

View File

@@ -148,13 +148,13 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
if (event.getDeviceChannels() != null) {
deviceChannelList.addAll(event.getDeviceChannels());
}
if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
if (event.getGbStreams() != null && !event.getGbStreams().isEmpty()){
for (GbStream gbStream : event.getGbStreams()) {
deviceChannelList.add(
gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform));
}
}
if (deviceChannelList.size() > 0) {
if (!deviceChannelList.isEmpty()) {
logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
try {
sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
@@ -163,10 +163,10 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
}
}
}else if (parentPlatformMap.keySet().size() > 0) {
}else if (!parentPlatformMap.keySet().isEmpty()) {
for (String gbId : parentPlatformMap.keySet()) {
List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
if (parentPlatforms != null && parentPlatforms.size() > 0) {
if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
for (ParentPlatform platform : parentPlatforms) {
SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
if (subscribeInfo == null) {

View File

@@ -38,7 +38,8 @@ public class SSRCFactory {
public void initMediaServerSSRC(String mediaServerId, Set<String> usedSet) {
String ssrcPrefix = sipConfig.getDomain().substring(3, 8);
String sipDomain = sipConfig.getDomain();
String ssrcPrefix = sipDomain.length() >= 8 ? sipDomain.substring(3, 8) : sipDomain;
String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
List<String> ssrcList = new ArrayList<>();
for (int i = 1; i < MAX_STREAM_COUNT; i++) {

View File

@@ -75,6 +75,33 @@ public class VideoStreamSessionManager {
return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
}
public SsrcTransaction getSsrcTransactionByCallId(String callId){
if (ObjectUtils.isEmpty(callId)) {
return null;
}
String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_" + callId+ "_*";
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
if (!scanResult.isEmpty()) {
return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
}else {
key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_play_*";
scanResult = RedisUtil.scan(redisTemplate, key);
if (scanResult.isEmpty()) {
return null;
}
for (Object keyObj : scanResult) {
SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj);
if (ssrcTransaction.getSipTransactionInfo() != null &&
ssrcTransaction.getSipTransactionInfo().getCallId().equals(callId)) {
return ssrcTransaction;
}
}
return null;
}
}
public List<SsrcTransaction> getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){
if (ObjectUtils.isEmpty(deviceId)) {
deviceId ="*";
@@ -117,8 +144,19 @@ public class VideoStreamSessionManager {
}
public void remove(String deviceId, String channelId, String stream) {
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
if (ssrcTransaction == null) {
List<SsrcTransaction> ssrcTransactionList = getSsrcTransactionForAll(deviceId, channelId, null, stream);
if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
return;
}
for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_"
+ deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream());
}
}
public void removeByCallId(String deviceId, String channelId, String callId) {
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
if (ssrcTransaction == null ) {
return;
}
redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_"

View File

@@ -129,4 +129,6 @@ public class SipRunner implements CommandLineRunner {
}
}
}
}

View File

@@ -66,17 +66,17 @@ public class SIPSender {
// 添加错误订阅
if (errorEvent != null) {
sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
errorEvent.response(eventResult);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
sipSubscribe.removeOkSubscribe(eventResult.callId);
errorEvent.response(eventResult);
}));
}
// 添加订阅
if (okEvent != null) {
sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
okEvent.response(eventResult);
sipSubscribe.removeOkSubscribe(eventResult.callId);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
okEvent.response(eventResult);
});
}
if ("TCP".equals(transport)) {

View File

@@ -164,6 +164,7 @@ public class SIPRequestHeaderProvider {
Request request = null;
//请求行
SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
// SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
@@ -174,6 +175,7 @@ public class SIPRequestHeaderProvider {
FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
//to
SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
// SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(),device.getHostAddress());
Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());

View File

@@ -40,6 +40,7 @@ import javax.sip.SipFactory;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.List;
/**
* @description:设备能力接口,用于定义设备的控制、查询能力
@@ -374,7 +375,8 @@ public class SIPCommander implements ISIPCommander {
}), e -> {
ResponseEvent responseEvent = (ResponseEvent) e.event;
SIPResponse response = (SIPResponse) responseEvent.getResponse();
streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response,
String callId = response.getCallIdHeader().getCallId();
streamSession.put(device.getDeviceId(), channelId, callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response,
InviteSessionType.PLAY);
okEvent.response(e);
});
@@ -676,22 +678,26 @@ public class SIPCommander implements ISIPCommander {
*/
@Override
public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
SsrcTransaction ssrcTransaction;
if (callId != null) {
ssrcTransaction = streamSession.getSsrcTransaction(null, null, callId, null);
}else {
ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, stream);
if (device == null) {
logger.warn("[发送BYE] device为null");
return;
}
if (ssrcTransaction == null) {
List<SsrcTransaction> ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream);
if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId);
throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream);
}
mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId());
mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
}
}
@Override
@@ -1000,7 +1006,7 @@ public class SIPCommander implements ISIPCommander {
catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
catalogXml.append("</Query>\r\n");
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));

View File

@@ -579,7 +579,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
@Override
public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException {
if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) {
if (parentPlatform == null || deviceChannels == null || deviceChannels.isEmpty() || subscribeInfo == null) {
return;
}
if (index == null) {
@@ -597,6 +597,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
Integer finalIndex = index;
String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
deviceChannels.size(), type, subscribeInfo);
logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
logger.error("发送NOTIFY通知消息失败。错误{} {}", eventResult.statusCode, eventResult.msg);
}, (eventResult -> {
@@ -620,7 +621,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest, errorEvent, okEvent);
}
private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
@@ -632,9 +633,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
.append("<CmdType>Catalog</CmdType>\r\n")
.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
.append("<SumNum>1</SumNum>\r\n")
.append("<SumNum>"+ sumNum +"</SumNum>\r\n")
.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
if (channels.size() > 0) {
if (!channels.isEmpty()) {
for (DeviceChannel channel : channels) {
if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
channel.setParentId(parentPlatform.getDeviceGBId());
@@ -701,6 +702,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}else {
channels = deviceChannels.subList(index, deviceChannels.size());
}
logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
Integer finalIndex = index;
String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type);
sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
@@ -747,13 +749,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
if ( parentPlatform ==null) {
return ;
}
logger.info("[国标级联] 发送录像数据通道: {}", recordInfo.getChannelId());
String characterSet = parentPlatform.getCharacterSet();
StringBuffer recordXml = new StringBuffer(600);
recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Response>\r\n")
.append("<CmdType>RecordInfo</CmdType>\r\n")
.append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
.append("<DeviceID>" + recordInfo.getChannelId() + "</DeviceID>\r\n")
.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n")
.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
if (recordInfo.getRecordList() == null ) {
recordXml.append("<RecordList Num=\"0\">\r\n");
@@ -763,7 +766,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
for (RecordItem recordItem : recordInfo.getRecordList()) {
recordXml.append("<Item>\r\n");
if (deviceChannel != null) {
recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
recordXml.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n")
.append("<Name>" + recordItem.getName() + "</Name>\r\n")
.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
@@ -783,12 +786,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
recordXml.append("</RecordList>\r\n")
.append("</Response>\r\n");
logger.info("[国标级联] 发送录像数据通道:{}, 内容: {}", recordInfo.getChannelId(), recordXml);
// callid
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
logger.info("[国标级联] 发送录像数据通道:{}, 发送成功", recordInfo.getChannelId());
});
}

View File

@@ -128,6 +128,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
callIdHeader.getCallId(), null);
zlmServerFactory.stopSendRtpStream(mediaInfo, param);
if (userSetting.getUseCustomSsrcForParentInvite()) {
mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
}
if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
if (platform != null) {
@@ -167,14 +170,12 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
}
}
// 发流端发送的停止
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
if (ssrcTransaction == null ) {
logger.info("[收到bye] 但是无法获取推流信息和发流信息,忽略此请求");
logger.info(request.toString());
return;
}
// 可能是设备发送的停止
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransactionByCallId(callIdHeader.getCallId());
if (ssrcTransaction == null) {
return;
}
logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
ParentPlatform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId());
if (platform != null ) {
@@ -216,7 +217,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
if (mediaServerItem != null) {
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
}
streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getStream());
streamSession.removeByCallId(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getCallId());
if (ssrcTransaction.getType() == InviteSessionType.BROADCAST) {
// 查找来源的对讲设备,发送停止
Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());

View File

@@ -152,7 +152,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
String requesterId = SipUtils.getUserIdFromFromHeader(request);
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
if (requesterId == null || channelId == null) {
logger.info("无法从FromHeader的Address中获取到平台id返回400");
logger.info("无法从请求中获取到平台id返回400");
// 参数不全, 发400请求错误
try {
responseAck(request, Response.BAD_REQUEST);
@@ -162,6 +162,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
return;
}
logger.info("[INVITE] requesterId: {}, callId: {}, 来自:{}{}",
requesterId, callIdHeader.getCallId(), request.getRemoteAddress(), request.getRemotePort());
// 查询请求是否来自上级平台\设备
ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
@@ -409,7 +411,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 非严格模式端口不统一, 增加兼容性修改为一个不为0的端口
localPort = new Random().nextInt(65535) + 1;
}
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
if (sendRtpItem.isTcp()) {
content.append("m=video " + localPort + " TCP/RTP/AVP 96\r\n");
if (!sendRtpItem.isTcpActive()) {
content.append("a=setup:active\r\n");
} else {
content.append("a=setup:passive\r\n");
}
}else {
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
}
content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
@@ -524,7 +535,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
});
} else {
sendRtpItem.setPlayType(InviteStreamType.PLAY);
String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
sendRtpItem.setStreamId(streamId);
redisCatchStorage.updateSendRTPSever(sendRtpItem);
SSRCInfo ssrcInfo = playService.play(mediaServerItem, device.getDeviceId(), channelId, ssrc, ((code, msg, data) -> {
if (code == InviteErrorCode.SUCCESS.getCode()) {
hookEvent.run(code, msg, data);
@@ -536,9 +550,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
errorEvent.run(code, msg, data);
}
}));
sendRtpItem.setPlayType(InviteStreamType.PLAY);
String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
sendRtpItem.setStream(streamId);
sendRtpItem.setSsrc(ssrcInfo.getSsrc());
redisCatchStorage.updateSendRTPSever(sendRtpItem);
@@ -721,8 +732,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
dynamicTask.stop(callIdHeader.getCallId());
}
} else if ("push".equals(gbStream.getStreamType())) {
if (!platform.isStartOfflinePush()) {
// 平台设置中关闭了拉起离线的推流则直接回复
@@ -745,13 +754,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
try {
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
responseAck(request, Response.REQUEST_TIMEOUT); // 超时
} catch (SipException e) {
logger.error("未处理的异常 ", e);
} catch (InvalidArgumentException e) {
logger.error("未处理的异常 ", e);
} catch (ParseException e) {
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("未处理的异常 ", e);
}
}, userSetting.getPlatformPlayTimeout());
@@ -762,6 +768,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 添加在本机上线的通知
mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> {
dynamicTask.stop(callIdHeader.getCallId());
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
if (serverId.equals(userSetting.getServerId())) {
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());
@@ -826,7 +833,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 发送redis消息
redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(),
streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId,
channelId, mediaTransmissionTCP, platform.isRtcp(), null, responseSendItemMsg -> {
channelId, mediaTransmissionTCP, platform.isRtcp(),platform.getName(), responseSendItemMsg -> {
SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem();
if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
logger.warn("服务器端口资源不足");

View File

@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.service.IDeviceChannelService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
@@ -185,6 +186,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
// 判断此通道是否存在
DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId());
if (deviceChannel != null) {
logger.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
channel.setId(deviceChannel.getId());
updateChannelMap.put(channel.getChannelId(), channel);
if (updateChannelMap.keySet().size() > 300) {
@@ -222,6 +224,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId());
if (deviceChannelForUpdate != null) {
channel.setId(deviceChannelForUpdate.getId());
channel.setUpdateTime(DateUtil.getNow());
updateChannelMap.put(channel.getChannelId(), channel);
if (updateChannelMap.keySet().size() > 300) {
executeSaveForUpdate();
@@ -244,11 +247,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
// 转发变化信息
eventPublisher.catalogEventPublish(null, channel, event);
if (updateChannelMap.keySet().size() > 0
|| addChannelMap.keySet().size() > 0
|| updateChannelOnlineList.size() > 0
|| updateChannelOfflineList.size() > 0
|| deleteChannelList.size() > 0) {
if (!updateChannelMap.keySet().isEmpty()
|| !addChannelMap.keySet().isEmpty()
|| !updateChannelOnlineList.isEmpty()
|| !updateChannelOfflineList.isEmpty()
|| !deleteChannelList.isEmpty()) {
if (!dynamicTask.contains(talkKey)) {
dynamicTask.startDelay(talkKey, this::executeSave, 1000);
@@ -262,16 +265,36 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
}
private void executeSave(){
executeSaveForAdd();
executeSaveForUpdate();
executeSaveForDelete();
executeSaveForOnline();
executeSaveForOffline();
try {
executeSaveForAdd();
} catch (Exception e) {
logger.error("[存储收到的增加通道] 异常: ", e );
}
try {
executeSaveForUpdate();
} catch (Exception e) {
logger.error("[存储收到的更新通道] 异常: ", e );
}
try {
executeSaveForDelete();
} catch (Exception e) {
logger.error("[存储收到的删除通道] 异常: ", e );
}
try {
executeSaveForOnline();
} catch (Exception e) {
logger.error("[存储收到的通道上线] 异常: ", e );
}
try {
executeSaveForOffline();
} catch (Exception e) {
logger.error("[存储收到的通道离线] 异常: ", e );
}
dynamicTask.stop(talkKey);
}
private void executeSaveForUpdate(){
if (updateChannelMap.values().size() > 0) {
if (!updateChannelMap.values().isEmpty()) {
ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
updateChannelMap.clear();
deviceChannelService.batchUpdateChannel(deviceChannels);
@@ -280,7 +303,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
}
private void executeSaveForAdd(){
if (addChannelMap.values().size() > 0) {
if (!addChannelMap.values().isEmpty()) {
ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values());
addChannelMap.clear();
deviceChannelService.batchAddChannel(deviceChannels);
@@ -288,21 +311,21 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
}
private void executeSaveForDelete(){
if (deleteChannelList.size() > 0) {
if (!deleteChannelList.isEmpty()) {
deviceChannelService.deleteChannels(deleteChannelList);
deleteChannelList.clear();
}
}
private void executeSaveForOnline(){
if (updateChannelOnlineList.size() > 0) {
if (!updateChannelOnlineList.isEmpty()) {
deviceChannelService.channelsOnline(updateChannelOnlineList);
updateChannelOnlineList.clear();
}
}
private void executeSaveForOffline(){
if (updateChannelOfflineList.size() > 0) {
if (!updateChannelOfflineList.isEmpty()) {
deviceChannelService.channelsOffline(updateChannelOfflineList);
updateChannelOfflineList.clear();
}

View File

@@ -106,23 +106,27 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
String title = registerFlag ? "[注册请求]": "[注销请求]";
logger.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress);
if (device != null &&
device.getSipTransactionInfo() != null &&
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
device.getSipTransactionInfo() != null &&
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
logger.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId());
device.setExpires(request.getExpires().getExpires());
device.setIp(remoteAddressInfo.getIp());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setLocalIp(request.getLocalAddress().getHostAddress());
Response registerOkResponse = getRegisterOkResponse(request);
// 判断TCP还是UDP
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
device.setRegisterTime(DateUtil.getNow());
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
deviceService.online(device, sipTransactionInfo);
if (registerFlag) {
device.setExpires(request.getExpires().getExpires());
device.setIp(remoteAddressInfo.getIp());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setLocalIp(request.getLocalAddress().getHostAddress());
Response registerOkResponse = getRegisterOkResponse(request);
// 判断TCP还是UDP
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
device.setRegisterTime(DateUtil.getNow());
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
deviceService.online(device, sipTransactionInfo);
}else {
deviceService.offline(deviceId, "主动注销");
}
return;
}
String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();

View File

@@ -61,7 +61,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
return;
}
SIPRequest request = (SIPRequest) evt.getRequest();
logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId());
logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId());
// 回复200 OK
try {
@@ -76,10 +76,15 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
logger.info("[心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
logger.info("[收到心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setIp(remoteAddressInfo.getIp());
// 设备地址变化会引起目录订阅任务失效,需要重新添加
if (device.getSubscribeCycleForCatalog() > 0) {
deviceService.removeCatalogSubscribe(device);
deviceService.addCatalogSubscribe(device);
}
}
if (device.getKeepaliveTime() == null) {
device.setKeepaliveIntervalTime(60);

View File

@@ -102,6 +102,7 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
// 接收录像数据
recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{
try {
logger.info("[国标级联] 录像查询收到数据, 通道: {},准备转发===", channelId);
cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, request.getFromTag(), recordInfo);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage());

View File

@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
@@ -214,8 +215,11 @@ public class XmlUtil {
return deviceChannel;
}
Element nameElement = itemDevice.element("Name");
if (nameElement != null) {
// 当通道名称为空时,设置通道名称为通道编码,避免级联时因通道名称为空导致上级接收通道失败
if (nameElement != null && StringUtils.isNotBlank(nameElement.getText())) {
deviceChannel.setName(nameElement.getText());
} else {
deviceChannel.setName(channelId);
}
if(channelId.length() <= 8) {
deviceChannel.setHasAudio(false);