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

This commit is contained in:
648540858 2023-05-04 14:21:58 +08:00
parent ebc904e4d5
commit 3fe47021b9
8 changed files with 61 additions and 21 deletions

View File

@ -53,6 +53,7 @@ public class UserSetting {
private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
private Boolean deviceStatusNotify = Boolean.FALSE;
private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
private String serverId = "000000";
@ -277,4 +278,12 @@ public class UserSetting {
public void setDeviceStatusNotify(Boolean deviceStatusNotify) {
this.deviceStatusNotify = deviceStatusNotify;
}
public Boolean getUseCustomSsrcForParentInvite() {
return useCustomSsrcForParentInvite;
}
public void setUseCustomSsrcForParentInvite(Boolean useCustomSsrcForParentInvite) {
this.useCustomSsrcForParentInvite = useCustomSsrcForParentInvite;
}
}

View File

@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
@ -37,6 +38,9 @@ public class SipRunner implements CommandLineRunner {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private SSRCFactory ssrcFactory;
@Autowired
private UserSetting userSetting;
@ -96,6 +100,7 @@ public class SipRunner implements CommandLineRunner {
MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStreamId());
if (mediaServerItem != null) {
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
Map<String, Object> param = new HashMap<>();
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());

View File

@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
@ -60,6 +61,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private SSRCFactory ssrcFactory;
@Autowired
private IMediaServerService mediaServerService;
@ -102,6 +106,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
logger.info("[收到bye] 停止向上级推流:{}", streamId);
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null);
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
if (totalReaderCount <= 0) {

View File

@ -245,18 +245,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
String contentString = new String(request.getRawContent());
// jainSip不支持y=字段 移除以解析
int ssrcIndex = contentString.indexOf("y=");
// 检查是否有y字段
String ssrcDefault = "0000000000";
String ssrc;
int ssrcIndex = contentString.indexOf("y=");
SessionDescription sdp;
if (ssrcIndex >= 0) {
//ssrc规定长度为10个字节不取余下长度以避免后续还有f=字段
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
String substring = contentString.substring(0, contentString.indexOf("y="));
String substring = contentString.substring(0, ssrcIndex);
sdp = SdpFactory.getInstance().createSessionDescription(substring);
} else {
ssrc = ssrcDefault;
sdp = SdpFactory.getInstance().createSessionDescription(contentString);
}
String sessionName = sdp.getSessionName().getValue();
@ -320,7 +317,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
String username = sdp.getOrigin().getUsername();
String addressStr = sdp.getConnection().getAddress();
logger.info("[上级点播]用户:{} 通道:{}, 地址:{}:{} ssrc{}", username, channelId, addressStr, port, ssrc);
Device device = null;
// 通过 channel gbStream 是否为null 值判断来源是直播流合适国标
if (channel != null) {
@ -344,6 +341,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
return;
}
String ssrc;
if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
}else {
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
}
logger.info("[上级点播] 用户:{} 通道:{}, 地址:{}:{} ssrc{}", username, channelId, addressStr, port, ssrc);
SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp());
@ -465,29 +471,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
}
if (playTransaction == null) {
// 被点播的通道目前未被点播则开始点播
String streamId = null;
if (mediaServerItem.isRtpEnable()) {
streamId = String.format("%s_%s", device.getDeviceId(), channelId);
}
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam());
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, ssrc, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam());
logger.info(JSONObject.toJSONString(ssrcInfo));
sendRtpItem.setStreamId(ssrcInfo.getStream());
sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc);
// sendRtpItem.setSsrc(ssrcInfo.getSsrc());
// 写入redis 超时时回复
redisCatchStorage.updateSendRTPSever(sendRtpItem);
MediaServerItem finalMediaServerItem = mediaServerItem;
playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
logger.info("[上级点播]超时, 用户:{} 通道:{}", username, channelId);
redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
});
} else {
// 当前系统作为下级平台使用当上级平台点播时不携带ssrc时并且设备在当前系统中已经点播了这个时候需要重新给生成一个ssrc不使用默认的"0000000000"
if (ssrc.equals(ssrcDefault)) {
ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
sendRtpItem.setSsrc(ssrc);
}
sendRtpItem.setStreamId(playTransaction.getStream());
// 写入redis 超时时回复
@ -499,11 +499,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
}
} else if (gbStream != null) {
if(ssrc.equals(ssrcDefault))
{
ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
String ssrc;
if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
}else {
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
}
if("push".equals(gbStream.getStreamType())) {
if (streamPushItem != null && streamPushItem.isPushIng()) {
// 推流状态

View File

@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
@ -105,6 +106,9 @@ public class ZLMHttpHookListener {
@Autowired
private AssistRESTfulUtils assistRESTfulUtils;
@Autowired
private SSRCFactory ssrcFactory;
@Qualifier("taskExecutor")
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@ -666,6 +670,7 @@ public class ZLMHttpHookListener {
if (sendRtpItems.size() > 0) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
try {
commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
} catch (SipException | InvalidArgumentException | ParseException e) {

View File

@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
@ -53,6 +54,9 @@ public class PlatformServiceImpl implements IPlatformService {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private SSRCFactory ssrcFactory;
@Autowired
private IMediaServerService mediaServerService;
@ -328,6 +332,7 @@ public class PlatformServiceImpl implements IPlatformService {
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(platformId);
if (sendRtpItems != null && sendRtpItems.size() > 0) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null);
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
Map<String, Object> param = new HashMap<>(3);

View File

@ -915,7 +915,12 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
logger.info("[redis通知] 推送设备/通道状态, {}/{}-{}", deviceId, channelId, online);
if (channelId == null) {
logger.info("[redis通知] 推送设备状态, {}-{}", deviceId, online);
}else {
logger.info("[redis通知] 推送通道状态, {}/{}-{}", deviceId, channelId, online);
}
StringBuilder msg = new StringBuilder();
msg.append(deviceId);
if (channelId != null) {

View File

@ -194,6 +194,8 @@ user-settings:
max-notify-count-queue: 10000
# 设备/通道状态变化时发送消息
device-status-notify: false
# 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
use-custom-ssrc-for-parent-invite: true
# 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
allowed-origins:
- http://localhost:8008