Przeglądaj źródła

临时上传版本,调整语音对讲部分代码

kindring 2 lat temu
rodzic
commit
e6d5cc1b91

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@@ -169,7 +169,7 @@ public interface ISIPCommander {
 	 *
 	 * @param device  视频设备
 	 */
-	void audioBroadcastCmd(Device device);
+//	void audioBroadcastCmd(Device device);
 	
 	/**
 	 * 语音广播
@@ -177,7 +177,7 @@ public interface ISIPCommander {
 	 * @param device  视频设备
 	 */
 	void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
-	void audioBroadcastCmd(Device device,String webrtcStreamId) throws InvalidArgumentException, SipException, ParseException;
+	void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException;
 	
 	/**
 	 * 音视频录像控制

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

@@ -749,9 +749,9 @@ public class SIPCommander implements ISIPCommander {
      *
      * @param device    视频设备
      */
-    @Override
-    public void audioBroadcastCmd(Device device) {
-    }
+//    @Override
+//    public void audioBroadcastCmd(Device device) {
+//    }
 
     /**
      * 语音广播
@@ -759,7 +759,7 @@ public class SIPCommander implements ISIPCommander {
      * @param device 视频设备
      */
     @Override
-    public void audioBroadcastCmd(Device device,String webrtcStreamId) throws InvalidArgumentException, SipException, ParseException {
+    public void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException {
 
         StringBuffer broadcastXml = new StringBuffer(200);
         String charset = device.getCharset();

+ 11 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java

@@ -900,11 +900,17 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 //boolean recvonly = false;
                 boolean mediaTransmissionTCP = false;
                 Boolean tcpActive = null;
+                Vector audioFormats = new Vector<>();
                 for (int i = 0; i < mediaDescriptions.size(); i++) {
                     MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i);
                     Media media = mediaDescription.getMedia();
-
                     Vector mediaFormats = media.getMediaFormats(false);
+                    logger.info("mediaFormats {}",mediaFormats);
+                    logger.info("getMediaType {}",media.getMediaType());
+                    if(media.getMediaType() == "audio"){
+                        logger.info("audio mediaFormats {}",mediaFormats);
+                        audioFormats.addAll(mediaFormats);
+                    }
                     if (mediaFormats.contains("8")) {
                         port = media.getMediaPort();
                         String protocol = media.getProtocol();
@@ -936,14 +942,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 String username = sdp.getOrigin().getUsername();
                 String addressStr = sdp.getConnection().getAddress();
                 logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
-			
-	            // todo 向zlm服务器申请语音推流转发通道
+                logger.info("设备invite MediaDescriptions {}",sdp.getMediaDescriptions(true));
+//                logger.info("设备invite MediaDescriptions {}",sdp.getMediaDescriptions(false));
+//                Vector mediaDescriptions = sdp.getMediaDescriptions(true);
 	            JSONObject subscribeKey = new JSONObject();
 	            subscribeKey.put("deviceId", username);
 	            subscribeKey.put("addr", addressStr);
 	            subscribeKey.put("port", port);
 	            subscribeKey.put("ssrc", ssrc);
-
+                subscribeKey.put("audioFormats", audioFormats);
 	            GBEventSubscribe.InviteEvent subscribe = GBHookSubscribe.sendStreamNotify(GB_Event.HOOK_BROADCAST_INVITE,subscribeKey);
 	            if (subscribe != null ) {
 	                subscribe.response(0, subscribeKey,request);

+ 6 - 4
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java

@@ -10,10 +10,7 @@ import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
-import com.genersoft.iot.vmp.service.bean.NodeCallBack;
-import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
-import com.genersoft.iot.vmp.service.bean.SSRCInfo;
+import com.genersoft.iot.vmp.service.bean.*;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
@@ -38,6 +35,11 @@ public interface IPlayService {
                        HookSubscribeForKey broadcastForInviteHook,
                        NodeCallBack nodeCallBack,
                        Runnable timeoutCallback);
+
+    void openBroadcast(MediaServerItem mediaServerItem,
+                       Device device,
+                       int waitTime,
+                       BroadcastCallback callback);
     MediaServerItem getNewMediaServerItem(Device device);
 
     /**

+ 13 - 0
src/main/java/com/genersoft/iot/vmp/service/bean/BroadcastCallback.java

@@ -0,0 +1,13 @@
+package com.genersoft.iot.vmp.service.bean;
+
+import com.alibaba.fastjson2.JSONObject;
+import gov.nist.javax.sip.message.SIPRequest;
+
+public interface BroadcastCallback {
+    /**
+     * @param code 0 ok,1 超时,2 异常
+     * @param json invite sdp , message data
+     * @param request SIPRequest
+     */
+    void run(int code, JSONObject json, SIPRequest request);
+}

+ 5 - 0
src/main/java/com/genersoft/iot/vmp/service/bean/NodeCallBack.java

@@ -1,5 +1,10 @@
 package com.genersoft.iot.vmp.service.bean;
 
+import com.alibaba.fastjson2.JSONObject;
+import gov.nist.javax.sip.message.SIPRequest;
+
 public interface NodeCallBack {
     void run(int code, String msg);
 }
+
+

+ 35 - 1
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -378,7 +378,7 @@ public class PlayServiceImpl implements IPlayService {
 
         // rtp语音通道创建完成,开始发送broadcast,
         try {
-            cmder.audioBroadcastCmd(device, audioStreamId);
+            cmder.audioBroadcastCmd(device);
         }catch (InvalidArgumentException | SipException | ParseException e) {
             logger.error("[命令发送失败] 发送broadcast中 errorMsg: {}", e.getMessage());
             nodeCallBack.run(1,"[命令发送失败] 无法发送broadcast消息");
@@ -432,6 +432,40 @@ public class PlayServiceImpl implements IPlayService {
         return ;
     }
 
+    public void openBroadcast(MediaServerItem mediaServerItem,
+                              Device device,
+                              int waitTime,
+                              BroadcastCallback callback){
+        logger.warn("[语音广播] 开语音广播");
+        JSONObject errJson = new JSONObject();
+        try {
+            cmder.audioBroadcastCmd(device);
+        }catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[命令发送失败] 发送broadcast中 errorMsg: {}", e.getMessage());
+            errJson.put("msg","[命令发送失败] 无法发送broadcast消息");
+            callback.run(2,errJson,null);
+        }
+        logger.warn("等待设备返回invite");
+        HookSubscribeForKey broadcastForInviteHook =  GBHookSubscribeFactory.on_broadcast_invite(device.getDeviceId());
+        // 创建计时器,计时结束未收到invite则自动进行失败处理
+        String timeOutTaskKey = UUID.randomUUID().toString();
+        dynamicTask.startDelay(timeOutTaskKey,()->{
+            // todo 发送 bye 通知给设备?
+            logger.warn("invite超时");
+            errJson.put("msg","等待设备语音invite信息超时");
+            callback.run(1,errJson,null);
+        },waitTime);
+
+        GBHookSubscribe.addInviteSubscribe(broadcastForInviteHook,
+        (int code, JSONObject json, SIPRequest request)->{
+            // invite信息返回
+            logger.info("[语音广播] 接收到设备invite信息___订阅事件触发 JSONDATA: {}",json.toJSONString());
+            // 取消计时器
+            dynamicTask.stop(timeOutTaskKey);
+            callback.run(0,json,request);
+        });
+
+    };
     @Override
     public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) {
         StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId);

+ 1 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/ErrorCode.java

@@ -12,6 +12,7 @@ public enum ErrorCode {
     ERROR401(401, "请登录后重新请求"),
     ERROR500(500, "系统异常"),
     VERSION_FAIL(9, "请求版本不对"),
+    ERR_TIMEOUT(599, "等待设备响应超时"),
     ERR_MEDIA(600, "流媒体服务异常"),
     ERR_NOTFOUND_STREAM(601, "无法找到流媒体"),
     ERR_Invite_fail(602, "流媒体交互异常"),

+ 77 - 21
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -31,6 +31,7 @@ import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import gov.nist.javax.sip.message.SIPRequest;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -353,22 +354,47 @@ public class PlayController {
 	}
 
 	/**
-	 * 获取webrtc推流地址
+	 * 开始语音广播,获取设备invite,音频编码协商 步骤1
 	 * @param deviceId
 	 * @param channelId
 	 * @return
 	 */
-	@Operation(summary = "获取webrtc推流地址")
+	@Operation(summary = "开始语音广播")
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "channelId", description = "设备国标编号", required = true)
-	@GetMapping("/getWebRtcAddr")
-	public WVPResult getWebRtcAddr(@RequestParam String deviceId,
-								   @RequestParam("channelId") String channelId) {
+	@Parameter(name = "waitTime", description = "设备国标编号", required = false)
+	@GetMapping("/startBroadcast")
+	public DeferredResult<WVPResult<String>> getWebRtcAddr(@RequestParam String deviceId,
+								   @RequestParam("channelId") String channelId,
+									@RequestParam(value = "waitTime",
+											required = false,
+											defaultValue = "5000") int waitTimeStr			   ) {
+
+		RequestMessage msg = new RequestMessage();
+		String key  = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId;
+		msg.setKey(key);
+		String uuid = UUID.randomUUID().toString();
+		msg.setId(uuid);
+		DeferredResult<WVPResult<String>> result = new DeferredResult<>(10*1000l);
+		WVPResult wvpResult = new WVPResult();
+		resultHolder.put(key, uuid, result);
+
+		String app = "audio";
+		String stream = "rtc_" + deviceId + "_" + channelId;
+		String type = "push";
+//		if(waitTimeStr.isEmpty() || waitTimeStr==""){
+//			waitTimeStr = "5000";
+//		}
+		int waitTime = waitTimeStr;
 
 		//首先判断设备是否正在对讲
 		if (redisCatchStorage.isBroadcastItem(deviceId)) {
 			// 设备正在进行语音对讲
-			return WVPResult.fail(ErrorCode.ERROR_Device_Busy);
+			wvpResult.setCode(ErrorCode.ERROR_Device_Busy.getCode());
+			wvpResult.setMsg(ErrorCode.ERROR_Device_Busy.getMsg());
+			msg.setData(wvpResult);
+			resultHolder.invokeAllResult(msg);
+			return result;
 		}
 
 		Device device = storager.queryVideoDevice(deviceId);
@@ -376,26 +402,56 @@ public class PlayController {
 
 		if (mediaServerItem == null) {
 			logger.error("流媒体未找到");
-			return WVPResult.fail(ErrorCode.ERR_MEDIA);
+			wvpResult.setCode(ErrorCode.ERR_MEDIA.getCode());
+			wvpResult.setMsg(ErrorCode.ERR_MEDIA.getMsg());
+			msg.setData(wvpResult);
+			resultHolder.invokeAllResult(msg);
+			return result;
 		}
-
-		Map<String, Object> result = new HashMap<>(16);
-		String app = "audio";
-		String stream = "rtc_" + deviceId + "_" + channelId;
-		String type = "push";
+		Map<String, Object> resultData = new HashMap<>(16);
 		LoginUser userInfo = SecurityUtils.getUserInfo();
 		String sign = Md5Utils.hash(userService.getUserByUsername(userInfo.getUsername()).getPushKey()); //获取推流鉴权密钥
 		//示例 https://192.168.126.111:9443/index/api/webrtc?app=live&stream=test&type=play&sign=...
 		String webRtcPushUrl = String.format("https://%s:%s/index/api/webrtc?app=%s&stream=%s&type=%s&sign=%s", mediaServerItem.getIp(), mediaServerItem.getHttpSSlPort(), app, stream, type,sign);
-		// 获取id
-		result.put("mediaId",mediaServerItem.getId());
-		result.put("app",app);
-		result.put("stream",stream);
-		result.put("type",type);
-		result.put("sign",sign);
-		result.put("webRtcPushUrl", webRtcPushUrl);
-		logger.info("获取webrtc推流地址:{}",webRtcPushUrl);
-		return WVPResult.success(result);
+		// 下发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);
+						// todo 存储invite信息和request信息至redis中
+						// 获取id
+						resultData.put("mediaId",mediaServerItem.getId());
+						resultData.put("app",app);
+						resultData.put("stream",stream);
+						resultData.put("type",type);
+						resultData.put("sign",sign);
+						resultData.put("webRtcPushUrl", webRtcPushUrl);
+						logger.info("获取webrtc推流地址:{}",webRtcPushUrl);
+						wvpResult.setCode(ErrorCode.SUCCESS.getCode());
+						wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
+						wvpResult.setData(resultData);
+					}
+					logger.warn("结束");
+					msg.setData(wvpResult);
+					resultHolder.invokeAllResult(msg);
+					logger.warn("结束2");
+//					return result;
+				}
+		);
+		return result;
 	}
 
 	@Operation(summary = "获取所有的ssrc")

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

@@ -59,7 +59,7 @@ export default {
       this.player = null;
       this.mediaStream = null;
       this.audioConnected = false;
-      this.getWebrtcAddress();
+      // this.getWebrtcAddress();
     },
     // 录音按钮按下
     mouseDownHandle() {
@@ -75,8 +75,9 @@ export default {
           this.$message.error("无法获取音频流!");
         }
       }else{
-        // 未创建webrtc 连接
-        this.startRecordAudio();
+        // 开始获取设备端的invite信息
+        this.startBroadcast();
+
       }
     },
     // 录音按钮抬起
@@ -172,8 +173,8 @@ export default {
       this.$message.error(msg);
       this.initAudioApplications();
     },
-    async getWebrtcAddress(){
-      let url = `/api/play/getWebRtcAddr`
+    async startBroadcast(){
+      let url = `/api/play/startBroadcast`
       url += `?deviceId=${this.deviceId}&channelId=${this.channelId}`;
       let [err,res] = await handle(this.$axios.axios({
         method: 'get',
@@ -184,6 +185,7 @@ export default {
         this.isLoadAddr = false;
         return 0;
       }
+      console.log(res);
       let data = res.data;
       if(data.code===0){
         this.pushConfig = data.data;