From 26739237e2d93460eb869067a6004bfa63a1bdb8 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: 星期一, 27 六月 2022 10:16:21 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/wvp-28181-2.0' into commercial

---
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java                                                               |   17 
 src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java                                                               |    4 
 src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java                                                                  |    9 
 src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java                                                     |   23 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java                                |    2 
 src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java                                                             |   19 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java                         |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java                                        |    4 
 src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java                                                         |   23 
 src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java                                                            |   15 
 src/main/resources/application-dev.yml                                                                                               |    2 
 src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java                                                                            |   12 
 src/main/java/com/genersoft/iot/vmp/service/IPlayService.java                                                                        |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java                                                            |    5 
 web_src/src/components/DeviceList.vue                                                                                                |   58 
 src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java                                                         |  377 ++++
 src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java                                                                    |  116 +
 src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java                                                                |  105 
 src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java                                                                            |   14 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java                                                      |   22 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java                                |   21 
 src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java                                                               |    9 
 src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java                                                          |    1 
 web_src/src/components/service/DeviceService.js                                                                                      |   46 
 web_src/src/components/common/DeviceTree.vue                                                                                         |   24 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java                                                                    |   13 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java                                                      |   85 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java                                                                   |   38 
 src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java                                                              |    1 
 pom.xml                                                                                                                              |    9 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java        |    6 
 src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java                                                                 |   12 
 src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java                                                       |   17 
 web_src/src/components/dialog/deviceEdit.vue                                                                                         |    6 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java                                   |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java                              |    2 
 src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java                                                          |   17 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java                                                                  |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java                                                       |   18 
 web_src/src/components/ParentPlatformList.vue                                                                                        |   32 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java                                     |   91 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java      |  141 +
 src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java                                                             |    4 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java                                                               |   47 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java                                           |  167 +
 web_src/src/components/control.vue                                                                                                   |    6 
 src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java                                                            |   31 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java                                                  |    4 
 src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java                                                            |    3 
 web_src/src/components/dialog/rtcPlayer.vue                                                                                          |   18 
 src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java                                                      |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java                                                                |   15 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java            |   14 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java                                                         |   35 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java                               |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java                                                          |    8 
 src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java                                                                   |    3 
 src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java                                                           |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java                                                                  |   52 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java                                     |   10 
 sql/mysql.sql                                                                                                                        |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java                                               |    8 
 src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java                                                  |    7 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java                                          |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java                                                                |   13 
 web_src/src/components/StreamProxyList.vue                                                                                           |   50 
 src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java                                                                              |   45 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java                               |  143 +
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java                                  |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java |    2 
 web_src/package.json                                                                                                                 |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java      |   45 
 web_src/src/components/map.vue                                                                                                       |   38 
 src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java                                                           |  170 ++
 README.md                                                                                                                            |    5 
 src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java                                                                |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java                                  | 1224 +++++++------
 src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java                                                   |   16 
 src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java                                                             |  173 ++
 web_src/src/router/index.js                                                                                                          |   12 
 web_src/src/layout/UiHeader.vue                                                                                                      |   12 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java                                                               |    7 
 sql/update.sql                                                                                                                       |   26 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java                                  |  106 
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java                                                                     |   15 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java                                                                         |   19 
 web_src/src/App.vue                                                                                                                  |    6 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java                                                               |   44 
 web_src/src/components/PushVideoList.vue                                                                                             |   51 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java   |  143 +
 web_src/src/components/CloudRecord.vue                                                                                               |   16 
 src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java                                                         |   83 
 src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java                                                         |    8 
 web_src/src/components/dialog/devicePlayer.vue                                                                                       |  300 ++
 src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java                                                                            |   24 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java                                |    2 
 web_src/src/components/MediaServerManger.vue                                                                                         |    2 
 src/main/resources/application-docker.yml                                                                                            |    2 
 src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java                                                              |   73 
 src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java                                                                       |   30 
 src/main/resources/all-application.yml                                                                                               |    2 
 /dev/null                                                                                                                            |   23 
 web_src/src/components/channelList.vue                                                                                               |  163 +
 web_src/src/components/common/jessibuca.vue                                                                                          |   79 
 DOCKERFILE                                                                                                                           |    3 
 src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java                                                            |   30 
 106 files changed, 3,638 insertions(+), 1,447 deletions(-)

diff --git a/DOCKERFILE b/DOCKERFILE
index d55e06a..f2a2ffa 100644
--- a/DOCKERFILE
+++ b/DOCKERFILE
@@ -1,3 +1,6 @@
+#寰堜箙娌$淮鎶や簡锛屽凡缁忎笌瀹氬墠鐗堟湰涓嶅尮閰�
+
+
 FROM ubuntu:20.04 AS build
 
 ARG DEBIAN_FRONTEND=noninteractive
diff --git a/README.md b/README.md
index 7ba443f..1c5f868 100644
--- a/README.md
+++ b/README.md
@@ -107,6 +107,8 @@
 - [X] 浜戠褰曞儚锛堥渶瑕侀儴缃插崟鐙湇鍔¢厤鍚堜娇鐢級
 - [X] 澶氭祦濯掍綋鑺傜偣锛岃嚜鍔ㄩ�夋嫨璐熻浇鏈�浣庣殑鑺傜偣浣跨敤銆�
 - [X] WEB绔敮鎸佹挱鏀綡264涓嶩265锛岄煶棰戞敮鎸丟.711A/G.711U/AAC,瑕嗙洊鍥芥爣甯哥敤缂栫爜鏍煎紡銆�
+- [X] 鏀寔鐢靛瓙鍦板浘銆�
+- [X] 鏀寔鎺ュ叆WGS84鍜孏CJ02涓ょ鍧愭爣绯汇��
 
 [//]: # (# docker蹇�熶綋楠�)
 
@@ -143,7 +145,7 @@
 
 # 鍚堜綔
 鐩墠寰堝鎵撶潃鍚堜綔鐨勫箤瀛愭潵绉佽亰鐨勶紝鍏跺疄澶у澶у彲涓嶅繀锛岀洰鍓嶄綔鑰呮病鏈夌簿鍔涳紝浣犳湁闂鍙互浠樿垂鎵炬垜瑙g瓟锛屼篃鍙互鎻怭R
-锛屽鏋滃浠g爜鏈夊缓璁彲浠ユ彁ISSUE锛涗篃鍙互鍔犵兢涓�璧疯亰鑱娿�傛垜浠杩庢墍鏈夋湁鍏磋叮浣嗛亣鍒伴」鐩腑鏉ョ殑浜恒��
+锛屽鏋滃浠g爜鏈夊缓璁彲浠ユ彁ISSUE锛涗篃鍙互鍔犵兢涓�璧疯亰鑱娿�傛垜浠杩庢墍鏈夋湁鍏磋叮鍙備笌鍒伴」鐩腑鏉ョ殑浜恒��
 
 
 
@@ -163,6 +165,7 @@
 [hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen)
 [chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb)
 [ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
+[mk1990](https://github.com/mk1990)
 
 ps: 鍒氬鍔犱簡杩欎釜鍚嶅崟锛岃偗瀹氶仐婕忎簡涓�浜涘ぇ浣紝娆㈣繋澶т浆鑱旂郴鎴戞坊鍔犮��
 
diff --git a/pom.xml b/pom.xml
index 1546e39..1209124 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
 
 	<groupId>com.genersoft</groupId>
 	<artifactId>wvp-pro</artifactId>
-	<version>2.2.1</version>
+	<version>2.3.1</version>
 	<name>web video platform</name>
 	<description>鍥芥爣28181瑙嗛骞冲彴</description>
 
@@ -159,8 +159,9 @@
 		<dependency>
 			<groupId>com.alibaba</groupId>
 			<artifactId>fastjson</artifactId>
-			<version>1.2.73</version>
+			<version>1.2.83</version>
 		</dependency>
+
 
 		<!-- okhttp -->
 		<dependency>
@@ -180,9 +181,9 @@
 
 		<!-- okhttp-digest -->
 		<dependency>
-			<groupId>com.burgstaller</groupId>
+			<groupId>io.github.rburgst</groupId>
 			<artifactId>okhttp-digest</artifactId>
-			<version>2.1</version>
+			<version>2.5</version>
 		</dependency>
 
 		<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
diff --git a/sql/mysql.sql b/sql/mysql.sql
index 4086d11..5e8467f 100644
--- a/sql/mysql.sql
+++ b/sql/mysql.sql
@@ -471,6 +471,7 @@
                                `createStamp` bigint(20) DEFAULT NULL,
                                `aliveSecond` int(11) DEFAULT NULL,
                                `mediaServerId` varchar(50) DEFAULT NULL,
+                               `serverId` varchar(50) not NULL,
                                PRIMARY KEY (`id`),
                                UNIQUE KEY `stream_push_pk` (`app`,`stream`)
 ) ENGINE=InnoDB AUTO_INCREMENT=300838 DEFAULT CHARSET=utf8mb4;
diff --git a/sql/update.sql b/sql/update.sql
index 8bfe343..b7e98f8 100644
--- a/sql/update.sql
+++ b/sql/update.sql
@@ -1,5 +1,29 @@
+alter table parent_platform
+    add startOfflinePush int default 0 null;
+
+alter table parent_platform
+    add administrativeDivision varchar(50) not null;
+
+alter table parent_platform
+    add catalogGroup int default 1 null;
+
 alter table device
     add audioChannelForReceive VARCHAR(50) null;
 
 alter table device
-    add audioChannelForSend VARCHAR(50) null;
\ No newline at end of file
+    add audioChannelForSend VARCHAR(50) null;
+
+alter table stream_push
+    add serverId varchar(50) not null;
+alter table device
+    add geoCoordSys varchar(50) not null;
+update device set device.geoCoordSys='WGS84';
+alter table device_channel
+    add longitudeGcj02 double default null;
+alter table device_channel
+    add latitudeGcj02 double default null;
+alter table device_channel
+    add longitudeWgs84 double default null;
+alter table device_channel
+    add latitudeWgs84 double default null;
+
diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
index eb98f6f..57f764b 100644
--- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
+++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -97,4 +97,5 @@
 	//**************************    绗笁鏂�  ****************************************
 	public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
 	public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
index 3b021de..ade2e62 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
@@ -103,12 +103,12 @@
 
     public void stop(String key) {
         if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
-            futureMap.get(key).cancel(true);
-            Runnable runnable = runnableMap.get(key);
-            if (runnable instanceof ISubscribeTask) {
-                ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
-                subscribeTask.stop();
-            }
+//            Runnable runnable = runnableMap.get(key);
+//            if (runnable instanceof ISubscribeTask) {
+//                ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
+//                subscribeTask.stop();
+//            }
+            futureMap.get(key).cancel(false);
         }
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
index 85f4684..c24e0ca 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -6,6 +6,10 @@
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.StringUtils;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.regex.Pattern;
+
 
 @Configuration("mediaConfig")
 public class MediaConfig{
@@ -161,7 +165,18 @@
         if (StringUtils.isEmpty(sdpIp)){
             return ip;
         }else {
-            return sdpIp;
+            if (isValidIPAddress(sdpIp)) {
+                return sdpIp;
+            }else {
+                // 鎸夌収鍩熷悕瑙f瀽
+                String hostAddress = null;
+                try {
+                    hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
+                } catch (UnknownHostException e) {
+                    throw new RuntimeException(e);
+                }
+                return hostAddress;
+            }
         }
     }
 
@@ -211,4 +226,11 @@
         return mediaServerItem;
     }
 
+    private boolean isValidIPAddress(String ipAddress) {
+        if ((ipAddress != null) && (!ipAddress.isEmpty())) {
+            return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
+        }
+        return false;
+    }
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
index 6b45eb5..ec1f9ba 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
@@ -2,7 +2,9 @@
 
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener;
-import com.genersoft.iot.vmp.service.impl.RedisGPSMsgListener;
+import com.genersoft.iot.vmp.service.impl.RedisGpsMsgListener;
+import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
+import com.genersoft.iot.vmp.service.impl.RedisStreamMsgListener;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -47,10 +49,16 @@
 	private int poolMaxWait;
 
 	@Autowired
-	private RedisGPSMsgListener redisGPSMsgListener;
+	private RedisGpsMsgListener redisGPSMsgListener;
 
 	@Autowired
 	private RedisAlarmMsgListener redisAlarmMsgListener;
+
+	@Autowired
+	private RedisStreamMsgListener redisStreamMsgListener;
+
+	@Autowired
+	private RedisGbPlayMsgListener redisGbPlayMsgListener;
 
 	@Bean
 	public JedisPool jedisPool() {
@@ -98,6 +106,8 @@
         container.setConnectionFactory(connectionFactory);
 		container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
 		container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
+		container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
+		container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY));
         return container;
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
index 9690c6d..2d7e8a1 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
@@ -11,6 +11,9 @@
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
+/**
+ * @author lin
+ */
 @Component
 public class LoginSuccessHandler implements AuthenticationSuccessHandler {
 
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
index 47cfdab..a4bbdba 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
@@ -20,6 +20,7 @@
 
 /**
  * 閰嶇疆Spring Security
+ * @author lin
  */
 @Configuration
 @EnableWebSecurity
@@ -132,15 +133,19 @@
                 .anyRequest().authenticated()
                 // 寮傚父澶勭悊(鏉冮檺鎷掔粷銆佺櫥褰曞け鏁堢瓑)
                 .and().exceptionHandling()
-                .authenticationEntryPoint(anonymousAuthenticationEntryPoint)//鍖垮悕鐢ㄦ埛璁块棶鏃犳潈闄愯祫婧愭椂鐨勫紓甯稿鐞�
+                //鍖垮悕鐢ㄦ埛璁块棶鏃犳潈闄愯祫婧愭椂鐨勫紓甯稿鐞�
+                .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
 //                .accessDeniedHandler(accessDeniedHandler)//鐧诲綍鐢ㄦ埛娌℃湁鏉冮檺璁块棶璧勬簮
-                // 鐧诲叆
-                .and().formLogin().permitAll()//鍏佽鎵�鏈夌敤鎴�
-                .successHandler(loginSuccessHandler)//鐧诲綍鎴愬姛澶勭悊閫昏緫
-                .failureHandler(loginFailureHandler)//鐧诲綍澶辫触澶勭悊閫昏緫
+                // 鐧诲叆 鍏佽鎵�鏈夌敤鎴�
+                .and().formLogin().permitAll()
+                //鐧诲綍鎴愬姛澶勭悊閫昏緫
+                .successHandler(loginSuccessHandler)
+                //鐧诲綍澶辫触澶勭悊閫昏緫
+                .failureHandler(loginFailureHandler)
                 // 鐧诲嚭
-                .and().logout().logoutUrl("/api/user/logout").permitAll()//鍏佽鎵�鏈夌敤鎴�
-                .logoutSuccessHandler(logoutHandler)//鐧诲嚭鎴愬姛澶勭悊閫昏緫
+                .and().logout().logoutUrl("/api/user/logout").permitAll()
+                //鐧诲嚭鎴愬姛澶勭悊閫昏緫
+                .logoutSuccessHandler(logoutHandler)
                 .deleteCookies("JSESSIONID")
                 // 浼氳瘽绠$悊
 //                .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 瓒呮椂澶勭悊
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
index a095f51..c6406c5 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
@@ -1,6 +1,10 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
 
+/**
+ * 鍥芥爣璁惧/骞冲彴
+ * @author lin
+ */
 public class Device {
 
 	/**
@@ -127,7 +131,12 @@
 	/**
 	 * 鏄惁寮�鍚痵src鏍¢獙锛岄粯璁ゅ叧闂紝寮�鍚彲浠ラ槻姝覆娴�
 	 */
-	private boolean ssrcCheck;
+	private boolean ssrcCheck = true;
+
+	/**
+	 * 鍦扮悊鍧愭爣绯伙紝 鐩墠鏀寔 WGS84,GCJ02 TODO CGCS2000
+	 */
+	private String geoCoordSys;
 
 
 	public String getDeviceId() {
@@ -322,4 +331,12 @@
 		this.ssrcCheck = ssrcCheck;
 	}
 
+	public String getGeoCoordSys() {
+		return geoCoordSys;
+	}
+
+	public void setGeoCoordSys(String geoCoordSys) {
+		this.geoCoordSys = geoCoordSys;
+	}
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
index 95576f3..6345277 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
@@ -155,6 +155,26 @@
 	private double latitude;
 
 	/**
+	 * 缁忓害 GCJ02
+	 */
+	private double longitudeGcj02;
+
+	/**
+	 * 绾害 GCJ02
+	 */
+	private double latitudeGcj02;
+
+	/**
+	 * 缁忓害 WGS84
+	 */
+	private double longitudeWgs84;
+
+	/**
+	 * 绾害 WGS84
+	 */
+	private double latitudeWgs84;
+
+	/**
 	 * 瀛愯澶囨暟
 	 */
 	private int subCount;
@@ -407,6 +427,38 @@
 		this.latitude = latitude;
 	}
 
+	public double getLongitudeGcj02() {
+		return longitudeGcj02;
+	}
+
+	public void setLongitudeGcj02(double longitudeGcj02) {
+		this.longitudeGcj02 = longitudeGcj02;
+	}
+
+	public double getLatitudeGcj02() {
+		return latitudeGcj02;
+	}
+
+	public void setLatitudeGcj02(double latitudeGcj02) {
+		this.latitudeGcj02 = latitudeGcj02;
+	}
+
+	public double getLongitudeWgs84() {
+		return longitudeWgs84;
+	}
+
+	public void setLongitudeWgs84(double longitudeWgs84) {
+		this.longitudeWgs84 = longitudeWgs84;
+	}
+
+	public double getLatitudeWgs84() {
+		return latitudeWgs84;
+	}
+
+	public void setLatitudeWgs84(double latitudeWgs84) {
+		this.latitudeWgs84 = latitudeWgs84;
+	}
+
 	public int getSubCount() {
 		return subCount;
 	}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java
new file mode 100644
index 0000000..97da863
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java
@@ -0,0 +1,44 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import org.dom4j.Element;
+
+import javax.sip.RequestEvent;
+
+/**
+ * @author lin
+ */
+public class HandlerCatchData {
+    private RequestEvent evt;
+    private Device device;
+    private Element rootElement;
+
+    public HandlerCatchData(RequestEvent evt, Device device, Element rootElement) {
+        this.evt = evt;
+        this.device = device;
+        this.rootElement = rootElement;
+    }
+
+    public RequestEvent getEvt() {
+        return evt;
+    }
+
+    public void setEvt(RequestEvent evt) {
+        this.evt = evt;
+    }
+
+    public Device getDevice() {
+        return device;
+    }
+
+    public void setDevice(Device device) {
+        this.device = device;
+    }
+
+    public Element getRootElement() {
+        return rootElement;
+    }
+
+    public void setRootElement(Element rootElement) {
+        this.rootElement = rootElement;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java
index c7f6182..41e1af7 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java
@@ -72,6 +72,11 @@
     private String mediaServerId;
 
     /**
+     * 浣跨敤鐨勬湇鍔$殑ID
+     */
+    private String serverId;
+
+    /**
      *  invite鐨刢allId
      */
     private String CallId;
@@ -259,4 +264,12 @@
     public void setOnlyAudio(boolean onlyAudio) {
         this.onlyAudio = onlyAudio;
     }
+
+    public String getServerId() {
+        return serverId;
+    }
+
+    public void setServerId(String serverId) {
+        this.serverId = serverId;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java
index f191c00..4a900c1 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java
@@ -2,6 +2,7 @@
 
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.DynamicTask;
+import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -38,7 +39,6 @@
         catalogMap.put(platformId, subscribeInfo);
         // 娣诲姞璁㈤槄鍒版湡
         String taskOverdueKey = taskOverduePrefix +  "catalog_" + platformId;
-        dynamicTask.stop(taskOverdueKey);
         // 娣诲姞浠诲姟澶勭悊璁㈤槄杩囨湡
         dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()),
                 subscribeInfo.getExpires() * 1000);
@@ -49,10 +49,17 @@
     }
 
     public void removeCatalogSubscribe(String platformId) {
+
         catalogMap.remove(platformId);
         String taskOverdueKey = taskOverduePrefix +  "catalog_" + platformId;
+        Runnable runnable = dynamicTask.get(taskOverdueKey);
+        if (runnable instanceof ISubscribeTask) {
+            ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
+            subscribeTask.stop();
+        }
         // 娣诲姞浠诲姟澶勭悊璁㈤槄杩囨湡
         dynamicTask.stop(taskOverdueKey);
+
     }
 
     public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) {
@@ -63,7 +70,6 @@
                 storager,  platformId, subscribeInfo.getSn(), key, this, dynamicTask),
                 subscribeInfo.getGpsInterval() * 1000);
         String taskOverdueKey = taskOverduePrefix +  "MobilePosition_" + platformId;
-        dynamicTask.stop(taskOverdueKey);
         // 娣诲姞浠诲姟澶勭悊璁㈤槄杩囨湡
         dynamicTask.startDelay(taskOverdueKey, () -> {
                     removeMobilePositionSubscribe(subscribeInfo.getId());
@@ -81,6 +87,11 @@
         // 缁撴潫浠诲姟澶勭悊GPS瀹氭椂鎺ㄩ��
         dynamicTask.stop(key);
         String taskOverdueKey = taskOverduePrefix +  "MobilePosition_" + platformId;
+        Runnable runnable = dynamicTask.get(taskOverdueKey);
+        if (runnable instanceof ISubscribeTask) {
+            ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
+            subscribeTask.stop();
+        }
         // 娣诲姞浠诲姟澶勭悊璁㈤槄杩囨湡
         dynamicTask.stop(taskOverdueKey);
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
index 3d817c3..c6cfc7a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
@@ -88,8 +88,8 @@
                 this.type = "timeout";
                 this.msg = "娑堟伅瓒呮椂鏈洖澶�";
                 this.statusCode = -1024;
-                this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();
                 this.dialog = timeoutEvent.getClientTransaction().getDialog();
+                this.callId = this.dialog != null?timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId(): null;
             }else if (event instanceof TransactionTerminatedEvent) {
                 TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
                 this.type = "transactionTerminated";
@@ -109,8 +109,8 @@
                 this.type = "deviceNotFoundEvent";
                 this.msg = "璁惧鏈壘鍒�";
                 this.statusCode = -1024;
-                this.callId = deviceNotFoundEvent.getDialog().getCallId().getCallId();
                 this.dialog = deviceNotFoundEvent.getDialog();
+                this.callId = this.dialog != null ?deviceNotFoundEvent.getDialog().getCallId().getCallId() : null;
             }
         }
     }
@@ -130,6 +130,9 @@
     }
 
     public void removeErrorSubscribe(String key) {
+        if(key == null){
+            return;
+        }
         errorSubscribes.remove(key);
         errorTimeSubscribes.remove(key);
     }
@@ -139,6 +142,9 @@
     }
 
     public void removeOkSubscribe(String key) {
+        if(key == null){
+            return;
+        }
         okSubscribes.remove(key);
         okTimeSubscribes.remove(key);
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
index 7e5ecb4..e38733d 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
@@ -66,7 +66,7 @@
             subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
 
             if (subscribe == null) {
-                logger.info("鍙戦�佽闃呮秷鎭椂鍙戠幇璁㈤槄淇℃伅宸茬粡涓嶅瓨鍦�");
+                logger.info("鍙戦�佽闃呮秷鎭椂鍙戠幇璁㈤槄淇℃伅宸茬粡涓嶅瓨鍦�: {}", event.getPlatformId());
                 return;
             }
         }else {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
index 85bc39d..a22d24d 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
@@ -99,8 +99,8 @@
 		return dialog;
 	}
 
-	public SIPDialog getDialogByCallId(String deviceId, String channelId, String callID){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callID, null);
+	public SIPDialog getDialogByCallId(String deviceId, String channelId, String callId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
 		if (ssrcTransaction == null) {
 			return null;
 		}
@@ -108,11 +108,17 @@
 		if (dialogByteArray == null) {
 			return null;
 		}
-		SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
-		return dialog;
+		return (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
 	}
 
 	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
+
+		if (StringUtils.isEmpty(deviceId)) {
+			deviceId ="*";
+		}
+		if (StringUtils.isEmpty(channelId)) {
+			channelId ="*";
+		}
 		if (StringUtils.isEmpty(callId)) {
 			callId ="*";
 		}
@@ -179,7 +185,7 @@
 
 
 	public List<SsrcTransaction> getAllSsrc() {
-		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId() + "_" ));
+		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId()));
 		List<SsrcTransaction> result= new ArrayList<>();
 		for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
 			String key = (String)ssrcTransactionKeys.get(i);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java
index c416766..66b57fe 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java
@@ -71,7 +71,9 @@
                 String gbId = gbStream.getGbId();
                 GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId);
                 if (gpsMsgInfo != null) { // 鏃犳渶鏂颁綅缃笉鍙戦��
-                    logger.info("鏃犳渶鏂颁綅缃笉鍙戦��");
+                   if (logger.isDebugEnabled()) {
+                       logger.debug("鏃犳渶鏂颁綅缃笉鍙戦��");
+                   }
                     // 缁忕含搴﹂兘涓�0涓嶅彂閫�
                     if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) {
                         continue;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
index a06a73d..a2fab81 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
@@ -150,30 +150,24 @@
     public void processTimeout(TimeoutEvent timeoutEvent) {
         logger.info("[娑堟伅鍙戦�佽秴鏃禲");
         ClientTransaction clientTransaction = timeoutEvent.getClientTransaction();
-        eventPublisher.requestTimeOut(timeoutEvent);
+
         if (clientTransaction != null) {
+            logger.info("[鍙戦�侀敊璇闃匽 clientTransaction != null");
             Request request = clientTransaction.getRequest();
             if (request != null) {
+                logger.info("[鍙戦�侀敊璇闃匽 request != null");
                 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
                 if (callIdHeader != null) {
+                    logger.info("[鍙戦�侀敊璇闃匽");
                     SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
                     SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent);
                     subscribe.response(eventResult);
+                    sipSubscribe.removeOkSubscribe(callIdHeader.getCallId());
                     sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId());
                 }
             }
         }
-
-//        Timeout timeout = timeoutEvent.getTimeout();
-//        ServerTransaction serverTransaction = timeoutEvent.getServerTransaction();
-//        if (serverTransaction != null) {
-//            Request request = serverTransaction.getRequest();
-//            URI requestURI = request.getRequestURI();
-//            Header header = request.getHeader(FromHeader.NAME);
-//        }
-//        if(timeoutProcessor != null) {
-//            timeoutProcessor.process(timeoutEvent);
-//        }
+        eventPublisher.requestTimeOut(timeoutEvent);
     }
 
     @Override
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
index bd51cfa..bf6146a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -148,6 +148,14 @@
 	 * 鍥炴斁鍊嶉�熸挱鏀�
 	 */
 	void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
+	
+	/**
+	 * 鍥炴斁鎺у埗
+	 * @param device
+	 * @param streamInfo
+	 * @param content
+	 */
+	void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
 
 	
 	/**
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
index 7007e5a..d000f5a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
@@ -104,6 +104,14 @@
     boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo);
 
     /**
+     * 褰曞儚鎾斁鎺ㄩ�佸畬鎴愭椂鍙戦�丮ediaStatus娑堟伅
+     * @param platform
+     * @param sendRtpItem
+     * @return
+     */
+    boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem);
+
+    /**
      * 鍚戝彂璧风偣鎾殑涓婄骇鍥炲bye
      * @param platform 骞冲彴淇℃伅
      * @param callId  callId
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
index d8deb5c..832f716 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -32,7 +32,9 @@
 import org.springframework.util.StringUtils;
 
 import javax.sip.*;
+import javax.sip.address.Address;
 import javax.sip.address.SipURI;
+import javax.sip.address.URI;
 import javax.sip.header.*;
 import javax.sip.message.Request;
 import java.lang.reflect.Field;
@@ -708,22 +710,19 @@
 			}
 			SIPDialog dialog;
 			if (callId != null) {
-				dialog = streamSession.getDialogByCallId(deviceId, channelId, callId);
+				dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);
 			}else {
-				if (stream == null) {
+				if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {
 					return;
 				}
-				dialog = streamSession.getDialogByStream(deviceId, channelId, stream);
+				dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
 			}
-			if (ssrcTransaction != null) {
-				MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
-				mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
-				mediaServerService.closeRTPServer(deviceId, channelId, ssrcTransaction.getStream());
-				streamSession.remove(deviceId, channelId, ssrcTransaction.getStream());
-			}
+			mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
+			mediaServerService.closeRTPServer(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
+			streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
 
 			if (dialog == null) {
-				logger.warn("[ {} -> {}]鍋滄瑙嗛娴佺殑鏃跺�欏彂鐜板璇濆凡涓㈠け", deviceId, channelId);
+				logger.warn("[ {} -> {}]鍋滄瑙嗛娴佺殑鏃跺�欏彂鐜板璇濆凡涓㈠け", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
 				return;
 			}
 			SipStack sipStack = udpSipProvider.getSipStack();
@@ -1456,12 +1455,20 @@
 
 			Request request;
 			if (dialog != null) {
-				logger.info("鍙戦�佺Щ鍔ㄤ綅缃闃呮秷鎭椂 dialog鐨勭姸鎬佷负锛� {}", dialog.getState());
+				SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
 				request = dialog.createRequest(Request.SUBSCRIBE);
+				ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
+				request.setExpires(expiresHeader);
+
+				request.setRequestURI(requestURI);
+
 				ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 				request.setContent(subscribePostitionXml.toString(), contentTypeHeader);
-				ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());
-				request.addHeader(expireHeader);
+
+				CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
+				cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
+				request.removeHeader(CSeqHeader.NAME);
+				request.addHeader(cSeqHeader);
 			}else {
 				String tm = Long.toString(System.currentTimeMillis());
 				CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
@@ -1552,12 +1559,21 @@
 
 			Request request;
 			if (dialog != null) {
-				logger.info("鍙戦�佺洰褰曡闃呮秷鎭椂 dialog鐨勭姸鎬佷负锛� {}", dialog.getState());
+				SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
 				request = dialog.createRequest(Request.SUBSCRIBE);
+				ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
+				request.setExpires(expiresHeader);
+
+				request.setRequestURI(requestURI);
+
 				ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 				request.setContent(cmdXml.toString(), contentTypeHeader);
-				ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());
-				request.addHeader(expireHeader);
+
+				CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
+				cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
+				request.removeHeader(CSeqHeader.NAME);
+				request.addHeader(cSeqHeader);
+
 			}else {
 				String tm = Long.toString(System.currentTimeMillis());
 
@@ -1779,6 +1795,43 @@
 			e.printStackTrace();
 		}
 	}
+	
+	@Override
+	public void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
+		try {
+			Request request = headerProvider.createInfoRequest(device, streamInfo, content);
+			if (request == null) {
+				return;
+			}
+			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);
+			}
+			CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
+			if(errorEvent != null) {
+				sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
+					errorEvent.response(eventResult);
+					sipSubscribe.removeErrorSubscribe(eventResult.callId);
+					sipSubscribe.removeOkSubscribe(eventResult.callId);
+				}));
+			}
+			
+			if(okEvent != null) {
+				sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
+					okEvent.response(eventResult);
+					sipSubscribe.removeOkSubscribe(eventResult.callId);
+					sipSubscribe.removeErrorSubscribe(eventResult.callId);
+				});
+			}
+			clientTransaction.sendRequest();
+			
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+		}
+	}
 
 	@Override
 	public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
index a4fd507..2e70ea7 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -31,6 +31,7 @@
 import javax.sip.header.*;
 import javax.sip.message.Request;
 import java.lang.reflect.Field;
+import java.net.InetAddress;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -276,8 +277,8 @@
                         catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n");
                         catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
                         catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
-                        catalogXml.append("<Longitude>" + channel.getLongitude() + "</Longitude>\r\n");
-                        catalogXml.append("<Latitude>" + channel.getLatitude() + "</Latitude>\r\n");
+                        catalogXml.append("<Longitude>" + channel.getLongitudeWgs84() + "</Longitude>\r\n");
+                        catalogXml.append("<Latitude>" + channel.getLatitudeWgs84() + "</Latitude>\r\n");
                         catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
                         catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
                         catalogXml.append("<Info>\r\n");
@@ -546,14 +547,8 @@
         }
         notifyRequest.addHeader(event);
         SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
-        if (subscribeInfo.getTransaction() != null) {
-            SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
-            sipURI.setHost(request.getRemoteAddress().getHostAddress());
-            sipURI.setPort(request.getRemotePort());
-        }else {
-            sipURI.setHost(parentPlatform.getServerIP());
-            sipURI.setPort(parentPlatform.getServerPort());
-        }
+        sipURI.setHost(parentPlatform.getServerIP());
+        sipURI.setPort(parentPlatform.getServerPort());
 
         ClientTransaction transaction = null;
         if ("TCP".equals(parentPlatform.getTransport())) {
@@ -751,6 +746,82 @@
     }
 
     @Override
+    public boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) {
+        if (sendRtpItem == null) {
+            return false;
+        }
+        if (platform == null) {
+            return false;
+        }
+
+        byte[] dialogByteArray = sendRtpItem.getDialog();
+        if (dialogByteArray == null) {
+            return false;
+        }
+        try{
+            SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
+            SipStack sipStack;
+            if ("TCP".equals(platform.getTransport())) {
+                sipStack = tcpSipProvider.getSipStack();
+            } else {
+                sipStack = udpSipProvider.getSipStack();
+            }
+            SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
+            if (dialog != sipDialog) {
+                dialog = sipDialog;
+            }
+            if ("TCP".equals(platform.getTransport())) {
+                dialog.setSipProvider(tcpSipProvider);
+            } else {
+                dialog.setSipProvider(udpSipProvider);
+            }
+
+            Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
+            sipStackField.setAccessible(true);
+            sipStackField.set(dialog, sipStack);
+            Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
+            eventListenersField.setAccessible(true);
+            eventListenersField.set(dialog, new HashSet<>());
+
+            SIPRequest messageRequest = (SIPRequest)dialog.createRequest(Request.MESSAGE);
+            String characterSet = platform.getCharacterSet();
+            StringBuffer mediaStatusXml = new StringBuffer(200);
+            mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+            mediaStatusXml.append("<Notify>\r\n");
+            mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
+            mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+            mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
+            mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
+            mediaStatusXml.append("</Notify>\r\n");
+            ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
+            messageRequest.setContent(mediaStatusXml.toString(), contentTypeHeader);
+            SipURI sipURI = (SipURI) messageRequest.getRequestURI();
+            sipURI.setHost(platform.getServerIP());
+            sipURI.setPort(platform.getServerPort());
+            ClientTransaction clientTransaction;
+            if ("TCP".equals(platform.getTransport())) {
+                clientTransaction = tcpSipProvider.getNewClientTransaction(messageRequest);
+            }else {
+                clientTransaction = udpSipProvider.getNewClientTransaction(messageRequest);
+            }
+            dialog.sendRequest(clientTransaction);
+        } catch (SipException e) {
+            e.printStackTrace();
+            return false;
+        } catch (ParseException e) {
+            e.printStackTrace();
+            return false;
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return true;
+
+
+    }
+
+    @Override
     public void streamByeCmd(ParentPlatform platform, String callId) {
         if (platform == null) {
             return;
@@ -766,45 +837,51 @@
             byte[] dialogByteArray = sendRtpItem.getDialog();
             if (dialogByteArray != null) {
                 SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
-                SipStack sipStack = udpSipProvider.getSipStack();
+                SipStack sipStack;
+                if ("TCP".equals(platform.getTransport())) {
+                    sipStack = tcpSipProvider.getSipStack();
+                } else {
+                    sipStack = udpSipProvider.getSipStack();
+                }
                 SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
                 if (dialog != sipDialog) {
                     dialog = sipDialog;
-                } else {
-                    try {
-                        dialog.setSipProvider(udpSipProvider);
-                        Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
-                        sipStackField.setAccessible(true);
-                        sipStackField.set(dialog, sipStack);
-                        Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
-                        eventListenersField.setAccessible(true);
-                        eventListenersField.set(dialog, new HashSet<>());
-
-                        byte[] transactionByteArray = sendRtpItem.getTransaction();
-                        ClientTransaction clientTransaction = (ClientTransaction) SerializeUtils.deSerialize(transactionByteArray);
-                        Request byeRequest = dialog.createRequest(Request.BYE);
-
-                        SipURI byeURI = (SipURI) byeRequest.getRequestURI();
-                        SIPRequest request = (SIPRequest) clientTransaction.getRequest();
-                        byeURI.setHost(request.getRemoteAddress().getHostAddress());
-                        byeURI.setPort(request.getRemotePort());
-                        if ("TCP".equals(platform.getTransport())) {
-                            clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
-                        } else if ("UDP".equals(platform.getTransport())) {
-                            clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
-                        }
-                        dialog.sendRequest(clientTransaction);
-                    } catch (SipException e) {
-                        e.printStackTrace();
-                    } catch (ParseException e) {
-                        e.printStackTrace();
-                    } catch (NoSuchFieldException e) {
-                        e.printStackTrace();
-                    } catch (IllegalAccessException e) {
-                        e.printStackTrace();
-                    }
-
                 }
+                try {
+                    if ("TCP".equals(platform.getTransport())) {
+                        dialog.setSipProvider(tcpSipProvider);
+                    } else {
+                        dialog.setSipProvider(udpSipProvider);
+                    }
+                    Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
+                    sipStackField.setAccessible(true);
+                    sipStackField.set(dialog, sipStack);
+                    Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
+                    eventListenersField.setAccessible(true);
+                    eventListenersField.set(dialog, new HashSet<>());
+
+                    Request byeRequest = dialog.createRequest(Request.BYE);
+
+                    SipURI byeURI = (SipURI) byeRequest.getRequestURI();
+                    byeURI.setHost(platform.getServerIP());
+                    byeURI.setPort(platform.getServerPort());
+                    ClientTransaction clientTransaction;
+                    if ("TCP".equals(platform.getTransport())) {
+                        clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
+                    } else {
+                        clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
+                    }
+                    dialog.sendRequest(clientTransaction);
+                } catch (SipException e) {
+                    e.printStackTrace();
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                } catch (NoSuchFieldException e) {
+                    e.printStackTrace();
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                }
+
             }
         }
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java
deleted file mode 100644
index dd098f7..0000000
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java
+++ /dev/null
@@ -1,23 +0,0 @@
-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鍙戞潵鐨凷IP鍗忚璇锋眰娑堟伅
- * @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;
-
-}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
index 85ee647..33bee75 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -15,10 +15,13 @@
 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.bean.RequestPushStreamMsg;
+import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.stack.SIPDialog;
+import com.genersoft.iot.vmp.utils.SerializeUtils;
 import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,7 +49,7 @@
 public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
 
 	private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
-	private String method = "ACK";
+	private final String method = "ACK";
 
 	@Autowired
 	private SIPProcessorObserver sipProcessorObserver;
@@ -83,6 +86,9 @@
 
 	@Autowired
 	private AudioBroadcastManager audioBroadcastManager;
+
+	@Autowired
+	private RedisGbPlayMsgListener redisGbPlayMsgListener;
 
 
 	/**   
@@ -159,60 +165,41 @@
 					// 鍚戜笂绾у钩鍙�
 					commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
 				}
+			if (mediaInfo == null) {
+				RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
+						sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(),
+						sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
+						sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
+				redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, jsonObject->{
+					startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
+				});
+			}else {
+				JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+				startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
 			}
 
 
-//			if (streamInfo == null) { // 娴佽繕娌′笂鏉ワ紝瀵规柟灏卞洖澶峚ck
-//				logger.info("鐩戝惉娴佷互绛夊緟娴佷笂绾�1 rtp/{}", sendRtpItem.getStreamId());
-//				// 鐩戝惉娴佷笂绾�
-//				// 娣诲姞璁㈤槄
-//				JSONObject subscribeKey = new JSONObject();
-//				subscribeKey.put("app", "rtp");
-//				subscribeKey.put("stream", sendRtpItem.getStreamId());
-//				subscribeKey.put("regist", true);
-//				subscribeKey.put("schema", "rtmp");
-//				subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());
-//				subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
-//						(MediaServerItem mediaServerItemInUse, JSONObject json)->{
-//							Map<String, Object> param = new HashMap<>();
-//							param.put("vhost","__defaultVhost__");
-//							param.put("app",json.getString("app"));
-//							param.put("stream",json.getString("stream"));
-//							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());
-//							zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
-//						});
-//			}else {
-//				Map<String, Object> param = new HashMap<>();
-//				param.put("vhost","__defaultVhost__");
-//				param.put("app",streamInfo.getApp());
-//				param.put("stream",streamInfo.getStream());
-//				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());
-//
-//				JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
-//				if (jsonObject.getInteger("code") != 0) {
-//					logger.info("鐩戝惉娴佷互绛夊緟娴佷笂绾�2 {}/{}", streamInfo.getApp(), streamInfo.getStream());
-//					// 鐩戝惉娴佷笂绾�
-//					// 娣诲姞璁㈤槄
-//					JSONObject subscribeKey = new JSONObject();
-//					subscribeKey.put("app", "rtp");
-//					subscribeKey.put("stream", streamInfo.getStream());
-//					subscribeKey.put("regist", true);
-//					subscribeKey.put("schema", "rtmp");
-//					subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());
-//					subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
-//							(MediaServerItem mediaServerItemInUse, JSONObject json)->{
-//								zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
-//							});
-//				}
-//			}
+		}
+	}
+	private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
+										JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
+		if (jsonObject == null) {
+			logger.error("RTP鎺ㄦ祦澶辫触: 璇锋鏌LM鏈嶅姟");
+		} else if (jsonObject.getInteger("code") == 0) {
+			logger.info("RTP鎺ㄦ祦鎴愬姛[ {}/{} ]锛寋}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
+			byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
+			sendRtpItem.setDialog(dialogByteArray);
+			byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
+			sendRtpItem.setTransaction(transactionByteArray);
+			redisCatchStorage.updateSendRTPSever(sendRtpItem);
+		} else {
+			logger.error("RTP鎺ㄦ祦澶辫触: {}, 鍙傛暟锛歿}",jsonObject.getString("msg"),JSONObject.toJSON(param));
+			if (sendRtpItem.isOnlyAudio()) {
+				// TODO 鍙兘鏄闊冲璁�
+			}else {
+				// 鍚戜笂绾у钩鍙�
+				commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
+			}
 		}
 	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
index 7944787..628bc15 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -114,13 +114,9 @@
 							playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
 						}
 						if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
-							MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
-							messageForPushChannel.setType(0);
-							messageForPushChannel.setGbId(sendRtpItem.getChannelId());
-							messageForPushChannel.setApp(sendRtpItem.getApp());
-							messageForPushChannel.setStream(sendRtpItem.getStreamId());
-							messageForPushChannel.setMediaServerId(sendRtpItem.getMediaServerId());
-							messageForPushChannel.setPlatFormId(sendRtpItem.getPlatformId());
+							MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
+									sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
+									sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
 							redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
 						}
 					}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java
index 0a818ee..b04352a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java
@@ -15,7 +15,7 @@
 @Component
 public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
 
-	private String method = "CANCEL";
+	private final String method = "CANCEL";
 
 	@Autowired
 	private SIPProcessorObserver sipProcessorObserver;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
index eabfd1a..095f5e2 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -26,11 +26,14 @@
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItemLite;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlayService;
+import com.genersoft.iot.vmp.service.IStreamPushService;
 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
+import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
@@ -66,33 +69,42 @@
 @Component
 public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
 
-	private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class);
+    private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class);
 
-	private String method = "INVITE";
+    private final String method = "INVITE";
 
-	@Autowired
-	private SIPCommanderFroPlatform cmderFroPlatform;
+    @Autowired
+    private SIPCommanderFroPlatform cmderFroPlatform;
 
-	@Autowired
-	private IVideoManagerStorage storager;
+    @Autowired
+    private IVideoManagerStorage storager;
 
-	@Autowired
-	private IRedisCatchStorage  redisCatchStorage;
+    @Autowired
+    private IStreamPushService streamPushService;
 
-	@Autowired
-	private DynamicTask dynamicTask;
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
 
-	@Autowired
-	private SIPCommander cmder;
+    @Autowired
+    private DynamicTask dynamicTask;
 
-	@Autowired
-	private IPlayService playService;
+    @Autowired
+    private SIPCommander cmder;
 
+    @Autowired
+    private IPlayService playService;
+
+    @Autowired
+    private ISIPCommander commander;
+	
 	@Autowired
 	private AudioBroadcastManager audioBroadcastManager;
 
-	@Autowired
-	private ZLMRTPServerFactory zlmrtpServerFactory;
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
 
 	@Autowired
 	private ZLMRESTfulUtils zlmresTfulUtils;
@@ -100,17 +112,17 @@
 	@Autowired
 	private IMediaServerService mediaServerService;
 
-	@Autowired
-	private SIPProcessorObserver sipProcessorObserver;
+    @Autowired
+    private SIPProcessorObserver sipProcessorObserver;
 
-	@Autowired
-	private VideoStreamSessionManager sessionManager;
+    @Autowired
+    private VideoStreamSessionManager sessionManager;
 
-	@Autowired
-	private UserSetting userSetting;
+    @Autowired
+    private UserSetting userSetting;
 
-	@Autowired
-	private ZLMMediaListManager mediaListManager;
+    @Autowired
+    private ZLMMediaListManager mediaListManager;
 
 	@Autowired
 	private DeferredResultHolder resultHolder;
@@ -123,542 +135,664 @@
 
 
 
-	@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();
-			//浠巗ubject璇诲彇channelId,涓嶅啀浠巖equest-line璇诲彇銆� 鏈変簺骞冲彴request-line鏄钩鍙板浗鏍囩紪鐮侊紝涓嶆槸璁惧鍥芥爣缂栫爜銆�
-			//String channelId = sipURI.getUser();
-			String channelId = SipUtils.getChannelIdFromHeader(request);
-			String requesterId = SipUtils.getUserIdFromFromHeader(request);
-			CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
-			if (requesterId == null || channelId == null) {
-				logger.info("鏃犳硶浠嶧romHeader鐨凙ddress涓幏鍙栧埌骞冲彴id锛岃繑鍥�400");
-				responseAck(evt, Response.BAD_REQUEST); // 鍙傛暟涓嶅叏锛� 鍙�400锛岃姹傞敊璇�
-				return;
-			}
-
-			// 鏌ヨ璇锋眰鏄惁鏉ヨ嚜涓婄骇骞冲彴\璁惧
-			ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
-			if (platform == null) {
-				inviteFromDeviceHandle(evt, requesterId, channelId);
-			}else {
-				// 鏌ヨ骞冲彴涓嬫槸鍚︽湁璇ラ�氶亾
-				DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
-				GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
-				PlatformCatalog catalog = storager.getCatalog(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={} ]鎵句笉鍒皕lm {}锛岃繑鍥�410",gbStream.getApp(), gbStream.getStream(), mediaServerId);
-						responseAck(evt, Response.GONE);
-						return;
-					}
-					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 閫氶亾瀛樺湪锛屽彂181锛屽懠鍙浆鎺ヤ腑
-				}else if (catalog != null) {
-					responseAck(evt, Response.BAD_REQUEST, "catalog channel can not play"); // 鐩綍涓嶆敮鎸佺偣鎾�
-					return;
-				} else {
-					logger.info("閫氶亾涓嶅瓨鍦紝杩斿洖404");
-					responseAck(evt, Response.NOT_FOUND); // 閫氶亾涓嶅瓨鍦紝鍙�404锛岃祫婧愪笉瀛樺湪
-					return;
-				}
-				// 瑙f瀽sdp娑堟伅, 浣跨敤jainsip 鑷甫鐨剆dp瑙f瀽鏂瑰紡
-				String contentString = new String(request.getRawContent());
-
-				// jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎浠ヨВ鏋愩��
-				int ssrcIndex = contentString.indexOf("y=");
-				// 妫�鏌ユ槸鍚︽湁y瀛楁
-				String ssrcDefault = "0000000000";
-				String ssrc;
-				SessionDescription sdp;
-				if (ssrcIndex >= 0) {
-					//ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈�
-					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);
-				}
-				String sessionName = sdp.getSessionName().getValue();
-
-				Long startTime = null;
-				Long stopTime = null;
-				Instant start = null;
-				Instant end = null;
-				if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) {
-					TimeDescriptionImpl timeDescription = (TimeDescriptionImpl)(sdp.getTimeDescriptions(false).get(0));
-					TimeField startTimeFiled = (TimeField)timeDescription.getTime();
-					startTime = startTimeFiled.getStartTime();
-					stopTime = startTimeFiled.getStopTime();
-
-					start = Instant.ofEpochMilli(startTime*1000);
-					end = Instant.ofEpochMilli(stopTime*1000);
-				}
-				//  鑾峰彇鏀寔鐨勬牸寮�
-				Vector mediaDescriptions = sdp.getMediaDescriptions(true);
-				// 鏌ョ湅鏄惁鏀寔PS 璐熻浇96
-				//String ip = null;
-				int port = -1;
-				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;
-									// 涓嶆敮鎸乼cp涓诲姩
-									responseAck(evt, Response.NOT_IMPLEMENTED, "tcp active not support"); // 鐩綍涓嶆敮鎸佺偣鎾�
-									return;
-								} 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);
-				Device device  = null;
-				// 閫氳繃 channel 鍜� gbStream 鏄惁涓簄ull 鍊煎垽鏂潵婧愭槸鐩存挱娴佸悎閫傚浗鏍�
-				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("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
-						responseAck(evt, Response.BUSY_HERE);
-						return;
-					}
-					sendRtpItem.setCallId(callIdHeader.getCallId());
-					sendRtpItem.setPlayType("Play".equals(sessionName)?InviteStreamType.PLAY:InviteStreamType.PLAYBACK);
-					byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
-					sendRtpItem.setDialog(dialogByteArray);
-					byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
-					sendRtpItem.setTransaction(transactionByteArray);
-					Long finalStartTime = startTime;
-					Long finalStopTime = stopTime;
-					ZLMHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON)->{
-						String app = responseJSON.getString("app");
-						String stream = responseJSON.getString("stream");
-						logger.info("[涓婄骇鐐规挱]涓嬬骇宸茬粡寮�濮嬫帹娴併�� 鍥炲200OK(SDP)锛� {}/{}", app, stream);
-						//     * 0 绛夊緟璁惧鎺ㄦ祦涓婃潵
-						//     * 1 涓嬬骇宸茬粡鎺ㄦ祦锛岀瓑寰呬笂绾у钩鍙板洖澶峚ck
-						//     * 2 鎺ㄦ祦涓�
-						sendRtpItem.setStatus(1);
-						redisCatchStorage.updateSendRTPSever(sendRtpItem);
-
-						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=" + sessionName+"\r\n");
-						content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
-						if ("Playback".equals(sessionName)) {
-							content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n");
-						}else {
-							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 {
-							// 瓒呮椂鏈敹鍒癆ck搴旇鍥炲bye,褰撳墠绛夊緟鏃堕棿涓�10绉�
-							dynamicTask.startDelay(callIdHeader.getCallId(), ()->{
-								logger.info("Ack 绛夊緟瓒呮椂");
-								mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), ssrc);
-								// 鍥炲bye
-								cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());
-							}, 60*1000);
-							responseSdpAck(evt, content.toString(), platform);
-
-						} catch (SipException e) {
-							e.printStackTrace();
-						} catch (InvalidArgumentException e) {
-							e.printStackTrace();
-						} catch (ParseException e) {
-							e.printStackTrace();
-						}
-					};
-					SipSubscribe.Event errorEvent = ((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();
-						}
-					});
-					sendRtpItem.setApp("rtp");
-					if ("Playback".equals(sessionName)) {
-						sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);
-						SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true);
-						sendRtpItem.setStreamId(ssrcInfo.getStream());
-						// 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
-						redisCatchStorage.updateSendRTPSever(sendRtpItem);
-						playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
-								DateUtil.formatter.format(end), null, result -> {
-								if (result.getCode() != 0){
-									logger.warn("褰曞儚鍥炴斁澶辫触");
-									if (result.getEvent() != null) {
-										errorEvent.response(result.getEvent());
-									}
-									redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
-									try {
-										responseAck(evt, Response.REQUEST_TIMEOUT);
-									} catch (SipException e) {
-										e.printStackTrace();
-									} catch (InvalidArgumentException e) {
-										e.printStackTrace();
-									} catch (ParseException e) {
-										e.printStackTrace();
-									}
-								}else {
-									if (result.getMediaServerItem() != null) {
-										hookEvent.response(result.getMediaServerItem(), result.getResponse());
-									}
-								}
-							});
-					}else {
-						sendRtpItem.setPlayType(InviteStreamType.PLAY);
-						SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
-						if (playTransaction != null) {
-							Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream());
-							if (!streamReady) {
-								playTransaction = null;
-							}
-						}
-						if (playTransaction == null) {
-							String streamId = null;
-							if (mediaServerItem.isRtpEnable()) {
-								streamId = String.format("%s_%s", device.getDeviceId(), channelId);
-							}
-							SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, true, false);
-							sendRtpItem.setStreamId(ssrcInfo.getStream());
-							// 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
-							redisCatchStorage.updateSendRTPSever(sendRtpItem);
-							playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg)->{
-								redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
-							}, null);
-						}else {
-							sendRtpItem.setStreamId(playTransaction.getStream());
-							// 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
-							redisCatchStorage.updateSendRTPSever(sendRtpItem);
-							JSONObject jsonObject = new JSONObject();
-							jsonObject.put("app", sendRtpItem.getApp());
-							jsonObject.put("stream", sendRtpItem.getStreamId());
-							hookEvent.response(mediaServerItem, jsonObject);
-						}
-					}
-				}else if (gbStream != null) {
-
-					Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
-					if (!streamReady ) {
-						if ("proxy".equals(gbStream.getStreamType())) {
-							// TODO 鎺у埗鍚敤浠ヤ娇璁惧涓婄嚎
-							logger.info("[ app={}, stream={} ]閫氶亾绂荤嚎锛屽惎鐢ㄦ祦鍚庡紑濮嬫帹娴�",gbStream.getApp(), gbStream.getStream());
-							responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
-						}else if ("push".equals(gbStream.getStreamType())) {
-							if (!platform.isStartOfflinePush()) {
-								responseAck(evt, Response.TEMPORARILY_UNAVAILABLE, "channel unavailable");
-								return;
-							}
-							// 鍙戦�乺edis娑堟伅浠ヤ娇璁惧涓婄嚎
-							logger.info("[ app={}, stream={} ]閫氶亾绂荤嚎锛屽彂閫乺edis淇℃伅鎺у埗璁惧寮�濮嬫帹娴�",gbStream.getApp(), gbStream.getStream());
-							MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
-							messageForPushChannel.setType(1);
-							messageForPushChannel.setGbId(gbStream.getGbId());
-							messageForPushChannel.setApp(gbStream.getApp());
-							messageForPushChannel.setStream(gbStream.getStream());
-							// TODO 鑾峰彇浣庤礋杞界殑鑺傜偣
-							messageForPushChannel.setMediaServerId(gbStream.getMediaServerId());
-							messageForPushChannel.setPlatFormId(platform.getServerGBId());
-							messageForPushChannel.setPlatFormName(platform.getName());
-							redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
-							// 璁剧疆瓒呮椂
-							dynamicTask.startDelay(callIdHeader.getCallId(), ()->{
-								logger.info("[ app={}, stream={} ] 绛夊緟璁惧寮�濮嬫帹娴佽秴鏃�", gbStream.getApp(), gbStream.getStream());
-								try {
-									mediaListManager.removedChannelOnlineEventLister(gbStream.getGbId());
-									responseAck(evt, Response.REQUEST_TIMEOUT); // 瓒呮椂
-								} catch (SipException e) {
-									e.printStackTrace();
-								} catch (InvalidArgumentException e) {
-									e.printStackTrace();
-								} catch (ParseException e) {
-									e.printStackTrace();
-								}
-							}, userSetting.getPlatformPlayTimeout());
-							// 娣诲姞鐩戝惉
-							MediaServerItem finalMediaServerItem = mediaServerItem;
-							int finalPort = port;
-							boolean finalMediaTransmissionTCP = mediaTransmissionTCP;
-							Boolean finalTcpActive = tcpActive;
-							mediaListManager.addChannelOnlineEventLister(gbStream.getGbId(), (app, stream)->{
-								SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(finalMediaServerItem, addressStr, finalPort, ssrc, requesterId,
-										app, stream, channelId, finalMediaTransmissionTCP);
-
-								if (sendRtpItem == null) {
-									logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
-									try {
-										responseAck(evt, Response.BUSY_HERE);
-									} catch (SipException e) {
-										e.printStackTrace();
-									} catch (InvalidArgumentException e) {
-										e.printStackTrace();
-									} catch (ParseException e) {
-										e.printStackTrace();
-									}
-									return;
-								}
-								if (finalTcpActive != null) {
-									sendRtpItem.setTcpActive(finalTcpActive);
-								}
-								sendRtpItem.setPlayType(InviteStreamType.PUSH);
-								// 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
-								sendRtpItem.setStatus(1);
-								sendRtpItem.setCallId(callIdHeader.getCallId());
-								byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
-								sendRtpItem.setDialog(dialogByteArray);
-								byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
-								sendRtpItem.setTransaction(transactionByteArray);
-								redisCatchStorage.updateSendRTPSever(sendRtpItem);
-								sendStreamAck(finalMediaServerItem, sendRtpItem, platform, evt);
-
-							});
-						}
-					}else {
-						SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
-								gbStream.getApp(), gbStream.getStream(), channelId,
-								mediaTransmissionTCP);
-
-						if (sendRtpItem == null) {
-							logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
-							responseAck(evt, Response.BUSY_HERE);
-							return;
-						}
-						if (tcpActive != null) {
-							sendRtpItem.setTcpActive(tcpActive);
-						}
-						sendRtpItem.setPlayType(InviteStreamType.PUSH);
-						// 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
-						sendRtpItem.setStatus(1);
-						sendRtpItem.setCallId(callIdHeader.getCallId());
-						byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
-						sendRtpItem.setDialog(dialogByteArray);
-						byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
-						sendRtpItem.setTransaction(transactionByteArray);
-						redisCatchStorage.updateSendRTPSever(sendRtpItem);
-						sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);
-					}
+    @Autowired
+    private RedisGbPlayMsgListener redisGbPlayMsgListener;
 
 
-				}
+    @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();
+            //浠巗ubject璇诲彇channelId,涓嶅啀浠巖equest-line璇诲彇銆� 鏈変簺骞冲彴request-line鏄钩鍙板浗鏍囩紪鐮侊紝涓嶆槸璁惧鍥芥爣缂栫爜銆�
+            //String channelId = sipURI.getUser();
+            String channelId = SipUtils.getChannelIdFromHeader(request);
+            String requesterId = SipUtils.getUserIdFromFromHeader(request);
+            CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
+            if (requesterId == null || channelId == null) {
+                logger.info("鏃犳硶浠嶧romHeader鐨凙ddress涓幏鍙栧埌骞冲彴id锛岃繑鍥�400");
+                // 鍙傛暟涓嶅叏锛� 鍙�400锛岃姹傞敊璇�
+                responseAck(evt, Response.BAD_REQUEST);
+                return;
+            }
 
-		} catch (SipException | InvalidArgumentException | ParseException e) {
-			e.printStackTrace();
-			logger.warn("sdp瑙f瀽閿欒");
-			e.printStackTrace();
-		} catch (SdpParseException e) {
-			e.printStackTrace();
-		} catch (SdpException e) {
-			e.printStackTrace();
-		}
-	}
+            // 鏌ヨ璇锋眰鏄惁鏉ヨ嚜涓婄骇骞冲彴\璁惧
+            ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
+            if (platform == null) {
+                inviteFromDeviceHandle(evt, requesterId);
+            } else {
+                // 鏌ヨ骞冲彴涓嬫槸鍚︽湁璇ラ�氶亾
+                DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
+                GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
+                PlatformCatalog catalog = storager.getCatalog(channelId);
 
-	public void sendStreamAck(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt){
+                MediaServerItem mediaServerItem = null;
+                StreamPushItem streamPushItem = 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) {
 
-		StringBuffer content = new StringBuffer(200);
-		content.append("v=0\r\n");
-		content.append("o="+ sendRtpItem.getChannelId() +" 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");
-		if (sendRtpItem.isTcp()) {
-			content.append("a=connection:new\r\n");
-			if (!sendRtpItem.isTcpActive()) {
-				content.append("a=setup:active\r\n");
-			}else {
-				content.append("a=setup:passive\r\n");
-			}
-		}
-		content.append("y="+ sendRtpItem.getSsrc() + "\r\n");
-		content.append("f=\r\n");
+                    String mediaServerId = gbStream.getMediaServerId();
+                    mediaServerItem = mediaServerService.getOne(mediaServerId);
+                    if (mediaServerItem == null) {
+                        if ("proxy".equals(gbStream.getStreamType())) {
+                            logger.info("[ app={}, stream={} ]鎵句笉鍒皕lm {}锛岃繑鍥�410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
+                            responseAck(evt, Response.GONE);
+                            return;
+                        } else {
+                            streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
+                            if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) {
+                                logger.info("[ app={}, stream={} ]鎵句笉鍒皕lm {}锛岃繑鍥�410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
+                                responseAck(evt, Response.GONE);
+                                return;
+                            }
+                        }
+                    } else {
+                        if ("push".equals(gbStream.getStreamType())) {
+                            streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream());
+                            if (streamPushItem == null) {
+                                logger.info("[ app={}, stream={} ]鎵句笉鍒皕lm {}锛岃繑鍥�410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
+                                responseAck(evt, Response.GONE);
+                                return;
+                            }
+                        }
+                    }
+                    responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 閫氶亾瀛樺湪锛屽彂181锛屽懠鍙浆鎺ヤ腑
+                } else if (catalog != null) {
+                    responseAck(evt, Response.BAD_REQUEST, "catalog channel can not play"); // 鐩綍涓嶆敮鎸佺偣鎾�
+                    return;
+                } else {
+                    logger.info("閫氶亾涓嶅瓨鍦紝杩斿洖404");
+                    responseAck(evt, Response.NOT_FOUND); // 閫氶亾涓嶅瓨鍦紝鍙�404锛岃祫婧愪笉瀛樺湪
+                    return;
+                }
+                // 瑙f瀽sdp娑堟伅, 浣跨敤jainsip 鑷甫鐨剆dp瑙f瀽鏂瑰紡
+                String contentString = new String(request.getRawContent());
 
-		try {
-			responseSdpAck(evt, content.toString(), platform);
-		} catch (SipException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			e.printStackTrace();
-		} catch (ParseException e) {
-			e.printStackTrace();
-		}
-	}
+                // jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎浠ヨВ鏋愩��
+                int ssrcIndex = contentString.indexOf("y=");
+                // 妫�鏌ユ槸鍚︽湁y瀛楁
+                String ssrcDefault = "0000000000";
+                String ssrc;
+                SessionDescription sdp;
+                if (ssrcIndex >= 0) {
+                    //ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈�
+                    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);
+                }
+                String sessionName = sdp.getSessionName().getValue();
 
-	public void inviteFromDeviceHandle(RequestEvent evt, String requesterId, String channelId1) throws InvalidArgumentException, ParseException, SipException, SdpException {
+                Long startTime = null;
+                Long stopTime = null;
+                Instant start = null;
+                Instant end = null;
+                if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) {
+                    TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0));
+                    TimeField startTimeFiled = (TimeField) timeDescription.getTime();
+                    startTime = startTimeFiled.getStartTime();
+                    stopTime = startTimeFiled.getStopTime();
 
-		// 闈炰笂绾у钩鍙拌姹傦紝鏌ヨ鏄惁璁惧璇锋眰锛堥�氬父涓烘帴鏀惰闊冲箍鎾殑璁惧锛�
-		Device device = redisCatchStorage.getDevice(requesterId);
-		AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId1);
-		if (audioBroadcastCatch == null) {
-			logger.warn("鏉ヨ嚜璁惧鐨処nvite璇锋眰闈炶闊冲箍鎾紝宸插拷鐣�");
-			responseAck(evt, Response.FORBIDDEN);
-			return;
-		}
-		Request request = evt.getRequest();
-		if (device != null) {
-			logger.info("鏀跺埌璁惧" + requesterId + "鐨勮闊冲箍鎾璉nvite璇锋眰");
-			responseAck(evt, Response.TRYING);
+                    start = Instant.ofEpochSecond(startTime);
+                    end = Instant.ofEpochSecond(stopTime);
+                }
+                //  鑾峰彇鏀寔鐨勬牸寮�
+                Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+                // 鏌ョ湅鏄惁鏀寔PS 璐熻浇96
+                //String ip = null;
+                int port = -1;
+                boolean mediaTransmissionTCP = false;
+                Boolean tcpActive = null;
+                for (Object description : mediaDescriptions) {
+                    MediaDescription mediaDescription = (MediaDescription) description;
+                    Media media = mediaDescription.getMedia();
 
-			String contentString = new String(request.getRawContent());
-			// jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎绉婚櫎浠ヨВ鏋愩��
-			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).trim();
-			}
-			ssrcIndex = substring.indexOf("f=");
-			if (ssrcIndex > 0) {
-				substring = contentString.substring(0, ssrcIndex);
-			}
-			SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
+                    Vector mediaFormats = media.getMediaFormats(false);
+                    if (mediaFormats.contains("96")) {
+                        port = media.getMediaPort();
+                        //String mediaType = media.getMediaType();
+                        String protocol = media.getProtocol();
 
-			//  鑾峰彇鏀寔鐨勬牸寮�
-			Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+                        // 鍖哄垎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;
+                                    // 涓嶆敮鎸乼cp涓诲姩
+                                    responseAck(evt, Response.NOT_IMPLEMENTED, "tcp active not support"); // 鐩綍涓嶆敮鎸佺偣鎾�
+                                    return;
+                                } 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();
 
-			// 鏌ョ湅鏄惁鏀寔PS 璐熻浇96
-			int port = -1;
-			boolean mediaTransmissionTCP = false;
-			Boolean tcpActive = null;
-			for (int i = 0; i < mediaDescriptions.size(); i++) {
-				MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
-				Media media = mediaDescription.getMedia();
+                logger.info("[涓婄骇鐐规挱]鐢ㄦ埛锛歿}锛� 閫氶亾锛歿}, 鍦板潃锛歿}:{}锛� ssrc锛歿}", username, channelId, addressStr, port, ssrc);
+                Device device = null;
+                // 閫氳繃 channel 鍜� gbStream 鏄惁涓簄ull 鍊煎垽鏂潵婧愭槸鐩存挱娴佸悎閫傚浗鏍�
+                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("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
+                        responseAck(evt, Response.BUSY_HERE);
+                        return;
+                    }
+                    sendRtpItem.setCallId(callIdHeader.getCallId());
+                    sendRtpItem.setPlayType("Play".equals(sessionName) ? InviteStreamType.PLAY : InviteStreamType.PLAYBACK);
 
-				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 addressStr = sdp.getOrigin().getAddress();
-			logger.info("璁惧{}璇锋眰璇煶娴侊紝鍦板潃锛歿}:{}锛宻src锛歿}", requesterId, addressStr, port, ssrc);
+                    Long finalStartTime = startTime;
+                    Long finalStopTime = stopTime;
+                    ZLMHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON) -> {
+                        String app = responseJSON.getString("app");
+                        String stream = responseJSON.getString("stream");
+                        logger.info("[涓婄骇鐐规挱]涓嬬骇宸茬粡寮�濮嬫帹娴併�� 鍥炲200OK(SDP)锛� {}/{}", app, stream);
+                        //     * 0 绛夊緟璁惧鎺ㄦ祦涓婃潵
+                        //     * 1 涓嬬骇宸茬粡鎺ㄦ祦锛岀瓑寰呬笂绾у钩鍙板洖澶峚ck
+                        //     * 2 鎺ㄦ祦涓�
+                        sendRtpItem.setStatus(1);
+                        redisCatchStorage.updateSendRTPSever(sendRtpItem);
 
-			MediaServerItem 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(), audioBroadcastCatch.getChannelId(),
-					mediaTransmissionTCP);
-			if (sendRtpItem == null) {
-				logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
-				responseAck(evt, Response.BUSY_HERE);
-				return;
-			}
-			sendRtpItem.setTcp(mediaTransmissionTCP);
-			if (tcpActive != null) {
-				sendRtpItem.setTcpActive(tcpActive);
-			}
-			String app = "broadcast";
-			String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId();
+                        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=" + sessionName + "\r\n");
+                        content.append("c=IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n");
+                        if ("Playback".equals(sessionName)) {
+                            content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n");
+                        } else {
+                            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 {
+                            // 瓒呮椂鏈敹鍒癆ck搴旇鍥炲bye,褰撳墠绛夊緟鏃堕棿涓�10绉�
+                            dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
+                                logger.info("Ack 绛夊緟瓒呮椂");
+                                mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), ssrc);
+                                // 鍥炲bye
+                                cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());
+                            }, 60 * 1000);
+                            responseSdpAck(evt, content.toString(), platform);
+
+                        } catch (SipException e) {
+                            e.printStackTrace();
+                        } catch (InvalidArgumentException e) {
+                            e.printStackTrace();
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                        }
+                    };
+                    SipSubscribe.Event errorEvent = ((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();
+                        }
+                    });
+                    sendRtpItem.setApp("rtp");
+                    if ("Playback".equals(sessionName)) {
+                        sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);
+                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true);
+                        sendRtpItem.setStreamId(ssrcInfo.getStream());
+                        // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+                        redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                        playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
+                                DateUtil.formatter.format(end), null, result -> {
+                                    if (result.getCode() != 0) {
+                                        logger.warn("褰曞儚鍥炴斁澶辫触");
+                                        if (result.getEvent() != null) {
+                                            errorEvent.response(result.getEvent());
+                                        }
+                                        redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
+                                        try {
+                                            responseAck(evt, Response.REQUEST_TIMEOUT);
+                                        } catch (SipException e) {
+                                            e.printStackTrace();
+                                        } catch (InvalidArgumentException e) {
+                                            e.printStackTrace();
+                                        } catch (ParseException e) {
+                                            e.printStackTrace();
+                                        }
+                                    } else {
+                                        if (result.getMediaServerItem() != null) {
+                                            hookEvent.response(result.getMediaServerItem(), result.getResponse());
+                                        }
+                                    }
+                                });
+                    } else {
+                        sendRtpItem.setPlayType(InviteStreamType.PLAY);
+                        SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
+                        if (playTransaction != null) {
+                            Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream());
+                            if (!streamReady) {
+                                playTransaction = null;
+                            }
+                        }
+                        if (playTransaction == null) {
+                            String streamId = null;
+                            if (mediaServerItem.isRtpEnable()) {
+                                streamId = String.format("%s_%s", device.getDeviceId(), channelId);
+                            }
+                            SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false);
+                            sendRtpItem.setStreamId(ssrcInfo.getStream());
+                            // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+                            redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                            playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
+                                logger.info("[涓婄骇鐐规挱]瓒呮椂, 鐢ㄦ埛锛歿}锛� 閫氶亾锛歿}", username, channelId);
+                                redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
+                            }, null);
+                        } else {
+                            sendRtpItem.setStreamId(playTransaction.getStream());
+                            // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+                            redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                            JSONObject jsonObject = new JSONObject();
+                            jsonObject.put("app", sendRtpItem.getApp());
+                            jsonObject.put("stream", sendRtpItem.getStreamId());
+                            hookEvent.response(mediaServerItem, jsonObject);
+                        }
+                    }
+                } else if (gbStream != null) {
+                    if (streamPushItem.isStatus()) {
+                        // 鍦ㄧ嚎鐘舵��
+                        pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                                mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+                    } else {
+                        // 涓嶅湪绾� 鎷夎捣
+                        notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                                mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+                    }
+
+                }
+
+            }
+
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            e.printStackTrace();
+            logger.warn("sdp瑙f瀽閿欒");
+            e.printStackTrace();
+        } catch (SdpParseException e) {
+            e.printStackTrace();
+        } catch (SdpException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 瀹夋帓鎺ㄦ祦
+     */
+
+    private void pushStream(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
+                            CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                            int port, Boolean tcpActive, boolean mediaTransmissionTCP,
+                            String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
+        // 鎺ㄦ祦
+        if (streamPushItem.getServerId().equals(userSetting.getServerId())) {
+            Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
+            if (streamReady) {
+                // 鑷钩鍙板唴瀹�
+                SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
+                        gbStream.getApp(), gbStream.getStream(), channelId,
+                        mediaTransmissionTCP);
+
+                if (sendRtpItem == null) {
+                    logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
+                    responseAck(evt, Response.BUSY_HERE);
+                    return;
+                }
+                if (tcpActive != null) {
+                    sendRtpItem.setTcpActive(tcpActive);
+                }
+                sendRtpItem.setPlayType(InviteStreamType.PUSH);
+                // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+                sendRtpItem.setStatus(1);
+                sendRtpItem.setCallId(callIdHeader.getCallId());
+                byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
+                sendRtpItem.setDialog(dialogByteArray);
+                byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
+                sendRtpItem.setTransaction(transactionByteArray);
+                redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);
+            } else {
+                // 涓嶅湪绾� 鎷夎捣
+                notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                        mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+            }
+
+        } else {
+            // 鍏朵粬骞冲彴鍐呭
+            otherWvpPushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                    mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+        }
+
+    }
+
+    /**
+     * 閫氱煡娴佷笂绾�
+     */
+    private void notifyStreamOnline(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
+                                    CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                                    int port, Boolean tcpActive, boolean mediaTransmissionTCP,
+                                    String channelId, String addressStr, String ssrc, String requesterId) throws InvalidArgumentException, ParseException, SipException {
+        if ("proxy".equals(gbStream.getStreamType())) {
+            // TODO 鎺у埗鍚敤浠ヤ娇璁惧涓婄嚎
+            logger.info("[ app={}, stream={} ]閫氶亾绂荤嚎锛屽惎鐢ㄦ祦鍚庡紑濮嬫帹娴�", gbStream.getApp(), gbStream.getStream());
+            responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
+        } else if ("push".equals(gbStream.getStreamType())) {
+            if (!platform.isStartOfflinePush()) {
+                responseAck(evt, Response.TEMPORARILY_UNAVAILABLE, "channel unavailable");
+                return;
+            }
+            // 鍙戦�乺edis娑堟伅浠ヤ娇璁惧涓婄嚎
+            logger.info("[ app={}, stream={} ]閫氶亾绂荤嚎锛屽彂閫乺edis淇℃伅鎺у埗璁惧寮�濮嬫帹娴�", gbStream.getApp(), gbStream.getStream());
+
+            MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1,
+                    gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(),
+                    platform.getName(), null, gbStream.getMediaServerId());
+            redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
+            // 璁剧疆瓒呮椂
+            dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
+                logger.info("[ app={}, stream={} ] 绛夊緟璁惧寮�濮嬫帹娴佽秴鏃�", gbStream.getApp(), gbStream.getStream());
+                try {
+                    mediaListManager.removedChannelOnlineEventLister(gbStream.getGbId());
+                    responseAck(evt, Response.REQUEST_TIMEOUT); // 瓒呮椂
+                } catch (SipException e) {
+                    e.printStackTrace();
+                } catch (InvalidArgumentException e) {
+                    e.printStackTrace();
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                }
+            }, userSetting.getPlatformPlayTimeout());
+            // 娣诲姞鐩戝惉
+            int finalPort = port;
+            Boolean finalTcpActive = tcpActive;
+
+            // 娣诲姞鍦ㄦ湰鏈轰笂绾跨殑閫氱煡
+            mediaListManager.addChannelOnlineEventLister(gbStream.getGbId(), (app, stream, serverId) -> {
+                dynamicTask.stop(callIdHeader.getCallId());
+                if (serverId.equals(userSetting.getServerId())) {
+                    SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
+                            app, stream, channelId, mediaTransmissionTCP);
+
+                    if (sendRtpItem == null) {
+                        logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
+                        try {
+                            responseAck(evt, Response.BUSY_HERE);
+                        } catch (SipException e) {
+                            e.printStackTrace();
+                        } catch (InvalidArgumentException e) {
+                            e.printStackTrace();
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                        }
+                        return;
+                    }
+                    if (finalTcpActive != null) {
+                        sendRtpItem.setTcpActive(finalTcpActive);
+                    }
+                    sendRtpItem.setPlayType(InviteStreamType.PUSH);
+                    // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+                    sendRtpItem.setStatus(1);
+                    sendRtpItem.setCallId(callIdHeader.getCallId());
+                    byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
+                    sendRtpItem.setDialog(dialogByteArray);
+                    byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
+                    sendRtpItem.setTransaction(transactionByteArray);
+                    redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                    sendStreamAck(mediaServerItem, sendRtpItem, platform, evt);
+                } else {
+                    // 鍏朵粬骞冲彴鍐呭
+                    otherWvpPushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                            mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+                }
+            });
+        }
+    }
+
+    /**
+     * 鏉ヨ嚜鍏朵粬wvp鐨勬帹娴�
+     */
+    private void otherWvpPushStream(RequestEvent evt, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
+                                    CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
+                                    int port, Boolean tcpActive, boolean mediaTransmissionTCP,
+                                    String channelId, String addressStr, String ssrc, String requesterId) {
+        logger.info("[绾ц仈鐐规挱]鐩存挱娴佹潵鑷叾浠栧钩鍙帮紝鍙戦�乺edis娑堟伅");
+        // 鍙戦�乺edis娑堟伅
+        redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(),
+                streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId,
+                channelId, mediaTransmissionTCP, null, responseSendItemMsg -> {
+                    SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem();
+                    if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
+                        logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
+                        try {
+                            responseAck(evt, Response.BUSY_HERE);
+                        } catch (SipException e) {
+                            e.printStackTrace();
+                        } catch (InvalidArgumentException e) {
+                            e.printStackTrace();
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                        }
+                        return;
+                    }
+                    // 鏀跺埌sendItem
+                    if (tcpActive != null) {
+                        sendRtpItem.setTcpActive(tcpActive);
+                    }
+                    sendRtpItem.setPlayType(InviteStreamType.PUSH);
+                    // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+                    sendRtpItem.setStatus(1);
+                    sendRtpItem.setCallId(callIdHeader.getCallId());
+                    byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
+                    sendRtpItem.setDialog(dialogByteArray);
+                    byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
+                    sendRtpItem.setTransaction(transactionByteArray);
+                    redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                    sendStreamAck(responseSendItemMsg.getMediaServerItem(), sendRtpItem, platform, evt);
+                }, (wvpResult) -> {
+                    try {
+                        // 閿欒
+                        if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) {
+                            // 绂荤嚎
+                            // 鏌ヨ鏄惁鍦ㄦ湰鏈轰笂绾夸簡
+                            StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
+                            if (currentStreamPushItem.isStatus()) {
+                                // 鍦ㄧ嚎鐘舵��
+                                pushStream(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                                        mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+
+                            } else {
+                                // 涓嶅湪绾� 鎷夎捣
+                                notifyStreamOnline(evt, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                                        mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+                            }
+                        }
+                    } catch (InvalidArgumentException e) {
+                        throw new RuntimeException(e);
+                    } catch (ParseException e) {
+                        throw new RuntimeException(e);
+                    } catch (SipException e) {
+                        throw new RuntimeException(e);
+                    }
+
+
+                    try {
+                        responseAck(evt, Response.BUSY_HERE);
+                    } catch (SipException e) {
+                        e.printStackTrace();
+                    } catch (InvalidArgumentException e) {
+                        e.printStackTrace();
+                    } catch (ParseException e) {
+                        e.printStackTrace();
+                    }
+                    return;
+                });
+    }
+
+    public void sendStreamAck(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
+
+        StringBuffer content = new StringBuffer(200);
+        content.append("v=0\r\n");
+        content.append("o=" + sendRtpItem.getChannelId() + " 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");
+        if (sendRtpItem.isTcp()) {
+            content.append("a=connection:new\r\n");
+            if (!sendRtpItem.isTcpActive()) {
+                content.append("a=setup:active\r\n");
+            } else {
+                content.append("a=setup:passive\r\n");
+            }
+        }
+        content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
+        content.append("f=\r\n");
+
+        try {
+            responseSdpAck(evt, content.toString(), platform);
+        } catch (SipException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void inviteFromDeviceHandle(RequestEvent evt, String requesterId) throws InvalidArgumentException, ParseException, SipException, SdpException {
+
+        // 闈炰笂绾у钩鍙拌姹傦紝鏌ヨ鏄惁璁惧璇锋眰锛堥�氬父涓烘帴鏀惰闊冲箍鎾殑璁惧锛�
+        Device device = redisCatchStorage.getDevice(requesterId);
+        Request request = evt.getRequest();
+        if (device != null) {
+            logger.info("鏀跺埌璁惧" + requesterId + "鐨勮闊冲箍鎾璉nvite璇锋眰");
+            responseAck(evt, Response.TRYING);
+
+            String contentString = new String(request.getRawContent());
+            // jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎绉婚櫎浠ヨВ鏋愩��
+            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("璁惧{}璇锋眰璇煶娴侊紝鍦板潃锛歿}:{}锛宻src锛歿}", username, addressStr, port, ssrc);
+
+        } else {
+            logger.warn("鏉ヨ嚜鏃犳晥璁惧/骞冲彴鐨勮姹�");
+            responseAck(evt, Response.BAD_REQUEST);
+        }
+    }
 			CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
 			sendRtpItem.setPlayType(InviteStreamType.PLAY);
 			sendRtpItem.setCallId(callIdHeader.getCallId());
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
index e923a54..7e66658 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -18,6 +17,7 @@
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
@@ -25,6 +25,8 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
@@ -35,6 +37,7 @@
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.util.Iterator;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * SIP鍛戒护绫诲瀷锛� NOTIFY璇锋眰
@@ -63,10 +66,18 @@
 	@Autowired
 	private EventPublisher publisher;
 
-	private String method = "NOTIFY";
+	private final String method = "NOTIFY";
 
 	@Autowired
 	private SIPProcessorObserver sipProcessorObserver;
+
+	private boolean taskQueueHandlerRun = false;
+
+	private final ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
+
+	@Qualifier("taskExecutor")
+	@Autowired
+	private ThreadPoolTaskExecutor taskExecutor;
 
 	@Override
 	public void afterPropertiesSet() throws Exception {
@@ -77,23 +88,40 @@
 	@Override
 	public void process(RequestEvent evt) {
 		try {
-			Element rootElement = getRootElement(evt);
-			String cmd = XmlUtil.getText(rootElement, "CmdType");
 
-			if (CmdType.CATALOG.equals(cmd)) {
-				logger.info("鎺ユ敹鍒癈atalog閫氱煡");
-				processNotifyCatalogList(evt);
-			} else if (CmdType.ALARM.equals(cmd)) {
-				logger.info("鎺ユ敹鍒癆larm閫氱煡");
-				processNotifyAlarm(evt);
-			} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
-				logger.info("鎺ユ敹鍒癕obilePosition閫氱煡");
-				processNotifyMobilePosition(evt);
-			} else {
-				logger.info("鎺ユ敹鍒版秷鎭細" + cmd);
-				responseAck(evt, Response.OK);
+			taskQueue.offer(new HandlerCatchData(evt, null, null));
+			responseAck(evt, Response.OK);
+			if (!taskQueueHandlerRun) {
+				taskQueueHandlerRun = true;
+				taskExecutor.execute(()-> {
+							while (!taskQueue.isEmpty()) {
+								try {
+									HandlerCatchData take = taskQueue.poll();
+									Element rootElement = getRootElement(take.getEvt());
+									String cmd = XmlUtil.getText(rootElement, "CmdType");
+
+									if (CmdType.CATALOG.equals(cmd)) {
+										logger.info("鎺ユ敹鍒癈atalog閫氱煡");
+										processNotifyCatalogList(take.getEvt());
+									} else if (CmdType.ALARM.equals(cmd)) {
+										logger.info("鎺ユ敹鍒癆larm閫氱煡");
+										processNotifyAlarm(take.getEvt());
+									} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
+										logger.info("鎺ユ敹鍒癕obilePosition閫氱煡");
+										processNotifyMobilePosition(take.getEvt());
+									} else {
+										logger.info("鎺ユ敹鍒版秷鎭細" + cmd);
+									}
+								} catch (DocumentException e) {
+									throw new RuntimeException(e);
+								}
+							}
+						taskQueueHandlerRun = false;
+						});
 			}
-		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+
+
+		} catch (SipException | InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
 		}
 	}
@@ -166,8 +194,7 @@
 			jsonObject.put("direction", mobilePosition.getDirection());
 			jsonObject.put("speed", mobilePosition.getSpeed());
 			redisCatchStorage.sendMobilePositionMsg(jsonObject);
-			responseAck(evt, Response.OK);
-		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+		} catch (DocumentException  e) {
 			e.printStackTrace();
 		}
 	}
@@ -188,6 +215,7 @@
 
 			Device device = redisCatchStorage.getDevice(deviceId);
 			if (device == null) {
+				logger.warn("[ NotifyAlarm ] 鏈壘鍒拌澶囷細{}", deviceId);
 				return;
 			}
 			rootElement = getRootElement(evt, device.getCharset());
@@ -195,7 +223,12 @@
 			deviceAlarm.setDeviceId(deviceId);
 			deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
 			deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
-			deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
+			String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
+			if (alarmTime == null) {
+				logger.warn("[ NotifyAlarm ] AlarmTime cannot be null");
+				return;
+			}
+			deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
 			if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
 				deviceAlarm.setAlarmDescription("");
 			} else {
@@ -212,7 +245,7 @@
 				deviceAlarm.setLatitude(0.00);
 			}
 			logger.info("[鏀跺埌Notify-Alarm]锛歿}/{}", device.getDeviceId(), deviceAlarm.getChannelId());
-			if (deviceAlarm.getAlarmMethod().equals("4")) {
+			if ("4".equals(deviceAlarm.getAlarmMethod())) {
 				MobilePosition mobilePosition = new MobilePosition();
 				mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
 				mobilePosition.setTime(deviceAlarm.getAlarmTime());
@@ -233,11 +266,10 @@
 			// TODO: 闇�瑕佸疄鐜板瓨鍌ㄦ姤璀︿俊鎭�佹姤璀﹀垎绫�
 
 			// 鍥炲200 OK
-			responseAck(evt, Response.OK);
 			if (redisCatchStorage.deviceIsOnline(deviceId)) {
 				publisher.deviceAlarmEventPublish(deviceAlarm);
 			}
-		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+		} catch (DocumentException e) {
 			e.printStackTrace();
 		}
 	}
@@ -273,64 +305,60 @@
 						continue;
 					}
 					Element eventElement = itemDevice.element("Event");
-					DeviceChannel channel = XmlUtil.channelContentHander(itemDevice);
+					String event;
+					if (eventElement == null) {
+						logger.warn("[鏀跺埌 鐩綍璁㈤槄]锛歿}, 浣嗘槸Event涓虹┖, 璁句负榛樿鍊� ADD", (device != null ? device.getDeviceId():"" ));
+						event = CatalogEvent.ADD;
+					}else {
+						event = eventElement.getText().toUpperCase();
+					}
+					DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device);
 					channel.setDeviceId(device.getDeviceId());
 					logger.info("[鏀跺埌 鐩綍璁㈤槄]锛歿}/{}", device.getDeviceId(), channel.getChannelId());
-					switch (eventElement.getText().toUpperCase()) {
+					switch (event) {
 						case CatalogEvent.ON:
 							// 涓婄嚎
 							logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑閫氶亾銆恵}銆戜笂绾块�氱煡", device.getDeviceId(), channel.getChannelId());
 							storager.deviceChannelOnline(deviceId, channel.getChannelId());
-							// 鍥炲200 OK
-							responseAck(evt, Response.OK);
 							break;
 						case CatalogEvent.OFF :
 							// 绂荤嚎
 							logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑閫氶亾銆恵}銆戠绾块�氱煡", device.getDeviceId(), channel.getChannelId());
 							storager.deviceChannelOffline(deviceId, channel.getChannelId());
-							// 鍥炲200 OK
-							responseAck(evt, Response.OK);
 							break;
 						case CatalogEvent.VLOST:
 							// 瑙嗛涓㈠け
 							logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑閫氶亾銆恵}銆戣棰戜涪澶遍�氱煡", device.getDeviceId(), channel.getChannelId());
 							storager.deviceChannelOffline(deviceId, channel.getChannelId());
-							// 鍥炲200 OK
-							responseAck(evt, Response.OK);
 							break;
 						case CatalogEvent.DEFECT:
 							// 鏁呴殰
-							// 鍥炲200 OK
-							responseAck(evt, Response.OK);
 							break;
 						case CatalogEvent.ADD:
 							// 澧炲姞
 							logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑澧炲姞閫氶亾銆恵}銆戦�氱煡", device.getDeviceId(), channel.getChannelId());
 							storager.updateChannel(deviceId, channel);
-							responseAck(evt, Response.OK);
 							break;
 						case CatalogEvent.DEL:
 							// 鍒犻櫎
 							logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑鍒犻櫎閫氶亾銆恵}銆戦�氱煡", device.getDeviceId(), channel.getChannelId());
 							storager.delChannel(deviceId, channel.getChannelId());
-							responseAck(evt, Response.OK);
 							break;
 						case CatalogEvent.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");
+							logger.warn("[ NotifyCatalog ] event not found 锛� {}", event );
 
 					}
 					// 杞彂鍙樺寲淇℃伅
-					eventPublisher.catalogEventPublish(null, channel, eventElement.getText().toUpperCase());
+					eventPublisher.catalogEventPublish(null, channel, event);
 
 				}
 			}
-		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+		} catch (DocumentException e) {
 			e.printStackTrace();
 		}
 	}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
index 47a4e00..c2226a1 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -1,17 +1,13 @@
 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.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.gb28181.auth.DigestServerAuthenticationHelper;
 import com.genersoft.iot.vmp.service.IDeviceService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import gov.nist.javax.sip.RequestEventExt;
 import gov.nist.javax.sip.address.AddressImpl;
@@ -45,19 +41,10 @@
 
     private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
 
-    public String method = "REGISTER";
+    public final String method = "REGISTER";
 
     @Autowired
     private SipConfig sipConfig;
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
-    @Autowired
-    private IVideoManagerStorage storager;
-
-    @Autowired
-    private EventPublisher publisher;
 
     @Autowired
     private SIPProcessorObserver sipProcessorObserver;
@@ -86,7 +73,7 @@
             ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
             Response response = null;
             boolean passwordCorrect = false;
-            // 娉ㄥ唽鏍囧織  0锛氭湭鎼哄甫鎺堟潈澶存垨鑰呭瘑鐮侀敊璇�  1锛氭敞鍐屾垚鍔�   2锛氭敞閿�鎴愬姛
+            // 娉ㄥ唽鏍囧織
             boolean registerFlag = false;
             FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
             AddressImpl address = (AddressImpl) fromHeader.getAddress();
@@ -105,7 +92,6 @@
             // 鏍¢獙瀵嗙爜鏄惁姝g‘
             passwordCorrect = StringUtils.isEmpty(sipConfig.getPassword()) ||
                     new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, sipConfig.getPassword());
-            // 鏈惡甯︽巿鏉冨ご鎴栬�呭瘑鐮侀敊璇� 鍥炲401
 
             if (!passwordCorrect) {
                 // 娉ㄥ唽澶辫触
@@ -154,6 +140,7 @@
                 device = new Device();
                 device.setStreamMode("UDP");
                 device.setCharset("GB2312");
+                device.setGeoCoordSys("WGS84");
                 device.setDeviceId(deviceId);
             }
             device.setIp(received);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
index 548dbde..d9d9479 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
@@ -82,7 +82,6 @@
 	@Override
 	public void process(RequestEvent evt) {
 		Request request = evt.getRequest();
-
 		try {
 			Element rootElement = getRootElement(evt);
 			String cmd = XmlUtil.getText(rootElement, "CmdType");
@@ -176,6 +175,8 @@
 	}
 
 	private void processNotifyCatalogList(RequestEvent evt, Element rootElement) throws SipException {
+
+		System.out.println(evt.getRequest().toString());
 		String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
 		String deviceId = XmlUtil.getText(rootElement, "DeviceID");
 		ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java
new file mode 100644
index 0000000..a1e98f9
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java
@@ -0,0 +1,143 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info;
+
+import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+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.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.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
+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.*;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+@Component
+public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
+
+    private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class);
+
+    private final String method = "INFO";
+
+    @Autowired
+    private SIPProcessorObserver sipProcessorObserver;
+
+    @Autowired
+    private IVideoManagerStorage storage;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private IVideoManagerStorage storager;
+
+    @Autowired
+    private SIPCommander cmder;
+
+    @Autowired
+    private VideoStreamSessionManager sessionManager;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        // 娣诲姞娑堟伅澶勭悊鐨勮闃�
+        sipProcessorObserver.addRequestProcessor(method, this);
+    }
+
+    @Override
+    public void process(RequestEvent evt) {
+        logger.debug("鎺ユ敹鍒版秷鎭細" + evt.getRequest());
+        String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
+        CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
+        // 鍏堜粠浼氳瘽鍐呮煡鎵�
+        SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
+        if (ssrcTransaction != null) { // 鍏煎娴峰悍 濯掍綋閫氱煡 娑堟伅from瀛楁涓嶆槸璁惧ID鐨勯棶棰�
+            deviceId = ssrcTransaction.getDeviceId();
+        }
+        // 鏌ヨ璁惧鏄惁瀛樺湪
+        Device device = redisCatchStorage.getDevice(deviceId);
+        // 鏌ヨ涓婄骇骞冲彴鏄惁瀛樺湪
+        ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
+        try {
+            if (device != null && parentPlatform != null) {
+                logger.warn("[閲嶅]骞冲彴涓庤澶囩紪鍙烽噸澶嶏細{}", deviceId);
+                SIPRequest request = (SIPRequest) evt.getRequest();
+                String hostAddress = request.getRemoteAddress().getHostAddress();
+                int remotePort = request.getRemotePort();
+                if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
+                    parentPlatform = null;
+                }else {
+                    device = null;
+                }
+            }
+            if (device == null && parentPlatform == null) {
+                // 涓嶅瓨鍦ㄥ垯鍥炲404
+                responseAck(evt, Response.NOT_FOUND, "device "+ deviceId +" not found");
+                logger.warn("[璁惧鏈壘鍒� ]锛� {}", deviceId);
+                if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
+                    SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
+                    sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
+                };
+            }else {
+                ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME);
+                String contentType = header.getContentType();
+                String contentSubType = header.getContentSubType();
+                if ("Application".equals(contentType) && "MANSRTSP".equals(contentSubType)) {
+                    SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
+                    String streamId = sendRtpItem.getStreamId();
+                    StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
+                    if (null == streamInfo) {
+                        responseAck(evt, Response.NOT_FOUND, "stream " + streamId + " not found");
+                        return;
+                    }
+                    Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
+                    cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> {
+                        // 澶辫触鐨勫洖澶�
+                        try {
+                            responseAck(evt, eventResult.statusCode, eventResult.msg);
+                        } catch (SipException e) {
+                            e.printStackTrace();
+                        } catch (InvalidArgumentException e) {
+                            e.printStackTrace();
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                        }
+                    }, eventResult -> {
+                        // 鎴愬姛鐨勫洖澶�
+                        try {
+                            responseAck(evt, eventResult.statusCode);
+                        } catch (SipException e) {
+                            e.printStackTrace();
+                        } catch (InvalidArgumentException e) {
+                            e.printStackTrace();
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                        }
+                    });
+                }
+            }
+        } catch (SipException e) {
+            logger.warn("SIP 鍥炲閿欒", e);
+        } catch (InvalidArgumentException e) {
+            logger.warn("鍙傛暟鏃犳晥", e);
+        } catch (ParseException e) {
+            logger.warn("SIP鍥炲鏃惰В鏋愬紓甯�", e);
+        }
+    }
+
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
index 136b912..cd85889 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
@@ -72,7 +72,7 @@
         String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
         CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
         // 鍏堜粠浼氳瘽鍐呮煡鎵�
-        SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, null, callIdHeader.getCallId());
+        SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
         if (ssrcTransaction != null) { // 鍏煎娴峰悍 濯掍綋閫氱煡 娑堟伅from瀛楁涓嶆槸璁惧ID鐨勯棶棰�
             deviceId = ssrcTransaction.getDeviceId();
         }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
index b7e222d..6a23dfa 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
@@ -9,9 +9,11 @@
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.utils.DateUtil;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -84,7 +86,11 @@
         deviceAlarm.setChannelId(channelId);
         deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
         deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
-        deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
+        String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
+        if (alarmTime == null) {
+            return;
+        }
+        deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
         String alarmDescription = getText(rootElement, "AlarmDescription");
         if (alarmDescription == null) {
             deviceAlarm.setAlarmDescription("");
@@ -175,7 +181,11 @@
         deviceAlarm.setChannelId(channelId);
         deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
         deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
-        deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
+        String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
+        if (alarmTime == null) {
+            return;
+        }
+        deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
         String alarmDescription = getText(rootElement, "AlarmDescription");
         if (alarmDescription == null) {
             deviceAlarm.setAlarmDescription("");
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
index cf07250..c429457 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -64,16 +64,14 @@
                 device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
             }
             device.setKeepaliveTime(DateUtil.getNow());
+            // 鍥炲200 OK
+            responseAck(evt, Response.OK);
             if (device.getOnline() == 1) {
-                // 鍥炲200 OK
-                responseAck(evt, Response.OK);
                 deviceService.updateDevice(device);
             }else {
                 // 瀵逛簬宸茬粡绂荤嚎鐨勮澶囧垽鏂粬鐨勬敞鍐屾槸鍚﹀凡缁忚繃鏈�
                 if (!deviceService.expire(device)){
                     deviceService.online(device);
-                    // 鍥炲200 OK
-                    responseAck(evt, Response.OK);
                 }
             }
         } catch (SipException e) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
index 4cf9768..8a5ef79 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
@@ -3,11 +3,16 @@
 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.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 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.notify.NotifyMessageHandler;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,7 +42,16 @@
     private SIPCommander cmder;
 
     @Autowired
+    private SIPCommanderFroPlatform sipCommanderFroPlatform;
+
+    @Autowired
     private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private IVideoManagerStorage storage;
+
+    @Autowired
+    private VideoStreamSessionManager sessionManager;
 
     @Override
     public void afterPropertiesSet() throws Exception {
@@ -59,17 +73,32 @@
         }
         CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
         String NotifyType =getText(rootElement, "NotifyType");
-        if (NotifyType.equals("121")){
+        if ("121".equals(NotifyType)){
             logger.info("[褰曞儚娴乚鎺ㄩ�佸畬姣曪紝鏀跺埌鍏虫祦閫氱煡");
-            String channelId =getText(rootElement, "DeviceID");
             // 鏌ヨ鏄澶�
-            StreamInfo streamInfo = redisCatchStorage.queryDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
-            // 璁剧疆杩涘害100%
-            streamInfo.setProgress(1);
-            redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
-            cmder.streamByeCmd(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
-            // TODO 濡傛灉绾ц仈鎾斁锛岄渶瑕佺粰涓婄骇鍙戦�佹閫氱煡
+            StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId());
+            if (streamInfo != null) {
+                // 璁剧疆杩涘害100%
+                streamInfo.setProgress(1);
+                redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
+            }
 
+            // 鍏堜粠浼氳瘽鍐呮煡鎵�
+            SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
+            if (ssrcTransaction != null) { // 鍏煎娴峰悍 濯掍綋閫氱煡 娑堟伅from瀛楁涓嶆槸璁惧ID鐨勯棶棰�
+                cmder.streamByeCmd(device.getDeviceId(), ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
+
+                // 濡傛灉绾ц仈鎾斁锛岄渶瑕佺粰涓婄骇鍙戦�佹閫氱煡 TODO 澶氫釜涓婄骇鍚屾椂瑙傜湅涓�涓笅绾� 鍙兘瀛樺湪鍋滈敊鐨勯棶棰橈紝闇�瑕佸皢鐐规挱CallId杩涜涓婁笅绾х粦瀹�
+                SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
+                if (sendRtpItem != null) {
+                    ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
+                    if (parentPlatform == null) {
+                        logger.warn("[绾ц仈娑堟伅鍙戦�乚锛氬彂閫丮ediaStatus鍙戠幇涓婄骇骞冲彴{}涓嶅瓨鍦�", sendRtpItem.getPlatformId());
+                        return;
+                    }
+                    sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem);
+                }
+            }
         }
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
index 891b21d..c4e88c5 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
@@ -20,6 +20,8 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
@@ -31,6 +33,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 @Component
 public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -38,8 +41,12 @@
     private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class);
     private final String cmdType = "Catalog";
 
+    private boolean taskQueueHandlerRun = false;
+
     @Autowired
     private ResponseMessageHandler responseMessageHandler;
+
+    private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
 
     @Autowired
     private IVideoManagerStorage storager;
@@ -63,6 +70,10 @@
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
+    @Qualifier("taskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
+
     @Override
     public void afterPropertiesSet() throws Exception {
         responseMessageHandler.addHandler(cmdType, this);
@@ -70,68 +81,88 @@
 
     @Override
     public void handForDevice(RequestEvent evt, Device device, Element element) {
-        String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + device.getDeviceId();
-        Element rootElement = null;
+        taskQueue.offer(new HandlerCatchData(evt, device, element));
+        // 鍥炲200 OK
         try {
-            rootElement = getRootElement(evt, device.getCharset());
-            Element deviceListElement = rootElement.element("DeviceList");
-            Element sumNumElement = rootElement.element("SumNum");
-            Element snElement = rootElement.element("SN");
-            if (snElement == null || sumNumElement == null || deviceListElement == null) {
-                responseAck(evt, Response.BAD_REQUEST, "xml error");
-                return;
-            }
-            int sumNum = Integer.parseInt(sumNumElement.getText());
-
-            if (sumNum == 0) {
-                // 鏁版嵁宸茬粡瀹屾暣鎺ユ敹
-                storager.cleanChannelsForDevice(device.getDeviceId());
-                catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);
-            }else {
-                Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
-                if (deviceListIterator != null) {
-                    List<DeviceChannel> channelList = new ArrayList<>();
-                    // 閬嶅巻DeviceList
-                    while (deviceListIterator.hasNext()) {
-                        Element itemDevice = deviceListIterator.next();
-                        Element channelDeviceElement = itemDevice.element("DeviceID");
-                        if (channelDeviceElement == null) {
-                            continue;
+            responseAck(evt, Response.OK);
+        } catch (SipException e) {
+            throw new RuntimeException(e);
+        } catch (InvalidArgumentException e) {
+            throw new RuntimeException(e);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+        if (!taskQueueHandlerRun) {
+            taskQueueHandlerRun = true;
+            taskExecutor.execute(()-> {
+                while (!taskQueue.isEmpty()) {
+                    HandlerCatchData take = taskQueue.poll();
+                    String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + take.getDevice().getDeviceId();
+                    Element rootElement = null;
+                    try {
+                        rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
+                        Element deviceListElement = rootElement.element("DeviceList");
+                        Element sumNumElement = rootElement.element("SumNum");
+                        Element snElement = rootElement.element("SN");
+                        if (snElement == null || sumNumElement == null || deviceListElement == null) {
+                            responseAck(take.getEvt(), Response.BAD_REQUEST, "xml error");
+                            return;
                         }
-                        //by brewswang
-//                        if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//濡傛灉鍖呭惈浣嶇疆淇℃伅锛屽氨鏇存柊涓�涓嬩綅缃�
-//                            processNotifyMobilePosition(evt, itemDevice);
-//                        }
-                        DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice);
-                        deviceChannel.setDeviceId(device.getDeviceId());
+                        int sumNum = Integer.parseInt(sumNumElement.getText());
 
-                        channelList.add(deviceChannel);
-                    }
-                    int sn = Integer.parseInt(snElement.getText());
-                    catalogDataCatch.put(device.getDeviceId(), sn, sumNum, device, channelList);
-                    logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑閫氶亾: {}涓紝{}/{}", device.getDeviceId(), channelList.size(), catalogDataCatch.get(device.getDeviceId()) == null ? 0 :catalogDataCatch.get(device.getDeviceId()).size(), sumNum);
-                    if (catalogDataCatch.get(device.getDeviceId()).size() == sumNum) {
-                        // 鏁版嵁宸茬粡瀹屾暣鎺ユ敹
-                        boolean resetChannelsResult = storager.resetChannels(device.getDeviceId(), catalogDataCatch.get(device.getDeviceId()));
-                        if (!resetChannelsResult) {
-                            String errorMsg = "鎺ユ敹鎴愬姛锛屽啓鍏ュけ璐ワ紝鍏�" + sumNum + "鏉★紝宸叉帴鏀�" + catalogDataCatch.get(device.getDeviceId()).size() + "鏉�";
-                            catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), errorMsg);
+                        if (sumNum == 0) {
+                            // 鏁版嵁宸茬粡瀹屾暣鎺ユ敹
+                            storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
+                            catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
                         }else {
-                            catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);
+                            Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
+                            if (deviceListIterator != null) {
+                                List<DeviceChannel> channelList = new ArrayList<>();
+                                // 閬嶅巻DeviceList
+                                while (deviceListIterator.hasNext()) {
+                                    Element itemDevice = deviceListIterator.next();
+                                    Element channelDeviceElement = itemDevice.element("DeviceID");
+                                    if (channelDeviceElement == null) {
+                                        continue;
+                                    }
+                                    //by brewswang
+    //                        if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//濡傛灉鍖呭惈浣嶇疆淇℃伅锛屽氨鏇存柊涓�涓嬩綅缃�
+    //                            processNotifyMobilePosition(evt, itemDevice);
+    //                        }
+                                    DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device);
+                                    deviceChannel.setDeviceId(take.getDevice().getDeviceId());
+
+                                    channelList.add(deviceChannel);
+                                }
+                                int sn = Integer.parseInt(snElement.getText());
+                                catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
+                                logger.info("鏀跺埌鏉ヨ嚜璁惧銆恵}銆戠殑閫氶亾: {}涓紝{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
+                                if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
+                                    // 鏁版嵁宸茬粡瀹屾暣鎺ユ敹
+                                    boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
+                                    if (!resetChannelsResult) {
+                                        String errorMsg = "鎺ユ敹鎴愬姛锛屽啓鍏ュけ璐ワ紝鍏�" + sumNum + "鏉★紝宸叉帴鏀�" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "鏉�";
+                                        catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg);
+                                    }else {
+                                        catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
+                                    }
+                                }
+                            }
+
                         }
+                    } catch (DocumentException e) {
+                        e.printStackTrace();
+                    } catch (InvalidArgumentException e) {
+                        e.printStackTrace();
+                    } catch (ParseException e) {
+                        e.printStackTrace();
+                    } catch (SipException e) {
+                        e.printStackTrace();
                     }
                 }
-                // 鍥炲200 OK
-                responseAck(evt, Response.OK);
-            }
-        } catch (DocumentException e) {
-            e.printStackTrace();
-        } catch (InvalidArgumentException e) {
-            e.printStackTrace();
-        } catch (ParseException e) {
-            e.printStackTrace();
-        } catch (SipException e) {
-            e.printStackTrace();
+                taskQueueHandlerRun = false;
+            });
+
         }
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
index 1fed401..17738a5 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
@@ -82,7 +82,7 @@
             deviceService.offline(device.getDeviceId());
         }
         RequestMessage msg = new RequestMessage();
-        msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId);
+        msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
         msg.setData(json);
         deferredResultHolder.invokeAllResult(msg);
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
index 4509e42..57e8045 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
@@ -1,9 +1,6 @@
 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.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -19,6 +16,8 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
@@ -28,6 +27,9 @@
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.util.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 
 import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
 
@@ -38,10 +40,11 @@
 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_";
 
+    private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
+
+    private boolean taskQueueHandlerRun = false;
     @Autowired
     private ResponseMessageHandler responseMessageHandler;
 
@@ -51,10 +54,12 @@
     @Autowired
     private DeferredResultHolder deferredResultHolder;
 
-
-
     @Autowired
     private EventPublisher eventPublisher;
+
+    @Qualifier("taskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
 
     @Override
     public void afterPropertiesSet() throws Exception {
@@ -67,66 +72,88 @@
         // 鍥炲200 OK
         try {
             responseAck(evt, Response.OK);
+            taskQueue.offer(new HandlerCatchData(evt, device, rootElement));
+            if (!taskQueueHandlerRun) {
+                taskQueueHandlerRun = true;
+                taskExecutor.execute(()->{
+                    try {
+                        while (!taskQueue.isEmpty()) {
+                            HandlerCatchData take = taskQueue.poll();
+                            Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset());
+                            String sn = getText(rootElementForCharset, "SN");
+                            String channelId = getText(rootElementForCharset, "DeviceID");
+                            RecordInfo recordInfo = new RecordInfo();
+                            recordInfo.setChannelId(channelId);
+                            recordInfo.setDeviceId(take.getDevice().getDeviceId());
+                            recordInfo.setSn(sn);
+                            recordInfo.setName(getText(rootElementForCharset, "Name"));
+                            String sumNumStr = getText(rootElementForCharset, "SumNum");
+                            int sumNum = 0;
+                            if (!StringUtils.isEmpty(sumNumStr)) {
+                                sumNum = Integer.parseInt(sumNumStr);
+                            }
+                            recordInfo.setSumNum(sumNum);
+                            Element recordListElement = rootElementForCharset.element("RecordList");
+                            if (recordListElement == null || sumNum == 0) {
+                                logger.info("鏃犲綍鍍忔暟鎹�");
+                                eventPublisher.recordEndEventPush(recordInfo);
+                                recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
+                                releaseRequest(take.getDevice().getDeviceId(), sn);
+                            } else {
+                                Iterator<Element> recordListIterator = recordListElement.elementIterator();
+                                if (recordListIterator != null) {
+                                    List<RecordItem> recordList = new ArrayList<>();
+                                    // 閬嶅巻DeviceList
+                                    while (recordListIterator.hasNext()) {
+                                        Element itemRecord = recordListIterator.next();
+                                        Element recordElement = itemRecord.element("DeviceID");
+                                        if (recordElement == null) {
+                                            logger.info("璁板綍涓虹┖锛屼笅涓�涓�...");
+                                            continue;
+                                        }
+                                        RecordItem record = new RecordItem();
+                                        record.setDeviceId(getText(itemRecord, "DeviceID"));
+                                        record.setName(getText(itemRecord, "Name"));
+                                        record.setFilePath(getText(itemRecord, "FilePath"));
+                                        record.setFileSize(getText(itemRecord, "FileSize"));
+                                        record.setAddress(getText(itemRecord, "Address"));
 
-            rootElement = getRootElement(evt, device.getCharset());
-            String sn = getText(rootElement, "SN");
+                                        String startTimeStr = getText(itemRecord, "StartTime");
+                                        record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
 
-            String sumNumStr = getText(rootElement, "SumNum");
-            int sumNum = 0;
-            if (!StringUtils.isEmpty(sumNumStr)) {
-                sumNum = Integer.parseInt(sumNumStr);
-            }
-            Element recordListElement = rootElement.element("RecordList");
-            if (recordListElement == null || sumNum == 0) {
-                logger.info("鏃犲綍鍍忔暟鎹�");
-                recordDataCatch.put(device.getDeviceId(), sn, sumNum, new ArrayList<>());
-                releaseRequest(device.getDeviceId(), sn);
-            } else {
-                Iterator<Element> recordListIterator = recordListElement.elementIterator();
-                if (recordListIterator != null) {
-                    List<RecordItem> recordList = new ArrayList<>();
-                    // 閬嶅巻DeviceList
-                    while (recordListIterator.hasNext()) {
-                        Element itemRecord = recordListIterator.next();
-                        Element recordElement = itemRecord.element("DeviceID");
-                        if (recordElement == null) {
-                            logger.info("璁板綍涓虹┖锛屼笅涓�涓�...");
-                            continue;
+                                        String endTimeStr = getText(itemRecord, "EndTime");
+                                        record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
+
+                                        record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
+                                                : Integer.parseInt(getText(itemRecord, "Secrecy")));
+                                        record.setType(getText(itemRecord, "Type"));
+                                        record.setRecorderId(getText(itemRecord, "RecorderID"));
+                                        recordList.add(record);
+                                    }
+                                    recordInfo.setRecordList(recordList);
+                                    // 鍙戦�佹秷鎭紝濡傛灉鏄笂绾ф煡璇㈡褰曞儚锛屽垯浼氶�氳繃杩欓噷閫氱煡缁欎笂绾�
+                                    eventPublisher.recordEndEventPush(recordInfo);
+                                    int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
+                                    logger.info("[鍥芥爣褰曞儚]锛� {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
+                                }
+
+                                if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
+                                    releaseRequest(take.getDevice().getDeviceId(), sn);
+                                }
+                            }
                         }
-                        RecordItem record = new RecordItem();
-                        record.setDeviceId(getText(itemRecord, "DeviceID"));
-                        record.setName(getText(itemRecord, "Name"));
-                        record.setFilePath(getText(itemRecord, "FilePath"));
-                        record.setFileSize(getText(itemRecord, "FileSize"));
-                        record.setAddress(getText(itemRecord, "Address"));
-
-                        String startTimeStr = getText(itemRecord, "StartTime");
-                        record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
-
-                        String endTimeStr = getText(itemRecord, "EndTime");
-                        record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
-
-                        record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
-                                : Integer.parseInt(getText(itemRecord, "Secrecy")));
-                        record.setType(getText(itemRecord, "Type"));
-                        record.setRecorderId(getText(itemRecord, "RecorderID"));
-                        recordList.add(record);
+                        taskQueueHandlerRun = false;
+                    }catch (DocumentException e) {
+                        throw new RuntimeException(e);
                     }
-                    int count = recordDataCatch.put(device.getDeviceId(), sn, sumNum, recordList);
-                    logger.info("[鍥芥爣褰曞儚]锛� {}->{}: {}/{}", device.getDeviceId(), sn, count, sumNum);
-                }
-
-                if (recordDataCatch.isComplete(device.getDeviceId(), sn)){
-                    releaseRequest(device.getDeviceId(), sn);
-                }
+                });
             }
+
         } catch (SipException e) {
             e.printStackTrace();
         } catch (InvalidArgumentException e) {
             e.printStackTrace();
         } catch (ParseException e) {
-            e.printStackTrace();
-        } catch (DocumentException e) {
             e.printStackTrace();
         }
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java
index 64933b8..ff63fad 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java
@@ -17,7 +17,7 @@
 @Component
 public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
 
-	private String method = "BYE";
+	private final String method = "BYE";
 
 	@Autowired
 	private SipLayer sipLayer;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java
index 80d7e2b..775beeb 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java
@@ -17,7 +17,7 @@
 @Component
 public class CancelResponseProcessor extends SIPResponseProcessorAbstract {
 
-	private String method = "CANCEL";
+	private final String method = "CANCEL";
 
 	@Autowired
 	private SipLayer sipLayer;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
index c81aabb..89958e9 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
@@ -31,7 +31,7 @@
 public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 
 	private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
-	private String method = "INVITE";
+	private final String method = "INVITE";
 
 	@Autowired
 	private SipLayer sipLayer;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
index 02f5e1d..f3a9f65 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
@@ -27,7 +27,7 @@
 public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
 
 	private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
-	private String method = "REGISTER";
+	private final String method = "REGISTER";
 
 	@Autowired
 	private ISIPCommanderForPlatform sipCommanderForPlatform;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
index 2caab0f..5ada1e4 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -2,6 +2,7 @@
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import org.dom4j.Attribute;
 import org.dom4j.Document;
@@ -180,7 +181,7 @@
         return xml.getRootElement();
     }
 
-    public static DeviceChannel channelContentHander(Element itemDevice){
+    public static DeviceChannel channelContentHander(Element itemDevice, Device device){
         Element channdelNameElement = itemDevice.element("Name");
         String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
         Element statusElement = itemDevice.element("Status");
@@ -254,6 +255,8 @@
             }else if (deviceChannel.getChannelId().length() == 20) {
                 if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 铏氭嫙缁勭粐
                     deviceChannel.setParentId(businessGroupID);
+                }else if (Integer.parseInt(device.getDeviceId().substring(10, 13) )== 118) {//NVR 濡傛灉涓婄骇璁惧缂栧彿鏄疦VR鍒欑洿鎺ュ皢NVR鐨勭紪鍙疯缃粰閫氶亾鐨勪笂绾х紪鍙�
+                    deviceChannel.setParentId(device.getDeviceId());
                 }else if (deviceChannel.getCivilCode() != null) {
                     // 璁惧锛� 鏃爌arentId鐨�20浣嶆槸浣跨敤CivilCode琛ㄧず涓婄骇鐨勮澶囷紝
                     // 娉細215 涓氬姟鍒嗙粍鏄渶瑕佹湁parentId鐨�
@@ -308,6 +311,31 @@
         } else {
             deviceChannel.setLatitude(0.00);
         }
+        if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) {
+            if ("WGS84".equals(device.getGeoCoordSys())) {
+                deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
+                deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
+                Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
+                deviceChannel.setLongitudeGcj02(position[0]);
+                deviceChannel.setLatitudeGcj02(position[1]);
+            }else if ("GCJ02".equals(device.getGeoCoordSys())) {
+                deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
+                deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
+                Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
+                deviceChannel.setLongitudeWgs84(position[0]);
+                deviceChannel.setLatitudeWgs84(position[1]);
+            }else {
+                deviceChannel.setLongitudeGcj02(0.00);
+                deviceChannel.setLatitudeGcj02(0.00);
+                deviceChannel.setLongitudeWgs84(0.00);
+                deviceChannel.setLatitudeWgs84(0.00);
+            }
+        }else {
+            deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
+            deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
+            deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
+            deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
+        }
         if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) {
             //鍏煎INFO涓殑淇℃伅
             Element info = itemDevice.element("Info");
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
index 7f62968..4ea9cf1 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -11,7 +11,6 @@
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
-import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.media.zlm.dto.*;
 import com.genersoft.iot.vmp.service.*;
@@ -92,10 +91,9 @@
 	public ResponseEntity<String> onServerKeepalive(@RequestBody JSONObject json){
 
 		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_server_keepalive API璋冪敤锛屽弬鏁帮細" + json.toString());
+			logger.debug("[ ZLM HOOK ] on_server_keepalive API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
 		String mediaServerId = json.getString("mediaServerId");
-
 		List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive);
 		if (subscribes != null  && subscribes.size() > 0) {
 			for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
@@ -165,7 +163,6 @@
 			if (mediaInfo != null) {
 				subscribe.response(mediaInfo, json);
 			}
-
 		}
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
@@ -241,6 +238,23 @@
 		
 		if (logger.isDebugEnabled()) {
 			logger.debug("[ ZLM HOOK ]on_record_mp4 API璋冪敤锛屽弬鏁帮細" + json.toString());
+		}
+		String mediaServerId = json.getString("mediaServerId");
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+	/**
+	 * 褰曞埗hls瀹屾垚鍚庨�氱煡浜嬩欢锛涙浜嬩欢瀵瑰洖澶嶄笉鏁忔劅銆�
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_record_ts", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onRecordTs(@RequestBody JSONObject json){
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("[ ZLM HOOK ]on_record_ts API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
 		String mediaServerId = json.getString("mediaServerId");
 		JSONObject ret = new JSONObject();
@@ -383,21 +397,22 @@
 							if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
 									|| item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
 									|| item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
-								streamPushItem = zlmMediaListManager.addPush(item);
+								item.setSeverId(userSetting.getServerId());
+								zlmMediaListManager.addPush(item);
 							}
 
-							List<GbStream> gbStreams = new ArrayList<>();
-							if (streamPushItem == null || streamPushItem.getGbId() == null) {
-								GbStream gbStream = storager.getGbStream(app, streamId);
-								gbStreams.add(gbStream);
-							}else {
-								if (streamPushItem.getGbId() != null) {
-									gbStreams.add(streamPushItem);
-								}
-							}
-							if (gbStreams.size() > 0) {
+//							List<GbStream> gbStreams = new ArrayList<>();
+//							if (streamPushItem == null || streamPushItem.getGbId() == null) {
+//								GbStream gbStream = storager.getGbStream(app, streamId);
+//								gbStreams.add(gbStream);
+//							}else {
+//								if (streamPushItem.getGbId() != null) {
+//									gbStreams.add(streamPushItem);
+//								}
+//							}
+//							if (gbStreams.size() > 0) {
 //								eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON);
-							}
+//							}
 
 						}else {
 							// 鍏煎娴佹敞閿�鏃剁被鍨嬩粠redis璁板綍鑾峰彇
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
index 9beac16..959c06e 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
@@ -24,6 +24,9 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+/**
+ * @author lin
+ */
 @Component
 public class ZLMMediaListManager {
 
@@ -147,7 +150,6 @@
                     }
                 }
             }
-            //            StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(transform.getApp(), transform.getStream());
             List<GbStream> gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId());
             if (gbStreamList != null && gbStreamList.size() == 1) {
                 transform.setGbStreamId(gbStreamList.get(0).getGbStreamId());
@@ -162,12 +164,11 @@
             }
             if (transform != null) {
                 if (channelOnlineEvents.get(transform.getGbId()) != null)  {
-                    channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream());
+                    channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream(), transform.getServerId());
                     channelOnlineEvents.remove(transform.getGbId());
                 }
             }
         }
-
 
         storager.updateMedia(transform);
         return transform;
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
index 75f55cf..54f148a 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -12,6 +12,7 @@
 
 import java.io.*;
 import java.net.ConnectException;
+import java.net.SocketTimeoutException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -28,6 +29,9 @@
 
     private OkHttpClient getClient(){
         OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
+        //todo 鏆傛椂鍐欐瓒呮椂鏃堕棿 鍧囦负5s
+        httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS);  //璁剧疆杩炴帴瓒呮椂鏃堕棿
+        httpClientBuilder.readTimeout(5,TimeUnit.SECONDS);     //璁剧疆璇诲彇瓒呮椂鏃堕棿
         if (logger.isDebugEnabled()) {
             HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
                 logger.debug("http璇锋眰鍙傛暟锛�" + message);
@@ -47,7 +51,10 @@
             return null;
         }
         String url = String.format("http://%s:%s/index/api/%s",  mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
-        JSONObject responseJSON = null;
+        JSONObject responseJSON = new JSONObject();
+        //-2鑷畾涔夋祦濯掍綋 璋冪敤閿欒鐮�
+        responseJSON.put("code",-2);
+        responseJSON.put("msg","娴佸獟浣撹皟鐢ㄥけ璐�");
 
         FormBody.Builder builder = new FormBody.Builder();
         builder.add("secret",mediaServerItem.getSecret());
@@ -78,11 +85,20 @@
                         response.close();
                         Objects.requireNonNull(response.body()).close();
                     }
-                } catch (ConnectException e) {
-                    logger.error(String.format("杩炴帴ZLM澶辫触: %s, %s", e.getCause().getMessage(), e.getMessage()));
-                    logger.info("璇锋鏌edia閰嶇疆骞剁‘璁LM宸插惎鍔�...");
                 }catch (IOException e) {
                     logger.error(String.format("[ %s ]璇锋眰澶辫触: %s", url, e.getMessage()));
+
+                    if(e instanceof SocketTimeoutException){
+                        //璇诲彇瓒呮椂瓒呮椂寮傚父
+                        logger.error(String.format("璇诲彇ZLM鏁版嵁澶辫触: %s, %s", url, e.getMessage()));
+                    }
+                    if(e instanceof ConnectException){
+                        //鍒ゆ柇杩炴帴寮傚父锛屾垜杩欓噷鏄姤Failed to connect to 10.7.5.144
+                        logger.error(String.format("杩炴帴ZLM澶辫触: %s, %s", url, e.getMessage()));
+                    }
+
+                }catch (Exception e){
+                    logger.error(String.format("璁块棶ZLM澶辫触: %s, %s", url, e.getMessage()));
                 }
             }else {
                 client.newCall(request).enqueue(new Callback(){
@@ -105,8 +121,16 @@
 
                     @Override
                     public void onFailure(@NotNull Call call, @NotNull IOException e) {
-                        logger.error(String.format("杩炴帴ZLM澶辫触: %s, %s", e.getCause().getMessage(), e.getMessage()));
-                        logger.info("璇锋鏌edia閰嶇疆骞剁‘璁LM宸插惎鍔�...");
+                        logger.error(String.format("杩炴帴ZLM澶辫触: %s, %s", call.request().toString(), e.getMessage()));
+
+                        if(e instanceof SocketTimeoutException){
+                            //璇诲彇瓒呮椂瓒呮椂寮傚父
+                            logger.error(String.format("璇诲彇ZLM鏁版嵁澶辫触: %s, %s", call.request().toString(), e.getMessage()));
+                        }
+                        if(e instanceof ConnectException){
+                            //鍒ゆ柇杩炴帴寮傚父锛屾垜杩欓噷鏄姤Failed to connect to 10.7.5.144
+                            logger.error(String.format("杩炴帴ZLM澶辫触: %s, %s", call.request().toString(), e.getMessage()));
+                        }
                     }
                 });
             }
@@ -151,7 +175,7 @@
                         }
 
                     }
-                    File snapFile = new File(targetPath + "/" + fileName);
+                    File snapFile = new File(targetPath + File.separator + fileName);
                     FileOutputStream outStream = new FileOutputStream(snapFile);
 
                     outStream.write(Objects.requireNonNull(response.body()).bytes());
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
index 34918ae..4007a40 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -2,6 +2,7 @@
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import org.slf4j.Logger;
@@ -19,6 +20,9 @@
 
     @Autowired
     private ZLMRESTfulUtils zlmresTfulUtils;
+
+    @Autowired
+    private UserSetting userSetting;
 
     private int[] portRangeArray = new int[2];
 
@@ -87,10 +91,15 @@
         int result = -1;
         // 鏌ヨ姝tp server 鏄惁宸茬粡瀛樺湪
         JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
-        if (rtpInfo != null && rtpInfo.getInteger("code") == 0 && rtpInfo.getBoolean("exist")) {
-            result = rtpInfo.getInteger("local_port");
+        if(rtpInfo.getInteger("code") == 0){
+            if (rtpInfo.getBoolean("exist")) {
+                result = rtpInfo.getInteger("local_port");
+                return result;
+            }
+        }else if(rtpInfo.getInteger("code") == -2){
             return result;
         }
+
         Map<String, Object> param = new HashMap<>();
         // 鎺ㄦ祦绔彛璁剧疆0鍒欎娇鐢ㄩ殢鏈虹鍙�
         param.put("enable_tcp", 1);
@@ -197,6 +206,7 @@
         sendRtpItem.setTcp(tcp);
         sendRtpItem.setApp("rtp");
         sendRtpItem.setLocalPort(localPort);
+        sendRtpItem.setServerId(userSetting.getServerId());
         sendRtpItem.setMediaServerId(serverItem.getId());
         return sendRtpItem;
     }
@@ -238,6 +248,7 @@
         sendRtpItem.setChannelId(channelId);
         sendRtpItem.setTcp(tcp);
         sendRtpItem.setLocalPort(localPort);
+        sendRtpItem.setServerId(userSetting.getServerId());
         sendRtpItem.setMediaServerId(serverItem.getId());
         return sendRtpItem;
     }
@@ -279,10 +290,10 @@
      */
     public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
-        Integer code = mediaInfo.getInteger("code");
         if (mediaInfo == null) {
             return 0;
         }
+        Integer code = mediaInfo.getInteger("code");
         if ( code < 0) {
             logger.warn("鏌ヨ娴�({}/{})鏄惁鏈夊叾瀹冭鐪嬭�呮椂寰楀埌锛� {}", app, streamId, mediaInfo.getString("msg"));
             return -1;
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java
index ba7daec..21e6ca0 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java
@@ -1,6 +1,9 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
+/**
+ * @author lin
+ */
 public interface ChannelOnlineEvent {
 
-    void run(String app, String stream);
+    void run(String app, String stream, String serverId);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java
index ad158ec..8abac5b 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java
@@ -61,9 +61,14 @@
     private String originUrl;
 
     /**
-     * 鏈嶅姟鍣╥d
+     * 娴佸獟浣撴湇鍔″櫒id
      */
     private String mediaServerId;
+
+    /**
+     * 鏈嶅姟鍣╥d
+     */
+    private String severId;
 
     /**
      * GMT unix绯荤粺鏃堕棿鎴筹紝鍗曚綅绉�
@@ -414,4 +419,12 @@
     public void setStreamInfo(StreamInfo streamInfo) {
         this.streamInfo = streamInfo;
     }
+
+    public String getSeverId() {
+        return severId;
+    }
+
+    public void setSeverId(String severId) {
+        this.severId = severId;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
index 81c9c76..ceb48b3 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
@@ -81,6 +81,11 @@
      */
     private String mediaServerId;
 
+    /**
+     * 浣跨敤鐨勬湇鍔D
+     */
+    private String serverId;
+
     public String getVhost() {
         return vhost;
     }
@@ -219,5 +224,13 @@
     public void setMediaServerId(String mediaServerId) {
         this.mediaServerId = mediaServerId;
     }
+
+    public String getServerId() {
+        return serverId;
+    }
+
+    public void setServerId(String serverId) {
+        this.serverId = serverId;
+    }
 }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java
index dd1f69e..d3af23c 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java
@@ -61,13 +61,12 @@
         // 鍙戣捣http璇锋眰楠岃瘉zlm鏄惁纭疄鏃犳硶杩炴帴锛屽鏋滅‘瀹炴棤娉曡繛鎺ュ垯鍙戦�佺绾夸簨浠讹紝鍚﹀垯涓嶄綔澶勭悊
         MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
         JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
-        if (mediaServerConfig == null) {
-            publisher.zlmOfflineEventPublish(mediaServerId);
-        }else {
+        if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) {
             logger.info("[zlm蹇冭烦鍒版湡]锛歿}楠岃瘉鍚巣lm浠嶅湪绾匡紝鎭㈠蹇冭烦淇℃伅", mediaServerId);
             // 娣诲姞zlm淇℃伅
             mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig);
+        }else {
+            publisher.zlmOfflineEventPublish(mediaServerId);
         }
-
     }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java
index f4a9feb..223ef13 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java
@@ -42,7 +42,7 @@
 		logger.info("[ZLM] 涓婄嚎 ID锛�" + event.getMediaServerId());
 		streamPushService.zlmServerOnline(event.getMediaServerId());
 		streamProxyService.zlmServerOnline(event.getMediaServerId());
-
+		playService.zlmServerOnline(event.getMediaServerId());
 	}
 
 	@Async
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
index 239d0c6..e87f53a 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -42,6 +42,8 @@
 
     StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
 
+    void zlmServerOnline(String mediaServerId);
+
     void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event);
     void stopAudioBroadcast(String deviceId, String channelId);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java
index 5bdd9f5..9d15c1f 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/StreamGPSSubscribeTask.java
@@ -23,7 +23,6 @@
     private IVideoManagerStorage storager;
 
 
-
     @Scheduled(fixedRate = 30 * 1000)   //姣�30绉掓墽琛屼竴娆�
     public void execute(){
         List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo();
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java
index 3ab1b80..6dcc515 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java
@@ -1,7 +1,10 @@
 package com.genersoft.iot.vmp.service.bean;
 
+import java.util.stream.Stream;
+
 /**
  * 褰撲笂绾у钩鍙�
+ * @author lin
  */
 public class MessageForPushChannel {
     /**
@@ -45,6 +48,20 @@
      */
     private String mediaServerId;
 
+    public static MessageForPushChannel getInstance(int type, String app, String stream, String gbId,
+                                                    String platFormId, String platFormName, String serverId,
+                                                    String mediaServerId){
+        MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
+        messageForPushChannel.setType(type);
+        messageForPushChannel.setGbId(gbId);
+        messageForPushChannel.setApp(app);
+        messageForPushChannel.setStream(stream);
+        messageForPushChannel.setMediaServerId(mediaServerId);
+        messageForPushChannel.setPlatFormId(platFormId);
+        messageForPushChannel.setPlatFormName(platFormName);
+        return messageForPushChannel;
+    }
+
 
     public int getType() {
         return type;
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java
new file mode 100644
index 0000000..5827d01
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java
@@ -0,0 +1,170 @@
+package com.genersoft.iot.vmp.service.bean;
+
+/**
+ * redis娑堟伅锛氳姹備笅绾ф帹閫佹祦淇℃伅
+ * @author lin
+ */
+public class RequestPushStreamMsg {
+
+
+    /**
+     * 涓嬬骇鏈嶅姟ID
+     */
+    private String mediaServerId;
+
+    /**
+     * 娴両D
+     */
+    private String app;
+
+    /**
+     * 搴旂敤鍚�
+     */
+    private String stream;
+
+    /**
+     * 鐩爣IP
+     */
+    private String ip;
+
+    /**
+     * 鐩爣绔彛
+     */
+    private int port;
+
+    /**
+     * ssrc
+     */
+    private String ssrc;
+
+    /**
+     * 鏄惁浣跨敤TCP鏂瑰紡
+     */
+    private boolean tcp;
+
+    /**
+     * 鏈湴浣跨敤鐨勭鍙�
+     */
+    private int srcPort;
+
+    /**
+     * 鍙戦�佹椂锛宺tp鐨刾t锛坲int8_t锛�,涓嶄紶鏃堕粯璁や负96
+     */
+    private int pt;
+
+    /**
+     * 鍙戦�佹椂锛宺tp鐨勮礋杞界被鍨嬨�備负true鏃讹紝璐熻浇涓簆s锛涗负false鏃讹紝涓篹s锛�
+     */
+    private boolean ps;
+
+    /**
+     * 鏄惁鍙湁闊抽
+     */
+    private boolean onlyAudio;
+
+
+    public static RequestPushStreamMsg getInstance(String mediaServerId, String app, String stream, String ip, int port, String ssrc,
+                                boolean tcp, int srcPort, int pt, boolean ps, boolean onlyAudio) {
+        RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg();
+        requestPushStreamMsg.setMediaServerId(mediaServerId);
+        requestPushStreamMsg.setApp(app);
+        requestPushStreamMsg.setStream(stream);
+        requestPushStreamMsg.setIp(ip);
+        requestPushStreamMsg.setPort(port);
+        requestPushStreamMsg.setSsrc(ssrc);
+        requestPushStreamMsg.setTcp(tcp);
+        requestPushStreamMsg.setSrcPort(srcPort);
+        requestPushStreamMsg.setPt(pt);
+        requestPushStreamMsg.setPs(ps);
+        requestPushStreamMsg.setOnlyAudio(onlyAudio);
+        return requestPushStreamMsg;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public boolean isTcp() {
+        return tcp;
+    }
+
+    public void setTcp(boolean tcp) {
+        this.tcp = tcp;
+    }
+
+    public int getSrcPort() {
+        return srcPort;
+    }
+
+    public void setSrcPort(int srcPort) {
+        this.srcPort = srcPort;
+    }
+
+    public int getPt() {
+        return pt;
+    }
+
+    public void setPt(int pt) {
+        this.pt = pt;
+    }
+
+    public boolean isPs() {
+        return ps;
+    }
+
+    public void setPs(boolean ps) {
+        this.ps = ps;
+    }
+
+    public boolean isOnlyAudio() {
+        return onlyAudio;
+    }
+
+    public void setOnlyAudio(boolean onlyAudio) {
+        this.onlyAudio = onlyAudio;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java
new file mode 100644
index 0000000..66689fa
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java
@@ -0,0 +1,173 @@
+package com.genersoft.iot.vmp.service.bean;
+
+/**
+ * redis娑堟伅锛氳姹備笅绾у洖澶嶆帹閫佷俊鎭�
+ * @author lin
+ */
+public class RequestSendItemMsg {
+
+    /**
+     * 涓嬬骇鏈嶅姟ID
+     */
+    private String serverId;
+
+    /**
+     * 涓嬬骇鏈嶅姟ID
+     */
+    private String mediaServerId;
+
+    /**
+     * 娴両D
+     */
+    private String app;
+
+    /**
+     * 搴旂敤鍚�
+     */
+    private String stream;
+
+    /**
+     * 鐩爣IP
+     */
+    private String ip;
+
+    /**
+     * 鐩爣绔彛
+     */
+    private int port;
+
+    /**
+     * ssrc
+     */
+    private String ssrc;
+
+    /**
+     * 骞冲彴鍥芥爣缂栧彿
+     */
+    private String platformId;
+
+    /**
+     * 骞冲彴鍚嶇О
+     */
+    private String platformName;
+
+    /**
+     * 閫氶亾ID
+     */
+    private String channelId;
+
+
+    /**
+     * 鏄惁浣跨敤TCP
+     */
+    private Boolean isTcp;
+
+
+
+
+    public static RequestSendItemMsg getInstance(String serverId, String mediaServerId, String app, String stream, String ip, int port,
+                                                          String ssrc, String platformId, String channelId, Boolean isTcp, String platformName) {
+        RequestSendItemMsg requestSendItemMsg = new RequestSendItemMsg();
+        requestSendItemMsg.setServerId(serverId);
+        requestSendItemMsg.setMediaServerId(mediaServerId);
+        requestSendItemMsg.setApp(app);
+        requestSendItemMsg.setStream(stream);
+        requestSendItemMsg.setIp(ip);
+        requestSendItemMsg.setPort(port);
+        requestSendItemMsg.setSsrc(ssrc);
+        requestSendItemMsg.setPlatformId(platformId);
+        requestSendItemMsg.setPlatformName(platformName);
+        requestSendItemMsg.setChannelId(channelId);
+        requestSendItemMsg.setTcp(isTcp);
+
+        return  requestSendItemMsg;
+    }
+
+    public String getServerId() {
+        return serverId;
+    }
+
+    public void setServerId(String serverId) {
+        this.serverId = serverId;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+    public String getPlatformName() {
+        return platformName;
+    }
+
+    public void setPlatformName(String platformName) {
+        this.platformName = platformName;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+
+    public Boolean getTcp() {
+        return isTcp;
+    }
+
+    public void setTcp(Boolean tcp) {
+        isTcp = tcp;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java
new file mode 100644
index 0000000..501621b
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java
@@ -0,0 +1,31 @@
+package com.genersoft.iot.vmp.service.bean;
+
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+
+/**
+ * redis娑堟伅锛氫笅绾у洖澶嶆帹閫佷俊鎭�
+ * @author lin
+ */
+public class ResponseSendItemMsg {
+
+    private SendRtpItem sendRtpItem;
+
+    private MediaServerItem mediaServerItem;
+
+    public SendRtpItem getSendRtpItem() {
+        return sendRtpItem;
+    }
+
+    public void setSendRtpItem(SendRtpItem sendRtpItem) {
+        this.sendRtpItem = sendRtpItem;
+    }
+
+    public MediaServerItem getMediaServerItem() {
+        return mediaServerItem;
+    }
+
+    public void setMediaServerItem(MediaServerItem mediaServerItem) {
+        this.mediaServerItem = mediaServerItem;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java
new file mode 100644
index 0000000..12d79cb
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java
@@ -0,0 +1,116 @@
+package com.genersoft.iot.vmp.service.bean;
+
+/**
+ * @author lin
+ */
+public class WvpRedisMsg {
+
+    public static WvpRedisMsg getInstance(String fromId, String toId, String type, String cmd, String serial, String content){
+        WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
+        wvpRedisMsg.setFromId(fromId);
+        wvpRedisMsg.setToId(toId);
+        wvpRedisMsg.setType(type);
+        wvpRedisMsg.setCmd(cmd);
+        wvpRedisMsg.setSerial(serial);
+        wvpRedisMsg.setContent(content);
+        return wvpRedisMsg;
+    }
+
+    private String fromId;
+
+    private String toId;
+    /**
+     * req 璇锋眰, res 鍥炲
+     */
+    private String type;
+    private String cmd;
+
+    /**
+     * 娑堟伅鐨処D
+     */
+    private String serial;
+    private Object content;
+
+    private final static String requestTag = "req";
+    private final static String responseTag = "res";
+
+    public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) {
+        WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
+        wvpRedisMsg.setType(requestTag);
+        wvpRedisMsg.setFromId(fromId);
+        wvpRedisMsg.setToId(toId);
+        wvpRedisMsg.setCmd(cmd);
+        wvpRedisMsg.setSerial(serial);
+        wvpRedisMsg.setContent(content);
+        return wvpRedisMsg;
+    }
+
+    public static WvpRedisMsg getResponseInstance() {
+        WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
+        wvpRedisMsg.setType(responseTag);
+        return wvpRedisMsg;
+    }
+
+    public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) {
+        WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
+        wvpRedisMsg.setType(responseTag);
+        wvpRedisMsg.setFromId(fromId);
+        wvpRedisMsg.setToId(toId);
+        wvpRedisMsg.setCmd(cmd);
+        wvpRedisMsg.setSerial(serial);
+        wvpRedisMsg.setContent(content);
+        return wvpRedisMsg;
+    }
+
+    public static boolean isRequest(WvpRedisMsg wvpRedisMsg) {
+        return requestTag.equals(wvpRedisMsg.getType());
+    }
+
+    public String getSerial() {
+        return serial;
+    }
+
+    public void setSerial(String serial) {
+        this.serial = serial;
+    }
+
+    public String getFromId() {
+        return fromId;
+    }
+
+    public void setFromId(String fromId) {
+        this.fromId = fromId;
+    }
+
+    public String getToId() {
+        return toId;
+    }
+
+    public void setToId(String toId) {
+        this.toId = toId;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getCmd() {
+        return cmd;
+    }
+
+    public void setCmd(String cmd) {
+        this.cmd = cmd;
+    }
+
+    public Object getContent() {
+        return content;
+    }
+
+    public void setContent(Object content) {
+        this.content = content;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java
new file mode 100644
index 0000000..cb11886
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java
@@ -0,0 +1,12 @@
+package com.genersoft.iot.vmp.service.bean;
+
+/**
+ * @author lin
+ */
+
+public class WvpRedisMsgCmd {
+
+    public static final String GET_SEND_ITEM = "GetSendItem";
+    public static final String REQUEST_PUSH_STREAM = "RequestPushStream";
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
index 0144e83..ec30cd4 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -2,16 +2,21 @@
 
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
+import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
 import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
 import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import org.slf4j.Logger;
@@ -50,6 +55,12 @@
     private DeviceMapper deviceMapper;
 
     @Autowired
+    private DeviceChannelMapper deviceChannelMapper;
+
+    @Autowired
+    private IVideoManagerStorage storage;
+
+    @Autowired
     private ISIPCommander commander;
 
     @Autowired
@@ -68,7 +79,6 @@
         if (deviceInRedis != null && deviceInDb == null) {
             // redis 瀛樺湪鑴忔暟鎹�
             redisCatchStorage.clearCatchByDeviceId(device.getDeviceId());
-
         }
         device.setUpdateTime(now);
         device.setOnline(1);
@@ -77,13 +87,15 @@
         if (device.getCreateTime() == null) {
             device.setCreateTime(now);
             logger.info("[璁惧涓婄嚎,棣栨娉ㄥ唽]: {}锛屾煡璇㈣澶囦俊鎭互鍙婇�氶亾淇℃伅", device.getDeviceId());
+            deviceMapper.add(device);
+            redisCatchStorage.updateDevice(device);
             commander.deviceInfoQuery(device);
             sync(device);
-            deviceMapper.add(device);
         }else {
             deviceMapper.update(device);
+            redisCatchStorage.updateDevice(device);
         }
-        redisCatchStorage.updateDevice(device);
+
         // 涓婄嚎娣诲姞璁㈤槄
         if (device.getSubscribeCycleForCatalog() > 0) {
             // 鏌ヨ鍦ㄧ嚎璁惧閭d簺寮�鍚簡璁㈤槄锛屼负璁惧寮�鍚畾鏃剁殑鐩綍璁㈤槄
@@ -94,7 +106,6 @@
         }
         // 鍒锋柊杩囨湡浠诲姟
         String registerExpireTaskKey = registerExpireTaskKeyPrefix + device.getDeviceId();
-        dynamicTask.stop(registerExpireTaskKey);
         dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getExpires() * 1000);
     }
 
@@ -143,8 +154,16 @@
         if (device == null || device.getSubscribeCycleForCatalog() < 0) {
             return false;
         }
-        logger.info("绉婚櫎鐩綍璁㈤槄: {}", device.getDeviceId());
-        dynamicTask.stop(device.getDeviceId() + "catalog");
+        logger.info("[绉婚櫎鐩綍璁㈤槄]: {}", device.getDeviceId());
+        String taskKey = device.getDeviceId() + "catalog";
+        if (device.getOnline() == 1) {
+            Runnable runnable = dynamicTask.get(taskKey);
+            if (runnable instanceof ISubscribeTask) {
+                ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
+                subscribeTask.stop();
+            }
+        }
+        dynamicTask.stop(taskKey);
         return true;
     }
 
@@ -168,8 +187,16 @@
         if (device == null || device.getSubscribeCycleForCatalog() < 0) {
             return false;
         }
-        logger.info("绉婚櫎绉诲姩浣嶇疆璁㈤槄: {}", device.getDeviceId());
-        dynamicTask.stop(device.getDeviceId() + "mobile_position");
+        logger.info("[绉婚櫎绉诲姩浣嶇疆璁㈤槄]: {}", device.getDeviceId());
+        String taskKey = device.getDeviceId() + "mobile_position";
+        if (device.getOnline() == 1) {
+            Runnable runnable = dynamicTask.get(taskKey);
+            if (runnable instanceof ISubscribeTask) {
+                ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
+                subscribeTask.stop();
+            }
+        }
+        dynamicTask.stop(taskKey);
         return true;
     }
 
@@ -275,6 +302,10 @@
                 removeMobilePositionSubscribe(deviceInStore);
             }
         }
+        // 鍧愭爣绯诲彉鍖栵紝闇�瑕侀噸鏂拌绠桮CJ02鍧愭爣鍜學GS84鍧愭爣
+        if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) {
+            updateDeviceChannelGeoCoordSys(device);
+        }
 
         String now = DateUtil.getNow();
         device.setUpdateTime(now);
@@ -282,6 +313,32 @@
         device.setUpdateTime(DateUtil.getNow());
         if (deviceMapper.update(device) > 0) {
             redisCatchStorage.updateDevice(device);
+
         }
     }
+
+    /**
+     * 鏇存柊閫氶亾鍧愭爣绯�
+     */
+    private void updateDeviceChannelGeoCoordSys(Device device) {
+       List<DeviceChannel> deviceChannels =  deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId());
+       if (deviceChannels.size() > 0) {
+           for (DeviceChannel deviceChannel : deviceChannels) {
+               if ("WGS84".equals(device.getGeoCoordSys())) {
+                   deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
+                   deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
+                   Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
+                   deviceChannel.setLongitudeGcj02(position[0]);
+                   deviceChannel.setLatitudeGcj02(position[1]);
+               }else if ("GCJ02".equals(device.getGeoCoordSys())) {
+                   deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
+                   deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
+                   Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
+                   deviceChannel.setLongitudeWgs84(position[0]);
+                   deviceChannel.setLatitudeWgs84(position[1]);
+               }
+           }
+       }
+        storage.updateChannels(device.getDeviceId(), deviceChannels);
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
index a7a5528..c813b11 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -106,7 +106,8 @@
         deviceChannel.setStatus(1);
         deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId);
         deviceChannel.setRegisterWay(1);
-        if (catalogId.length() <= 10) { // 鐖惰妭鐐规槸琛屾斂鍖哄垝,鍒欒缃瓹ivilCode浣跨敤姝よ鏀垮尯鍒�
+        if (catalogId.length() > 0 && catalogId.length() <= 10) {
+            // 鐖惰妭鐐规槸琛屾斂鍖哄垝,鍒欒缃瓹ivilCode浣跨敤姝よ鏀垮尯鍒�
             deviceChannel.setCivilCode(catalogId);
         }else {
             deviceChannel.setCivilCode(platform.getAdministrativeDivision());
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
index 5468fae..0c84b73 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -6,6 +6,7 @@
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
@@ -35,7 +36,9 @@
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 濯掍綋鏈嶅姟鍣ㄨ妭鐐圭鐞�
@@ -189,6 +192,7 @@
     public void clearRTPServer(MediaServerItem mediaServerItem) {
         mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()));
         redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0);
+
     }
 
 
@@ -229,11 +233,10 @@
         }
         result.sort((serverItem1, serverItem2)->{
             int sortResult = 0;
-            try {
-                sortResult = DateUtil.format.parse(serverItem1.getCreateTime()).compareTo(DateUtil.format.parse(serverItem2.getCreateTime()));
-            } catch (ParseException e) {
-                e.printStackTrace();
-            }
+            LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
+            LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
+
+            sortResult = localDateTime1.compareTo(localDateTime2);
             return  sortResult;
         });
         return result;
@@ -495,14 +498,14 @@
         param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
         param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264  -f flv %s");
         param.put("hook.enable","1");
-        param.put("hook.on_flow_report","");
+        param.put("hook.on_flow_report",String.format("%s/on_flow_report", hookPrex));
         param.put("hook.on_play",String.format("%s/on_play", hookPrex));
-        param.put("hook.on_http_access","");
+        param.put("hook.on_http_access",String.format("%s/on_http_access", hookPrex));
         param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
         param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): "");
-        param.put("hook.on_record_ts","");
-        param.put("hook.on_rtsp_auth","");
-        param.put("hook.on_rtsp_realm","");
+        param.put("hook.on_record_ts",String.format("%s/on_record_ts", hookPrex));
+        param.put("hook.on_rtsp_auth",String.format("%s/on_rtsp_auth", hookPrex));
+        param.put("hook.on_rtsp_realm",String.format("%s/on_rtsp_realm", hookPrex));
         param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex));
         param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex));
         param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
index f0ffb84..6422cfc 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -21,6 +21,8 @@
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.IMediaService;
+import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
 import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
 import com.genersoft.iot.vmp.service.bean.PlayBackResult;
@@ -32,8 +34,6 @@
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
-import com.genersoft.iot.vmp.service.IMediaService;
-import com.genersoft.iot.vmp.service.IPlayService;
 import gov.nist.javax.sip.stack.SIPDialog;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,6 +49,7 @@
 import java.io.FileNotFoundException;
 import java.math.BigDecimal;
 import java.text.ParseException;
+import java.math.RoundingMode;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -73,9 +74,6 @@
 
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
-
-    @Autowired
-    private RedisUtil redis;
 
     @Autowired
     private DeferredResultHolder resultHolder;
@@ -140,36 +138,19 @@
         result.onCompletion(()->{
             // 鐐规挱缁撴潫鏃惰皟鐢ㄦ埅鍥炬帴鍙�
             // TODO 搴旇鍦ㄤ笂娴佹椂璋冪敤鏇村ソ锛岀粨鏉熶篃鍙兘鏄敊璇粨鏉�
-            try {
-                String classPath = ResourceUtils.getURL("classpath:").getPath();
-                // 鍏煎鎵撳寘涓簀ar鐨刢lass璺緞
-                if(classPath.contains("jar")) {
-                    classPath = classPath.substring(0, classPath.lastIndexOf("."));
-                    classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);
+            String path =  "static/static/snap/";
+            String fileName =  deviceId + "_" + channelId + ".jpg";
+            ResponseEntity responseEntity =  (ResponseEntity)result.getResult();
+            if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
+                WVPResult wvpResult = (WVPResult)responseEntity.getBody();
+                if (Objects.requireNonNull(wvpResult).getCode() == 0) {
+                    StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
+                    MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
+                    String streamUrl = streamInfoForSuccess.getFmp4();
+                    // 璇锋眰鎴浘
+                    logger.info("[璇锋眰鎴浘]: " + fileName);
+                    zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
                 }
-                if (classPath.startsWith("file:")) {
-                    classPath = classPath.substring(classPath.indexOf(":") + 1);
-                }
-                String path = classPath + "static/static/snap/";
-                // 鍏煎Windows绯荤粺璺緞锛堝幓闄ゅ墠闈㈢殑鈥�/鈥濓級
-                if(System.getProperty("os.name").contains("indows")) {
-                    path = path.substring(1);
-                }
-                String fileName =  deviceId + "_" + channelId + ".jpg";
-                ResponseEntity responseEntity =  (ResponseEntity)result.getResult();
-                if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
-                    WVPResult wvpResult = (WVPResult)responseEntity.getBody();
-                    if (Objects.requireNonNull(wvpResult).getCode() == 0) {
-                        StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
-                        MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
-                        String streamUrl = streamInfoForSuccess.getFmp4();
-                        // 璇锋眰鎴浘
-                        logger.info("[璇锋眰鎴浘]: " + fileName);
-                        zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
-                    }
-                }
-            } catch (FileNotFoundException e) {
-                e.printStackTrace();
             }
         });
         if (streamInfo != null) {
@@ -186,23 +167,32 @@
             MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 
             JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
-            if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
+            if(rtpInfo.getInteger("code") == 0){
+                if (rtpInfo.getBoolean("exist")) {
 
-                WVPResult wvpResult = new WVPResult();
-                wvpResult.setCode(0);
-                wvpResult.setMsg("success");
-                wvpResult.setData(streamInfo);
-                msg.setData(wvpResult);
+                    WVPResult wvpResult = new WVPResult();
+                    wvpResult.setCode(0);
+                    wvpResult.setMsg("success");
+                    wvpResult.setData(streamInfo);
+                    msg.setData(wvpResult);
 
-                resultHolder.invokeAllResult(msg);
-                if (hookEvent != null) {
-                    hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
+                    resultHolder.invokeAllResult(msg);
+                    if (hookEvent != null) {
+                        hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
+                    }
+                }else {
+                    redisCatchStorage.stopPlay(streamInfo);
+                    storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
+                    streamInfo = null;
                 }
             }else {
+                //zlm杩炴帴澶辫触
                 redisCatchStorage.stopPlay(streamInfo);
                 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
                 streamInfo = null;
+
             }
+
 
         }
         if (streamInfo == null) {
@@ -256,33 +246,41 @@
         if (ssrcInfo == null) {
             ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);
         }
-
+        logger.info("[鐐规挱寮�濮媇 deviceId: {}, channelId: {}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getSsrc() );
         // 瓒呮椂澶勭悊
         String timeOutTaskKey = UUID.randomUUID().toString();
         SSRCInfo finalSsrcInfo = ssrcInfo;
         dynamicTask.startDelay( timeOutTaskKey,()->{
-            logger.warn(String.format("璁惧鐐规挱瓒呮椂锛宒eviceId锛�%s 锛宑hannelId锛�%s", device.getDeviceId(), channelId));
 
             SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
             if (dialog != null) {
+                logger.info("[鐐规挱瓒呮椂] 鏀舵祦瓒呮椂 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
                 timeoutCallback.run(1, "鏀舵祦瓒呮椂");
                 // 鐐规挱瓒呮椂鍥炲BYE 鍚屾椂閲婃斁ssrc浠ュ強姝ゆ鐐规挱鐨勮祫婧�
                 cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null);
             }else {
+                logger.info("[鐐规挱瓒呮椂] 娑堟伅鏈搷搴� deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
                 timeoutCallback.run(0, "鐐规挱瓒呮椂");
                 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
                 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
                 streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
             }
-        }, userSetting.getPlayTimeout()*1000);
+        }, userSetting.getPlayTimeout());
         final String ssrc = ssrcInfo.getSsrc();
         final String stream = ssrcInfo.getStream();
+        //绔彛鑾峰彇澶辫触鐨剆srcInfo 娌℃湁蹇呰鍙戦�佺偣鎾寚浠�
+        if(ssrcInfo.getPort() <= 0){
+            logger.info("[鐐规挱绔彛鍒嗛厤寮傚父]锛宒eviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
+            return;
+        }
         cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
             logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString());
             dynamicTask.stop(timeOutTaskKey);
             // hook鍝嶅簲
             onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
             hookEvent.response(mediaServerItemInuse, response);
+            logger.info("[鐐规挱鎴愬姛] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
+
         }, (event) -> {
             ResponseEvent responseEvent = (ResponseEvent)event.event;
             String contentString = new String(responseEvent.getResponse().getRawContent());
@@ -296,8 +294,10 @@
                 if (ssrc.equals(ssrcInResponse)) {
                     return;
                 }
-                logger.info("[SIP 娑堟伅] 鏀跺埌invite 200, 鍙戠幇涓嬬骇鑷畾涔変簡ssrc 寮�鍚慨姝�");
+                logger.info("[鐐规挱娑堟伅] 鏀跺埌invite 200, 鍙戠幇涓嬬骇鑷畾涔変簡ssrc: {}", ssrcInResponse );
                 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
+                    logger.info("[SIP 娑堟伅] SSRC淇 {}->{}", ssrc, ssrcInResponse);
+
                     if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
                         // ssrc 涓嶅彲鐢�
                         // 閲婃斁ssrc
@@ -450,7 +450,7 @@
             cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
             // 鍥炲涔嬪墠鎵�鏈夌殑鐐规挱璇锋眰
             playBackCallback.call(playBackResult);
-        }, userSetting.getPlayTimeout()*1000);
+        }, userSetting.getPlayTimeout());
 
         cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
                 (InviteStreamInfo inviteStreamInfo) -> {
@@ -539,7 +539,7 @@
             cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
             // 鍥炲涔嬪墠鎵�鏈夌殑鐐规挱璇锋眰
             hookCallBack.call(downloadResult);
-        }, userSetting.getPlayTimeout()*1000);
+        }, userSetting.getPlayTimeout());
         cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
                 inviteStreamInfo -> {
                     logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + inviteStreamInfo.getResponse().toJSONString());
@@ -605,7 +605,7 @@
 
                         BigDecimal currentCount = new BigDecimal(duration/1000);
                         BigDecimal totalCount = new BigDecimal(end-start);
-                        BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP);
+                        BigDecimal divide = currentCount.divide(totalCount,2, RoundingMode.HALF_UP);
                         double process = divide.doubleValue();
                         streamInfo.setProgress(process);
                     }
@@ -728,4 +728,9 @@
 
 
     }
+
+    @Override
+    public void zlmServerOnline(String mediaServerId) {
+        // 浼间箮娌″暐闇�瑕佸仛鐨�
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java
new file mode 100644
index 0000000..638ea41
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGbPlayMsgListener.java
@@ -0,0 +1,377 @@
+package com.genersoft.iot.vmp.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.conf.DynamicTask;
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
+import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
+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.bean.*;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * 鐩戝惉涓嬬骇鍙戦�佹帹閫佷俊鎭紝骞跺彂閫佸浗鏍囨帹娴佹秷鎭笂绾�
+ * @author lin
+ */
+@Component
+public class RedisGbPlayMsgListener implements MessageListener {
+
+    private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class);
+
+    public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM";
+
+    /**
+     * 娴佸獟浣撲笉瀛樺湪鐨勯敊璇帥
+     */
+    public static final  int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1;
+
+    /**
+     * 绂荤嚎鐨勯敊璇帥
+     */
+    public static final  int ERROR_CODE_OFFLINE = -2;
+
+    /**
+     * 瓒呮椂鐨勯敊璇帥
+     */
+    public static final  int ERROR_CODE_TIMEOUT = -3;
+
+    private Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
+    private Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
+    private Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
+
+    @Autowired
+    private UserSetting userSetting;
+
+    @Autowired
+    private RedisUtil redis;
+
+    @Autowired
+    private ZLMMediaListManager zlmMediaListManager;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private DynamicTask dynamicTask;
+
+    @Autowired
+    private ZLMMediaListManager mediaListManager;
+
+    @Autowired
+    private ZLMHttpHookSubscribe subscribe;
+
+
+    public interface PlayMsgCallback{
+        void handler(ResponseSendItemMsg responseSendItemMsg);
+    }
+
+    public interface PlayMsgCallbackForStartSendRtpStream{
+        void handler(JSONObject jsonObject);
+    }
+
+    public interface PlayMsgErrorCallback{
+        void handler(WVPResult wvpResult);
+    }
+
+    @Override
+    public void onMessage(Message message, byte[] bytes) {
+        JSONObject msgJSON = JSON.parseObject(message.getBody(), JSONObject.class);
+        WvpRedisMsg wvpRedisMsg = JSON.toJavaObject(msgJSON, WvpRedisMsg.class);
+        if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
+            return;
+        }
+        if (WvpRedisMsg.isRequest(wvpRedisMsg)) {
+            logger.info("[鏀跺埌REDIS閫氱煡] 璇锋眰锛� {}", new String(message.getBody()));
+
+            switch (wvpRedisMsg.getCmd()){
+                case WvpRedisMsgCmd.GET_SEND_ITEM:
+                    RequestSendItemMsg content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestSendItemMsg.class);
+                    requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
+                    break;
+                case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
+                    RequestPushStreamMsg param = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestPushStreamMsg.class);;
+                    requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
+                    break;
+                default:
+                    break;
+            }
+
+        }else {
+            logger.info("[鏀跺埌REDIS閫氱煡] 鍥炲锛� {}", new String(message.getBody()));
+            switch (wvpRedisMsg.getCmd()){
+                case WvpRedisMsgCmd.GET_SEND_ITEM:
+
+                    WVPResult content  = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
+
+                    String key = wvpRedisMsg.getSerial();
+                    switch (content.getCode()) {
+                        case 0:
+                            ResponseSendItemMsg responseSendItemMsg =JSON.toJavaObject((JSONObject)content.getData(), ResponseSendItemMsg.class);
+                            PlayMsgCallback playMsgCallback = callbacks.get(key);
+                            if (playMsgCallback != null) {
+                                callbacksForError.remove(key);
+                                playMsgCallback.handler(responseSendItemMsg);
+                            }
+                            break;
+                        case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
+                        case ERROR_CODE_OFFLINE:
+                        case ERROR_CODE_TIMEOUT:
+                            PlayMsgErrorCallback errorCallback = callbacksForError.get(key);
+                            if (errorCallback != null) {
+                                callbacks.remove(key);
+                                errorCallback.handler(content);
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
+                    WVPResult wvpResult  = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
+                    String serial = wvpRedisMsg.getSerial();
+                    switch (wvpResult.getCode()) {
+                        case 0:
+                            JSONObject jsonObject = (JSONObject)wvpResult.getData();
+                            PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial);
+                            if (playMsgCallback != null) {
+                                callbacksForError.remove(serial);
+                                playMsgCallback.handler(jsonObject);
+                            }
+                            break;
+                        case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
+                        case ERROR_CODE_OFFLINE:
+                        case ERROR_CODE_TIMEOUT:
+                            PlayMsgErrorCallback errorCallback = callbacksForError.get(serial);
+                            if (errorCallback != null) {
+                                callbacks.remove(serial);
+                                errorCallback.handler(wvpResult);
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+
+
+
+
+    }
+
+    /**
+     * 澶勭悊鏀跺埌鐨勮姹傛帹娴佺殑璇锋眰
+     */
+    private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) {
+        MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
+        if (mediaInfo == null) {
+            // TODO 鍥炲閿欒
+            return;
+        }
+        String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1";
+        Map<String, Object> param = new HashMap<>();
+        param.put("vhost","__defaultVhost__");
+        param.put("app",requestPushStreamMsg.getApp());
+        param.put("stream",requestPushStreamMsg.getStream());
+        param.put("ssrc", requestPushStreamMsg.getSsrc());
+        param.put("dst_url",requestPushStreamMsg.getIp());
+        param.put("dst_port", requestPushStreamMsg.getPort());
+        param.put("is_udp", is_Udp);
+        param.put("src_port", requestPushStreamMsg.getSrcPort());
+        param.put("pt", requestPushStreamMsg.getPt());
+        param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0");
+        param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0");
+        JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+        // 鍥炲娑堟伅
+        responsePushStream(jsonObject, fromId, serial);
+    }
+
+    private void responsePushStream(JSONObject content, String toId, String serial) {
+
+        WVPResult<JSONObject> result = new WVPResult<>();
+        result.setCode(0);
+        result.setData(content);
+
+        WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
+                WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result);
+        JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
+        redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
+    }
+
+    /**
+     * 澶勭悊鏀跺埌鐨勮姹俿endItem鐨勮姹�
+     */
+    private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) {
+        MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
+        if (mediaServerItem == null) {
+            logger.info("[鍥炲鎺ㄦ祦淇℃伅] 娴佸獟浣搟}涓嶅瓨鍦� ", content.getMediaServerId());
+
+            WVPResult<SendRtpItem> result = new WVPResult<>();
+            result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND);
+            result.setMsg("娴佸獟浣撲笉瀛樺湪");
+
+            WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
+                    WvpRedisMsgCmd.GET_SEND_ITEM, serial, result);
+
+            JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
+            redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
+            return;
+        }
+        // 纭畾娴佹槸鍚﹀湪绾�
+        boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
+        if (streamReady) {
+            logger.info("[鍥炲鎺ㄦ祦淇℃伅]  {}/{}", content.getApp(), content.getStream());
+            responseSendItem(mediaServerItem, content, toId, serial);
+        }else {
+            // 娴佸凡缁忕绾�
+            // 鍙戦�乺edis娑堟伅浠ヤ娇璁惧涓婄嚎
+            logger.info("[ app={}, stream={} ]閫氶亾绂荤嚎锛屽彂閫乺edis淇℃伅鎺у埗璁惧寮�濮嬫帹娴�",content.getApp(), content.getStream());
+
+            String taskKey = UUID.randomUUID().toString();
+            // 璁剧疆瓒呮椂
+            dynamicTask.startDelay(taskKey, ()->{
+                logger.info("[ app={}, stream={} ] 绛夊緟璁惧寮�濮嬫帹娴佽秴鏃�", content.getApp(), content.getStream());
+                WVPResult<SendRtpItem> result = new WVPResult<>();
+                result.setCode(ERROR_CODE_TIMEOUT);
+                WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
+                        userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
+                );
+                JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
+                redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
+            }, userSetting.getPlatformPlayTimeout());
+
+            // 娣诲姞璁㈤槄
+            JSONObject subscribeKey = new JSONObject();
+            subscribeKey.put("app", content.getApp());
+            subscribeKey.put("stream", content.getStream());
+            subscribeKey.put("regist", true);
+            subscribeKey.put("schema", "rtmp");
+            subscribeKey.put("mediaServerId", mediaServerItem.getId());
+            subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
+                    (MediaServerItem mediaServerItemInUse, JSONObject json)->{
+                        dynamicTask.stop(taskKey);
+                        responseSendItem(mediaServerItem, content, toId, serial);
+                    });
+
+            MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(),
+                    content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(),
+                    content.getMediaServerId());
+            redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
+
+        }
+    }
+
+    /**
+     * 灏嗚幏鍙栧埌鐨剆endItem鍙戦�佸嚭鍘�
+     */
+    private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
+        SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
+                content.getPort(), content.getSsrc(), content.getPlatformId(),
+                content.getApp(), content.getStream(), content.getChannelId(),
+                content.getTcp());
+
+        WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
+        result.setCode(0);
+        ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg();
+        responseSendItemMsg.setSendRtpItem(sendRtpItem);
+        responseSendItemMsg.setMediaServerItem(mediaServerItem);
+        result.setData(responseSendItemMsg);
+
+        WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
+                userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
+        );
+        JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
+        redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
+    }
+
+    /**
+     * 鍙戦�佹秷鎭姹備笅绾х敓鎴愭帹娴佷俊鎭�
+     * @param serverId 涓嬬骇鏈嶅姟ID
+     * @param app 搴旂敤鍚�
+     * @param stream 娴両D
+     * @param ip 鐩爣IP
+     * @param port 鐩爣绔彛
+     * @param ssrc  ssrc
+     * @param platformId 骞冲彴鍥芥爣缂栧彿
+     * @param channelId 閫氶亾ID
+     * @param isTcp 鏄惁浣跨敤TCP
+     * @param callback 寰楀埌淇℃伅鐨勫洖璋�
+     */
+    public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc,
+                        String platformId, String channelId, boolean isTcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) {
+        RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance(
+                serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, platformName);
+        requestSendItemMsg.setServerId(serverId);
+        String key = UUID.randomUUID().toString();
+        WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
+                key, requestSendItemMsg);
+
+        JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
+        logger.info("[璇锋眰鎺ㄦ祦SendItem] {}: {}", serverId, jsonObject);
+        callbacks.put(key, callback);
+        callbacksForError.put(key, errorCallback);
+        dynamicTask.startDelay(key, ()->{
+            callbacks.remove(key);
+            callbacksForError.remove(key);
+            WVPResult<Object> wvpResult = new WVPResult<>();
+            wvpResult.setCode(ERROR_CODE_TIMEOUT);
+            wvpResult.setMsg("timeout");
+            errorCallback.handler(wvpResult);
+        }, userSetting.getPlatformPlayTimeout());
+        redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
+    }
+
+    /**
+     * 鍙戦�佽姹傛帹娴佺殑娑堟伅
+     * @param param 鎺ㄦ祦鍙傛暟
+     * @param callback 鍥炶皟
+     */
+    public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
+        String key = UUID.randomUUID().toString();
+        WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
+                WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param);
+
+        JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
+        logger.info("[REDIS 璇锋眰鍏朵粬骞冲彴鎺ㄦ祦] {}: {}", serverId, jsonObject);
+        dynamicTask.startDelay(key, ()->{
+            callbacksForStartSendRtpStream.remove(key);
+            callbacksForError.remove(key);
+        }, userSetting.getPlatformPlayTimeout());
+        callbacksForStartSendRtpStream.put(key, callback);
+        callbacksForError.put(key, (wvpResult)->{
+            logger.info("[REDIS 璇锋眰鍏朵粬骞冲彴鎺ㄦ祦] 澶辫触: {}", wvpResult.getMsg());
+            callbacksForStartSendRtpStream.remove(key);
+            callbacksForError.remove(key);
+        });
+        redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGPSMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java
similarity index 66%
rename from src/main/java/com/genersoft/iot/vmp/service/impl/RedisGPSMsgListener.java
rename to src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java
index c688d13..238aafd 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGPSMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisGpsMsgListener.java
@@ -3,6 +3,7 @@
 import com.alibaba.fastjson.JSON;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -10,17 +11,23 @@
 import org.springframework.data.redis.connection.MessageListener;
 import org.springframework.stereotype.Component;
 
+/**
+ * 鎺ユ敹鏉ヨ嚜redis鐨凣PS鏇存柊閫氱煡
+ * @author lin
+ */
 @Component
-public class RedisGPSMsgListener implements MessageListener {
+public class RedisGpsMsgListener implements MessageListener {
 
-    private final static Logger logger = LoggerFactory.getLogger(RedisGPSMsgListener.class);
+    private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class);
 
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
     @Override
-    public void onMessage(Message message, byte[] bytes) {
-        logger.info("鏀跺埌鏉ヨ嚜REDIS鐨凣PS閫氱煡锛� {}", new String(message.getBody()));
+    public void onMessage(@NotNull Message message, byte[] bytes) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("鏀跺埌鏉ヨ嚜REDIS鐨凣PS閫氱煡锛� {}", new String(message.getBody()));
+        }
         GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class);
         redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java
new file mode 100644
index 0000000..07fffdc
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RedisStreamMsgListener.java
@@ -0,0 +1,83 @@
+package com.genersoft.iot.vmp.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.utils.DateUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * @author lin
+ */
+@Component
+public class RedisStreamMsgListener implements MessageListener {
+
+    private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class);
+
+    @Autowired
+    private ISIPCommander commander;
+
+    @Autowired
+    private ISIPCommanderForPlatform commanderForPlatform;
+
+    @Autowired
+    private IVideoManagerStorage storage;
+
+    @Autowired
+    private UserSetting userSetting;
+
+    @Autowired
+    private ZLMMediaListManager zlmMediaListManager;
+
+    @Override
+    public void onMessage(Message message, byte[] bytes) {
+
+        JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class);
+        if (steamMsgJson == null) {
+            logger.warn("[REDIS鐨凙LARM閫氱煡]娑堟伅瑙f瀽澶辫触");
+            return;
+        }
+        String serverId = steamMsgJson.getString("serverId");
+
+        if (userSetting.getServerId().equals(serverId)) {
+            // 鑷繁鍙戦�佺殑娑堟伅蹇界暐鍗冲彲
+            return;
+        }
+        logger.info("[REDIS閫氱煡] 娴佸彉鍖栵細 {}", new String(message.getBody()));
+        String app = steamMsgJson.getString("app");
+        String stream = steamMsgJson.getString("stream");
+        boolean register = steamMsgJson.getBoolean("register");
+        String mediaServerId = steamMsgJson.getString("mediaServerId");
+        MediaItem mediaItem = new MediaItem();
+        mediaItem.setSeverId(serverId);
+        mediaItem.setApp(app);
+        mediaItem.setStream(stream);
+        mediaItem.setRegist(register);
+        mediaItem.setMediaServerId(mediaServerId);
+        mediaItem.setCreateStamp(System.currentTimeMillis()/1000);
+        mediaItem.setAliveSecond(0L);
+        mediaItem.setTotalReaderCount("0");
+        mediaItem.setOriginType(0);
+        mediaItem.setOriginTypeStr("0");
+        mediaItem.setOriginTypeStr("unknown");
+
+        zlmMediaListManager.addPush(mediaItem);
+
+
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
index d710dad..1e00faa 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -107,6 +107,7 @@
         streamPushItem.setStatus(true);
         streamPushItem.setStreamType("push");
         streamPushItem.setVhost(item.getVhost());
+        streamPushItem.setServerId(item.getSeverId());
         return streamPushItem;
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
index d94669b..6b86280 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
@@ -357,6 +357,15 @@
 
 
 	/**
+	 * 鑾峰彇浣嗕釜鎺ㄦ祦
+	 * @param app
+	 * @param stream
+	 * @return
+	 */
+	StreamPushItem getMedia(String app, String stream);
+
+
+	/**
 	 * 娓呯┖鎺ㄦ祦鍒楄〃
 	 */
 	void clearMediaList();
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
index c3b94f6..5c46fb9 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -17,10 +17,10 @@
 
     @Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " +
             "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
-            "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
+            "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
             "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," +
             "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " +
-            "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude},'${createTime}', '${updateTime}')")
+            "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude}, ${longitudeGcj02}, ${latitudeGcj02}, ${longitudeWgs84}, ${latitudeWgs84},'${createTime}', '${updateTime}')")
     int add(DeviceChannel channel);
 
     @Update(value = {" <script>" +
@@ -50,6 +50,10 @@
             "<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" +
             "<if test='longitude != null'>, longitude=${longitude}</if>" +
             "<if test='latitude != null'>, latitude=${latitude}</if>" +
+            "<if test='longitudeGcj02 != null'>, longitudeGcj02=${longitudeGcj02}</if>" +
+            "<if test='latitudeGcj02 != null'>, latitudeGcj02=${latitudeGcj02}</if>" +
+            "<if test='longitudeWgs84 != null'>, longitudeWgs84=${longitudeWgs84}</if>" +
+            "<if test='latitudeWgs84 != null'>, latitudeWgs84=${latitudeWgs84}</if>" +
             "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+
             " </script>"})
     int update(DeviceChannel channel);
@@ -67,7 +71,7 @@
             " <if test='online == false' > AND dc.status=0</if>" +
             " <if test='hasSubChannel == true' >  AND dc.subCount > 0 </if>" +
             " <if test='hasSubChannel == false' >  AND dc.subCount = 0 </if>" +
-            "GROUP BY dc.channelId " +
+            "ORDER BY dc.channelId " +
             " </script>"})
     List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online);
 
@@ -138,7 +142,8 @@
             "insert into device_channel " +
             "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
             "  address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
-            "  ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
+            "  ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " +
+            "  longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
             "values " +
             "<foreach collection='addChannels' index='index' item='item' separator=','> " +
             "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " +
@@ -146,7 +151,8 @@
             "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " +
             "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " +
             "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " +
-            "'${item.streamId}', ${item.longitude}, ${item.latitude},'${item.createTime}', '${item.updateTime}')" +
+            "'${item.streamId}', ${item.longitude}, ${item.latitude},${item.longitudeGcj02}, " +
+            "${item.latitudeGcj02},${item.longitudeWgs84}, ${item.latitudeWgs84},'${item.createTime}', '${item.updateTime}')" +
             "</foreach> " +
             "ON DUPLICATE KEY UPDATE " +
             "updateTime=VALUES(updateTime), " +
@@ -173,7 +179,11 @@
             "status=VALUES(status), " +
             "streamId=VALUES(streamId), " +
             "longitude=VALUES(longitude), " +
-            "latitude=VALUES(latitude)" +
+            "latitude=VALUES(latitude), " +
+            "longitudeGcj02=VALUES(longitudeGcj02), " +
+            "latitudeGcj02=VALUES(latitudeGcj02), " +
+            "longitudeWgs84=VALUES(longitudeWgs84), " +
+            "latitudeWgs84=VALUES(latitudeWgs84) " +
             "</script>")
     int batchAdd(List<DeviceChannel> addChannels);
 
@@ -207,7 +217,11 @@
             "<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" +
             "<if test='item.longitude != null'>, longitude=${item.longitude}</if>" +
             "<if test='item.latitude != null'>, latitude=${item.latitude}</if>" +
-            "WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+
+            "<if test='item.longitudeGcj02 != null'>, longitudeGcj02=${item.longitudeGcj02}</if>" +
+            "<if test='item.latitudeGcj02 != null'>, latitudeGcj02=${item.latitudeGcj02}</if>" +
+            "<if test='item.longitudeWgs84 != null'>, longitudeWgs84=${item.longitudeWgs84}</if>" +
+            "<if test='item.latitudeWgs84 != null'>, latitudeWgs84=${item.latitudeWgs84}</if>" +
+            "WHERE deviceId='${item.deviceId}' AND channelId='${item.channelId}'"+
             "</foreach>" +
             "</script>"})
     int batchUpdate(List<DeviceChannel> updateChannels);
@@ -261,4 +275,6 @@
     @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0")
     List<DeviceChannel> getAllChannelInPlay();
 
+    @Select("select * from device_channel where longitude*latitude > 0 and deviceId = #{deviceId}")
+    List<DeviceChannel> getAllChannelWithCoordinate(String deviceId);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
index 37d951e..3e15b73 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
@@ -38,6 +38,7 @@
                 "mobilePositionSubmissionInterval," +
                 "subscribeCycleForAlarm," +
                 "ssrcCheck," +
+                "geoCoordSys," +
                 "online" +
             ") VALUES (" +
                 "#{deviceId}," +
@@ -61,6 +62,7 @@
                 "#{mobilePositionSubmissionInterval}," +
                 "#{subscribeCycleForAlarm}," +
                 "#{ssrcCheck}," +
+                "#{geoCoordSys}," +
                 "#{online}" +
             ")")
     int add(Device device);
@@ -87,6 +89,7 @@
                 "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" +
                 "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" +
                 "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" +
+                "<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
                 "WHERE deviceId='${deviceId}'"+
             " </script>"})
     int update(Device device);
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
index 909d3a8..ebd3478 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
@@ -14,9 +14,9 @@
 public interface StreamPushMapper {
 
     @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
-            "createStamp, aliveSecond, mediaServerId) VALUES" +
+            "createStamp, aliveSecond, mediaServerId, serverId) VALUES" +
             "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
-            "'${createStamp}', '${aliveSecond}', '${mediaServerId}' )")
+            "'${createStamp}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' )")
     int add(StreamPushItem streamPushItem);
 
     @Update("UPDATE stream_push " +
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
index 39daeda..5377e23 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -587,11 +587,11 @@
         String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*";
         List<GPSMsgInfo> result = new ArrayList<>();
         List<Object> keys = redis.scan(scanKey);
-        for (int i = 0; i < keys.size(); i++) {
-            String key = (String) keys.get(i);
+        for (Object o : keys) {
+            String key = (String) o;
             GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key);
             if (!gpsMsgInfo.isStored()) { // 鍙彇娌℃湁瀛樿繃寰�
-                result.add((GPSMsgInfo)redis.get(key));
+                result.add((GPSMsgInfo) redis.get(key));
             }
         }
 
@@ -667,7 +667,7 @@
     @Override
     public void sendStreamPushRequestedMsg(MessageForPushChannel msg) {
         String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
-        logger.info("[redis 鎺ㄦ祦琚姹傞�氱煡] {}: {}-{}", key, msg.getApp(), msg.getStream());
+        logger.info("[redis 鎺ㄦ祦琚姹傞�氱煡] {}: {}/{}", key, msg.getApp(), msg.getStream());
         redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
index 1f35911..ac870f7 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
@@ -25,12 +25,13 @@
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.TransactionStatus;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
-/**    
+/**
  * 瑙嗛璁惧鏁版嵁瀛樺偍-jdbc瀹炵幇
  * swwheihei
  * 2020骞�5鏈�6鏃� 涓嬪崍2:31:42
@@ -195,7 +196,7 @@
 
 	@Override
 	public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
-		if (deviceChannelList == null) {
+		if (CollectionUtils.isEmpty(deviceChannelList)) {
 			return false;
 		}
 		List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay();
@@ -245,6 +246,10 @@
 		}
 		if (stringBuilder.length() > 0) {
 			logger.info("[鐩綍鏌ヨ]鏀跺埌鐨勬暟鎹瓨鍦ㄩ噸澶嶏細 {}" , stringBuilder);
+		}
+		if(CollectionUtils.isEmpty(channels)){
+			logger.info("閫氶亾閲嶈锛屾暟鎹负绌�={}" , deviceChannelList);
+			return false;
 		}
 		try {
 			int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels);
@@ -315,6 +320,9 @@
 		List<DeviceChannel> all;
 		if (catalogUnderDevice != null && catalogUnderDevice) {
 			all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online);
+			// 娴峰悍璁惧鐨刾arentId鏄疭IP id
+			List<DeviceChannel> deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online);
+			all.addAll(deviceChannels);
 		}else {
 			all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online);
 		}
@@ -877,6 +885,11 @@
 	}
 
 	@Override
+	public StreamPushItem getMedia(String app, String stream) {
+		return streamPushMapper.selectOne(app, stream);
+	}
+
+	@Override
 	public void clearMediaList() {
 		streamPushMapper.clear();
 	}
diff --git a/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
index 9d37dcd..494bcbb 100644
--- a/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
+++ b/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.utils;
 
 
-import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
@@ -18,35 +17,63 @@
  */
 public class DateUtil {
 
-	private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss";
-    public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
+    /**
+     * 鍏煎涓嶈鑼冪殑iso8601鏃堕棿鏍煎紡
+     */
+	private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s";
 
-    public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault());
-    public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault());
+    /**
+     * 鐢ㄤ互杈撳嚭鏍囧噯鐨刬so8601鏃堕棿鏍煎紡
+     */
+	private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
 
-    public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault());
-    public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault());
+    /**
+     * wvp鍐呴儴缁熶竴鏃堕棿鏍煎紡
+     */
+    public static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+    public static final String zoneStr = "Asia/Shanghai";
+
+    public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
+    public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
+    public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
 
 	public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
+
         return formatterISO8601.format(formatter.parse(formatTime));
     }
 	
 	public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
-        return formatter.format(formatterISO8601.parse(formatTime));
+        return formatter.format(formatterCompatibleISO8601.parse(formatTime));
 
     }
-	
+
+    /**
+     * yyyy_MM_dd_HH_mm_ss 杞椂闂存埑
+     * @param formatTime
+     * @return
+     */
 	public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) {
         TemporalAccessor temporalAccessor = formatter.parse(formatTime);
         Instant instant = Instant.from(temporalAccessor);
         return instant.getEpochSecond();
 	}
 
+    /**
+     * 鑾峰彇褰撳墠鏃堕棿
+     * @return
+     */
     public static String getNow() {
         LocalDateTime nowDateTime = LocalDateTime.now();
         return formatter.format(nowDateTime);
     }
 
+    /**
+     * 鏍煎紡鏍¢獙
+     * @param timeStr 鏃堕棿瀛楃涓�
+     * @param dateTimeFormatter 寰呮牎楠岀殑鏍煎紡
+     * @return
+     */
     public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) {
         try {
             LocalDate.parse(timeStr, dateTimeFormatter);
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
index 56864db..509c988 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
@@ -24,6 +24,7 @@
 import org.springframework.web.bind.annotation.*;
 
 import java.text.ParseException;
+import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.List;
 
@@ -68,8 +69,8 @@
             @ApiImplicitParam(name="alarmMethod", value = "鏌ヨ鍐呭" ,dataTypeClass = String.class),
             @ApiImplicitParam(name="alarmMethod", value = "鏌ヨ鍐呭" ,dataTypeClass = String.class),
             @ApiImplicitParam(name="alarmType", value = "鏌ヨ鍐呭" ,dataTypeClass = String.class),
-            @ApiImplicitParam(name="startTime", value = "鏌ヨ鍐呭" ,dataTypeClass = String.class),
-            @ApiImplicitParam(name="endTime", value = "鏌ヨ鍐呭" ,dataTypeClass = String.class),
+            @ApiImplicitParam(name="startTime", value = "寮�濮嬫椂闂�" ,dataTypeClass = String.class),
+            @ApiImplicitParam(name="endTime", value = "缁撴潫鏃堕棿" ,dataTypeClass = String.class),
     })
     public ResponseEntity<PageInfo<DeviceAlarm>> getAll(
                                              @RequestParam int page,
@@ -98,14 +99,7 @@
         }
 
 
-        try {
-            if (startTime != null) {
-                DateUtil.format.parse(startTime);
-            }
-            if (endTime != null) {
-                DateUtil.format.parse(endTime);
-            }
-        } catch (ParseException e) {
+        if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
             return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
         }
 
@@ -144,11 +138,7 @@
         if (StringUtils.isEmpty(time)) {
             time = null;
         }
-        try {
-            if (time != null) {
-                DateUtil.format.parse(time);
-            }
-        } catch (ParseException e) {
+        if (!DateUtil.verification(time, DateUtil.formatter) ){
             return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
         }
         List<String> deviceIdList = null;
@@ -189,7 +179,7 @@
         deviceAlarm.setAlarmDescription("test");
         deviceAlarm.setAlarmMethod("1");
         deviceAlarm.setAlarmPriority("1");
-        deviceAlarm.setAlarmTime(DateUtil.formatISO8601.format(System.currentTimeMillis()));
+        deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
         deviceAlarm.setAlarmType("1");
         deviceAlarm.setLongitude(115.33333);
         deviceAlarm.setLatitude(39.33333);
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
index 3e53848..5e1e40b 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -21,16 +21,22 @@
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.compress.utils.IOUtils;
+import org.apache.http.HttpResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 
+import javax.servlet.http.HttpServletResponse;
 import javax.sip.DialogState;
+import java.io.*;
+import java.nio.file.Files;
 import java.util.*;
 
 @Api(tags = "鍥芥爣璁惧鏌ヨ", value = "鍥芥爣璁惧鏌ヨ")
@@ -200,6 +206,11 @@
 			Set<String> allKeys = dynamicTask.getAllKeys();
 			for (String key : allKeys) {
 				if (key.startsWith(deviceId)) {
+					Runnable runnable = dynamicTask.get(key);
+					if (runnable instanceof ISubscribeTask) {
+						ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
+						subscribeTask.stop();
+					}
 					dynamicTask.stop(key);
 				}
 			}
@@ -306,12 +317,7 @@
 	public ResponseEntity<WVPResult<String>> updateDevice(Device device){
 
 		if (device != null && device.getDeviceId() != null) {
-
-
-			// TODO 鎶ヨ璁㈤槄鐩稿叧鐨勪俊鎭�
-
 			deviceService.updateDevice(device);
-//			cmder.deviceInfoQuery(device);
 		}
 		WVPResult<String> result = new WVPResult<>();
 		result.setCode(0);
@@ -336,6 +342,11 @@
 		Device device = storager.queryVideoDevice(deviceId);
 		String uuid = UUID.randomUUID().toString();
 		String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId;
+		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
+		if(device == null) {
+			result.setResult(new ResponseEntity(String.format("璁惧%s涓嶅瓨鍦�", deviceId),HttpStatus.OK));
+			return result;
+		}
 		cmder.deviceStatusQuery(device, event -> {
 			RequestMessage msg = new RequestMessage();
 			msg.setId(uuid);
@@ -343,7 +354,6 @@
 			msg.setData(String.format("鑾峰彇璁惧鐘舵�佸け璐ワ紝閿欒鐮侊細 %s, %s", event.statusCode, event.msg));
 			resultHolder.invokeResult(msg);
 		});
-        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
 		result.onTimeout(()->{
 			logger.warn(String.format("鑾峰彇璁惧鐘舵�佽秴鏃�"));
 			// 閲婃斁rtpserver
@@ -456,4 +466,17 @@
 		wvpResult.setData(dialogStateMap);
 		return wvpResult;
 	}
+
+	@GetMapping("/snap/{deviceId}/{channelId}")
+	@ApiOperation(value = "璇锋眰鎴浘", notes = "璇锋眰鎴浘")
+	public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) {
+
+		try {
+			final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath());
+			resp.setContentType(MediaType.IMAGE_PNG_VALUE);
+			IOUtils.copy(in, resp.getOutputStream());
+		} catch (IOException e) {
+			resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
+		}
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
index 8cb923a..f0f2eb2 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
@@ -72,7 +72,7 @@
 		if (!DateUtil.verification(startTime, DateUtil.formatter)){
 			WVPResult<RecordInfo> wvpResult = new WVPResult<>();
 			wvpResult.setCode(-1);
-			wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss);
+			wvpResult.setMsg("startTime error, format is " + DateUtil.PATTERN);
 
 			ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
 			result.setResult(resultResponseEntity);
@@ -81,7 +81,7 @@
 		if (!DateUtil.verification(endTime, DateUtil.formatter)){
 			WVPResult<RecordInfo> wvpResult = new WVPResult<>();
 			wvpResult.setCode(-1);
-			wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss);
+			wvpResult.setMsg("endTime error, format is " + DateUtil.PATTERN);
 			ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
 			result.setResult(resultResponseEntity);
 			return result;
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
index 65f5f7c..a9b23ef 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
@@ -76,14 +76,7 @@
             logger.warn("鑷姩璁板綍鏃ュ織鍔熻兘宸插叧闂紝鏌ヨ缁撴灉鍙兘涓嶅畬鏁淬��");
         }
 
-        try {
-            if (startTime != null) {
-                DateUtil.format.parse(startTime);
-            }
-            if (endTime != null) {
-                DateUtil.format.parse(endTime);
-            }
-        } catch (ParseException e) {
+        if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
             return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
         }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
index d4928ec..3be4be3 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
@@ -146,8 +146,8 @@
                                                      // 2-鍩轰簬鍙d护鐨勫弻鍚戣璇�,
                                                      // 3-鍩轰簬鏁板瓧璇佷功鐨勫弻鍚戣璇�
             deviceJOSNChannel.put("Status", deviceChannel.getStatus());
-            deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude());
-            deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude());
+            deviceJOSNChannel.put("Longitude", deviceChannel.getLongitudeWgs84());
+            deviceJOSNChannel.put("Latitude", deviceChannel.getLatitudeWgs84());
             deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 浜戝彴绫诲瀷, 0 - 鏈煡, 1 - 鐞冩満, 2 - 鍗婄悆,
                                                                             //   3 - 鍥哄畾鏋満, 4 - 閬ユ帶鏋満
             deviceJOSNChannel.put("CustomPTZType", "");
diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml
index 1233a89..9dedcb1 100644
--- a/src/main/resources/all-application.yml
+++ b/src/main/resources/all-application.yml
@@ -32,7 +32,7 @@
     datasource:
         type: com.alibaba.druid.pool.DruidDataSource
         driver-class-name: com.mysql.cj.jdbc.Driver
-        url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false
+        url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
         username: root
         password: root123
         druid:
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 3531431..35ddc86 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -20,7 +20,7 @@
     datasource:
         type: com.alibaba.druid.pool.DruidDataSource
         driver-class-name: com.mysql.cj.jdbc.Driver
-        url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false
+        url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
         username: root
         password: 123456
         druid:
diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml
index 1653a58..53a8635 100644
--- a/src/main/resources/application-docker.yml
+++ b/src/main/resources/application-docker.yml
@@ -20,7 +20,7 @@
     datasource:
         # 浣跨敤mysql 鎵撳紑23-28琛屾敞閲婏紝 鍒犻櫎29-36琛�
          name: wvp
-         url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false
+         url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true
          username: root
          password: root
          type: com.alibaba.druid.pool.DruidDataSource
diff --git a/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java b/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java
index 6d6ff37..c627511 100644
--- a/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java
+++ b/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java
@@ -8,6 +8,10 @@
 import org.springframework.test.context.junit4.SpringRunner;
 
 import javax.annotation.Resource;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.temporal.TemporalAccessor;
 import java.util.Date;
 
 
@@ -64,8 +68,8 @@
              * 	 * 7鍏朵粬鎶ヨ;鍙互涓虹洿鎺ョ粍鍚堝12涓虹數璇濇姤璀︽垨 璁惧鎶ヨ-
              */
             deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + "");
-            Date date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00");
-            deviceAlarm.setAlarmTime(DateUtil.format.format(date));
+            Instant date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00");
+            deviceAlarm.setAlarmTime(DateUtil.formatter.format(date));
             /**
              * 鎶ヨ绾у埆, 1涓轰竴绾ц鎯�, 2涓轰簩绾ц鎯�, 3涓轰笁绾ц鎯�, 4涓哄洓绾� 璀︽儏-
              */
@@ -85,17 +89,20 @@
 
 
 
-    private Date randomDate(String beginDate, String endDate) {
+    private Instant randomDate(String beginDate, String endDate) {
         try {
 
-            Date start = DateUtil.format.parse(beginDate);//鏋勯�犲紑濮嬫棩鏈�
-            Date end = DateUtil.format.parse(endDate);//鏋勯�犵粨鏉熸棩鏈�
+            //鏋勯�犲紑濮嬫棩鏈�
+            LocalDateTime start = LocalDateTime.parse(beginDate, DateUtil.formatter);
+
+            //鏋勯�犵粨鏉熸棩鏈�
+            LocalDateTime end = LocalDateTime.parse(endDate, DateUtil.formatter);
             //getTime()琛ㄧず杩斿洖鑷� 1970 骞� 1 鏈� 1 鏃� 00:00:00 GMT 浠ユ潵姝� Date 瀵硅薄琛ㄧず鐨勬绉掓暟銆�
-            if (start.getTime() >= end.getTime()) {
+            if (start.isAfter(end)) {
                 return null;
             }
-            long date = random(start.getTime(), end.getTime());
-            return new Date(date);
+            long date = random(start.toInstant(ZoneOffset.of("+8")).toEpochMilli(), end.toInstant(ZoneOffset.of("+8")).toEpochMilli());
+            return Instant.ofEpochMilli(date);
         } catch (Exception e) {
             e.printStackTrace();
         }
diff --git a/web_src/package.json b/web_src/package.json
index 1e7f043..b825f84 100644
--- a/web_src/package.json
+++ b/web_src/package.json
@@ -52,7 +52,7 @@
     "postcss-url": "^7.2.1",
     "rimraf": "^2.6.0",
     "semver": "^5.3.0",
-    "shelljs": "^0.7.6",
+    "shelljs": "^0.8.5",
     "uglifyjs-webpack-plugin": "^1.1.1",
     "url-loader": "^0.5.8",
     "vue-loader": "^13.3.0",
diff --git a/web_src/src/App.vue b/web_src/src/App.vue
index 3590f73..4ae7ea8 100644
--- a/web_src/src/App.vue
+++ b/web_src/src/App.vue
@@ -76,7 +76,7 @@
   line-height: 60px;
 }
 .el-main {
-  background-color: #e9eef3;
+  background-color: #f0f2f5;
   color: #333;
   text-align: center;
   padding-top: 0px !important;
@@ -101,4 +101,8 @@
   box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
   -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
 }
+.table-header {
+  color: #727272;
+  font-weight: 600;
+}
 </style>
diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue
index 1d0819b..897e142 100644
--- a/web_src/src/components/CloudRecord.vue
+++ b/web_src/src/components/CloudRecord.vue
@@ -18,19 +18,17 @@
     <div v-if="!recordDetail">
 
       <!--璁惧鍒楄〃-->
-      <el-table :data="recordList" border style="width: 100%" :height="winHeight">
-        <el-table-column prop="app" label="搴旂敤鍚�" align="center">
+      <el-table :data="recordList" style="width: 100%" :height="winHeight">
+        <el-table-column prop="app" label="搴旂敤鍚�" >
         </el-table-column>
-        <el-table-column prop="stream" label="娴両D" align="center">
+        <el-table-column prop="stream" label="娴両D" >
         </el-table-column>
-        <el-table-column prop="time" label="鏃堕棿" align="center">
+        <el-table-column prop="time" label="鏃堕棿" >
         </el-table-column>
-        <el-table-column label="鎿嶄綔" width="360" align="center" fixed="right">
+        <el-table-column label="鎿嶄綔" width="360"  fixed="right">
           <template slot-scope="scope">
-            <el-button-group>
-              <el-button size="mini" icon="el-icon-video-camera-solid" type="primary" @click="showRecordDetail(scope.row)">鏌ョ湅</el-button>
-              <!--                  <el-button size="mini" icon="el-icon-delete" type="danger"  @click="deleteRecord(scope.row)">鍒犻櫎</el-button>-->
-            </el-button-group>
+            <el-button size="medium" icon="el-icon-folder-opened" type="text" @click="showRecordDetail(scope.row)">鏌ョ湅</el-button>
+            <!--                  <el-button size="mini" icon="el-icon-delete" type="danger"  @click="deleteRecord(scope.row)">鍒犻櫎</el-button>-->
           </template>
         </el-table-column>
       </el-table>
diff --git a/web_src/src/components/DeviceList.vue b/web_src/src/components/DeviceList.vue
index 62cba31..1911d1d 100644
--- a/web_src/src/components/DeviceList.vue
+++ b/web_src/src/components/DeviceList.vue
@@ -7,34 +7,33 @@
                    @click="getDeviceList()"></el-button>
       </div>
     </div>
-    <!-- <devicePlayer ref="devicePlayer"></devicePlayer> -->
     <!--璁惧鍒楄〃-->
-    <el-table :data="deviceList" border style="width: 100%;font-size: 12px;" :height="winHeight">
-      <el-table-column prop="name" label="鍚嶇О" align="center">
+    <el-table :data="deviceList" style="width: 100%;font-size: 12px;" :height="winHeight" header-row-class-name="table-header">
+      <el-table-column prop="name" label="鍚嶇О" min-width="160">
       </el-table-column>
-      <el-table-column prop="deviceId" label="璁惧缂栧彿" width="180" align="center">
+      <el-table-column prop="deviceId" label="璁惧缂栧彿" min-width="200" >
       </el-table-column>
-      <el-table-column label="鍦板潃" width="180" align="center">
+      <el-table-column label="鍦板潃" min-width="160" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium">{{ scope.row.hostAddress }}</el-tag>
           </div>
         </template>
       </el-table-column>
-      <el-table-column prop="manufacturer" label="鍘傚" align="center">
+      <el-table-column prop="manufacturer" label="鍘傚" min-width="120" >
       </el-table-column>
-      <el-table-column label="娴佷紶杈撴ā寮�" align="center" width="120">
+      <el-table-column label="娴佷紶杈撴ā寮�"  min-width="160" >
         <template slot-scope="scope">
-          <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="璇烽�夋嫨">
+          <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="璇烽�夋嫨" style="width: 120px">
             <el-option key="UDP" label="UDP" value="UDP"></el-option>
             <el-option key="TCP-ACTIVE" label="TCP涓诲姩妯″紡" :disabled="true" value="TCP-ACTIVE"></el-option>
             <el-option key="TCP-PASSIVE" label="TCP琚姩妯″紡" value="TCP-PASSIVE"></el-option>
           </el-select>
         </template>
       </el-table-column>
-      <el-table-column prop="channelCount" label="閫氶亾鏁�" align="center">
+      <el-table-column prop="channelCount" label="閫氶亾鏁�" min-width="120" >
       </el-table-column>
-      <el-table-column label="鐘舵��" width="120" align="center">
+      <el-table-column label="鐘舵��" min-width="120">
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.online == 1">鍦ㄧ嚎</el-tag>
@@ -42,30 +41,32 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column prop="keepaliveTime" label="鏈�杩戝績璺�" align="center" width="140">
+      <el-table-column prop="keepaliveTime" label="鏈�杩戝績璺�" min-width="160" >
       </el-table-column>
-      <el-table-column prop="registerTime" label="鏈�杩戞敞鍐�" align="center" width="140">
+      <el-table-column prop="registerTime" label="鏈�杩戞敞鍐�"  min-width="160">
       </el-table-column>
-      <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" align="center" width="140">
-      </el-table-column>
-      <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" align="center" width="140">
-      </el-table-column>
+<!--      <el-table-column prop="updateTime" label="鏇存柊鏃堕棿"  width="140">-->
+<!--      </el-table-column>-->
+<!--      <el-table-column prop="createTime" label="鍒涘缓鏃堕棿"  width="140">-->
+<!--      </el-table-column>-->
 
-      <el-table-column label="鎿嶄綔" width="450" align="center" fixed="right">
+      <el-table-column label="鎿嶄綔" min-width="450" fixed="right">
         <template slot-scope="scope">
-          <el-button size="mini" v-if="scope.row.online!=0" icon="el-icon-refresh" @click="refDevice(scope.row)"
+          <el-button type="text" size="medium" v-bind:disabled="scope.row.online==0" icon="el-icon-refresh" @click="refDevice(scope.row)"
                      @mouseover="getTooltipContent(scope.row.deviceId)">鍒锋柊
           </el-button>
-          <el-button-group>
-            <el-button size="mini" icon="el-icon-video-camera-solid" v-bind:disabled="scope.row.online==0"
-                       type="primary" @click="showChannelList(scope.row)">閫氶亾
-            </el-button>
-            <el-button size="mini" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="primary"
-                       @click="showDevicePosition(scope.row)">瀹氫綅
-            </el-button>
-            <el-button size="mini" icon="el-icon-edit" type="primary" @click="edit(scope.row)">缂栬緫</el-button>
-            <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteDevice(scope.row)">鍒犻櫎</el-button>
-          </el-button-group>
+          <el-divider direction="vertical"></el-divider>
+          <el-button type="text" size="medium" icon="el-icon-video-camera" v-bind:disabled="scope.row.online==0"
+                     @click="showChannelList(scope.row)">閫氶亾
+          </el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-location" v-bind:disabled="scope.row.online==0" type="text"
+                     @click="showDevicePosition(scope.row)">瀹氫綅
+          </el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">缂栬緫</el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-delete" type="text" @click="deleteDevice(scope.row)" style="color: #f56c6c">鍒犻櫎</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -347,4 +348,5 @@
   padding: 0.3rem;
   width: 14.4rem;
 }
+
 </style>
diff --git a/web_src/src/components/MediaServerManger.vue b/web_src/src/components/MediaServerManger.vue
index 2e3eeef..1d3c057 100644
--- a/web_src/src/components/MediaServerManger.vue
+++ b/web_src/src/components/MediaServerManger.vue
@@ -15,7 +15,7 @@
             <span style="font-size: 16px">{{item.id}}</span>
             <el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">缂栬緫</el-button>
             <el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">鏌ョ湅</el-button>
-            <el-button icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">绉婚櫎</el-button>
+            <el-button v-if="!item.defaultServer" icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">绉婚櫎</el-button>
             <div style="margin-top: 13px; line-height: 12px; ">
               <span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span>
               <span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span>
diff --git a/web_src/src/components/ParentPlatformList.vue b/web_src/src/components/ParentPlatformList.vue
index 20a3e82..3ae0b65 100644
--- a/web_src/src/components/ParentPlatformList.vue
+++ b/web_src/src/components/ParentPlatformList.vue
@@ -4,14 +4,15 @@
       <div class="page-title">涓婄骇骞冲彴鍒楄〃</div>
       <div class="page-header-btn">
         <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addParentPlatform">娣诲姞</el-button>
+        <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
       </div>
     </div>
 
     <!--璁惧鍒楄〃-->
-    <el-table :data="platformList" border style="width: 100%" :height="winHeight">
-      <el-table-column prop="name" label="鍚嶇О" align="center"></el-table-column>
-      <el-table-column prop="serverGBId" label="骞冲彴缂栧彿" align="center"></el-table-column>
-      <el-table-column label="鏄惁鍚敤" width="120" align="center">
+    <el-table :data="platformList" style="width: 100%" :height="winHeight">
+      <el-table-column prop="name" label="鍚嶇О" ></el-table-column>
+      <el-table-column prop="serverGBId" label="骞冲彴缂栧彿" min-width="200"></el-table-column>
+      <el-table-column label="鏄惁鍚敤" min-width="80" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.enable">宸插惎鐢�</el-tag>
@@ -19,7 +20,7 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="鐘舵��" width="120" align="center">
+      <el-table-column label="鐘舵��" min-width="80" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.status">鍦ㄧ嚎</el-tag>
@@ -27,17 +28,17 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="鍦板潃" width="180" align="center">
+      <el-table-column label="鍦板潃" min-width="160" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium">{{ scope.row.serverIP}}:{{scope.row.serverPort }}</el-tag>
           </div>
         </template>
       </el-table-column>
-      <el-table-column prop="deviceGBId" label="璁惧鍥芥爣缂栧彿" width="200" align="center"></el-table-column>
-      <el-table-column prop="transport" label="淇′护浼犺緭妯″紡" width="120" align="center"></el-table-column>
-      <el-table-column prop="channelCount" label="閫氶亾鏁�" width="120" align="center"></el-table-column>
-      <el-table-column label="璁㈤槄淇℃伅" width="240" align="center" fixed="right">
+      <el-table-column prop="deviceGBId" label="璁惧鍥芥爣缂栧彿" min-width="200" ></el-table-column>
+      <el-table-column prop="transport" label="淇′护浼犺緭妯″紡" min-width="120" ></el-table-column>
+      <el-table-column prop="channelCount" label="閫氶亾鏁�" min-width="120" ></el-table-column>
+      <el-table-column label="璁㈤槄淇℃伅" min-width="120"  fixed="right">
         <template slot-scope="scope">
           <i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="鎶ヨ璁㈤槄" class="iconfont icon-gbaojings subscribe-on " ></i>
           <i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="鎶ヨ璁㈤槄" class="iconfont icon-gbaojings subscribe-off " ></i>
@@ -48,11 +49,11 @@
         </template>
       </el-table-column>
 
-      <el-table-column label="鎿嶄綔" width="300" align="center" fixed="right">
+      <el-table-column label="鎿嶄綔" min-width="240" fixed="right">
         <template slot-scope="scope">
-          <el-button size="mini" icon="el-icon-edit" @click="editPlatform(scope.row)">缂栬緫</el-button>
-          <el-button size="mini" icon="el-icon-share"  type="primary"  @click="chooseChannel(scope.row)">閫夋嫨閫氶亾</el-button>
-          <el-button size="mini" icon="el-icon-delete"  type="danger" @click="deletePlatform(scope.row)">鍒犻櫎</el-button>
+          <el-button size="medium" icon="el-icon-edit" type="text" @click="editPlatform(scope.row)">缂栬緫</el-button>
+          <el-button size="medium" icon="el-icon-share"  type="text"  @click="chooseChannel(scope.row)">閫夋嫨閫氶亾</el-button>
+          <el-button size="medium" icon="el-icon-delete"  type="text" style="color: #f56c6c" @click="deletePlatform(scope.row)">鍒犻櫎</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -168,6 +169,9 @@
         console.log(error);
       });
 
+    },
+    refresh: function (){
+      this.initData();
     }
 
   }
diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue
index 7b5a406..678d13f 100644
--- a/web_src/src/components/PushVideoList.vue
+++ b/web_src/src/components/PushVideoList.vue
@@ -34,52 +34,54 @@
         <el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;"
                    :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">鎵归噺绉婚櫎
         </el-button>
+        <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
       </div>
     </div>
     <devicePlayer ref="devicePlayer"></devicePlayer>
     <addStreamTOGB ref="addStreamTOGB"></addStreamTOGB>
-    <el-table ref="pushListTable" :data="pushList" border style="width: 100%" :height="winHeight"
+    <el-table ref="pushListTable" :data="pushList" style="width: 100%" :height="winHeight"
               @selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream">
-      <el-table-column align="center" type="selection" :reserve-selection="true" width="55">
+      <el-table-column  type="selection" :reserve-selection="true" min-width="55">
       </el-table-column>
-      <el-table-column prop="name" label="鍚嶇О" align="center">
+      <el-table-column prop="name" label="鍚嶇О" min-width="200">
       </el-table-column>
-      <el-table-column prop="app" label="APP" align="center">
+      <el-table-column prop="app" label="APP" min-width="200">
       </el-table-column>
-      <el-table-column prop="stream" label="娴両D" align="center">
+      <el-table-column prop="stream" label="娴両D" min-width="200">
       </el-table-column>
-      <el-table-column prop="gbId" label="鍥芥爣缂栫爜" width="200" align="center">
+      <el-table-column prop="gbId" label="鍥芥爣缂栫爜" min-width="200" >
       </el-table-column>
-      <el-table-column prop="mediaServerId" label="娴佸獟浣�" width="200" align="center">
+      <el-table-column prop="mediaServerId" label="娴佸獟浣�" min-width="200" >
       </el-table-column>
-      <el-table-column label="寮�濮嬫椂闂�" align="center" width="200">
+      <el-table-column label="寮�濮嬫椂闂�"  min-width="200">
         <template slot-scope="scope">
           <el-button-group>
             {{ dateFormat(parseInt(scope.row.createStamp)) }}
           </el-button-group>
         </template>
       </el-table-column>
-      <el-table-column label="姝e湪鎺ㄦ祦" align="center" width="100">
+      <el-table-column label="姝e湪鎺ㄦ祦"  min-width="100">
         <template slot-scope="scope">
           {{ (scope.row.status == false && scope.row.gbId == null) || scope.row.status ? '鏄�' : '鍚�' }}
         </template>
       </el-table-column>
 
-      <el-table-column label="鎿嶄綔" width="360" align="center" fixed="right">
+      <el-table-column label="鎿嶄綔" min-width="360"  fixed="right">
         <template slot-scope="scope">
-          <el-button-group>
-            <el-button size="mini" icon="el-icon-video-play"
-                       v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"
-                       @click="playPush(scope.row)">鎾斁
-            </el-button>
-            <el-button size="mini" icon="el-icon-delete" type="danger" @click="stopPush(scope.row)">绉婚櫎</el-button>
-            <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!!scope.row.gbId"
-                       @click="addToGB(scope.row)">鍔犲叆鍥芥爣
-            </el-button>
-            <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!scope.row.gbId"
-                       @click="removeFromGB(scope.row)">绉诲嚭鍥芥爣
-            </el-button>
-          </el-button-group>
+          <el-button size="medium" icon="el-icon-video-play"
+                     v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status"
+                     @click="playPush(scope.row)" type="text">鎾斁
+          </el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-delete" type="text" @click="stopPush(scope.row)" style="color: #f56c6c" >绉婚櫎</el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-position" type="text" v-if="!!!scope.row.gbId"
+                     @click="addToGB(scope.row)">鍔犲叆鍥芥爣
+          </el-button>
+          <el-divider v-if="!!!scope.row.gbId" direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-position" type="text" v-if="!!scope.row.gbId"
+                     @click="removeFromGB(scope.row)">绉诲嚭鍥芥爣
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -284,6 +286,9 @@
     handleSelectionChange: function (val) {
       this.multipleSelection = val;
     },
+    refresh: function () {
+      this.initData();
+    },
   }
 };
 </script>
diff --git a/web_src/src/components/StreamProxyList.vue b/web_src/src/components/StreamProxyList.vue
index d553392..9f7ed61 100644
--- a/web_src/src/components/StreamProxyList.vue
+++ b/web_src/src/components/StreamProxyList.vue
@@ -5,14 +5,15 @@
       <div class="page-header-btn">
         <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">娣诲姞浠g悊</el-button>
         <el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">鎼滅储ONVIF</el-button>
+        <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
       </div>
     </div>
     <devicePlayer ref="devicePlayer"></devicePlayer>
-    <el-table :data="streamProxyList" border style="width: 100%" :height="winHeight">
-      <el-table-column prop="name" label="鍚嶇О" align="center" show-overflow-tooltip/>
-      <el-table-column prop="app" label="娴佸簲鐢ㄥ悕" align="center" show-overflow-tooltip/>
-      <el-table-column prop="stream" label="娴両D" align="center" show-overflow-tooltip/>
-      <el-table-column label="娴佸湴鍧�" width="400" align="center" show-overflow-tooltip >
+    <el-table :data="streamProxyList" style="width: 100%" :height="winHeight">
+      <el-table-column prop="name" label="鍚嶇О" min-width="120" show-overflow-tooltip/>
+      <el-table-column prop="app" label="娴佸簲鐢ㄥ悕" min-width="120" show-overflow-tooltip/>
+      <el-table-column prop="stream" label="娴両D" min-width="120" show-overflow-tooltip/>
+      <el-table-column label="娴佸湴鍧�" min-width="400"  show-overflow-tooltip >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
 
@@ -27,8 +28,8 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column prop="mediaServerId" label="娴佸獟浣�" width="150" align="center"></el-table-column>
-      <el-table-column label="绫诲瀷" width="100" align="center">
+      <el-table-column prop="mediaServerId" label="娴佸獟浣�" min-width="180" ></el-table-column>
+      <el-table-column label="绫诲瀷" width="100" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium">{{scope.row.type}}</el-tag>
@@ -36,8 +37,8 @@
         </template>
       </el-table-column>
 
-      <el-table-column prop="gbId" label="鍥芥爣缂栫爜" width="180" align="center" show-overflow-tooltip/>
-      <el-table-column label="鐘舵��" width="120" align="center">
+      <el-table-column prop="gbId" label="鍥芥爣缂栫爜" min-width="180"  show-overflow-tooltip/>
+      <el-table-column label="鐘舵��" min-width="120" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.status">鍦ㄧ嚎</el-tag>
@@ -45,7 +46,7 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="鍚敤" width="120" align="center">
+      <el-table-column label="鍚敤" min-width="120" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.enable">宸插惎鐢�</el-tag>
@@ -53,8 +54,8 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" align="center" width="150" show-overflow-tooltip/>
-      <el-table-column label="杞琀LS" width="120" align="center">
+      <el-table-column prop="createTime" label="鍒涘缓鏃堕棿"  min-width="150" show-overflow-tooltip/>
+      <el-table-column label="杞琀LS" min-width="120" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.enable_hls">宸插惎鐢�</el-tag>
@@ -62,7 +63,7 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="MP4褰曞埗" width="120" align="center">
+      <el-table-column label="MP4褰曞埗" min-width="120" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.enable_mp4">宸插惎鐢�</el-tag>
@@ -70,7 +71,7 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="鏃犱汉瑙傜湅鑷姩鍒犻櫎" width="160" align="center">
+      <el-table-column label="鏃犱汉瑙傜湅鑷姩鍒犻櫎" min-width="160" >
         <template slot-scope="scope">
           <div slot="reference" class="name-wrapper">
             <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">宸插惎鐢�</el-tag>
@@ -80,14 +81,15 @@
       </el-table-column>
 
 
-      <el-table-column label="鎿嶄綔" width="360" align="center" fixed="right">
+      <el-table-column label="鎿嶄綔" width="360"  fixed="right">
         <template slot-scope="scope">
-          <el-button-group>
-            <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.enable" @click="play(scope.row)">鎾斁</el-button>
-            <el-button size="mini" icon="el-icon-close" type="success" v-if="scope.row.enable" @click="stop(scope.row)">鍋滅敤</el-button>
-            <el-button size="mini" icon="el-icon-check" type="primary" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">鍚敤</el-button>
-            <el-button size="mini" icon="el-icon-delete" type="danger"  @click="deleteStreamProxy(scope.row)">鍒犻櫎</el-button>
-          </el-button-group>
+          <el-button size="medium" icon="el-icon-video-play" type="text" v-if="scope.row.enable" @click="play(scope.row)">鎾斁</el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-switch-button" type="text" v-if="scope.row.enable" @click="stop(scope.row)">鍋滅敤</el-button>
+          <el-divider direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-check" type="text" :loading="startBtnLaoding" v-if="!scope.row.enable" @click="start(scope.row)">鍚敤</el-button>
+          <el-divider v-if="!scope.row.enable" direction="vertical"></el-divider>
+          <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deleteStreamProxy(scope.row)">鍒犻櫎</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -305,8 +307,10 @@
 					console.log(error);
 					that.getListLoading = false;
 				});
-			}
-
+			},
+      refresh: function (){
+        this.initData();
+      }
 		}
 	};
 </script>
diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue
index 2d62e29..d27e62b 100644
--- a/web_src/src/components/channelList.vue
+++ b/web_src/src/components/channelList.vue
@@ -2,10 +2,10 @@
   <div id="channelList" style="width: 100%">
     <div class="page-header">
       <div class="page-title">
-        <el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem;" type="primary" @click="showDevice">
-          杩斿洖
-        </el-button>
-        閫氶亾鍒楄〃({{ parentChannelId == 0 ? deviceId : parentChannelId }})</div>
+        <el-button icon="el-icon-back" size="mini" style="font-size: 20px; color: #000;" type="text" @click="showDevice" ></el-button>
+        <el-divider direction="vertical"></el-divider>
+        閫氶亾鍒楄〃
+      </div>
       <div class="page-header-btn">
       鎼滅储:
       <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="鍏抽敭瀛�"
@@ -25,84 +25,85 @@
         <el-option label="鍦ㄧ嚎" value="true"></el-option>
         <el-option label="绂荤嚎" value="false"></el-option>
       </el-select>
-      <el-checkbox size="mini" v-model="autoList" @change="autoListChange">
-        鑷姩鍒锋柊
-      </el-checkbox>
+      <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
     </div>
   </div>
   <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer>
   <!--璁惧鍒楄〃-->
-  <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%">
-    <el-table-column prop="channelId" label="閫氶亾缂栧彿" width="200">
+  <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%" header-row-class-name="table-header">
+    <el-table-column prop="channelId" label="閫氶亾缂栧彿" min-width="200">
     </el-table-column>
-    <el-table-column prop="name" label="閫氶亾鍚嶇О">
+    <el-table-column prop="deviceId" label="璁惧缂栧彿" min-width="200">
     </el-table-column>
-    <el-table-column label="蹇収" width="80" align="center">
-      <template slot-scope="scope">
-        <img style="max-height: 3rem;max-width: 4rem;"
-             v-if="scope.row.subCount === 0 && scope.row.parental === 0"
-             :id="scope.row.deviceId + '_' + scope.row.channelId"
-             :src="getSnap(scope.row)"
-             @error="getSnapErrorEvent($event.target.id)"
-             alt="">
-        <!--                    <el-image-->
-        <!--                      :id="'snapImg_' + scope.row.deviceId + '_' + scope.row.channelId"-->
-        <!--                      :src="getSnap(scope.row)"-->
-        <!--                      @error="getSnapErrorEvent($event, scope.row)"-->
-        <!--                      :fit="'contain'">-->
-        <!--                      <div slot="error" class="image-slot">-->
-        <!--                        <i class="el-icon-picture-outline"></i>-->
-        <!--                      </div>-->
-        <!--                    </el-image>-->
+    <el-table-column prop="name" label="閫氶亾鍚嶇О" min-width="200">
+    </el-table-column>
+    <el-table-column label="蹇収" min-width="120">
+      <template v-slot:default="scope">
+        <el-image
+          :src="getSnap(scope.row)"
+          :preview-src-list="getBigSnap(scope.row)"
+          @error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)"
+          :fit="'contain'"
+          style="width: 60px">
+          <div slot="error" class="image-slot">
+            <i class="el-icon-picture-outline"></i>
+          </div>
+        </el-image>
       </template>
     </el-table-column>
-    <el-table-column prop="subCount" label="瀛愯妭鐐规暟">
+    <el-table-column prop="subCount" label="瀛愯妭鐐规暟" min-width="120">
     </el-table-column>
-    <el-table-column prop="manufacture" label="鍘傚">
+    <el-table-column prop="manufacture" label="鍘傚" min-width="120">
     </el-table-column>
-    <el-table-column label="浣嶇疆淇℃伅" align="center">
+    <el-table-column label="浣嶇疆淇℃伅"  min-width="200">
       <template slot-scope="scope">
-        <span>{{ scope.row.longitude }},{{ scope.row.latitude }}</span>
+        <span v-if="scope.row.longitude*scope.row.latitude > 0">{{ scope.row.longitude }},<br>{{ scope.row.latitude }}</span>
+        <span v-if="scope.row.longitude*scope.row.latitude === 0">鏃�</span>
       </template>
     </el-table-column>
-    <el-table-column prop="ptztypeText" label="浜戝彴绫诲瀷"/>
-    <el-table-column label="寮�鍚煶棰�" align="center">
+    <el-table-column prop="ptztypeText" label="浜戝彴绫诲瀷" min-width="120"/>
+    <el-table-column label="寮�鍚煶棰�" min-width="120">
       <template slot-scope="scope">
         <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF">
         </el-switch>
       </template>
     </el-table-column>
-    <el-table-column label="鐘舵��" width="180" align="center">
+    <el-table-column label="鐘舵��" min-width="120">
       <template slot-scope="scope">
         <div slot="reference" class="name-wrapper">
-          <el-tag size="medium" v-if="scope.row.status == 1">寮�鍚�</el-tag>
-          <el-tag size="medium" type="info" v-if="scope.row.status == 0">鍏抽棴</el-tag>
+          <el-tag size="medium" v-if="scope.row.status === 1">鍦ㄧ嚎</el-tag>
+          <el-tag size="medium" type="info" v-if="scope.row.status === 0">绂荤嚎</el-tag>
         </div>
       </template>
     </el-table-column>
 
 
-    <el-table-column label="鎿嶄綔" width="280" align="center" fixed="right">
+    <el-table-column label="鎿嶄綔" min-width="280" fixed="right">
       <template slot-scope="scope">
-        <el-button-group>
-          <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">鎾斁</el-button> -->
-          <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">鎾斁</el-button>
-          <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId"
-                     @click="stopDevicePush(scope.row)">鍋滄
-          </el-button>
-          <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
-                     @click="changeSubchannel(scope.row)">鏌ョ湅
-          </el-button>
-          <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">璁惧褰曞儚
-          </el-button>
-          <!--                             <el-button size="mini" @click="sendDevicePush(scope.row)">褰曞儚鏌ヨ</el-button> -->
-        </el-button-group>
+        <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">鎾斁</el-button> -->
+        <el-button size="medium" icon="el-icon-video-play" type="text" @click="sendDevicePush(scope.row)">鎾斁</el-button>
+        <el-button size="medium" icon="el-icon-switch-button" type="text"  style="color: #f56c6c" v-if="!!scope.row.streamId"
+                   @click="stopDevicePush(scope.row)">鍋滄
+        </el-button>
+        <el-divider direction="vertical"></el-divider>
+        <el-button size="medium" icon="el-icon-s-open" type="text" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
+                   @click="changeSubchannel(scope.row)">鏌ョ湅
+        </el-button>
+        <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider>
+        <el-button size="medium" icon="el-icon-video-camera" type="text" @click="queryRecords(scope.row)">璁惧褰曞儚
+        </el-button>
       </template>
     </el-table-column>
   </el-table>
-  <el-pagination style="float: right" @size-change="handleSizeChange" @current-change="currentChange"
-                 :current-page="currentPage" :page-size="count" :page-sizes="[15, 20, 30, 50]"
-                 layout="total, sizes, prev, pager, next" :total="total">
+  <el-pagination
+    style="float: right"
+    @size-change="handleSizeChange"
+    @current-change="currentChange"
+    :current-page="currentPage"
+    :page-size="count"
+    :page-sizes="[15, 25, 35, 50]"
+    layout="total, sizes, prev, pager, next"
+    :total="total">
   </el-pagination>
   </div>
 </template>
@@ -111,6 +112,8 @@
 import devicePlayer from './dialog/devicePlayer.vue'
 import uiHeader from '../layout/UiHeader.vue'
 import moment from "moment";
+import DviceService from "./service/DeviceService";
+import DeviceService from "./service/DeviceService";
 
 export default {
   name: 'channelList',
@@ -120,6 +123,8 @@
   },
   data() {
     return {
+      deviceService: new DeviceService(),
+      device: null,
       deviceId: this.$route.params.deviceId,
       parentChannelId: this.$route.params.parentChannelId,
       deviceChannelList: [],
@@ -135,16 +140,21 @@
       total: 0,
       beforeUrl: "/deviceList",
       isLoging: false,
-      autoList: true,
       loadSnap: {}
     };
   },
 
   mounted() {
-    this.initData();
-    if (this.autoList) {
-      this.updateLooper = setInterval(this.initData, 5000);
+    if (this.deviceId) {
+      this.deviceService.getDevice(this.deviceId, (result)=>{
+          this.device = result;
+
+      }, (error)=>{
+        console.log("鑾峰彇璁惧淇℃伅澶辫触")
+        console.error(error)
+      })
     }
+    this.initData();
 
   },
   destroyed() {
@@ -177,12 +187,8 @@
       })
     },
     handleSizeChange: function (val) {
-      var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1`
-      this.$router.push(url).then(() => {
-        this.initParam();
-        this.initData();
-      })
-
+      this.count = val;
+      this.getDeviceChannelList();
     },
     getDeviceChannelList: function () {
       let that = this;
@@ -227,7 +233,7 @@
           setTimeout(() => {
 
             let snapId = deviceId + "_" + channelId;
-            that.loadSnap[snapId] = 0;
+            that.loadSnap[deviceId + channelId] = 0;
             that.getSnapErrorEvent(snapId)
           }, 5000)
           that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
@@ -269,19 +275,24 @@
       });
     },
     getSnap: function (row) {
-      return '/static/snap/' + row.deviceId + '_' + row.channelId + '.jpg'
+      let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId
+      return url
     },
-    getSnapErrorEvent: function (id) {
+    getBigSnap: function (row) {
+      return [this.getSnap(row)]
+    },
+    getSnapErrorEvent: function (deviceId, channelId) {
 
-      if (typeof (this.loadSnap[id]) != "undefined") {
-        console.log("涓嬭浇鎴浘" + this.loadSnap[id])
-        if (this.loadSnap[id] > 5) {
-          delete this.loadSnap[id];
+      if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") {
+        console.log("涓嬭浇鎴浘" + this.loadSnap[deviceId + channelId])
+        if (this.loadSnap[deviceId + channelId] > 5) {
+          delete this.loadSnap[deviceId + channelId];
           return;
         }
         setTimeout(() => {
-          this.loadSnap[id]++
-          document.getElementById(id).setAttribute("src", '/static/snap/' + id + '.jpg?' + new Date().getTime())
+          let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + deviceId + '/' + channelId
+          this.loadSnap[deviceId + channelId]++
+          document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime())
         }, 1000)
 
       }
@@ -342,12 +353,8 @@
         console.log(JSON.stringify(res));
       });
     },
-    autoListChange: function () {
-      if (this.autoList) {
-        this.updateLooper = setInterval(this.initData, 1500);
-      } else {
-        window.clearInterval(this.updateLooper);
-      }
+    refresh: function () {
+      this.initData();
     }
 
   }
diff --git a/web_src/src/components/common/DeviceTree.vue b/web_src/src/components/common/DeviceTree.vue
index 066c344..73618cc 100644
--- a/web_src/src/components/common/DeviceTree.vue
+++ b/web_src/src/components/common/DeviceTree.vue
@@ -84,22 +84,34 @@
             }else {
               resolve([])
             }
+          }, (list)=>{
+              console.log("璁惧鍔犺浇瀹屾垚")
           }, (error)=>{
 
           })
         }
         if (node.level === 1) {
-          this.deviceService.getAllChannel(true, true, node.data.id, (catalogData) => {
-            this.deviceService.getAllChannel(false, true, node.data.id, (channelData) => {
-              let data = catalogData.concat(channelData)
-              this.channelDataHandler(data, resolve)
+          let channelArray = []
+          this.deviceService.getAllChannel(true, true, node.data.id, catalogData =>{
+            channelArray = channelArray.concat(catalogData)
+            this.channelDataHandler(channelArray, resolve)
+          },(endCatalogData) => {
+            this.deviceService.getAllChannel(false, true, node.data.id, channelData => {
+              channelArray = channelArray.concat(channelData)
+              this.channelDataHandler(channelArray, resolve)
+            }, endChannelList => {
+
             })
           })
         }else if (node.level > 1){
+          let channelArray = []
           this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{
+            channelArray = channelArray.concat(catalogData)
+            this.channelDataHandler(channelArray, resolve)
+          }, (endCatalogData)=>{
             this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{
-              let data = catalogData.concat(channelData)
-              this.channelDataHandler(data, resolve)
+              channelArray = channelArray.concat(channelData)
+              this.channelDataHandler(channelArray, resolve)
             })
           })
         }
diff --git a/web_src/src/components/common/jessibuca.vue b/web_src/src/components/common/jessibuca.vue
index 2eda2dc..7ab0b93 100644
--- a/web_src/src/components/common/jessibuca.vue
+++ b/web_src/src/components/common/jessibuca.vue
@@ -23,11 +23,11 @@
 </template>
 
 <script>
+let jessibucaPlayer = {};
 export default {
   name: 'jessibuca',
   data() {
     return {
-      jessibuca: null,
       playing: false,
       isNotMute: false,
       quieting: false,
@@ -49,6 +49,7 @@
     window.onerror = (msg) => {
       // console.error(msg)
     };
+    console.log(this._uid)
     let paramUrl = decodeURIComponent(this.$route.params.url)
     this.$nextTick(() => {
       this.updatePlayerDomSize()
@@ -88,7 +89,7 @@
       let options = {};
       console.log("hasAudio  " + this.hasAudio)
 
-      this.jessibuca = new window.Jessibuca(Object.assign(
+      jessibucaPlayer[this._uid] = new window.Jessibuca(Object.assign(
         {
           container: this.$refs.container,
           videoBuffer: 0.2, // 鏈�澶х紦鍐叉椂闀匡紝鍗曚綅绉�
@@ -117,70 +118,70 @@
         },
         options
       ));
-
+      let jessibuca = jessibucaPlayer[this._uid];
       let _this = this;
-      this.jessibuca.on("load", function () {
+      jessibuca.on("load", function () {
         console.log("on load init");
       });
 
-      this.jessibuca.on("log", function (msg) {
+      jessibuca.on("log", function (msg) {
         console.log("on log", msg);
       });
-      this.jessibuca.on("record", function (msg) {
+      jessibuca.on("record", function (msg) {
         console.log("on record:", msg);
       });
-      this.jessibuca.on("pause", function () {
+      jessibuca.on("pause", function () {
         _this.playing = false;
       });
-      this.jessibuca.on("play", function () {
+      jessibuca.on("play", function () {
         _this.playing = true;
       });
-      this.jessibuca.on("fullscreen", function (msg) {
+      jessibuca.on("fullscreen", function (msg) {
         console.log("on fullscreen", msg);
         _this.fullscreen = msg
       });
 
-      this.jessibuca.on("mute", function (msg) {
+      jessibuca.on("mute", function (msg) {
         console.log("on mute", msg);
         _this.isNotMute = !msg;
       });
-      this.jessibuca.on("audioInfo", function (msg) {
+      jessibuca.on("audioInfo", function (msg) {
         // console.log("audioInfo", msg);
       });
 
-      this.jessibuca.on("videoInfo", function (msg) {
+      jessibuca.on("videoInfo", function (msg) {
         // this.videoInfo = msg;
         console.log("videoInfo", msg);
 
       });
 
-      this.jessibuca.on("bps", function (bps) {
+      jessibuca.on("bps", function (bps) {
         // console.log('bps', bps);
 
       });
       let _ts = 0;
-      this.jessibuca.on("timeUpdate", function (ts) {
+      jessibuca.on("timeUpdate", function (ts) {
         // console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
         _ts = ts;
       });
 
-      this.jessibuca.on("videoInfo", function (info) {
+      jessibuca.on("videoInfo", function (info) {
         console.log("videoInfo", info);
       });
 
-      this.jessibuca.on("error", function (error) {
+      jessibuca.on("error", function (error) {
         console.log("error", error);
       });
 
-      this.jessibuca.on("timeout", function () {
+      jessibuca.on("timeout", function () {
         console.log("timeout");
       });
 
-      this.jessibuca.on('start', function () {
+      jessibuca.on('start', function () {
         console.log('start');
       })
 
-      this.jessibuca.on("performance", function (performance) {
+      jessibuca.on("performance", function (performance) {
         let show = "鍗¢】";
         if (performance === 2) {
           show = "闈炲父娴佺晠";
@@ -189,25 +190,25 @@
         }
         _this.performance = show;
       });
-      this.jessibuca.on('buffer', function (buffer) {
+      jessibuca.on('buffer', function (buffer) {
         // console.log('buffer', buffer);
       })
 
-      this.jessibuca.on('stats', function (stats) {
+      jessibuca.on('stats', function (stats) {
         // console.log('stats', stats);
       })
 
-      this.jessibuca.on('kBps', function (kBps) {
+      jessibuca.on('kBps', function (kBps) {
         _this.kBps = Math.round(kBps);
       });
 
       // 鏄剧ず鏃堕棿鎴� PTS
-      this.jessibuca.on('videoFrame', function () {
+      jessibuca.on('videoFrame', function () {
 
       })
 
       //
-      this.jessibuca.on('metadata', function () {
+      jessibuca.on('metadata', function () {
 
       });
     },
@@ -216,40 +217,40 @@
     },
     play: function (url) {
       console.log(url)
-      if (this.jessibuca) {
+      if (jessibucaPlayer[this._uid]) {
         this.destroy();
       }
       this.create();
-      this.jessibuca.on("play", () => {
+      jessibucaPlayer[this._uid].on("play", () => {
         this.playing = true;
         this.loaded = true;
-        this.quieting = this.jessibuca.quieting;
+        this.quieting = jessibuca.quieting;
       });
-      if (this.jessibuca.hasLoaded()) {
-        this.jessibuca.play(url);
+      if (jessibucaPlayer[this._uid].hasLoaded()) {
+        jessibucaPlayer[this._uid].play(url);
       } else {
-        this.jessibuca.on("load", () => {
+        jessibucaPlayer[this._uid].on("load", () => {
           console.log("load 鎾斁")
-          this.jessibuca.play(url);
+          jessibucaPlayer[this._uid].play(url);
         });
       }
     },
     pause: function () {
-      if (this.jessibuca) {
-        this.jessibuca.pause();
+      if (jessibucaPlayer[this._uid]) {
+        jessibucaPlayer[this._uid].pause();
       }
       this.playing = false;
       this.err = "";
       this.performance = "";
     },
     destroy: function () {
-      if (this.jessibuca) {
-        this.jessibuca.destroy();
+      if (jessibucaPlayer[this._uid]) {
+        jessibucaPlayer[this._uid].destroy();
       }
       if (document.getElementById("buttonsBox") == null) {
         this.$refs.container.appendChild(this.btnDom)
       }
-      this.jessibuca = null;
+      jessibucaPlayer[this._uid] = null;
       this.playing = false;
       this.err = "";
       this.performance = "";
@@ -262,7 +263,7 @@
     },
     fullscreenSwich: function () {
       let isFull = this.isFullscreen()
-      this.jessibuca.setFullscreen(!isFull)
+      jessibucaPlayer[this._uid].setFullscreen(!isFull)
       this.fullscreen = !isFull;
     },
     isFullscreen: function () {
@@ -273,8 +274,8 @@
     }
   },
   destroyed() {
-    if (this.jessibuca) {
-      this.jessibuca.destroy();
+    if (jessibucaPlayer[this._uid]) {
+      jessibucaPlayer[this._uid].destroy();
     }
     this.playing = false;
     this.loaded = false;
diff --git a/web_src/src/components/control.vue b/web_src/src/components/control.vue
index b8b3e34..507cc0d 100644
--- a/web_src/src/components/control.vue
+++ b/web_src/src/components/control.vue
@@ -235,10 +235,8 @@
       <el-table-column prop="local_ip" label="鏈湴"></el-table-column>
       <el-table-column prop="typeid" label="绫诲瀷"></el-table-column>
       <el-table-column align="right">
-        <template slot="header" slot-scope="scope">
-          <el-button icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button>
-        </template>
-        <template slot-scope="scope">
+        <template v-slot:default="scope">
+          <el-button size="mini" icon="el-icon-refresh-right" circle @click="getAllSession()"></el-button>
           <el-button @click.native.prevent="deleteRow(scope.$index, allSessionData)" type="text" size="small">绉婚櫎
           </el-button>
         </template>
diff --git a/web_src/src/components/dialog/deviceEdit.vue b/web_src/src/components/dialog/deviceEdit.vue
index 7e4b3b2..a04f021 100644
--- a/web_src/src/components/dialog/deviceEdit.vue
+++ b/web_src/src/components/dialog/deviceEdit.vue
@@ -39,6 +39,12 @@
           <el-form-item label="璇煶鍙戦�侀�氶亾" prop="name">
             <el-input v-model="form.audioChannelForReceive" clearable></el-input>
           </el-form-item>
+          <el-form-item label="鍦扮悊鍧愭爣绯�" prop="geoCoordSys" >
+            <el-select v-model="form.geoCoordSys" style="float: left; width: 100%" >
+              <el-option key="WGS84" label="WGS84" value="WGS84"></el-option>
+              <el-option key="GCJ02" label="GCJ02" value="GCJ02"></el-option>
+            </el-select>
+          </el-form-item>
           <el-form-item label="鐩綍璁㈤槄" title="0涓哄彇娑堣闃�" prop="subscribeCycleForCatalog" >
             <el-input v-model="form.subscribeCycleForCatalog" clearable ></el-input>
           </el-form-item>
diff --git a/web_src/src/components/dialog/devicePlayer.vue b/web_src/src/components/dialog/devicePlayer.vue
index a98656a..9c254c9 100644
--- a/web_src/src/components/dialog/devicePlayer.vue
+++ b/web_src/src/components/dialog/devicePlayer.vue
@@ -3,9 +3,23 @@
 
     <el-dialog title="瑙嗛鎾斁" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()">
         <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> -->
-        <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :height="false" :hasAudio="hasAudio" fluent autoplay live ></player>
+      <div style="width: 100%; height: 100%">
+        <el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer" v-if="Object.keys(this.player).length > 1">
+          <el-tab-pane label="Jessibuca" name="jessibuca">
+            <jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
+          </el-tab-pane>
+          <el-tab-pane label="WebRTC" name="webRTC">
+            <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
+          </el-tab-pane>
+          <el-tab-pane label="h265web">h265web鏁鏈熷緟</el-tab-pane>
+          <el-tab-pane label="wsPlayer">wsPlayer 鏁鏈熷緟</el-tab-pane>
+        </el-tabs>
+        <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
+        <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
+
+      </div>
         <div id="shared" style="text-align: right; margin-top: 1rem;">
-            <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick">
+            <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick" >
                 <el-tab-pane label="瀹炴椂瑙嗛" name="media">
                     <div style="margin-bottom: 0.5rem;">
                         <!--		<el-button type="primary" size="small" @click="playRecord(true, '')">鎾斁</el-button>-->
@@ -31,10 +45,100 @@
                     <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
                         <span style="width: 5rem; line-height: 2.5rem; text-align: right;">璧勬簮鍦板潃锛�</span>
                         <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" >
-                          <template slot="append">
-                            <i class="cpoy-btn el-icon-document-copy"  title="鐐瑰嚮鎷疯礉" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'鎴愬姛鎷疯礉鍒扮矘璐存澘'})"></i>
-                          </template>
+                          <el-button slot="append" icon="el-icon-document-copy" title="鐐瑰嚮鎷疯礉" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'鎴愬姛鎷疯礉鍒扮矘璐存澘'})"></el-button>
+                            <el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl">
+                              <el-button >
+                                鏇村鍦板潃<i class="el-icon-arrow-down el-icon--right"></i>
+                              </el-button>
+                              <el-dropdown-menu slot="dropdown" >
+                                <el-dropdown-item :command="streamInfo.flv">
+                                  <el-tag >FLV:</el-tag>
+                                  <span>{{ streamInfo.flv }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.https_flv">
+                                  <el-tag >FLV(https):</el-tag>
+                                  <span>{{ streamInfo.https_flv }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.ws_flv">
+                                  <el-tag  >FLV(ws):</el-tag>
+                                  <span >{{ streamInfo.ws_flv }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.wss_flv">
+                                  <el-tag  >FLV(wss):</el-tag>
+                                  <span>{{ streamInfo.wss_flv }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.fmp4">
+                                  <el-tag >FMP4:</el-tag>
+                                  <span>{{ streamInfo.fmp4 }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.https_fmp4">
+                                  <el-tag >FMP4(https):</el-tag>
+                                  <span>{{ streamInfo.https_fmp4 }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.ws_fmp4">
+                                  <el-tag >FMP4(ws):</el-tag>
+                                  <span>{{ streamInfo.ws_fmp4 }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.wss_fmp4">
+                                  <el-tag >FMP4(wss):</el-tag>
+                                  <span>{{ streamInfo.wss_fmp4 }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.hls">
+                                  <el-tag>HLS:</el-tag>
+                                  <span>{{ streamInfo.hls }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.https_hls">
+                                  <el-tag >HLS(https):</el-tag>
+                                  <span>{{ streamInfo.https_hls }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.ws_hls">
+                                  <el-tag >HLS(ws):</el-tag>
+                                  <span>{{ streamInfo.ws_hls }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.wss_hls">
+                                  <el-tag >HLS(wss):</el-tag>
+                                  <span>{{ streamInfo.wss_hls }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.ts">
+                                  <el-tag>TS:</el-tag>
+                                  <span>{{ streamInfo.ts }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.https_ts">
+                                  <el-tag>TS(https):</el-tag>
+                                  <span>{{ streamInfo.https_ts }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.ws_ts">
+                                  <el-tag>TS(ws):</el-tag>
+                                  <span>{{ streamInfo.ws_ts }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.wss_ts">
+                                  <el-tag>TS(wss):</el-tag>
+                                  <span>{{ streamInfo.wss_ts }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.rtc">
+                                  <el-tag >RTC:</el-tag>
+                                  <span>{{ streamInfo.rtc }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.rtmp">
+                                  <el-tag >RTMP:</el-tag>
+                                  <span>{{ streamInfo.rtmp }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.rtmps">
+                                  <el-tag >RTMPS:</el-tag>
+                                  <span>{{ streamInfo.rtmps }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.rtsp">
+                                  <el-tag >RTSP:</el-tag>
+                                  <span>{{ streamInfo.rtsp }}</span>
+                                </el-dropdown-item>
+                                <el-dropdown-item :command="streamInfo.rtsps">
+                                  <el-tag >RTSPS:</el-tag>
+                                  <span>{{ streamInfo.rtsps }}</span>
+                                </el-dropdown-item>
+                              </el-dropdown-menu>
+                            </el-dropdown>
                         </el-input>
+
                     </div>
                 </el-tab-pane>
                 <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
@@ -115,27 +219,27 @@
 
                         <div class="control-panel">
                             <el-button-group>
-                                <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium" type="info">棰勭疆浣嶇紪鍙�</el-tag>
+                                <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium">棰勭疆浣嶇紪鍙�</el-tag>
                                 <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number>
                                 <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">璁剧疆</el-button>
                                 <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">璋冪敤</el-button>
                                 <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">鍒犻櫎</el-button>
-                                <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">宸¤埅閫熷害</el-tag>
+                                <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">宸¤埅閫熷害</el-tag>
                                 <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
                                 <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">璁剧疆</el-button>
-                                <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">鍋滅暀鏃堕棿</el-tag>
+                                <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">鍋滅暀鏃堕棿</el-tag>
                                 <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
                                 <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">璁剧疆</el-button>
-                                <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium" type="info">宸¤埅缁勭紪鍙�</el-tag>
+                                <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium">宸¤埅缁勭紪鍙�</el-tag>
                                 <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number>
                                 <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">娣诲姞鐐�</el-button>
                                 <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">鍒犻櫎鐐�</el-button>
                                 <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">鍒犻櫎缁�</el-button>
                                 <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">宸¤埅</el-button>
-                                <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium" type="info">鎵弿閫熷害</el-tag>
+                                <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium">鎵弿閫熷害</el-tag>
                                 <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
                                 <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">璁剧疆</el-button>
-                                <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium" type="info">鎵弿缁勭紪鍙�</el-tag>
+                                <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium">鎵弿缁勭紪鍙�</el-tag>
                                 <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number>
                                 <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">宸﹁竟鐣�</el-button>
                                 <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">鍙宠竟鐣�</el-button>
@@ -172,26 +276,28 @@
                     </div>
 
                 </el-tab-pane>
+
             </el-tabs>
         </div>
     </el-dialog>
+    <recordDownload ref="recordDownload"></recordDownload>
 </div>
 </template>
 
 <script>
-import player from '../dialog/rtcPlayer.vue'
+import rtcPlayer from '../dialog/rtcPlayer.vue'
 // import LivePlayer from '@liveqing/liveplayer'
 // import player from '../dialog/easyPlayer.vue'
-// import player from '../dialog/jessibuca.vue'
+import jessibucaPlayer from '../common/jessibuca.vue'
+import recordDownload from '../dialog/recordDownload.vue'
 export default {
     name: 'devicePlayer',
     props: {},
     components: {
-        player,
+        jessibucaPlayer, rtcPlayer, recordDownload,
     },
     computed: {
         getPlayerShared: function () {
-
             return {
                 sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl),
                 sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>',
@@ -199,11 +305,22 @@
             };
         }
     },
-    created() {},
+    created() {
+      console.log(this.player)
+      if (Object.keys(this.player).length === 1) {
+        this.activePlayer = Object.keys(this.player)[0]
+      }
+    },
     data() {
         return {
             video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
             videoUrl: '',
+            activePlayer: "jessibuca",
+            // 濡備綍浣犲彧鏄敤涓�绉嶆挱鏀惧櫒锛岀洿鎺ユ敞閲婃帀涓嶇敤鐨勯儴鍒嗗嵆鍙�
+            player: {
+              jessibuca : ["ws_flv", "wss_flv"],
+              webRTC: ["rtc", "rtc"],
+            },
             videoHistory: {
                 date: '',
                 searchHistoryResult: [] //濯掍綋娴佸巻鍙茶褰曟悳绱㈢粨鏋�
@@ -241,6 +358,7 @@
             seekTime: 0,
             recordStartTime: 0,
             showTimeText: "00:00:00",
+            streamInfo: null,
         };
     },
     methods: {
@@ -250,7 +368,7 @@
             that.tracks = [];
             that.tracksLoading = true;
             that.tracksNotLoaded = false;
-            if (tab.name == "codec") {
+            if (tab.name === "codec") {
                 this.$axios({
                     method: 'get',
                     url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId
@@ -269,6 +387,12 @@
                 }).catch(function (e) {});
             }
         },
+        changePlayer: function (tab) {
+            console.log(this.player[tab.name][0])
+            this.activePlayer = tab.name;
+            this.videoUrl = this.streamInfo[this.player[tab.name][0]]
+            console.log(this.videoUrl)
+        },
         openDialog: function (tab, deviceId, channelId, param) {
             this.tabActiveName = tab;
             this.channelId = channelId;
@@ -277,8 +401,8 @@
             this.mediaServerId = "";
             this.app = "";
             this.videoUrl = ""
-            if (!!this.$refs.videoPlayer) {
-                this.$refs.videoPlayer.pause();
+            if (!!this.$refs[this.activePlayer]) {
+              this.$refs[this.activePlayer].pause();
             }
             switch (tab) {
                 case "media":
@@ -303,44 +427,32 @@
             console.log(val)
         },
         play: function (streamInfo, hasAudio) {
+            this.streamInfo = streamInfo;
             this.hasAudio = hasAudio;
             this.isLoging = false;
-            this.videoUrl = streamInfo.rtc;
-            // this.videoUrl = this.getUrlByStreamInfo(streamInfo);
-            this.streamId = streamInfo.streamId;
+            // this.videoUrl = streamInfo.rtc;
+            this.videoUrl = this.getUrlByStreamInfo();
+            this.streamId = streamInfo.stream;
             this.app = streamInfo.app;
             this.mediaServerId = streamInfo.mediaServerId;
             this.playFromStreamInfo(false, streamInfo)
         },
-        getUrlByStreamInfo(streamInfo){
-            let baseZlmApi = process.env.NODE_ENV === 'development'?`${location.host}/debug/zlm`:`${location.host}/zlm`
-            // return `${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
-            // return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
+        getUrlByStreamInfo(){
             if (location.protocol === "https:") {
-              if (streamInfo.wss_flv === null) {
-                console.error("濯掍綋鏈嶅姟鍣ㄦ湭閰嶇疆ssl绔彛, 浣跨敤http绔彛")
-                // this.$message({
-                //   showClose: true,
-                //   message: '濯掍綋鏈嶅姟鍣ㄦ湭閰嶇疆ssl绔彛, ',
-                //   type: 'error'
-                // });
-                return streamInfo.ws_flv
-              }else {
-                return streamInfo.wss_flv;
-              }
-
+              this.videoUrl = this.streamInfo[this.player[this.activePlayer][1]]
             }else {
-              return streamInfo.ws_flv;
+              this.videoUrl = this.streamInfo[this.player[this.activePlayer][0]]
             }
+            return this.videoUrl;
 
         },
         coverPlay: function () {
             var that = this;
             this.coverPlaying = true;
-            this.$refs.videoPlayer.pause()
+            this.$refs[this.activePlayer].pause()
             that.$axios({
                 method: 'post',
-                url: '/api/play/convert/' + that.streamId
+                url: '/api/gb_record/convert/' + that.streamId
                 }).then(function (res) {
                     if (res.data.code == 0) {
                         that.convertKey = res.data.key;
@@ -369,7 +481,7 @@
         },
         convertStopClick: function() {
             this.convertStop(()=>{
-                this.$refs.videoPlayer.play(this.videoUrl)
+                this.$refs[this.activePlayer].play(this.videoUrl)
             });
         },
         convertStop: function(callback) {
@@ -394,12 +506,12 @@
         playFromStreamInfo: function (realHasAudio, streamInfo) {
           this.showVideoDialog = true;
           this.hasaudio = realHasAudio && this.hasaudio;
-          this.$refs.videoPlayer.play(this.getUrlByStreamInfo(streamInfo))
+          this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
         },
         close: function () {
             console.log('鍏抽棴瑙嗛');
-            if (!!this.$refs.videoPlayer){
-              this.$refs.videoPlayer.pause();
+            if (!!this.$refs[this.activePlayer]){
+              this.$refs[this.activePlayer].pause();
             }
             this.videoUrl = '';
             this.coverPlaying = false;
@@ -450,9 +562,19 @@
                 method: 'get',
                 url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime
             }).then(function (res) {
-                // 澶勭悊鏃堕棿淇℃伅
-                that.videoHistory.searchHistoryResult = res.data.recordList;
-                that.recordsLoading = false;
+                console.log(res)
+                if(res.data.code === 0) {
+                  // 澶勭悊鏃堕棿淇℃伅
+                  that.videoHistory.searchHistoryResult = res.data.data.recordList;
+                  that.recordsLoading = false;
+                }else {
+                  this.$message({
+                    showClose: true,
+                    message: res.data.msg,
+                    type: "error",
+                  });
+                }
+
             }).catch(function (e) {
                 console.log(e.message);
                 // that.videoHistory.searchHistoryResult = falsificationData.recordData;
@@ -474,8 +596,8 @@
             console.log(this.seekTime)
             if (that.streamId != "") {
                 that.stopPlayRecord(function () {
-                    that.streamId = "",
-                        that.playRecord(row);
+                    that.streamId = "";
+                    that.playRecord(row);
                 })
             } else {
                 this.$axios({
@@ -483,21 +605,22 @@
                     url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
                         row.endTime
                 }).then(function (res) {
-                    var streamInfo = res.data;
-                    that.app = streamInfo.app;
-                    that.streamId = streamInfo.streamId;
-                    that.mediaServerId = streamInfo.mediaServerId;
-                    that.videoUrl = that.getUrlByStreamInfo(streamInfo);
+                    that.streamInfo = res.data;
+                    that.app = that.streamInfo.app;
+                    that.streamId = that.streamInfo.stream;
+                    that.mediaServerId = that.streamInfo.mediaServerId;
+                    that.ssrc = that.streamInfo.ssrc;
+                    that.videoUrl = that.getUrlByStreamInfo();
                     that.recordPlay = true;
                 });
             }
         },
         stopPlayRecord: function (callback) {
-            this.$refs.videoPlayer.pause();
+          this.$refs[this.activePlayer].pause();
             this.videoUrl = '';
             this.$axios({
                 method: 'get',
-                url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId
+                url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
             }).then(function (res) {
                 if (callback) callback()
             });
@@ -505,33 +628,47 @@
         downloadRecord: function (row) {
             let that = this;
             if (that.streamId != "") {
-                that.stopDownloadRecord(function () {
-                    that.streamId = "",
-                        that.downloadRecord(row);
+                that.stopDownloadRecord(function (res) {
+                  if (res.code == 0) {
+                    that.streamId = "";
+                    that.downloadRecord(row);
+                  }else {
+                    this.$message({
+                      showClose: true,
+                      message: res.data.msg,
+                      type: "error",
+                    });
+                  }
+
                 })
             } else {
                 this.$axios({
                     method: 'get',
-                    url: '/api/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
+                    url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
                         row.endTime + '&downloadSpeed=4'
                 }).then(function (res) {
-                    var streamInfo = res.data;
-                    that.app = streamInfo.app;
-                    that.streamId = streamInfo.streamId;
-                    that.mediaServerId = streamInfo.mediaServerId;
-                    that.videoUrl = that.getUrlByStreamInfo(streamInfo);
-                    that.recordPlay = true;
+                  if (res.data.code == 0) {
+                    let streamInfo = res.data.data;
+                    that.recordPlay = false;
+                    that.$refs.recordDownload.openDialog(that.deviceId, that.channelId, streamInfo.app, streamInfo.stream, streamInfo.mediaServerId);
+                  }else {
+                    that.$message({
+                      showClose: true,
+                      message: res.data.msg,
+                      type: "error",
+                    });
+                  }
                 });
             }
         },
         stopDownloadRecord: function (callback) {
-            this.$refs.videoPlayer.pause();
+            this.$refs[this.activePlayer].pause();
             this.videoUrl = '';
             this.$axios({
                 method: 'get',
-                url: '/api/download/stop/' + this.deviceId + "/" + this.channelId
-            }).then(function (res) {
-                if (callback) callback()
+                url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
+            }).then((res)=> {
+                if (callback) callback(res)
             });
         },
         ptzCamera: function (command) {
@@ -539,8 +676,6 @@
             let that = this;
             this.$axios({
                 method: 'post',
-                // url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown +
-                //     '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50'
                 url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed
             }).then(function (res) {});
         },
@@ -620,13 +755,21 @@
             console.log(resultArray)
             return resultArray;
         },
+        copyUrl: function (dropdownItem){
+            console.log(dropdownItem)
+            this.$copyText(dropdownItem).then((e)=> {
+              this.$message.success("鎴愬姛鎷疯礉鍒扮矘璐存澘");
+            }, (e)=> {
+
+            })
+        },
         gbPlay(){
           console.log('鍓嶇鎺у埗锛氭挱鏀�');
           this.$axios({
             method: 'get',
             url: '/api/playback/resume/' + this.streamId
           }).then((res)=> {
-            this.$refs.videoPlayer.play(this.videoUrl)
+            this.$refs[this.activePlayer].play(this.videoUrl)
           });
         },
         gbPause(){
@@ -655,8 +798,13 @@
           this.$axios({
             method: 'get',
             url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000)
-          }).then(function (res) {});
-        }
+          }).then( (res)=> {
+            setTimeout(()=>{
+              this.$refs[this.activePlayer].play(this.videoUrl)
+            }, 600)
+          });
+        },
+
 
     }
 };
diff --git a/web_src/src/components/dialog/rtcPlayer.vue b/web_src/src/components/dialog/rtcPlayer.vue
index 75c18f3..4737849 100644
--- a/web_src/src/components/dialog/rtcPlayer.vue
+++ b/web_src/src/components/dialog/rtcPlayer.vue
@@ -7,11 +7,11 @@
 </template>
 
 <script>
+let webrtcPlayer = null;
 export default {
     name: 'rtcPlayer',
     data() {
         return {
-            webrtcPlayer: null,
             timer: null
         };
     },
@@ -35,7 +35,7 @@
     },
     methods: {
         play: function (url) {
-            this.webrtcPlayer = new ZLMRTCClient.Endpoint({
+            webrtcPlayer = new ZLMRTCClient.Endpoint({
                 element: document.getElementById('webRtcPlayerBox'),// video 鏍囩
                 debug: true,// 鏄惁鎵撳嵃鏃ュ織
                 zlmsdpUrl: url,//娴佸湴鍧�
@@ -45,17 +45,17 @@
                 videoEnable: false,
                 recvOnly: true,
             })
-            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 鍗忓晢鍑洪敊
+            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 鍗忓晢鍑洪敊
                 console.error('ICE 鍗忓晢鍑洪敊')
                 this.eventcallbacK("ICE ERROR", "ICE 鍗忓晢鍑洪敊")
             });
 
-            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//鑾峰彇鍒颁簡杩滅娴侊紝鍙互鎾斁
+            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//鑾峰彇鍒颁簡杩滅娴侊紝鍙互鎾斁
                 console.error('鎾斁鎴愬姛',e.streams)
                 this.eventcallbacK("playing", "鎾斁鎴愬姛")
             });
 
-            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 浜ゆ崲澶辫触
+            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 浜ゆ崲澶辫触
                 console.error('offer anwser 浜ゆ崲澶辫触',e)
                 this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 浜ゆ崲澶辫触")
                 if (e.code ==-400 && e.msg=="娴佷笉瀛樺湪"){
@@ -68,7 +68,7 @@
                 }
             });
 
-            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 鑾峰彇鍒颁簡鏈湴娴�
+            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 鑾峰彇鍒颁簡鏈湴娴�
 
                 // document.getElementById('selfVideo').srcObject=s;
                 this.eventcallbacK("LOCAL STREAM", "鑾峰彇鍒颁簡鏈湴娴�")
@@ -76,9 +76,9 @@
 
         },
         pause: function () {
-            if (this.webrtcPlayer != null) {
-                this.webrtcPlayer.close();
-                this.webrtcPlayer = null;
+            if (webrtcPlayer != null) {
+                webrtcPlayer.close();
+                webrtcPlayer = null;
             }
 
         },
diff --git a/web_src/src/components/devicePosition.vue b/web_src/src/components/map.vue
similarity index 89%
rename from web_src/src/components/devicePosition.vue
rename to web_src/src/components/map.vue
index db19677..e9e8628 100644
--- a/web_src/src/components/devicePosition.vue
+++ b/web_src/src/components/map.vue
@@ -49,7 +49,7 @@
 import queryTrace from './dialog/queryTrace.vue'
 
 export default {
-  name: "devicePosition",
+  name: "map",
   components: {
     MapComponent,
     DeviceTree,
@@ -183,12 +183,27 @@
         this.clean()
         this.closeInfoBox()
         let params = [];
+        let longitudeStr;
+        let latitudeStr;
+        if (window.mapParam.coordinateSystem == "GCJ-02") {
+          longitudeStr = "longitudeGcj02";
+          latitudeStr = "latitudeGcj02";
+        }else if (window.mapParam.coordinateSystem == "WGS84") {
+          longitudeStr = "longitudeWgs84";
+          latitudeStr = "latitudeWgs84";
+        }else {
+          longitudeStr = "longitude";
+          latitudeStr = "latitude";
+        }
+
         for (let i = 0; i < channels.length; i++) {
-          if (channels[i].longitude * channels[i].latitude === 0) {
+          let longitude = channels[i][longitudeStr];
+          let latitude = channels[i][latitudeStr];
+          if (longitude * latitude === 0) {
             continue;
           }
           let item = {
-            position: [channels[i].longitude, channels[i].latitude],
+            position: [longitude, latitude],
             image: {
               src: this.getImageByChannel(channels[i]),
               anchor: [0.5, 1]
@@ -202,7 +217,7 @@
         this.layer = this.$refs.map.addLayer(params, this.featureClickEvent)
         console.log(4)
         if (params.length === 1) {
-          this.$refs.map.panTo([channels[0].longitude, channels[0].latitude], mapParam.maxZoom)
+          this.$refs.map.panTo([channels[0][longitudeStr], channels[0][latitudeStr]], mapParam.maxZoom)
         } else if (params.length > 1) {
           this.$refs.map.fit(this.layer)
         } else {
@@ -251,7 +266,20 @@
         this.channel = channels[0]
       }
       this.$nextTick(() => {
-        this.infoBoxId = this.$refs.map.openInfoBox([this.channel.longitude, this.channel.latitude], this.$refs.infobox, [0, -50])
+        let longitudeStr;
+        let latitudeStr;
+        if (window.mapParam.coordinateSystem == "GCJ-02") {
+          longitudeStr = "longitudeGcj02";
+          latitudeStr = "latitudeGcj02";
+        }else if (window.mapParam.coordinateSystem == "WGS84") {
+          longitudeStr = "longitudeWgs84";
+          latitudeStr = "latitudeWgs84";
+        }else {
+          longitudeStr = "longitude";
+          latitudeStr = "latitude";
+        }
+        let position = [this.channel[longitudeStr], this.channel[latitudeStr]];
+        this.infoBoxId = this.$refs.map.openInfoBox(position, this.$refs.infobox, [0, -50])
       })
     },
     closeInfoBox: function () {
diff --git a/web_src/src/components/service/DeviceService.js b/web_src/src/components/service/DeviceService.js
index dbe10d1..fb66374 100644
--- a/web_src/src/components/service/DeviceService.js
+++ b/web_src/src/components/service/DeviceService.js
@@ -21,47 +21,60 @@
       if (typeof (errorCallback) == "function") errorCallback(error)
     });
   }
-  getAllDeviceList(callback, errorCallback) {
+
+  getDevice(deviceId, callback, errorCallback){
+    this.$axios({
+      method: 'get',
+      url:`/api/device/query/devices/${deviceId}`,
+    }).then((res) => {
+      if (typeof (callback) == "function") callback(res.data)
+    }).catch((error) => {
+      console.log(error);
+      if (typeof (errorCallback) == "function") errorCallback(error)
+    });
+  }
+
+  getAllDeviceList(callback,endCallback, errorCallback) {
     let currentPage = 1;
     let count = 100;
     let deviceList = []
-    this.getAllDeviceListIteration(deviceList, currentPage, count, (data) => {
-      if (typeof (callback) == "function") callback(data)
-    }, errorCallback)
+    this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback)
   }
 
-  getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) {
+  getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) {
     this.getDeviceList(currentPage, count, (data) => {
       if (data.list) {
+        if (typeof (callback) == "function") callback(data.list)
         deviceList = deviceList.concat(data.list);
         if (deviceList.length < data.total) {
           currentPage ++
-          this.getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback)
+          this.getAllDeviceListIteration(deviceList, currentPage, count, callback,  endCallback, errorCallback)
         }else {
-          if (typeof (callback) == "function") callback(deviceList)
+          if (typeof (endCallback) == "function") endCallback(deviceList)
         }
       }
     }, errorCallback)
   }
 
 
-  getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, errorCallback) {
+  getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, endCallback, errorCallback) {
     let currentPage = 1;
     let count = 100;
     let catalogList = []
-    this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback)
+    this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback)
   }
 
-  getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) {
+  getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback) {
     this.getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, (data) => {
       if (data.list) {
+        if (typeof (callback) == "function") callback(data.list)
         catalogList = catalogList.concat(data.list);
         if (catalogList.length < data.total) {
           currentPage ++
           this.getAllChannelIteration(isCatalog,catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback)
         }else {
           console.log(1)
-          if (typeof (callback) == "function") callback(catalogList)
+          if (typeof (endCallback) == "function") endCallback(catalogList)
         }
       }
     }, errorCallback)
@@ -84,22 +97,23 @@
   }
 
 
-  getAllSubChannel(isCatalog, deviceId, channelId, callback, errorCallback) {
+  getAllSubChannel(isCatalog, deviceId, channelId, callback, endCallback, errorCallback) {
     let currentPage = 1;
     let count = 100;
     let catalogList = []
-    this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback)
+    this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback)
   }
 
-  getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, errorCallback) {
+  getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) {
     this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => {
       if (data.list) {
+        if (typeof (callback) == "function") callback(data.list)
         catalogList = catalogList.concat(data.list);
         if (catalogList.length < data.total) {
           currentPage ++
-          this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback)
+          this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback)
         }else {
-          if (typeof (callback) == "function") callback(catalogList)
+          if (typeof (endCallback) == "function") endCallback(catalogList)
         }
       }
     }, errorCallback)
diff --git a/web_src/src/layout/UiHeader.vue b/web_src/src/layout/UiHeader.vue
index a0a252c..0550593 100644
--- a/web_src/src/layout/UiHeader.vue
+++ b/web_src/src/layout/UiHeader.vue
@@ -1,9 +1,11 @@
 <template>
   <div id="UiHeader">
-    <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff"
-             active-text-color="#ffd04b" mode="horizontal">
+
+    <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#001529" text-color="#fff"
+             active-text-color="#1890ff" mode="horizontal">
+
       <el-menu-item index="/control">鎺у埗鍙�</el-menu-item>
-      <el-menu-item index="/live">瀹炴椂鐩戞帶</el-menu-item>
+      <el-menu-item index="/live">鍒嗗睆鐩戞帶</el-menu-item>
       <el-menu-item index="/deviceList">鍥芥爣璁惧</el-menu-item>
       <el-menu-item index="/map">鐢靛瓙鍦板浘</el-menu-item>
       <el-menu-item index="/pushVideoList">鎺ㄦ祦鍒楄〃</el-menu-item>
@@ -148,4 +150,8 @@
 #UiHeader .el-switch__label.is-active{
   color: #409EFF;
 }
+#UiHeader .el-menu-item.is-active {
+  color: #fff!important;
+  background-color: #1890ff!important;
+}
 </style>
diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js
index 8844862..2f44fd8 100644
--- a/web_src/src/router/index.js
+++ b/web_src/src/router/index.js
@@ -7,7 +7,7 @@
 import channelList from '../components/channelList.vue'
 import pushVideoList from '../components/PushVideoList.vue'
 import streamProxyList from '../components/StreamProxyList.vue'
-import devicePosition from  '../components/devicePosition.vue'
+import map from '../components/map.vue'
 import login from '../components/Login.vue'
 import parentPlatformList from '../components/ParentPlatformList.vue'
 import cloudRecord from '../components/CloudRecord.vue'
@@ -69,9 +69,9 @@
           component: parentPlatformList,
         },
         {
-          path: '/devicePosition/:deviceId/:parentChannelId/:count/:page',
-          name: 'devicePosition',
-          component: devicePosition,
+          path: '/map/:deviceId/:parentChannelId/:count/:page',
+          name: 'map',
+          component: map,
         },
         {
           path: '/cloudRecord',
@@ -100,8 +100,8 @@
         },
         {
           path: '/map',
-          name: 'devicePosition',
-          component: devicePosition,
+          name: 'map',
+          component: map,
         },
         ]
     },

--
Gitblit v1.8.0