| | |
| | | @Override |
| | | public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType, @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType, @NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) { |
| | | // 排除api文档的接口,这个接口不需要统一 |
| | | String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook"}; |
| | | String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook","/api/video-"}; |
| | | for (String path : excludePath) { |
| | | if (request.getURI().getPath().startsWith(path)) { |
| | | return body; |
| | |
| | | * 防止返回string时出错 |
| | | * @return |
| | | */ |
| | | @Bean |
| | | /*@Bean |
| | | public HttpMessageConverters custHttpMessageConverter() { |
| | | return new HttpMessageConverters(new FastJsonHttpMessageConverter()); |
| | | } |
| | | }*/ |
| | | } |
| | |
| | | |
| | | public String getHookIp() { |
| | | if (ObjectUtils.isEmpty(hookIp)){ |
| | | return sipIp.split(",")[0]; |
| | | return sipIp; |
| | | }else { |
| | | return hookIp; |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.gb28181.bean; |
| | | |
| | | |
| | | import gov.nist.javax.sip.message.SIPRequest; |
| | | import gov.nist.javax.sip.message.SIPResponse; |
| | | import gov.nist.javax.sip.stack.SIPDialog; |
| | | |
| | | import javax.sip.Dialog; |
| | | |
| | | /** |
| | | * 缓存语音广播的状态 |
| | |
| | | } |
| | | |
| | | public void setSipTransactionInfoByRequset(SIPResponse response) { |
| | | this.sipTransactionInfo = new SipTransactionInfo(response); |
| | | this.sipTransactionInfo = new SipTransactionInfo(response, false); |
| | | } |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.gb28181.bean; |
| | | |
| | | import gov.nist.javax.sip.message.SIPRequest; |
| | | import gov.nist.javax.sip.message.SIPResponse; |
| | | |
| | | public class SipTransactionInfo { |
| | |
| | | private String toTag; |
| | | private String viaBranch; |
| | | |
| | | private boolean fromServer; |
| | | |
| | | public SipTransactionInfo(SIPResponse response, boolean fromServer) { |
| | | this.callId = response.getCallIdHeader().getCallId(); |
| | | this.fromTag = response.getFromTag(); |
| | | this.toTag = response.getToTag(); |
| | | this.viaBranch = response.getTopmostViaHeader().getBranch(); |
| | | this.fromServer = fromServer; |
| | | } |
| | | |
| | | public SipTransactionInfo(SIPResponse response) { |
| | | this.callId = response.getCallIdHeader().getCallId(); |
| | | this.fromTag = response.getFromTag(); |
| | | this.toTag = response.getToTag(); |
| | | this.viaBranch = response.getTopmostViaHeader().getBranch(); |
| | | this.fromServer = true; |
| | | } |
| | | |
| | | public SipTransactionInfo() { |
| | |
| | | public void setViaBranch(String viaBranch) { |
| | | this.viaBranch = viaBranch; |
| | | } |
| | | |
| | | public boolean isFromServer() { |
| | | return fromServer; |
| | | } |
| | | |
| | | public void setFromServer(boolean fromServer) { |
| | | this.fromServer = fromServer; |
| | | } |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.gb28181.session;
|
| | |
|
| | | import com.alibaba.fastjson2.JSON;
|
| | | import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
| | | import com.genersoft.iot.vmp.conf.UserSetting;
|
| | | import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
| | |
| | | ssrcTransaction.setSsrc(ssrc);
|
| | | ssrcTransaction.setMediaServerId(mediaServerId);
|
| | | ssrcTransaction.setType(type);
|
| | | System.out.println(22222);
|
| | | System.out.println(JSON.toJSONString(ssrcTransaction));
|
| | | RedisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId()
|
| | | + "_" + deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
|
| | | }
|
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device;
|
| | | import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
|
| | | import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
|
| | | import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
| | | 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 javax.sip.InvalidArgumentException;
|
| | | import javax.sip.SipException;
|
| | | import java.text.ParseException;
|
| | | import javax.sip.message.Message;
|
| | | import javax.sip.message.Request;
|
| | |
|
| | | /**
|
| | | * @description:设备能力接口,用于定义设备的控制、查询能力
|
| | |
| | |
|
| | | public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
|
| | | Request request = null;
|
| | | //请求行
|
| | | SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
|
| | |
|
| | | // via
|
| | | ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
| | | ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
|
| | |
| | | //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.getFromTag());
|
| | | FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.isFromServer()?transactionInfo.getFromTag():transactionInfo.getToTag());
|
| | | //to
|
| | | SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress());
|
| | | Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
|
| | | ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
|
| | | ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,transactionInfo.isFromServer()?transactionInfo.getToTag():transactionInfo.getFromTag());
|
| | |
|
| | | //Forwards
|
| | | MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
|
| | |
| | | @Override
|
| | | public void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
|
| | | Request byteRequest = headerProvider.createByteRequest(device, channelId, sipTransactionInfo);
|
| | | sipSender.transmitRequest(device.getTransport(), byteRequest, null, okEvent);
|
| | | sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
|
| | | }
|
| | |
|
| | | /**
|
| | | /**
|
| | | * 语音广播
|
| | | *
|
| | | * @param device 视频设备
|
| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| | | import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| | |
| | | param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0"); |
| | | } |
| | | |
| | | JSONObject jsonObject; |
| | | if (sendRtpItem.isTcpActive()) { |
| | | jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param); |
| | | 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); |
| | | }); |
| | | } else { |
| | | param.put("is_udp", is_Udp); |
| | | param.put("dst_url", sendRtpItem.getIp()); |
| | | param.put("dst_port", sendRtpItem.getPort()); |
| | | jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); |
| | | System.out.println(JSON.toJSONString(param)); |
| | | System.out.println(); |
| | | System.out.println(jsonObject); |
| | | } |
| | | |
| | | if (jsonObject == null) { |
| | | logger.error("RTP推流失败: 请检查ZLM服务"); |
| | | } else if (jsonObject.getInteger("code") == 0) { |
| | | logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, ", param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); |
| | | } else { |
| | | logger.error("RTP推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSON(param)); |
| | | if (sendRtpItem.isOnlyAudio()) { |
| | | // 语音对讲 |
| | | Device device = deviceService.getDevice(platformGbId); |
| | | if (device != null) { |
| | | try { |
| | | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), sendRtpItem.getStreamId(), null); |
| | | } catch (SipException | ParseException | InvalidArgumentException | |
| | | SsrcTransactionNotFoundException e) { |
| | | logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | } else { |
| | | // 向上级平台 |
| | | try { |
| | | commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] 国标级联, 回复BYE: {}", e.getMessage()); |
| | | } |
| | | } |
| | | 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); |
| | | }); |
| | | } else { |
| | | // 如果是非严格模式,需要关闭端口占用 |
| | | JSONObject startSendRtpStreamResult = null; |
| | | if (sendRtpItem.getLocalPort() != 0) { |
| | | if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) { |
| | | startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); |
| | | } |
| | | // 如果是非严格模式,需要关闭端口占用 |
| | | JSONObject startSendRtpStreamResult = null; |
| | | if (sendRtpItem.getLocalPort() != 0) { |
| | | 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); |
| | | } |
| | | if (startSendRtpStreamResult != null) { |
| | | startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader); |
| | | } |
| | | } |
| | | |
| | | |
| | | }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); |
| | | } |
| | | } |
| | | if (startSendRtpStreamResult != null) { |
| | | startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader); |
| | | } |
| | | } |
| | | } |
| | | private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, |
| | |
| | | } else { |
| | | logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); |
| | | if (sendRtpItem.isOnlyAudio()) { |
| | | // TODO 可能是语音对讲 |
| | | Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); |
| | | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); |
| | | if (audioBroadcastCatch != null) { |
| | | try { |
| | | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); |
| | | } catch (SipException | ParseException | InvalidArgumentException | |
| | | SsrcTransactionNotFoundException e) { |
| | | logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage()); |
| | | } |
| | | } |
| | | }else { |
| | | // 向上级平台 |
| | | try { |
| | |
| | | if (sendRtpItem != null){ |
| | | logger.info("[收到bye] {}/{}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId()); |
| | | String streamId = sendRtpItem.getStreamId(); |
| | | MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); |
| | | if (mediaServerItem == null) { |
| | | return; |
| | | } |
| | | |
| | | Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), streamId); |
| | | if (!ready) { |
| | | logger.info("[收到bye] 发现流{}/{}已经结束,不需处理", sendRtpItem.getApp(), sendRtpItem.getStreamId()); |
| | | return; |
| | | } |
| | | Map<String, Object> param = new HashMap<>(); |
| | | param.put("vhost","__defaultVhost__"); |
| | | param.put("app",sendRtpItem.getApp()); |
| | |
| | | MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); |
| | | redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), callIdHeader.getCallId(), null); |
| | | zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); |
| | | |
| | | int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); |
| | | if (totalReaderCount <= 0) { |
| | | logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); |
| | |
| | | redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); |
| | | } |
| | | } |
| | | |
| | | playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); |
| | | } |
| | | |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | |
| | | private IMediaServerService mediaServerService; |
| | | |
| | | @Autowired |
| | | private IMediaService mediaService; |
| | | private ISIPCommander commander; |
| | | |
| | | @Autowired |
| | | private ZLMRESTfulUtils zlmresTfulUtils; |
| | |
| | | String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId(); |
| | | |
| | | CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); |
| | | sendRtpItem.setPlayType(InviteStreamType.PLAY); |
| | | sendRtpItem.setPlayType(InviteStreamType.TALK); |
| | | sendRtpItem.setCallId(callIdHeader.getCallId()); |
| | | sendRtpItem.setPlatformId(requesterId); |
| | | sendRtpItem.setStatus(1); |
| | |
| | | |
| | | Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream); |
| | | if (streamReady) { |
| | | SIPResponse sipResponse = sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); |
| | | // 添加事务信息 |
| | | streamSession.put(device.getDeviceId(), audioBroadcastCatch.getChannelId(), request.getCallIdHeader().getCallId() |
| | | , stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), sipResponse, VideoStreamSessionManager.SessionType.broadcast ); |
| | | sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); |
| | | }else { |
| | | logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", app, stream); |
| | | try { |
| | | responseAck(request, Response.GONE); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); |
| | | } |
| | | playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); |
| | | } |
| | | } catch (SdpException e) { |
| | |
| | | if (platform != null) {
|
| | | commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
|
| | | }else {
|
| | | if ("talk".equals(param.getApp()) && sendRtpItem.isOnlyAudio()) {
|
| | | if (sendRtpItem.isOnlyAudio()) {
|
| | | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
|
| | | if (device != null && audioBroadcastCatch != null) {
|
| | | // cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
|
| | | if (audioBroadcastCatch != null) {
|
| | | // playService.stopAudioBroadcast(device.getDeviceId(), sendRtpItem.getChannelId());
|
| | | if ("talk".equals(param.getApp())) {
|
| | | // cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
|
| | | }else {
|
| | | // cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
|
| | | }
|
| | | }
|
| | | }else {
|
| | | cmder.streamByeCmd(device, null, null, sendRtpItem.getCallId());
|
| | | }
|
| | |
|
| | |
|
| | |
|
| | | }
|
| | | } catch (SipException | InvalidArgumentException | ParseException | SsrcTransactionNotFoundException e) {
|
| | | } catch (SipException | InvalidArgumentException | ParseException e) {
|
| | | logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
|
| | | }
|
| | | }
|
| | |
| | | if (device == null) { |
| | | return; |
| | | } |
| | | // if (audioBroadcastCatch.getStatus() == AudioBroadcastCatchStatus.Ok) { |
| | | // cmder.streamByeCmd(device, audioBroadcastCatch.getChannelId(), null, audioBroadcastCatch.getSipTransactionInfo().getCallId()); |
| | | // } |
| | | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); |
| | | if (sendRtpItem != null) { |
| | | redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); |
| | |
| | | deviceAlarm.setAlarmPriority("1"); |
| | | deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601()); |
| | | deviceAlarm.setAlarmType("1"); |
| | | deviceAlarm.setLongitude(0); |
| | | deviceAlarm.setLatitude(0); |
| | | deviceAlarm.setLongitude(0D); |
| | | deviceAlarm.setLatitude(0D); |
| | | |
| | | if (ObjectUtils.isEmpty(gbId)) { |
| | | // 发送给所有的上级 |