Commit 885842249fb6b264b0abf78668872d04bdc179ce

Authored by 648540858
1 parent 0a3d25fb

优化第三方对接接口

src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -154,6 +154,7 @@ public class VideoManagerConstants { @@ -154,6 +154,7 @@ public class VideoManagerConstants {
154 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; 154 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
155 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; 155 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
156 public static final String WVP_OTHER_SEND_RTP_INFO = "VMP_OTHER_SEND_RTP_INFO_"; 156 public static final String WVP_OTHER_SEND_RTP_INFO = "VMP_OTHER_SEND_RTP_INFO_";
  157 + public static final String WVP_OTHER_RECEIVE_RTP_INFO = "VMP_OTHER_RECEIVE_RTP_INFO_";
157 158
158 /** 159 /**
159 * Redis Const 160 * Redis Const
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.media.zlm; @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.media.zlm;
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
4 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
5 import com.genersoft.iot.vmp.common.StreamInfo; 5 import com.genersoft.iot.vmp.common.StreamInfo;
  6 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
6 import com.genersoft.iot.vmp.conf.UserSetting; 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 8 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
8 import com.genersoft.iot.vmp.gb28181.bean.*; 9 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -22,14 +23,13 @@ import com.genersoft.iot.vmp.service.*; @@ -22,14 +23,13 @@ import com.genersoft.iot.vmp.service.*;
22 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; 23 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
23 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 24 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
24 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 25 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
25 -import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;  
26 -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;  
27 -import com.genersoft.iot.vmp.vmanager.bean.StreamContent;  
28 -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; 26 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  27 +import com.genersoft.iot.vmp.vmanager.bean.*;
29 import org.slf4j.Logger; 28 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory; 29 import org.slf4j.LoggerFactory;
31 import org.springframework.beans.factory.annotation.Autowired; 30 import org.springframework.beans.factory.annotation.Autowired;
32 import org.springframework.beans.factory.annotation.Qualifier; 31 import org.springframework.beans.factory.annotation.Qualifier;
  32 +import org.springframework.data.redis.core.RedisTemplate;
33 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 33 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
34 import org.springframework.util.ObjectUtils; 34 import org.springframework.util.ObjectUtils;
35 import org.springframework.web.bind.annotation.*; 35 import org.springframework.web.bind.annotation.*;
@@ -106,6 +106,9 @@ public class ZLMHttpHookListener { @@ -106,6 +106,9 @@ public class ZLMHttpHookListener {
106 @Autowired 106 @Autowired
107 private AssistRESTfulUtils assistRESTfulUtils; 107 private AssistRESTfulUtils assistRESTfulUtils;
108 108
  109 + @Autowired
  110 + private RedisTemplate<Object, Object> redisTemplate;
  111 +
109 @Qualifier("taskExecutor") 112 @Qualifier("taskExecutor")
110 @Autowired 113 @Autowired
111 private ThreadPoolTaskExecutor taskExecutor; 114 private ThreadPoolTaskExecutor taskExecutor;
@@ -255,6 +258,21 @@ public class ZLMHttpHookListener { @@ -255,6 +258,21 @@ public class ZLMHttpHookListener {
255 result.setEnable_mp4(true); 258 result.setEnable_mp4(true);
256 } 259 }
257 } 260 }
  261 +
  262 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "*";
  263 + // 将信息写入redis中,以备后用
  264 + List<Object> scan = RedisUtil.scan(redisTemplate, receiveKey);
  265 + if (scan.size()>0) {
  266 + for (Object o : scan) {
  267 + String key = (String) o;
  268 + OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key);
  269 + if (otherRtpSendInfo != null && otherRtpSendInfo.getStream().equalsIgnoreCase(param.getStream())) {
  270 + result.setEnable_audio(true);
  271 + result.setEnable_mp4(true);
  272 + }
  273 + }
  274 + }
  275 +
258 if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { 276 if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
259 logger.info("推流时发现尚未设置录像路径,从assist服务中读取"); 277 logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
260 JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null); 278 JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java
@@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
11 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; 11 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
12 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 12 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
13 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout; 13 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
14 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
15 import com.genersoft.iot.vmp.service.IDeviceChannelService; 16 import com.genersoft.iot.vmp.service.IDeviceChannelService;
16 import com.genersoft.iot.vmp.service.IDeviceService; 17 import com.genersoft.iot.vmp.service.IDeviceService;
@@ -34,6 +35,7 @@ import org.springframework.web.bind.annotation.*; @@ -34,6 +35,7 @@ import org.springframework.web.bind.annotation.*;
34 import java.io.IOException; 35 import java.io.IOException;
35 import java.util.HashMap; 36 import java.util.HashMap;
36 import java.util.Map; 37 import java.util.Map;
  38 +import java.util.UUID;
37 39
38 @SuppressWarnings("rawtypes") 40 @SuppressWarnings("rawtypes")
39 @Tag(name = "第三方服务对接") 41 @Tag(name = "第三方服务对接")
@@ -120,12 +122,12 @@ public class RtpController { @@ -120,12 +122,12 @@ public class RtpController {
120 int localPort = zlmServerFactory.createRTPServer(mediaServerItem, stream, ssrcInt, null, false, tcpMode); 122 int localPort = zlmServerFactory.createRTPServer(mediaServerItem, stream, ssrcInt, null, false, tcpMode);
121 // 注册回调如果rtp收流超时则通过回调发送通知 123 // 注册回调如果rtp收流超时则通过回调发送通知
122 if (callBack != null) { 124 if (callBack != null) {
123 - HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, mediaServerItem.getId()); 125 + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(stream, String.valueOf(ssrcInt), mediaServerItem.getId());
124 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 126 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
125 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, 127 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
126 (mediaServerItemInUse, response)->{ 128 (mediaServerItemInUse, response)->{
127 if (stream.equals(response.getString("stream_id"))) { 129 if (stream.equals(response.getString("stream_id"))) {
128 - logger.info("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId); 130 + logger.info("[第三方服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId);
129 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); 131 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
130 OkHttpClient client = httpClientBuilder.build(); 132 OkHttpClient client = httpClientBuilder.build();
131 String url = callBack + "?callId=" + callId; 133 String url = callBack + "?callId=" + callId;
@@ -133,7 +135,7 @@ public class RtpController { @@ -133,7 +135,7 @@ public class RtpController {
133 try { 135 try {
134 client.newCall(request).execute(); 136 client.newCall(request).execute();
135 } catch (IOException e) { 137 } catch (IOException e) {
136 - logger.error("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); 138 + logger.error("[第三方服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e);
137 } 139 }
138 } 140 }
139 }); 141 });
@@ -143,6 +145,9 @@ public class RtpController { @@ -143,6 +145,9 @@ public class RtpController {
143 otherRtpSendInfo.setReceivePort(localPort); 145 otherRtpSendInfo.setReceivePort(localPort);
144 otherRtpSendInfo.setCallId(callId); 146 otherRtpSendInfo.setCallId(callId);
145 otherRtpSendInfo.setStream(stream); 147 otherRtpSendInfo.setStream(stream);
  148 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + stream;
  149 + // 将信息写入redis中,以备后用
  150 + redisTemplate.opsForValue().set(receiveKey, otherRtpSendInfo);
146 if (isSend != null && isSend) { 151 if (isSend != null && isSend) {
147 String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; 152 String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId;
148 // 预创建发流信息 153 // 预创建发流信息
@@ -160,7 +165,7 @@ public class RtpController { @@ -160,7 +165,7 @@ public class RtpController {
160 }, 15000); 165 }, 15000);
161 otherRtpSendInfo.setIp(mediaServerItem.getSdpIp()); 166 otherRtpSendInfo.setIp(mediaServerItem.getSdpIp());
162 otherRtpSendInfo.setPort(port); 167 otherRtpSendInfo.setPort(port);
163 - logger.info("[开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo); 168 + logger.info("[第三方服务对接->开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo);
164 } 169 }
165 return otherRtpSendInfo; 170 return otherRtpSendInfo;
166 } 171 }
@@ -173,6 +178,9 @@ public class RtpController { @@ -173,6 +178,9 @@ public class RtpController {
173 logger.info("[第三方服务对接->关闭收流] stream->{}", stream); 178 logger.info("[第三方服务对接->关闭收流] stream->{}", stream);
174 MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); 179 MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer();
175 zlmServerFactory.closeRtpServer(mediaServerItem,stream); 180 zlmServerFactory.closeRtpServer(mediaServerItem,stream);
  181 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + stream;
  182 + // 将信息写入redis中,以备后用
  183 + redisTemplate.delete(receiveKey);
176 } 184 }
177 185
178 @GetMapping(value = "/send/start") 186 @GetMapping(value = "/send/start")
@@ -187,9 +195,10 @@ public class RtpController { @@ -187,9 +195,10 @@ public class RtpController {
187 @Parameter(name = "onlyAudio", description = "是否只有音频", required = true) 195 @Parameter(name = "onlyAudio", description = "是否只有音频", required = true)
188 @Parameter(name = "isUdp", description = "是否为UDP", required = true) 196 @Parameter(name = "isUdp", description = "是否为UDP", required = true)
189 @Parameter(name = "streamType", description = "流类型,1为es流,2为ps流, 默认es流", required = false) 197 @Parameter(name = "streamType", description = "流类型,1为es流,2为ps流, 默认es流", required = false)
190 - public void sendRTP(String ssrc, String ip, Integer port, String app, String stream, String callId, Boolean onlyAudio, Boolean isUdp, @RequestParam(required = false)Integer streamType) {  
191 - logger.info("[第三方服务对接->发送流] ssrc->{}, ip->{}, port->{}, app->{}, stream->{}, callId->{}, onlyAudio->{}, streamType->{}",  
192 - ssrc, ip, port, app, stream, callId, onlyAudio, streamType == 1? "ES":"PS"); 198 + @Parameter(name = "pt", description = "rtp的pt", required = true)
  199 + public void sendRTP(String ssrc, String ip, Integer port, String app, String stream, String callId, Boolean onlyAudio, Boolean isUdp, @RequestParam(required = false)Integer streamType, Integer pt) {
  200 + logger.info("[第三方服务对接->发送流] ssrc->{}, ip->{}, port->{}, app->{}, stream->{}, callId->{}, onlyAudio->{}, streamType->{}, pt->{}",
  201 + ssrc, ip, port, app, stream, callId, onlyAudio, streamType == 1? "ES":"PS", pt);
193 if (ObjectUtils.isEmpty(streamType)) { 202 if (ObjectUtils.isEmpty(streamType)) {
194 streamType = 1; 203 streamType = 1;
195 } 204 }
@@ -197,7 +206,7 @@ public class RtpController { @@ -197,7 +206,7 @@ public class RtpController {
197 String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; 206 String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId;
198 OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key); 207 OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key);
199 if (sendInfo != null) { 208 if (sendInfo != null) {
200 - zlmServerFactory.releasePort(mediaServerItem, sendInfo.getCallId()); 209 + zlmServerFactory.releasePort(mediaServerItem, callId);
201 }else { 210 }else {
202 sendInfo = new OtherRtpSendInfo(); 211 sendInfo = new OtherRtpSendInfo();
203 } 212 }
@@ -218,20 +227,52 @@ public class RtpController { @@ -218,20 +227,52 @@ public class RtpController {
218 param.put("src_port", sendInfo.getPort()); 227 param.put("src_port", sendInfo.getPort());
219 param.put("use_ps", streamType==2 ? "1" : "0"); 228 param.put("use_ps", streamType==2 ? "1" : "0");
220 param.put("only_audio", onlyAudio ? "1" : "0"); 229 param.put("only_audio", onlyAudio ? "1" : "0");
221 -  
222 - JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, param);  
223 - if (jsonObject.getInteger("code") == 0) {  
224 - logger.info("[第三方服务对接->发送流] 发流成功,callId->{}", callId);  
225 - redisTemplate.opsForValue().set(key, sendInfo); 230 + param.put("pt", pt);
  231 +
  232 + dynamicTask.stop(key);
  233 + Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream);
  234 + if (streamReady) {
  235 + logger.info("[第三方服务对接->发送流] 流存在,开始发流,callId->{}", callId);
  236 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, param);
  237 + if (jsonObject.getInteger("code") == 0) {
  238 + logger.info("[第三方服务对接->发送流] 发流成功,callId->{}", callId);
  239 + redisTemplate.opsForValue().set(key, sendInfo);
  240 + }else {
  241 + redisTemplate.delete(key);
  242 + logger.info("[第三方服务对接->发送流] 发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));
  243 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[发流失败] " + jsonObject.getString("msg"));
  244 + }
226 }else { 245 }else {
227 - redisTemplate.delete(key);  
228 - logger.info("[第三方服务对接->发送流] 发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));  
229 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "[发流失败] " + jsonObject.getString("msg")); 246 + logger.info("[第三方服务对接->发送流] 流不存在,等待流上线,callId->{}", callId);
  247 + String uuid = UUID.randomUUID().toString();
  248 + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(app, stream, true, "rtsp", mediaServerItem.getId());
  249 + dynamicTask.startDelay(uuid, ()->{
  250 + logger.info("[第三方服务对接->发送流] 等待流上线超时 callId->{}", callId);
  251 + redisTemplate.delete(key);
  252 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
  253 + }, 10000);
  254 +
  255 + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
  256 + OtherRtpSendInfo finalSendInfo = sendInfo;
  257 + hookSubscribe.addSubscribe(hookSubscribeForStreamChange,
  258 + (mediaServerItemInUse, response)->{
  259 + dynamicTask.stop(uuid);
  260 + logger.info("[第三方服务对接->发送流] 流上线,开始发流 callId->{}", callId);
  261 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, param);
  262 + System.out.println("========发流结果==========");
  263 + System.out.println(jsonObject);
  264 + if (jsonObject.getInteger("code") == 0) {
  265 + logger.info("[第三方服务对接->发送流] 发流成功,callId->{}", callId);
  266 + redisTemplate.opsForValue().set(key, finalSendInfo);
  267 + }else {
  268 + redisTemplate.delete(key);
  269 + logger.info("[第三方服务对接->发送流] 发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));
  270 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[发流失败] " + jsonObject.getString("msg"));
  271 + }
  272 + });
230 } 273 }
231 } 274 }
232 275
233 -  
234 -  
235 @GetMapping(value = "/send/stop") 276 @GetMapping(value = "/send/stop")
236 @ResponseBody 277 @ResponseBody
237 @Operation(summary = "关闭发送流") 278 @Operation(summary = "关闭发送流")