Commit b64f32080537840cfd772d14dfc1df1ccddc4330

Authored by 648540858
1 parent c9164330

优化发流逻辑

sql/2.6.8升级2.6.9.sql
@@ -205,6 +205,12 @@ alter table media_server @@ -205,6 +205,12 @@ alter table media_server
205 change hookIp hook_ip varchar(50) not null; 205 change hookIp hook_ip varchar(50) not null;
206 206
207 alter table media_server 207 alter table media_server
  208 + add send_rtp_port_range varchar(50) not null;
  209 +
  210 +alter table media_server
  211 + add column send_rtp_port_range varchar(50) default null;
  212 +
  213 +alter table media_server
208 change sdpIp sdp_ip varchar(50) not null; 214 change sdpIp sdp_ip varchar(50) not null;
209 215
210 alter table media_server 216 alter table media_server
sql/初始化.sql
@@ -153,6 +153,7 @@ create table wvp_media_server ( @@ -153,6 +153,7 @@ create table wvp_media_server (
153 secret character varying(50), 153 secret character varying(50),
154 rtp_enable bool default false, 154 rtp_enable bool default false,
155 rtp_port_range character varying(50), 155 rtp_port_range character varying(50),
  156 + send_rtp_port_range character varying(50),
156 record_assist_port integer, 157 record_assist_port integer,
157 default_server bool default false, 158 default_server bool default false,
158 create_time character varying(50), 159 create_time character varying(50),
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -75,6 +75,9 @@ public class MediaConfig{ @@ -75,6 +75,9 @@ public class MediaConfig{
75 @Value("${media.rtp.port-range}") 75 @Value("${media.rtp.port-range}")
76 private String rtpPortRange; 76 private String rtpPortRange;
77 77
  78 + @Value("${media.rtp.send-port-range}")
  79 + private String rtpSendPortRange;
  80 +
78 @Value("${media.record-assist-port:0}") 81 @Value("${media.record-assist-port:0}")
79 private Integer recordAssistPort = 0; 82 private Integer recordAssistPort = 0;
80 83
@@ -206,6 +209,7 @@ public class MediaConfig{ @@ -206,6 +209,7 @@ public class MediaConfig{
206 mediaServerItem.setSecret(secret); 209 mediaServerItem.setSecret(secret);
207 mediaServerItem.setRtpEnable(rtpEnable); 210 mediaServerItem.setRtpEnable(rtpEnable);
208 mediaServerItem.setRtpPortRange(rtpPortRange); 211 mediaServerItem.setRtpPortRange(rtpPortRange);
  212 + mediaServerItem.setSendRtpPortRange(rtpSendPortRange);
209 mediaServerItem.setRecordAssistPort(recordAssistPort); 213 mediaServerItem.setRecordAssistPort(recordAssistPort);
210 mediaServerItem.setHookAliveInterval(30.00f); 214 mediaServerItem.setHookAliveInterval(30.00f);
211 215
@@ -215,6 +219,14 @@ public class MediaConfig{ @@ -215,6 +219,14 @@ public class MediaConfig{
215 return mediaServerItem; 219 return mediaServerItem;
216 } 220 }
217 221
  222 + public String getRtpSendPortRange() {
  223 + return rtpSendPortRange;
  224 + }
  225 +
  226 + public void setRtpSendPortRange(String rtpSendPortRange) {
  227 + this.rtpSendPortRange = rtpSendPortRange;
  228 + }
  229 +
218 private boolean isValidIPAddress(String ipAddress) { 230 private boolean isValidIPAddress(String ipAddress) {
219 if ((ipAddress != null) && (!ipAddress.isEmpty())) { 231 if ((ipAddress != null) && (!ipAddress.isEmpty())) {
220 return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress); 232 return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -140,15 +140,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -140,15 +140,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
140 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); 140 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
141 }); 141 });
142 }else { 142 }else {
143 - // 如果是非严格模式,需要关闭端口占用  
144 - JSONObject startSendRtpStreamResult = null;  
145 - if (sendRtpItem.getLocalPort() != 0) {  
146 - if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) {  
147 - startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
148 - }  
149 - }else {  
150 - startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
151 - } 143 + JSONObject startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
152 if (startSendRtpStreamResult != null) { 144 if (startSendRtpStreamResult != null) {
153 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader); 145 startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
154 } 146 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -151,8 +151,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -151,8 +151,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
151 // 可能是设备发送的停止 151 // 可能是设备发送的停止
152 SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); 152 SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
153 if (ssrcTransaction == null) { 153 if (ssrcTransaction == null) {
154 - logger.info("[收到bye] 但是无法获取推流信息和发流信息,忽略此请求");  
155 - logger.info(request.toString());  
156 return; 154 return;
157 } 155 }
158 logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); 156 logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -346,9 +346,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -346,9 +346,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
346 } 346 }
347 logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc); 347 logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc);
348 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 348 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
349 - device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> {  
350 - return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;  
351 - }); 349 + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp());
352 350
353 if (tcpActive != null) { 351 if (tcpActive != null) {
354 sendRtpItem.setTcpActive(tcpActive); 352 sendRtpItem.setTcpActive(tcpActive);
@@ -554,9 +552,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -554,9 +552,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
554 if (streamReady != null && streamReady) { 552 if (streamReady != null && streamReady) {
555 // 自平台内容 553 // 自平台内容
556 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 554 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
557 - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{  
558 - return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;  
559 - }); 555 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
560 556
561 if (sendRtpItem == null) { 557 if (sendRtpItem == null) {
562 logger.warn("服务器端口资源不足"); 558 logger.warn("服务器端口资源不足");
@@ -595,9 +591,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -595,9 +591,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
595 if (streamReady != null && streamReady) { 591 if (streamReady != null && streamReady) {
596 // 自平台内容 592 // 自平台内容
597 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, 593 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
598 - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback ->{  
599 - return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;  
600 - }); 594 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
601 595
602 if (sendRtpItem == null) { 596 if (sendRtpItem == null) {
603 logger.warn("服务器端口资源不足"); 597 logger.warn("服务器端口资源不足");
@@ -713,9 +707,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -713,9 +707,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
713 dynamicTask.stop(callIdHeader.getCallId()); 707 dynamicTask.stop(callIdHeader.getCallId());
714 if (serverId.equals(userSetting.getServerId())) { 708 if (serverId.equals(userSetting.getServerId())) {
715 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, 709 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
716 - app, stream, channelId, mediaTransmissionTCP, platform.isRtcp(), ssrcFromCallback -> {  
717 - return redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, null, callIdHeader.getCallId()) != null;  
718 - }); 710 + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());
719 711
720 if (sendRtpItem == null) { 712 if (sendRtpItem == null) {
721 logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); 713 logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足");
src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm;
  2 +
  3 +import com.genersoft.iot.vmp.conf.UserSetting;
  4 +import com.genersoft.iot.vmp.media.zlm.dto.MediaSendRtpPortInfo;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.data.redis.core.RedisTemplate;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +@Component
  12 +public class SendRtpPortManager {
  13 +
  14 + private final static Logger logger = LoggerFactory.getLogger(SendRtpPortManager.class);
  15 +
  16 + @Autowired
  17 + private UserSetting userSetting;
  18 +
  19 + @Autowired
  20 + private RedisTemplate<Object, Object> redisTemplate;
  21 +
  22 + private final String KEY = "VM_MEDIA_SEND_RTP_PORT_RANGE_";
  23 +
  24 +
  25 + public void initServerPort(String mediaServerId, int startPort, int endPort){
  26 + String key = KEY + userSetting.getServerId() + "_" + mediaServerId;
  27 + MediaSendRtpPortInfo mediaSendRtpPortInfo = new MediaSendRtpPortInfo(startPort, endPort, mediaServerId);
  28 + redisTemplate.opsForValue().set(key, mediaSendRtpPortInfo);
  29 + }
  30 +
  31 + public int getNextPort(String mediaServerId) {
  32 + String key = KEY + userSetting.getServerId() + "_" + mediaServerId;
  33 + MediaSendRtpPortInfo mediaSendRtpPortInfo = (MediaSendRtpPortInfo)redisTemplate.opsForValue().get(key);
  34 + if (mediaSendRtpPortInfo == null) {
  35 + logger.warn("[发送端口管理] 获取{}的发送端口时未找到端口信息", mediaSendRtpPortInfo);
  36 + return 0;
  37 + }
  38 + int port;
  39 + if (mediaSendRtpPortInfo.getCurrent() %2 != 0) {
  40 + port = mediaSendRtpPortInfo.getCurrent() + 1;
  41 + }else {
  42 + port = mediaSendRtpPortInfo.getCurrent() + 2;
  43 + }
  44 + if (port > mediaSendRtpPortInfo.getEnd()) {
  45 + if (mediaSendRtpPortInfo.getStart() %2 != 0) {
  46 + port = mediaSendRtpPortInfo.getStart() + 1;
  47 + }else {
  48 + port = mediaSendRtpPortInfo.getStart();
  49 + }
  50 + }
  51 + mediaSendRtpPortInfo.setCurrent(port);
  52 + redisTemplate.opsForValue().set(key, mediaSendRtpPortInfo);
  53 + return port;
  54 + }
  55 +}
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
1 package com.genersoft.iot.vmp.media.zlm; 1 package com.genersoft.iot.vmp.media.zlm;
2 2
3 import com.alibaba.fastjson2.JSON; 3 import com.alibaba.fastjson2.JSON;
4 -import com.alibaba.fastjson2.JSONArray;  
5 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
6 import com.genersoft.iot.vmp.common.CommonCallback; 5 import com.genersoft.iot.vmp.common.CommonCallback;
7 import com.genersoft.iot.vmp.conf.UserSetting; 6 import com.genersoft.iot.vmp.conf.UserSetting;
8 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; 7 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
9 -import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;  
10 -import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;  
11 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 8 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
12 -import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;  
13 -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRtpServerTimeoutHookParam;  
14 import org.slf4j.Logger; 9 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory; 10 import org.slf4j.LoggerFactory;
16 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
17 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
18 13
19 -import java.util.*; 14 +import java.util.HashMap;
  15 +import java.util.Map;
20 16
21 @Component 17 @Component
22 public class ZLMRTPServerFactory { 18 public class ZLMRTPServerFactory {
@@ -32,68 +28,9 @@ public class ZLMRTPServerFactory { @@ -32,68 +28,9 @@ public class ZLMRTPServerFactory {
32 @Autowired 28 @Autowired
33 private ZlmHttpHookSubscribe hookSubscribe; 29 private ZlmHttpHookSubscribe hookSubscribe;
34 30
35 - private int[] portRangeArray = new int[2]; 31 + @Autowired
  32 + private SendRtpPortManager sendRtpPortManager;
36 33
37 - public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {  
38 - if (endPort <= startPort) {  
39 - return -1;  
40 - }  
41 - if (usedFreelist == null) {  
42 - usedFreelist = new ArrayList<>();  
43 - }  
44 - JSONObject listRtpServerJsonResult = zlmresTfulUtils.listRtpServer(mediaServerItem);  
45 - if (listRtpServerJsonResult != null) {  
46 - JSONArray data = listRtpServerJsonResult.getJSONArray("data");  
47 - if (data != null) {  
48 - for (int i = 0; i < data.size(); i++) {  
49 - JSONObject dataItem = data.getJSONObject(i);  
50 - usedFreelist.add(dataItem.getInteger("port"));  
51 - }  
52 - }  
53 - }  
54 -  
55 - Map<String, Object> param = new HashMap<>();  
56 - int result = -1;  
57 - // 设置推流端口  
58 - if (startPort%2 == 1) {  
59 - startPort ++;  
60 - }  
61 - boolean checkPort = false;  
62 - for (int i = startPort; i < endPort + 1; i+=2) {  
63 - if (!usedFreelist.contains(i)){  
64 - checkPort = true;  
65 - startPort = i;  
66 - break;  
67 - }  
68 - }  
69 - if (!checkPort) {  
70 - logger.warn("未找到节点{}上范围[{}-{}]的空闲端口", mediaServerItem.getId(), startPort, endPort);  
71 - return -1;  
72 - }  
73 - param.put("port", startPort);  
74 - String stream = UUID.randomUUID().toString();  
75 - param.put("enable_tcp", 1);  
76 - param.put("stream_id", stream);  
77 -// param.put("port", 0);  
78 - JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);  
79 -  
80 - if (openRtpServerResultJson != null) {  
81 - if (openRtpServerResultJson.getInteger("code") == 0) {  
82 - result= openRtpServerResultJson.getInteger("port");  
83 - Map<String, Object> closeRtpServerParam = new HashMap<>();  
84 - closeRtpServerParam.put("stream_id", stream);  
85 - zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);  
86 - }else {  
87 - usedFreelist.add(startPort);  
88 - startPort +=2;  
89 - result = getFreePort(mediaServerItem, startPort, endPort,usedFreelist);  
90 - }  
91 - }else {  
92 - // 检查ZLM状态  
93 - logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port"));  
94 - }  
95 - return result;  
96 - }  
97 34
98 /** 35 /**
99 * 开启rtpServer 36 * 开启rtpServer
@@ -222,16 +159,14 @@ public class ZLMRTPServerFactory { @@ -222,16 +159,14 @@ public class ZLMRTPServerFactory {
222 * @return SendRtpItem 159 * @return SendRtpItem
223 */ 160 */
224 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, 161 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
225 - String deviceId, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){ 162 + String deviceId, String channelId, boolean tcp, boolean rtcp){
226 163
227 // 默认为随机端口 164 // 默认为随机端口
228 int localPort = 0; 165 int localPort = 0;
229 if (userSetting.getGbSendStreamStrict()) { 166 if (userSetting.getGbSendStreamStrict()) {
230 - if (userSetting.getGbSendStreamStrict()) {  
231 - localPort = keepPort(serverItem, ssrc, localPort, callback);  
232 - if (localPort == 0) {  
233 - return null;  
234 - } 167 + localPort = sendRtpPortManager.getNextPort(serverItem.getId());
  168 + if (localPort == 0) {
  169 + return null;
235 } 170 }
236 } 171 }
237 SendRtpItem sendRtpItem = new SendRtpItem(); 172 SendRtpItem sendRtpItem = new SendRtpItem();
@@ -261,11 +196,11 @@ public class ZLMRTPServerFactory { @@ -261,11 +196,11 @@ public class ZLMRTPServerFactory {
261 * @return SendRtpItem 196 * @return SendRtpItem
262 */ 197 */
263 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, 198 public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
264 - String app, String stream, String channelId, boolean tcp, boolean rtcp, KeepPortCallback callback){ 199 + String app, String stream, String channelId, boolean tcp, boolean rtcp){
265 // 默认为随机端口 200 // 默认为随机端口
266 int localPort = 0; 201 int localPort = 0;
267 if (userSetting.getGbSendStreamStrict()) { 202 if (userSetting.getGbSendStreamStrict()) {
268 - localPort = keepPort(serverItem, ssrc, localPort, callback); 203 + localPort = sendRtpPortManager.getNextPort(serverItem.getId());
269 if (localPort == 0) { 204 if (localPort == 0) {
270 return null; 205 return null;
271 } 206 }
@@ -286,58 +221,6 @@ public class ZLMRTPServerFactory { @@ -286,58 +221,6 @@ public class ZLMRTPServerFactory {
286 return sendRtpItem; 221 return sendRtpItem;
287 } 222 }
288 223
289 - public interface KeepPortCallback{  
290 - Boolean keep(String ssrc);  
291 - }  
292 -  
293 - /**  
294 - * 保持端口,直到需要需要发流时再释放  
295 - */  
296 - public int keepPort(MediaServerItem serverItem, String ssrc, int localPort, KeepPortCallback keepPortCallback) {  
297 - Map<String, Object> param = new HashMap<>(3);  
298 - param.put("port", localPort);  
299 - param.put("enable_tcp", 1);  
300 - param.put("stream_id", ssrc);  
301 - JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param);  
302 - if (jsonObject.getInteger("code") == 0) {  
303 - localPort = jsonObject.getInteger("port");  
304 - HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());  
305 - // 订阅 zlm启动事件, 新的zlm也会从这里进入系统  
306 - Integer finalLocalPort = localPort;  
307 - hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,  
308 - (MediaServerItem mediaServerItem, HookParam hookParam)->{  
309 - logger.info("[上级点播] {}->监听端口到期继续保持监听: {}", ssrc, finalLocalPort);  
310 - OnRtpServerTimeoutHookParam rtpServerTimeoutHookParam = (OnRtpServerTimeoutHookParam) hookParam;  
311 - if (ssrc.equals(rtpServerTimeoutHookParam.getSsrc())) {  
312 - if (keepPortCallback.keep(ssrc)) {  
313 - logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);  
314 - keepPort(serverItem, ssrc, finalLocalPort, keepPortCallback);  
315 - }else {  
316 - logger.info("[上级点播] {}->发送取消,无需继续监听", ssrc);  
317 - releasePort(serverItem, ssrc);  
318 - }  
319 - }  
320 - });  
321 - logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort);  
322 - return localPort;  
323 - }else {  
324 - logger.info("[上级点播] 监听端口失败: {}->{}", ssrc, localPort);  
325 - return 0;  
326 - }  
327 - }  
328 -  
329 - /**  
330 - * 释放保持的端口  
331 - */  
332 - public boolean releasePort(MediaServerItem serverItem, String ssrc) {  
333 - logger.info("[上级点播] {}->释放监听端口", ssrc);  
334 - boolean closeRTPServerResult = closeRtpServer(serverItem, ssrc);  
335 - HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());  
336 - // 订阅 zlm启动事件, 新的zlm也会从这里进入系统  
337 - hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);  
338 - return closeRTPServerResult;  
339 - }  
340 -  
341 /** 224 /**
342 * 调用zlm RESTFUL API —— startSendRtp 225 * 调用zlm RESTFUL API —— startSendRtp
343 */ 226 */
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaSendRtpPortInfo.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +public class MediaSendRtpPortInfo {
  4 +
  5 + private int start;
  6 + private int end;
  7 + private String mediaServerId;
  8 +
  9 + private int current;
  10 +
  11 +
  12 + public MediaSendRtpPortInfo(int start, int end, String mediaServerId) {
  13 + this.start = start;
  14 + this.current = start;
  15 + this.end = end;
  16 + this.mediaServerId = mediaServerId;
  17 + }
  18 +
  19 + public int getStart() {
  20 + return start;
  21 + }
  22 +
  23 + public void setStart(int start) {
  24 + this.start = start;
  25 + }
  26 +
  27 + public int getEnd() {
  28 + return end;
  29 + }
  30 +
  31 + public void setEnd(int end) {
  32 + this.end = end;
  33 + }
  34 +
  35 + public String getMediaServerId() {
  36 + return mediaServerId;
  37 + }
  38 +
  39 + public void setMediaServerId(String mediaServerId) {
  40 + this.mediaServerId = mediaServerId;
  41 + }
  42 +
  43 + public int getCurrent() {
  44 + return current;
  45 + }
  46 +
  47 + public void setCurrent(int current) {
  48 + this.current = current;
  49 + }
  50 +}
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
@@ -62,6 +62,9 @@ public class MediaServerItem{ @@ -62,6 +62,9 @@ public class MediaServerItem{
62 @Schema(description = "多端口RTP收流端口范围") 62 @Schema(description = "多端口RTP收流端口范围")
63 private String rtpPortRange; 63 private String rtpPortRange;
64 64
  65 + @Schema(description = "RTP发流端口范围")
  66 + private String sendRtpPortRange;
  67 +
65 @Schema(description = "assist服务端口") 68 @Schema(description = "assist服务端口")
66 private int recordAssistPort; 69 private int recordAssistPort;
67 70
@@ -297,4 +300,12 @@ public class MediaServerItem{ @@ -297,4 +300,12 @@ public class MediaServerItem{
297 public void setHookAliveInterval(Float hookAliveInterval) { 300 public void setHookAliveInterval(Float hookAliveInterval) {
298 this.hookAliveInterval = hookAliveInterval; 301 this.hookAliveInterval = hookAliveInterval;
299 } 302 }
  303 +
  304 + public String getSendRtpPortRange() {
  305 + return sendRtpPortRange;
  306 + }
  307 +
  308 + public void setSendRtpPortRange(String sendRtpPortRange) {
  309 + this.sendRtpPortRange = sendRtpPortRange;
  310 + }
300 } 311 }
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -11,10 +11,7 @@ import com.genersoft.iot.vmp.conf.UserSetting; @@ -11,10 +11,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
11 import com.genersoft.iot.vmp.conf.exception.ControllerException; 11 import com.genersoft.iot.vmp.conf.exception.ControllerException;
12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
13 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; 13 import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
14 -import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;  
15 -import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;  
16 -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;  
17 -import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; 14 +import com.genersoft.iot.vmp.media.zlm.*;
18 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
19 import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData; 16 import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
20 import com.genersoft.iot.vmp.service.IInviteStreamService; 17 import com.genersoft.iot.vmp.service.IInviteStreamService;
@@ -71,6 +68,9 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -71,6 +68,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
71 private UserSetting userSetting; 68 private UserSetting userSetting;
72 69
73 @Autowired 70 @Autowired
  71 + private SendRtpPortManager sendRtpPortManager;
  72 +
  73 + @Autowired
74 private AssistRESTfulUtils assistRESTfulUtils; 74 private AssistRESTfulUtils assistRESTfulUtils;
75 75
76 @Autowired 76 @Autowired
@@ -119,13 +119,40 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -119,13 +119,40 @@ public class MediaServerServiceImpl implements IMediaServerService {
119 if (ssrcFactory.hasMediaServerSSRC(mediaServerItem.getId())) { 119 if (ssrcFactory.hasMediaServerSSRC(mediaServerItem.getId())) {
120 ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null); 120 ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
121 } 121 }
  122 + if (userSetting.getGbSendStreamStrict()) {
  123 + int startPort = 50000;
  124 + int endPort = 60000;
  125 + String sendRtpPortRange = mediaServerItem.getSendRtpPortRange();
  126 + if (sendRtpPortRange == null) {
  127 + logger.warn("[zlm] ] 未配置发流端口范围,默认使用50000到60000");
  128 + }else {
  129 + String[] sendRtpPortRangeArray = sendRtpPortRange.trim().split(",");
  130 + if (sendRtpPortRangeArray.length != 2) {
  131 + logger.warn("[zlm] ] 发流端口范围错误,默认使用50000到60000");
  132 + }else {
  133 + try {
  134 + startPort = Integer.parseInt(sendRtpPortRangeArray[0]);
  135 + endPort = Integer.parseInt(sendRtpPortRangeArray[1]);
  136 + if (endPort <= startPort) {
  137 + logger.warn("[zlm] ] 发流端口范围错误,结束端口应大于开始端口,使用默认端口");
  138 + startPort = 50000;
  139 + endPort = 60000;
  140 + }
  141 +
  142 + }catch (NumberFormatException e) {
  143 + logger.warn("[zlm] ] 发流端口范围错误,默认使用50000到60000");
  144 + }
  145 + }
  146 + }
  147 + logger.info("[[zlm] ] 配置发流端口范围,{}-{}", startPort, endPort);
  148 + sendRtpPortManager.initServerPort(mediaServerItem.getId(), startPort, endPort);
  149 + }
122 // 查询redis是否存在此mediaServer 150 // 查询redis是否存在此mediaServer
123 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); 151 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
124 Boolean hasKey = redisTemplate.hasKey(key); 152 Boolean hasKey = redisTemplate.hasKey(key);
125 if (hasKey != null && ! hasKey) { 153 if (hasKey != null && ! hasKey) {
126 redisTemplate.opsForValue().set(key, mediaServerItem); 154 redisTemplate.opsForValue().set(key, mediaServerItem);
127 } 155 }
128 -  
129 } 156 }
130 } 157 }
131 158
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
@@ -314,9 +314,7 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -314,9 +314,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
314 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), 314 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
315 content.getPort(), content.getSsrc(), content.getPlatformId(), 315 content.getPort(), content.getSsrc(), content.getPlatformId(),
316 content.getApp(), content.getStream(), content.getChannelId(), 316 content.getApp(), content.getStream(), content.getChannelId(),
317 - content.getTcp(), content.getRtcp(), ssrcFromCallback -> {  
318 - return querySendRTPServer(content.getPlatformId(), content.getChannelId(), content.getStream(), null) != null;  
319 - }); 317 + content.getTcp(), content.getRtcp());
320 318
321 WVPResult<ResponseSendItemMsg> result = new WVPResult<>(); 319 WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
322 result.setCode(0); 320 result.setCode(0);
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
@@ -28,6 +28,7 @@ public interface MediaServerMapper { @@ -28,6 +28,7 @@ public interface MediaServerMapper {
28 "secret,"+ 28 "secret,"+
29 "rtp_enable,"+ 29 "rtp_enable,"+
30 "rtp_port_range,"+ 30 "rtp_port_range,"+
  31 + "send_rtp_port_range,"+
31 "record_assist_port,"+ 32 "record_assist_port,"+
32 "default_server,"+ 33 "default_server,"+
33 "create_time,"+ 34 "create_time,"+
@@ -51,6 +52,7 @@ public interface MediaServerMapper { @@ -51,6 +52,7 @@ public interface MediaServerMapper {
51 "#{secret}, " + 52 "#{secret}, " +
52 "#{rtpEnable}, " + 53 "#{rtpEnable}, " +
53 "#{rtpPortRange}, " + 54 "#{rtpPortRange}, " +
  55 + "#{sendRtpPortRange}, " +
54 "#{recordAssistPort}, " + 56 "#{recordAssistPort}, " +
55 "#{defaultServer}, " + 57 "#{defaultServer}, " +
56 "#{createTime}, " + 58 "#{createTime}, " +
@@ -75,6 +77,7 @@ public interface MediaServerMapper { @@ -75,6 +77,7 @@ public interface MediaServerMapper {
75 "<if test=\"autoConfig != null\">, auto_config=#{autoConfig}</if>" + 77 "<if test=\"autoConfig != null\">, auto_config=#{autoConfig}</if>" +
76 "<if test=\"rtpEnable != null\">, rtp_enable=#{rtpEnable}</if>" + 78 "<if test=\"rtpEnable != null\">, rtp_enable=#{rtpEnable}</if>" +
77 "<if test=\"rtpPortRange != null\">, rtp_port_range=#{rtpPortRange}</if>" + 79 "<if test=\"rtpPortRange != null\">, rtp_port_range=#{rtpPortRange}</if>" +
  80 + "<if test=\"sendRtpPortRange != null\">, send_rtp_port_range=#{sendRtpPortRange}</if>" +
78 "<if test=\"secret != null\">, secret=#{secret}</if>" + 81 "<if test=\"secret != null\">, secret=#{secret}</if>" +
79 "<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" + 82 "<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" +
80 "<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" + 83 "<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
@@ -98,6 +101,7 @@ public interface MediaServerMapper { @@ -98,6 +101,7 @@ public interface MediaServerMapper {
98 "<if test=\"autoConfig != null\">, auto_config=#{autoConfig}</if>" + 101 "<if test=\"autoConfig != null\">, auto_config=#{autoConfig}</if>" +
99 "<if test=\"rtpEnable != null\">, rtp_enable=#{rtpEnable}</if>" + 102 "<if test=\"rtpEnable != null\">, rtp_enable=#{rtpEnable}</if>" +
100 "<if test=\"rtpPortRange != null\">, rtp_port_range=#{rtpPortRange}</if>" + 103 "<if test=\"rtpPortRange != null\">, rtp_port_range=#{rtpPortRange}</if>" +
  104 + "<if test=\"sendRtpPortRange != null\">, send_rtp_port_range=#{sendRtpPortRange}</if>" +
101 "<if test=\"secret != null\">, secret=#{secret}</if>" + 105 "<if test=\"secret != null\">, secret=#{secret}</if>" +
102 "<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" + 106 "<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" +
103 "<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" + 107 "<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
web_src/src/components/dialog/MediaServerEdit.vue
@@ -81,6 +81,7 @@ @@ -81,6 +81,7 @@
81 <el-switch active-text="多端口" inactive-text="单端口" @change="portRangeChange" v-model="mediaServerForm.rtpEnable" :disabled="mediaServerForm.defaultServer"></el-switch> 81 <el-switch active-text="多端口" inactive-text="单端口" @change="portRangeChange" v-model="mediaServerForm.rtpEnable" :disabled="mediaServerForm.defaultServer"></el-switch>
82 </el-form-item> 82 </el-form-item>
83 83
  84 +
84 <el-form-item v-if="!mediaServerForm.rtpEnable" label="收流端口" prop="rtpProxyPort"> 85 <el-form-item v-if="!mediaServerForm.rtpEnable" label="收流端口" prop="rtpProxyPort">
85 <el-input v-model.number="mediaServerForm.rtpProxyPort" clearable :disabled="mediaServerForm.defaultServer"></el-input> 86 <el-input v-model.number="mediaServerForm.rtpProxyPort" clearable :disabled="mediaServerForm.defaultServer"></el-input>
86 </el-form-item> 87 </el-form-item>
@@ -89,6 +90,11 @@ @@ -89,6 +90,11 @@
89 - 90 -
90 <el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input> 91 <el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
91 </el-form-item> 92 </el-form-item>
  93 + <el-form-item v-if="mediaServerForm.sendRtpEnable" label="发流端口" >
  94 + <el-input v-model="sendRtpPortRange1" placeholder="起始" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange1" :disabled="mediaServerForm.defaultServer"></el-input>
  95 + -
  96 + <el-input v-model="sendRtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
  97 + </el-form-item>
92 <el-form-item label="录像管理服务端口" prop="recordAssistPort"> 98 <el-form-item label="录像管理服务端口" prop="recordAssistPort">
93 <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer"> 99 <el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer">
94 <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>--> 100 <!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>-->
@@ -172,6 +178,7 @@ export default { @@ -172,6 +178,7 @@ export default {
172 rtmpSSlPort: "", 178 rtmpSSlPort: "",
173 rtpEnable: false, 179 rtpEnable: false,
174 rtpPortRange: "", 180 rtpPortRange: "",
  181 + sendRtpPortRange: "",
175 rtpProxyPort: "", 182 rtpProxyPort: "",
176 rtspPort: "", 183 rtspPort: "",
177 rtspSSLPort: "", 184 rtspSSLPort: "",
@@ -179,6 +186,9 @@ export default { @@ -179,6 +186,9 @@ export default {
179 rtpPortRange1:30000, 186 rtpPortRange1:30000,
180 rtpPortRange2:30500, 187 rtpPortRange2:30500,
181 188
  189 + sendRtpPortRange1:50000,
  190 + sendRtpPortRange2:60000,
  191 +
182 rules: { 192 rules: {
183 ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }], 193 ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
184 httpPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], 194 httpPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
@@ -214,10 +224,15 @@ export default { @@ -214,10 +224,15 @@ export default {
214 this.currentStep = 3; 224 this.currentStep = 3;
215 if (param.rtpPortRange) { 225 if (param.rtpPortRange) {
216 let rtpPortRange = this.mediaServerForm.rtpPortRange.split(","); 226 let rtpPortRange = this.mediaServerForm.rtpPortRange.split(",");
  227 + let sendRtpPortRange = this.mediaServerForm.sendRtpPortRange.split(",");
217 if (rtpPortRange.length > 0) { 228 if (rtpPortRange.length > 0) {
218 this.rtpPortRange1 = rtpPortRange[0] 229 this.rtpPortRange1 = rtpPortRange[0]
219 this.rtpPortRange2 = rtpPortRange[1] 230 this.rtpPortRange2 = rtpPortRange[1]
220 } 231 }
  232 + if (sendRtpPortRange.length > 0) {
  233 + this.sendRtpPortRange1 = sendRtpPortRange[0]
  234 + this.sendRtpPortRange2 = sendRtpPortRange[1]
  235 + }
221 } 236 }
222 } 237 }
223 }, 238 },
@@ -240,6 +255,8 @@ export default { @@ -240,6 +255,8 @@ export default {
240 that.mediaServerForm.autoConfig = true; 255 that.mediaServerForm.autoConfig = true;
241 that.rtpPortRange1 = 30000 256 that.rtpPortRange1 = 30000
242 that.rtpPortRange2 = 30500 257 that.rtpPortRange2 = 30500
  258 + that.sendRtpPortRange1 = 50000
  259 + that.sendRtpPortRange2 = 60000
243 that.serverCheck = 1; 260 that.serverCheck = 1;
244 }else { 261 }else {
245 that.serverCheck = -1; 262 that.serverCheck = -1;
@@ -321,12 +338,15 @@ export default { @@ -321,12 +338,15 @@ export default {
321 rtmpSSlPort: "", 338 rtmpSSlPort: "",
322 rtpEnable: false, 339 rtpEnable: false,
323 rtpPortRange: "", 340 rtpPortRange: "",
  341 + sendRtpPortRange: "",
324 rtpProxyPort: "", 342 rtpProxyPort: "",
325 rtspPort: "", 343 rtspPort: "",
326 rtspSSLPort: "", 344 rtspSSLPort: "",
327 }; 345 };
328 this.rtpPortRange1 = 30500; 346 this.rtpPortRange1 = 30500;
329 this.rtpPortRange2 = 30500; 347 this.rtpPortRange2 = 30500;
  348 + this.sendRtpPortRange1 = 50000;
  349 + this.sendRtpPortRange2 = 60000;
330 this.listChangeCallback = null 350 this.listChangeCallback = null
331 this.currentStep = 1; 351 this.currentStep = 1;
332 }, 352 },
@@ -351,7 +371,7 @@ export default { @@ -351,7 +371,7 @@ export default {
351 portRangeChange: function() { 371 portRangeChange: function() {
352 if (this.mediaServerForm.rtpEnable) { 372 if (this.mediaServerForm.rtpEnable) {
353 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2 373 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2
354 - console.log(this.mediaServerForm.rtpPortRange) 374 + this.mediaServerForm.sendRtpPortRange = this.sendRtpPortRange1 + "," + this.sendRtpPortRange2
355 } 375 }
356 } 376 }
357 }, 377 },
web_src/src/components/dialog/StreamProxyEdit.vue
@@ -82,17 +82,21 @@ @@ -82,17 +82,21 @@
82 <el-option label="组播" value="2"></el-option> 82 <el-option label="组播" value="2"></el-option>
83 </el-select> 83 </el-select>
84 </el-form-item> 84 </el-form-item>
  85 +
85 <el-form-item label="无人观看" prop="rtpType" > 86 <el-form-item label="无人观看" prop="rtpType" >
86 - <el-select  
87 - @change="noneReaderHandler"  
88 - v-model="proxyParam.noneReader"  
89 - style="width: 100%"  
90 - placeholder="请选择无人观看的处理方式"  
91 - >  
92 - <el-option label="不做处理" value="0"></el-option>  
93 - <el-option label="停用" value="1"></el-option>  
94 - <el-option label="移除" value="2"></el-option>  
95 - </el-select> 87 + <el-radio v-model="proxyParam.noneReader" label="1">不做处理</el-radio>
  88 + <el-radio v-model="proxyParam.enableDisableNoneReader" label="2">停用</el-radio>
  89 + <el-radio v-model="proxyParam.enableRemoveNoneReader" label="3">移除</el-radio>
  90 +<!-- <el-select-->
  91 +<!-- @change="noneReaderHandler"-->
  92 +<!-- v-model="proxyParam.noneReader"-->
  93 +<!-- style="width: 100%"-->
  94 +<!-- placeholder="请选择无人观看的处理方式"-->
  95 +<!-- >-->
  96 +<!-- <el-option label="不做处理" value="0"></el-option>-->
  97 +<!-- <el-option label="停用" value="1"></el-option>-->
  98 +<!-- <el-option label="移除" value="2"></el-option>-->
  99 +<!-- </el-select>-->
96 </el-form-item> 100 </el-form-item>
97 <el-form-item label="其他选项"> 101 <el-form-item label="其他选项">
98 <div style="float: left;"> 102 <div style="float: left;">