| | |
| | | } |
| | | } |
| | | |
| | | public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) { |
| | | String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam); |
| | | public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) { |
| | | String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, callIdParam, isPlay?"play":"push"); |
| | | this.rtc = new StreamURL("http", host, port, file); |
| | | if (sslPort != 0) { |
| | | this.rtcs = new StreamURL("https", host, sslPort, file); |
| | |
| | | this.alarm = alarm; |
| | | } |
| | | |
| | | public void getLocalIp(String deviceLocalIp) { |
| | | if (ObjectUtils.isEmpty(deviceLocalIp)) { |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | private Boolean logInDatebase = Boolean.TRUE; |
| | | |
| | | private Boolean usePushingAsStatus = Boolean.TRUE; |
| | | private Boolean usePushingAsStatus = Boolean.FALSE; |
| | | |
| | | private Boolean useSourceIpAsStreamIp = Boolean.FALSE; |
| | | |
| | |
| | | |
| | | import com.genersoft.iot.vmp.conf.SipConfig; |
| | | import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; |
| | | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | |
| | | } |
| | | |
| | | public void update(AudioBroadcastCatch audioBroadcastCatch) { |
| | | data.put(audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(), audioBroadcastCatch); |
| | | if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) { |
| | | data.put(audioBroadcastCatch.getDeviceId(), audioBroadcastCatch); |
| | | }else { |
| | | data.put(audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(), audioBroadcastCatch); |
| | | } |
| | | } |
| | | |
| | | public void del(String deviceId, String channelId) { |
| | | data.remove(deviceId + channelId); |
| | | if (SipUtils.isFrontEnd(deviceId)) { |
| | | data.remove(deviceId); |
| | | }else { |
| | | data.remove(deviceId + channelId); |
| | | } |
| | | |
| | | } |
| | | |
| | | public void delByDeviceId(String deviceId) { |
| | |
| | | |
| | | public boolean exit(String deviceId, String channelId) { |
| | | for (String key : data.keySet()) { |
| | | if (key.equals(deviceId + channelId)) { |
| | | return true; |
| | | if (SipUtils.isFrontEnd(deviceId)) { |
| | | return key.equals(deviceId); |
| | | }else { |
| | | return key.equals(deviceId + channelId); |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | public AudioBroadcastCatch get(String deviceId, String channelId) { |
| | | AudioBroadcastCatch audioBroadcastCatch = data.get(deviceId + channelId); |
| | | AudioBroadcastCatch audioBroadcastCatch; |
| | | if (SipUtils.isFrontEnd(deviceId)) { |
| | | audioBroadcastCatch = data.get(deviceId); |
| | | }else { |
| | | audioBroadcastCatch = data.get(deviceId + channelId); |
| | | } |
| | | if (audioBroadcastCatch == null) { |
| | | Stream<AudioBroadcastCatch> allAudioBroadcastCatchStreamForDevice = data.values().stream().filter( |
| | | audioBroadcastCatchItem -> Objects.equals(audioBroadcastCatchItem.getDeviceId(), deviceId)); |
| | |
| | | import javax.sip.message.Message;
|
| | | import javax.sip.message.Request;
|
| | | import java.text.ParseException;
|
| | | import javax.sip.message.Message;
|
| | | import javax.sip.message.Request;
|
| | |
|
| | | /**
|
| | |
| | | */
|
| | | void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException;
|
| | |
|
| | |
|
| | | }
|
| | |
| | | public Request createBroadcastMessageRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
|
| | | Request request = null;
|
| | | // sipuri
|
| | | SipURI requestURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
|
| | | SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
|
| | | // via
|
| | | ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
| | | ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
|
| | | ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
|
| | | viaHeader.setRPort();
|
| | | viaHeaders.add(viaHeader);
|
| | | // from
|
| | | SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
|
| | | Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
|
| | | FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
|
| | | 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);
|
| | | // to
|
| | | SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
|
| | | Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
|
| | | ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
|
| | | SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
|
| | | Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
|
| | | ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag);
|
| | |
|
| | | // Forwards
|
| | | MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
|
| | | MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
|
| | | // ceq
|
| | | CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
|
| | | CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
|
| | |
|
| | | ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
| | | ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
| | |
|
| | | request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
|
| | | request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
|
| | | toHeader, viaHeaders, maxForwards, contentTypeHeader, content);
|
| | |
|
| | | request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
|
| | | request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
|
| | |
|
| | | return request;
|
| | | }
|
| | |
| | | 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.HookSubscribeForStreamPush;
|
| | | import com.genersoft.iot.vmp.utils.DateUtil;
|
| | | import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
|
| | | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
|
| | | 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.utils.GitUtil;
|
| | | import gov.nist.javax.sip.SipProviderImpl;
|
| | | import com.genersoft.iot.vmp.utils.DateUtil;
|
| | | import com.genersoft.iot.vmp.utils.GitUtil;
|
| | | import gov.nist.javax.sip.message.SIPRequest;
|
| | | import gov.nist.javax.sip.message.SIPResponse;
|
| | | import org.slf4j.Logger;
|
| | |
| | | import javax.sip.ResponseEvent;
|
| | | import javax.sip.SipException;
|
| | | import javax.sip.header.CallIdHeader;
|
| | | import javax.sip.*;
|
| | | import javax.sip.header.*;
|
| | | import javax.sip.message.Request;
|
| | | import java.text.ParseException;
|
| | |
|
| | |
| | |
|
| | | @Autowired
|
| | | private ZlmHttpHookSubscribe subscribe;
|
| | |
|
| | | @Autowired
|
| | | private GitUtil gitUtil;
|
| | |
|
| | |
|
| | |
|
| | |
| | | }
|
| | | });
|
| | |
|
| | | CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
|
| | | : udpSipProvider.getNewCallId();
|
| | | CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
|
| | | callIdHeader.setCallId(callId);
|
| | | HookSubscribeForStreamPush hookSubscribeForStreamPush = HookSubscribeFactory.on_publish("rtp", stream, null, mediaServerItem.getId());
|
| | | subscribe.addSubscribe(hookSubscribeForStreamPush, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
|
| | |
| | | content.append("f=v/////a/1/8/1" + "\r\n");
|
| | |
|
| | | Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(), callIdHeader);
|
| | | transmitRequest(device.getTransport(), request, (e -> {
|
| | | sipSender.transmitRequest(device.getTransport(), request, (e -> {
|
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
|
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
|
| | | errorEvent.response(e);
|
| | |
| | | @Override
|
| | | public synchronized void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
|
| | | Request byteRequest = headerProvider.createByteRequest(device, channelId, sipTransactionInfo);
|
| | | transmitRequest(device.getTransport(), byteRequest, null, okEvent);
|
| | | sipSender.transmitRequest(device.getTransport(), byteRequest, null, okEvent);
|
| | | }
|
| | |
|
| | | /**
|
| | |
| | | broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n");
|
| | | broadcastXml.append("</Notify>\r\n");
|
| | |
|
| | |
|
| | |
|
| | | Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
| | | sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
|
| | | sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
|
| | |
|
| | | }
|
| | |
|
| | |
| | | logger.debug("拉框信令: " + request.toString());
|
| | | sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
|
| | | }
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
| | | package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | 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.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.storager.dao.dto.PlatformRegisterInfo; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| | | 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.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 org.slf4j.Logger; |
| | |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.ObjectUtils; |
| | | |
| | | |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import javax.sip.*; |
| | | import javax.sip.header.*; |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.SipException; |
| | | import javax.sip.header.CallIdHeader; |
| | | import javax.sip.header.WWWAuthenticateHeader; |
| | | import javax.sip.message.Request; |
| | | import java.text.ParseException; |
| | | import java.util.ArrayList; |
| | |
| | | logger.info("[向上级发送BYE], sendRtpItem 为NULL"); |
| | | return; |
| | | } |
| | | if (parentPlatform == null) { |
| | | if (platform == null) { |
| | | logger.info("[向上级发送BYE], platform 为NULL"); |
| | | return; |
| | | } |
| | | logger.info("[向上级发送BYE], {}/{}", parentPlatform.getServerGBId(), sendRtpItem.getChannelId()); |
| | | logger.info("[向上级发送BYE], {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId()); |
| | | String mediaServerId = sendRtpItem.getMediaServerId(); |
| | | MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); |
| | | if (mediaServerItem != null) { |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); |
| | | zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId()); |
| | | } |
| | | SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem); |
| | | SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem); |
| | | if (byeRequest == null) { |
| | | logger.warn("[向上级发送bye]:无法创建 byeRequest"); |
| | | } |
| | | sipSender.transmitRequest(parentPlatform.getDeviceIp(),byeRequest); |
| | | sipSender.transmitRequest(platform.getDeviceIp(),byeRequest); |
| | | } |
| | | } |
| | |
| | | |
| | | import com.genersoft.iot.vmp.conf.SipConfig; |
| | | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; |
| | | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| | | import gov.nist.javax.sip.SipProviderImpl; |
| | |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.security.core.parameters.P; |
| | | |
| | | import javax.sip.*; |
| | | import javax.sip.address.Address; |
| | |
| | | return responseAck(sipRequest, statusCode, msg, null); |
| | | } |
| | | |
| | | // public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException { |
| | | // if (serverTransaction == null) { |
| | | // logger.warn("[回复消息] ServerTransaction 为null"); |
| | | // return null; |
| | | // } |
| | | // ToHeader toHeader = (ToHeader) serverTransaction.getRequest().getHeader(ToHeader.NAME); |
| | | // if (toHeader.getTag() == null) { |
| | | // toHeader.setTag(SipUtils.getNewTag()); |
| | | // } |
| | | // SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, serverTransaction.getRequest()); |
| | | // if (msg != null) { |
| | | // response.setReasonPhrase(msg); |
| | | // } |
| | | // if (responseAckExtraParam != null) { |
| | | // if (responseAckExtraParam.sipURI != null && serverTransaction.getRequest().getMethod().equals(Request.INVITE)) { |
| | | // logger.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort()); |
| | | // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress( |
| | | // SipFactory.getInstance().createAddressFactory().createSipURI(responseAckExtraParam.sipURI.getUser(), responseAckExtraParam.sipURI.getHost()+":"+responseAckExtraParam.sipURI.getPort() |
| | | // )); |
| | | // response.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); |
| | | // } |
| | | // if (responseAckExtraParam.contentTypeHeader != null) { |
| | | // response.setContent(responseAckExtraParam.content, responseAckExtraParam.contentTypeHeader); |
| | | // } |
| | | // |
| | | // if (serverTransaction.getRequest().getMethod().equals(Request.SUBSCRIBE)) { |
| | | // if (responseAckExtraParam.expires == -1) { |
| | | // logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); |
| | | // }else { |
| | | // ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(responseAckExtraParam.expires); |
| | | // response.addHeader(expiresHeader); |
| | | // } |
| | | // } |
| | | // }else { |
| | | // if (serverTransaction.getRequest().getMethod().equals(Request.SUBSCRIBE)) { |
| | | // logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); |
| | | // } |
| | | // } |
| | | // serverTransaction.sendResponse(response); |
| | | // if (statusCode >= 200 && !"NOTIFY".equalsIgnoreCase(serverTransaction.getRequest().getMethod())) { |
| | | // if (serverTransaction.getDialog() != null) { |
| | | // serverTransaction.getDialog().delete(); |
| | | // } |
| | | // } |
| | | // return response; |
| | | // } |
| | | |
| | | public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException { |
| | | if (sipRequest.getToHeader().getTag() == null) { |
| | |
| | | return response; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 回复带sdp的200 |
| | | */ |
| | |
| | | } 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"), JSONObject.toJSON(param)); |
| | | logger.error("RTP推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSON(param)); |
| | | if (sendRtpItem.isOnlyAudio()) { |
| | | // 语音对讲 |
| | | Device device = deviceService.queryDevice(platformGbId); |
| | | Device device = deviceService.getDevice(platformGbId); |
| | | if (device != null) { |
| | | try { |
| | | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), sendRtpItem.getStreamId(), null); |
| | |
| | | if (totalReaderCount <= 0) { |
| | | logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); |
| | | if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { |
| | | Device device = deviceService.queryDevice(sendRtpItem.getDeviceId()); |
| | | Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); |
| | | if (device == null) { |
| | | logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | import com.genersoft.iot.vmp.conf.SipConfig; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | 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.gb28181.session.AudioBroadcastManager; |
| | | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| | | 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.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | 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; |
| | | 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.*; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.service.IMediaService; |
| | | import com.genersoft.iot.vmp.service.IPlayService; |
| | | import com.genersoft.iot.vmp.service.IStreamProxyService; |
| | | import com.genersoft.iot.vmp.service.IStreamPushService; |
| | | import com.genersoft.iot.vmp.service.*; |
| | | import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; |
| | | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| | | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; |
| | |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import gov.nist.javax.sdp.TimeDescriptionImpl; |
| | | import gov.nist.javax.sdp.fields.TimeField; |
| | | import gov.nist.javax.sip.message.SIPRequest; |
| | |
| | | |
| | | // 写入redis, 超时时回复 |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | | MediaServerItem finalMediaServerItem = mediaServerItem; |
| | | playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> { |
| | | logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); |
| | | redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); |
| | | logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, finalChannelId); |
| | | redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), finalChannelId, callIdHeader.getCallId(), null); |
| | | }); |
| | | } else { |
| | | sendRtpItem.setStreamId(playTransaction.getStream()); |
| | |
| | | if (audioBroadcastCatch == null) { |
| | | logger.warn("来自设备的Invite请求非语音广播,已忽略,requesterId: {}/{}", requesterId, channelId); |
| | | try { |
| | | responseAck(serverTransaction, Response.FORBIDDEN); |
| | | responseAck(request, Response.FORBIDDEN); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] 来自设备的Invite请求非语音广播 FORBIDDEN: {}", e.getMessage()); |
| | | } |
| | | return; |
| | | } |
| | | Request request = serverTransaction.getRequest(); |
| | | if (device != null) { |
| | | logger.info("收到设备" + requesterId + "的语音广播Invite请求"); |
| | | try { |
| | |
| | | if (mediaServerItem == null) { |
| | | logger.warn("未找到可用的zlm"); |
| | | try { |
| | | responseAck(serverTransaction, Response.BUSY_HERE); |
| | | responseAck(request, Response.BUSY_HERE); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); |
| | | } |
| | |
| | | if (sendRtpItem == null) { |
| | | logger.warn("服务器端口资源不足"); |
| | | try { |
| | | responseAck(serverTransaction, Response.BUSY_HERE); |
| | | responseAck(request, Response.BUSY_HERE); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); |
| | | } |
| | |
| | | |
| | | Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream); |
| | | if (streamReady) { |
| | | sendOk(device, sendRtpItem, sdp, serverTransaction, mediaServerItem, mediaTransmissionTCP, ssrc); |
| | | sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); |
| | | }else { |
| | | logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", app, stream); |
| | | playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); |
| | |
| | | } |
| | | } |
| | | |
| | | void sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, ServerTransaction serverTransaction, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc){ |
| | | void sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc){ |
| | | try { |
| | | sendRtpItem.setStatus(2); |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | |
| | | parentPlatform.setServerPort(device.getPort()); |
| | | parentPlatform.setServerGBId(device.getDeviceId()); |
| | | |
| | | SIPResponse sipResponse = responseSdpAck(serverTransaction, content.toString(), parentPlatform); |
| | | SIPResponse sipResponse = responseSdpAck(request, content.toString(), parentPlatform); |
| | | |
| | | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), sendRtpItem.getChannelId()); |
| | | |
| | |
| | | if (!taskQueueHandlerRun) { |
| | | taskQueueHandlerRun = true; |
| | | taskExecutor.execute(()-> { |
| | | while (!taskQueue.isEmpty()) { |
| | | try { |
| | | HandlerCatchData take = taskQueue.poll(); |
| | | Element rootElement = getRootElement(take.getEvt()); |
| | | if (rootElement == null) { |
| | | logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest()); |
| | | continue; |
| | | } |
| | | String cmd = XmlUtil.getText(rootElement, "CmdType"); |
| | | try { |
| | | while (!taskQueue.isEmpty()) { |
| | | try { |
| | | HandlerCatchData take = taskQueue.poll(); |
| | | Element rootElement = getRootElement(take.getEvt()); |
| | | if (rootElement == null) { |
| | | logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest()); |
| | | continue; |
| | | } |
| | | String cmd = XmlUtil.getText(rootElement, "CmdType"); |
| | | |
| | | if (CmdType.CATALOG.equals(cmd)) { |
| | | logger.info("接收到Catalog通知"); |
| | | processNotifyCatalogList(take.getEvt()); |
| | | } else if (CmdType.ALARM.equals(cmd)) { |
| | | logger.info("接收到Alarm通知"); |
| | | processNotifyAlarm(take.getEvt()); |
| | | } else if (CmdType.MOBILE_POSITION.equals(cmd)) { |
| | | logger.info("接收到MobilePosition通知"); |
| | | processNotifyMobilePosition(take.getEvt()); |
| | | } else { |
| | | logger.info("接收到消息:" + cmd); |
| | | if (CmdType.CATALOG.equals(cmd)) { |
| | | logger.info("接收到Catalog通知"); |
| | | processNotifyCatalogList(take.getEvt()); |
| | | } else if (CmdType.ALARM.equals(cmd)) { |
| | | logger.info("接收到Alarm通知"); |
| | | processNotifyAlarm(take.getEvt()); |
| | | } else if (CmdType.MOBILE_POSITION.equals(cmd)) { |
| | | logger.info("接收到MobilePosition通知"); |
| | | processNotifyMobilePosition(take.getEvt()); |
| | | } else { |
| | | logger.info("接收到消息:" + cmd); |
| | | } |
| | | } catch (DocumentException e) { |
| | | logger.error("处理NOTIFY消息时错误", e); |
| | | } |
| | | } catch (DocumentException e) { |
| | | logger.error("处理NOTIFY消息时错误", e); |
| | | } |
| | | }catch (Exception e) { |
| | | logger.error("处理NOTIFY消息时错误", e); |
| | | }finally { |
| | | taskQueueHandlerRun = false; |
| | | } |
| | | taskQueueHandlerRun = false; |
| | | }); |
| | | } |
| | | } |
| | |
| | | |
| | | if (request.getExpires() == null) { |
| | | response = getMessageFactory().createResponse(Response.BAD_REQUEST, request); |
| | | if (evt.getDialog() != null ) { |
| | | if (evt.getDialog().isServer()) { |
| | | ServerTransaction serverTransaction = getServerTransaction(evt); |
| | | serverTransaction.sendResponse(response); |
| | | serverTransaction.getDialog().delete(); |
| | | } |
| | | } |
| | | sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); |
| | | return; |
| | | } |
| | |
| | | } |
| | | } catch (SipException | NoSuchAlgorithmException | ParseException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | private void sendResponse(RequestEvent evt, Response response) throws InvalidArgumentException, SipException { |
| | | ServerTransaction serverTransaction = getServerTransaction(evt); |
| | | serverTransaction.sendResponse(response); |
| | | if (serverTransaction.getDialog() != null) { |
| | | serverTransaction.getDialog().delete(); |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.AckRequestProcessor; |
| | | import gov.nist.javax.sip.message.SIPRequest; |
| | | import org.dom4j.Element; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.RequestEvent; |
| | | import javax.sip.ServerTransaction; |
| | | import javax.sip.SipException; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | |
| | | public void handForDevice(RequestEvent evt, Device device, Element element) { |
| | | String cmd = getText(element, "CmdType"); |
| | | if (cmd == null) { |
| | | handNullCmd(evt); |
| | | try { |
| | | responseAck((SIPRequest) evt.getRequest(), Response.OK); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] 回复200 OK: {}", e.getMessage()); |
| | | } |
| | | return; |
| | | } |
| | | IMessageHandler messageHandler = messageHandlerMap.get(cmd); |
| | |
| | | IMessageHandler messageHandler = messageHandlerMap.get(cmd); |
| | | if (messageHandler != null) { |
| | | messageHandler.handForPlatform(evt, parentPlatform, element); |
| | | } |
| | | } |
| | | |
| | | public void handNullCmd(RequestEvent evt){ |
| | | try { |
| | | ServerTransaction serverTransaction = getServerTransaction(evt); |
| | | responseAck(serverTransaction, Response.OK); |
| | | } catch (SipException | InvalidArgumentException | ParseException e) { |
| | | logger.error("[命令发送失败] 回复200 OK: {}", e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | 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; |
| | |
| | | public static String getNewCallId() { |
| | | return (int) Math.floor(Math.random() * 10000) + ""; |
| | | } |
| | | |
| | | public static int getTypeCodeFromGbCode(String deviceId) { |
| | | if (ObjectUtils.isEmpty(deviceId)) { |
| | | return 0; |
| | | } |
| | | return Integer.parseInt(deviceId.substring(10, 13)); |
| | | } |
| | | |
| | | /** |
| | | * 判断是否是前端外围设备 |
| | | * @param deviceId |
| | | * @return |
| | | */ |
| | | public static boolean isFrontEnd(String deviceId) { |
| | | int typeCodeFromGbCode = getTypeCodeFromGbCode(deviceId); |
| | | return typeCodeFromGbCode > 130 && typeCodeFromGbCode < 199; |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
| | | import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
|
| | | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.HookType;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
|
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.*;
|
| | | import com.genersoft.iot.vmp.service.*;
|
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
| | | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
|
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
| | | import org.slf4j.Logger;
|
| | | import org.slf4j.LoggerFactory;
|
| | | import org.springframework.beans.factory.annotation.Autowired;
|
| | |
| | |
|
| | |
|
| | | JSONObject json = (JSONObject) JSON.toJSON(param);
|
| | | taskExecutor.execute(()->{
|
| | | taskExecutor.execute(()-> {
|
| | | ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
|
| | | if (subscribe != null ) {
|
| | | if (subscribe != null) {
|
| | | MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
|
| | | if (mediaInfo != null) {
|
| | | subscribe.response(mediaInfo, json);
|
| | |
| | | StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
|
| | | if (streamAuthorityInfo == null) {
|
| | | streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
|
| | | }else {
|
| | | } else {
|
| | | streamAuthorityInfo.setOriginType(param.getOriginType());
|
| | | streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
|
| | | }
|
| | | redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
|
| | | }
|
| | | }else {
|
| | | } else {
|
| | | redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
|
| | | }
|
| | | });
|
| | |
|
| | | if ("rtsp".equals(param.getSchema())){
|
| | | logger.info("on_stream_changed:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
|
| | |
| | | }else {
|
| | | mediaServerService.removeCount(param.getMediaServerId());
|
| | | }
|
| | | if (item.getOriginType() == OriginType.PULL.ordinal()
|
| | | || item.getOriginType() == OriginType.FFMPEG_PULL.ordinal()) {
|
| | | if (param.getOriginType() == OriginType.PULL.ordinal()
|
| | | || param.getOriginType() == OriginType.FFMPEG_PULL.ordinal()) {
|
| | | // 设置拉流代理上线/离线
|
| | | streamProxyService.updateStatus(param.isRegist(), app, param.getStream());
|
| | | streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
|
| | | }
|
| | | if ("rtp".equals(app) && !regist ) {
|
| | | if ("rtp".equals(param.getApp()) && !param.isRegist() ) {
|
| | | StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(param.getStream());
|
| | | if (streamInfo!=null){
|
| | | redisCatchStorage.stopPlay(streamInfo);
|
| | |
| | | streamInfo.getStream(), null);
|
| | | }
|
| | | }
|
| | | }else if ("broadcast".equals(app)){
|
| | | }else if ("broadcast".equals(param.getApp())){
|
| | | // 语音对讲推流 stream需要满足格式deviceId_channelId
|
| | | if (regist && param.getStream().indexOf("_") > 0) {
|
| | | if (param.isRegist() && param.getStream().indexOf("_") > 0) {
|
| | | String[] streamArray = param.getStream().split("_");
|
| | | if (streamArray.length == 2) {
|
| | | String deviceId = streamArray[0];
|
| | | String channelId = streamArray[1];
|
| | | Device device = deviceService.queryDevice(deviceId);
|
| | | Device device = deviceService.getDevice(deviceId);
|
| | | if (device != null) {
|
| | | DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
|
| | | if (deviceChannel != null) {
|
| | | if (audioBroadcastManager.exit(deviceId, channelId)) {
|
| | | // 直接推流
|
| | | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, stream, null);
|
| | | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
|
| | | if (sendRtpItem == null) {
|
| | | // TODO 可能数据错误,重新开启语音通道
|
| | | }else {
|
| | | String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
|
| | | MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
| | | logger.info("rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
|
| | | 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");
|
| | | Map<String, Object> sendParam = new HashMap<>(12);
|
| | | sendParam.put("vhost","__defaultVhost__");
|
| | | sendParam.put("app",sendRtpItem.getApp());
|
| | | sendParam.put("stream",sendRtpItem.getStreamId());
|
| | | sendParam.put("ssrc", sendRtpItem.getSsrc());
|
| | | sendParam.put("src_port", sendRtpItem.getLocalPort());
|
| | | sendParam.put("pt", sendRtpItem.getPt());
|
| | | sendParam.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
|
| | | sendParam.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
|
| | |
|
| | | JSONObject jsonObject;
|
| | | if (sendRtpItem.isTcpActive()) {
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param);
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, sendParam);
|
| | | } else {
|
| | | param.put("is_udp", is_Udp);
|
| | | param.put("dst_url", sendRtpItem.getIp());
|
| | | param.put("dst_port", sendRtpItem.getPort());
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
| | | sendParam.put("is_udp", is_Udp);
|
| | | sendParam.put("dst_url", sendRtpItem.getIp());
|
| | | sendParam.put("dst_port", sendRtpItem.getPort());
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, sendParam);
|
| | | }
|
| | | if (jsonObject != null && jsonObject.getInteger("code") == 0) {
|
| | | logger.info("[语音对讲] 自动推流成功, device: {}, channel: {}", deviceId, channelId);
|
| | | }else {
|
| | | logger.info("[语音对讲] 推流失败, 结果: {}", jsonObject);
|
| | | }
|
| | |
|
| | | }
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | }else if ("talk".equals(app)){
|
| | | }else if ("talk".equals(param.getApp())){
|
| | | // 语音对讲推流 stream需要满足格式deviceId_channelId
|
| | | if (regist && stream.indexOf("_") > 0) {
|
| | | String[] streamArray = stream.split("_");
|
| | | if (param.isRegist() && param.getStream().indexOf("_") > 0) {
|
| | | String[] streamArray = param.getStream().split("_");
|
| | | if (streamArray.length == 2) {
|
| | | String deviceId = streamArray[0];
|
| | | String channelId = streamArray[1];
|
| | | Device device = deviceService.queryDevice(deviceId);
|
| | | Device device = deviceService.getDevice(deviceId);
|
| | | if (device != null) {
|
| | | DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
|
| | | if (deviceChannel != null) {
|
| | | if (audioBroadcastManager.exit(deviceId, channelId)) {
|
| | | // 直接推流
|
| | | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, stream, null);
|
| | | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
|
| | | if (sendRtpItem == null) {
|
| | | // TODO 可能数据错误,重新开启语音通道
|
| | | }else {
|
| | | MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
| | | logger.info("rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
|
| | | 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");
|
| | | Map<String, Object> sendParam = new HashMap<>(12);
|
| | | sendParam.put("vhost","__defaultVhost__");
|
| | | sendParam.put("app",sendRtpItem.getApp());
|
| | | sendParam.put("stream",sendRtpItem.getStreamId());
|
| | | sendParam.put("ssrc", sendRtpItem.getSsrc());
|
| | | sendParam.put("src_port", sendRtpItem.getLocalPort());
|
| | | sendParam.put("pt", sendRtpItem.getPt());
|
| | | sendParam.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
|
| | | sendParam.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
|
| | |
|
| | | JSONObject jsonObject;
|
| | | if (sendRtpItem.isTcpActive()) {
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param);
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, sendParam);
|
| | | } else {
|
| | | param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
|
| | | param.put("dst_url", sendRtpItem.getIp());
|
| | | param.put("dst_port", sendRtpItem.getPort());
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
| | | sendParam.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
|
| | | sendParam.put("dst_url", sendRtpItem.getIp());
|
| | | sendParam.put("dst_port", sendRtpItem.getPort());
|
| | | jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, sendParam);
|
| | | }
|
| | | if (jsonObject != null && jsonObject.getInteger("code") == 0) {
|
| | | logger.info("[语音对讲] 自动推流成功, device: {}, channel: {}", deviceId, channelId);
|
| | |
| | | }
|
| | | }else {
|
| | | // 开启语音对讲通道
|
| | | MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
| | | MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
|
| | | playService.talk(mediaServerItem, device, channelId, (mediaServer, jsonObject)->{
|
| | | System.out.println("开始推流");
|
| | | }, eventResult -> {
|
| | |
| | | }
|
| | |
|
| | | }else{
|
| | | if (!"rtp".equals(app)){
|
| | | String type = OriginType.values()[item.getOriginType()].getType();
|
| | | MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
| | | if (!"rtp".equals(param.getApp())){
|
| | | String type = OriginType.values()[param.getOriginType()].getType();
|
| | | MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
|
| | |
|
| | | if (mediaServerItem != null){
|
| | | if (param.isRegist()) {
|
| | |
| | | callId = streamAuthorityInfo.getCallId();
|
| | | }
|
| | | StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
|
| | | param.getApp(), param.getStream(), tracks, callId);
|
| | | param.getApp(), param.getStream(), param.getTracks(), callId);
|
| | | param.setStreamInfo(streamInfoByAppAndStream);
|
| | | redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);
|
| | | if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|
| | |
| | | }
|
| | | }else {
|
| | | // 兼容流注销时类型从redis记录获取
|
| | | OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(param.getApp(), param.getStream(), param.getMediaServerId());
|
| | | OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
|
| | | param.getApp(), param.getStream(), param.getMediaServerId());
|
| | | if (onStreamChangedHookParam != null) {
|
| | | type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
|
| | | redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream());
|
| | |
| | | if (platform != null) {
|
| | | commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
|
| | | }else {
|
| | | if ("talk".equals(app) && sendRtpItem.isOnlyAudio()) {
|
| | | if ("talk".equals(param.getApp()) && sendRtpItem.isOnlyAudio()) {
|
| | | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
|
| | | if (device != null && audioBroadcastCatch != null) {
|
| | | // cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
|
| | | }
|
| | | }else {
|
| | | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), stream, sendRtpItem.getCallId());
|
| | | cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
|
| | | }
|
| | |
|
| | | }
|
| | |
| | | if (sendRtpItems.size() > 0) {
|
| | | for (SendRtpItem sendRtpItem : sendRtpItems) {
|
| | | ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
|
| | | if (parentPlatform == null) {
|
| | | continue;
|
| | | }
|
| | | try {
|
| | | commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
|
| | | } catch (SipException | InvalidArgumentException | ParseException e) {
|
| | |
| | | if (callback == null) { |
| | | try { |
| | | Response response = client.newCall(request).execute(); |
| | | |
| | | if (response.isSuccessful()) { |
| | | ResponseBody responseBody = response.body(); |
| | | if (responseBody != null) { |
| | |
| | | responseJSON = JSON.parseObject(responseStr); |
| | | } |
| | | }else { |
| | | System.out.println( 2222); |
| | | System.out.println( response.code()); |
| | | response.close(); |
| | | Objects.requireNonNull(response.body()).close(); |
| | | } |
| | |
| | | |
| | | if(e instanceof SocketTimeoutException){ |
| | | //读取超时超时异常 |
| | | logger.error(String.format("读取ZLM数据失败: %s, %s", url, e.getMessage())); |
| | | logger.error(String.format("读取ZLM数据超时失败: %s, %s", url, e.getMessage())); |
| | | } |
| | | if(e instanceof ConnectException){ |
| | | //判断连接异常,我这里是报Failed to connect to 10.7.5.144 |
| | | logger.error(String.format("连接ZLM失败: %s, %s", url, e.getMessage())); |
| | | logger.error(String.format("连接ZLM连接失败: %s, %s", url, e.getMessage())); |
| | | } |
| | | |
| | | }catch (Exception e){ |
| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; |
| | | 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.media.zlm.dto.*; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | return result; |
| | | } |
| | | |
| | | public void closeAllSendRtpStream() { |
| | | |
| | | } |
| | | |
| | | public MediaItem getMediaInfo(MediaServerItem mediaServerItem, String app, String stream) { |
| | | JSONObject json = zlmresTfulUtils.getMediaList(mediaServerItem, app, stream); |
| | | MediaItem mediaItem = null; |
| | | if (json == null || json.getInteger("code") != 0) { |
| | | return null; |
| | | } else { |
| | | JSONArray data = json.getJSONArray("data"); |
| | | if (data == null || data.size() == 0) { |
| | | return null; |
| | | }else { |
| | | mediaItem = JSONObject.toJavaObject(data.getJSONObject(0), MediaItem.class); |
| | | } |
| | | } |
| | | return mediaItem; |
| | | } |
| | | } |
| | |
| | | |
| | | public static HookSubscribeForStreamPush on_publish(String app, String stream, String scheam, String mediaServerId) { |
| | | HookSubscribeForStreamPush hookSubscribe = new HookSubscribeForStreamPush(); |
| | | JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject(); |
| | | JSONObject subscribeKey = new JSONObject(); |
| | | subscribeKey.put("app", app); |
| | | subscribeKey.put("stream", stream); |
| | | if (scheam != null) { |
| | |
| | | package com.genersoft.iot.vmp.media.zlm.dto; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | |
| | | import java.time.Instant; |
| | | |
| | |
| | | package com.genersoft.iot.vmp.media.zlm.dto; |
| | | |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.HashMap; |
| | | |
| | | /** |
| | | * 精简的MediaServerItem信息,方便给前端返回数据 |
| | | */ |
| | |
| | | |
| | | private String secret; |
| | | |
| | | private int hookAliveInterval; |
| | | |
| | | private int recordAssistPort; |
| | | |
| | | |
| | |
| | | this.rtspPort = mediaServerItem.getRtspPort(); |
| | | this.rtspSSLPort = mediaServerItem.getRtspSSLPort(); |
| | | this.secret = mediaServerItem.getSecret(); |
| | | this.hookAliveInterval = mediaServerItem.getHookAliveInterval(); |
| | | this.recordAssistPort = mediaServerItem.getRecordAssistPort(); |
| | | } |
| | | |
| | |
| | | |
| | | public void setSecret(String secret) { |
| | | this.secret = secret; |
| | | } |
| | | |
| | | public int getHookAliveInterval() { |
| | | return hookAliveInterval; |
| | | } |
| | | |
| | | public void setHookAliveInterval(int hookAliveInterval) { |
| | | this.hookAliveInterval = hookAliveInterval; |
| | | } |
| | | |
| | | public int getRecordAssistPort() { |
| | |
| | | package com.genersoft.iot.vmp.service; |
| | | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData; |
| | | import com.genersoft.iot.vmp.service.bean.MediaServerLoad; |
| | | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 媒体服务节点 |
| | |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.genersoft.iot.vmp.common.StreamInfo; |
| | | import com.genersoft.iot.vmp.common.StreamURL; |
| | | import com.genersoft.iot.vmp.conf.MediaConfig; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.service.IMediaService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.service.IMediaService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.ObjectUtils; |
| | | |
| | | import java.net.URL; |
| | | |
| | | @Service |
| | | public class MediaServiceImpl implements IMediaService { |
| | |
| | | streamInfoResult.setFmp4(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setHls(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setTs(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setRtc(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setRtc(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam, isPlay); |
| | | |
| | | streamInfoResult.setTracks(tracks); |
| | | return streamInfoResult; |
| | |
| | | package com.genersoft.iot.vmp.service.impl; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.text.ParseException; |
| | | import java.util.*; |
| | | |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.ResponseEvent; |
| | | import javax.sip.SipException; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.genersoft.iot.vmp.common.StreamInfo; |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | import com.genersoft.iot.vmp.conf.SipConfig; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.conf.exception.ControllerException; |
| | | import com.genersoft.iot.vmp.conf.exception.ServiceException; |
| | | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; |
| | | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| | | 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.beans.factory.annotation.Value; |
| | | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.ObjectUtils; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.context.request.async.DeferredResult; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.genersoft.iot.vmp.common.StreamInfo; |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | import com.genersoft.iot.vmp.conf.SipConfig; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| | | import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; |
| | | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| | | 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.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; |
| | | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| | | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.text.ParseException; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.UUID; |
| | | |
| | | @SuppressWarnings(value = {"rawtypes", "unchecked"}) |
| | |
| | | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(deviceId, channelId); |
| | | if (audioBroadcastCatch != null) { |
| | | |
| | | Device device = deviceService.queryDevice(deviceId); |
| | | Device device = deviceService.getDevice(deviceId); |
| | | if (device == null) { |
| | | return; |
| | | } |
| | |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | |
| | | // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误 |
| | | deferredResultEx.setFilter(result1 -> { |
| | | WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>)result1; |
| | | WVPResult<StreamInfo> clone = null; |
| | | try { |
| | | clone = (WVPResult<StreamInfo>)wvpResult1.clone(); |
| | | } catch (CloneNotSupportedException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | if (clone.getCode() == ErrorCode.SUCCESS.getCode()) { |
| | | StreamInfo data = clone.getData().clone(); |
| | | data.channgeStreamIp(request.getLocalName()); |
| | | clone.setData(data); |
| | | } |
| | | return clone; |
| | | }); |
| | | if (userSetting.isUsePushingAsStatus()) { |
| | | // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误 |
| | | deferredResultEx.setFilter(result1 -> { |
| | | WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>)result1; |
| | | WVPResult<StreamInfo> clone = null; |
| | | try { |
| | | clone = (WVPResult<StreamInfo>)wvpResult1.clone(); |
| | | } catch (CloneNotSupportedException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | if (clone.getCode() == ErrorCode.SUCCESS.getCode()) { |
| | | StreamInfo data = clone.getData().clone(); |
| | | data.channgeStreamIp(request.getLocalName()); |
| | | clone.setData(data); |
| | | } |
| | | return clone; |
| | | }); |
| | | } |
| | | |
| | | |
| | | // 录像查询以channelId作为deviceId查询 |
| | | resultHolder.put(key, uuid, deferredResultEx); |
| | |
| | | <script> |
| | | import rtcPlayer from '../dialog/rtcPlayer.vue' |
| | | import crypto from 'crypto' |
| | | // import LivePlayer from '@liveqing/liveplayer' |
| | | // import player from '../dialog/easyPlayer.vue' |
| | | import jessibucaPlayer from '../common/jessibuca.vue' |
| | | import recordDownload from '../dialog/recordDownload.vue' |
| | | export default { |
| | | name: 'devicePlayer', |
| | | props: {}, |
| | | components: { |
| | | LivePlayer, jessibucaPlayer, rtcPlayer, recordDownload, |
| | | jessibucaPlayer, rtcPlayer, recordDownload, |
| | | }, |
| | | computed: { |
| | | getPlayerShared: function () { |
| | |
| | | if (res.data.code == 0) { |
| | | let streamInfo = res.data.data.streamInfo; |
| | | if (document.location.protocol.includes("https")) { |
| | | this.startBroadcast(streamInfo.rtcs) |
| | | this.startBroadcast(streamInfo.rtcs.url) |
| | | }else { |
| | | this.startBroadcast(streamInfo.rtc) |
| | | this.startBroadcast(streamInfo.rtc.url) |
| | | } |
| | | |
| | | }else { |