浏览代码

测试2 合并测试至最新代码

kindring 2 年之前
父节点
当前提交
c9a3c4a04d
共有 43 个文件被更改,包括 728 次插入14693 次删除
  1. 7 0
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  2. 1 1
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  3. 7 1
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  4. 7 2
      src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
  5. 5 1
      src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
  6. 148 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/BroadcastItem.java
  7. 25 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
  8. 25 5
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
  9. 0 3
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
  10. 27 0
      src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
  11. 6 0
      src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
  12. 26 0
      src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
  13. 68 0
      src/main/java/com/genersoft/iot/vmp/utils/Md5Utils.java
  14. 5 1
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/ErrorCode.java
  15. 0 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/aiLib/AiApi.java
  16. 73 4
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
  17. 34 13
      src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
  18. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java
  19. 2 2
      src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
  20. 1 2
      src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java
  21. 1 2
      src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
  22. 1 1
      src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
  23. 1 1
      src/main/java/com/genersoft/iot/vmp/web/gb28181/AuthController.java
  24. 2 0
      src/main/resources/application.yml
  25. 1 1
      web_src/config/dev.env.js
  26. 1 13
      web_src/config/index.js
  27. 1 14460
      web_src/package-lock.json
  28. 0 1
      web_src/package.json
  29. 35 35
      web_src/src/apiStore/axionsBefore.js
  30. 47 45
      web_src/src/apiStore/axios.js
  31. 56 39
      web_src/src/components/DeviceList.vue
  32. 1 0
      web_src/src/components/Login.vue
  33. 6 6
      web_src/src/components/channelList.vue
  34. 33 16
      web_src/src/components/common/microphone.vue
  35. 9 10
      web_src/src/components/common/ptzControl.vue
  36. 4 4
      web_src/src/components/console.vue
  37. 9 1
      web_src/src/components/dialog/MediaServerEdit.vue
  38. 18 0
      web_src/src/components/dialog/configInfo.vue
  39. 4 2
      web_src/src/components/dialog/customPlayer.vue
  40. 2 1
      web_src/src/components/dialog/rtcPlayer.vue
  41. 12 11
      web_src/src/layout/UiHeader.vue
  42. 0 1
      web_src/src/layout/index.vue
  43. 16 5
      web_src/src/main.js

+ 7 - 0
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@@ -1,8 +1,10 @@
 package com.genersoft.iot.vmp;
 
 import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.SpringApplication;
@@ -46,7 +48,12 @@ public class VManageBootstrap extends SpringBootServletInitializer {
 	// 项目重启
 	public static void restart() {
 		context.close();
+		try {
+		Thread.sleep(3000);
 		VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
+		} catch (InterruptedException e) {
+			logger.error("服务启动异常");
+		}
 	}
 
 	@Override

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@@ -76,7 +76,7 @@ public class VideoManagerConstants {
 
 	public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_";
 
-
+	public static final String WVP_BROADCAST_FLAG  = "WVP_BROADCAST_FLAG_";
 
 
 	//************************** redis 消息*********************************

+ 7 - 1
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@@ -86,6 +86,11 @@ public class MediaConfig{
     @Value("${media.record-assist-port:0}")
     private Integer recordAssistPort = 0;
 
+    @Value("${media.externIP:${media.rtcIp}}")
+    private String externIP;
+    @Value("${media.externPort:${media.rtcPort}}")
+    private Integer externPort = 0;
+
     public String getId() {
         return id;
     }
@@ -226,7 +231,8 @@ public class MediaConfig{
         mediaServerItem.setSendRtpPortRange(sendRtpPortRange);
         mediaServerItem.setRecordAssistPort(recordAssistPort);
         mediaServerItem.setHookAliveInterval(30.00f);
-
+        mediaServerItem.setExternIP(externIP);
+        mediaServerItem.setExternPort(externPort);
         mediaServerItem.setCreateTime(DateUtil.getNow());
         mediaServerItem.setUpdateTime(DateUtil.getNow());
 

+ 7 - 2
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java

@@ -54,8 +54,13 @@ public class SecurityUtils {
         if(authentication!=null){
             Object principal = authentication.getPrincipal();
             if(principal!=null && !"anonymousUser".equals(principal)){
-                LoginUser user = (LoginUser) authentication.getPrincipal();
-                return user;
+//                LoginUser user = (LoginUser) authentication.getPrincipal();
+
+                String username = (String) principal;
+                User user = new User();
+                user.setUsername(username);
+                LoginUser loginUser = new LoginUser(user, LocalDateTime.now());
+                return loginUser;
             }
         }
         return null;

+ 5 - 1
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java

@@ -113,7 +113,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.headers().contentTypeOptions().disable()
-                .and().cors().configurationSource(configurationSource())
+//                .and().cors().configurationSource(configurationSource())
+                .and().cors()
                 .and().csrf().disable()
                 .sessionManagement()
                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
@@ -141,6 +142,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
         CorsConfiguration corsConfiguration = new CorsConfiguration();
         corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
         corsConfiguration.setAllowedMethods(Arrays.asList("*"));
+		corsConfiguration.addAllowedOrigin("*"); // 允许任何域名使用
+        corsConfiguration.addAllowedHeader("*"); // 允许任何头
+        corsConfiguration.addAllowedMethod("*"); // 允许任何方法(post、get等)
         corsConfiguration.setMaxAge(3600L);
         corsConfiguration.setAllowCredentials(true);
         corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());

+ 148 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/BroadcastItem.java

@@ -0,0 +1,148 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+/**
+ * @author
+ * @date :Created in 2022/8/24 15:17
+ * @description:
+ * @modified By:
+ * @version: ${1.0.0}
+ */
+public class BroadcastItem {
+
+    public BroadcastItem() {
+    }
+
+    public BroadcastItem(String deviceId, String localIp, Integer localPort) {
+        this.deviceId = deviceId;
+        this.localIp = localIp;
+        this.localPort = localPort;
+    }
+
+    private String deviceId;
+    private String channelId;
+    private String localIp;
+    private String ipcIp;
+    private Integer localPort;
+    private Integer ipcAudioPort;
+    // 0是udp 1是tcp
+    private Integer udpOrTcp;
+    private String callId;
+    private String ssrc;
+    private String fromTag;
+    private String toTag;
+    private String viaBranch;
+    private String app;
+    private String stream;
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+
+    public String getLocalIp() {
+        return localIp;
+    }
+
+    public void setLocalIp(String localIp) {
+        this.localIp = localIp;
+    }
+
+    public String getIpcIp() {
+        return ipcIp;
+    }
+
+    public void setIpcIp(String ipcIp) {
+        this.ipcIp = ipcIp;
+    }
+
+    public Integer getLocalPort() {
+        return localPort;
+    }
+
+    public void setLocalPort(Integer localPort) {
+        this.localPort = localPort;
+    }
+
+    public Integer getIpcAudioPort() {
+        return ipcAudioPort;
+    }
+
+    public void setIpcAudioPort(Integer ipcAudioPort) {
+        this.ipcAudioPort = ipcAudioPort;
+    }
+
+    public Integer getUdpOrTcp() {
+        return udpOrTcp;
+    }
+
+    public void setUdpOrTcp(Integer udpOrTcp) {
+        this.udpOrTcp = udpOrTcp;
+    }
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getFromTag() {
+        return fromTag;
+    }
+
+    public void setFromTag(String fromTag) {
+        this.fromTag = fromTag;
+    }
+
+    public String getToTag() {
+        return toTag;
+    }
+
+    public void setToTag(String toTag) {
+        this.toTag = toTag;
+    }
+
+    public String getViaBranch() {
+        return viaBranch;
+    }
+
+    public void setViaBranch(String viaBranch) {
+        this.viaBranch = viaBranch;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+}

+ 25 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java

@@ -92,6 +92,12 @@ public class MediaServerItem{
     @Schema(description = "当前使用到的端口")
     private int currentPort;
 
+    @Schema(description = "rtc端口")
+    private int externPort;
+
+    @Schema(description = "rtc使用的ip")
+    private String externIP;
+
 
     /**
      * 每一台ZLM都有一套独立的SSRC列表
@@ -256,7 +262,7 @@ public class MediaServerItem{
     }
 
     public String getRtpPortRange() {
-        return rtpPortRange;
+        return rtpPortRange==null?"":rtpPortRange;
     }
 
     public void setRtpPortRange(String rtpPortRange) {
@@ -350,4 +356,22 @@ public class MediaServerItem{
     public void setHookAliveInterval(Float hookAliveInterval) {
         this.hookAliveInterval = hookAliveInterval;
     }
+
+
+    public int getExternPort() {
+        return externPort;
+    }
+
+    public void setExternPort(int externPort) {
+        this.externPort = externPort;
+    }
+
+    public String getExternIP() {
+        return externIP;
+    }
+
+    public void setExternIP(String externIP) {
+        this.externIP = externIP;
+    }
+
 }

+ 25 - 5
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@@ -274,27 +274,46 @@ public class MediaServerServiceImpl implements IMediaServerService {
                     )
             );
         }
+        // 更新服务端的配置,防止修改了zlm配置不更新的情况出现
+        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaSerItem);
+        if(responseJSON != null){
+            JSONArray data = responseJSON.getJSONArray("data");
+            ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+            logger.info("[ZLM update] 更新zlm配置成功,重新连接zlm");
+            zlmServerOnline(zlmServerConfig);
+        }
+
         String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItemInDataBase.getId();
         RedisUtil.set(key, mediaServerItemInDataBase);
     }
 
     @Override
     public List<MediaServerItem> getAll() {
-        List<MediaServerItem> result = new ArrayList<>();
+
+        List<MediaServerItem> result = mediaServerMapper.queryAll();
+
+        // redis 获取?
+//        List<MediaServerItem> result = new ArrayList<>();
         List<Object> mediaServerKeys = RedisUtil.scan(String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + "_" ));
         String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
         for (Object mediaServerKey : mediaServerKeys) {
             String key = (String) mediaServerKey;
+            // 判断当前id是否有与列表中一致的值
+
             MediaServerItem mediaServerItem = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
             if (Objects.isNull(mediaServerItem)) {
                 continue;
             }
             // 检查状态
             Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId());
-            if (aDouble != null) {
-                mediaServerItem.setStatus(true);
+            for(int i = 0; i < result.size(); i++){
+                if(Objects.equals(result.get(i).getId(), mediaServerItem.getId())){
+                    if (aDouble != null) {
+                        result.get(i).setStatus(true);
+                    }
+                }
             }
-            result.add(mediaServerItem);
+//            result.add(mediaServerItem);
         }
         result.sort((serverItem1, serverItem2)->{
             int sortResult = 0;
@@ -636,7 +655,8 @@ public class MediaServerServiceImpl implements IMediaServerService {
         if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
             param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
         }
-        param.put("rtc.externIP",mediaServerItem.getIp());
+        param.put("rtc.externIP",mediaServerItem.getExternIP());
+        param.put("rtc.externPort",mediaServerItem.getExternPort());
         JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
 
         if (responseJSON != null && responseJSON.getInteger("code") == 0) {

+ 0 - 3
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java

@@ -5,9 +5,6 @@ import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
-import com.genersoft.iot.vmp.gb28181.bean.GbStream;
-import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
-import com.genersoft.iot.vmp.gb28181.bean.TreeType;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;

+ 27 - 0
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java

@@ -262,4 +262,31 @@ public interface IRedisCatchStorage {
     List<Device> getAllDevices();
 
     void removeAllDevice();
+
+    /**
+     * 添加语音对讲信息
+     *
+     * @param deviceId
+     * @return
+     */
+    boolean addBroadcastItem(String deviceId, BroadcastItem broadcastItem);
+
+    /**
+     * 查询语音对讲信息
+     *
+     * @param deviceId
+     * @return
+     */
+    BroadcastItem queryBroadcastItem(String deviceId);
+
+    /**
+     * 删除语音对讲信息
+     *
+     * @param deviceId
+
+     * @return
+     */
+    boolean deleteBroadcastItem(String deviceId);
+
+    boolean isBroadcastItem(String deviceId);
 }

+ 6 - 0
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java

@@ -32,6 +32,8 @@ public interface MediaServerMapper {
             "defaultServer, " +
             "createTime, " +
             "updateTime, " +
+            "externIP, " +
+            "externPort, " +
             "hookAliveInterval" +
             ") VALUES " +
             "(" +
@@ -55,6 +57,8 @@ public interface MediaServerMapper {
             "#{defaultServer}, " +
             "#{createTime}, " +
             "#{updateTime}, " +
+            "#{externIP}, " +
+            "#{externPort}, " +
             "#{hookAliveInterval})")
     int add(MediaServerItem mediaServerItem);
 
@@ -78,6 +82,8 @@ public interface MediaServerMapper {
             "<if test=\"secret != null\">, secret=#{secret}</if>" +
             "<if test=\"recordAssistPort != null\">, recordAssistPort=#{recordAssistPort}</if>" +
             "<if test=\"hookAliveInterval != null\">, hookAliveInterval=#{hookAliveInterval}</if>" +
+            "<if test=\"externIP != null\">, externIP=#{externIP}</if>" +
+            "<if test=\"externPort != null\">, externPort=#{externPort}</if>" +
             "WHERE id=#{id}"+
             " </script>"})
     int update(MediaServerItem mediaServerItem);

+ 26 - 0
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@@ -921,4 +921,30 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
                 + userSetting.getServerId() + "_*_" + id + "_*";
         return RedisUtil.scan(key).size();
     }
+
+    @Override
+    public boolean addBroadcastItem(String deviceId, BroadcastItem broadcastItem) {
+        String key = VideoManagerConstants.WVP_BROADCAST_FLAG + deviceId;
+
+        return RedisUtil.set(key, broadcastItem);
+    }
+
+    @Override
+    public BroadcastItem queryBroadcastItem(String deviceId) {
+        String key = VideoManagerConstants.WVP_BROADCAST_FLAG + deviceId;
+        return (BroadcastItem) RedisUtil.get(key);
+    }
+
+    @Override
+    public boolean deleteBroadcastItem(String deviceId) {
+        String key = VideoManagerConstants.WVP_BROADCAST_FLAG + deviceId;
+        return RedisUtil.del(key);
+    }
+
+    @Override
+    public boolean isBroadcastItem(String deviceId) {
+        String key = VideoManagerConstants.WVP_BROADCAST_FLAG + deviceId;
+        BroadcastItem broadcastItem = (BroadcastItem) RedisUtil.get(key);
+        return broadcastItem != null && broadcastItem.getIpcAudioPort() != null;
+    }
 }

+ 68 - 0
src/main/java/com/genersoft/iot/vmp/utils/Md5Utils.java

@@ -0,0 +1,68 @@
+package com.genersoft.iot.vmp.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+/**
+ * Md5加密方法
+ *
+ * @author ruoyi
+ */
+public class Md5Utils
+{
+    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
+
+    private static byte[] md5(String s)
+    {
+        MessageDigest algorithm;
+        try
+        {
+            algorithm = MessageDigest.getInstance("MD5");
+            algorithm.reset();
+            algorithm.update(s.getBytes("UTF-8"));
+            byte[] messageDigest = algorithm.digest();
+            return messageDigest;
+        }
+        catch (Exception e)
+        {
+            log.error("MD5 Error...", e);
+        }
+        return null;
+    }
+
+    private static final String toHex(byte hash[])
+    {
+        if (hash == null)
+        {
+            return null;
+        }
+        StringBuffer buf = new StringBuffer(hash.length * 2);
+        int i;
+
+        for (i = 0; i < hash.length; i++)
+        {
+            if ((hash[i] & 0xff) < 0x10)
+            {
+                buf.append("0");
+            }
+            buf.append(Long.toString(hash[i] & 0xff, 16));
+        }
+        return buf.toString();
+    }
+
+    public static String hash(String s)
+    {
+        try
+        {
+            return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+        }
+        catch (Exception e)
+        {
+            log.error("not supported charset...{}", e);
+            return s;
+        }
+    }
+}

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

@@ -11,7 +11,11 @@ public enum ErrorCode {
     ERROR403(403, "无权限操作"),
     ERROR401(401, "请登录后重新请求"),
     ERROR500(500, "系统异常"),
-    VERSION_FAIL(9, "请求版本不对");
+    VERSION_FAIL(9, "请求版本不对"),
+    ERR_MEDIA(600, "流媒体服务异常"),
+    ERR_NOTFOUND_STREAM(601, "无法找到流媒体"),
+    ERR_Invite_fail(602, "流媒体交互异常"),
+    ERROR_Device_Busy(603, "设备繁忙");
 //    void ERROR200(200, "系统异常1");
 
     private final int code;

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/aiLib/AiApi.java

@@ -14,7 +14,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 @Tag(name = "ai数据接口")
-@CrossOrigin
 @RestController
 @RequestMapping("/ai")
 public class AiApi {

+ 73 - 4
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -6,6 +6,8 @@ import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.conf.security.SecurityUtils;
+import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
 import com.genersoft.iot.vmp.gb28181.GBEventSubscribe;
 import com.genersoft.iot.vmp.gb28181.GBHookSubscribeFactory;
 import com.genersoft.iot.vmp.gb28181.HookSubscribeForKey;
@@ -17,11 +19,14 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IMediaService;
 import com.genersoft.iot.vmp.service.IPlayService;
+import com.genersoft.iot.vmp.service.IUserService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.utils.Md5Utils;
 import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
@@ -39,7 +44,9 @@ import javax.servlet.http.HttpServletRequest;
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
 import java.text.ParseException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 @Tag(name  = "国标设备点播")
@@ -83,6 +90,9 @@ public class PlayController {
 	@Autowired
 	private GBEventSubscribe GBHookSubscribe;
 
+	@Autowired
+	private IUserService userService;
+
 	@Operation(summary = "开始点播")
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
 	@Parameter(name = "channelId", description = "通道国标编号", required = true)
@@ -255,15 +265,17 @@ public class PlayController {
 
 	@Operation(summary = "语音广播命令")
 	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
+	@Parameter(name = "app", description = "推流app", required = true)
 	@Parameter(name = "stream", description = "音频推流编号", required = true)
     @GetMapping("/broadcast")
 //    @PostMapping("/broadcast/{deviceId}")
     public DeferredResult<WVPResult<String>> broadcastApi(
 			HttpServletRequest request,
-			@RequestParam String deviceId,@RequestParam String stream) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("语音广播API调用");
-        }
+			@RequestParam String deviceId,
+			@RequestParam String app,
+			@RequestParam String stream) {
+		logger.info("[语音广播] 开始语音广播交互流程");
+
 		// 添加计时器,待事件结束则自动触发超时回复
 		RequestMessage msg = new RequestMessage();
 		String key  = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId;
@@ -275,6 +287,17 @@ public class PlayController {
 		DeferredResultEx<WVPResult<String>> deferredResultEx = new DeferredResultEx<>(result);
 		HookSubscribeForKey broadcastForInviteHook =  GBHookSubscribeFactory.on_broadcast_invite(deviceId);
 
+		// 判断流是否存在
+		StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
+		if (streamAuthorityInfo == null) {
+			logger.error("webrtc推流未找到");
+			WVPResult wvpResult = new WVPResult();
+			wvpResult.setCode(ErrorCode.ERR_NOTFOUND_STREAM.getCode());
+			wvpResult.setMsg("[广播失败] 无法获取webrtc流信息");
+			msg.setData(wvpResult);
+			resultHolder.invokeAllResult(msg);
+			return result;
+		}
 
 		result.onTimeout(()->{
 			logger.warn("[广播超时] 与设备交互broadcast流程超时,未收到设备invite信息");
@@ -327,6 +350,52 @@ public class PlayController {
 
 	}
 
+	/**
+	 * 获取webrtc推流地址
+	 * @param deviceId
+	 * @param channelId
+	 * @return
+	 */
+	@Operation(summary = "获取webrtc推流地址")
+	@Parameter(name = "deviceId", description = "设备国标编号", required = true)
+	@Parameter(name = "channelId", description = "设备国标编号", required = true)
+	@GetMapping("/getWebRtcAddr")
+	public WVPResult getWebRtcAddr(@RequestParam String deviceId,
+								   @RequestParam("channelId") String channelId) {
+
+		//首先判断设备是否正在对讲
+		if (redisCatchStorage.isBroadcastItem(deviceId)) {
+			// 设备正在进行语音对讲
+			return WVPResult.fail(ErrorCode.ERROR_Device_Busy);
+		}
+
+		Device device = storager.queryVideoDevice(deviceId);
+		MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device);
+
+		if (mediaServerItem == null) {
+			logger.error("流媒体未找到");
+			return WVPResult.fail(ErrorCode.ERR_MEDIA);
+		}
+
+		Map<String, Object> result = new HashMap<>(16);
+		String app = "audio";
+		String stream = "rtc_" + deviceId + "_" + channelId;
+		String type = "push";
+		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);
+	}
+
 	@Operation(summary = "获取所有的ssrc")
 	@GetMapping("/ssrc")
 	public JSONObject getSSRC() {

+ 34 - 13
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java

@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.server;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.VManageBootstrap;
 import com.genersoft.iot.vmp.common.SystemAllInfo;
 import com.genersoft.iot.vmp.common.VersionPo;
 import com.genersoft.iot.vmp.conf.MediaConfig;
@@ -16,20 +17,30 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.*;
 import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.utils.SpringBeanFactory;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceInfo;
 import com.genersoft.iot.vmp.vmanager.bean.SystemConfigInfo;
+import gov.nist.javax.sip.SipStackImpl;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.bind.annotation.*;
 
+import javax.sip.ListeningPoint;
+import javax.sip.ObjectInUseException;
+import javax.sip.SipProvider;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @SuppressWarnings("rawtypes")
 @Tag(name = "服务控制")
@@ -37,6 +48,7 @@ import java.util.List;
 @RestController
 @RequestMapping("/api/server")
 public class ServerController {
+    private final static Logger logger =LoggerFactory.getLogger(ServerController.class);
 
     @Autowired
     private ZlmHttpHookSubscribe zlmHttpHookSubscribe;
@@ -77,6 +89,10 @@ public class ServerController {
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
+    @Qualifier("taskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
+
     @GetMapping(value = "/media_server/list")
     @ResponseBody
     @Operation(summary = "流媒体服务列表")
@@ -152,22 +168,27 @@ public class ServerController {
     @GetMapping(value = "/restart")
     @ResponseBody
     public void restart() {
+//        VManageBootstrap.restart();
+        logger.info("--------------");
+        logger.info("开始重启服务");
+        logger.info("--------------");
 //        taskExecutor.execute(()-> {
 //            try {
-//                Thread.sleep(3000);
-//                SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
-//                SipStackImpl stack = (SipStackImpl) up.getSipStack();
-//                stack.stop();
-//                Iterator listener = stack.getListeningPoints();
-//                while (listener.hasNext()) {
-//                    stack.deleteListeningPoint((ListeningPoint) listener.next());
-//                }
-//                Iterator providers = stack.getSipProviders();
-//                while (providers.hasNext()) {
-//                    stack.deleteSipProvider((SipProvider) providers.next());
-//                }
+//                Thread.sleep(5000);
+////                SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
+////                SipStackImpl stack = (SipStackImpl) up.getSipStack();
+//////                stack.stop();
 //                VManageBootstrap.restart();
-//            } catch (InterruptedException | ObjectInUseException e) {
+////                Iterator listener = stack.getListeningPoints();
+////                while (listener.hasNext()) {
+////                    stack.deleteListeningPoint((ListeningPoint) listener.next());
+////                }
+////                Iterator providers = stack.getSipProviders();
+////                while (providers.hasNext()) {
+////                    stack.deleteSipProvider((SipProvider) providers.next());
+////                }
+//
+//            } catch (InterruptedException e) {
 //                throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
 //            }
 //        });

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java

@@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.*;
 import java.util.List;
 
 @Tag(name  = "角色管理")
-@CrossOrigin
+
 @RestController
 @RequestMapping("/api/role")
 public class RoleController {

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java

@@ -12,7 +12,6 @@ import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.github.pagehelper.PageInfo;
-
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -28,7 +27,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 @Tag(name  = "用户管理")
-@CrossOrigin
+@CrossOrigin(origins = "*")
 @RestController
 @RequestMapping("/api/user")
 public class UserController {
@@ -66,6 +65,7 @@ public class UserController {
         return user;
     }
 
+
     @PostMapping("/changePassword")
     @Operation(summary = "修改密码")
     @Parameter(name = "username", description = "用户名", required = true)

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java

@@ -6,7 +6,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
@@ -14,7 +13,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
  * API兼容:系统接口
  */
 @Controller
-@CrossOrigin
+
 @RequestMapping(value = "/api/v1")
 public class ApiController {
 

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java

@@ -11,7 +11,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
@@ -23,7 +22,7 @@ import java.util.List;
  * API兼容:设备信息
  */
 @SuppressWarnings("unchecked")
-@CrossOrigin
+
 @RestController
 @RequestMapping(value = "/api/v1/device")
 public class ApiDeviceController {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java

@@ -27,7 +27,7 @@ import java.text.ParseException;
  * API兼容:实时直播
  */
 @SuppressWarnings(value = {"rawtypes", "unchecked"})
-@CrossOrigin
+
 @RestController
 @RequestMapping(value = "/api/v1/stream")
 public class ApiStreamController {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/web/gb28181/AuthController.java

@@ -5,7 +5,7 @@ import com.genersoft.iot.vmp.storager.dao.dto.User;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
-@CrossOrigin
+
 @RestController
 @RequestMapping(value = "/auth")
 public class AuthController {

+ 2 - 0
src/main/resources/application.yml

@@ -167,6 +167,8 @@ media:
         port-range: 30000,30500 # 端口范围
         # [可选] 国标级联在此范围内选择端口发送媒体流
         send-port-range: 30000,30500 # 端口范围
+    rtcIp:
+    rtcPort:
     # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
     record-assist-port: 0
 

+ 1 - 1
web_src/config/dev.env.js

@@ -4,5 +4,5 @@ const prodEnv = require('./prod.env')
 
 module.exports = merge(prodEnv, {
   NODE_ENV: '"development"',
-  BASE_API: '"/debug"'
+  BASE_API: '"debug"'
 })

+ 1 - 13
web_src/config/index.js

@@ -17,9 +17,7 @@ module.exports = {
         changeOrigin: true,
         secure: false,
         pathRewrite: {
-          '^/debug': ''
-        },headers: {
-          Referer: baseUrl
+          '^/debug': '/'
         }
       },
       '/zlmServer': {
@@ -32,16 +30,6 @@ module.exports = {
           Referer: baseUrl
         }
       },
-      '/debug/zlmServer': {
-        target: ZLMServer,
-        changeOrigin: true,
-        secure: false,
-        pathRewrite: {
-          '^/debug/zlmServer': ''
-        },headers: {
-          Referer: baseUrl
-        }
-      },
       '/aiLib':{
         target: baseUrl,
         secure: false,

文件差异内容过多而无法显示
+ 1 - 14460
web_src/package-lock.json


+ 0 - 1
web_src/package.json

@@ -13,7 +13,6 @@
     "@liveqing/liveplayer": "^2.7.0",
     "axios": "^0.24.0",
     "core-js": "^2.6.5",
-    "crypto-js": "^4.1.1",
     "echarts": "^4.9.0",
     "element-ui": "^2.15.6",
     "fingerprintjs2": "^2.1.2",

+ 35 - 35
web_src/src/apiStore/axionsBefore.js

@@ -8,40 +8,40 @@
  */
 import axios from 'axios'
 import router from '@/router/index.js';
-
-axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : "";
-console.log(axios.defaults.baseURL);
-axios.interceptors.request.use(config => {
-    config.headers.withCredentials = true;
-    // App.$message.info('test')
-    config.changeOrigin= true
-    config.credentials= true;
-        config.secure= true
-    return config
-},error =>{
-    return Promise.reject(error)
-})
-
-// 统一处理错误
-axios.interceptors.response.use(response=>{
-    return response;
-},error => {
-    if (error && error.response) {
-        switch (error.response.status) {
-            case 400:
-                console.log('400错误');
-                // 对400错误您的处理\
-                break
-            case 401:
-                console.log('401权限错误');
-                router.push('/login').then();
-                break
-            // 对 401 错误进行处理
-            default:
-                // 如果以上都不是的处理
-                return Promise.reject(error);
-        }
-    }
-})
+//
+// axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : "";
+// console.log(axios.defaults.baseURL);
+// axios.interceptors.request.use(config => {
+//     config.headers.withCredentials = true;
+//     // App.$message.info('test')
+//     config.changeOrigin= true
+//     config.credentials= true;
+//         config.secure= true
+//     return config
+// },error =>{
+//     return Promise.reject(error)
+// })
+//
+// // 统一处理错误
+// axios.interceptors.response.use(response=>{
+//     return response;
+// },error => {
+//     if (error && error.response) {
+//         switch (error.response.status) {
+//             case 400:
+//                 console.log('400错误');
+//                 // 对400错误您的处理\
+//                 break
+//             case 401:
+//                 console.log('401权限错误');
+//                 router.push('/login').then();
+//                 break
+//             // 对 401 错误进行处理
+//             default:
+//                 // 如果以上都不是的处理
+//                 return Promise.reject(error);
+//         }
+//     }
+// })
 
 export default axios

+ 47 - 45
web_src/src/apiStore/axios.js

@@ -18,49 +18,51 @@ import axios from './axionsBefore'
 let header_data = {
     Authorization: ''
 }
+let _axios = function (){
+  return axios
+}
+
+_axios.get = (url,auth = false)=>{
+  if(auth){
+    return axios.get(url,{headers:{Authorization:header_data.Authorization}})
+  }else{
+    return axios.get(url)
+  }
+}
+_axios.post = (url,data,headers)=>
+{
+  return axios.post(url,data,headers)
+}
+_axios.put = (url,data,auth = false) => {
+  if(auth)
+  {
+    return axios.put(url,data,{headers:{Authorization:header_data.Authorization}})
+  }else
+  {
+    return axios.put(url,data);
+  }
+}
+_axios.del = (url,auth = false)=>
+{
+  if(auth)
+  {
+    return axios.delete(url,{headers:{Authorization:header_data.Authorization}})
+  }else
+  {
+    return axios.delete(url);
+  }
+}
+_axios.uploader = (url,file,auth = false)=>
+{
+  let param = new FormData();
+  param.append('file',file)
+  if(auth){
+    return axios.post(url,param,{headers:{Authorization:header_data.Authorization}})
+  }else
+  {
+    return axios.post(url,param)
+  }
+}
+_axios.axios = axios;
 // axios.get('/')
-export default {
-    get(url,auth = false){
-        if(auth){
-            return axios.get(url,{headers:{Authorization:header_data.Authorization}})
-        }else{
-            return axios.get(url)
-        }
-    },
-    post(url,data,headers)
-    {
-        return axios.post(url,data,headers)
-    },
-    put(url,data,auth = false)
-    {
-        if(auth)
-        {
-            return axios.put(url,data,{headers:{Authorization:header_data.Authorization}})
-        }else
-        {
-            return axios.put(url,data);
-        }
-    },
-    del(url,auth = false)
-    {
-        if(auth)
-        {
-            return axios.delete(url,{headers:{Authorization:header_data.Authorization}})
-        }else
-        {
-            return axios.delete(url);
-        }
-    },
-    uploader(url,file,auth = false)
-    {
-        let param = new FormData();
-        param.append('file',file)
-        if(auth){
-            return axios.post(url,param,{headers:{Authorization:header_data.Authorization}})
-        }else
-        {
-            return axios.post(url,param)
-        }
-    },
-    axios:axios
-}
+export default _axios;

+ 56 - 39
web_src/src/components/DeviceList.vue

@@ -96,6 +96,7 @@
 import uiHeader from '../layout/UiHeader.vue'
 import deviceEdit from './dialog/deviceEdit.vue'
 import syncChannelProgress from './dialog/SyncChannelProgress.vue'
+import handle from "../until/handle";
 
 export default {
   name: 'app',
@@ -154,7 +155,7 @@ export default {
     },
     getDeviceList: function () {
       this.getDeviceListLoading = true;
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: `/api/device/query/devices`,
         params: {
@@ -184,7 +185,7 @@ export default {
         center: true,
         type: 'warning'
       }).then(() => {
-        this.$axios({
+        this.$axios.axios({
           method: 'delete',
           url: `/api/device/query/devices/${row.deviceId}/delete`
         }).then((res) => {
@@ -210,57 +211,73 @@ export default {
 
     //gb28181平台对接
     //刷新设备信息
-    refDevice: function (itemData) {
+    async refDevice(itemData) {
       console.log("刷新对应设备:" + itemData.deviceId);
       let that = this;
-      this.$axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'get',
         url: '/api/device/query/devices/' + itemData.deviceId + '/sync'
-      }).then((res) => {
-        console.log("刷新设备结果:" + JSON.stringify(res));
-        if (res.data.code !== 0) {
-          that.$message({
-            showClose: true,
-            message: res.data.msg,
-            type: 'error'
-          });
-        } else {
-          // that.$message({
-          // 	showClose: true,
-          // 	message: res.data.msg,
-          // 	type: 'success'
-          // });
-          this.$refs.syncChannelProgress.openDialog(itemData.deviceId)
-        }
-        that.initData()
-      }).catch((e) => {
+      }))
+      if(err){
         console.error(e)
-        that.$message({
+        this.$message({
           showClose: true,
           message: e,
           type: 'error'
         });
-      });
+      }
+      res = res.data;
+      console.log("刷新设备结果:" + JSON.stringify(res));
+      if (res.data.code !== 0) {
+        this.$message({
+          showClose: true,
+          message: res.data.msg,
+          type: 'error'
+        });
+      } else {
+        // that.$message({
+        // 	showClose: true,
+        // 	message: res.data.msg,
+        // 	type: 'success'
+        // });
+        this.$refs.syncChannelProgress.openDialog(itemData.deviceId)
+      }
+      this.initData()
 
     },
 
-    getTooltipContent: async function (deviceId) {
+    async getTooltipContent(deviceId) {
       let result = "";
-      await this.$axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'get',
-        async: false,
-        url: `/api/device/query/${deviceId}/sync_status/`,
-      }).then((res) => {
-        if (res.data.code == 0) {
-          if (res.data.data.errorMsg !== null) {
-            result = res.data.data.errorMsg
-          } else if (res.data.msg !== null) {
-            result = res.data.msg
-          } else {
-            result = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
-          }
+        url: `/api/device/query/${deviceId}/sync_status/`
+      }))
+      if(err){
+        console.error(e)
+        this.$message({
+          showClose: true,
+          message: e,
+          type: 'error'
+        });
+      }
+      res = res.data;
+      console.log("刷新设备结果:" + JSON.stringify(res));
+      if (res.data.code === 0) {
+        if (res.data.data.errorMsg !== null) {
+          result = res.data.data.errorMsg
+        } else if (res.data.msg !== null) {
+          result = res.data.msg
+        } else {
+          result = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
         }
-      })
+      }
+      // await this.$axios.axios({
+      //   method: 'get',
+      //   async: false,
+      //   url: ``,
+      // }).then((res) => {
+      //
+      // })
       return result;
     },
     //通知设备上传媒体流
@@ -281,7 +298,7 @@ export default {
     transportChange: function (row) {
       console.log(`修改传输方式为 ${row.streamMode}:${row.deviceId} `);
       let that = this;
-      this.$axios({
+      this.$axios.axios({
         method: 'post',
         url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode
       }).then(function (res) {

+ 1 - 0
web_src/src/components/Login.vue

@@ -36,6 +36,7 @@
 <script>
 import crypto from 'crypto'
 import api_user from "@/apiStore/api_user";
+import userService from "./service/UserService";
 export default {
   name: 'Login',
   data(){

+ 6 - 6
web_src/src/components/channelList.vue

@@ -214,7 +214,7 @@ export default {
     getDeviceChannelList: function () {
       let that = this;
       if (typeof (this.$route.params.deviceId) == "undefined") return;
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: `/api/device/query/devices/${this.$route.params.deviceId}/channels`,
         params: {
@@ -259,7 +259,7 @@ export default {
       console.log(pushUrl);
       console.log("通知设备推流1:" + deviceId + " : " + channelId);
       let that = this;
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: pushUrl
       }).then(function (res) {
@@ -299,7 +299,7 @@ export default {
     },
     stopDevicePush: function (itemData) {
       var that = this;
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId
       }).then(function (res) {
@@ -355,7 +355,7 @@ export default {
     },
     showSubchannels: function (channelId) {
       if (!this.showTree) {
-        this.$axios({
+        this.$axios.axios({
           method: 'get',
           url: `/api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,
           params: {
@@ -379,7 +379,7 @@ export default {
           console.log(error);
         });
       }else {
-        this.$axios({
+        this.$axios.axios({
           method: 'get',
           url: `/api/device/query/tree/channel/${this.deviceId}`,
           params: {
@@ -408,7 +408,7 @@ export default {
       this.initData();
     },
     updateChannel: function (row) {
-      this.$axios({
+      this.$axios.axios({
         method: 'post',
         url: `/api/device/query/channel/update/${this.deviceId}`,
         params: row

+ 33 - 16
web_src/src/components/common/microphone.vue

@@ -10,7 +10,6 @@
 import handle from "@/until/handle";
 import {sleep} from "@/until/time";
 import ZLMRTCClient from "@/assets/ZLMRTCClient";
-import CryptoJS from "crypto-js"
 
 export default {
   name: "microphone.vue",
@@ -18,14 +17,12 @@ export default {
     size: {default:"medium"},
     deviceId: {required:true},
     channelId: {required:true},
-    httpsHook: {default:""},
-    httpHook: {default:""},
-    pushKey: {default:""},
     enableDebug: {default: false}
   },
   data(){
     return {
       iconStyle: "",
+      pushConfig: null,
       // 音频流
       audioStream: null,
       // record
@@ -45,6 +42,7 @@ export default {
   },
   beforeMount() {
     this.initAudioApplications();
+    this.getWebrtcAddress();
   },
   beforeDestroy() {
     if(this.player){
@@ -94,16 +92,13 @@ export default {
     async startRecordAudio() {
       let err, stream, res;
       if (!this.mediaStream) {
-        let httpsHook = this.httpsHook;
-        let pushKey = this.pushKey;
-        pushKey = CryptoJS.MD5(pushKey);
-        let serverHost = `${httpsHook}`
-        let app = "rtc";
-        let stream = `audio${this.deviceId}`
+        if(!this.pushConfig){
+          return this.audioStartFailed(null,"无法获取到推流配置")
+        }
         // webrtc 请求推流 url
-        let zlmSdpUrl = `${serverHost}/index/api/webrtc?app=${app}&stream=${stream}&type=push&sign=${pushKey}`;
+        let zlmSdpUrl = this.pushConfig.webRtcPushUrl;
         // webrtc 拉流地址
-        let playAudioStreamUrl = `${serverHost}/index/api/webrtc?app=${app}&stream=${stream}&type=play`;
+        let playAudioStreamUrl = zlmSdpUrl.replace("push","play");
         console.log(playAudioStreamUrl);
 
         let player = new ZLMRTCClient.Endpoint(
@@ -127,7 +122,7 @@ export default {
           if (state === 'connected') {
             // 等待1秒
             await sleep(1000);
-            [err, res] = await handle(this.sendBroaderCast(stream));
+            [err, res] = await handle(this.sendBroaderCast(this.pushConfig.stream,this.pushConfig.app));
             if (err) {
               this.audioStartFailed(err,`与设备交互错误信息失败${err.message}`)
               this.mediaStream = null;
@@ -170,11 +165,33 @@ export default {
       }
       this.$message.error(msg);
     },
+    async getWebrtcAddress(){
+      let url = `/api/play/getWebRtcAddr`
+      url += `?deviceId=${this.deviceId}&channelId=${this.channelId}`;
+      let [err,res] = await handle(this.$axios.axios({
+        method: 'get',
+        url: url
+      }));
+      if (err){
+        console.log(err)
+        this.isLoadAddr = false;
+        return 0;
+      }
+      let data = res.data;
+      if(data.code===0){
+        this.pushConfig = data.data;
+      }else{
+        console.log(res);
+        console.error(data.msg);
+      }
+
+    },
     // 通知设备开启推流
-    sendBroaderCast(stream) {
+    sendBroaderCast(stream,app) {
+
       let url = `/api/play/broadcast`
-      url += `?deviceId=${this.deviceId}&stream=${stream}`;
-      return this.$axios({
+      url += `?deviceId=${this.deviceId}&stream=${stream}&app=${app}`;
+      return this.$axios.axios({
         method: 'get',
         url: url
       });

+ 9 - 10
web_src/src/components/common/ptzControl.vue

@@ -136,7 +136,7 @@ export default {
     }
   },
   beforeMount() {
-    this.queryPushParam();
+    // this.queryPushParam();
   },
   methods:{
     timeSendFocus(){
@@ -192,7 +192,7 @@ export default {
         clearTimeout(changToLongDownStateTimer);
         changToLongDownStateTimer = null;
       }
-      let [err,res] = await handle(this.$axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'post',
         url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.zoomSpeed
       }));
@@ -208,7 +208,7 @@ export default {
       let url = `/api/ptz/focus/`
       url+=`${this.deviceId}/`;
       url+=`${this.channelId}/`;
-      let [err,res] = await handle(this.$axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'post',
         url: url
       }));
@@ -218,7 +218,7 @@ export default {
     presetPosition: function (cmdCode, presetPos) {
       console.log('预置位控制:' + this.presetPos + ' : 0x' + cmdCode.toString(16));
       let that = this;
-      this.$axios({
+      this.$axios.axios({
         method: 'post',
         url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=0&parameter2=' + presetPos + '&combindCode2=0'
       }).then(function (res) {});
@@ -228,7 +228,7 @@ export default {
       let parameter2 = parameter % 256;
       let combindCode2 = Math.floor(parameter / 256) * 16;
       console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter2.toString(16) + ' 0x' + combindCode2.toString(16));
-      this.$axios({
+      this.$axios.axios({
         method: 'post',
         url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=' + groupNum + '&parameter2=' + parameter2 + '&combindCode2=' + combindCode2
       }).then(function (res) {});
@@ -236,7 +236,7 @@ export default {
     setCommand: function (cmdCode, groupNum, parameter) {
       let that = this;
       console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter.toString(16) + ' 0x0');
-      this.$axios({
+      this.$axios.axios({
         method: 'post',
         url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=' + groupNum + '&parameter2=' + parameter + '&combindCode2=0'
       }).then(function (res) {});
@@ -267,7 +267,7 @@ export default {
         setTimeout(()=>{
           console.log(`按下的延迟isSendAutoMove: ${this.isSendAutoMove}`)
           if(this.isSendAutoMove){
-            console.log('长按抬起延迟结束')
+            console.log('长按抬起延迟结束');
             this.sendCommand('stop',0);
           }
           this.isSendAutoMove = true;
@@ -279,7 +279,6 @@ export default {
       clearTimeout(conClickTimer);
       conClickTimer = null;
 
-
     },
     /**
      * 松开按钮逻辑
@@ -340,7 +339,7 @@ export default {
       // step = step * 5
       let url = `/api/ptz/c/${this.deviceId}/${this.channelId}/?c=${command}&step=${step*this.stepValue}`
       console.log(url);
-      let [err,res] = await handle(this.$axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'post',
         url: url
       }));
@@ -349,7 +348,7 @@ export default {
     },
     async queryPushParam(){
       let url = `/api/server/pushConfig`
-      let [err,res] = await handle(this.$axios({
+      let [err,res] = await handle(this.$axios.axios({
         method: 'get',
         url: url
       }));

+ 4 - 4
web_src/src/components/console.vue

@@ -112,7 +112,7 @@ export default {
       }, 2000)
     },
     getSystemInfo: function (){
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: `/api/server/system/info`,
       }).then( (res)=> {
@@ -126,7 +126,7 @@ export default {
       });
     },
     getLoad: function (){
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: `/api/server/media_server/load`,
       }).then( (res)=> {
@@ -137,7 +137,7 @@ export default {
       });
     },
     getResourceInfo: function (){
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: `/api/server/resource/info`,
       }).then( (res)=> {
@@ -149,7 +149,7 @@ export default {
     },
     showInfo: function (){
 
-      this.$axios({
+      this.$axios.axios({
         method: 'get',
         url: `/api/server/system/configInfo`,
       }).then( (res)=> {

+ 9 - 1
web_src/src/components/dialog/MediaServerEdit.vue

@@ -98,6 +98,11 @@
                 -
                 <el-input v-model="sendRtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="sendRtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
               </el-form-item>
+              <el-form-item label="rtc配置" prop="extern">
+                <el-input v-model="mediaServerForm.externIP" placeholder="ip" @change="portRangeChange" clearable style="width: 150px" prop="externIP" :disabled="mediaServerForm.defaultServer"></el-input>
+                :
+                <el-input v-model="mediaServerForm.externPort" placeholder="port" @change="portRangeChange" clearable style="width: 100px" prop="externPort" :disabled="mediaServerForm.defaultServer"></el-input>
+              </el-form-item>
               <el-form-item label="无人观看多久后停止拉流" >
                 <el-input v-model.number="mediaServerForm.streamNoneReaderDelayMS" clearable :disabled="mediaServerForm.defaultServer"></el-input>
               </el-form-item>
@@ -194,9 +199,11 @@ export default {
       rtpPortRange2:30500,
       sendRtpPortRange1:30000,
       sendRtpPortRange2:30500,
-
+      externIP: "",
+      externPort: null,
       rules: {
         ip:  [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
+        externIP:  [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
         httpPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
         httpSSlPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
         recordAssistPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
@@ -209,6 +216,7 @@ export default {
         rtpProxyPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
         rtspPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
         rtspSSLPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
+        externPort:  [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
         secret: [{ required: true, message: "请输入secret", trigger: "blur" }],
         timeout_ms: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],
         ffmpeg_cmd_key: [{ required: false, message: "请输入FFmpeg命令参数模板(可选)", trigger: "blur" }],

+ 18 - 0
web_src/src/components/dialog/configInfo.vue

@@ -25,12 +25,19 @@
           <el-descriptions-item label="GIT版本">{{configInfoData.version.GIT_Revision_SHORT}}</el-descriptions-item>
           <el-descriptions-item label="GIT最后提交时间">{{configInfoData.version.GIT_DATE}}</el-descriptions-item>
         </el-descriptions>
+<!--        <el-descriptions title="服务管理">-->
+<!--          <el-descriptions-item label="重启">-->
+<!--            <el-button @click="restart">重启服务</el-button>-->
+<!--          </el-descriptions-item>-->
+<!--        </el-descriptions>-->
       </div>
     </el-dialog>
   </div>
 </template>
 
 <script>
+import handle from "../../until/handle";
+
 export default {
   name: "configInfo",
   props: {},
@@ -54,6 +61,17 @@ export default {
     close: function () {
       this.showDialog = false;
     },
+    // async restart(){
+    //   let url = "/api/server/restart"
+    //   let [err,res] = await handle(this.$axios.axios({
+    //     method: "get",
+    //     url:url,
+    //   }));
+    //   if(err){
+    //     console.log(err);
+    //     return this.$message.error("重启服务器失败");
+    //   }
+    // }
   },
 };
 </script>

+ 4 - 2
web_src/src/components/dialog/customPlayer.vue

@@ -73,7 +73,7 @@ export default {
       // 如何你只是用一种播放器,直接注释掉不用的部分即可
       player: {
         //jessibuca : ["ws_flv", "wss_flv"],
-        webRTC: ["rtc", "rtc"],
+        webRTC: ["rtc", "rtcs"],
       },
       videoHistory: {
         date: '',
@@ -123,7 +123,7 @@ export default {
       that.tracksLoading = true;
       that.tracksNotLoaded = false;
       if (tab.name === "codec") {
-        this.$axios({
+        this.$axios.axios({
           method: 'get',
           url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtsp&app='+ this.app +'&stream='+ this.streamId
         }).then(function (res) {
@@ -195,12 +195,14 @@ export default {
       }
     },
     getUrlByStreamInfo(){
+      console.log("获取基础拉流地址")
       console.log(this.streamInfo)
       if (location.protocol === "https:") {
         this.videoUrl = this.streamInfo[this.player[this.activePlayer][1]]
       }else {
         this.videoUrl = this.streamInfo[this.player[this.activePlayer][0]]
       }
+      console.log(this.videoUrl)
       return this.videoUrl;
 
     },

+ 2 - 1
web_src/src/components/dialog/rtcPlayer.vue

@@ -22,7 +22,7 @@ export default {
          if (typeof (this.videoUrl) == "undefined") {
            this.videoUrl = paramUrl;
          }
-         console.log("初始化时的地址为: " + this.videoUrl)
+         // console.log("初始化时的地址为: " + this.videoUrl)
          this.play(this.videoUrl)
         })
     },
@@ -35,6 +35,7 @@ export default {
     },
     methods: {
         play: function (url) {
+          console.log(url);
             webrtcPlayer = new ZLMRTCClient.Endpoint({
                 element: document.getElementById('webRtcPlayerBox'),// video 标签
                 debug: true,// 是否打印日志

+ 12 - 11
web_src/src/layout/UiHeader.vue

@@ -11,7 +11,7 @@
       <el-menu-item index="/pushVideoList">推流列表</el-menu-item>
       <el-menu-item index="/streamProxyList">拉流代理</el-menu-item>
       <el-menu-item index="/cloudRecord">云端录像</el-menu-item>
-      <el-menu-item index="/mediaServerManger">节点管理</el-menu-item>
+      <el-menu-item index="/mediaServerManger">媒体服务管理</el-menu-item>
       <el-menu-item index="/parentPlatformList/15/1">国标级联</el-menu-item>
       <el-menu-item index="/aiLib">算法管理</el-menu-item>
 
@@ -33,7 +33,7 @@
       <!--            <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>-->
       <el-submenu index="" style="float: right;display: flex;align-items: center;">
         <template slot="title" class="flex align-center space-between">
-            <span class="ml-5">欢迎,{{ this.$cookies.get("session").username }}</span>
+            <span class="ml-5">欢迎,{{ username }}</span>
         </template>
 <!--        <el-menu-item @click="openDoc">在线文档</el-menu-item>-->
         <el-menu-item @click="openUserCenter">个人中心</el-menu-item>
@@ -43,9 +43,8 @@
 <!--        <el-menu-item @click="changePassword">修改密码</el-menu-item>-->
         <el-menu-item @click="loginout">注销</el-menu-item>
       </el-submenu>
-
     </el-menu>
-
+    <changePasswordDialog ref="changePasswordDialog"></changePasswordDialog>
   </div>
 </template>
 
@@ -68,10 +67,11 @@ export default {
       // 刷新时间
       refreshTime: 15000,
       requestInProgress: false,
-      editUser: this.$cookies.get("session").roleId==1
+      editUser: userService.getUser() ? userService.getUser().role.id === 1 : false
     };
   },
   created() {
+    console.log(4444)
     console.log(JSON.stringify(userService.getUser()))
     if (this.$route.path.startsWith("/channelList")) {
       this.activeIndex = "/deviceList"
@@ -134,8 +134,6 @@ export default {
             type: 'warning'
           });
           console.log("收到信息:" + evt.data);
-          // 推送消息给其他组件
-
         });
         this.sseSource.addEventListener('open', function (e) {
           console.log("SSE连接打开.");
@@ -205,17 +203,20 @@ export default {
 </script>
 <style>
 #UiHeader .el-switch__label {
-  color: white ;
+  color: white;
 }
+
 .el-menu--popup .el-menu-item .el-switch .el-switch__label {
   color: white !important;
 }
-#UiHeader .el-switch__label.is-active{
+
+#UiHeader .el-switch__label.is-active {
   color: #409EFF;
 }
+
 #UiHeader .el-menu-item.is-active {
-  color: #fff!important;
-  background-color: #1890ff!important;
+  color: #fff !important;
+  background-color: #1890ff !important;
 }
 .badge{
   display: flex;

+ 0 - 1
web_src/src/layout/index.vue

@@ -3,7 +3,6 @@
     <el-header>
       <ui-header/>
     </el-header>
-
     <el-main>
       <el-container>
         <transition name="fade">

+ 16 - 5
web_src/src/main.js

@@ -7,7 +7,7 @@ import 'element-ui/lib/theme-chalk/index.css';
 
 import "./assets/index.css";
 import router from './router/index.js';
-import axios from '@/apiStore/axios';
+import axios from './apiStore/axios';
 import VueCookies from 'vue-cookies';
 import echarts from 'echarts';
 import VCharts from 'v-charts';
@@ -46,10 +46,11 @@ Vue.prototype.$notify = Notification;
 Vue.use(Contextmenu);
 Vue.use(VCharts);
 
-axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : (window.baseUrl ? window.baseUrl : "");
-axios.defaults.withCredentials = true;
+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自动回登陆页面
-axios.interceptors.response.use((response) => {
+_axios.interceptors.response.use((response) => {
   // 对响应数据做点什么
   let token = response.headers["access-token"];
   if (token) {
@@ -64,7 +65,7 @@ axios.interceptors.response.use((response) => {
   }
   return Promise.reject(error);
 });
-axios.interceptors.request.use(
+_axios.interceptors.request.use(
   config => {
     if (userService.getToken() != null && config.url !== "/api/user/login") {
       config.headers['access-token'] = `${userService.getToken()}`;
@@ -75,6 +76,16 @@ axios.interceptors.request.use(
     return Promise.reject(error);
   }
 );
+_axios.interceptors.request.use(config => {
+  config.headers.withCredentials = true;
+  // App.$message.info('test')
+  config.changeOrigin= true
+  config.credentials= true;
+  config.secure= true
+  return config
+},error =>{
+  return Promise.reject(error)
+})
 
 Vue.prototype.$axios = axios;
 Vue.prototype.$cookies.config(60*30);

部分文件因为文件数量过多而无法显示