优化多wvp国标级联推流

2.7.0
648540858 2024-04-16 00:10:38 +08:00
parent f9abfca003
commit b4168c02cb
17 changed files with 335 additions and 408 deletions

View File

@ -74,6 +74,7 @@ public class VideoManagerConstants {
public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_"; public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";
public static final String WAITE_SEND_PUSH_STREAM = "VMP_WAITE_SEND_PUSH_STREAM:"; public static final String WAITE_SEND_PUSH_STREAM = "VMP_WAITE_SEND_PUSH_STREAM:";
public static final String START_SEND_PUSH_STREAM = "VMP_START_SEND_PUSH_STREAM:"; public static final String START_SEND_PUSH_STREAM = "VMP_START_SEND_PUSH_STREAM:";
public static final String PUSH_STREAM_ONLINE = "VMP_PUSH_STREAM_ONLINE:";

View File

@ -28,9 +28,6 @@ public class RedisMsgListenConfig {
@Autowired @Autowired
private RedisAlarmMsgListener redisAlarmMsgListener; private RedisAlarmMsgListener redisAlarmMsgListener;
@Autowired
private RedisStreamMsgListener redisStreamMsgListener;
@Autowired @Autowired
private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener; private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener;
@ -52,6 +49,9 @@ public class RedisMsgListenConfig {
@Autowired @Autowired
private RedisPlatformStartSendRtpListener redisPlatformStartSendRtpListener; private RedisPlatformStartSendRtpListener redisPlatformStartSendRtpListener;
@Autowired
private RedisPlatformPushStreamOnlineLister redisPlatformPushStreamOnlineLister;
/** /**
* redis redis * redis redis
@ -67,14 +67,14 @@ public class RedisMsgListenConfig {
container.setConnectionFactory(connectionFactory); container.setConnectionFactory(connectionFactory);
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS)); container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE)); container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE)); container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));
container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE)); container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE));
container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED)); container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED));
container.addMessageListener(redisPlatformWaitPushStreamOnlineListener, new PatternTopic(VideoManagerConstants.WAITE_SEND_PUSH_STREAM));
container.addMessageListener(redisPlatformStartSendRtpListener, new PatternTopic(VideoManagerConstants.START_SEND_PUSH_STREAM)); container.addMessageListener(redisPlatformStartSendRtpListener, new PatternTopic(VideoManagerConstants.START_SEND_PUSH_STREAM));
container.addMessageListener(redisPlatformWaitPushStreamOnlineListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED));
container.addMessageListener(redisPlatformPushStreamOnlineLister, new PatternTopic(VideoManagerConstants.PUSH_STREAM_ONLINE));
return container; return container;
} }
} }

View File

@ -132,6 +132,11 @@ public class SendRtpItem {
*/ */
private String receiveStream; private String receiveStream;
/**
*
*/
private String sessionName;
public String getIp() { public String getIp() {
return ip; return ip;
} }
@ -332,6 +337,14 @@ public class SendRtpItem {
this.localIp = localIp; this.localIp = localIp;
} }
public String getSessionName() {
return sessionName;
}
public void setSessionName(String sessionName) {
this.sessionName = sessionName;
}
@Override @Override
public String toString() { public String toString() {
return "SendRtpItem{" + return "SendRtpItem{" +
@ -347,7 +360,7 @@ public class SendRtpItem {
", stream='" + stream + '\'' + ", stream='" + stream + '\'' +
", tcp=" + tcp + ", tcp=" + tcp +
", tcpActive=" + tcpActive + ", tcpActive=" + tcpActive +
", localIp=" + localIp + ", localIp='" + localIp + '\'' +
", localPort=" + localPort + ", localPort=" + localPort +
", mediaServerId='" + mediaServerId + '\'' + ", mediaServerId='" + mediaServerId + '\'' +
", serverId='" + serverId + '\'' + ", serverId='" + serverId + '\'' +
@ -360,6 +373,7 @@ public class SendRtpItem {
", rtcp=" + rtcp + ", rtcp=" + rtcp +
", playType=" + playType + ", playType=" + playType +
", receiveStream='" + receiveStream + '\'' + ", receiveStream='" + receiveStream + '\'' +
", sessionName='" + sessionName + '\'' +
'}'; '}';
} }
} }

View File

@ -16,7 +16,6 @@ import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -77,9 +76,6 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
@Autowired @Autowired
private DynamicTask dynamicTask; private DynamicTask dynamicTask;
@Autowired
private RedisGbPlayMsgListener redisGbPlayMsgListener;
@Autowired @Autowired
private IPlayService playService; private IPlayService playService;
@ -117,13 +113,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
if (parentPlatform != null) { if (parentPlatform != null) {
Map<String, Object> param = getSendRtpParam(sendRtpItem); Map<String, Object> param = getSendRtpParam(sendRtpItem);
if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) { if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) {
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( redisCatchStorage.sendStartSendRtp(sendRtpItem);
sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStream(),
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> {
playService.startSendRtpStreamHand(sendRtpItem, parentPlatform, json, param, callIdHeader);
});
} else { } else {
JSONObject startSendRtpStreamResult = sendRtp(sendRtpItem, mediaInfo, param); JSONObject startSendRtpStreamResult = sendRtp(sendRtpItem, mediaInfo, param);
if (startSendRtpStreamResult != null) { if (startSendRtpStreamResult != null) {

View File

@ -19,7 +19,6 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.*; import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.service.bean.RequestStopPushStreamMsg; import com.genersoft.iot.vmp.service.bean.RequestStopPushStreamMsg;
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPRequest;
@ -98,8 +97,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
@Autowired @Autowired
private IStreamPushService pushService; private IStreamPushService pushService;
@Autowired
private RedisGbPlayMsgListener redisGbPlayMsgListener;
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
@ -142,7 +139,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
if (platform != null) { if (platform != null) {
RequestStopPushStreamMsg streamMsg = RequestStopPushStreamMsg.getInstance(sendRtpItem, platform.getName(), platform.getId()); RequestStopPushStreamMsg streamMsg = RequestStopPushStreamMsg.getInstance(sendRtpItem, platform.getName(), platform.getId());
redisGbPlayMsgListener.sendMsgForStopSendRtpStream(sendRtpItem.getServerId(), streamMsg); // redisGbPlayMsgListener.sendMsgForStopSendRtpStream(sendRtpItem.getServerId(), streamMsg);
} }
}else { }else {
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());

View File

@ -19,7 +19,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; import com.genersoft.iot.vmp.service.redisMsg.RedisPlatformPushStreamOnlineLister;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.*; import com.genersoft.iot.vmp.media.zlm.dto.*;
@ -122,7 +122,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
private UserSetting userSetting; private UserSetting userSetting;
@Autowired @Autowired
private ZLMMediaListManager mediaListManager; private RedisPlatformPushStreamOnlineLister mediaListManager;
@Autowired @Autowired
private SipConfig config; private SipConfig config;
@ -568,19 +568,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} }
} else if (gbStream != null) { } else if (gbStream != null) {
String ssrc;
if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
}else {
ssrc = gb28181Sdp.getSsrc();
}
SendRtpItem sendRtpItem = new SendRtpItem(); SendRtpItem sendRtpItem = new SendRtpItem();
if (!userSetting.getUseCustomSsrcForParentInvite() && gb28181Sdp.getSsrc() != null) {
sendRtpItem.setSsrc(gb28181Sdp.getSsrc());
}
if (tcpActive != null) {
sendRtpItem.setTcpActive(tcpActive); sendRtpItem.setTcpActive(tcpActive);
}
sendRtpItem.setTcp(mediaTransmissionTCP); sendRtpItem.setTcp(mediaTransmissionTCP);
sendRtpItem.setRtcp(platform.isRtcp()); sendRtpItem.setRtcp(platform.isRtcp());
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setPlatformName(platform.getName()); sendRtpItem.setPlatformName(platform.getName());
sendRtpItem.setPlatformId(platform.getServerGBId()); sendRtpItem.setPlatformId(platform.getServerGBId());
sendRtpItem.setMediaServerId(mediaServerItem.getId()); sendRtpItem.setMediaServerId(mediaServerItem.getId());
@ -593,37 +590,48 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
sendRtpItem.setCallId(callIdHeader.getCallId()); sendRtpItem.setCallId(callIdHeader.getCallId());
sendRtpItem.setFromTag(request.getFromTag()); sendRtpItem.setFromTag(request.getFromTag());
sendRtpItem.setOnlyAudio(false); sendRtpItem.setOnlyAudio(false);
sendRtpItem.setPlayType(InviteStreamType.PUSH);
sendRtpItem.setStatus(0); sendRtpItem.setStatus(0);
sendRtpItem.setSessionName(sessionName);
if ("push".equals(gbStream.getStreamType())) { if ("push".equals(gbStream.getStreamType())) {
if (streamPushItem != null) { if (streamPushItem != null) {
// 从redis查询是否正在接收这个推流 // 从redis查询是否正在接收这个推流
OnStreamChangedHookParam pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream()); OnStreamChangedHookParam pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream());
sendRtpItem.setServerId(pushListItem.getSeverId());
if (pushListItem != null) { if (pushListItem != null) {
sendRtpItem.setServerId(pushListItem.getSeverId());
StreamPushItem transform = streamPushService.transform(pushListItem); StreamPushItem transform = streamPushService.transform(pushListItem);
transform.setSelf(userSetting.getServerId().equals(pushListItem.getSeverId())); transform.setSelf(userSetting.getServerId().equals(pushListItem.getSeverId()));
// 推流状态 // 推流状态
pushStream(sendRtpItem, mediaServerItem, platform, request); sendPushStream(sendRtpItem, mediaServerItem, platform, request);
}else { }else {
// 未推流 拉起 if (!platform.isStartOfflinePush()) {
// 平台设置中关闭了拉起离线的推流则直接回复
try {
logger.info("[上级点播] 失败推流设备未推流channel: {}, app: {}, stream: {}", sendRtpItem.getChannelId(), sendRtpItem.getApp(), sendRtpItem.getStream());
responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
}
return;
}
notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request); notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request);
} }
} }
} else if ("proxy".equals(gbStream.getStreamType())) { } else if ("proxy".equals(gbStream.getStreamType())) {
if (null != proxyByAppAndStream) { if (null != proxyByAppAndStream) {
if (sendRtpItem.getSsrc() == null) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
String ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
sendRtpItem.setSsrc(ssrc);
}
if (proxyByAppAndStream.isStatus()) { if (proxyByAppAndStream.isStatus()) {
pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, sendProxyStream(sendRtpItem, mediaServerItem, platform, request);
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
} else { } else {
//开启代理拉流 //开启代理拉流
notifyProxyStreamOnline(sendRtpItem, mediaServerItem, platform, request); notifyProxyStreamOnline(sendRtpItem, mediaServerItem, platform, request);
} }
} }
} }
} }
} }
@ -649,17 +657,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
/** /**
* *
*/ */
private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform, private void sendProxyStream(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) {
CallIdHeader callIdHeader, MediaServerItem mediaServerItem, Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream());
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
String channelId, String addressStr, String ssrc, String requesterId) {
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
if (streamReady != null && streamReady) { if (streamReady != null && streamReady) {
// 自平台内容 // 自平台内容
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, int localPort = sendRtpPortManager.getNextPort(mediaServerItem);
gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); if (localPort == 0) {
if (sendRtpItem == null) {
logger.warn("服务器端口资源不足"); logger.warn("服务器端口资源不足");
try { try {
responseAck(request, Response.BUSY_HERE); responseAck(request, Response.BUSY_HERE);
@ -668,14 +671,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} }
return; return;
} }
if (tcpActive != null) { sendRtpItem.setPlayType(InviteStreamType.PROXY);
sendRtpItem.setTcpActive(tcpActive);
}
sendRtpItem.setPlayType(InviteStreamType.PUSH);
// 写入redis 超时时回复 // 写入redis 超时时回复
sendRtpItem.setStatus(1); sendRtpItem.setStatus(1);
sendRtpItem.setCallId(callIdHeader.getCallId());
sendRtpItem.setFromTag(request.getFromTag());
sendRtpItem.setLocalIp(mediaServerItem.getSdpIp()); sendRtpItem.setLocalIp(mediaServerItem.getSdpIp());
SIPResponse response = sendStreamAck(request, sendRtpItem, platform); SIPResponse response = sendStreamAck(request, sendRtpItem, platform);
@ -683,12 +681,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
sendRtpItem.setToTag(response.getToTag()); sendRtpItem.setToTag(response.getToTag());
} }
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
}
} }
} private void sendPushStream(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) {
private void pushStream(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) {
// 推流 // 推流
if (sendRtpItem.getServerId().equals(userSetting.getServerId())) { if (sendRtpItem.getServerId().equals(userSetting.getServerId())) {
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream()); Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream());
@ -712,6 +708,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
if (response != null) { if (response != null) {
sendRtpItem.setToTag(response.getToTag()); sendRtpItem.setToTag(response.getToTag());
} }
if (sendRtpItem.getSsrc() == null) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
sendRtpItem.setSsrc(ssrc);
}
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
} else { } else {
@ -719,10 +720,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request); notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request);
} }
} else { } else {
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setRtcp(platform.isRtcp());
sendRtpItem.setTcp(mediaTransmissionTCP);
sendRtpItem.setTcpActive();
// 其他平台内容 // 其他平台内容
otherWvpPushStream(sendRtpItem, request, platform); otherWvpPushStream(sendRtpItem, request, platform);
} }
@ -733,29 +730,28 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
*/ */
private void notifyProxyStreamOnline(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) { private void notifyProxyStreamOnline(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) {
// TODO 控制启用以使设备上线 // TODO 控制启用以使设备上线
logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream()); logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", sendRtpItem.getApp(), sendRtpItem.getStream());
// 监听流上线 // 监听流上线
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId()); HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(sendRtpItem.getApp(), sendRtpItem.getStream(), true, "rtsp", mediaServerItem.getId());
zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, hookParam) -> { zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, hookParam) -> {
OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam; OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
logger.info("[上级点播]拉流代理已经就绪, {}/{}", streamChangedHookParam.getApp(), streamChangedHookParam.getStream()); logger.info("[上级点播]拉流代理已经就绪, {}/{}", streamChangedHookParam.getApp(), streamChangedHookParam.getStream());
dynamicTask.stop(callIdHeader.getCallId()); dynamicTask.stop(sendRtpItem.getCallId());
pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, sendProxyStream(sendRtpItem, mediaServerItem, platform, request);
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
}); });
dynamicTask.startDelay(callIdHeader.getCallId(), () -> { dynamicTask.startDelay(sendRtpItem.getCallId(), () -> {
logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream()); logger.info("[ app={}, stream={} ] 等待拉流代理流超时", sendRtpItem.getApp(), sendRtpItem.getStream());
zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
}, userSetting.getPlatformPlayTimeout()); }, userSetting.getPlatformPlayTimeout());
boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream()); boolean start = streamProxyService.start(sendRtpItem.getApp(), sendRtpItem.getStream());
if (!start) { if (!start) {
try { try {
responseAck(request, Response.BUSY_HERE, "channel [" + gbStream.getGbId() + "] offline"); responseAck(request, Response.BUSY_HERE, "channel [" + sendRtpItem.getChannelId() + "] offline");
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
} }
zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
dynamicTask.stop(callIdHeader.getCallId()); dynamicTask.stop(sendRtpItem.getCallId());
} }
} }
@ -763,50 +759,28 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
* 线 * 线
*/ */
private void notifyPushStreamOnline(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) { private void notifyPushStreamOnline(SendRtpItem sendRtpItem, MediaServerItem mediaServerItem, ParentPlatform platform, SIPRequest request) {
if (!platform.isStartOfflinePush()) { // 发送redis消息以使设备上线流上线后被
// 平台设置中关闭了拉起离线的推流则直接回复 logger.info("[ app={}, stream={} ]通道未推流发送redis信息控制设备开始推流", sendRtpItem.getApp(), sendRtpItem.getStream());
try {
logger.info("[上级点播] 失败推流设备未推流channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream());
responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
}
return;
}
// 发送redis消息以使设备上线
logger.info("[ app={}, stream={} ]通道未推流发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream());
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1,
gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(), sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(), sendRtpItem.getPlatformId(),
platform.getName(), userSetting.getServerId(), gbStream.getMediaServerId()); platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
// 设置超时 // 设置超时
dynamicTask.startDelay(callIdHeader.getCallId(), () -> { dynamicTask.startDelay(sendRtpItem.getCallId(), () -> {
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream()); logger.info("[ app={}, stream={} ] 等待设备开始推流超时", sendRtpItem.getApp(), sendRtpItem.getStream());
try { try {
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream()); redisPushStreamResponseListener.removeEvent(sendRtpItem.getApp(), sendRtpItem.getStream());
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); mediaListManager.removedChannelOnlineEventLister(sendRtpItem.getApp(), sendRtpItem.getStream());
responseAck(request, Response.REQUEST_TIMEOUT); // 超时 responseAck(request, Response.REQUEST_TIMEOUT); // 超时
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("未处理的异常 ", e); logger.error("未处理的异常 ", e);
} }
}, userSetting.getPlatformPlayTimeout()); }, userSetting.getPlatformPlayTimeout());
// 写入redis待发流信息供其他wvp读取并生成发流信息 redisCatchStorage.addWaiteSendRtpItem(sendRtpItem, userSetting.getPlatformPlayTimeout());
SendRtpItem sendRtpItemTemp = new SendRtpItem();
sendRtpItemTemp.setIp(addressStr);
sendRtpItemTemp.setPort(port);
sendRtpItemTemp.setSsrc(ssrc);
sendRtpItemTemp.setPlatformId(requesterId);
sendRtpItemTemp.setPlatformName(platform.getName());
sendRtpItemTemp.setTcp(mediaTransmissionTCP);
sendRtpItemTemp.setRtcp(platform.isRtcp());
sendRtpItemTemp.setTcpActive(tcpActive);
sendRtpItemTemp.setPlayType(InviteStreamType.PUSH);
redisCatchStorage.addWaiteSendRtpItem(sendRtpItemTemp, userSetting.getPlatformPlayTimeout());
// 添加上线的通知 // 添加上线的通知
mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (sendRtpItemFromRedis) -> { mediaListManager.addChannelOnlineEventLister(sendRtpItem.getApp(), sendRtpItem.getStream(), (sendRtpItemFromRedis) -> {
dynamicTask.stop(callIdHeader.getCallId()); dynamicTask.stop(sendRtpItem.getCallId());
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream()); redisPushStreamResponseListener.removeEvent(sendRtpItem.getApp(), sendRtpItem.getStream());
if (sendRtpItemFromRedis.getServerId().equals(userSetting.getServerId())) { if (sendRtpItemFromRedis.getServerId().equals(userSetting.getServerId())) {
int localPort = sendRtpPortManager.getNextPort(mediaServerItem); int localPort = sendRtpPortManager.getNextPort(mediaServerItem);
@ -823,18 +797,18 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} }
return; return;
} }
sendRtpItemTemp.setLocalPort(localPort); sendRtpItem.setLocalPort(localPort);
sendRtpItemTemp.setLocalIp(ObjectUtils.isEmpty(platform.getSendStreamIp()): ); if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) {
// 写入redis 超时时回复 sendRtpItem.setLocalIp(platform.getSendStreamIp());
sendRtpItemTemp.setStatus(1);
sendRtpItemTemp.setCallId(callIdHeader.getCallId());
sendRtpItemTemp.setFromTag(request.getFromTag());
SIPResponse response = sendStreamAck(request, sendRtpItemTemp, platform);
if (response != null) {
sendRtpItemTemp.setToTag(response.getToTag());
} }
redisCatchStorage.updateSendRTPSever(sendRtpItemTemp);
// 写入redis 超时时回复
sendRtpItem.setStatus(1);
SIPResponse response = sendStreamAck(request, sendRtpItem, platform);
if (response != null) {
sendRtpItem.setToTag(response.getToTag());
}
redisCatchStorage.updateSendRTPSever(sendRtpItem);
} else { } else {
// 其他平台内容 // 其他平台内容
otherWvpPushStream(sendRtpItemFromRedis, request, platform); otherWvpPushStream(sendRtpItemFromRedis, request, platform);
@ -842,10 +816,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}); });
// 添加回复的拒绝或者错误的通知 // 添加回复的拒绝或者错误的通知
redisPushStreamResponseListener.addEvent(gbStream.getApp(), gbStream.getStream(), response -> { redisPushStreamResponseListener.addEvent(sendRtpItem.getApp(), sendRtpItem.getStream(), response -> {
if (response.getCode() != 0) { if (response.getCode() != 0) {
dynamicTask.stop(callIdHeader.getCallId()); dynamicTask.stop(sendRtpItem.getCallId());
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); mediaListManager.removedChannelOnlineEventLister(sendRtpItem.getApp(), sendRtpItem.getStream());
try { try {
responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg()); responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg());
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {

View File

@ -18,14 +18,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.media.zlm.dto.HookType; import com.genersoft.iot.vmp.media.zlm.dto.*;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.*; import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
import com.genersoft.iot.vmp.service.*; import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.service.redisMsg.RedisPlatformPushStreamOnlineLister;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.DateUtil;
@ -103,7 +101,7 @@ public class ZLMHttpHookListener {
private EventPublisher eventPublisher; private EventPublisher eventPublisher;
@Autowired @Autowired
private ZLMMediaListManager zlmMediaListManager; private RedisPlatformPushStreamOnlineLister zlmMediaListManager;
@Autowired @Autowired
private ZlmHttpHookSubscribe subscribe; private ZlmHttpHookSubscribe subscribe;
@ -130,6 +128,9 @@ public class ZLMHttpHookListener {
@Autowired @Autowired
private RedisTemplate<Object, Object> redisTemplate; private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private IStreamPushService streamPushService;
/** /**
* 10s * 10s
*/ */
@ -236,11 +237,8 @@ public class ZLMHttpHookListener {
// 鉴权通过 // 鉴权通过
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
} }
} else {
zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
} }
HookResultForOnPublish result = HookResultForOnPublish.SUCCESS(); HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
result.setEnable_audio(true); result.setEnable_audio(true);
taskExecutor.execute(() -> { taskExecutor.execute(() -> {
@ -465,8 +463,7 @@ public class ZLMHttpHookListener {
|| param.getOriginType() == OriginType.RTMP_PUSH.ordinal() || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
param.setSeverId(userSetting.getServerId()); param.setSeverId(userSetting.getServerId());
zlmMediaListManager.addPush(param); streamPushService.updatePush(param);
// 冗余数据,自己系统中自用 // 冗余数据,自己系统中自用
redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param); redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
} }
@ -483,10 +480,13 @@ public class ZLMHttpHookListener {
} }
} }
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream()); GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) { // 查找是否关联了国标, 关联了不删除, 置为离线
if (gbStream == null) {
storager.removeMedia(param.getApp(), param.getStream());
}else {
// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF); // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
storager.mediaOffline(param.getApp(), param.getStream());
} }
zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
} }
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream()); GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) { if (gbStream != null) {

View File

@ -1,138 +0,0 @@
package com.genersoft.iot.vmp.media.zlm;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.dto.*;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.StreamPushMapper;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author lin
*/
@Component
public class ZLMMediaListManager {
private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager");
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IVideoManagerStorage storager;
@Autowired
private GbStreamMapper gbStreamMapper;
@Autowired
private PlatformGbStreamMapper platformGbStreamMapper;
@Autowired
private IStreamPushService streamPushService;
@Autowired
private IStreamProxyService streamProxyService;
@Autowired
private StreamPushMapper streamPushMapper;
@Autowired
private ZlmHttpHookSubscribe subscribe;
@Autowired
private UserSetting userSetting;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private IMediaServerService mediaServerService;
private Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
public StreamPushItem addPush(OnStreamChangedHookParam onStreamChangedHookParam) {
StreamPushItem transform = streamPushService.transform(onStreamChangedHookParam);
StreamPushItem pushInDb = streamPushService.getPush(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream());
transform.setPushIng(onStreamChangedHookParam.isRegist());
transform.setUpdateTime(DateUtil.getNow());
transform.setPushTime(DateUtil.getNow());
transform.setSelf(userSetting.getServerId().equals(onStreamChangedHookParam.getSeverId()));
if (pushInDb == null) {
transform.setCreateTime(DateUtil.getNow());
streamPushMapper.add(transform);
}else {
streamPushMapper.update(transform);
gbStreamMapper.updateMediaServer(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), onStreamChangedHookParam.getMediaServerId());
}
ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream());
if ( channelOnlineEventLister != null) {
try {
channelOnlineEventLister.run(transform.getApp(), transform.getStream(), transform.getServerId());;
} catch (ParseException e) {
logger.error("addPush: ", e);
}
removedChannelOnlineEventLister(transform.getApp(), transform.getStream());
}
return transform;
}
public void sendStreamEvent(String app, String stream, String mediaServerId) {
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
// 查看推流状态
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream);
if (streamReady != null && streamReady) {
ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
if (channelOnlineEventLister != null) {
try {
channelOnlineEventLister.run(app, stream, mediaServerId);
} catch (ParseException e) {
logger.error("sendStreamEvent: ", e);
}
removedChannelOnlineEventLister(app, stream);
}
}
}
public int removeMedia(String app, String streamId) {
// 查找是否关联了国标, 关联了不删除, 置为离线
GbStream gbStream = gbStreamMapper.selectOne(app, streamId);
int result;
if (gbStream == null) {
result = storager.removeMedia(app, streamId);
}else {
result =storager.mediaOffline(app, streamId);
}
return result;
}
public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) {
this.channelOnPublishEvents.put(app + "_" + stream, callback);
}
public void removedChannelOnlineEventLister(String app, String stream) {
this.channelOnPublishEvents.remove(app + "_" + stream);
}
public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) {
return this.channelOnPublishEvents.get(app + "_" + stream);
}
}

View File

@ -118,4 +118,5 @@ public interface IStreamPushService {
Map<String, StreamPushItem> getAllAppAndStreamMap(); Map<String, StreamPushItem> getAllAppAndStreamMap();
void updatePush(OnStreamChangedHookParam param);
} }

View File

@ -31,7 +31,6 @@ import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.*; import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.*; import com.genersoft.iot.vmp.service.bean.*;
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper; import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
@ -134,9 +133,6 @@ public class PlayServiceImpl implements IPlayService {
@Autowired @Autowired
private ThreadPoolTaskExecutor taskExecutor; private ThreadPoolTaskExecutor taskExecutor;
@Autowired
private RedisGbPlayMsgListener redisGbPlayMsgListener;
@Autowired @Autowired
private ZlmHttpHookSubscribe hookSubscribe; private ZlmHttpHookSubscribe hookSubscribe;
@ -1366,15 +1362,7 @@ public class PlayServiceImpl implements IPlayService {
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0"); param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0");
} }
if (mediaInfo == null) { if (mediaInfo != null) {
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStream(),
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> {
startSendRtpStreamHand(sendRtpItem, platform, json, param, callIdHeader);
});
} else {
// 如果是严格模式,需要关闭端口占用 // 如果是严格模式,需要关闭端口占用
JSONObject startSendRtpStreamResult = null; JSONObject startSendRtpStreamResult = null;
if (sendRtpItem.getLocalPort() != 0) { if (sendRtpItem.getLocalPort() != 0) {

View File

@ -553,4 +553,21 @@ public class StreamPushServiceImpl implements IStreamPushService {
public Map<String, StreamPushItem> getAllAppAndStreamMap() { public Map<String, StreamPushItem> getAllAppAndStreamMap() {
return streamPushMapper.getAllAppAndStreamMap(); return streamPushMapper.getAllAppAndStreamMap();
} }
@Override
public void updatePush(OnStreamChangedHookParam param) {
StreamPushItem transform = transform(param);
StreamPushItem pushInDb = getPush(param.getApp(), param.getStream());
transform.setPushIng(param.isRegist());
transform.setUpdateTime(DateUtil.getNow());
transform.setPushTime(DateUtil.getNow());
transform.setSelf(userSetting.getServerId().equals(param.getSeverId()));
if (pushInDb == null) {
transform.setCreateTime(DateUtil.getNow());
streamPushMapper.add(transform);
}else {
streamPushMapper.update(transform);
gbStreamMapper.updateMediaServer(param.getApp(), param.getStream(), param.getMediaServerId());
}
}
} }

View File

@ -0,0 +1,97 @@
package com.genersoft.iot.vmp.service.redisMsg;
import com.alibaba.fastjson2.JSON;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.gb28181.bean.HandlerCatchData;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.*;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannelResponse;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.StreamPushMapper;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* @author lin
*/
@Component
public class RedisPlatformPushStreamOnlineLister implements MessageListener {
private final Logger logger = LoggerFactory.getLogger("RedisPlatformPushStreamOnlineLister");
private final ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
@Qualifier("taskExecutor")
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
/**
* redis线
*/
@Override
public void onMessage(Message message, byte[] pattern) {
boolean isEmpty = taskQueue.isEmpty();
taskQueue.offer(message);
if (isEmpty) {
taskExecutor.execute(() -> {
while (!taskQueue.isEmpty()) {
Message msg = taskQueue.poll();
SendRtpItem sendRtpItem = JSON.parseObject(new String(msg.getBody()), SendRtpItem.class);
sendStreamEvent(sendRtpItem);
}
});
}
}
private final Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
public void sendStreamEvent(SendRtpItem sendRtpItem) {
// 查看推流状态
ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(sendRtpItem.getApp(), sendRtpItem.getStream());
if (channelOnlineEventLister != null) {
try {
channelOnlineEventLister.run(sendRtpItem);
} catch (ParseException e) {
logger.error("sendStreamEvent: ", e);
}
removedChannelOnlineEventLister(sendRtpItem.getApp(), sendRtpItem.getStream());
}
}
public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) {
this.channelOnPublishEvents.put(app + "_" + stream, callback);
}
public void removedChannelOnlineEventLister(String app, String stream) {
this.channelOnPublishEvents.remove(app + "_" + stream);
}
public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) {
return this.channelOnPublishEvents.get(app + "_" + stream);
}
}

View File

@ -1,12 +1,16 @@
package com.genersoft.iot.vmp.service.redisMsg; package com.genersoft.iot.vmp.service.redisMsg;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -18,6 +22,8 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
/** /**
@ -32,10 +38,10 @@ public class RedisPlatformStartSendRtpListener implements MessageListener {
private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
@Autowired @Autowired
private UserSetting userSetting; private ZLMServerFactory zlmServerFactory;
@Autowired @Autowired
private ZlmHttpHookSubscribe hookSubscribe; private IMediaServerService mediaServerService;
@Qualifier("taskExecutor") @Qualifier("taskExecutor")
@Autowired @Autowired
@ -52,23 +58,14 @@ public class RedisPlatformStartSendRtpListener implements MessageListener {
while (!taskQueue.isEmpty()) { while (!taskQueue.isEmpty()) {
Message msg = taskQueue.poll(); Message msg = taskQueue.poll();
try { try {
MessageForPushChannel messageForPushChannel = JSON.parseObject(new String(msg.getBody()), MessageForPushChannel.class); SendRtpItem sendRtpItem = JSON.parseObject(new String(msg.getBody()), SendRtpItem.class);
if (messageForPushChannel == null sendRtpItem.getMediaServerId();
|| ObjectUtils.isEmpty(messageForPushChannel.getApp()) MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|| ObjectUtils.isEmpty(messageForPushChannel.getStream()) if (mediaServer == null) {
|| userSetting.getServerId().equals(messageForPushChannel.getServerId())){ return;
continue;
} }
Map<String, Object> sendRtpParam = getSendRtpParam(sendRtpItem);
// 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 sendRtp(sendRtpItem, mediaServer, sendRtpParam);
HookSubscribeForStreamChange hook = HookSubscribeFactory.on_stream_changed(
messageForPushChannel.getApp(), messageForPushChannel.getStream(), true, "rtsp",
null);
hookSubscribe.addSubscribe(hook, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
// 读取redis中的上级点播信息生成sendRtpItm发送出去
});
}catch (Exception e) { }catch (Exception e) {
logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
@ -78,4 +75,48 @@ public class RedisPlatformStartSendRtpListener implements MessageListener {
}); });
} }
} }
private Map<String, Object> getSendRtpParam(SendRtpItem sendRtpItem) {
String isUdp = sendRtpItem.isTcp() ? "0" : "1";
Map<String, Object> param = new HashMap<>(12);
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());
param.put("stream",sendRtpItem.getStream());
param.put("ssrc", sendRtpItem.getSsrc());
param.put("dst_url",sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
param.put("src_port", sendRtpItem.getLocalPort());
param.put("pt", sendRtpItem.getPt());
param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
param.put("is_udp", isUdp);
if (!sendRtpItem.isTcp()) {
// udp模式下开启rtcp保活
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
}
return param;
}
private JSONObject sendRtp(SendRtpItem sendRtpItem, MediaServerItem mediaInfo, Map<String, Object> param){
JSONObject startSendRtpStreamResult = null;
if (sendRtpItem.getLocalPort() != 0) {
if (sendRtpItem.isTcpActive()) {
startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
}else {
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
startSendRtpStreamResult = zlmServerFactory.startSendRtpStream(mediaInfo, param);
}
}else {
if (sendRtpItem.isTcpActive()) {
startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
}else {
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
startSendRtpStreamResult = zlmServerFactory.startSendRtpStream(mediaInfo, param);
}
}
return startSendRtpStreamResult;
}
} }

View File

@ -2,12 +2,15 @@ package com.genersoft.iot.vmp.service.redisMsg;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -34,14 +37,26 @@ public class RedisPlatformWaitPushStreamOnlineListener implements MessageListene
@Autowired @Autowired
private UserSetting userSetting; private UserSetting userSetting;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private ZlmHttpHookSubscribe hookSubscribe; private ZlmHttpHookSubscribe hookSubscribe;
@Autowired
private RedisPlatformPushStreamOnlineLister redisPlatformPushStreamOnlineLister;
@Autowired
private SSRCFactory ssrcFactory;
@Qualifier("taskExecutor") @Qualifier("taskExecutor")
@Autowired @Autowired
private ThreadPoolTaskExecutor taskExecutor; private ThreadPoolTaskExecutor taskExecutor;
/**
* 线线wvpredis
*/
@Override @Override
public void onMessage(Message message, byte[] bytes) { public void onMessage(Message message, byte[] bytes) {
logger.info("[REDIS消息-收到上级等到设备推流的redis消息] {}", new String(message.getBody())); logger.info("[REDIS消息-收到上级等到设备推流的redis消息] {}", new String(message.getBody()));
@ -66,7 +81,17 @@ public class RedisPlatformWaitPushStreamOnlineListener implements MessageListene
null); null);
hookSubscribe.addSubscribe(hook, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> { hookSubscribe.addSubscribe(hook, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
// 读取redis中的上级点播信息生成sendRtpItm发送出去 // 读取redis中的上级点播信息生成sendRtpItm发送出去
SendRtpItem sendRtpItem = redisCatchStorage.getWaiteSendRtpItem(messageForPushChannel.getApp(), messageForPushChannel.getStream());
if (sendRtpItem.getSsrc() == null) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServerItemInUse.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItemInUse.getId());
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setMediaServerId(mediaServerItemInUse.getId());
sendRtpItem.setLocalIp(mediaServerItemInUse.getSdpIp());
redisPlatformPushStreamOnlineLister.sendStreamEvent(sendRtpItem);
// 通知其他wvp 由RedisPlatformPushStreamOnlineLister接收此监听。
redisCatchStorage.sendPushStreamOnline(sendRtpItem);
}
}); });

View File

@ -1,97 +0,0 @@
package com.genersoft.iot.vmp.service.redisMsg;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
import com.genersoft.iot.vmp.media.zlm.dto.ChannelOnlineEvent;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* wvp
* @author lin
*/
@Component
public class RedisStreamMsgListener implements MessageListener {
private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class);
@Autowired
private UserSetting userSetting;
@Autowired
private ZLMMediaListManager zlmMediaListManager;
private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
@Qualifier("taskExecutor")
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Override
public void onMessage(Message message, byte[] bytes) {
boolean isEmpty = taskQueue.isEmpty();
taskQueue.offer(message);
if (isEmpty) {
taskExecutor.execute(() -> {
while (!taskQueue.isEmpty()) {
Message msg = taskQueue.poll();
try {
JSONObject steamMsgJson = JSON.parseObject(msg.getBody(), JSONObject.class);
if (steamMsgJson == null) {
logger.warn("[收到redis 流变化]消息解析失败");
continue;
}
String serverId = steamMsgJson.getString("serverId");
if (userSetting.getServerId().equals(serverId)) {
// 自己发送的消息忽略即可
continue;
}
logger.info("[收到redis 流变化] {}", new String(message.getBody()));
String app = steamMsgJson.getString("app");
String stream = steamMsgJson.getString("stream");
boolean register = steamMsgJson.getBoolean("register");
String mediaServerId = steamMsgJson.getString("mediaServerId");
OnStreamChangedHookParam onStreamChangedHookParam = new OnStreamChangedHookParam();
onStreamChangedHookParam.setSeverId(serverId);
onStreamChangedHookParam.setApp(app);
onStreamChangedHookParam.setStream(stream);
onStreamChangedHookParam.setRegist(register);
onStreamChangedHookParam.setMediaServerId(mediaServerId);
onStreamChangedHookParam.setCreateStamp(System.currentTimeMillis()/1000);
onStreamChangedHookParam.setAliveSecond(0L);
onStreamChangedHookParam.setTotalReaderCount("0");
onStreamChangedHookParam.setOriginType(0);
onStreamChangedHookParam.setOriginTypeStr("0");
onStreamChangedHookParam.setOriginTypeStr("unknown");
ChannelOnlineEvent channelOnlineEventLister = zlmMediaListManager.getChannelOnlineEventLister(app, stream);
if ( channelOnlineEventLister != null) {
try {
channelOnlineEventLister.run(app, stream, serverId);;
} catch (ParseException e) {
logger.error("addPush: ", e);
}
zlmMediaListManager.removedChannelOnlineEventLister(app, stream);
}
}catch (Exception e) {
logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
logger.error("[REDIS消息-流变化] 异常内容: ", e);
}
}
});
}
}
}

View File

@ -219,5 +219,9 @@ public interface IRedisCatchStorage {
void addWaiteSendRtpItem(SendRtpItem sendRtpItem, int platformPlayTimeout); void addWaiteSendRtpItem(SendRtpItem sendRtpItem, int platformPlayTimeout);
SendRtpItem getWaiteSendRtpItem(String app, String stream);
void sendStartSendRtp(SendRtpItem sendRtpItem); void sendStartSendRtp(SendRtpItem sendRtpItem);
void sendPushStreamOnline(SendRtpItem sendRtpItem);
} }

View File

@ -685,9 +685,22 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
redisTemplate.opsForValue().set(key, platformPlayTimeout); redisTemplate.opsForValue().set(key, platformPlayTimeout);
} }
@Override
public SendRtpItem getWaiteSendRtpItem(String app, String stream) {
String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + app + "_" + stream;
return (SendRtpItem)redisTemplate.opsForValue().get(key);
}
@Override @Override
public void sendStartSendRtp(SendRtpItem sendRtpItem) { public void sendStartSendRtp(SendRtpItem sendRtpItem) {
String key = VideoManagerConstants.START_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream(); String key = VideoManagerConstants.START_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream();
redisTemplate.opsForValue().set(key, JSON.toJSONString(sendRtpItem)); redisTemplate.opsForValue().set(key, JSON.toJSONString(sendRtpItem));
} }
@Override
public void sendPushStreamOnline(SendRtpItem sendRtpItem) {
String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED;
logger.info("[redis发送通知] 流上线 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getPlatformId());
redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem));
}
} }