src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
@@ -33,6 +33,9 @@ // 设置所有平台离线 storager.outlineForAllParentPlatform(); // 清理所有平台注册缓存 redisCatchStorage.cleanPlatformRegisterInfos(); List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true); for (ParentPlatform parentPlatform : parentPlatforms) { src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -16,7 +16,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; // import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
@@ -41,6 +41,8 @@ public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>(); public void put(String key, DeferredResult result) { src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -120,6 +120,14 @@ boolean audioBroadcastCmd(Device device,String channelId); /** * 语音广播 * * @param device 视频设备 */ void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); boolean audioBroadcastCmd(Device device); /** * 音视频录像控制 * * @param device 视频设备 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -3,7 +3,7 @@ import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; // import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.util.DigestUtils; src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
@@ -6,14 +6,14 @@ import javax.sip.InvalidArgumentException; import javax.sip.PeerUnavailableException; import javax.sip.SipFactory; import javax.sip.SipProvider; // import javax.sip.SipProvider; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.*; import javax.sip.message.Request; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; // import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import com.genersoft.iot.vmp.conf.SipConfig; src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -23,7 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; // import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -47,8 +47,7 @@ public class SIPCommander implements ISIPCommander { private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); @Autowired private SipConfig sipConfig; @@ -623,11 +622,67 @@ */ @Override public boolean audioBroadcastCmd(Device device, String channelId) { // TODO Auto-generated method stub // 改为新的实现 return false; } /** * 语音广播 * * @param device 视频设备 * @param channelId 预览通道 */ @Override public boolean audioBroadcastCmd(Device device) { try { StringBuffer broadcastXml = new StringBuffer(200); broadcastXml.append("<?xml version=\"1.0\" ?>\r\n"); broadcastXml.append("<Notify>\r\n"); broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); broadcastXml.append("<SourceID>" + sipConfig.getSipId() + "</SourceID>\r\n"); broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); broadcastXml.append("</Notify>\r\n"); String tm = Long.toString(System.currentTimeMillis()); CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId(); Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); transmitRequest(device, request); return true; } catch (SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); } return false; } @Override public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) { try { StringBuffer broadcastXml = new StringBuffer(200); broadcastXml.append("<?xml version=\"1.0\" ?>\r\n"); broadcastXml.append("<Notify>\r\n"); broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); broadcastXml.append("<SourceID>" + sipConfig.getSipId() + "</SourceID>\r\n"); broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); broadcastXml.append("</Notify>\r\n"); String tm = Long.toString(System.currentTimeMillis()); CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId(); Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); transmitRequest(device, request, errorEvent); } catch (SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); } } /** * 音视频录像控制 * * @param device 视频设备 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; // import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Lazy; import org.springframework.lang.Nullable; src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
@@ -14,6 +14,7 @@ import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; @@ -74,144 +75,216 @@ Request request = evt.getRequest(); SipURI sipURI = (SipURI) request.getRequestURI(); String channelId = sipURI.getUser(); String platformId = null; String requesterId = null; FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); AddressImpl address = (AddressImpl) fromHeader.getAddress(); SipUri uri = (SipUri) address.getURI(); platformId = uri.getUser(); requesterId = uri.getUser(); if (platformId == null || channelId == null) { logger.info("无法从FromHeader的Address中获取到平台id,返回404"); if (requesterId == null || channelId == null) { logger.info("无法从FromHeader的Address中获取到平台id,返回400"); responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误 return; } // 查询平台下是否有该通道 DeviceChannel channel = storager.queryChannelInParentPlatform(platformId, channelId); if (channel == null) { logger.info("通道不存在,返回404"); responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在 return; }else { responseAck(evt, Response.TRYING); // 通道存在,发100,trying } // 解析sdp消息, 使用jainsip 自带的sdp解析方式 String contentString = new String(request.getRawContent()); // jainSip不支持y=字段, 移除移除以解析。 int ssrcIndex = contentString.indexOf("y="); String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 // String ssrc = contentString.substring(ssrcIndex + 2, contentString.length()) // .replace("\r\n", "").replace("\n", ""); // 查询请求方是否上级平台 ParentPlatform platform = storager.queryParentPlatById(requesterId); if (platform != null) { // 查询平台下是否有该通道 DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); if (channel == null) { logger.info("通道不存在,返回404"); responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在 return; }else { responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中 } // 解析sdp消息, 使用jainsip 自带的sdp解析方式 String contentString = new String(request.getRawContent()); String substring = contentString.substring(0, contentString.indexOf("y=")); SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); // jainSip不支持y=字段, 移除移除以解析。 int ssrcIndex = contentString.indexOf("y="); //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); String substring = contentString.substring(0, contentString.indexOf("y=")); SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); // 获取支持的格式 Vector mediaDescriptions = sdp.getMediaDescriptions(true); // 查看是否支持PS 负载96 //String ip = null; int port = -1; //boolean recvonly = false; boolean mediaTransmissionTCP = false; Boolean tcpActive = null; for (int i = 0; i < mediaDescriptions.size(); i++) { MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); Media media = mediaDescription.getMedia(); // 获取支持的格式 Vector mediaDescriptions = sdp.getMediaDescriptions(true); // 查看是否支持PS 负载96 //String ip = null; int port = -1; //boolean recvonly = false; boolean mediaTransmissionTCP = false; Boolean tcpActive = null; for (int i = 0; i < mediaDescriptions.size(); i++) { MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); Media media = mediaDescription.getMedia(); Vector mediaFormats = media.getMediaFormats(false); if (mediaFormats.contains("96")) { port = media.getMediaPort(); //String mediaType = media.getMediaType(); String protocol = media.getProtocol(); Vector mediaFormats = media.getMediaFormats(false); if (mediaFormats.contains("96")) { port = media.getMediaPort(); //String mediaType = media.getMediaType(); String protocol = media.getProtocol(); // 区分TCP发流还是udp, 当前默认udp if ("TCP/RTP/AVP".equals(protocol)) { String setup = mediaDescription.getAttribute("setup"); if (setup != null) { mediaTransmissionTCP = true; if ("active".equals(setup)) { tcpActive = true; }else if ("passive".equals(setup)) { tcpActive = false; // 区分TCP发流还是udp, 当前默认udp if ("TCP/RTP/AVP".equals(protocol)) { String setup = mediaDescription.getAttribute("setup"); if (setup != null) { mediaTransmissionTCP = true; if ("active".equals(setup)) { tcpActive = true; }else if ("passive".equals(setup)) { tcpActive = false; } } } break; } break; } } if (port == -1) { logger.info("不支持的媒体格式,返回415"); // 回复不支持的格式 responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 return; } String username = sdp.getOrigin().getUsername(); String addressStr = sdp.getOrigin().getAddress(); //String sessionName = sdp.getSessionName().getValue(); logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc); if (port == -1) { logger.info("不支持的媒体格式,返回415"); // 回复不支持的格式 responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 return; } String username = sdp.getOrigin().getUsername(); String addressStr = sdp.getOrigin().getAddress(); //String sessionName = sdp.getSessionName().getValue(); logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc); Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId); if (device == null) { logger.warn("点播平台{}的通道{}时未找到设备信息", platformId, channel); responseAck(evt, Response.SERVER_INTERNAL_ERROR); return; } SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, platformId, device.getDeviceId(), channelId, mediaTransmissionTCP); if (tcpActive != null) { sendRtpItem.setTcpActive(tcpActive); } if (sendRtpItem == null) { logger.warn("服务器端口资源不足"); responseAck(evt, Response.BUSY_HERE); return; } Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); if (device == null) { logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel); responseAck(evt, Response.SERVER_INTERNAL_ERROR); return; } SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, requesterId, device.getDeviceId(), channelId, mediaTransmissionTCP); if (tcpActive != null) { sendRtpItem.setTcpActive(tcpActive); } if (sendRtpItem == null) { logger.warn("服务器端口资源不足"); responseAck(evt, Response.BUSY_HERE); return; } // 写入redis, 超时时回复 redisCatchStorage.updateSendRTPSever(sendRtpItem); // 通知下级推流, PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{ // 收到推流, 回复200OK, 等待ack sendRtpItem.setStatus(1); // 写入redis, 超时时回复 redisCatchStorage.updateSendRTPSever(sendRtpItem); // TODO 添加对tcp的支持 MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); content.append("s=Play\r\n"); content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); content.append("t=0 0\r\n"); content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); content.append("a=sendonly\r\n"); content.append("a=rtpmap:96 PS/90000\r\n"); content.append("y="+ ssrc + "\r\n"); content.append("f=\r\n"); // 通知下级推流, PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{ // 收到推流, 回复200OK, 等待ack sendRtpItem.setStatus(1); redisCatchStorage.updateSendRTPSever(sendRtpItem); // TODO 添加对tcp的支持 MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); content.append("s=Play\r\n"); content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); content.append("t=0 0\r\n"); content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); content.append("a=sendonly\r\n"); content.append("a=rtpmap:96 PS/90000\r\n"); content.append("y="+ ssrc + "\r\n"); content.append("f=\r\n"); try { responseAck(evt, content.toString()); } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); try { responseAck(evt, content.toString()); } catch (SipException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } },(event -> { // 未知错误。直接转发设备点播的错误 Response response = null; try { response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest()); getServerTransaction(evt).sendResponse(response); } catch (ParseException | SipException | InvalidArgumentException e) { e.printStackTrace(); } })); if (logger.isDebugEnabled()) { logger.debug(playResult.getResult().toString()); } },(event -> { // 未知错误。直接转发设备点播的错误 Response response = null; try { response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest()); getServerTransaction(evt).sendResponse(response); } else { // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) Device device = storager.queryVideoDevice(requesterId); if (device != null) { logger.info("收到设备" + requesterId + "的语音广播Invite请求"); responseAck(evt, Response.TRYING); } catch (ParseException | SipException | InvalidArgumentException e) { e.printStackTrace(); String contentString = new String(request.getRawContent()); // jainSip不支持y=字段, 移除移除以解析。 String substring = contentString; String ssrc = "0000000404"; int ssrcIndex = contentString.indexOf("y="); if (ssrcIndex > 0) { substring = contentString.substring(0, ssrcIndex); ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); } ssrcIndex = substring.indexOf("f="); if (ssrcIndex > 0) { substring = contentString.substring(0, ssrcIndex); } SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); // 获取支持的格式 Vector mediaDescriptions = sdp.getMediaDescriptions(true); // 查看是否支持PS 负载96 int port = -1; //boolean recvonly = false; boolean mediaTransmissionTCP = false; Boolean tcpActive = null; for (int i = 0; i < mediaDescriptions.size(); i++) { MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); Media media = mediaDescription.getMedia(); Vector mediaFormats = media.getMediaFormats(false); if (mediaFormats.contains("8")) { port = media.getMediaPort(); String protocol = media.getProtocol(); // 区分TCP发流还是udp, 当前默认udp if ("TCP/RTP/AVP".equals(protocol)) { String setup = mediaDescription.getAttribute("setup"); if (setup != null) { mediaTransmissionTCP = true; if ("active".equals(setup)) { tcpActive = true; } else if ("passive".equals(setup)) { tcpActive = false; } } } break; } } if (port == -1) { logger.info("不支持的媒体格式,返回415"); // 回复不支持的格式 responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 return; } String username = sdp.getOrigin().getUsername(); String addressStr = sdp.getOrigin().getAddress(); logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); } else { logger.warn("来自无效设备/平台的请求"); responseAck(evt, Response.BAD_REQUEST); } })); if (logger.isDebugEnabled()) { logger.debug(playResult.getResult().toString()); } } catch (SipException | InvalidArgumentException | ParseException e) { src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -93,7 +93,7 @@ private static final String MESSAGE_ALARM = "Alarm"; private static final String MESSAGE_RECORD_INFO = "RecordInfo"; private static final String MESSAGE_MEDIA_STATUS = "MediaStatus"; // private static final String MESSAGE_BROADCAST = "Broadcast"; private static final String MESSAGE_BROADCAST = "Broadcast"; private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus"; private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl"; private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig"; @@ -123,7 +123,7 @@ logger.info("接收到Catalog消息"); processMessageCatalogList(evt); } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { //DeviceInfo消息处理 // DeviceInfo消息处理 processMessageDeviceInfo(evt); } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { // DeviceStatus消息处理 @@ -149,6 +149,9 @@ } else if (MESSAGE_PRESET_QUERY.equals(cmd)) { logger.info("接收到PresetQuery消息"); processMessagePresetQuery(evt); } else if (MESSAGE_BROADCAST.equals(cmd)) { // Broadcast消息处理 processMessageBroadcast(evt); } else { logger.info("接收到消息:" + cmd); responseAck(evt); @@ -298,7 +301,7 @@ // 远程启动功能 if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement, "TeleBoot"))) { if (deviceId.equals(targetGBId)) { // 远程启动功能:需要在重新启动程序后先对SipStack解绑 // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 logger.info("执行远程启动本平台命令"); ParentPlatform parentPlatform = storager.queryParentPlatById(platformId); cmderFroPlatform.unregister(parentPlatform, null, null); @@ -333,6 +336,7 @@ // 远程启动指定设备 } } // 云台/前端控制命令 if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) { String cmdString = XmlUtil.getText(rootElement,"PTZCmd"); Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId); @@ -895,6 +899,37 @@ } } /** * 处理AudioBroadcast语音广播Message * * @param evt */ private void processMessageBroadcast(RequestEvent evt) { try { Element rootElement = getRootElement(evt); String deviceId = XmlUtil.getText(rootElement, "DeviceID"); // 回复200 OK responseAck(evt); if (rootElement.getName().equals("Response")) { // 此处是对本平台发出Broadcast指令的应答 JSONObject json = new JSONObject(); XmlUtil.node2Json(rootElement, json); if (logger.isDebugEnabled()) { logger.debug(json.toJSONString()); } RequestMessage msg = new RequestMessage(); msg.setDeviceId(deviceId); msg.setType(DeferredResultHolder.CALLBACK_CMD_BROADCAST); msg.setData(json); deferredResultHolder.invokeResult(msg); } else { // 此处是上级发出的Broadcast指令 } } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { e.printStackTrace(); } } /*** * 回复200 OK src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java
@@ -50,7 +50,6 @@ */ @Override public void process(ResponseEvent evt, SipLayer layer, SipConfig config) { // TODO Auto-generated method stub Response response = evt.getResponse(); CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME); String callId = callIdHeader.getCallId(); src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
@@ -81,6 +81,8 @@ void delPlatformRegisterInfo(String callId); void cleanPlatformRegisterInfos(); void updateSendRTPSever(SendRtpItem sendRtpItem); /** src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -13,6 +13,7 @@ import java.util.*; @SuppressWarnings("rawtypes") @Component public class RedisCatchStorageImpl implements IRedisCatchStorage { @@ -213,6 +214,14 @@ } @Override public void cleanPlatformRegisterInfos() { List regInfos = redis.scan(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + "*"); for (Object key : regInfos) { redis.del(key.toString()); } } @Override public void updateSendRTPSever(SendRtpItem sendRtpItem) { String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + sendRtpItem.getPlatformId() + "_" + sendRtpItem.getChannelId(); redis.set(key, sendRtpItem); src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -2,6 +2,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; @@ -26,6 +27,8 @@ import org.springframework.web.context.request.async.DeferredResult; import java.util.UUID; import javax.sip.message.Response; @CrossOrigin @RestController @@ -204,5 +207,47 @@ } return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK); } /** * 语音广播命令API接口 * * @param deviceId */ @GetMapping("/broadcast/{deviceId}") @PostMapping("/broadcast/{deviceId}") public DeferredResult<ResponseEntity<String>> broadcastApi(@PathVariable String deviceId) { if (logger.isDebugEnabled()) { logger.debug("语音广播API调用"); } Device device = storager.queryVideoDevice(deviceId); cmder.audioBroadcastCmd(device, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId); JSONObject json = new JSONObject(); json.put("DeviceID", deviceId); json.put("CmdType", "Broadcast"); json.put("Result", "Failed"); json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); msg.setData(json); resultHolder.invokeResult(msg); }); DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); result.onTimeout(() -> { logger.warn(String.format("语音广播操作超时, 设备未返回应答指令")); RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId); JSONObject json = new JSONObject(); json.put("DeviceID", deviceId); json.put("CmdType", "Broadcast"); json.put("Result", "Failed"); json.put("Error", "Timeout. Device did not response to broadcast command."); msg.setData(json); resultHolder.invokeResult(msg); }); resultHolder.put(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId, result); return result; } } web_src/src/components/channelList.vue
@@ -99,7 +99,7 @@ currentPage: parseInt(this.$route.params.page), count: parseInt(this.$route.params.count), total: 0, beforeUrl: "/videoList", beforeUrl: "/deviceList", isLoging: false, autoList: true }; @@ -131,7 +131,7 @@ this.currentPage = parseInt(this.$route.params.page); this.count = parseInt(this.$route.params.count); if (this.parentChannelId == "" || this.parentChannelId == 0) { this.beforeUrl = "/videoList" this.beforeUrl = "/deviceList" } }, web_src/src/components/devicePosition.vue
@@ -81,7 +81,7 @@ parentChannelId: this.$route.params.parentChannelId, updateLooper: 0, //数据刷新轮训标志 total: 0, beforeUrl: "/videoList", beforeUrl: "/deviceList", isLoging: false, autoList: false, }; @@ -111,7 +111,7 @@ // this.currentPage = parseInt(this.$route.params.page); // this.count = parseInt(this.$route.params.count); // if (this.parentChannelId == "" || this.parentChannelId == 0) { // this.beforeUrl = "/videoList"; // this.beforeUrl = "/deviceList"; // } }, initBaiduMap() {