Commit 100252a253263321873e79d43dff94e19defe353
1 parent
a209d173
完善语音对讲
Showing
6 changed files
with
74 additions
and
42 deletions
src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java
| ... | ... | @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.conf.redis; |
| 3 | 3 | |
| 4 | 4 | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| 5 | 5 | import com.genersoft.iot.vmp.service.redisMsg.*; |
| 6 | +import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; | |
| 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; |
| 7 | 8 | import org.springframework.cache.annotation.CachingConfigurerSupport; |
| 8 | 9 | import org.springframework.context.annotation.Bean; |
| ... | ... | @@ -13,8 +14,6 @@ import org.springframework.data.redis.listener.PatternTopic; |
| 13 | 14 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 14 | 15 | import org.springframework.data.redis.serializer.StringRedisSerializer; |
| 15 | 16 | |
| 16 | -import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; | |
| 17 | - | |
| 18 | 17 | |
| 19 | 18 | /** |
| 20 | 19 | * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 | ... | ... |
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 com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | |
| 4 | 5 | import gov.nist.javax.sip.message.SIPResponse; |
| 5 | 6 | |
| 6 | 7 | /** |
| ... | ... | @@ -10,10 +11,18 @@ import gov.nist.javax.sip.message.SIPResponse; |
| 10 | 11 | public class AudioBroadcastCatch { |
| 11 | 12 | |
| 12 | 13 | |
| 13 | - public AudioBroadcastCatch(String deviceId, String channelId, AudioBroadcastCatchStatus status) { | |
| 14 | + public AudioBroadcastCatch(String deviceId, | |
| 15 | + String channelId, | |
| 16 | + AudioBroadcastCatchStatus status, | |
| 17 | + MediaServerItem mediaServerItem, | |
| 18 | + String app, | |
| 19 | + String stream) { | |
| 14 | 20 | this.deviceId = deviceId; |
| 15 | 21 | this.channelId = channelId; |
| 16 | 22 | this.status = status; |
| 23 | + this.mediaServerItem = mediaServerItem; | |
| 24 | + this.app = app; | |
| 25 | + this.stream = stream; | |
| 17 | 26 | } |
| 18 | 27 | |
| 19 | 28 | public AudioBroadcastCatch() { |
| ... | ... | @@ -30,6 +39,21 @@ public class AudioBroadcastCatch { |
| 30 | 39 | private String channelId; |
| 31 | 40 | |
| 32 | 41 | /** |
| 42 | + * 使用的流媒体 | |
| 43 | + */ | |
| 44 | + private MediaServerItem mediaServerItem; | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 待推送给设备的流应用名 | |
| 48 | + */ | |
| 49 | + private String app; | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 待推送给设备的流ID | |
| 53 | + */ | |
| 54 | + private String stream; | |
| 55 | + | |
| 56 | + /** | |
| 33 | 57 | * 语音广播状态 |
| 34 | 58 | */ |
| 35 | 59 | private AudioBroadcastCatchStatus status; |
| ... | ... | @@ -68,6 +92,30 @@ public class AudioBroadcastCatch { |
| 68 | 92 | return sipTransactionInfo; |
| 69 | 93 | } |
| 70 | 94 | |
| 95 | + public MediaServerItem getMediaServerItem() { | |
| 96 | + return mediaServerItem; | |
| 97 | + } | |
| 98 | + | |
| 99 | + public void setMediaServerItem(MediaServerItem mediaServerItem) { | |
| 100 | + this.mediaServerItem = mediaServerItem; | |
| 101 | + } | |
| 102 | + | |
| 103 | + public String getApp() { | |
| 104 | + return app; | |
| 105 | + } | |
| 106 | + | |
| 107 | + public void setApp(String app) { | |
| 108 | + this.app = app; | |
| 109 | + } | |
| 110 | + | |
| 111 | + public String getStream() { | |
| 112 | + return stream; | |
| 113 | + } | |
| 114 | + | |
| 115 | + public void setStream(String stream) { | |
| 116 | + this.stream = stream; | |
| 117 | + } | |
| 118 | + | |
| 71 | 119 | public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { |
| 72 | 120 | this.sipTransactionInfo = sipTransactionInfo; |
| 73 | 121 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| ... | ... | @@ -903,8 +903,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 903 | 903 | |
| 904 | 904 | // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) |
| 905 | 905 | Device device = redisCatchStorage.getDevice(requesterId); |
| 906 | - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId); | |
| 907 | - if (audioBroadcastCatch == null) { | |
| 906 | + AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(requesterId, channelId); | |
| 907 | + if (broadcastCatch == null) { | |
| 908 | 908 | logger.warn("来自设备的Invite请求非语音广播,已忽略,requesterId: {}/{}", requesterId, channelId); |
| 909 | 909 | try { |
| 910 | 910 | responseAck(request, Response.FORBIDDEN); |
| ... | ... | @@ -915,13 +915,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 915 | 915 | } |
| 916 | 916 | if (device != null) { |
| 917 | 917 | logger.info("收到设备" + requesterId + "的语音广播Invite请求"); |
| 918 | - String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + audioBroadcastCatch.getChannelId(); | |
| 918 | + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + broadcastCatch.getChannelId(); | |
| 919 | 919 | dynamicTask.stop(key); |
| 920 | 920 | try { |
| 921 | 921 | responseAck(request, Response.TRYING); |
| 922 | 922 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 923 | 923 | logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); |
| 924 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 924 | + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); | |
| 925 | 925 | return; |
| 926 | 926 | } |
| 927 | 927 | String contentString = new String(request.getRawContent()); |
| ... | ... | @@ -977,7 +977,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 977 | 977 | responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 |
| 978 | 978 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 979 | 979 | logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); |
| 980 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 980 | + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); | |
| 981 | 981 | return; |
| 982 | 982 | } |
| 983 | 983 | return; |
| ... | ... | @@ -986,19 +986,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 986 | 986 | logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, ssrc, |
| 987 | 987 | mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP"); |
| 988 | 988 | |
| 989 | - MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); | |
| 990 | - if (mediaServerItem == null) { | |
| 991 | - logger.warn("未找到可用的zlm"); | |
| 992 | - try { | |
| 993 | - responseAck(request, Response.BUSY_HERE); | |
| 994 | - } catch (SipException | InvalidArgumentException | ParseException e) { | |
| 995 | - logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); | |
| 996 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 997 | - } | |
| 998 | - return; | |
| 999 | - } | |
| 989 | + MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem(); | |
| 1000 | 990 | SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, |
| 1001 | - device.getDeviceId(), audioBroadcastCatch.getChannelId(), | |
| 991 | + device.getDeviceId(), broadcastCatch.getChannelId(), | |
| 1002 | 992 | mediaTransmissionTCP, false); |
| 1003 | 993 | |
| 1004 | 994 | if (sendRtpItem == null) { |
| ... | ... | @@ -1007,22 +997,20 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 1007 | 997 | responseAck(request, Response.BUSY_HERE); |
| 1008 | 998 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 1009 | 999 | logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); |
| 1010 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 1000 | + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); | |
| 1011 | 1001 | return; |
| 1012 | 1002 | } |
| 1013 | 1003 | return; |
| 1014 | 1004 | } |
| 1015 | 1005 | |
| 1016 | - String app = "broadcast"; | |
| 1017 | - String stream = device.getDeviceId() + "_" + audioBroadcastCatch.getChannelId(); | |
| 1018 | 1006 | |
| 1019 | 1007 | CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); |
| 1020 | 1008 | sendRtpItem.setPlayType(InviteStreamType.TALK); |
| 1021 | 1009 | sendRtpItem.setCallId(callIdHeader.getCallId()); |
| 1022 | 1010 | sendRtpItem.setPlatformId(requesterId); |
| 1023 | 1011 | sendRtpItem.setStatus(1); |
| 1024 | - sendRtpItem.setApp(app); | |
| 1025 | - sendRtpItem.setStreamId(stream); | |
| 1012 | + sendRtpItem.setApp(broadcastCatch.getApp()); | |
| 1013 | + sendRtpItem.setStreamId(broadcastCatch.getStream()); | |
| 1026 | 1014 | sendRtpItem.setPt(8); |
| 1027 | 1015 | sendRtpItem.setUsePs(false); |
| 1028 | 1016 | sendRtpItem.setRtcp(false); |
| ... | ... | @@ -1034,22 +1022,22 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 1034 | 1022 | |
| 1035 | 1023 | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| 1036 | 1024 | |
| 1037 | - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream); | |
| 1025 | + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream()); | |
| 1038 | 1026 | if (streamReady) { |
| 1039 | 1027 | sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); |
| 1040 | 1028 | }else { |
| 1041 | - logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", app, stream); | |
| 1029 | + logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream()); | |
| 1042 | 1030 | try { |
| 1043 | 1031 | responseAck(request, Response.GONE); |
| 1044 | 1032 | } catch (SipException | InvalidArgumentException | ParseException e) { |
| 1045 | 1033 | logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); |
| 1046 | 1034 | return; |
| 1047 | 1035 | } |
| 1048 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 1036 | + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); | |
| 1049 | 1037 | } |
| 1050 | 1038 | } catch (SdpException e) { |
| 1051 | 1039 | logger.error("[SDP解析异常]", e); |
| 1052 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 1040 | + playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); | |
| 1053 | 1041 | } |
| 1054 | 1042 | } else { |
| 1055 | 1043 | logger.warn("来自无效设备/平台的请求"); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| ... | ... | @@ -274,18 +274,17 @@ public class ZLMHttpHookListener { |
| 274 | 274 | logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); |
| 275 | 275 | } |
| 276 | 276 | |
| 277 | - | |
| 277 | + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); | |
| 278 | 278 | JSONObject json = (JSONObject) JSON.toJSON(param); |
| 279 | 279 | taskExecutor.execute(() -> { |
| 280 | 280 | ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); |
| 281 | 281 | if (subscribe != null) { |
| 282 | - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); | |
| 282 | + | |
| 283 | 283 | if (mediaInfo != null) { |
| 284 | 284 | subscribe.response(mediaInfo, json); |
| 285 | 285 | } |
| 286 | 286 | } |
| 287 | 287 | // 流消失移除redis play |
| 288 | - List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks(); | |
| 289 | 288 | if (param.isRegist()) { |
| 290 | 289 | if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() |
| 291 | 290 | || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() |
| ... | ... | @@ -343,7 +342,7 @@ public class ZLMHttpHookListener { |
| 343 | 342 | } |
| 344 | 343 | // 开启语音对讲通道 |
| 345 | 344 | try { |
| 346 | - playService.audioBroadcastCmd(device, channelId, 60, (msg)->{ | |
| 345 | + playService.audioBroadcastCmd(device, channelId, 60, mediaInfo, param.getApp(), param.getStream(), (msg)->{ | |
| 347 | 346 | logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); |
| 348 | 347 | }); |
| 349 | 348 | } catch (InvalidArgumentException | ParseException | SipException e) { |
| ... | ... | @@ -375,7 +374,7 @@ public class ZLMHttpHookListener { |
| 375 | 374 | if (sendRtpItem == null) { |
| 376 | 375 | // TODO 可能数据错误,重新开启语音通道 |
| 377 | 376 | }else { |
| 378 | - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 377 | + MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 379 | 378 | logger.info("rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); |
| 380 | 379 | Map<String, Object> sendParam = new HashMap<>(12); |
| 381 | 380 | sendParam.put("vhost","__defaultVhost__"); |
| ... | ... | @@ -389,12 +388,12 @@ public class ZLMHttpHookListener { |
| 389 | 388 | |
| 390 | 389 | JSONObject jsonObject; |
| 391 | 390 | if (sendRtpItem.isTcpActive()) { |
| 392 | - jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, sendParam); | |
| 391 | + jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaServerItem, sendParam); | |
| 393 | 392 | } else { |
| 394 | 393 | sendParam.put("is_udp", sendRtpItem.isTcp() ? "0" : "1"); |
| 395 | 394 | sendParam.put("dst_url", sendRtpItem.getIp()); |
| 396 | 395 | sendParam.put("dst_port", sendRtpItem.getPort()); |
| 397 | - jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, sendParam); | |
| 396 | + jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaServerItem, sendParam); | |
| 398 | 397 | } |
| 399 | 398 | if (jsonObject != null && jsonObject.getInteger("code") == 0) { |
| 400 | 399 | logger.info("[语音对讲] 自动推流成功, device: {}, channel: {}", deviceId, channelId); | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
| ... | ... | @@ -12,9 +12,7 @@ import com.genersoft.iot.vmp.service.bean.PlayBackCallback; |
| 12 | 12 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 13 | 13 | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; |
| 14 | 14 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| 15 | -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | |
| 16 | 15 | import gov.nist.javax.sip.message.SIPResponse; |
| 17 | -import org.springframework.web.context.request.async.DeferredResult; | |
| 18 | 16 | |
| 19 | 17 | import javax.sip.InvalidArgumentException; |
| 20 | 18 | import javax.sip.SipException; |
| ... | ... | @@ -62,7 +60,7 @@ public interface IPlayService { |
| 62 | 60 | AudioBroadcastResult audioBroadcast(Device device, String channelId); |
| 63 | 61 | void stopAudioBroadcast(String deviceId, String channelId); |
| 64 | 62 | |
| 65 | - void audioBroadcastCmd(Device device, String channelId, int timeout, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; | |
| 63 | + void audioBroadcastCmd(Device device, String channelId, int timeout, MediaServerItem mediaServerItem, String sourceApp, String sourceStream, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; | |
| 66 | 64 | |
| 67 | 65 | void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; |
| 68 | 66 | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -1011,7 +1011,7 @@ public class PlayServiceImpl implements IPlayService { |
| 1011 | 1011 | } |
| 1012 | 1012 | |
| 1013 | 1013 | @Override |
| 1014 | - public void audioBroadcastCmd(Device device, String channelId, int timeout, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { | |
| 1014 | + public void audioBroadcastCmd(Device device, String channelId, int timeout, MediaServerItem mediaServerItem, String sourceApp, String sourceStream, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { | |
| 1015 | 1015 | if (device == null || channelId == null) { |
| 1016 | 1016 | return; |
| 1017 | 1017 | } |
| ... | ... | @@ -1027,7 +1027,6 @@ public class PlayServiceImpl implements IPlayService { |
| 1027 | 1027 | SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); |
| 1028 | 1028 | if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { |
| 1029 | 1029 | // 查询流是否存在,不存在则认为是异常状态 |
| 1030 | - MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 1031 | 1030 | Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStreamId()); |
| 1032 | 1031 | if (streamReady) { |
| 1033 | 1032 | logger.warn("语音广播已经开启: {}", channelId); |
| ... | ... | @@ -1042,7 +1041,8 @@ public class PlayServiceImpl implements IPlayService { |
| 1042 | 1041 | // 发送通知 |
| 1043 | 1042 | cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> { |
| 1044 | 1043 | // 发送成功 |
| 1045 | - AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, AudioBroadcastCatchStatus.Ready); | |
| 1044 | + AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, | |
| 1045 | + AudioBroadcastCatchStatus.Ready, mediaServerItem, sourceApp, sourceStream); | |
| 1046 | 1046 | audioBroadcastManager.update(audioBroadcastCatch); |
| 1047 | 1047 | }, eventResultForError -> { |
| 1048 | 1048 | // 发送失败 | ... | ... |