Commit d679a9fcf8355e56f7be79a535e81f8300c70cb5

Authored by 648540858
1 parent f6fa1eed

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

src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java
@@ -136,4 +136,7 @@ public class SsrcConfig { @@ -136,4 +136,7 @@ public class SsrcConfig {
136 this.notUsed = notUsed; 136 this.notUsed = notUsed;
137 } 137 }
138 138
  139 + public boolean checkSsrc(String ssrcInResponse) {
  140 + return !isUsed.contains(ssrcInResponse);
  141 + }
139 } 142 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -93,8 +93,8 @@ public interface ISIPCommander { @@ -93,8 +93,8 @@ public interface ISIPCommander {
93 * @param device 视频设备 93 * @param device 视频设备
94 * @param channelId 预览通道 94 * @param channelId 预览通道
95 */ 95 */
96 - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);  
97 - 96 + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
  97 +
98 /** 98 /**
99 * 请求回放视频流 99 * 请求回放视频流
100 * 100 *
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -343,7 +343,7 @@ public class SIPCommander implements ISIPCommander { @@ -343,7 +343,7 @@ public class SIPCommander implements ISIPCommander {
343 */ 343 */
344 @Override 344 @Override
345 public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, 345 public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
346 - ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { 346 + ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
347 String streamId = ssrcInfo.getStream(); 347 String streamId = ssrcInfo.getStream();
348 try { 348 try {
349 if (device == null) return; 349 if (device == null) return;
@@ -436,6 +436,7 @@ public class SIPCommander implements ISIPCommander { @@ -436,6 +436,7 @@ public class SIPCommander implements ISIPCommander {
436 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 436 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
437 streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play); 437 streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
438 streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog); 438 streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
  439 + okEvent.response(e);
439 }); 440 });
440 441
441 442
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
@@ -202,6 +202,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme @@ -202,6 +202,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
202 String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); 202 String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
203 String deviceID = XmlUtil.getText(rootElement, "DeviceID"); 203 String deviceID = XmlUtil.getText(rootElement, "DeviceID");
204 ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); 204 ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  205 + if (platform == null)return;
205 SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId); 206 SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId);
206 if (evt.getServerTransaction() == null) { 207 if (evt.getServerTransaction() == null) {
207 ServerTransaction serverTransaction = platform.getTransport().equals("TCP") ? tcpSipProvider.getNewServerTransaction(evt.getRequest()) 208 ServerTransaction serverTransaction = platform.getTransport().equals("TCP") ? tcpSipProvider.getNewServerTransaction(evt.getRequest())
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -222,7 +222,24 @@ public class XmlUtil { @@ -222,7 +222,24 @@ public class XmlUtil {
222 // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 222 // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1
223 deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0); 223 deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0);
224 } 224 }
225 - deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID")); 225 + /**
  226 + * 行政区划展示设备树与业务分组展示设备树是两种不同的模式
  227 + * 行政区划展示设备树 各个目录之间主要靠deviceId做关联,摄像头通过CivilCode指定其属于那个行政区划;都是不超过十位的编号; 结构如下:
  228 + * 河北省
  229 + * --> 石家庄市
  230 + * --> 摄像头
  231 + * --> 正定县
  232 + * --> 摄像头
  233 + * --> 摄像头
  234 + *
  235 + * 业务分组展示设备树是顶级是业务分组,其下的虚拟组织靠BusinessGroupID指定其所属的业务分组;摄像头通过ParentId来指定其所属于的虚拟组织:
  236 + * 业务分组
  237 + * --> 虚拟组织
  238 + * --> 摄像头
  239 + * --> 虚拟组织
  240 + * --> 摄像头
  241 + * --> 摄像头
  242 + */
226 String parentId = XmlUtil.getText(itemDevice, "ParentID"); 243 String parentId = XmlUtil.getText(itemDevice, "ParentID");
227 if (parentId != null) { 244 if (parentId != null) {
228 if (parentId.contains("/")) { 245 if (parentId.contains("/")) {
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -46,7 +46,7 @@ public interface IMediaServerService { @@ -46,7 +46,7 @@ public interface IMediaServerService {
46 46
47 SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck); 47 SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck);
48 48
49 - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback); 49 + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback);
50 50
51 void closeRTPServer(String deviceId, String channelId, String ssrc); 51 void closeRTPServer(String deviceId, String channelId, String ssrc);
52 52
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -118,11 +118,11 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -118,11 +118,11 @@ public class MediaServerServiceImpl implements IMediaServerService {
118 118
119 @Override 119 @Override
120 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) { 120 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) {
121 - return openRTPServer(mediaServerItem, streamId, ssrcCheck,false); 121 + return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,false);
122 } 122 }
123 123
124 @Override 124 @Override
125 - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) { 125 + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback) {
126 if (mediaServerItem == null || mediaServerItem.getId() == null) { 126 if (mediaServerItem == null || mediaServerItem.getId() == null) {
127 return null; 127 return null;
128 } 128 }
@@ -135,10 +135,14 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -135,10 +135,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
135 return null; 135 return null;
136 }else { 136 }else {
137 String ssrc = null; 137 String ssrc = null;
138 - if (isPlayback) {  
139 - ssrc = ssrcConfig.getPlayBackSsrc(); 138 + if (presetSsrc != null) {
  139 + ssrc = presetSsrc;
140 }else { 140 }else {
141 - ssrc = ssrcConfig.getPlaySsrc(); 141 + if (isPlayback) {
  142 + ssrc = ssrcConfig.getPlayBackSsrc();
  143 + }else {
  144 + ssrc = ssrcConfig.getPlaySsrc();
  145 + }
142 } 146 }
143 147
144 if (streamId == null) { 148 if (streamId == null) {
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -39,6 +39,7 @@ import org.springframework.stereotype.Service; @@ -39,6 +39,7 @@ import org.springframework.stereotype.Service;
39 import org.springframework.util.ResourceUtils; 39 import org.springframework.util.ResourceUtils;
40 import org.springframework.web.context.request.async.DeferredResult; 40 import org.springframework.web.context.request.async.DeferredResult;
41 41
  42 +import javax.sip.ResponseEvent;
42 import java.io.FileNotFoundException; 43 import java.io.FileNotFoundException;
43 import java.math.BigDecimal; 44 import java.math.BigDecimal;
44 import java.util.*; 45 import java.util.*;
@@ -256,7 +257,7 @@ public class PlayServiceImpl implements IPlayService { @@ -256,7 +257,7 @@ public class PlayServiceImpl implements IPlayService {
256 } 257 }
257 } 258 }
258 }, userSetting.getPlayTimeout()); 259 }, userSetting.getPlayTimeout());
259 - 260 + final String ssrc = ssrcInfo.getSsrc();
260 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { 261 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
261 logger.info("收到订阅消息: " + response.toJSONString()); 262 logger.info("收到订阅消息: " + response.toJSONString());
262 timer.cancel(); 263 timer.cancel();
@@ -264,10 +265,38 @@ public class PlayServiceImpl implements IPlayService { @@ -264,10 +265,38 @@ public class PlayServiceImpl implements IPlayService {
264 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); 265 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
265 hookEvent.response(mediaServerItemInuse, response); 266 hookEvent.response(mediaServerItemInuse, response);
266 }, (event) -> { 267 }, (event) -> {
  268 + ResponseEvent responseEvent = (ResponseEvent)event.event;
  269 + String contentString = new String(responseEvent.getResponse().getRawContent());
  270 + // 获取ssrc
  271 + int ssrcIndex = contentString.indexOf("y=");
  272 + // 检查是否有y字段
  273 + if (ssrcIndex >= 0) {
  274 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
  275 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  276 + if (!ssrc.equals(ssrcInResponse) && device.isSsrcCheck()) { // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
  277 + // 查询 ssrcInResponse 是否可用
  278 + if (mediaServerItem.isRtpEnable() && !mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
  279 + // ssrc 不可用
  280 + // 释放ssrc
  281 + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
  282 + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  283 + event.msg = "下级自定义了ssrc,但是此ssrc不可用";
  284 + event.statusCode = 400;
  285 + errorEvent.response(event);
  286 + return;
  287 + }
  288 + // 关闭rtp server
  289 + mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  290 + // 重新开启ssrc server
  291 + mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false);
  292 + }
  293 + }
  294 + }, (event) -> {
267 timer.cancel(); 295 timer.cancel();
268 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 296 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
269 // 释放ssrc 297 // 释放ssrc
270 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); 298 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
  299 +
271 streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); 300 streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
272 errorEvent.response(event); 301 errorEvent.response(event);
273 }); 302 });