Browse Source

dialog去除以及异常情况处理优化

648540858 3 years ago
parent
commit
cd117ed228
53 changed files with 3136 additions and 3083 deletions
  1. 6 1
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  2. 51 0
      src/main/java/com/genersoft/iot/vmp/conf/exception/SsrcTransactionNotFoundException.java
  3. 1 3
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  4. 27 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdSendFailEvent.java
  5. 8 2
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java
  6. 6 5
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipTransactionInfo.java
  7. 11 18
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
  8. 0 5
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java
  9. 3 10
      src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
  10. 1 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java
  11. 27 4
      src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
  12. 7 51
      src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  13. 40 32
      src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java
  14. 39 32
      src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java
  15. 56 43
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  16. 19 16
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
  17. 74 42
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
  18. 1432 1775
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  19. 226 390
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
  20. 9 8
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
  21. 10 3
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
  22. 23 13
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
  23. 5 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
  24. 3 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java
  25. 3 3
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
  26. 48 54
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
  27. 12 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
  28. 7 7
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
  29. 7 7
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java
  30. 29 37
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java
  31. 62 78
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
  32. 19 26
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java
  33. 32 25
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
  34. 8 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
  35. 43 0
      src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
  36. 42 7
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  37. 11 12
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
  38. 36 7
      src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
  39. 75 43
      src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
  40. 209 159
      src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
  41. 18 4
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
  42. 16 7
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java
  43. 20 2
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
  44. 31 16
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java
  45. 88 55
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
  46. 28 15
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
  47. 40 16
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
  48. 33 13
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
  49. 34 6
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
  50. 29 9
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java
  51. 36 10
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
  52. 14 6
      src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java
  53. 22 1
      src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java

+ 6 - 1
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@@ -2,6 +2,9 @@ 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 com.genersoft.iot.vmp.vmanager.gb28181.device.DeviceQuery;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
@@ -15,6 +18,8 @@ import java.util.regex.Pattern;
 @Configuration("mediaConfig")
 @Configuration("mediaConfig")
 public class MediaConfig{
 public class MediaConfig{
 
 
+    private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class);
+
     // 修改必须配置,不再支持自动获取
     // 修改必须配置,不再支持自动获取
     @Value("${media.id}")
     @Value("${media.id}")
     private String id;
     private String id;
@@ -174,7 +179,7 @@ public class MediaConfig{
                 try {
                 try {
                     hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
                     hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
                 } catch (UnknownHostException e) {
                 } catch (UnknownHostException e) {
-                    throw new RuntimeException(e);
+                    logger.error("[获取SDP IP]: 域名解析失败");
                 }
                 }
                 return hostAddress;
                 return hostAddress;
             }
             }

+ 51 - 0
src/main/java/com/genersoft/iot/vmp/conf/exception/SsrcTransactionNotFoundException.java

@@ -0,0 +1,51 @@
+package com.genersoft.iot.vmp.conf.exception;
+
+import com.sun.javafx.binding.StringFormatter;
+
+/**
+ * @author lin
+ */
+public class SsrcTransactionNotFoundException extends Exception{
+    private String deviceId;
+    private String channelId;
+    private String callId;
+    private String stream;
+
+
+
+    public SsrcTransactionNotFoundException(String deviceId, String channelId, String callId, String stream) {
+        this.deviceId = deviceId;
+        this.channelId = channelId;
+        this.callId = callId;
+        this.stream = stream;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    @Override
+    public String getMessage() {
+        StringBuffer msg = new StringBuffer();
+        msg.append(StringFormatter.format("缓存事务信息未找到,device:%s channel: %s ",  deviceId, channelId));
+        if (callId != null) {
+            msg.append("callId: " + callId);
+        }
+        if (stream != null) {
+            msg.append("stream: " + stream);
+        }
+        return msg.toString();
+    }
+}

+ 1 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@@ -41,7 +41,7 @@ public class SipLayer{
 	
 	
 	@Bean("sipStack")
 	@Bean("sipStack")
 	@DependsOn({"sipFactory"})
 	@DependsOn({"sipFactory"})
-	SipStack createSipStack() throws PeerUnavailableException {
+	SipStackImpl createSipStack() throws PeerUnavailableException {
 		sipStack = ( SipStackImpl )sipFactory.createSipStack(DefaultProperties.getProperties(sipConfig.getMonitorIp(), false));
 		sipStack = ( SipStackImpl )sipFactory.createSipStack(DefaultProperties.getProperties(sipConfig.getMonitorIp(), false));
 		return sipStack;
 		return sipStack;
 	}
 	}
@@ -56,7 +56,6 @@ public class SipLayer{
 			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
 			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
 			tcpSipProvider.setDialogErrorsAutomaticallyHandled();
 			tcpSipProvider.setDialogErrorsAutomaticallyHandled();
 			tcpSipProvider.addSipListener(sipProcessorObserver);
 			tcpSipProvider.addSipListener(sipProcessorObserver);
-//			tcpSipProvider.setAutomaticDialogSupportEnabled(false);
 			logger.info("[Sip Server] TCP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
 			logger.info("[Sip Server] TCP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
 		} catch (TransportNotSupportedException e) {
 		} catch (TransportNotSupportedException e) {
 			e.printStackTrace();
 			e.printStackTrace();
@@ -80,7 +79,6 @@ public class SipLayer{
 			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
 			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
 			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
 			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
 			udpSipProvider.addSipListener(sipProcessorObserver);
 			udpSipProvider.addSipListener(sipProcessorObserver);
-//			udpSipProvider.setAutomaticDialogSupportEnabled(false);
 		} catch (TransportNotSupportedException e) {
 		} catch (TransportNotSupportedException e) {
 			e.printStackTrace();
 			e.printStackTrace();
 		} catch (InvalidArgumentException e) {
 		} catch (InvalidArgumentException e) {

+ 27 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdSendFailEvent.java

@@ -0,0 +1,27 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import javax.sip.Dialog;
+import java.util.EventObject;
+
+public class CmdSendFailEvent extends EventObject {
+
+    private String callId;
+
+    /**
+     * Constructs a prototypical Event.
+     *
+     * @param dialog
+     * @throws IllegalArgumentException if source is null.
+     */
+    public CmdSendFailEvent(Dialog dialog) {
+        super(dialog);
+    }
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+}

+ 8 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java

@@ -4,6 +4,9 @@ import javax.sip.Dialog;
 import java.util.EventObject;
 import java.util.EventObject;
 
 
 public class DeviceNotFoundEvent extends EventObject {
 public class DeviceNotFoundEvent extends EventObject {
+
+    private String callId;
+
     /**
     /**
      * Constructs a prototypical Event.
      * Constructs a prototypical Event.
      *
      *
@@ -14,8 +17,11 @@ public class DeviceNotFoundEvent extends EventObject {
         super(dialog);
         super(dialog);
     }
     }
 
 
+    public String getCallId() {
+        return callId;
+    }
 
 
-    public Dialog getDialog() {
-        return (Dialog)super.getSource();
+    public void setCallId(String callId) {
+        this.callId = callId;
     }
     }
 }
 }

+ 6 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipTransactionInfo.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 package com.genersoft.iot.vmp.gb28181.bean;
 
 
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
 
 
 public class SipTransactionInfo {
 public class SipTransactionInfo {
 
 
@@ -9,11 +10,11 @@ public class SipTransactionInfo {
     private String toTag;
     private String toTag;
     private String viaBranch;
     private String viaBranch;
 
 
-    public SipTransactionInfo(SIPRequest request) {
-        this.callId = request.getCallIdHeader().getCallId();
-        this.fromTag = request.getFromTag();
-        this.toTag = request.getToTag();
-        this.viaBranch = request.getTopmostViaHeader().getBranch();
+    public SipTransactionInfo(SIPResponse response) {
+        this.callId = response.getCallIdHeader().getCallId();
+        this.fromTag = response.getFromTag();
+        this.toTag = response.getToTag();
+        this.viaBranch = response.getTopmostViaHeader().getBranch();
     }
     }
 
 
     public SipTransactionInfo() {
     public SipTransactionInfo() {

+ 11 - 18
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java

@@ -8,10 +8,11 @@ public class SsrcTransaction {
     private String channelId;
     private String channelId;
     private String callId;
     private String callId;
     private String stream;
     private String stream;
-    private byte[] transaction;
-    private byte[] dialog;
     private String mediaServerId;
     private String mediaServerId;
     private String ssrc;
     private String ssrc;
+
+    private SipTransactionInfo sipTransactionInfo;
+
     private VideoStreamSessionManager.SessionType type;
     private VideoStreamSessionManager.SessionType type;
 
 
     public String getDeviceId() {
     public String getDeviceId() {
@@ -46,22 +47,6 @@ public class SsrcTransaction {
         this.stream = stream;
         this.stream = stream;
     }
     }
 
 
-    public byte[] getTransaction() {
-        return transaction;
-    }
-
-    public void setTransaction(byte[] transaction) {
-        this.transaction = transaction;
-    }
-
-    public byte[] getDialog() {
-        return dialog;
-    }
-
-    public void setDialog(byte[] dialog) {
-        this.dialog = dialog;
-    }
-
     public String getMediaServerId() {
     public String getMediaServerId() {
         return mediaServerId;
         return mediaServerId;
     }
     }
@@ -85,4 +70,12 @@ public class SsrcTransaction {
     public void setType(VideoStreamSessionManager.SessionType type) {
     public void setType(VideoStreamSessionManager.SessionType type) {
         this.type = type;
         this.type = type;
     }
     }
+
+    public SipTransactionInfo getSipTransactionInfo() {
+        return sipTransactionInfo;
+    }
+
+    public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
+        this.sipTransactionInfo = sipTransactionInfo;
+    }
 }
 }

+ 0 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java

@@ -1,15 +1,10 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 package com.genersoft.iot.vmp.gb28181.bean;
 
 
-import com.genersoft.iot.vmp.utils.SerializeUtils;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.message.SIPResponse;
 
 
-import javax.sip.ClientTransaction;
-import javax.sip.Dialog;
-import javax.sip.RequestEvent;
 import javax.sip.ServerTransaction;
 import javax.sip.ServerTransaction;
 import javax.sip.header.*;
 import javax.sip.header.*;
-import javax.sip.message.Request;
 
 
 public class SubscribeInfo {
 public class SubscribeInfo {
 
 

+ 3 - 10
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java

@@ -10,6 +10,7 @@ import org.springframework.stereotype.Component;
 import javax.sip.*;
 import javax.sip.*;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.CallIdHeader;
 import javax.sip.message.Response;
 import javax.sip.message.Response;
+import java.text.ParseException;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Map;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -56,8 +57,7 @@ public class SipSubscribe {
         logger.debug("errorSubscribes.size:{}",errorSubscribes.size());
         logger.debug("errorSubscribes.size:{}",errorSubscribes.size());
     }
     }
 
 
-    public interface Event {
-        void response(EventResult eventResult);
+    public interface Event { void response(EventResult eventResult) ;
     }
     }
 
 
     /**
     /**
@@ -81,18 +81,13 @@ public class SipSubscribe {
         public EventResultType type;
         public EventResultType type;
         public String msg;
         public String msg;
         public String callId;
         public String callId;
-        public Dialog dialog;
         public EventObject event;
         public EventObject event;
 
 
-        public EventResult() {
-        }
-
         public EventResult(EventObject event) {
         public EventResult(EventObject event) {
             this.event = event;
             this.event = event;
             if (event instanceof ResponseEvent) {
             if (event instanceof ResponseEvent) {
                 ResponseEvent responseEvent = (ResponseEvent)event;
                 ResponseEvent responseEvent = (ResponseEvent)event;
                 Response response = responseEvent.getResponse();
                 Response response = responseEvent.getResponse();
-                this.dialog = responseEvent.getDialog();
                 this.type = EventResultType.response;
                 this.type = EventResultType.response;
                 if (response != null) {
                 if (response != null) {
                     this.msg = response.getReasonPhrase();
                     this.msg = response.getReasonPhrase();
@@ -127,12 +122,10 @@ public class SipSubscribe {
                 this.statusCode = -1024;
                 this.statusCode = -1024;
                 this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId();
                 this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId();
             }else if (event instanceof DeviceNotFoundEvent) {
             }else if (event instanceof DeviceNotFoundEvent) {
-                DeviceNotFoundEvent deviceNotFoundEvent = (DeviceNotFoundEvent)event;
                 this.type = EventResultType.deviceNotFoundEvent;
                 this.type = EventResultType.deviceNotFoundEvent;
                 this.msg = "设备未找到";
                 this.msg = "设备未找到";
                 this.statusCode = -1024;
                 this.statusCode = -1024;
-                this.dialog = deviceNotFoundEvent.getDialog();
-                this.callId = this.dialog != null ?deviceNotFoundEvent.getDialog().getCallId().getCallId() : null;
+                this.callId = ((DeviceNotFoundEvent) event).getCallId();
             }
             }
         }
         }
     }
     }

+ 1 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java

@@ -36,6 +36,7 @@ public class RequestTimeoutEventImpl implements ApplicationListener<RequestTimeo
                 }
                 }
                 deviceService.offline(device.getDeviceId());
                 deviceService.offline(device.getDeviceId());
             }
             }
+
         }
         }
     }
     }
 }
 }

+ 27 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java

@@ -18,6 +18,9 @@ import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.*;
 import java.util.*;
 
 
 /**
 /**
@@ -96,7 +99,12 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
                     }
                     }
                     if (deviceChannelList.size() > 0) {
                     if (deviceChannelList.size() > 0) {
                         logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
                         logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
-                        sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
+                        try {
+                            sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
+                        } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
+                                 IllegalAccessException e) {
+                            logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
+                        }
                     }
                     }
                 }else if (parentPlatformMap.keySet().size() > 0) {
                 }else if (parentPlatformMap.keySet().size() > 0) {
                     for (String gbId : parentPlatformMap.keySet()) {
                     for (String gbId : parentPlatformMap.keySet()) {
@@ -112,7 +120,12 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
                                 DeviceChannel deviceChannel = new DeviceChannel();
                                 DeviceChannel deviceChannel = new DeviceChannel();
                                 deviceChannel.setChannelId(gbId);
                                 deviceChannel.setChannelId(gbId);
                                 deviceChannelList.add(deviceChannel);
                                 deviceChannelList.add(deviceChannel);
-                                sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null);
+                                try {
+                                    sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null);
+                                } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
+                                         IllegalAccessException e) {
+                                    logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
@@ -137,7 +150,12 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
                     }
                     }
                     if (deviceChannelList.size() > 0) {
                     if (deviceChannelList.size() > 0) {
                         logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
                         logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
-                        sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
+                        try {
+                            sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
+                        } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
+                                 IllegalAccessException e) {
+                            logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
+                        }
                     }
                     }
                 }else if (parentPlatformMap.keySet().size() > 0) {
                 }else if (parentPlatformMap.keySet().size() > 0) {
                     for (String gbId : parentPlatformMap.keySet()) {
                     for (String gbId : parentPlatformMap.keySet()) {
@@ -157,7 +175,12 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
                                     DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), platform);
                                     DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), platform);
                                     deviceChannelList.add(deviceChannelByStream);
                                     deviceChannelList.add(deviceChannelByStream);
                                 }
                                 }
-                                sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), platform, deviceChannelList, subscribeInfo, null);
+                                try {
+                                    sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), platform, deviceChannelList, subscribeInfo, null);
+                                } catch (InvalidArgumentException | ParseException | NoSuchFieldException |
+                                         SipException | IllegalAccessException e) {
+                                    logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
+                                }
                             }
                             }
                         }
                         }
                     }
                     }

+ 7 - 51
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java

@@ -8,9 +8,13 @@ import javax.sip.Dialog;
 
 
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.gb28181.bean.SipMsgInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.utils.SerializeUtils;
 import com.genersoft.iot.vmp.utils.SerializeUtils;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.stack.SIPDialog;
 import gov.nist.javax.sip.stack.SIPDialog;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
@@ -42,15 +46,14 @@ public class VideoStreamSessionManager {
 	 * @param callId 一次请求的CallID
 	 * @param callId 一次请求的CallID
 	 * @param stream 流名称
 	 * @param stream 流名称
 	 * @param mediaServerId 所使用的流媒体ID
 	 * @param mediaServerId 所使用的流媒体ID
-	 * @param transaction 事务
+	 * @param response 回复
 	 */
 	 */
-	public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, ClientTransaction transaction, SessionType type){
+	public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, SIPResponse response, SessionType type){
 		SsrcTransaction ssrcTransaction = new SsrcTransaction();
 		SsrcTransaction ssrcTransaction = new SsrcTransaction();
 		ssrcTransaction.setDeviceId(deviceId);
 		ssrcTransaction.setDeviceId(deviceId);
 		ssrcTransaction.setChannelId(channelId);
 		ssrcTransaction.setChannelId(channelId);
 		ssrcTransaction.setStream(stream);
 		ssrcTransaction.setStream(stream);
-		byte[] transactionByteArray = SerializeUtils.serialize(transaction);
-		ssrcTransaction.setTransaction(transactionByteArray);
+		ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response));
 		ssrcTransaction.setCallId(callId);
 		ssrcTransaction.setCallId(callId);
 		ssrcTransaction.setSsrc(ssrc);
 		ssrcTransaction.setSsrc(ssrc);
 		ssrcTransaction.setMediaServerId(mediaServerId);
 		ssrcTransaction.setMediaServerId(mediaServerId);
@@ -62,53 +65,6 @@ public class VideoStreamSessionManager {
 				+ "_" +  deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
 				+ "_" +  deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
 	}
 	}
 
 
-	public void put(String deviceId, String channelId, String callId, Dialog dialog){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
-		if (ssrcTransaction != null) {
-			byte[] dialogByteArray = SerializeUtils.serialize(dialog);
-			ssrcTransaction.setDialog(dialogByteArray);
-		}
-		RedisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId()
-				+ "_" +  deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_"
-				+ ssrcTransaction.getStream(), ssrcTransaction);
-	}
-
-	
-	public ClientTransaction getTransaction(String deviceId, String channelId, String stream, String callId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, stream);
-		if (ssrcTransaction == null) {
-			return null;
-		}
-		byte[] transactionByteArray = ssrcTransaction.getTransaction();
-		ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
-		return clientTransaction;
-	}
-
-	public SIPDialog getDialogByStream(String deviceId, String channelId, String stream){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
-		if (ssrcTransaction == null) {
-			return null;
-		}
-		byte[] dialogByteArray = ssrcTransaction.getDialog();
-		if (dialogByteArray == null) {
-			return null;
-		}
-		SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
-		return dialog;
-	}
-
-	public SIPDialog getDialogByCallId(String deviceId, String channelId, String callId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
-		if (ssrcTransaction == null) {
-			return null;
-		}
-		byte[] dialogByteArray = ssrcTransaction.getDialog();
-		if (dialogByteArray == null) {
-			return null;
-		}
-		return (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
-	}
-
 	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
 	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
 
 
 		if (ObjectUtils.isEmpty(deviceId)) {
 		if (ObjectUtils.isEmpty(deviceId)) {

+ 40 - 32
src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java

@@ -10,9 +10,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
 
-import javax.sip.Dialog;
-import javax.sip.DialogState;
-import javax.sip.ResponseEvent;
+import javax.sip.*;
 import javax.sip.header.ToHeader;
 import javax.sip.header.ToHeader;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.util.Timer;
 import java.util.Timer;
@@ -44,23 +42,29 @@ public class CatalogSubscribeTask implements ISubscribeTask {
         if (dynamicTask.get(taskKey) != null) {
         if (dynamicTask.get(taskKey) != null) {
             dynamicTask.stop(taskKey);
             dynamicTask.stop(taskKey);
         }
         }
-        SIPRequest sipRequest = sipCommander.catalogSubscribe(device, request, eventResult -> {
-            ResponseEvent event = (ResponseEvent) eventResult.event;
-            // 成功
-            logger.info("[目录订阅]成功: {}", device.getDeviceId());
-            ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME);
-            try {
-                this.request.getToHeader().setTag(toHeader.getTag());
-            } catch (ParseException e) {
-                logger.info("[目录订阅]成功: 但为request设置ToTag失败");
+        SIPRequest sipRequest = null;
+        try {
+            sipRequest = sipCommander.catalogSubscribe(device, request, eventResult -> {
+                ResponseEvent event = (ResponseEvent) eventResult.event;
+                // 成功
+                logger.info("[目录订阅]成功: {}", device.getDeviceId());
+                ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME);
+                try {
+                    this.request.getToHeader().setTag(toHeader.getTag());
+                } catch (ParseException e) {
+                    logger.info("[目录订阅]成功: 但为request设置ToTag失败");
+                    this.request = null;
+                }
+            },eventResult -> {
                 this.request = null;
                 this.request = null;
-            }
-        },eventResult -> {
-            this.request = null;
-            // 失败
-            logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
-            dynamicTask.startDelay(taskKey, CatalogSubscribeTask.this, 2000);
-        });
+                // 失败
+                logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
+                dynamicTask.startDelay(taskKey, CatalogSubscribeTask.this, 2000);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 目录订阅: {}", e.getMessage());
+
+        }
         if (sipRequest != null) {
         if (sipRequest != null) {
             this.request = sipRequest;
             this.request = sipRequest;
         }
         }
@@ -80,18 +84,22 @@ public class CatalogSubscribeTask implements ISubscribeTask {
             dynamicTask.stop(taskKey);
             dynamicTask.stop(taskKey);
         }
         }
         device.setSubscribeCycleForCatalog(0);
         device.setSubscribeCycleForCatalog(0);
-        sipCommander.catalogSubscribe(device, request, eventResult -> {
-            ResponseEvent event = (ResponseEvent) eventResult.event;
-            if (event.getResponse().getRawContent() != null) {
-                // 成功
-                logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId());
-            }else {
-                // 成功
-                logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId());
-            }
-        },eventResult -> {
-            // 失败
-            logger.warn("[取消目录订阅订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
-        });
+        try {
+            sipCommander.catalogSubscribe(device, request, eventResult -> {
+                ResponseEvent event = (ResponseEvent) eventResult.event;
+                if (event.getResponse().getRawContent() != null) {
+                    // 成功
+                    logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId());
+                }else {
+                    // 成功
+                    logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId());
+                }
+            },eventResult -> {
+                // 失败
+                logger.warn("[取消目录订阅订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 取消目录订阅订阅: {}", e.getMessage());
+        }
     }
     }
 }
 }

+ 39 - 32
src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java

@@ -11,9 +11,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Async;
 
 
-import javax.sip.Dialog;
-import javax.sip.DialogState;
-import javax.sip.ResponseEvent;
+import javax.sip.*;
 import javax.sip.header.ToHeader;
 import javax.sip.header.ToHeader;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.util.Timer;
 import java.util.Timer;
@@ -43,23 +41,28 @@ public class MobilePositionSubscribeTask implements ISubscribeTask {
         if (dynamicTask.get(taskKey) != null) {
         if (dynamicTask.get(taskKey) != null) {
             dynamicTask.stop(taskKey);
             dynamicTask.stop(taskKey);
         }
         }
-        SIPRequest sipRequest = sipCommander.mobilePositionSubscribe(device, request, eventResult -> {
-            // 成功
-            logger.info("[移动位置订阅]成功: {}", device.getDeviceId());
-            ResponseEvent event = (ResponseEvent) eventResult.event;
-            ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME);
-            try {
-                this.request.getToHeader().setTag(toHeader.getTag());
-            } catch (ParseException e) {
-                logger.info("[移动位置订阅]成功: 为request设置ToTag失败");
+        SIPRequest sipRequest = null;
+        try {
+            sipRequest = sipCommander.mobilePositionSubscribe(device, request, eventResult -> {
+                // 成功
+                logger.info("[移动位置订阅]成功: {}", device.getDeviceId());
+                ResponseEvent event = (ResponseEvent) eventResult.event;
+                ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME);
+                try {
+                    this.request.getToHeader().setTag(toHeader.getTag());
+                } catch (ParseException e) {
+                    logger.info("[移动位置订阅]成功: 为request设置ToTag失败");
+                    this.request = null;
+                }
+            },eventResult -> {
                 this.request = null;
                 this.request = null;
-            }
-        },eventResult -> {
-            this.request = null;
-            // 失败
-            logger.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
-            dynamicTask.startDelay(taskKey, MobilePositionSubscribeTask.this, 2000);
-        });
+                // 失败
+                logger.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
+                dynamicTask.startDelay(taskKey, MobilePositionSubscribeTask.this, 2000);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 移动位置订阅: {}", e.getMessage());
+        }
         if (sipRequest != null) {
         if (sipRequest != null) {
             this.request = sipRequest;
             this.request = sipRequest;
         }
         }
@@ -79,18 +82,22 @@ public class MobilePositionSubscribeTask implements ISubscribeTask {
             dynamicTask.stop(taskKey);
             dynamicTask.stop(taskKey);
         }
         }
         device.setSubscribeCycleForMobilePosition(0);
         device.setSubscribeCycleForMobilePosition(0);
-        sipCommander.mobilePositionSubscribe(device, request, eventResult -> {
-            ResponseEvent event = (ResponseEvent) eventResult.event;
-            if (event.getResponse().getRawContent() != null) {
-                // 成功
-                logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
-            }else {
-                // 成功
-                logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
-            }
-        },eventResult -> {
-            // 失败
-            logger.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
-        });
+        try {
+            sipCommander.mobilePositionSubscribe(device, request, eventResult -> {
+                ResponseEvent event = (ResponseEvent) eventResult.event;
+                if (event.getResponse().getRawContent() != null) {
+                    // 成功
+                    logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
+                }else {
+                    // 成功
+                    logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
+                }
+            },eventResult -> {
+                // 失败
+                logger.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 取消移动位置订阅: {}", e.getMessage());
+        }
     }
     }
 }
 }

+ 56 - 43
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 
 
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
@@ -9,6 +10,11 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
 
 
 import javax.sip.Dialog;
 import javax.sip.Dialog;
+import javax.sip.InvalidArgumentException;
+import javax.sip.PeerUnavailableException;
+import javax.sip.SipException;
+import javax.sip.message.Request;
+import java.text.ParseException;
 
 
 /**    
 /**    
  * @description:设备能力接口,用于定义设备的控制、查询能力   
  * @description:设备能力接口,用于定义设备的控制、查询能力   
@@ -25,7 +31,7 @@ public interface ISIPCommander {
 	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
 	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
      * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
      * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
 	 */
 	 */
-	boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
+	void ptzdirectCmd(Device device,String channelId,int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException;
 	
 	
 	/**
 	/**
 	 * 云台方向放控制
 	 * 云台方向放控制
@@ -36,7 +42,7 @@ public interface ISIPCommander {
      * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
      * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
      * @param moveSpeed  镜头移动速度
      * @param moveSpeed  镜头移动速度
 	 */
 	 */
-	boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
+	void ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException;
 	
 	
 	/**
 	/**
 	 * 云台缩放控制,使用配置文件中的默认镜头缩放速度
 	 * 云台缩放控制,使用配置文件中的默认镜头缩放速度
@@ -45,7 +51,7 @@ public interface ISIPCommander {
 	 * @param channelId  预览通道
 	 * @param channelId  预览通道
      * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
      * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
 	 */
 	 */
-	boolean ptzZoomCmd(Device device,String channelId,int inOut);
+	void ptzZoomCmd(Device device,String channelId,int inOut) throws InvalidArgumentException, ParseException, SipException;
 	
 	
 	/**
 	/**
 	 * 云台缩放控制
 	 * 云台缩放控制
@@ -54,7 +60,7 @@ public interface ISIPCommander {
 	 * @param channelId  预览通道
 	 * @param channelId  预览通道
      * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
      * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
 	 */
 	 */
-	boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
+	void ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed) throws InvalidArgumentException, ParseException, SipException;
 	
 	
 	/**
 	/**
 	 * 云台控制,支持方向与缩放控制
 	 * 云台控制,支持方向与缩放控制
@@ -67,7 +73,7 @@ public interface ISIPCommander {
      * @param moveSpeed  镜头移动速度
      * @param moveSpeed  镜头移动速度
      * @param zoomSpeed  镜头缩放速度
      * @param zoomSpeed  镜头缩放速度
 	 */
 	 */
-	boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
+	void ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
 	 * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
@@ -79,7 +85,7 @@ public interface ISIPCommander {
      * @param parameter2	数据2
      * @param parameter2	数据2
      * @param combineCode2	组合码2
      * @param combineCode2	组合码2
 	 */
 	 */
-	boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
+	void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException;
 	
 	
 	/**
 	/**
 	 * 前端控制指令(用于转发上级指令)
 	 * 前端控制指令(用于转发上级指令)
@@ -87,14 +93,14 @@ public interface ISIPCommander {
 	 * @param channelId		预览通道
 	 * @param channelId		预览通道
 	 * @param cmdString		前端控制指令串
 	 * @param cmdString		前端控制指令串
 	 */
 	 */
-	boolean fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
+	void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 请求预览视频流
 	 * 请求预览视频流
 	 * @param device  视频设备
 	 * @param device  视频设备
 	 * @param channelId  预览通道
 	 * @param channelId  预览通道
 	 */
 	 */
-	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
+	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 请求回放视频流
 	 * 请求回放视频流
@@ -104,7 +110,7 @@ public interface ISIPCommander {
 	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 */
 	 */
-	void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
+	void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 请求历史媒体下载
 	 * 请求历史媒体下载
@@ -117,33 +123,33 @@ public interface ISIPCommander {
 	 */ 
 	 */ 
 	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
 	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
 						   String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
 						   String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
-						   SipSubscribe.Event errorEvent);
+						   SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 视频流停止
 	 * 视频流停止
 	 */
 	 */
-	void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent);
-	void streamByeCmd(String deviceId, String channelId, String stream, String callId);
+	void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
+	void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException;
 
 
 	/**
 	/**
 	 * 回放暂停
 	 * 回放暂停
 	 */
 	 */
-	void playPauseCmd(Device device, StreamInfo streamInfo);
+	void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException;
 
 
 	/**
 	/**
 	 * 回放恢复
 	 * 回放恢复
 	 */
 	 */
-	void playResumeCmd(Device device, StreamInfo streamInfo);
+	void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException;
 
 
 	/**
 	/**
 	 * 回放拖动播放
 	 * 回放拖动播放
 	 */
 	 */
-	void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime);
+	void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException;
 
 
 	/**
 	/**
 	 * 回放倍速播放
 	 * 回放倍速播放
 	 */
 	 */
-	void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
+	void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException;
 	
 	
 	/**
 	/**
 	 * 回放控制
 	 * 回放控制
@@ -151,23 +157,24 @@ public interface ISIPCommander {
 	 * @param streamInfo
 	 * @param streamInfo
 	 * @param content
 	 * @param content
 	 */
 	 */
-	void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
+	void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
 
 
-	/**
+
+    /**
 	 * 语音广播
 	 * 语音广播
 	 * 
 	 * 
 	 * @param device  视频设备
 	 * @param device  视频设备
 	 * @param channelId  预览通道
 	 * @param channelId  预览通道
 	 */
 	 */
-	boolean audioBroadcastCmd(Device device,String channelId);
+	void audioBroadcastCmd(Device device,String channelId);
 	
 	
 	/**
 	/**
 	 * 语音广播
 	 * 语音广播
 	 * 
 	 * 
 	 * @param device  视频设备
 	 * @param device  视频设备
 	 */
 	 */
-	void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);
-	boolean audioBroadcastCmd(Device device);
+	void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
+	void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 音视频录像控制
 	 * 音视频录像控制
@@ -176,21 +183,21 @@ public interface ISIPCommander {
 	 * @param channelId  	预览通道
 	 * @param channelId  	预览通道
 	 * @param recordCmdStr	录像命令:Record / StopRecord
 	 * @param recordCmdStr	录像命令:Record / StopRecord
 	 */
 	 */
-	boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
+	void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 远程启动控制命令
 	 * 远程启动控制命令
 	 * 
 	 * 
 	 * @param device	视频设备
 	 * @param device	视频设备
 	 */
 	 */
-	boolean teleBootCmd(Device device);
+	void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 报警布防/撤防命令
 	 * 报警布防/撤防命令
 	 * 
 	 * 
 	 * @param device  	视频设备
 	 * @param device  	视频设备
 	 */
 	 */
-	boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
+	void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 报警复位命令
 	 * 报警复位命令
@@ -199,7 +206,7 @@ public interface ISIPCommander {
 	 * @param alarmMethod	报警方式(可选)
 	 * @param alarmMethod	报警方式(可选)
 	 * @param alarmType		报警类型(可选)
 	 * @param alarmType		报警类型(可选)
 	 */
 	 */
-	boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
+	void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
 	 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
@@ -207,7 +214,7 @@ public interface ISIPCommander {
 	 * @param device  视频设备
 	 * @param device  视频设备
 	 * @param channelId  预览通道
 	 * @param channelId  预览通道
 	 */
 	 */
-	boolean iFrameCmd(Device device, String channelId);
+	void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 看守位控制命令
 	 * 看守位控制命令
@@ -217,14 +224,14 @@ public interface ISIPCommander {
 	 * @param resetTime		自动归位时间间隔,开启看守位时使用,单位:秒(s)
 	 * @param resetTime		自动归位时间间隔,开启看守位时使用,单位:秒(s)
 	 * @param presetIndex	调用预置位编号,开启看守位时使用,取值范围0~255
 	 * @param presetIndex	调用预置位编号,开启看守位时使用,取值范围0~255
 	 */
 	 */
-	boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
+	void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 设备配置命令
 	 * 设备配置命令
 	 * 
 	 * 
 	 * @param device  视频设备
 	 * @param device  视频设备
 	 */
 	 */
-	boolean deviceConfigCmd(Device device);
+	void deviceConfigCmd(Device device);
 	
 	
 		/**
 		/**
 	 * 设备配置命令:basicParam
 	 * 设备配置命令:basicParam
@@ -236,14 +243,14 @@ public interface ISIPCommander {
 	 * @param heartBeatInterval	心跳间隔时间(可选)
 	 * @param heartBeatInterval	心跳间隔时间(可选)
 	 * @param heartBeatCount	心跳超时次数(可选)
 	 * @param heartBeatCount	心跳超时次数(可选)
 	 */  
 	 */  
-	boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
+	void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询设备状态
 	 * 查询设备状态
 	 * 
 	 * 
 	 * @param device 视频设备
 	 * @param device 视频设备
 	 */
 	 */
-	boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
+	void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询设备信息
 	 * 查询设备信息
@@ -251,14 +258,14 @@ public interface ISIPCommander {
 	 * @param device 视频设备
 	 * @param device 视频设备
 	 * @return 
 	 * @return 
 	 */
 	 */
-	boolean deviceInfoQuery(Device device);
+	void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询目录列表
 	 * 查询目录列表
 	 * 
 	 * 
 	 * @param device 视频设备
 	 * @param device 视频设备
 	 */
 	 */
-	boolean catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent);
+	void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询录像信息
 	 * 查询录像信息
@@ -268,7 +275,7 @@ public interface ISIPCommander {
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param sn
 	 * @param sn
 	 */
 	 */
-	boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn,  Integer Secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
+	void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn,  Integer Secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询报警信息
 	 * 查询报警信息
@@ -282,8 +289,8 @@ public interface ISIPCommander {
 	 * @param endTime		报警发生终止时间(可选)
 	 * @param endTime		报警发生终止时间(可选)
 	 * @return				true = 命令发送成功
 	 * @return				true = 命令发送成功
 	 */
 	 */
-	boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
-							String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);	
+	void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
+							String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询设备配置
 	 * 查询设备配置
@@ -292,21 +299,21 @@ public interface ISIPCommander {
 	 * @param channelId		通道编码(可选)
 	 * @param channelId		通道编码(可选)
 	 * @param configType	配置类型:
 	 * @param configType	配置类型:
 	 */
 	 */
-	boolean deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent);
+	void deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询设备预置位置
 	 * 查询设备预置位置
 	 * 
 	 * 
 	 * @param device 视频设备
 	 * @param device 视频设备
 	 */
 	 */
-	boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
+	void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	
 	/**
 	/**
 	 * 查询移动设备位置数据
 	 * 查询移动设备位置数据
 	 * 
 	 * 
 	 * @param device 视频设备
 	 * @param device 视频设备
 	 */
 	 */
-	boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
+	void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 订阅、取消订阅移动位置
 	 * 订阅、取消订阅移动位置
@@ -314,7 +321,7 @@ public interface ISIPCommander {
 	 * @param device	视频设备
 	 * @param device	视频设备
 	 * @return			true = 命令发送成功
 	 * @return			true = 命令发送成功
 	 */
 	 */
-	SIPRequest mobilePositionSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent);
+	SIPRequest mobilePositionSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 订阅、取消订阅报警信息
 	 * 订阅、取消订阅报警信息
@@ -327,14 +334,14 @@ public interface ISIPCommander {
 	 * @param endTime		报警发生终止时间(可选)
 	 * @param endTime		报警发生终止时间(可选)
 	 * @return				true = 命令发送成功
 	 * @return				true = 命令发送成功
 	 */
 	 */
-	boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
+	void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 订阅、取消订阅目录信息
 	 * 订阅、取消订阅目录信息
 	 * @param device		视频设备
 	 * @param device		视频设备
 	 * @return				true = 命令发送成功
 	 * @return				true = 命令发送成功
 	 */
 	 */
-	SIPRequest catalogSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent);
+	SIPRequest catalogSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 	/**
 	/**
 	 * 拉框控制命令
 	 * 拉框控制命令
@@ -343,7 +350,7 @@ public interface ISIPCommander {
 	 * @param channelId 通道id
 	 * @param channelId 通道id
 	 * @param cmdString 前端控制指令串
 	 * @param cmdString 前端控制指令串
 	 */
 	 */
-	boolean dragZoomCmd(Device device, String channelId, String cmdString);
+	void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException;
 
 
 
 
 	/**
 	/**
@@ -352,5 +359,11 @@ public interface ISIPCommander {
 	 * @param deviceAlarm 报警信息信息
 	 * @param deviceAlarm 报警信息信息
 	 * @return
 	 * @return
 	 */
 	 */
-	boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm);
+	void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException;
+
+	void transmitRequest(String transport, Request request) throws SipException, ParseException ;
+
+	void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent) throws SipException, ParseException;
+
+	void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, ParseException;
 }
 }

+ 19 - 16
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java

@@ -4,7 +4,10 @@ import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
 import javax.sip.header.WWWAuthenticateHeader;
 import javax.sip.header.WWWAuthenticateHeader;
+import java.text.ParseException;
 import java.util.List;
 import java.util.List;
 
 
 public interface ISIPCommanderForPlatform {
 public interface ISIPCommanderForPlatform {
@@ -14,15 +17,15 @@ public interface ISIPCommanderForPlatform {
      * @param parentPlatform
      * @param parentPlatform
      * @return
      * @return
      */
      */
-    boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent);
-    boolean register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister);
+    void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
+    void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 向上级平台注销
      * 向上级平台注销
      * @param parentPlatform
      * @param parentPlatform
      * @return
      * @return
      */
      */
-    boolean unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent);
+    void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
 
 
 
 
     /**
     /**
@@ -30,7 +33,7 @@ public interface ISIPCommanderForPlatform {
      * @param parentPlatform
      * @param parentPlatform
      * @return callId(作为接受回复的判定)
      * @return callId(作为接受回复的判定)
      */
      */
-    String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent);
+    String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
 
 
 
 
     /**
     /**
@@ -42,8 +45,8 @@ public interface ISIPCommanderForPlatform {
      * @param size
      * @param size
      * @return
      * @return
      */
      */
-    boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size);
-    boolean catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag);
+    void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException;
+    void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException;
 
 
     /**
     /**
      * 向上级回复DeviceInfo查询信息
      * 向上级回复DeviceInfo查询信息
@@ -52,7 +55,7 @@ public interface ISIPCommanderForPlatform {
      * @param fromTag
      * @param fromTag
      * @return
      * @return
      */
      */
-    boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag);
+    void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 向上级回复DeviceStatus查询信息
      * 向上级回复DeviceStatus查询信息
@@ -61,7 +64,7 @@ public interface ISIPCommanderForPlatform {
      * @param fromTag
      * @param fromTag
      * @return
      * @return
      */
      */
-    boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag);
+    void deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 向上级回复移动位置订阅消息
      * 向上级回复移动位置订阅消息
@@ -70,7 +73,7 @@ public interface ISIPCommanderForPlatform {
      * @param subscribeInfo 订阅相关的信息
      * @param subscribeInfo 订阅相关的信息
      * @return
      * @return
      */
      */
-    boolean sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo);
+    void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
 
 
     /**
     /**
      * 向上级回复报警消息
      * 向上级回复报警消息
@@ -78,21 +81,21 @@ public interface ISIPCommanderForPlatform {
      * @param deviceAlarm 报警信息信息
      * @param deviceAlarm 报警信息信息
      * @return
      * @return
      */
      */
-    boolean sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm);
+    void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 回复catalog事件-增加/更新
      * 回复catalog事件-增加/更新
      * @param parentPlatform
      * @param parentPlatform
      * @param deviceChannels
      * @param deviceChannels
      */
      */
-    boolean sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index);
+    void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
 
 
     /**
     /**
      * 回复catalog事件-删除
      * 回复catalog事件-删除
      * @param parentPlatform
      * @param parentPlatform
      * @param deviceChannels
      * @param deviceChannels
      */
      */
-    boolean sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index);
+    void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
 
 
     /**
     /**
      * 回复recordInfo
      * 回复recordInfo
@@ -101,7 +104,7 @@ public interface ISIPCommanderForPlatform {
      * @param fromTag fromTag
      * @param fromTag fromTag
      * @param recordInfo 录像信息
      * @param recordInfo 录像信息
      */
      */
-    boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo);
+    void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 录像播放推送完成时发送MediaStatus消息
      * 录像播放推送完成时发送MediaStatus消息
@@ -109,13 +112,13 @@ public interface ISIPCommanderForPlatform {
      * @param sendRtpItem
      * @param sendRtpItem
      * @return
      * @return
      */
      */
-    boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem);
+    void sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 向发起点播的上级回复bye
      * 向发起点播的上级回复bye
      * @param platform 平台信息
      * @param platform 平台信息
      * @param callId  callId
      * @param callId  callId
      */
      */
-    void streamByeCmd(ParentPlatform platform, String callId);
-    void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem);
+    void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException;
+    void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException;
 }
 }

+ 74 - 42
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java

@@ -10,6 +10,9 @@ import javax.sip.header.*;
 import javax.sip.message.Request;
 import javax.sip.message.Request;
 
 
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SipMsgInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -17,6 +20,7 @@ import com.genersoft.iot.vmp.utils.GitUtil;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.stack.SIPDialog;
 import gov.nist.javax.sip.stack.SIPDialog;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -168,34 +172,37 @@ public class SIPRequestHeaderProvider {
 		return request;
 		return request;
 	}
 	}
 
 
-	public Request createByteRequest(Device device, String channelId, String viaTag, String fromTag, String toTag, String callId) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+	public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		Request request = null;
 		//请求行
 		//请求行
 		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		// via
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
 		viaHeaders.add(viaHeader);
 		viaHeaders.add(viaHeader);
 		//from
 		//from
 		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
 		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
 		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
 		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
 		//to
 		//to
 		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,device.getHostAddress());
 		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,device.getHostAddress());
 		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
 		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,toTag);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,	transactionInfo.getToTag());
 
 
 		//Forwards
 		//Forwards
 		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
 		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
 
 
 		//ceq
 		//ceq
 		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
 		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
-		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(callId);
+		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
 		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 
 
 		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
 		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
 
 
 		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
 		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
+		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
 
 
 		return request;
 		return request;
 	}
 	}
@@ -251,47 +258,72 @@ public class SIPRequestHeaderProvider {
 		return request;
 		return request;
 	}
 	}
 
 
-	public Request createInfoRequest(Device device, StreamInfo streamInfo, String content)
+	public SIPRequest createInfoRequest(Device device, String channelId, String content, SipTransactionInfo transactionInfo)
 			throws SipException, ParseException, InvalidArgumentException {
 			throws SipException, ParseException, InvalidArgumentException {
-		if (streamInfo == null) {
-			return null;
-		}
-		Request request = null;
-		SIPDialog dialog = streamSession.getDialogByStream(streamInfo.getDeviceID(), streamInfo.getChannelId(), streamInfo.getStream());
-		if (dialog == null) {
+		if (device == null || transactionInfo == null) {
 			return null;
 			return null;
 		}
 		}
+		SIPRequest request = null;
+		//请求行
+		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
+		viaHeaders.add(viaHeader);
+		//from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
+		//to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,device.getHostAddress());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,	transactionInfo.getToTag());
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
+		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
+		request = (SIPRequest)sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 
 
-		SipStack sipStack = udpSipProvider.getSipStack();
-		SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
-		if (dialog != sipDialog) {
-			dialog = sipDialog;
-		}else {
-			dialog.setSipProvider(udpSipProvider);
+		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
+		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+
+		if (content != null) {
+			ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
+					"MANSRTSP");
+			request.setContent(content, contentTypeHeader);
 		}
 		}
-		streamSession.put(streamInfo.getDeviceID(), streamInfo.getChannelId(), dialog.getCallId().getCallId(), dialog);
-		Request infoRequest = dialog.createRequest(Request.INFO);
-		SipURI sipURI = (SipURI) infoRequest.getRequestURI();
-		sipURI.setHost(device.getIp());
-		sipURI.setPort(device.getPort());
-		sipURI.setUser(streamInfo.getChannelId());
-
-		ViaHeader viaHeader = (ViaHeader) infoRequest.getHeader(ViaHeader.NAME);
-		viaHeader.setRPort();
-		// 增加Contact header
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
-				.createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
-		infoRequest.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
-		infoRequest.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
-
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
-				"MANSRTSP");
-		infoRequest.setContent(content, contentTypeHeader);
-
-		CSeqHeader cSeqHeader = (CSeqHeader)infoRequest.getHeader(CSeqHeader.NAME);
-		cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ());
-		// ceq
-		infoRequest.addHeader(cSeqHeader);
-		return infoRequest;
+		return request;
+	}
+
+	public Request createAckRequest(SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
+		viaHeaders.add(viaHeader);
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
+
+		Request request = sipFactory.createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
+		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+
+		return request;
 	}
 	}
 }
 }

+ 1432 - 1775
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 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.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
@@ -22,17 +23,20 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.GitUtil;
+import gov.nist.javax.sip.SIPConstants;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.SipStackImpl;
-import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.stack.SIPClientTransaction;
 import gov.nist.javax.sip.stack.SIPClientTransaction;
+import gov.nist.javax.sip.stack.SIPClientTransactionImpl;
 import gov.nist.javax.sip.stack.SIPDialog;
 import gov.nist.javax.sip.stack.SIPDialog;
 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.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 
 
@@ -41,1791 +45,1444 @@ import javax.sip.address.Address;
 import javax.sip.address.SipURI;
 import javax.sip.address.SipURI;
 import javax.sip.header.*;
 import javax.sip.header.*;
 import javax.sip.message.Request;
 import javax.sip.message.Request;
+import javax.sip.message.Response;
 import java.lang.reflect.Field;
 import java.lang.reflect.Field;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.util.HashSet;
 import java.util.HashSet;
 
 
-/**    
- * @description:设备能力接口,用于定义设备的控制、查询能力   
+/**
+ * @description:设备能力接口,用于定义设备的控制、查询能力
  * @author: swwheihei
  * @author: swwheihei
- * @date:   2020年5月3日 下午9:22:48     
+ * @date: 2020年5月3日 下午9:22:48
  */
  */
 @Component
 @Component
 @DependsOn("sipLayer")
 @DependsOn("sipLayer")
 public class SIPCommander implements ISIPCommander {
 public class SIPCommander implements ISIPCommander {
 
 
-	private final Logger logger = LoggerFactory.getLogger(SIPCommander.class);
-
-	@Autowired
-	private SipConfig sipConfig;
-
-	@Autowired
-	private SipFactory sipFactory;
-
-	@Autowired
-	private GitUtil gitUtil;
-
-	@Autowired
-	@Qualifier(value="tcpSipProvider")
-	private SipProviderImpl tcpSipProvider;
-
-	@Autowired
-	@Qualifier(value="udpSipProvider")
-	private SipProviderImpl udpSipProvider;
-
-	@Autowired
-	private SIPRequestHeaderProvider headerProvider;
-	
-	@Autowired
-	private VideoStreamSessionManager streamSession;
-
-	@Autowired
-	private IVideoManagerStorage storager;
-
-	@Autowired
-	private IRedisCatchStorage redisCatchStorage;
-
-	@Autowired
-	private UserSetting userSetting;
-
-	@Autowired
-	private ZlmHttpHookSubscribe subscribe;
-
-	@Autowired
-	private SipSubscribe sipSubscribe;
-
-	@Autowired
-	private IMediaServerService mediaServerService;
-
-	@Autowired
-	private DynamicTask dynamicTask;
-
-
-	/**
-	 * 云台方向放控制,使用配置文件中的默认镜头移动速度
-	 * 
-	 * @param device  控制设备
-	 * @param channelId  预览通道
-	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
-     * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
-	 */
-	@Override
-	public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) {
-		return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0);
-	}
-
-	/**
-	 * 云台方向放控制
-	 * 
-	 * @param device  控制设备
-	 * @param channelId  预览通道
-	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
-     * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
-     * @param moveSpeed  镜头移动速度
-	 */
-	@Override
-	public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) {
-		return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0);
-	}
-
-	/**
-	 * 云台缩放控制,使用配置文件中的默认镜头缩放速度
-	 * 
-	 * @param device  控制设备
-	 * @param channelId  预览通道
-     * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
-	 */  
-	@Override
-	public boolean ptzZoomCmd(Device device, String channelId, int inOut) {
-		return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed());
-	}
-
-	/**
-	 * 云台缩放控制
-	 * 
-	 * @param device  控制设备
-	 * @param channelId  预览通道
-     * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
-     * @param zoomSpeed  镜头缩放速度
-	 */ 
-	@Override
-	public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) {
-		return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);
-	}
-  
-   /**
-	* 云台指令码计算 
-	*
-    * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
-    * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
-    * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
-    * @param moveSpeed  镜头移动速度 默认 0XFF (0-255)
-    * @param zoomSpeed  镜头缩放速度 默认 0X1 (0-255)
-    */
-    public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) {
-		int cmdCode = 0;
-		if (leftRight == 2) {
-			cmdCode|=0x01;		// 右移
-		} else if(leftRight == 1) {
-			cmdCode|=0x02;		// 左移
-		}
-		if (upDown == 2) {
-			cmdCode|=0x04;		// 下移
-		} else if(upDown == 1) {
-			cmdCode|=0x08;		// 上移
-		}
-		if (inOut == 2) {
-			cmdCode |= 0x10;	// 放大
-		} else if(inOut == 1) {
-			cmdCode |= 0x20;	// 缩小
-		}
-		StringBuilder builder = new StringBuilder("A50F01");
-		String strTmp;
-		strTmp = String.format("%02X", cmdCode);
-		builder.append(strTmp, 0, 2);
-		strTmp = String.format("%02X", moveSpeed);
-		builder.append(strTmp, 0, 2);
-		builder.append(strTmp, 0, 2);
-		strTmp = String.format("%X", zoomSpeed);
-		builder.append(strTmp, 0, 1).append("0");
-		//计算校验码
-		int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100;
-		strTmp = String.format("%02X", checkCode);
-		builder.append(strTmp, 0, 2);
-		return builder.toString();
-}
-
-   /**
-	* 云台指令码计算 
-	*
-	 * @param cmdCode 		指令码
-	 * @param parameter1	数据1
-	 * @param parameter2	数据2
-	 * @param combineCode2	组合码2
-	 */
+    private final Logger logger = LoggerFactory.getLogger(SIPCommander.class);
+
+    @Autowired
+    private SipConfig sipConfig;
+
+    @Autowired
+    private SipFactory sipFactory;
+
+    @Autowired
+    private GitUtil gitUtil;
+
+    @Autowired
+    @Qualifier(value = "tcpSipProvider")
+    private SipProviderImpl tcpSipProvider;
+
+    @Autowired
+    @Qualifier(value = "udpSipProvider")
+    private SipProviderImpl udpSipProvider;
+
+    @Autowired
+    private SIPRequestHeaderProvider headerProvider;
+
+    @Autowired
+    private VideoStreamSessionManager streamSession;
+
+    @Autowired
+    private UserSetting userSetting;
+
+    @Autowired
+    private ZlmHttpHookSubscribe subscribe;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+
+    /**
+     * 云台方向放控制,使用配置文件中的默认镜头移动速度
+     *
+     * @param device    控制设备
+     * @param channelId 预览通道
+     * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown    镜头上移下移 0:停止 1:上移 2:下移
+     */
+    @Override
+    public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException {
+        ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0);
+    }
+
+    /**
+     * 云台方向放控制
+     *
+     * @param device    控制设备
+     * @param channelId 预览通道
+     * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown    镜头上移下移 0:停止 1:上移 2:下移
+     * @param moveSpeed 镜头移动速度
+     */
+    @Override
+    public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException {
+        ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0);
+    }
+
+    /**
+     * 云台缩放控制,使用配置文件中的默认镜头缩放速度
+     *
+     * @param device    控制设备
+     * @param channelId 预览通道
+     * @param inOut     镜头放大缩小 0:停止 1:缩小 2:放大
+     */
+    @Override
+    public void ptzZoomCmd(Device device, String channelId, int inOut) throws InvalidArgumentException, ParseException, SipException {
+        ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed());
+    }
+
+    /**
+     * 云台缩放控制
+     *
+     * @param device    控制设备
+     * @param channelId 预览通道
+     * @param inOut     镜头放大缩小 0:停止 1:缩小 2:放大
+     * @param zoomSpeed 镜头缩放速度
+     */
+    @Override
+    public void ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) throws InvalidArgumentException, ParseException, SipException {
+        ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);
+    }
+
+    /**
+     * 云台指令码计算
+     *
+     * @param cmdCode      指令码
+     * @param parameter1   数据1
+     * @param parameter2   数据2
+     * @param combineCode2 组合码2
+     */
     public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) {
     public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) {
-		StringBuilder builder = new StringBuilder("A50F01");
-		String strTmp;
-		strTmp = String.format("%02X", cmdCode);
-		builder.append(strTmp, 0, 2);
-		strTmp = String.format("%02X", parameter1);
-		builder.append(strTmp, 0, 2);
-		strTmp = String.format("%02X", parameter2);
-		builder.append(strTmp, 0, 2);
-		strTmp = String.format("%X", combineCode2);
-		builder.append(strTmp, 0, 1).append("0");
-		//计算校验码
-		int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;
-		strTmp = String.format("%02X", checkCode);
-		builder.append(strTmp, 0, 2);
-		return builder.toString();
-	}
-
-	/**
-	 * 云台控制,支持方向与缩放控制
-	 * 
-	 * @param device  	控制设备
-	 * @param channelId	预览通道
-	 * @param leftRight	镜头左移右移 0:停止 1:左移 2:右移
-     * @param upDown	镜头上移下移 0:停止 1:上移 2:下移
-     * @param inOut		镜头放大缩小 0:停止 1:缩小 2:放大
-     * @param moveSpeed	镜头移动速度
-     * @param zoomSpeed	镜头缩放速度
-	 */
-	@Override
-	public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
-			int zoomSpeed) {
-		try {
-			String cmdStr= cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
-			StringBuffer ptzXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			ptzXml.append("<Control>\r\n");
-			ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
-			ptzXml.append("<Info>\r\n");
-			ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
-			ptzXml.append("</Info>\r\n");
-			ptzXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			
-			transmitRequest(device, request);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		} 
-		return false;
-	}
-
-	/**
-	 * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
-	 * 
-	 * @param device  		控制设备
-	 * @param channelId		预览通道
-	 * @param cmdCode		指令码
-     * @param parameter1	数据1
-     * @param parameter2	数据2
-     * @param combineCode2	组合码2
-	 */
-	@Override
-	public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) {
-		try {
-			String cmdStr= frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2);
-			logger.debug("控制字符串:" + cmdStr);
-			StringBuffer ptzXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			ptzXml.append("<Control>\r\n");
-			ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
-			ptzXml.append("<Info>\r\n");
-			ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
-			ptzXml.append("</Info>\r\n");
-			ptzXml.append("</Control>\r\n");
-			
-
-			CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		} 
-		return false;
-	}
-
-	/**
-	 * 前端控制指令(用于转发上级指令)
-	 * @param device		控制设备
-	 * @param channelId		预览通道
-	 * @param cmdString		前端控制指令串
-	 */
-	@Override
-	public boolean fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
-		try {
-			StringBuffer ptzXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			ptzXml.append("<Control>\r\n");
-			ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n");
-			ptzXml.append("<Info>\r\n");
-			ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
-			ptzXml.append("</Info>\r\n");
-			ptzXml.append("</Control>\r\n");
-			
-
-			CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent, okEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		} 
-		return false;
-	}
-	
-	 /**
-	 * 	请求预览视频流
-	  * @param device  视频设备
-	  * @param channelId  预览通道
-	  * @param event hook订阅
-	  * @param errorEvent sip错误订阅
-	*/
-	@Override
-	public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-							  ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
-		String stream = ssrcInfo.getStream();
-		try {
-			if (device == null) {
-				return;
-			}
-//			String streamMode = device.getStreamMode().toUpperCase();
-
-			logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
-			HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
-			subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
-				if (event != null) {
-					event.response(mediaServerItemInUse, json);
-					subscribe.removeSubscribe(hookSubscribe);
-				}
-			});
-			//
-			StringBuffer content = new StringBuffer(200);
-			content.append("v=0\r\n");
-			content.append("o="+ channelId+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
-			content.append("s=Play\r\n");
-			content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
-			content.append("t=0 0\r\n");
-
-			if (userSetting.isSeniorSdp()) {
-				if("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}else if("UDP".equalsIgnoreCase(device.getStreamMode())) {
-					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}
-				content.append("a=recvonly\r\n");
-				content.append("a=rtpmap:96 PS/90000\r\n");
-				content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
-				content.append("a=rtpmap:126 H264/90000\r\n");
-				content.append("a=rtpmap:125 H264S/90000\r\n");
-				content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
-				content.append("a=rtpmap:99 H265/90000\r\n");
-				content.append("a=rtpmap:98 H264/90000\r\n");
-				content.append("a=rtpmap:97 MPEG4/90000\r\n");
-				if("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())){ // tcp被动模式
-					content.append("a=setup:passive\r\n");
-					content.append("a=connection:new\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
-					content.append("a=setup:active\r\n");
-					content.append("a=connection:new\r\n");
-				}
-			}else {
-				if("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
-				}else if("UDP".equalsIgnoreCase(device.getStreamMode())) {
-					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
-				}
-				content.append("a=recvonly\r\n");
-				content.append("a=rtpmap:96 PS/90000\r\n");
-				content.append("a=rtpmap:98 H264/90000\r\n");
-				content.append("a=rtpmap:97 MPEG4/90000\r\n");
-				content.append("a=rtpmap:99 H265/90000\r\n");
-				if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
-					content.append("a=setup:passive\r\n");
-					content.append("a=connection:new\r\n");
-				} else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
-					content.append("a=setup:active\r\n");
-					content.append("a=connection:new\r\n");
-				}
-			}
-
-			content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc
-			// f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
+        StringBuilder builder = new StringBuilder("A50F01");
+        String strTmp;
+        strTmp = String.format("%02X", cmdCode);
+        builder.append(strTmp, 0, 2);
+        strTmp = String.format("%02X", parameter1);
+        builder.append(strTmp, 0, 2);
+        strTmp = String.format("%02X", parameter2);
+        builder.append(strTmp, 0, 2);
+        strTmp = String.format("%X", combineCode2);
+        builder.append(strTmp, 0, 1).append("0");
+        //计算校验码
+        int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;
+        strTmp = String.format("%02X", checkCode);
+        builder.append(strTmp, 0, 2);
+        return builder.toString();
+    }
+
+    /**
+     * 云台控制,支持方向与缩放控制
+     *
+     * @param device    控制设备
+     * @param channelId 预览通道
+     * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown    镜头上移下移 0:停止 1:上移 2:下移
+     * @param inOut     镜头放大缩小 0:停止 1:缩小 2:放大
+     * @param moveSpeed 镜头移动速度
+     * @param zoomSpeed 镜头缩放速度
+     */
+    @Override
+    public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
+                       int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
+        String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
+        StringBuffer ptzXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        ptzXml.append("<Control>\r\n");
+        ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
+        ptzXml.append("<Info>\r\n");
+        ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
+        ptzXml.append("</Info>\r\n");
+        ptzXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+
+        transmitRequest(device.getTransport(), request);
+    }
+
+    /**
+     * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
+     *
+     * @param device       控制设备
+     * @param channelId    预览通道
+     * @param cmdCode      指令码
+     * @param parameter1   数据1
+     * @param parameter2   数据2
+     * @param combineCode2 组合码2
+     */
+    @Override
+    public void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException {
+
+        String cmdStr = frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2);
+        StringBuffer ptzXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        ptzXml.append("<Control>\r\n");
+        ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
+        ptzXml.append("<Info>\r\n");
+        ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
+        ptzXml.append("</Info>\r\n");
+        ptzXml.append("</Control>\r\n");
+
+
+        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request);
+
+    }
+
+    /**
+     * 前端控制指令(用于转发上级指令)
+     *
+     * @param device    控制设备
+     * @param channelId 预览通道
+     * @param cmdString 前端控制指令串
+     */
+    @Override
+    public void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer ptzXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        ptzXml.append("<Control>\r\n");
+        ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n");
+        ptzXml.append("<Info>\r\n");
+        ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
+        ptzXml.append("</Info>\r\n");
+        ptzXml.append("</Control>\r\n");
+
+
+        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+
+    }
+
+    /**
+     * 请求预览视频流
+     *
+     * @param device     视频设备
+     * @param channelId  预览通道
+     * @param event      hook订阅
+     * @param errorEvent sip错误订阅
+     */
+    @Override
+    public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
+                              ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+        String stream = ssrcInfo.getStream();
+
+        if (device == null) {
+            return;
+        }
+
+        logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
+        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
+            if (event != null) {
+                event.response(mediaServerItemInUse, json);
+                subscribe.removeSubscribe(hookSubscribe);
+            }
+        });
+        //
+        StringBuffer content = new StringBuffer(200);
+        content.append("v=0\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("s=Play\r\n");
+        content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("t=0 0\r\n");
+
+        if (userSetting.isSeniorSdp()) {
+            if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+            } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
+                content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
+            }
+            content.append("a=recvonly\r\n");
+            content.append("a=rtpmap:96 PS/90000\r\n");
+            content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
+            content.append("a=rtpmap:126 H264/90000\r\n");
+            content.append("a=rtpmap:125 H264S/90000\r\n");
+            content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
+            content.append("a=rtpmap:99 H265/90000\r\n");
+            content.append("a=rtpmap:98 H264/90000\r\n");
+            content.append("a=rtpmap:97 MPEG4/90000\r\n");
+            if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
+                content.append("a=setup:passive\r\n");
+                content.append("a=connection:new\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
+                content.append("a=setup:active\r\n");
+                content.append("a=connection:new\r\n");
+            }
+        } else {
+            if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
+            } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
+                content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
+            }
+            content.append("a=recvonly\r\n");
+            content.append("a=rtpmap:96 PS/90000\r\n");
+            content.append("a=rtpmap:98 H264/90000\r\n");
+            content.append("a=rtpmap:97 MPEG4/90000\r\n");
+            content.append("a=rtpmap:99 H265/90000\r\n");
+            if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
+                content.append("a=setup:passive\r\n");
+                content.append("a=connection:new\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
+                content.append("a=setup:active\r\n");
+                content.append("a=connection:new\r\n");
+            }
+        }
+
+        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
+        // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
 //			content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
 //			content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
 
 
-			CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(), callIdHeader);
-
-			transmitRequest(device, request, (e -> {
-				streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
-				mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
-				errorEvent.response(e);
-			}), e ->{
-				// 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
-				streamSession.put(device.getDeviceId(), channelId ,"play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
-				Dialog sipDialog = null;
-				if (e.dialog == null) {
-					SIPClientTransaction clientTransaction = (SIPClientTransaction)((ResponseEvent)e.event).getClientTransaction();
-					sipDialog = new SIPDialog(clientTransaction, clientTransaction.getLastResponse());
-				}else {
-					sipDialog = e.dialog;
-				}
-				streamSession.put(device.getDeviceId(), channelId ,"play", sipDialog);
-				okEvent.response(e);
-			});
-
-			
-		} catch ( SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-	
-	/**
-	 * 请求回放视频流
-	 * 
-	 * @param device  视频设备
-	 * @param channelId  预览通道
-	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
-	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
-	 */ 
-	@Override
-	public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-								  String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
-								  SipSubscribe.Event okEvent,SipSubscribe.Event errorEvent) {
-		try {
-
-			logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
-
-			StringBuffer content = new StringBuffer(200);
-	        content.append("v=0\r\n");
-	        content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
-	        content.append("s=Playback\r\n");
-	        content.append("u="+channelId+":0\r\n");
-	        content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
-	        content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
-					+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
-
-			String streamMode = device.getStreamMode();
-
-			if (userSetting.isSeniorSdp()) {
-				if("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}else if("UDP".equalsIgnoreCase(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}
-				content.append("a=recvonly\r\n");
-				content.append("a=rtpmap:96 PS/90000\r\n");
-				content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
-				content.append("a=rtpmap:126 H264/90000\r\n");
-				content.append("a=rtpmap:125 H264S/90000\r\n");
-				content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
-				content.append("a=rtpmap:99 H265/90000\r\n");
-				content.append("a=rtpmap:98 H264/90000\r\n");
-				content.append("a=rtpmap:97 MPEG4/90000\r\n");
-				if("TCP-PASSIVE".equalsIgnoreCase(streamMode)){ // tcp被动模式
-					content.append("a=setup:passive\r\n");
-					content.append("a=connection:new\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
-					content.append("a=setup:active\r\n");
-					content.append("a=connection:new\r\n");
-				}
-			}else {
-				if("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
-				}else if("UDP".equalsIgnoreCase(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
-				}
-				content.append("a=recvonly\r\n");
-				content.append("a=rtpmap:96 PS/90000\r\n");
-				content.append("a=rtpmap:97 MPEG4/90000\r\n");
-				content.append("a=rtpmap:98 H264/90000\r\n");
-				content.append("a=rtpmap:99 H265/90000\r\n");
-				if("TCP-PASSIVE".equalsIgnoreCase(streamMode)){ // tcp被动模式
-					content.append("a=setup:passive\r\n");
-					content.append("a=connection:new\r\n");
-				}else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
-					content.append("a=setup:active\r\n");
-					content.append("a=connection:new\r\n");
-				}
-			}
-
-	        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-			HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
-			// 添加订阅
-			subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
-						if (hookEvent != null) {
-							InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream());
-							hookEvent.call(inviteStreamInfo);
-						}
-						subscribe.removeSubscribe(hookSubscribe);
-					});
-	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader, ssrcInfo.getSsrc());
-
-	        transmitRequest(device, request, errorEvent, event -> {
-				ResponseEvent responseEvent = (ResponseEvent) event.event;
-	        	streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.playback);
-				streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), event.dialog);
-				okEvent.response(event);
-			});
-			if (inviteStreamCallback != null) {
-				inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
-			}
-		} catch ( SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * 请求历史媒体下载
-	 * 
-	 * @param device  视频设备
-	 * @param channelId  预览通道
-	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
-	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
-	 * @param downloadSpeed 下载倍速参数
-	 */ 
-	@Override
-	public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-								  String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
-								  SipSubscribe.Event errorEvent) {
-		try {
-			logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
-
-			StringBuffer content = new StringBuffer(200);
-	        content.append("v=0\r\n");
-	        content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
-	        content.append("s=Download\r\n");
-	        content.append("u="+channelId+":0\r\n");
-	        content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
-	        content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
-					+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
-
-			String streamMode = device.getStreamMode().toUpperCase();
-
-			if (userSetting.isSeniorSdp()) {
-				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
-				}
-				content.append("a=recvonly\r\n");
-				content.append("a=rtpmap:96 PS/90000\r\n");
-				content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
-				content.append("a=rtpmap:126 H264/90000\r\n");
-				content.append("a=rtpmap:125 H264S/90000\r\n");
-				content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
-				content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
-				content.append("a=fmtp:99 profile-level-id=3\r\n");
-				content.append("a=rtpmap:98 H264/90000\r\n");
-				content.append("a=rtpmap:97 MPEG4/90000\r\n");
-				if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
-					content.append("a=setup:passive\r\n");
-					content.append("a=connection:new\r\n");
-				}else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
-					content.append("a=setup:active\r\n");
-					content.append("a=connection:new\r\n");
-				}
-			}else {
-				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
-				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
-				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
-				}
-				content.append("a=recvonly\r\n");
-				content.append("a=rtpmap:96 PS/90000\r\n");
-				content.append("a=rtpmap:97 MPEG4/90000\r\n");
-				content.append("a=rtpmap:98 H264/90000\r\n");
-				content.append("a=rtpmap:99 H265/90000\r\n");
-				if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
-					content.append("a=setup:passive\r\n");
-					content.append("a=connection:new\r\n");
-				}else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
-					content.append("a=setup:active\r\n");
-					content.append("a=connection:new\r\n");
-				}
-			}
-			content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
-
-	        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
-			// 添加订阅
-			subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{
-						hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
-						subscribe.removeSubscribe(hookSubscribe);
-						hookSubscribe.getContent().put("regist", false);
-						hookSubscribe.getContent().put("schema", "rtsp");
-						// 添加流注销的订阅,注销了后向设备发送bye
-						subscribe.addSubscribe(hookSubscribe,
-								(MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{
-									ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
-									if (transaction != null) {
-										logger.info("[录像]下载结束, 发送BYE");
-										streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
-									}
-								});
-					});
-
-	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader, ssrcInfo.getSsrc());
-			if (inviteStreamCallback != null) {
-				inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
-			}
-	        transmitRequest(device, request, errorEvent, okEvent->{
-				ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
-				streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.download);
-				streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);
-			});
-
-
-		} catch ( SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * 视频流停止, 不使用回调
-	 */
-	@Override
-	public void streamByeCmd(String deviceId, String channelId, String stream, String callId) {
-		streamByeCmd(deviceId, channelId, stream, callId, null);
-	}
-
-	/**
-	 * 视频流停止
-	 */
-	@Override
-	public void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent) {
-		try {
-			SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, callId, stream);
-			ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId, stream, callId);
-
-			if (transaction == null ) {
-				logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
-				SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
-				if (okEvent != null) {
-					okEvent.response(eventResult);
-				}
-				return;
-			}
-			SIPDialog dialog;
-			if (callId != null) {
-				dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);
-			}else {
-				if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {
-					return;
-				}
-				dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
-			}
-			mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
-			mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
-			streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
-
-			if (dialog == null) {
-				logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
-				return;
-			}
-			SipStack sipStack = udpSipProvider.getSipStack();
-			SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
-			if (dialog != sipDialog) {
-				dialog = sipDialog;
-			}else {
-				dialog.setSipProvider(udpSipProvider);
-				try {
-					Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
-					sipStackField.setAccessible(true);
-					sipStackField.set(dialog, sipStack);
-					Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
-					eventListenersField.setAccessible(true);
-					eventListenersField.set(dialog, new HashSet<>());
-				} catch (NoSuchFieldException | IllegalAccessException e) {
-					e.printStackTrace();
-				}
-			}
-
-			Request byeRequest = dialog.createRequest(Request.BYE);
-			SipURI byeURI = (SipURI) byeRequest.getRequestURI();
-			SIPRequest request = (SIPRequest)transaction.getRequest();
-			byeURI.setHost(request.getRemoteAddress().getHostAddress());
-			byeURI.setPort(request.getRemotePort());
-			byeURI.setUser(channelId);
-			ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME);
-			String protocol = viaHeader.getTransport().toUpperCase();
-			viaHeader.setRPort();
-			// 增加Contact header
-			Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-			byeRequest.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
-			UserAgentHeader userAgentHeader = SipUtils.createUserAgentHeader(sipFactory, gitUtil);
-			byeRequest.addHeader(userAgentHeader);
-			ClientTransaction clientTransaction = null;
-			if("TCP".equals(protocol)) {
-				clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
-			} else if("UDP".equals(protocol)) {
-				clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
-			}
-
-			CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME);
-			if (okEvent != null) {
-				sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent);
-			}
-			CSeqHeader cSeqHeader = (CSeqHeader)byeRequest.getHeader(CSeqHeader.NAME);
-			cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ());
-			dialog.sendRequest(clientTransaction);
-
-		} catch (SipException | ParseException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	/**
-	 * 语音广播
-	 * 
-	 * @param device  视频设备
-	 * @param channelId  预览通道
-	 */
-	@Override
-	public boolean audioBroadcastCmd(Device device, String channelId) {
-		// 改为新的实现
-		return false;
-	}
-
-	/**
-	 * 语音广播
-	 * 
-	 * @param device  视频设备
-	 */
-	@Override
-	public boolean audioBroadcastCmd(Device device) {
-		try {
-			StringBuffer broadcastXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			broadcastXml.append("<Notify>\r\n");
-			broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
-			broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
-			broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
-			broadcastXml.append("</Notify>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-								
-			Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		} 
-		return false;
-	}
-	@Override
-	public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer broadcastXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			broadcastXml.append("<Notify>\r\n");
-			broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
-			broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
-			broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
-			broadcastXml.append("</Notify>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-								
-			Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		} 
-	} 
-	
-	
-	/**
-	 * 音视频录像控制
-	 * 
-	 * @param device		视频设备
-	 * @param channelId  	预览通道
-	 * @param recordCmdStr	录像命令:Record / StopRecord
-	 */  
-	@Override
-	public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 远程启动控制命令
-	 * 
-	 * @param device	视频设备
-	 */
-	@Override
-	public boolean teleBootCmd(Device device) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-	
-	/**
-	 * 报警布防/撤防命令
-	 * 
-	 * @param device  		视频设备
-	 * @param guardCmdStr	"SetGuard"/"ResetGuard"
-	 */
-	@Override
-	public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 报警复位命令
-	 * 
-	 * @param device  视频设备
-	 */  
-	@Override
-	public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
-			if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
-				cmdXml.append("<Info>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmMethod)) {
-				cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmType)) {
-				cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
-				cmdXml.append("</Info>\r\n");
-			}
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
-	 * 
-	 * @param device  视频设备
-	 * @param channelId  预览通道
-	 */ 
-	@Override
-	public boolean iFrameCmd(Device device, String channelId) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 看守位控制命令
-	 * 
-	 * @param device		视频设备
-	 * @param enabled		看守位使能:1 = 开启,0 = 关闭
-	 * @param resetTime		自动归位时间间隔,开启看守位时使用,单位:秒(s)
-	 * @param presetIndex	调用预置位编号,开启看守位时使用,取值范围0~255
-	 */  
-	@Override
-	public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			cmdXml.append("<HomePosition>\r\n");
-			if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
-				cmdXml.append("<Enabled>1</Enabled>\r\n");
-				if (NumericUtil.isInteger(resetTime)) {
-					cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
-				} else {
-					cmdXml.append("<ResetTime>0</ResetTime>\r\n");
-				}
-				if (NumericUtil.isInteger(presetIndex)) {
-					cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
-				} else {
-					cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
-				}
-			} else {
-				cmdXml.append("<Enabled>0</Enabled>\r\n");
-			}
-			cmdXml.append("</HomePosition>\r\n");
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 设备配置命令
-	 * 
-	 * @param device  视频设备
-	 */  
-	@Override
-	public boolean deviceConfigCmd(Device device) {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-	/**
-	 * 设备配置命令:basicParam
-	 * 
-	 * @param device  			视频设备
-	 * @param channelId			通道编码(可选)
-	 * @param name				设备/通道名称(可选)
-	 * @param expiration		注册过期时间(可选)
-	 * @param heartBeatInterval	心跳间隔时间(可选)
-	 * @param heartBeatCount	心跳超时次数(可选)
-	 */  
-	@Override
-	public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, 
-										String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Control>\r\n");
-			cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			cmdXml.append("<BasicParam>\r\n");
-			if (!ObjectUtils.isEmpty(name)) {
-				cmdXml.append("<Name>" + name + "</Name>\r\n");
-			}
-			if (NumericUtil.isInteger(expiration)) {
-				if (Integer.valueOf(expiration) > 0) {
-					cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
-				}
-			}
-			if (NumericUtil.isInteger(heartBeatInterval)) {
-				if (Integer.valueOf(heartBeatInterval) > 0) {
-					cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
-				}
-			}
-			if (NumericUtil.isInteger(heartBeatCount)) {
-				if (Integer.valueOf(heartBeatCount) > 0) {
-					cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
-				}
-			}
-			cmdXml.append("</BasicParam>\r\n");
-			cmdXml.append("</Control>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 查询设备状态
-	 * 
-	 * @param device 视频设备
-	 */  
-	@Override
-	public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) {
-		try {
-			String charset = device.getCharset();
-			StringBuffer catalogXml = new StringBuffer(200);
-			catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			catalogXml.append("<Query>\r\n");
-			catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
-			catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			catalogXml.append("</Query>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-
-			transmitRequest(device, request, errorEvent);
-			return true;
-			
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		}
-	}
-
-	/**
-	 * 查询设备信息
-	 * 
-	 * @param device 视频设备
-	 */  
-	@Override
-	public boolean deviceInfoQuery(Device device) {
-		try {
-			StringBuffer catalogXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			catalogXml.append("<Query>\r\n");
-			catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
-			catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			catalogXml.append("</Query>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-
-			transmitRequest(device, request);
-			
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		}
-		return true;
-	}
-
-	/**
-	 * 查询目录列表
-	 * 
-	 * @param device 视频设备
-	 */ 
-	@Override
-	public boolean catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer catalogXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			catalogXml.append("<Query>\r\n");
-			catalogXml.append("  <CmdType>Catalog</CmdType>\r\n");
-			catalogXml.append("  <SN>" + sn + "</SN>\r\n");
-			catalogXml.append("  <DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			catalogXml.append("</Query>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(),  SipUtils.getNewFromTag(), null, callIdHeader);
-
-			transmitRequest(device, request, errorEvent);
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		}
-		return true;
-	}
-
-	/**
-	 * 查询录像信息
-	 * 
-	 * @param device 视频设备
-	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
-	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
-	 */  
-	@Override
-	public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
-		if (secrecy == null) {
-			secrecy = 0;
-		}
-		if (type == null) {
-			type = "all";
-		}
-		try {
-			StringBuffer recordInfoXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			recordInfoXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			recordInfoXml.append("<Query>\r\n");
-			recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
-			recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
-			recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			if (startTime != null) {
-				recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
-			}
-			if (endTime != null) {
-				recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
-			}
-			if (secrecy != null) {
-				recordInfoXml.append("<Secrecy> "+ secrecy + " </Secrecy>\r\n");
-			}
-			if (type != null) {
-				// 大华NVR要求必须增加一个值为all的文本元素节点Type
-				recordInfoXml.append("<Type>" + type+"</Type>\r\n");
-			}
-			recordInfoXml.append("</Query>\r\n");
-			
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
-					SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-
-			transmitRequest(device, request, errorEvent, okEvent);
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		}
-		return true;
-	}
-
-	/**
-	 * 查询报警信息
-	 * 
-	 * @param device		视频设备
-	 * @param startPriority	报警起始级别(可选)
-	 * @param endPriority	报警终止级别(可选)
-	 * @param alarmMethod	报警方式条件(可选)
-	 * @param alarmType		报警类型
-	 * @param startTime		报警发生起始时间(可选)
-	 * @param endTime		报警发生终止时间(可选)
-	 * @return				true = 命令发送成功
-	 */
-	@Override
-	public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
-								 String startTime, String endTime, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Query>\r\n");
-			cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			if (!ObjectUtils.isEmpty(startPriority)) {
-				cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(endPriority)) {
-				cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmMethod)) {
-				cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmType)) {
-				cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(startTime)) {
-				cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(endTime)) {
-				cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
-			}
-			cmdXml.append("</Query>\r\n");
-			
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 查询设备配置
-	 * 
-	 * @param device 		视频设备
-	 * @param channelId		通道编码(可选)
-	 * @param configType	配置类型:
-	 */
-	@Override
-	public boolean deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Query>\r\n");
-			cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
-			cmdXml.append("</Query>\r\n");
-			
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 查询设备预置位置
-	 * 
-	 * @param device 视频设备
-	 */  
-	@Override
-	public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Query>\r\n");
-			cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			cmdXml.append("</Query>\r\n");
-			
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request, errorEvent);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		} 
-	}
-
-	/**
-	 * 查询移动设备位置数据
-	 * 
-	 * @param device 视频设备
-	 */  
-	@Override
-	public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer mobilePostitionXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			mobilePostitionXml.append("<Query>\r\n");
-			mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
-			mobilePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			mobilePostitionXml.append("<Interval>60</Interval>\r\n");
-			mobilePostitionXml.append("</Query>\r\n");
-			
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-
-			transmitRequest(device, request, errorEvent);
-			
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-			return false;
-		}
-		return true;
-	}
-
-	/**
-	 * 订阅、取消订阅移动位置
-	 * 
-	 * @param device	视频设备
-	 * @return			true = 命令发送成功
-	 */
-	@Override
-	public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer subscribePostitionXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			subscribePostitionXml.append("<Query>\r\n");
-			subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
-			subscribePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			if (device.getSubscribeCycleForMobilePosition() > 0) {
-				subscribePostitionXml.append("<Interval>" + device.getMobilePositionSubmissionInterval() + "</Interval>\r\n");
-			}
-			subscribePostitionXml.append("</Query>\r\n");
-
-			CallIdHeader callIdHeader;
-
-			if (requestOld != null) {
-				callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
-			}else {
-				callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-						: udpSipProvider.getNewCallId();
-			}
-			SIPRequest request = (SIPRequest)headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence" ,callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
-
-			transmitRequest(device, request, errorEvent, okEvent);
-
-			return request;
-
-		} catch ( NumberFormatException | ParseException | InvalidArgumentException	| SipException e) {
-			e.printStackTrace();
-			return null;
-		}
-	}
-
-	/**
-	 * 订阅、取消订阅报警信息
-	 * 
-	 * @param device		视频设备
-	 * @param expires		订阅过期时间(0 = 取消订阅)
-	 * @param startPriority	报警起始级别(可选)
-	 * @param endPriority	报警终止级别(可选)
-	 * @param alarmMethod	报警方式条件(可选)
-	 * @param alarmType		报警类型
-	 * @param startTime		报警发生起始时间(可选)
-	 * @param endTime		报警发生终止时间(可选)
-	 * @return				true = 命令发送成功
-	 */
-	@Override
-	public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Query>\r\n");
-			cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			if (!ObjectUtils.isEmpty(startPriority)) {
-				cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(endPriority)) {
-				cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmMethod)) {
-				cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(alarmType)) {
-				cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(startTime)) {
-				cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
-			}
-			if (!ObjectUtils.isEmpty(endTime)) {
-				cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
-			}
-			cmdXml.append("</Query>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-
-			Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence" , callIdHeader);
-			transmitRequest(device, request);
-
-			return true;
-
-		} catch ( NumberFormatException | ParseException | InvalidArgumentException	| SipException e) {
-			e.printStackTrace();
-			return false;
-		}
-	}
-
-	@Override
-	public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
-		try {
-			StringBuffer cmdXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			cmdXml.append("<Query>\r\n");
-			cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
-			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			cmdXml.append("</Query>\r\n");
-
-			CallIdHeader callIdHeader ;
-
-			if (requestOld != null) {
-				callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
-			}else {
-				callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-						: udpSipProvider.getNewCallId();
-			}
-
-			// 有效时间默认为60秒以上
-			SIPRequest request = (SIPRequest)headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld,  device.getSubscribeCycleForCatalog(), "Catalog" ,
-					callIdHeader);
-			transmitRequest(device, request, errorEvent, okEvent);
-			return request;
-
-		} catch ( NumberFormatException | ParseException | InvalidArgumentException	| SipException e) {
-			e.printStackTrace();
-			return null;
-		}
-	}
-
-	@Override
-	public boolean dragZoomCmd(Device device, String channelId, String cmdString) {
-		try {
-			StringBuffer dragXml = new StringBuffer(200);
-			String charset = device.getCharset();
-			dragXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
-			dragXml.append("<Control>\r\n");
-			dragXml.append("<CmdType>DeviceControl</CmdType>\r\n");
-			dragXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
-			if (ObjectUtils.isEmpty(channelId)) {
-				dragXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-			} else {
-				dragXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-			}
-			dragXml.append(cmdString);
-			dragXml.append("</Control>\r\n");
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-			Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			logger.debug("拉框信令: " + request.toString());
-			transmitRequest(device, request);
-			return true;
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-		return false;
-	}
-
-
-	private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
-		return transmitRequest(device, request, null, null);
-	}
-
-	private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException {
-		return transmitRequest(device, request, errorEvent, null);
-	}
-
-	private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException {
-		ClientTransaction clientTransaction = null;
-		if("TCP".equals(device.getTransport())) {
-			clientTransaction = tcpSipProvider.getNewClientTransaction(request);
-		} else if("UDP".equals(device.getTransport())) {
-			clientTransaction = udpSipProvider.getNewClientTransaction(request);
-		}
-		if (request.getHeader(UserAgentHeader.NAME) == null) {
-			try {
-				request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
-			} catch (ParseException e) {
-				logger.error("添加UserAgentHeader失败", e);
-			}
-		}
-		CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
-		// 添加错误订阅
-		if (errorEvent != null) {
-			sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
-				errorEvent.response(eventResult);
-				sipSubscribe.removeErrorSubscribe(eventResult.callId);
-				sipSubscribe.removeOkSubscribe(eventResult.callId);
-			}));
-		}
-		// 添加订阅
-		if (okEvent != null) {
-			sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{
-				okEvent.response(eventResult);
-				sipSubscribe.removeOkSubscribe(eventResult.callId);
-				sipSubscribe.removeErrorSubscribe(eventResult.callId);
-			});
-		}
-
-		clientTransaction.sendRequest();
-		return clientTransaction;
-	}
-
-	/**
-	 * 回放暂停
-	 */
-	@Override
-	public void playPauseCmd(Device device, StreamInfo streamInfo) {
-		try {
-			StringBuffer content = new StringBuffer(200);
-			content.append("PAUSE RTSP/1.0\r\n");
-			content.append("CSeq: " + getInfoCseq() + "\r\n");
-			content.append("PauseTime: now\r\n");
-			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
-			if (request == null) {
-				return;
-			}
-			logger.info(request.toString());
-			ClientTransaction clientTransaction = null;
-			if ("TCP".equals(device.getTransport())) {
-				clientTransaction = tcpSipProvider.getNewClientTransaction(request);
-			} else if ("UDP".equals(device.getTransport())) {
-				clientTransaction = udpSipProvider.getNewClientTransaction(request);
-			}
-			if (clientTransaction != null) {
-				clientTransaction.sendRequest();
-			}
-
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * 回放恢复
-	 */
-	@Override
-	public void playResumeCmd(Device device, StreamInfo streamInfo) {
-		try {
-			StringBuffer content = new StringBuffer(200);
-			content.append("PLAY RTSP/1.0\r\n");
-			content.append("CSeq: " + getInfoCseq() + "\r\n");
-			content.append("Range: npt=now-\r\n");
-			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
-			if (request == null) {
-				return;
-			}
-			logger.info(request.toString());
-			ClientTransaction clientTransaction = null;
-			if ("TCP".equals(device.getTransport())) {
-				clientTransaction = tcpSipProvider.getNewClientTransaction(request);
-			} else if ("UDP".equals(device.getTransport())) {
-				clientTransaction = udpSipProvider.getNewClientTransaction(request);
-			}
-
-			clientTransaction.sendRequest();
-
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * 回放拖动播放
-	 */
-	@Override
-	public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) {
-		try {
-			StringBuffer content = new StringBuffer(200);
-			content.append("PLAY RTSP/1.0\r\n");
-			content.append("CSeq: " + getInfoCseq() + "\r\n");
-			content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
-
-			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
-			if (request == null) {
-				return;
-			}
-			logger.info(request.toString());
-			ClientTransaction clientTransaction = null;
-			if ("TCP".equals(device.getTransport())) {
-				clientTransaction = tcpSipProvider.getNewClientTransaction(request);
-			} else if ("UDP".equals(device.getTransport())) {
-				clientTransaction = udpSipProvider.getNewClientTransaction(request);
-			}
-
-			clientTransaction.sendRequest();
-
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	/**
-	 * 回放倍速播放
-	 */
-	@Override
-	public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) {
-		try {
-
-			StringBuffer content = new StringBuffer(200);
-			content.append("PLAY RTSP/1.0\r\n");
-			content.append("CSeq: " + getInfoCseq() + "\r\n");
-			content.append("Scale: " + String.format("%.6f",speed) + "\r\n");
-			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
-			if (request == null) {
-				return;
-			}
-			logger.info(request.toString());
-			ClientTransaction clientTransaction = null;
-			if ("TCP".equals(device.getTransport())) {
-				clientTransaction = tcpSipProvider.getNewClientTransaction(request);
-			} else if ("UDP".equals(device.getTransport())) {
-				clientTransaction = udpSipProvider.getNewClientTransaction(request);
-			}
-
-			clientTransaction.sendRequest();
-
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	private int getInfoCseq() {
-		return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8));
-	}
-	
-	@Override
-	public void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
-		try {
-			Request request = headerProvider.createInfoRequest(device, streamInfo, content);
-			if (request == null) {
-				return;
-			}
-			ClientTransaction clientTransaction = null;
-			if ("TCP".equals(device.getTransport())) {
-				clientTransaction = tcpSipProvider.getNewClientTransaction(request);
-			} else if ("UDP".equals(device.getTransport())) {
-				clientTransaction = udpSipProvider.getNewClientTransaction(request);
-			}
-			CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
-			if(errorEvent != null) {
-				sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
-					errorEvent.response(eventResult);
-					sipSubscribe.removeErrorSubscribe(eventResult.callId);
-					sipSubscribe.removeOkSubscribe(eventResult.callId);
-				}));
-			}
-			
-			if(okEvent != null) {
-				sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
-					okEvent.response(eventResult);
-					sipSubscribe.removeOkSubscribe(eventResult.callId);
-					sipSubscribe.removeErrorSubscribe(eventResult.callId);
-				});
-			}
-			clientTransaction.sendRequest();
-			
-		} catch (SipException | ParseException | InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-	}
-
-	@Override
-	public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
-		if (device == null) {
-			return false;
-		}
-		logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
-				deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
-		try {
-			String characterSet = device.getCharset();
-			StringBuffer deviceStatusXml = new StringBuffer(600);
-			deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-			deviceStatusXml.append("<Notify>\r\n");
-			deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
-			deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-			deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
-			deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
-			deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
-			deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
-			deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
-			deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
-			deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
-			deviceStatusXml.append("<info>\r\n");
-			deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
-			deviceStatusXml.append("</info>\r\n");
-			deviceStatusXml.append("</Notify>\r\n");
-
-			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-					: udpSipProvider.getNewCallId();
-			Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-			transmitRequest(device, request);
-
-
-		} catch (SipException | ParseException  e) {
-			e.printStackTrace();
-			return false;
-		} catch (InvalidArgumentException e) {
-			throw new RuntimeException(e);
-		}
-		return true;
-	}
+        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(), callIdHeader);
+        transmitRequest(device.getTransport(), request, (e -> {
+            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
+            mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+            errorEvent.response(e);
+        }), e -> {
+            // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
+            ResponseEvent responseEvent = (ResponseEvent) e.event;
+            SIPResponse response = (SIPResponse) responseEvent.getResponse();
+            streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play);
+            okEvent.response(e);
+        });
+    }
+
+    /**
+     * 请求回放视频流
+     *
+     * @param device    视频设备
+     * @param channelId 预览通道
+     * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
+     * @param endTime   结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+     */
+    @Override
+    public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
+                                  String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
+                                  SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+
+        logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+
+        StringBuffer content = new StringBuffer(200);
+        content.append("v=0\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("s=Playback\r\n");
+        content.append("u=" + channelId + ":0\r\n");
+        content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
+                + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
+
+        String streamMode = device.getStreamMode();
+
+        if (userSetting.isSeniorSdp()) {
+            if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+            } else if ("UDP".equalsIgnoreCase(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
+            }
+            content.append("a=recvonly\r\n");
+            content.append("a=rtpmap:96 PS/90000\r\n");
+            content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
+            content.append("a=rtpmap:126 H264/90000\r\n");
+            content.append("a=rtpmap:125 H264S/90000\r\n");
+            content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
+            content.append("a=rtpmap:99 H265/90000\r\n");
+            content.append("a=rtpmap:98 H264/90000\r\n");
+            content.append("a=rtpmap:97 MPEG4/90000\r\n");
+            if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式
+                content.append("a=setup:passive\r\n");
+                content.append("a=connection:new\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
+                content.append("a=setup:active\r\n");
+                content.append("a=connection:new\r\n");
+            }
+        } else {
+            if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
+            } else if ("UDP".equalsIgnoreCase(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
+            }
+            content.append("a=recvonly\r\n");
+            content.append("a=rtpmap:96 PS/90000\r\n");
+            content.append("a=rtpmap:97 MPEG4/90000\r\n");
+            content.append("a=rtpmap:98 H264/90000\r\n");
+            content.append("a=rtpmap:99 H265/90000\r\n");
+            if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式
+                content.append("a=setup:passive\r\n");
+                content.append("a=connection:new\r\n");
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
+                content.append("a=setup:active\r\n");
+                content.append("a=connection:new\r\n");
+            }
+        }
+
+        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+        // 添加订阅
+        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
+            if (hookEvent != null) {
+                InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream());
+                hookEvent.call(inviteStreamInfo);
+            }
+            subscribe.removeSubscribe(hookSubscribe);
+        });
+        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader, ssrcInfo.getSsrc());
+
+        transmitRequest(device.getTransport(), request, errorEvent, event -> {
+            ResponseEvent responseEvent = (ResponseEvent) event.event;
+            SIPResponse response = (SIPResponse) responseEvent.getResponse();
+            streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.playback);
+            okEvent.response(event);
+        });
+        if (inviteStreamCallback != null) {
+            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
+        }
+    }
+
+    /**
+     * 请求历史媒体下载
+     *
+     * @param device        视频设备
+     * @param channelId     预览通道
+     * @param startTime     开始时间,格式要求:yyyy-MM-dd HH:mm:ss
+     * @param endTime       结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+     * @param downloadSpeed 下载倍速参数
+     */
+    @Override
+    public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
+                                  String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
+                                  SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+
+        StringBuffer content = new StringBuffer(200);
+        content.append("v=0\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("s=Download\r\n");
+        content.append("u=" + channelId + ":0\r\n");
+        content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
+                + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
+
+        String streamMode = device.getStreamMode().toUpperCase();
+
+        if (userSetting.isSeniorSdp()) {
+            if ("TCP-PASSIVE".equals(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+            } else if ("TCP-ACTIVE".equals(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+            } else if ("UDP".equals(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
+            }
+            content.append("a=recvonly\r\n");
+            content.append("a=rtpmap:96 PS/90000\r\n");
+            content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
+            content.append("a=rtpmap:126 H264/90000\r\n");
+            content.append("a=rtpmap:125 H264S/90000\r\n");
+            content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
+            content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
+            content.append("a=fmtp:99 profile-level-id=3\r\n");
+            content.append("a=rtpmap:98 H264/90000\r\n");
+            content.append("a=rtpmap:97 MPEG4/90000\r\n");
+            if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
+                content.append("a=setup:passive\r\n");
+                content.append("a=connection:new\r\n");
+            } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
+                content.append("a=setup:active\r\n");
+                content.append("a=connection:new\r\n");
+            }
+        } else {
+            if ("TCP-PASSIVE".equals(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
+            } else if ("TCP-ACTIVE".equals(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
+            } else if ("UDP".equals(streamMode)) {
+                content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
+            }
+            content.append("a=recvonly\r\n");
+            content.append("a=rtpmap:96 PS/90000\r\n");
+            content.append("a=rtpmap:97 MPEG4/90000\r\n");
+            content.append("a=rtpmap:98 H264/90000\r\n");
+            content.append("a=rtpmap:99 H265/90000\r\n");
+            if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
+                content.append("a=setup:passive\r\n");
+                content.append("a=connection:new\r\n");
+            } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
+                content.append("a=setup:active\r\n");
+                content.append("a=connection:new\r\n");
+            }
+        }
+        content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
+
+        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
+        // 添加订阅
+        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
+            hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
+            subscribe.removeSubscribe(hookSubscribe);
+            hookSubscribe.getContent().put("regist", false);
+            hookSubscribe.getContent().put("schema", "rtsp");
+            // 添加流注销的订阅,注销了后向设备发送bye
+            subscribe.addSubscribe(hookSubscribe,
+                    (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
+                        logger.info("[录像]下载结束, 发送BYE");
+                        try {
+                            streamByeCmd(device, channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
+                        } catch (InvalidArgumentException | ParseException | SipException |
+                                 SsrcTransactionNotFoundException e) {
+                            logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
+                        }
+                    });
+        });
+
+        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader, ssrcInfo.getSsrc());
+        if (inviteStreamCallback != null) {
+            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
+        }
+        transmitRequest(device.getTransport(), request, errorEvent, okEvent -> {
+            ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
+            SIPResponse response = (SIPResponse) responseEvent.getResponse();
+            streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
+        });
+    }
+
+    /**
+     * 视频流停止, 不使用回调
+     */
+    @Override
+    public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException {
+        streamByeCmd(device, channelId, stream, callId, null);
+    }
+
+    /**
+     * 视频流停止
+     */
+    @Override
+    public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
+        SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callId, stream);
+        if (ssrcTransaction == null) {
+            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());
+
+        Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
+        transmitRequest(device.getTransport(), byteRequest, null, okEvent);
+    }
+
+    /**
+     * 语音广播
+     *
+     * @param device    视频设备
+     * @param channelId 预览通道
+     */
+    @Override
+    public void audioBroadcastCmd(Device device, String channelId) {
+    }
+
+    /**
+     * 语音广播
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer broadcastXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        broadcastXml.append("<Notify>\r\n");
+        broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
+        broadcastXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
+        broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
+        broadcastXml.append("</Notify>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request);
+
+    }
+
+    @Override
+    public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer broadcastXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        broadcastXml.append("<Notify>\r\n");
+        broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
+        broadcastXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
+        broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
+        broadcastXml.append("</Notify>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+
+    }
+
+
+    /**
+     * 音视频录像控制
+     *
+     * @param device       视频设备
+     * @param channelId    预览通道
+     * @param recordCmdStr 录像命令:Record / StopRecord
+     */
+    @Override
+    public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 远程启动控制命令
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request);
+    }
+
+    /**
+     * 报警布防/撤防命令
+     *
+     * @param device      视频设备
+     * @param guardCmdStr "SetGuard"/"ResetGuard"
+     */
+    @Override
+    public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 报警复位命令
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
+        if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
+            cmdXml.append("<Info>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmMethod)) {
+            cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmType)) {
+            cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
+            cmdXml.append("</Info>\r\n");
+        }
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
+     *
+     * @param device    视频设备
+     * @param channelId 预览通道
+     */
+    @Override
+    public void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request);
+    }
+
+    /**
+     * 看守位控制命令
+     *
+     * @param device      视频设备
+     * @param enabled     看守位使能:1 = 开启,0 = 关闭
+     * @param resetTime   自动归位时间间隔,开启看守位时使用,单位:秒(s)
+     * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
+     */
+    @Override
+    public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        cmdXml.append("<HomePosition>\r\n");
+        if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
+            cmdXml.append("<Enabled>1</Enabled>\r\n");
+            if (NumericUtil.isInteger(resetTime)) {
+                cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
+            } else {
+                cmdXml.append("<ResetTime>0</ResetTime>\r\n");
+            }
+            if (NumericUtil.isInteger(presetIndex)) {
+                cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
+            } else {
+                cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
+            }
+        } else {
+            cmdXml.append("<Enabled>0</Enabled>\r\n");
+        }
+        cmdXml.append("</HomePosition>\r\n");
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 设备配置命令
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void deviceConfigCmd(Device device) {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * 设备配置命令:basicParam
+     *
+     * @param device            视频设备
+     * @param channelId         通道编码(可选)
+     * @param name              设备/通道名称(可选)
+     * @param expiration        注册过期时间(可选)
+     * @param heartBeatInterval 心跳间隔时间(可选)
+     * @param heartBeatCount    心跳超时次数(可选)
+     */
+    @Override
+    public void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration,
+                                     String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Control>\r\n");
+        cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        cmdXml.append("<BasicParam>\r\n");
+        if (!ObjectUtils.isEmpty(name)) {
+            cmdXml.append("<Name>" + name + "</Name>\r\n");
+        }
+        if (NumericUtil.isInteger(expiration)) {
+            if (Integer.valueOf(expiration) > 0) {
+                cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
+            }
+        }
+        if (NumericUtil.isInteger(heartBeatInterval)) {
+            if (Integer.valueOf(heartBeatInterval) > 0) {
+                cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
+            }
+        }
+        if (NumericUtil.isInteger(heartBeatCount)) {
+            if (Integer.valueOf(heartBeatCount) > 0) {
+                cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
+            }
+        }
+        cmdXml.append("</BasicParam>\r\n");
+        cmdXml.append("</Control>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 查询设备状态
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        String charset = device.getCharset();
+        StringBuffer catalogXml = new StringBuffer(200);
+        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        catalogXml.append("<Query>\r\n");
+        catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
+        catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        catalogXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 查询设备信息
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer catalogXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        catalogXml.append("<Query>\r\n");
+        catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
+        catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        catalogXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+
+        transmitRequest(device.getTransport(), request);
+
+    }
+
+    /**
+     * 查询目录列表
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException {
+
+        StringBuffer catalogXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        catalogXml.append("<Query>\r\n");
+        catalogXml.append("  <CmdType>Catalog</CmdType>\r\n");
+        catalogXml.append("  <SN>" + sn + "</SN>\r\n");
+        catalogXml.append("  <DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        catalogXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 查询录像信息
+     *
+     * @param device    视频设备
+     * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
+     * @param endTime   结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+     */
+    @Override
+    public void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+        if (secrecy == null) {
+            secrecy = 0;
+        }
+        if (type == null) {
+            type = "all";
+        }
+
+        StringBuffer recordInfoXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        recordInfoXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        recordInfoXml.append("<Query>\r\n");
+        recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
+        recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
+        recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        if (startTime != null) {
+            recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
+        }
+        if (endTime != null) {
+            recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
+        }
+        if (secrecy != null) {
+            recordInfoXml.append("<Secrecy> " + secrecy + " </Secrecy>\r\n");
+        }
+        if (type != null) {
+            // 大华NVR要求必须增加一个值为all的文本元素节点Type
+            recordInfoXml.append("<Type>" + type + "</Type>\r\n");
+        }
+        recordInfoXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
+                SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+
+        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+    }
+
+    /**
+     * 查询报警信息
+     *
+     * @param device        视频设备
+     * @param startPriority 报警起始级别(可选)
+     * @param endPriority   报警终止级别(可选)
+     * @param alarmMethod   报警方式条件(可选)
+     * @param alarmType     报警类型
+     * @param startTime     报警发生起始时间(可选)
+     * @param endTime       报警发生终止时间(可选)
+     * @return true = 命令发送成功
+     */
+    @Override
+    public void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
+                               String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Query>\r\n");
+        cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        if (!ObjectUtils.isEmpty(startPriority)) {
+            cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(endPriority)) {
+            cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmMethod)) {
+            cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmType)) {
+            cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(startTime)) {
+            cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(endTime)) {
+            cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
+        }
+        cmdXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 查询设备配置
+     *
+     * @param device     视频设备
+     * @param channelId  通道编码(可选)
+     * @param configType 配置类型:
+     */
+    @Override
+    public void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Query>\r\n");
+        cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
+        cmdXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 查询设备预置位置
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Query>\r\n");
+        cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        cmdXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent);
+    }
+
+    /**
+     * 查询移动设备位置数据
+     *
+     * @param device 视频设备
+     */
+    @Override
+    public void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer mobilePostitionXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        mobilePostitionXml.append("<Query>\r\n");
+        mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
+        mobilePostitionXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        mobilePostitionXml.append("<Interval>60</Interval>\r\n");
+        mobilePostitionXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+
+        transmitRequest(device.getTransport(), request, errorEvent);
+
+    }
+
+    /**
+     * 订阅、取消订阅移动位置
+     *
+     * @param device 视频设备
+     * @return true = 命令发送成功
+     */
+    @Override
+    public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer subscribePostitionXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        subscribePostitionXml.append("<Query>\r\n");
+        subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
+        subscribePostitionXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        if (device.getSubscribeCycleForMobilePosition() > 0) {
+            subscribePostitionXml.append("<Interval>" + device.getMobilePositionSubmissionInterval() + "</Interval>\r\n");
+        }
+        subscribePostitionXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader;
+
+        if (requestOld != null) {
+            callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
+        } else {
+            callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                    : udpSipProvider.getNewCallId();
+        }
+        SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence", callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
+
+        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        return request;
+    }
+
+    /**
+     * 订阅、取消订阅报警信息
+     *
+     * @param device        视频设备
+     * @param expires       订阅过期时间(0 = 取消订阅)
+     * @param startPriority 报警起始级别(可选)
+     * @param endPriority   报警终止级别(可选)
+     * @param alarmMethod   报警方式条件(可选)
+     * @param alarmType     报警类型
+     * @param startTime     报警发生起始时间(可选)
+     * @param endTime       报警发生终止时间(可选)
+     * @return true = 命令发送成功
+     */
+    @Override
+    public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Query>\r\n");
+        cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        if (!ObjectUtils.isEmpty(startPriority)) {
+            cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(endPriority)) {
+            cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmMethod)) {
+            cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(alarmType)) {
+            cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(startTime)) {
+            cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
+        }
+        if (!ObjectUtils.isEmpty(endTime)) {
+            cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
+        }
+        cmdXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence", callIdHeader);
+        transmitRequest(device.getTransport(), request);
+
+    }
+
+    @Override
+    public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer cmdXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        cmdXml.append("<Query>\r\n");
+        cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
+        cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        cmdXml.append("</Query>\r\n");
+
+        CallIdHeader callIdHeader;
+
+        if (requestOld != null) {
+            callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
+        } else {
+            callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                    : udpSipProvider.getNewCallId();
+        }
+
+        // 有效时间默认为60秒以上
+        SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog",
+                callIdHeader);
+        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        return request;
+    }
+
+    @Override
+    public void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException {
+
+        StringBuffer dragXml = new StringBuffer(200);
+        String charset = device.getCharset();
+        dragXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
+        dragXml.append("<Control>\r\n");
+        dragXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+        dragXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        if (ObjectUtils.isEmpty(channelId)) {
+            dragXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        } else {
+            dragXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+        }
+        dragXml.append(cmdString);
+        dragXml.append("</Control>\r\n");
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+        Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        logger.debug("拉框信令: " + request.toString());
+        transmitRequest(device.getTransport(), request);
+    }
+
+
+    @Override
+    public void transmitRequest(String transport, Request request) throws SipException, ParseException {
+        transmitRequest(transport, request, null, null);
+    }
+
+    @Override
+    public void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent) throws SipException, ParseException {
+        transmitRequest(transport, request, errorEvent, null);
+    }
+
+    @Override
+    public void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
+
+        if (request.getHeader(UserAgentHeader.NAME) == null) {
+            try {
+                request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+            } catch (ParseException e) {
+                logger.error("添加UserAgentHeader失败", e);
+            }
+        }
+
+        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
+        // 添加错误订阅
+        if (errorEvent != null) {
+            sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
+                errorEvent.response(eventResult);
+                sipSubscribe.removeErrorSubscribe(eventResult.callId);
+                sipSubscribe.removeOkSubscribe(eventResult.callId);
+            }));
+        }
+        // 添加订阅
+        if (okEvent != null) {
+            sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
+                okEvent.response(eventResult);
+                sipSubscribe.removeOkSubscribe(eventResult.callId);
+                sipSubscribe.removeErrorSubscribe(eventResult.callId);
+            });
+        }
+        if ("TCP".equals(transport)) {
+            tcpSipProvider.sendRequest(request);
+        } else if ("UDP".equals(transport)) {
+            udpSipProvider.sendRequest(request);
+        }
+
+    }
+
+
+    /**
+     * 回放暂停
+     */
+    @Override
+    public void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException {
+        StringBuffer content = new StringBuffer(200);
+        content.append("PAUSE RTSP/1.0\r\n");
+        content.append("CSeq: " + getInfoCseq() + "\r\n");
+        content.append("PauseTime: now\r\n");
+
+        playbackControlCmd(device, streamInfo, content.toString(), null, null);
+    }
+
+
+    /**
+     * 回放恢复
+     */
+    @Override
+    public void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException {
+        StringBuffer content = new StringBuffer(200);
+        content.append("PLAY RTSP/1.0\r\n");
+        content.append("CSeq: " + getInfoCseq() + "\r\n");
+        content.append("Range: npt=now-\r\n");
+
+        playbackControlCmd(device, streamInfo, content.toString(), null, null);
+    }
+
+    /**
+     * 回放拖动播放
+     */
+    @Override
+    public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException {
+        StringBuffer content = new StringBuffer(200);
+        content.append("PLAY RTSP/1.0\r\n");
+        content.append("CSeq: " + getInfoCseq() + "\r\n");
+        content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
+
+        playbackControlCmd(device, streamInfo, content.toString(), null, null);
+    }
+
+    /**
+     * 回放倍速播放
+     */
+    @Override
+    public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException {
+        StringBuffer content = new StringBuffer(200);
+        content.append("PLAY RTSP/1.0\r\n");
+        content.append("CSeq: " + getInfoCseq() + "\r\n");
+        content.append("Scale: " + String.format("%.6f", speed) + "\r\n");
+
+        playbackControlCmd(device, streamInfo, content.toString(), null, null);
+    }
+
+    private int getInfoCseq() {
+        return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8));
+    }
+
+    @Override
+    public void playbackControlCmd(Device device, StreamInfo streamInfo, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
+
+        SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), streamInfo.getChannelId(), null, streamInfo.getStream());
+        if (ssrcTransaction == null) {
+            logger.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream());
+            return;
+        }
+
+        SIPRequest request = headerProvider.createInfoRequest(device, streamInfo.getChannelId(), content.toString(), ssrcTransaction.getSipTransactionInfo());
+        if (request == null) {
+            logger.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream());
+            return;
+        }
+
+        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+    }
+
+    @Override
+    public void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException {
+        if (device == null) {
+            return;
+        }
+        logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
+                deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
+
+        String characterSet = device.getCharset();
+        StringBuffer deviceStatusXml = new StringBuffer(600);
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        deviceStatusXml.append("<Notify>\r\n");
+        deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
+        deviceStatusXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
+        deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
+        deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
+        deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
+        deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
+        deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
+        deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
+        deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
+        deviceStatusXml.append("<info>\r\n");
+        deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
+        deviceStatusXml.append("</info>\r\n");
+        deviceStatusXml.append("</Notify>\r\n");
+
+        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+        Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        transmitRequest(device.getTransport(), request);
+
+
+    }
 }
 }

+ 226 - 390
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java

@@ -13,12 +13,13 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.SerializeUtils;
 import com.genersoft.iot.vmp.utils.SerializeUtils;
+import gov.nist.javax.sip.SIPConstants;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
-import gov.nist.javax.sip.stack.SIPDialog;
 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;
@@ -26,17 +27,15 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.lang.Nullable;
 import org.springframework.lang.Nullable;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 
 
 import javax.sip.*;
 import javax.sip.*;
-import javax.sip.address.SipURI;
 import javax.sip.header.*;
 import javax.sip.header.*;
 import javax.sip.message.Request;
 import javax.sip.message.Request;
-import java.lang.reflect.Field;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.List;
 
 
 @Component
 @Component
@@ -77,19 +76,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     private SubscribeHolder subscribeHolder;
     private SubscribeHolder subscribeHolder;
 
 
     @Override
     @Override
-    public boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
-        return register(parentPlatform, null, null, errorEvent, okEvent, false, true);
+    public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
+        register(parentPlatform, null, null, errorEvent, okEvent, false, true);
     }
     }
 
 
     @Override
     @Override
-    public boolean unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
-        return register(parentPlatform, null, null, errorEvent, okEvent, false, false);
+    public void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
+        register(parentPlatform, null, null, errorEvent, okEvent, false, false);
     }
     }
 
 
     @Override
     @Override
-    public boolean register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
-                            SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) {
-        try {
+    public void register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
+                            SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
             Request request;
             Request request;
             if (!registerAgain ) {
             if (!registerAgain ) {
                 CallIdHeader callIdHeader = null;
                 CallIdHeader callIdHeader = null;
@@ -126,23 +124,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
             }
             }
 
 
             transmitRequest(parentPlatform, request, null, okEvent);
             transmitRequest(parentPlatform, request, null, okEvent);
-            return true;
-        } catch (ParseException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        } catch (PeerUnavailableException e) {
-            e.printStackTrace();
-        } catch (SipException e) {
-            e.printStackTrace();
-        }
-        return false;
     }
     }
 
 
     @Override
     @Override
-    public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
-        String callId = null;
-        try {
+    public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
             String characterSet = parentPlatform.getCharacterSet();
             String characterSet = parentPlatform.getCharacterSet();
             StringBuffer keepaliveXml = new StringBuffer(200);
             StringBuffer keepaliveXml = new StringBuffer(200);
             keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
             keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
@@ -163,11 +148,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                     SipUtils.getNewViaTag(),
                     SipUtils.getNewViaTag(),
                     callIdHeader);
                     callIdHeader);
             transmitRequest(parentPlatform, request, errorEvent, okEvent);
             transmitRequest(parentPlatform, request, errorEvent, okEvent);
-            callId = callIdHeader.getCallId();
-        } catch (ParseException | InvalidArgumentException | SipException e) {
-            e.printStackTrace();
-        }
-        return callId;
+        return callIdHeader.getCallId();
     }
     }
 
 
     private void transmitRequest(ParentPlatform parentPlatform, Request request) throws SipException {
     private void transmitRequest(ParentPlatform parentPlatform, Request request) throws SipException {
@@ -206,39 +187,32 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
      * @return
      * @return
      */
      */
     @Override
     @Override
-    public boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) {
+    public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException {
 
 
         if ( parentPlatform ==null) {
         if ( parentPlatform ==null) {
-            return false;
+            return ;
         }
         }
-        try {
-            List<DeviceChannel> channels = new ArrayList<>();
-            if (channel != null) {
-                channels.add(channel);
-            }
-            String catalogXml = getCatalogXml(channels, sn, parentPlatform, size);
+        List<DeviceChannel> channels = new ArrayList<>();
+        if (channel != null) {
+            channels.add(channel);
+        }
+        String catalogXml = getCatalogXml(channels, sn, parentPlatform, size);
 
 
-            // callid
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
+        // callid
+        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
 
 
-            Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-            transmitRequest(parentPlatform, request);
+        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
+        transmitRequest(parentPlatform, request);
 
 
-        } catch (SipException | ParseException | InvalidArgumentException e) {
-            e.printStackTrace();
-            return false;
-        }
-        return true;
     }
     }
 
 
     @Override
     @Override
-    public boolean catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag) {
+    public void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException {
         if ( parentPlatform ==null) {
         if ( parentPlatform ==null) {
-            return false;
+            return ;
         }
         }
         sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0);
         sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0);
-        return true;
     }
     }
     private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
     private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
         String characterSet = parentPlatform.getCharacterSet();
         String characterSet = parentPlatform.getCharacterSet();
@@ -300,30 +274,30 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         return catalogXml.toString();
         return catalogXml.toString();
     }
     }
 
 
-    private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) {
+    private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) throws SipException, InvalidArgumentException, ParseException {
         if (index >= channels.size()) {
         if (index >= channels.size()) {
             return;
             return;
         }
         }
-        try {
-            List<DeviceChannel> deviceChannels;
-            if (index + parentPlatform.getCatalogGroup() < channels.size()) {
-                deviceChannels = channels.subList(index, index + parentPlatform.getCatalogGroup());
-            }else {
-                deviceChannels = channels.subList(index, channels.size());
-            }
-            String catalogXml = getCatalogXml(deviceChannels, sn, parentPlatform, channels.size());
-            // callid
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
+        List<DeviceChannel> deviceChannels;
+        if (index + parentPlatform.getCatalogGroup() < channels.size()) {
+            deviceChannels = channels.subList(index, index + parentPlatform.getCatalogGroup());
+        }else {
+            deviceChannels = channels.subList(index, channels.size());
+        }
+        String catalogXml = getCatalogXml(deviceChannels, sn, parentPlatform, channels.size());
+        // callid
+        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
 
 
-            Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
-            transmitRequest(parentPlatform, request, null, eventResult -> {
-                int indexNext = index + parentPlatform.getCatalogGroup();
+        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
+        transmitRequest(parentPlatform, request, null, eventResult -> {
+            int indexNext = index + parentPlatform.getCatalogGroup();
+            try {
                 sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
                 sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
-            });
-        } catch (SipException | ParseException | InvalidArgumentException e) {
-            e.printStackTrace();
-        }
+            } catch (SipException | InvalidArgumentException | ParseException e) {
+                logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
+            }
+        });
     }
     }
 
 
     /**
     /**
@@ -334,36 +308,29 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
      * @return
      * @return
      */
      */
     @Override
     @Override
-    public boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) {
+    public void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
         if (parentPlatform == null) {
         if (parentPlatform == null) {
-            return false;
-        }
-        try {
-            String characterSet = parentPlatform.getCharacterSet();
-            StringBuffer deviceInfoXml = new StringBuffer(600);
-            deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            deviceInfoXml.append("<Response>\r\n");
-            deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
-            deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
-            deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-            deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
-            deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
-            deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
-            deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
-            deviceInfoXml.append("<Result>OK</Result>\r\n");
-            deviceInfoXml.append("</Response>\r\n");
-
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
-
-            Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-            transmitRequest(parentPlatform, request);
-
-        } catch (SipException | ParseException | InvalidArgumentException e) {
-            e.printStackTrace();
-            return false;
+            return;
         }
         }
-        return true;
+        String characterSet = parentPlatform.getCharacterSet();
+        StringBuffer deviceInfoXml = new StringBuffer(600);
+        deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        deviceInfoXml.append("<Response>\r\n");
+        deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
+        deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
+        deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
+        deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
+        deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
+        deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
+        deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
+        deviceInfoXml.append("<Result>OK</Result>\r\n");
+        deviceInfoXml.append("</Response>\r\n");
+
+        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
+        transmitRequest(parentPlatform, request);
     }
     }
 
 
     /**
     /**
@@ -374,129 +341,103 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
      * @return
      * @return
      */
      */
     @Override
     @Override
-    public boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) {
+    public void deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
         if (parentPlatform == null) {
         if (parentPlatform == null) {
-            return false;
+            return ;
         }
         }
-        try {
-            String characterSet = parentPlatform.getCharacterSet();
-            StringBuffer deviceStatusXml = new StringBuffer(600);
-            deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            deviceStatusXml.append("<Response>\r\n");
-            deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
-            deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
-            deviceStatusXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-            deviceStatusXml.append("<Result>OK</Result>\r\n");
-            deviceStatusXml.append("<Online>ONLINE</Online>\r\n");
-            deviceStatusXml.append("<Status>OK</Status>\r\n");
-            deviceStatusXml.append("</Response>\r\n");
-
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
-
-            Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-            transmitRequest(parentPlatform, request);
+        String characterSet = parentPlatform.getCharacterSet();
+        StringBuffer deviceStatusXml = new StringBuffer(600);
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        deviceStatusXml.append("<Response>\r\n");
+        deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
+        deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
+        deviceStatusXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
+        deviceStatusXml.append("<Result>OK</Result>\r\n");
+        deviceStatusXml.append("<Online>ONLINE</Online>\r\n");
+        deviceStatusXml.append("<Status>OK</Status>\r\n");
+        deviceStatusXml.append("</Response>\r\n");
+
+        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
+        transmitRequest(parentPlatform, request);
 
 
-        } catch (SipException | ParseException | InvalidArgumentException e) {
-            e.printStackTrace();
-            return false;
-        }
-        return true;
     }
     }
 
 
     @Override
     @Override
-    public boolean sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) {
+    public void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException {
         if (parentPlatform == null) {
         if (parentPlatform == null) {
-            return false;
+            return;
         }
         }
         if (logger.isDebugEnabled()) {
         if (logger.isDebugEnabled()) {
             logger.debug("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat());
             logger.debug("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat());
         }
         }
 
 
-        try {
-            String characterSet = parentPlatform.getCharacterSet();
-            StringBuffer deviceStatusXml = new StringBuffer(600);
-            deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            deviceStatusXml.append("<Notify>\r\n");
-            deviceStatusXml.append("<CmdType>MobilePosition</CmdType>\r\n");
-            deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-            deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");
-            deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");
-            deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");
-            deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");
-            deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");
-            deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");
-            deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");
-            deviceStatusXml.append("</Notify>\r\n");
-
-           sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
-                logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
-            }, null);
-
-        } catch (SipException | ParseException  e) {
-            e.printStackTrace();
-            return false;
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        }
-        return true;
+        String characterSet = parentPlatform.getCharacterSet();
+        StringBuffer deviceStatusXml = new StringBuffer(600);
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        deviceStatusXml.append("<Notify>\r\n");
+        deviceStatusXml.append("<CmdType>MobilePosition</CmdType>\r\n");
+        deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+        deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");
+        deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");
+        deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");
+        deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");
+        deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");
+        deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");
+        deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");
+        deviceStatusXml.append("</Notify>\r\n");
+
+       sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
+            logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
+        }, null);
+
     }
     }
 
 
     @Override
     @Override
-    public boolean sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) {
+    public void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException {
         if (parentPlatform == null) {
         if (parentPlatform == null) {
-            return false;
+            return;
         }
         }
         logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
         logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSONObject.toJSON(deviceAlarm));
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSONObject.toJSON(deviceAlarm));
-        try {
-            String characterSet = parentPlatform.getCharacterSet();
-            StringBuffer deviceStatusXml = new StringBuffer(600);
-            deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            deviceStatusXml.append("<Notify>\r\n");
-            deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
-            deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-            deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
-            deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
-            deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
-            deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
-            deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
-            deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
-            deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
-            deviceStatusXml.append("<info>\r\n");
-            deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
-            deviceStatusXml.append("</info>\r\n");
-            deviceStatusXml.append("</Notify>\r\n");
-
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
-
-            Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader);
-            transmitRequest(parentPlatform, request);
+        String characterSet = parentPlatform.getCharacterSet();
+        StringBuffer deviceStatusXml = new StringBuffer(600);
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        deviceStatusXml.append("<Notify>\r\n");
+        deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
+        deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+        deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
+        deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
+        deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
+        deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
+        deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
+        deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
+        deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
+        deviceStatusXml.append("<info>\r\n");
+        deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
+        deviceStatusXml.append("</info>\r\n");
+        deviceStatusXml.append("</Notify>\r\n");
+
+        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+
+        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader);
+        transmitRequest(parentPlatform, request);
 
 
-        } catch (SipException | ParseException  e) {
-            e.printStackTrace();
-            return false;
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        }
-        return true;
     }
     }
 
 
     @Override
     @Override
-    public boolean sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) {
+    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.size() == 0 || subscribeInfo == null) {
-            return false;
+            return;
         }
         }
         if (index == null) {
         if (index == null) {
             index = 0;
             index = 0;
         }
         }
         if (index >= deviceChannels.size()) {
         if (index >= deviceChannels.size()) {
-            return true;
+            return;
         }
         }
         List<DeviceChannel> channels;
         List<DeviceChannel> channels;
         if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) {
         if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) {
@@ -504,32 +445,25 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         }else {
         }else {
             channels = deviceChannels.subList(index, deviceChannels.size());
             channels = deviceChannels.subList(index, deviceChannels.size());
         }
         }
-        try {
-            Integer finalIndex = index;
-            String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
-                    deviceChannels.size(), type, subscribeInfo);
-            sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
-                logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
-            }, (eventResult -> {
+        Integer finalIndex = index;
+        String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
+                deviceChannels.size(), type, subscribeInfo);
+        sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
+            logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
+        }, (eventResult -> {
+            try {
                 sendNotifyForCatalogAddOrUpdate(type, parentPlatform, deviceChannels, subscribeInfo,
                 sendNotifyForCatalogAddOrUpdate(type, parentPlatform, deviceChannels, subscribeInfo,
                         finalIndex + parentPlatform.getCatalogGroup());
                         finalIndex + parentPlatform.getCatalogGroup());
-            }));
-        } catch (SipException | ParseException e) {
-            e.printStackTrace();
-            return false;
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        }
-        return true;
+            } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
+                     IllegalAccessException e) {
+                logger.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage());
+            }
+        }));
     }
     }
 
 
-    private ClientTransaction sendNotify(ParentPlatform parentPlatform, String catalogXmlContent,
+    private void sendNotify(ParentPlatform parentPlatform, String catalogXmlContent,
                                    SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent,  SipSubscribe.Event okEvent )
                                    SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent,  SipSubscribe.Event okEvent )
-            throws NoSuchFieldException, IllegalAccessException, SipException, ParseException, InvalidArgumentException {
+            throws SipException, ParseException, InvalidArgumentException {
 		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
 		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
         String characterSet = parentPlatform.getCharacterSet();
         String characterSet = parentPlatform.getCharacterSet();
  		// 设置编码, 防止中文乱码
  		// 设置编码, 防止中文乱码
@@ -537,50 +471,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
 
 
         SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
         SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
 
 
-        notifyRequest.getCSeqHeader().setSeqNumber(redisCatchStorage.getCSEQ());
-        
-        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
-        notifyRequest.setContent(catalogXmlContent, contentTypeHeader);
-
-        SubscriptionStateHeader subscriptionState = sipFactory.createHeaderFactory()
-                .createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE);
-        notifyRequest.addHeader(subscriptionState);
-
-        EventHeader event = sipFactory.createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
-        if (subscribeInfo.getEventId() != null) {
-            event.setEventId(subscribeInfo.getEventId());
-        }
-        notifyRequest.addHeader(event);
-        SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
-        sipURI.setHost(parentPlatform.getServerIP());
-        sipURI.setPort(parentPlatform.getServerPort());
-
-//        ClientTransaction transaction = subscribeInfo.getClientTransaction();
-//        if (transaction == null || transaction.getState().equals(TransactionState.COMPLETED)) {
-//            if ("TCP".equals(parentPlatform.getTransport())) {
-//                transaction = tcpSipProvider.getNewClientTransaction(notifyRequest);
-//            } else if ("UDP".equals(parentPlatform.getTransport())) {
-//                transaction = udpSipProvider.getNewClientTransaction(notifyRequest);
-//            }
-//        }
-
-        ClientTransaction transaction = null;
-        if ("TCP".equals(parentPlatform.getTransport())) {
-            transaction = tcpSipProvider.getNewClientTransaction(notifyRequest);
-        } else if ("UDP".equals(parentPlatform.getTransport())) {
-            transaction = udpSipProvider.getNewClientTransaction(notifyRequest);
-        }
-
-        // 添加错误订阅
-        if (errorEvent != null) {
-            sipSubscribe.addErrorSubscribe(subscribeInfo.getRequest().getCallIdHeader().getCallId(), errorEvent);
-        }
-        // 添加订阅
-        if (okEvent != null) {
-            sipSubscribe.addOkSubscribe(subscribeInfo.getRequest().getCallIdHeader().getCallId(), okEvent);
-        }
-        transaction.sendRequest();
-        return transaction;
+        transmitRequest(parentPlatform, notifyRequest);
     }
     }
 
 
     private  String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
     private  String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
@@ -639,20 +530,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     }
     }
 
 
     @Override
     @Override
-    public boolean sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels,
-                                             SubscribeInfo subscribeInfo, Integer index) {
+    public void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels,
+                                             SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException {
         if (parentPlatform == null
         if (parentPlatform == null
                 || deviceChannels == null
                 || deviceChannels == null
                 || deviceChannels.size() == 0
                 || deviceChannels.size() == 0
                 || subscribeInfo == null) {
                 || subscribeInfo == null) {
-            return false;
+            logger.warn("[缺少必要参数]");
+            return;
         }
         }
 
 
         if (index == null) {
         if (index == null) {
             index = 0;
             index = 0;
         }
         }
         if (index >= deviceChannels.size()) {
         if (index >= deviceChannels.size()) {
-            return true;
+            return;
         }
         }
         List<DeviceChannel> channels;
         List<DeviceChannel> channels;
         if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) {
         if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) {
@@ -660,28 +552,19 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         }else {
         }else {
             channels = deviceChannels.subList(index, deviceChannels.size());
             channels = deviceChannels.subList(index, deviceChannels.size());
         }
         }
-        try {
-            Integer finalIndex = index;
-            String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type);
-            sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
-                logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
-            }, (eventResult -> {
+        Integer finalIndex = index;
+        String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type);
+        sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
+            logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
+        }, eventResult -> {
+            try {
                 sendNotifyForCatalogOther(type, parentPlatform, deviceChannels, subscribeInfo,
                 sendNotifyForCatalogOther(type, parentPlatform, deviceChannels, subscribeInfo,
                         finalIndex + parentPlatform.getCatalogGroup());
                         finalIndex + parentPlatform.getCatalogGroup());
-            }));
-        } catch (SipException e) {
-            e.printStackTrace();
-        } catch (ParseException e) {
-            e.printStackTrace();
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        }
-
-        return true;
+            } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
+                     IllegalAccessException e) {
+                logger.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage());
+            }
+        });
     }
     }
 
 
     private String getCatalogXmlContentForCatalogOther(ParentPlatform parentPlatform, List<DeviceChannel> channels, String type) {
     private String getCatalogXmlContentForCatalogOther(ParentPlatform parentPlatform, List<DeviceChannel> channels, String type) {
@@ -711,113 +594,81 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         return catalogXml.toString();
         return catalogXml.toString();
     }
     }
     @Override
     @Override
-    public boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) {
+    public void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException {
         if ( parentPlatform ==null) {
         if ( parentPlatform ==null) {
-            return false;
+            return ;
         }
         }
-        try {
-            String characterSet = parentPlatform.getCharacterSet();
-            StringBuffer recordXml = new StringBuffer(600);
-            recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            recordXml.append("<Response>\r\n");
-            recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");
-            recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");
-            recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");
-            recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
-            if (recordInfo.getRecordList() == null ) {
-                recordXml.append("<RecordList Num=\"0\">\r\n");
-            }else {
-                recordXml.append("<RecordList Num=\"" + recordInfo.getRecordList().size()+"\">\r\n");
-                if (recordInfo.getRecordList().size() > 0) {
-                    for (RecordItem recordItem : recordInfo.getRecordList()) {
-                        recordXml.append("<Item>\r\n");
-                        if (deviceChannel != null) {
-                            recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");
-                            recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");
-                            recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");
-                            recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");
-                            recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");
-                            recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n");
-                            if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
-                                recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
-                            }
-                            if (!ObjectUtils.isEmpty(recordItem.getFilePath())) {
-                                recordXml.append("<FilePath>" + recordItem.getFilePath() + "</FilePath>\r\n");
-                            }
+        String characterSet = parentPlatform.getCharacterSet();
+        StringBuffer recordXml = new StringBuffer(600);
+        recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        recordXml.append("<Response>\r\n");
+        recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");
+        recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");
+        recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");
+        recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
+        if (recordInfo.getRecordList() == null ) {
+            recordXml.append("<RecordList Num=\"0\">\r\n");
+        }else {
+            recordXml.append("<RecordList Num=\"" + recordInfo.getRecordList().size()+"\">\r\n");
+            if (recordInfo.getRecordList().size() > 0) {
+                for (RecordItem recordItem : recordInfo.getRecordList()) {
+                    recordXml.append("<Item>\r\n");
+                    if (deviceChannel != null) {
+                        recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");
+                        recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");
+                        recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");
+                        recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");
+                        recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");
+                        recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n");
+                        if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
+                            recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
+                        }
+                        if (!ObjectUtils.isEmpty(recordItem.getFilePath())) {
+                            recordXml.append("<FilePath>" + recordItem.getFilePath() + "</FilePath>\r\n");
                         }
                         }
-                        recordXml.append("</Item>\r\n");
                     }
                     }
+                    recordXml.append("</Item>\r\n");
                 }
                 }
             }
             }
+        }
 
 
-            recordXml.append("</RecordList>\r\n");
-            recordXml.append("</Response>\r\n");
+        recordXml.append("</RecordList>\r\n");
+        recordXml.append("</Response>\r\n");
 
 
-            // callid
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
-            Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-            transmitRequest(parentPlatform, request);
+        // callid
+        CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                : udpSipProvider.getNewCallId();
+        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
+        transmitRequest(parentPlatform, request);
 
 
-        } catch (SipException | ParseException | InvalidArgumentException e) {
-            e.printStackTrace();
-            return false;
-        }
-        return true;
     }
     }
 
 
     @Override
     @Override
-    public boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) {
-        if (sendRtpItem == null) {
-            return false;
-        }
-        if (platform == null) {
-            return false;
-        }
-
-        try{
-
-            String characterSet = platform.getCharacterSet();
-            StringBuffer mediaStatusXml = new StringBuffer(200);
-            mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            mediaStatusXml.append("<Notify>\r\n");
-            mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
-            mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-            mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
-            mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
-            mediaStatusXml.append("</Notify>\r\n");
-
-            SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(platform, mediaStatusXml.toString(),
-                    sendRtpItem);
-
-            ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
-            messageRequest.setContent(mediaStatusXml.toString(), contentTypeHeader);
-            SipURI sipURI = (SipURI) messageRequest.getRequestURI();
-            sipURI.setHost(platform.getServerIP());
-            sipURI.setPort(platform.getServerPort());
-            ClientTransaction clientTransaction;
-            if ("TCP".equals(platform.getTransport())) {
-                clientTransaction = tcpSipProvider.getNewClientTransaction(messageRequest);
-            }else {
-                clientTransaction = udpSipProvider.getNewClientTransaction(messageRequest);
-            }
-            clientTransaction.sendRequest();
-        } catch (SipException e) {
-            e.printStackTrace();
-            return false;
-        } catch (ParseException e) {
-            e.printStackTrace();
-            return false;
-        } catch (InvalidArgumentException e) {
-            throw new RuntimeException(e);
+    public void sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException {
+        if (sendRtpItem == null || platform == null) {
+            return;
         }
         }
-        return true;
 
 
 
 
+        String characterSet = platform.getCharacterSet();
+        StringBuffer mediaStatusXml = new StringBuffer(200);
+        mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        mediaStatusXml.append("<Notify>\r\n");
+        mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
+        mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+        mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
+        mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
+        mediaStatusXml.append("</Notify>\r\n");
+
+        SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(platform, mediaStatusXml.toString(),
+                sendRtpItem);
+
+        transmitRequest(platform, messageRequest);
+
     }
     }
 
 
     @Override
     @Override
-    public void streamByeCmd(ParentPlatform platform, String callId) {
+    public void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException {
         if (platform == null) {
         if (platform == null) {
             return;
             return;
         }
         }
@@ -828,7 +679,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     }
     }
 
 
     @Override
     @Override
-    public void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) {
+    public void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException {
         if (sendRtpItem == null ) {
         if (sendRtpItem == null ) {
             logger.info("[向上级发送BYE], sendRtpItem 为NULL");
             logger.info("[向上级发送BYE], sendRtpItem 为NULL");
             return;
             return;
@@ -844,25 +695,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
             mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
             mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
             zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId());
             zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId());
         }
         }
-        try {
-
-            SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
-            if (byeRequest == null) {
-                logger.warn("[向上级发送bye]:无法创建 byeRequest");
-            }
-            ClientTransaction clientTransaction;
-            if ("TCP".equals(platform.getTransport())) {
-                clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
-            } else {
-                clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
-            }
-            clientTransaction.sendRequest();
-        } catch (SipException e) {
-            e.printStackTrace();
-        } catch (ParseException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            throw new RuntimeException(e);
+        SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
+        if (byeRequest == null) {
+            logger.warn("[向上级发送bye]:无法创建 byeRequest");
         }
         }
+        transmitRequest(platform,byeRequest);
     }
     }
 }
 }

+ 9 - 8
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java

@@ -7,6 +7,7 @@ import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.stack.SIPServerTransaction;
 import gov.nist.javax.sip.stack.SIPServerTransaction;
+import gov.nist.javax.sip.stack.SIPServerTransactionImpl;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.dom4j.Document;
 import org.dom4j.Document;
 import org.dom4j.DocumentException;
 import org.dom4j.DocumentException;
@@ -57,7 +58,7 @@ public abstract class SIPRequestProcessorParent {
 	 */
 	 */
 	public ServerTransaction getServerTransaction(RequestEvent evt) {
 	public ServerTransaction getServerTransaction(RequestEvent evt) {
 		Request request = evt.getRequest();
 		Request request = evt.getRequest();
-		ServerTransaction serverTransaction = evt.getServerTransaction();
+		SIPServerTransactionImpl serverTransaction = (SIPServerTransactionImpl)evt.getServerTransaction();
 		// 判断TCP还是UDP
 		// 判断TCP还是UDP
 		boolean isTcp = false;
 		boolean isTcp = false;
 		ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
 		ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
@@ -65,28 +66,28 @@ public abstract class SIPRequestProcessorParent {
 		if (transport.equalsIgnoreCase("TCP")) {
 		if (transport.equalsIgnoreCase("TCP")) {
 			isTcp = true;
 			isTcp = true;
 		}
 		}
-
+		if (serverTransaction != null && serverTransaction.getOriginalRequest() == null) {
+			serverTransaction.setOriginalRequest((SIPRequest) evt.getRequest());
+		}
 		if (serverTransaction == null) {
 		if (serverTransaction == null) {
 			try {
 			try {
 				if (isTcp) {
 				if (isTcp) {
 					SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
 					SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
-					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
+					serverTransaction = (SIPServerTransactionImpl) stack.findTransaction((SIPRequest)request, true);
 					if (serverTransaction == null) {
 					if (serverTransaction == null) {
-						serverTransaction = tcpSipProvider.getNewServerTransaction(request);
+						serverTransaction = (SIPServerTransactionImpl)tcpSipProvider.getNewServerTransaction(request);
 					}
 					}
 				} else {
 				} else {
 					SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
 					SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
-					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
+					serverTransaction = (SIPServerTransactionImpl) stack.findTransaction((SIPRequest)request, true);
 					if (serverTransaction == null) {
 					if (serverTransaction == null) {
-						serverTransaction = udpSipProvider.getNewServerTransaction(request);
+						serverTransaction = (SIPServerTransactionImpl)udpSipProvider.getNewServerTransaction(request);
 					}
 					}
 				}
 				}
 			} catch (TransactionAlreadyExistsException e) {
 			} catch (TransactionAlreadyExistsException e) {
 				logger.error(e.getMessage());
 				logger.error(e.getMessage());
 			} catch (TransactionUnavailableException e) {
 			} catch (TransactionUnavailableException e) {
 				logger.error(e.getMessage());
 				logger.error(e.getMessage());
-			}finally {
-
 			}
 			}
 		}
 		}
 		return serverTransaction;
 		return serverTransaction;

+ 10 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java

@@ -23,12 +23,15 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
+import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
 import javax.sip.RequestEvent;
+import javax.sip.SipException;
 import javax.sip.address.SipURI;
 import javax.sip.address.SipURI;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.FromHeader;
 import javax.sip.header.FromHeader;
 import javax.sip.header.HeaderAddress;
 import javax.sip.header.HeaderAddress;
 import javax.sip.header.ToHeader;
 import javax.sip.header.ToHeader;
+import java.text.ParseException;
 import java.util.*;
 import java.util.*;
 
 
 /**
 /**
@@ -95,8 +98,8 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 		SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());
 		SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());
 		String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
 		String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
 		MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 		MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
-		logger.info("收到ACK,rtp/{}开始向上级推流, 目标 {}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
-		Map<String, Object> param = new HashMap<>();
+		logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
+		Map<String, Object> param = new HashMap<>(12);
 		param.put("vhost","__defaultVhost__");
 		param.put("vhost","__defaultVhost__");
 		param.put("app",sendRtpItem.getApp());
 		param.put("app",sendRtpItem.getApp());
 		param.put("stream",sendRtpItem.getStreamId());
 		param.put("stream",sendRtpItem.getStreamId());
@@ -139,7 +142,11 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 				// TODO 可能是语音对讲
 				// TODO 可能是语音对讲
 			}else {
 			}else {
 				// 向上级平台
 				// 向上级平台
-				commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
+				try {
+					commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
+				} catch (SipException | InvalidArgumentException | ParseException e) {
+					logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+				}
 			}
 			}
 		}
 		}
 	}
 	}

+ 23 - 13
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java

@@ -1,6 +1,7 @@
 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.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
 import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
@@ -12,12 +13,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-import com.genersoft.iot.vmp.utils.SerializeUtils;
-import gov.nist.javax.sip.stack.SIPDialog;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.InitializingBean;
@@ -50,6 +50,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 	@Autowired
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 	private IRedisCatchStorage redisCatchStorage;
 
 
+	@Autowired
+	private IDeviceService deviceService;
+
 	@Autowired
 	@Autowired
 	private IVideoManagerStorage storager;
 	private IVideoManagerStorage storager;
 
 
@@ -77,9 +80,13 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 	 */
 	 */
 	@Override
 	@Override
 	public void process(RequestEvent evt) {
 	public void process(RequestEvent evt) {
+
 		try {
 		try {
 			responseAck(getServerTransaction(evt), Response.OK);
 			responseAck(getServerTransaction(evt), Response.OK);
-			CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
+		} catch (SipException | InvalidArgumentException | ParseException e) {
+			logger.error("[回复BYE信息失败],{}", e.getMessage());
+		}
+		CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
 			String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
 			String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
 			String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
 			String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
 			SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());
 			SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());
@@ -99,7 +106,17 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 				if (totalReaderCount <= 0) {
 				if (totalReaderCount <= 0) {
 					logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
 					logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
 					if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
 					if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
-						cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId, streamId, null);
+						Device device = deviceService.queryDevice(sendRtpItem.getDeviceId());
+						if (device == null) {
+							logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);
+						}
+						try {
+							logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId);
+							cmder.streamByeCmd(device, channelId, streamId, null);
+						} catch (InvalidArgumentException | ParseException | SipException |
+								 SsrcTransactionNotFoundException e) {
+							logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());
+						}
 					}
 					}
 					if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
 					if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
 						MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
 						MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
@@ -120,8 +137,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 				}
 				}
 				SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
 				SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
 				if (ssrcTransactionForPlay != null){
 				if (ssrcTransactionForPlay != null){
-					SIPDialog dialogForPlay = (SIPDialog) SerializeUtils.deSerialize(ssrcTransactionForPlay.getDialog());
-					if (dialogForPlay.getCallId().getCallId().equals(callIdHeader.getCallId())){
+					if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){
 						// 释放ssrc
 						// 释放ssrc
 						MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId());
 						MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId());
 						if (mediaServerItem != null) {
 						if (mediaServerItem != null) {
@@ -140,12 +156,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 					streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream());
 					streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream());
 				}
 				}
 			}
 			}
-		} catch (SipException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			e.printStackTrace();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
+
 	}
 	}
 }
 }

+ 5 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java

@@ -341,7 +341,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                                 logger.info("Ack 等待超时");
                                 logger.info("Ack 等待超时");
                                 mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), ssrc);
                                 mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), ssrc);
                                 // 回复bye
                                 // 回复bye
-                                cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());
+                                try {
+                                    cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());
+                                } catch (SipException | InvalidArgumentException | ParseException e) {
+                                    logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+                                }
                             }, 60 * 1000);
                             }, 60 * 1000);
                             responseSdpAck(serverTransaction, content.toString(), platform);
                             responseSdpAck(serverTransaction, content.toString(), platform);
 
 

+ 3 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java

@@ -93,7 +93,9 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
                 responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
                 responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
                 logger.warn("[设备未找到 ]: {}", deviceId);
                 logger.warn("[设备未找到 ]: {}", deviceId);
                 if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
                 if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
-                    SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
+                    DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog());
+                    deviceNotFoundEvent.setCallId(callIdHeader.getCallId());
+                    SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent);
                     sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
                     sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
                 };
                 };
             }else {
             }else {

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java

@@ -89,8 +89,6 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
         ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
         ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
         try {
         try {
             if (device != null && parentPlatform != null) {
             if (device != null && parentPlatform != null) {
-
-                logger.warn("[重复]平台与设备编号重复:{}", deviceId);
                 SIPRequest request = (SIPRequest) evt.getRequest();
                 SIPRequest request = (SIPRequest) evt.getRequest();
                 String hostAddress = request.getRemoteAddress().getHostAddress();
                 String hostAddress = request.getRemoteAddress().getHostAddress();
                 int remotePort = request.getRemotePort();
                 int remotePort = request.getRemotePort();
@@ -105,7 +103,9 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
                 responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
                 responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
                 logger.warn("[设备未找到 ]: {}", deviceId);
                 logger.warn("[设备未找到 ]: {}", deviceId);
                 if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
                 if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
-                    SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
+                    DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog());
+                    deviceNotFoundEvent.setCallId(callIdHeader.getCallId());
+                    SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent);
                     sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
                     sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
                 };
                 };
             }else {
             }else {

+ 48 - 54
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java

@@ -16,6 +16,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
@@ -48,6 +50,10 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
     @Autowired
     @Autowired
     private SIPCommanderFroPlatform cmderFroPlatform;
     private SIPCommanderFroPlatform cmderFroPlatform;
 
 
+    @Qualifier("taskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
+
     @Override
     @Override
     public void afterPropertiesSet() throws Exception {
     public void afterPropertiesSet() throws Exception {
         controlMessageHandler.addHandler(cmdType, this);
         controlMessageHandler.addHandler(cmdType, this);
@@ -71,34 +77,30 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
             if (parentPlatform.getServerGBId().equals(targetGBId)) {
             if (parentPlatform.getServerGBId().equals(targetGBId)) {
                 // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
                 // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
                 logger.info("执行远程启动本平台命令");
                 logger.info("执行远程启动本平台命令");
-                cmderFroPlatform.unregister(parentPlatform, null, null);
-
-                Thread restartThread = new Thread(new Runnable() {
-                    @Override
-                    public void run() {
-                        try {
-                            Thread.sleep(3000);
-                            SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
-                            SipStackImpl stack = (SipStackImpl)up.getSipStack();
-                            stack.stop();
-                            Iterator listener = stack.getListeningPoints();
-                            while (listener.hasNext()) {
-                                stack.deleteListeningPoint((ListeningPoint) listener.next());
-                            }
-                            Iterator providers = stack.getSipProviders();
-                            while (providers.hasNext()) {
-                                stack.deleteSipProvider((SipProvider) providers.next());
-                            }
-                            VManageBootstrap.restart();
-                        } catch (InterruptedException ignored) {
-                        } catch (ObjectInUseException e) {
-                            e.printStackTrace();
+                try {
+                    cmderFroPlatform.unregister(parentPlatform, null, null);
+                } catch (InvalidArgumentException | ParseException | SipException e) {
+                    logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+                }
+                taskExecutor.execute(()->{
+                    try {
+                        Thread.sleep(3000);
+                        SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
+                        SipStackImpl stack = (SipStackImpl)up.getSipStack();
+                        stack.stop();
+                        Iterator listener = stack.getListeningPoints();
+                        while (listener.hasNext()) {
+                            stack.deleteListeningPoint((ListeningPoint) listener.next());
                         }
                         }
+                        Iterator providers = stack.getSipProviders();
+                        while (providers.hasNext()) {
+                            stack.deleteSipProvider((SipProvider) providers.next());
+                        }
+                        VManageBootstrap.restart();
+                    } catch (InterruptedException | ObjectInUseException e) {
+                        logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
                     }
                     }
                 });
                 });
-
-                restartThread.setDaemon(false);
-                restartThread.start();
             } else {
             } else {
                 // 远程启动指定设备
                 // 远程启动指定设备
             }
             }
@@ -111,37 +113,29 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
                 try {
                 try {
                     responseAck(serverTransaction, Response.NOT_FOUND);
                     responseAck(serverTransaction, Response.NOT_FOUND);
                     return;
                     return;
-                } catch (SipException e) {
-                    e.printStackTrace();
-                } catch (InvalidArgumentException e) {
-                    e.printStackTrace();
-                } catch (ParseException e) {
-                    e.printStackTrace();
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[命令发送失败] 错误信息: {}", e.getMessage());
                 }
                 }
             }
             }
-            cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
-                // 失败的回复
-                try {
-                    responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
-                } catch (SipException e) {
-                    e.printStackTrace();
-                } catch (InvalidArgumentException e) {
-                    e.printStackTrace();
-                } catch (ParseException e) {
-                    e.printStackTrace();
-                }
-            }, eventResult -> {
-                // 成功的回复
-                try {
-                    responseAck(serverTransaction, eventResult.statusCode);
-                } catch (SipException e) {
-                    e.printStackTrace();
-                } catch (InvalidArgumentException e) {
-                    e.printStackTrace();
-                } catch (ParseException e) {
-                    e.printStackTrace();
-                }
-            });
+            try {
+                cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
+                    // 失败的回复
+                    try {
+                        responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
+                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                        logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
+                    }
+                }, eventResult -> {
+                    // 成功的回复
+                    try {
+                        responseAck(serverTransaction, eventResult.statusCode);
+                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                        logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
+                    }
+                });
+            } catch (InvalidArgumentException | SipException | ParseException e) {
+                logger.error("[命令发送失败] 云台/前端: {}", e.getMessage());
+            }
         }
         }
     }
     }
 }
 }

+ 12 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
 
 
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
@@ -89,7 +90,12 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
             // 先从会话内查找
             // 先从会话内查找
             SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
             SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
             if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
             if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
-                cmder.streamByeCmd(device.getDeviceId(), ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
+
+                try {
+                    cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
+                } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) {
+                    logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage());
+                }
 
 
                 // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
                 // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
                 SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
                 SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
@@ -99,7 +105,11 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
                         logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId());
                         logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId());
                         return;
                         return;
                     }
                     }
-                    sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem);
+                    try {
+                        sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem);
+                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                        logger.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage());
+                    }
                 }
                 }
             }
             }
         }
         }

+ 7 - 7
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java

@@ -49,14 +49,14 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
         try {
         try {
             // 回复200 OK
             // 回复200 OK
             responseAck(getServerTransaction(evt), Response.OK);
             responseAck(getServerTransaction(evt), Response.OK);
-        } catch (SipException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        } catch (ParseException e) {
-            e.printStackTrace();
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage());
         }
         }
         String sn = rootElement.element("SN").getText();
         String sn = rootElement.element("SN").getText();
-        cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
+        try {
+            cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
+        }
     }
     }
 }
 }

+ 7 - 7
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java

@@ -62,14 +62,14 @@ public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent i
         // 回复200 OK
         // 回复200 OK
         try {
         try {
             responseAck(getServerTransaction(evt), Response.OK);
             responseAck(getServerTransaction(evt), Response.OK);
-        } catch (SipException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        } catch (ParseException e) {
-            e.printStackTrace();
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage());
         }
         }
         String sn = rootElement.element("SN").getText();
         String sn = rootElement.element("SN").getText();
-        cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag());
+        try {
+            cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag());
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复: {}", e.getMessage());
+        }
     }
     }
 }
 }

+ 29 - 37
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java

@@ -103,53 +103,45 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
             DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId);
             DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId);
             // 接收录像数据
             // 接收录像数据
             recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{
             recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{
-                cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, fromHeader.getTag(), recordInfo);
+                try {
+                    cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, fromHeader.getTag(), recordInfo);
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage());
+                }
             });
             });
-            commander.recordInfoQuery(device, channelId, DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime),
-                    DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> {
-                        // 回复200 OK
-                        try {
-                            responseAck(serverTransaction, Response.OK);
-                        } catch (SipException e) {
-                            e.printStackTrace();
-                        } catch (InvalidArgumentException e) {
-                            e.printStackTrace();
-                        } catch (ParseException e) {
-                            e.printStackTrace();
-                        }
-                    }),(eventResult -> {
-                        // 查询失败
-                        try {
-                            responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
-                        } catch (SipException e) {
-                            e.printStackTrace();
-                        } catch (InvalidArgumentException e) {
-                            e.printStackTrace();
-                        } catch (ParseException e) {
-                            e.printStackTrace();
-                        }
-                    }));
+            try {
+                commander.recordInfoQuery(device, channelId, DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime),
+                        DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> {
+                            // 回复200 OK
+                            try {
+                                responseAck(serverTransaction, Response.OK);
+                            } catch (SipException | InvalidArgumentException | ParseException e) {
+                                logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage());
+                            }
+                        }),(eventResult -> {
+                            // 查询失败
+                            try {
+                                responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
+                            } catch (SipException | InvalidArgumentException | ParseException e) {
+                                logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage());
+                            }
+                        }));
+            } catch (InvalidArgumentException | ParseException | SipException e) {
+                logger.error("[命令发送失败] 录像查询: {}", e.getMessage());
+            }
 
 
         }else if (channelSources.get(1).getCount() > 0) { // 直播流
         }else if (channelSources.get(1).getCount() > 0) { // 直播流
             // TODO
             // TODO
             try {
             try {
                 responseAck(serverTransaction, Response.NOT_IMPLEMENTED); // 回复未实现
                 responseAck(serverTransaction, Response.NOT_IMPLEMENTED); // 回复未实现
-            } catch (SipException e) {
-                e.printStackTrace();
-            } catch (InvalidArgumentException e) {
-                e.printStackTrace();
-            } catch (ParseException e) {
-                e.printStackTrace();
+            } catch (SipException | InvalidArgumentException | ParseException e) {
+                logger.error("[命令发送失败] 录像查询: {}", e.getMessage());
             }
             }
         }else { // 错误的请求
         }else { // 错误的请求
             try {
             try {
                 responseAck(serverTransaction, Response.BAD_REQUEST);
                 responseAck(serverTransaction, Response.BAD_REQUEST);
-            } catch (SipException e) {
-                e.printStackTrace();
-            } catch (InvalidArgumentException e) {
-                e.printStackTrace();
-            } catch (ParseException e) {
-                e.printStackTrace();
+            } catch (SipException | InvalidArgumentException | ParseException e) {
+                logger.error("[命令发送失败] 录像查询: {}", e.getMessage());
             }
             }
         }
         }
     }
     }

+ 62 - 78
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java

@@ -87,89 +87,73 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
     public void handForDevice(RequestEvent evt, Device device, Element element) {
     public void handForDevice(RequestEvent evt, Device device, Element element) {
         taskQueue.offer(new HandlerCatchData(evt, device, element));
         taskQueue.offer(new HandlerCatchData(evt, device, element));
         // 回复200 OK
         // 回复200 OK
+        ServerTransaction serverTransaction = getServerTransaction(evt);
         try {
         try {
-            ServerTransaction serverTransaction = getServerTransaction(evt);
             responseAck(serverTransaction, Response.OK);
             responseAck(serverTransaction, Response.OK);
-            if (!taskQueueHandlerRun) {
-                taskQueueHandlerRun = true;
-                taskExecutor.execute(()-> {
-                    while (!taskQueue.isEmpty()) {
-                        HandlerCatchData take = taskQueue.poll();
-                        try {
-                            Element rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
-                            if (rootElement == null) {
-                                logger.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest());
-                                continue;
-                            }
-                            Element deviceListElement = rootElement.element("DeviceList");
-                            Element sumNumElement = rootElement.element("SumNum");
-                            Element snElement = rootElement.element("SN");
-                            if (snElement == null || sumNumElement == null || deviceListElement == null) {
-                                responseAck(serverTransaction, Response.BAD_REQUEST, "xml error");
-                                continue;
-                            }
-                            int sumNum = Integer.parseInt(sumNumElement.getText());
-
-                            if (sumNum == 0) {
-                                logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId());
-                                // 数据已经完整接收
-                                storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
-                                catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
-                            }else {
-                                Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
-                                if (deviceListIterator != null) {
-                                    List<DeviceChannel> channelList = new ArrayList<>();
-                                    // 遍历DeviceList
-                                    while (deviceListIterator.hasNext()) {
-                                        Element itemDevice = deviceListIterator.next();
-                                        Element channelDeviceElement = itemDevice.element("DeviceID");
-                                        if (channelDeviceElement == null) {
-                                            continue;
-                                        }
-                                        DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null);
-                                        deviceChannel.setDeviceId(take.getDevice().getDeviceId());
-
-                                        channelList.add(deviceChannel);
-                                    }
-                                    int sn = Integer.parseInt(snElement.getText());
-                                    catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
-                                    logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
-                                    if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
-                                        // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理,
-                                        // 目前支持设备通道上线通知时和设备上线时向上级通知
-                                        boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
-                                        if (!resetChannelsResult) {
-                                            String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条";
-                                            catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg);
-                                        }else {
-                                            catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
-                                        }
-                                    }
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[命令发送失败] 目录查询回复: {}", e.getMessage());
+        }
+        if (!taskQueueHandlerRun) {
+            taskQueueHandlerRun = true;
+            taskExecutor.execute(() -> {
+                while (!taskQueue.isEmpty()) {
+                    HandlerCatchData take = taskQueue.poll();
+                    Element rootElement = null;
+                    try {
+                        rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
+                    } catch (DocumentException e) {
+                        logger.error("[xml解析] 失败: ", e);
+                        continue;
+                    }
+                    if (rootElement == null) {
+                        logger.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest());
+                        continue;
+                    }
+                    Element deviceListElement = rootElement.element("DeviceList");
+                    Element sumNumElement = rootElement.element("SumNum");
+                    Element snElement = rootElement.element("SN");
+                    int sumNum = Integer.parseInt(sumNumElement.getText());
+
+                    if (sumNum == 0) {
+                        logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId());
+                        // 数据已经完整接收
+                        storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
+                        catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
+                    } else {
+                        Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
+                        if (deviceListIterator != null) {
+                            List<DeviceChannel> channelList = new ArrayList<>();
+                            // 遍历DeviceList
+                            while (deviceListIterator.hasNext()) {
+                                Element itemDevice = deviceListIterator.next();
+                                Element channelDeviceElement = itemDevice.element("DeviceID");
+                                if (channelDeviceElement == null) {
+                                    continue;
                                 }
                                 }
+                                DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null);
+                                deviceChannel.setDeviceId(take.getDevice().getDeviceId());
 
 
+                                channelList.add(deviceChannel);
+                            }
+                            int sn = Integer.parseInt(snElement.getText());
+                            catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
+                            logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 : catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
+                            if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
+                                // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理,
+                                // 目前支持设备通道上线通知时和设备上线时向上级通知
+                                boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
+                                if (!resetChannelsResult) {
+                                    String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条";
+                                    catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg);
+                                } else {
+                                    catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
+                                }
                             }
                             }
-                        } catch (DocumentException e) {
-                            e.printStackTrace();
-                        } catch (InvalidArgumentException e) {
-                            e.printStackTrace();
-                        } catch (ParseException e) {
-                            e.printStackTrace();
-                        } catch (SipException e) {
-                            e.printStackTrace();
-                        } finally {
-                            taskQueueHandlerRun = false;
                         }
                         }
+
                     }
                     }
-                });
-            }
-        } catch (SipException e) {
-            throw new RuntimeException(e);
-        } catch (InvalidArgumentException e) {
-            throw new RuntimeException(e);
-        } catch (ParseException e) {
-            throw new RuntimeException(e);
-        } finally {
-            taskQueueHandlerRun = false;
+                }
+            });
         }
         }
 
 
     }
     }
@@ -182,7 +166,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
     public SyncStatus getChannelSyncProgress(String deviceId) {
     public SyncStatus getChannelSyncProgress(String deviceId) {
         if (catalogDataCatch.get(deviceId) == null) {
         if (catalogDataCatch.get(deviceId) == null) {
             return null;
             return null;
-        }else {
+        } else {
             return catalogDataCatch.getSyncStatus(deviceId);
             return catalogDataCatch.getSyncStatus(deviceId);
         }
         }
     }
     }
@@ -190,7 +174,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
     public boolean isSyncRunning(String deviceId) {
     public boolean isSyncRunning(String deviceId) {
         if (catalogDataCatch.get(deviceId) == null) {
         if (catalogDataCatch.get(deviceId) == null) {
             return false;
             return false;
-        }else {
+        } else {
             return catalogDataCatch.isSyncRunning(deviceId);
             return catalogDataCatch.isSyncRunning(deviceId);
         }
         }
     }
     }

+ 19 - 26
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java

@@ -50,12 +50,12 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
 
 
     @Override
     @Override
     public void handForDevice(RequestEvent evt, Device device, Element element) {
     public void handForDevice(RequestEvent evt, Device device, Element element) {
-        Element rootElement = null;
-        try {
 
 
-            ServerTransaction serverTransaction = getServerTransaction(evt);
+        ServerTransaction serverTransaction = getServerTransaction(evt);
+
+        try {
+             Element rootElement = getRootElement(evt, device.getCharset());
 
 
-            rootElement = getRootElement(evt, device.getCharset());
             if (rootElement == null) {
             if (rootElement == null) {
                 logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest());
                 logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest());
                 responseAck(serverTransaction, Response.BAD_REQUEST);
                 responseAck(serverTransaction, Response.BAD_REQUEST);
@@ -66,32 +66,29 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
             //该字段可能为通道或则设备的id
             //该字段可能为通道或则设备的id
             String deviceId = getText(rootElement, "DeviceID");
             String deviceId = getText(rootElement, "DeviceID");
             String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId;
             String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId;
-            if (snElement == null ||  presetListNumElement == null) {
+            if (snElement == null || presetListNumElement == null) {
                 responseAck(serverTransaction, Response.BAD_REQUEST, "xml error");
                 responseAck(serverTransaction, Response.BAD_REQUEST, "xml error");
                 return;
                 return;
             }
             }
             int sumNum = Integer.parseInt(presetListNumElement.attributeValue("Num"));
             int sumNum = Integer.parseInt(presetListNumElement.attributeValue("Num"));
             List<PresetQuerySipReq> presetQuerySipReqList = new ArrayList<>();
             List<PresetQuerySipReq> presetQuerySipReqList = new ArrayList<>();
             if (sumNum > 0) {
             if (sumNum > 0) {
-                for (Iterator<Element> presetIterator =  presetListNumElement.elementIterator();presetIterator.hasNext();){
+                for (Iterator<Element> presetIterator = presetListNumElement.elementIterator(); presetIterator.hasNext(); ) {
                     Element itemListElement = presetIterator.next();
                     Element itemListElement = presetIterator.next();
                     PresetQuerySipReq presetQuerySipReq = new PresetQuerySipReq();
                     PresetQuerySipReq presetQuerySipReq = new PresetQuerySipReq();
-                    for (Iterator<Element> itemListIterator =  itemListElement.elementIterator();itemListIterator.hasNext();){
-                                // 遍历item
-                                Element itemOne = itemListIterator.next();
-                                String name = itemOne.getName();
-                                String textTrim = itemOne.getTextTrim();
-                                if("PresetID".equalsIgnoreCase(name)){
-                                    presetQuerySipReq.setPresetId(textTrim);
-                                }else {
-                                    presetQuerySipReq.setPresetName(textTrim);
-                                }
+                    for (Iterator<Element> itemListIterator = itemListElement.elementIterator(); itemListIterator.hasNext(); ) {
+                        // 遍历item
+                        Element itemOne = itemListIterator.next();
+                        String name = itemOne.getName();
+                        String textTrim = itemOne.getTextTrim();
+                        if ("PresetID".equalsIgnoreCase(name)) {
+                            presetQuerySipReq.setPresetId(textTrim);
+                        } else {
+                            presetQuerySipReq.setPresetName(textTrim);
+                        }
                     }
                     }
                     presetQuerySipReqList.add(presetQuerySipReq);
                     presetQuerySipReqList.add(presetQuerySipReq);
-
-
                 }
                 }
-
             }
             }
             RequestMessage requestMessage = new RequestMessage();
             RequestMessage requestMessage = new RequestMessage();
             requestMessage.setKey(key);
             requestMessage.setKey(key);
@@ -99,13 +96,9 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
             deferredResultHolder.invokeAllResult(requestMessage);
             deferredResultHolder.invokeAllResult(requestMessage);
             responseAck(serverTransaction, Response.OK);
             responseAck(serverTransaction, Response.OK);
         } catch (DocumentException e) {
         } catch (DocumentException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        } catch (ParseException e) {
-            e.printStackTrace();
-        } catch (SipException e) {
-            e.printStackTrace();
+            logger.error("[解析xml]失败: ", e);
+        } catch (InvalidArgumentException | ParseException | SipException e) {
+            logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
         }
         }
     }
     }
 
 

+ 32 - 25
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java

@@ -1,18 +1,27 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
 package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
 
 
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
 import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
 import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import gov.nist.javax.sip.ResponseEventExt;
 import gov.nist.javax.sip.ResponseEventExt;
+import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.stack.SIPClientTransaction;
 import gov.nist.javax.sip.stack.SIPClientTransaction;
 import gov.nist.javax.sip.stack.SIPDialog;
 import gov.nist.javax.sip.stack.SIPDialog;
+import gov.nist.javax.sip.stack.SIPTransaction;
+import gov.nist.javax.sip.stack.SIPTransactionImpl;
 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.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
 import javax.sdp.SdpFactory;
 import javax.sdp.SdpFactory;
@@ -54,6 +63,20 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 	@Autowired
 	@Autowired
 	private GitUtil gitUtil;
 	private GitUtil gitUtil;
 
 
+	@Autowired
+	private ISIPCommander commander;
+
+	@Autowired
+	private IDeviceService deviceService;
+
+	@Autowired
+	private SIPRequestHeaderProvider headerProvider;
+
+	@Autowired
+	@Qualifier(value="udpSipProvider")
+	private SipProviderImpl udpSipProvider;
+
+
 	@Override
 	@Override
 	public void afterPropertiesSet() throws Exception {
 	public void afterPropertiesSet() throws Exception {
 		// 添加消息处理的订阅
 		// 添加消息处理的订阅
@@ -71,7 +94,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 	@Override
 	@Override
 	public void process(ResponseEvent evt ){
 	public void process(ResponseEvent evt ){
 		try {
 		try {
-			Response response = evt.getResponse();
+
+			SIPResponse response = (SIPResponse)evt.getResponse();
 			int statusCode = response.getStatusCode();
 			int statusCode = response.getStatusCode();
 			// trying不会回复
 			// trying不会回复
 			if (statusCode == Response.TRYING) {
 			if (statusCode == Response.TRYING) {
@@ -80,10 +104,7 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 			// 下发ack
 			// 下发ack
 			if (statusCode == Response.OK) {
 			if (statusCode == Response.OK) {
 				ResponseEventExt event = (ResponseEventExt)evt;
 				ResponseEventExt event = (ResponseEventExt)evt;
-				SIPDialog dialog = new SIPDialog((SIPClientTransaction) event.getClientTransaction(), (SIPResponse) event.getResponse());
-				CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
-				Request reqAck = dialog.createAck(cseq.getSeqNumber());
-				SipURI requestURI = (SipURI) reqAck.getRequestURI();
+
 				String contentString = new String(response.getRawContent());
 				String contentString = new String(response.getRawContent());
 				// jainSip不支持y=字段, 移除以解析。
 				// jainSip不支持y=字段, 移除以解析。
 				int ssrcIndex = contentString.indexOf("y=");
 				int ssrcIndex = contentString.indexOf("y=");
@@ -96,29 +117,15 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 				} else {
 				} else {
 					sdp = SdpFactory.getInstance().createSessionDescription(contentString);
 					sdp = SdpFactory.getInstance().createSessionDescription(contentString);
 				}
 				}
-				requestURI.setUser(sdp.getOrigin().getUsername());
-				try {
-					requestURI.setHost(event.getRemoteIpAddress());
-				} catch (ParseException e) {
-					e.printStackTrace();
-				}
-				requestURI.setPort(event.getRemotePort());
-				reqAck.setRequestURI(requestURI);
-				UserAgentHeader userAgentHeader = SipUtils.createUserAgentHeader(sipFactory, gitUtil);
-				reqAck.addHeader(userAgentHeader);
-				Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-				reqAck.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
-				logger.info("[回复ack] {}-> {}:{} ",requestURI, event.getRemoteIpAddress(), event.getRemotePort());
+				SipURI requestUri = sipFactory.createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
+				Request reqAck = headerProvider.createAckRequest(requestUri, response);
 
 
-				dialog.sendAck(reqAck);
+				logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort());
+				commander.transmitRequest(response.getTopmostViaHeader().getTransport(), reqAck, null, null);
 
 
 			}
 			}
-		} catch (InvalidArgumentException | SipException e) {
-			e.printStackTrace();
-		} catch (ParseException e) {
-			throw new RuntimeException(e);
-		} catch (SdpParseException e) {
-			throw new RuntimeException(e);
+		} catch (InvalidArgumentException | ParseException | SipException | SdpParseException e) {
+			logger.info("[点播回复ACK],异常:", e );
 		}
 		}
 	}
 	}
 
 

+ 8 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java

@@ -15,10 +15,13 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
+import javax.sip.InvalidArgumentException;
 import javax.sip.ResponseEvent;
 import javax.sip.ResponseEvent;
+import javax.sip.SipException;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.WWWAuthenticateHeader;
 import javax.sip.header.WWWAuthenticateHeader;
 import javax.sip.message.Response;
 import javax.sip.message.Response;
+import java.text.ParseException;
 
 
 /**    
 /**    
  * @description:Register响应处理器
  * @description:Register响应处理器
@@ -87,7 +90,11 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
 
 
 		if (response.getStatusCode() == Response.UNAUTHORIZED) {
 		if (response.getStatusCode() == Response.UNAUTHORIZED) {
 			WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
 			WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
-			sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister());
+			try {
+				sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister());
+			} catch (SipException | InvalidArgumentException | ParseException e) {
+				logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
+			}
 		}else if (response.getStatusCode() == Response.OK){
 		}else if (response.getStatusCode() == Response.OK){
 
 
 			if (platformRegisterInfo.isRegister()) {
 			if (platformRegisterInfo.isRegister()) {

+ 43 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java

@@ -70,4 +70,47 @@ public class SipUtils {
         return String.valueOf(System.currentTimeMillis());
         return String.valueOf(System.currentTimeMillis());
     }
     }
 
 
+
+    /**
+     * 云台指令码计算
+     *
+     * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
+     * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
+     * @param moveSpeed  镜头移动速度 默认 0XFF (0-255)
+     * @param zoomSpeed  镜头缩放速度 默认 0X1 (0-255)
+     */
+    public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) {
+        int cmdCode = 0;
+        if (leftRight == 2) {
+            cmdCode|=0x01;		// 右移
+        } else if(leftRight == 1) {
+            cmdCode|=0x02;		// 左移
+        }
+        if (upDown == 2) {
+            cmdCode|=0x04;		// 下移
+        } else if(upDown == 1) {
+            cmdCode|=0x08;		// 上移
+        }
+        if (inOut == 2) {
+            cmdCode |= 0x10;	// 放大
+        } else if(inOut == 1) {
+            cmdCode |= 0x20;	// 缩小
+        }
+        StringBuilder builder = new StringBuilder("A50F01");
+        String strTmp;
+        strTmp = String.format("%02X", cmdCode);
+        builder.append(strTmp, 0, 2);
+        strTmp = String.format("%02X", moveSpeed);
+        builder.append(strTmp, 0, 2);
+        builder.append(strTmp, 0, 2);
+        strTmp = String.format("%X", zoomSpeed);
+        builder.append(strTmp, 0, 1).append("0");
+        //计算校验码
+        int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100;
+        strTmp = String.format("%02X", checkCode);
+        builder.append(strTmp, 0, 2);
+        return builder.toString();
+    }
+
 }
 }

+ 42 - 7
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm;
 package com.genersoft.iot.vmp.media.zlm;
 
 
+import java.text.ParseException;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -7,6 +8,7 @@ import java.util.Map;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 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.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
@@ -31,6 +33,8 @@ import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
 
 
 /**    
 /**    
  * @description:针对 ZLMediaServer的hook事件监听
  * @description:针对 ZLMediaServer的hook事件监听
@@ -58,6 +62,9 @@ public class ZLMHttpHookListener {
 	@Autowired
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 	private IRedisCatchStorage redisCatchStorage;
 
 
+	@Autowired
+	private IDeviceService deviceService;
+
 	@Autowired
 	@Autowired
 	private IMediaServerService mediaServerService;
 	private IMediaServerService mediaServerService;
 
 
@@ -515,7 +522,11 @@ public class ZLMHttpHookListener {
 							String platformId = sendRtpItem.getPlatformId();
 							String platformId = sendRtpItem.getPlatformId();
 							ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
 							ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
 
 
-							commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
+							try {
+								commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
+							} catch (SipException | InvalidArgumentException | ParseException e) {
+								logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+							}
 						}
 						}
 					}
 					}
 				}
 				}
@@ -552,21 +563,41 @@ public class ZLMHttpHookListener {
 					if (sendRtpItems.size() > 0) {
 					if (sendRtpItems.size() > 0) {
 						for (SendRtpItem sendRtpItem : sendRtpItems) {
 						for (SendRtpItem sendRtpItem : sendRtpItems) {
 							ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
 							ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-							commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+							try {
+								commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+							} catch (SipException | InvalidArgumentException | ParseException e) {
+								logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+							}
 							redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
 							redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
 									sendRtpItem.getCallId(), sendRtpItem.getStreamId());
 									sendRtpItem.getCallId(), sendRtpItem.getStreamId());
 						}
 						}
 					}
 					}
 				}
 				}
-				cmder.streamByeCmd(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId(),
-						streamInfoForPlayCatch.getStream(), null);
+				Device device = deviceService.queryDevice(streamInfoForPlayCatch.getDeviceID());
+				if (device != null) {
+					try {
+						cmder.streamByeCmd(device, streamInfoForPlayCatch.getChannelId(),
+								streamInfoForPlayCatch.getStream(), null);
+					} catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+						logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
+					}
+				}
+
 				redisCatchStorage.stopPlay(streamInfoForPlayCatch);
 				redisCatchStorage.stopPlay(streamInfoForPlayCatch);
 				storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
 				storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
 			}else{
 			}else{
 				StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, streamId, null);
 				StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, streamId, null);
 				if (streamInfoForPlayBackCatch != null) {
 				if (streamInfoForPlayBackCatch != null) {
-					cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(),
-							streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
+					Device device = deviceService.queryDevice(streamInfoForPlayCatch.getDeviceID());
+					if (device != null) {
+						try {
+							cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
+									streamInfoForPlayBackCatch.getStream(), null);
+						} catch (InvalidArgumentException | ParseException | SipException |
+								 SsrcTransactionNotFoundException e) {
+							logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
+						}
+					}
 					redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
 					redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
 							streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
 							streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
 				}else {
 				}else {
@@ -689,7 +720,11 @@ public class ZLMHttpHookListener {
 		if (sendRtpItems.size() > 0) {
 		if (sendRtpItems.size() > 0) {
 			for (SendRtpItem sendRtpItem : sendRtpItems) {
 			for (SendRtpItem sendRtpItem : sendRtpItems) {
 				ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
 				ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-				commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+				try {
+					commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+				} catch (SipException | InvalidArgumentException | ParseException e) {
+					logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+				}
 				redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
 				redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
 						sendRtpItem.getCallId(), sendRtpItem.getStreamId());
 						sendRtpItem.getCallId(), sendRtpItem.getStreamId());
 			}
 			}

+ 11 - 12
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java

@@ -68,7 +68,6 @@ public class ZLMMediaListManager {
     private Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
     private Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
 
 
     public StreamPushItem addPush(MediaItem mediaItem) {
     public StreamPushItem addPush(MediaItem mediaItem) {
-        // 查找此直播流是否存在redis预设gbId
         StreamPushItem transform = streamPushService.transform(mediaItem);
         StreamPushItem transform = streamPushService.transform(mediaItem);
         StreamPushItem pushInDb = streamPushService.getPush(mediaItem.getApp(), mediaItem.getStream());
         StreamPushItem pushInDb = streamPushService.getPush(mediaItem.getApp(), mediaItem.getStream());
         transform.setPushIng(mediaItem.isRegist());
         transform.setPushIng(mediaItem.isRegist());
@@ -82,15 +81,14 @@ public class ZLMMediaListManager {
             streamPushMapper.update(transform);
             streamPushMapper.update(transform);
             gbStreamMapper.updateMediaServer(mediaItem.getApp(), mediaItem.getStream(), mediaItem.getMediaServerId());
             gbStreamMapper.updateMediaServer(mediaItem.getApp(), mediaItem.getStream(), mediaItem.getMediaServerId());
         }
         }
-        if (transform != null) {
-            if (getChannelOnlineEventLister(transform.getApp(), transform.getStream()) != null)  {
-                try {
-                    getChannelOnlineEventLister(transform.getApp(), transform.getStream()).run(transform.getApp(), transform.getStream(), transform.getServerId());
-                } catch (ParseException e) {
-                    throw new RuntimeException(e);
-                }
-                removedChannelOnlineEventLister(transform.getApp(), transform.getStream());
+        ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream());
+        if ( channelOnlineEventLister != null)  {
+            try {
+                channelOnlineEventLister.run(transform.getApp(), transform.getStream(), transform.getServerId());;
+            } catch (ParseException e) {
+                logger.error("addPush: ", e);
             }
             }
+            removedChannelOnlineEventLister(transform.getApp(), transform.getStream());
         }
         }
         return transform;
         return transform;
     }
     }
@@ -99,11 +97,12 @@ public class ZLMMediaListManager {
         MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
         MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
         // 查看推流状态
         // 查看推流状态
         if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) {
         if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) {
-            if (getChannelOnlineEventLister(app, stream) != null)  {
+            ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
+            if (channelOnlineEventLister != null)  {
                 try {
                 try {
-                    getChannelOnlineEventLister(app, stream).run(app, stream, mediaServerId);
+                    channelOnlineEventLister.run(app, stream, mediaServerId);
                 } catch (ParseException e) {
                 } catch (ParseException e) {
-                    throw new RuntimeException(e);
+                    logger.error("sendStreamEvent: ", e);
                 }
                 }
                 removedChannelOnlineEventLister(app, stream);
                 removedChannelOnlineEventLister(app, stream);
             }
             }

+ 36 - 7
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java

@@ -26,6 +26,9 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
@@ -95,7 +98,11 @@ public class DeviceServiceImpl implements IDeviceService {
             logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId());
             logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId());
             deviceMapper.add(device);
             deviceMapper.add(device);
             redisCatchStorage.updateDevice(device);
             redisCatchStorage.updateDevice(device);
-            commander.deviceInfoQuery(device);
+            try {
+                commander.deviceInfoQuery(device);
+            } catch (InvalidArgumentException | SipException | ParseException e) {
+                logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
+            }
             sync(device);
             sync(device);
         }else {
         }else {
             if(device.getOnline() == 0){
             if(device.getOnline() == 0){
@@ -104,7 +111,11 @@ public class DeviceServiceImpl implements IDeviceService {
                 logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId());
                 logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId());
                 deviceMapper.update(device);
                 deviceMapper.update(device);
                 redisCatchStorage.updateDevice(device);
                 redisCatchStorage.updateDevice(device);
-                commander.deviceInfoQuery(device);
+                try {
+                    commander.deviceInfoQuery(device);
+                } catch (InvalidArgumentException | SipException | ParseException e) {
+                    logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
+                }
                 sync(device);
                 sync(device);
                 // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台
                 // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台
             }else {
             }else {
@@ -129,6 +140,7 @@ public class DeviceServiceImpl implements IDeviceService {
 
 
     @Override
     @Override
     public void offline(String deviceId) {
     public void offline(String deviceId) {
+        logger.info("[设备离线], device:{}", deviceId);
         Device device = deviceMapper.getDeviceByDeviceId(deviceId);
         Device device = deviceMapper.getDeviceByDeviceId(deviceId);
         if (device == null) {
         if (device == null) {
             return;
             return;
@@ -238,15 +250,28 @@ public class DeviceServiceImpl implements IDeviceService {
         }
         }
         int sn = (int)((Math.random()*9+1)*100000);
         int sn = (int)((Math.random()*9+1)*100000);
         catalogResponseMessageHandler.setChannelSyncReady(device, sn);
         catalogResponseMessageHandler.setChannelSyncReady(device, sn);
-        sipCommander.catalogQuery(device, sn, event -> {
-            String errorMsg = String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg);
+        try {
+            sipCommander.catalogQuery(device, sn, event -> {
+                String errorMsg = String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg);
+                catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg);
+            });
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[同步通道], 信令发送失败:{}", e.getMessage() );
+            String errorMsg = String.format("同步通道失败,信令发送失败: %s", e.getMessage());
             catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg);
             catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg);
-        });
+        }
     }
     }
 
 
     @Override
     @Override
     public Device queryDevice(String deviceId) {
     public Device queryDevice(String deviceId) {
-        return deviceMapper.getDeviceByDeviceId(deviceId);
+        Device device = redisCatchStorage.getDevice(deviceId);
+        if (device == null) {
+            device = deviceMapper.getDeviceByDeviceId(deviceId);
+            if (device != null) {
+                redisCatchStorage.updateDevice(device);
+            }
+        }
+        return device;
     }
     }
 
 
     @Override
     @Override
@@ -266,7 +291,11 @@ public class DeviceServiceImpl implements IDeviceService {
         if (device == null || device.getOnline() == 0) {
         if (device == null || device.getOnline() == 0) {
             return;
             return;
         }
         }
-        sipCommander.deviceStatusQuery(device, null);
+        try {
+            sipCommander.deviceStatusQuery(device, null);
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 设备状态查询: {}", e.getMessage());
+        }
 
 
     }
     }
 
 

+ 75 - 43
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java

@@ -20,7 +20,10 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
 import javax.sip.TimeoutEvent;
 import javax.sip.TimeoutEvent;
+import java.text.ParseException;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -99,9 +102,13 @@ public class PlatformServiceImpl implements IPlatformService {
         if (parentPlatform.isEnable()) {
         if (parentPlatform.isEnable()) {
             // 保存时启用就发送注册
             // 保存时启用就发送注册
             // 注册成功时由程序直接调用了online方法
             // 注册成功时由程序直接调用了online方法
-            commanderForPlatform.register(parentPlatform, eventResult -> {
-                logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
-            }, null);
+            try {
+                commanderForPlatform.register(parentPlatform, eventResult -> {
+                    logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
+                }, null);
+            } catch (InvalidArgumentException | ParseException | SipException e) {
+                logger.error("[命令发送失败] 国标级联: {}", e.getMessage());
+            }
         }
         }
         return result > 0;
         return result > 0;
     }
     }
@@ -130,46 +137,62 @@ public class PlatformServiceImpl implements IPlatformService {
         // 添加注册任务
         // 添加注册任务
         dynamicTask.startDelay(registerTaskKey,
         dynamicTask.startDelay(registerTaskKey,
                 // 注册失败(注册成功时由程序直接调用了online方法)
                 // 注册失败(注册成功时由程序直接调用了online方法)
-                ()->commanderForPlatform.register(parentPlatform, eventResult -> offline(parentPlatform),null),
+                ()-> {
+                    try {
+                        commanderForPlatform.register(parentPlatform, eventResult -> offline(parentPlatform),null);
+                    } catch (InvalidArgumentException | ParseException | SipException e) {
+                        logger.error("[命令发送失败] 国标级联定时注册: {}", e.getMessage());
+                    }
+                },
                 (parentPlatform.getExpires() - 10) *1000);
                 (parentPlatform.getExpires() - 10) *1000);
 
 
         final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
         final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
         if (!dynamicTask.contains(keepaliveTaskKey)) {
         if (!dynamicTask.contains(keepaliveTaskKey)) {
             // 添加心跳任务
             // 添加心跳任务
             dynamicTask.startCron(keepaliveTaskKey,
             dynamicTask.startCron(keepaliveTaskKey,
-                    ()-> commanderForPlatform.keepalive(parentPlatform, eventResult -> {
-                        // 心跳失败
-                        if (eventResult.type == SipSubscribe.EventResultType.timeout) {
-                            // 心跳超时
-                            ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
-                            // 此时是第三次心跳超时, 平台离线
-                            if (platformCatch.getKeepAliveReply()  == 2) {
-                                // 设置平台离线,并重新注册
-                                offline(parentPlatform);
-                                logger.info("[国标级联] {},三次心跳超时后再次发起注册", parentPlatform.getServerGBId());
-                                commanderForPlatform.register(parentPlatform, eventResult1 -> {
-                                    logger.info("[国标级联] {},三次心跳超时后再次发起注册仍然失败,开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId());
-                                    // 添加注册任务
-                                    dynamicTask.startCron(registerTaskKey,
-                                            // 注册失败(注册成功时由程序直接调用了online方法)
-                                            ()->logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()),
-                                            60*1000);
-                                }, null);
-                            }
+                    ()-> {
+                        try {
+                            commanderForPlatform.keepalive(parentPlatform, eventResult -> {
+                                // 心跳失败
+                                if (eventResult.type == SipSubscribe.EventResultType.timeout) {
+                                    // 心跳超时
+                                    ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
+                                    // 此时是第三次心跳超时, 平台离线
+                                    if (platformCatch.getKeepAliveReply()  == 2) {
+                                        // 设置平台离线,并重新注册
+                                        offline(parentPlatform);
+                                        logger.info("[国标级联] {},三次心跳超时后再次发起注册", parentPlatform.getServerGBId());
+                                        try {
+                                            commanderForPlatform.register(parentPlatform, eventResult1 -> {
+                                                logger.info("[国标级联] {},三次心跳超时后再次发起注册仍然失败,开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId());
+                                                // 添加注册任务
+                                                dynamicTask.startCron(registerTaskKey,
+                                                        // 注册失败(注册成功时由程序直接调用了online方法)
+                                                        ()->logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()),
+                                                        60*1000);
+                                            }, null);
+                                        } catch (InvalidArgumentException | ParseException | SipException e) {
+                                            logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
+                                        }
+                                    }
 
 
-                        }else {
-                            logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg);
-                        }
+                                }else {
+                                    logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg);
+                                }
 
 
-                    }, eventResult -> {
-                        // 心跳成功
-                        // 清空之前的心跳超时计数
-                        ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
-                        if (platformCatch.getKeepAliveReply() > 0) {
-                            platformCatch.setKeepAliveReply(0);
-                            redisCatchStorage.updatePlatformCatchInfo(platformCatch);
+                            }, eventResult -> {
+                                // 心跳成功
+                                // 清空之前的心跳超时计数
+                                ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
+                                if (platformCatch.getKeepAliveReply() > 0) {
+                                    platformCatch.setKeepAliveReply(0);
+                                    redisCatchStorage.updatePlatformCatchInfo(platformCatch);
+                                }
+                            });
+                        } catch (SipException | InvalidArgumentException | ParseException e) {
+                            logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
                         }
                         }
-                    }),
+                    },
                     (parentPlatform.getKeepTimeout() - 10)*1000);
                     (parentPlatform.getKeepTimeout() - 10)*1000);
         }
         }
     }
     }
@@ -225,14 +248,18 @@ public class PlatformServiceImpl implements IPlatformService {
     @Override
     @Override
     public void login(ParentPlatform parentPlatform) {
     public void login(ParentPlatform parentPlatform) {
         final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
         final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
-        commanderForPlatform.register(parentPlatform, eventResult1 -> {
-            logger.info("[国标级联] {},开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId());
-            // 添加注册任务
-            dynamicTask.startCron(registerTaskKey,
-                    // 注册失败(注册成功时由程序直接调用了online方法)
-                    ()->logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()),
-                    60*1000);
-        }, null);
+        try {
+            commanderForPlatform.register(parentPlatform, eventResult1 -> {
+                logger.info("[国标级联] {},开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId());
+                // 添加注册任务
+                dynamicTask.startCron(registerTaskKey,
+                        // 注册失败(注册成功时由程序直接调用了online方法)
+                        ()->logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()),
+                        60*1000);
+            }, null);
+        } catch (InvalidArgumentException | ParseException | SipException e) {
+            logger.error("[命令发送失败] 国标级联注册: {}", e.getMessage());
+        }
     }
     }
 
 
     @Override
     @Override
@@ -259,7 +286,12 @@ public class PlatformServiceImpl implements IPlatformService {
                         continue;
                         continue;
                     }
                     }
                     // 发送GPS消息
                     // 发送GPS消息
-                    commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, subscribe);
+                    try {
+                        commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, subscribe);
+                    } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
+                             IllegalAccessException e) {
+                        logger.error("[命令发送失败] 国标级联 移动位置通知: {}", e.getMessage());
+                    }
                 }
                 }
             }
             }
         }
         }

+ 209 - 159
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -2,11 +2,17 @@ package com.genersoft.iot.vmp.service.impl;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.math.RoundingMode;
+import java.text.ParseException;
 import java.util.*;
 import java.util.*;
 
 
+import javax.sip.InvalidArgumentException;
 import javax.sip.ResponseEvent;
 import javax.sip.ResponseEvent;
+import javax.sip.SipException;
 
 
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
@@ -22,13 +28,6 @@ import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.UserSetting;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
-import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
-import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
-import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -92,6 +91,10 @@ public class PlayServiceImpl implements IPlayService {
     @Autowired
     @Autowired
     private VideoStreamSessionManager streamSession;
     private VideoStreamSessionManager streamSession;
 
 
+
+    @Autowired
+    private IDeviceService deviceService;
+
     @Autowired
     @Autowired
     private UserSetting userSetting;
     private UserSetting userSetting;
 
 
@@ -261,14 +264,14 @@ public class PlayServiceImpl implements IPlayService {
         System.out.println("设置超时任务: " + timeOutTaskKey);
         System.out.println("设置超时任务: " + timeOutTaskKey);
         dynamicTask.startDelay( timeOutTaskKey,()->{
         dynamicTask.startDelay( timeOutTaskKey,()->{
 
 
-            SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
-            if (dialog != null) {
-                logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, finalSsrcInfo.getPort(), finalSsrcInfo.getSsrc());
-                timeoutCallback.run(1, "收流超时");
-                // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
-                cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null);
-            }else {
-                logger.info("[点播超时] 消息未响应 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, finalSsrcInfo.getPort(), finalSsrcInfo.getSsrc());
+            logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, finalSsrcInfo.getPort(), finalSsrcInfo.getSsrc());
+            timeoutCallback.run(1, "收流超时");
+            // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
+            try {
+                cmder.streamByeCmd(device, channelId, finalSsrcInfo.getStream(), null);
+            } catch (InvalidArgumentException | ParseException | SipException e) {
+                logger.error("[点播超时], 发送BYE失败 {}", e.getMessage());
+            } catch (SsrcTransactionNotFoundException e) {
                 timeoutCallback.run(0, "点播超时");
                 timeoutCallback.run(0, "点播超时");
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
                 mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
                 mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
@@ -282,73 +285,87 @@ public class PlayServiceImpl implements IPlayService {
             logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
             logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
             return;
             return;
         }
         }
-        cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
-            logger.info("收到订阅消息: " + response.toJSONString());
-            System.out.println("停止超时任务: " + timeOutTaskKey);
-            dynamicTask.stop(timeOutTaskKey);
-            // hook响应
-            onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
-            hookEvent.response(mediaServerItemInuse, response);
-            logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
-
-        }, (event) -> {
-            ResponseEvent responseEvent = (ResponseEvent)event.event;
-            String contentString = new String(responseEvent.getResponse().getRawContent());
-            // 获取ssrc
-            int ssrcIndex = contentString.indexOf("y=");
-            // 检查是否有y字段
-            if (ssrcIndex >= 0) {
-                //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
-                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
-                // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
-                if (ssrc.equals(ssrcInResponse)) {
-                    return;
-                }
-                logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
-                if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
-                    logger.info("[点播消息] SSRC修正 {}->{}", ssrc, ssrcInResponse);
-
-                    if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
-                        // ssrc 不可用
-                        // 释放ssrc
-                        mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
-                        streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
-                        event.msg = "下级自定义了ssrc,但是此ssrc不可用";
-                        event.statusCode = 400;
-                        errorEvent.response(event);
+        try {
+            cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
+                logger.info("收到订阅消息: " + response.toJSONString());
+                System.out.println("停止超时任务: " + timeOutTaskKey);
+                dynamicTask.stop(timeOutTaskKey);
+                // hook响应
+                onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
+                hookEvent.response(mediaServerItemInuse, response);
+                logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
+
+            }, (event) -> {
+                ResponseEvent responseEvent = (ResponseEvent)event.event;
+                String contentString = new String(responseEvent.getResponse().getRawContent());
+                // 获取ssrc
+                int ssrcIndex = contentString.indexOf("y=");
+                // 检查是否有y字段
+                if (ssrcIndex >= 0) {
+                    //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
+                    String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+                    // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
+                    if (ssrc.equals(ssrcInResponse)) {
                         return;
                         return;
                     }
                     }
+                    logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
+                    if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
+                        logger.info("[点播消息] SSRC修正 {}->{}", ssrc, ssrcInResponse);
+
+                        if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
+                            // ssrc 不可用
+                            // 释放ssrc
+                            mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
+                            streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
+                            event.msg = "下级自定义了ssrc,但是此ssrc不可用";
+                            event.statusCode = 400;
+                            errorEvent.response(event);
+                            return;
+                        }
 
 
-                    // 单端口模式streamId也有变化,需要重新设置监听
-                    if (!mediaServerItem.isRtpEnable()) {
-                        // 添加订阅
-                        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
-                        subscribe.removeSubscribe(hookSubscribe);
-                        hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
-                        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{
-                                    logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
-                                    dynamicTask.stop(timeOutTaskKey);
-                                    // hook响应
-                                    onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid);
-                                    hookEvent.response(mediaServerItemInUse, response);
-                                });
-                    }
-                    // 关闭rtp server
-                    mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
-                    // 重新开启ssrc server
-                    mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, finalSsrcInfo.getPort());
+                        // 单端口模式streamId也有变化,需要重新设置监听
+                        if (!mediaServerItem.isRtpEnable()) {
+                            // 添加订阅
+                            HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
+                            subscribe.removeSubscribe(hookSubscribe);
+                            hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
+                            subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{
+                                        logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
+                                        dynamicTask.stop(timeOutTaskKey);
+                                        // hook响应
+                                        onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid);
+                                        hookEvent.response(mediaServerItemInUse, response);
+                                    });
+                        }
+                        // 关闭rtp server
+                        mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
+                        // 重新开启ssrc server
+                        mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, finalSsrcInfo.getPort());
 
 
+                    }
                 }
                 }
-            }
-        }, (event) -> {
+            }, (event) -> {
+                dynamicTask.stop(timeOutTaskKey);
+                mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
+                // 释放ssrc
+                mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
+
+                streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
+                errorEvent.response(event);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+
+            logger.error("[命令发送失败] 点播消息: {}", e.getMessage());
             dynamicTask.stop(timeOutTaskKey);
             dynamicTask.stop(timeOutTaskKey);
             mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
             mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream());
             // 释放ssrc
             // 释放ssrc
             mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
             mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
 
 
             streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
             streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
-            errorEvent.response(event);
-        });
+            SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
+            eventResult.msg = "命令发送失败";
+            errorEvent.response(eventResult);
+        }
     }
     }
 
 
     @Override
     @Override
@@ -439,17 +456,18 @@ public class PlayServiceImpl implements IPlayService {
             playBackResult.setCode(ErrorCode.ERROR100.getCode());
             playBackResult.setCode(ErrorCode.ERROR100.getCode());
             playBackResult.setMsg("回放超时");
             playBackResult.setMsg("回放超时");
             playBackResult.setData(requestMessage);
             playBackResult.setData(requestMessage);
-            SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream());
-            // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
-            if (dialog != null) {
+
+            try {
+                cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
+            } catch (InvalidArgumentException | ParseException | SipException e) {
+                logger.error("[录像流]回放超时 发送BYE失败 {}", e.getMessage());
+            } catch (SsrcTransactionNotFoundException e) {
                 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
                 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
-                cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
-            }else {
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                 streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
                 streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
             }
             }
-            cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
+
             // 回复之前所有的点播请求
             // 回复之前所有的点播请求
             playBackCallback.call(playBackResult);
             playBackCallback.call(playBackResult);
             result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "回放超时"));
             result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "回放超时"));
@@ -489,59 +507,67 @@ public class PlayServiceImpl implements IPlayService {
             playBackCallback.call(playBackResult);
             playBackCallback.call(playBackResult);
         };
         };
 
 
-        cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
-                hookEvent, eventResult -> {
-                    if (eventResult.type == SipSubscribe.EventResultType.response) {
-                        ResponseEvent responseEvent = (ResponseEvent)eventResult.event;
-                        String contentString = new String(responseEvent.getResponse().getRawContent());
-                        // 获取ssrc
-                        int ssrcIndex = contentString.indexOf("y=");
-                        // 检查是否有y字段
-                        if (ssrcIndex >= 0) {
-                            //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
-                            String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
-                            // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
-                            if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
-                                return;
-                            }
-                            logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
-                            if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
-                                logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
-
-                                if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
-                                    // ssrc 不可用
-                                    // 释放ssrc
-                                    mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
-                                    streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
-                                    eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
-                                    eventResult.statusCode = 400;
-                                    errorEvent.response(eventResult);
+        try {
+            cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
+                    hookEvent, eventResult -> {
+                        if (eventResult.type == SipSubscribe.EventResultType.response) {
+                            ResponseEvent responseEvent = (ResponseEvent)eventResult.event;
+                            String contentString = new String(responseEvent.getResponse().getRawContent());
+                            // 获取ssrc
+                            int ssrcIndex = contentString.indexOf("y=");
+                            // 检查是否有y字段
+                            if (ssrcIndex >= 0) {
+                                //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
+                                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+                                // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
+                                if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
                                     return;
                                     return;
                                 }
                                 }
-
-                                // 单端口模式streamId也有变化,需要重新设置监听
-                                if (!mediaServerItem.isRtpEnable()) {
-                                    // 添加订阅
-                                    HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
-                                    subscribe.removeSubscribe(hookSubscribe);
-                                    hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
-                                    subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{
-                                        logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
-                                        dynamicTask.stop(playBackTimeOutTaskKey);
-                                        // hook响应
-                                        onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid);
-                                        hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
-                                    });
+                                logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
+                                if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
+                                    logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
+
+                                    if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
+                                        // ssrc 不可用
+                                        // 释放ssrc
+                                        mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+                                        streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
+                                        eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
+                                        eventResult.statusCode = 400;
+                                        errorEvent.response(eventResult);
+                                        return;
+                                    }
+
+                                    // 单端口模式streamId也有变化,需要重新设置监听
+                                    if (!mediaServerItem.isRtpEnable()) {
+                                        // 添加订阅
+                                        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+                                        subscribe.removeSubscribe(hookSubscribe);
+                                        hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
+                                        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{
+                                            logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
+                                            dynamicTask.stop(playBackTimeOutTaskKey);
+                                            // hook响应
+                                            onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid);
+                                            hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
+                                        });
+                                    }
+                                    // 关闭rtp server
+                                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
+                                    // 重新开启ssrc server
+                                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
                                 }
                                 }
-                                // 关闭rtp server
-                                mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
-                                // 重新开启ssrc server
-                                mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
                             }
                             }
                         }
                         }
-                    }
 
 
-                }, errorEvent);
+                    }, errorEvent);
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 回放: {}", e.getMessage());
+
+            SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
+            eventResult.msg = "命令发送失败";
+            errorEvent.response(eventResult);
+        }
         return result;
         return result;
     }
     }
 
 
@@ -587,46 +613,57 @@ public class PlayServiceImpl implements IPlayService {
             downloadResult.setCode(ErrorCode.ERROR100.getCode());
             downloadResult.setCode(ErrorCode.ERROR100.getCode());
             downloadResult.setMsg("录像下载请求超时");
             downloadResult.setMsg("录像下载请求超时");
             hookCallBack.call(downloadResult);
             hookCallBack.call(downloadResult);
-            SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream());
+
             // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
             // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
-            if (dialog != null) {
-                // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
-                cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
-            }else {
+            try {
+                cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
+            } catch (InvalidArgumentException | ParseException | SipException e) {
+                logger.error("[录像流]录像下载请求超时, 发送BYE失败 {}", e.getMessage());
+            } catch (SsrcTransactionNotFoundException e) {
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                 streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
                 streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
             }
             }
-            cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
             // 回复之前所有的点播请求
             // 回复之前所有的点播请求
             hookCallBack.call(downloadResult);
             hookCallBack.call(downloadResult);
         }, userSetting.getPlayTimeout());
         }, userSetting.getPlayTimeout());
-        cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
-                inviteStreamInfo -> {
-                    logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
-                    dynamicTask.stop(downLoadTimeOutTaskKey);
-                    StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
-                    streamInfo.setStartTime(startTime);
-                    streamInfo.setEndTime(endTime);
-                    redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
-                    wvpResult.setCode(ErrorCode.SUCCESS.getCode());
-                    wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
-                    wvpResult.setData(streamInfo);
-                    downloadResult.setCode(ErrorCode.SUCCESS.getCode());
-                    downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
-                    downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
-                    downloadResult.setResponse(inviteStreamInfo.getResponse());
-                    hookCallBack.call(downloadResult);
-                }, event -> {
-                    dynamicTask.stop(downLoadTimeOutTaskKey);
-                    downloadResult.setCode(ErrorCode.ERROR100.getCode());
-                    downloadResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
-                    wvpResult.setCode(ErrorCode.ERROR100.getCode());
-                    wvpResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
-                    downloadResult.setEvent(event);
-                    hookCallBack.call(downloadResult);
-                    streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
-                });
+
+        SipSubscribe.Event errorEvent = event -> {
+            dynamicTask.stop(downLoadTimeOutTaskKey);
+            downloadResult.setCode(ErrorCode.ERROR100.getCode());
+            downloadResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
+            wvpResult.setCode(ErrorCode.ERROR100.getCode());
+            wvpResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
+            downloadResult.setEvent(event);
+            hookCallBack.call(downloadResult);
+            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
+        };
+
+        try {
+            cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
+                    inviteStreamInfo -> {
+                        logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
+                        dynamicTask.stop(downLoadTimeOutTaskKey);
+                        StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
+                        streamInfo.setStartTime(startTime);
+                        streamInfo.setEndTime(endTime);
+                        redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
+                        wvpResult.setCode(ErrorCode.SUCCESS.getCode());
+                        wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
+                        wvpResult.setData(streamInfo);
+                        downloadResult.setCode(ErrorCode.SUCCESS.getCode());
+                        downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
+                        downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
+                        downloadResult.setResponse(inviteStreamInfo.getResponse());
+                        hookCallBack.call(downloadResult);
+                    }, errorEvent);
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
+
+            SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
+            eventResult.msg = "命令发送失败";
+            errorEvent.response(eventResult);
+        }
         return result;
         return result;
     }
     }
 
 
@@ -705,7 +742,11 @@ public class PlayServiceImpl implements IPlayService {
             for (SendRtpItem sendRtpItem : sendRtpItems) {
             for (SendRtpItem sendRtpItem : sendRtpItems) {
                 if (sendRtpItem.getMediaServerId().equals(mediaServerId)) {
                 if (sendRtpItem.getMediaServerId().equals(mediaServerId)) {
                     ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
                     ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-                    sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId());
+                    try {
+                        sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId());
+                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                        logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -714,8 +755,17 @@ public class PlayServiceImpl implements IPlayService {
         if (allSsrc.size() > 0) {
         if (allSsrc.size() > 0) {
             for (SsrcTransaction ssrcTransaction : allSsrc) {
             for (SsrcTransaction ssrcTransaction : allSsrc) {
                 if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) {
                 if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) {
-                    cmder.streamByeCmd(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(),
-                            ssrcTransaction.getStream(), null);
+                    Device device = deviceService.queryDevice(ssrcTransaction.getDeviceId());
+                    if (device == null) {
+                        continue;
+                    }
+                    try {
+                        cmder.streamByeCmd(device, ssrcTransaction.getChannelId(),
+                                ssrcTransaction.getStream(), null);
+                    } catch (InvalidArgumentException | ParseException | SipException |
+                             SsrcTransactionNotFoundException e) {
+                        logger.error("[zlm离线]为正在使用此zlm的设备, 发送BYE失败 {}", e.getMessage());
+                    }
                 }
                 }
             }
             }
         }
         }

+ 18 - 4
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java

@@ -16,7 +16,9 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 
 
-import javax.validation.constraints.NotNull;
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.List;
 import java.util.List;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 
@@ -78,16 +80,28 @@ public class RedisAlarmMsgListener implements MessageListener {
                         List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
                         List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
                         if (parentPlatforms.size() > 0) {
                         if (parentPlatforms.size() > 0) {
                             for (ParentPlatform parentPlatform : parentPlatforms) {
                             for (ParentPlatform parentPlatform : parentPlatforms) {
-                                commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
+                                try {
+                                    commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
+                                } catch (SipException | InvalidArgumentException | ParseException e) {
+                                    logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+                                }
                             }
                             }
                         }
                         }
                     }else {
                     }else {
                         Device device = storage.queryVideoDevice(gbId);
                         Device device = storage.queryVideoDevice(gbId);
                         ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
                         ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
                         if (device != null && platform == null) {
                         if (device != null && platform == null) {
-                            commander.sendAlarmMessage(device, deviceAlarm);
+                            try {
+                                commander.sendAlarmMessage(device, deviceAlarm);
+                            } catch (InvalidArgumentException | SipException | ParseException e) {
+                                logger.error("[命令发送失败] 发送报警: {}", e.getMessage());
+                            }
                         }else if (device == null && platform != null){
                         }else if (device == null && platform != null){
-                            commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
+                            try {
+                                commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
+                            } catch (InvalidArgumentException | SipException | ParseException e) {
+                                logger.error("[命令发送失败] 发送报警: {}", e.getMessage());
+                            }
                         }else {
                         }else {
                             logger.warn("无法确定" + gbId + "是平台还是设备");
                             logger.warn("无法确定" + gbId + "是平台还是设备");
                         }
                         }

+ 16 - 7
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java

@@ -1,5 +1,6 @@
 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.List;
 import java.util.UUID;
 import java.util.UUID;
 
 
@@ -31,6 +32,9 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 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.SipException;
+
 /**
 /**
  *  位置信息管理
  *  位置信息管理
  */
  */
@@ -105,13 +109,18 @@ public class MobilePositionController {
         Device device = storager.queryVideoDevice(deviceId);
         Device device = storager.queryVideoDevice(deviceId);
         String uuid = UUID.randomUUID().toString();
         String uuid = UUID.randomUUID().toString();
         String key = DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId;
         String key = DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId;
-        cmder.mobilePostitionQuery(device, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-            msg.setKey(key);
-			msg.setData(String.format("获取移动位置信息失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
+        try {
+            cmder.mobilePostitionQuery(device, event -> {
+                RequestMessage msg = new RequestMessage();
+                msg.setId(uuid);
+                msg.setKey(key);
+                msg.setData(String.format("获取移动位置信息失败,错误码: %s, %s", event.statusCode, event.msg));
+                resultHolder.invokeResult(msg);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 获取移动位置信息: {}", e.getMessage());
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+        }
         DeferredResult<MobilePosition> result = new DeferredResult<MobilePosition>(5*1000L);
         DeferredResult<MobilePosition> result = new DeferredResult<MobilePosition>(5*1000L);
 		result.onTimeout(()->{
 		result.onTimeout(()->{
 			logger.warn(String.format("获取移动位置信息超时"));
 			logger.warn(String.format("获取移动位置信息超时"));

+ 20 - 2
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java

@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener;
 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
@@ -16,6 +17,8 @@ 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.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.ResponseEntity;
@@ -23,6 +26,9 @@ import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.List;
 import java.util.List;
@@ -33,6 +39,8 @@ import java.util.List;
 @RequestMapping("/api/alarm")
 @RequestMapping("/api/alarm")
 public class AlarmController {
 public class AlarmController {
 
 
+    private final static Logger logger = LoggerFactory.getLogger(AlarmController.class);
+
     @Autowired
     @Autowired
     private IDeviceAlarmService deviceAlarmService;
     private IDeviceAlarmService deviceAlarmService;
 
 
@@ -108,9 +116,19 @@ public class AlarmController {
         deviceAlarm.setLatitude(39.33333);
         deviceAlarm.setLatitude(39.33333);
 
 
         if (device != null && platform == null) {
         if (device != null && platform == null) {
-            commander.sendAlarmMessage(device, deviceAlarm);
+
+            try {
+                commander.sendAlarmMessage(device, deviceAlarm);
+            } catch (InvalidArgumentException | SipException | ParseException e) {
+
+            }
         }else if (device == null && platform != null){
         }else if (device == null && platform != null){
-            commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
+            try {
+                commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
+            } catch (SipException | InvalidArgumentException | ParseException e) {
+                logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+                throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " +  e.getMessage());
+            }
         }else {
         }else {
             throw new ControllerException(ErrorCode.ERROR100.getCode(),"无法确定" + deviceId + "是平台还是设备");
             throw new ControllerException(ErrorCode.ERROR100.getCode(),"无法确定" + deviceId + "是平台还是设备");
         }
         }

+ 31 - 16
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java

@@ -8,12 +8,14 @@
 package com.genersoft.iot.vmp.vmanager.gb28181.device;
 package com.genersoft.iot.vmp.vmanager.gb28181.device;
 
 
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
+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.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.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 
 
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 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;
@@ -26,6 +28,9 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name = "国标设备配置")
 @Tag(name = "国标设备配置")
@@ -75,14 +80,19 @@ public class DeviceConfig {
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
 		String uuid = UUID.randomUUID().toString();
 		String uuid = UUID.randomUUID().toString();
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId;
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId;
-		cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("设备配置操作失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
-        DeferredResult<String> result = new DeferredResult<String>(3 * 1000L);
+		try {
+			cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("设备配置操作失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 设备配置: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
+		DeferredResult<String> result = new DeferredResult<String>(3 * 1000L);
 		result.onTimeout(() -> {
 		result.onTimeout(() -> {
 			logger.warn(String.format("设备配置操作超时, 设备未返回应答指令"));
 			logger.warn(String.format("设备配置操作超时, 设备未返回应答指令"));
 			// 释放rtpserver
 			// 释放rtpserver
@@ -121,14 +131,19 @@ public class DeviceConfig {
 		String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId);
 		String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId);
 		String uuid = UUID.randomUUID().toString();
 		String uuid = UUID.randomUUID().toString();
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
-		cmder.deviceConfigQuery(device, channelId, configType, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("获取设备配置失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
-        DeferredResult<String> result = new DeferredResult<String > (3 * 1000L);
+		try {
+			cmder.deviceConfigQuery(device, channelId, configType, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("获取设备配置失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 获取设备配置: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
+		DeferredResult<String> result = new DeferredResult<String > (3 * 1000L);
 		result.onTimeout(()->{
 		result.onTimeout(()->{
 			logger.warn(String.format("获取设备配置超时"));
 			logger.warn(String.format("获取设备配置超时"));
 			// 释放rtpserver
 			// 释放rtpserver

+ 88 - 55
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java

@@ -29,6 +29,9 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name  = "国标设备控制")
 @Tag(name  = "国标设备控制")
@@ -61,10 +64,12 @@ public class DeviceControl {
             logger.debug("设备远程启动API调用");
             logger.debug("设备远程启动API调用");
         }
         }
         Device device = storager.queryVideoDevice(deviceId);
         Device device = storager.queryVideoDevice(deviceId);
-        if (!cmder.teleBootCmd(device)) {
-			logger.warn("设备远程启动API调用失败!");
-            throw new ControllerException(ErrorCode.ERROR100);
-        }
+		try {
+			cmder.teleBootCmd(device);
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 远程启动: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
     }
     }
 
 
     /**
     /**
@@ -101,13 +106,18 @@ public class DeviceControl {
 			return result;
 			return result;
 		}
 		}
 		resultHolder.put(key, uuid, result);
 		resultHolder.put(key, uuid, result);
-		cmder.recordCmd(device, channelId, recordCmdStr, event -> {
-            RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeAllResult(msg);
-		});
+		try {
+			cmder.recordCmd(device, channelId, recordCmdStr, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeAllResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 
 
 		return result;
 		return result;
 	}
 	}
@@ -123,21 +133,26 @@ public class DeviceControl {
 	@Parameter(name = "channelId", description = "通道国标编号", required = true)
 	@Parameter(name = "channelId", description = "通道国标编号", required = true)
 	@Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true)
 	@Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true)
 	@GetMapping("/guard/{deviceId}/{guardCmdStr}")
 	@GetMapping("/guard/{deviceId}/{guardCmdStr}")
-	public DeferredResult<ResponseEntity<String>> guardApi(@PathVariable String deviceId, String channelId, @PathVariable String guardCmdStr) {
+	public DeferredResult<String> guardApi(@PathVariable String deviceId, String channelId, @PathVariable String guardCmdStr) {
 		if (logger.isDebugEnabled()) {
 		if (logger.isDebugEnabled()) {
 			logger.debug("布防/撤防API调用");
 			logger.debug("布防/撤防API调用");
 		}
 		}
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId;
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId;
 		String uuid =UUID.randomUUID().toString();
 		String uuid =UUID.randomUUID().toString();
-		cmder.guardCmd(device, guardCmdStr, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
-        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		try {
+			cmder.guardCmd(device, guardCmdStr, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage());
+		}
+		DeferredResult<String> result = new DeferredResult<>(3 * 1000L);
 		resultHolder.put(key, uuid, result);
 		resultHolder.put(key, uuid, result);
 		result.onTimeout(() -> {
 		result.onTimeout(() -> {
 			logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令"));
 			logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令"));
@@ -174,14 +189,19 @@ public class DeviceControl {
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
 		String uuid = UUID.randomUUID().toString();
 		String uuid = UUID.randomUUID().toString();
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId;
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId;
-		cmder.alarmCmd(device, alarmMethod, alarmType, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
-        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		try {
+			cmder.alarmCmd(device, alarmMethod, alarmType, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 报警复位: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
+		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
 		result.onTimeout(() -> {
 		result.onTimeout(() -> {
 			logger.warn(String.format("报警复位操作超时, 设备未返回应答指令"));
 			logger.warn(String.format("报警复位操作超时, 设备未返回应答指令"));
 			// 释放rtpserver
 			// 释放rtpserver
@@ -205,23 +225,23 @@ public class DeviceControl {
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "channelId", description = "通道国标编号")
 	@Parameter(name = "channelId", description = "通道国标编号")
 	@GetMapping("/i_frame/{deviceId}")
 	@GetMapping("/i_frame/{deviceId}")
-	public ResponseEntity<String> iFrame(@PathVariable String deviceId,
+	public JSONObject iFrame(@PathVariable String deviceId,
 										@RequestParam(required = false) String channelId) {
 										@RequestParam(required = false) String channelId) {
 		if (logger.isDebugEnabled()) {
 		if (logger.isDebugEnabled()) {
 			logger.debug("强制关键帧API调用");
 			logger.debug("强制关键帧API调用");
 		}
 		}
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
-		boolean sucsess = cmder.iFrameCmd(device, channelId);
-		if (sucsess) {
-			JSONObject json = new JSONObject();
-			json.put("DeviceID", deviceId);
-			json.put("ChannelID", channelId);
-			json.put("Result", "OK");
-			return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
-		} else {
-			logger.warn("强制关键帧API调用失败!");
-			return new ResponseEntity<String>("强制关键帧API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
+		try {
+			cmder.iFrameCmd(device, channelId);
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
 		}
 		}
+		JSONObject json = new JSONObject();
+		json.put("DeviceID", deviceId);
+		json.put("ChannelID", channelId);
+		json.put("Result", "OK");
+		return json;
 	}
 	}
 
 
 	/**
 	/**
@@ -240,7 +260,7 @@ public class DeviceControl {
 	@Parameter(name = "presetIndex", description = "调用预置位编号")
 	@Parameter(name = "presetIndex", description = "调用预置位编号")
 	@Parameter(name = "resetTime", description = "自动归位时间间隔")
 	@Parameter(name = "resetTime", description = "自动归位时间间隔")
 	@GetMapping("/home_position/{deviceId}/{enabled}")
 	@GetMapping("/home_position/{deviceId}/{enabled}")
-	public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
+	public DeferredResult<String> homePositionApi(@PathVariable String deviceId,
 																@PathVariable String enabled,
 																@PathVariable String enabled,
 																@RequestParam(required = false) String resetTime,
 																@RequestParam(required = false) String resetTime,
 																@RequestParam(required = false) String presetIndex,
 																@RequestParam(required = false) String presetIndex,
@@ -251,14 +271,19 @@ public class DeviceControl {
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId);
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId);
 		String uuid = UUID.randomUUID().toString();
 		String uuid = UUID.randomUUID().toString();
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
-		cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
-        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		try {
+			cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 看守位控制: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
+		DeferredResult<String> result = new DeferredResult<>(3 * 1000L);
 		result.onTimeout(() -> {
 		result.onTimeout(() -> {
 			logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令"));
 			logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令"));
 			// 释放rtpserver
 			// 释放rtpserver
@@ -297,14 +322,14 @@ public class DeviceControl {
 	@Parameter(name = "lengthx", description = "拉框长度像素值", required = true)
 	@Parameter(name = "lengthx", description = "拉框长度像素值", required = true)
 	@Parameter(name = "lengthy", description = "lengthy", required = true)
 	@Parameter(name = "lengthy", description = "lengthy", required = true)
 	@GetMapping("drag_zoom/zoom_in")
 	@GetMapping("drag_zoom/zoom_in")
-	public ResponseEntity<String> dragZoomIn(@RequestParam String deviceId,
+	public void dragZoomIn(@RequestParam String deviceId,
 											 @RequestParam(required = false) String channelId,
 											 @RequestParam(required = false) String channelId,
 											 @RequestParam int length,
 											 @RequestParam int length,
 											 @RequestParam int width,
 											 @RequestParam int width,
 											 @RequestParam int midpointx,
 											 @RequestParam int midpointx,
 											 @RequestParam int midpointy,
 											 @RequestParam int midpointy,
 											 @RequestParam int lengthx,
 											 @RequestParam int lengthx,
-											 @RequestParam int lengthy){
+											 @RequestParam int lengthy) throws RuntimeException {
 		if (logger.isDebugEnabled()) {
 		if (logger.isDebugEnabled()) {
 			logger.debug(String.format("设备拉框放大 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy));
 			logger.debug(String.format("设备拉框放大 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy));
 		}
 		}
@@ -318,8 +343,12 @@ public class DeviceControl {
 		cmdXml.append("<LengthX>" + lengthx+ "</LengthX>\r\n");
 		cmdXml.append("<LengthX>" + lengthx+ "</LengthX>\r\n");
 		cmdXml.append("<LengthY>" + lengthy+ "</LengthY>\r\n");
 		cmdXml.append("<LengthY>" + lengthy+ "</LengthY>\r\n");
 		cmdXml.append("</DragZoomIn>\r\n");
 		cmdXml.append("</DragZoomIn>\r\n");
-		cmder.dragZoomCmd(device, channelId, cmdXml.toString());
-		return new ResponseEntity<String>("success", HttpStatus.OK);
+		try {
+			cmder.dragZoomCmd(device, channelId, cmdXml.toString());
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 拉框放大: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " +  e.getMessage());
+		}
 	}
 	}
 
 
 	/**
 	/**
@@ -344,7 +373,7 @@ public class DeviceControl {
 	@Parameter(name = "lengthx", description = "拉框长度像素值", required = true)
 	@Parameter(name = "lengthx", description = "拉框长度像素值", required = true)
 	@Parameter(name = "lengthy", description = "拉框宽度像素值", required = true)
 	@Parameter(name = "lengthy", description = "拉框宽度像素值", required = true)
 	@GetMapping("/drag_zoom/zoom_out")
 	@GetMapping("/drag_zoom/zoom_out")
-	public ResponseEntity<String> dragZoomOut(@RequestParam String deviceId,
+	public void dragZoomOut(@RequestParam String deviceId,
 											  @RequestParam(required = false) String channelId,
 											  @RequestParam(required = false) String channelId,
 											  @RequestParam int length,
 											  @RequestParam int length,
 											  @RequestParam int width,
 											  @RequestParam int width,
@@ -366,7 +395,11 @@ public class DeviceControl {
 		cmdXml.append("<LengthX>" + lengthx+ "</LengthX>\r\n");
 		cmdXml.append("<LengthX>" + lengthx+ "</LengthX>\r\n");
 		cmdXml.append("<LengthY>" + lengthy+ "</LengthY>\r\n");
 		cmdXml.append("<LengthY>" + lengthy+ "</LengthY>\r\n");
 		cmdXml.append("</DragZoomOut>\r\n");
 		cmdXml.append("</DragZoomOut>\r\n");
-		cmder.dragZoomCmd(device, channelId, cmdXml.toString());
-		return new ResponseEntity<String>("success",HttpStatus.OK);
+		try {
+			cmder.dragZoomCmd(device, channelId, cmdXml.toString());
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 拉框缩小: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " +  e.getMessage());
+		}
 	}
 	}
 }
 }

+ 28 - 15
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java

@@ -39,8 +39,11 @@ import org.springframework.web.context.request.async.DeferredResult;
 
 
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
 import javax.sip.DialogState;
 import javax.sip.DialogState;
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
 import java.io.*;
 import java.io.*;
 import java.nio.file.Files;
 import java.nio.file.Files;
+import java.text.ParseException;
 import java.util.*;
 import java.util.*;
 
 
 @Tag(name  = "国标设备查询", description = "国标设备查询")
 @Tag(name  = "国标设备查询", description = "国标设备查询")
@@ -315,13 +318,18 @@ public class DeviceQuery {
 			result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK));
 			result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK));
 			return result;
 			return result;
 		}
 		}
-		cmder.deviceStatusQuery(device, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
+		try {
+			cmder.deviceStatusQuery(device, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 		result.onTimeout(()->{
 		result.onTimeout(()->{
 			logger.warn(String.format("获取设备状态超时"));
 			logger.warn(String.format("获取设备状态超时"));
 			// 释放rtpserver
 			// 释放rtpserver
@@ -368,14 +376,19 @@ public class DeviceQuery {
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
 		String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId;
 		String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId;
 		String uuid = UUID.randomUUID().toString();
 		String uuid = UUID.randomUUID().toString();
-		cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
-        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
+		try {
+			cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
+		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
 		result.onTimeout(()->{
 		result.onTimeout(()->{
 			logger.warn(String.format("设备报警查询超时"));
 			logger.warn(String.format("设备报警查询超时"));
 			// 释放rtpserver
 			// 释放rtpserver

+ 40 - 16
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java

@@ -29,6 +29,9 @@ import org.springframework.util.ObjectUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.SipConfig;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
@@ -212,20 +215,37 @@ public class PlatformController {
             // 保存时启用就发送注册
             // 保存时启用就发送注册
             if (parentPlatform.isEnable()) {
             if (parentPlatform.isEnable()) {
                 if (parentPlatformOld != null && parentPlatformOld.isStatus()) {
                 if (parentPlatformOld != null && parentPlatformOld.isStatus()) {
-                    commanderForPlatform.unregister(parentPlatformOld, null, null);
+                    try {
+                        commanderForPlatform.unregister(parentPlatformOld, null, null);
+                    } catch (InvalidArgumentException | ParseException | SipException e) {
+                        logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+                    }
                     try {
                     try {
                         Thread.sleep(500);
                         Thread.sleep(500);
                     } catch (InterruptedException e) {
                     } catch (InterruptedException e) {
-                        e.printStackTrace();
+                        logger.error("[线程休眠失败] : {}", e.getMessage());
                     }
                     }
                     //  只要保存就发送注册
                     //  只要保存就发送注册
-                    commanderForPlatform.register(parentPlatform, null, null);
+                    try {
+                        commanderForPlatform.register(parentPlatform, null, null);
+                    } catch (InvalidArgumentException | ParseException | SipException e) {
+                        logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
+                    }
+
                 } else {
                 } else {
                     //  只要保存就发送注册
                     //  只要保存就发送注册
-                    commanderForPlatform.register(parentPlatform, null, null);
+                    try {
+                        commanderForPlatform.register(parentPlatform, null, null);
+                    } catch (InvalidArgumentException | ParseException | SipException e) {
+                        logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
+                    }
                 }
                 }
             } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销
             } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销
-                commanderForPlatform.unregister(parentPlatformOld, null, null);
+                try {
+                    commanderForPlatform.unregister(parentPlatformOld, null, null);
+                } catch (InvalidArgumentException | ParseException | SipException e) {
+                    logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+                }
                 // 停止订阅相关的定时任务
                 // 停止订阅相关的定时任务
                 subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
                 subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
             }
             }
@@ -258,17 +278,21 @@ public class PlatformController {
             throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
             throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
         }
         }
         // 发送离线消息,无论是否成功都删除缓存
         // 发送离线消息,无论是否成功都删除缓存
-        commanderForPlatform.unregister(parentPlatform, (event -> {
-            // 清空redis缓存
-            redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
-            redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
-            redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId());
-        }), (event -> {
-            // 清空redis缓存
-            redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
-            redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
-            redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId());
-        }));
+        try {
+            commanderForPlatform.unregister(parentPlatform, (event -> {
+                // 清空redis缓存
+                redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
+                redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
+                redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId());
+            }), (event -> {
+                // 清空redis缓存
+                redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
+                redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
+                redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId());
+            }));
+        } catch (InvalidArgumentException | ParseException | SipException e) {
+            logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+        }
 
 
         boolean deleteResult = storager.deleteParentPlatform(parentPlatform);
         boolean deleteResult = storager.deleteParentPlatform(parentPlatform);
         storager.delCatalogByPlatformId(parentPlatform.getServerGBId());
         storager.delCatalogByPlatformId(parentPlatform.getServerGBId());

+ 33 - 13
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONArray;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -36,6 +37,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.List;
 import java.util.List;
 import java.util.UUID;
 import java.util.UUID;
 
 
@@ -102,12 +106,23 @@ public class PlayController {
 			throw new ControllerException(ErrorCode.ERROR400);
 			throw new ControllerException(ErrorCode.ERROR400);
 		}
 		}
 
 
+		Device device = storager.queryVideoDevice(deviceId);
+		if (device == null) {
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在");
+		}
+
 		StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
 		StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
 		if (streamInfo == null) {
 		if (streamInfo == null) {
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到");
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到");
 		}
 		}
 
 
-		cmder.streamByeCmd(deviceId, channelId, streamInfo.getStream(), null, null);
+		try {
+			logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId);
+			cmder.streamByeCmd(device, channelId, streamInfo.getStream(), null, null);
+		} catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
+			logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 		redisCatchStorage.stopPlay(streamInfo);
 		redisCatchStorage.stopPlay(streamInfo);
 
 
 		storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
 		storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
@@ -221,18 +236,23 @@ public class PlayController {
 			resultHolder.invokeResult(msg);
 			resultHolder.invokeResult(msg);
 			return result;
 			return result;
 		}
 		}
-		cmder.audioBroadcastCmd(device, (event) -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setKey(key);
-			msg.setId(uuid);
-			JSONObject json = new JSONObject();
-			json.put("DeviceID", deviceId);
-			json.put("CmdType", "Broadcast");
-			json.put("Result", "Failed");
-			json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", event.statusCode, event.msg));
-			msg.setData(json);
-			resultHolder.invokeResult(msg);
-		});
+		try {
+			cmder.audioBroadcastCmd(device, (event) -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setKey(key);
+				msg.setId(uuid);
+				JSONObject json = new JSONObject();
+				json.put("DeviceID", deviceId);
+				json.put("CmdType", "Broadcast");
+				json.put("Result", "Failed");
+				json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", event.statusCode, event.msg));
+				msg.setData(json);
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 语音广播: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 
 
 		result.onTimeout(() -> {
 		result.onTimeout(() -> {
 			logger.warn("语音广播操作超时, 设备未返回应答指令");
 			logger.warn("语音广播操作超时, 设备未返回应答指令");

+ 34 - 6
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java

@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.playback;
 
 
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 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.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -21,12 +22,15 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
-import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 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.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
+
 /**
 /**
  * @author lin
  * @author lin
  */
  */
@@ -92,7 +96,15 @@ public class PlaybackController {
 		if (ObjectUtils.isEmpty(deviceId) || ObjectUtils.isEmpty(channelId) || ObjectUtils.isEmpty(stream)) {
 		if (ObjectUtils.isEmpty(deviceId) || ObjectUtils.isEmpty(channelId) || ObjectUtils.isEmpty(stream)) {
 			throw new ControllerException(ErrorCode.ERROR400);
 			throw new ControllerException(ErrorCode.ERROR400);
 		}
 		}
-		cmder.streamByeCmd(deviceId, channelId, stream, null);
+		Device device = storager.queryVideoDevice(deviceId);
+		if (device == null) {
+			throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + " 未找到");
+		}
+		try {
+			cmder.streamByeCmd(device, channelId, stream, null);
+		} catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "发送bye失败: " + e.getMessage());
+		}
 	}
 	}
 
 
 
 
@@ -107,7 +119,11 @@ public class PlaybackController {
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
 		}
 		}
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
-		cmder.playPauseCmd(device, streamInfo);
+		try {
+			cmder.playPauseCmd(device, streamInfo);
+		} catch (InvalidArgumentException | ParseException | SipException e) {
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
+		}
 	}
 	}
 
 
 
 
@@ -122,7 +138,11 @@ public class PlaybackController {
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
 		}
 		}
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
-		cmder.playResumeCmd(device, streamInfo);
+		try {
+			cmder.playResumeCmd(device, streamInfo);
+		} catch (InvalidArgumentException | ParseException | SipException e) {
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
+		}
 	}
 	}
 
 
 
 
@@ -138,7 +158,11 @@ public class PlaybackController {
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
 		}
 		}
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
-		cmder.playSeekCmd(device, streamInfo, seekTime);
+		try {
+			cmder.playSeekCmd(device, streamInfo, seekTime);
+		} catch (InvalidArgumentException | ParseException | SipException e) {
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
+		}
 	}
 	}
 
 
 	@Operation(summary = "回放倍速播放")
 	@Operation(summary = "回放倍速播放")
@@ -157,6 +181,10 @@ public class PlaybackController {
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)");
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)");
 		}
 		}
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
 		Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
-		cmder.playSpeedCmd(device, streamInfo, speed);
+		try {
+			cmder.playSpeedCmd(device, streamInfo, speed);
+		} catch (InvalidArgumentException | ParseException | SipException e) {
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
+		}
 	}
 	}
 }
 }

+ 29 - 9
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java

@@ -1,6 +1,8 @@
 package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
 package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
 
 
  
  
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 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;
@@ -18,6 +20,9 @@ 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.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name  = "云台控制")
 @Tag(name  = "云台控制")
@@ -98,7 +103,12 @@ public class PtzController {
 			default:
 			default:
 				break;
 				break;
 		}
 		}
-		cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
+		try {
+			cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
+		} catch (SipException | InvalidArgumentException | ParseException e) {
+			logger.error("[命令发送失败] 云台控制: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 	}
 	}
 
 
 
 
@@ -117,7 +127,12 @@ public class PtzController {
 		}
 		}
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);
 
 
-		cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
+		try {
+			cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
+		} catch (SipException | InvalidArgumentException | ParseException e) {
+			logger.error("[命令发送失败] 前端控制: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 	}
 	}
 
 
 
 
@@ -146,13 +161,18 @@ public class PtzController {
 			return result;
 			return result;
 		}
 		}
 		resultHolder.put(key, uuid, result);
 		resultHolder.put(key, uuid, result);
-		cmder.presetQuery(device, channelId, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", event.statusCode, event.msg));
-			resultHolder.invokeResult(msg);
-		});
+		try {
+			cmder.presetQuery(device, channelId, event -> {
+				RequestMessage msg = new RequestMessage();
+				msg.setId(uuid);
+				msg.setKey(key);
+				msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", event.statusCode, event.msg));
+				resultHolder.invokeResult(msg);
+			});
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 获取设备预置位: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		}
 		return result;
 		return result;
 	}
 	}
 }
 }

+ 36 - 10
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java

@@ -3,7 +3,9 @@ package com.genersoft.iot.vmp.vmanager.gb28181.record;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
@@ -31,6 +33,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 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.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 import java.time.LocalDate;
 import java.time.LocalDate;
 import java.util.UUID;
 import java.util.UUID;
 
 
@@ -54,6 +59,12 @@ public class GBRecordController {
 	@Autowired
 	@Autowired
 	private IPlayService playService;
 	private IPlayService playService;
 
 
+	@Autowired
+	private IDeviceService deviceService;
+
+
+
+
 	@Operation(summary = "录像查询")
 	@Operation(summary = "录像查询")
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "channelId", description = "通道国标编号", required = true)
 	@Parameter(name = "channelId", description = "通道国标编号", required = true)
@@ -81,13 +92,18 @@ public class GBRecordController {
 		RequestMessage msg = new RequestMessage();
 		RequestMessage msg = new RequestMessage();
 		msg.setId(uuid);
 		msg.setId(uuid);
 		msg.setKey(key);
 		msg.setKey(key);
-		cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, null, null, null, (eventResult -> {
-			WVPResult<RecordInfo> wvpResult = new WVPResult<>();
-			wvpResult.setCode(ErrorCode.ERROR100.getCode());
-			wvpResult.setMsg("查询录像失败, status: " +  eventResult.statusCode + ", message: " + eventResult.msg);
-			msg.setData(wvpResult);
-			resultHolder.invokeResult(msg);
-		}));
+		try {
+			cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, null, null, null, (eventResult -> {
+				WVPResult<RecordInfo> wvpResult = new WVPResult<>();
+				wvpResult.setCode(ErrorCode.ERROR100.getCode());
+				wvpResult.setMsg("查询录像失败, status: " +  eventResult.statusCode + ", message: " + eventResult.msg);
+				msg.setData(wvpResult);
+				resultHolder.invokeResult(msg);
+			}));
+		} catch (InvalidArgumentException | SipException | ParseException e) {
+			logger.error("[命令发送失败] 查询录像: {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " +  e.getMessage());
+		}
 
 
 		// 录像查询以channelId作为deviceId查询
 		// 录像查询以channelId作为deviceId查询
 		resultHolder.put(key, uuid, result);
 		resultHolder.put(key, uuid, result);
@@ -131,14 +147,24 @@ public class GBRecordController {
 	@GetMapping("/download/stop/{deviceId}/{channelId}/{stream}")
 	@GetMapping("/download/stop/{deviceId}/{channelId}/{stream}")
 	public void playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
 	public void playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
 
 
-		cmder.streamByeCmd(deviceId, channelId, stream, null);
-
 		if (logger.isDebugEnabled()) {
 		if (logger.isDebugEnabled()) {
 			logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId));
 			logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId));
 		}
 		}
 
 
 		if (deviceId == null || channelId == null) {
 		if (deviceId == null || channelId == null) {
-			throw new ControllerException(ErrorCode.ERROR100);
+			throw new ControllerException(ErrorCode.ERROR400);
+		}
+
+		Device device = deviceService.queryDevice(deviceId);
+		if (device == null) {
+			throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "未找到");
+		}
+
+		try {
+			cmder.streamByeCmd(device, channelId, stream, null);
+		} catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+			logger.error("[停止历史媒体下载]停止历史媒体下载,发送BYE失败 {}", e.getMessage());
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
 		}
 		}
 	}
 	}
 
 

+ 14 - 6
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java

@@ -1,14 +1,20 @@
 package com.genersoft.iot.vmp.web.gb28181;
 package com.genersoft.iot.vmp.web.gb28181;
 
 
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
+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.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 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.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
+
 /**
 /**
  * API兼容:设备控制
  * API兼容:设备控制
  */
  */
@@ -35,7 +41,7 @@ public class ApiControlController {
      * @return
      * @return
      */
      */
     @RequestMapping(value = "/ptz")
     @RequestMapping(value = "/ptz")
-    private JSONObject list(String serial,String command,
+    private void list(String serial,String command,
                             @RequestParam(required = false)Integer channel,
                             @RequestParam(required = false)Integer channel,
                             @RequestParam(required = false)String code,
                             @RequestParam(required = false)String code,
                             @RequestParam(required = false)Integer speed){
                             @RequestParam(required = false)Integer speed){
@@ -48,9 +54,7 @@ public class ApiControlController {
         if (speed == null) {speed = 0;}
         if (speed == null) {speed = 0;}
         Device device = storager.queryVideoDevice(serial);
         Device device = storager.queryVideoDevice(serial);
         if (device == null) {
         if (device == null) {
-            JSONObject result = new JSONObject();
-            result.put("error","device[ " + serial + " ]未找到");
-            return result;
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到");
         }
         }
         int cmdCode = 0;
         int cmdCode = 0;
         switch (command){
         switch (command){
@@ -91,7 +95,11 @@ public class ApiControlController {
                 break;
                 break;
         }
         }
         // 默认值 50
         // 默认值 50
-        cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed);
-        return null;
+        try {
+            cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed);
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[命令发送失败] 云台控制: {}", e.getMessage());
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+        }
     }
     }
 }
 }

+ 22 - 1
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java

@@ -3,10 +3,12 @@ package com.genersoft.iot.vmp.web.gb28181;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 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.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -17,6 +19,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
+
 /**
 /**
  * API兼容:实时直播
  * API兼容:实时直播
  */
  */
@@ -40,6 +46,9 @@ public class ApiStreamController {
     @Autowired
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
     private IRedisCatchStorage redisCatchStorage;
 
 
+    @Autowired
+    private IDeviceService deviceService;
+
     @Autowired
     @Autowired
     private IPlayService playService;
     private IPlayService playService;
 
 
@@ -177,7 +186,19 @@ public class ApiStreamController {
             result.put("error","未找到流信息");
             result.put("error","未找到流信息");
             return result;
             return result;
         }
         }
-        cmder.streamByeCmd(serial, code, streamInfo.getStream(), null);
+        Device device = deviceService.queryDevice(serial);
+        if (device == null) {
+            JSONObject result = new JSONObject();
+            result.put("error","未找到设备");
+            return result;
+        }
+        try {
+            cmder.streamByeCmd(device, code, streamInfo.getStream(), null);
+        } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+            JSONObject result = new JSONObject();
+            result.put("error","发送BYE失败:" + e.getMessage());
+            return result;
+        }
         redisCatchStorage.stopPlay(streamInfo);
         redisCatchStorage.stopPlay(streamInfo);
         storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
         storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
         return null;
         return null;