优化集群方案, 每个zlm一套ssrc;
优化集群下的docker接入逻辑; 更正sql脚本; 支持重启不设置设备离线。重启SIP事务不丢失
This commit is contained in:
@@ -12,6 +12,7 @@ import javax.sip.header.CallIdHeader;
|
||||
import javax.sip.message.Response;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import gov.nist.javax.sip.SipProviderImpl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -39,7 +40,7 @@ public class SipLayer implements SipListener {
|
||||
@Autowired
|
||||
private SipSubscribe sipSubscribe;
|
||||
|
||||
private SipStack sipStack;
|
||||
private SipStackImpl sipStack;
|
||||
|
||||
private SipFactory sipFactory;
|
||||
|
||||
@@ -52,7 +53,7 @@ public class SipLayer implements SipListener {
|
||||
private ThreadPoolExecutor initSipServer() {
|
||||
|
||||
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
|
||||
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<Runnable>(10000);
|
||||
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
|
||||
processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,
|
||||
0L,TimeUnit.MILLISECONDS,processQueue,
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
@@ -88,17 +89,14 @@ public class SipLayer implements SipListener {
|
||||
|
||||
@Bean("tcpSipProvider")
|
||||
@DependsOn("sipStack")
|
||||
private SipProvider startTcpListener() {
|
||||
private SipProviderImpl startTcpListener() {
|
||||
ListeningPoint tcpListeningPoint = null;
|
||||
SipProvider tcpSipProvider = null;
|
||||
SipProviderImpl tcpSipProvider = null;
|
||||
try {
|
||||
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP");
|
||||
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
|
||||
tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
|
||||
tcpSipProvider.addSipListener(this);
|
||||
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "}");
|
||||
// } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
|
||||
// logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
|
||||
// }
|
||||
} catch (TransportNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
@@ -114,13 +112,14 @@ public class SipLayer implements SipListener {
|
||||
|
||||
@Bean("udpSipProvider")
|
||||
@DependsOn("sipStack")
|
||||
private SipProvider startUdpListener() {
|
||||
private SipProviderImpl startUdpListener() {
|
||||
ListeningPoint udpListeningPoint = null;
|
||||
SipProvider udpSipProvider = null;
|
||||
SipProviderImpl udpSipProvider = null;
|
||||
try {
|
||||
udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP");
|
||||
udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
|
||||
udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
|
||||
udpSipProvider.addSipListener(this);
|
||||
// udpSipProvider.setAutomaticDialogSupportEnabled(false);
|
||||
} catch (TransportNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
@@ -141,7 +140,7 @@ public class SipLayer implements SipListener {
|
||||
*/
|
||||
@Override
|
||||
public void processRequest(RequestEvent evt) {
|
||||
// logger.debug(evt.getRequest().toString());
|
||||
logger.debug(evt.getRequest().toString());
|
||||
// 由于jainsip是单线程程序,为提高性能并发处理
|
||||
processThreadPool.execute(() -> {
|
||||
if (processorFactory != null) {
|
||||
@@ -153,7 +152,7 @@ public class SipLayer implements SipListener {
|
||||
@Override
|
||||
public void processResponse(ResponseEvent evt) {
|
||||
Response response = evt.getResponse();
|
||||
// logger.debug(evt.getResponse().toString());
|
||||
logger.debug(evt.getResponse().toString());
|
||||
int status = response.getStatusCode();
|
||||
if (((status >= 200) && (status < 300)) || status == 401) { // Success!
|
||||
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
|
||||
@@ -163,6 +162,7 @@ public class SipLayer implements SipListener {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
|
||||
if (callIdHeader != null) {
|
||||
@@ -220,7 +220,6 @@ public class SipLayer implements SipListener {
|
||||
@Override
|
||||
public void processIOException(IOExceptionEvent exceptionEvent) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +235,6 @@ public class SipLayer implements SipListener {
|
||||
@Override
|
||||
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import javax.sip.message.Request;
|
||||
|
||||
public class SsrcTransaction {
|
||||
|
||||
private String deviceId;
|
||||
private String channelId;
|
||||
private String ssrc;
|
||||
private String streamId;
|
||||
private byte[] transaction;
|
||||
private byte[] dialog;
|
||||
private String mediaServerId;
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public void setChannelId(String channelId) {
|
||||
this.channelId = channelId;
|
||||
}
|
||||
|
||||
public String getSsrc() {
|
||||
return ssrc;
|
||||
}
|
||||
|
||||
public void setSsrc(String ssrc) {
|
||||
this.ssrc = ssrc;
|
||||
}
|
||||
|
||||
public String getStreamId() {
|
||||
return streamId;
|
||||
}
|
||||
|
||||
public void setStreamId(String streamId) {
|
||||
this.streamId = streamId;
|
||||
}
|
||||
|
||||
public byte[] getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public void setTransaction(byte[] transaction) {
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
public byte[] getDialog() {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public void setDialog(byte[] dialog) {
|
||||
this.dialog = dialog;
|
||||
}
|
||||
|
||||
public String getMediaServerId() {
|
||||
return mediaServerId;
|
||||
}
|
||||
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
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.IMediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@@ -77,7 +76,7 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
|
||||
}
|
||||
stream.append(sendRtpItem.getStreamId());
|
||||
redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
|
||||
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app.toString());
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.genersoft.iot.vmp.gb28181.session;
|
||||
|
||||
import com.genersoft.iot.vmp.utils.ConfigConst;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class SsrcConfig {
|
||||
|
||||
/**
|
||||
* zlm流媒体服务器Id
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
private String ssrcPrefix;
|
||||
/**
|
||||
* zlm流媒体服务器已用会话句柄
|
||||
*/
|
||||
private List<String> isUsed;
|
||||
/**
|
||||
* zlm流媒体服务器可用会话句柄
|
||||
*/
|
||||
private List<String> notUsed;
|
||||
|
||||
public SsrcConfig() {
|
||||
}
|
||||
|
||||
public SsrcConfig(String mediaServerId, Set<String> usedSet, String sipDomain) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
this.isUsed = new ArrayList<>();
|
||||
this.ssrcPrefix = sipDomain.substring(3, 8);
|
||||
this.notUsed = new ArrayList<>();
|
||||
for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) {
|
||||
String ssrc;
|
||||
if (i < 10) {
|
||||
ssrc = "000" + i;
|
||||
} else if (i < 100) {
|
||||
ssrc = "00" + i;
|
||||
} else if (i < 1000) {
|
||||
ssrc = "0" + i;
|
||||
} else {
|
||||
ssrc = String.valueOf(i);
|
||||
}
|
||||
if (null == usedSet || !usedSet.contains(ssrc)) {
|
||||
this.notUsed.add(ssrc);
|
||||
} else {
|
||||
this.isUsed.add(ssrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取视频预览的SSRC值,第一位固定为0
|
||||
* @return ssrc
|
||||
*/
|
||||
public String getPlaySsrc() {
|
||||
return "0" + getSsrcPrefix() + getSN();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取录像回放的SSRC值,第一位固定为1
|
||||
*
|
||||
*/
|
||||
public String getPlayBackSsrc() {
|
||||
return "1" + getSsrcPrefix() + getSN();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
|
||||
* @param ssrc 需要重置的ssrc
|
||||
*/
|
||||
public void releaseSsrc(String ssrc) {
|
||||
if (ssrc == null) {
|
||||
return;
|
||||
}
|
||||
String sn = ssrc.substring(6);
|
||||
try {
|
||||
isUsed.remove(sn);
|
||||
notUsed.add(sn);
|
||||
}catch (NullPointerException e){
|
||||
System.out.printf("11111");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后四位数SN,随机数
|
||||
*
|
||||
*/
|
||||
private String getSN() {
|
||||
String sn = null;
|
||||
int index = 0;
|
||||
if (notUsed.size() == 0) {
|
||||
throw new RuntimeException("ssrc已经用完");
|
||||
} else if (notUsed.size() == 1) {
|
||||
sn = notUsed.get(0);
|
||||
} else {
|
||||
index = new Random().nextInt(notUsed.size() - 1);
|
||||
sn = notUsed.get(index);
|
||||
}
|
||||
notUsed.remove(index);
|
||||
isUsed.add(sn);
|
||||
return sn;
|
||||
}
|
||||
|
||||
public String getSsrcPrefix() {
|
||||
return ssrcPrefix;
|
||||
}
|
||||
|
||||
public String getMediaServerId() {
|
||||
return mediaServerId;
|
||||
}
|
||||
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public void setSsrcPrefix(String ssrcPrefix) {
|
||||
this.ssrcPrefix = ssrcPrefix;
|
||||
}
|
||||
|
||||
public List<String> getIsUsed() {
|
||||
return isUsed;
|
||||
}
|
||||
|
||||
public void setIsUsed(List<String> isUsed) {
|
||||
this.isUsed = isUsed;
|
||||
}
|
||||
|
||||
public List<String> getNotUsed() {
|
||||
return notUsed;
|
||||
}
|
||||
|
||||
public void setNotUsed(List<String> notUsed) {
|
||||
this.notUsed = notUsed;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.genersoft.iot.vmp.gb28181.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @Description:SIP信令中的SSRC工具类。SSRC值由10位十进制整数组成的字符串,第一位为0代表实况,为1则代表回放;第二位至第六位由监控域ID的第4位到第8位组成;最后4位为不重复的4个整数
|
||||
* @author: swwheihei
|
||||
* @date: 2020年5月10日 上午11:57:57
|
||||
*/
|
||||
public class SsrcUtil {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(SsrcUtil.class);
|
||||
|
||||
private static String ssrcPrefix;
|
||||
|
||||
private static List<String> isUsed;
|
||||
|
||||
private static List<String> notUsed;
|
||||
|
||||
private static void init() {
|
||||
SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig");
|
||||
ssrcPrefix = sipConfig.getSipDomain().substring(3, 8);
|
||||
isUsed = new ArrayList<String>();
|
||||
notUsed = new ArrayList<String>();
|
||||
for (int i = 1; i < 10000; i++) {
|
||||
if (i < 10) {
|
||||
notUsed.add("000" + i);
|
||||
} else if (i < 100) {
|
||||
notUsed.add("00" + i);
|
||||
} else if (i < 1000) {
|
||||
notUsed.add("0" + i);
|
||||
} else {
|
||||
notUsed.add(String.valueOf(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频预览的SSRC值,第一位固定为0
|
||||
*
|
||||
*/
|
||||
public static String getPlaySsrc() {
|
||||
return "0" + getSsrcPrefix() + getSN();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取录像回放的SSRC值,第一位固定为1
|
||||
*
|
||||
*/
|
||||
public static String getPlayBackSsrc() {
|
||||
return "1" + getSsrcPrefix() + getSN();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
|
||||
*
|
||||
*/
|
||||
public static void releaseSsrc(String ssrc) {
|
||||
if (ssrc == null) {
|
||||
logger.error("要释放ssrc为null");
|
||||
return;
|
||||
}
|
||||
String sn = ssrc.substring(6);
|
||||
isUsed.remove(sn);
|
||||
notUsed.add(sn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后四位数SN,随机数
|
||||
*
|
||||
*/
|
||||
private static String getSN() {
|
||||
String sn = null;
|
||||
int index = 0;
|
||||
if (notUsed.size() == 0) {
|
||||
throw new RuntimeException("ssrc已经用完");
|
||||
} else if (notUsed.size() == 1) {
|
||||
sn = notUsed.get(0);
|
||||
} else {
|
||||
index = new Random().nextInt(notUsed.size() - 1);
|
||||
sn = notUsed.get(index);
|
||||
}
|
||||
notUsed.remove(index);
|
||||
isUsed.add(sn);
|
||||
return sn;
|
||||
}
|
||||
|
||||
private static String getSsrcPrefix() {
|
||||
if (ssrcPrefix == null) {
|
||||
init();
|
||||
}
|
||||
return ssrcPrefix;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,23 @@
|
||||
package com.genersoft.iot.vmp.gb28181.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.sip.ClientTransaction;
|
||||
import javax.sip.Dialog;
|
||||
import javax.sip.message.Request;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.utils.SerializeUtils;
|
||||
import com.genersoft.iot.vmp.utils.redis.JedisUtil;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -14,50 +28,85 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class VideoStreamSessionManager {
|
||||
|
||||
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
|
||||
private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>();
|
||||
private ConcurrentHashMap<String, String> streamIdMap = new ConcurrentHashMap<>();
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
public String createPlaySsrc(){
|
||||
return SsrcUtil.getPlaySsrc();
|
||||
public void put(String deviceId, String channelId ,String ssrc, String streamId, String mediaServerId, ClientTransaction transaction){
|
||||
SsrcTransaction ssrcTransaction = new SsrcTransaction();
|
||||
ssrcTransaction.setDeviceId(deviceId);
|
||||
ssrcTransaction.setChannelId(channelId);
|
||||
ssrcTransaction.setStreamId(streamId);
|
||||
byte[] transactionByteArray = SerializeUtils.serialize(transaction);
|
||||
ssrcTransaction.setTransaction(transactionByteArray);
|
||||
ssrcTransaction.setSsrc(ssrc);
|
||||
ssrcTransaction.setMediaServerId(mediaServerId);
|
||||
|
||||
redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId, ssrcTransaction);
|
||||
}
|
||||
|
||||
public String createPlayBackSsrc(){
|
||||
return SsrcUtil.getPlayBackSsrc();
|
||||
}
|
||||
|
||||
public void put(String deviceId, String channelId ,String ssrc, String streamId, ClientTransaction transaction){
|
||||
sessionMap.put(deviceId + "_" + channelId, transaction);
|
||||
ssrcMap.put(deviceId + "_" + channelId, ssrc);
|
||||
streamIdMap.put(deviceId + "_" + channelId, streamId);
|
||||
|
||||
public void put(String deviceId, String channelId , Dialog dialog){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction != null) {
|
||||
byte[] dialogByteArray = SerializeUtils.serialize(dialog);
|
||||
ssrcTransaction.setDialog(dialogByteArray);
|
||||
}
|
||||
redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId, ssrcTransaction);
|
||||
}
|
||||
|
||||
|
||||
public ClientTransaction getTransaction(String deviceId, String channelId){
|
||||
return sessionMap.get(deviceId + "_" + channelId);
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction == null) return null;
|
||||
byte[] transactionByteArray = ssrcTransaction.getTransaction();
|
||||
ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
|
||||
return clientTransaction;
|
||||
}
|
||||
|
||||
public SIPDialog getDialog(String deviceId, String channelId){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction == null) return null;
|
||||
byte[] dialogByteArray = ssrcTransaction.getDialog();
|
||||
if (dialogByteArray == null) return null;
|
||||
SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public SsrcTransaction getSsrcTransaction(String deviceId, String channelId){
|
||||
SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId);
|
||||
return ssrcTransaction;
|
||||
}
|
||||
|
||||
public String getStreamId(String deviceId, String channelId){
|
||||
return streamIdMap.get(deviceId + "_" + channelId);
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction == null) return null;
|
||||
return ssrcTransaction.getStreamId();
|
||||
}
|
||||
public String getMediaServerId(String deviceId, String channelId){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction == null) return null;
|
||||
return ssrcTransaction.getMediaServerId();
|
||||
}
|
||||
|
||||
public String getSSRC(String deviceId, String channelId){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction == null) return null;
|
||||
return ssrcTransaction.getSsrc();
|
||||
}
|
||||
|
||||
public void remove(String deviceId, String channelId) {
|
||||
sessionMap.remove(deviceId + "_" + channelId);
|
||||
if (ssrcMap.get(deviceId + "_" + channelId) != null) {
|
||||
SsrcUtil.releaseSsrc(ssrcMap.get(deviceId + "_" + channelId));
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction == null) return;
|
||||
redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId);
|
||||
}
|
||||
|
||||
public List<SsrcTransaction> getAllSsrc() {
|
||||
List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX));
|
||||
List<SsrcTransaction> result= new ArrayList<>();
|
||||
for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
|
||||
String key = (String)ssrcTransactionKeys.get(i);
|
||||
SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(key);
|
||||
result.add(ssrcTransaction);
|
||||
}
|
||||
ssrcMap.remove(deviceId + "_" + channelId);
|
||||
streamIdMap.remove(deviceId + "_" + channelId);
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, ClientTransaction> getSessionMap() {
|
||||
return sessionMap;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, String> getSsrcMap() {
|
||||
return ssrcMap;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, String> getStreamIdMap() {
|
||||
return streamIdMap;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import javax.sip.header.CSeqHeader;
|
||||
import javax.sip.message.Request;
|
||||
import javax.sip.message.Response;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
@@ -108,7 +109,6 @@ public class SIPProcessorFactory {
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
|
||||
// 注:这里使用注解会导致循环依赖注入,暂用springBean
|
||||
private SipProvider tcpSipProvider;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||
|
||||
/**
|
||||
* @Description:设备能力接口,用于定义设备的控制、查询能力
|
||||
@@ -92,7 +92,7 @@ public interface ISIPCommander {
|
||||
* @param device 视频设备
|
||||
* @param channelId 预览通道
|
||||
*/
|
||||
void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||
void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||
|
||||
/**
|
||||
* 请求回放视频流
|
||||
@@ -102,7 +102,7 @@ public interface ISIPCommander {
|
||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||
void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||
|
||||
/**
|
||||
* 请求历史媒体下载
|
||||
@@ -113,12 +113,10 @@ public interface ISIPCommander {
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
* @param downloadSpeed 下载倍速参数
|
||||
*/
|
||||
void downloadStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||
void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||
|
||||
/**
|
||||
* 视频流停止
|
||||
*
|
||||
* @param ssrc ssrc
|
||||
*/
|
||||
void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
|
||||
void streamByeCmd(String deviceId, String channelId);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashSet;
|
||||
|
||||
import javax.sip.*;
|
||||
import javax.sip.address.SipURI;
|
||||
@@ -8,18 +10,21 @@ import javax.sip.header.CallIdHeader;
|
||||
import javax.sip.header.ViaHeader;
|
||||
import javax.sip.message.Request;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.MediaConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetup;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.media.zlm.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||
import gov.nist.javax.sip.SipProviderImpl;
|
||||
import gov.nist.javax.sip.SipStackImpl;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import gov.nist.javax.sip.stack.SIPTransaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -35,7 +40,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -55,12 +59,12 @@ public class SIPCommander implements ISIPCommander {
|
||||
@Lazy
|
||||
@Autowired
|
||||
@Qualifier(value="tcpSipProvider")
|
||||
private SipProvider tcpSipProvider;
|
||||
private SipProviderImpl tcpSipProvider;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
@Qualifier(value="udpSipProvider")
|
||||
private SipProvider udpSipProvider;
|
||||
private SipProviderImpl udpSipProvider;
|
||||
|
||||
@Autowired
|
||||
private SIPRequestHeaderProvider headerProvider;
|
||||
@@ -74,9 +78,6 @@ public class SIPCommander implements ISIPCommander {
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private ZLMRTPServerFactory zlmrtpServerFactory;
|
||||
|
||||
@Autowired
|
||||
private UserSetup userSetup;
|
||||
|
||||
@@ -86,6 +87,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
@Autowired
|
||||
private SipSubscribe sipSubscribe;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
private SIPDialog dialog;
|
||||
|
||||
public SipConfig getSipConfig() {
|
||||
return sipConfig;
|
||||
}
|
||||
@@ -334,26 +340,13 @@ public class SIPCommander implements ISIPCommander {
|
||||
* @param errorEvent sip错误订阅
|
||||
*/
|
||||
@Override
|
||||
public void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
|
||||
String streamId = null;
|
||||
public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
|
||||
String streamId = ssrcInfo.getStreamId();
|
||||
try {
|
||||
if (device == null) return;
|
||||
String streamMode = device.getStreamMode().toUpperCase();
|
||||
|
||||
String ssrc = streamSession.createPlaySsrc();
|
||||
if (mediaServerItem.isRtpEnable()) {
|
||||
streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
|
||||
}else {
|
||||
streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
|
||||
}
|
||||
Integer mediaPort = null;
|
||||
// 使用动态udp端口
|
||||
if (mediaServerItem.isRtpEnable()) {
|
||||
mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
|
||||
}else {
|
||||
mediaPort = mediaServerItem.getRtpProxyPort();
|
||||
}
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
// 添加订阅
|
||||
JSONObject subscribeKey = new JSONObject();
|
||||
subscribeKey.put("app", "rtp");
|
||||
@@ -361,7 +354,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
subscribeKey.put("regist", true);
|
||||
subscribeKey.put("mediaServerId", mediaServerItem.getId());
|
||||
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
|
||||
event.response(mediaServerItemInUse, json);
|
||||
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
|
||||
@@ -369,7 +362,6 @@ public class SIPCommander implements ISIPCommander {
|
||||
//
|
||||
StringBuffer content = new StringBuffer(200);
|
||||
content.append("v=0\r\n");
|
||||
// content.append("o=" + sipConfig.getSipId() + " 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
|
||||
content.append("o="+"00000"+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
||||
content.append("s=Play\r\n");
|
||||
content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
||||
@@ -377,11 +369,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
if (userSetup.isSeniorSdp()) {
|
||||
if("TCP-PASSIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}else if("UDP".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -402,11 +394,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
}else {
|
||||
if("TCP-PASSIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}else if("UDP".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -421,20 +413,25 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
}
|
||||
|
||||
content.append("y="+ssrc+"\r\n");//ssrc
|
||||
content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc
|
||||
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
|
||||
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
|
||||
: udpSipProvider.getNewCallId();
|
||||
|
||||
Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc, callIdHeader);
|
||||
Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader);
|
||||
|
||||
ClientTransaction transaction = transmitRequest(device, request, (e -> {
|
||||
String finalStreamId = streamId;
|
||||
transmitRequest(device, request, (e -> {
|
||||
streamSession.remove(device.getDeviceId(), channelId);
|
||||
mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
|
||||
errorEvent.response(e);
|
||||
}));
|
||||
streamSession.put(device.getDeviceId(), channelId ,ssrc,streamId, transaction);
|
||||
}), e ->{
|
||||
streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(),e.getClientTransaction());
|
||||
streamSession.put(device.getDeviceId(), channelId , e.getDialog());
|
||||
});
|
||||
|
||||
|
||||
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
@@ -450,30 +447,21 @@ public class SIPCommander implements ISIPCommander {
|
||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
@Override
|
||||
public void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event
|
||||
public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event
|
||||
, SipSubscribe.Event errorEvent) {
|
||||
try {
|
||||
String ssrc = streamSession.createPlayBackSsrc();
|
||||
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
|
||||
|
||||
Integer mediaPort = null;
|
||||
// 使用动态udp端口
|
||||
if (mediaServerItem.isRtpEnable()) {
|
||||
mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
|
||||
}else {
|
||||
mediaPort = mediaServerItem.getRtpProxyPort();
|
||||
}
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
|
||||
// 添加订阅
|
||||
JSONObject subscribeKey = new JSONObject();
|
||||
subscribeKey.put("app", "rtp");
|
||||
subscribeKey.put("stream", streamId);
|
||||
subscribeKey.put("stream", ssrcInfo.getStreamId());
|
||||
subscribeKey.put("regist", true);
|
||||
subscribeKey.put("mediaServerId", mediaServerItem.getId());
|
||||
logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
|
||||
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
|
||||
event.response(mediaServerItemInUse, json);
|
||||
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
|
||||
@@ -494,11 +482,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
if (userSetup.isSeniorSdp()) {
|
||||
if("TCP-PASSIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}else if("UDP".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -519,11 +507,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
}else {
|
||||
if("TCP-PASSIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}else if("UDP".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -538,7 +526,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
}
|
||||
|
||||
content.append("y="+ssrc+"\r\n");//ssrc
|
||||
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
|
||||
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
|
||||
@@ -547,9 +535,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
|
||||
|
||||
ClientTransaction transaction = transmitRequest(device, request, errorEvent);
|
||||
streamSession.put(device.getDeviceId(), channelId, ssrc, streamId, transaction);
|
||||
|
||||
transmitRequest(device, request, errorEvent, okEvent -> {
|
||||
Dialog dialog = okEvent.getClientTransaction().getDialog();
|
||||
streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), okEvent.getClientTransaction());
|
||||
streamSession.put(device.getDeviceId(), channelId, dialog);
|
||||
});
|
||||
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -565,30 +555,20 @@ public class SIPCommander implements ISIPCommander {
|
||||
* @param downloadSpeed 下载倍速参数
|
||||
*/
|
||||
@Override
|
||||
public void downloadStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
|
||||
public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
|
||||
, SipSubscribe.Event errorEvent) {
|
||||
try {
|
||||
String ssrc = streamSession.createPlayBackSsrc();
|
||||
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
|
||||
|
||||
Integer mediaPort = null;
|
||||
// 使用动态udp端口
|
||||
if (mediaServerItem.isRtpEnable()) {
|
||||
mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
|
||||
}else {
|
||||
mediaPort = mediaServerItem.getRtpProxyPort();
|
||||
}
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
|
||||
// 添加订阅
|
||||
JSONObject subscribeKey = new JSONObject();
|
||||
subscribeKey.put("app", "rtp");
|
||||
subscribeKey.put("stream", streamId);
|
||||
subscribeKey.put("stream", ssrcInfo.getStreamId());
|
||||
subscribeKey.put("regist", true);
|
||||
subscribeKey.put("mediaServerId", mediaServerItem.getId());
|
||||
logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
|
||||
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
|
||||
event.response(mediaServerItemInUse, json);
|
||||
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
|
||||
@@ -609,11 +589,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
if (userSetup.isSeniorSdp()) {
|
||||
if("TCP-PASSIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}else if("UDP".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -634,11 +614,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
}else {
|
||||
if("TCP-PASSIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}else if ("TCP-ACTIVE".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
||||
}else if("UDP".equals(streamMode)) {
|
||||
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
|
||||
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
||||
}
|
||||
content.append("a=recvonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
@@ -654,7 +634,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
|
||||
|
||||
content.append("y="+ssrc+"\r\n");//ssrc
|
||||
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
|
||||
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
|
||||
@@ -664,7 +644,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
|
||||
|
||||
ClientTransaction transaction = transmitRequest(device, request, errorEvent);
|
||||
streamSession.put(device.getDeviceId(), channelId, ssrc, streamId, transaction);
|
||||
streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
|
||||
|
||||
} catch ( SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
@@ -684,53 +664,35 @@ public class SIPCommander implements ISIPCommander {
|
||||
*/
|
||||
@Override
|
||||
public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) {
|
||||
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
|
||||
try {
|
||||
ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
|
||||
// 服务重启后, 无法直接发送bye, 通过手动构建发送
|
||||
// if (transaction == null) {
|
||||
//
|
||||
// if (streamInfo != null) {
|
||||
// MediaServerItem mediaServerItem = redisCatchStorage.getMediaInfo(streamInfo.getMediaServerId());
|
||||
// JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaServerItem,streamInfo.getApp(), streamInfo.getStreamId());
|
||||
// if (mediaList != null) { // 仍在推流才发送
|
||||
// if (mediaList.getInteger("code") == 0) {
|
||||
// JSONArray data = mediaList.getJSONArray("data");
|
||||
// if (data != null && data.size() > 0) {
|
||||
// Device device = storager.queryVideoDevice(deviceId);
|
||||
// if (device != null) {
|
||||
// StreamInfo.TransactionInfo transactionInfo = streamInfo.getTransactionInfo();
|
||||
// try {
|
||||
// Request byteRequest = headerProvider.createByteRequest(device, channelId,
|
||||
// transactionInfo.branch,
|
||||
// transactionInfo.localTag,
|
||||
// transactionInfo.remoteTag,
|
||||
// transactionInfo.callId);
|
||||
// transmitRequest(device, byteRequest);
|
||||
// } catch (InvalidArgumentException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// redisCatchStorage.stopPlay(streamInfo);
|
||||
// }
|
||||
//
|
||||
// if (okEvent != null) {
|
||||
// okEvent.response(null);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
if (transaction == null) {
|
||||
logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
|
||||
return;
|
||||
}
|
||||
Dialog dialog = transaction.getDialog();
|
||||
SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
|
||||
if (dialog == null) {
|
||||
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId);
|
||||
return;
|
||||
}
|
||||
SipStack sipStack = udpSipProvider.getSipStack();
|
||||
SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
|
||||
if (dialog != sipDialog) {
|
||||
dialog = sipDialog;
|
||||
}else {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
try {
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Request byeRequest = dialog.createRequest(Request.BYE);
|
||||
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
|
||||
SIPRequest request = (SIPRequest)transaction.getRequest();
|
||||
@@ -752,7 +714,12 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
dialog.sendRequest(clientTransaction);
|
||||
|
||||
streamSession.remove(deviceId, channelId);
|
||||
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId);
|
||||
if (ssrcTransaction != null) {
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
|
||||
mediaServerService.releaseSsrc(mediaServerItem, ssrcTransaction.getSsrc());
|
||||
streamSession.remove(deviceId, channelId);
|
||||
}
|
||||
} catch (SipException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@@ -81,7 +80,7 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
|
||||
while (!rtpPushed) {
|
||||
try {
|
||||
if (System.currentTimeMillis() - startTime < 30 * 1000) {
|
||||
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
|
||||
rtpPushed = true;
|
||||
logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@@ -65,7 +64,7 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
|
||||
param.put("stream",streamId);
|
||||
param.put("ssrc",sendRtpItem.getSsrc());
|
||||
logger.info("停止向上级推流:" + streamId);
|
||||
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
|
||||
redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
|
||||
if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
|
||||
|
||||
@@ -11,14 +11,11 @@ import javax.sip.header.*;
|
||||
import javax.sip.message.Request;
|
||||
import javax.sip.message.Response;
|
||||
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@@ -97,7 +94,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
|
||||
// 查询平台下是否有该通道
|
||||
DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
|
||||
GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
|
||||
IMediaServerItem mediaServerItem = null;
|
||||
MediaServerItem mediaServerItem = null;
|
||||
// 不是通道可能是直播流
|
||||
if (channel != null && gbStream == null ) {
|
||||
if (channel.getStatus() == 0) {
|
||||
|
||||
@@ -3,14 +3,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.sip.*;
|
||||
import javax.sip.address.Address;
|
||||
import javax.sip.address.SipURI;
|
||||
import javax.sip.header.CSeqHeader;
|
||||
import javax.sip.message.Request;
|
||||
import javax.sip.message.Response;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import gov.nist.javax.sip.ResponseEventExt;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
@@ -28,6 +32,9 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
|
||||
|
||||
@Autowired
|
||||
private VideoStreamSessionManager streamSession;
|
||||
|
||||
/**
|
||||
* 处理invite响应
|
||||
*
|
||||
@@ -46,7 +53,7 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
|
||||
// 下发ack
|
||||
if (statusCode == Response.OK) {
|
||||
ResponseEventExt event = (ResponseEventExt)evt;
|
||||
Dialog dialog = evt.getDialog();
|
||||
SIPDialog dialog = (SIPDialog)evt.getDialog();
|
||||
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
|
||||
Request reqAck = dialog.createAck(cseq.getSeqNumber());
|
||||
SipURI requestURI = (SipURI) reqAck.getRequestURI();
|
||||
@@ -54,7 +61,12 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
|
||||
requestURI.setPort(event.getRemotePort());
|
||||
reqAck.setRequestURI(requestURI);
|
||||
logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
|
||||
SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
|
||||
String deviceId = requestURI.getUser();
|
||||
String channelId = sipURI.getUser();
|
||||
|
||||
dialog.sendAck(reqAck);
|
||||
|
||||
}
|
||||
} catch (InvalidArgumentException | SipException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
Reference in New Issue
Block a user