diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java index 10b47800c..4d820afca 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java @@ -574,9 +574,9 @@ public class DeviceServiceImpl implements IDeviceService { @Override public boolean removeCatalogSubscribe(@NotNull Device device, CommonCallback callback) { - log.info("[移除目录订阅]: {}", device.getDeviceId()); String key = SubscribeTaskForCatalog.getKey(device); if (subscribeTaskRunner.containsKey(key)) { + log.info("[移除目录订阅]: {}", device.getDeviceId()); SipTransactionInfo transactionInfo = subscribeTaskRunner.getTransactionInfo(key); if (transactionInfo == null) { log.warn("[移除目录订阅] 未找到事务信息,{}", device.getDeviceId()); @@ -638,9 +638,9 @@ public class DeviceServiceImpl implements IDeviceService { @Override public boolean removeMobilePositionSubscribe(Device device, CommonCallback callback) { - log.info("[移除移动位置订阅]: {}", device.getDeviceId()); String key = SubscribeTaskForMobilPosition.getKey(device); if (subscribeTaskRunner.containsKey(key)) { + log.info("[移除移动位置订阅]: {}", device.getDeviceId()); SipTransactionInfo transactionInfo = subscribeTaskRunner.getTransactionInfo(key); if (transactionInfo == null) { log.warn("[移除移动位置订阅] 未找到事务信息,{}", device.getDeviceId()); @@ -703,9 +703,9 @@ public class DeviceServiceImpl implements IDeviceService { @Override public boolean removeAlarmSubscribe(Device device, CommonCallback callback) { - log.info("[移除报警订阅]: {}", device.getDeviceId()); String key = SubscribeTaskForAlarm.getKey(device); if (subscribeTaskRunner.containsKey(key)) { + log.info("[移除报警订阅]: {}", device.getDeviceId()); SipTransactionInfo transactionInfo = subscribeTaskRunner.getTransactionInfo(key); if (transactionInfo == null) { log.warn("[移除报警订阅] 未找到事务信息,{}", device.getDeviceId()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java index f0967ea57..31e3b3e68 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java @@ -1229,6 +1229,12 @@ public class PlayServiceImpl implements IPlayService { audioBroadcastResult.setApp(app); audioBroadcastResult.setStream(stream); audioBroadcastResult.setStreamInfo(new StreamContent(mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null, false))); + if (!broadcastMode) { + audioBroadcastResult.setPlayStreamInfo(new StreamContent( + mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, + MediaStreamUtil.GB28181_TALK, stream + "_talk", + null, null, null, true))); + } audioBroadcastResult.setCodec("G.711"); return audioBroadcastResult; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourceBroadcastServiceForGbImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourceBroadcastServiceForGbImpl.java index 32990c89a..a194d9aa7 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourceBroadcastServiceForGbImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/SourceBroadcastServiceForGbImpl.java @@ -1,8 +1,6 @@ package com.genersoft.iot.vmp.gb28181.service.impl; import com.genersoft.iot.vmp.common.enums.ChannelDataType; -import com.genersoft.iot.vmp.common.enums.MediaStreamUtil; -import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.Device; @@ -11,12 +9,9 @@ import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IPlayService; import com.genersoft.iot.vmp.gb28181.service.ISourceBroadcastService; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; import com.genersoft.iot.vmp.vmanager.bean.AudioTalkResult; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.StreamContent; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -34,12 +29,6 @@ public class SourceBroadcastServiceForGbImpl implements ISourceBroadcastService @Autowired private IDeviceChannelService deviceChannelService; - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - private UserSetting userSetting; - @Override public AudioTalkResult startBroadcast(CommonGBChannel channel) { Device device = deviceService.getDevice(channel.getDataDeviceId()); @@ -79,14 +68,9 @@ public class SourceBroadcastServiceForGbImpl implements ISourceBroadcastService } AudioBroadcastResult abResult = playService.audioBroadcast( device.getDeviceId(), deviceChannel.getDeviceId(), false); - MediaServer mediaServer = mediaServerService.getMediaServerForMinimumLoad(null); - StreamContent playStream = new StreamContent( - mediaServerService.getStreamInfoByAppAndStream(mediaServer, - MediaStreamUtil.GB28181_TALK, abResult.getStream() + "_talk", - null, null, null, false)); AudioTalkResult result = new AudioTalkResult(); result.setPushStream(abResult.getStreamInfo()); - result.setPlayStream(playStream); + result.setPlayStream(abResult.getPlayStreamInfo()); return result; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 4ae3b99fa..e7acf1527 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -496,7 +496,7 @@ public class SIPCommander implements ISIPCommander { } if (!mediaServerItem.isRtpEnable()) { // 单端口暂不支持语音喊话 - log.info("[语音喊话] 单端口暂不支持此操作"); + log.warn("[语音喊话] 单端口暂不支持此操作"); return; } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/AudioBroadcastResult.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/AudioBroadcastResult.java index 1126081df..32ab9022d 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/AudioBroadcastResult.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/AudioBroadcastResult.java @@ -1,8 +1,13 @@ package com.genersoft.iot.vmp.vmanager.bean; +import lombok.Getter; +import lombok.Setter; + /** * @author lin */ +@Setter +@Getter public class AudioBroadcastResult { /** * 推流的各个方式流地址 @@ -24,36 +29,10 @@ public class AudioBroadcastResult { */ private String stream; + /** + * 播放流地址(设备音频通过ZLM播放给浏览器),对讲时设置 + */ + private StreamContent playStreamInfo; - public StreamContent getStreamInfo() { - return streamInfo; - } - public void setStreamInfo(StreamContent streamInfo) { - this.streamInfo = streamInfo; - } - - public String getCodec() { - return codec; - } - - public void setCodec(String codec) { - this.codec = codec; - } - - public String getApp() { - return app; - } - - public void setApp(String app) { - this.app = app; - } - - public String getStream() { - return stream; - } - - public void setStream(String stream) { - this.stream = stream; - } } diff --git a/web/src/views/channel/audioTalk.vue b/web/src/views/channel/audioTalk.vue index c18120225..071be5303 100644 --- a/web/src/views/channel/audioTalk.vue +++ b/web/src/views/channel/audioTalk.vue @@ -355,7 +355,9 @@ export default { this.talkAudioRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, (s) => { console.warn('[ChAudioTalk] 音频播放连接状态:', s) - if (s === 'disconnected' || s === 'failed' || s === 'closed') { + if (s === 'connected') { + this.playConnected = true + } else if (s === 'disconnected' || s === 'failed' || s === 'closed') { this.playConnected = false this.talkAudioFailed = true if (this.talkStatus === 1) { diff --git a/web/src/views/device/dialog/audioTalk.vue b/web/src/views/device/dialog/audioTalk.vue index 86f9b03c3..419854214 100644 --- a/web/src/views/device/dialog/audioTalk.vue +++ b/web/src/views/device/dialog/audioTalk.vue @@ -51,9 +51,19 @@ />

正在释放资源 - 点击开始{{ talkMode ? '对讲' : '喊话' }} + 点击开始{{ talkMode ? '喊话' : '对讲' }} 等待接通中... - 请说话 + 喊话中 + 等待接通中... + 对讲中 +

+

+ 重试音频

@@ -129,7 +139,10 @@ export default { if (this.talkStatus === -2) return 'primary' if (this.talkStatus === -1) return 'primary' if (this.talkStatus === 0) return 'warning' - if (this.talkStatus === 1) return 'danger' + if (this.talkStatus === 1) { + if (!this.talkMode && !this.playConnected) return 'warning' + return 'danger' + } }, async talkButtonClick() { if (this.talkStatus === -1) { @@ -145,6 +158,13 @@ export default { const si = data.streamInfo const url = document.location.protocol.includes('https') ? si.rtcs : si.rtc this.startWebrtcPush(url) + + const playStreamInfo = data?.playStreamInfo + if (!this.talkMode && playStreamInfo) { + this.talkAudioPlayStream = playStreamInfo + this.startTalkAudioPlay(playStreamInfo) + this.muteVideoPlayer() + } } catch (e) { this.$message({ showClose: true, message: e, type: 'error' }) this.talkStatus = -1 @@ -181,6 +201,105 @@ export default { }) .catch(() => { this.talkStatus = -1 }) }, + muteVideoPlayer() { + const player = this.$refs.playerTabs + if (!player) return + if (player.mute) { + player.mute() + } + }, + unmuteVideoPlayer() { + const player = this.$refs.playerTabs + if (!player) return + if (player.cancelMute) { + player.cancelMute() + } + }, + startTalkAudioPlay(playStreamInfo) { + if (this.talkAudioRtc) { + this.talkAudioRtc.close() + } + if (this.talkAudioRetryTimer) { + clearTimeout(this.talkAudioRetryTimer) + } + + const url = location.protocol === 'https:' ? playStreamInfo.rtcs : playStreamInfo.rtc + if (!url) { + console.warn('[AudioTalk] 无可用的设备音频播放地址') + return + } + this.talkAudioRetryTimer = setTimeout(() => { + this.pollMediaInfoAndPlay(playStreamInfo) + }, 800) + }, + async pollMediaInfoAndPlay(playStreamInfo) { + try { + const data = await this.$store.dispatch('server/getMediaInfo', { + app: playStreamInfo.app, + stream: playStreamInfo.stream, + mediaServerId: playStreamInfo.mediaServerId + }) + if (data) { + const url = location.protocol === 'https:' ? playStreamInfo.rtcs : playStreamInfo.rtc + this.startTalkAudioByRtc(url) + } else { + throw new Error('no data') + } + } catch (e) { + if (this.talkStatus === 1 || this.talkStatus === 0) { + this.talkAudioRetryTimer = setTimeout(() => { + this.pollMediaInfoAndPlay(playStreamInfo) + }, 800) + } + } + }, + startTalkAudioByRtc(url) { + this.talkAudioFailed = false + this.talkAudioRtc = new ZLMRTCClient.Endpoint({ + debug: false, + element: document.getElementById('audioTalkVideo'), + zlmsdpUrl: url, + simulecast: false, + useCamera: false, + audioEnable: true, + videoEnable: false, + recvOnly: true, + usedatachannel: false + }) + + this.talkAudioRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => { + console.warn('[AudioTalk] 播放流offer失败:', e?.code, e?.msg) + if (e && e.code == -400 && e.msg == '流不存在') { + this.talkAudioRetryTimer = setTimeout(() => { + this.startTalkAudioByRtc(url) + }, 1000) + } + }) + + this.talkAudioRtc.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, () => { + console.warn('[AudioTalk] 设备音频流到达') + this.playConnected = true + }) + + this.talkAudioRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, () => { + console.error('[AudioTalk] 音频播放ICE协商失败') + }) + + this.talkAudioRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, (s) => { + console.warn('[AudioTalk] 音频播放连接状态:', s) + if (s === 'connected') { + this.playConnected = true + } else if (s === 'disconnected' || s === 'failed' || s === 'closed') { + this.playConnected = false + this.talkAudioFailed = true + if (this.talkStatus === 1) { + this.talkAudioRetryTimer = setTimeout(() => { + this.startTalkAudioByRtc(url) + }, 2000) + } + } + }) + }, async stopTalk() { this.talkStatus = -2 if (this.broadcastRtc) { @@ -198,6 +317,7 @@ export default { this.talkAudioFailed = false this.talkAudioPlayStream = null this.playConnected = false + this.unmuteVideoPlayer() try { await this.$store.dispatch('play/broadcastStop', [this.deviceId, this.channelId]) } catch (e) { @@ -205,6 +325,11 @@ export default { } this.talkStatus = -1 }, + retryTalkAudio() { + if (this.talkAudioPlayStream) { + this.startTalkAudioPlay(this.talkAudioPlayStream) + } + }, close() { if (this.showPlayer && this.$refs.playerTabs) { this.$refs.playerTabs.stop()