Commit f8f65d473bec182abeecd6fd17a9d4c4c4cfc7c5

Authored by 648540858
1 parent 65fa75fb

优化语音广播流程

src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
1 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3 3  
  4 +import gov.nist.javax.sip.message.SIPRequest;
  5 +import gov.nist.javax.sip.stack.SIPDialog;
  6 +
  7 +import javax.sip.Dialog;
  8 +
4 9 /**
5 10 * 缓存语音广播的状态
6 11 * @author lin
... ... @@ -32,6 +37,16 @@ public class AudioBroadcastCatch {
32 37 */
33 38 private AudioBroadcastCatchStatus status;
34 39  
  40 + /**
  41 + * 请求信息
  42 + */
  43 + private SIPRequest request;
  44 +
  45 + /**
  46 + * 会话信息
  47 + */
  48 + private SIPDialog dialog;
  49 +
35 50  
36 51 public String getDeviceId() {
37 52 return deviceId;
... ... @@ -56,4 +71,20 @@ public class AudioBroadcastCatch {
56 71 public void setStatus(AudioBroadcastCatchStatus status) {
57 72 this.status = status;
58 73 }
  74 +
  75 + public void setDialog(SIPDialog dialog) {
  76 + this.dialog = dialog;
  77 + }
  78 +
  79 + public SIPDialog getDialog() {
  80 + return dialog;
  81 + }
  82 +
  83 + public SIPRequest getRequest() {
  84 + return request;
  85 + }
  86 +
  87 + public void setRequest(SIPRequest request) {
  88 + this.request = request;
  89 + }
59 90 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java
1 1 package com.genersoft.iot.vmp.gb28181.session;
2 2  
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
3 4 import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
  5 +import org.springframework.beans.factory.annotation.Autowired;
4 6 import org.springframework.stereotype.Component;
5 7  
6   -import java.util.ArrayList;
7   -import java.util.Collection;
8   -import java.util.List;
9   -import java.util.Map;
  8 +import java.util.*;
10 9 import java.util.concurrent.ConcurrentHashMap;
  10 +import java.util.stream.Collectors;
  11 +import java.util.stream.Stream;
11 12  
12 13 /**
13 14 * 语音广播消息管理类
... ... @@ -16,6 +17,9 @@ import java.util.concurrent.ConcurrentHashMap;
16 17 @Component
17 18 public class AudioBroadcastManager {
18 19  
  20 + @Autowired
  21 + private SipConfig config;
  22 +
19 23 public static Map<String, AudioBroadcastCatch> data = new ConcurrentHashMap<>();
20 24  
21 25 public void add(AudioBroadcastCatch audioBroadcastCatch) {
... ... @@ -54,6 +58,16 @@ public class AudioBroadcastManager {
54 58 }
55 59  
56 60 public AudioBroadcastCatch get(String deviceId, String channelId) {
57   - return data.get(deviceId + channelId);
  61 + AudioBroadcastCatch audioBroadcastCatch = data.get(deviceId + channelId);
  62 + if (audioBroadcastCatch == null) {
  63 + Stream<AudioBroadcastCatch> allAudioBroadcastCatchStreamForDevice = data.values().stream().filter(
  64 + audioBroadcastCatchItem -> Objects.equals(audioBroadcastCatchItem.getDeviceId(), deviceId));
  65 + List<AudioBroadcastCatch> audioBroadcastCatchList = allAudioBroadcastCatchStreamForDevice.collect(Collectors.toList());
  66 + if (audioBroadcastCatchList.size() == 1 && Objects.equals(config.getId(), channelId)) {
  67 + audioBroadcastCatch = audioBroadcastCatchList.get(0);
  68 + }
  69 + }
  70 +
  71 + return audioBroadcastCatch;
58 72 }
59 73 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
... ... @@ -18,6 +18,8 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
18 18 import com.genersoft.iot.vmp.service.IMediaServerService;
19 19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
20 20 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  21 +import gov.nist.javax.sip.message.SIPRequest;
  22 +import gov.nist.javax.sip.stack.SIPDialog;
21 23 import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine;
22 24 import org.slf4j.Logger;
23 25 import org.slf4j.LoggerFactory;
... ... @@ -28,15 +30,18 @@ import org.springframework.stereotype.Component;
28 30 import javax.sip.Dialog;
29 31 import javax.sip.DialogState;
30 32 import javax.sip.RequestEvent;
  33 +import javax.sip.SipException;
31 34 import javax.sip.address.SipURI;
32 35 import javax.sip.header.CallIdHeader;
33 36 import javax.sip.header.FromHeader;
34 37 import javax.sip.header.HeaderAddress;
35 38 import javax.sip.header.ToHeader;
  39 +import java.text.ParseException;
36 40 import java.util.*;
37 41  
38 42 /**
39 43 * SIP命令类型: ACK请求
  44 + * @author lin
40 45 */
41 46 @Component
42 47 public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
... ... @@ -96,8 +101,8 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
96 101 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformGbId);
97 102 // 取消设置的超时任务
98 103 dynamicTask.stop(callIdHeader.getCallId());
99   - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
100   - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());
  104 +// String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  105 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, null, null, callIdHeader.getCallId());
101 106 String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
102 107 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
103 108 logger.info("收到ACK,开始向上级推流 rtp/{}", sendRtpItem.getStreamId());
... ... @@ -121,7 +126,14 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
121 126 } else {
122 127 logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param));
123 128 if (sendRtpItem.isOnlyAudio()) {
124   - // TODO 可能是语音对讲
  129 + // 语音对讲
  130 + try {
  131 + cmder.streamByeCmd((SIPDialog) evt.getDialog(), (SIPRequest)evt.getRequest(), null);
  132 + } catch (SipException e) {
  133 + throw new RuntimeException(e);
  134 + } catch (ParseException e) {
  135 + throw new RuntimeException(e);
  136 + }
125 137 }else {
126 138 // 向上级平台
127 139 commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
... ... @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
13 13 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
14 14 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
15 15 import com.genersoft.iot.vmp.service.IMediaServerService;
  16 +import com.genersoft.iot.vmp.service.IPlayService;
16 17 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
17 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
18 19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
... ... @@ -65,6 +66,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
65 66 @Autowired
66 67 private VideoStreamSessionManager streamSession;
67 68  
  69 + @Autowired
  70 + private IPlayService playService;
  71 +
68 72 @Override
69 73 public void afterPropertiesSet() throws Exception {
70 74 // 添加消息处理的订阅
... ... @@ -106,6 +110,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
106 110 if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
107 111 cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId, streamId, null);
108 112 }
  113 + if (sendRtpItem.isOnlyAudio()) {
  114 + playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), channelId);
  115 + }
109 116 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
110 117 MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
111 118 messageForPushChannel.setType(0);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
... ... @@ -114,6 +114,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
114 114 private SipConfig config;
115 115  
116 116  
  117 +
117 118 @Override
118 119 public void afterPropertiesSet() throws Exception {
119 120 // 添加消息处理的订阅
... ... @@ -492,7 +493,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
492 493 gbStream.getApp(), gbStream.getStream(), channelId,
493 494 mediaTransmissionTCP);
494 495  
495   -
496 496 if (sendRtpItem == null) {
497 497 logger.warn("服务器端口资源不足");
498 498 responseAck(evt, Response.BUSY_HERE);
... ... @@ -562,25 +562,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
562 562 }
563 563 }
564 564  
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   -// }
  565 + public void inviteFromDeviceHandle(RequestEvent evt, String requesterId, String channelId1) throws InvalidArgumentException, ParseException, SipException, SdpException {
  566 +
581 567 // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
582 568 Device device = redisCatchStorage.getDevice(requesterId);
583   -
  569 + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId1);
  570 + if (audioBroadcastCatch == null) {
  571 + logger.warn("来自设备的Invite请求非语音广播,已忽略");
  572 + responseAck(evt, Response.FORBIDDEN);
  573 + return;
  574 + }
584 575 Request request = evt.getRequest();
585 576 if (device != null) {
586 577 logger.info("收到设备" + requesterId + "的语音广播Invite请求");
... ... @@ -606,7 +597,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
606 597  
607 598 // 查看是否支持PS 负载96
608 599 int port = -1;
609   - //boolean recvonly = false;
610 600 boolean mediaTransmissionTCP = false;
611 601 Boolean tcpActive = null;
612 602 for (int i = 0; i < mediaDescriptions.size(); i++) {
... ... @@ -638,7 +628,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
638 628 responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
639 629 return;
640 630 }
641   - String sessionName = sdp.getSessionName().getValue();
642 631 String addressStr = sdp.getOrigin().getAddress();
643 632 logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", requesterId, addressStr, port, ssrc);
644 633  
... ... @@ -649,20 +638,19 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
649 638 return;
650 639 }
651 640 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
652   - device.getDeviceId(), channelId,
  641 + device.getDeviceId(), audioBroadcastCatch.getChannelId(),
653 642 mediaTransmissionTCP);
654   - sendRtpItem.setTcp(mediaTransmissionTCP);
655   - if (tcpActive != null) {
656   - sendRtpItem.setTcpActive(tcpActive);
657   - }
658 643 if (sendRtpItem == null) {
659 644 logger.warn("服务器端口资源不足");
660 645 responseAck(evt, Response.BUSY_HERE);
661 646 return;
662 647 }
663   -
  648 + sendRtpItem.setTcp(mediaTransmissionTCP);
  649 + if (tcpActive != null) {
  650 + sendRtpItem.setTcpActive(tcpActive);
  651 + }
664 652 String app = "broadcast";
665   - String stream = device.getDeviceId() + "_" + channelId;
  653 + String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId();
666 654  
667 655 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
668 656 sendRtpItem.setPlayType(InviteStreamType.PLAY);
... ... @@ -685,12 +673,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
685 673 subscribeKey.put("schema", "rtmp");
686 674 subscribeKey.put("mediaServerId", mediaServerItem.getId());
687 675 String finalSsrc = ssrc;
688   - String waiteStreamTimeoutTaskKey = "waite-stream-" + device.getDeviceId() + channelId;
689   -
690 676 // 流已经存在时直接推流
691 677 if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) {
692 678 logger.info("发现已经在推流");
693   - dynamicTask.stop(waiteStreamTimeoutTaskKey);
694 679 sendRtpItem.setStatus(2);
695 680 redisCatchStorage.updateSendRTPSever(sendRtpItem);
696 681 StringBuffer content = new StringBuffer(200);
... ... @@ -711,6 +696,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
711 696 parentPlatform.setServerGBId(device.getDeviceId());
712 697 try {
713 698 responseSdpAck(evt, content.toString(), parentPlatform);
  699 + Dialog dialog = evt.getDialog();
  700 + audioBroadcastCatch.setDialog((SIPDialog) dialog);
  701 + audioBroadcastCatch.setRequest((SIPRequest) request);
  702 + audioBroadcastManager.update(audioBroadcastCatch);
714 703 } catch (SipException e) {
715 704 throw new RuntimeException(e);
716 705 } catch (InvalidArgumentException e) {
... ... @@ -721,20 +710,17 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
721 710 }else {
722 711 // 流不存在时监听流上线
723 712 // 设置等待推流的超时; 默认20s
  713 + String waiteStreamTimeoutTaskKey = "waite-stream-" + device.getDeviceId() + audioBroadcastCatch.getChannelId();
724 714 dynamicTask.startDelay(waiteStreamTimeoutTaskKey, ()->{
725 715 logger.info("等待推流超时: {}/{}", app, stream);
726   - if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
727   - audioBroadcastManager.del(device.getDeviceId(), channelId);
728   - }else {
729   - // 兼容海康使用了错误的通道ID的情况
730   - audioBroadcastManager.delByDeviceId(device.getDeviceId());
731   - }
732   -
  716 + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId());
733 717 // 发送bye
734 718 try {
735   - cmder.streamByeCmd((SIPDialog)evt.getServerTransaction().getDialog(), (SIPRequest) evt.getRequest(), null);
  719 + responseAck(evt, Response.BUSY_HERE);
736 720 } catch (SipException e) {
737 721 throw new RuntimeException(e);
  722 + } catch (InvalidArgumentException e) {
  723 + throw new RuntimeException(e);
738 724 } catch (ParseException e) {
739 725 throw new RuntimeException(e);
740 726 }
... ... @@ -743,10 +729,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
743 729 subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
744 730 (MediaServerItem mediaServerItemInUse, JSONObject json)->{
745 731 sendRtpItem.setStatus(2);
  732 + dynamicTask.stop(waiteStreamTimeoutTaskKey);
746 733 redisCatchStorage.updateSendRTPSever(sendRtpItem);
747 734 StringBuffer content = new StringBuffer(200);
748 735 content.append("v=0\r\n");
749   - content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  736 + content.append("o="+ audioBroadcastCatch.getChannelId() +" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
750 737 content.append("s=Play\r\n");
751 738 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
752 739 content.append("t=0 0\r\n");
... ... @@ -771,8 +758,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
771 758 }
772 759 });
773 760 }
774   - String timeOutTaskKey = "audio-broadcast-" + device.getDeviceId() + channelId;
775   - dynamicTask.stop(timeOutTaskKey);
776 761 String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId();
777 762 WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>();
778 763 wvpResult.setCode(0);
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
... ... @@ -43,4 +43,5 @@ public interface IPlayService {
43 43 StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
44 44  
45 45 void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event);
  46 + void stopAudioBroadcast(String deviceId, String channelId);
46 47 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import com.genersoft.iot.vmp.common.StreamInfo;
7 7 import com.genersoft.iot.vmp.conf.DynamicTask;
  8 +import com.genersoft.iot.vmp.conf.SipConfig;
8 9 import com.genersoft.iot.vmp.conf.UserSetting;
9 10 import com.genersoft.iot.vmp.gb28181.bean.*;
10 11 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
... ... @@ -44,9 +45,13 @@ import org.springframework.util.ResourceUtils;
44 45 import org.springframework.web.context.request.async.DeferredResult;
45 46  
46 47 import javax.sip.ResponseEvent;
  48 +import javax.sip.SipException;
47 49 import java.io.FileNotFoundException;
48 50 import java.math.BigDecimal;
  51 +import java.text.ParseException;
49 52 import java.util.*;
  53 +import java.util.stream.Collectors;
  54 +import java.util.stream.Stream;
50 55  
51 56 @SuppressWarnings(value = {"rawtypes", "unchecked"})
52 57 @Service
... ... @@ -94,6 +99,9 @@ public class PlayServiceImpl implements IPlayService {
94 99 private UserSetting userSetting;
95 100  
96 101 @Autowired
  102 + private SipConfig sipConfig;
  103 +
  104 + @Autowired
97 105 private DynamicTask dynamicTask;
98 106  
99 107  
... ... @@ -641,16 +649,13 @@ public class PlayServiceImpl implements IPlayService {
641 649 }
642 650 // 查询通道使用状态
643 651 if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
644   - logger.warn("语音广播已经开启: {}", channelId);
645   - event.call("语音广播已经开启");
646   - return;
  652 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
  653 + if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) {
  654 + logger.warn("语音广播已经开启: {}", channelId);
  655 + event.call("语音广播已经开启");
  656 + return;
  657 + }
647 658 }
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 659  
655 660 // 发送通知
656 661 cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> {
... ... @@ -658,11 +663,38 @@ public class PlayServiceImpl implements IPlayService {
658 663 AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, AudioBroadcastCatchStatus.Ready);
659 664 audioBroadcastManager.add(audioBroadcastCatch);
660 665 }, eventResultForError -> {
661   - dynamicTask.stop(timeOutTaskKey);
662 666 // 发送失败
663 667 logger.error("语音广播发送失败: {}:{}", channelId, eventResultForError.msg);
664 668 event.call("语音广播发送失败");
665   - audioBroadcastManager.del(device.getDeviceId(), channelId);
  669 + stopAudioBroadcast(device.getDeviceId(), channelId);
666 670 });
667 671 }
  672 +
  673 + @Override
  674 + public void stopAudioBroadcast(String deviceId, String channelId){
  675 + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(deviceId, channelId);
  676 + if (audioBroadcastCatch != null) {
  677 + audioBroadcastManager.del(deviceId, audioBroadcastCatch.getChannelId());
  678 + }
  679 + try {
  680 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, channelId, null, null);
  681 + if (sendRtpItem != null) {
  682 + redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null);
  683 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  684 + Map<String, Object> param = new HashMap<>();
  685 + param.put("vhost", "__defaultVhost__");
  686 + param.put("app", sendRtpItem.getApp());
  687 + param.put("stream", sendRtpItem.getStreamId());
  688 + zlmresTfulUtils.stopSendRtp(mediaInfo, param);
  689 + }
  690 + if (audioBroadcastCatch.getStatus() == AudioBroadcastCatchStatus.Ok) {
  691 + cmder.streamByeCmd(audioBroadcastCatch.getDialog(), audioBroadcastCatch.getRequest(), null);
  692 + }
  693 + } catch (SipException e) {
  694 + throw new RuntimeException(e);
  695 + } catch (ParseException e) {
  696 + throw new RuntimeException(e);
  697 + }
  698 +
  699 + }
668 700 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
... ... @@ -319,6 +319,22 @@ public class PlayController {
319 319 return result;
320 320 }
321 321  
  322 +
  323 + @ApiOperation("停止语音广播")
  324 + @ApiImplicitParams({
  325 + @ApiImplicitParam(name = "deviceId", value = "设备Id", dataTypeClass = String.class),
  326 + @ApiImplicitParam(name = "channelId", value = "通道Id", dataTypeClass = String.class),
  327 + })
  328 + @GetMapping("/broadcast/stop/{deviceId}/{channelId}")
  329 + @PostMapping("/broadcast/stop/{deviceId}/{channelId}")
  330 + public WVPResult<String> stopBroadcastA(@PathVariable String deviceId, @PathVariable String channelId) {
  331 + if (logger.isDebugEnabled()) {
  332 + logger.debug("停止语音广播API调用");
  333 + }
  334 + playService.stopAudioBroadcast(deviceId, channelId);
  335 + return new WVPResult<>(0, "success", null);
  336 + }
  337 +
322 338 @ApiOperation("获取所有的ssrc")
323 339 @GetMapping("/ssrc")
324 340 public WVPResult<JSONObject> getSsrc() {
... ...