Browse Source

添加对点播时设备自定义ssrc的支持

648540858 3 years ago
parent
commit
d679a9fcf8

+ 3 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java

@@ -136,4 +136,7 @@ public class SsrcConfig {
         this.notUsed = notUsed;
     }
 
+    public boolean checkSsrc(String ssrcInResponse) {
+        return !isUsed.contains(ssrcInResponse);
+    }
 }

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

@@ -93,8 +93,8 @@ public interface ISIPCommander {
 	 * @param device  视频设备
 	 * @param channelId  预览通道
 	 */
-	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
-	
+	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
+
 	/**
 	 * 请求回放视频流
 	 * 

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

@@ -343,7 +343,7 @@ public class SIPCommander implements ISIPCommander {
 	  */
 	@Override
 	public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-							  ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
+							  ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
 		String streamId = ssrcInfo.getStream();
 		try {
 			if (device == null) return;
@@ -436,6 +436,7 @@ public class SIPCommander implements ISIPCommander {
 				// 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
 				streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
 				streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
+				okEvent.response(e);
 			});
 
 			

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

@@ -202,6 +202,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 		String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
 		String deviceID = XmlUtil.getText(rootElement, "DeviceID");
 		ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
+		if (platform == null)return;
 		SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId);
 		if (evt.getServerTransaction() == null) {
 			ServerTransaction serverTransaction = platform.getTransport().equals("TCP") ? tcpSipProvider.getNewServerTransaction(evt.getRequest())

+ 18 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java

@@ -222,7 +222,24 @@ public class XmlUtil {
             // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1
             deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0);
         }
-        deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
+        /**
+         * 行政区划展示设备树与业务分组展示设备树是两种不同的模式
+         * 行政区划展示设备树 各个目录之间主要靠deviceId做关联,摄像头通过CivilCode指定其属于那个行政区划;都是不超过十位的编号; 结构如下:
+         * 河北省
+         *    --> 石家庄市
+         *          --> 摄像头
+         *          --> 正定县
+         *                  --> 摄像头
+         *                  --> 摄像头
+         *
+         * 业务分组展示设备树是顶级是业务分组,其下的虚拟组织靠BusinessGroupID指定其所属的业务分组;摄像头通过ParentId来指定其所属于的虚拟组织:
+         * 业务分组
+         *    --> 虚拟组织
+         *         --> 摄像头
+         *         --> 虚拟组织
+         *             --> 摄像头
+         *             --> 摄像头
+         */
         String parentId = XmlUtil.getText(itemDevice, "ParentID");
         if (parentId != null) {
             if (parentId.contains("/")) {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java

@@ -46,7 +46,7 @@ public interface IMediaServerService {
 
     SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck);
 
-    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback);
+    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback);
 
     void closeRTPServer(String deviceId, String channelId, String ssrc);
 

+ 9 - 5
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@@ -118,11 +118,11 @@ public class MediaServerServiceImpl implements IMediaServerService {
 
     @Override
     public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) {
-        return openRTPServer(mediaServerItem, streamId, ssrcCheck,false);
+        return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,false);
     }
 
     @Override
-    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) {
+    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback) {
         if (mediaServerItem == null || mediaServerItem.getId() == null) {
             return null;
         }
@@ -135,10 +135,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
             return null;
         }else {
             String ssrc = null;
-            if (isPlayback) {
-                ssrc = ssrcConfig.getPlayBackSsrc();
+            if (presetSsrc != null) {
+                ssrc = presetSsrc;
             }else {
-                ssrc = ssrcConfig.getPlaySsrc();
+                if (isPlayback) {
+                    ssrc = ssrcConfig.getPlayBackSsrc();
+                }else {
+                    ssrc = ssrcConfig.getPlaySsrc();
+                }
             }
 
             if (streamId == null) {

+ 30 - 1
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -39,6 +39,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.ResourceUtils;
 import org.springframework.web.context.request.async.DeferredResult;
 
+import javax.sip.ResponseEvent;
 import java.io.FileNotFoundException;
 import java.math.BigDecimal;
 import java.util.*;
@@ -256,18 +257,46 @@ public class PlayServiceImpl implements IPlayService {
                 }
             }
         }, userSetting.getPlayTimeout());
-
+        final String ssrc = ssrcInfo.getSsrc();
         cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
             logger.info("收到订阅消息: " + response.toJSONString());
             timer.cancel();
             // hook响应
             onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
             hookEvent.response(mediaServerItemInuse, response);
+        }, (event) -> {
+            ResponseEvent responseEvent = (ResponseEvent)event.event;
+            String contentString = new String(responseEvent.getResponse().getRawContent());
+            // 获取ssrc
+            int ssrcIndex = contentString.indexOf("y=");
+            // 检查是否有y字段
+            if (ssrcIndex >= 0) {
+                //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
+                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+                if (!ssrc.equals(ssrcInResponse) && device.isSsrcCheck()) { // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
+                    // 查询 ssrcInResponse 是否可用
+                    if (mediaServerItem.isRtpEnable() && !mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
+                        // ssrc 不可用
+                        // 释放ssrc
+                        mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
+                        streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
+                        event.msg = "下级自定义了ssrc,但是此ssrc不可用";
+                        event.statusCode = 400;
+                        errorEvent.response(event);
+                        return;
+                    }
+                    // 关闭rtp server
+                    mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
+                    // 重新开启ssrc server
+                    mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false);
+                }
+            }
         }, (event) -> {
             timer.cancel();
             mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
             // 释放ssrc
             mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
+
             streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
             errorEvent.response(event);
         });