| | |
| | | tcpSipProvider.setDialogErrorsAutomaticallyHandled();
|
| | | tcpSipProvider.addSipListener(sipProcessorObserver);
|
| | | // tcpSipProvider.setAutomaticDialogSupportEnabled(false);
|
| | | logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
|
| | | logger.info("[Sip Server] TCP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | } catch (TransportNotSupportedException e) {
|
| | | e.printStackTrace();
|
| | | } catch (InvalidArgumentException e) {
|
| | | logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | logger.error("[Sip Server] 无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | , sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | } catch (TooManyListenersException e) {
|
| | | e.printStackTrace();
|
| | |
| | | } catch (TransportNotSupportedException e) {
|
| | | e.printStackTrace();
|
| | | } catch (InvalidArgumentException e) {
|
| | | logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | logger.error("[Sip Server] 无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | , sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | } catch (TooManyListenersException e) {
|
| | | e.printStackTrace();
|
| | | } catch (ObjectInUseException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "]");
|
| | | logger.info("[Sip Server] UDP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | return udpSipProvider;
|
| | | }
|
| | |
|
| | |
| | | package com.genersoft.iot.vmp.gb28181.bean;
|
| | |
|
| | |
|
| | | //import gov.nist.javax.sip.header.SIPDate;
|
| | |
|
| | | import java.util.Date;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | |
| | | private String name;
|
| | |
|
| | | private int sumNum;
|
| | |
|
| | | private Date lastTime;
|
| | |
|
| | | private List<RecordItem> recordList;
|
| | |
|
| | |
| | | public void setSn(String sn) {
|
| | | this.sn = sn;
|
| | | }
|
| | |
|
| | | public Date getLastTime() {
|
| | | return lastTime;
|
| | | }
|
| | |
|
| | | public void setLastTime(Date lastTime) {
|
| | | this.lastTime = lastTime;
|
| | | }
|
| | | }
|
| New file |
| | |
| | | package com.genersoft.iot.vmp.gb28181.session; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class RecordDataCatch { |
| | | |
| | | public static Map<String, RecordInfo> data = new ConcurrentHashMap<>(); |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder deferredResultHolder; |
| | | |
| | | |
| | | public int put(String deviceId, String sn, int sumNum, List<RecordItem> recordItems) { |
| | | String key = deviceId + sn; |
| | | RecordInfo recordInfo = data.get(key); |
| | | if (recordInfo == null) { |
| | | recordInfo = new RecordInfo(); |
| | | recordInfo.setDeviceId(deviceId); |
| | | recordInfo.setSn(sn.trim()); |
| | | recordInfo.setSumNum(sumNum); |
| | | recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>())); |
| | | recordInfo.setLastTime(new Date(System.currentTimeMillis())); |
| | | recordInfo.getRecordList().addAll(recordItems); |
| | | data.put(key, recordInfo); |
| | | }else { |
| | | // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 |
| | | if (!Objects.equals(sn.trim(), recordInfo.getSn())) { |
| | | return 0; |
| | | } |
| | | recordInfo.getRecordList().addAll(recordItems); |
| | | recordInfo.setLastTime(new Date(System.currentTimeMillis())); |
| | | } |
| | | return recordInfo.getRecordList().size(); |
| | | } |
| | | |
| | | @Scheduled(fixedRate = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 |
| | | private void timerTask(){ |
| | | Set<String> keys = data.keySet(); |
| | | Calendar calendarBefore5S = Calendar.getInstance(); |
| | | calendarBefore5S.setTime(new Date()); |
| | | calendarBefore5S.set(Calendar.SECOND, calendarBefore5S.get(Calendar.SECOND) - 5); |
| | | |
| | | for (String key : keys) { |
| | | RecordInfo recordInfo = data.get(key); |
| | | // 超过五秒收不到消息任务超时, 只更新这一部分数据 |
| | | if ( recordInfo.getLastTime().before(calendarBefore5S.getTime())) { |
| | | // 处理录像数据, 返回给前端 |
| | | String msgKey = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn(); |
| | | |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(0); |
| | | wvpResult.setMsg("success"); |
| | | // 对数据进行排序 |
| | | Collections.sort(recordInfo.getRecordList()); |
| | | wvpResult.setData(recordInfo); |
| | | |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(msgKey); |
| | | msg.setData(wvpResult); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | data.remove(key); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public boolean isComplete(String deviceId, String sn) { |
| | | RecordInfo recordInfo = data.get(deviceId + sn); |
| | | return recordInfo != null && recordInfo.getRecordList().size() == recordInfo.getSumNum(); |
| | | } |
| | | |
| | | public RecordInfo getRecordInfo(String deviceId, String sn) { |
| | | return data.get(deviceId + sn); |
| | | } |
| | | |
| | | public void remove(String deviceId, String sn) { |
| | | data.remove(deviceId + sn); |
| | | } |
| | | } |
| | |
| | | //
|
| | | StringBuffer content = new StringBuffer(200);
|
| | | content.append("v=0\r\n");
|
| | | content.append("o="+ sipConfig.getId()+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
| | | content.append("o="+ channelId+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
| | | content.append("s=Play\r\n");
|
| | | content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
| | | content.append("t=0 0\r\n");
|
| | |
| | | content.append("a=rtpmap:126 H264/90000\r\n");
|
| | | content.append("a=rtpmap:125 H264S/90000\r\n");
|
| | | content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
|
| | | content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
|
| | | content.append("a=fmtp:99 profile-level-id=3\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | |
| | | }
|
| | | }else {
|
| | | if("TCP-PASSIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if ("TCP-ACTIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if("UDP".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
|
| | | }
|
| | | content.append("a=recvonly\r\n");
|
| | | content.append("a=rtpmap:96 PS/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
|
| | | content.append("a=setup:passive\r\n");
|
| | | content.append("a=connection:new\r\n");
|
| | |
| | |
|
| | | StringBuffer content = new StringBuffer(200);
|
| | | content.append("v=0\r\n");
|
| | | content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("s=Playback\r\n");
|
| | | content.append("u="+channelId+":0\r\n");
|
| | | content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
|
| | |
| | | content.append("a=rtpmap:126 H264/90000\r\n");
|
| | | content.append("a=rtpmap:125 H264S/90000\r\n");
|
| | | content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
|
| | | content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
|
| | | content.append("a=fmtp:99 profile-level-id=3\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | |
| | | }
|
| | | }else {
|
| | | if("TCP-PASSIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if ("TCP-ACTIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if("UDP".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
|
| | | }
|
| | | content.append("a=recvonly\r\n");
|
| | | content.append("a=rtpmap:96 PS/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | | content.append("a=setup:passive\r\n");
|
| | | content.append("a=connection:new\r\n");
|
| | |
| | |
|
| | | StringBuffer content = new StringBuffer(200);
|
| | | content.append("v=0\r\n");
|
| | | content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("s=Download\r\n");
|
| | | content.append("u="+channelId+":0\r\n");
|
| | | content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
|
| | |
| | | }
|
| | | }else {
|
| | | if("TCP-PASSIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if ("TCP-ACTIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if("UDP".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
|
| | | }
|
| | | content.append("a=recvonly\r\n");
|
| | | content.append("a=rtpmap:96 PS/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | | content.append("a=setup:passive\r\n");
|
| | | content.append("a=connection:new\r\n");
|
| | |
| | | sendRtpItem.setApp("rtp"); |
| | | if ("Playback".equals(sessionName)) { |
| | | sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true); |
| | | sendRtpItem.setStreamId(ssrcInfo.getStream()); |
| | | // 写入redis, 超时时回复 |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | |
| | | if (mediaServerItem.isRtpEnable()) { |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, true, false); |
| | | sendRtpItem.setStreamId(ssrcInfo.getStream()); |
| | | // 写入redis, 超时时回复 |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | |
| | | try { |
| | | RequestEventExt evtExt = (RequestEventExt) evt; |
| | | String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); |
| | | logger.info("[{}] 收到注册请求,开始处理", requestAddress); |
| | | logger.info("[注册请求] 开始处理: {}", requestAddress); |
| | | Request request = evt.getRequest(); |
| | | ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME); |
| | | Response response = null; |
| | |
| | | |
| | | AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); |
| | | if (authHead == null) { |
| | | logger.info("[{}] 未携带授权头 回复401", requestAddress); |
| | | logger.info("[注册请求] 未携带授权头 回复401: {}", requestAddress); |
| | | response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); |
| | | new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); |
| | | sendResponse(evt, response); |
| | |
| | | // 注册失败 |
| | | response = getMessageFactory().createResponse(Response.FORBIDDEN, request); |
| | | response.setReasonPhrase("wrong password"); |
| | | logger.info("[{}] 密码/SIP服务器ID错误, 回复403", requestAddress); |
| | | logger.info("[注册请求] 密码/SIP服务器ID错误, 回复403: {}", requestAddress); |
| | | sendResponse(evt, response); |
| | | return; |
| | | } |
| | |
| | | // 注册成功 |
| | | // 保存到redis |
| | | if (registerFlag) { |
| | | logger.info("[{}] 注册成功! deviceId:" + deviceId, requestAddress); |
| | | logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); |
| | | device.setRegisterTime(DateUtil.getNow()); |
| | | deviceService.online(device); |
| | | } else { |
| | | logger.info("[{}] 注销成功! deviceId:" + deviceId, requestAddress); |
| | | logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); |
| | | deviceService.offline(deviceId); |
| | | } |
| | | } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) { |
| | |
| | | private void sendResponse(RequestEvent evt, Response response) throws InvalidArgumentException, SipException { |
| | | ServerTransaction serverTransaction = getServerTransaction(evt); |
| | | if (serverTransaction == null) { |
| | | logger.warn("回复失败:{}", response); |
| | | logger.warn("[回复失败]:{}", response); |
| | | return; |
| | | } |
| | | serverTransaction.sendResponse(response); |
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; |
| | | import com.genersoft.iot.vmp.gb28181.bean.RecordItem; |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread; |
| | | import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch; |
| | | 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.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import org.dom4j.DocumentException; |
| | | import org.dom4j.Element; |
| | | import org.slf4j.Logger; |
| | |
| | | import org.springframework.beans.factory.InitializingBean; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.RequestEvent; |
| | | import javax.sip.SipException; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | import java.util.*; |
| | | |
| | | import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { |
| | | |
| | |
| | | private ResponseMessageHandler responseMessageHandler; |
| | | |
| | | @Autowired |
| | | private RedisUtil redis; |
| | | private RecordDataCatch recordDataCatch; |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder deferredResultHolder; |
| | | |
| | | |
| | | |
| | | @Autowired |
| | | private EventPublisher eventPublisher; |
| | |
| | | responseAck(evt, Response.OK); |
| | | |
| | | rootElement = getRootElement(evt, device.getCharset()); |
| | | String uuid = UUID.randomUUID().toString().replace("-", ""); |
| | | RecordInfo recordInfo = new RecordInfo(); |
| | | String sn = getText(rootElement, "SN"); |
| | | String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn; |
| | | recordInfo.setDeviceId(device.getDeviceId()); |
| | | recordInfo.setSn(sn); |
| | | recordInfo.setName(getText(rootElement, "Name")); |
| | | if (getText(rootElement, "SumNum") == null || getText(rootElement, "SumNum") == "") { |
| | | recordInfo.setSumNum(0); |
| | | } else { |
| | | recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum"))); |
| | | |
| | | String sumNumStr = getText(rootElement, "SumNum"); |
| | | int sumNum = 0; |
| | | if (!StringUtils.isEmpty(sumNumStr)) { |
| | | sumNum = Integer.parseInt(sumNumStr); |
| | | } |
| | | Element recordListElement = rootElement.element("RecordList"); |
| | | if (recordListElement == null || recordInfo.getSumNum() == 0) { |
| | | if (recordListElement == null || sumNum == 0) { |
| | | logger.info("无录像数据"); |
| | | eventPublisher.recordEndEventPush(recordInfo); |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(key); |
| | | msg.setData(recordInfo); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | recordDataCatch.put(device.getDeviceId(), sn, sumNum, new ArrayList<>()); |
| | | releaseRequest(device.getDeviceId(), sn); |
| | | } else { |
| | | Iterator<Element> recordListIterator = recordListElement.elementIterator(); |
| | | List<RecordItem> recordList = new ArrayList<RecordItem>(); |
| | | if (recordListIterator != null) { |
| | | RecordItem record = new RecordItem(); |
| | | logger.info("处理录像列表数据..."); |
| | | List<RecordItem> recordList = new ArrayList<>(); |
| | | // 遍历DeviceList |
| | | while (recordListIterator.hasNext()) { |
| | | Element itemRecord = recordListIterator.next(); |
| | |
| | | logger.info("记录为空,下一个..."); |
| | | continue; |
| | | } |
| | | record = new RecordItem(); |
| | | RecordItem record = new RecordItem(); |
| | | record.setDeviceId(getText(itemRecord, "DeviceID")); |
| | | record.setName(getText(itemRecord, "Name")); |
| | | record.setFilePath(getText(itemRecord, "FilePath")); |
| | | record.setFileSize(getText(itemRecord, "FileSize")); |
| | | record.setAddress(getText(itemRecord, "Address")); |
| | | record.setStartTime( |
| | | DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "StartTime"))); |
| | | record.setEndTime( |
| | | DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "EndTime"))); |
| | | |
| | | String startTimeStr = getText(itemRecord, "StartTime"); |
| | | record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr)); |
| | | |
| | | String endTimeStr = getText(itemRecord, "EndTime"); |
| | | record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr)); |
| | | |
| | | record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 |
| | | : Integer.parseInt(getText(itemRecord, "Secrecy"))); |
| | | record.setType(getText(itemRecord, "Type")); |
| | | record.setRecorderId(getText(itemRecord, "RecorderID")); |
| | | recordList.add(record); |
| | | } |
| | | recordInfo.setRecordList(recordList); |
| | | int count = recordDataCatch.put(device.getDeviceId(), sn, sumNum, recordList); |
| | | logger.info("[国标录像], {}->{}: {}/{}", device.getDeviceId(), sn, count, sumNum); |
| | | } |
| | | eventPublisher.recordEndEventPush(recordInfo); |
| | | // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 |
| | | String cacheKey = CACHE_RECORDINFO_KEY + device.getDeviceId() + sn; |
| | | redis.set(cacheKey + "_" + uuid, recordList, 90); |
| | | if (!threadNameList.contains(cacheKey)) { |
| | | threadNameList.add(cacheKey); |
| | | CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo); |
| | | chk.setName(cacheKey); |
| | | chk.setDeferredResultHolder(deferredResultHolder); |
| | | chk.setRedis(redis); |
| | | chk.setLogger(logger); |
| | | chk.start(); |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug("Start Thread " + cacheKey + "."); |
| | | } |
| | | } else { |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug("Thread " + cacheKey + " already started."); |
| | | } |
| | | |
| | | if (recordDataCatch.isComplete(device.getDeviceId(), sn)){ |
| | | releaseRequest(device.getDeviceId(), sn); |
| | | } |
| | | } |
| | | } catch (SipException e) { |
| | |
| | | public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { |
| | | |
| | | } |
| | | |
| | | public void releaseRequest(String deviceId, String sn){ |
| | | String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(0); |
| | | wvpResult.setMsg("success"); |
| | | // 对数据进行排序 |
| | | Collections.sort(recordDataCatch.getRecordInfo(deviceId, sn).getRecordList()); |
| | | wvpResult.setData(recordDataCatch.getRecordInfo(deviceId, sn)); |
| | | |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(key); |
| | | msg.setData(wvpResult); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | recordDataCatch.remove(deviceId, sn); |
| | | } |
| | | } |
| | |
| | | }); |
| | | |
| | | // 获取zlm信息 |
| | | logger.info("[zlm接入]等待默认zlm中..."); |
| | | logger.info("[zlm] 等待默认zlm中..."); |
| | | |
| | | // 获取所有的zlm, 并开启主动连接 |
| | | List<MediaServerItem> all = mediaServerService.getAllFromDatabase(); |
| | |
| | | @Async |
| | | @EventListener |
| | | public void onApplicationEvent(ZLMOnlineEvent event) { |
| | | |
| | | logger.info("【ZLM上线】ID:" + event.getMediaServerId()); |
| | | logger.info("[ZLM] 上线 ID:" + event.getMediaServerId()); |
| | | streamPushService.zlmServerOnline(event.getMediaServerId()); |
| | | streamProxyService.zlmServerOnline(event.getMediaServerId()); |
| | | |
| | |
| | | @EventListener |
| | | public void onApplicationEvent(ZLMOfflineEvent event) { |
| | | |
| | | logger.info("ZLM离线事件触发,ID:" + event.getMediaServerId()); |
| | | logger.info("[ZLM] 离线,ID:" + event.getMediaServerId()); |
| | | // 处理ZLM离线 |
| | | mediaServerService.zlmServerOffline(event.getMediaServerId()); |
| | | streamProxyService.zlmServerOffline(event.getMediaServerId()); |
| | |
| | | |
| | | void updateVmServer(List<MediaServerItem> mediaServerItemList); |
| | | |
| | | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck); |
| | | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback); |
| | | |
| | | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback); |
| | | |
| | |
| | | |
| | | @Override |
| | | public void online(Device device) { |
| | | logger.info("[设备上线],deviceId:" + device.getDeviceId()); |
| | | logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); |
| | | Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId()); |
| | | Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); |
| | | |
| | |
| | | */ |
| | | @Override |
| | | public void updateVmServer(List<MediaServerItem> mediaServerItemList) { |
| | | logger.info("[缓存初始化] Media Server "); |
| | | logger.info("[zlm] 缓存初始化 "); |
| | | for (MediaServerItem mediaServerItem : mediaServerItemList) { |
| | | if (StringUtils.isEmpty(mediaServerItem.getId())) { |
| | | continue; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) { |
| | | return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,false); |
| | | public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) { |
| | | return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,isPlayback); |
| | | } |
| | | |
| | | @Override |
| | |
| | | */ |
| | | @Override |
| | | public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]正在连接", |
| | | logger.info("[ZLM] 正在连接 : {} -> {}:{}", |
| | | zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); |
| | | |
| | | MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); |
| | |
| | | setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); |
| | | } |
| | | publisher.zlmOnlineEventPublish(serverItem.getId()); |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]连接成功", |
| | | logger.info("[ZLM] 连接成功 {} - {}:{} ", |
| | | zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); |
| | | } |
| | | |
| | |
| | | */ |
| | | @Override |
| | | public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]正在设置zlm", |
| | | logger.info("[ZLM] 正在设置 :{} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | String protocol = sslEnabled ? "https" : "http"; |
| | | String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); |
| | |
| | | |
| | | if (responseJSON != null && responseJSON.getInteger("code") == 0) { |
| | | if (restart) { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm成功, 开始重启以保证配置生效", |
| | | logger.info("[ZLM] 设置成功,开始重启以保证配置生效 {} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | zlmresTfulUtils.restartServer(mediaServerItem); |
| | | }else { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm成功", |
| | | logger.info("[ZLM] 设置成功 {} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | } |
| | | |
| | | |
| | | }else { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm失败", |
| | | logger.info("[ZLM] 设置zlm失败 {} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | } |
| | | |
| | |
| | | if (mediaServerItem.isRtpEnable()) { |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck()); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| | | play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response)->{ |
| | | if (hookEvent != null) { |
| | | hookEvent.response(mediaServerItem, response); |
| | |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | if (ssrcInfo == null) { |
| | | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck()); |
| | | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| | | } |
| | | |
| | | // 超时处理 |
| | |
| | | return null; |
| | | } |
| | | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true, true); |
| | | |
| | | return playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback); |
| | | } |
| | |
| | | return null; |
| | | } |
| | | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true, true); |
| | | |
| | | return download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed,infoCallBack, hookCallBack); |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.utils; |
| | | |
| | | import java.text.ParseException; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.time.Instant; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.time.temporal.TemporalAccessor; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | /** |
| | |
| | | public class DateUtil { |
| | | |
| | | private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss"; |
| | | private static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; |
| | | public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; |
| | | |
| | | public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); |
| | | public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); |
| | | |
| | | public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { |
| | | public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault()); |
| | | public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault()); |
| | | |
| | | try { |
| | | return formatISO8601.format(format.parse(formatTime)); |
| | | } catch (ParseException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return ""; |
| | | public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { |
| | | return formatterISO8601.format(formatter.parse(formatTime)); |
| | | } |
| | | |
| | | public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { |
| | | return formatter.format(formatterISO8601.parse(formatTime)); |
| | | |
| | | try { |
| | | return format.format(formatISO8601.parse(formatTime)); |
| | | } catch (ParseException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { |
| | | //设置要读取的时间字符串格式 |
| | | Date date; |
| | | try { |
| | | date = format.parse(formatTime); |
| | | Long timestamp=date.getTime()/1000; |
| | | //转换为Date类 |
| | | return timestamp; |
| | | } catch (ParseException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return 0; |
| | | TemporalAccessor temporalAccessor = formatter.parse(formatTime); |
| | | Instant instant = Instant.from(temporalAccessor); |
| | | return instant.getEpochSecond(); |
| | | } |
| | | |
| | | public static String getNow() { |
| | | return format.format(System.currentTimeMillis()); |
| | | LocalDateTime nowDateTime = LocalDateTime.now(); |
| | | return formatter.format(nowDateTime); |
| | | } |
| | | |
| | | public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { |
| | | try { |
| | | LocalDate.parse(timeStr, dateTimeFormatter); |
| | | return true; |
| | | }catch (DateTimeParseException exception) { |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.service.IPlayService; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiImplicitParam; |
| | | import io.swagger.annotations.ApiImplicitParams; |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.UUID; |
| | | |
| | | @Api(tags = "国标录像") |
| | |
| | | @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class), |
| | | }) |
| | | @GetMapping("/query/{deviceId}/{channelId}") |
| | | public DeferredResult<ResponseEntity<RecordInfo>> recordinfo(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ |
| | | public DeferredResult<ResponseEntity<WVPResult<RecordInfo>>> recordinfo(@PathVariable String deviceId, @PathVariable String channelId, String startTime, String endTime){ |
| | | |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s",deviceId, startTime, endTime)); |
| | | } |
| | | DeferredResult<ResponseEntity<WVPResult<RecordInfo>>> result = new DeferredResult<>(); |
| | | if (!DateUtil.verification(startTime, DateUtil.formatter)){ |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); |
| | | |
| | | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); |
| | | result.setResult(resultResponseEntity); |
| | | return result; |
| | | } |
| | | if (!DateUtil.verification(endTime, DateUtil.formatter)){ |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); |
| | | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); |
| | | result.setResult(resultResponseEntity); |
| | | return result; |
| | | } |
| | | |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | // 指定超时时间 1分钟30秒 |
| | | DeferredResult<ResponseEntity<RecordInfo>> result = new DeferredResult<>(90*1000L); |
| | | String uuid = UUID.randomUUID().toString(); |
| | | int sn = (int)((Math.random()*9+1)*100000); |
| | | String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; |
| | |
| | | msg.setId(uuid); |
| | | msg.setKey(key); |
| | | cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, null, null, null, (eventResult -> { |
| | | msg.setData("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg ); |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg); |
| | | msg.setData(wvpResult); |
| | | resultHolder.invokeResult(msg); |
| | | })); |
| | | |
| | |
| | | resultHolder.put(key, uuid, result); |
| | | result.onTimeout(()->{ |
| | | msg.setData("timeout"); |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("timeout"); |
| | | msg.setData(wvpResult); |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | return result; |
| | |
| | | }); |
| | | }, |
| | | queryRecords: function (itemData) { |
| | | var format = moment().format("YYYY-M-D"); |
| | | var format = moment().format("yyyy-MM-DD"); |
| | | let deviceId = this.deviceId; |
| | | let channelId = itemData.channelId; |
| | | this.$refs.devicePlayer.openDialog("record", deviceId, channelId, {date: format}) |
| | |
| | | method: 'get', |
| | | url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime |
| | | }).then(function (res) { |
| | | // 处理时间信息 |
| | | that.videoHistory.searchHistoryResult = res.data.recordList; |
| | | that.recordsLoading = false; |
| | | console.log(res) |
| | | if(res.data.code === 0) { |
| | | // 处理时间信息 |
| | | that.videoHistory.searchHistoryResult = res.data.data.recordList; |
| | | that.recordsLoading = false; |
| | | }else { |
| | | this.$message({ |
| | | showClose: true, |
| | | message: res.data.msg, |
| | | type: "error", |
| | | }); |
| | | } |
| | | |
| | | }).catch(function (e) { |
| | | console.log(e.message); |
| | | // that.videoHistory.searchHistoryResult = falsificationData.recordData; |