Commit 3fe47021b9aefb11b1e659383ac2f3d0edd2aa42

Authored by 648540858
1 parent ebc904e4

优化国标规范,参考国标文档中-点播外域设备媒体流SSRC处理方式,上级点播时自定义ssrc,不适用上级携带的ssrc,也避免上级兼容性差,不携带ssrc的问题,可通过配置关闭此特性

src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -53,6 +53,7 @@ public class UserSetting {
53 53 private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
54 54  
55 55 private Boolean deviceStatusNotify = Boolean.FALSE;
  56 + private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
56 57  
57 58 private String serverId = "000000";
58 59  
... ... @@ -277,4 +278,12 @@ public class UserSetting {
277 278 public void setDeviceStatusNotify(Boolean deviceStatusNotify) {
278 279 this.deviceStatusNotify = deviceStatusNotify;
279 280 }
  281 +
  282 + public Boolean getUseCustomSsrcForParentInvite() {
  283 + return useCustomSsrcForParentInvite;
  284 + }
  285 +
  286 + public void setUseCustomSsrcForParentInvite(Boolean useCustomSsrcForParentInvite) {
  287 + this.useCustomSsrcForParentInvite = useCustomSsrcForParentInvite;
  288 + }
280 289 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
5 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 6 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
7 7 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  8 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
8 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
9 10 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
10 11 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -38,6 +39,9 @@ public class SipRunner implements CommandLineRunner {
38 39 private IRedisCatchStorage redisCatchStorage;
39 40  
40 41 @Autowired
  42 + private SSRCFactory ssrcFactory;
  43 +
  44 + @Autowired
41 45 private UserSetting userSetting;
42 46  
43 47 @Autowired
... ... @@ -96,6 +100,7 @@ public class SipRunner implements CommandLineRunner {
96 100 MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
97 101 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStreamId());
98 102 if (mediaServerItem != null) {
  103 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
99 104 Map<String, Object> param = new HashMap<>();
100 105 param.put("vhost","__defaultVhost__");
101 106 param.put("app",sendRtpItem.getApp());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
... ... @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
6 6 import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
7 7 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
8 8 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  9 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
9 10 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
10 11 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
11 12 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
... ... @@ -61,6 +62,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
61 62 private ZLMRTPServerFactory zlmrtpServerFactory;
62 63  
63 64 @Autowired
  65 + private SSRCFactory ssrcFactory;
  66 +
  67 + @Autowired
64 68 private IMediaServerService mediaServerService;
65 69  
66 70 @Autowired
... ... @@ -102,6 +106,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
102 106 logger.info("[收到bye] 停止向上级推流:{}", streamId);
103 107 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
104 108 redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null);
  109 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
105 110 zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
106 111 int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
107 112 if (totalReaderCount <= 0) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
... ... @@ -245,18 +245,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
245 245 String contentString = new String(request.getRawContent());
246 246  
247 247 // jainSip不支持y=字段, 移除以解析。
248   - int ssrcIndex = contentString.indexOf("y=");
249 248 // 检查是否有y字段
250   - String ssrcDefault = "0000000000";
251   - String ssrc;
  249 + int ssrcIndex = contentString.indexOf("y=");
  250 +
252 251 SessionDescription sdp;
253 252 if (ssrcIndex >= 0) {
254 253 //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段
255   - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
256   - String substring = contentString.substring(0, contentString.indexOf("y="));
  254 + String substring = contentString.substring(0, ssrcIndex);
257 255 sdp = SdpFactory.getInstance().createSessionDescription(substring);
258 256 } else {
259   - ssrc = ssrcDefault;
260 257 sdp = SdpFactory.getInstance().createSessionDescription(contentString);
261 258 }
262 259 String sessionName = sdp.getSessionName().getValue();
... ... @@ -320,7 +317,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
320 317 String username = sdp.getOrigin().getUsername();
321 318 String addressStr = sdp.getConnection().getAddress();
322 319  
323   - logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc);
  320 +
324 321 Device device = null;
325 322 // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
326 323 if (channel != null) {
... ... @@ -344,6 +341,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
344 341 }
345 342 return;
346 343 }
  344 +
  345 + String ssrc;
  346 + if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) {
  347 + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  348 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
  349 + }else {
  350 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  351 + }
  352 + logger.info("[上级点播] 用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc);
347 353 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
348 354 device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp());
349 355  
... ... @@ -465,29 +471,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
465 471 }
466 472 }
467 473 if (playTransaction == null) {
  474 + // 被点播的通道目前未被点播,则开始点播
468 475 String streamId = null;
469 476 if (mediaServerItem.isRtpEnable()) {
470 477 streamId = String.format("%s_%s", device.getDeviceId(), channelId);
471 478 }
472   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam());
  479 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, ssrc, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam());
473 480 logger.info(JSONObject.toJSONString(ssrcInfo));
474 481 sendRtpItem.setStreamId(ssrcInfo.getStream());
475   - sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc);
  482 +// sendRtpItem.setSsrc(ssrcInfo.getSsrc());
476 483  
477 484 // 写入redis, 超时时回复
478 485 redisCatchStorage.updateSendRTPSever(sendRtpItem);
479   - MediaServerItem finalMediaServerItem = mediaServerItem;
480 486 playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
481 487 logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
482 488 redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
483 489 });
484 490 } else {
485   - // 当前系统作为下级平台使用,当上级平台点播时不携带ssrc时,并且设备在当前系统中已经点播了。这个时候需要重新给生成一个ssrc,不使用默认的"0000000000"。
486   - if (ssrc.equals(ssrcDefault)) {
487   - ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
488   - ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
489   - sendRtpItem.setSsrc(ssrc);
490   - }
491 491  
492 492 sendRtpItem.setStreamId(playTransaction.getStream());
493 493 // 写入redis, 超时时回复
... ... @@ -499,11 +499,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
499 499 }
500 500 }
501 501 } else if (gbStream != null) {
502   - if(ssrc.equals(ssrcDefault))
503   - {
504   - ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
505   - ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
  502 +
  503 + String ssrc;
  504 + if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) {
  505 + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  506 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
  507 + }else {
  508 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
506 509 }
  510 +
507 511 if("push".equals(gbStream.getStreamType())) {
508 512 if (streamPushItem != null && streamPushItem.isPushIng()) {
509 513 // 推流状态
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
8 8 import com.genersoft.iot.vmp.gb28181.bean.*;
9 9 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
10 10 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  11 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
11 12 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
12 13 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
13 14 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
... ... @@ -105,6 +106,9 @@ public class ZLMHttpHookListener {
105 106 @Autowired
106 107 private AssistRESTfulUtils assistRESTfulUtils;
107 108  
  109 + @Autowired
  110 + private SSRCFactory ssrcFactory;
  111 +
108 112 @Qualifier("taskExecutor")
109 113 @Autowired
110 114 private ThreadPoolTaskExecutor taskExecutor;
... ... @@ -666,6 +670,7 @@ public class ZLMHttpHookListener {
666 670 if (sendRtpItems.size() > 0) {
667 671 for (SendRtpItem sendRtpItem : sendRtpItems) {
668 672 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  673 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
669 674 try {
670 675 commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
671 676 } catch (SipException | InvalidArgumentException | ParseException e) {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
... ... @@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
4 4 import com.genersoft.iot.vmp.conf.UserSetting;
5 5 import com.genersoft.iot.vmp.gb28181.bean.*;
6 6 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  7 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
7 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8 9 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
9 10 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -54,6 +55,9 @@ public class PlatformServiceImpl implements IPlatformService {
54 55 private IRedisCatchStorage redisCatchStorage;
55 56  
56 57 @Autowired
  58 + private SSRCFactory ssrcFactory;
  59 +
  60 + @Autowired
57 61 private IMediaServerService mediaServerService;
58 62  
59 63 @Autowired
... ... @@ -328,6 +332,7 @@ public class PlatformServiceImpl implements IPlatformService {
328 332 List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(platformId);
329 333 if (sendRtpItems != null && sendRtpItems.size() > 0) {
330 334 for (SendRtpItem sendRtpItem : sendRtpItems) {
  335 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
331 336 redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null);
332 337 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
333 338 Map<String, Object> param = new HashMap<>(3);
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -915,7 +915,12 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
915 915 @Override
916 916 public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
917 917 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
918   - logger.info("[redis通知] 推送设备/通道状态, {}/{}-{}", deviceId, channelId, online);
  918 + if (channelId == null) {
  919 + logger.info("[redis通知] 推送设备状态, {}-{}", deviceId, online);
  920 + }else {
  921 + logger.info("[redis通知] 推送通道状态, {}/{}-{}", deviceId, channelId, online);
  922 + }
  923 +
919 924 StringBuilder msg = new StringBuilder();
920 925 msg.append(deviceId);
921 926 if (channelId != null) {
... ...
src/main/resources/all-application.yml
... ... @@ -194,6 +194,8 @@ user-settings:
194 194 max-notify-count-queue: 10000
195 195 # 设备/通道状态变化时发送消息
196 196 device-status-notify: false
  197 + # 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  198 + use-custom-ssrc-for-parent-invite: true
197 199 # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
198 200 allowed-origins:
199 201 - http://localhost:8008
... ...