级联语音对讲部分

This commit is contained in:
648540858
2022-12-13 11:57:07 +08:00
parent 38a85d432a
commit 4b827f3897
28 changed files with 940 additions and 223 deletions

View File

@@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
import gov.nist.javax.sip.message.SIPResponse;
/**
@@ -10,10 +12,24 @@ import gov.nist.javax.sip.message.SIPResponse;
public class AudioBroadcastCatch {
public AudioBroadcastCatch(String deviceId, String channelId, AudioBroadcastCatchStatus status) {
public AudioBroadcastCatch(
String deviceId,
String channelId,
MediaServerItem mediaServerItem,
String app,
String stream,
AudioBroadcastEvent event,
AudioBroadcastCatchStatus status,
boolean isFromPlatform
) {
this.deviceId = deviceId;
this.channelId = channelId;
this.status = status;
this.event = event;
this.isFromPlatform = isFromPlatform;
this.app = app;
this.stream = stream;
this.mediaServerItem = mediaServerItem;
}
public AudioBroadcastCatch() {
@@ -29,6 +45,26 @@ public class AudioBroadcastCatch {
*/
private String channelId;
/**
* 流媒体信息
*/
private MediaServerItem mediaServerItem;
/**
* 关联的流APP
*/
private String app;
/**
* 关联的流STREAM
*/
private String stream;
/**
* 是否是级联语音喊话
*/
private boolean isFromPlatform;
/**
* 语音广播状态
*/
@@ -39,6 +75,11 @@ public class AudioBroadcastCatch {
*/
private SipTransactionInfo sipTransactionInfo;
/**
* 请求结果回调
*/
private AudioBroadcastEvent event;
public String getDeviceId() {
return deviceId;
@@ -75,4 +116,44 @@ public class AudioBroadcastCatch {
public void setSipTransactionInfoByRequset(SIPResponse response) {
this.sipTransactionInfo = new SipTransactionInfo(response, false);
}
public AudioBroadcastEvent getEvent() {
return event;
}
public void setEvent(AudioBroadcastEvent event) {
this.event = event;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public boolean isFromPlatform() {
return isFromPlatform;
}
public void setFromPlatform(boolean fromPlatform) {
isFromPlatform = fromPlatform;
}
public MediaServerItem getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServerItem mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}
}

View File

@@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
public enum InviteStreamType {
PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY,TALK
PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY,BROADCAST,TALK
}

View File

@@ -66,7 +66,7 @@ public class ParentPlatform {
* 设备端口
*/
@Schema(description = "设备端口")
private String devicePort;
private int devicePort;
/**
* SIP认证用户名(默认使用设备国标编号)
@@ -261,11 +261,11 @@ public class ParentPlatform {
this.deviceIp = deviceIp;
}
public String getDevicePort() {
public int getDevicePort() {
return devicePort;
}
public void setDevicePort(String devicePort) {
public void setDevicePort(int devicePort) {
this.devicePort = devicePort;
}

View File

@@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.gb28181.event;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.DeviceNotFoundEvent;
import gov.nist.javax.sip.message.SIPRequest;
import org.slf4j.Logger;
@@ -87,6 +86,11 @@ public class SipSubscribe {
public String callId;
public EventObject event;
public EventResult(int statusCode, String msg) {
this.statusCode = statusCode;
this.msg = msg;
}
public EventResult(EventObject event) {
this.event = event;
if (event instanceof ResponseEvent) {

View File

@@ -23,10 +23,6 @@ public class AudioBroadcastManager {
public static Map<String, AudioBroadcastCatch> data = new ConcurrentHashMap<>();
public void add(AudioBroadcastCatch audioBroadcastCatch) {
this.update(audioBroadcastCatch);
}
public void update(AudioBroadcastCatch audioBroadcastCatch) {
if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) {
data.put(audioBroadcastCatch.getDeviceId(), audioBroadcastCatch);

View File

@@ -49,8 +49,6 @@ public class DeferredResultHolder {
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
private Map<String, Map<String, DeferredResultEx>> map = new ConcurrentHashMap<>();

View File

@@ -1,8 +1,12 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
@@ -14,77 +18,98 @@ public interface ISIPCommanderForPlatform {
/**
* 向上级平台注册
*
* @param parentPlatform
* @return
*/
void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException;
void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
throws InvalidArgumentException, ParseException, SipException;
void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www,
SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister)
throws SipException, InvalidArgumentException, ParseException;
/**
* 向上级平台注销
*
* @param parentPlatform
* @return
*/
void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
throws InvalidArgumentException, ParseException, SipException;
/**
* 向上级平发送心跳信息
*
* @param parentPlatform
* @return callId(作为接受回复的判定)
*/
String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
String keepalive(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
throws SipException, InvalidArgumentException, ParseException;
/**
* 向上级回复通道信息
* @param channel 通道信息
*
* @param channel 通道信息
* @param parentPlatform 平台信息
* @param sn
* @param fromTag
* @param size
* @return
*/
void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException;
void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException;
void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size)
throws SipException, InvalidArgumentException, ParseException;
void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag)
throws InvalidArgumentException, ParseException, SipException;
/**
* 向上级回复DeviceInfo查询信息
*
* @param parentPlatform 平台信息
* @param sn
* @param fromTag
* @return
*/
void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag)
throws SipException, InvalidArgumentException, ParseException;
/**
* 向上级回复DeviceStatus查询信息
*
* @param parentPlatform 平台信息
* @param sn
* @param fromTag
* @return
*/
void deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
void deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag)
throws SipException, InvalidArgumentException, ParseException;
/**
* 向上级回复移动位置订阅消息
*
* @param parentPlatform 平台信息
* @param gpsMsgInfo GPS信息
* @param subscribeInfo 订阅相关的信息
* @param gpsMsgInfo GPS信息
* @param subscribeInfo 订阅相关的信息
* @return
*/
void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo)
throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
/**
* 向上级回复报警消息
*
* @param parentPlatform 平台信息
* @param deviceAlarm 报警信息信息
* @param deviceAlarm 报警信息信息
* @return
*/
void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException;
/**
* 回复catalog事件-增加/更新
*
* @param parentPlatform
* @param deviceChannels
*/
@@ -92,22 +117,28 @@ public interface ISIPCommanderForPlatform {
/**
* 回复catalog事件-删除
*
* @param parentPlatform
* @param deviceChannels
*/
void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels,
SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException,
ParseException, NoSuchFieldException, SipException, IllegalAccessException;
/**
* 回复recordInfo
* @param deviceChannel 通道信息
*
* @param deviceChannel 通道信息
* @param parentPlatform 平台信息
* @param fromTag fromTag
* @param recordInfo 录像信息
* @param fromTag fromTag
* @param recordInfo 录像信息
*/
void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException;
void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo)
throws SipException, InvalidArgumentException, ParseException;
/**
* 录像播放推送完成时发送MediaStatus消息
*
* @param platform
* @param sendRtpItem
* @return
@@ -116,9 +147,19 @@ public interface ISIPCommanderForPlatform {
/**
* 向发起点播的上级回复bye
*
* @param platform 平台信息
* @param callId callId
* @param callId callId
*/
void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException;
void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException;
void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException;
void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
}

View File

@@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -14,7 +15,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import javax.sip.*;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
@@ -22,7 +24,6 @@ import javax.sip.message.Request;
import javax.validation.constraints.NotNull;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
@@ -175,7 +176,7 @@ public class SIPRequestHeaderPlarformProvider {
SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
parentPlatform.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
@@ -212,7 +213,7 @@ public class SIPRequestHeaderPlarformProvider {
SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
parentPlatform.getTransport(), SipUtils.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
@@ -272,7 +273,7 @@ public class SIPRequestHeaderPlarformProvider {
SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()),
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), platform.getDevicePort(),
platform.getTransport(), SipUtils.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
@@ -306,6 +307,83 @@ public class SIPRequestHeaderPlarformProvider {
.createSipURI(platform.getDeviceGBId(), sipAddress));
request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
return request;
}
public Request createInviteRequest(ParentPlatform platform, String channelId, String toString, String viaTag, String fromTag, Object content, String ssrc, CallIdHeader callIdHeader, String transport) throws PeerUnavailableException, ParseException, InvalidArgumentException {
Request request = null;
//请求行
String deviceHostAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
HeaderFactory headerFactory = sipLayer.getSipFactory().createHeaderFactory();
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记否则无法创建会话无法回应ack
//to
SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
//Forwards
MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(platform.getDeviceIp())+":"+ deviceHostAddress));
// Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
// Subject
SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
request.addHeader(subjectHeader);
ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createByteRequest(ParentPlatform platform, String channelId, SipTransactionInfo transactionInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException {
String deviceHostAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
Request request = null;
SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), SipUtils.getNewViaTag());
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.isFromServer()?transactionInfo.getFromTag():transactionInfo.getToTag());
//to
SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,transactionInfo.isFromServer()?transactionInfo.getToTag():transactionInfo.getFromTag());
//Forwards
MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort()));
request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
return request;
}
}

View File

@@ -590,12 +590,12 @@ public class SIPCommander implements ISIPCommander {
return;
}
if (!mediaServerItem.isRtpEnable()) {
// 单端口暂不支持语音对讲
logger.info("[语音对讲] 单端口暂不支持此操作");
// 单端口暂不支持语音喊话
logger.info("[语音喊话] 单端口暂不支持此操作");
return;
}
logger.info("[语音对讲] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
logger.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribeForStreamChange, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
if (event != null) {

View File

@@ -1,22 +1,31 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
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.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
import com.genersoft.iot.vmp.utils.DateUtil;
import gov.nist.javax.sip.message.MessageFactoryImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +35,7 @@ import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.header.CallIdHeader;
import javax.sip.header.WWWAuthenticateHeader;
@@ -61,6 +71,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
@Autowired
private SIPSender sipSender;
@Autowired
private ZlmHttpHookSubscribe subscribe;
@Autowired
private UserSetting userSetting;
@Autowired
private VideoStreamSessionManager streamSession;
@Override
public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
register(parentPlatform, null, null, errorEvent, okEvent, false, true);
@@ -645,4 +665,107 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}
sipSender.transmitRequest(platform.getDeviceIp(),byeRequest);
}
@Override
public void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(platform.getServerGBId(), channelId, callId, stream);
if (ssrcTransaction == null) {
throw new SsrcTransactionNotFoundException(platform.getServerGBId(), channelId, callId, stream);
}
mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
Request byteRequest = headerProviderPlatformProvider.createByteRequest(platform, channelId, ssrcTransaction.getSipTransactionInfo());
sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), byteRequest, null, okEvent);
}
@Override
public void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
if (platform == null || deviceChannel == null) {
return;
}
String characterSet = platform.getCharacterSet();
StringBuffer mediaStatusXml = new StringBuffer(200);
mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
mediaStatusXml.append("<Notify>\r\n");
mediaStatusXml.append("<CmdType>Broadcast</CmdType>\r\n");
mediaStatusXml.append("<SN>" + sn + "</SN>\r\n");
mediaStatusXml.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n");
mediaStatusXml.append("<Result>" + (result?"OK":"ERROR") + "</Result>\r\n");
mediaStatusXml.append("</Notify>\r\n");
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(platform.getDeviceIp(), platform.getTransport());
SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(platform, mediaStatusXml.toString(),
SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader);
sipSender.transmitRequest(platform.getDeviceIp(),messageRequest, errorEvent, okEvent);
}
@Override
public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException {
String stream = ssrcInfo.getStream();
if (platform == null) {
return;
}
logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
if (event != null) {
event.response(mediaServerItemInUse, json);
subscribe.removeSubscribe(hookSubscribe);
}
});
String sdpIp = mediaServerItem.getSdpIp();
StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n");
content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 " + sdpIp + "\r\n");
content.append("t=0 0\r\n");
if ("TCP-PASSIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 8 96\r\n");
} else if ("TCP-ACTIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 8 96\r\n");
} else if ("UDP".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 8 96\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:8 PCMA/8000\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
if ("TCP-PASSIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
content.append("a=setup:passive\r\n");
content.append("a=connection:new\r\n");
}else if ("TCP-ACTIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
content.append("a=setup:active\r\n");
content.append("a=connection:new\r\n");
}
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getTransport());
Request request = headerProviderPlatformProvider.createInviteRequest(platform, channelId,
content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),
callIdHeader ,platform.getTransport());
sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> {
streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
errorEvent.response(e);
}), e -> {
// 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
ResponseEvent responseEvent = (ResponseEvent) e.event;
SIPResponse response = (SIPResponse) responseEvent.getResponse();
streamSession.put(platform.getServerGBId(), channelId, callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play);
okEvent.response(e);
});
}
}

View File

@@ -16,8 +16,6 @@ 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.media.zlm.ZLMRTPServerFactory;
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.HookSubscribeForRtpServerTimeout;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService;
@@ -40,8 +38,6 @@ import javax.sip.header.FromHeader;
import javax.sip.header.HeaderAddress;
import javax.sip.header.ToHeader;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
/**
* SIP命令类型 ACK请求
@@ -123,68 +119,31 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
logger.info("收到ACKrtp/{}开始向上级推流, 目标={}:{}SSRC={}, RTCP={}", sendRtpItem.getStreamId(),
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp());
Map<String, Object> param = new HashMap<>(12);
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());
param.put("stream",sendRtpItem.getStreamId());
param.put("ssrc", sendRtpItem.getSsrc());
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");
if (!sendRtpItem.isTcp()) {
// udp模式下开启rtcp保活
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
}
if (mediaInfo == null) {
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(),
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> {
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, json, param, callIdHeader);
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, json, callIdHeader);
});
} else {
// 如果是非严格模式,需要关闭端口占用
JSONObject startSendRtpStreamResult = null;
if (sendRtpItem.getLocalPort() != 0) {
HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(sendRtpItem.getSsrc(), null, mediaInfo.getId());
hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) {
if (sendRtpItem.isTcpActive()) {
startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param);
}else {
param.put("is_udp", is_Udp);
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
}
}
}else {
if (sendRtpItem.isTcpActive()) {
startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param);
}else {
param.put("is_udp", is_Udp);
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
}
}
}else {
JSONObject startSendRtpStreamResult = zlmrtpServerFactory.startSendRtp(mediaInfo, sendRtpItem);
if (startSendRtpStreamResult != null) {
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, callIdHeader);
}
}
}
private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
JSONObject jsonObject, CallIdHeader callIdHeader) {
if (jsonObject == null) {
logger.error("RTP推流失败: 请检查ZLM服务");
} else if (jsonObject.getInteger("code") == 0) {
logger.info("调用ZLM推流接口, 结果: {}", jsonObject);
logger.info("RTP推流成功[ {}/{} ]{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
logger.info("RTP推流成功[ {}/{} ]{}->{}:{}, " ,sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getIp(), sendRtpItem.getPort());
} else {
logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param));
logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(sendRtpItem));
if (sendRtpItem.isOnlyAudio()) {
Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
@@ -193,7 +152,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
} catch (SipException | ParseException | InvalidArgumentException |
SsrcTransactionNotFoundException e) {
logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage());
logger.error("[命令发送失败] 停止语音喊话: {}", e.getMessage());
}
}
}else {

View File

@@ -201,7 +201,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
MediaServerItem mediaServerItem = null;
StreamPushItem streamPushItem = null;
StreamProxyItem proxyByAppAndStream =null;
StreamProxyItem proxyByAppAndStream = null;
// 不是通道可能是直播流
if (channel != null && gbStream == null) {
// 通道存在发100TRYING
@@ -1001,7 +1001,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId();
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
sendRtpItem.setPlayType(InviteStreamType.TALK);
sendRtpItem.setPlayType(InviteStreamType.BROADCAST);
sendRtpItem.setCallId(callIdHeader.getCallId());
sendRtpItem.setPlatformId(requesterId);
sendRtpItem.setStatus(1);
@@ -1013,6 +1013,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
sendRtpItem.setOnlyAudio(true);
redisCatchStorage.updateSendRTPSever(sendRtpItem);
Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream);
if (streamReady) {
sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc);
@@ -1084,7 +1085,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
audioBroadcastManager.update(audioBroadcastCatch);
} catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) {
logger.error("[命令发送失败] 语音对讲 回复200OKSDP: {}", e.getMessage());
logger.error("[命令发送失败] 语音喊话 回复200OKSDP: {}", e.getMessage());
}
return sipResponse;
}

View File

@@ -0,0 +1,200 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IPlatformService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import gov.nist.javax.sip.message.SIPRequest;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.message.Response;
import java.text.ParseException;
/**
* 状态信息(心跳)报送
*/
@Component
public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
private Logger logger = LoggerFactory.getLogger(BroadcastNotifyMessageHandler.class);
private final static String cmdType = "Broadcast";
@Autowired
private NotifyMessageHandler notifyMessageHandler;
@Autowired
private IVideoManagerStorage storage;
@Autowired
private ISIPCommanderForPlatform commanderForPlatform;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private IPlayService playService;
@Autowired
private IDeviceService deviceService;
@Autowired
private IPlatformService platformService;
@Autowired
private AudioBroadcastManager audioBroadcastManager;
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Override
public void afterPropertiesSet() throws Exception {
notifyMessageHandler.addHandler(cmdType, this);
}
@Override
public void handForDevice(RequestEvent evt, Device device, Element element) {
}
@Override
public void handForPlatform(RequestEvent evt, ParentPlatform platform, Element rootElement) {
// 来自上级平台的语音喊话请求
SIPRequest request = (SIPRequest) evt.getRequest();
try {
Element snElement = rootElement.element("SN");
if (snElement == null) {
responseAck(request, Response.BAD_REQUEST, "sn must not null");
return;
}
String sn = snElement.getText();
Element targetIDElement = rootElement.element("TargetID");
if (targetIDElement == null) {
responseAck(request, Response.BAD_REQUEST, "TargetID must not null");
return;
}
String targetId = targetIDElement.getText();
logger.info("[国标级联 语音喊话] platform: {}, channel: {}", platform.getServerGBId(), targetId);
DeviceChannel deviceChannel = storage.queryChannelInParentPlatform(platform.getServerGBId(), targetId);
if (deviceChannel == null) {
responseAck(request, Response.NOT_FOUND, "TargetID not found");
return;
}
// 向下级发送语音的喊话请求
Device device = deviceService.getDevice(deviceChannel.getDeviceId());
if (device == null) {
responseAck(request, Response.NOT_FOUND, "device not found");
return;
}
responseAck(request, Response.OK);
// 查看语音通道是否已经建立并且已经在使用
if (playService.audioBroadcastInUse(device, targetId)) {
commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, false,null, null);
return;
}
MediaServerItem mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad();
commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, true, eventResult->{
logger.info("[国标级联] 语音喊话 回复失败 platform {} 错误:{}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg);
}, eventResult->{
// 消息发送成功, 向上级发送invite获取推流
try {
platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad, (mediaServerItem, response)->{
// 上级平台推流成功
String app = response.getString("app");
String stream = response.getString("stream");
AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(device.getDeviceId(), targetId);
if (broadcastCatch != null ) {
if (playService.audioBroadcastInUse(device, targetId)) {
logger.info("[国标级联] 语音喊话 设备正正在使用中 platform {} channel: {}",
platform.getServerGBId(), deviceChannel.getChannelId());
// 查看语音通道已经建立且已经占用 回复BYE
try {
platformService.stopBroadcast(platform, deviceChannel.getChannelId(), stream);
} catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException |
SipException e) {
logger.info("[消息发送失败] 国标级联 语音喊话 platform {} channel: {}", platform.getServerGBId(), deviceChannel.getChannelId());
}
}else {
// 查看语音通道已经建立但是未占用
broadcastCatch.setApp(app);
broadcastCatch.setStream(stream);
broadcastCatch.setMediaServerItem(mediaServerItem);
audioBroadcastManager.update(broadcastCatch);
// 推流到设备
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, stream, null);
if (sendRtpItem == null) {
logger.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, stream);
logger.info("[国标级联] 语音喊话 重新开始channelId: {}, stream: {}", targetId, stream);
try {
playService.audioBroadcastCmd(device, targetId, mediaServerItem, app, stream, 60, true, msg -> {
logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId);
});
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.info("[消息发送失败] 国标级联 语音喊话 platform {}", platform.getServerGBId());
}
}else {
// 发流
JSONObject jsonObject = zlmrtpServerFactory.startSendRtp(mediaServerItem, sendRtpItem);
if (jsonObject != null && jsonObject.getInteger("code") == 0 ) {
logger.info("[语音喊话] 自动推流成功, device: {}, channel: {}", device.getDeviceId(), targetId);
}else {
logger.info("[语音喊话] 推流失败, 结果: {}", jsonObject);
}
}
}
}else {
try {
playService.audioBroadcastCmd(device, targetId, mediaServerItem, app, stream, 60, true, msg -> {
logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId);
});
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.info("[消息发送失败] 国标级联 语音喊话 platform {}", platform.getServerGBId());
}
}
}, eventResultForBroadcastInvite -> {
// 收到错误
logger.info("[国标级联-语音喊话] 与下级通道建立失败 device: {}, channel: {} 错误:{}/{}", device.getDeviceId(),
targetId, eventResultForBroadcastInvite.statusCode, eventResultForBroadcastInvite.msg);
}, (code, msg)->{
// 超时
logger.info("[国标级联-语音喊话] 与下级通道建立超时 device: {}, channel: {} 错误:{}/{}", device.getDeviceId(),
targetId, code, msg);
});
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.info("[消息发送失败] 国标级联 语音喊话 invite消息 platform {}", platform.getServerGBId());
}
});
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.info("[消息发送失败] 国标级联 语音喊话 platform {}", platform.getServerGBId());
}
}
}

View File

@@ -1,17 +1,14 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatchStatus;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
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.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import gov.nist.javax.sip.message.SIPRequest;
import org.dom4j.Element;
import org.slf4j.Logger;
@@ -52,26 +49,13 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i
public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
try {
String channelId = getText(rootElement, "DeviceID");
String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId;
// 此处是对本平台发出Broadcast指令的应答
JSONObject json = new JSONObject();
XmlUtil.node2Json(rootElement, json);
if (logger.isDebugEnabled()) {
logger.debug(json.toJSONString());
}
RequestMessage msg = new RequestMessage();
msg.setKey(key);
msg.setData(json);
deferredResultHolder.invokeAllResult(msg);
if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
// 回复410
responseAck((SIPRequest) evt.getRequest(), Response.GONE);
return;
}
logger.info("收到语音广播的回复:{}/{}", device.getDeviceId(), channelId );
String result = getText(rootElement, "Result");
logger.info("收到语音广播的回复 {}{}/{}", result, device.getDeviceId(), channelId );
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId);
audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite);
audioBroadcastManager.update(audioBroadcastCatch);

View File

@@ -1,39 +1,24 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.ResponseEventExt;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.stack.SIPClientTransaction;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPTransaction;
import gov.nist.javax.sip.stack.SIPTransactionImpl;
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.stereotype.Component;
import javax.sdp.SdpFactory;
import javax.sdp.SdpParseException;
import javax.sdp.SessionDescription;
import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.UserAgentHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
@@ -104,6 +89,7 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
} else {
sdp = SdpFactory.getInstance().createSessionDescription(contentString);
}
// 查看是否是来自设备的,此是回复
SipURI requestUri = sipLayer.getSipFactory().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);