Commit c2aaae9325db012c9960b69784330ced5ec15ab9
1 parent
9f0ef439
初步实现语音喊话
Showing
20 changed files
with
761 additions
and
179 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.gb28181.bean; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * 缓存语音广播的状态 | ||
| 6 | + * @author lin | ||
| 7 | + */ | ||
| 8 | +public class AudioBroadcastCatch { | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + public AudioBroadcastCatch(String deviceId, String channelId, AudioBroadcastCatchStatus status) { | ||
| 12 | + this.deviceId = deviceId; | ||
| 13 | + this.channelId = channelId; | ||
| 14 | + this.status = status; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + public AudioBroadcastCatch() { | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 设备编号 | ||
| 22 | + */ | ||
| 23 | + private String deviceId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 通道编号 | ||
| 27 | + */ | ||
| 28 | + private String channelId; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 语音广播状态 | ||
| 32 | + */ | ||
| 33 | + private AudioBroadcastCatchStatus status; | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + public String getDeviceId() { | ||
| 37 | + return deviceId; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + public void setDeviceId(String deviceId) { | ||
| 41 | + this.deviceId = deviceId; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + public String getChannelId() { | ||
| 45 | + return channelId; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + public void setChannelId(String channelId) { | ||
| 49 | + this.channelId = channelId; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + public AudioBroadcastCatchStatus getStatus() { | ||
| 53 | + return status; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public void setStatus(AudioBroadcastCatchStatus status) { | ||
| 57 | + this.status = status; | ||
| 58 | + } | ||
| 59 | +} |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatchStatus.java
0 → 100644
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
| @@ -134,16 +134,6 @@ public class Device { | @@ -134,16 +134,6 @@ public class Device { | ||
| 134 | */ | 134 | */ |
| 135 | private boolean ssrcCheck; | 135 | private boolean ssrcCheck; |
| 136 | 136 | ||
| 137 | - /** | ||
| 138 | - * 设备用于接收语音消息的通道 | ||
| 139 | - */ | ||
| 140 | - private String audioChannelForReceive; | ||
| 141 | - | ||
| 142 | - /** | ||
| 143 | - * 设备用于发送语音消息的通道 | ||
| 144 | - */ | ||
| 145 | - private String audioChannelForSend; | ||
| 146 | - | ||
| 147 | 137 | ||
| 148 | public String getDeviceId() { | 138 | public String getDeviceId() { |
| 149 | return deviceId; | 139 | return deviceId; |
| @@ -345,11 +335,4 @@ public class Device { | @@ -345,11 +335,4 @@ public class Device { | ||
| 345 | this.ssrcCheck = ssrcCheck; | 335 | this.ssrcCheck = ssrcCheck; |
| 346 | } | 336 | } |
| 347 | 337 | ||
| 348 | - public String getAudioChannelForReceive() { | ||
| 349 | - return audioChannelForReceive; | ||
| 350 | - } | ||
| 351 | - | ||
| 352 | - public void setAudioChannelForReceive(String audioChannelForReceive) { | ||
| 353 | - this.audioChannelForReceive = audioChannelForReceive; | ||
| 354 | - } | ||
| 355 | } | 338 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.gb28181.session; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; | ||
| 4 | +import org.springframework.stereotype.Component; | ||
| 5 | + | ||
| 6 | +import java.util.ArrayList; | ||
| 7 | +import java.util.Collection; | ||
| 8 | +import java.util.List; | ||
| 9 | +import java.util.Map; | ||
| 10 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 语音广播消息管理类 | ||
| 14 | + * @author lin | ||
| 15 | + */ | ||
| 16 | +@Component | ||
| 17 | +public class AudioBroadcastManager { | ||
| 18 | + | ||
| 19 | + public static Map<String, AudioBroadcastCatch> data = new ConcurrentHashMap<>(); | ||
| 20 | + | ||
| 21 | + public void add(AudioBroadcastCatch audioBroadcastCatch) { | ||
| 22 | + this.update(audioBroadcastCatch); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public void update(AudioBroadcastCatch audioBroadcastCatch) { | ||
| 26 | + data.put(audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(), audioBroadcastCatch); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public void del(String deviceId, String channelId) { | ||
| 30 | + data.remove(deviceId + channelId); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public void delByDeviceId(String deviceId) { | ||
| 34 | + for (String key : data.keySet()) { | ||
| 35 | + if (key.startsWith(deviceId)) { | ||
| 36 | + data.remove(key); | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public List<AudioBroadcastCatch> getAll(){ | ||
| 42 | + Collection<AudioBroadcastCatch> values = data.values(); | ||
| 43 | + return new ArrayList<>(values); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + public boolean exit(String deviceId, String channelId) { | ||
| 48 | + for (String key : data.keySet()) { | ||
| 49 | + if (key.equals(deviceId + channelId)) { | ||
| 50 | + return true; | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + return false; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public AudioBroadcastCatch get(String deviceId, String channelId) { | ||
| 57 | + return data.get(deviceId + channelId); | ||
| 58 | + } | ||
| 59 | +} |
src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
| @@ -21,9 +21,6 @@ public class CatalogDataCatch { | @@ -21,9 +21,6 @@ public class CatalogDataCatch { | ||
| 21 | public static Map<String, CatalogData> data = new ConcurrentHashMap<>(); | 21 | public static Map<String, CatalogData> data = new ConcurrentHashMap<>(); |
| 22 | 22 | ||
| 23 | @Autowired | 23 | @Autowired |
| 24 | - private DeferredResultHolder deferredResultHolder; | ||
| 25 | - | ||
| 26 | - @Autowired | ||
| 27 | private IVideoManagerStorage storager; | 24 | private IVideoManagerStorage storager; |
| 28 | 25 | ||
| 29 | public void addReady(Device device, int sn ) { | 26 | public void addReady(Device device, int sn ) { |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| @@ -6,8 +6,12 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | @@ -6,8 +6,12 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | ||
| 6 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | 6 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 7 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 7 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 8 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; | 8 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 9 | +import gov.nist.javax.sip.message.SIPRequest; | ||
| 10 | +import gov.nist.javax.sip.stack.SIPDialog; | ||
| 9 | 11 | ||
| 10 | import javax.sip.Dialog; | 12 | import javax.sip.Dialog; |
| 13 | +import javax.sip.SipException; | ||
| 14 | +import java.text.ParseException; | ||
| 11 | 15 | ||
| 12 | /** | 16 | /** |
| 13 | * @description:设备能力接口,用于定义设备的控制、查询能力 | 17 | * @description:设备能力接口,用于定义设备的控制、查询能力 |
| @@ -123,6 +127,7 @@ public interface ISIPCommander { | @@ -123,6 +127,7 @@ public interface ISIPCommander { | ||
| 123 | */ | 127 | */ |
| 124 | void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent); | 128 | void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent); |
| 125 | void streamByeCmd(String deviceId, String channelId, String stream, String callId); | 129 | void streamByeCmd(String deviceId, String channelId, String stream, String callId); |
| 130 | + void streamByeCmd(SIPDialog dialog, SIPRequest request, SipSubscribe.Event okEvent) throws SipException, ParseException; | ||
| 126 | 131 | ||
| 127 | /** | 132 | /** |
| 128 | * 回放暂停 | 133 | * 回放暂停 |
| @@ -144,21 +149,13 @@ public interface ISIPCommander { | @@ -144,21 +149,13 @@ public interface ISIPCommander { | ||
| 144 | */ | 149 | */ |
| 145 | void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed); | 150 | void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed); |
| 146 | 151 | ||
| 147 | - /** | ||
| 148 | - * 语音广播 | ||
| 149 | - * | ||
| 150 | - * @param device 视频设备 | ||
| 151 | - * @param channelId 预览通道 | ||
| 152 | - */ | ||
| 153 | - boolean audioBroadcastCmd(Device device,String channelId); | ||
| 154 | 152 | ||
| 155 | /** | 153 | /** |
| 156 | * 语音广播 | 154 | * 语音广播 |
| 157 | * | 155 | * |
| 158 | * @param device 视频设备 | 156 | * @param device 视频设备 |
| 159 | */ | 157 | */ |
| 160 | - void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); | ||
| 161 | - boolean audioBroadcastCmd(Device device); | 158 | + boolean audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent); |
| 162 | 159 | ||
| 163 | /** | 160 | /** |
| 164 | * 音视频录像控制 | 161 | * 音视频录像控制 |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| @@ -733,42 +733,34 @@ public class SIPCommander implements ISIPCommander { | @@ -733,42 +733,34 @@ public class SIPCommander implements ISIPCommander { | ||
| 733 | } | 733 | } |
| 734 | } | 734 | } |
| 735 | 735 | ||
| 736 | - Request byeRequest = dialog.createRequest(Request.BYE); | ||
| 737 | - SipURI byeURI = (SipURI) byeRequest.getRequestURI(); | ||
| 738 | - SIPRequest request = (SIPRequest)transaction.getRequest(); | ||
| 739 | - byeURI.setHost(request.getRemoteAddress().getHostAddress()); | ||
| 740 | - byeURI.setPort(request.getRemotePort()); | ||
| 741 | - ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME); | ||
| 742 | - String protocol = viaHeader.getTransport().toUpperCase(); | ||
| 743 | - ClientTransaction clientTransaction = null; | ||
| 744 | - if("TCP".equals(protocol)) { | ||
| 745 | - clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest); | ||
| 746 | - } else if("UDP".equals(protocol)) { | ||
| 747 | - clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); | ||
| 748 | - } | ||
| 749 | - | ||
| 750 | - CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME); | ||
| 751 | - if (okEvent != null) { | ||
| 752 | - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); | ||
| 753 | - } | ||
| 754 | - | ||
| 755 | - dialog.sendRequest(clientTransaction); | 736 | + streamByeCmd(dialog, (SIPRequest)transaction.getRequest(), okEvent); |
| 756 | 737 | ||
| 757 | } catch (SipException | ParseException e) { | 738 | } catch (SipException | ParseException e) { |
| 758 | e.printStackTrace(); | 739 | e.printStackTrace(); |
| 759 | } | 740 | } |
| 760 | } | 741 | } |
| 761 | 742 | ||
| 762 | - /** | ||
| 763 | - * 语音广播 | ||
| 764 | - * | ||
| 765 | - * @param device 视频设备 | ||
| 766 | - * @param channelId 预览通道 | ||
| 767 | - */ | ||
| 768 | @Override | 743 | @Override |
| 769 | - public boolean audioBroadcastCmd(Device device, String channelId) { | ||
| 770 | - // 改为新的实现 | ||
| 771 | - return false; | 744 | + public void streamByeCmd(SIPDialog dialog, SIPRequest request, SipSubscribe.Event okEvent) throws SipException, ParseException { |
| 745 | + Request byeRequest = dialog.createRequest(Request.BYE); | ||
| 746 | + SipURI byeURI = (SipURI) byeRequest.getRequestURI(); | ||
| 747 | + byeURI.setHost(request.getRemoteAddress().getHostAddress()); | ||
| 748 | + byeURI.setPort(request.getRemotePort()); | ||
| 749 | + ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME); | ||
| 750 | + String protocol = viaHeader.getTransport().toUpperCase(); | ||
| 751 | + ClientTransaction clientTransaction = null; | ||
| 752 | + if("TCP".equals(protocol)) { | ||
| 753 | + clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest); | ||
| 754 | + } else if("UDP".equals(protocol)) { | ||
| 755 | + clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); | ||
| 756 | + } | ||
| 757 | + | ||
| 758 | + CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME); | ||
| 759 | + if (okEvent != null) { | ||
| 760 | + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); | ||
| 761 | + } | ||
| 762 | + | ||
| 763 | + dialog.sendRequest(clientTransaction); | ||
| 772 | } | 764 | } |
| 773 | 765 | ||
| 774 | /** | 766 | /** |
| @@ -777,7 +769,7 @@ public class SIPCommander implements ISIPCommander { | @@ -777,7 +769,7 @@ public class SIPCommander implements ISIPCommander { | ||
| 777 | * @param device 视频设备 | 769 | * @param device 视频设备 |
| 778 | */ | 770 | */ |
| 779 | @Override | 771 | @Override |
| 780 | - public boolean audioBroadcastCmd(Device device) { | 772 | + public boolean audioBroadcastCmd(Device device,String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) { |
| 781 | try { | 773 | try { |
| 782 | StringBuffer broadcastXml = new StringBuffer(200); | 774 | StringBuffer broadcastXml = new StringBuffer(200); |
| 783 | String charset = device.getCharset(); | 775 | String charset = device.getCharset(); |
| @@ -786,7 +778,7 @@ public class SIPCommander implements ISIPCommander { | @@ -786,7 +778,7 @@ public class SIPCommander implements ISIPCommander { | ||
| 786 | broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); | 778 | broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); |
| 787 | broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | 779 | broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); |
| 788 | broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); | 780 | broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); |
| 789 | - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); | 781 | + broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n"); |
| 790 | broadcastXml.append("</Notify>\r\n"); | 782 | broadcastXml.append("</Notify>\r\n"); |
| 791 | 783 | ||
| 792 | String tm = Long.toString(System.currentTimeMillis()); | 784 | String tm = Long.toString(System.currentTimeMillis()); |
| @@ -795,39 +787,14 @@ public class SIPCommander implements ISIPCommander { | @@ -795,39 +787,14 @@ public class SIPCommander implements ISIPCommander { | ||
| 795 | : udpSipProvider.getNewCallId(); | 787 | : udpSipProvider.getNewCallId(); |
| 796 | 788 | ||
| 797 | Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); | 789 | Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); |
| 798 | - transmitRequest(device, request); | 790 | + transmitRequest(device, request, errorEvent, okEvent); |
| 799 | return true; | 791 | return true; |
| 800 | } catch (SipException | ParseException | InvalidArgumentException e) { | 792 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| 801 | e.printStackTrace(); | 793 | e.printStackTrace(); |
| 802 | } | 794 | } |
| 803 | return false; | 795 | return false; |
| 804 | } | 796 | } |
| 805 | - @Override | ||
| 806 | - public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) { | ||
| 807 | - try { | ||
| 808 | - StringBuffer broadcastXml = new StringBuffer(200); | ||
| 809 | - String charset = device.getCharset(); | ||
| 810 | - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); | ||
| 811 | - broadcastXml.append("<Notify>\r\n"); | ||
| 812 | - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); | ||
| 813 | - broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | ||
| 814 | - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); | ||
| 815 | - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); | ||
| 816 | - broadcastXml.append("</Notify>\r\n"); | ||
| 817 | - | ||
| 818 | - String tm = Long.toString(System.currentTimeMillis()); | ||
| 819 | 797 | ||
| 820 | - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() | ||
| 821 | - : udpSipProvider.getNewCallId(); | ||
| 822 | - | ||
| 823 | - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); | ||
| 824 | - transmitRequest(device, request, errorEvent); | ||
| 825 | - } catch (SipException | ParseException | InvalidArgumentException e) { | ||
| 826 | - e.printStackTrace(); | ||
| 827 | - } | ||
| 828 | - } | ||
| 829 | - | ||
| 830 | - | ||
| 831 | /** | 798 | /** |
| 832 | * 音视频录像控制 | 799 | * 音视频录像控制 |
| 833 | * | 800 | * |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
| @@ -94,6 +94,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | @@ -94,6 +94,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In | ||
| 94 | param.put("dst_port", sendRtpItem.getPort()); | 94 | param.put("dst_port", sendRtpItem.getPort()); |
| 95 | param.put("is_udp", is_Udp); | 95 | param.put("is_udp", is_Udp); |
| 96 | param.put("src_port", sendRtpItem.getLocalPort()); | 96 | param.put("src_port", sendRtpItem.getLocalPort()); |
| 97 | + param.put("pt", 8); | ||
| 98 | + param.put("use_ps", 0); | ||
| 99 | + param.put("only_audio", 1); | ||
| 97 | zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); | 100 | zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); |
| 98 | 101 | ||
| 99 | 102 |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| @@ -2,21 +2,27 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | @@ -2,21 +2,27 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; | ||
| 2 | 2 | ||
| 3 | import com.alibaba.fastjson.JSONObject; | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | import com.genersoft.iot.vmp.conf.DynamicTask; | 4 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 5 | +import com.genersoft.iot.vmp.conf.SipConfig; | ||
| 5 | import com.genersoft.iot.vmp.conf.UserSetting; | 6 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 6 | import com.genersoft.iot.vmp.gb28181.bean.*; | 7 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 7 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | 8 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 9 | +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | ||
| 8 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | 10 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 9 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; | 11 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 12 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | ||
| 13 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | ||
| 10 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; | 14 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| 11 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | 15 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 12 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | 16 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| 13 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; | 17 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| 14 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; | 18 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 15 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; | 19 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| 20 | +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; | ||
| 16 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | 21 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 17 | import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; | 22 | import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; |
| 18 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | 23 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 19 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 24 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 25 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItemLite; | ||
| 20 | import com.genersoft.iot.vmp.service.IMediaServerService; | 26 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 21 | import com.genersoft.iot.vmp.service.IPlayService; | 27 | import com.genersoft.iot.vmp.service.IPlayService; |
| 22 | import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; | 28 | import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; |
| @@ -24,8 +30,12 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; | @@ -24,8 +30,12 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; | ||
| 24 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 30 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 25 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | 31 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 26 | import com.genersoft.iot.vmp.utils.SerializeUtils; | 32 | import com.genersoft.iot.vmp.utils.SerializeUtils; |
| 33 | +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | ||
| 34 | +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | ||
| 27 | import gov.nist.javax.sdp.TimeDescriptionImpl; | 35 | import gov.nist.javax.sdp.TimeDescriptionImpl; |
| 28 | import gov.nist.javax.sdp.fields.TimeField; | 36 | import gov.nist.javax.sdp.fields.TimeField; |
| 37 | +import gov.nist.javax.sip.message.SIPRequest; | ||
| 38 | +import gov.nist.javax.sip.stack.SIPDialog; | ||
| 29 | import org.slf4j.Logger; | 39 | import org.slf4j.Logger; |
| 30 | import org.slf4j.LoggerFactory; | 40 | import org.slf4j.LoggerFactory; |
| 31 | import org.springframework.beans.factory.InitializingBean; | 41 | import org.springframework.beans.factory.InitializingBean; |
| @@ -41,6 +51,7 @@ import javax.sip.message.Response; | @@ -41,6 +51,7 @@ import javax.sip.message.Response; | ||
| 41 | import java.text.ParseException; | 51 | import java.text.ParseException; |
| 42 | import java.text.SimpleDateFormat; | 52 | import java.text.SimpleDateFormat; |
| 43 | import java.util.Date; | 53 | import java.util.Date; |
| 54 | +import java.util.List; | ||
| 44 | import java.util.Vector; | 55 | import java.util.Vector; |
| 45 | 56 | ||
| 46 | /** | 57 | /** |
| @@ -73,7 +84,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -73,7 +84,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 73 | private IPlayService playService; | 84 | private IPlayService playService; |
| 74 | 85 | ||
| 75 | @Autowired | 86 | @Autowired |
| 76 | - private ISIPCommander commander; | 87 | + private AudioBroadcastManager audioBroadcastManager; |
| 77 | 88 | ||
| 78 | @Autowired | 89 | @Autowired |
| 79 | private ZLMRTPServerFactory zlmrtpServerFactory; | 90 | private ZLMRTPServerFactory zlmrtpServerFactory; |
| @@ -93,6 +104,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -93,6 +104,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 93 | @Autowired | 104 | @Autowired |
| 94 | private ZLMMediaListManager mediaListManager; | 105 | private ZLMMediaListManager mediaListManager; |
| 95 | 106 | ||
| 107 | + @Autowired | ||
| 108 | + private DeferredResultHolder resultHolder; | ||
| 109 | + | ||
| 110 | + @Autowired | ||
| 111 | + private ZLMHttpHookSubscribe subscribe; | ||
| 112 | + | ||
| 113 | + @Autowired | ||
| 114 | + private SipConfig config; | ||
| 115 | + | ||
| 96 | 116 | ||
| 97 | @Override | 117 | @Override |
| 98 | public void afterPropertiesSet() throws Exception { | 118 | public void afterPropertiesSet() throws Exception { |
| @@ -126,7 +146,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -126,7 +146,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 126 | // 查询请求是否来自上级平台\设备 | 146 | // 查询请求是否来自上级平台\设备 |
| 127 | ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); | 147 | ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); |
| 128 | if (platform == null) { | 148 | if (platform == null) { |
| 129 | - inviteFromDeviceHandle(evt, requesterId); | 149 | + inviteFromDeviceHandle(evt, requesterId, channelId); |
| 130 | }else { | 150 | }else { |
| 131 | // 查询平台下是否有该通道 | 151 | // 查询平台下是否有该通道 |
| 132 | DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); | 152 | DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); |
| @@ -542,10 +562,25 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -542,10 +562,25 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 542 | } | 562 | } |
| 543 | } | 563 | } |
| 544 | 564 | ||
| 545 | - public void inviteFromDeviceHandle(RequestEvent evt, String requesterId) throws InvalidArgumentException, ParseException, SipException, SdpException { | ||
| 546 | - | 565 | + public void inviteFromDeviceHandle(RequestEvent evt, String requesterId, String channelId) throws InvalidArgumentException, ParseException, SipException, SdpException { |
| 566 | + | ||
| 567 | + // 兼容奇葩的海康这里使用的不是通道编号而是本平台编号 | ||
| 568 | +// if (channelId.equals(config.getId())) { | ||
| 569 | +// List<AudioBroadcastCatch> all = audioBroadcastManager.getAll(); | ||
| 570 | +// for (AudioBroadcastCatch audioBroadcastCatch : all) { | ||
| 571 | +// if (audioBroadcastCatch.getDeviceId().equals(requesterId)) { | ||
| 572 | +// channelId = audioBroadcastCatch.getChannelId(); | ||
| 573 | +// } | ||
| 574 | +// } | ||
| 575 | +// } | ||
| 576 | +// // 兼容失败 | ||
| 577 | +// if (channelId.equals(config.getId())) { | ||
| 578 | +// responseAck(evt, Response.BAD_REQUEST); | ||
| 579 | +// return; | ||
| 580 | +// } | ||
| 547 | // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) | 581 | // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) |
| 548 | Device device = redisCatchStorage.getDevice(requesterId); | 582 | Device device = redisCatchStorage.getDevice(requesterId); |
| 583 | + | ||
| 549 | Request request = evt.getRequest(); | 584 | Request request = evt.getRequest(); |
| 550 | if (device != null) { | 585 | if (device != null) { |
| 551 | logger.info("收到设备" + requesterId + "的语音广播Invite请求"); | 586 | logger.info("收到设备" + requesterId + "的语音广播Invite请求"); |
| @@ -558,7 +593,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -558,7 +593,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 558 | int ssrcIndex = contentString.indexOf("y="); | 593 | int ssrcIndex = contentString.indexOf("y="); |
| 559 | if (ssrcIndex > 0) { | 594 | if (ssrcIndex > 0) { |
| 560 | substring = contentString.substring(0, ssrcIndex); | 595 | substring = contentString.substring(0, ssrcIndex); |
| 561 | - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | 596 | + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); |
| 562 | } | 597 | } |
| 563 | ssrcIndex = substring.indexOf("f="); | 598 | ssrcIndex = substring.indexOf("f="); |
| 564 | if (ssrcIndex > 0) { | 599 | if (ssrcIndex > 0) { |
| @@ -568,6 +603,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -568,6 +603,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 568 | 603 | ||
| 569 | // 获取支持的格式 | 604 | // 获取支持的格式 |
| 570 | Vector mediaDescriptions = sdp.getMediaDescriptions(true); | 605 | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| 606 | + | ||
| 571 | // 查看是否支持PS 负载96 | 607 | // 查看是否支持PS 负载96 |
| 572 | int port = -1; | 608 | int port = -1; |
| 573 | //boolean recvonly = false; | 609 | //boolean recvonly = false; |
| @@ -602,10 +638,150 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -602,10 +638,150 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 602 | responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 | 638 | responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 |
| 603 | return; | 639 | return; |
| 604 | } | 640 | } |
| 605 | - String username = sdp.getOrigin().getUsername(); | 641 | + String sessionName = sdp.getSessionName().getValue(); |
| 606 | String addressStr = sdp.getOrigin().getAddress(); | 642 | String addressStr = sdp.getOrigin().getAddress(); |
| 607 | - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); | 643 | + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", requesterId, addressStr, port, ssrc); |
| 644 | + | ||
| 645 | + MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); | ||
| 646 | + if (mediaServerItem == null) { | ||
| 647 | + logger.warn("未找到可用的zlm"); | ||
| 648 | + responseAck(evt, Response.BUSY_HERE); | ||
| 649 | + return; | ||
| 650 | + } | ||
| 651 | + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, | ||
| 652 | + device.getDeviceId(), channelId, | ||
| 653 | + mediaTransmissionTCP); | ||
| 654 | + sendRtpItem.setTcp(mediaTransmissionTCP); | ||
| 655 | + if (tcpActive != null) { | ||
| 656 | + sendRtpItem.setTcpActive(tcpActive); | ||
| 657 | + } | ||
| 658 | + if (sendRtpItem == null) { | ||
| 659 | + logger.warn("服务器端口资源不足"); | ||
| 660 | + responseAck(evt, Response.BUSY_HERE); | ||
| 661 | + return; | ||
| 662 | + } | ||
| 663 | + | ||
| 664 | + String app = "broadcast"; | ||
| 665 | + String stream = device.getDeviceId() + "_" + channelId; | ||
| 666 | + | ||
| 667 | + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); | ||
| 668 | + sendRtpItem.setPlayType(InviteStreamType.PLAY); | ||
| 669 | + sendRtpItem.setCallId(callIdHeader.getCallId()); | ||
| 670 | + sendRtpItem.setPlatformId(requesterId); | ||
| 671 | + sendRtpItem.setStatus(1); | ||
| 672 | + sendRtpItem.setApp(app); | ||
| 673 | + sendRtpItem.setStreamId(stream); | ||
| 674 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | ||
| 675 | + | ||
| 676 | + // hook监听等待设备推流上来 | ||
| 677 | + // 添加订阅 | ||
| 678 | + JSONObject subscribeKey = new JSONObject(); | ||
| 679 | + subscribeKey.put("app", app); | ||
| 680 | + subscribeKey.put("stream", stream); | ||
| 681 | + subscribeKey.put("regist", true); | ||
| 682 | + subscribeKey.put("schema", "rtmp"); | ||
| 683 | + subscribeKey.put("mediaServerId", mediaServerItem.getId()); | ||
| 684 | + String finalSsrc = ssrc; | ||
| 685 | + String waiteStreamTimeoutTaskKey = "waite-stream-" + device.getDeviceId() + channelId; | ||
| 686 | + if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) { | ||
| 687 | + logger.info("发现已经在推流"); | ||
| 688 | + dynamicTask.stop(waiteStreamTimeoutTaskKey); | ||
| 689 | + sendRtpItem.setStatus(2); | ||
| 690 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | ||
| 691 | + StringBuffer content = new StringBuffer(200); | ||
| 692 | + content.append("v=0\r\n"); | ||
| 693 | + content.append("o="+ config.getId() +" "+ sdp.getOrigin().getSessionId() +" " + sdp.getOrigin().getSessionVersion() + " IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | ||
| 694 | + content.append("s=Play\r\n"); | ||
| 695 | + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | ||
| 696 | + content.append("t=0 0\r\n"); | ||
| 697 | + content.append("m=audio "+ sendRtpItem.getLocalPort()+" RTP/AVP 8\r\n"); | ||
| 698 | + content.append("a=sendonly\r\n"); | ||
| 699 | + content.append("a=rtpmap:8 PCMA/8000\r\n"); | ||
| 700 | + content.append("y="+ finalSsrc + "\r\n"); | ||
| 701 | + content.append("f=v/////a/1/8/1\r\n"); | ||
| 702 | + | ||
| 703 | + ParentPlatform parentPlatform = new ParentPlatform(); | ||
| 704 | + parentPlatform.setServerIP(device.getIp()); | ||
| 705 | + parentPlatform.setServerPort(device.getPort()); | ||
| 706 | + parentPlatform.setServerGBId(device.getDeviceId()); | ||
| 707 | + try { | ||
| 708 | + responseSdpAck(evt, content.toString(), parentPlatform); | ||
| 709 | + } catch (SipException e) { | ||
| 710 | + throw new RuntimeException(e); | ||
| 711 | + } catch (InvalidArgumentException e) { | ||
| 712 | + throw new RuntimeException(e); | ||
| 713 | + } catch (ParseException e) { | ||
| 714 | + throw new RuntimeException(e); | ||
| 715 | + } | ||
| 716 | + }else { | ||
| 717 | + // 设置等待推流的超时; 默认20s | ||
| 718 | + String finalChannelId = channelId; | ||
| 719 | + dynamicTask.startDelay(waiteStreamTimeoutTaskKey, ()->{ | ||
| 720 | + logger.info("等待推流超时: {}/{}", app, stream); | ||
| 721 | + if (audioBroadcastManager.exit(device.getDeviceId(), finalChannelId)) { | ||
| 722 | + audioBroadcastManager.del(device.getDeviceId(), finalChannelId); | ||
| 723 | + }else { | ||
| 724 | + // 兼容海康使用了错误的通道ID的情况 | ||
| 725 | + audioBroadcastManager.delByDeviceId(device.getDeviceId()); | ||
| 726 | + } | ||
| 608 | 727 | ||
| 728 | + // 发送bye | ||
| 729 | + try { | ||
| 730 | + cmder.streamByeCmd((SIPDialog)evt.getServerTransaction().getDialog(), (SIPRequest) evt.getRequest(), null); | ||
| 731 | + } catch (SipException e) { | ||
| 732 | + throw new RuntimeException(e); | ||
| 733 | + } catch (ParseException e) { | ||
| 734 | + throw new RuntimeException(e); | ||
| 735 | + } | ||
| 736 | + }, 20*1000); | ||
| 737 | + | ||
| 738 | + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, | ||
| 739 | + (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | ||
| 740 | + sendRtpItem.setStatus(2); | ||
| 741 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | ||
| 742 | + StringBuffer content = new StringBuffer(200); | ||
| 743 | + content.append("v=0\r\n"); | ||
| 744 | + content.append("o="+ finalChannelId +" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | ||
| 745 | + content.append("s=Play\r\n"); | ||
| 746 | + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | ||
| 747 | + content.append("t=0 0\r\n"); | ||
| 748 | + content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 8\r\n"); | ||
| 749 | + content.append("a=sendonly\r\n"); | ||
| 750 | + content.append("a=rtpmap:8 PCMA/8000\r\n"); | ||
| 751 | + content.append("y="+ finalSsrc + "\r\n"); | ||
| 752 | + content.append("f=v/////a/1/8/1\r\n"); | ||
| 753 | + | ||
| 754 | + ParentPlatform parentPlatform = new ParentPlatform(); | ||
| 755 | + parentPlatform.setServerIP(device.getIp()); | ||
| 756 | + parentPlatform.setServerPort(device.getPort()); | ||
| 757 | + parentPlatform.setServerGBId(device.getDeviceId()); | ||
| 758 | + try { | ||
| 759 | + responseSdpAck(evt, content.toString(), parentPlatform); | ||
| 760 | + } catch (SipException e) { | ||
| 761 | + throw new RuntimeException(e); | ||
| 762 | + } catch (InvalidArgumentException e) { | ||
| 763 | + throw new RuntimeException(e); | ||
| 764 | + } catch (ParseException e) { | ||
| 765 | + throw new RuntimeException(e); | ||
| 766 | + } | ||
| 767 | + }); | ||
| 768 | + } | ||
| 769 | + String timeOutTaskKey = "audio-broadcast-" + device.getDeviceId() + channelId; | ||
| 770 | + dynamicTask.stop(timeOutTaskKey); | ||
| 771 | + String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId(); | ||
| 772 | + WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | ||
| 773 | + wvpResult.setCode(0); | ||
| 774 | + wvpResult.setMsg("success"); | ||
| 775 | + AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult(); | ||
| 776 | + audioBroadcastResult.setApp(app); | ||
| 777 | + audioBroadcastResult.setStream(stream); | ||
| 778 | + audioBroadcastResult.setMediaServerItem(new MediaServerItemLite(mediaServerItem)); | ||
| 779 | + audioBroadcastResult.setCodec("G.711"); | ||
| 780 | + wvpResult.setData(audioBroadcastResult); | ||
| 781 | + RequestMessage requestMessage = new RequestMessage(); | ||
| 782 | + requestMessage.setKey(key); | ||
| 783 | + requestMessage.setData(wvpResult); | ||
| 784 | + resultHolder.invokeAllResult(requestMessage); | ||
| 609 | } else { | 785 | } else { |
| 610 | logger.warn("来自无效设备/平台的请求"); | 786 | logger.warn("来自无效设备/平台的请求"); |
| 611 | responseAck(evt, Response.BAD_REQUEST); | 787 | responseAck(evt, Response.BAD_REQUEST); |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java
| @@ -6,7 +6,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP | @@ -6,7 +6,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP | ||
| 6 | import org.dom4j.Element; | 6 | import org.dom4j.Element; |
| 7 | import org.springframework.beans.factory.annotation.Autowired; | 7 | import org.springframework.beans.factory.annotation.Autowired; |
| 8 | 8 | ||
| 9 | +import javax.sip.InvalidArgumentException; | ||
| 9 | import javax.sip.RequestEvent; | 10 | import javax.sip.RequestEvent; |
| 11 | +import javax.sip.SipException; | ||
| 12 | +import javax.sip.message.Response; | ||
| 13 | +import java.text.ParseException; | ||
| 10 | import java.util.Map; | 14 | import java.util.Map; |
| 11 | import java.util.concurrent.ConcurrentHashMap; | 15 | import java.util.concurrent.ConcurrentHashMap; |
| 12 | 16 | ||
| @@ -23,6 +27,10 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i | @@ -23,6 +27,10 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i | ||
| 23 | @Override | 27 | @Override |
| 24 | public void handForDevice(RequestEvent evt, Device device, Element element) { | 28 | public void handForDevice(RequestEvent evt, Device device, Element element) { |
| 25 | String cmd = getText(element, "CmdType"); | 29 | String cmd = getText(element, "CmdType"); |
| 30 | + if (cmd == null) { | ||
| 31 | + handNullCmd(evt); | ||
| 32 | + return; | ||
| 33 | + } | ||
| 26 | IMessageHandler messageHandler = messageHandlerMap.get(cmd); | 34 | IMessageHandler messageHandler = messageHandlerMap.get(cmd); |
| 27 | if (messageHandler != null) { | 35 | if (messageHandler != null) { |
| 28 | messageHandler.handForDevice(evt, device, element); | 36 | messageHandler.handForDevice(evt, device, element); |
| @@ -37,4 +45,17 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i | @@ -37,4 +45,17 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i | ||
| 37 | messageHandler.handForPlatform(evt, parentPlatform, element); | 45 | messageHandler.handForPlatform(evt, parentPlatform, element); |
| 38 | } | 46 | } |
| 39 | } | 47 | } |
| 48 | + | ||
| 49 | + public void handNullCmd(RequestEvent evt){ | ||
| 50 | + try { | ||
| 51 | + responseAck(evt, Response.OK); | ||
| 52 | + } catch (SipException e) { | ||
| 53 | + throw new RuntimeException(e); | ||
| 54 | + } catch (InvalidArgumentException e) { | ||
| 55 | + throw new RuntimeException(e); | ||
| 56 | + } catch (ParseException e) { | ||
| 57 | + throw new RuntimeException(e); | ||
| 58 | + } | ||
| 59 | + return; | ||
| 60 | + } | ||
| 40 | } | 61 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
| 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; |
| 2 | 2 | ||
| 3 | import com.alibaba.fastjson.JSONObject; | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | +import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; | ||
| 5 | +import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatchStatus; | ||
| 4 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 6 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 5 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | 7 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 8 | +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | ||
| 6 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | 9 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 7 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | 10 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 8 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; | 11 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| @@ -36,6 +39,9 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | @@ -36,6 +39,9 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | ||
| 36 | @Autowired | 39 | @Autowired |
| 37 | private DeferredResultHolder deferredResultHolder; | 40 | private DeferredResultHolder deferredResultHolder; |
| 38 | 41 | ||
| 42 | + @Autowired | ||
| 43 | + private AudioBroadcastManager audioBroadcastManager; | ||
| 44 | + | ||
| 39 | @Override | 45 | @Override |
| 40 | public void afterPropertiesSet() throws Exception { | 46 | public void afterPropertiesSet() throws Exception { |
| 41 | responseMessageHandler.addHandler(cmdType, this); | 47 | responseMessageHandler.addHandler(cmdType, this); |
| @@ -45,21 +51,16 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | @@ -45,21 +51,16 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i | ||
| 45 | public void handForDevice(RequestEvent evt, Device device, Element rootElement) { | 51 | public void handForDevice(RequestEvent evt, Device device, Element rootElement) { |
| 46 | try { | 52 | try { |
| 47 | String channelId = getText(rootElement, "DeviceID"); | 53 | String channelId = getText(rootElement, "DeviceID"); |
| 48 | - String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId; | ||
| 49 | - // 回复200 OK | ||
| 50 | - responseAck(evt, Response.OK); | ||
| 51 | - // 此处是对本平台发出Broadcast指令的应答 | ||
| 52 | - JSONObject json = new JSONObject(); | ||
| 53 | - XmlUtil.node2Json(rootElement, json); | ||
| 54 | - if (logger.isDebugEnabled()) { | ||
| 55 | - logger.debug(json.toJSONString()); | 54 | + if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) { |
| 55 | + // 回复410 | ||
| 56 | + responseAck(evt, Response.GONE); | ||
| 57 | + return; | ||
| 56 | } | 58 | } |
| 57 | - RequestMessage msg = new RequestMessage(); | ||
| 58 | - msg.setKey(key); | ||
| 59 | - msg.setData(json); | ||
| 60 | - deferredResultHolder.invokeAllResult(msg); | ||
| 61 | - | ||
| 62 | - | 59 | + logger.info("收到语音广播的回复:{}/{}", device.getDeviceId(), channelId ); |
| 60 | + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId); | ||
| 61 | + audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite); | ||
| 62 | + audioBroadcastManager.update(audioBroadcastCatch); | ||
| 63 | + responseAck(evt, Response.OK); | ||
| 63 | } catch (ParseException | SipException | InvalidArgumentException e) { | 64 | } catch (ParseException | SipException | InvalidArgumentException e) { |
| 64 | e.printStackTrace(); | 65 | e.printStackTrace(); |
| 65 | } | 66 | } |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
| @@ -271,7 +271,7 @@ public class ZLMRTPServerFactory { | @@ -271,7 +271,7 @@ public class ZLMRTPServerFactory { | ||
| 271 | * 查询待转推的流是否就绪 | 271 | * 查询待转推的流是否就绪 |
| 272 | */ | 272 | */ |
| 273 | public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { | 273 | public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { |
| 274 | - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId); | 274 | + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId); |
| 275 | return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); | 275 | return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); |
| 276 | } | 276 | } |
| 277 | 277 |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItemLite.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; | ||
| 5 | +import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; | ||
| 6 | +import org.springframework.util.StringUtils; | ||
| 7 | + | ||
| 8 | +import java.util.HashMap; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 精简的MediaServerItem信息,方便给前端返回数据 | ||
| 12 | + */ | ||
| 13 | +public class MediaServerItemLite { | ||
| 14 | + | ||
| 15 | + private String id; | ||
| 16 | + | ||
| 17 | + private String ip; | ||
| 18 | + | ||
| 19 | + private String hookIp; | ||
| 20 | + | ||
| 21 | + private String sdpIp; | ||
| 22 | + | ||
| 23 | + private String streamIp; | ||
| 24 | + | ||
| 25 | + private int httpPort; | ||
| 26 | + | ||
| 27 | + private int httpSSlPort; | ||
| 28 | + | ||
| 29 | + private int rtmpPort; | ||
| 30 | + | ||
| 31 | + private int rtmpSSlPort; | ||
| 32 | + | ||
| 33 | + private int rtpProxyPort; | ||
| 34 | + | ||
| 35 | + private int rtspPort; | ||
| 36 | + | ||
| 37 | + private int rtspSSLPort; | ||
| 38 | + | ||
| 39 | + private String secret; | ||
| 40 | + | ||
| 41 | + private int streamNoneReaderDelayMS; | ||
| 42 | + | ||
| 43 | + private int hookAliveInterval; | ||
| 44 | + | ||
| 45 | + private int recordAssistPort; | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + | ||
| 49 | + public MediaServerItemLite(MediaServerItem mediaServerItem) { | ||
| 50 | + this.id = mediaServerItem.getId(); | ||
| 51 | + this.ip = mediaServerItem.getIp(); | ||
| 52 | + this.hookIp = mediaServerItem.getHookIp(); | ||
| 53 | + this.sdpIp = mediaServerItem.getSdpIp(); | ||
| 54 | + this.streamIp = mediaServerItem.getStreamIp(); | ||
| 55 | + this.httpPort = mediaServerItem.getHttpPort(); | ||
| 56 | + this.httpSSlPort = mediaServerItem.getHttpSSlPort(); | ||
| 57 | + this.rtmpPort = mediaServerItem.getRtmpPort(); | ||
| 58 | + this.rtmpSSlPort = mediaServerItem.getRtmpSSlPort(); | ||
| 59 | + this.rtpProxyPort = mediaServerItem.getRtpProxyPort(); | ||
| 60 | + this.rtspPort = mediaServerItem.getRtspPort(); | ||
| 61 | + this.rtspSSLPort = mediaServerItem.getRtspSSLPort(); | ||
| 62 | + this.secret = mediaServerItem.getSecret(); | ||
| 63 | + this.streamNoneReaderDelayMS = mediaServerItem.getStreamNoneReaderDelayMS(); | ||
| 64 | + this.hookAliveInterval = mediaServerItem.getHookAliveInterval(); | ||
| 65 | + this.streamNoneReaderDelayMS = mediaServerItem.getStreamNoneReaderDelayMS(); | ||
| 66 | + this.recordAssistPort = mediaServerItem.getRecordAssistPort(); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + public String getId() { | ||
| 70 | + return id; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + public void setId(String id) { | ||
| 74 | + this.id = id; | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + public String getIp() { | ||
| 78 | + return ip; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + public void setIp(String ip) { | ||
| 82 | + this.ip = ip; | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + public String getHookIp() { | ||
| 86 | + return hookIp; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + public void setHookIp(String hookIp) { | ||
| 90 | + this.hookIp = hookIp; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + public String getSdpIp() { | ||
| 94 | + return sdpIp; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + public void setSdpIp(String sdpIp) { | ||
| 98 | + this.sdpIp = sdpIp; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + public String getStreamIp() { | ||
| 102 | + return streamIp; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + public void setStreamIp(String streamIp) { | ||
| 106 | + this.streamIp = streamIp; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + public int getHttpPort() { | ||
| 110 | + return httpPort; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + public void setHttpPort(int httpPort) { | ||
| 114 | + this.httpPort = httpPort; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + public int getHttpSSlPort() { | ||
| 118 | + return httpSSlPort; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + public void setHttpSSlPort(int httpSSlPort) { | ||
| 122 | + this.httpSSlPort = httpSSlPort; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + public int getRtmpPort() { | ||
| 126 | + return rtmpPort; | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + public void setRtmpPort(int rtmpPort) { | ||
| 130 | + this.rtmpPort = rtmpPort; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + public int getRtmpSSlPort() { | ||
| 134 | + return rtmpSSlPort; | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + public void setRtmpSSlPort(int rtmpSSlPort) { | ||
| 138 | + this.rtmpSSlPort = rtmpSSlPort; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + public int getRtpProxyPort() { | ||
| 142 | + return rtpProxyPort; | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + public void setRtpProxyPort(int rtpProxyPort) { | ||
| 146 | + this.rtpProxyPort = rtpProxyPort; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + public int getRtspPort() { | ||
| 150 | + return rtspPort; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + public void setRtspPort(int rtspPort) { | ||
| 154 | + this.rtspPort = rtspPort; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + public int getRtspSSLPort() { | ||
| 158 | + return rtspSSLPort; | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + public void setRtspSSLPort(int rtspSSLPort) { | ||
| 162 | + this.rtspSSLPort = rtspSSLPort; | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + | ||
| 166 | + public String getSecret() { | ||
| 167 | + return secret; | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + public void setSecret(String secret) { | ||
| 171 | + this.secret = secret; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + public int getStreamNoneReaderDelayMS() { | ||
| 175 | + return streamNoneReaderDelayMS; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + public void setStreamNoneReaderDelayMS(int streamNoneReaderDelayMS) { | ||
| 179 | + this.streamNoneReaderDelayMS = streamNoneReaderDelayMS; | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + public int getHookAliveInterval() { | ||
| 183 | + return hookAliveInterval; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + public void setHookAliveInterval(int hookAliveInterval) { | ||
| 187 | + this.hookAliveInterval = hookAliveInterval; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + public int getRecordAssistPort() { | ||
| 191 | + return recordAssistPort; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + public void setRecordAssistPort(int recordAssistPort) { | ||
| 195 | + this.recordAssistPort = recordAssistPort; | ||
| 196 | + } | ||
| 197 | +} |
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
| @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 11 | import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; | 11 | import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; |
| 12 | import com.genersoft.iot.vmp.service.bean.PlayBackCallback; | 12 | import com.genersoft.iot.vmp.service.bean.PlayBackCallback; |
| 13 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; | 13 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 14 | +import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; | ||
| 14 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; | 15 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 15 | import org.springframework.http.ResponseEntity; | 16 | import org.springframework.http.ResponseEntity; |
| 16 | import org.springframework.web.context.request.async.DeferredResult; | 17 | import org.springframework.web.context.request.async.DeferredResult; |
| @@ -40,4 +41,6 @@ public interface IPlayService { | @@ -40,4 +41,6 @@ public interface IPlayService { | ||
| 40 | DeferredResult<ResponseEntity<String>> download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack); | 41 | DeferredResult<ResponseEntity<String>> download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack); |
| 41 | 42 | ||
| 42 | StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); | 43 | StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); |
| 44 | + | ||
| 45 | + void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event); | ||
| 43 | } | 46 | } |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask; | @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask; | ||
| 8 | import com.genersoft.iot.vmp.conf.UserSetting; | 8 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 9 | import com.genersoft.iot.vmp.gb28181.bean.*; | 9 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 10 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | 10 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 11 | +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | ||
| 11 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | 12 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 12 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | 13 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 13 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | 14 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| @@ -26,7 +27,9 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; | @@ -26,7 +27,9 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo; | ||
| 26 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 27 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 27 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | 28 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 28 | import com.genersoft.iot.vmp.utils.redis.RedisUtil; | 29 | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| 30 | +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | ||
| 29 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 31 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 32 | +import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; | ||
| 30 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; | 33 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 31 | import com.genersoft.iot.vmp.service.IMediaService; | 34 | import com.genersoft.iot.vmp.service.IMediaService; |
| 32 | import com.genersoft.iot.vmp.service.IPlayService; | 35 | import com.genersoft.iot.vmp.service.IPlayService; |
| @@ -58,6 +61,9 @@ public class PlayServiceImpl implements IPlayService { | @@ -58,6 +61,9 @@ public class PlayServiceImpl implements IPlayService { | ||
| 58 | private SIPCommander cmder; | 61 | private SIPCommander cmder; |
| 59 | 62 | ||
| 60 | @Autowired | 63 | @Autowired |
| 64 | + private AudioBroadcastManager audioBroadcastManager; | ||
| 65 | + | ||
| 66 | + @Autowired | ||
| 61 | private SIPCommanderFroPlatform sipCommanderFroPlatform; | 67 | private SIPCommanderFroPlatform sipCommanderFroPlatform; |
| 62 | 68 | ||
| 63 | @Autowired | 69 | @Autowired |
| @@ -621,4 +627,42 @@ public class PlayServiceImpl implements IPlayService { | @@ -621,4 +627,42 @@ public class PlayServiceImpl implements IPlayService { | ||
| 621 | } | 627 | } |
| 622 | } | 628 | } |
| 623 | } | 629 | } |
| 630 | + | ||
| 631 | + @Override | ||
| 632 | + public void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event) { | ||
| 633 | + if (device == null || channelId == null) { | ||
| 634 | + return; | ||
| 635 | + } | ||
| 636 | + DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); | ||
| 637 | + if (deviceChannel == null) { | ||
| 638 | + logger.warn("开启语音广播的时候未找到通道: {}", channelId); | ||
| 639 | + event.call("开启语音广播的时候未找到通道"); | ||
| 640 | + return; | ||
| 641 | + } | ||
| 642 | + // 查询通道使用状态 | ||
| 643 | + if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { | ||
| 644 | + logger.warn("语音广播已经开启: {}", channelId); | ||
| 645 | + event.call("语音广播已经开启"); | ||
| 646 | + return; | ||
| 647 | + } | ||
| 648 | + String timeOutTaskKey = "audio-broadcast-" + device.getDeviceId() + channelId; | ||
| 649 | + dynamicTask.startDelay(timeOutTaskKey, ()->{ | ||
| 650 | + logger.error("语音广播发送超时: {}:{}", device.getDeviceId(), channelId); | ||
| 651 | + event.call("语音广播发送超时"); | ||
| 652 | + audioBroadcastManager.del(device.getDeviceId(), channelId); | ||
| 653 | + }, timeout * 1000); | ||
| 654 | + | ||
| 655 | + // 发送通知 | ||
| 656 | + cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> { | ||
| 657 | + // 发送成功 | ||
| 658 | + AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, AudioBroadcastCatchStatus.Ready); | ||
| 659 | + audioBroadcastManager.add(audioBroadcastCatch); | ||
| 660 | + }, eventResultForError -> { | ||
| 661 | + dynamicTask.stop(timeOutTaskKey); | ||
| 662 | + // 发送失败 | ||
| 663 | + logger.error("语音广播发送失败: {}:{}", channelId, eventResultForError.msg); | ||
| 664 | + event.call("语音广播发送失败"); | ||
| 665 | + audioBroadcastManager.del(device.getDeviceId(), channelId); | ||
| 666 | + }); | ||
| 667 | + } | ||
| 624 | } | 668 | } |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
| @@ -37,8 +37,6 @@ public interface DeviceMapper { | @@ -37,8 +37,6 @@ public interface DeviceMapper { | ||
| 37 | "subscribeCycleForMobilePosition," + | 37 | "subscribeCycleForMobilePosition," + |
| 38 | "mobilePositionSubmissionInterval," + | 38 | "mobilePositionSubmissionInterval," + |
| 39 | "subscribeCycleForAlarm," + | 39 | "subscribeCycleForAlarm," + |
| 40 | - "audioChannelForReceive," + | ||
| 41 | - "audioChannelForSend," + | ||
| 42 | "ssrcCheck," + | 40 | "ssrcCheck," + |
| 43 | "online" + | 41 | "online" + |
| 44 | ") VALUES (" + | 42 | ") VALUES (" + |
| @@ -62,8 +60,6 @@ public interface DeviceMapper { | @@ -62,8 +60,6 @@ public interface DeviceMapper { | ||
| 62 | "#{subscribeCycleForMobilePosition}," + | 60 | "#{subscribeCycleForMobilePosition}," + |
| 63 | "#{mobilePositionSubmissionInterval}," + | 61 | "#{mobilePositionSubmissionInterval}," + |
| 64 | "#{subscribeCycleForAlarm}," + | 62 | "#{subscribeCycleForAlarm}," + |
| 65 | - "#{audioChannelForReceive}," + | ||
| 66 | - "#{audioChannelForSend}," + | ||
| 67 | "#{ssrcCheck}," + | 63 | "#{ssrcCheck}," + |
| 68 | "#{online}" + | 64 | "#{online}" + |
| 69 | ")") | 65 | ")") |
| @@ -90,8 +86,6 @@ public interface DeviceMapper { | @@ -90,8 +86,6 @@ public interface DeviceMapper { | ||
| 90 | "<if test=\"subscribeCycleForMobilePosition != null\">, subscribeCycleForMobilePosition=${subscribeCycleForMobilePosition}</if>" + | 86 | "<if test=\"subscribeCycleForMobilePosition != null\">, subscribeCycleForMobilePosition=${subscribeCycleForMobilePosition}</if>" + |
| 91 | "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" + | 87 | "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" + |
| 92 | "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" + | 88 | "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" + |
| 93 | - "<if test=\"audioChannelForReceive != null\">, audioChannelForReceive=#{audioChannelForReceive}</if>" + | ||
| 94 | - "<if test=\"audioChannelForSend != null\">, audioChannelForSend=#{audioChannelForSend}</if>" + | ||
| 95 | "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" + | 89 | "<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" + |
| 96 | "WHERE deviceId='${deviceId}'"+ | 90 | "WHERE deviceId='${deviceId}'"+ |
| 97 | " </script>"}) | 91 | " </script>"}) |
src/main/java/com/genersoft/iot/vmp/vmanager/bean/AudioBroadcastResult.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.bean; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 4 | +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItemLite; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @author lin | ||
| 8 | + */ | ||
| 9 | +public class AudioBroadcastResult { | ||
| 10 | + /** | ||
| 11 | + * 推流的媒体节点信息 | ||
| 12 | + */ | ||
| 13 | + private MediaServerItemLite mediaServerItem; | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 编码格式 | ||
| 17 | + */ | ||
| 18 | + private String codec; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 向zlm推流的应用名 | ||
| 22 | + */ | ||
| 23 | + private String app; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 向zlm推流的流ID | ||
| 27 | + */ | ||
| 28 | + private String stream; | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + public MediaServerItemLite getMediaServerItem() { | ||
| 32 | + return mediaServerItem; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public void setMediaServerItem(MediaServerItemLite mediaServerItem) { | ||
| 36 | + this.mediaServerItem = mediaServerItem; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public String getCodec() { | ||
| 40 | + return codec; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public void setCodec(String codec) { | ||
| 44 | + this.codec = codec; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public String getApp() { | ||
| 48 | + return app; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public void setApp(String app) { | ||
| 52 | + this.app = app; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public String getStream() { | ||
| 56 | + return stream; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public void setStream(String stream) { | ||
| 60 | + this.stream = stream; | ||
| 61 | + } | ||
| 62 | +} |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
| @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | ||
| 11 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 11 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 12 | import com.genersoft.iot.vmp.service.IMediaServerService; | 12 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 13 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 13 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 14 | +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | ||
| 14 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 15 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 15 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; | 16 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 16 | import com.genersoft.iot.vmp.service.IMediaService; | 17 | import com.genersoft.iot.vmp.service.IMediaService; |
| @@ -39,6 +40,9 @@ import org.springframework.web.context.request.async.DeferredResult; | @@ -39,6 +40,9 @@ import org.springframework.web.context.request.async.DeferredResult; | ||
| 39 | import java.util.List; | 40 | import java.util.List; |
| 40 | import java.util.UUID; | 41 | import java.util.UUID; |
| 41 | 42 | ||
| 43 | +/** | ||
| 44 | + * @author lin | ||
| 45 | + */ | ||
| 42 | @Api(tags = "国标设备点播") | 46 | @Api(tags = "国标设备点播") |
| 43 | @CrossOrigin | 47 | @CrossOrigin |
| 44 | @RestController | 48 | @RestController |
| @@ -102,7 +106,7 @@ public class PlayController { | @@ -102,7 +106,7 @@ public class PlayController { | ||
| 102 | logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); | 106 | logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); |
| 103 | 107 | ||
| 104 | String uuid = UUID.randomUUID().toString(); | 108 | String uuid = UUID.randomUUID().toString(); |
| 105 | - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); | 109 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(); |
| 106 | 110 | ||
| 107 | // 录像查询以channelId作为deviceId查询 | 111 | // 录像查询以channelId作为deviceId查询 |
| 108 | String key = DeferredResultHolder.CALLBACK_CMD_STOP + deviceId + channelId; | 112 | String key = DeferredResultHolder.CALLBACK_CMD_STOP + deviceId + channelId; |
| @@ -123,7 +127,7 @@ public class PlayController { | @@ -123,7 +127,7 @@ public class PlayController { | ||
| 123 | RequestMessage msgForSuccess = new RequestMessage(); | 127 | RequestMessage msgForSuccess = new RequestMessage(); |
| 124 | msgForSuccess.setId(uuid); | 128 | msgForSuccess.setId(uuid); |
| 125 | msgForSuccess.setKey(key); | 129 | msgForSuccess.setKey(key); |
| 126 | - msgForSuccess.setData(String.format("success")); | 130 | + msgForSuccess.setData("success"); |
| 127 | resultHolder.invokeAllResult(msgForSuccess); | 131 | resultHolder.invokeAllResult(msgForSuccess); |
| 128 | }); | 132 | }); |
| 129 | 133 | ||
| @@ -251,81 +255,73 @@ public class PlayController { | @@ -251,81 +255,73 @@ public class PlayController { | ||
| 251 | @ApiOperation("语音广播命令") | 255 | @ApiOperation("语音广播命令") |
| 252 | @ApiImplicitParams({ | 256 | @ApiImplicitParams({ |
| 253 | @ApiImplicitParam(name = "deviceId", value = "设备Id", dataTypeClass = String.class), | 257 | @ApiImplicitParam(name = "deviceId", value = "设备Id", dataTypeClass = String.class), |
| 254 | - @ApiImplicitParam(name = "channelForSend", value = "设备用于发送语音数据的通道", dataTypeClass = String.class), | ||
| 255 | - @ApiImplicitParam(name = "channelForReceive", value = "设备用于接收语音数据的通道", dataTypeClass = String.class), | 258 | + @ApiImplicitParam(name = "channelId", value = "通道Id", dataTypeClass = String.class), |
| 259 | + @ApiImplicitParam(name = "timeout", value = "推流超时时间(秒)", dataTypeClass = Integer.class), | ||
| 256 | }) | 260 | }) |
| 257 | - @GetMapping("/broadcast/{deviceId}") | ||
| 258 | - @PostMapping("/broadcast/{deviceId}") | ||
| 259 | - public DeferredResult<ResponseEntity<String>> broadcastApi(@PathVariable String deviceId, | ||
| 260 | - String channelForSend, | ||
| 261 | - String channelForReceive) { | 261 | + @GetMapping("/broadcast/{deviceId}/{channelId}") |
| 262 | + @PostMapping("/broadcast/{deviceId}/{channelId}") | ||
| 263 | + public DeferredResult<WVPResult<AudioBroadcastResult>> broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout) { | ||
| 262 | if (logger.isDebugEnabled()) { | 264 | if (logger.isDebugEnabled()) { |
| 263 | logger.debug("语音广播API调用"); | 265 | logger.debug("语音广播API调用"); |
| 264 | } | 266 | } |
| 265 | Device device = storager.queryVideoDevice(deviceId); | 267 | Device device = storager.queryVideoDevice(deviceId); |
| 266 | - DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(3 * 1000L); | ||
| 267 | - String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId; | ||
| 268 | - if (resultHolder.exist(key, null)) { | ||
| 269 | - result.setResult(new ResponseEntity<>("设备使用中",HttpStatus.OK)); | ||
| 270 | - return result; | 268 | + if (device == null) { |
| 269 | + WVPResult<AudioBroadcastResult> result = new WVPResult<>(); | ||
| 270 | + result.setCode(-1); | ||
| 271 | + result.setMsg("未找到设备: " + deviceId); | ||
| 272 | + DeferredResult<WVPResult<AudioBroadcastResult>> deferredResult = new DeferredResult<>(); | ||
| 273 | + deferredResult.setResult(result); | ||
| 274 | + return deferredResult; | ||
| 275 | + } | ||
| 276 | + if (channelId == null) { | ||
| 277 | + WVPResult<AudioBroadcastResult> result = new WVPResult<>(); | ||
| 278 | + result.setCode(-1); | ||
| 279 | + result.setMsg("未找到通道: " + channelId); | ||
| 280 | + DeferredResult<WVPResult<AudioBroadcastResult>> deferredResult = new DeferredResult<>(); | ||
| 281 | + deferredResult.setResult(result); | ||
| 282 | + return deferredResult; | ||
| 271 | } | 283 | } |
| 272 | 284 | ||
| 273 | -// playService.audioBroadcast(deviceId, channelForSend, channelForReceive); | ||
| 274 | - | ||
| 275 | - | ||
| 276 | - | ||
| 277 | - | ||
| 278 | - | ||
| 279 | - | ||
| 280 | - String uuid = UUID.randomUUID().toString(); | ||
| 281 | - if (device == null) { | ||
| 282 | - | ||
| 283 | - resultHolder.put(key, key, result); | ||
| 284 | - RequestMessage msg = new RequestMessage(); | ||
| 285 | - msg.setKey(key); | ||
| 286 | - msg.setId(uuid); | ||
| 287 | - JSONObject json = new JSONObject(); | ||
| 288 | - json.put("DeviceID", deviceId); | ||
| 289 | - json.put("CmdType", "Broadcast"); | ||
| 290 | - json.put("Result", "Failed"); | ||
| 291 | - json.put("Description", "Device 不存在"); | ||
| 292 | - msg.setData(json); | ||
| 293 | - resultHolder.invokeResult(msg); | ||
| 294 | - return result; | 285 | + String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId; |
| 286 | + if (resultHolder.exist(key, null)) { | ||
| 287 | + WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | ||
| 288 | + wvpResult.setCode(-1); | ||
| 289 | + wvpResult.setMsg("设备使用中"); | ||
| 290 | + DeferredResult<WVPResult<AudioBroadcastResult>> deferredResult = new DeferredResult<>(); | ||
| 291 | + deferredResult.setResult(wvpResult); | ||
| 292 | + return deferredResult; | ||
| 295 | } | 293 | } |
| 296 | - cmder.audioBroadcastCmd(device, (event) -> { | ||
| 297 | - RequestMessage msg = new RequestMessage(); | ||
| 298 | - msg.setKey(key); | ||
| 299 | - msg.setId(uuid); | ||
| 300 | - JSONObject json = new JSONObject(); | ||
| 301 | - json.put("DeviceID", deviceId); | ||
| 302 | - json.put("CmdType", "Broadcast"); | ||
| 303 | - json.put("Result", "Failed"); | ||
| 304 | - json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", event.statusCode, event.msg)); | ||
| 305 | - msg.setData(json); | ||
| 306 | - resultHolder.invokeResult(msg); | 294 | + if (timeout == null){ |
| 295 | + timeout = 30; | ||
| 296 | + } | ||
| 297 | + DeferredResult<WVPResult<AudioBroadcastResult>> result = new DeferredResult<>(timeout.longValue()*1000 + 2000); | ||
| 298 | + String uuid = UUID.randomUUID().toString(); | ||
| 299 | + result.onTimeout(()->{ | ||
| 300 | + WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | ||
| 301 | + wvpResult.setCode(-1); | ||
| 302 | + wvpResult.setMsg("请求超时"); | ||
| 303 | + RequestMessage requestMessage = new RequestMessage(); | ||
| 304 | + requestMessage.setKey(key); | ||
| 305 | + requestMessage.setData(wvpResult); | ||
| 306 | + resultHolder.invokeAllResult(requestMessage); | ||
| 307 | }); | 307 | }); |
| 308 | - | ||
| 309 | - result.onTimeout(() -> { | ||
| 310 | - logger.warn(String.format("语音广播操作超时, 设备未返回应答指令")); | ||
| 311 | - RequestMessage msg = new RequestMessage(); | ||
| 312 | - msg.setKey(key); | ||
| 313 | - msg.setId(uuid); | ||
| 314 | - JSONObject json = new JSONObject(); | ||
| 315 | - json.put("DeviceID", deviceId); | ||
| 316 | - json.put("CmdType", "Broadcast"); | ||
| 317 | - json.put("Result", "Failed"); | ||
| 318 | - json.put("Error", "Timeout. Device did not response to broadcast command."); | ||
| 319 | - msg.setData(json); | ||
| 320 | - resultHolder.invokeResult(msg); | 308 | + playService.audioBroadcast(device, channelId, timeout, (msg)->{ |
| 309 | + WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | ||
| 310 | + wvpResult.setCode(-1); | ||
| 311 | + wvpResult.setMsg(msg); | ||
| 312 | + RequestMessage requestMessage = new RequestMessage(); | ||
| 313 | + requestMessage.setKey(key); | ||
| 314 | + requestMessage.setData(wvpResult); | ||
| 315 | + resultHolder.invokeAllResult(requestMessage); | ||
| 321 | }); | 316 | }); |
| 322 | resultHolder.put(key, uuid, result); | 317 | resultHolder.put(key, uuid, result); |
| 318 | + | ||
| 323 | return result; | 319 | return result; |
| 324 | } | 320 | } |
| 325 | 321 | ||
| 326 | @ApiOperation("获取所有的ssrc") | 322 | @ApiOperation("获取所有的ssrc") |
| 327 | @GetMapping("/ssrc") | 323 | @GetMapping("/ssrc") |
| 328 | - public WVPResult<JSONObject> getSSRC() { | 324 | + public WVPResult<JSONObject> getSsrc() { |
| 329 | if (logger.isDebugEnabled()) { | 325 | if (logger.isDebugEnabled()) { |
| 330 | logger.debug("获取所有的ssrc"); | 326 | logger.debug("获取所有的ssrc"); |
| 331 | } | 327 | } |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/AudioBroadcastEvent.java
0 → 100644
web_src/src/components/dialog/deviceEdit.vue
| @@ -37,9 +37,6 @@ | @@ -37,9 +37,6 @@ | ||
| 37 | </el-select> | 37 | </el-select> |
| 38 | </el-form-item> | 38 | </el-form-item> |
| 39 | <el-form-item label="语音发送通道" prop="name"> | 39 | <el-form-item label="语音发送通道" prop="name"> |
| 40 | - <el-input v-model="form.audioChannelForSend" clearable></el-input> | ||
| 41 | - </el-form-item> | ||
| 42 | - <el-form-item label="语音接收送通道" prop="name"> | ||
| 43 | <el-input v-model="form.audioChannelForReceive" clearable></el-input> | 40 | <el-input v-model="form.audioChannelForReceive" clearable></el-input> |
| 44 | </el-form-item> | 41 | </el-form-item> |
| 45 | <el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" > | 42 | <el-form-item label="目录订阅" title="0为取消订阅" prop="subscribeCycleForCatalog" > |
| @@ -105,6 +102,8 @@ export default { | @@ -105,6 +102,8 @@ export default { | ||
| 105 | }) | 102 | }) |
| 106 | }, | 103 | }, |
| 107 | onSubmit: function () { | 104 | onSubmit: function () { |
| 105 | + console.log("onSubmit"); | ||
| 106 | + console.log(this.form); | ||
| 108 | this.form.subscribeCycleForCatalog = this.form.subscribeCycleForCatalog||0 | 107 | this.form.subscribeCycleForCatalog = this.form.subscribeCycleForCatalog||0 |
| 109 | this.form.subscribeCycleForMobilePosition = this.form.subscribeCycleForMobilePosition||0 | 108 | this.form.subscribeCycleForMobilePosition = this.form.subscribeCycleForMobilePosition||0 |
| 110 | this.form.mobilePositionSubmissionInterval = this.form.mobilePositionSubmissionInterval||0 | 109 | this.form.mobilePositionSubmissionInterval = this.form.mobilePositionSubmissionInterval||0 |
| @@ -124,7 +123,7 @@ export default { | @@ -124,7 +123,7 @@ export default { | ||
| 124 | }); | 123 | }); |
| 125 | } | 124 | } |
| 126 | }).catch(function (error) { | 125 | }).catch(function (error) { |
| 127 | - console.error(error); | 126 | + console.log(error); |
| 128 | }); | 127 | }); |
| 129 | }, | 128 | }, |
| 130 | close: function () { | 129 | close: function () { |