From 2eb1ca2d94a09c2d3ced69de28de72d2d6d77d8e Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: 星期四, 03 三月 2022 15:57:28 +0800
Subject: [PATCH] 国标录像支持多端同时播放

---
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java                                           |   64 +-----
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java                                                 |   43 ++--
 src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java                                                                      |   10 
 src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java                                                        |    4 
 src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java                                                          |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java                                  |   11 +
 src/main/resources/logback-spring-local.xml                                                                                     |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java                                                     |    4 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java                                                         |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java                                          |    2 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java                                           |   11 
 src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java                                                    |   18 +
 web_src/src/components/dialog/devicePlayer.vue                                                                                  |   13 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java                                |    5 
 src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java                                                            |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java                         |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java                                          |    2 
 src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java                                                          |    9 +
 src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java                                                    |    9 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java                                                           |   29 ++-
 src/main/java/com/genersoft/iot/vmp/service/IPlayService.java                                                                   |    6 
 src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java                                                                  |   14 
 src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java                                                           |   81 +++++---
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java |    2 
 src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java                                                            |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java                                |   14 
 src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java                                                 |   17 +
 src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java                                                           |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java                                              |   70 ++++--
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java                                                          |    7 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java                                      |    9 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java                                                   |   42 ++--
 src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java                                                    |    2 
 33 files changed, 282 insertions(+), 230 deletions(-)

diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
index e16c1ad..0626384 100644
--- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
+++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
@@ -5,7 +5,7 @@
 public class StreamInfo {
 
     private String app;
-    private String streamId;
+    private String stream;
     private String deviceID;
     private String channelId;
     private String flv;
@@ -153,12 +153,12 @@
         this.ws_ts = ws_ts;
     }
 
-    public String getStreamId() {
-        return streamId;
+    public String getStream() {
+        return stream;
     }
 
-    public void setStreamId(String streamId) {
-        this.streamId = streamId;
+    public void setStream(String stream) {
+        this.stream = stream;
     }
 
     public String getRtc() {
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 923e411..0d63843 100644
--- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
+++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -29,6 +29,7 @@
 	// 姝ゅ澶氫簡涓�涓猒锛屾殏涓嶄慨鏀�
 	public static final String PLAYER_PREFIX = "VMP_PLAYER_";
 	public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
+	public static final String PLAY_INFO_PREFIX = "VMP_PLAY_INFO_";
 
 	public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
index 9700f8a..c2dedec 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
@@ -4,11 +4,12 @@
 
     private String deviceId;
     private String channelId;
-    private String ssrc;
-    private String streamId;
+    private String callId;
+    private String stream;
     private byte[] transaction;
     private byte[] dialog;
     private String mediaServerId;
+    private String ssrc;
 
     public String getDeviceId() {
         return deviceId;
@@ -26,20 +27,20 @@
         this.channelId = channelId;
     }
 
-    public String getSsrc() {
-        return ssrc;
+    public String getCallId() {
+        return callId;
     }
 
-    public void setSsrc(String ssrc) {
-        this.ssrc = ssrc;
+    public void setCallId(String callId) {
+        this.callId = callId;
     }
 
-    public String getStreamId() {
-        return streamId;
+    public String getStream() {
+        return stream;
     }
 
-    public void setStreamId(String streamId) {
-        this.streamId = streamId;
+    public void setStream(String stream) {
+        this.stream = stream;
     }
 
     public byte[] getTransaction() {
@@ -65,4 +66,12 @@
     public void setMediaServerId(String mediaServerId) {
         this.mediaServerId = mediaServerId;
     }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
 }
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 d41b04d..3e9f28a 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
@@ -14,6 +14,7 @@
 import gov.nist.javax.sip.stack.SIPDialog;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
 
 /**    
  * @description:瑙嗛娴乻ession绠$悊鍣紝绠$悊瑙嗛棰勮銆侀瑙堝洖鏀剧殑閫氫俊鍙ユ焺 
@@ -29,39 +30,55 @@
 	@Autowired
 	private UserSetup userSetup;
 
-	public void put(String deviceId, String channelId ,String ssrc, String streamId, String mediaServerId, ClientTransaction transaction){
+	/**
+	 * 娣诲姞涓�涓偣鎾�/鍥炴斁鐨勪簨鍔′俊鎭�
+	 * 鍚庣画鍙互閫氳繃娴両d/callID
+	 * @param deviceId 璁惧ID
+	 * @param channelId 閫氶亾ID
+	 * @param callId 涓�娆¤姹傜殑CallID
+	 * @param stream 娴佸悕绉�
+	 * @param mediaServerId 鎵�浣跨敤鐨勬祦濯掍綋ID
+	 * @param transaction 浜嬪姟
+	 */
+	public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, ClientTransaction transaction){
 		SsrcTransaction ssrcTransaction = new SsrcTransaction();
 		ssrcTransaction.setDeviceId(deviceId);
 		ssrcTransaction.setChannelId(channelId);
-		ssrcTransaction.setStreamId(streamId);
+		ssrcTransaction.setStream(stream);
 		byte[] transactionByteArray = SerializeUtils.serialize(transaction);
 		ssrcTransaction.setTransaction(transactionByteArray);
+		ssrcTransaction.setCallId(callId);
 		ssrcTransaction.setSsrc(ssrc);
 		ssrcTransaction.setMediaServerId(mediaServerId);
 
-		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" +  deviceId + "_" + channelId, ssrcTransaction);
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
+				+ "_" +  deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
+				+ "_" +  deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
 	}
 
-	public void put(String deviceId, String channelId , Dialog dialog){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+	public void put(String deviceId, String channelId, String callId, Dialog dialog){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
 		if (ssrcTransaction != null) {
 			byte[] dialogByteArray = SerializeUtils.serialize(dialog);
 			ssrcTransaction.setDialog(dialogByteArray);
 		}
-		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" +  deviceId + "_" + channelId, ssrcTransaction);
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
+				+ "_" +  deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_"
+				+ ssrcTransaction.getStream(), ssrcTransaction);
 	}
 
 	
-	public ClientTransaction getTransaction(String deviceId, String channelId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+	public ClientTransaction getTransactionByStream(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
 		if (ssrcTransaction == null) return null;
 		byte[] transactionByteArray = ssrcTransaction.getTransaction();
 		ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
 		return clientTransaction;
 	}
 
-	public SIPDialog getDialog(String deviceId, String channelId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+	public SIPDialog getDialogByStream(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
 		if (ssrcTransaction == null) return null;
 		byte[] dialogByteArray = ssrcTransaction.getDialog();
 		if (dialogByteArray == null) return null;
@@ -69,36 +86,37 @@
 		return dialog;
 	}
 
-	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId){
-		SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId);
-		return ssrcTransaction;
+	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
+		if (StringUtils.isEmpty(callId)) callId ="*";
+		if (StringUtils.isEmpty(stream)) stream ="*";
+		String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream;
+		List<Object> scanResult = redisUtil.scan(key);
+		if (scanResult.size() == 0) return null;
+		return (SsrcTransaction)redisUtil.get((String) scanResult.get(0));
 	}
 
-	public String getStreamId(String deviceId, String channelId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
-		if (ssrcTransaction == null) return null;
-		return ssrcTransaction.getStreamId();
-	}
-	public String getMediaServerId(String deviceId, String channelId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+	public String getMediaServerId(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
 		if (ssrcTransaction == null) return null;
 		return ssrcTransaction.getMediaServerId();
 	}
 
-	public String getSSRC(String deviceId, String channelId){
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+	public String getSSRC(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
 		if (ssrcTransaction == null) return null;
 		return ssrcTransaction.getSsrc();
 	}
 	
-	public void remove(String deviceId, String channelId) {
-		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+	public void remove(String deviceId, String channelId, String stream) {
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
 		if (ssrcTransaction == null) return;
-		redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" +  deviceId + "_" + channelId);
+		redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_"
+				+  deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream());
 	}
 
+
 	public List<SsrcTransaction> getAllSsrc() {
-		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetup.getServerId() + "_" ));
+		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetup.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/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
index 8be07ca..e76f427 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
@@ -119,8 +119,8 @@
 	/**
 	 * 瑙嗛娴佸仠姝�
 	 */
-	void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
-	void streamByeCmd(String deviceId, String channelId);
+	void streamByeCmd(String deviceId, String channelId, String ssrc, SipSubscribe.Event okEvent);
+	void streamByeCmd(String deviceId, String channelId, String ssrc);
 
 	/**
 	 * 鍥炴斁鏆傚仠
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 4c97315..d6294fa 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
@@ -17,7 +17,7 @@
      * @return
      */
     boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent);
-    boolean register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent);
+    boolean register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain);
 
     /**
      * 鍚戜笂绾у钩鍙版敞閿�
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
index 947dc94..c0e7281 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -128,7 +128,15 @@
 
 
 		Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(Request.REGISTER), fromTag, viaTag, callIdHeader);
-
+		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
+		if (www == null) {
+			AuthorizationHeader authorizationHeader = sipFactory.createHeaderFactory().createAuthorizationHeader("Digest");
+			authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
+			authorizationHeader.setURI(requestURI);
+			authorizationHeader.setAlgorithm("MD5");
+			registerRequest.addHeader(authorizationHeader);
+			return  registerRequest;
+		}
 		String realm = www.getRealm();
 		String nonce = www.getNonce();
 		String scheme = www.getScheme();
@@ -139,7 +147,6 @@
 
 		callIdHeader.setCallId(callId);
 
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 		String cNonce = null;
 		String nc = "00000001";
 		if (qop != null) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
index 2da4c46..f4a0ec4 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
@@ -226,7 +226,7 @@
 			throws PeerUnavailableException, ParseException, InvalidArgumentException {
 		Request request = null;
 		if (streamInfo == null) return null;
-		Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId());
+		Dialog dialog = streamSession.getDialogByStream(streamInfo.getDeviceID(), streamInfo.getChannelId(), streamInfo.getStream());
 
 		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(),
 				device.getHostAddress());
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 29f8c05..734fe23 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
@@ -331,7 +331,7 @@
 	  */
 	@Override
 	public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
-		String streamId = ssrcInfo.getStreamId();
+		String streamId = ssrcInfo.getStream();
 		try {
 			if (device == null) return;
 			String streamMode = device.getStreamMode().toUpperCase();
@@ -404,6 +404,8 @@
 			}
 
 			content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc
+			// f瀛楁:f= v/缂栫爜鏍煎紡/鍒嗚鲸鐜�/甯х巼/鐮佺巼绫诲瀷/鐮佺巼澶у皬a/缂栫爜鏍煎紡/鐮佺巼澶у皬/閲囨牱鐜�
+//			content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 鏈彂鐜版敮鎸佹鐗规�х殑璁惧
 
 			String tm = Long.toString(System.currentTimeMillis());
 
@@ -412,14 +414,14 @@
 
 			Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader);
 
-			String finalStreamId = streamId;
 			transmitRequest(device, request, (e -> {
-				streamSession.remove(device.getDeviceId(), channelId);
+				streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
 				mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
 				errorEvent.response(e);
 			}), e ->{
-				streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction());
-				streamSession.put(device.getDeviceId(), channelId , e.dialog);
+				// 杩欓噷涓轰緥閬垮厤涓�涓�氶亾鐨勭偣鎾彧鏈変竴涓猚allID杩欎釜鍙傛暟浣跨敤涓�涓浐瀹氬��
+				streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction());
+				streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
 			});
 
 			
@@ -441,12 +443,12 @@
 			, SipSubscribe.Event errorEvent) {
 		try {
 
-			logger.info("{} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+			logger.info("{} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
 
 			// 娣诲姞璁㈤槄
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
-			subscribeKey.put("stream", ssrcInfo.getStreamId());
+			subscribeKey.put("stream", ssrcInfo.getStream());
 			subscribeKey.put("regist", true);
 			subscribeKey.put("mediaServerId", mediaServerItem.getId());
 			logger.debug("褰曞儚鍥炴斁娣诲姞璁㈤槄锛岃闃呭唴瀹癸細" + subscribeKey.toString());
@@ -465,8 +467,6 @@
 	        content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
 	        content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
 					+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
-
-
 
 			String streamMode = device.getStreamMode().toUpperCase();
 
@@ -527,8 +527,8 @@
 
 	        transmitRequest(device, request, errorEvent, okEvent -> {
 				ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
-	        	streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), responseEvent.getClientTransaction());
-				streamSession.put(device.getDeviceId(), channelId, okEvent.dialog);
+	        	streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction());
+				streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);
 			});
 		} catch ( SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
@@ -548,12 +548,12 @@
 	public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
 			, SipSubscribe.Event errorEvent) {
 		try {
-			logger.info("{} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+			logger.info("{} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
 
 			// 娣诲姞璁㈤槄
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
-			subscribeKey.put("stream", ssrcInfo.getStreamId());
+			subscribeKey.put("stream", ssrcInfo.getStream());
 			subscribeKey.put("regist", true);
 			subscribeKey.put("mediaServerId", mediaServerItem.getId());
 			logger.debug("褰曞儚鍥炴斁娣诲姞璁㈤槄锛岃闃呭唴瀹癸細" + subscribeKey.toString());
@@ -634,7 +634,8 @@
 	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
 
 	        ClientTransaction transaction = transmitRequest(device, request, errorEvent);
-	        streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
+	        streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), transaction);
+	        streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), transaction);
 
 		} catch ( SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
@@ -645,17 +646,17 @@
 	 * 瑙嗛娴佸仠姝�, 涓嶄娇鐢ㄥ洖璋�
 	 */
 	@Override
-	public void streamByeCmd(String deviceId, String channelId) {
-		streamByeCmd(deviceId, channelId, null);
+	public void streamByeCmd(String deviceId, String channelId, String stream) {
+		streamByeCmd(deviceId, channelId, stream, null);
 	}
 
 	/**
 	 * 瑙嗛娴佸仠姝�
 	 */
 	@Override
-	public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) {
+	public void streamByeCmd(String deviceId, String channelId, String stream, SipSubscribe.Event okEvent) {
 		try {
-			ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
+			ClientTransaction transaction = streamSession.getTransactionByStream(deviceId, channelId, stream);
 			if (transaction == null) {
 				logger.warn("[ {} -> {}]鍋滄瑙嗛娴佺殑鏃跺�欏彂鐜颁簨鍔″凡涓㈠け", deviceId, channelId);
 				SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
@@ -664,7 +665,7 @@
 				}
 				return;
 			}
-			SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
+			SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, stream);
 			if (dialog == null) {
 				logger.warn("[ {} -> {}]鍋滄瑙嗛娴佺殑鏃跺�欏彂鐜板璇濆凡涓㈠け", deviceId, channelId);
 				return;
@@ -708,11 +709,11 @@
 
 			dialog.sendRequest(clientTransaction);
 
-			SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId);
+			SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, callIdHeader.getCallId(), null);
 			if (ssrcTransaction != null) {
 				MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
 				mediaServerService.releaseSsrc(mediaServerItem, ssrcTransaction.getSsrc());
-				streamSession.remove(deviceId, channelId);
+				streamSession.remove(deviceId, channelId, ssrcTransaction.getStream());
 			}
 		} catch (SipException | ParseException e) {
 			e.printStackTrace();
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 2165262..5dcffe7 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
@@ -53,7 +53,7 @@
 
     @Override
     public boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
-        return register(parentPlatform, null, null, errorEvent, okEvent);
+        return register(parentPlatform, null, null, errorEvent, okEvent, false);
     }
 
     @Override
@@ -65,15 +65,16 @@
             redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
         }
 
-        return register(parentPlatform, null, null, errorEvent, okEvent);
+        return register(parentPlatform, null, null, errorEvent, okEvent, false);
     }
 
     @Override
-    public boolean register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
+    public boolean register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
+                            SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain) {
         try {
             Request request = null;
             String tm = Long.toString(System.currentTimeMillis());
-            if (www == null ) {
+            if (!registerAgain ) {
                 //		//callid
                 CallIdHeader callIdHeader = null;
                 if(parentPlatform.getTransport().equals("TCP")) {
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 127ef29..98ae86f 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
@@ -72,10 +72,10 @@
 			if (deviceId == null) {
 				streamInfo = new StreamInfo();
 				streamInfo.setApp(sendRtpItem.getApp());
-				streamInfo.setStreamId(sendRtpItem.getStreamId());
+				streamInfo.setStream(sendRtpItem.getStreamId());
 			}else {
 				streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
-				sendRtpItem.setStreamId(streamInfo.getStreamId());
+				sendRtpItem.setStreamId(streamInfo.getStream());
 				streamInfo.setApp("rtp");
 			}
 
@@ -85,7 +85,7 @@
 			Map<String, Object> param = new HashMap<>();
 			param.put("vhost","__defaultVhost__");
 			param.put("app",streamInfo.getApp());
-			param.put("stream",streamInfo.getStreamId());
+			param.put("stream",streamInfo.getStream());
 			param.put("ssrc", sendRtpItem.getSsrc());
 			param.put("dst_url",sendRtpItem.getIp());
 			param.put("dst_port", sendRtpItem.getPort());
@@ -98,21 +98,21 @@
 				try {
 					if (System.currentTimeMillis() - startTime < 30 * 1000) {
 						MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
-						if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
+						if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStream())) {
 							rtpPushed = true;
 							logger.info("宸茶幏鍙栬澶囨帹娴乕{}/{}]锛屽紑濮嬪悜涓婄骇鎺ㄦ祦[{}:{}]",
-									streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
+									streamInfo.getApp() ,streamInfo.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort());
 							zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
 						} else {
 							logger.info("绛夊緟璁惧鎺ㄦ祦[{}/{}].......",
-									streamInfo.getApp() ,streamInfo.getStreamId());
+									streamInfo.getApp() ,streamInfo.getStream());
 							Thread.sleep(1000);
 							continue;
 						}
 					} else {
 						rtpPushed = true;
 						logger.info("璁惧鎺ㄦ祦[{}/{}]瓒呮椂锛岀粓姝㈠悜涓婄骇鎺ㄦ祦",
-								streamInfo.getApp() ,streamInfo.getStreamId());
+								streamInfo.getApp() ,streamInfo.getStream());
 					}
 				} catch (InterruptedException e) {
 					e.printStackTrace();
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 feb44c5..eb25cde 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
@@ -89,18 +89,19 @@
 					redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
 					if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
 						logger.info(streamId + "鏃犲叾瀹冭鐪嬭�咃紝閫氱煡璁惧鍋滄鎺ㄦ祦");
-						cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
+						cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId, streamId);
 					}
 				}
 				// 鍙兘鏄澶囦富鍔ㄥ仠姝�
 				Device device = storager.queryVideoDeviceByChannelId(platformGbId);
 				if (device != null) {
 					StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
+
 					if (streamInfo != null) {
 						redisCatchStorage.stopPlay(streamInfo);
 					}
 					storager.stopPlay(device.getDeviceId(), channelId);
-					mediaServerService.closeRTPServer(device, channelId);
+					mediaServerService.closeRTPServer(device, channelId, streamInfo.getStream());
 				}
 			}
 		} 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 c61b727..3c83ec2 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
@@ -62,7 +62,7 @@
             StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(device.getDeviceId(), "*");
             if (streamInfo != null) {
                 redisCatchStorage.stopPlayback(streamInfo);
-                cmder.streamByeCmd(streamInfo.getDeviceID(), streamInfo.getChannelId());
+                cmder.streamByeCmd(streamInfo.getDeviceID(), streamInfo.getChannelId(), streamInfo.getStream());
             }
         }
     }
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 a5dced3..b6040aa 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
@@ -78,7 +78,7 @@
 
 		if (response.getStatusCode() == 401) {
 			WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
-			sipCommanderForPlatform.register(parentPlatform, callId, www, null, null);
+			sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true);
 		}else if (response.getStatusCode() == 200){
 			// 娉ㄥ唽/娉ㄩ攢鎴愬姛
 			logger.info(String.format("%s %s鎴愬姛", platformGBId, action));
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 424188d..f8e872f 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
@@ -360,6 +360,7 @@
 							StreamPushItem streamPushItem = null;
 							StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, streamId, tracks);
 							item.setStreamInfo(streamInfoByAppAndStream);
+
 							redisCatchStorage.addStream(mediaServerItem, type, app, streamId, item);
 							if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
 									|| item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
@@ -438,14 +439,16 @@
 				if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
 					ret.put("close", false);
 				} else {
-					cmder.streamByeCmd(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
+					cmder.streamByeCmd(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId(),
+							streamInfoForPlayCatch.getStream());
 					redisCatchStorage.stopPlay(streamInfoForPlayCatch);
 					storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
 				}
 			}else{
 				StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlaybackByStreamId(streamId);
 				if (streamInfoForPlayBackCatch != null) {
-					cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(), streamInfoForPlayBackCatch.getChannelId());
+					cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(),
+							streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream());
 					redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch);
 				}else {
 					StreamInfo streamInfoForDownload = redisCatchStorage.queryDownloadByStreamId(streamId);
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
index 9b5b519..ff8204c 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
@@ -91,7 +91,8 @@
                 }
             }
             if (null != result && result){
-                eventMap.remove(key);
+                // TODO 鎶ラ敊鏈鐞�
+                iterator.remove();
             }
         }
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
index ec2714f..3a4c3ee 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -46,7 +46,7 @@
 
     SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean isPlayback);
 
-    void closeRTPServer(Device device, String channelId);
+    void closeRTPServer(Device device, String channelId, String ssrc);
 
     void clearRTPServer(MediaServerItem mediaServerItem);
 
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 8a7437c..12bb8fa 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -5,14 +5,16 @@
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.context.request.async.DeferredResult;
 
 /**
  * 鐐规挱澶勭悊
  */
 public interface IPlayService {
 
-    void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
     void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
 
     PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
@@ -20,4 +22,6 @@
     MediaServerItem getNewMediaServerItem(Device device);
 
     void onPublishHandlerForDownload(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String toString);
+
+    DeferredResult<ResponseEntity<String>> playBack(String deviceId, String channelId, String startTime, String endTime, PlayBackCallback errorCallBack);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java b/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java
new file mode 100644
index 0000000..089523f
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java
@@ -0,0 +1,9 @@
+package com.genersoft.iot.vmp.service.bean;
+
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+
+public interface PlayBackCallback {
+
+    void call(RequestMessage msg);
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java
index faab1c8..1723bc5 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java
@@ -4,12 +4,12 @@
 
     private int port;
     private String ssrc;
-    private String StreamId;
+    private String Stream;
 
-    public SSRCInfo(int port, String ssrc, String streamId) {
+    public SSRCInfo(int port, String ssrc, String stream) {
         this.port = port;
         this.ssrc = ssrc;
-        StreamId = streamId;
+        Stream = stream;
     }
 
     public int getPort() {
@@ -28,11 +28,11 @@
         this.ssrc = ssrc;
     }
 
-    public String getStreamId() {
-        return StreamId;
+    public String getStream() {
+        return Stream;
     }
 
-    public void setStreamId(String streamId) {
-        StreamId = streamId;
+    public void setStream(String stream) {
+        Stream = stream;
     }
 }
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 159cd05..6a5642e 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
@@ -162,15 +162,16 @@
     }
 
     @Override
-    public void closeRTPServer(Device device, String channelId) {
-        String mediaServerId = streamSession.getMediaServerId(device.getDeviceId(), channelId);
+    public void closeRTPServer(Device device, String channelId, String stream) {
+        String mediaServerId = streamSession.getMediaServerId(device.getDeviceId(), channelId, stream);
+        String ssrc = streamSession.getSSRC(device.getDeviceId(), channelId, stream);
         MediaServerItem mediaServerItem = this.getOne(mediaServerId);
         if (mediaServerItem != null) {
             String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
             zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId);
-            releaseSsrc(mediaServerItem, streamSession.getSSRC(device.getDeviceId(), channelId));
+            releaseSsrc(mediaServerItem, ssrc);
         }
-        streamSession.remove(device.getDeviceId(), channelId);
+        streamSession.remove(device.getDeviceId(), channelId, stream);
     }
 
     @Override
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
index 6e3deba..3c776b4 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -74,7 +74,7 @@
     @Override
     public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr) {
         StreamInfo streamInfoResult = new StreamInfo();
-        streamInfoResult.setStreamId(stream);
+        streamInfoResult.setStream(stream);
         streamInfoResult.setApp(app);
         if (addr == null) {
             addr = mediaInfo.getStreamIp();
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 cf30a79..9c87a0a 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
@@ -16,10 +16,10 @@
 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.bean.PlayBackCallback;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
 import com.genersoft.iot.vmp.service.IMediaService;
@@ -52,9 +52,6 @@
 
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
-
-    @Autowired
-    private RedisUtil redis;
 
     @Autowired
     private DeferredResultHolder resultHolder;
@@ -104,19 +101,21 @@
             logger.warn(String.format("璁惧鐐规挱瓒呮椂锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId));
             WVPResult wvpResult = new WVPResult();
             wvpResult.setCode(-1);
-            SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
+            SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, streamInfo.getStream());
             if (dialog != null) {
                 wvpResult.setMsg("鏀舵祦瓒呮椂锛岃绋嶅�欓噸璇�");
             }else {
                 wvpResult.setMsg("鐐规挱瓒呮椂锛岃绋嶅�欓噸璇�");
             }
+
             msg.setData(wvpResult);
             // 鐐规挱瓒呮椂鍥炲BYE
-            cmder.streamByeCmd(device.getDeviceId(), channelId);
+            cmder.streamByeCmd(device.getDeviceId(), channelId, streamInfo.getStream());
             // 閲婃斁rtpserver
-            mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
+            mediaServerService.closeRTPServer(playResult.getDevice(), channelId, streamInfo.getStream());
             // 鍥炲涔嬪墠鎵�鏈夌殑鐐规挱璇锋眰
             resultHolder.invokeAllResult(msg);
+            // TODO 閲婃斁ssrc
         });
         result.onCompletion(()->{
             // 鐐规挱缁撴潫鏃惰皟鐢ㄦ埅鍥炬帴鍙�
@@ -154,14 +153,12 @@
             }
         });
         if (streamInfo == null) {
-            SSRCInfo ssrcInfo;
             String streamId = null;
             if (mediaServerItem.isRtpEnable()) {
                 streamId = String.format("%s_%s", device.getDeviceId(), channelId);
             }
 
-            ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId);
-
+            SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId);
             // 鍙戦�佺偣鎾秷鎭�
             cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
                 logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString());
@@ -173,7 +170,7 @@
                 WVPResult wvpResult = new WVPResult();
                 wvpResult.setCode(-1);
                 // 鐐规挱杩斿洖sip閿欒
-                mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
+                mediaServerService.closeRTPServer(playResult.getDevice(), channelId, ssrcInfo.getStream());
                 wvpResult.setMsg(String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg));
                 msg.setData(wvpResult);
                 resultHolder.invokeAllResult(msg);
@@ -184,7 +181,7 @@
 
             });
         } else {
-            String streamId = streamInfo.getStreamId();
+            String streamId = streamInfo.getStream();
             if (streamId == null) {
                 WVPResult wvpResult = new WVPResult();
                 wvpResult.setCode(-1);
@@ -213,18 +210,16 @@
                 // TODO 鐐规挱鍓嶆槸鍚﹂噸缃姸鎬�
                 redisCatchStorage.stopPlay(streamInfo);
                 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
-                SSRCInfo ssrcInfo;
                 String streamId2 = null;
                 if (mediaServerItem.isRtpEnable()) {
                     streamId2 = String.format("%s_%s", device.getDeviceId(), channelId);
                 }
-                ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId2);
-
+                SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId2);
                 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
                     logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString());
                     onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid);
                 }, (event) -> {
-                    mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
+                    mediaServerService.closeRTPServer(playResult.getDevice(), channelId, ssrcInfo.getStream());
                     WVPResult wvpResult = new WVPResult();
                     wvpResult.setCode(-1);
                     wvpResult.setMsg(String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg));
@@ -242,12 +237,12 @@
         RequestMessage msg = new RequestMessage();
         msg.setId(uuid);
         msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId);
-        StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
+        StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId);
         if (streamInfo != null) {
             DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
             if (deviceChannel != null) {
-                deviceChannel.setStreamId(streamInfo.getStreamId());
-                storager.startPlay(deviceId, channelId, streamInfo.getStreamId());
+                deviceChannel.setStreamId(streamInfo.getStream());
+                storager.startPlay(deviceId, channelId, streamInfo.getStream());
             }
             redisCatchStorage.startPlay(streamInfo);
             msg.setData(JSON.toJSONString(streamInfo));
@@ -284,21 +279,45 @@
 
 
     @Override
-    public void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
+    public DeferredResult<ResponseEntity<String>> playBack(String deviceId, String channelId, String startTime, String endTime, PlayBackCallback callback) {
+        String uuid = UUID.randomUUID().toString();
+        String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(30000L);
+        Device device = storager.queryVideoDevice(deviceId);
+        if (device == null) {
+            result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
+            return result;
+        }
+
+        MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
+        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
+        resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId, uuid, result);
         RequestMessage msg = new RequestMessage();
-        msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId);
         msg.setId(uuid);
-        StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
-        if (streamInfo != null) {
+        msg.setKey(key);
+        result.onTimeout(()->{
+            msg.setData("鍥炴斁瓒呮椂");
+            callback.call(msg);
+        });
+        cmder.playbackStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, (MediaServerItem mediaServerItem, JSONObject response) -> {
+            logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString());
+            StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId);
+            if (streamInfo == null) {
+                logger.warn("璁惧鍥炴斁API璋冪敤澶辫触锛�");
+                msg.setData("璁惧鍥炴斁API璋冪敤澶辫触锛�");
+                callback.call(msg);
+                return;
+            }
             redisCatchStorage.startPlayback(streamInfo);
             msg.setData(JSON.toJSONString(streamInfo));
-            resultHolder.invokeResult(msg);
-        } else {
-            logger.warn("璁惧鍥炴斁API璋冪敤澶辫触锛�");
-            msg.setData("璁惧鍥炴斁API璋冪敤澶辫触锛�");
-            resultHolder.invokeResult(msg);
-        }
+            callback.call(msg);
+        }, event -> {
+            msg.setData(String.format("鍥炴斁澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg));
+            callback.call(msg);
+        });
+        return result;
     }
+
 
 
     @Override
@@ -306,7 +325,7 @@
         RequestMessage msg = new RequestMessage();
         msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId);
         msg.setId(uuid);
-        StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId, uuid);
+        StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId);
         if (streamInfo != null) {
             redisCatchStorage.startDownload(streamInfo);
             msg.setData(JSON.toJSONString(streamInfo));
@@ -319,7 +338,7 @@
     }
 
 
-    public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
+    public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId) {
         String streamId = resonse.getString("stream");
         JSONArray tracks = resonse.getJSONArray("tracks");
         StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
index afac6eb..564deb5 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -132,7 +132,7 @@
                 }else {
                     streamLive = true;
                     StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
-                            mediaInfo, param.getApp(), param.getStream(), null);
+                            mediaInfo, param.getApp(), param.getStream(), null, null);
                     wvpResult.setData(streamInfo);
 
                 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
index edd6cbc..1a93902 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
@@ -7,6 +7,7 @@
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.service.bean.ThirdPartyGB;
 
 import java.util.List;
@@ -220,4 +221,5 @@
     void addMemInfo(double memInfo);
 
     void addNetInfo(Map<String, String> networkInterfaces);
+
 }
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 92fdf6c..0a78a53 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
@@ -10,6 +10,7 @@
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.service.bean.ThirdPartyGB;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
@@ -91,7 +92,8 @@
      */
     @Override
     public boolean startPlay(StreamInfo stream) {
-        return redis.set(String.format("%S_%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, userSetup.getServerId(), stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()),
+        return redis.set(String.format("%S_%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, userSetup.getServerId(),
+                        stream.getStream(), stream.getDeviceID(), stream.getChannelId()),
                 stream);
     }
 
@@ -105,7 +107,7 @@
         if (streamInfo == null) return false;
         return redis.del(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
                 userSetup.getServerId(),
-                streamInfo.getStreamId(),
+                streamInfo.getStream(),
                 streamInfo.getDeviceID(),
                 streamInfo.getChannelId()));
     }
@@ -119,7 +121,7 @@
         return (StreamInfo)redis.get(String.format("%S_%s_%s_%s_%s",
                 VideoManagerConstants.PLAYER_PREFIX,
                 userSetup.getServerId(),
-                streamInfo.getStreamId(),
+                streamInfo.getStream(),
                 streamInfo.getDeviceID(),
                 streamInfo.getChannelId()));
     }
@@ -165,14 +167,14 @@
 
     @Override
     public boolean startPlayback(StreamInfo stream) {
-        return redis.set(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, userSetup.getServerId(),stream.getStreamId(),
-                        stream.getDeviceID(), stream.getChannelId()), stream);
+        return redis.set(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
+                userSetup.getServerId(), stream.getStream(), stream.getDeviceID(), stream.getChannelId()), stream);
     }
 
     @Override
     public boolean startDownload(StreamInfo streamInfo) {
-        return redis.set(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, userSetup.getServerId(),streamInfo.getStreamId(),
-                        streamInfo.getDeviceID(), streamInfo.getChannelId()), streamInfo);
+        return redis.set(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, userSetup.getServerId(),
+                streamInfo.getStream(), streamInfo.getDeviceID(), streamInfo.getChannelId()), streamInfo);
     }
 
     @Override
@@ -186,7 +188,7 @@
         }
         return redis.del(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
                 userSetup.getServerId(),
-                streamInfo.getStreamId(),
+                streamInfo.getStream(),
                 streamInfo.getDeviceID(),
                 streamInfo.getChannelId()));
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
index 5ab940c..bd8f379 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.storager.impl;
 
+import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
@@ -156,7 +157,10 @@
 	public synchronized void updateChannel(String deviceId, DeviceChannel channel) {
 		String channelId = channel.getChannelId();
 		channel.setDeviceId(deviceId);
-		channel.setStreamId(streamSession.getStreamId(deviceId, channel.getChannelId()));
+		StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
+		if (streamInfo != null) {
+			channel.setStreamId(streamInfo.getStream());
+		}
 		String now = this.format.format(System.currentTimeMillis());
 		channel.setUpdateTime(now);
 		DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId);
@@ -178,7 +182,10 @@
 			if (channelList.size() == 0) {
 				for (DeviceChannel channel : channels) {
 					channel.setDeviceId(deviceId);
-					channel.setStreamId(streamSession.getStreamId(deviceId, channel.getChannelId()));
+					StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
+					if (streamInfo != null) {
+						channel.setStreamId(streamInfo.getStream());
+					}
 					String now = this.format.format(System.currentTimeMillis());
 					channel.setUpdateTime(now);
 					channel.setCreateTime(now);
@@ -189,9 +196,11 @@
 					channelsInStore.put(deviceChannel.getChannelId(), deviceChannel);
 				}
 				for (DeviceChannel channel : channels) {
-					String channelId = channel.getChannelId();
 					channel.setDeviceId(deviceId);
-					channel.setStreamId(streamSession.getStreamId(deviceId, channel.getChannelId()));
+					StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
+					if (streamInfo != null) {
+						channel.setStreamId(streamInfo.getStream());
+					}
 					String now = this.format.format(System.currentTimeMillis());
 					channel.setUpdateTime(now);
 					if (channelsInStore.get(channel.getChannelId()) != null) {
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
index c22a558..8350d29 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -110,26 +110,26 @@
 		String key = DeferredResultHolder.CALLBACK_CMD_STOP + deviceId + channelId;
 		resultHolder.put(key, uuid, result);
 		Device device = storager.queryVideoDevice(deviceId);
-		cmder.streamByeCmd(deviceId, channelId, (event) -> {
-			StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
-			if (streamInfo == null) {
-				RequestMessage msg = new RequestMessage();
-				msg.setId(uuid);
-				msg.setKey(key);
-				msg.setData("鐐规挱鏈壘鍒�");
-				resultHolder.invokeAllResult(msg);
-				storager.stopPlay(deviceId, channelId);
-			}else {
-				redisCatchStorage.stopPlay(streamInfo);
-				storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
-				RequestMessage msg = new RequestMessage();
-				msg.setId(uuid);
-				msg.setKey(key);
-				//Response response = event.getResponse();
-				msg.setData(String.format("success"));
-				resultHolder.invokeAllResult(msg);
-			}
-			mediaServerService.closeRTPServer(device, channelId);
+		StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
+		if (streamInfo == null) {
+			RequestMessage msg = new RequestMessage();
+			msg.setId(uuid);
+			msg.setKey(key);
+			msg.setData("鐐规挱鏈壘鍒�");
+			resultHolder.invokeAllResult(msg);
+			storager.stopPlay(deviceId, channelId);
+			return result;
+		}
+		cmder.streamByeCmd(deviceId, channelId, streamInfo.getStream(), (event) -> {
+			redisCatchStorage.stopPlay(streamInfo);
+			storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
+			RequestMessage msg = new RequestMessage();
+			msg.setId(uuid);
+			msg.setKey(key);
+			//Response response = event.getResponse();
+			msg.setData(String.format("success"));
+			resultHolder.invokeAllResult(msg);
+			mediaServerService.closeRTPServer(device, channelId, streamInfo.getStream());
 		});
 
 		if (deviceId != null || channelId != null) {
@@ -329,7 +329,7 @@
 			jsonObject.put("deviceId", transaction.getDeviceId());
 			jsonObject.put("channelId", transaction.getChannelId());
 			jsonObject.put("ssrc", transaction.getSsrc());
-			jsonObject.put("streamId", transaction.getStreamId());
+			jsonObject.put("streamId", transaction.getStream());
 			objects.add(jsonObject);
 		}
 
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java
index 3f846c6..c2053e6 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java
@@ -96,7 +96,7 @@
 		StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
 		if (streamInfo != null) {
 			// 鍋滄涔嬪墠鐨勪笅杞�
-			cmder.streamByeCmd(deviceId, channelId);
+			cmder.streamByeCmd(deviceId, channelId, streamInfo.getStream());
 		}
 
 		MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
@@ -114,7 +114,7 @@
 
 		cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (MediaServerItem mediaServerItem, JSONObject response) -> {
 			logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString());
-			playService.onPublishHandlerForDownload(mediaServerItem, response, deviceId, channelId, uuid.toString());
+			playService.onPublishHandlerForDownload(mediaServerItem, response, deviceId, channelId, uuid);
 		}, event -> {
 			RequestMessage msg = new RequestMessage();
 			msg.setId(uuid);
@@ -130,11 +130,12 @@
 	@ApiImplicitParams({
 			@ApiImplicitParam(name = "deviceId", value = "璁惧ID", dataTypeClass = String.class),
 			@ApiImplicitParam(name = "channelId", value = "閫氶亾ID", dataTypeClass = String.class),
+			@ApiImplicitParam(name = "stream", value = "娴両D", dataTypeClass = String.class),
 	})
-	@GetMapping("/stop/{deviceId}/{channelId}")
-	public ResponseEntity<String> playStop(@PathVariable String deviceId, @PathVariable String channelId) {
+	@GetMapping("/stop/{deviceId}/{channelId}/{stream}")
+	public ResponseEntity<String> playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
 
-		cmder.streamByeCmd(deviceId, channelId);
+		cmder.streamByeCmd(deviceId, channelId, stream);
 
 		if (logger.isDebugEnabled()) {
 			logger.debug(String.format("璁惧鍘嗗彶濯掍綋涓嬭浇鍋滄 API璋冪敤锛宒eviceId/channelId锛�%s_%s", deviceId, channelId));
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
index 8f83e7c..3607a8d 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
@@ -18,6 +18,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -75,52 +76,8 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug(String.format("璁惧鍥炴斁 API璋冪敤锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId));
 		}
-		String uuid = UUID.randomUUID().toString();
-		String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
-		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
-		Device device = storager.queryVideoDevice(deviceId);
-		if (device == null) {
-			result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
-			return result;
-		}
-		MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
-		SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
 
-		// 瓒呮椂澶勭悊
-		result.onTimeout(()->{
-			logger.warn(String.format("璁惧鍥炴斁瓒呮椂锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId));
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData("Timeout");
-			resultHolder.invokeResult(msg);
-		});
-
-		StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
-		if (streamInfo != null) {
-			// 鍋滄涔嬪墠鐨勫洖鏀�
-			cmder.streamByeCmd(deviceId, channelId);
-		}
-		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId, uuid, result);
-
-		if (newMediaServerItem == null) {
-			logger.warn(String.format("璁惧鍥炴斁瓒呮椂锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId));
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData("Timeout");
-			resultHolder.invokeResult(msg);
-			return result;
-		}
-
-		cmder.playbackStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, (MediaServerItem mediaServerItem, JSONObject response) -> {
-			logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString());
-			playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
-		}, event -> {
-			RequestMessage msg = new RequestMessage();
-			msg.setId(uuid);
-			msg.setKey(key);
-			msg.setData(String.format("鍥炴斁澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg));
+		DeferredResult<ResponseEntity<String>> result = playService.playBack(deviceId, channelId, startTime, endTime, msg->{
 			resultHolder.invokeResult(msg);
 		});
 
@@ -131,24 +88,31 @@
 	@ApiImplicitParams({
 			@ApiImplicitParam(name = "deviceId", value = "璁惧ID", dataTypeClass = String.class),
 			@ApiImplicitParam(name = "channelId", value = "閫氶亾ID", dataTypeClass = String.class),
+			@ApiImplicitParam(name = "stream", value = "娴両D", dataTypeClass = String.class),
 	})
-	@GetMapping("/stop/{deviceId}/{channelId}")
-	public ResponseEntity<String> playStop(@PathVariable String deviceId, @PathVariable String channelId) {
+	@GetMapping("/stop/{deviceId}/{channelId}/{stream}")
+	public ResponseEntity<String> playStop(
+			@PathVariable String deviceId,
+			@PathVariable String channelId,
+			@PathVariable String stream) {
 
-		cmder.streamByeCmd(deviceId, channelId);
+		cmder.streamByeCmd(deviceId, channelId, stream);
 
 		if (logger.isDebugEnabled()) {
 			logger.debug(String.format("璁惧褰曞儚鍥炴斁鍋滄 API璋冪敤锛宒eviceId/channelId锛�%s/%s", deviceId, channelId));
+		}
+		if (StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(channelId) || StringUtils.isEmpty(stream)) {
+			return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
 		}
 
 		if (deviceId != null && channelId != null) {
 			JSONObject json = new JSONObject();
 			json.put("deviceId", deviceId);
 			json.put("channelId", channelId);
-			return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
+			return new ResponseEntity<>(json.toString(), HttpStatus.OK);
 		} else {
 			logger.warn("璁惧褰曞儚鍥炴斁鍋滄API璋冪敤澶辫触锛�");
-			return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
+			return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
 		}
 	}
 
diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
index 70f9811..853ec56 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
@@ -103,7 +103,7 @@
         PlayResult play = playService.play(newMediaServerItem, serial, code, (mediaServerItem, response)->{
             StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code);
             JSONObject result = new JSONObject();
-            result.put("StreamID", streamInfo.getStreamId());
+            result.put("StreamID", streamInfo.getStream());
             result.put("DeviceID", device.getDeviceId());
             result.put("ChannelID", code);
             result.put("ChannelName", deviceChannel.getName());
@@ -177,7 +177,7 @@
             result.put("error","鏈壘鍒版祦淇℃伅");
             return result;
         }
-        cmder.streamByeCmd(serial, code);
+        cmder.streamByeCmd(serial, code, streamInfo.getStream());
         redisCatchStorage.stopPlay(streamInfo);
         storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
         return null;
diff --git a/src/main/resources/logback-spring-local.xml b/src/main/resources/logback-spring-local.xml
index 90fe940..9951c69 100644
--- a/src/main/resources/logback-spring-local.xml
+++ b/src/main/resources/logback-spring-local.xml
@@ -83,7 +83,7 @@
 	<logger name="com.genersoft.iot.vmp.storager.dao" level="INFO">
 		<appender-ref ref="STDOUT"/>
 	</logger>
-	<logger name="com.genersoft.iot.vmp.gb28181" level="DEBUG">
+	<logger name="com.genersoft.iot.vmp.gb28181" level="INFO">
 		<appender-ref ref="STDOUT"/>
 	</logger>
 
diff --git a/web_src/src/components/dialog/devicePlayer.vue b/web_src/src/components/dialog/devicePlayer.vue
index 74c14c8..5a08006 100644
--- a/web_src/src/components/dialog/devicePlayer.vue
+++ b/web_src/src/components/dialog/devicePlayer.vue
@@ -307,7 +307,7 @@
             this.isLoging = false;
             // this.videoUrl = streamInfo.rtc;
             this.videoUrl = this.getUrlByStreamInfo(streamInfo);
-            this.streamId = streamInfo.streamId;
+            this.streamId = streamInfo.stream;
             this.app = streamInfo.app;
             this.mediaServerId = streamInfo.mediaServerId;
             this.playFromStreamInfo(false, streamInfo)
@@ -485,8 +485,9 @@
                 }).then(function (res) {
                     var streamInfo = res.data;
                     that.app = streamInfo.app;
-                    that.streamId = streamInfo.streamId;
+                    that.streamId = streamInfo.stream;
                     that.mediaServerId = streamInfo.mediaServerId;
+                    that.ssrc = streamInfo.ssrc;
                     that.videoUrl = that.getUrlByStreamInfo(streamInfo);
                     that.recordPlay = true;
                 });
@@ -497,7 +498,7 @@
             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()
             });
@@ -517,7 +518,7 @@
                 }).then(function (res) {
                     var streamInfo = res.data;
                     that.app = streamInfo.app;
-                    that.streamId = streamInfo.streamId;
+                    that.streamId = streamInfo.stream;
                     that.mediaServerId = streamInfo.mediaServerId;
                     that.videoUrl = that.getUrlByStreamInfo(streamInfo);
                     that.recordPlay = true;
@@ -529,7 +530,7 @@
             this.videoUrl = '';
             this.$axios({
                 method: 'get',
-                url: '/api/download/stop/' + this.deviceId + "/" + this.channelId
+                url: '/api/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
             }).then(function (res) {
                 if (callback) callback()
             });
@@ -539,8 +540,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) {});
         },

--
Gitblit v1.8.0