瀏覽代碼

Merge remote-tracking branch 'origin/wvp-28181-2.0' into wvp-28181-2.0

xiaoxie 4 年之前
父節點
當前提交
6ac131bcf6
共有 100 個文件被更改,包括 5739 次插入3156 次删除
  1. 10 0
      .github/ISSUE_TEMPLATE/-------.md
  2. 29 0
      .github/ISSUE_TEMPLATE/--bug---.md
  3. 1 1
      .gitmodules
  4. 11 9
      README.md
  5. 二進制
      libs/onvif-java-1.0.2.jar
  6. 0 11
      pom.xml
  7. 2 0
      sql/mysql.sql
  8. 1 0
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  9. 3 3
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  10. 8 2
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  11. 43 0
      src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
  12. 2 2
      src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
  13. 1 1
      src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
  14. 1 3
      src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
  15. 2 0
      src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java
  16. 59 0
      src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java
  17. 10 0
      src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
  18. 0 25
      src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java
  19. 16 4
      src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java
  20. 1 1
      src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java
  21. 17 162
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  22. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java
  23. 13 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  24. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java
  25. 21 2
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
  26. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
  27. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java
  28. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
  29. 91 2
      src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
  30. 1 5
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
  31. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java
  32. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java
  33. 5 2
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java
  34. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
  35. 6 4
      src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
  36. 4 3
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java
  37. 2 2
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
  38. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  39. 6 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java
  40. 0 243
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
  41. 162 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
  42. 9 12
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java
  43. 66 11
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
  44. 8 20
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
  45. 31 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  46. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
  47. 62 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
  48. 181 44
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  49. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
  50. 14 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/ISIPRequestProcessor.java
  51. 23 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java
  52. 179 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
  53. 123 142
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
  54. 114 150
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
  55. 40 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java
  56. 395 478
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
  57. 1104 1100
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
  58. 177 142
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java
  59. 197 201
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
  60. 75 62
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java
  61. 23 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java
  62. 40 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java
  63. 91 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
  64. 27 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/ControlMessageHandler.java
  65. 112 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
  66. 25 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java
  67. 114 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
  68. 117 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java
  69. 63 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
  70. 74 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
  71. 103 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
  72. 25 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/QueryMessageHandler.java
  73. 77 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java
  74. 120 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
  75. 62 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
  76. 75 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java
  77. 25 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/ResponseMessageHandler.java
  78. 58 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java
  79. 72 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
  80. 182 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
  81. 81 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java
  82. 58 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java
  83. 59 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java
  84. 103 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
  85. 89 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
  86. 103 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
  87. 151 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
  88. 15 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java
  89. 8 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/SIPResponseProcessorAbstract.java
  90. 48 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java
  91. 47 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java
  92. 97 75
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
  93. 15 11
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java
  94. 7 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/ITimeoutProcessor.java
  95. 36 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java
  96. 0 12
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java
  97. 0 131
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java
  98. 0 28
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java
  99. 0 31
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
  100. 0 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java

+ 10 - 0
.github/ISSUE_TEMPLATE/-------.md

@@ -0,0 +1,10 @@
+---
+name: "[ 新功能 ]"
+about: 新功能
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+

+ 29 - 0
.github/ISSUE_TEMPLATE/--bug---.md

@@ -0,0 +1,29 @@
+---
+name: "[ BUG ] "
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**描述错误**
+描述下您遇到的问题
+
+**如何复现**
+有明确复现步骤的问题会很容易被解决
+
+**预期行为**
+清晰简洁的描述您期望发生的事情
+
+**截图**
+
+
+**环境信息:**
+ - 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/
+ - 2. 部署环境 windows / ubuntu/ centos ...
+ - 3. 端口开放情况
+ - 4. 是否是公网部署
+ - 5. 是否使用https
+ - 6. 方便的话提供下使用的设备品牌或平台
+ - 7. 你做过哪些尝试

+ 1 - 1
.gitmodules

@@ -1,3 +1,3 @@
 [submodule "be.teletask.onvif-java"]
 	path = be.teletask.onvif-java
-	url = https://gitee.com/18010473990/be.teletask.onvif-java.git
+	url = https://gitee.com/pan648540858/be.teletask.onvif-java.git

+ 11 - 9
README.md

@@ -1,4 +1,4 @@
-![logo](https://gitee.com/18010473990/wvp-GB28181/raw/wvp-28181-2.0/web_src/static/logo.png)
+![logo](https://gitee.com/pan648540858/wvp-GB28181-pro/raw/wvp-28181-2.0/web_src/static/logo.png)
 # 开箱即用的的28181协议视频平台
 
 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit)
@@ -13,13 +13,7 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网
 流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit
 
 前端页面基于MediaServerUI进行修改.  
-# 快速体验
-```shell
-docker pull 648540858/wvp_pro
 
-docker run  --env WVP_IP="你的IP" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro
-```
-docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro)
 # 应用场景:
 支持浏览器无插件播放摄像头视频。  
 支持摄像机、平台、NVR等设备接入。 
@@ -34,7 +28,7 @@ docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://
 [https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki)
 
 # gitee同步仓库
-https://gitee.com/18010473990/wvp-GB28181.git
+https://gitee.com/pan648540858/wvp-GB28181-pro.git
 
 # 截图
 ![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_1.png)
@@ -106,20 +100,28 @@ https://gitee.com/18010473990/wvp-GB28181.git
 - [ ] 添加ONVIF探测局域网内的设备
 - [X] 添加RTMP视频
 - [X] 云端录像(需要部署单独服务配合使用)
+- [X] 多流媒体节点,自动选择负载最低的节点使用。
 - [X] 支持使用mysql作为数据库,默认sqlite3,开箱即用。
 - [ ] 添加系统配置
 - [ ] 添加用户管理
 - [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。
 
+# docker快速体验
+```shell
+docker pull 648540858/wvp_pro
 
+docker run  --env WVP_IP="你的IP" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro
+```
+docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro)
 
 # gitee同步仓库
-https://gitee.com/18010473990/wvp-GB28181.git  
+https://gitee.com/pan648540858/wvp-GB28181-pro.git  
 
 # 使用帮助
 QQ群: 901799015, 690854210(ZLM大群)  
 QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对你有帮助,欢迎star和提交pr。
 
+
 # 致谢
 感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架  
 

二進制
libs/onvif-java-1.0.2.jar


+ 0 - 11
pom.xml

@@ -212,17 +212,6 @@
 <!--			<version>1.0.8</version>-->
 <!--		</dependency>-->
 
-
-
-		<!-- onvif协议栈 -->
-		<dependency>
-			<groupId>be.teletask</groupId>
-			<artifactId>onvif-java</artifactId>
-			<version>1.0.2</version>
-			<scope>system</scope>
-			<systemPath>${project.basedir}/libs/onvif-java-1.0.2.jar</systemPath>
-		</dependency>
-
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-test</artifactId>

+ 2 - 0
sql/mysql.sql

@@ -23,6 +23,7 @@ create table device
     updateTime    varchar(50)  not null,
     port          int          not null,
     expires       int          not null,
+    subscribeCycleForCatalog       int          not null,
     hostAddress   varchar(50)  not null,
     charset       varchar(50)  not null
 );
@@ -207,6 +208,7 @@ create table stream_proxy
     enable_hls     bit          null,
     enable_mp4     bit          null,
     enable         bit          not null,
+    enable_remove_none_reader    bit          not null,
     createTime     varchar(50)  not null,
     primary key (app, stream)
 );

+ 1 - 0
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@@ -6,6 +6,7 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
 import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import springfox.documentation.oas.annotations.EnableOpenApi;
 

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java

@@ -30,7 +30,7 @@ public class StreamInfo {
     private String rtsps;
     private String rtc;
     private String mediaServerId;
-    private JSONArray tracks;
+    private Object tracks;
 
     public static class TransactionInfo{
         public String callId;
@@ -105,11 +105,11 @@ public class StreamInfo {
         this.rtsp = rtsp;
     }
 
-    public JSONArray getTracks() {
+    public Object getTracks() {
         return tracks;
     }
 
-    public void setTracks(JSONArray tracks) {
+    public void setTracks(Object tracks) {
         this.tracks = tracks;
     }
 

+ 8 - 2
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@@ -1,14 +1,16 @@
 package com.genersoft.iot.vmp.common;
 
 /**    
- * @Description: 定义常量   
+ * @description: 定义常量   
  * @author: swwheihei
  * @date:   2019年5月30日 下午3:04:04   
  *   
  */
 public class VideoManagerConstants {
 	
-	public static final String WVP_SERVER_PREFIX = "VMP_wvp_server";
+	public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_";
+
+	public static final String WVP_SERVER_STREAM_PUSH_PREFIX = "VMP_SIGNALLING_STREAM_PUSH_";
 
 	public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
 
@@ -25,6 +27,7 @@ public class VideoManagerConstants {
 	public static final String PLAYER_PREFIX = "VMP_PLAYER_";
 
 	public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
+	public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
 
 	public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
 
@@ -51,4 +54,7 @@ public class VideoManagerConstants {
 	public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
 
 	public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
+
+	//************************** redis 消息*********************************
+	public static final String WVP_MSG_STREAM_PUSH_CHANGE_PREFIX = "WVP_MSG_STREAM_PUSH_CHANGE";
 }

+ 43 - 0
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java

@@ -0,0 +1,43 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.scheduling.support.CronTrigger;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+
+/**
+ * 动态定时任务
+ */
+@Component
+public class DynamicTask {
+
+    @Autowired
+    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
+
+    private Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
+
+    @Bean
+    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
+        return new ThreadPoolTaskScheduler();
+    }
+
+    public String startCron(String key, Runnable task, int cycleForCatalog) {
+        stopCron(key);
+        // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔
+        ScheduledFuture future = threadPoolTaskScheduler.scheduleWithFixedDelay(task, cycleForCatalog * 1000L);
+        futureMap.put(key, future);
+        return "startCron";
+    }
+
+    public void stopCron(String key) {
+        if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
+            futureMap.get(key).cancel(true);
+        }
+    }
+
+}

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java

@@ -163,9 +163,9 @@ public class ProxyServletConfig {
          * 异常处理
          */
         @Override
-        protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
+        protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){
             try {
-                super.handleRequestException(proxyRequest, proxyResonse, e);
+                super.handleRequestException(proxyRequest, proxyResponse, e);
             } catch (ServletException servletException) {
                 logger.error("录像服务 代理失败: ", e);
             } catch (IOException ioException) {

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

@@ -16,7 +16,7 @@ import redis.clients.jedis.JedisPool;
 import redis.clients.jedis.JedisPoolConfig;
 
 /**
- * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
+ * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
  * @author: swwheihei
  * @date: 2019年5月30日 上午10:58:25
  * 

+ 1 - 3
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java

@@ -1,9 +1,7 @@
 package com.genersoft.iot.vmp.conf;
 
 
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -27,7 +25,7 @@ public class SipConfig {
 	
 	Integer ptzSpeed = 50;
 
-	Integer keepaliveTimeOut = 180;
+	Integer keepaliveTimeOut = 255;
 
 	Integer registerTimeInterval = 60;
 

+ 2 - 0
src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java

@@ -32,5 +32,7 @@ public class SipDeviceRunner implements CommandLineRunner {
         for (String deviceId : onlineForAll) {
             storager.online(deviceId);
         }
+
+        // TODO 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
     }
 }

+ 59 - 0
src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java

@@ -0,0 +1,59 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+@Configuration
+@EnableAsync(proxyTargetClass = true)
+public class ThreadPoolTaskConfig {
+
+    public static final int cpuNum = Runtime.getRuntime().availableProcessors();
+
+    /**
+     *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
+     *    当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
+     *  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
+     */
+
+    /**
+     * 核心线程数(默认线程数)
+     */
+    private static final int corePoolSize = cpuNum;
+    /**
+     * 最大线程数
+     */
+    private static final int maxPoolSize = cpuNum*2;
+    /**
+     * 允许线程空闲时间(单位:默认为秒)
+     */
+    private static final int keepAliveTime = 30;
+    /**
+     * 缓冲队列大小
+     */
+    private static final int queueCapacity = 500;
+    /**
+     * 线程池名前缀
+     */
+    private static final String threadNamePrefix = "wvp-sip-handle-";
+
+    @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
+    public ThreadPoolTaskExecutor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(corePoolSize);
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveTime);
+        executor.setThreadNamePrefix(threadNamePrefix);
+
+        // 线程池对拒绝任务的处理策略
+        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        // 初始化
+        executor.initialize();
+        return executor;
+    }
+}

+ 10 - 0
src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java

@@ -27,6 +27,8 @@ public class UserSetup {
 
     private Boolean logInDatebase = Boolean.TRUE;
 
+    private String serverId = "000000";
+
     private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
 
     public Boolean getSavePositionHistory() {
@@ -104,4 +106,12 @@ public class UserSetup {
     public void setLogInDatebase(Boolean logInDatebase) {
         this.logInDatebase = logInDatebase;
     }
+
+    public String getServerId() {
+        return serverId;
+    }
+
+    public void setServerId(String serverId) {
+        this.serverId = serverId;
+    }
 }

+ 0 - 25
src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java

@@ -1,25 +0,0 @@
-package com.genersoft.iot.vmp.conf;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-
-/**    
- * @Description: 获取数据库配置   
- * @author: swwheihei
- * @date:   2020年5月6日 下午2:46:00     
- */
-@Configuration("vmConfig")
-public class VManagerConfig {
-
-	@Value("${spring.application.database:redis}")
-    private String database;
-
-
-    public String getDatabase() {
-        return database;
-    }
-
-    public void setDatabase(String database) {
-        this.database = database;
-    }
-}

+ 16 - 4
src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java

@@ -1,7 +1,10 @@
 package com.genersoft.iot.vmp.conf;
 
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -12,13 +15,22 @@ public class WVPTimerTask {
     private IRedisCatchStorage redisCatchStorage;
 
     @Autowired
-    private SipConfig sipConfig;
+    private IMediaServerService mediaServerService;
 
     @Autowired
-    private MediaConfig mediaConfig;
+    private UserSetup userSetup;
+
+    @Value("${server.port}")
+    private int serverPort;
+
+    @Autowired
+    private SipConfig sipConfig;
 
-    @Scheduled(cron="0/2 * *  * * ? ")   //每3秒执行一次
+    @Scheduled(fixedRate = 2 * 1000)   //每3秒执行一次
     public void execute(){
-//        redisCatchStorage.updateWVPInfo();
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("ip", sipConfig.getIp());
+        jsonObject.put("port", serverPort);
+        redisCatchStorage.updateWVPInfo(userSetup.getServerId(), jsonObject, 3);
     }
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java

@@ -34,7 +34,7 @@ public class LoginFailureHandler implements AuthenticationFailureHandler {
 
         } else if (e instanceof BadCredentialsException) {
             // 密码错误
-            logger.info("[登录失败] - 用户[{}]密码错误", username);
+            logger.info("[登录失败] - 用户[{}]密码/SIP服务器ID 错误", username);
 
         } else if (e instanceof CredentialsExpiredException) {
             // 密码过期

+ 17 - 162
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@@ -1,18 +1,11 @@
 package com.genersoft.iot.vmp.gb28181;
 
-import java.text.ParseException;
-import java.util.Properties;
-import java.util.TooManyListenersException;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import javax.sip.*;
-import javax.sip.header.CallIdHeader;
-import javax.sip.message.Response;
-
+import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import gov.nist.javax.sip.SipProviderImpl;
+import gov.nist.javax.sip.SipStackImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -20,14 +13,15 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.stereotype.Component;
 
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
-import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
-
-import gov.nist.javax.sip.SipStackImpl;
+import javax.sip.*;
+import java.util.Properties;
+import java.util.TooManyListenersException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 @Component
-public class SipLayer implements SipListener {
+public class SipLayer{
 
 	private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
 
@@ -35,33 +29,14 @@ public class SipLayer implements SipListener {
 	private SipConfig sipConfig;
 
 	@Autowired
-	private SIPProcessorFactory processorFactory;
-
-	@Autowired
-	private SipSubscribe sipSubscribe;
+	private ISIPProcessorObserver sipProcessorObserver;
 
 	private SipStackImpl sipStack;
 
 	private SipFactory sipFactory;
 
-	/**   
-	 * 消息处理器线程池
-	 */
-	private ThreadPoolExecutor processThreadPool;
 
-	@Bean("initSipServer")
-	private ThreadPoolExecutor initSipServer() {
-		
-		int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
-		LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
-		processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,
-				0L,TimeUnit.MILLISECONDS,processQueue,
-				new ThreadPoolExecutor.CallerRunsPolicy());
-		return processThreadPool;
-	}
-	
 	@Bean("sipFactory")
-	@DependsOn("initSipServer")
 	private SipFactory createSipFactory() {
 		sipFactory = SipFactory.getInstance();
 		sipFactory.setPathName("gov.nist");
@@ -69,7 +44,7 @@ public class SipLayer implements SipListener {
 	}
 	
 	@Bean("sipStack")
-	@DependsOn({"initSipServer", "sipFactory"})
+	@DependsOn({"sipFactory"})
 	private SipStack createSipStack() throws PeerUnavailableException {
 		Properties properties = new Properties();
 		properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
@@ -87,7 +62,7 @@ public class SipLayer implements SipListener {
 		return sipStack;
 	}
 
-	@Bean("tcpSipProvider")
+	@Bean(name = "tcpSipProvider")
 	@DependsOn("sipStack")
 	private SipProviderImpl startTcpListener() {
 		ListeningPoint tcpListeningPoint = null;
@@ -95,7 +70,7 @@ public class SipLayer implements SipListener {
 		try {
 			tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP");
 			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
-			tcpSipProvider.addSipListener(this);
+			tcpSipProvider.addSipListener(sipProcessorObserver);
 			logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
 		} catch (TransportNotSupportedException e) {
 			e.printStackTrace();
@@ -110,7 +85,7 @@ public class SipLayer implements SipListener {
 		return tcpSipProvider;
 	}
 	
-	@Bean("udpSipProvider")
+	@Bean(name = "udpSipProvider")
 	@DependsOn("sipStack")
 	private SipProviderImpl startUdpListener() {
 		ListeningPoint udpListeningPoint = null;
@@ -118,8 +93,7 @@ public class SipLayer implements SipListener {
 		try {
 			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
 			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
-			udpSipProvider.addSipListener(this);
-//			udpSipProvider.setAutomaticDialogSupportEnabled(false);
+			udpSipProvider.addSipListener(sipProcessorObserver);
 		} catch (TransportNotSupportedException e) {
 			e.printStackTrace();
 		} catch (InvalidArgumentException e) {
@@ -134,123 +108,4 @@ public class SipLayer implements SipListener {
 		return udpSipProvider;
 	}
 
-	/**
-	 * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a
-	 * new request arrives.
-	 */
-	@Override
-	public void processRequest(RequestEvent evt) {
-		logger.debug(evt.getRequest().toString());
-		// 由于jainsip是单线程程序,为提高性能并发处理
-		processThreadPool.execute(() -> {
-			if (processorFactory != null) {
-				processorFactory.createRequestProcessor(evt).process();
-			}
-		});
-	}
-
-	@Override
-	public void processResponse(ResponseEvent evt) {
-		Response response = evt.getResponse();
-		logger.debug(evt.getResponse().toString());
-		int status = response.getStatusCode();
-		if (((status >= 200) && (status < 300)) || status == 401) { // Success!
-			ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
-			try {
-				processor.process(evt, this, sipConfig);
-			} catch (ParseException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-			}
-
-			if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
-				CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
-				if (callIdHeader != null) {
-					SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId());
-					if (subscribe != null) {
-						subscribe.response(evt);
-					}
-				}
-			}
-		} else if ((status >= 100) && (status < 200)) {
-			// 增加其它无需回复的响应,如101、180等
-		} else {
-			logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
-			if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {
-				CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
-				if (callIdHeader != null) {
-					SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
-					if (subscribe != null) {
-						subscribe.response(evt);
-					}
-				}
-			}
-		}
-
-
-
-	}
-
-	/**
-	 * <p>
-	 * Title: processTimeout
-	 * </p>
-	 * <p>
-	 * Description:
-	 * </p>
-	 * 
-	 * @param timeoutEvent
-	 */
-	@Override
-	public void processTimeout(TimeoutEvent timeoutEvent) {
-		// TODO Auto-generated method stub
-
-	}
-
-	/**
-	 * <p>
-	 * Title: processIOException
-	 * </p>
-	 * <p>
-	 * Description:
-	 * </p>
-	 * 
-	 * @param exceptionEvent
-	 */
-	@Override
-	public void processIOException(IOExceptionEvent exceptionEvent) {
-		// TODO Auto-generated method stub
-	}
-
-	/**
-	 * <p>
-	 * Title: processTransactionTerminated
-	 * </p>
-	 * <p>
-	 * Description:
-	 * </p>
-	 * 
-	 * @param transactionTerminatedEvent
-	 */
-	@Override
-	public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
-		// TODO Auto-generated method stub
-	}
-
-	/**
-	 * <p>
-	 * Title: processDialogTerminated
-	 * </p>
-	 * <p>
-	 * Description:
-	 * </p>
-	 * 
-	 * @param dialogTerminatedEvent
-	 */
-	@Override
-	public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
-		// TODO Auto-generated method stub
-
-	}
-
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java

@@ -9,7 +9,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 
 /**    
- * @Description:注册逻辑处理,当设备注册后触发逻辑。
+ * @description:注册逻辑处理,当设备注册后触发逻辑。
  * @author: swwheihei
  * @date:   2020年5月8日 下午9:41:46     
  */

+ 13 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java

@@ -109,6 +109,11 @@ public class Device {
 	 */
 	private String charset ;
 
+	/**
+	 * 目录订阅周期,0为不订阅
+	 */
+	private int subscribeCycleForCatalog ;
+
 
 
 	public String getDeviceId() {
@@ -270,4 +275,12 @@ public class Device {
 	public void setCharset(String charset) {
 		this.charset = charset;
 	}
+
+	public int getSubscribeCycleForCatalog() {
+		return subscribeCycleForCatalog;
+	}
+
+	public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) {
+		this.subscribeCycleForCatalog = subscribeCycleForCatalog;
+	}
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
 /**
- * @Description: 移动位置bean
+ * @description: 移动位置bean
  * @author: lawrencehj
  * @date: 2021年1月23日
  */

+ 21 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java

@@ -6,14 +6,18 @@ package com.genersoft.iot.vmp.gb28181.bean;
 import java.util.List;
 
 /**    
- * @Description:设备录像信息bean 
+ * @description:设备录像信息bean 
  * @author: swwheihei
  * @date:   2020年5月8日 下午2:05:56     
  */
 public class RecordInfo {
 
 	private String deviceId;
-	
+
+	private String channelId;
+
+	private String sn;
+
 	private String name;
 	
 	private int sumNum;
@@ -52,4 +56,19 @@ public class RecordInfo {
 		this.recordList = recordList;
 	}
 
+	public String getChannelId() {
+		return channelId;
+	}
+
+	public void setChannelId(String channelId) {
+		this.channelId = channelId;
+	}
+
+	public String getSn() {
+		return sn;
+	}
+
+	public void setSn(String sn) {
+		this.sn = sn;
+	}
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java

@@ -8,7 +8,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 
 /**
- * @Description:设备录像bean 
+ * @description:设备录像bean 
  * @author: swwheihei
  * @date:   2020年5月8日 下午2:06:54     
  */

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java

@@ -7,7 +7,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 
 /**    
- * @Description:设备离在线状态检测器,用于检测设备状态
+ * @description:设备离在线状态检测器,用于检测设备状态
  * @author: swwheihei
  * @date:   2020年5月13日 下午2:40:29     
  */

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java

@@ -13,7 +13,7 @@ import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent;
 import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
 
 /**    
- * @Description:Event事件通知推送器,支持推送在线事件、离线事件
+ * @description:Event事件通知推送器,支持推送在线事件、离线事件
  * @author: swwheihei
  * @date:   2020年5月6日 上午11:30:50     
  */

+ 91 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java

@@ -1,38 +1,127 @@
 package com.genersoft.iot.vmp.gb28181.event;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
-import javax.sip.ResponseEvent;
+import javax.sip.*;
+import javax.sip.header.CallIdHeader;
+import javax.sip.message.Response;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 @Component
 public class SipSubscribe {
 
+    private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class);
+
     private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>();
 
     private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
 
+    private Map<String, Date> timeSubscribes = new ConcurrentHashMap<>();
+
+//    @Scheduled(cron="*/5 * * * * ?")   //每五秒执行一次
+//    @Scheduled(fixedRate= 100 * 60 * 60 )
+    @Scheduled(cron="0 0 * * * ?")   //每小时执行一次, 每个整点
+    public void execute(){
+        logger.info("[定时任务] 清理过期的订阅信息");
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.set(Calendar.HOUR, calendar.get(Calendar.HOUR) - 1);
+        for (String key : timeSubscribes.keySet()) {
+            if (timeSubscribes.get(key).before(calendar.getTime())){
+                logger.info("[定时任务] 清理过期的订阅信息: {}", key);
+                errorSubscribes.remove(key);
+                okSubscribes.remove(key);
+                timeSubscribes.remove(key);
+            }
+        }
+    }
+
     public interface Event {
-        void response(ResponseEvent event);
+        void response(EventResult eventResult);
+    }
+
+    public static class EventResult<EventObject>{
+        public int statusCode;
+        public String type;
+        public String msg;
+        public String callId;
+        public Dialog dialog;
+        public EventObject event;
+
+        public EventResult() {
+        }
+
+        public EventResult(EventObject event) {
+            this.event = event;
+            if (event instanceof ResponseEvent) {
+                ResponseEvent responseEvent = (ResponseEvent)event;
+                Response response = responseEvent.getResponse();
+                this.dialog = responseEvent.getDialog();
+                this.type = "response";
+                if (response != null) {
+                    this.msg = response.getReasonPhrase();
+                    this.statusCode = response.getStatusCode();
+                }
+                this.callId = ((CallIdHeader)response.getHeader(CallIdHeader.NAME)).getCallId();
+
+            }else if (event instanceof TimeoutEvent) {
+                TimeoutEvent timeoutEvent = (TimeoutEvent)event;
+                this.type = "timeout";
+                this.msg = "消息超时未回复";
+                this.statusCode = -1024;
+                this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();
+                this.dialog = timeoutEvent.getClientTransaction().getDialog();
+            }else if (event instanceof TransactionTerminatedEvent) {
+                TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
+                this.type = "transactionTerminated";
+                this.msg = "事务已结束";
+                this.statusCode = -1024;
+                this.callId = transactionTerminatedEvent.getClientTransaction().getDialog().getCallId().getCallId();
+                this.dialog = transactionTerminatedEvent.getClientTransaction().getDialog();
+            }else if (event instanceof DialogTerminatedEvent) {
+                DialogTerminatedEvent dialogTerminatedEvent = (DialogTerminatedEvent)event;
+                this.type = "dialogTerminated";
+                this.msg = "会话已结束";
+                this.statusCode = -1024;
+                this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId();
+                this.dialog = dialogTerminatedEvent.getDialog();
+            }
+        }
     }
 
     public void addErrorSubscribe(String key, SipSubscribe.Event event) {
         errorSubscribes.put(key, event);
+        timeSubscribes.put(key, new Date());
     }
 
     public void addOkSubscribe(String key, SipSubscribe.Event event) {
         okSubscribes.put(key, event);
+        timeSubscribes.put(key, new Date());
     }
 
     public SipSubscribe.Event getErrorSubscribe(String key) {
         return errorSubscribes.get(key);
     }
 
+    public void removeErrorSubscribe(String key) {
+        errorSubscribes.remove(key);
+        timeSubscribes.remove(key);
+    }
+
     public SipSubscribe.Event getOkSubscribe(String key) {
         return okSubscribes.get(key);
     }
 
+    public void removeOkSubscribe(String key) {
+        okSubscribes.remove(key);
+        timeSubscribes.remove(key);
+    }
     public int getErrorSubscribesSize(){
         return errorSubscribes.size();
     }

+ 1 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java

@@ -12,7 +12,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 
 /**    
- * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
+ * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
  * @author: swwheihei
  * @date:   2020年5月6日 上午11:35:46     
  */
@@ -39,10 +39,6 @@ public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessa
         //  获取失效的key
         String expiredKey = message.toString();
         logger.debug(expiredKey);
-        if(!expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)){
-        	logger.debug("收到redis过期监听,但开头不是"+VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX+",忽略");
-        	return;
-        }
         // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线
         if (expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)) {
             String platformGBId = expiredKey.substring(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length());

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java

@@ -12,7 +12,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 
 /**    
- * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
+ * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
  * @author: swwheihei
  * @date:   2020年5月6日 上午11:35:46     
  */

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java

@@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.gb28181.event.offline;
 import org.springframework.context.ApplicationEvent;
 
 /**    
- * @Description: 离线事件类   
+ * @description: 离线事件类   
  * @author: swwheihei
  * @date:   2020年5月6日 上午11:33:13     
  */

+ 5 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java

@@ -11,8 +11,8 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 
 /**
- * @Description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源:
- *               1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor}
+ * @description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源:
+ *               1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.RegisterRequestProcessor}
  *               2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener}
  * @author: swwheihei
  * @date: 2020年5月6日 下午1:51:23
@@ -54,5 +54,8 @@ public class OfflineEventListener implements ApplicationListener<OfflineEvent> {
 
 		// 处理离线监听
 		storager.outline(event.getDeviceId());
+
+		// TODO 离线取消订阅
+
 	}
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java

@@ -4,7 +4,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
 import org.springframework.context.ApplicationEvent;
 
 /**    
- * @Description: 在线事件类   
+ * @description: 在线事件类   
  * @author: swwheihei
  * @date:   2020年5月6日 上午11:32:56     
  */

+ 6 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java

@@ -13,12 +13,11 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 
 import java.text.SimpleDateFormat;
-import java.util.Date;
 
 /**
- * @Description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:
- *               1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor}
- *               2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor}
+ * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:
+ *               1、设备主动注销,发送注销指令
+ *               2、设备未知原因离线,心跳超时
  * @author: swwheihei
  * @date: 2020年5月6日 下午1:51:23
  */
@@ -74,5 +73,8 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> {
 		device.setOnline(1);
 		// 处理上线监听
 		storager.updateDevice(device);
+
+		// TODO 上线添加订阅
+
 	}
 }

+ 4 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java

@@ -18,7 +18,7 @@ import javax.sip.ResponseEvent;
 import javax.sip.message.Response;
 
 /**
- * @Description: 平台心跳超时事件
+ * @description: 平台心跳超时事件
  * @author: panll
  * @date: 2020年11月5日 10:00
  */
@@ -66,6 +66,7 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener<P
             storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
             publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
             parentPlatformCatch.setKeepAliveReply(0);
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
         }else {
             // 再次发送心跳
             String callId = sipCommanderForPlatform.keepalive(parentPlatform);
@@ -75,8 +76,8 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener<P
             redisCatchStorage.updatePlatformKeepalive(parentPlatform);
             redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
 
-            sipSubscribe.addOkSubscribe(callId, (ResponseEvent responseEvent) ->{
-                if (responseEvent.getResponse().getStatusCode() == Response.OK) {
+            sipSubscribe.addOkSubscribe(callId, (SipSubscribe.EventResult eventResult) ->{
+                if (eventResult.statusCode == Response.OK) {
                     // 收到心跳响应信息,
                     parentPlatformCatch.setKeepAliveReply(0);
                     redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java

@@ -19,7 +19,7 @@ import org.springframework.stereotype.Component;
 import java.util.*;
 
 /**
- * @Description: 平台未注册事件,来源有二:
+ * @description: 平台未注册事件,来源有二:
  *               1、平台新添加
  *               2、平台心跳超时
  * @author: panll
@@ -100,6 +100,6 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
                 logger.info("再次向平台注册,平台国标ID:" + event.getPlatformGbID());
                 sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
             }
-        }, config.getRegisterTimeInterval(), config.getRegisterTimeInterval());//十五秒后再次发起注册
+        }, config.getRegisterTimeInterval()* 1000, config.getRegisterTimeInterval()* 1000);//十五秒后再次发起注册
     }
 }

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

@@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 /**    
- * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 
+ * @description:视频流session管理器,管理视频预览、预览回放的通信句柄 
  * @author: swwheihei
  * @date:   2020年5月13日 下午4:03:02     
  */

+ 6 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java

@@ -0,0 +1,6 @@
+package com.genersoft.iot.vmp.gb28181.transmit;
+
+import javax.sip.SipListener;
+
+public interface ISIPProcessorObserver extends SipListener {
+}

+ 0 - 243
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java

@@ -1,243 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.transmit;
-
-import javax.sip.RequestEvent;
-import javax.sip.ResponseEvent;
-import javax.sip.SipProvider;
-import javax.sip.header.CSeqHeader;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
-import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.service.IDeviceAlarmService;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;
-import com.genersoft.iot.vmp.service.IPlayService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
-import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
-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.request.ISIPRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor;
-import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import com.genersoft.iot.vmp.utils.SpringBeanFactory;
-import com.genersoft.iot.vmp.utils.redis.RedisUtil;
-
-/**    
- * @Description: SIP信令处理分配
- * @author: swwheihei
- * @date:   2020年5月3日 下午4:24:37     
- */
-@Component
-public class SIPProcessorFactory {
-	
-	// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
-	
-	@Autowired
-	private SipConfig sipConfig;
-	
-	@Autowired
-	private RegisterLogicHandler handler;
-	
-	@Autowired
-	private IVideoManagerStorager storager;
-
-	@Autowired
-	private IRedisCatchStorage redisCatchStorage;
-
-	@Autowired
-	private EventPublisher publisher;
-	
-	@Autowired
-	private SIPCommander cmder;
-
-	@Autowired
-	private SIPCommanderFroPlatform cmderFroPlatform;
-
-	@Autowired
-	private IDeviceAlarmService deviceAlarmService;
-
-	@Autowired
-	private RedisUtil redis;
-	
-	@Autowired
-	private DeferredResultHolder deferredResultHolder;
-	
-	@Autowired
-	private DeviceOffLineDetector offLineDetector;
-	
-	@Autowired
-	private InviteResponseProcessor inviteResponseProcessor;
-	
-	@Autowired
-	private ByeResponseProcessor byeResponseProcessor;
-	
-	@Autowired
-	private CancelResponseProcessor cancelResponseProcessor;
-
-	@Autowired
-	@Lazy
-	private RegisterResponseProcessor registerResponseProcessor;
-
-
-	@Autowired
-	private OtherResponseProcessor otherResponseProcessor;
-
-	@Autowired
-	private IPlayService playService;
-
-	@Autowired
-	private ZLMRTPServerFactory zlmrtpServerFactory;
-
-	@Autowired
-	private IMediaServerService mediaServerService;
-
-	// 注:这里使用注解会导致循环依赖注入,暂用springBean
-	private SipProvider tcpSipProvider;
-		
-	// 注:这里使用注解会导致循环依赖注入,暂用springBean
-	private SipProvider udpSipProvider;
-	
-	public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {
-		Request request = evt.getRequest();
-		String method = request.getMethod();
-//		logger.info("接收到消息:"+request.getMethod());
-//		sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
-		if (Request.INVITE.equals(method)) {
-			InviteRequestProcessor processor = new InviteRequestProcessor();
-			processor.setRequestEvent(evt);
-			processor.setTcpSipProvider(getTcpSipProvider());
-			processor.setUdpSipProvider(getUdpSipProvider());
-
-			processor.setCmder(cmder);
-			processor.setCmderFroPlatform(cmderFroPlatform);
-			processor.setPlayService(playService);
-			processor.setStorager(storager);
-			processor.setRedisCatchStorage(redisCatchStorage);
-			processor.setZlmrtpServerFactory(zlmrtpServerFactory);
-			processor.setMediaServerService(mediaServerService);
-			return processor;
-		} else if (Request.REGISTER.equals(method)) {
-			RegisterRequestProcessor processor = new RegisterRequestProcessor();
-			processor.setRequestEvent(evt);
-			processor.setTcpSipProvider(getTcpSipProvider());
-			processor.setUdpSipProvider(getUdpSipProvider());
-			processor.setHandler(handler);
-			processor.setPublisher(publisher);
-			processor.setSipConfig(sipConfig);
-			processor.setVideoManagerStorager(storager);
-			return processor;
-		} else if (Request.SUBSCRIBE.equals(method)) {
-			SubscribeRequestProcessor processor = new SubscribeRequestProcessor();
-			processor.setTcpSipProvider(getTcpSipProvider());
-			processor.setUdpSipProvider(getUdpSipProvider());
-			processor.setRequestEvent(evt);
-			return processor;
-		} else if (Request.ACK.equals(method)) {
-			AckRequestProcessor processor = new AckRequestProcessor();
-			processor.setRequestEvent(evt);
-			processor.setRedisCatchStorage(redisCatchStorage);
-			processor.setZlmrtpServerFactory(zlmrtpServerFactory);
-			processor.setMediaServerService(mediaServerService);
-			return processor;
-		} else if (Request.BYE.equals(method)) {
-			ByeRequestProcessor processor = new ByeRequestProcessor();
-			processor.setRequestEvent(evt);
-			processor.setRedisCatchStorage(redisCatchStorage);
-			processor.setStorager(storager);
-			processor.setZlmrtpServerFactory(zlmrtpServerFactory);
-			processor.setSIPCommander(cmder);
-			processor.setMediaServerService(mediaServerService);
-			return processor;
-		} else if (Request.CANCEL.equals(method)) {
-			CancelRequestProcessor processor = new CancelRequestProcessor();
-			processor.setRequestEvent(evt);
-			return processor;
-		} else if (Request.MESSAGE.equals(method)) {
-			MessageRequestProcessor processor = new MessageRequestProcessor();
-			processor.setRequestEvent(evt);
-			processor.setTcpSipProvider(getTcpSipProvider());
-			processor.setUdpSipProvider(getUdpSipProvider());
-			processor.setPublisher(publisher);
-			processor.setRedis(redis);
-			processor.setDeferredResultHolder(deferredResultHolder);
-			processor.setOffLineDetector(offLineDetector);
-			processor.setCmder(cmder);
-			processor.setCmderFroPlatform(cmderFroPlatform);
-			processor.setDeviceAlarmService(deviceAlarmService);
-			processor.setStorager(storager);
-			processor.setRedisCatchStorage(redisCatchStorage);
-			return processor;
-		} else if (Request.NOTIFY.equalsIgnoreCase(method)) {
-			NotifyRequestProcessor processor = new NotifyRequestProcessor();
-			processor.setRequestEvent(evt);
-			processor.setTcpSipProvider(getTcpSipProvider());
-			processor.setUdpSipProvider(getUdpSipProvider());
-			processor.setPublisher(publisher);
-			processor.setRedis(redis);
-			processor.setDeferredResultHolder(deferredResultHolder);
-			processor.setOffLineDetector(offLineDetector);
-			processor.setCmder(cmder);
-			processor.setStorager(storager);
-			processor.setRedisCatchStorage(redisCatchStorage);
-			return processor;
-		} else {
-			OtherRequestProcessor processor = new OtherRequestProcessor();
-			processor.setRequestEvent(evt);
-			return processor;
-		}
-	}
-	
-	public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
-
-		Response response = evt.getResponse();
-		CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
-		String method = cseqHeader.getMethod();
-		if(Request.INVITE.equals(method)){
-			return inviteResponseProcessor;
-		} else if (Request.BYE.equals(method)) {
-			return byeResponseProcessor;
-		} else if (Request.CANCEL.equals(method)) {
-			return cancelResponseProcessor;
-		}else if (Request.REGISTER.equals(method)) {
-			return registerResponseProcessor;
-		} else {
-			return otherResponseProcessor;
-		}
-	}
-	
-	private SipProvider getTcpSipProvider() {
-		if (tcpSipProvider == null) {
-			tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider");
-		}
-		return tcpSipProvider;
-	}
-	
-	private SipProvider getUdpSipProvider() {
-		if (udpSipProvider == null) {
-			udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
-		}
-		return udpSipProvider;
-	}
-	
-}

+ 162 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java

@@ -0,0 +1,162 @@
+package com.genersoft.iot.vmp.gb28181.transmit;
+
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.sip.*;
+import javax.sip.header.CSeqHeader;
+import javax.sip.header.CallIdHeader;
+import javax.sip.message.Response;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @description: SIP信令处理类观察者
+ * @author: panlinlin
+ * @date:   2021年11月5日 下午15:32
+ */
+@Component
+public class SIPProcessorObserver implements ISIPProcessorObserver {
+
+    private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class);
+
+    private static Map<String, ISIPRequestProcessor> requestProcessorMap = new ConcurrentHashMap<>();
+    private static Map<String, ISIPResponseProcessor> responseProcessorMap = new ConcurrentHashMap<>();
+    private static ITimeoutProcessor timeoutProcessor;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+//    @Autowired
+//    @Qualifier(value = "taskExecutor")
+//    private ThreadPoolTaskExecutor poolTaskExecutor;
+
+    /**
+     * 添加 request订阅
+     * @param method 方法名
+     * @param processor 处理程序
+     */
+    public void addRequestProcessor(String method, ISIPRequestProcessor processor) {
+        requestProcessorMap.put(method, processor);
+    }
+
+    /**
+     * 添加 response订阅
+     * @param method 方法名
+     * @param processor 处理程序
+     */
+    public void addResponseProcessor(String method, ISIPResponseProcessor processor) {
+        responseProcessorMap.put(method, processor);
+    }
+
+    /**
+     * 添加 超时事件订阅
+     * @param processor 处理程序
+     */
+    public void addTimeoutProcessor(ITimeoutProcessor processor) {
+        this.timeoutProcessor = processor;
+    }
+
+    /**
+     * 分发RequestEvent事件
+     * @param requestEvent RequestEvent事件
+     */
+    @Override
+    @Async
+    public void processRequest(RequestEvent requestEvent) {
+        String method = requestEvent.getRequest().getMethod();
+        ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method);
+        if (sipRequestProcessor == null) {
+            logger.warn("不支持方法{}的request", method);
+            return;
+        }
+        requestProcessorMap.get(method).process(requestEvent);
+
+    }
+
+    /**
+     * 分发ResponseEvent事件
+     * @param responseEvent responseEvent事件
+     */
+    @Override
+    @Async
+    public void processResponse(ResponseEvent responseEvent) {
+        logger.debug(responseEvent.getResponse().toString());
+        Response response = responseEvent.getResponse();
+        logger.debug(responseEvent.getResponse().toString());
+        int status = response.getStatusCode();
+        if (((status >= 200) && (status < 300)) || status == 401) { // Success!
+//            ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
+            CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
+            String method = cseqHeader.getMethod();
+            ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method);
+            if (sipRequestProcessor != null) {
+                sipRequestProcessor.process(responseEvent);
+            }
+            if (responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
+                CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
+                if (callIdHeader != null) {
+                    SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId());
+                    if (subscribe != null) {
+                        SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent);
+                        subscribe.response(eventResult);
+                    }
+                }
+            }
+        } else if ((status >= 100) && (status < 200)) {
+            // 增加其它无需回复的响应,如101、180等
+        } else {
+            logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
+            if (responseEvent.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {
+                CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
+                if (callIdHeader != null) {
+                    SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
+                    if (subscribe != null) {
+                        SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent);
+                        subscribe.response(eventResult);
+                    }
+                }
+            }
+            if (responseEvent.getDialog() != null) {
+                responseEvent.getDialog().delete();
+            }
+        }
+
+
+    }
+
+    /**
+     * 向超时订阅发送消息
+     * @param timeoutEvent timeoutEvent事件
+     */
+    @Override
+    public void processTimeout(TimeoutEvent timeoutEvent) {
+        if(timeoutProcessor != null) {
+            timeoutProcessor.process(timeoutEvent);
+        }
+    }
+
+    @Override
+    public void processIOException(IOExceptionEvent exceptionEvent) {
+    }
+
+    @Override
+    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
+    }
+
+    @Override
+    public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
+        CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId();
+    }
+
+
+}

+ 9 - 12
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java

@@ -1,17 +1,16 @@
 package com.genersoft.iot.vmp.gb28181.transmit.callback;
 
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
 import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
-import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.RecordInfoResponseMessageHandler;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
-
 import org.slf4j.Logger;
 
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
 @SuppressWarnings("unchecked")
 public class CheckForAllRecordsThread extends Thread {
 
@@ -54,13 +53,11 @@ public class CheckForAllRecordsThread extends Thread {
         // 自然顺序排序, 元素进行升序排列
         this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
         RequestMessage msg = new RequestMessage();
-        String deviceId = recordInfo.getDeviceId();
-        msg.setDeviceId(deviceId);
-        msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
+        msg.setKey(DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn());
         msg.setData(recordInfo);
-        deferredResultHolder.invokeResult(msg);
+        deferredResultHolder.invokeAllResult(msg);
         logger.info("处理完成,返回结果");
-        MessageRequestProcessor.threadNameList.remove(cacheKey);
+        RecordInfoResponseMessageHandler.threadNameList.remove(cacheKey);
     }
     
 	public void setRedis(RedisUtil redis) {

+ 66 - 11
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java

@@ -1,6 +1,8 @@
 package com.genersoft.iot.vmp.gb28181.transmit.callback;
 
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.springframework.http.HttpStatus;
@@ -9,7 +11,7 @@ import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.async.DeferredResult;
 
 /**    
- * @Description: 异步请求处理
+ * @description: 异步请求处理
  * @author: swwheihei
  * @date:   2020年5月8日 下午7:59:05     
  */
@@ -31,11 +33,13 @@ public class DeferredResultHolder {
 	
 	public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
 
-	public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
+	public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY";
 
-	public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
+	public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAY";
+
+	public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD";
 
-	public static final String CALLBACK_ONVIF = "CALLBACK_ONVIF";
+	public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
 
 	public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
 
@@ -45,21 +49,72 @@ public class DeferredResultHolder {
 
 	public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
 
-	private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
+	private Map<String, Map<String, DeferredResult>> map = new ConcurrentHashMap<>();
 
-	public void put(String key, DeferredResult result) {
-		map.put(key, result);
+
+	public void put(String key, String id, DeferredResult result) {
+		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		if (deferredResultMap == null) {
+			deferredResultMap = new ConcurrentHashMap<>();
+			map.put(key, deferredResultMap);
+		}
+		deferredResultMap.put(id, result);
 	}
 	
-	public DeferredResult get(String key) {
-		return map.get(key);
+	public DeferredResult get(String key, String id) {
+		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		if (deferredResultMap == null) return null;
+		return deferredResultMap.get(id);
 	}
-	
+
+	public boolean exist(String key, String id){
+		if (key == null) return false;
+		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		if (id == null) {
+			return deferredResultMap != null;
+		}else {
+			return deferredResultMap != null && deferredResultMap.get(id) != null;
+		}
+	}
+
+	/**
+	 * 释放单个请求
+	 * @param msg
+	 */
 	public void invokeResult(RequestMessage msg) {
-		DeferredResult result = map.get(msg.getId());
+		Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
+		if (deferredResultMap == null) {
+			return;
+		}
+		DeferredResult result = deferredResultMap.get(msg.getId());
 		if (result == null) {
 			return;
 		}
 		result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
+		deferredResultMap.remove(msg.getId());
+		if (deferredResultMap.size() == 0) {
+			map.remove(msg.getKey());
+		}
+	}
+
+	/**
+	 * 释放所有的请求
+	 * @param msg
+	 */
+	public void invokeAllResult(RequestMessage msg) {
+		Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
+		if (deferredResultMap == null) {
+			return;
+		}
+		Set<String> ids = deferredResultMap.keySet();
+		for (String id : ids) {
+			DeferredResult result = deferredResultMap.get(id);
+			if (result == null) {
+				return;
+			}
+			result.setResult(ResponseEntity.ok().body(msg.getData()));
+		}
+		map.remove(msg.getKey());
+
 	}
 }

+ 8 - 20
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.callback;
 
 /**    
- * @Description: 请求信息定义   
+ * @description: 请求信息定义   
  * @author: swwheihei
  * @date:   2020年5月8日 下午1:09:18     
  */
@@ -9,12 +9,10 @@ public class RequestMessage {
 	
 	private String id;
 
-	private String deviceId;
-	
-	private String type;
-	
+	private String key;
+
 	private Object data;
-	
+
 	public String getId() {
 		return id;
 	}
@@ -23,22 +21,12 @@ public class RequestMessage {
 		this.id = id;
 	}
 
-	public String getDeviceId() {
-		return deviceId;
-	}
-
-	public void setDeviceId(String deviceId) {
-		this.deviceId = deviceId;
-		this.id = type + deviceId;
-	}
-
-	public String getType() {
-		return type;
+	public void setKey(String key) {
+		this.key = key;
 	}
 
-	public void setType(String type) {
-		this.type = type;
-		this.id = type + deviceId;
+	public String getKey() {
+		return key;
 	}
 
 	public Object getData() {

+ 31 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 
+import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
@@ -7,7 +8,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 
 /**    
- * @Description:设备能力接口,用于定义设备的控制、查询能力   
+ * @description:设备能力接口,用于定义设备的控制、查询能力   
  * @author: swwheihei
  * @date:   2020年5月3日 下午9:16:34     
  */
@@ -121,6 +122,26 @@ public interface ISIPCommander {
 	void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
 	void streamByeCmd(String deviceId, String channelId);
 
+	/**
+	 * 回放暂停
+	 */
+	void playPauseCmd(Device device, StreamInfo streamInfo);
+
+	/**
+	 * 回放恢复
+	 */
+	void playResumeCmd(Device device, StreamInfo streamInfo);
+
+	/**
+	 * 回放拖动播放
+	 */
+	void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime);
+
+	/**
+	 * 回放倍速播放
+	 */
+	void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
+
 	/**
 	 * 语音广播
 	 * 
@@ -235,8 +256,9 @@ public interface ISIPCommander {
 	 * @param device 视频设备
 	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 * @param sn
 	 */
-	boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
+	boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 查询报警信息
@@ -299,4 +321,11 @@ public interface ISIPCommander {
 	 * @return				true = 命令发送成功
 	 */
 	boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
+
+	/**
+	 * 订阅、取消订阅目录信息
+	 * @param device		视频设备
+	 * @return				true = 命令发送成功
+	 */
+	boolean catalogSubscribe(Device device, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent);
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java

@@ -19,7 +19,7 @@ import java.util.List;
 import java.util.UUID;
 
 /**
- * @Description: 平台命令request创造器 TODO 冗余代码太多待优化
+ * @description: 平台命令request创造器 TODO 冗余代码太多待优化
  * @author: panll
  * @date: 2020年5月6日 上午9:29:02
  */

+ 62 - 6
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java

@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 import java.text.ParseException;
 import java.util.ArrayList;
 
+import javax.sip.Dialog;
 import javax.sip.InvalidArgumentException;
 import javax.sip.PeerUnavailableException;
 import javax.sip.SipFactory;
@@ -11,6 +12,9 @@ import javax.sip.address.SipURI;
 import javax.sip.header.*;
 import javax.sip.message.Request;
 
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -18,7 +22,7 @@ import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 
 /**
- * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
+ * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
  * @author: swwheihei
  * @date: 2020年5月6日 上午9:29:02
  */
@@ -30,6 +34,9 @@ public class SIPRequestHeaderProvider {
 	
 	@Autowired
 	private SipFactory sipFactory;
+
+	@Autowired
+	private VideoStreamSessionManager streamSession;
 	
 	public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
@@ -99,13 +106,13 @@ public class SIPRequestHeaderProvider {
 		return request;
 	}
 	
-	public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+	public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		//请求行
-		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		//from
@@ -113,7 +120,7 @@ public class SIPRequestHeaderProvider {
 		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
 		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
 		//to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain()); 
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
 		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
 		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
 		
@@ -127,7 +134,10 @@ public class SIPRequestHeaderProvider {
 		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
 		// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
 		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
-		
+		// Subject
+		SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
+		request.addHeader(subjectHeader);
+
 		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
 		request.setContent(content, contentTypeHeader);
 		return request;
@@ -207,4 +217,50 @@ public class SIPRequestHeaderProvider {
 		request.setContent(content, contentTypeHeader);
 		return request;
 	}
+
+	public Request createInfoRequest(Device device, StreamInfo streamInfo, String content)
+			throws PeerUnavailableException, ParseException, InvalidArgumentException {
+		Request request = null;
+		Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId());
+
+		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(),
+				device.getHostAddress());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
+				device.getTransport(), null);
+		viaHeader.setRPort();
+		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, dialog.getLocalTag());
+		// to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(streamInfo.getChannelId(),
+				sipConfig.getDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag());
+
+		// callid
+		CallIdHeader callIdHeader = dialog.getCallId();
+
+		// Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		// ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory()
+				.createCSeqHeader(InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()), Request.INFO);
+
+		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,
+				fromHeader, toHeader, viaHeaders, maxForwards);
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
+				.createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
+		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
+				"MANSRTSP");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
 }

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

@@ -1,25 +1,24 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
-import java.lang.reflect.Field;
-import java.text.ParseException;
-import java.util.HashSet;
-
-import javax.sip.*;
-import javax.sip.address.SipURI;
-import javax.sip.header.CallIdHeader;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.Request;
-
 import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
-import com.genersoft.iot.vmp.media.zlm.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
+import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.SIPRequest;
@@ -29,20 +28,20 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.DependsOn;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
-
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
-import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
-import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
 import org.springframework.util.StringUtils;
 
+import javax.sip.*;
+import javax.sip.address.SipURI;
+import javax.sip.header.CallIdHeader;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.Request;
+import java.lang.reflect.Field;
+import java.text.ParseException;
+import java.util.HashSet;
+
 /**    
- * @Description:设备能力接口,用于定义设备的控制、查询能力   
+ * @description:设备能力接口,用于定义设备的控制、查询能力   
  * @author: swwheihei
  * @date:   2020年5月3日 下午9:22:48     
  */
@@ -55,12 +54,10 @@ public class SIPCommander implements ISIPCommander {
 	@Autowired
 	private SipConfig sipConfig;
 
-	@Lazy
 	@Autowired
 	@Qualifier(value="tcpSipProvider")
 	private SipProviderImpl tcpSipProvider;
 
-	@Lazy
 	@Autowired
 	@Qualifier(value="udpSipProvider")
 	private SipProviderImpl udpSipProvider;
@@ -89,11 +86,6 @@ public class SIPCommander implements ISIPCommander {
 	@Autowired
 	private IMediaServerService mediaServerService;
 
-	private SIPDialog dialog;
-
-	public SipConfig getSipConfig() {
-		return sipConfig;
-	}
 
 	/**
 	 * 云台方向放控制,使用配置文件中的默认镜头移动速度
@@ -361,7 +353,7 @@ public class SIPCommander implements ISIPCommander {
 			//
 			StringBuffer content = new StringBuffer(200);
 			content.append("v=0\r\n");
-			content.append("o="+"00000"+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
+			content.append("o="+ sipConfig.getId()+" 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");
@@ -427,8 +419,8 @@ public class SIPCommander implements ISIPCommander {
 				mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
 				errorEvent.response(e);
 			}), e ->{
-				streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(),e.getClientTransaction());
-				streamSession.put(device.getDeviceId(), channelId , e.getDialog());
+				streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction());
+				streamSession.put(device.getDeviceId(), channelId , e.dialog);
 			});
 
 			
@@ -468,7 +460,7 @@ public class SIPCommander implements ISIPCommander {
 
 			StringBuffer content = new StringBuffer(200);
 	        content.append("v=0\r\n");
-	        content.append("o="+sipConfig.getId()+" 0 0 IN IP4 "+sipConfig.getIp()+"\r\n");
+	        content.append("o="+sipConfig.getId()+" 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");
@@ -532,12 +524,12 @@ public class SIPCommander implements ISIPCommander {
 			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
 					: udpSipProvider.getNewCallId();
 
-	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
+	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
 
 	        transmitRequest(device, request, errorEvent, okEvent -> {
-				Dialog dialog = okEvent.getClientTransaction().getDialog();
-	        	streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), okEvent.getClientTransaction());
-				streamSession.put(device.getDeviceId(), channelId, dialog);
+				ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
+	        	streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), responseEvent.getClientTransaction());
+				streamSession.put(device.getDeviceId(), channelId, okEvent.dialog);
 			});
 		} catch ( SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
@@ -575,7 +567,7 @@ public class SIPCommander implements ISIPCommander {
 
 			StringBuffer content = new StringBuffer(200);
 	        content.append("v=0\r\n");
-	        content.append("o="+sipConfig.getId()+" 0 0 IN IP4 "+sipConfig.getIp()+"\r\n");
+	        content.append("o="+sipConfig.getId()+" 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");
@@ -640,7 +632,7 @@ public class SIPCommander implements ISIPCommander {
 			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
 					: udpSipProvider.getNewCallId();
 
-	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
+	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
 
 	        ClientTransaction transaction = transmitRequest(device, request, errorEvent);
 	        streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
@@ -667,6 +659,10 @@ public class SIPCommander implements ISIPCommander {
 			ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
 			if (transaction == null) {
 				logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
+				SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
+				if (okEvent != null) {
+					okEvent.response(eventResult);
+				}
 				return;
 			}
 			SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
@@ -1200,14 +1196,15 @@ public class SIPCommander implements ISIPCommander {
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 */  
 	@Override
-	public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) {
-		
+	public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent) {
+
+
 		try {
 			StringBuffer recordInfoXml = new StringBuffer(200);
 			recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
 			recordInfoXml.append("<Query>\r\n");
 			recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
-			recordInfoXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
 			recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
 			recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
 			recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
@@ -1224,7 +1221,7 @@ public class SIPCommander implements ISIPCommander {
 			Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
 					"z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);
 
-			transmitRequest(device, request);
+			transmitRequest(device, request, errorEvent);
 		} catch (SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
 			return false;
@@ -1486,6 +1483,33 @@ public class SIPCommander implements ISIPCommander {
 		}
 	}
 
+	@Override
+	public boolean catalogSubscribe(Device device, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\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");
+
+			String tm = Long.toString(System.currentTimeMillis());
+
+			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+					: udpSipProvider.getNewCallId();
+
+			Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "Catalog" , callIdHeader);
+			transmitRequest(device, request, errorEvent, okEvent);
+
+			return true;
+
+		} catch ( NumberFormatException | ParseException | InvalidArgumentException	| SipException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
 
 	private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
 		return transmitRequest(device, request, null, null);
@@ -1506,14 +1530,127 @@ public class SIPCommander implements ISIPCommander {
 		CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
 		// 添加错误订阅
 		if (errorEvent != null) {
-			sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent);
+			sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
+				errorEvent.response(eventResult);
+				sipSubscribe.removeErrorSubscribe(eventResult.callId);
+			}));
 		}
 		// 添加订阅
 		if (okEvent != null) {
-			sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent);
+			sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{
+				okEvent.response(eventResult);
+				sipSubscribe.removeOkSubscribe(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: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
+			content.append("PauseTime: now\r\n");
+			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
+			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: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
+			content.append("Range: npt=now-\r\n");
+			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
+			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: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
+			content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
+
+			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
+			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: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
+			content.append("Scale: " + String.format("%.1f",speed) + "\r\n");
+			Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
+			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();
+		}
+	}
 }

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

@@ -100,7 +100,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                     if (event != null) {
                         logger.info("向上级平台 [ {} ] 注册发上错误: {} ",
                                 parentPlatform.getServerGBId(),
-                                event.getResponse().getReasonPhrase());
+                                event.msg);
                     }
                     if (errorEvent != null ) {
                         errorEvent.response(event);

+ 14 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/ISIPRequestProcessor.java

@@ -0,0 +1,14 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request;
+
+import javax.sip.RequestEvent;
+
+/**
+ * @description: 对SIP事件进行处理,包括request, response, timeout, ioException, transactionTerminated,dialogTerminated
+ * @author: panlinlin
+ * @date:   2021年11月5日 15:47
+ */
+public interface ISIPRequestProcessor {
+
+	void process(RequestEvent event);
+
+}

+ 23 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java

@@ -0,0 +1,23 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request;
+
+import gov.nist.javax.sip.SipProviderImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+/**    
+ * @description:处理接收IPCamera发来的SIP协议请求消息
+ * @author: songww
+ * @date:   2020年5月3日 下午4:42:22     
+ */
+public abstract class SIPRequestProcessorAbstract  {
+
+
+	@Autowired
+	@Qualifier(value="tcpSipProvider")
+	private SipProviderImpl tcpSipProvider;
+
+	@Autowired
+	@Qualifier(value="udpSipProvider")
+	private SipProviderImpl udpSipProvider;
+
+}

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

@@ -0,0 +1,179 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request;
+
+import gov.nist.javax.sip.SipProviderImpl;
+import gov.nist.javax.sip.SipStackImpl;
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.stack.SIPServerTransaction;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+import javax.sip.*;
+import javax.sip.address.Address;
+import javax.sip.address.AddressFactory;
+import javax.sip.address.SipURI;
+import javax.sip.header.ContentTypeHeader;
+import javax.sip.header.HeaderFactory;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.MessageFactory;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.io.ByteArrayInputStream;
+import java.text.ParseException;
+
+/**    
+ * @description:处理接收IPCamera发来的SIP协议请求消息
+ * @author: songww
+ * @date:   2020年5月3日 下午4:42:22     
+ */
+public abstract class SIPRequestProcessorParent {
+
+	private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class);
+
+	@Autowired
+	@Qualifier(value="tcpSipProvider")
+	private SipProviderImpl tcpSipProvider;
+
+	@Autowired
+	@Qualifier(value="udpSipProvider")
+	private SipProviderImpl udpSipProvider;
+
+	/**
+	 * 根据 RequestEvent 获取 ServerTransaction
+	 * @param evt
+	 * @return
+	 */
+	public ServerTransaction getServerTransaction(RequestEvent evt) {
+		Request request = evt.getRequest();
+		ServerTransaction serverTransaction = evt.getServerTransaction();
+		// 判断TCP还是UDP
+		boolean isTcp = false;
+		ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+		String transport = reqViaHeader.getTransport();
+		if (transport.equals("TCP")) {
+			isTcp = true;
+		}
+
+		if (serverTransaction == null) {
+			try {
+				if (isTcp) {
+					SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
+					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
+					if (serverTransaction == null) {
+						serverTransaction = tcpSipProvider.getNewServerTransaction(request);
+					}
+				} else {
+					SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
+					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
+					if (serverTransaction == null) {
+						serverTransaction = udpSipProvider.getNewServerTransaction(request);
+					}
+				}
+			} catch (TransactionAlreadyExistsException e) {
+				logger.error(e.getMessage());
+			} catch (TransactionUnavailableException e) {
+				logger.error(e.getMessage());
+			}
+		}
+		return serverTransaction;
+	}
+	
+	public AddressFactory getAddressFactory() {
+		try {
+			return SipFactory.getInstance().createAddressFactory();
+		} catch (PeerUnavailableException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public HeaderFactory getHeaderFactory() {
+		try {
+			return SipFactory.getInstance().createHeaderFactory();
+		} catch (PeerUnavailableException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public MessageFactory getMessageFactory() {
+		try {
+			return SipFactory.getInstance().createMessageFactory();
+		} catch (PeerUnavailableException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/***
+	 * 回复状态码
+	 * 100 trying
+	 * 200 OK
+	 * 400
+	 * 404
+	 * @param evt
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	public void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
+		ServerTransaction serverTransaction = getServerTransaction(evt);
+		serverTransaction.sendResponse(response);
+		if (statusCode >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) {
+
+			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+		}
+	}
+
+	public void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
+		response.setReasonPhrase(msg);
+		ServerTransaction serverTransaction = getServerTransaction(evt);
+		serverTransaction.sendResponse(response);
+		if (statusCode >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) {
+			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+		}
+	}
+
+	/**
+	 * 回复带sdp的200
+	 * @param evt
+	 * @param sdp
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	public void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
+		SipFactory sipFactory = SipFactory.getInstance();
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		response.setContent(sdp, contentTypeHeader);
+
+		SipURI sipURI = (SipURI)evt.getRequest().getRequestURI();
+
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(
+				sipFactory.createAddressFactory().createSipURI(sipURI.getUser(),  sipURI.getHost()+":"+sipURI.getPort()
+				));
+		response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		getServerTransaction(evt).sendResponse(response);
+	}
+
+	public Element getRootElement(RequestEvent evt) throws DocumentException {
+		return getRootElement(evt, "gb2312");
+	}
+	public Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
+		if (charset == null) charset = "gb2312";
+		Request request = evt.getRequest();
+		SAXReader reader = new SAXReader();
+		reader.setEncoding(charset);
+		Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
+		return xml.getRootElement();
+	}
+
+}

+ 123 - 142
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java

@@ -1,142 +1,123 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.sip.*;
-import javax.sip.address.SipURI;
-import javax.sip.header.FromHeader;
-import javax.sip.header.HeaderAddress;
-import javax.sip.header.ToHeader;
-
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**    
- * @Description:ACK请求处理器  
- * @author: swwheihei
- * @date:   2020年5月3日 下午5:31:45     
- */
-public class AckRequestProcessor extends SIPRequestAbstractProcessor {
-
-	private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
-
-    private IRedisCatchStorage redisCatchStorage;
-
-	private ZLMRTPServerFactory zlmrtpServerFactory;
-
-	private IMediaServerService mediaServerService;
-
-	/**   
-	 * 处理  ACK请求
-	 * 
-	 * @param evt
-	 */
-	@Override
-	public void process(RequestEvent evt) {
-		//Request request = evt.getRequest();
-		Dialog dialog = evt.getDialog();
-		if (dialog == null) return;
-		//DialogState state = dialog.getState();
-		if (/*request.getMecodewwthod().equals(Request.INVITE) &&*/ dialog.getState()== DialogState.CONFIRMED) {
-			String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
-			String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
-			SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId);
-			String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
-			String deviceId = sendRtpItem.getDeviceId();
-			StreamInfo streamInfo = null;
-			if (deviceId == null) {
-				streamInfo = new StreamInfo();
-				streamInfo.setApp(sendRtpItem.getApp());
-				streamInfo.setStreamId(sendRtpItem.getStreamId());
-			}else {
-				streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
-				sendRtpItem.setStreamId(streamInfo.getStreamId());
-				streamInfo.setApp("rtp");
-			}
-
-			redisCatchStorage.updateSendRTPSever(sendRtpItem);
-			logger.info(platformGbId);
-			logger.info(channelId);
-			Map<String, Object> param = new HashMap<>();
-			param.put("vhost","__defaultVhost__");
-			param.put("app",streamInfo.getApp());
-			param.put("stream",streamInfo.getStreamId());
-			param.put("ssrc", sendRtpItem.getSsrc());
-			param.put("dst_url",sendRtpItem.getIp());
-			param.put("dst_port", sendRtpItem.getPort());
-			param.put("is_udp", is_Udp);
-			//param.put ("src_port", sendRtpItem.getLocalPort());
-			// 设备推流查询,成功后才能转推
-			boolean rtpPushed = false;
-			long startTime = System.currentTimeMillis();
-			while (!rtpPushed) {
-				try {
-					if (System.currentTimeMillis() - startTime < 30 * 1000) {
-						MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
-						if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
-							rtpPushed = true;
-							logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
-									streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
-							zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
-						} else {
-							logger.info("等待设备推流[{}/{}].......",
-									streamInfo.getApp() ,streamInfo.getStreamId());
-							Thread.sleep(1000);
-							continue;
-						}
-					} else {
-						rtpPushed = true;
-						logger.info("设备推流[{}/{}]超时,终止向上级推流",
-								streamInfo.getApp() ,streamInfo.getStreamId());
-					}
-				} catch (InterruptedException e) {
-					e.printStackTrace();
-				}
-			}
-		}
-		// try {
-		// 	Request ackRequest = null;
-		// 	CSeq csReq = (CSeq) request.getHeader(CSeq.NAME);
-		// 	ackRequest = dialog.createAck(csReq.getSeqNumber());
-		// 	dialog.sendAck(ackRequest);
-		// 	logger.info("send ack to callee:" + ackRequest.toString());
-		// } catch (SipException e) {
-		// 	e.printStackTrace();
-		// } catch (InvalidArgumentException e) {
-		// 	e.printStackTrace();
-		// }
-		
-	}
-
-	public IRedisCatchStorage getRedisCatchStorage() {
-		return redisCatchStorage;
-	}
-
-	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
-		this.redisCatchStorage = redisCatchStorage;
-	}
-
-	public ZLMRTPServerFactory getZlmrtpServerFactory() {
-		return zlmrtpServerFactory;
-	}
-
-	public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
-		this.zlmrtpServerFactory = zlmrtpServerFactory;
-	}
-
-	public IMediaServerService getMediaServerService() {
-		return mediaServerService;
-	}
-
-	public void setMediaServerService(IMediaServerService mediaServerService) {
-		this.mediaServerService = mediaServerService;
-	}
-}
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.Dialog;
+import javax.sip.DialogState;
+import javax.sip.RequestEvent;
+import javax.sip.address.SipURI;
+import javax.sip.header.FromHeader;
+import javax.sip.header.HeaderAddress;
+import javax.sip.header.ToHeader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * SIP命令类型: ACK请求
+ */
+@Component
+public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
+	private String method = "ACK";
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
+
+	@Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+	@Autowired
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	@Autowired
+	private IMediaServerService mediaServerService;
+
+
+	/**   
+	 * 处理  ACK请求
+	 * 
+	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		Dialog dialog = evt.getDialog();
+		if (dialog == null) return;
+		if (dialog.getState()== DialogState.CONFIRMED) {
+			String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
+			String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
+			SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId);
+			String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
+			String deviceId = sendRtpItem.getDeviceId();
+			StreamInfo streamInfo = null;
+			if (deviceId == null) {
+				streamInfo = new StreamInfo();
+				streamInfo.setApp(sendRtpItem.getApp());
+				streamInfo.setStreamId(sendRtpItem.getStreamId());
+			}else {
+				streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
+				sendRtpItem.setStreamId(streamInfo.getStreamId());
+				streamInfo.setApp("rtp");
+			}
+
+			redisCatchStorage.updateSendRTPSever(sendRtpItem);
+			logger.info(platformGbId);
+			logger.info(channelId);
+			Map<String, Object> param = new HashMap<>();
+			param.put("vhost","__defaultVhost__");
+			param.put("app",streamInfo.getApp());
+			param.put("stream",streamInfo.getStreamId());
+			param.put("ssrc", sendRtpItem.getSsrc());
+			param.put("dst_url",sendRtpItem.getIp());
+			param.put("dst_port", sendRtpItem.getPort());
+			param.put("is_udp", is_Udp);
+			//param.put ("src_port", sendRtpItem.getLocalPort());
+			// 设备推流查询,成功后才能转推
+			boolean rtpPushed = false;
+			long startTime = System.currentTimeMillis();
+			while (!rtpPushed) {
+				try {
+					if (System.currentTimeMillis() - startTime < 30 * 1000) {
+						MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+						if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
+							rtpPushed = true;
+							logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
+									streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
+							zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+						} else {
+							logger.info("等待设备推流[{}/{}].......",
+									streamInfo.getApp() ,streamInfo.getStreamId());
+							Thread.sleep(1000);
+							continue;
+						}
+					} else {
+						rtpPushed = true;
+						logger.info("设备推流[{}/{}]超时,终止向上级推流",
+								streamInfo.getApp() ,streamInfo.getStreamId());
+					}
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+}

+ 114 - 150
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java

@@ -1,150 +1,114 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import javax.sip.*;
-import javax.sip.address.SipURI;
-import javax.sip.header.FromHeader;
-import javax.sip.header.HeaderAddress;
-import javax.sip.header.ToHeader;
-import javax.sip.message.Response;
-
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.text.ParseException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**    
- * @Description: BYE请求处理器
- * @author: lawrencehj
- * @date:   2021年3月9日     
- */
-public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
-
-	private Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
-
-	private ISIPCommander cmder;
-
-	private IRedisCatchStorage redisCatchStorage;
-
-	private IVideoManagerStorager storager;
-
-	private ZLMRTPServerFactory zlmrtpServerFactory;
-
-	private IMediaServerService mediaServerService;
-
-	/**
-	 * 处理BYE请求
-	 * @param evt
-	 */
-	@Override
-	public void process(RequestEvent evt) {
-		try {
-			responseAck(evt);
-			Dialog dialog = evt.getDialog();
-			if (dialog == null) return;
-			if (dialog.getState().equals(DialogState.TERMINATED)) {
-				String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
-				String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
-				SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId);
-				logger.info("收到bye, [{}/{}]", platformGbId, channelId);
-				if (sendRtpItem != null){
-					String streamId = sendRtpItem.getStreamId();
-					Map<String, Object> param = new HashMap<>();
-					param.put("vhost","__defaultVhost__");
-					param.put("app",sendRtpItem.getApp());
-					param.put("stream",streamId);
-					param.put("ssrc",sendRtpItem.getSsrc());
-					logger.info("停止向上级推流:" + streamId);
-					MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
-					zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
-					redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
-					if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
-						logger.info(streamId + "无其它观看者,通知设备停止推流");
-						cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
-					}
-				}
-				// 可能是设备主动停止
-				Device device = storager.queryVideoDeviceByChannelId(platformGbId);
-				if (device != null) {
-					StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
-					if (streamInfo != null) {
-						redisCatchStorage.stopPlay(streamInfo);
-					}
-					storager.stopPlay(device.getDeviceId(), channelId);
-					mediaServerService.closeRTPServer(device, channelId);
-				}
-			}
-		} catch (SipException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			e.printStackTrace();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-	}
-
-	/***
-	 * 回复200 OK
-	 * @param evt
-	 * @throws SipException
-	 * @throws InvalidArgumentException
-	 * @throws ParseException
-	 */
-	private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
-		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
-		ServerTransaction serverTransaction = getServerTransaction(evt);
-		serverTransaction.sendResponse(response);
-		if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-	}
-
-	public IRedisCatchStorage getRedisCatchStorage() {
-		return redisCatchStorage;
-	}
-
-	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
-		this.redisCatchStorage = redisCatchStorage;
-	}
-
-	public ZLMRTPServerFactory getZlmrtpServerFactory() {
-		return zlmrtpServerFactory;
-	}
-
-	public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
-		this.zlmrtpServerFactory = zlmrtpServerFactory;
-	}
-
-	public ISIPCommander getSIPCommander() {
-		return cmder;
-	}
-
-	public void setSIPCommander(ISIPCommander cmder) {
-		this.cmder = cmder;
-	}
-
-	public IMediaServerService getMediaServerService() {
-		return mediaServerService;
-	}
-
-	public void setMediaServerService(IMediaServerService mediaServerService) {
-		this.mediaServerService = mediaServerService;
-	}
-
-	public IVideoManagerStorager getStorager() {
-		return storager;
-	}
-
-	public void setStorager(IVideoManagerStorager storager) {
-		this.storager = storager;
-	}
-}
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+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.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.*;
+import javax.sip.address.SipURI;
+import javax.sip.header.FromHeader;
+import javax.sip.header.HeaderAddress;
+import javax.sip.header.ToHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * SIP命令类型: BYE请求
+ */
+@Component
+public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+	private final Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
+	private final String method = "BYE";
+
+	@Autowired
+	private ISIPCommander cmder;
+
+	@Autowired
+	private IRedisCatchStorage redisCatchStorage;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	@Autowired
+	private IMediaServerService mediaServerService;
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
+
+	/**
+	 * 处理BYE请求
+	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		try {
+			responseAck(evt, Response.OK);
+			Dialog dialog = evt.getDialog();
+			if (dialog == null) return;
+			if (dialog.getState().equals(DialogState.TERMINATED)) {
+				String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
+				String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
+				SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId);
+				logger.info("收到bye, [{}/{}]", platformGbId, channelId);
+				if (sendRtpItem != null){
+					String streamId = sendRtpItem.getStreamId();
+					Map<String, Object> param = new HashMap<>();
+					param.put("vhost","__defaultVhost__");
+					param.put("app",sendRtpItem.getApp());
+					param.put("stream",streamId);
+					param.put("ssrc",sendRtpItem.getSsrc());
+					logger.info("停止向上级推流:" + streamId);
+					MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+					zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
+					redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
+					if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
+						logger.info(streamId + "无其它观看者,通知设备停止推流");
+						cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
+					}
+				}
+				// 可能是设备主动停止
+				Device device = storager.queryVideoDeviceByChannelId(platformGbId);
+				if (device != null) {
+					StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
+					if (streamInfo != null) {
+						redisCatchStorage.stopPlay(streamInfo);
+					}
+					storager.stopPlay(device.getDeviceId(), channelId);
+					mediaServerService.closeRTPServer(device, channelId);
+				}
+			}
+		} catch (SipException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			e.printStackTrace();
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 40 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java

@@ -0,0 +1,40 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.RequestEvent;
+
+/**
+ * SIP命令类型: CANCEL请求
+ */
+@Component
+public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+	private String method = "CANCEL";
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
+
+	/**   
+	 * 处理CANCEL请求
+	 *  
+	 * @param evt 事件
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		// TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令
+		
+	}
+
+}

+ 395 - 478
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java

@@ -1,478 +1,395 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import javax.sdp.*;
-import javax.sip.*;
-import javax.sip.address.Address;
-import javax.sip.address.SipURI;
-import javax.sip.header.*;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-import com.genersoft.iot.vmp.gb28181.bean.*;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
-import com.genersoft.iot.vmp.service.IPlayService;
-import gov.nist.javax.sip.address.AddressImpl;
-import gov.nist.javax.sip.address.SipUri;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.text.ParseException;
-import java.util.Vector;
-
-/**    
- * @Description:处理INVITE请求
- * @author: panll
- * @date:   2021年1月14日
- */
-@SuppressWarnings("rawtypes")
-public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
-
-	private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
-
-	private SIPCommanderFroPlatform cmderFroPlatform;
-
-	private IVideoManagerStorager storager;
-
-	private IRedisCatchStorage  redisCatchStorage;
-
-	private SIPCommander cmder;
-
-	private IPlayService playService;
-
-	private ZLMRTPServerFactory zlmrtpServerFactory;
-
-	private IMediaServerService mediaServerService;
-
-	public ZLMRTPServerFactory getZlmrtpServerFactory() {
-		return zlmrtpServerFactory;
-	}
-
-	public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
-		this.zlmrtpServerFactory = zlmrtpServerFactory;
-	}
-
-	/**
-	 * 处理invite请求
-	 * 
-	 * @param evt
-	 *            请求消息
-	 */ 
-	@Override
-	public void process(RequestEvent evt) {
-		//  Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
-		try {
-			Request request = evt.getRequest();
-			SipURI sipURI = (SipURI) request.getRequestURI();
-			String channelId = sipURI.getUser();
-			String requesterId = null;
-
-			FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
-			AddressImpl address = (AddressImpl) fromHeader.getAddress();
-			SipUri uri = (SipUri) address.getURI();
-			requesterId = uri.getUser();
-
-			if (requesterId == null || channelId == null) {
-				logger.info("无法从FromHeader的Address中获取到平台id,返回400");
-				responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误
-				return;
-			}
-
-			// 查询请求方是否上级平台
-			ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
-			if (platform != null) {
-				// 查询平台下是否有该通道
-				DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
-				GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
-				MediaServerItem mediaServerItem = null;
-				// 不是通道可能是直播流
-				if (channel != null && gbStream == null ) {
-					if (channel.getStatus() == 0) {
-						logger.info("通道离线,返回400");
-						responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
-						return;
-					}
-					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
-				}else if(channel == null && gbStream != null){
-					String mediaServerId = gbStream.getMediaServerId();
-					mediaServerItem = mediaServerService.getOne(mediaServerId);
-					if (mediaServerItem == null) {
-						logger.info("[ app={}, stream={} ]zlm找不到,返回410",gbStream.getApp(), gbStream.getStream());
-						responseAck(evt, Response.GONE, "media server not found");
-						return;
-					}
-					Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
-					if (!streamReady ) {
-						logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream());
-						responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
-						return;
-					}
-					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
-				}else {
-					logger.info("通道不存在,返回404");
-					responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
-					return;
-				}
-				// 解析sdp消息, 使用jainsip 自带的sdp解析方式
-				String contentString = new String(request.getRawContent());
-
-				// jainSip不支持y=字段, 移除移除以解析。
-				int ssrcIndex = contentString.indexOf("y=");
-				//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
-				String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
-				String substring = contentString.substring(0, contentString.indexOf("y="));
-				SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
-
-				//  获取支持的格式
-				Vector mediaDescriptions = sdp.getMediaDescriptions(true);
-				// 查看是否支持PS 负载96
-				//String ip = null;
-				int port = -1;
-				//boolean recvonly = false;
-				boolean mediaTransmissionTCP = false;
-				Boolean tcpActive = null;
-				for (Object description : mediaDescriptions) {
-					MediaDescription mediaDescription = (MediaDescription) description;
-					Media media = mediaDescription.getMedia();
-
-					Vector mediaFormats = media.getMediaFormats(false);
-					if (mediaFormats.contains("96")) {
-						port = media.getMediaPort();
-						//String mediaType = media.getMediaType();
-						String protocol = media.getProtocol();
-
-						// 区分TCP发流还是udp, 当前默认udp
-						if ("TCP/RTP/AVP".equals(protocol)) {
-							String setup = mediaDescription.getAttribute("setup");
-							if (setup != null) {
-								mediaTransmissionTCP = true;
-								if ("active".equals(setup)) {
-									tcpActive = true;
-								} else if ("passive".equals(setup)) {
-									tcpActive = false;
-								}
-							}
-						}
-						break;
-					}
-				}
-				if (port == -1) {
-					logger.info("不支持的媒体格式,返回415");
-					// 回复不支持的格式
-					responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
-					return;
-				}
-				String username = sdp.getOrigin().getUsername();
-				String addressStr = sdp.getOrigin().getAddress();
-				//String sessionName = sdp.getSessionName().getValue();
-				logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);
-				Device device  = null;
-				// 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
-				if (channel != null) {
-					device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
-					if (device == null) {
-						logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
-						responseAck(evt, Response.SERVER_INTERNAL_ERROR);
-						return;
-					}
-					mediaServerItem = playService.getNewMediaServerItem(device);
-					if (mediaServerItem == null) {
-						logger.warn("未找到可用的zlm");
-						responseAck(evt, Response.BUSY_HERE);
-						return;
-					}
-					SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
-							device.getDeviceId(), channelId,
-							mediaTransmissionTCP);
-					if (tcpActive != null) {
-						sendRtpItem.setTcpActive(tcpActive);
-					}
-					if (sendRtpItem == null) {
-						logger.warn("服务器端口资源不足");
-						responseAck(evt, Response.BUSY_HERE);
-						return;
-					}
-
-					// 写入redis, 超时时回复
-					redisCatchStorage.updateSendRTPSever(sendRtpItem);
-					// 通知下级推流,
-					PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{
-						// 收到推流, 回复200OK, 等待ack
-						// if (sendRtpItem == null) return;
-						sendRtpItem.setStatus(1);
-						redisCatchStorage.updateSendRTPSever(sendRtpItem);
-						// TODO 添加对tcp的支持
-
-						StringBuffer content = new StringBuffer(200);
-						content.append("v=0\r\n");
-						content.append("o="+"00000"+" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
-						content.append("s=Play\r\n");
-						content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
-						content.append("t=0 0\r\n");
-						content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
-						content.append("a=sendonly\r\n");
-						content.append("a=rtpmap:96 PS/90000\r\n");
-						content.append("y="+ ssrc + "\r\n");
-						content.append("f=\r\n");
-
-						try {
-							responseAck(evt, content.toString());
-						} catch (SipException e) {
-							e.printStackTrace();
-						} catch (InvalidArgumentException e) {
-							e.printStackTrace();
-						} catch (ParseException e) {
-							e.printStackTrace();
-						}
-					} ,((event) -> {
-						// 未知错误。直接转发设备点播的错误
-						Response response = null;
-						try {
-							response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest());
-							ServerTransaction serverTransaction = getServerTransaction(evt);
-							serverTransaction.sendResponse(response);
-							if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-						} catch (ParseException | SipException | InvalidArgumentException e) {
-							e.printStackTrace();
-						}
-					}));
-					if (logger.isDebugEnabled()) {
-						logger.debug(playResult.getResult().toString());
-					}
-
-				}else if (gbStream != null) {
-					SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
-							gbStream.getApp(), gbStream.getStream(), channelId,
-							mediaTransmissionTCP);
-
-					if (tcpActive != null) {
-						sendRtpItem.setTcpActive(tcpActive);
-					}
-					if (sendRtpItem == null) {
-						logger.warn("服务器端口资源不足");
-						responseAck(evt, Response.BUSY_HERE);
-						return;
-					}
-
-					// 写入redis, 超时时回复
-					redisCatchStorage.updateSendRTPSever(sendRtpItem);
-
-					sendRtpItem.setStatus(1);
-					redisCatchStorage.updateSendRTPSever(sendRtpItem);
-					// TODO 添加对tcp的支持
-					StringBuffer content = new StringBuffer(200);
-					content.append("v=0\r\n");
-					content.append("o="+"00000"+" 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");
-					content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
-					content.append("a=sendonly\r\n");
-					content.append("a=rtpmap:96 PS/90000\r\n");
-					content.append("y="+ ssrc + "\r\n");
-					content.append("f=\r\n");
-
-					try {
-						responseAck(evt, content.toString());
-					} catch (SipException e) {
-						e.printStackTrace();
-					} catch (InvalidArgumentException e) {
-						e.printStackTrace();
-					} catch (ParseException e) {
-						e.printStackTrace();
-					}
-				}
-
-			} else {
-				// 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
-				Device device = storager.queryVideoDevice(requesterId);
-				if (device != null) {
-					logger.info("收到设备" + requesterId + "的语音广播Invite请求");
-					responseAck(evt, Response.TRYING);
-
-					String contentString = new String(request.getRawContent());
-					// jainSip不支持y=字段, 移除移除以解析。
-					String substring = contentString;
-					String ssrc = "0000000404";
-					int ssrcIndex = contentString.indexOf("y=");
-					if (ssrcIndex > 0) {
-						substring = contentString.substring(0, ssrcIndex);
-						ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
-					}
-					ssrcIndex = substring.indexOf("f=");
-					if (ssrcIndex > 0) {
-						substring = contentString.substring(0, ssrcIndex);
-					}
-					SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
-
-					//  获取支持的格式
-					Vector mediaDescriptions = sdp.getMediaDescriptions(true);
-					// 查看是否支持PS 负载96
-					int port = -1;
-					//boolean recvonly = false;
-					boolean mediaTransmissionTCP = false;
-					Boolean tcpActive = null;
-					for (int i = 0; i < mediaDescriptions.size(); i++) {
-						MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
-						Media media = mediaDescription.getMedia();
-
-						Vector mediaFormats = media.getMediaFormats(false);
-						if (mediaFormats.contains("8")) {
-							port = media.getMediaPort();
-							String protocol = media.getProtocol();
-							// 区分TCP发流还是udp, 当前默认udp
-							if ("TCP/RTP/AVP".equals(protocol)) {
-								String setup = mediaDescription.getAttribute("setup");
-								if (setup != null) {
-									mediaTransmissionTCP = true;
-									if ("active".equals(setup)) {
-										tcpActive = true;
-									} else if ("passive".equals(setup)) {
-										tcpActive = false;
-									}
-								}
-							}
-							break;
-						}
-					}
-					if (port == -1) {
-						logger.info("不支持的媒体格式,返回415");
-						// 回复不支持的格式
-						responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
-						return;
-					}
-					String username = sdp.getOrigin().getUsername();
-					String addressStr = sdp.getOrigin().getAddress();
-					logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
-
-				} else {
-					logger.warn("来自无效设备/平台的请求");
-					responseAck(evt, Response.BAD_REQUEST);
-				}
-			}
-
-		} catch (SipException | InvalidArgumentException | ParseException e) {
-			e.printStackTrace();
-			logger.warn("sdp解析错误");
-			e.printStackTrace();
-		} catch (SdpParseException e) {
-			e.printStackTrace();
-		} catch (SdpException e) {
-			e.printStackTrace();
-		}
-	}
-
-
-	/***
-	 * 回复状态码
-	 * 100 trying
-	 * 200 OK
-	 * 400
-	 * 404
-	 * @param evt
-	 * @throws SipException
-	 * @throws InvalidArgumentException
-	 * @throws ParseException
-	 */
-	private void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {
-		Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
-		ServerTransaction serverTransaction = getServerTransaction(evt);
-		serverTransaction.sendResponse(response);
-		if (statusCode >= 200) {
-			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-		}
-	}
-
-	private void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
-		Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
-		response.setReasonPhrase(msg);
-		ServerTransaction serverTransaction = getServerTransaction(evt);
-		serverTransaction.sendResponse(response);
-		if (statusCode >= 200) {
-			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-		}
-	}
-
-	/**
-	 * 回复带sdp的200
-	 * @param evt
-	 * @param sdp
-	 * @throws SipException
-	 * @throws InvalidArgumentException
-	 * @throws ParseException
-	 */
-	private void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {
-		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
-		SipFactory sipFactory = SipFactory.getInstance();
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
-		response.setContent(sdp, contentTypeHeader);
-
-		SipURI sipURI = (SipURI)evt.getRequest().getRequestURI();
-
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(
-				sipFactory.createAddressFactory().createSipURI(sipURI.getUser(),  sipURI.getHost()+":"+sipURI.getPort()
-				));
-		response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
-		getServerTransaction(evt).sendResponse(response);
-	}
-
-
-
-
-
-
-	public SIPCommanderFroPlatform getCmderFroPlatform() {
-		return cmderFroPlatform;
-	}
-
-	public void setCmderFroPlatform(SIPCommanderFroPlatform cmderFroPlatform) {
-		this.cmderFroPlatform = cmderFroPlatform;
-	}
-
-	public IVideoManagerStorager getStorager() {
-		return storager;
-	}
-
-	public void setStorager(IVideoManagerStorager storager) {
-		this.storager = storager;
-	}
-
-	public SIPCommander getCmder() {
-		return cmder;
-	}
-
-	public void setCmder(SIPCommander cmder) {
-		this.cmder = cmder;
-	}
-
-	public IPlayService getPlayService() {
-		return playService;
-	}
-
-	public void setPlayService(IPlayService playService) {
-		this.playService = playService;
-	}
-
-	public IRedisCatchStorage getRedisCatchStorage() {
-		return redisCatchStorage;
-	}
-
-	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
-		this.redisCatchStorage = redisCatchStorage;
-	}
-
-	public IMediaServerService getMediaServerService() {
-		return mediaServerService;
-	}
-
-	public void setMediaServerService(IMediaServerService mediaServerService) {
-		this.mediaServerService = mediaServerService;
-	}
-}
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+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.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.IPlayService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
+import gov.nist.javax.sip.address.AddressImpl;
+import gov.nist.javax.sip.address.SipUri;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sdp.*;
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.address.SipURI;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.Vector;
+
+/**
+ * SIP命令类型: INVITE请求
+ */
+@SuppressWarnings("rawtypes")
+@Component
+public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+	private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class);
+
+	private String method = "INVITE";
+
+	@Autowired
+	private SIPCommanderFroPlatform cmderFroPlatform;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private IRedisCatchStorage  redisCatchStorage;
+
+	@Autowired
+	private SIPCommander cmder;
+
+	@Autowired
+	private IPlayService playService;
+
+	@Autowired
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	@Autowired
+	private IMediaServerService mediaServerService;
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
+
+	/**
+	 * 处理invite请求
+	 * 
+	 * @param evt
+	 *            请求消息
+	 */ 
+	@Override
+	public void process(RequestEvent evt) {
+		//  Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
+		try {
+			Request request = evt.getRequest();
+			SipURI sipURI = (SipURI) request.getRequestURI();
+			String channelId = sipURI.getUser();
+			String requesterId = null;
+
+			FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
+			AddressImpl address = (AddressImpl) fromHeader.getAddress();
+			SipUri uri = (SipUri) address.getURI();
+			requesterId = uri.getUser();
+
+			if (requesterId == null || channelId == null) {
+				logger.info("无法从FromHeader的Address中获取到平台id,返回400");
+				responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误
+				return;
+			}
+
+			// 查询请求方是否上级平台
+			ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
+			if (platform != null) {
+				// 查询平台下是否有该通道
+				DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
+				GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
+				MediaServerItem mediaServerItem = null;
+				// 不是通道可能是直播流
+				if (channel != null && gbStream == null ) {
+					if (channel.getStatus() == 0) {
+						logger.info("通道离线,返回400");
+						responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
+						return;
+					}
+					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
+				}else if(channel == null && gbStream != null){
+					String mediaServerId = gbStream.getMediaServerId();
+					mediaServerItem = mediaServerService.getOne(mediaServerId);
+					if (mediaServerItem == null) {
+						logger.info("[ app={}, stream={} ]找不到zlm {},返回410",gbStream.getApp(), gbStream.getStream(), mediaServerId);
+						responseAck(evt, Response.GONE, "media server not found");
+						return;
+					}
+					Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
+					if (!streamReady ) {
+						logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream());
+						responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
+						return;
+					}
+					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
+				}else {
+					logger.info("通道不存在,返回404");
+					responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
+					return;
+				}
+				// 解析sdp消息, 使用jainsip 自带的sdp解析方式
+				String contentString = new String(request.getRawContent());
+
+				// jainSip不支持y=字段, 移除以解析。
+				int ssrcIndex = contentString.indexOf("y=");
+				// 检查是否有y字段
+				String ssrcDefault = "0000000000";
+				String ssrc;
+				SessionDescription sdp;
+				if (ssrcIndex >= 0) {
+					//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
+					ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+					String substring = contentString.substring(0, contentString.indexOf("y="));
+					sdp = SdpFactory.getInstance().createSessionDescription(substring);
+				}else {
+					ssrc = ssrcDefault;
+					sdp = SdpFactory.getInstance().createSessionDescription(contentString);
+				}
+
+				//  获取支持的格式
+				Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+				// 查看是否支持PS 负载96
+				//String ip = null;
+				int port = -1;
+				//boolean recvonly = false;
+				boolean mediaTransmissionTCP = false;
+				Boolean tcpActive = null;
+				for (Object description : mediaDescriptions) {
+					MediaDescription mediaDescription = (MediaDescription) description;
+					Media media = mediaDescription.getMedia();
+
+					Vector mediaFormats = media.getMediaFormats(false);
+					if (mediaFormats.contains("96")) {
+						port = media.getMediaPort();
+						//String mediaType = media.getMediaType();
+						String protocol = media.getProtocol();
+
+						// 区分TCP发流还是udp, 当前默认udp
+						if ("TCP/RTP/AVP".equals(protocol)) {
+							String setup = mediaDescription.getAttribute("setup");
+							if (setup != null) {
+								mediaTransmissionTCP = true;
+								if ("active".equals(setup)) {
+									tcpActive = true;
+								} else if ("passive".equals(setup)) {
+									tcpActive = false;
+								}
+							}
+						}
+						break;
+					}
+				}
+				if (port == -1) {
+					logger.info("不支持的媒体格式,返回415");
+					// 回复不支持的格式
+					responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
+					return;
+				}
+				String username = sdp.getOrigin().getUsername();
+				String addressStr = sdp.getOrigin().getAddress();
+				//String sessionName = sdp.getSessionName().getValue();
+				logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);
+				Device device  = null;
+				// 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
+				if (channel != null) {
+					device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
+					if (device == null) {
+						logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
+						responseAck(evt, Response.SERVER_INTERNAL_ERROR);
+						return;
+					}
+					mediaServerItem = playService.getNewMediaServerItem(device);
+					if (mediaServerItem == null) {
+						logger.warn("未找到可用的zlm");
+						responseAck(evt, Response.BUSY_HERE);
+						return;
+					}
+					SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
+							device.getDeviceId(), channelId,
+							mediaTransmissionTCP);
+					if (tcpActive != null) {
+						sendRtpItem.setTcpActive(tcpActive);
+					}
+					if (sendRtpItem == null) {
+						logger.warn("服务器端口资源不足");
+						responseAck(evt, Response.BUSY_HERE);
+						return;
+					}
+
+					// 写入redis, 超时时回复
+					redisCatchStorage.updateSendRTPSever(sendRtpItem);
+					// 通知下级推流,
+					PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{
+						// 收到推流, 回复200OK, 等待ack
+						// if (sendRtpItem == null) return;
+						sendRtpItem.setStatus(1);
+						redisCatchStorage.updateSendRTPSever(sendRtpItem);
+						// TODO 添加对tcp的支持
+
+						StringBuffer content = new StringBuffer(200);
+						content.append("v=0\r\n");
+						content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
+						content.append("s=Play\r\n");
+						content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
+						content.append("t=0 0\r\n");
+						content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
+						content.append("a=sendonly\r\n");
+						content.append("a=rtpmap:96 PS/90000\r\n");
+						content.append("y="+ ssrc + "\r\n");
+						content.append("f=\r\n");
+
+						try {
+							responseAck(evt, content.toString());
+						} catch (SipException e) {
+							e.printStackTrace();
+						} catch (InvalidArgumentException e) {
+							e.printStackTrace();
+						} catch (ParseException e) {
+							e.printStackTrace();
+						}
+					} ,((event) -> {
+						// 未知错误。直接转发设备点播的错误
+						Response response = null;
+						try {
+							response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
+							ServerTransaction serverTransaction = getServerTransaction(evt);
+							serverTransaction.sendResponse(response);
+							if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+						} catch (ParseException | SipException | InvalidArgumentException e) {
+							e.printStackTrace();
+						}
+					}));
+					if (logger.isDebugEnabled()) {
+						logger.debug(playResult.getResult().toString());
+					}
+
+				}else if (gbStream != null) {
+					SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
+							gbStream.getApp(), gbStream.getStream(), channelId,
+							mediaTransmissionTCP);
+
+					if (tcpActive != null) {
+						sendRtpItem.setTcpActive(tcpActive);
+					}
+					if (sendRtpItem == null) {
+						logger.warn("服务器端口资源不足");
+						responseAck(evt, Response.BUSY_HERE);
+						return;
+					}
+
+					// 写入redis, 超时时回复
+					redisCatchStorage.updateSendRTPSever(sendRtpItem);
+
+					sendRtpItem.setStatus(1);
+					redisCatchStorage.updateSendRTPSever(sendRtpItem);
+					// TODO 添加对tcp的支持
+					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");
+					content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
+					content.append("a=sendonly\r\n");
+					content.append("a=rtpmap:96 PS/90000\r\n");
+					content.append("y="+ ssrc + "\r\n");
+					content.append("f=\r\n");
+
+					try {
+						responseAck(evt, content.toString());
+					} catch (SipException e) {
+						e.printStackTrace();
+					} catch (InvalidArgumentException e) {
+						e.printStackTrace();
+					} catch (ParseException e) {
+						e.printStackTrace();
+					}
+				}
+
+			} else {
+				// 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
+				Device device = storager.queryVideoDevice(requesterId);
+				if (device != null) {
+					logger.info("收到设备" + requesterId + "的语音广播Invite请求");
+					responseAck(evt, Response.TRYING);
+
+					String contentString = new String(request.getRawContent());
+					// jainSip不支持y=字段, 移除移除以解析。
+					String substring = contentString;
+					String ssrc = "0000000404";
+					int ssrcIndex = contentString.indexOf("y=");
+					if (ssrcIndex > 0) {
+						substring = contentString.substring(0, ssrcIndex);
+						ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+					}
+					ssrcIndex = substring.indexOf("f=");
+					if (ssrcIndex > 0) {
+						substring = contentString.substring(0, ssrcIndex);
+					}
+					SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
+
+					//  获取支持的格式
+					Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+					// 查看是否支持PS 负载96
+					int port = -1;
+					//boolean recvonly = false;
+					boolean mediaTransmissionTCP = false;
+					Boolean tcpActive = null;
+					for (int i = 0; i < mediaDescriptions.size(); i++) {
+						MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
+						Media media = mediaDescription.getMedia();
+
+						Vector mediaFormats = media.getMediaFormats(false);
+						if (mediaFormats.contains("8")) {
+							port = media.getMediaPort();
+							String protocol = media.getProtocol();
+							// 区分TCP发流还是udp, 当前默认udp
+							if ("TCP/RTP/AVP".equals(protocol)) {
+								String setup = mediaDescription.getAttribute("setup");
+								if (setup != null) {
+									mediaTransmissionTCP = true;
+									if ("active".equals(setup)) {
+										tcpActive = true;
+									} else if ("passive".equals(setup)) {
+										tcpActive = false;
+									}
+								}
+							}
+							break;
+						}
+					}
+					if (port == -1) {
+						logger.info("不支持的媒体格式,返回415");
+						// 回复不支持的格式
+						responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
+						return;
+					}
+					String username = sdp.getOrigin().getUsername();
+					String addressStr = sdp.getOrigin().getAddress();
+					logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
+
+				} else {
+					logger.warn("来自无效设备/平台的请求");
+					responseAck(evt, Response.BAD_REQUEST);
+				}
+			}
+
+		} catch (SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+			logger.warn("sdp解析错误");
+			e.printStackTrace();
+		} catch (SdpParseException e) {
+			e.printStackTrace();
+		} catch (SdpException e) {
+			e.printStackTrace();
+		}
+	}
+}

文件差異過大導致無法顯示
+ 1104 - 1100
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java


+ 177 - 142
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java

@@ -1,67 +1,76 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import java.io.ByteArrayInputStream;
-import java.text.ParseException;
-import java.util.Iterator;
-
-import javax.sip.InvalidArgumentException;
-import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.UserSetup;
-import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
+import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 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.request.SIPRequestAbstractProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.genersoft.iot.vmp.utils.GpsUtil;
-import com.genersoft.iot.vmp.utils.SpringBeanFactory;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
-
-import org.dom4j.Document;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
-import org.dom4j.io.SAXReader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.Iterator;
+
 /**
- * @Description: Notify请求处理器
- * @author: lawrencehj
- * @date: 2021年1月27日
+ * SIP命令类型: NOTIFY请求
  */
+@Component
+public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
 
-public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
-    
-	private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup");
+    private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class);
 
-    private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
+	@Autowired
+	private UserSetup userSetup;
 
+	@Autowired
 	private IVideoManagerStorager storager;
 
+	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 
+	@Autowired
 	private EventPublisher publisher;
 
+	@Autowired
 	private DeviceOffLineDetector offLineDetector;
 
 	private static final String NOTIFY_CATALOG = "Catalog";
 	private static final String NOTIFY_ALARM = "Alarm";
 	private static final String NOTIFY_MOBILE_POSITION = "MobilePosition";
+	private String method = "NOTIFY";
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
 
 	@Override
 	public void process(RequestEvent evt) {
@@ -80,7 +89,7 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 				processNotifyMobilePosition(evt);
 			} else {
 				logger.info("接收到消息:" + cmd);
-				response200Ok(evt);
+				responseAck(evt, Response.OK);
 			}
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
@@ -135,7 +144,7 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 				storager.clearMobilePositionsByDeviceId(deviceId);
 			}
 			storager.insertMobilePosition(mobilePosition);
-			response200Ok(evt);
+			responseAck(evt, Response.OK);
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
 		}
@@ -199,7 +208,7 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 			// TODO: 需要实现存储报警信息、报警分类
 
 			// 回复200 OK
-			response200Ok(evt);
+			responseAck(evt, Response.OK);
 			if (offLineDetector.isOnline(deviceId)) {
 				publisher.deviceAlarmEventPublish(deviceAlarm);
 			}
@@ -215,10 +224,14 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 	 */
 	private void processNotifyCatalogList(RequestEvent evt) {
 		try {
+			FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
+			String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
+
 			Element rootElement = getRootElement(evt);
-			Element deviceIdElement = rootElement.element("DeviceID");
-			String deviceId = deviceIdElement.getText();
 			Device device = storager.queryVideoDevice(deviceId);
+			if (device == null) {
+				return;
+			}
 			if (device != null ) {
 				rootElement = getRootElement(evt, device.getCharset());
 			}
@@ -228,9 +241,7 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 			}
 			Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
 			if (deviceListIterator != null) {
-				if (device == null) {
-					return;
-				}
+
 				// 遍历DeviceList
 				while (deviceListIterator.hasNext()) {
 					Element itemDevice = deviceListIterator.next();
@@ -238,87 +249,51 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 					if (channelDeviceElement == null) {
 						continue;
 					}
-					String channelDeviceId = channelDeviceElement.getTextTrim();
-					Element channdelNameElement = itemDevice.element("Name");
-					String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
-					Element statusElement = itemDevice.element("Status");
-					String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
-					DeviceChannel deviceChannel = new DeviceChannel();
-					deviceChannel.setName(channelName);
-					deviceChannel.setChannelId(channelDeviceId);
-					// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
-					if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
-						deviceChannel.setStatus(1);
-					}
-					if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
-						deviceChannel.setStatus(0);
-					}
+					Element eventElement = itemDevice.element("Event");
+					DeviceChannel channel = channelContentHander(itemDevice);
+					switch (eventElement.getText().toUpperCase()) {
+						case "ON" : // 上线
+							logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId());
+							storager.deviceChannelOnline(deviceId, channel.getChannelId());
+							// 回复200 OK
+							responseAck(evt, Response.OK);
+							break;
+						case "OFF" : // 离线
+							logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId());
+							storager.deviceChannelOffline(deviceId, channel.getChannelId());
+							// 回复200 OK
+							responseAck(evt, Response.OK);
+							break;
+						case "VLOST" : // 视频丢失
+							logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId());
+							storager.deviceChannelOffline(deviceId, channel.getChannelId());
+							// 回复200 OK
+							responseAck(evt, Response.OK);
+							break;
+						case "DEFECT" : // 故障
+							// 回复200 OK
+							responseAck(evt, Response.OK);
+							break;
+						case "ADD" : // 增加
+							logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId());
+							storager.updateChannel(deviceId, channel);
+							responseAck(evt, Response.OK);
+							break;
+						case "DEL" : // 删除
+							logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId());
+							storager.delChannel(deviceId, channel.getChannelId());
+							responseAck(evt, Response.OK);
+							break;
+						case "UPDATE" : // 更新
+							logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId());
+							storager.updateChannel(deviceId, channel);
+							responseAck(evt, Response.OK);
+							break;
+						default:
+							responseAck(evt, Response.BAD_REQUEST, "event not found");
 
-					deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
-					deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
-					deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
-					deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
-					deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
-					deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
-					if (XmlUtil.getText(itemDevice, "Parental") == null
-							|| XmlUtil.getText(itemDevice, "Parental") == "") {
-						deviceChannel.setParental(0);
-					} else {
-						deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
 					}
-					deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
-					if (XmlUtil.getText(itemDevice, "SafetyWay") == null
-							|| XmlUtil.getText(itemDevice, "SafetyWay") == "") {
-						deviceChannel.setSafetyWay(0);
-					} else {
-						deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
-					}
-					if (XmlUtil.getText(itemDevice, "RegisterWay") == null
-							|| XmlUtil.getText(itemDevice, "RegisterWay") == "") {
-						deviceChannel.setRegisterWay(1);
-					} else {
-						deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
-					}
-					deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
-					if (XmlUtil.getText(itemDevice, "Certifiable") == null
-							|| XmlUtil.getText(itemDevice, "Certifiable") == "") {
-						deviceChannel.setCertifiable(0);
-					} else {
-						deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
-					}
-					if (XmlUtil.getText(itemDevice, "ErrCode") == null
-							|| XmlUtil.getText(itemDevice, "ErrCode") == "") {
-						deviceChannel.setErrCode(0);
-					} else {
-						deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
-					}
-					deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
-					deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
-					deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
-					if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") {
-						deviceChannel.setPort(0);
-					} else {
-						deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
-					}
-					deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
-					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
-						deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
-					} else {
-						deviceChannel.setLongitude(0.00);
-					}
-					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) {
-						deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
-					} else {
-						deviceChannel.setLatitude(0.00);
-					}
-					if (XmlUtil.getText(itemDevice, "PTZType") == null
-							|| XmlUtil.getText(itemDevice, "PTZType") == "") {
-						deviceChannel.setPTZType(0);
-					} else {
-						deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
-					}
-					deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
-					storager.updateChannel(device.getDeviceId(), deviceChannel);
+
 				}
 
 				// RequestMessage msg = new RequestMessage();
@@ -326,8 +301,7 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 				// msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
 				// msg.setData(device);
 				// deferredResultHolder.invokeResult(msg);
-				// 回复200 OK
-				response200Ok(evt);
+
 				if (offLineDetector.isOnline(deviceId)) {
 					publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
 				}
@@ -337,32 +311,93 @@ public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
 		}
 	}
 
-	/***
-	 * 回复200 OK
-	 * 
-	 * @param evt
-	 * @throws SipException
-	 * @throws InvalidArgumentException
-	 * @throws ParseException
-	 */
-	private void response200Ok(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
-		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
-		ServerTransaction serverTransaction = getServerTransaction(evt);
-		serverTransaction.sendResponse(response);
-		if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-	}
-	private Element getRootElement(RequestEvent evt) throws DocumentException {
-		return getRootElement(evt, "gb2312");
-	}
-	private Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
-		if (charset == null) charset = "gb2312";
-		Request request = evt.getRequest();
-		SAXReader reader = new SAXReader();
-		reader.setEncoding(charset);
-		Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
-		return xml.getRootElement();
+	public DeviceChannel channelContentHander(Element itemDevice){
+		Element channdelNameElement = itemDevice.element("Name");
+		String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
+		Element statusElement = itemDevice.element("Status");
+		String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
+		DeviceChannel deviceChannel = new DeviceChannel();
+		deviceChannel.setName(channelName);
+		Element channdelIdElement = itemDevice.element("DeviceID");
+		String channelId = channdelIdElement != null ? channdelIdElement.getTextTrim().toString() : "";
+		deviceChannel.setChannelId(channelId);
+		// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
+		if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
+			deviceChannel.setStatus(1);
+		}
+		if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
+			deviceChannel.setStatus(0);
+		}
+
+		deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
+		deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
+		deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
+		deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
+		deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
+		deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
+		if (XmlUtil.getText(itemDevice, "Parental") == null
+				|| XmlUtil.getText(itemDevice, "Parental") == "") {
+			deviceChannel.setParental(0);
+		} else {
+			deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
+		}
+		deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
+		if (XmlUtil.getText(itemDevice, "SafetyWay") == null
+				|| XmlUtil.getText(itemDevice, "SafetyWay") == "") {
+			deviceChannel.setSafetyWay(0);
+		} else {
+			deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
+		}
+		if (XmlUtil.getText(itemDevice, "RegisterWay") == null
+				|| XmlUtil.getText(itemDevice, "RegisterWay") == "") {
+			deviceChannel.setRegisterWay(1);
+		} else {
+			deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
+		}
+		deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
+		if (XmlUtil.getText(itemDevice, "Certifiable") == null
+				|| XmlUtil.getText(itemDevice, "Certifiable") == "") {
+			deviceChannel.setCertifiable(0);
+		} else {
+			deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
+		}
+		if (XmlUtil.getText(itemDevice, "ErrCode") == null
+				|| XmlUtil.getText(itemDevice, "ErrCode") == "") {
+			deviceChannel.setErrCode(0);
+		} else {
+			deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
+		}
+		deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
+		deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
+		deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
+		if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") {
+			deviceChannel.setPort(0);
+		} else {
+			deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
+		}
+		deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
+		if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
+			deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
+		} else {
+			deviceChannel.setLongitude(0.00);
+		}
+		if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) {
+			deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
+		} else {
+			deviceChannel.setLatitude(0.00);
+		}
+		if (XmlUtil.getText(itemDevice, "PTZType") == null
+				|| XmlUtil.getText(itemDevice, "PTZType") == "") {
+			deviceChannel.setPTZType(0);
+		} else {
+			deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
+		}
+		deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
+		return deviceChannel;
 	}
 
+
+
 	public void setCmder(SIPCommander cmder) {
 	}
 

+ 197 - 201
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java

@@ -1,201 +1,197 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import java.security.NoSuchAlgorithmException;
-import java.text.ParseException;
-import java.util.Calendar;
-import java.util.Locale;
-
-import javax.sip.InvalidArgumentException;
-import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.header.AuthorizationHeader;
-import javax.sip.header.ContactHeader;
-import javax.sip.header.ExpiresHeader;
-import javax.sip.header.FromHeader;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
-import gov.nist.javax.sip.RequestEventExt;
-import gov.nist.javax.sip.header.SIPDateHeader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.util.StringUtils;
-
-import com.genersoft.iot.vmp.common.VideoManagerConstants;
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
-import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-
-import gov.nist.javax.sip.address.AddressImpl;
-import gov.nist.javax.sip.address.SipUri;
-import gov.nist.javax.sip.header.Expires;
-
-/**    
- * @Description:收到注册请求 处理 
- * @author: swwheihei
- * @date:   2020年5月3日 下午4:47:25     
- */
-public class RegisterRequestProcessor extends SIPRequestAbstractProcessor {
-
-	private Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
-
-	private SipConfig sipConfig;
-	
-	private RegisterLogicHandler handler;
-	
-	private IVideoManagerStorager storager;
-	
-	private EventPublisher publisher;
-
-	/**
-	 * 收到注册请求 处理
- 	 * @param evt
-	 */
-	@Override
-	public void process(RequestEvent evt) {
-		try {
-			RequestEventExt evtExt = (RequestEventExt)evt;
-			String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
-			logger.info("[{}] 收到注册请求,开始处理", requestAddress);
-			Request request = evt.getRequest();
-
-			Response response = null; 
-			boolean passwordCorrect = false;
-			// 注册标志  0:未携带授权头或者密码错误  1:注册成功   2:注销成功
-			int registerFlag = 0;
-			FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
-			AddressImpl address = (AddressImpl) fromHeader.getAddress();
-			SipUri uri = (SipUri) address.getURI();
-			String deviceId = uri.getUser();
-			Device device = storager.queryVideoDevice(deviceId);
-			AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); 
-			// 校验密码是否正确
-			if (authorhead != null) {
-				passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,
-						sipConfig.getPassword());
-			}
-			if (StringUtils.isEmpty(sipConfig.getPassword())){
-				passwordCorrect = true;
-			}
-
-			// 未携带授权头或者密码错误 回复401
-			if (authorhead == null ) {
-
-				logger.info("[{}] 未携带授权头 回复401", requestAddress);
-				response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
-				new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
-			}else {
-				if (!passwordCorrect){
-					// 注册失败
-					response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
-					response.setReasonPhrase("wrong password");
-					logger.info("[{}] 密码错误 回复403", requestAddress);
-				}else {
-					// 携带授权头并且密码正确
-					response = getMessageFactory().createResponse(Response.OK, request);
-					// 添加date头
-					SIPDateHeader dateHeader = new SIPDateHeader();
-					// 使用自己修改的
-					WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
-					dateHeader.setDate(wvpSipDate);
-					response.addHeader(dateHeader);
-
-					ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
-					if (expiresHeader == null) {
-						response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
-						ServerTransaction serverTransaction = getServerTransaction(evt);
-						serverTransaction.sendResponse(response);
-						if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-						return;
-					}
-					// 添加Contact头
-					response.addHeader(request.getHeader(ContactHeader.NAME));
-					// 添加Expires头
-					response.addHeader(request.getExpires());
-
-					// 获取到通信地址等信息
-					ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
-					String received = viaHeader.getReceived();
-					int rPort = viaHeader.getRPort();
-					// 解析本地地址替代
-					if (StringUtils.isEmpty(received) || rPort == -1) {
-						received = viaHeader.getHost();
-						rPort = viaHeader.getPort();
-					}
-					//
-
-					if (device == null) {
-						device = new Device();
-						device.setStreamMode("UDP");
-						device.setCharset("gb2312");
-						device.setDeviceId(deviceId);
-						device.setFirsRegister(true);
-					}
-					device.setIp(received);
-					device.setPort(rPort);
-					device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
-					// 注销成功
-					if (expiresHeader.getExpires() == 0) {
-						registerFlag = 2;
-					}
-					// 注册成功
-					else {
-						device.setExpires(expiresHeader.getExpires());
-						registerFlag = 1;
-						// 判断TCP还是UDP
-						boolean isTcp = false;
-						ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
-						String transport = reqViaHeader.getTransport();
-						if (transport.equals("TCP")) {
-							isTcp = true;
-						}
-						device.setTransport(isTcp ? "TCP" : "UDP");
-					}
-				}
-			}
-
-			ServerTransaction serverTransaction = getServerTransaction(evt);
-			serverTransaction.sendResponse(response);
-			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
-			// 注册成功
-			// 保存到redis
-			// 下发catelog查询目录
-			if (registerFlag == 1 ) {
-				logger.info("[{}] 注册成功! deviceId:" + device.getDeviceId(), requestAddress);
-				publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_REGISTER);
-				// 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
-				handler.onRegister(device);
-			} else if (registerFlag == 2) {
-				logger.info("[{}] 注销成功! deviceId:" + device.getDeviceId(), requestAddress);
-				publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);
-			}
-		} catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
-			e.printStackTrace();
-		}
-		
-	}
-	
-	public void setSipConfig(SipConfig sipConfig) {
-		this.sipConfig = sipConfig;
-	}
-
-	public void setHandler(RegisterLogicHandler handler) {
-		this.handler = handler;
-	}
-
-	public void setVideoManagerStorager(IVideoManagerStorager storager) {
-		this.storager = storager;
-	}
-
-	public void setPublisher(EventPublisher publisher) {
-		this.publisher = publisher;
-	}
-
-}
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
+import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import gov.nist.javax.sip.RequestEventExt;
+import gov.nist.javax.sip.address.AddressImpl;
+import gov.nist.javax.sip.address.SipUri;
+import gov.nist.javax.sip.header.Expires;
+import gov.nist.javax.sip.header.SIPDateHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.header.*;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * SIP命令类型: REGISTER请求
+ */
+@Component
+public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
+
+	public String method = "REGISTER";
+
+	@Autowired
+	private SipConfig sipConfig;
+
+	@Autowired
+	private RegisterLogicHandler handler;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private EventPublisher publisher;
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
+
+	/**
+	 * 收到注册请求 处理
+ 	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		try {
+			RequestEventExt evtExt = (RequestEventExt)evt;
+			String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
+			logger.info("[{}] 收到注册请求,开始处理", requestAddress);
+			Request request = evt.getRequest();
+
+			Response response = null; 
+			boolean passwordCorrect = false;
+			// 注册标志  0:未携带授权头或者密码错误  1:注册成功   2:注销成功
+			int registerFlag = 0;
+			FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
+			AddressImpl address = (AddressImpl) fromHeader.getAddress();
+			SipUri uri = (SipUri) address.getURI();
+			String deviceId = uri.getUser();
+			Device device = storager.queryVideoDevice(deviceId);
+			AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); 
+			// 校验密码是否正确
+			if (authorhead != null) {
+				passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,
+						sipConfig.getPassword());
+			}
+			if (StringUtils.isEmpty(sipConfig.getPassword())){
+				passwordCorrect = true;
+			}
+
+			// 未携带授权头或者密码错误 回复401
+			if (authorhead == null ) {
+
+				logger.info("[{}] 未携带授权头 回复401", requestAddress);
+				response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
+				new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
+			}else {
+				if (!passwordCorrect){
+					// 注册失败
+					response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
+					response.setReasonPhrase("wrong password");
+					logger.info("[{}] 密码/SIP服务器ID错误, 回复403", requestAddress);
+				}else {
+					// 携带授权头并且密码正确
+					response = getMessageFactory().createResponse(Response.OK, request);
+					// 添加date头
+					SIPDateHeader dateHeader = new SIPDateHeader();
+					// 使用自己修改的
+					WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
+					dateHeader.setDate(wvpSipDate);
+					response.addHeader(dateHeader);
+
+					ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
+					if (expiresHeader == null) {
+						response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
+						ServerTransaction serverTransaction = getServerTransaction(evt);
+						serverTransaction.sendResponse(response);
+						if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+						return;
+					}
+					// 添加Contact头
+					response.addHeader(request.getHeader(ContactHeader.NAME));
+					// 添加Expires头
+					response.addHeader(request.getExpires());
+
+					// 获取到通信地址等信息
+					ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+					String received = viaHeader.getReceived();
+					int rPort = viaHeader.getRPort();
+					// 解析本地地址替代
+					if (StringUtils.isEmpty(received) || rPort == -1) {
+						received = viaHeader.getHost();
+						rPort = viaHeader.getPort();
+					}
+					//
+
+					if (device == null) {
+						device = new Device();
+						device.setStreamMode("UDP");
+						device.setCharset("gb2312");
+						device.setDeviceId(deviceId);
+						device.setFirsRegister(true);
+					}
+					device.setIp(received);
+					device.setPort(rPort);
+					device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
+					// 注销成功
+					if (expiresHeader.getExpires() == 0) {
+						registerFlag = 2;
+					}
+					// 注册成功
+					else {
+						device.setExpires(expiresHeader.getExpires());
+						registerFlag = 1;
+						// 判断TCP还是UDP
+						boolean isTcp = false;
+						ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+						String transport = reqViaHeader.getTransport();
+						if (transport.equals("TCP")) {
+							isTcp = true;
+						}
+						device.setTransport(isTcp ? "TCP" : "UDP");
+					}
+				}
+			}
+
+			ServerTransaction serverTransaction = getServerTransaction(evt);
+			serverTransaction.sendResponse(response);
+			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+			// 注册成功
+			// 保存到redis
+			// 下发catelog查询目录
+			if (registerFlag == 1 ) {
+				logger.info("[{}] 注册成功! deviceId:" + device.getDeviceId(), requestAddress);
+				publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_REGISTER);
+				// 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
+				handler.onRegister(device);
+			} else if (registerFlag == 2) {
+				logger.info("[{}] 注销成功! deviceId:" + device.getDeviceId(), requestAddress);
+				publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);
+			}
+		} catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
+			e.printStackTrace();
+		}
+		
+	}
+
+}

+ 75 - 62
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java

@@ -1,62 +1,75 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import java.text.ParseException;
-
-import javax.sip.InvalidArgumentException;
-import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.header.ExpiresHeader;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**    
- * @Description:SUBSCRIBE请求处理器
- * @author: swwheihei
- * @date:   2020年5月3日 下午5:31:20     
- */
-public class SubscribeRequestProcessor extends SIPRequestAbstractProcessor {
-
-	private Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class);
-
-	/**   
-	 * 处理SUBSCRIBE请求  
-	 * 
-	 * @param evt
-	 */
-	@Override
-	public void process(RequestEvent evt) {
-		Request request = evt.getRequest();
-
-		try {
-			Response response = null;
-			response = getMessageFactory().createResponse(200, request);
-			if (response != null) {
-				ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30);
-				response.setExpires(expireHeader);
-			}
-			logger.info("response : " + response.toString());
-			ServerTransaction transaction = getServerTransaction(evt);
-			if (transaction != null) {
-				transaction.sendResponse(response);
-				transaction.getDialog().delete();
-				transaction.terminate();
-			} else {
-				logger.info("processRequest serverTransactionId is null.");
-			}
-
-		} catch (ParseException e) {
-			e.printStackTrace();
-		} catch (SipException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			e.printStackTrace();
-		}
-		
-	}
-
-}
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.header.ExpiresHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+/**
+ * SIP命令类型: SUBSCRIBE请求
+ */
+@Component
+public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class);
+	private String method = "SUBSCRIBE";
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addRequestProcessor(method, this);
+	}
+
+	/**   
+	 * 处理SUBSCRIBE请求  
+	 * 
+	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		Request request = evt.getRequest();
+
+		try {
+			Response response = null;
+			response = getMessageFactory().createResponse(200, request);
+			if (response != null) {
+				ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30);
+				response.setExpires(expireHeader);
+			}
+			logger.info("response : " + response.toString());
+			ServerTransaction transaction = getServerTransaction(evt);
+			if (transaction != null) {
+				transaction.sendResponse(response);
+				transaction.getDialog().delete();
+				transaction.terminate();
+			} else {
+				logger.info("processRequest serverTransactionId is null.");
+			}
+
+		} catch (ParseException e) {
+			e.printStackTrace();
+		} catch (SipException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			e.printStackTrace();
+		}
+		
+	}
+
+}

+ 23 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java

@@ -0,0 +1,23 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import org.dom4j.Element;
+
+import javax.sip.RequestEvent;
+
+public interface IMessageHandler {
+    /**
+     * 处理来自设备的信息
+     * @param evt
+     * @param device
+     */
+    void handForDevice(RequestEvent evt, Device device, Element element);
+
+    /**
+     * 处理来自平台的信息
+     * @param evt
+     * @param parentPlatform
+     */
+    void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element);
+}

+ 40 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java

@@ -0,0 +1,40 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import org.dom4j.Element;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.sip.RequestEvent;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{
+
+    public Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
+
+    public void addHandler(String cmdType, IMessageHandler messageHandler) {
+        messageHandlerMap.put(cmdType, messageHandler);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        String cmd = getText(element, "CmdType");
+        IMessageHandler messageHandler = messageHandlerMap.get(cmd);
+        if (messageHandler != null) {
+            messageHandler.handForDevice(evt, device, element);
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+        String cmd = getText(element, "CmdType");
+        IMessageHandler messageHandler = messageHandlerMap.get(cmd);
+        if (messageHandler != null) {
+            messageHandler.handForPlatform(evt, parentPlatform, element);
+        }
+    }
+}

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

@@ -0,0 +1,91 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class MessageRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+    private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
+
+    private final String method = "MESSAGE";
+
+    private static Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
+
+    @Autowired
+    private SIPProcessorObserver sipProcessorObserver;
+
+    @Autowired
+    private IVideoManagerStorager storage;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        // 添加消息处理的订阅
+        sipProcessorObserver.addRequestProcessor(method, this);
+    }
+
+    public void addHandler(String name, IMessageHandler handler) {
+        messageHandlerMap.put(name, handler);
+    }
+
+    @Override
+    public void process(RequestEvent evt) {
+        logger.debug("接收到消息:" + evt.getRequest());
+        String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
+        // 查询设备是否存在
+        Device device = storage.queryVideoDevice(deviceId);
+        // 查询上级平台是否存在
+        ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
+        try {
+            if (device == null && parentPlatform == null) {
+                // 不存在则回复404
+                responseAck(evt, Response.NOT_FOUND, "device id not found");
+            }else {
+                Element rootElement = getRootElement(evt);
+                String name = rootElement.getName();
+                IMessageHandler messageHandler = messageHandlerMap.get(name);
+                if (messageHandler != null) {
+                    if (device != null) {
+                        messageHandler.handForDevice(evt, device, rootElement);
+                    }else { // 由于上面已经判断都为null则直接返回,所以这里device和parentPlatform必有一个不为null
+                        messageHandler.handForPlatform(evt, parentPlatform, rootElement);
+                    }
+                }else {
+                    // 不支持的message
+                    // 不存在则回复415
+                    responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
+                }
+            }
+        } catch (SipException e) {
+            logger.warn("SIP 回复错误", e);
+        } catch (InvalidArgumentException e) {
+            logger.warn("参数无效", e);
+        } catch (ParseException e) {
+            logger.warn("SIP回复时解析异常", e);
+        } catch (DocumentException e) {
+            logger.warn("解析XML消息内容异常", e);
+        }
+    }
+
+
+}

+ 27 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/ControlMessageHandler.java

@@ -0,0 +1,27 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control;
+
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 命令类型: 控制命令
+ * 命令类型: 设备控制: 远程启动, 录像控制(TODO), 报警布防/撤防命令(TODO), 报警复位命令(TODO),
+ *                   强制关键帧命令(TODO), 拉框放大/缩小控制命令(TODO), 看守位控制(TODO), 报警复位(TODO)
+ * 命令类型: 设备配置: SVAC编码配置(TODO), 音频参数(TODO), SVAC解码配置(TODO)
+ */
+@Component
+public class ControlMessageHandler extends MessageHandlerAbstract implements InitializingBean  {
+
+    private final String messageType = "Control";
+
+    @Autowired
+    private MessageRequestProcessor messageRequestProcessor;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        messageRequestProcessor.addHandler(messageType, this);
+    }
+}

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

@@ -0,0 +1,112 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd;
+
+import com.genersoft.iot.vmp.VManageBootstrap;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.ControlMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.SpringBeanFactory;
+import gov.nist.javax.sip.SipStackImpl;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.sip.ListeningPoint;
+import javax.sip.ObjectInUseException;
+import javax.sip.RequestEvent;
+import javax.sip.SipProvider;
+import javax.sip.address.SipURI;
+import javax.sip.header.HeaderAddress;
+import javax.sip.header.ToHeader;
+import java.util.Iterator;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceControlQueryMessageHandler.class);
+    private final String cmdType = "DeviceControl";
+
+    @Autowired
+    private ControlMessageHandler controlMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommander cmder;
+
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        controlMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+        // 此处是上级发出的DeviceControl指令
+        String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
+        String channelId = getText(rootElement, "DeviceID");
+        // 远程启动功能
+        if (!StringUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
+            if (parentPlatform.getServerGBId().equals(targetGBId)) {
+                // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
+                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();
+                        }
+                    }
+                });
+
+                restartThread.setDaemon(false);
+                restartThread.start();
+            } else {
+                // 远程启动指定设备
+            }
+        }
+        // 云台/前端控制命令
+        if (!StringUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) {
+            String cmdString = getText(rootElement,"PTZCmd");
+            Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
+            cmder.fronEndCmd(deviceForPlatform, channelId, cmdString);
+        }
+    }
+}

+ 25 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java

@@ -0,0 +1,25 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify;
+
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 命令类型: 通知命令
+ * 命令类型: 状态信息(心跳)报送, 报警通知, 媒体通知, 移动设备位置数据,语音广播通知(TODO), 设备预置位(TODO)
+ */
+@Component
+public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean  {
+
+    private final String messageType = "Notify";
+
+    @Autowired
+    private MessageRequestProcessor messageRequestProcessor;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        messageRequestProcessor.addHandler(messageType, this);
+    }
+}

+ 114 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java

@@ -0,0 +1,114 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.service.IDeviceAlarmService;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.GpsUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.sip.RequestEvent;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class);
+    private final String cmdType = "Alarm";
+
+    @Autowired
+    private NotifyMessageHandler notifyMessageHandler;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IDeviceAlarmService deviceAlarmService;
+
+    @Autowired
+    private DeviceOffLineDetector offLineDetector;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        notifyMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+        Element deviceIdElement = rootElement.element("DeviceID");
+        String channelId = deviceIdElement.getText().toString();
+        DeviceAlarm deviceAlarm = new DeviceAlarm();
+        deviceAlarm.setDeviceId(device.getDeviceId());
+        deviceAlarm.setChannelId(channelId);
+        deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
+        deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
+        deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
+        if (getText(rootElement, "AlarmDescription") == null) {
+            deviceAlarm.setAlarmDescription("");
+        } else {
+            deviceAlarm.setAlarmDescription(getText(rootElement, "AlarmDescription"));
+        }
+        if (NumericUtil.isDouble(getText(rootElement, "Longitude"))) {
+            deviceAlarm.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
+        } else {
+            deviceAlarm.setLongitude(0.00);
+        }
+        if (NumericUtil.isDouble(getText(rootElement, "Latitude"))) {
+            deviceAlarm.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
+        } else {
+            deviceAlarm.setLatitude(0.00);
+        }
+
+        if (!StringUtils.isEmpty(deviceAlarm.getAlarmMethod())) {
+            if ( deviceAlarm.getAlarmMethod().equals("4")) {
+                MobilePosition mobilePosition = new MobilePosition();
+                mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
+                mobilePosition.setTime(deviceAlarm.getAlarmTime());
+                mobilePosition.setLongitude(deviceAlarm.getLongitude());
+                mobilePosition.setLatitude(deviceAlarm.getLatitude());
+                mobilePosition.setReportSource("GPS Alarm");
+                BaiduPoint bp = new BaiduPoint();
+                bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+                logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
+                mobilePosition.setGeodeticSystem("BD-09");
+                mobilePosition.setCnLng(bp.getBdLng());
+                mobilePosition.setCnLat(bp.getBdLat());
+                if (!userSetup.getSavePositionHistory()) {
+                    storager.clearMobilePositionsByDeviceId(device.getDeviceId());
+                }
+                storager.insertMobilePosition(mobilePosition);
+            }
+        }
+        logger.debug("存储报警信息、报警分类");
+        // 存储报警信息、报警分类
+        deviceAlarmService.add(deviceAlarm);
+
+        if (offLineDetector.isOnline(device.getDeviceId())) {
+            publisher.deviceAlarmEventPublish(deviceAlarm);
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

+ 117 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java

@@ -0,0 +1,117 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.List;
+
+@Component
+public class CatalogNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(CatalogNotifyMessageHandler.class);
+    private final String cmdType = "Catalog";
+
+    @Autowired
+    private NotifyMessageHandler notifyMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
+
+    @Autowired
+    private SipConfig config;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        notifyMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+        String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId();
+        FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
+        try {
+            // 回复200 OK
+            responseAck(evt, Response.OK);
+            Element snElement = rootElement.element("SN");
+            String sn = snElement.getText();
+            // 准备回复通道信息
+            List<ChannelReduce> channelReduces = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId());
+            // 查询关联的直播通道
+            List<GbStream> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
+            int size = channelReduces.size() + gbStreams.size();
+            // 回复级联的通道
+            if (channelReduces.size() > 0) {
+                for (ChannelReduce channelReduce : channelReduces) {
+                    DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
+                    cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
+                }
+            }
+            // 回复直播的通道
+            if (gbStreams.size() > 0) {
+                for (GbStream gbStream : gbStreams) {
+                    DeviceChannel deviceChannel = new DeviceChannel();
+                    deviceChannel.setChannelId(gbStream.getGbId());
+                    deviceChannel.setName(gbStream.getName());
+                    deviceChannel.setLongitude(gbStream.getLongitude());
+                    deviceChannel.setLatitude(gbStream.getLatitude());
+                    deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
+                    deviceChannel.setManufacture("wvp-pro");
+                    deviceChannel.setStatus(gbStream.isStatus()?1:0);
+    //							deviceChannel.setParentId(parentPlatform.getDeviceGBId());
+                    deviceChannel.setRegisterWay(1);
+                    deviceChannel.setCivilCode(config.getDomain());
+                    deviceChannel.setModel("live");
+                    deviceChannel.setOwner("wvp-pro");
+                    deviceChannel.setParental(0);
+                    deviceChannel.setSecrecy("0");
+                    deviceChannel.setSecrecy("0");
+
+                    cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
+                }
+            }
+            if (size == 0) {
+                // 回复无通道
+                cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), size);
+            }
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+    }
+}

+ 63 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java

@@ -0,0 +1,63 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+@Component
+public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class);
+    private final String cmdType = "Keepalive";
+
+    @Autowired
+    private NotifyMessageHandler notifyMessageHandler;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        notifyMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        // 检查设备是否存在并在线, 不在线则设置为在线
+        try {
+            if (device != null ) {
+                // 回复200 OK
+                responseAck(evt, Response.OK);
+                publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
+            }
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+        // 不会收到上级平台的心跳信息
+
+    }
+}

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

@@ -0,0 +1,74 @@
+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.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(MediaStatusNotifyMessageHandler.class);
+    private final String cmdType = "MediaStatus";
+
+    @Autowired
+    private NotifyMessageHandler notifyMessageHandler;
+
+    @Autowired
+    private SIPCommander cmder;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        notifyMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+
+        // 回复200 OK
+        try {
+            responseAck(evt, Response.OK);
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        String NotifyType =getText(rootElement, "NotifyType");
+        if (NotifyType.equals("121")){
+            logger.info("媒体播放完毕,通知关流");
+            StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(device.getDeviceId(), "*");
+            if (streamInfo != null) {
+                redisCatchStorage.stopPlayback(streamInfo);
+                cmder.streamByeCmd(streamInfo.getDeviceID(), streamInfo.getChannelId());
+            }
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

+ 103 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java

@@ -0,0 +1,103 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.GpsUtil;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(MobilePositionNotifyMessageHandler.class);
+    private final String cmdType = "MobilePosition";
+
+    @Autowired
+    private NotifyMessageHandler notifyMessageHandler;
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        notifyMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+
+        try {
+            rootElement = getRootElement(evt, device.getCharset());
+
+            MobilePosition mobilePosition = new MobilePosition();
+            if (!StringUtils.isEmpty(device.getName())) {
+                mobilePosition.setDeviceName(device.getName());
+            }
+            mobilePosition.setDeviceId(device.getDeviceId());
+            mobilePosition.setChannelId(getText(rootElement, "DeviceID"));
+            mobilePosition.setTime(getText(rootElement, "Time"));
+            mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
+            mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
+            if (NumericUtil.isDouble(getText(rootElement, "Speed"))) {
+                mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed")));
+            } else {
+                mobilePosition.setSpeed(0.0);
+            }
+            if (NumericUtil.isDouble(getText(rootElement, "Direction"))) {
+                mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction")));
+            } else {
+                mobilePosition.setDirection(0.0);
+            }
+            if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) {
+                mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude")));
+            } else {
+                mobilePosition.setAltitude(0.0);
+            }
+            mobilePosition.setReportSource("Mobile Position");
+            BaiduPoint bp = new BaiduPoint();
+            bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+            logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
+            mobilePosition.setGeodeticSystem("BD-09");
+            mobilePosition.setCnLng(bp.getBdLng());
+            mobilePosition.setCnLat(bp.getBdLat());
+            if (!userSetup.getSavePositionHistory()) {
+                storager.clearMobilePositionsByDeviceId(device.getDeviceId());
+            }
+            storager.insertMobilePosition(mobilePosition);
+            //回复 200 OK
+            responseAck(evt, Response.OK);
+        } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

+ 25 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/QueryMessageHandler.java

@@ -0,0 +1,25 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query;
+
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 命令类型: 查询指令
+ * 命令类型: 设备状态, 设备目录信息, 设备信息, 文件目录检索(TODO), 报警(TODO), 设备配置(TODO), 设备预置位(TODO), 移动设备位置数据(TODO)
+ */
+@Component
+public class QueryMessageHandler extends MessageHandlerAbstract implements InitializingBean  {
+
+    private final String messageType = "Query";
+
+    @Autowired
+    private MessageRequestProcessor messageRequestProcessor;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        messageRequestProcessor.addHandler(messageType, this);
+    }
+}

+ 77 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java

@@ -0,0 +1,77 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.List;
+
+@Component
+public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(AlarmQueryMessageHandler.class);
+    private final String cmdType = "Alarm";
+
+    @Autowired
+    private QueryMessageHandler queryMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
+
+    @Autowired
+    private SipConfig config;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        queryMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+        logger.info("不支持alarm查询");
+        try {
+            responseAck(evt, Response.NOT_FOUND, "not support alarm query");
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+    }
+}

+ 120 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java

@@ -0,0 +1,120 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.List;
+
+@Component
+public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(CatalogQueryMessageHandler.class);
+    private final String cmdType = "Catalog";
+
+    @Autowired
+    private QueryMessageHandler queryMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
+
+    @Autowired
+    private SipConfig config;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        queryMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+        String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId();
+        FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
+        try {
+            // 回复200 OK
+            responseAck(evt, Response.OK);
+            Element snElement = rootElement.element("SN");
+            String sn = snElement.getText();
+            // 准备回复通道信息
+            List<ChannelReduce> channelReduces = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId());
+            // 查询关联的直播通道
+            List<GbStream> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
+            int size = channelReduces.size() + gbStreams.size();
+            // 回复级联的通道
+            if (channelReduces.size() > 0) {
+                for (ChannelReduce channelReduce : channelReduces) {
+                    DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
+                    cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
+                }
+            }
+            // 回复直播的通道
+            if (gbStreams.size() > 0) {
+                for (GbStream gbStream : gbStreams) {
+                    DeviceChannel deviceChannel = new DeviceChannel();
+                    deviceChannel.setChannelId(gbStream.getGbId());
+                    deviceChannel.setName(gbStream.getName());
+                    deviceChannel.setLongitude(gbStream.getLongitude());
+                    deviceChannel.setLatitude(gbStream.getLatitude());
+                    deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
+                    deviceChannel.setManufacture("wvp-pro");
+                    deviceChannel.setStatus(gbStream.isStatus()?1:0);
+    //							deviceChannel.setParentId(parentPlatform.getDeviceGBId());
+                    deviceChannel.setRegisterWay(1);
+                    deviceChannel.setCivilCode(config.getDomain());
+                    deviceChannel.setModel("live");
+                    deviceChannel.setOwner("wvp-pro");
+                    deviceChannel.setParental(0);
+                    deviceChannel.setSecrecy("0");
+                    deviceChannel.setSecrecy("0");
+
+                    cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
+                }
+            }
+            if (size == 0) {
+                // 回复无通道
+                cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), size);
+            }
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+    }
+}

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

@@ -0,0 +1,62 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+@Component
+public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceInfoQueryMessageHandler.class);
+    private final String cmdType = "DeviceInfo";
+
+    @Autowired
+    private QueryMessageHandler queryMessageHandler;
+
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        queryMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+        logger.info("接收到DeviceInfo查询消息");
+        FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
+        try {
+            // 回复200 OK
+            responseAck(evt, Response.OK);
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        String sn = rootElement.element("SN").getText();
+        cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
+    }
+}

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

@@ -0,0 +1,75 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+@Component
+public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceStatusQueryMessageHandler.class);
+    private final String cmdType = "DeviceStatus";
+
+    @Autowired
+    private QueryMessageHandler queryMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
+
+    @Autowired
+    private SipConfig config;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        queryMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+        logger.info("接收到DeviceStatus查询消息");
+        FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
+        // 回复200 OK
+        try {
+            responseAck(evt, Response.OK);
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        String sn = rootElement.element("SN").getText();
+        cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag());
+    }
+}

+ 25 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/ResponseMessageHandler.java

@@ -0,0 +1,25 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response;
+
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 命令类型: 请求动作的应答
+ * 命令类型: 设备控制, 报警通知, 设备目录信息查询, 目录信息查询, 目录收到, 设备信息查询, 设备状态信息查询 ......
+ */
+@Component
+public class ResponseMessageHandler extends MessageHandlerAbstract implements InitializingBean  {
+
+    private final String messageType = "Response";
+
+    @Autowired
+    private MessageRequestProcessor messageRequestProcessor;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        messageRequestProcessor.addHandler(messageType, this);
+    }
+}

+ 58 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java

@@ -0,0 +1,58 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.RequestEvent;
+
+@Component
+public class AlarmResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(AlarmResponseMessageHandler.class);
+    private final String cmdType = "Alarm";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+        Element deviceIdElement = rootElement.element("DeviceID");
+        String channelId = deviceIdElement.getText().toString();
+        String key = DeferredResultHolder.CALLBACK_CMD_ALARM + device.getDeviceId() + channelId;
+        JSONObject json = new JSONObject();
+        XmlUtil.node2Json(rootElement, json);
+        if (logger.isDebugEnabled()) {
+            logger.debug(json.toJSONString());
+        }
+        RequestMessage msg = new RequestMessage();
+        msg.setKey(key);
+        msg.setData(json);
+        deferredResultHolder.invokeAllResult(msg);
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

+ 72 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java

@@ -0,0 +1,72 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(BroadcastResponseMessageHandler.class);
+    private final String cmdType = "Broadcast";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+        try {
+            String channelId = getText(rootElement, "DeviceID");
+            String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId;
+            // 回复200 OK
+            responseAck(evt, Response.OK);
+            // 此处是对本平台发出Broadcast指令的应答
+            JSONObject json = new JSONObject();
+            XmlUtil.node2Json(rootElement, json);
+            if (logger.isDebugEnabled()) {
+                logger.debug(json.toJSONString());
+            }
+            RequestMessage msg = new RequestMessage();
+            msg.setKey(key);
+            msg.setData(json);
+            deferredResultHolder.invokeAllResult(msg);
+
+
+        } catch (ParseException | SipException | InvalidArgumentException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

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

@@ -0,0 +1,182 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.Iterator;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class);
+    private final String cmdType = "Catalog";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Autowired
+    private DeviceOffLineDetector offLineDetector;
+
+    @Autowired
+    private SipConfig config;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + device.getDeviceId();
+        Element rootElement = null;
+        try {
+            rootElement = getRootElement(evt, device.getCharset());
+            Element deviceListElement = rootElement.element("DeviceList");
+            Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
+            if (deviceListIterator != null) {
+
+                // 遍历DeviceList
+                while (deviceListIterator.hasNext()) {
+                    Element itemDevice = deviceListIterator.next();
+                    Element channelDeviceElement = itemDevice.element("DeviceID");
+                    if (channelDeviceElement == null) {
+                        continue;
+                    }
+                    String channelDeviceId = channelDeviceElement.getText();
+                    Element channdelNameElement = itemDevice.element("Name");
+                    String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
+                    Element statusElement = itemDevice.element("Status");
+                    String status = statusElement != null ? statusElement.getText().toString() : "ON";
+                    DeviceChannel deviceChannel = new DeviceChannel();
+                    deviceChannel.setName(channelName);
+                    deviceChannel.setChannelId(channelDeviceId);
+                    // ONLINE OFFLINE  HIKVISION DS-7716N-E4 NVR的兼容性处理
+                    if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
+                        deviceChannel.setStatus(1);
+                    }
+                    if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
+                        deviceChannel.setStatus(0);
+                    }
+
+                    deviceChannel.setManufacture(getText(itemDevice, "Manufacturer"));
+                    deviceChannel.setModel(getText(itemDevice, "Model"));
+                    deviceChannel.setOwner(getText(itemDevice, "Owner"));
+                    deviceChannel.setCivilCode(getText(itemDevice, "CivilCode"));
+                    deviceChannel.setBlock(getText(itemDevice, "Block"));
+                    deviceChannel.setAddress(getText(itemDevice, "Address"));
+                    if (getText(itemDevice, "Parental") == null || getText(itemDevice, "Parental") == "") {
+                        deviceChannel.setParental(0);
+                    } else {
+                        deviceChannel.setParental(Integer.parseInt(getText(itemDevice, "Parental")));
+                    }
+                    deviceChannel.setParentId(getText(itemDevice, "ParentID"));
+                    if (getText(itemDevice, "SafetyWay") == null || getText(itemDevice, "SafetyWay") == "") {
+                        deviceChannel.setSafetyWay(0);
+                    } else {
+                        deviceChannel.setSafetyWay(Integer.parseInt(getText(itemDevice, "SafetyWay")));
+                    }
+                    if (getText(itemDevice, "RegisterWay") == null || getText(itemDevice, "RegisterWay") == "") {
+                        deviceChannel.setRegisterWay(1);
+                    } else {
+                        deviceChannel.setRegisterWay(Integer.parseInt(getText(itemDevice, "RegisterWay")));
+                    }
+                    deviceChannel.setCertNum(getText(itemDevice, "CertNum"));
+                    if (getText(itemDevice, "Certifiable") == null || getText(itemDevice, "Certifiable") == "") {
+                        deviceChannel.setCertifiable(0);
+                    } else {
+                        deviceChannel.setCertifiable(Integer.parseInt(getText(itemDevice, "Certifiable")));
+                    }
+                    if (getText(itemDevice, "ErrCode") == null || getText(itemDevice, "ErrCode") == "") {
+                        deviceChannel.setErrCode(0);
+                    } else {
+                        deviceChannel.setErrCode(Integer.parseInt(getText(itemDevice, "ErrCode")));
+                    }
+                    deviceChannel.setEndTime(getText(itemDevice, "EndTime"));
+                    deviceChannel.setSecrecy(getText(itemDevice, "Secrecy"));
+                    deviceChannel.setIpAddress(getText(itemDevice, "IPAddress"));
+                    if (getText(itemDevice, "Port") == null || getText(itemDevice, "Port") == "") {
+                        deviceChannel.setPort(0);
+                    } else {
+                        deviceChannel.setPort(Integer.parseInt(getText(itemDevice, "Port")));
+                    }
+                    deviceChannel.setPassword(getText(itemDevice, "Password"));
+                    if (NumericUtil.isDouble(getText(itemDevice, "Longitude"))) {
+                        deviceChannel.setLongitude(Double.parseDouble(getText(itemDevice, "Longitude")));
+                    } else {
+                        deviceChannel.setLongitude(0.00);
+                    }
+                    if (NumericUtil.isDouble(getText(itemDevice, "Latitude"))) {
+                        deviceChannel.setLatitude(Double.parseDouble(getText(itemDevice, "Latitude")));
+                    } else {
+                        deviceChannel.setLatitude(0.00);
+                    }
+                    if (getText(itemDevice, "PTZType") == null || getText(itemDevice, "PTZType") == "") {
+                        deviceChannel.setPTZType(0);
+                    } else {
+                        deviceChannel.setPTZType(Integer.parseInt(getText(itemDevice, "PTZType")));
+                    }
+                    deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
+                    storager.updateChannel(device.getDeviceId(), deviceChannel);
+                }
+
+                RequestMessage msg = new RequestMessage();
+                msg.setKey(key);
+                msg.setData(device);
+                deferredResultHolder.invokeAllResult(msg);
+                // 回复200 OK
+                responseAck(evt, Response.OK);
+                if (offLineDetector.isOnline(device.getDeviceId())) {
+                    publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
+                }
+            }
+        } catch (DocumentException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        } catch (SipException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+    }
+}

+ 81 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java

@@ -0,0 +1,81 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(ConfigDownloadResponseMessageHandler.class);
+    private final String cmdType = "ConfigDownload";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        String channelId = getText(element, "DeviceID");
+        String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId;
+        try {
+            // 回复200 OK
+            responseAck(evt, Response.OK);
+            // 此处是对本平台发出DeviceControl指令的应答
+            JSONObject json = new JSONObject();
+            XmlUtil.node2Json(element, json);
+            if (logger.isDebugEnabled()) {
+                logger.debug(json.toJSONString());
+            }
+            RequestMessage msg = new RequestMessage();
+            msg.setKey(key);
+            msg.setData(json);
+            deferredResultHolder.invokeAllResult(msg);
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+        // 不会收到上级平台的心跳信息
+
+    }
+}

+ 58 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java

@@ -0,0 +1,58 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.RequestEvent;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class DeviceConfigResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceConfigResponseMessageHandler.class);
+    private final String cmdType = "DeviceConfig";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        JSONObject json = new JSONObject();
+        XmlUtil.node2Json(element, json);
+        String channelId = getText(element, "DeviceID");
+        if (logger.isDebugEnabled()) {
+            logger.debug(json.toJSONString());
+        }
+        String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + device.getDeviceId() + channelId;
+        RequestMessage msg = new RequestMessage();
+        msg.setKey(key);
+        msg.setData(json);
+        deferredResultHolder.invokeAllResult(msg);
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+    }
+}

+ 59 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java

@@ -0,0 +1,59 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.RequestEvent;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class DeviceControlResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceControlResponseMessageHandler.class);
+    private final String cmdType = "DeviceControl";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        // 此处是对本平台发出DeviceControl指令的应答
+        JSONObject json = new JSONObject();
+        String channelId = getText(element, "DeviceID");
+        XmlUtil.node2Json(element, json);
+        if (logger.isDebugEnabled()) {
+            logger.debug(json.toJSONString());
+        }
+        RequestMessage msg = new RequestMessage();
+        String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL +  device.getDeviceId() + channelId;
+        msg.setKey(key);
+        msg.setData(json);
+        deferredResultHolder.invokeAllResult(msg);
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+    }
+}

+ 103 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java

@@ -0,0 +1,103 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceInfoResponseMessageHandler.class);
+    private final String cmdType = "DeviceInfo";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Autowired
+    private DeviceOffLineDetector offLineDetector;
+
+    @Autowired
+    private SipConfig config;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+        logger.debug("接收到DeviceInfo应答消息");
+        try {
+            rootElement = getRootElement(evt, device.getCharset());
+            Element deviceIdElement = rootElement.element("DeviceID");
+            String channelId = deviceIdElement.getTextTrim();
+            String key = DeferredResultHolder.CALLBACK_CMD_DEVICEINFO + device.getDeviceId() + channelId;
+            device.setName(getText(rootElement, "DeviceName"));
+
+            device.setManufacturer(getText(rootElement, "Manufacturer"));
+            device.setModel(getText(rootElement, "Model"));
+            device.setFirmware(getText(rootElement, "Firmware"));
+            if (StringUtils.isEmpty(device.getStreamMode())) {
+                device.setStreamMode("UDP");
+            }
+            storager.updateDevice(device);
+
+            RequestMessage msg = new RequestMessage();
+            msg.setKey(key);
+            msg.setData(device);
+            deferredResultHolder.invokeAllResult(msg);
+            // 回复200 OK
+            responseAck(evt, Response.OK);
+            if (offLineDetector.isOnline(device.getDeviceId())) {
+                publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
+            }
+        } catch (DocumentException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        } catch (SipException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+    }
+}

+ 89 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java

@@ -0,0 +1,89 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+@Component
+public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(DeviceStatusResponseMessageHandler.class);
+    private final String cmdType = "DeviceStatus";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private DeviceOffLineDetector offLineDetector;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+        logger.info("接收到DeviceStatus应答消息");
+        // 检查设备是否存在, 不存在则不回复
+        // 回复200 OK
+        try {
+            responseAck(evt, Response.OK);
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        Element deviceIdElement = element.element("DeviceID");
+        String channelId = deviceIdElement.getText();
+        JSONObject json = new JSONObject();
+        XmlUtil.node2Json(element, json);
+        if (logger.isDebugEnabled()) {
+            logger.debug(json.toJSONString());
+        }
+        RequestMessage msg = new RequestMessage();
+        msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId);
+        msg.setData(json);
+        deferredResultHolder.invokeAllResult(msg);
+
+        if (offLineDetector.isOnline(device.getDeviceId())) {
+            publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
+        } else {
+
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
+
+
+    }
+}

+ 103 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java

@@ -0,0 +1,103 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.GpsUtil;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class MobilePositionResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(MobilePositionResponseMessageHandler.class);
+    private final String cmdType = "MobilePosition";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+
+        try {
+            rootElement = getRootElement(evt, device.getCharset());
+
+            MobilePosition mobilePosition = new MobilePosition();
+            if (!StringUtils.isEmpty(device.getName())) {
+                mobilePosition.setDeviceName(device.getName());
+            }
+            mobilePosition.setDeviceId(device.getDeviceId());
+            mobilePosition.setChannelId(getText(rootElement, "DeviceID"));
+            mobilePosition.setTime(getText(rootElement, "Time"));
+            mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
+            mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
+            if (NumericUtil.isDouble(getText(rootElement, "Speed"))) {
+                mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed")));
+            } else {
+                mobilePosition.setSpeed(0.0);
+            }
+            if (NumericUtil.isDouble(getText(rootElement, "Direction"))) {
+                mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction")));
+            } else {
+                mobilePosition.setDirection(0.0);
+            }
+            if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) {
+                mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude")));
+            } else {
+                mobilePosition.setAltitude(0.0);
+            }
+            mobilePosition.setReportSource("Mobile Position");
+            BaiduPoint bp = new BaiduPoint();
+            bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+            logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
+            mobilePosition.setGeodeticSystem("BD-09");
+            mobilePosition.setCnLng(bp.getBdLng());
+            mobilePosition.setCnLat(bp.getBdLat());
+            if (!userSetup.getSavePositionHistory()) {
+                storager.clearMobilePositionsByDeviceId(device.getDeviceId());
+            }
+            storager.insertMobilePosition(mobilePosition);
+            //回复 200 OK
+            responseAck(evt, Response.OK);
+        } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

+ 151 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java

@@ -0,0 +1,151 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
+@Component
+public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class);
+    public static volatile List<String> threadNameList = new ArrayList();
+    private final String cmdType = "RecordInfo";
+    private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
+
+    @Autowired
+    private ResponseMessageHandler responseMessageHandler;
+
+    @Autowired
+    private RedisUtil redis;
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        responseMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
+
+        // 回复200 OK
+        try {
+            responseAck(evt, Response.OK);
+
+            rootElement = getRootElement(evt, device.getCharset());
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            RecordInfo recordInfo = new RecordInfo();
+            String sn = getText(rootElement, "SN");
+            String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn;
+            recordInfo.setDeviceId(device.getDeviceId());
+            recordInfo.setSn(sn);
+            recordInfo.setName(getText(rootElement, "Name"));
+            if (getText(rootElement, "SumNum") == null || getText(rootElement, "SumNum") == "") {
+                recordInfo.setSumNum(0);
+            } else {
+                recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum")));
+            }
+            Element recordListElement = rootElement.element("RecordList");
+            if (recordListElement == null || recordInfo.getSumNum() == 0) {
+                logger.info("无录像数据");
+                RequestMessage msg = new RequestMessage();
+                msg.setKey(key);
+                msg.setData(recordInfo);
+                deferredResultHolder.invokeAllResult(msg);
+            } else {
+                Iterator<Element> recordListIterator = recordListElement.elementIterator();
+                List<RecordItem> recordList = new ArrayList<RecordItem>();
+                if (recordListIterator != null) {
+                    RecordItem record = new RecordItem();
+                    logger.info("处理录像列表数据...");
+                    // 遍历DeviceList
+                    while (recordListIterator.hasNext()) {
+                        Element itemRecord = recordListIterator.next();
+                        Element recordElement = itemRecord.element("DeviceID");
+                        if (recordElement == null) {
+                            logger.info("记录为空,下一个...");
+                            continue;
+                        }
+                        record = new RecordItem();
+                        record.setDeviceId(getText(itemRecord, "DeviceID"));
+                        record.setName(getText(itemRecord, "Name"));
+                        record.setFilePath(getText(itemRecord, "FilePath"));
+                        record.setAddress(getText(itemRecord, "Address"));
+                        record.setStartTime(
+                                DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "StartTime")));
+                        record.setEndTime(
+                                DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "EndTime")));
+                        record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
+                                : Integer.parseInt(getText(itemRecord, "Secrecy")));
+                        record.setType(getText(itemRecord, "Type"));
+                        record.setRecorderId(getText(itemRecord, "RecorderID"));
+                        recordList.add(record);
+                    }
+                    recordInfo.setRecordList(recordList);
+                }
+
+                // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题
+                String cacheKey = CACHE_RECORDINFO_KEY + device.getDeviceId() + sn;
+                redis.set(cacheKey + "_" + uuid, recordList, 90);
+                if (!threadNameList.contains(cacheKey)) {
+                    threadNameList.add(cacheKey);
+                    CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo);
+                    chk.setName(cacheKey);
+                    chk.setDeferredResultHolder(deferredResultHolder);
+                    chk.setRedis(redis);
+                    chk.setLogger(logger);
+                    chk.start();
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("Start Thread " + cacheKey + ".");
+                    }
+                } else {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("Thread " + cacheKey + " already started.");
+                    }
+                }
+            }
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        } catch (DocumentException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
+
+    }
+}

+ 15 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java

@@ -0,0 +1,15 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.response;
+
+import javax.sip.ResponseEvent;
+
+/**    
+ * @description:处理接收IPCamera发来的SIP协议响应消息
+ * @author: swwheihei
+ * @date:   2020年5月3日 下午4:42:22     
+ */
+public interface ISIPResponseProcessor {
+
+	void process(ResponseEvent evt);
+
+
+}

+ 8 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/SIPResponseProcessorAbstract.java

@@ -0,0 +1,8 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.response;
+
+import org.springframework.beans.factory.InitializingBean;
+
+public abstract class SIPResponseProcessorAbstract implements InitializingBean, ISIPResponseProcessor {
+
+
+}

+ 48 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java

@@ -0,0 +1,48 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.ResponseEvent;
+
+/**    
+ * @description: BYE请求响应器
+ * @author: swwheihei
+ * @date:   2020年5月3日 下午5:32:05     
+ */
+@Component
+public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
+
+	private String method = "BYE";
+
+	@Autowired
+	private SipLayer sipLayer;
+
+	@Autowired
+	private SipConfig config;
+
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addResponseProcessor(method, this);
+	}
+	/**
+	 * 处理BYE响应
+	 * 
+	 * @param evt
+	 */
+	@Override
+	public void process(ResponseEvent evt) {
+		// TODO Auto-generated method stub
+	}
+
+
+}

+ 47 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java

@@ -0,0 +1,47 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.ResponseEvent;
+
+/**    
+ * @description: CANCEL响应处理器
+ * @author: panlinlin
+ * @date:   2021年11月5日 16:35
+ */
+@Component
+public class CancelResponseProcessor extends SIPResponseProcessorAbstract {
+
+	private String method = "CANCEL";
+
+	@Autowired
+	private SipLayer sipLayer;
+
+	@Autowired
+	private SipConfig config;
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addResponseProcessor(method, this);
+	}
+	/**   
+	 * 处理CANCEL响应
+	 *  
+	 * @param evt
+	 */
+	@Override
+	public void process(ResponseEvent evt) {
+		// TODO Auto-generated method stub
+		
+	}
+
+}

+ 97 - 75
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java

@@ -1,75 +1,97 @@
-package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
-
-import java.text.ParseException;
-
-import javax.sip.*;
-import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
-import gov.nist.javax.sip.ResponseEventExt;
-import gov.nist.javax.sip.stack.SIPDialog;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.SipLayer;
-import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
-
-
-/**
- * @Description:处理INVITE响应
- * @author: swwheihei
- * @date: 2020年5月3日 下午4:43:52
- */
-@Component
-public class InviteResponseProcessor implements ISIPResponseProcessor {
-
-	 private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
-
-	@Autowired
-	private VideoStreamSessionManager streamSession;
-
-	/**
-	 * 处理invite响应
-	 * 
-	 * @param evt 响应消息
-	 * @throws ParseException
-	 */
-	@Override
-	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException {
-		try {
-			Response response = evt.getResponse();
-			int statusCode = response.getStatusCode();
-			// trying不会回复
-			if (statusCode == Response.TRYING) {
-			}
-			// 成功响应
-			// 下发ack
-			if (statusCode == Response.OK) {
-				ResponseEventExt event = (ResponseEventExt)evt;
-				SIPDialog dialog = (SIPDialog)evt.getDialog();
-				CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
-				Request reqAck = dialog.createAck(cseq.getSeqNumber());
-				SipURI requestURI = (SipURI) reqAck.getRequestURI();
-				requestURI.setHost(event.getRemoteIpAddress());
-				requestURI.setPort(event.getRemotePort());
-				reqAck.setRequestURI(requestURI);
-				logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
-				SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
-				String deviceId = requestURI.getUser();
-				String channelId = sipURI.getUser();
-
-				dialog.sendAck(reqAck);
-
-			}
-		} catch (InvalidArgumentException | SipException e) {
-			e.printStackTrace();
-		}
-	}
-
-}
+package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
+import gov.nist.javax.sip.ResponseEventExt;
+import gov.nist.javax.sip.stack.SIPDialog;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.ResponseEvent;
+import javax.sip.SipException;
+import javax.sip.address.SipURI;
+import javax.sip.header.CSeqHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+
+/**
+ * @description: 处理INVITE响应
+ * @author: panlinlin
+ * @date: 2021年11月5日 16:40
+ */
+@Component
+public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
+
+	private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
+	private String method = "INVITE";
+
+	@Autowired
+	private SipLayer sipLayer;
+
+	@Autowired
+	private SipConfig config;
+
+
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addResponseProcessor(method, this);
+	}
+
+	@Autowired
+	private VideoStreamSessionManager streamSession;
+
+	/**
+	 * 处理invite响应
+	 * 
+	 * @param evt 响应消息
+	 * @throws ParseException
+	 */
+	@Override
+	public void process(ResponseEvent evt ){
+		try {
+			Response response = evt.getResponse();
+			int statusCode = response.getStatusCode();
+			// trying不会回复
+			if (statusCode == Response.TRYING) {
+			}
+			// 成功响应
+			// 下发ack
+			if (statusCode == Response.OK) {
+				ResponseEventExt event = (ResponseEventExt)evt;
+				SIPDialog dialog = (SIPDialog)evt.getDialog();
+				CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
+				Request reqAck = dialog.createAck(cseq.getSeqNumber());
+				SipURI requestURI = (SipURI) reqAck.getRequestURI();
+				try {
+					requestURI.setHost(event.getRemoteIpAddress());
+				} catch (ParseException e) {
+					e.printStackTrace();
+				}
+				requestURI.setPort(event.getRemotePort());
+				reqAck.setRequestURI(requestURI);
+				logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
+				SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
+				String deviceId = requestURI.getUser();
+				String channelId = sipURI.getUser();
+
+				dialog.sendAck(reqAck);
+
+			}
+		} catch (InvalidArgumentException | SipException e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 15 - 11
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java

@@ -1,11 +1,10 @@
-package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
+package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
 
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
-import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import org.slf4j.Logger;
@@ -19,14 +18,15 @@ import javax.sip.header.WWWAuthenticateHeader;
 import javax.sip.message.Response;
 
 /**    
- * @Description:Register响应处理器
+ * @description:Register响应处理器
  * @author: swwheihei
  * @date:   2020年5月3日 下午5:32:23     
  */
 @Component
-public class RegisterResponseProcessor implements ISIPResponseProcessor {
+public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
 
 	private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
+	private String method = "REGISTER";
 
 	@Autowired
 	private ISIPCommanderForPlatform sipCommanderForPlatform;
@@ -37,18 +37,22 @@ public class RegisterResponseProcessor implements ISIPResponseProcessor {
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 
-	public RegisterResponseProcessor() {
+	@Autowired
+	private SIPProcessorObserver sipProcessorObserver;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		// 添加消息处理的订阅
+		sipProcessorObserver.addResponseProcessor(method, this);
 	}
 
 	/**
 	 * 处理Register响应
 	 *
- 	 * @param evt
-	 * @param layer
-	 * @param config
+ 	 * @param evt 事件
 	 */
 	@Override
-	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
+	public void process(ResponseEvent evt) {
 		Response response = evt.getResponse();
 		CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
 		String callId = callIdHeader.getCallId();

+ 7 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/ITimeoutProcessor.java

@@ -0,0 +1,7 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.timeout;
+
+import javax.sip.TimeoutEvent;
+
+public interface ITimeoutProcessor {
+    void process(TimeoutEvent event);
+}

+ 36 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java

@@ -0,0 +1,36 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.timeout.impl;
+
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.TimeoutEvent;
+import javax.sip.header.CallIdHeader;
+
+@Component
+public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor {
+
+    @Autowired
+    private SIPProcessorObserver processorObserver;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        processorObserver.addTimeoutProcessor(this);
+    }
+
+    @Override
+    public void process(TimeoutEvent event) {
+        // TODO Auto-generated method stub
+        CallIdHeader callIdHeader = event.getClientTransaction().getDialog().getCallId();
+        String callId = callIdHeader.getCallId();
+        SipSubscribe.Event errorSubscribe = sipSubscribe.getErrorSubscribe(callId);
+        SipSubscribe.EventResult<TimeoutEvent> timeoutEventEventResult = new SipSubscribe.EventResult<>(event);
+        errorSubscribe.response(timeoutEventEventResult);
+    }
+}

+ 0 - 12
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java

@@ -1,12 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request;
-
-/**    
- * @Description:处理接收IPCamera发来的SIP协议请求消息
- * @author: swwheihei
- * @date:   2020年5月3日 下午4:42:22     
- */
-public interface ISIPRequestProcessor {
-
-	public void process();
-
-}

+ 0 - 131
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java

@@ -1,131 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request;
-
-import javax.sip.PeerUnavailableException;
-import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipFactory;
-import javax.sip.SipProvider;
-import javax.sip.TransactionAlreadyExistsException;
-import javax.sip.TransactionUnavailableException;
-import javax.sip.address.AddressFactory;
-import javax.sip.header.HeaderFactory;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.MessageFactory;
-import javax.sip.message.Request;
-
-import gov.nist.javax.sip.SipStackImpl;
-import gov.nist.javax.sip.message.SIPRequest;
-import gov.nist.javax.sip.stack.SIPServerTransaction;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**    
- * @Description:处理接收IPCamera发来的SIP协议请求消息
- * @author: songww
- * @date:   2020年5月3日 下午4:42:22     
- */
-public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor {
-
-	private final static Logger logger = LoggerFactory.getLogger(SIPRequestAbstractProcessor.class);
-
-	protected RequestEvent evt;
-	
-	private SipProvider tcpSipProvider;
-	
-	private SipProvider udpSipProvider;
-	
-	@Override
-	public void process() {
-		this.process(evt);
-	}
-	
-	public abstract void process(RequestEvent evt);
-	
-	public ServerTransaction getServerTransaction(RequestEvent evt) {
-		Request request = evt.getRequest();
-		ServerTransaction serverTransaction = evt.getServerTransaction();
-		// 判断TCP还是UDP
-		boolean isTcp = false;
-		ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
-		String transport = reqViaHeader.getTransport();
-		if (transport.equals("TCP")) {
-			isTcp = true;
-		}
-
-		if (serverTransaction == null) {
-			try {
-				if (isTcp) {
-					SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
-					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
-					if (serverTransaction == null) {
-						serverTransaction = tcpSipProvider.getNewServerTransaction(request);
-					}
-				} else {
-					SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
-					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
-					if (serverTransaction == null) {
-						serverTransaction = udpSipProvider.getNewServerTransaction(request);
-					}
-				}
-			} catch (TransactionAlreadyExistsException e) {
-				logger.error(e.getMessage());
-			} catch (TransactionUnavailableException e) {
-				logger.error(e.getMessage());
-			}
-		}
-		return serverTransaction;
-	}
-	
-	public AddressFactory getAddressFactory() {
-		try {
-			return SipFactory.getInstance().createAddressFactory();
-		} catch (PeerUnavailableException e) {
-			e.printStackTrace();
-		}
-		return null;
-	}
-
-	public HeaderFactory getHeaderFactory() {
-		try {
-			return SipFactory.getInstance().createHeaderFactory();
-		} catch (PeerUnavailableException e) {
-			e.printStackTrace();
-		}
-		return null;
-	}
-
-	public MessageFactory getMessageFactory() {
-		try {
-			return SipFactory.getInstance().createMessageFactory();
-		} catch (PeerUnavailableException e) {
-			e.printStackTrace();
-		}
-		return null;
-	}
-
-	public RequestEvent getRequestEvent() {
-		return evt;
-	}
-
-	public void setRequestEvent(RequestEvent evt) {
-		this.evt = evt;
-	}
-
-	public SipProvider getTcpSipProvider() {
-		return tcpSipProvider;
-	}
-
-	public void setTcpSipProvider(SipProvider tcpSipProvider) {
-		this.tcpSipProvider = tcpSipProvider;
-	}
-
-	public SipProvider getUdpSipProvider() {
-		return udpSipProvider;
-	}
-
-	public void setUdpSipProvider(SipProvider udpSipProvider) {
-		this.udpSipProvider = udpSipProvider;
-	}
-	
-	
-}

+ 0 - 28
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java

@@ -1,28 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import javax.sip.RequestEvent;
-
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-
-/**    
- * @Description:CANCEL请求处理器
- * @author: swwheihei
- * @date:   2020年5月3日 下午5:32:23     
- */
-public class CancelRequestProcessor extends SIPRequestAbstractProcessor {
-
-	/**   
-	 * 处理CANCEL请求
-	 *  
-	 * @param evt
-	 * @param layer
-	 * @param transaction
-	 * @param config    
-	 */  
-	@Override
-	public void process(RequestEvent evt) {
-		// TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令
-		
-	}
-
-}

+ 0 - 31
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java

@@ -1,31 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
-
-import javax.sip.RequestEvent;
-
-import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**    
- * @Description:暂不支持的消息请求处理器
- * @author: swwheihei
- * @date:   2020年5月3日 下午5:32:59     
- */
-public class OtherRequestProcessor extends SIPRequestAbstractProcessor {
-
-	private Logger logger = LoggerFactory.getLogger(OtherRequestProcessor.class);
-
-	/**   
-	 * <p>Title: process</p>   
-	 * <p>Description: </p>   
-	 * @param evt
-	 * @param layer
-	 * @param transaction
-	 * @param config    
-	 */  
-	@Override
-	public void process(RequestEvent evt) {
-		logger.info("Unsupported the method: " + evt.getRequest().getMethod());
-	}
-
-}

+ 0 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java


部分文件因文件數量過多而無法顯示