Browse Source

fix
1. 语音对讲断开功能修复

kindring 1 year ago
parent
commit
a7bd1232d4

+ 29 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java

@@ -77,7 +77,21 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
             logger.warn("不支持方法{}的request", method);
             return;
         }
-        logger.info("[device --> server] request: \n{}",requestEvent.getRequest());
+        // 获取设备ID form
+        FromHeader fromHeader = (FromHeader) requestEvent.getRequest().getHeader(FromHeader.NAME);
+        // 获取设备ID sip:34020000002000000123@3402000000
+        String deviceId = "";
+        // 获取设备ID 34020000002000000123 通过正则
+        try{
+            if (fromHeader != null) {
+                deviceId = fromHeader.getAddress().getURI().toString().split(":")[1].split("@")[0];
+            }
+        }catch (Exception e){
+            logger.error("获取设备ID失败");
+        }
+
+        logger.info("[device --req--> server] {} ({}): \n{}", deviceId, method, requestEvent.getRequest());
+        // 将对应的设备设置为上线状态
         requestProcessorMap.get(method).process(requestEvent);
 
     }
@@ -92,6 +106,20 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
         Response response = responseEvent.getResponse();
         int status = response.getStatusCode();
 
+        // 获取设备ID form
+        FromHeader fromHeader = (FromHeader) responseEvent.getResponse().getHeader(FromHeader.NAME);
+        // 获取设备ID sip:34020000002000000123@3402000000
+        String deviceId = "";
+        // 获取设备ID 34020000002000000123 通过正则
+        try{
+            if (fromHeader != null) {
+                deviceId = fromHeader.getAddress().getURI().toString().split(":")[1].split("@")[0];
+            }
+        }catch (Exception e){
+            logger.error("获取设备ID失败");
+        }
+        logger.info("[device --res--> server] {}: \n{}", deviceId, responseEvent.getResponse());
+
         // Success
         if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) {
             CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);

+ 5 - 32
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -549,26 +549,6 @@ public class SIPCommander implements ISIPCommander {
         // 使用 返回sdp ack 数据给设备
         logger.info("返回给设备的 audio invite sdp部分为{}",sdpContent);
         try {
-//            CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-//                    : udpSipProvider.getNewCallId();
-//            dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
-//                logger.info("Ack 等待超时");
-//                // 释放 zlm 的推流端口
-//                mediaServerService.releaseSsrc(mediaServerItem.getId(), _ssrc );
-//                // todo 等待 broadcast invite的 200 sdp 超时  回复bye
-//                try {
-//                    cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId());
-//                } catch (SipException | InvalidArgumentException | ParseException e) {
-//                    logger.error("[命令发送失败] 语音广播 发送BYE: {}", e.getMessage());
-//                }
-//            }, 60 * 1000);
-//            testInviteRequestProcessor.responseBroadcastSdpACK(serverTransaction,
-//                    sdpContent,
-//                    device.getDeviceId(),
-//                    device.getHostAddress(),
-//                    device.getPort()
-//            );
-
             SIPResponse sipResponse = testInviteRequestProcessor.responseBroadcastSdpACK(request,
                     sdpContent,
                     sipConfig.getId(),
@@ -585,17 +565,6 @@ public class SIPCommander implements ISIPCommander {
                 sipResponse,
 				VideoStreamSessionManager.SessionType.broadcast);
 
-//            ResponseEvent responseEvent = (ResponseEvent) e.event;
-//            SIPResponse response = (SIPResponse) responseEvent.getResponse();
-//            streamSession.put(
-//                    device.getDeviceId(),
-//                    broadcastItem.getChannelId(),
-//                    "play",
-//                    broadcastItem.getRecv_stream(),
-//                    ssrcInfo.getSsrc(),
-//                    mediaServerItem.getId(),
-//                    response,
-//                    VideoStreamSessionManager.SessionType.play);
         }catch(SipException e){
             logger.warn("返回audio invite 失败");
             if(errorEvent != null){
@@ -791,8 +760,12 @@ public class SIPCommander implements ISIPCommander {
             return false;
         }
         Device device = storager.queryVideoDevice(deviceId);
+        if(device == null){
+            logger.error("无法找到该设备信息");
+            return false;
+        }
         SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(),device.getDeviceId(), "broadcast", broadcastItem.getRecv_stream());
-        logger.info("关闭语音广播:{}",ssrcTransaction);
+        logger.info("关闭语音广播: {}-{}",broadcastItem.getApp() , broadcastItem.getStream());
         MediaServerItem mediaServerItem = mediaServerService.getOne(broadcastItem.getMediaId());
         if(mediaServerItem == null){
             logger.warn("无法连接至语音广播对应的媒体服务器,不尝试从流媒体服务器中获取语音广播信息");

+ 1 - 7
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java

@@ -40,13 +40,7 @@ public interface IPlayService {
             SipSubscribe.Event errorEvent,
             Runnable timeoutCallback);
 
-    void openBroadcast(MediaServerItem mediaServerItem,
-                       String deviceId,
-                       String app,
-                       String audioStreamId,
-                       HookSubscribeForKey broadcastForInviteHook,
-                       NodeCallBack nodeCallBack,
-                       Runnable timeoutCallback);
+
 
     void openBroadcast(MediaServerItem mediaServerItem,
                        Device device,

+ 1 - 8
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@@ -171,10 +171,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
     }
 
 
-//    @Override
-//    public SSRCInfo startSendRtpServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) {
-//        return startSendRtpServer(mediaServerItem, streamId, null, ssrcCheck,isPlayback);
-//    }
 
 
     @Override
@@ -219,10 +215,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
         }
     }
 
-//    @Override
-//    public SSRCInfo startSendRtpServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) {
-//        return startSendRtpServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null);
-//    }
+
 
     @Override
     public void closeSendRtpServer(MediaServerItem mediaServerItem, String streamId){

+ 3 - 68
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -390,74 +390,7 @@ public class PlayServiceImpl implements IPlayService {
     }
 
 
-    public void openBroadcast(MediaServerItem mediaServerItem,
-                              String deviceId,
-                              String app,
-                              String audioStreamId,
-                              HookSubscribeForKey broadcastForInviteHook,
-                              NodeCallBack nodeCallBack,
-                              Runnable timeoutCallback){
-        logger.warn("[语音广播] 开语音广播");
-//        PlayResult playResult = new PlayResult();
-
-        Device device = redisCatchStorage.getDevice(deviceId);
-
-        // rtp语音通道创建完成,开始发送broadcast,
-        try {
-            cmder.audioBroadcastCmd(device);
-        }catch (InvalidArgumentException | SipException | ParseException e) {
-            logger.error("[命令发送失败] 发送broadcast中 errorMsg: {}", e.getMessage());
-            nodeCallBack.run(1,"[命令发送失败] 无法发送broadcast消息");
-        }
-        // 获取对应的音频流信息
-//        logger.info("webrtc stream {}",streamAuthorityInfo);
-
-
-        // 注册subScript事件
-        GBHookSubscribe.addInviteSubscribe(broadcastForInviteHook,(int code, JSONObject json, SIPRequest request)->{
-            logger.info("[语音广播] 接收到设备invite信息___订阅事件触发 JSONDATA: {}",json.toJSONString());
-            String streamId = null;
-            String ssrcStr = json.getString("ssrc");
-            ssrcStr = ssrcStr.replaceAll("\\s*|\r|\n|\t","");
-            SSRCInfo ssrcInfo = new SSRCInfo(31234,ssrcStr,audioStreamId);
-            if (mediaServerItem.isRtpEnable()) {
-                streamId = String.format("broadcast_%s", device.getDeviceId());
-            }
-            logger.info("[语音广播] 尝试创建rtp语音推流通道");
 
-            // TODO: 2023/3/7 开始下发invite信息给设备
-            try {
-                // 创建音频解码服务
-
-                // 向流媒体服务器申请创建 rtpServer 服务端口
-                ssrcInfo = mediaServerService.startSendRtpServer(mediaServerItem,app, streamId, audioStreamId,
-                        json.getString("addr"),
-                        json.getString("port"),
-                        ssrcStr,
-                        15*1000
-                );
-                if(ssrcInfo == null){
-                    logger.error("[zlm控制异常] 创建媒体流失败");
-                    nodeCallBack.run(2,"[zlm控制异常] 创建媒体流失败");
-                    return;
-                }
-                // 回复invite 200 信息至设备
-                cmder.sendBoradcastInviteCmd(request,mediaServerItem, ssrcInfo, device, null, null,
-                null,
-                null,
-                null);
-                //
-                nodeCallBack.run(0,"ok");
-
-            }catch(InvalidArgumentException |  SipException | ParseException e){
-                logger.error("[下发audio拉流invite失败]",e);
-//                    SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
-//                    eventResult.msg = "命令发送失败";
-//                    errorEvent.response(eventResult);
-            }
-        });
-        return ;
-    }
 
     public void openBroadcast(MediaServerItem mediaServerItem,
                               Device device,
@@ -1106,13 +1039,15 @@ public class PlayServiceImpl implements IPlayService {
                 callback.run(2, errJson, null);
                 return;
             }
+            // 更新
+            broadcastItem.setSsrc(ssrcInfo.getSsrc());
+            broadcastItem.setRecv_stream(steamId);
             // 回复invite 200 信息至设备
             cmder.sendBoradcastInviteCmd(broadcastItem.getRequest(), mediaServerItem, ssrcInfo, device, null, null,
                     null,
                     null,
                     null);
 
-            //
             callback.run(0, null,null);
         }catch(InvalidArgumentException |  SipException | ParseException e){
             logger.error("[下发audio拉流invite失败]",e);

+ 13 - 60
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -283,9 +283,9 @@ public class PlayController {
 	 * 开始语音广播
 	 * @param deviceId
 	 * @param channelId
-	 * @return
+	 * @return 获取webrtc推流地址
 	 */
-	@Operation(summary = "开始语音广播,获取参数")
+	@Operation(summary = "开始语音广播,获取webrtc推流地址")
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "channelId", description = "设备国标编号", required = true)
 	@Parameter(name = "waitTime", description = "设备国标编号", required = false)
@@ -343,60 +343,6 @@ public class PlayController {
 		wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
 		wvpResult.setData(resultData);
 		return wvpResult;
-		//示例 https://192.168.126.111:9443/index/api/webrtc?app=live&stream=test&type=play&sign=...
-		// 下发broadcast给设备
-//		playService.openBroadcast(
-//				mediaServerItem,
-//				device,
-//				waitTime,
-//				(int code, JSONObject json, SIPRequest request)->{
-//					// 0 ok,1 超时,2 异常
-//					// invite sdp , message data
-//					// request , null
-//					if(code == 1){
-//						logger.warn("invite超时");
-//						wvpResult.setCode(ErrorCode.ERR_TIMEOUT.getCode());
-//						wvpResult.setMsg(ErrorCode.ERR_TIMEOUT.getMsg());
-//					} else if (code == 2) {
-//						wvpResult.setCode(ErrorCode.ERROR100.getCode());
-//						wvpResult.setMsg((String) json.get("msg"));
-//					} else if (code == 0) {
-//						logger.info("收到设备invite信息: {}",json);
-//
-//						BroadcastItem broadcastItem = new BroadcastItem();
-//						broadcastItem.setMediaId(mediaServerItem.getId());
-//						broadcastItem.setDeviceId(deviceId);
-//						broadcastItem.setApp(app);
-//						broadcastItem.setStream(stream);
-//						broadcastItem.setIpcIp((String) json.get("addr"));
-//						broadcastItem.setIpcAudioPort((Integer) json.get("port"));
-//						broadcastItem.setSsrc((String) json.get("ssrc"));
-//						broadcastItem.setRequest(request);
-//						broadcastItem.setAudioFormats((Vector) json.get("audioFormats"));
-//						// 获取id
-//
-//						//存储invite信息和request信息至redis中
-//						gbStore.addBroadcastStore(
-//								"broadcast_"+deviceId,
-//								broadcastItem
-//						);
-////						if(redisCatchStorage.addBroadcastItem(
-////								deviceId,
-////								broadcastItem
-////						)){
-////							logger.info("语音对讲信息存储成功");
-////						}else{
-////							logger.warn("无法存储数据至zlm");
-////						}
-//						//设置过期时间
-//
-//					}
-//					msg.setData(wvpResult);
-//					resultHolder.invokeAllResult(msg);
-////					return result;
-//				}
-//		);
-//		return result;
 	}
 
 	@Operation(summary = "开始建立语音广播连接")
@@ -428,23 +374,26 @@ public class PlayController {
 			// 检查设备是否存在
 			Device device = storager.queryVideoDevice(deviceId);
 			if (device == null) {
+				logger.error("[语音广播] 无法找到设备 {}", deviceId);
 				// 无法找到设备
 				wvpResult.setCode(ErrorCode.ERROR404.getCode());
 				wvpResult.setMsg("无法找到设备");
 				msg.setData(wvpResult);
 				resultHolder.invokeAllResult(msg);
+				return result;
 			}
 
 			// 获取对应的媒体服务
+			logger.info("[语音广播] 开始为{}分配流媒体服务器", deviceId);
 			MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device);
-			logger.info("[语言广播] 分配的流媒体服务器为 {}", mediaServerItem.getId());
 			if (mediaServerItem == null) {
-				logger.warn("[语音广播] 无法连接至ZLM服务器");
+				logger.error("[语音广播] 无法连接至ZLM服务器");
 				wvpResult.setCode(ErrorCode.ERR_NOTFOUND_STREAM.getCode());
 				wvpResult.setMsg("无法连接至流媒体服务器");
 				msg.setData(wvpResult);
 				resultHolder.invokeAllResult(msg);
 			} else {
+				logger.info("[语音广播] 分配的流媒体服务器为 {}", mediaServerItem.getId());
 				playService.openBroadcast(
 						mediaServerItem,
 						device,
@@ -491,7 +440,6 @@ public class PlayController {
 												wvpResult.setMsg((String) _json.get("msg"));
 											} else if (_code == 0) {
 												logger.info("回复 invite 200 成功: {}", _json);
-
 												// 获取id
 												resultData.put("mediaId", mediaServerItem.getId());
 												wvpResult.setCode(ErrorCode.SUCCESS.getCode());
@@ -514,7 +462,6 @@ public class PlayController {
 //					return result;
 						}
 				);
-
 			}
 			// 获取zlm推流端口
 			// todo 回复 invite 200 给设备
@@ -545,6 +492,12 @@ public class PlayController {
 			wvpResult.setCode(ErrorCode.ERROR100.getCode());
 			wvpResult.setMsg("[命令发送失败] 停止语音广播, 发送BYE: {}");
 //			throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
+		} finally {
+			// 清除redis中的语音广播信息
+			logger.error("[停止对讲] 移除语音广播缓存信息");
+			gbStore.delBroadcastStore("broadcast_" + deviceId);
+			logger.error("[停止对讲] 移除语音广播redis缓存信息");
+			redisCatchStorage.deleteBroadcastItem(deviceId);
 		}
 		return wvpResult;
 	}

+ 2 - 1
web_src/config/index.js

@@ -3,7 +3,8 @@
 // see http://vuejs-templates.github.io/webpack for documentation.
 
 const path = require('path')
-const baseUrl = "https://127.0.0.1:19200"
+// const baseUrl = "https://127.0.0.1:19200"
+const baseUrl = "https://hofuniot.cn:29072"
 const ZLMServer = "https://192.168.1.60:29010"
 module.exports = {
   dev: {

+ 7 - 0
web_src/src/App.vue

@@ -6,6 +6,8 @@
 
 <script>
 import  userService from './components/service/UserService'
+import {mountFn, setAxios, testBroadCast} from "@/test";
+
 export default {
   name: 'app',
   data(){
@@ -32,6 +34,11 @@ export default {
   mounted(){
     //组件开始挂载时获取用户信息
     // this.getUserInfo();
+    // console.log(this.$axios);
+    // axios 挂载至window对象
+    window.axios = this.$axios;
+    window.testBroadCast = mountFn(this.$axios.axios);
+    // testBroadCast(34020000001320000257, "test", "live")
   },
   methods: {
     checkLogin(){

+ 45 - 24
web_src/src/assets/ZLMRTCClient.js

@@ -13,7 +13,8 @@ let ZLMRTCClient = (function (exports) {
 	  WEBRTC_ON_DATA_CHANNEL_ERR: 'WEBRTC_ON_DATA_CHANNEL_ERR',
 	  WEBRTC_ON_DATA_CHANNEL_MSG: 'WEBRTC_ON_DATA_CHANNEL_MSG',
 	  CAPTURE_STREAM_FAILED: 'CAPTURE_STREAM_FAILED',
-    CONNECT_FAIL: 'CONNECT_FAIL',// web端连接 zlm 失败
+		CONNECT_FAIL: 'CONNECT_FAIL',// web端连接 zlm 失败
+	  DEBUG_LOG: 'DEBUG_LOG',// zlm log 输出
 	};
 
   /// ^rtpmap:\d+ PCMU\/8000(\r\n|\n)?$/g
@@ -5787,12 +5788,28 @@ let ZLMRTCClient = (function (exports) {
 
 	let logger;
 	let errorLogger;
+
+
 	function setLogger() {
 	  /*eslint-disable */
 	  logger = console.log;
 	  errorLogger = console.error;
+	  // 日志使用事件触发
 	  /*eslint-enable */
 	}
+	function setLocalLogger(type, handle){
+		switch (type) {
+			case 'error':
+				errorLogger = handle;
+				break;
+			default :
+				logger = handle;
+		}
+		if(!errorLogger){
+			errorLogger = logger;
+		}
+	}
+
 	function log(message, ...optionalParams) {
 	  if (logger) {
 	    logger(message, ...optionalParams);
@@ -7891,12 +7908,11 @@ let ZLMRTCClient = (function (exports) {
         enableCodings: [0,8,9,13],
 	      usedatachannel: false
 	    };
-      console.log(options);
+		// 初始化log
+
+      log(options);
 	    this.options = Object.assign({}, defaults, options);
-      console.log(this.options );
-	    if (this.options.debug) {
-	      setLogger();
-	    }
+      log(this.options );
 
 	    this.e = {
 	      onicecandidate: this._onIceCandidate.bind(this),
@@ -7949,7 +7965,7 @@ let ZLMRTCClient = (function (exports) {
 
 	    this.pc.createOffer().then(desc => {
 	      log(this.TAG, 'offer:' + desc.sdp.toString());
-        // console.log(sdp);
+        // log(sdp);
         let sdp = this.reWriteAudioCodingSdp(desc.sdp.toString());
 
         // sdp = sdp.replace("a=rtpmap:9 G722/8000","");
@@ -8070,7 +8086,7 @@ let ZLMRTCClient = (function (exports) {
 
 	      this.pc.createOffer().then(desc => {
 	        // log(this.TAG, 'offer:', desc.sdp);
-          // console.log(desc.sdp.replace("a=rtpmap:0 PCMU/8000\n",""));
+          // log(desc.sdp.replace("a=rtpmap:0 PCMU/8000\n",""));
           let sdp = this.reWriteAudioCodingSdp(desc.sdp.toString());
           log(this.TAG, 'offer sdp                           :', sdp);
 
@@ -8218,12 +8234,13 @@ let ZLMRTCClient = (function (exports) {
 
     reWriteAudioCodingSdp(sdp){
       // 获取需要修改
-      console.log('change sdp ---------------------------------------------------------');
-      console.log(sdp);
+      log('change sdp ---------------------------------------------------------');
+      log(sdp);
+
       let _sdp = sdp;
       let audioEncodingKeys = Object.keys(audioEncoding);
-      console.log(audioEncoding);
-      // console.log(audioEncodingKeys);
+      log(audioEncoding);
+      // log(audioEncodingKeys);
       let ptReg = /m=audio\s9\sUDP\/TLS\/RTP\/SAVPF(\s\d+)+/
 
       _sdp=_sdp.replace(ptReg,`m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 ${this.options.enableCodings.join(' ')}`)
@@ -8231,29 +8248,33 @@ let ZLMRTCClient = (function (exports) {
       // _sdp=_sdp.replace("a=group:BUNDLE 0","a=group:BUNDLE 8")
       for(let key of audioEncodingKeys){
         if(this.options.enableCodings.includes(key)){
-          console.log(`已经启用的音频编码 ${audioEncoding[key].name}`);
+          log(`已经启用的音频编码 ${audioEncoding[key].name}`);
           // continue;
         }else{
-          // console.log(key);
-          // console.log(audioEncoding[key]);
-          // console.log(audioEncoding[key].replaceStr);
-          // console.log(_sdp)
-          // console.log(_sdp.replace(new RegExp(audioEncoding[key].replaceStr,"g"),""))
-          // console.log(_sdp.replace("a=rtpmap:0 PCMU/8000\n",""));
+          // log(key);
+          // log(audioEncoding[key]);
+          // log(audioEncoding[key].replaceStr);
+          // log(_sdp)
+          // log(_sdp.replace(new RegExp(audioEncoding[key].replaceStr,"g"),""))
+          // log(_sdp.replace("a=rtpmap:0 PCMU/8000\n",""));
           //
-          // console.log(Function.prototype.toString.call(_sdp.replace))
+          // log(Function.prototype.toString.call(_sdp.replace))
           _sdp = _sdp.replace(new RegExp(audioEncoding[key].replaceStr,"g"),"");
-          // console.log(_sdp)
+          // log(_sdp)
         }
       }
-      // console.log(_sdp);
+      // log(_sdp);
       return _sdp;
     }
 
     loadSdp(sdp){
 
     }
+
+	setLog(type, handle){
+		setLocalLogger(type, handle)
 	}
+}
 
 	const quickScan = [{
 	  'label': '4K(UHD)',
@@ -8344,8 +8365,8 @@ let ZLMRTCClient = (function (exports) {
 
 
 
-	console.log('build date:', BUILD_DATE);
-	console.log('version:', VERSION$1);
+	log('build date:', BUILD_DATE);
+	log('version:', VERSION$1);
 	const Events = Events$1;
 	const Media = media;
 	const Endpoint = RTCEndpoint;

+ 40 - 7
web_src/src/components/common/microphone.vue

@@ -74,6 +74,10 @@ export default {
      * 初始化app应用
      */
     initAudioApplications(){
+        // 防止出现多个实例
+      if(this.player && this.player.close && this.player.close.call){
+          this.player.close();
+      }
       this.player = null;
       this.mediaStream = null;
       this.audioConnected = false;
@@ -88,6 +92,7 @@ export default {
       this.player = null;
       this.mediaStream = null;
       this.stopBroadcast();
+
     },
     // 录音按钮按下
     mouseDownHandle() {
@@ -163,7 +168,16 @@ export default {
           }
         );
         this.player = player;
-        // console.log(player);
+          console.log('---888---');
+        console.log(this.player);
+          console.log('---888---');
+        this.player.setLog('info',(tag, msg) => {
+
+            console.log('收到zlm log');
+            if(enableDebug){
+                console.log(tag, msg);
+            }
+        })
         player.on(ZLMRTCClient.Events.CONNECT_FAIL, async (err) => {
           console.log('error', err);
           this.$notify.error({
@@ -185,13 +199,18 @@ export default {
             console.log('----------*********==>', state);
             // todo 分析sdp,看最终使用的是什么音频协议,传输给wvp下发invite 与创建zlm转流服务
             [err, res] = await handle(this.sendBroaderCast(this.pushConfig.stream,this.pushConfig.app));
-            let response = res.data;
-            if (err|| response.code!==0 ) {
+
+            if (err) {
               // console.log(err);
               this.audioStartFailed(err?err:response,`与设备交互错误信息失败${err?err.message:response.msg}`,true)
               this.mediaStream = null;
               return player.close();
             }
+            let response = res.data;
+            if(response.code!==0){
+              this.audioStartFailed(`与设备交互错误信息失败${response.msg}`,true)
+              return
+            }
             console.log(res);
             this.audioConnected = true;
             this.isRecording = true;
@@ -279,14 +298,28 @@ export default {
       });
     },
     // 停止语音广播
-    stopBroadcast() {
-      this.initAudioApplications();
+    async stopBroadcast() {
+
       let url = `/api/play/stopBroadcast`
       url += `?deviceId=${this.deviceId}&channelId=${this.channelId}`;
-      return this.$axios.axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'get',
         url: url
-      });
+      }));
+      console.log('关闭webrtc连接');
+      this.player.close();
+      this.player = null;
+      this.initAudioApplications();
+      if (err) {
+          this.$message.error(`关闭语音广播失败${err.message}`);
+          return -1;
+      }
+      let response = res.data;
+      if(response.code!==0){
+        this.$message.error(`关闭语音广播失败${response.msg}`);
+        return -1;
+      }
+      return 0;
     }
   }
 

+ 75 - 33
web_src/src/components/dialog/rtcPlayer.vue

@@ -1,6 +1,6 @@
 <template>
-    <div id="rtcPlayer">
-        <video id='webRtcPlayerBox' controls autoplay style="text-align:left;">
+    <div class="rtcPlayer">
+        <video ref="webrtcPlayer" class='webRtcPlayerBox' controls autoplay style="text-align:left;">
             Your browser is too old which doesn't support HTML5 video.
           </video>
     </div>
@@ -11,16 +11,33 @@ import ZLMRTCClient from "@/assets/ZLMRTCClient";
 import {webrtcEvent} from "@/map/eventMap";
 
 
-let webrtcPlayer = null;
+// let  = null;
 export default {
     name: 'rtcPlayer',
     data() {
         return {
           timer: null,
-          enableDebug: false,
+          webrtcPlayer: null,
         };
     },
-    props: ['videoUrl', 'error', 'hasaudio'],
+    props: {
+      videoUrl:{
+        type: String,
+        default: null
+      },
+      hasAudio:{
+        type: Boolean,
+        default: false
+      },
+      fps:{
+        type: Number,
+        default: 30
+      },
+      enableDebug:{
+        type: Boolean,
+        default: false
+      },
+    },
     mounted () {
       let paramUrl = decodeURIComponent(this.$route.params.url)
        this.$nextTick(() =>{
@@ -31,21 +48,41 @@ export default {
          this.play(this.videoUrl)
         })
     },
-    // watch:{
-    //     videoUrl(newData, oldData){
-    //         this.pause();
-    //         this.play(newData);
-    //     },
-    //     immediate:true
-    // },
+    watch:{
+        videoUrl(newData, oldData){
+          console.log("播放地址为: " + newData);
+            this.pause();
+            this.play(newData, this.hasAudio, this.fps, true);
+        },
+        immediate:true
+    },
     methods: {
-        play: function (url,hasAudio,fps,enableDebug = false) {
-          this.enableDebug = enableDebug;
+        play(url,hasAudio,fps,enableDebug = false) {
+          // this.enableDebug = enableDebug;
           if(enableDebug){
             console.log("播放地址为: " + url);
           }
-            webrtcPlayer = new ZLMRTCClient.Endpoint({
-                element: document.getElementById('webRtcPlayerBox'),// video 标签
+
+          // 获取ref webrtcPlayer
+          let webrtcPlayer = this.$refs.webrtcPlayer;
+          if(!webrtcPlayer){
+            console.log("无法获取到对应的 dom");
+            return
+          }
+          // console.log(webrtcPlayer);
+          webrtcPlayer.addEventListener("pause", () => {
+            if(enableDebug){
+              console.log("暂停");
+            }
+            this.eventCallback(webrtcEvent.paused.code, "暂停")
+          });
+
+          webrtcPlayer.addEventListener("error", (e)=>{
+            console.log('播放异常');
+            console.log(e);
+          })
+          this.webrtcPlayer = new ZLMRTCClient.Endpoint({
+                element: webrtcPlayer,// video 标签
                 debug: enableDebug,// 是否打印日志
                 zlmsdpUrl: url,//流地址
                 simulecast: false,
@@ -54,21 +91,30 @@ export default {
                 videoEnable: true,
                 recvOnly: true,
             });
-            webrtcPlayer.on(ZLMRTCClient.Events.CONNECT_FAIL, (err) => {
+
+          this.webrtcPlayer.setLog('info',(tag, msg) => {
+
+              console.log('收到zlm log');
+              if(enableDebug){
+                  console.log(tag, msg);
+              }
+          })
+          this.webrtcPlayer.on(ZLMRTCClient.Events.CONNECT_FAIL, (err) => {
               if(enableDebug){console.log('error', err);}
                 this.eventCallback(webrtcEvent.apiFail.code, err)
             });
-            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错
+          this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错
                 if(enableDebug){console.error('ICE 协商出错')}
                 this.eventCallback(webrtcEvent.iceError.code, "ICE 协商出错")
             });
 
-            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放
+            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放
                 if(enableDebug){console.log('播放成功',e.streams)}
                 this.eventCallback(webrtcEvent.played.code, "播放成功")
+                webrtcPlayer.play();
             });
 
-            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败
+            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败
                 if(enableDebug){console.error('offer anwser 交换失败',e)}
 
                 this.eventCallback(webrtcEvent.sdpFail.code, "offer anwser 交换失败")
@@ -76,13 +122,12 @@ export default {
                     if(enableDebug){console.log("流不存在")}
                     this.timer = setTimeout(()=>{
                         this.webrtcPlayer.close();
-                        this.play(url)
                     }, 100)
 
                 }
             });
 
-            webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流
+            this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流
 
                 // document.getElementById('selfVideo').srcObject=s;
                 this.eventCallback(webrtcEvent.localStream.code, "获取到了本地流")
@@ -90,9 +135,9 @@ export default {
 
         },
         pause: function () {
-            if (webrtcPlayer != null) {
-                webrtcPlayer.close();
-                webrtcPlayer = null;
+            if (this.webrtcPlayer != null) {
+                this.webrtcPlayer.close();
+                this.webrtcPlayer = null;
             }
 
         },
@@ -115,17 +160,14 @@ export default {
     .LodingTitle {
         min-width: 70px;
     }
-    #rtcPlayer{
+    .rtcPlayer{
         width: 100%;
+        height: 100%;
     }
-    #webRtcPlayerBox{
+    .rtcPlayer > video{
         width: 100%;
-        max-height: 56vh;
-        background-color: #000;
+        height: 100%;
     }
-    /* 隐藏logo */
-    /* .iconqingxiLOGO {
-        display: none !important;
-    } */
+
 
 </style>

+ 37 - 22
web_src/src/components/live.vue

@@ -28,7 +28,9 @@
                  :style="liveStyle" :class="{redborder:playerIdx == (i-1)}"
                  @click="playerIdx = (i-1)">
               <div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 30px;font-weight: bold;">{{ i }}</div>
-              <player ref="player" v-else :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"
+<!--              <player ref="player" v-else :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"-->
+<!--                      @destroy="destroy"/>-->
+              <rtc-player ref="player" @eventCallback="rtcPlayHandle" v-else :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"
                       @destroy="destroy"/>
             </div>
           </div>
@@ -43,19 +45,25 @@ import uiHeader from "../layout/UiHeader.vue";
 import player from './common/jessibuca.vue'
 import DeviceTree from './common/DeviceTree.vue'
 import {exitFullscreen, launchIntoFullscreen} from "@/until/dom";
+import rtcPlayer from "@/components/dialog/rtcPlayer.vue";
+import handle from "@/until/handle";
 
 
 export default {
   name: "live",
   components: {
-    uiHeader, player, DeviceTree
+    uiHeader, player, DeviceTree, rtcPlayer
   },
   data() {
     return {
       videoUrl: [''],
       spilt: 1,//分屏
       playerIdx: 0,//激活播放器
-
+      player: {
+        webRTC: ["rtc", "rtcs"],
+        flv: ["ws_flv", "wss_flv"],
+        h265: ["ws_flv", "wss_flv"]
+      },
       updateLooper: 0, //数据刷新轮训标志
       count: 15,
       total: 0,
@@ -134,7 +142,7 @@ export default {
 
     },
     //通知设备上传媒体流
-    sendDevicePush: function (itemData) {
+    async sendDevicePush(itemData) {
       // if (itemData.status === 0) {
       //   this.$message.error('设备离线!');
       //   return
@@ -146,30 +154,33 @@ export default {
       console.log(itemData);
       console.log("通知设备推流1:" + deviceId + " : " + channelId);
       let idxTmp = this.playerIdx
-      let that = this;
-      this.loading = true
-      this.$axios.axios({
+      this.loading = true;
+      let [err,res] = await handle(this.$axios.axios({
         method: 'get',
         url: '/api/play/start/' + deviceId + '/' + channelId
-      }).then(function (res) {
-        if (res.data.code === 0 && res.data.data) {
-          let videoUrl;
-          if (location.protocol === "https:") {
-            videoUrl = res.data.data.wss_flv;
-          } else {
-            videoUrl = res.data.data.ws_flv;
-          }
-          itemData.playUrl = videoUrl;
-          that.setPlayUrl(videoUrl, idxTmp);
+      }))
+      this.loading = false
+      if(err){
+        this.$message.error('推流失败');
+        return
+      }
+      if (res.data.code === 0 && res.data.data) {
+        let videoUrl;
+        if (location.protocol === "https:") {
+          videoUrl = res.data.data[this.player.webRTC[1]];
         } else {
-          that.$message.error(res.data.msg);
+          videoUrl = res.data.data[this.player.webRTC[0]];
         }
-      }).catch(function (e) {
-      }).finally(() => {
-        that.loading = false
-      });
+        console.log("推流成功:" + videoUrl)
+        itemData.playUrl = videoUrl;
+        this.setPlayUrl(videoUrl, idxTmp);
+      } else {
+        this.$message.error(res.data.msg);
+      }
+
     },
     setPlayUrl(url, idx) {
+      console.log("设置播放地址:" + url + " : " + idx);
       this.$set(this.videoUrl, idx, url)
       let _this = this
       setTimeout(() => {
@@ -230,6 +241,9 @@ export default {
         launchIntoFullscreen(el);
         this.isFullScreen= true;
       }
+    },
+    rtcPlayHandle(e){
+      console.log(e);
     }
   }
 };
@@ -260,6 +274,7 @@ export default {
   align-items: center;
   justify-content: center;
 }
+
 </style>
 <style>
 .videoList {

+ 2 - 0
web_src/src/main.js

@@ -47,6 +47,8 @@ Vue.use(Contextmenu);
 Vue.use(VCharts);
 
 let _axios = axios.axios;
+
+
 _axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : (window.baseUrl ? window.baseUrl : "");
 _axios.defaults.withCredentials = true;
 // api 返回401自动回登陆页面

+ 5 - 0
web_src/src/map/eventMap.js

@@ -14,6 +14,11 @@ export const webrtcEvent = {
       icon: 'played',
       code: 'onPlayed',
     },
+    paused: {
+        title: "视频暂停",
+        icon: 'paused',
+        code: 'onPaused',
+    },
     apiFail: {
       title: "api调用失败",
       icon: 'fail',

+ 35 - 0
web_src/src/test.js

@@ -0,0 +1,35 @@
+import handle from "@/until/handle";
+
+
+
+function sendBroaderCast(axios, deviceId, stream, app) {
+  let url = `/api/play/broadcast`
+  url += `?deviceId=${deviceId}&stream=${stream}&app=${app}`;
+  return axios({
+    method: 'get',
+    url: url
+  });
+}
+
+export async function testBroadCast(axios, deviceId, stream, app){
+  let err,res;
+  [err, res] = await handle(sendBroaderCast(axios, deviceId, stream, app));
+
+  if (err ) {
+    console.error(err);
+    return;
+  }
+  let response = res.data;
+  if(response.code!==0){
+    console.error(`与设备交互错误信息失败${response.msg}`,true)
+    return
+  }
+  console.log(res);
+  console.log('创建音视频通道成功');
+}
+
+export function mountFn(axios){
+  return function (deviceId, stream, app){
+    testBroadCast(axios, deviceId, stream, app);
+  }
+}