Commit f9d30bdfadd68e61f545e1c5f6260d4f58dca040
1 parent
c5a05c15
实现语音广播信令(web语音推流开发中)
Showing
11 changed files
with
352 additions
and
135 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
| @@ -16,7 +16,7 @@ import org.slf4j.Logger; | @@ -16,7 +16,7 @@ import org.slf4j.Logger; | ||
| 16 | import org.slf4j.LoggerFactory; | 16 | import org.slf4j.LoggerFactory; |
| 17 | import org.springframework.beans.factory.annotation.Autowired; | 17 | import org.springframework.beans.factory.annotation.Autowired; |
| 18 | import org.springframework.context.annotation.Bean; | 18 | import org.springframework.context.annotation.Bean; |
| 19 | -import org.springframework.context.annotation.ComponentScan; | 19 | +// import org.springframework.context.annotation.ComponentScan; |
| 20 | import org.springframework.context.annotation.DependsOn; | 20 | import org.springframework.context.annotation.DependsOn; |
| 21 | import org.springframework.stereotype.Component; | 21 | import org.springframework.stereotype.Component; |
| 22 | 22 |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
| @@ -41,6 +41,8 @@ public class DeferredResultHolder { | @@ -41,6 +41,8 @@ public class DeferredResultHolder { | ||
| 41 | 41 | ||
| 42 | public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; | 42 | public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; |
| 43 | 43 | ||
| 44 | + public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; | ||
| 45 | + | ||
| 44 | private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>(); | 46 | private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>(); |
| 45 | 47 | ||
| 46 | public void put(String key, DeferredResult result) { | 48 | public void put(String key, DeferredResult result) { |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| @@ -120,6 +120,14 @@ public interface ISIPCommander { | @@ -120,6 +120,14 @@ public interface ISIPCommander { | ||
| 120 | boolean audioBroadcastCmd(Device device,String channelId); | 120 | boolean audioBroadcastCmd(Device device,String channelId); |
| 121 | 121 | ||
| 122 | /** | 122 | /** |
| 123 | + * 语音广播 | ||
| 124 | + * | ||
| 125 | + * @param device 视频设备 | ||
| 126 | + */ | ||
| 127 | + void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); | ||
| 128 | + boolean audioBroadcastCmd(Device device); | ||
| 129 | + | ||
| 130 | + /** | ||
| 123 | * 音视频录像控制 | 131 | * 音视频录像控制 |
| 124 | * | 132 | * |
| 125 | * @param device 视频设备 | 133 | * @param device 视频设备 |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
| @@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; | @@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; | ||
| 3 | import com.genersoft.iot.vmp.conf.SipConfig; | 3 | import com.genersoft.iot.vmp.conf.SipConfig; |
| 4 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | 4 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 5 | import org.springframework.beans.factory.annotation.Autowired; | 5 | import org.springframework.beans.factory.annotation.Autowired; |
| 6 | -import org.springframework.beans.factory.annotation.Qualifier; | 6 | +// import org.springframework.beans.factory.annotation.Qualifier; |
| 7 | import org.springframework.stereotype.Component; | 7 | import org.springframework.stereotype.Component; |
| 8 | import org.springframework.util.DigestUtils; | 8 | import org.springframework.util.DigestUtils; |
| 9 | 9 |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| @@ -6,14 +6,14 @@ import java.util.ArrayList; | @@ -6,14 +6,14 @@ import java.util.ArrayList; | ||
| 6 | import javax.sip.InvalidArgumentException; | 6 | import javax.sip.InvalidArgumentException; |
| 7 | import javax.sip.PeerUnavailableException; | 7 | import javax.sip.PeerUnavailableException; |
| 8 | import javax.sip.SipFactory; | 8 | import javax.sip.SipFactory; |
| 9 | -import javax.sip.SipProvider; | 9 | +// import javax.sip.SipProvider; |
| 10 | import javax.sip.address.Address; | 10 | import javax.sip.address.Address; |
| 11 | import javax.sip.address.SipURI; | 11 | import javax.sip.address.SipURI; |
| 12 | import javax.sip.header.*; | 12 | import javax.sip.header.*; |
| 13 | import javax.sip.message.Request; | 13 | import javax.sip.message.Request; |
| 14 | 14 | ||
| 15 | import org.springframework.beans.factory.annotation.Autowired; | 15 | import org.springframework.beans.factory.annotation.Autowired; |
| 16 | -import org.springframework.beans.factory.annotation.Qualifier; | 16 | +// import org.springframework.beans.factory.annotation.Qualifier; |
| 17 | import org.springframework.stereotype.Component; | 17 | import org.springframework.stereotype.Component; |
| 18 | 18 | ||
| 19 | import com.genersoft.iot.vmp.conf.SipConfig; | 19 | 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.slf4j.LoggerFactory; | @@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory; | ||
| 23 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
| 24 | import org.springframework.beans.factory.annotation.Qualifier; | 24 | import org.springframework.beans.factory.annotation.Qualifier; |
| 25 | import org.springframework.beans.factory.annotation.Value; | 25 | import org.springframework.beans.factory.annotation.Value; |
| 26 | -import org.springframework.context.annotation.ComponentScan; | 26 | +// import org.springframework.context.annotation.ComponentScan; |
| 27 | import org.springframework.context.annotation.DependsOn; | 27 | import org.springframework.context.annotation.DependsOn; |
| 28 | import org.springframework.context.annotation.Lazy; | 28 | import org.springframework.context.annotation.Lazy; |
| 29 | import org.springframework.stereotype.Component; | 29 | import org.springframework.stereotype.Component; |
| @@ -47,8 +47,7 @@ import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; | @@ -47,8 +47,7 @@ import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; | ||
| 47 | public class SIPCommander implements ISIPCommander { | 47 | public class SIPCommander implements ISIPCommander { |
| 48 | 48 | ||
| 49 | private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); | 49 | private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); |
| 50 | - | ||
| 51 | - | 50 | + |
| 52 | @Autowired | 51 | @Autowired |
| 53 | private SipConfig sipConfig; | 52 | private SipConfig sipConfig; |
| 54 | 53 | ||
| @@ -623,11 +622,67 @@ public class SIPCommander implements ISIPCommander { | @@ -623,11 +622,67 @@ public class SIPCommander implements ISIPCommander { | ||
| 623 | */ | 622 | */ |
| 624 | @Override | 623 | @Override |
| 625 | public boolean audioBroadcastCmd(Device device, String channelId) { | 624 | public boolean audioBroadcastCmd(Device device, String channelId) { |
| 626 | - // TODO Auto-generated method stub | 625 | + // 改为新的实现 |
| 627 | return false; | 626 | return false; |
| 628 | } | 627 | } |
| 629 | 628 | ||
| 630 | /** | 629 | /** |
| 630 | + * 语音广播 | ||
| 631 | + * | ||
| 632 | + * @param device 视频设备 | ||
| 633 | + * @param channelId 预览通道 | ||
| 634 | + */ | ||
| 635 | + @Override | ||
| 636 | + public boolean audioBroadcastCmd(Device device) { | ||
| 637 | + try { | ||
| 638 | + StringBuffer broadcastXml = new StringBuffer(200); | ||
| 639 | + broadcastXml.append("<?xml version=\"1.0\" ?>\r\n"); | ||
| 640 | + broadcastXml.append("<Notify>\r\n"); | ||
| 641 | + broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); | ||
| 642 | + broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | ||
| 643 | + broadcastXml.append("<SourceID>" + sipConfig.getSipId() + "</SourceID>\r\n"); | ||
| 644 | + broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); | ||
| 645 | + broadcastXml.append("</Notify>\r\n"); | ||
| 646 | + | ||
| 647 | + String tm = Long.toString(System.currentTimeMillis()); | ||
| 648 | + | ||
| 649 | + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() | ||
| 650 | + : udpSipProvider.getNewCallId(); | ||
| 651 | + | ||
| 652 | + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); | ||
| 653 | + transmitRequest(device, request); | ||
| 654 | + return true; | ||
| 655 | + } catch (SipException | ParseException | InvalidArgumentException e) { | ||
| 656 | + e.printStackTrace(); | ||
| 657 | + } | ||
| 658 | + return false; | ||
| 659 | + } | ||
| 660 | + @Override | ||
| 661 | + public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) { | ||
| 662 | + try { | ||
| 663 | + StringBuffer broadcastXml = new StringBuffer(200); | ||
| 664 | + broadcastXml.append("<?xml version=\"1.0\" ?>\r\n"); | ||
| 665 | + broadcastXml.append("<Notify>\r\n"); | ||
| 666 | + broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); | ||
| 667 | + broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | ||
| 668 | + broadcastXml.append("<SourceID>" + sipConfig.getSipId() + "</SourceID>\r\n"); | ||
| 669 | + broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); | ||
| 670 | + broadcastXml.append("</Notify>\r\n"); | ||
| 671 | + | ||
| 672 | + String tm = Long.toString(System.currentTimeMillis()); | ||
| 673 | + | ||
| 674 | + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() | ||
| 675 | + : udpSipProvider.getNewCallId(); | ||
| 676 | + | ||
| 677 | + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); | ||
| 678 | + transmitRequest(device, request, errorEvent); | ||
| 679 | + } catch (SipException | ParseException | InvalidArgumentException e) { | ||
| 680 | + e.printStackTrace(); | ||
| 681 | + } | ||
| 682 | + } | ||
| 683 | + | ||
| 684 | + | ||
| 685 | + /** | ||
| 631 | * 音视频录像控制 | 686 | * 音视频录像控制 |
| 632 | * | 687 | * |
| 633 | * @param device 视频设备 | 688 | * @param device 视频设备 |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
| @@ -15,7 +15,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | @@ -15,7 +15,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | ||
| 15 | import org.springframework.beans.factory.annotation.Autowired; | 15 | import org.springframework.beans.factory.annotation.Autowired; |
| 16 | import org.springframework.beans.factory.annotation.Qualifier; | 16 | import org.springframework.beans.factory.annotation.Qualifier; |
| 17 | import org.springframework.beans.factory.annotation.Value; | 17 | import org.springframework.beans.factory.annotation.Value; |
| 18 | -import org.springframework.context.annotation.ComponentScan; | 18 | +// import org.springframework.context.annotation.ComponentScan; |
| 19 | import org.springframework.context.annotation.DependsOn; | 19 | import org.springframework.context.annotation.DependsOn; |
| 20 | import org.springframework.context.annotation.Lazy; | 20 | import org.springframework.context.annotation.Lazy; |
| 21 | import org.springframework.lang.Nullable; | 21 | import org.springframework.lang.Nullable; |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
| @@ -14,6 +14,7 @@ import javax.sip.message.Response; | @@ -14,6 +14,7 @@ import javax.sip.message.Response; | ||
| 14 | import com.genersoft.iot.vmp.conf.MediaServerConfig; | 14 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 15 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 15 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 16 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | 16 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 17 | +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | ||
| 17 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | 18 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 18 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | 19 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 19 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | 20 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| @@ -74,144 +75,216 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { | @@ -74,144 +75,216 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 74 | Request request = evt.getRequest(); | 75 | Request request = evt.getRequest(); |
| 75 | SipURI sipURI = (SipURI) request.getRequestURI(); | 76 | SipURI sipURI = (SipURI) request.getRequestURI(); |
| 76 | String channelId = sipURI.getUser(); | 77 | String channelId = sipURI.getUser(); |
| 77 | - String platformId = null; | 78 | + String requesterId = null; |
| 78 | 79 | ||
| 79 | FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); | 80 | FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); |
| 80 | AddressImpl address = (AddressImpl) fromHeader.getAddress(); | 81 | AddressImpl address = (AddressImpl) fromHeader.getAddress(); |
| 81 | SipUri uri = (SipUri) address.getURI(); | 82 | SipUri uri = (SipUri) address.getURI(); |
| 82 | - platformId = uri.getUser(); | 83 | + requesterId = uri.getUser(); |
| 83 | 84 | ||
| 84 | - if (platformId == null || channelId == null) { | ||
| 85 | - logger.info("无法从FromHeader的Address中获取到平台id,返回404"); | 85 | + if (requesterId == null || channelId == null) { |
| 86 | + logger.info("无法从FromHeader的Address中获取到平台id,返回400"); | ||
| 86 | responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误 | 87 | responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误 |
| 87 | return; | 88 | return; |
| 88 | } | 89 | } |
| 89 | - // 查询平台下是否有该通道 | ||
| 90 | - DeviceChannel channel = storager.queryChannelInParentPlatform(platformId, channelId); | ||
| 91 | - if (channel == null) { | ||
| 92 | - logger.info("通道不存在,返回404"); | ||
| 93 | - responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在 | ||
| 94 | - return; | ||
| 95 | - }else { | ||
| 96 | - responseAck(evt, Response.TRYING); // 通道存在,发100,trying | ||
| 97 | - } | ||
| 98 | - // 解析sdp消息, 使用jainsip 自带的sdp解析方式 | ||
| 99 | - String contentString = new String(request.getRawContent()); | ||
| 100 | - | ||
| 101 | - // jainSip不支持y=字段, 移除移除以解析。 | ||
| 102 | - int ssrcIndex = contentString.indexOf("y="); | ||
| 103 | - String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | ||
| 104 | - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 | ||
| 105 | - // String ssrc = contentString.substring(ssrcIndex + 2, contentString.length()) | ||
| 106 | - // .replace("\r\n", "").replace("\n", ""); | ||
| 107 | - | ||
| 108 | - String substring = contentString.substring(0, contentString.indexOf("y=")); | ||
| 109 | - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | ||
| 110 | - | ||
| 111 | - // 获取支持的格式 | ||
| 112 | - Vector mediaDescriptions = sdp.getMediaDescriptions(true); | ||
| 113 | - // 查看是否支持PS 负载96 | ||
| 114 | - //String ip = null; | ||
| 115 | - int port = -1; | ||
| 116 | - //boolean recvonly = false; | ||
| 117 | - boolean mediaTransmissionTCP = false; | ||
| 118 | - Boolean tcpActive = null; | ||
| 119 | - for (int i = 0; i < mediaDescriptions.size(); i++) { | ||
| 120 | - MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); | ||
| 121 | - Media media = mediaDescription.getMedia(); | ||
| 122 | - | ||
| 123 | - Vector mediaFormats = media.getMediaFormats(false); | ||
| 124 | - if (mediaFormats.contains("96")) { | ||
| 125 | - port = media.getMediaPort(); | ||
| 126 | - //String mediaType = media.getMediaType(); | ||
| 127 | - String protocol = media.getProtocol(); | ||
| 128 | - | ||
| 129 | - // 区分TCP发流还是udp, 当前默认udp | ||
| 130 | - if ("TCP/RTP/AVP".equals(protocol)) { | ||
| 131 | - String setup = mediaDescription.getAttribute("setup"); | ||
| 132 | - if (setup != null) { | ||
| 133 | - mediaTransmissionTCP = true; | ||
| 134 | - if ("active".equals(setup)) { | ||
| 135 | - tcpActive = true; | ||
| 136 | - }else if ("passive".equals(setup)) { | ||
| 137 | - tcpActive = false; | 90 | + |
| 91 | + // 查询请求方是否上级平台 | ||
| 92 | + ParentPlatform platform = storager.queryParentPlatById(requesterId); | ||
| 93 | + if (platform != null) { | ||
| 94 | + // 查询平台下是否有该通道 | ||
| 95 | + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); | ||
| 96 | + if (channel == null) { | ||
| 97 | + logger.info("通道不存在,返回404"); | ||
| 98 | + responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在 | ||
| 99 | + return; | ||
| 100 | + }else { | ||
| 101 | + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中 | ||
| 102 | + } | ||
| 103 | + // 解析sdp消息, 使用jainsip 自带的sdp解析方式 | ||
| 104 | + String contentString = new String(request.getRawContent()); | ||
| 105 | + | ||
| 106 | + // jainSip不支持y=字段, 移除移除以解析。 | ||
| 107 | + int ssrcIndex = contentString.indexOf("y="); | ||
| 108 | + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 | ||
| 109 | + String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | ||
| 110 | + String substring = contentString.substring(0, contentString.indexOf("y=")); | ||
| 111 | + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | ||
| 112 | + | ||
| 113 | + // 获取支持的格式 | ||
| 114 | + Vector mediaDescriptions = sdp.getMediaDescriptions(true); | ||
| 115 | + // 查看是否支持PS 负载96 | ||
| 116 | + //String ip = null; | ||
| 117 | + int port = -1; | ||
| 118 | + //boolean recvonly = false; | ||
| 119 | + boolean mediaTransmissionTCP = false; | ||
| 120 | + Boolean tcpActive = null; | ||
| 121 | + for (int i = 0; i < mediaDescriptions.size(); i++) { | ||
| 122 | + MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); | ||
| 123 | + Media media = mediaDescription.getMedia(); | ||
| 124 | + | ||
| 125 | + Vector mediaFormats = media.getMediaFormats(false); | ||
| 126 | + if (mediaFormats.contains("96")) { | ||
| 127 | + port = media.getMediaPort(); | ||
| 128 | + //String mediaType = media.getMediaType(); | ||
| 129 | + String protocol = media.getProtocol(); | ||
| 130 | + | ||
| 131 | + // 区分TCP发流还是udp, 当前默认udp | ||
| 132 | + if ("TCP/RTP/AVP".equals(protocol)) { | ||
| 133 | + String setup = mediaDescription.getAttribute("setup"); | ||
| 134 | + if (setup != null) { | ||
| 135 | + mediaTransmissionTCP = true; | ||
| 136 | + if ("active".equals(setup)) { | ||
| 137 | + tcpActive = true; | ||
| 138 | + }else if ("passive".equals(setup)) { | ||
| 139 | + tcpActive = false; | ||
| 140 | + } | ||
| 138 | } | 141 | } |
| 139 | } | 142 | } |
| 143 | + break; | ||
| 140 | } | 144 | } |
| 141 | - break; | ||
| 142 | } | 145 | } |
| 143 | - } | ||
| 144 | - if (port == -1) { | ||
| 145 | - logger.info("不支持的媒体格式,返回415"); | ||
| 146 | - // 回复不支持的格式 | ||
| 147 | - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 | ||
| 148 | - return; | ||
| 149 | - } | ||
| 150 | - String username = sdp.getOrigin().getUsername(); | ||
| 151 | - String addressStr = sdp.getOrigin().getAddress(); | ||
| 152 | - //String sessionName = sdp.getSessionName().getValue(); | ||
| 153 | - logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc); | ||
| 154 | - | ||
| 155 | - Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId); | ||
| 156 | - if (device == null) { | ||
| 157 | - logger.warn("点播平台{}的通道{}时未找到设备信息", platformId, channel); | ||
| 158 | - responseAck(evt, Response.SERVER_INTERNAL_ERROR); | ||
| 159 | - return; | ||
| 160 | - } | ||
| 161 | - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, platformId, device.getDeviceId(), channelId, | ||
| 162 | - mediaTransmissionTCP); | ||
| 163 | - if (tcpActive != null) { | ||
| 164 | - sendRtpItem.setTcpActive(tcpActive); | ||
| 165 | - } | ||
| 166 | - if (sendRtpItem == null) { | ||
| 167 | - logger.warn("服务器端口资源不足"); | ||
| 168 | - responseAck(evt, Response.BUSY_HERE); | ||
| 169 | - return; | ||
| 170 | - } | 146 | + if (port == -1) { |
| 147 | + logger.info("不支持的媒体格式,返回415"); | ||
| 148 | + // 回复不支持的格式 | ||
| 149 | + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 | ||
| 150 | + return; | ||
| 151 | + } | ||
| 152 | + String username = sdp.getOrigin().getUsername(); | ||
| 153 | + String addressStr = sdp.getOrigin().getAddress(); | ||
| 154 | + //String sessionName = sdp.getSessionName().getValue(); | ||
| 155 | + logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc); | ||
| 156 | + | ||
| 157 | + Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); | ||
| 158 | + if (device == null) { | ||
| 159 | + logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel); | ||
| 160 | + responseAck(evt, Response.SERVER_INTERNAL_ERROR); | ||
| 161 | + return; | ||
| 162 | + } | ||
| 163 | + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, requesterId, device.getDeviceId(), channelId, | ||
| 164 | + mediaTransmissionTCP); | ||
| 165 | + if (tcpActive != null) { | ||
| 166 | + sendRtpItem.setTcpActive(tcpActive); | ||
| 167 | + } | ||
| 168 | + if (sendRtpItem == null) { | ||
| 169 | + logger.warn("服务器端口资源不足"); | ||
| 170 | + responseAck(evt, Response.BUSY_HERE); | ||
| 171 | + return; | ||
| 172 | + } | ||
| 171 | 173 | ||
| 172 | - // 写入redis, 超时时回复 | ||
| 173 | - redisCatchStorage.updateSendRTPSever(sendRtpItem); | ||
| 174 | - // 通知下级推流, | ||
| 175 | - PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{ | ||
| 176 | - // 收到推流, 回复200OK, 等待ack | ||
| 177 | - sendRtpItem.setStatus(1); | 174 | + // 写入redis, 超时时回复 |
| 178 | redisCatchStorage.updateSendRTPSever(sendRtpItem); | 175 | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| 179 | - // TODO 添加对tcp的支持 | ||
| 180 | - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); | ||
| 181 | - StringBuffer content = new StringBuffer(200); | ||
| 182 | - content.append("v=0\r\n"); | ||
| 183 | - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 184 | - content.append("s=Play\r\n"); | ||
| 185 | - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 186 | - content.append("t=0 0\r\n"); | ||
| 187 | - content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); | ||
| 188 | - content.append("a=sendonly\r\n"); | ||
| 189 | - content.append("a=rtpmap:96 PS/90000\r\n"); | ||
| 190 | - content.append("y="+ ssrc + "\r\n"); | ||
| 191 | - content.append("f=\r\n"); | ||
| 192 | - | ||
| 193 | - try { | ||
| 194 | - responseAck(evt, content.toString()); | ||
| 195 | - } catch (SipException e) { | ||
| 196 | - e.printStackTrace(); | ||
| 197 | - } catch (InvalidArgumentException e) { | ||
| 198 | - e.printStackTrace(); | ||
| 199 | - } catch (ParseException e) { | ||
| 200 | - e.printStackTrace(); | 176 | + // 通知下级推流, |
| 177 | + PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{ | ||
| 178 | + // 收到推流, 回复200OK, 等待ack | ||
| 179 | + sendRtpItem.setStatus(1); | ||
| 180 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | ||
| 181 | + // TODO 添加对tcp的支持 | ||
| 182 | + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); | ||
| 183 | + StringBuffer content = new StringBuffer(200); | ||
| 184 | + content.append("v=0\r\n"); | ||
| 185 | + content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 186 | + content.append("s=Play\r\n"); | ||
| 187 | + content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | ||
| 188 | + content.append("t=0 0\r\n"); | ||
| 189 | + content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); | ||
| 190 | + content.append("a=sendonly\r\n"); | ||
| 191 | + content.append("a=rtpmap:96 PS/90000\r\n"); | ||
| 192 | + content.append("y="+ ssrc + "\r\n"); | ||
| 193 | + content.append("f=\r\n"); | ||
| 194 | + | ||
| 195 | + try { | ||
| 196 | + responseAck(evt, content.toString()); | ||
| 197 | + } catch (SipException e) { | ||
| 198 | + e.printStackTrace(); | ||
| 199 | + } catch (InvalidArgumentException e) { | ||
| 200 | + e.printStackTrace(); | ||
| 201 | + } catch (ParseException e) { | ||
| 202 | + e.printStackTrace(); | ||
| 203 | + } | ||
| 204 | + },(event -> { | ||
| 205 | + // 未知错误。直接转发设备点播的错误 | ||
| 206 | + Response response = null; | ||
| 207 | + try { | ||
| 208 | + response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest()); | ||
| 209 | + getServerTransaction(evt).sendResponse(response); | ||
| 210 | + } catch (ParseException | SipException | InvalidArgumentException e) { | ||
| 211 | + e.printStackTrace(); | ||
| 212 | + } | ||
| 213 | + })); | ||
| 214 | + if (logger.isDebugEnabled()) { | ||
| 215 | + logger.debug(playResult.getResult().toString()); | ||
| 201 | } | 216 | } |
| 202 | - },(event -> { | ||
| 203 | - // 未知错误。直接转发设备点播的错误 | ||
| 204 | - Response response = null; | ||
| 205 | - try { | ||
| 206 | - response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest()); | ||
| 207 | - getServerTransaction(evt).sendResponse(response); | ||
| 208 | - | ||
| 209 | - } catch (ParseException | SipException | InvalidArgumentException e) { | ||
| 210 | - e.printStackTrace(); | 217 | + } else { |
| 218 | + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) | ||
| 219 | + Device device = storager.queryVideoDevice(requesterId); | ||
| 220 | + if (device != null) { | ||
| 221 | + logger.info("收到设备" + requesterId + "的语音广播Invite请求"); | ||
| 222 | + responseAck(evt, Response.TRYING); | ||
| 223 | + | ||
| 224 | + String contentString = new String(request.getRawContent()); | ||
| 225 | + // jainSip不支持y=字段, 移除移除以解析。 | ||
| 226 | + String substring = contentString; | ||
| 227 | + String ssrc = "0000000404"; | ||
| 228 | + int ssrcIndex = contentString.indexOf("y="); | ||
| 229 | + if (ssrcIndex > 0) { | ||
| 230 | + substring = contentString.substring(0, ssrcIndex); | ||
| 231 | + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | ||
| 232 | + } | ||
| 233 | + ssrcIndex = substring.indexOf("f="); | ||
| 234 | + if (ssrcIndex > 0) { | ||
| 235 | + substring = contentString.substring(0, ssrcIndex); | ||
| 236 | + } | ||
| 237 | + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | ||
| 238 | + | ||
| 239 | + // 获取支持的格式 | ||
| 240 | + Vector mediaDescriptions = sdp.getMediaDescriptions(true); | ||
| 241 | + // 查看是否支持PS 负载96 | ||
| 242 | + int port = -1; | ||
| 243 | + //boolean recvonly = false; | ||
| 244 | + boolean mediaTransmissionTCP = false; | ||
| 245 | + Boolean tcpActive = null; | ||
| 246 | + for (int i = 0; i < mediaDescriptions.size(); i++) { | ||
| 247 | + MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); | ||
| 248 | + Media media = mediaDescription.getMedia(); | ||
| 249 | + | ||
| 250 | + Vector mediaFormats = media.getMediaFormats(false); | ||
| 251 | + if (mediaFormats.contains("8")) { | ||
| 252 | + port = media.getMediaPort(); | ||
| 253 | + String protocol = media.getProtocol(); | ||
| 254 | + // 区分TCP发流还是udp, 当前默认udp | ||
| 255 | + if ("TCP/RTP/AVP".equals(protocol)) { | ||
| 256 | + String setup = mediaDescription.getAttribute("setup"); | ||
| 257 | + if (setup != null) { | ||
| 258 | + mediaTransmissionTCP = true; | ||
| 259 | + if ("active".equals(setup)) { | ||
| 260 | + tcpActive = true; | ||
| 261 | + } else if ("passive".equals(setup)) { | ||
| 262 | + tcpActive = false; | ||
| 263 | + } | ||
| 264 | + } | ||
| 265 | + } | ||
| 266 | + break; | ||
| 267 | + } | ||
| 268 | + } | ||
| 269 | + if (port == -1) { | ||
| 270 | + logger.info("不支持的媒体格式,返回415"); | ||
| 271 | + // 回复不支持的格式 | ||
| 272 | + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 | ||
| 273 | + return; | ||
| 274 | + } | ||
| 275 | + String username = sdp.getOrigin().getUsername(); | ||
| 276 | + String addressStr = sdp.getOrigin().getAddress(); | ||
| 277 | + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); | ||
| 278 | + | ||
| 279 | + | ||
| 280 | + | ||
| 281 | + | ||
| 282 | + | ||
| 283 | + | ||
| 284 | + } else { | ||
| 285 | + logger.warn("来自无效设备/平台的请求"); | ||
| 286 | + responseAck(evt, Response.BAD_REQUEST); | ||
| 211 | } | 287 | } |
| 212 | - })); | ||
| 213 | - if (logger.isDebugEnabled()) { | ||
| 214 | - logger.debug(playResult.getResult().toString()); | ||
| 215 | } | 288 | } |
| 216 | 289 | ||
| 217 | } catch (SipException | InvalidArgumentException | ParseException e) { | 290 | } catch (SipException | InvalidArgumentException | ParseException e) { |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
| @@ -93,7 +93,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -93,7 +93,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 93 | private static final String MESSAGE_ALARM = "Alarm"; | 93 | private static final String MESSAGE_ALARM = "Alarm"; |
| 94 | private static final String MESSAGE_RECORD_INFO = "RecordInfo"; | 94 | private static final String MESSAGE_RECORD_INFO = "RecordInfo"; |
| 95 | private static final String MESSAGE_MEDIA_STATUS = "MediaStatus"; | 95 | private static final String MESSAGE_MEDIA_STATUS = "MediaStatus"; |
| 96 | - // private static final String MESSAGE_BROADCAST = "Broadcast"; | 96 | + private static final String MESSAGE_BROADCAST = "Broadcast"; |
| 97 | private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus"; | 97 | private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus"; |
| 98 | private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl"; | 98 | private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl"; |
| 99 | private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig"; | 99 | private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig"; |
| @@ -123,7 +123,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -123,7 +123,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 123 | logger.info("接收到Catalog消息"); | 123 | logger.info("接收到Catalog消息"); |
| 124 | processMessageCatalogList(evt); | 124 | processMessageCatalogList(evt); |
| 125 | } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { | 125 | } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { |
| 126 | - //DeviceInfo消息处理 | 126 | + // DeviceInfo消息处理 |
| 127 | processMessageDeviceInfo(evt); | 127 | processMessageDeviceInfo(evt); |
| 128 | } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { | 128 | } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { |
| 129 | // DeviceStatus消息处理 | 129 | // DeviceStatus消息处理 |
| @@ -149,6 +149,9 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -149,6 +149,9 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 149 | } else if (MESSAGE_PRESET_QUERY.equals(cmd)) { | 149 | } else if (MESSAGE_PRESET_QUERY.equals(cmd)) { |
| 150 | logger.info("接收到PresetQuery消息"); | 150 | logger.info("接收到PresetQuery消息"); |
| 151 | processMessagePresetQuery(evt); | 151 | processMessagePresetQuery(evt); |
| 152 | + } else if (MESSAGE_BROADCAST.equals(cmd)) { | ||
| 153 | + // Broadcast消息处理 | ||
| 154 | + processMessageBroadcast(evt); | ||
| 152 | } else { | 155 | } else { |
| 153 | logger.info("接收到消息:" + cmd); | 156 | logger.info("接收到消息:" + cmd); |
| 154 | responseAck(evt); | 157 | responseAck(evt); |
| @@ -298,7 +301,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -298,7 +301,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 298 | // 远程启动功能 | 301 | // 远程启动功能 |
| 299 | if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement, "TeleBoot"))) { | 302 | if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement, "TeleBoot"))) { |
| 300 | if (deviceId.equals(targetGBId)) { | 303 | if (deviceId.equals(targetGBId)) { |
| 301 | - // 远程启动功能:需要在重新启动程序后先对SipStack解绑 | 304 | + // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 |
| 302 | logger.info("执行远程启动本平台命令"); | 305 | logger.info("执行远程启动本平台命令"); |
| 303 | ParentPlatform parentPlatform = storager.queryParentPlatById(platformId); | 306 | ParentPlatform parentPlatform = storager.queryParentPlatById(platformId); |
| 304 | cmderFroPlatform.unregister(parentPlatform, null, null); | 307 | cmderFroPlatform.unregister(parentPlatform, null, null); |
| @@ -333,6 +336,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -333,6 +336,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 333 | // 远程启动指定设备 | 336 | // 远程启动指定设备 |
| 334 | } | 337 | } |
| 335 | } | 338 | } |
| 339 | + // 云台/前端控制命令 | ||
| 336 | if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) { | 340 | if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) { |
| 337 | String cmdString = XmlUtil.getText(rootElement,"PTZCmd"); | 341 | String cmdString = XmlUtil.getText(rootElement,"PTZCmd"); |
| 338 | Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId); | 342 | Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId); |
| @@ -895,6 +899,37 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -895,6 +899,37 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 895 | } | 899 | } |
| 896 | } | 900 | } |
| 897 | 901 | ||
| 902 | + /** | ||
| 903 | + * 处理AudioBroadcast语音广播Message | ||
| 904 | + * | ||
| 905 | + * @param evt | ||
| 906 | + */ | ||
| 907 | + private void processMessageBroadcast(RequestEvent evt) { | ||
| 908 | + try { | ||
| 909 | + Element rootElement = getRootElement(evt); | ||
| 910 | + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); | ||
| 911 | + // 回复200 OK | ||
| 912 | + responseAck(evt); | ||
| 913 | + if (rootElement.getName().equals("Response")) { | ||
| 914 | + // 此处是对本平台发出Broadcast指令的应答 | ||
| 915 | + JSONObject json = new JSONObject(); | ||
| 916 | + XmlUtil.node2Json(rootElement, json); | ||
| 917 | + if (logger.isDebugEnabled()) { | ||
| 918 | + logger.debug(json.toJSONString()); | ||
| 919 | + } | ||
| 920 | + RequestMessage msg = new RequestMessage(); | ||
| 921 | + msg.setDeviceId(deviceId); | ||
| 922 | + msg.setType(DeferredResultHolder.CALLBACK_CMD_BROADCAST); | ||
| 923 | + msg.setData(json); | ||
| 924 | + deferredResultHolder.invokeResult(msg); | ||
| 925 | + } else { | ||
| 926 | + // 此处是上级发出的Broadcast指令 | ||
| 927 | + } | ||
| 928 | + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { | ||
| 929 | + e.printStackTrace(); | ||
| 930 | + } | ||
| 931 | + } | ||
| 932 | + | ||
| 898 | 933 | ||
| 899 | /*** | 934 | /*** |
| 900 | * 回复200 OK | 935 | * 回复200 OK |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java
| @@ -50,7 +50,6 @@ public class RegisterResponseProcessor implements ISIPResponseProcessor { | @@ -50,7 +50,6 @@ public class RegisterResponseProcessor implements ISIPResponseProcessor { | ||
| 50 | */ | 50 | */ |
| 51 | @Override | 51 | @Override |
| 52 | public void process(ResponseEvent evt, SipLayer layer, SipConfig config) { | 52 | public void process(ResponseEvent evt, SipLayer layer, SipConfig config) { |
| 53 | - // TODO Auto-generated method stub | ||
| 54 | Response response = evt.getResponse(); | 53 | Response response = evt.getResponse(); |
| 55 | CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME); | 54 | CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME); |
| 56 | String callId = callIdHeader.getCallId(); | 55 | String callId = callIdHeader.getCallId(); |
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
| @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.play; | @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.play; | ||
| 2 | 2 | ||
| 3 | import com.genersoft.iot.vmp.common.StreamInfo; | 3 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 4 | import com.genersoft.iot.vmp.conf.MediaServerConfig; | 4 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 5 | +import com.genersoft.iot.vmp.gb28181.bean.Device; | ||
| 5 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | 6 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 6 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | 7 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 7 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | 8 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| @@ -27,6 +28,8 @@ import org.springframework.web.context.request.async.DeferredResult; | @@ -27,6 +28,8 @@ import org.springframework.web.context.request.async.DeferredResult; | ||
| 27 | 28 | ||
| 28 | import java.util.UUID; | 29 | import java.util.UUID; |
| 29 | 30 | ||
| 31 | +import javax.sip.message.Response; | ||
| 32 | + | ||
| 30 | @CrossOrigin | 33 | @CrossOrigin |
| 31 | @RestController | 34 | @RestController |
| 32 | @RequestMapping("/api") | 35 | @RequestMapping("/api") |
| @@ -204,5 +207,47 @@ public class PlayController { | @@ -204,5 +207,47 @@ public class PlayController { | ||
| 204 | } | 207 | } |
| 205 | return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK); | 208 | return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK); |
| 206 | } | 209 | } |
| 210 | + | ||
| 211 | + /** | ||
| 212 | + * 语音广播命令API接口 | ||
| 213 | + * | ||
| 214 | + * @param deviceId | ||
| 215 | + */ | ||
| 216 | + @GetMapping("/broadcast/{deviceId}") | ||
| 217 | + @PostMapping("/broadcast/{deviceId}") | ||
| 218 | + public DeferredResult<ResponseEntity<String>> broadcastApi(@PathVariable String deviceId) { | ||
| 219 | + if (logger.isDebugEnabled()) { | ||
| 220 | + logger.debug("语音广播API调用"); | ||
| 221 | + } | ||
| 222 | + Device device = storager.queryVideoDevice(deviceId); | ||
| 223 | + cmder.audioBroadcastCmd(device, event -> { | ||
| 224 | + Response response = event.getResponse(); | ||
| 225 | + RequestMessage msg = new RequestMessage(); | ||
| 226 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId); | ||
| 227 | + JSONObject json = new JSONObject(); | ||
| 228 | + json.put("DeviceID", deviceId); | ||
| 229 | + json.put("CmdType", "Broadcast"); | ||
| 230 | + json.put("Result", "Failed"); | ||
| 231 | + json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); | ||
| 232 | + msg.setData(json); | ||
| 233 | + resultHolder.invokeResult(msg); | ||
| 234 | + }); | ||
| 235 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); | ||
| 236 | + result.onTimeout(() -> { | ||
| 237 | + logger.warn(String.format("语音广播操作超时, 设备未返回应答指令")); | ||
| 238 | + RequestMessage msg = new RequestMessage(); | ||
| 239 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId); | ||
| 240 | + JSONObject json = new JSONObject(); | ||
| 241 | + json.put("DeviceID", deviceId); | ||
| 242 | + json.put("CmdType", "Broadcast"); | ||
| 243 | + json.put("Result", "Failed"); | ||
| 244 | + json.put("Error", "Timeout. Device did not response to broadcast command."); | ||
| 245 | + msg.setData(json); | ||
| 246 | + resultHolder.invokeResult(msg); | ||
| 247 | + }); | ||
| 248 | + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId, result); | ||
| 249 | + return result; | ||
| 250 | + } | ||
| 251 | + | ||
| 207 | } | 252 | } |
| 208 | 253 |