src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
@@ -4,11 +4,6 @@ public class Device { /** * Id */ private int id; /** * 设备Id */ private String deviceId; @@ -119,13 +114,7 @@ */ private int subscribeCycleForCatalog ; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getDeviceId() { return deviceId; @@ -294,6 +283,4 @@ public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) { this.subscribeCycleForCatalog = subscribeCycleForCatalog; } } src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java
@@ -8,10 +8,6 @@ public class MobilePosition { /** * Id */ private int id; /** * 设备Id */ private String deviceId; @@ -76,13 +72,6 @@ */ private String cnLat; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getDeviceId() { return deviceId; src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
@@ -19,7 +19,9 @@ private String name; private String filePath; private String fileSize; private String address; private String startTime; @@ -104,6 +106,14 @@ this.recorderId = recorderId; } public String getFileSize() { return fileSize; } public void setFileSize(String fileSize) { this.fileSize = fileSize; } @Override public int compareTo(@NotNull RecordItem recordItem) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
@@ -1,12 +1,11 @@ package com.genersoft.iot.vmp.gb28181.event; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent; import com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire.PlatformKeepaliveExpireEvent; import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformCycleRegisterEvent; import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent; import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEvent; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.media.zlm.event.ZLMOfflineEvent; import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent; @@ -15,7 +14,6 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent; import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent; @@ -144,4 +142,11 @@ GbStream[] gbStreams = {gbStream}; catalogEventPublishForStream(platformId, gbStreams, type); } public void recordEndEventPush(RecordInfo recordInfo) { RecordEndEvent outEvent = new RecordEndEvent(this); outEvent.setRecordInfo(recordInfo); applicationEventPublisher.publishEvent(outEvent); } } src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
@@ -76,10 +76,7 @@ eventResult.callId = callid; eventResult.msg = "注册超时"; eventResult.type = "register timeout"; if (sipSubscribe.getErrorSubscribe(callid) != null) { sipSubscribe.getErrorSubscribe(callid).response(eventResult); } sipSubscribe.getErrorSubscribe(callid).response(eventResult); } } src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
@@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.gb28181.event.record; import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; import com.genersoft.iot.vmp.gb28181.bean.RecordItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,12 +24,8 @@ private static Map<String, SseEmitter> sseEmitters = new Hashtable<>(); public void addSseEmitters(String browserId, SseEmitter sseEmitter) { sseEmitters.put(browserId, sseEmitter); } public interface RecordEndEventHandler{ void handler(List<RecordItem> recordItems); void handler(RecordInfo recordInfo); } private Map<String, RecordEndEventHandler> handlerMap = new HashMap<>(); @@ -38,6 +35,15 @@ logger.debug("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(), event.getRecordInfo().getChannelId(), event.getRecordInfo().getRecordList().size() ); } if (handlerMap.size() > 0) { for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) { recordEndEventHandler.handler(event.getRecordInfo()); } } } public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) { handlerMap.put(device + channelId, recordEndEventHandler); } } src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
@@ -2,6 +2,7 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; @@ -87,4 +88,12 @@ */ boolean sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo); /** * 回复recordInfo * @param deviceChannel 通道信息 * @param parentPlatform 平台信息 * @param fromTag fromTag * @param recordInfo 录像信息 */ boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo); } src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -1210,11 +1210,19 @@ recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n"); recordInfoXml.append("<SN>" + sn + "</SN>\r\n"); recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n"); recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n"); recordInfoXml.append("<Secrecy> "+ secrecy + " </Secrecy>\r\n"); // 大华NVR要求必须增加一个值为all的文本元素节点Type recordInfoXml.append("<Type>" + type+"</Type>\r\n"); if (startTime != null) { recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n"); } if (endTime != null) { recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n"); } if (secrecy != null) { recordInfoXml.append("<Secrecy> "+ secrecy + " </Secrecy>\r\n"); } if (type != null) { // 大华NVR要求必须增加一个值为all的文本元素节点Type recordInfoXml.append("<Type>" + type+"</Type>\r\n"); } recordInfoXml.append("</Query>\r\n"); String tm = Long.toString(System.currentTimeMillis()); src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -1,12 +1,10 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; 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.DateUtil; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import org.slf4j.Logger; @@ -17,6 +15,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.sip.*; import javax.sip.header.CallIdHeader; @@ -470,4 +469,55 @@ return true; } @Override public boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) { if ( parentPlatform ==null) { return false; } try { StringBuffer recordXml = new StringBuffer(600); recordXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n"); recordXml.append("<Response>\r\n"); recordXml.append("<CmdType>RecordInfo</CmdType>\r\n"); recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n"); recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n"); recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n"); recordXml.append("<RecordList Num=\"" + recordInfo.getRecordList().size()+"\">\r\n"); for (RecordItem recordItem : recordInfo.getRecordList()) { recordXml.append("<Item>\r\n"); if (deviceChannel != null) { recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n"); recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n"); recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n"); recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n"); recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n"); recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n"); if (!StringUtils.isEmpty(recordItem.getFileSize())) { recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n"); } if (!StringUtils.isEmpty(recordItem.getFilePath())) { recordXml.append("<FilePath>" + recordItem.getFilePath() + "</FilePath>\r\n"); } } recordXml.append("</Item>\r\n"); } recordXml.append("</RecordList>\r\n"); recordXml.append("</Response>\r\n"); // callid CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId(); System.out.println( recordXml.toString() ); Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, callIdHeader); transmitRequest(parentPlatform, request); } catch (SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); return false; } return true; } } src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java
@@ -3,6 +3,7 @@ import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; @@ -47,6 +48,9 @@ private SIPCommander commander; @Autowired private RecordEndEventListener recordEndEventListener; @Autowired private SipConfig config; @Autowired @@ -65,49 +69,89 @@ @Override public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId(); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); try { // 回复200 OK responseAck(evt, Response.OK); Element snElement = rootElement.element("SN"); int sn = Integer.parseInt(snElement.getText()); Element deviceIDElement = rootElement.element("DeviceID"); String channelId = deviceIDElement.getText(); Element startTimeElement = rootElement.element("StartTime"); String startTime = startTimeElement.getText(); Element endTimeElement = rootElement.element("EndTime"); String endTime = endTimeElement.getText(); Element secrecyElement = rootElement.element("Secrecy"); int secrecy = Integer.parseInt(secrecyElement.getText()); Element typeElement = rootElement.element("Type"); String type = typeElement.getText(); // 确认是直播还是国标, 国标直接请求下级,直播请求录像管理服务 List<ChannelSourceInfo> channelSources = storager.getChannelSource(parentPlatform.getServerGBId(), channelId); if (channelSources.get(0).getCount() > 0) { // 国标 // 向国标设备请求录像数据 Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); commander.recordInfoQuery(device, channelId, DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime), DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> { // 查询成功 }),(eventResult -> { // 查询失败 })); }else if (channelSources.get(0).getCount() > 0) { // 直播流 // TODO }else { // 错误的请求 } } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); Element snElement = rootElement.element("SN"); int sn = Integer.parseInt(snElement.getText()); Element deviceIDElement = rootElement.element("DeviceID"); String channelId = deviceIDElement.getText(); Element startTimeElement = rootElement.element("StartTime"); String startTime = null; if (startTimeElement != null) { startTime = startTimeElement.getText(); } Element endTimeElement = rootElement.element("EndTime"); String endTime = null; if (endTimeElement != null) { endTime = endTimeElement.getText(); } Element secrecyElement = rootElement.element("Secrecy"); int secrecy = 0; if (secrecyElement != null) { secrecy = Integer.parseInt(secrecyElement.getText()); } String type = "all"; Element typeElement = rootElement.element("Type"); if (typeElement != null) { type = typeElement.getText(); } // 确认是直播还是国标, 国标直接请求下级,直播请求录像管理服务 List<ChannelSourceInfo> channelSources = storager.getChannelSource(parentPlatform.getServerGBId(), channelId); if (channelSources.get(0).getCount() > 0) { // 国标 // 向国标设备请求录像数据 Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); // 接收录像数据 recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{ cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, fromHeader.getTag(), recordInfo); }); commander.recordInfoQuery(device, channelId, DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime), DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> { // 回复200 OK try { responseAck(evt, Response.OK); } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } }),(eventResult -> { // 查询失败 try { responseAck(evt, eventResult.statusCode, eventResult.msg); } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } })); }else if (channelSources.get(1).getCount() > 0) { // 直播流 // TODO try { responseAck(evt, Response.NOT_IMPLEMENTED); // 回复未实现 } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } }else { // 错误的请求 try { responseAck(evt, Response.BAD_REQUEST); } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } } } } src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
@@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; 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.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; @@ -49,6 +50,9 @@ @Autowired private DeferredResultHolder deferredResultHolder; @Autowired private EventPublisher eventPublisher; @Override public void afterPropertiesSet() throws Exception { responseMessageHandler.addHandler(cmdType, this); @@ -77,6 +81,7 @@ Element recordListElement = rootElement.element("RecordList"); if (recordListElement == null || recordInfo.getSumNum() == 0) { logger.info("无录像数据"); eventPublisher.recordEndEventPush(recordInfo); RequestMessage msg = new RequestMessage(); msg.setKey(key); msg.setData(recordInfo); @@ -99,6 +104,7 @@ 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"))); @@ -112,7 +118,7 @@ } recordInfo.setRecordList(recordList); } eventPublisher.recordEndEventPush(recordInfo); // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 String cacheKey = CACHE_RECORDINFO_KEY + device.getDeviceId() + sn; redis.set(cacheKey + "_" + uuid, recordList, 90); src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
@@ -90,6 +90,6 @@ @Select("select 'channel' as name, count(pgc.platformId) count from platform_gb_channel pgc where pgc.platformId=#{platformId} and pgc.channelId =#{gbId} " + "union " + "select 'stream' as name, count(pgs.platformId) count from platform_gb_stream pgs left join gb_stream gs on pgs.gbStreamId = gs.id where pgs.platformId=#{platformId} and gs.gbId = #{gbId}") "select 'stream' as name, count(pgs.platformId) count from platform_gb_stream pgs left join gb_stream gs on pgs.gbStreamId = gs.gbStreamId where pgs.platformId=#{platformId} and gs.gbId = #{gbId}") List<ChannelSourceInfo> getChannelSource(String platformId, String gbId); } src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
@@ -34,6 +34,7 @@ "</script>") int addChannels(String platformId, List<ChannelReduce> channelReducesToAdd); @Delete("<script> "+ "DELETE FROM platform_gb_channel WHERE platformId='${platformId}' AND deviceAndChannelId in" + "<foreach collection='channelReducesToDel' item='item' open='(' separator=',' close=')' > '${item.deviceId}_${item.channelId}'</foreach>" + @@ -50,10 +51,12 @@ "</script>") int cleanChannelForGB(String platformId); @Select("SELECT * FROM device_channel WHERE deviceId = (SELECT deviceId FROM platform_gb_channel WHERE " + "platformId='${platformId}' AND channelId='${channelId}' ) AND channelId='${channelId}'") DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); @Select("select dc.channelId as id, dc.name as name, pgc.platformId as platformId, pgc.catalogId as parentId, 0 as childrenCount, 1 as type " + "from device_channel dc left join platform_gb_channel pgc on dc.deviceId = pgc.deviceId and dc.channelId = pgc.channelId " + "where pgc.platformId=#{platformId} and pgc.catalogId=#{catalogId}")