From b1084891961232e3c697ea9fc52f127cdccffb6b Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: 星期四, 11 七月 2024 17:28:44 +0800
Subject: [PATCH] 优化:流媒体

---
 ard-work/src/main/resources/templates/test.html                                         |  548 ++++++++---
 ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java                  |   18 
 ard-work/src/main/resources/templates/test1.html                                        | 1161 +++++++++++++++++++++++++
 ard-work/src/main/java/com/ruoyi/device/camera/domain/ArdCameras.java                   |    4 
 ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java                       |    6 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SyncTask.java                          |   19 
 ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java                   |    2 
 ard-work/src/main/resources/mapper/device/ArdCamerasMapper.xml                          |    7 
 /dev/null                                                                               |  484 ----------
 ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java                          |   22 
 ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java |   74 
 ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java                |   82 +
 ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java        |  124 +-
 ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java         |   20 
 ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java               |   29 
 ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/module/ConfigModule.java               |   13 
 server/mediamtx/mediamtx.yml                                                            |    6 
 ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java                        |    4 
 ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/LoginResultCallBack.java          |    6 
 ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java             |   65 
 20 files changed, 1,809 insertions(+), 885 deletions(-)

diff --git a/ard-work/src/main/java/com/ruoyi/device/camera/domain/ArdCameras.java b/ard-work/src/main/java/com/ruoyi/device/camera/domain/ArdCameras.java
index 696247d..317c818 100644
--- a/ard-work/src/main/java/com/ruoyi/device/camera/domain/ArdCameras.java
+++ b/ard-work/src/main/java/com/ruoyi/device/camera/domain/ArdCameras.java
@@ -178,7 +178,7 @@
     /**
      * 鐧诲綍ID
      */
-    private Integer loginId;
+    private Long loginId;
     /**
      * 鍦ㄧ嚎鐘舵�� 0-绂荤嚎 1-鍦ㄧ嚎
      */
@@ -192,7 +192,7 @@
     /**
      * 璧峰閫氶亾鍙�
      */
-    private Integer startDChan;
+    private Integer startChan;
     /**
      * 閫氶亾鏁�
      */
diff --git a/ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java b/ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java
index 5b435a0..d535bbd 100644
--- a/ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java
+++ b/ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java
@@ -4,6 +4,7 @@
 import com.ruoyi.device.camera.domain.ArdCameras;
 import com.ruoyi.device.camera.domain.CameraCmd;
 import com.ruoyi.device.channel.domain.ArdChannel;
+import com.ruoyi.media.domain.Vtdu;
 
 import javax.servlet.http.HttpServletResponse;
 import java.util.List;
@@ -116,4 +117,5 @@
 
     //鏈湴褰曞儚鍋滄
     AjaxResult localRecordStop(CameraCmd cmd);
+
 }
diff --git a/ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java b/ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java
index eea7265..17a7a6d 100644
--- a/ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java
+++ b/ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java
@@ -1,6 +1,8 @@
 package com.ruoyi.device.channel.service;
 
 import java.util.List;
+
+import com.ruoyi.device.camera.domain.ArdCameras;
 import com.ruoyi.device.channel.domain.ArdChannel;
 
 /**
@@ -65,22 +67,6 @@
      * @return 缁撴灉
      */
      public int deleteArdChannelByDeviceId(String deviceId);
-     /**
-      * @Author 鍒樿嫃涔�
-      * @Description 鑾峰彇2涓�氶亾鍒楄〃鐨勪氦闆�
-      * @Date   2024/7/10 9:38
-      * @Param
-      * @return
-      */
-    public List<ArdChannel> sameList(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList);
-    /**
-     * @Author 鍒樿嫃涔�
-     * @Description 鍙�2涓�氶亾鍒楄〃鐨勫樊闆�
-     * @Date   2024/7/10 9:39
-     * @Param
-     * @return
-     */
-    public List<ArdChannel> diffList(List<ArdChannel> firstArrayList, List<ArdChannel> secondArrayList);
 
     /**
      * @Author 鍒樿嫃涔�
@@ -89,5 +75,5 @@
      * @Param
      * @return
      */
-    public void asyncChannel(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList);
+    public void asyncChannel(ArdCameras ardCameras, List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList);
 }
diff --git a/ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java b/ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java
index ce6565d..c715669 100644
--- a/ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java
+++ b/ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java
@@ -1,9 +1,16 @@
 package com.ruoyi.device.channel.service.impl;
 
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import com.ruoyi.common.utils.uuid.IdUtils;
+import com.ruoyi.device.camera.domain.ArdCameras;
+import com.ruoyi.device.camera.factory.CameraSDK;
+import com.ruoyi.device.camera.factory.CameraSDKFactory;
+import com.ruoyi.device.camera.mapper.ArdCamerasMapper;
+import com.ruoyi.media.mapper.VtduMapper;
+import com.ruoyi.media.service.IVtduService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.device.channel.mapper.ArdChannelMapper;
@@ -22,7 +29,10 @@
 public class ArdChannelServiceImpl implements IArdChannelService {
     @Resource
     private ArdChannelMapper ardChannelMapper;
-
+    @Resource
+    private CameraSDKFactory cameraSDKFactory;
+    @Resource
+    private IVtduService vtduService;
     /**
      * 鏌ヨ閫氶亾绠$悊
      *
@@ -101,38 +111,40 @@
         return ardChannelMapper.deleteArdChannelByDeviceId(deviceId);
     }
 
-    //姹備袱涓璞ist鐨勪氦闆�
     @Override
-    public List<ArdChannel> sameList(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) {
-        List<ArdChannel> resultList = newArrayList.stream()
-                .filter(item -> oldArrayList.stream().map(e -> e.getChanNo())
-                        .collect(Collectors.toList()).contains(item.getChanNo()))
-                .collect(Collectors.toList());
-        return resultList;
-    }
+    public void asyncChannel(ArdCameras ardCameras,List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) {
+        // 灏嗗垪琛ㄨ浆鎹负鏄犲皠浠ユ彁楂樻�ц兘
+        Map<Integer, ArdChannel> oldMap = oldArrayList.stream()
+                .collect(Collectors.toMap(ArdChannel::getChanNo, channel -> channel));
+        Map<Integer, ArdChannel> newMap = newArrayList.stream()
+                .collect(Collectors.toMap(ArdChannel::getChanNo, channel -> channel));
 
-    //姹備袱涓璞ist鐨勫樊闆�
-    @Override
-    public List<ArdChannel> diffList(List<ArdChannel> firstArrayList, List<ArdChannel> secondArrayList) {
-        List<ArdChannel> resultList = firstArrayList.stream()
-                .filter(item -> !secondArrayList.stream().map(e -> e.getChanNo()).collect(Collectors.toList()).contains(item.getChanNo()))
-                .collect(Collectors.toList());
-        return resultList;
-    }
+        // 闇�瑕佹洿鏂扮殑鏁版嵁
+        newArrayList.stream()
+                .filter(channel -> {
+                    ArdChannel oldChannel = oldMap.get(channel.getChanNo());
+                    return oldChannel != null && !oldChannel.getName().equals(channel.getName());
+                })
+                .forEach(channel -> {
+                    ArdChannel oldChannel = oldMap.get(channel.getChanNo());
+                    channel.setId(oldChannel.getId());
+                    updateArdChannel(channel);
+                });
 
-    @Override
-    public void asyncChannel(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) {
-        //闇�瑕佹洿鏂扮殑鏁版嵁,鍙傛暟椤哄簭娉ㄦ剰
-        sameList(oldArrayList, newArrayList).stream().forEach(ardChannel -> {
-            updateArdChannel(ardChannel);
-        });
-        //闇�瑕佸垹闄ょ殑鏁版嵁
-        diffList(oldArrayList, newArrayList).stream().forEach(ardChannel -> {
-            deleteArdChannelById(ardChannel.getId());
-        });
-        //闇�瑕佹柊澧炵殑鏁版嵁
-        diffList(newArrayList, oldArrayList).stream().forEach(ardChannel -> {
-            insertArdChannel(ardChannel);
-        });
+        // 闇�瑕佸垹闄ょ殑鏁版嵁
+        oldArrayList.stream()
+                .filter(channel -> !newMap.containsKey(channel.getChanNo()))
+                .forEach(channel -> {
+                    deleteArdChannelById(channel.getId());
+                    vtduService.deleteVtduByName(channel.getDeviceId() + "_" + channel.getChanNo());
+                });
+
+        // 闇�瑕佹柊澧炵殑鏁版嵁
+        newArrayList.stream()
+                .filter(channel -> !oldMap.containsKey(channel.getChanNo()))
+                .forEach(channel -> {
+                    insertArdChannel(channel);
+                    vtduService.addChanToVtdu(ardCameras, channel);
+                });
     }
 }
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java b/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
index 7232194..6d89b58 100644
--- a/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
+++ b/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
@@ -4,10 +4,12 @@
 import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.BaseEntity;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.uuid.IdUtils;
 import com.ruoyi.media.domain.Config;
+import com.ruoyi.media.domain.Paths;
 import com.ruoyi.media.domain.StreamInfo;
 import com.ruoyi.media.domain.Vtdu;
 import com.ruoyi.media.service.IMediaService;
@@ -112,7 +114,7 @@
     @PreAuthorize("@ss.hasPermi('media:stream:remove')")
     @DeleteMapping("/{sessionId}")
     public AjaxResult removePullStreamSession(@PathVariable String sessionId) {
-        List<StreamInfo> pullStreamList = mediaService.getPullStreamList();
+        List<StreamInfo> pullStreamList = mediaService.getPullStreamList(1,1000);
         StreamInfo streamInfo = pullStreamList.stream()
                 .filter(object -> object.getId().equals(sessionId))
                 .collect(Collectors.toList()).get(0);
@@ -143,9 +145,9 @@
     @GetMapping("/path/list")
     @ApiOperation("鑾峰彇褰撳墠閫氶亾鍒楄〃")
     @ApiOperationSupport(order = 5)
-    public TableDataInfo getPaths() {
+    public TableDataInfo getPaths(Integer pageNum,Integer pageSize) {
         startPage();
-        return getDataTable(mediaService.paths());
+        return getDataTable(mediaService.paths(pageNum,pageSize));
     }
 
     /**
@@ -154,7 +156,7 @@
     @GetMapping("/getRtspSessionById")
     @ApiOperation("鎸塈D鏌ヨ鎷夋祦璇︽儏")
     public AjaxResult getRtspSessionById(String sessionId) {
-        List<StreamInfo> pullStreamList = mediaService.getPullStreamList();
+        List<StreamInfo> pullStreamList = mediaService.getPullStreamList(1,1000);
         StreamInfo streamInfo = pullStreamList.stream()
                 .filter(object -> object.getId().equals(sessionId))
                 .collect(Collectors.toList()).get(0);
@@ -176,9 +178,9 @@
     @GetMapping("/pushList")
     @ApiOperation("鑾峰彇鎺ㄦ祦鍒楄〃")
     @ApiOperationSupport(order = 6)
-    public TableDataInfo getPushStreamList() {
+    public TableDataInfo getPushStreamList(Integer pageNum,Integer pageSize) {
         startPage();
-        return getDataTable(mediaService.getPushStreamList());
+        return getDataTable(mediaService.getPushStreamList(pageNum,pageSize));
     }
 
     /**
@@ -188,9 +190,9 @@
     @GetMapping("/pullList")
     @ApiOperation("鑾峰彇鎷夋祦鍒楄〃")
     @ApiOperationSupport(order = 7)
-    public TableDataInfo getPullStreamList() {
+    public TableDataInfo getPullStreamList(Integer pageNum,Integer pageSize) {
         startPage();
-        return getDataTable(mediaService.getPullStreamList());
+        return getDataTable(mediaService.getPullStreamList(pageNum,pageSize));
     }
 
 }
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java b/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
index 360b7fe..9c0602e 100644
--- a/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
+++ b/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
@@ -33,7 +33,7 @@
 
     void removePath(String name);
 
-    List<StreamInfo> paths();
+    List<StreamInfo> paths(Integer pageNum,Integer pageSize);
 
     List<String> getNameList();
 
@@ -45,9 +45,9 @@
 
     RtmpSession getRtmpSessionById(String sessionId);
 
-    List<StreamInfo> getPushStreamList();
+    List<StreamInfo> getPushStreamList(Integer pageNum,Integer pageSize);
 
-    List<StreamInfo> getPullStreamList();
+    List<StreamInfo> getPullStreamList(Integer pageNum,Integer pageSize);
 
     Boolean kickRtspSession(String sessionId);
 
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/IMediaV2Service.java b/ard-work/src/main/java/com/ruoyi/media/service/IMediaV2Service.java
deleted file mode 100644
index cccbc36..0000000
--- a/ard-work/src/main/java/com/ruoyi/media/service/IMediaV2Service.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.ruoyi.media.service;
-
-import com.ruoyi.media.domain.*;
-
-import java.util.List;
-import java.util.Map;
-
-public interface IMediaV2Service {
-    /**
-     * 澧炲姞璺緞
-     * name 鍚嶇О
-     * rtspPath rtsp鍦板潃
-     * mode 妯″紡锛歡pu纭В鐮�/cpu杞В鐮�
-     * isCode 鏄惁杞爜
-     * 鍒樿嫃涔�
-     * 2023/8/12 13:56:52
-     */
-    Map<String, String> addPath(String name, String sourceUrl, String mode, String isCode);
-
-    /**
-     * 淇敼璺緞
-     * name 鍚嶇О
-     * rtspPath rtsp鍦板潃
-     * mode 妯″紡锛氬疄鏃�/鎸夐渶
-     * isCode 鏄惁杞爜
-     * 鍒樿嫃涔�
-     * 2023/8/12 13:56:52
-     */
-    Map<String, String> editPath(String name, String sourceUrl, String mode, String isCode);
-
-    StreamInfo getPathInfo(String name);
-
-    void removePath(String[] names);
-
-    void removePath(String name);
-
-    List<StreamInfo> paths();
-
-    List<String> getNameList();
-
-    boolean checkNameExist(String name);
-
-    RtspSession getRtspSessionById(String sessionId);
-
-    WebrtcSession getWebrtcSessionById(String sessionId);
-
-    RtmpSession getRtmpSessionById(String sessionId);
-
-    List<StreamInfo> getPushStreamList();
-
-    List<StreamInfo> getPullStreamList();
-
-    Boolean kickRtspSession(String sessionId);
-
-    Boolean kickRtmpSession(String sessionId);
-
-    Boolean kickWebrtcSession(String sessionId);
-
-    /**
-     * 閰嶇疆娴佸獟浣撳弬鏁�
-     * 鍒樿嫃涔�
-     * 2023/10/13 15:17:57
-     */
-    public String setConfig(Config config);
-}
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java b/ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java
index 7775461..dca1a55 100644
--- a/ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java
+++ b/ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java
@@ -2,6 +2,7 @@
 
 import java.util.List;
 
+import com.ruoyi.device.camera.domain.ArdCameras;
 import com.ruoyi.device.channel.domain.ArdChannel;
 import com.ruoyi.media.domain.Vtdu;
 
@@ -103,4 +104,7 @@
      * @Param
      */
     public void asyncVtdu(List<Vtdu> vtdus, List<String> names);
+
+    //娣诲姞閫氶亾鑷虫祦濯掍綋
+    void addChanToVtdu(ArdCameras camera, ArdChannel channel);
 }
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
index de4f9fc..0079687 100644
--- a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
+++ b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
@@ -8,6 +8,7 @@
 import com.ruoyi.media.service.IMediaService;
 import com.ruoyi.utils.forest.MediaClient;
 import com.ruoyi.utils.tools.ArdTool;
+import io.swagger.models.auth.In;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.ApplicationArguments;
@@ -146,6 +147,8 @@
         if (StringUtils.isNotEmpty(conf.getRunOnDemand())) {
             runOn = conf.getRunOnDemand();
             info.setMode("0");
+        } else {
+            info.setMode("1");
         }
         //RTSP婧愬湴鍧�
         Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn);
@@ -184,8 +187,8 @@
     }
 
     @Override
-    public List<StreamInfo> paths() {
-        Paths paths = mediaClient.paths();
+    public List<StreamInfo> paths(Integer pageNum, Integer pageSize) {
+        Paths paths = mediaClient.paths(pageNum - 1, pageSize);
         List<Items> items = paths.getItems();
         List<StreamInfo> pathInfoList = new ArrayList<>();
         for (Items item : items) {
@@ -221,23 +224,17 @@
 
     @Override
     public RtspSession getRtspSessionById(String sessionId) {
-        String list = mediaClient.getRtspsessionById(sessionId);
-        RtspSession rtspSession = JSONObject.parseObject(list, RtspSession.class);
-        return rtspSession;
+        return mediaClient.getRtspsessionById(sessionId);
     }
 
     @Override
     public WebrtcSession getWebrtcSessionById(String sessionId) {
-        String list = mediaClient.getWebrtcsessionById(sessionId);
-        WebrtcSession webrtcSession = JSONObject.parseObject(list, WebrtcSession.class);
-        return webrtcSession;
+        return mediaClient.getWebrtcsessionById(sessionId);
     }
 
     @Override
     public RtmpSession getRtmpSessionById(String sessionId) {
-        String list = mediaClient.getRtmpsessionById(sessionId);
-        RtmpSession rtmpSession = JSONObject.parseObject(list, RtmpSession.class);
-        return rtmpSession;
+        return mediaClient.getRtmpsessionById(sessionId);
     }
 
     /**
@@ -246,9 +243,9 @@
      * 2023/8/29 9:37:05
      */
     @Override
-    public List<StreamInfo> getPushStreamList() {
+    public List<StreamInfo> getPushStreamList(Integer pageNum, Integer pageSize) {
         List<StreamInfo> PushStreamInfoList = new ArrayList<>();
-        Paths paths = mediaClient.paths();
+        Paths paths = mediaClient.paths(pageNum - 1, pageSize);
         List<Items> items = paths.getItems();
         for (Items item : items) {
             StreamInfo info = new StreamInfo();
@@ -321,9 +318,9 @@
      * 2023/8/29 9:37:05
      */
     @Override
-    public List<StreamInfo> getPullStreamList() {
+    public List<StreamInfo> getPullStreamList(Integer pageNum, Integer pageSize) {
         List<StreamInfo> PullStreamInfoList = new ArrayList<>();
-        Paths paths = mediaClient.paths();
+        Paths paths = mediaClient.paths(pageNum - 1, pageSize);
         List<Items> items = paths.getItems();
         for (Items item : items) {
             List<Readers> readers = item.getReaders();
@@ -467,7 +464,7 @@
     public List<String> getNameList() {
         List<String> nameList = new ArrayList<>();
         try {
-            Paths paths = mediaClient.paths();
+            Paths paths = mediaClient.paths(0, 1000);
             List<Items> items = paths.getItems();
             for (Items item : items) {
                 nameList.add(item.getName());
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaV2ServiceImpl.java b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaV2ServiceImpl.java
deleted file mode 100644
index ec24786..0000000
--- a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaV2ServiceImpl.java
+++ /dev/null
@@ -1,484 +0,0 @@
-package com.ruoyi.media.service.impl;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.dtflys.forest.exceptions.ForestNetworkException;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.media.domain.*;
-import com.ruoyi.media.service.IMediaV2Service;
-import com.ruoyi.utils.forest.MediaClient;
-import com.ruoyi.utils.tools.ArdTool;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * @Description: 娴佸獟浣撲笟鍔�
- * @ClassName: MediaService
- * @Author: 鍒樿嫃涔�
- * @Date: 2023骞�07鏈�13鏃�9:28
- * @Version: 1.0
- **/
-@Service
-@Slf4j(topic = "cmd")
-@Order(2)
-public class MediaV2ServiceImpl implements IMediaV2Service {
-
-    @Resource
-    MediaClient mediaClient;
-
-    @Value("${mediamtx.host}")
-    String mediamtxHost;
-
-    /**
-     * 娣诲姞娴佸獟浣�
-     * name 鐩告満ID
-     * sourceUrl rtsp鍦板潃
-     * isCode 0-涓嶈浆鐮� 1-杞爜
-     * mode 0-gpu纭В鐮� 1-cpu杞В鐮�
-     * <p>
-     * 鍒樿嫃涔�
-     * 2023/10/12 9:03:41
-     */
-    @Override
-    public Map<String, String> addPath(String name, String sourceUrl, String mode, String isCode) {
-
-        String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name;
-        String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name;
-        String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name;
-
-        Conf conf = new Conf();
-        String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/";
-        if (isCode.equals("1")) {
-            conf.setSource("publisher");
-            //榛樿杞В鐮�
-            String cmd = "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 60 -sc_threshold 0 -threads 6  -b:v 2048k -acodec opus  -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
-            if (mode.equals("0")) {//纭В鐮�
-                cmd = "ffmpeg -hwaccel cuvid -c:v hevc_cuvid  -rtsp_transport tcp  -i " + sourceUrl + " -c:v h264_nvenc  -r 25 -g 60 -sc_threshold 0 -threads 6  -b:v 2048k -bf 0 -acodec opus  -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
-            }
-            conf.setRunOnDemand(cmd);
-            conf.setRunOnDemandRestart(true);
-            conf.setRunOnDemandCloseAfter("5s");
-        } else {
-            conf.setSource(sourceUrl);
-            conf.setRunOnDemandRestart(true);
-        }
-        conf.setMaxReaders(100);
-        conf.setSourceProtocol("tcp");
-
-        List<String> nameList = new ArrayList<>();
-        Paths paths = mediaClient.paths();
-        List<Items> items = paths.getItems();
-        for (Items item : items) {
-            nameList.add(item.getName());
-        }
-        if (!nameList.contains(name)) {
-            mediaClient.addPath(name, conf);
-        }
-        Map<String, String> map = new HashMap<>();
-        map.put("rtspUrl", rtspUrl);
-        map.put("rtmpUrl", rtmpUrl);
-        map.put("webrtcUrl", webrtcUrl);
-        return map;
-    }
-
-    @Override
-    public Map<String, String> editPath(String name, String sourceUrl, String mode, String isCode) {
-        Map<String, String> map = new HashMap<>();
-        try {
-            String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name;
-            String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name;
-            String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name;
-
-            Conf mediaInfo = new Conf();
-            String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/";
-            if (isCode.equals("1")) {
-                mediaInfo.setSource("publisher");
-                //榛樿杞В鐮�
-                String cmd = "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 60 -sc_threshold 0 -threads 6  -b:v 2048k -acodec opus  -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
-                if (mode.equals("0")) {//纭В鐮�
-                    cmd = "ffmpeg -hwaccel cuvid -c:v hevc_cuvid  -rtsp_transport tcp  -i " + sourceUrl + " -c:v h264_nvenc  -r 25 -g 60 -sc_threshold 0 -threads 6  -b:v 2048k -bf 0 -acodec opus  -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
-                }
-                mediaInfo.setRunOnDemand(cmd);
-                mediaInfo.setRunOnDemandRestart(true);
-                mediaInfo.setRunOnDemandCloseAfter("5s");
-            } else {
-                mediaInfo.setSource(sourceUrl);
-            }
-            mediaInfo.setMaxReaders(100);
-            mediaInfo.setSourceProtocol("tcp");
-
-            if (checkNameExist(name)) {
-                mediaClient.editPath(name, mediaInfo);
-            }
-
-            map.put("rtspUrl", rtspUrl);
-            map.put("rtmpUrl", rtmpUrl);
-            map.put("webrtcUrl", webrtcUrl);
-        } catch (ForestNetworkException ex) {
-            log.error(ex.getMessage());
-        }
-        return map;
-    }
-
-    @Override
-    public StreamInfo getPathInfo(String name) {
-        Conf conf = mediaClient.getPathInfo(name);
-        StreamInfo info = new StreamInfo();
-        //ID
-        info.setName(name);
-        String runOn="";
-        if (StringUtils.isNotEmpty(conf.getRunOnDemand())) {
-            runOn = conf.getRunOnDemand();
-        }
-        //RTSP婧愬湴鍧�
-        Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn);
-        if (matcher.find()) {
-            info.setRtspSource(matcher.group());
-            info.setIsCode("1");
-        } else {
-            info.setRtspSource(conf.getSource());
-            info.setIsCode("0");
-        }
-        return info;
-    }
-
-    @Override
-    public void removePath(String[] names) {
-        for (String name : names) {
-            if(checkNameExist(name)) {
-            mediaClient.removePath(name);}
-        }
-    }
-
-    @Override
-    public void removePath(String name) {
-        if(checkNameExist(name)) {
-            mediaClient.removePath(name);
-        }
-    }
-
-    @Override
-    public List<StreamInfo> paths() {
-        Paths paths = mediaClient.paths();
-        List<Items> items = paths.getItems();
-        List<StreamInfo> pathInfoList = new ArrayList<>();
-        for (Items item : items) {
-            StreamInfo info = new StreamInfo();
-            //ID
-            String name = item.getName();
-            info.setName(name);
-            Conf conf = mediaClient.getPathInfo(name);
-
-            String runOn = "";
-            if (StringUtils.isNotEmpty(conf.getRunOnDemand())) {
-                runOn = conf.getRunOnDemand();
-            }
-            //RTSP婧愬湴鍧�
-            Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn);
-            if (matcher.find()) {
-                info.setRtspSource(matcher.group());
-                info.setIsCode("1");
-            } else {
-                info.setRtspSource(conf.getSource());
-                info.setIsCode("0");
-            }
-            //浼犺緭鍗忚
-            matcher = Pattern.compile("-rtsp_transport\\s+(\\w+)").matcher(runOn);
-            if (matcher.find()) {
-                info.setProtocol(matcher.group(1));
-            }
-
-            pathInfoList.add(info);
-        }
-        return pathInfoList;
-    }
-
-
-    @Override
-    public RtspSession getRtspSessionById(String sessionId) {
-        String list = mediaClient.getRtspsessionById(sessionId);
-        RtspSession rtspSession = JSONObject.parseObject(list, RtspSession.class);
-        return rtspSession;
-    }
-
-    @Override
-    public WebrtcSession getWebrtcSessionById(String sessionId) {
-        String list = mediaClient.getWebrtcsessionById(sessionId);
-        WebrtcSession webrtcSession = JSONObject.parseObject(list, WebrtcSession.class);
-        return webrtcSession;
-    }
-
-    @Override
-    public RtmpSession getRtmpSessionById(String sessionId) {
-        String list = mediaClient.getRtmpsessionById(sessionId);
-        RtmpSession rtmpSession = JSONObject.parseObject(list, RtmpSession.class);
-        return rtmpSession;
-    }
-
-    /**
-     * 鑾峰彇鎺ㄦ祦鍒楄〃
-     * 鍒樿嫃涔�
-     * 2023/8/29 9:37:05
-     */
-    @Override
-    public List<StreamInfo> getPushStreamList() {
-        List<StreamInfo> PushStreamInfoList = new ArrayList<>();
-        Paths paths = mediaClient.paths();
-        List<Items> items = paths.getItems();
-        for (Items item : items) {
-            StreamInfo info = new StreamInfo();
-            //ID
-            String name = item.getName();
-            info.setName(name);
-
-            Conf conf = mediaClient.getPathInfo(name);
-            //RTMP鎾斁鍦板潃
-            String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name;
-            info.setRtmpUrl(rtmpUrl);
-            //RTSP鎾斁鍦板潃
-            String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name;
-            info.setRtspUrl(rtspUrl);
-            //WEBRTC鎾斁鍦板潃
-            String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name;
-            info.setWebrtcUrl(webrtcUrl);
-            Source source = item.getSource();
-            if (source == null || source.getId().equals("")) {
-                //浼氳瘽ID
-                info.setId("0");
-                //涓婅娴侀噺
-                long bytesReceived = item.getBytesReceived();
-                String formatReceivedSize = ArdTool.formatFileSize(bytesReceived);
-                info.setUpTraffic(formatReceivedSize);
-            } else {
-                RtspSession rtspSession = getRtspSessionById(source.getId());
-                //浼氳瘽ID
-                info.setId(rtspSession.getId());
-                //寮�濮嬫帹娴佹椂闂�
-                info.setBeginTime(rtspSession.getCreated());
-                //涓婅娴侀噺
-                long bytesReceived = rtspSession.getBytesReceived();
-                String formatReceivedSize = ArdTool.formatFileSize(bytesReceived);
-                info.setUpTraffic(formatReceivedSize);
-                //涓嬭娴侀噺
-                long bytesSent = rtspSession.getBytesSent();
-                String formatSentSize = ArdTool.formatFileSize(bytesSent);
-                info.setDownTraffic(formatSentSize);
-                //鎺ㄦ祦鏈嶅姟鍣�
-                info.setRemoteAddr(rtspSession.getRemoteAddr());
-            }
-            //RTSP婧愬湴鍧�
-            String runOn = "";
-            if (StringUtils.isNotEmpty(conf.getRunOnDemand())) {
-                runOn = conf.getRunOnDemand();
-            }
-            Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn);
-            if (matcher.find()) {
-                info.setRtspSource(matcher.group());
-            } else {
-                info.setRtspSource(conf.getSource());
-            }
-            //浼犺緭鍗忚
-            info.setProtocol(conf.getSourceProtocol());
-            //鎷夋祦鏁伴噺
-            List<Readers> readers = item.getReaders();
-            info.setNum(readers.size());
-
-            PushStreamInfoList.add(info);
-        }
-        return PushStreamInfoList;
-    }
-
-    /**
-     * 鑾峰彇鎷夋祦鍒楄〃
-     * 鍒樿嫃涔�
-     * 2023/8/29 9:37:05
-     */
-    @Override
-    public List<StreamInfo> getPullStreamList() {
-        List<StreamInfo> PullStreamInfoList = new ArrayList<>();
-        Paths paths = mediaClient.paths();
-        List<Items> items = paths.getItems();
-        for (Items item : items) {
-            List<Readers> readers = item.getReaders();
-            for (Readers reader : readers) {
-                StreamInfo info = new StreamInfo();
-                //ID
-                String name = item.getName();
-                info.setName(name);
-
-                Conf conf = mediaClient.getPathInfo(name);
-                //浼犺緭鍗忚
-                info.setProtocol(conf.getSourceProtocol());
-
-                String type = reader.getType();
-                switch (type) {
-                    case "rtmpConn":
-                        info.setSessionType("rtmp");
-                        //webrtc鎾斁鍦板潃
-                        String url = "rtmp://" + mediamtxHost + ":1935/" + name;
-                        info.setRtspUrl(url);
-                        RtmpSession rtmpSession = getRtmpSessionById(reader.getId());
-                        //浼氳瘽ID
-                        info.setId(rtmpSession.getId());
-                        //寮�濮嬫媺娴佹椂闂�
-                        info.setBeginTime(rtmpSession.getCreated());
-                        //涓婅娴侀噺
-                        long bytesReceived = rtmpSession.getBytesReceived();
-                        String formatReceivedSize = ArdTool.formatFileSize(bytesReceived);
-                        info.setUpTraffic(formatReceivedSize);
-                        //涓嬭娴侀噺
-                        long bytesSent = rtmpSession.getBytesSent();
-                        String formatSentSize = ArdTool.formatFileSize(bytesSent);
-                        info.setDownTraffic(formatSentSize);
-                        //鎷夋祦鏈嶅姟鍣�
-                        info.setRemoteAddr(rtmpSession.getRemoteAddr());
-                        PullStreamInfoList.add(info);
-                        break;
-                    case "webRTCSession":
-                        info.setSessionType("webrtc");
-                        //webrtc鎾斁鍦板潃
-                        url = "http://" + mediamtxHost + ":8889/" + name;
-                        info.setRtspUrl(url);
-                        WebrtcSession webrtcSession = getWebrtcSessionById(reader.getId());
-                        //浼氳瘽ID
-                        info.setId(webrtcSession.getId());
-                        //寮�濮嬫媺娴佹椂闂�
-                        info.setBeginTime(webrtcSession.getCreated());
-                        //涓婅娴侀噺
-                        bytesReceived = webrtcSession.getBytesReceived();
-                        formatReceivedSize = ArdTool.formatFileSize(bytesReceived);
-                        info.setUpTraffic(formatReceivedSize);
-                        //涓嬭娴侀噺
-                        bytesSent = webrtcSession.getBytesSent();
-                        formatSentSize = ArdTool.formatFileSize(bytesSent);
-                        info.setDownTraffic(formatSentSize);
-                        //鎷夋祦鏈嶅姟鍣�
-                        info.setRemoteAddr(webrtcSession.getRemoteAddr());
-                        PullStreamInfoList.add(info);
-                        break;
-                    case "rtspSession":
-                        info.setSessionType("rtsp");
-                        //RTSP鎾斁鍦板潃
-                        String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name;
-                        info.setRtspUrl(rtspUrl);
-                        RtspSession rtspSession = getRtspSessionById(reader.getId());
-                        //浼氳瘽ID
-                        info.setId(rtspSession.getId());
-                        //寮�濮嬫媺娴佹椂闂�
-                        info.setBeginTime(rtspSession.getCreated());
-                        //涓婅娴侀噺
-                        bytesReceived = rtspSession.getBytesReceived();
-                        formatReceivedSize = ArdTool.formatFileSize(bytesReceived);
-                        info.setUpTraffic(formatReceivedSize);
-                        //涓嬭娴侀噺
-                        bytesSent = rtspSession.getBytesSent();
-                        formatSentSize = ArdTool.formatFileSize(bytesSent);
-                        info.setDownTraffic(formatSentSize);
-                        //鎷夋祦鏈嶅姟鍣�
-                        info.setRemoteAddr(rtspSession.getRemoteAddr());
-                        PullStreamInfoList.add(info);
-                        break;
-                }
-            }
-        }
-        Comparator<StreamInfo> comparator = Comparator.comparing(streamInfo -> streamInfo.getBeginTime()); // 浣跨敤Collections.sort鏂规硶杩涜鎺掑簭 Collections.sort(personList, comparator);
-        Collections.sort(PullStreamInfoList, comparator.reversed());
-        return PullStreamInfoList;
-    }
-
-    /**
-     * 韪㈠嚭rtsp浼氳瘽
-     * 鍒樿嫃涔�
-     * 2023/8/29 9:37:05
-     */
-    @Override
-    public Boolean kickRtspSession(String sessionId) {
-        try {
-            mediaClient.kickRtspSessions(sessionId);
-            return true;
-        } catch (Exception ex) {
-            return false;
-        }
-    }
-
-    /**
-     * 韪㈠嚭rtmp浼氳瘽
-     * 鍒樿嫃涔�
-     * 2023/8/29 9:37:05
-     */
-    @Override
-    public Boolean kickRtmpSession(String sessionId) {
-        try {
-            mediaClient.kickRtmpSessions(sessionId);
-            return true;
-        } catch (Exception ex) {
-            return false;
-        }
-    }
-
-    /**
-     * 韪㈠嚭webrtc浼氳瘽
-     * 鍒樿嫃涔�
-     * 2023/8/29 9:37:05
-     */
-    @Override
-    public Boolean kickWebrtcSession(String sessionId) {
-        try {
-            mediaClient.kickWebrtcSessions(sessionId);
-            return true;
-        } catch (Exception ex) {
-            return false;
-        }
-    }
-
-    /**
-     * 鑾峰彇娴佸獟浣搉ame鍒楄〃
-     * 鍒樿嫃涔�
-     * 2023/10/13 14:19:07
-     */
-    @Override
-    public List<String> getNameList() {
-        List<String> nameList = new ArrayList<>();
-        try {
-            Paths paths = mediaClient.paths();
-            List<Items> items = paths.getItems();
-            for (Items item : items) {
-                nameList.add(item.getName());
-            }
-        }
-        catch (Exception ex)
-        {
-            log.error(ex.getMessage());
-        }
-        return nameList;
-    }
-
-    /**
-     * 妫�鏌ュ悕绉版槸鍚﹀瓨鍦�
-     * 鍒樿嫃涔�
-     * 2023/10/19 15:18:45
-     */
-    @Override
-    public boolean checkNameExist(String name) {
-        boolean result = false;
-        List<String> nameList = getNameList();
-        if (nameList.contains(name)) {
-            result = true;
-        }
-        return result;
-    }
-
-    /**
-     * 閰嶇疆娴佸獟浣撳弬鏁�
-     */
-    @Override
-    public String setConfig(Config config) {
-        return mediaClient.setConfig(config);
-    }
-}
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java b/ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java
index 36a8628..f0be3d3 100644
--- a/ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java
+++ b/ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java
@@ -6,9 +6,15 @@
 import java.util.stream.Collectors;
 
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.device.camera.domain.ArdCameras;
+import com.ruoyi.device.camera.domain.CameraCmd;
+import com.ruoyi.device.camera.factory.CameraSDK;
+import com.ruoyi.device.camera.factory.CameraSDKFactory;
 import com.ruoyi.device.channel.domain.ArdChannel;
+import com.ruoyi.media.domain.StreamInfo;
 import com.ruoyi.media.service.IMediaService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import com.ruoyi.media.mapper.VtduMapper;
 import com.ruoyi.media.domain.Vtdu;
@@ -30,7 +36,10 @@
     private VtduMapper vtduMapper;
     @Resource
     private IMediaService mediaService;
-
+    @Resource
+    private CameraSDKFactory cameraSDKFactory;
+    @Value("${mediamtx.host}")
+    String mediamtxHost;
     /**
      * 鏌ヨ娴佸獟浣撶鐞�
      *
@@ -39,7 +48,6 @@
      */
     @Override
     public Vtdu selectVtduByName(String name) {
-
         return vtduMapper.selectVtduByName(name);
     }
 
@@ -62,17 +70,6 @@
      */
     @Override
     public int insertVtdu(Vtdu vtdu) {
-        log.debug("娴佸獟浣撱��" + vtdu.getName() + "銆戦�氶亾娣诲姞");
-        Map<String, String> map = mediaService.addPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode());
-        vtdu.setRtspUrl(map.get("rtspUrl"));
-        vtdu.setRtmpUrl(map.get("rtmpUrl"));
-        vtdu.setWebrtcUrl(map.get("webrtcUrl"));
-
-        vtdu.setName(vtdu.getName());
-        vtdu.setRtspSource(vtdu.getRtspSource());
-        vtdu.setIsCode(vtdu.getIsCode());
-        vtdu.setMode(vtdu.getMode());
-        vtdu.setCreateTime(DateUtils.getNowDate());
         return vtduMapper.insertVtdu(vtdu);
     }
 
@@ -84,7 +81,6 @@
      */
     @Override
     public int updateVtdu(Vtdu vtdu) {
-        log.debug("娴佸獟浣撱��" + vtdu.getName() + "銆戦�氶亾鏇存柊");
         Map<String, String> map = mediaService.editPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode());
         vtdu.setName(vtdu.getName());
         vtdu.setRtspSource(vtdu.getRtspSource());
@@ -171,12 +167,10 @@
                     .collect(Collectors.toList());
             return nameList;
         } else {
-
             List<String> nameList = names.stream().filter(item -> !vtdus.stream().map(Vtdu::getName).collect(Collectors.toList()).contains(item))
                     .collect(Collectors.toList());
             return nameList;
         }
-
     }
 
     //闇�瑕佹柊澧炵殑锛堟祦濯掍綋涓皯鐨勶級
@@ -186,18 +180,21 @@
     }
 
     /**
-     * @Author 鍒樿嫃涔�
-     * @Description  鍚屾鏈湴vtdu搴撳拰娴佸獟浣撲腑鐨勬暟鎹�
-     * @Date   2024/7/10 15:26
-     * @Param  vtdus vtdu搴撶殑闆嗗悎
-     * @Param  names 娴佸獟浣撶殑name闆嗗悎
      * @return
+     * @Author 鍒樿嫃涔�
+     * @Description 鍚屾鏈湴vtdu搴撳拰娴佸獟浣撲腑鐨勬暟鎹�
+     * @Date 2024/7/10 15:26
+     * @Param vtdus vtdu搴撶殑闆嗗悎
+     * @Param names 娴佸獟浣撶殑name闆嗗悎
      */
     @Override
     public void asyncVtdu(List<Vtdu> vtdus, List<String> names) {
         //闇�瑕佹洿鏂扮殑鏁版嵁,鍙傛暟椤哄簭娉ㄦ剰
         sameList(vtdus, names).stream().forEach(vtdu -> {
-            mediaService.editPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode());
+            StreamInfo streamInfo = mediaService.getPathInfo(vtdu.getName());
+            if (!streamInfo.getRtspSource().equals(vtdu.getRtspSource())) {
+                mediaService.editPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode());
+            }
         });
         //闇�瑕佸垹闄ょ殑鏁版嵁
         diffListToDel(vtdus, names).stream().forEach(name -> {
@@ -208,4 +205,45 @@
             mediaService.addPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode());
         });
     }
+    @Override
+    public void addChanToVtdu(ArdCameras camera, ArdChannel channel) {
+        String name = camera.getId() + "_" + channel.getChanNo();
+        String rtspSource="";
+        switch (camera.getFactory())
+        {
+            case "1": rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";break;
+            case "2": rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";break;
+        }
+
+        //鍒犻櫎娴佸獟浣�
+        if (selectVtduByName(name) != null) {
+            deleteVtduByName(name);
+        }
+        //娣诲姞鍒版祦濯掍綋
+        CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo());
+        CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(camera.getFactory());
+        Map<String, Object> videoCompressionCfg = cameraSDK.getVideoCompressionCfg(cmd);
+        Vtdu vtdu = new Vtdu();
+        if (videoCompressionCfg.get("videoEncType") != null) {
+            if (videoCompressionCfg.get("videoEncType").equals("鏍囧噯h264")) {
+                vtdu.setIsCode("0");//榛樿涓嶈浆鐮�
+            } else {
+                vtdu.setIsCode("1");//榛樿杞爜
+            }
+        } else {
+            vtdu.setIsCode("0");//榛樿涓嶈浆鐮�
+        }
+        vtdu.setRtspSource(rtspSource);
+        vtdu.setName(name);
+        vtdu.setMode("1");//榛樿CPU杞В鐮�
+        vtdu.setCameraId(camera.getId());
+        String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name;
+        String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name;
+        String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name;
+        vtdu.setRtmpUrl(rtmpUrl);
+        vtdu.setWebrtcUrl(webrtcUrl);
+        vtdu.setRtspUrl(rtspUrl);
+        insertVtdu(vtdu);
+    }
+
 }
diff --git a/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java b/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
index 158a6ee..e441bc0 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
@@ -1,10 +1,7 @@
 package com.ruoyi.utils.forest;
 
 import com.dtflys.forest.annotation.*;
-import com.ruoyi.media.domain.Conf;
-import com.ruoyi.media.domain.Config;
-import com.ruoyi.media.domain.Items;
-import com.ruoyi.media.domain.Paths;
+import com.ruoyi.media.domain.*;
 
 import java.nio.file.Path;
 
@@ -38,14 +35,14 @@
     /**
      * 鑾峰彇璺緞璇︽儏
      */
-    @Get(url ="/config/paths/get/{name}")
+    @Get(url = "/config/paths/get/{name}")
     public Conf getPathInfo(@Var("name") String name);
 
     /**
      * 鏌ヨ鎵�鏈夎矾寰�
      */
-    @Get("/paths/list")
-    public Paths paths();
+    @Get("/paths/list?page={pageNum}&itemsPerPage={pageSize}")
+    public Paths paths(@Var("pageNum") Integer pageNum, @Var("pageSize") Integer pageSize);
 
     /**
      * 鏌ヨ鎵�鏈塺tsp浼氳瘽
@@ -57,19 +54,19 @@
      * 鎸塻essionId鏌ヨrtsp浼氳瘽
      */
     @Get("/rtspsessions/get/{sessionId}")
-    public String getRtspsessionById(@Var("sessionId") String sessionId);
+    public RtspSession getRtspsessionById(@Var("sessionId") String sessionId);
 
     /**
      * 鎸塻essionId鏌ヨwebrtc浼氳瘽
      */
     @Get("/webrtcsessions/get/{sessionId}")
-    public String getWebrtcsessionById(@Var("sessionId") String sessionId);
+    public WebrtcSession getWebrtcsessionById(@Var("sessionId") String sessionId);
 
     /**
      * 鎸塻essionId鏌ヨrtmp浼氳瘽
      */
     @Get("/rtmpconns/get/{sessionId}")
-    public String getRtmpsessionById(@Var("sessionId") String sessionId);
+    public RtmpSession getRtmpsessionById(@Var("sessionId") String sessionId);
 
     /**
      * 鎸塻essionId鍒犻櫎rtsp浼氳瘽
@@ -89,9 +86,4 @@
     @Post("/webrtcsessions/kick/{sessionId}")
     public String kickWebrtcSessions(@Var("sessionId") String sessionId);
 
-    /**
-     * 閰嶇疆娴佸獟浣撳弬鏁�
-     */
-    @Post("/config/set")
-    public String setConfig(@JSONBody Config config);
 }
diff --git a/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/module/ConfigModule.java b/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/module/ConfigModule.java
index 631218d..e5c60f4 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/module/ConfigModule.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/module/ConfigModule.java
@@ -81,10 +81,12 @@
 
         return result;
     }
-    public static String getChannelName(NetSDKLib.LLong hLoginHandle,Integer channel) {
+
+    //鏌ヨ閫氶亾鍚嶇О
+    public static String getChannelName(NetSDKLib.LLong hLoginHandle, Integer channel) {
         String channelName = "";
         NetSDKLib.AV_CFG_ChannelName channelTitleName = new NetSDKLib.AV_CFG_ChannelName();
-        if (ToolKits.GetDevConfig(hLoginHandle, channel, NetSDKLib.CFG_CMD_CHANNELTITLE, channelTitleName)) {
+        if (ToolKits.GetDevConfig(hLoginHandle, channel - 1, NetSDKLib.CFG_CMD_CHANNELTITLE, channelTitleName)) {
             try {
                 channelName = new String(channelTitleName.szName, "GBK");
             } catch (Exception e) {
@@ -95,6 +97,7 @@
         }
         return channelName;
     }
+
     public static boolean GetDevConfig(NetSDKLib.LLong hLoginHandle, int nChn, String strCmd, Structure cmdObject) {
         boolean result = true;
         IntByReference error = new IntByReference(0);
@@ -111,6 +114,7 @@
         return result;
     }
 
+    //鏌ヨ鐩告満鐘舵��
     public static boolean queryCameraState(NetSDKLib.LLong hLoginHandle, Integer chanNum, Integer chanNo) {
         boolean bRet = false;
         try {
@@ -141,9 +145,9 @@
                 stOut.read();
                 ToolKits.GetPointerDataToStructArr(stOut.pCameraStateInfo, arrCameraStatus);  // 灏哖ointer鎷疯礉鍒版暟缁勫唴瀛�
                 final String[] connectionState = {"鏈煡", "姝e湪杩炴帴", "宸茶繛鎺�", "鏈繛鎺�", "閫氶亾鏈厤缃�,鏃犱俊鎭�", "閫氶亾鏈夐厤缃�,浣嗚绂佺敤"};
-               log.debug(connectionState[arrCameraStatus[chanNo - 1].emConnectionState]);
+
                 if (connectionState[arrCameraStatus[chanNo - 1].emConnectionState].equals("宸茶繛鎺�")) {
-                    log.debug("閫氶亾" + arrCameraStatus[chanNo - 1].nChannel + connectionState[arrCameraStatus[chanNo - 1].emConnectionState]);
+                    //log.debug("閫氶亾锛�" + arrCameraStatus[chanNo - 1].nChannel + "鐘舵�侊細" + connectionState[arrCameraStatus[chanNo - 1].emConnectionState]);
                     bRet = true;
                 } else {
                     bRet = false;
@@ -205,6 +209,5 @@
 
         return result;
     }
-
 
 }
diff --git a/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java b/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java
index 3957ce6..be1a771 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java
@@ -104,7 +104,7 @@
             NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo);
             if (loginId.longValue() <= 0) {
                 camera.setChanNum(0);
-                camera.setLoginId(-1);
+                camera.setLoginId(-1l);
                 camera.setState("0");
                 ardCamerasService.updateArdCameras(camera);
                 //鍒犻櫎绠$悊閫氶亾
@@ -112,11 +112,11 @@
                 log.error("璁惧[" + camera.getIp() + ":" + camera.getPort() + "]鐧诲綍澶辫触:" + getErrorCodePrint());
                 return AjaxResult.warn(ErrorCode.getErrorCode(LoginModule.netsdk.CLIENT_GetLastError()));
             }
-
+            log.debug("璁惧[" + camera.getIp() + ":" + camera.getPort() + "]鐧诲綍鎴愬姛:" + (int) loginId.longValue());
             camera.setState("1");
             camera.setChanNum(m_stDeviceInfo.byChanNum);
-            camera.setStartDChan(1);
-            camera.setLoginId((int) loginId.longValue());
+            camera.setStartChan(1);
+            camera.setLoginId(loginId.longValue());
             GlobalVariable.loginMap.put(camera.getId(), loginId);
             //鑾峰彇鏈�鏂伴�氶亾
             List<ArdChannel> ardChannelList = getChannels(camera);
@@ -125,12 +125,11 @@
                 ardChannelList.stream().forEach(channel -> {
                     ardChannelService.insertArdChannel(channel);
                 });
-                camera.setChanNum(ardChannelList.size());
                 camera.setChannelList(ardChannelList);
-                ardCamerasService.updateArdCameras(camera);
-                //閰嶇疆鍒版祦濯掍綋
-                addVtdu(camera);
+                //閫氶亾鎵归噺娣诲姞鍒版祦濯掍綋
+                batchAddVtdu(camera);
             }
+            ardCamerasService.updateArdCameras(camera);
             //鍒涘缓寮曞闃熷垪
             createGuideQueue(camera);
             return AjaxResult.success("璁惧鐧诲綍鎴愬姛");
@@ -149,7 +148,7 @@
             NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo);
             if (loginId.longValue() <= 0) {
                 camera.setChanNum(0);
-                camera.setLoginId(-1);
+                camera.setLoginId(-1l);
                 camera.setState("0");
                 ardCamerasService.updateArdCameras(camera);
                 //鍒犻櫎绠$悊閫氶亾
@@ -157,10 +156,11 @@
                 log.error("璁惧[" + camera.getIp() + ":" + camera.getPort() + "]鐧诲綍澶辫触:" + getErrorCodePrint());
                 return AjaxResult.warn(getErrorCodePrint());
             }
+            log.debug("璁惧[" + camera.getIp() + ":" + camera.getPort() + "]鐧诲綍鎴愬姛:" + loginId);
             camera.setState("1");
             camera.setChanNum(m_stDeviceInfo.byChanNum);
-            camera.setStartDChan(1);
-            camera.setLoginId((int) loginId.longValue());
+            camera.setStartChan(1);
+            camera.setLoginId((Long) loginId.longValue());
             ardCamerasService.updateArdCameras(camera);
             GlobalVariable.loginMap.put(camera.getId(), loginId);
 
@@ -171,12 +171,11 @@
                 ardChannelList.stream().forEach(channel -> {
                     ardChannelService.insertArdChannel(channel);
                 });
-                camera.setChanNum(ardChannelList.size());
                 camera.setChannelList(ardChannelList);
-                ardCamerasService.updateArdCameras(camera);
-                //閰嶇疆鍒版祦濯掍綋
-                addVtdu(camera);
+                //閫氶亾鎵归噺娣诲姞鍒版祦濯掍綋
+                batchAddVtdu(camera);
             }
+            ardCamerasService.updateArdCameras(camera);
             //鍒涘缓寮曞闃熷垪
             createGuideQueue(camera);
             return AjaxResult.success("鐧诲綍鎴愬姛");
@@ -186,35 +185,13 @@
         }
     }
 
-    //娣诲姞鍒版祦濯掍綋
-    private void addVtdu(ArdCameras camera) {
+    //閫氶亾鎵归噺娣诲姞鍒版祦濯掍綋
+    public void batchAddVtdu(ArdCameras camera) {
         camera.getChannelList().stream().forEach(channel -> {
-            String name = camera.getId() + "_" + channel.getChanNo();
-            String rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";
-             //鍒犻櫎娴佸獟浣�
-            if (vtduService.selectVtduByName(name) != null) {
-                vtduService.deleteVtduByName(name);
-            }
-            //娣诲姞鍒版祦濯掍綋
-            CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo());
-            Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd);
-            Vtdu vtdu = new Vtdu();
-            if (videoCompressionCfg.get("videoEncType") != null) {
-                if (videoCompressionCfg.get("videoEncType").equals("鏍囧噯h264")) {
-                    vtdu.setIsCode("0");//榛樿涓嶈浆鐮�
-                } else {
-                    vtdu.setIsCode("1");//榛樿杞爜
-                }
-            } else {
-                vtdu.setIsCode("0");//榛樿涓嶈浆鐮�
-            }
-            vtdu.setRtspSource(rtspSource);
-            vtdu.setName(name);
-            vtdu.setMode("1");//榛樿CPU杞В鐮�
-            vtdu.setCameraId(camera.getId());
-            vtduService.insertVtdu(vtdu);
+            vtduService.addChanToVtdu(camera, channel);
         });
     }
+
 
     //鍒涘缓寮曞闃熷垪
     private void createGuideQueue(ArdCameras camera) {
@@ -231,12 +208,14 @@
 
     //鑾峰彇閫氶亾
     public List<ArdChannel> getChannels(ArdCameras camera) {
+        if (camera.getLoginId().equals(-1)) {
+            return new ArrayList<>();
+        }
         LLong loginId = new LLong(camera.getLoginId());
         List<ArdChannel> ardChannelList = new ArrayList<>();
         for (int i = 1; i < camera.getChanNum() + 1; i++) {
             ArdChannel channel = new ArdChannel();
-            String chanName = ConfigModule.getChannelName(loginId, i - 1).trim();
-            log.debug("鑾峰彇閫氶亾鍚嶇О锛�" + chanName);
+            String chanName = ConfigModule.getChannelName(loginId, i).trim();
             channel.setName(chanName.equals("") ? "閫氶亾" + i : chanName);
             channel.setDeviceId(camera.getId());
             channel.setChanNo(i);
diff --git a/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/LoginResultCallBack.java b/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/LoginResultCallBack.java
index 8e05ebe..b1ecad8 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/LoginResultCallBack.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/LoginResultCallBack.java
@@ -48,10 +48,10 @@
         if (dwResult == 1) {
             GlobalVariable.loginMap.put(camera.getId(), lUserID);
             log.debug(camera.getIp() + ":" + camera.getPort() + "鐧诲綍鎴愬姛");
-            camera.setLoginId(lUserID);
+            camera.setLoginId((long)lUserID);
             camera.setState("1");
             camera.setChanNum((int) lpDeviceinfo.byChanNum);
-            camera.setStartDChan((int) lpDeviceinfo.byStartDChan);
+            camera.setStartChan((int) lpDeviceinfo.byStartDChan);
             //鑾峰彇鏈�鏂伴�氶亾
             List<ArdChannel> cameraChannelList = hikClientService.getChannels(camera);
             if (cameraChannelList.size() > 0) {
@@ -89,7 +89,7 @@
         } else {
             log.debug(camera.getIp() + ":" + camera.getPort() + "鐧诲綍澶辫触");
             camera.setChanNum(0);
-            camera.setLoginId(-1);
+            camera.setLoginId(-1l);
             camera.setState("0");
         }
         ardCamerasService.updateArdCameras(camera);
diff --git a/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java b/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java
index 0269e99..36d5d94 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java
@@ -94,7 +94,7 @@
                 String WIN_PATH = System.getProperty("user.dir") + File.separator + "ardLog" + File.separator + "logs" + File.separator;
                 hCNetSDK.NET_DVR_SetLogToFile(3, WIN_PATH, true);
             } else {
-                hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog" , true);
+                hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog", true);
             }
             String m_sDeviceIP = camera.getIp();
             String m_sUsername = camera.getUsername();
@@ -125,7 +125,7 @@
             if (lUserID < 0) {
                 int errorCode = hCNetSDK.NET_DVR_GetLastError();
                 camera.setChanNum(0);
-                camera.setLoginId(-1);
+                camera.setLoginId(-1l);
                 camera.setState("0");
                 //鍒犻櫎绠$悊閫氶亾
                 ardChannelService.deleteArdChannelByDeviceId(camera.getId());
@@ -146,7 +146,7 @@
             }
             GlobalVariable.loginMap.put(camera.getId(), lUserID);
             GlobalVariable.loginCameraMap.put(lUserID, camera);
-            camera.setLoginId(lUserID);
+            camera.setLoginId((long)lUserID);
             camera.setState("1");
             int chanNum = m_strDeviceInfo.struDeviceV30.byChanNum;
             int startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan + 1;
@@ -155,7 +155,7 @@
                 startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan;
             }
             camera.setChanNum(chanNum);
-            camera.setStartDChan(startDchan);
+            camera.setStartChan(startDchan);
             //鑾峰彇鏈�鏂伴�氶亾
             List<ArdChannel> cameraChannelList = getChannels(camera);
             if (cameraChannelList.size() > 0) {
@@ -164,16 +164,16 @@
                     ardChannelService.insertArdChannel(channel);
                 });
                 camera.setChannelList(cameraChannelList);
-                camera.setChanNum(cameraChannelList.size());
-                ardCamerasService.updateArdCameras(camera);
-                //娣诲姞鍒版祦濯掍綋
-                addVtdu(camera);
+                //camera.setChanNum(cameraChannelList.size());
+                //閫氶亾鎵归噺娣诲姞鍒版祦濯掍綋
+                batchAddVtdu(camera);
             }
+            ardCamerasService.updateArdCameras(camera);
             //鍒涘缓寮曞闃熷垪
             createGuideQueue(camera);
             return AjaxResult.success("璁惧鐧诲綍鎴愬姛");
         } catch (Exception ex) {
-            log.error("璁惧鐧诲綍寮傚父" , ex);
+            log.error("璁惧鐧诲綍寮傚父", ex);
             return AjaxResult.error("璁惧鐧诲綍寮傚父" + ex.getMessage());
         }
     }
@@ -200,7 +200,7 @@
                 String WIN_PATH = System.getProperty("user.dir") + File.separator + "ardLog" + File.separator + "logs" + File.separator;
                 hCNetSDK.NET_DVR_SetLogToFile(3, WIN_PATH, true);
             } else {
-                hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog" , true);
+                hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog", true);
             }
             String m_sDeviceIP = camera.getIp();
             String m_sUsername = camera.getUsername();
@@ -231,7 +231,7 @@
             if (lUserID < 0) {
                 int errorCode = hCNetSDK.NET_DVR_GetLastError();
                 camera.setChanNum(0);
-                camera.setLoginId(-1);
+                camera.setLoginId(-1l);
                 camera.setState("0");
                 //鍒犻櫎绠$悊閫氶亾
                 ardChannelService.deleteArdChannelByDeviceId(camera.getId());
@@ -254,7 +254,7 @@
 
             GlobalVariable.loginMap.put(camera.getId(), lUserID);
             GlobalVariable.loginCameraMap.put(lUserID, camera);
-            camera.setLoginId(lUserID);
+            camera.setLoginId((long)lUserID);
             camera.setState("1");
             int chanNum = m_strDeviceInfo.struDeviceV30.byChanNum;
             int startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan + 1;
@@ -263,7 +263,7 @@
                 startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan;
             }
             camera.setChanNum(chanNum);
-            camera.setStartDChan(startDchan);
+            camera.setStartChan(startDchan);
             //鑾峰彇鏈�鏂伴�氶亾
             List<ArdChannel> cameraChannelList = getChannels(camera);
             if (cameraChannelList.size() > 0) {
@@ -272,16 +272,16 @@
                     ardChannelService.insertArdChannel(channel);
                 });
                 camera.setChannelList(cameraChannelList);
-                camera.setChanNum(cameraChannelList.size());
-                ardCamerasService.updateArdCameras(camera);
-                //娣诲姞鍒版祦濯掍綋
-                addVtdu(camera);
+                //camera.setChanNum(cameraChannelList.size());
+                //閫氶亾鎵归噺娣诲姞鍒版祦濯掍綋
+                batchAddVtdu(camera);
             }
+            ardCamerasService.updateArdCameras(camera);
             //鍒涘缓寮曞闃熷垪
             createGuideQueue(camera);
             return AjaxResult.success("璁惧鐧诲綍鎴愬姛");
         } catch (Exception ex) {
-            log.error("娉ㄥ唽璁惧寮傚父" , ex);
+            log.error("娉ㄥ唽璁惧寮傚父", ex);
             return AjaxResult.error("娉ㄥ唽璁惧寮傚父" + ex.getMessage());
         }
     }
@@ -299,36 +299,19 @@
         }
     }
 
-    //娣诲姞鍒版祦濯掍綋
-    private void addVtdu(ArdCameras camera) {
+    //閫氶亾鎵归噺娣诲姞鍒版祦濯掍綋
+    public void batchAddVtdu(ArdCameras camera) {
         try {
-            camera.getChannelList().stream().forEach(channel->{
-                String name = camera.getId() + "_" + channel.getChanNo();
-                String rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";
-                //鍒犻櫎娴佸獟浣�
-                if (vtduService.selectVtduByName(name) != null) {
-                    vtduService.deleteVtduByName(name);
-                }
-                //娣诲姞鍒版祦濯掍綋
-                Vtdu vtdu = new Vtdu();
-                vtdu.setRtspSource(rtspSource);
-                vtdu.setName(name);
-                CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo());
-                Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd);
-                if (videoCompressionCfg.get("videoEncType").equals("鏍囧噯h264")) {
-                    vtdu.setIsCode("0");//榛樿涓嶈浆鐮�
-                } else {
-                    vtdu.setIsCode("1");//榛樿杞爜
-                }
-                vtdu.setMode("1");//榛樿CPU杞В鐮�
-                vtdu.setCameraId(camera.getId());
-                vtduService.insertVtdu(vtdu);
+            camera.getChannelList().stream().forEach(channel -> {
+                vtduService.addChanToVtdu(camera, channel);
             });
         } catch (Exception ex) {
             log.error("閫氶亾娣诲姞鍒版祦濯掍綋寮傚父锛�" + ex.getMessage());
         }
 
     }
+
+
 
     /**
      * @鎻忚堪 娉ㄩ攢鐧诲綍
@@ -875,10 +858,10 @@
                         nFrameRate = "鏈煡";
                         break;
                 }
-                map.put("resolution" , resolution);//鍒嗚鲸鐜�
-                map.put("videoBitrate" , videoBitrate);//姣旂壒鐜�
-                map.put("videoEncType" , videoEncType);//缂栫爜
-                map.put("nFrameRate" , nFrameRate);//甯х巼
+                map.put("resolution", resolution);//鍒嗚鲸鐜�
+                map.put("videoBitrate", videoBitrate);//姣旂壒鐜�
+                map.put("videoEncType", videoEncType);//缂栫爜
+                map.put("nFrameRate", nFrameRate);//甯х巼
 
             } else {
                 int code = hCNetSDK.NET_DVR_GetLastError();
@@ -931,9 +914,9 @@
         double z = d.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
         //log.debug("T鍨傜洿鍙傛暟涓�: " + p + "P姘村钩鍙傛暟涓�: " + t + "Z鍙樺�嶅弬鏁颁负: " + z);
         Map<String, Object> ptzMap = new HashMap<>();
-        ptzMap.put("p" , p);
-        ptzMap.put("t" , t);
-        ptzMap.put("z" , z);
+        ptzMap.put("p", p);
+        ptzMap.put("t", t);
+        ptzMap.put("z", z);
         return AjaxResult.success(ptzMap);
     }
 
@@ -980,9 +963,9 @@
             float fTilt = lpPTZAbsoluteEX_cfg.struPTZCtrl.fTilt;
             float t = fTilt < 0 ? fTilt + 360 : fTilt;
             float z = lpPTZAbsoluteEX_cfg.struPTZCtrl.fZoom;
-            ptzMap.put("p" , p);
-            ptzMap.put("t" , t);
-            ptzMap.put("z" , z);
+            ptzMap.put("p", p);
+            ptzMap.put("t", t);
+            ptzMap.put("z", z);
             return AjaxResult.success(ptzMap);
         } catch (Exception ex) {
             log.error("鑾峰彇楂樼簿搴TZ缁濆浣嶇疆寮傚父:" + ex.getMessage());
@@ -1087,7 +1070,7 @@
             return AjaxResult.success("璁剧疆楂樼簿搴TZ鍙傛暟鎴愬姛");
 
         } catch (Exception ex) {
-            log.error("璁剧疆楂樼簿搴TZ鍙傛暟寮傚父" , ex);
+            log.error("璁剧疆楂樼簿搴TZ鍙傛暟寮傚父", ex);
             return AjaxResult.error("璁剧疆楂樼簿搴TZ鍙傛暟寮傚父:" + ex);
         }
     }
@@ -1125,7 +1108,7 @@
                 log.error("璁剧疆ptz澶辫触,璇风◢鍚庨噸璇�" + code);
                 return AjaxResult.warn("璁剧疆ptz澶辫触:" + SdkErrorCodeEnum.getDescByCode(code) + "(" + code + ")");
             }
-            return AjaxResult.success("寮曞鍧愭爣鎴愬姛",correctPitch);
+            return AjaxResult.success("寮曞鍧愭爣鎴愬姛", correctPitch);
         } catch (Exception ex) {
             log.error("寮曞鍧愭爣寮傚父:" + ex.getMessage());
             return AjaxResult.error("寮曞鍧愭爣寮傚父:" + ex.getMessage());
@@ -1234,12 +1217,12 @@
             String wZoomPosMax = df.format((float) Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wZoomPosMax)) / 10);
             String wZoomPosMin = df.format((float) Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wZoomPosMin)) / 10);
             Map<String, Object> ptzScopeMap = new HashMap<>();
-            ptzScopeMap.put("pMax" , wPanPosMax);
-            ptzScopeMap.put("pMin" , wPanPosMin);
-            ptzScopeMap.put("tMax" , wTiltPosMax);
-            ptzScopeMap.put("tMin" , wTiltPosMin);
-            ptzScopeMap.put("zMax" , wZoomPosMax);
-            ptzScopeMap.put("zMin" , wZoomPosMin);
+            ptzScopeMap.put("pMax", wPanPosMax);
+            ptzScopeMap.put("pMin", wPanPosMin);
+            ptzScopeMap.put("tMax", wTiltPosMax);
+            ptzScopeMap.put("tMin", wTiltPosMin);
+            ptzScopeMap.put("zMax", wZoomPosMax);
+            ptzScopeMap.put("zMin", wZoomPosMin);
             return AjaxResult.success(ptzScopeMap);
         }
     }
@@ -1335,7 +1318,7 @@
         boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_CCDPARAMCFG, chanNo, point, struDayNigh.size());
         if (!bool) {
             int code = hCNetSDK.NET_DVR_GetLastError();
-            log.error("鍒囨崲绾㈠澶辫触 ErrorCode:{},ErrorInfo:{}" , code, SdkErrorCodeEnum.getDescByCode(code));
+            log.error("鍒囨崲绾㈠澶辫触 ErrorCode:{},ErrorInfo:{}", code, SdkErrorCodeEnum.getDescByCode(code));
             return AjaxResult.warn("鍒囨崲绾㈠澶辫触:" + SdkErrorCodeEnum.getDescByCode(code) + "(" + code + ")");
         }
         log.debug("鍒囨崲绾㈠鎴愬姛");
@@ -1848,6 +1831,9 @@
 
     //鑾峰彇IP閫氶亾
     public List<ArdChannel> getChannels(ArdCameras camera) {
+        if (camera.getLoginId().equals(-1)) {
+            return new ArrayList<>();
+        }
         //鑾峰彇閫氶亾
         List<ArdChannel> channelList = new ArrayList<>();
         try {
@@ -1856,19 +1842,19 @@
             m_strIpparaCfg.write();
             //lpIpParaConfig 鎺ユ敹鏁版嵁鐨勭紦鍐叉寚閽�
             Pointer lpIpParaConfig = m_strIpparaCfg.getPointer();
-            boolean bRet = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId(), HCNetSDK.NET_DVR_GET_IPPARACFG_V40, 0, lpIpParaConfig, m_strIpparaCfg.size(), ibrBytesReturned);
+            boolean bRet = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId().intValue(), HCNetSDK.NET_DVR_GET_IPPARACFG_V40, 0, lpIpParaConfig, m_strIpparaCfg.size(), ibrBytesReturned);
             m_strIpparaCfg.read();
             //log.debug("璧峰鏁板瓧閫氶亾鍙凤細" + m_strIpparaCfg.dwStartDChan);//m_strIpparaCfg.dwDChanNum
             for (int iChannum = 0; iChannum < camera.getChanNum(); iChannum++) {
                 ArdChannel channel = new ArdChannel();
-                int chanNo = iChannum + camera.getStartDChan();
+                int chanNo = iChannum + camera.getStartChan();
                 HCNetSDK.NET_DVR_PICCFG_V40 strPicCfg = new HCNetSDK.NET_DVR_PICCFG_V40();
                 strPicCfg.dwSize = strPicCfg.size();
                 strPicCfg.write();
                 Pointer pStrPicCfg = strPicCfg.getPointer();
                 NativeLong lChannel = new NativeLong(chanNo);
                 IntByReference pInt = new IntByReference(0);
-                boolean b_GetPicCfg = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId(), HCNetSDK.NET_DVR_GET_PICCFG_V40, lChannel.intValue(), pStrPicCfg, strPicCfg.size(), pInt);
+                boolean b_GetPicCfg = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId().intValue(), HCNetSDK.NET_DVR_GET_PICCFG_V40, lChannel.intValue(), pStrPicCfg, strPicCfg.size(), pInt);
                 if (!b_GetPicCfg) {
                     // log.error("鑾峰彇鍥惧儚鍙傛暟澶辫触锛岄敊璇爜锛�" + hCNetSDK.NET_DVR_GetLastError());
                 }
@@ -1932,11 +1918,11 @@
         }
         struGisInfo.read();
         Map<String, Object> map = new HashMap<>();
-        map.put("p" , struGisInfo.struPtzPos.fPanPos);
-        map.put("t" , struGisInfo.struPtzPos.fTiltPos < 0 ? struGisInfo.struPtzPos.fTiltPos + 360 : struGisInfo.struPtzPos.fTiltPos);
-        map.put("z" , struGisInfo.struPtzPos.fZoomPos);
-        map.put("fHorFieldAngle" , struGisInfo.fHorizontalValue);// 姘村钩瑙嗗満瑙�
-        map.put("fVerFieldAngle" , struGisInfo.fVerticalValue);// 鍨傜洿瑙嗗満瑙�
+        map.put("p", struGisInfo.struPtzPos.fPanPos);
+        map.put("t", struGisInfo.struPtzPos.fTiltPos < 0 ? struGisInfo.struPtzPos.fTiltPos + 360 : struGisInfo.struPtzPos.fTiltPos);
+        map.put("z", struGisInfo.struPtzPos.fZoomPos);
+        map.put("fHorFieldAngle", struGisInfo.fHorizontalValue);// 姘村钩瑙嗗満瑙�
+        map.put("fVerFieldAngle", struGisInfo.fVerticalValue);// 鍨傜洿瑙嗗満瑙�
         return AjaxResult.success(map);
 
     }
@@ -1996,7 +1982,7 @@
                 return AjaxResult.warn("鏈湴褰曞儚鍙栨祦澶辫触:" + SdkErrorCodeEnum.getDescByCode(code) + "(" + code + ")");
             }
             log.debug("鏈湴褰曞儚寮�濮�");
-            return AjaxResult.success("褰曞儚寮�濮�" , lRealHandle);
+            return AjaxResult.success("褰曞儚寮�濮�", lRealHandle);
         } catch (Exception ex) {
             log.error("鏈湴褰曞儚寮�濮嬪紓甯�" + ex.getMessage());
             return AjaxResult.error("鏈湴褰曞儚寮�濮嬪紓甯�" + ex.getMessage());
diff --git a/ard-work/src/main/resources/mapper/device/ArdCamerasMapper.xml b/ard-work/src/main/resources/mapper/device/ArdCamerasMapper.xml
index 78083d2..318e511 100644
--- a/ard-work/src/main/resources/mapper/device/ArdCamerasMapper.xml
+++ b/ard-work/src/main/resources/mapper/device/ArdCamerasMapper.xml
@@ -15,6 +15,7 @@
         <result property="gdtype" column="gdtype"/>
         <result property="factory" column="factory"/>
         <result property="chanNum" column="channel"/>
+        <result property="startChan" column="start_chan"/>
         <result property="longitude" column="longitude"/>
         <result property="latitude" column="latitude"/>
         <result property="altitude" column="altitude"/>
@@ -48,6 +49,7 @@
                c.gdtype,
                c.factory,
                c.channel,
+               c.start_chan,
                c.longitude,
                c.latitude,
                c.altitude,
@@ -86,6 +88,7 @@
             <if test="gdtype != null  and gdtype != ''">and c.gdtype = #{gdtype}</if>
             <if test="factory != null  and factory != ''">and c.factory = #{factory}</if>
             <if test="chanNum != null ">and c.channel = #{chanNum}</if>
+            <if test="startChan != null ">and c.start_chan = #{startChan}</if>
             <if test="longitude != null ">and c.longitude = #{longitude}</if>
             <if test="latitude != null ">and c.latitude = #{latitude}</if>
             <if test="altitude != null ">and c.altitude = #{altitude}</if>
@@ -123,6 +126,7 @@
             <if test="gdtype != null  and gdtype != ''">and c.gdtype = #{gdtype}</if>
             <if test="factory != null  and factory != ''">and c.factory = #{factory}</if>
             <if test="chanNum != null ">and c.channel = #{chanNum}</if>
+            <if test="startChan != null ">and c.start_chan= #{startChan}</if>
             <if test="longitude != null ">and c.longitude = #{longitude}</if>
             <if test="latitude != null ">and c.latitude = #{latitude}</if>
             <if test="altitude != null ">and c.altitude = #{altitude}</if>
@@ -163,6 +167,7 @@
             <if test="factory != null">factory,</if>
             <if test="towerId != null">tower_id,</if>
             <if test="chanNum != null">channel,</if>
+            <if test="startChan != null">start_chan,</if>
             <if test="longitude != null">longitude,</if>
             <if test="latitude != null">latitude,</if>
             <if test="altitude != null">altitude,</if>
@@ -197,6 +202,7 @@
             <if test="factory != null">#{factory},</if>
             <if test="towerId != null">#{towerId},</if>
             <if test="chanNum != null">#{chanNum},</if>
+            <if test="startChan != null">#{startChan},</if>
             <if test="longitude != null">#{longitude},</if>
             <if test="latitude != null">#{latitude},</if>
             <if test="altitude != null">#{altitude},</if>
@@ -234,6 +240,7 @@
             <if test="factory != null">factory = #{factory},</if>
             <if test="towerId != null">tower_id = #{towerId},</if>
             <if test="chanNum != null">channel = #{chanNum},</if>
+            <if test="startChan != null">start_chan = #{startChan},</if>
             <if test="longitude != null">longitude = #{longitude},</if>
             <if test="latitude != null">latitude = #{latitude},</if>
             <if test="altitude != null">altitude = #{altitude},</if>
diff --git a/ard-work/src/main/resources/templates/test.html b/ard-work/src/main/resources/templates/test.html
index 5623b4b..e805ee4 100644
--- a/ard-work/src/main/resources/templates/test.html
+++ b/ard-work/src/main/resources/templates/test.html
@@ -916,10 +916,32 @@
 
     let webrtcClient;
     //whep鎿嶄綔鏂规硶
-    const restartPause = 2000;
+    const retryPause = 2000;
+
+    const video = document.getElementById('video');
+    const message = document.getElementById('message');
+
+    let nonAdvertisedCodecs = [];
+    let pc = null;
+    let restartTimeout = null;
+    let sessionUrl = '';
+    let offerData = '';
+    let queuedCandidates = [];
+    let defaultControls = false;
+
+    const setMessage = (str) => {
+        if (str !== '') {
+            video.controls = false;
+        } else {
+            video.controls = defaultControls;
+        }
+        message.innerText = str;
+    };
+
     const unquoteCredential = (v) => (
         JSON.parse(`"${v}"`)
     );
+
     const linkToIceServers = (links) => (
         (links !== null) ? links.split(', ').map((link) => {
             const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
@@ -930,20 +952,21 @@
             if (m[3] !== undefined) {
                 ret.username = unquoteCredential(m[3]);
                 ret.credential = unquoteCredential(m[4]);
-                ret.credentialType = "password";
+                ret.credentialType = 'password';
             }
 
             return ret;
         }) : []
     );
-    const parseOffer = (offer) => {
+
+    const parseOffer = (sdp) => {
         const ret = {
             iceUfrag: '',
             icePwd: '',
             medias: [],
         };
 
-        for (const line of offer.split('\r\n')) {
+        for (const line of sdp.split('\r\n')) {
             if (line.startsWith('m=')) {
                 ret.medias.push(line.slice('m='.length));
             } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) {
@@ -955,7 +978,131 @@
 
         return ret;
     };
-    const generateSdpFragment = (offerData, candidates) => {
+
+    const enableStereoPcmau = (section) => {
+        let lines = section.split('\r\n');
+
+        lines[0] += ' 118';
+        lines.splice(lines.length - 1, 0, 'a=rtpmap:118 PCMU/8000/2');
+        lines.splice(lines.length - 1, 0, 'a=rtcp-fb:118 transport-cc');
+
+        lines[0] += ' 119';
+        lines.splice(lines.length - 1, 0, 'a=rtpmap:119 PCMA/8000/2');
+        lines.splice(lines.length - 1, 0, 'a=rtcp-fb:119 transport-cc');
+
+        return lines.join('\r\n');
+    };
+
+    const enableMultichannelOpus = (section) => {
+        let lines = section.split('\r\n');
+
+        lines[0] += " 112";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:112 multiopus/48000/3");
+        lines.splice(lines.length - 1, 0, "a=fmtp:112 channel_mapping=0,2,1;num_streams=2;coupled_streams=1");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:112 transport-cc");
+
+        lines[0] += " 113";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:113 multiopus/48000/4");
+        lines.splice(lines.length - 1, 0, "a=fmtp:113 channel_mapping=0,1,2,3;num_streams=2;coupled_streams=2");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:113 transport-cc");
+
+        lines[0] += " 114";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:114 multiopus/48000/5");
+        lines.splice(lines.length - 1, 0, "a=fmtp:114 channel_mapping=0,4,1,2,3;num_streams=3;coupled_streams=2");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:114 transport-cc");
+
+        lines[0] += " 115";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:115 multiopus/48000/6");
+        lines.splice(lines.length - 1, 0, "a=fmtp:115 channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:115 transport-cc");
+
+        lines[0] += " 116";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:116 multiopus/48000/7");
+        lines.splice(lines.length - 1, 0, "a=fmtp:116 channel_mapping=0,4,1,2,3,5,6;num_streams=4;coupled_streams=4");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:116 transport-cc");
+
+        lines[0] += " 117";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:117 multiopus/48000/8");
+        lines.splice(lines.length - 1, 0, "a=fmtp:117 channel_mapping=0,6,1,4,5,2,3,7;num_streams=5;coupled_streams=4");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:117 transport-cc");
+
+        return lines.join('\r\n');
+    };
+
+    const enableL16 = (section) => {
+        let lines = section.split('\r\n');
+
+        lines[0] += " 120";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:120 L16/8000/2");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:120 transport-cc");
+
+        lines[0] += " 121";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:121 L16/16000/2");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:121 transport-cc");
+
+        lines[0] += " 122";
+        lines.splice(lines.length - 1, 0, "a=rtpmap:122 L16/48000/2");
+        lines.splice(lines.length - 1, 0, "a=rtcp-fb:122 transport-cc");
+
+        return lines.join('\r\n');
+    };
+
+    const enableStereoOpus = (section) => {
+        let opusPayloadFormat = '';
+        let lines = section.split('\r\n');
+
+        for (let i = 0; i < lines.length; i++) {
+            if (lines[i].startsWith('a=rtpmap:') && lines[i].toLowerCase().includes('opus/')) {
+                opusPayloadFormat = lines[i].slice('a=rtpmap:'.length).split(' ')[0];
+                break;
+            }
+        }
+
+        if (opusPayloadFormat === '') {
+            return section;
+        }
+
+        for (let i = 0; i < lines.length; i++) {
+            if (lines[i].startsWith('a=fmtp:' + opusPayloadFormat + ' ')) {
+                if (!lines[i].includes('stereo')) {
+                    lines[i] += ';stereo=1';
+                }
+                if (!lines[i].includes('sprop-stereo')) {
+                    lines[i] += ';sprop-stereo=1';
+                }
+            }
+        }
+
+        return lines.join('\r\n');
+    };
+
+    const editOffer = (sdp) => {
+        const sections = sdp.split('m=');
+
+        for (let i = 0; i < sections.length; i++) {
+            if (sections[i].startsWith('audio')) {
+                sections[i] = enableStereoOpus(sections[i]);
+
+                if (nonAdvertisedCodecs.includes('pcma/8000/2')) {
+                    sections[i] = enableStereoPcmau(sections[i]);
+                }
+
+                if (nonAdvertisedCodecs.includes('multiopus/48000/6')) {
+                    sections[i] = enableMultichannelOpus(sections[i]);
+                }
+
+                if (nonAdvertisedCodecs.includes('L16/48000/2')) {
+                    sections[i] = enableL16(sections[i]);
+                }
+
+                break;
+            }
+        }
+
+        return sections.join('m=');
+    };
+
+    const generateSdpFragment = (od, candidates) => {
         const candidatesByMedia = {};
         for (const candidate of candidates) {
             const mid = candidate.sdpMLineIndex;
@@ -965,12 +1112,12 @@
             candidatesByMedia[mid].push(candidate);
         }
 
-        let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n'
-            + 'a=ice-pwd:' + offerData.icePwd + '\r\n';
+        let frag = 'a=ice-ufrag:' + od.iceUfrag + '\r\n'
+            + 'a=ice-pwd:' + od.icePwd + '\r\n';
 
         let mid = 0;
 
-        for (const media of offerData.medias) {
+        for (const media of od.medias) {
             if (candidatesByMedia[mid] !== undefined) {
                 frag += 'm=' + media + '\r\n'
                     + 'a=mid:' + mid + '\r\n';
@@ -983,174 +1130,269 @@
         }
 
         return frag;
-    }
+    };
 
-    class WHEPClient {
-        constructor(whepUrl, videoId) {
-            this.video = videoId;
-            this.wurl = new URL('whep', whepUrl);
-            this.pc = null;
-            this.restartTimeout = null;
-            this.eTag = '';
-            this.queuedCandidates = [];
-            this.start();
-        }
+    const loadStream = () => {
+        requestICEServers();
+    };
 
-        start() {
-            console.log("requesting ICE servers");
-            fetch(this.wurl, {
-                method: 'OPTIONS',
-            })
-                .then((res) => this.onIceServers(res))
+    const supportsNonAdvertisedCodec = (codec, fmtp) => (
+        new Promise((resolve, reject) => {
+            const pc = new RTCPeerConnection({ iceServers: [] });
+            pc.addTransceiver('audio', { direction: 'recvonly' });
+            pc.createOffer()
+                .then((offer) => {
+                    if (offer.sdp.includes(' ' + codec)) { // codec is advertised, there's no need to add it manually
+                        resolve(false);
+                        return;
+                    }
+                    const sections = offer.sdp.split('m=audio');
+                    const lines = sections[1].split('\r\n');
+                    lines[0] += ' 118';
+                    lines.splice(lines.length - 1, 0, 'a=rtpmap:118 ' + codec);
+                    if (fmtp !== undefined) {
+                        lines.splice(lines.length - 1, 0, 'a=fmtp:118 ' + fmtp);
+                    }
+                    sections[1] = lines.join('\r\n');
+                    offer.sdp = sections.join('m=audio');
+                    return pc.setLocalDescription(offer);
+                })
+                .then(() => {
+                    return pc.setRemoteDescription(new RTCSessionDescription({
+                        type: 'answer',
+                        sdp: 'v=0\r\n'
+                            + 'o=- 6539324223450680508 0 IN IP4 0.0.0.0\r\n'
+                            + 's=-\r\n'
+                            + 't=0 0\r\n'
+                            + 'a=fingerprint:sha-256 0D:9F:78:15:42:B5:4B:E6:E2:94:3E:5B:37:78:E1:4B:54:59:A3:36:3A:E5:05:EB:27:EE:8F:D2:2D:41:29:25\r\n'
+                            + 'm=audio 9 UDP/TLS/RTP/SAVPF 118\r\n'
+                            + 'c=IN IP4 0.0.0.0\r\n'
+                            + 'a=ice-pwd:7c3bf4770007e7432ee4ea4d697db675\r\n'
+                            + 'a=ice-ufrag:29e036dc\r\n'
+                            + 'a=sendonly\r\n'
+                            + 'a=rtcp-mux\r\n'
+                            + 'a=rtpmap:118 ' + codec + '\r\n'
+                            + ((fmtp !== undefined) ? 'a=fmtp:118 ' + fmtp + '\r\n' : ''),
+                    }));
+                })
+                .then(() => {
+                    resolve(true);
+                })
                 .catch((err) => {
-                    console.log('error: ' + err);
-                    this.scheduleRestart();
+                    resolve(false);
+                })
+                .finally(() => {
+                    pc.close();
                 });
-        }
+        })
+    );
 
-        onIceServers(res) {
-            this.pc = new RTCPeerConnection({
-                iceServers: linkToIceServers(res.headers.get('Link')),
+    const getNonAdvertisedCodecs = () => {
+        Promise.all([
+            ['pcma/8000/2'],
+            ['multiopus/48000/6', 'channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2'],
+            ['L16/48000/2']
+        ].map((c) => supportsNonAdvertisedCodec(c[0], c[1]).then((r) => (r) ? c[0] : false)))
+            .then((c) => c.filter((e) => e !== false))
+            .then((codecs) => {
+                nonAdvertisedCodecs = codecs;
+                loadStream();
             });
+    };
 
-            const direction = "sendrecv";
-            this.pc.addTransceiver("video", {direction});
-            this.pc.addTransceiver("audio", {direction});
+    const onError = (err) => {
+        if (restartTimeout === null) {
+            setMessage(err + ', retrying in some seconds');
 
-            this.pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
-            this.pc.oniceconnectionstatechange = () => this.onConnectionState();
+            if (pc !== null) {
+                pc.close();
+                pc = null;
+            }
 
-            this.pc.ontrack = (evt) => {
-                console.log("new track:", evt.track.kind);
-                document.getElementById(this.video).srcObject = evt.streams[0];
-            };
+            restartTimeout = window.setTimeout(() => {
+                restartTimeout = null;
+                loadStream();
+            }, retryPause);
 
-            this.pc.createOffer()
-                .then((offer) => this.onLocalOffer(offer));
-        }
-
-        onLocalOffer(offer) {
-            this.offerData = parseOffer(offer.sdp);
-            this.pc.setLocalDescription(offer);
-
-            console.log("sending offer");
-            console.log(this.wurl);
-            fetch(this.wurl, {
-                method: 'POST',
-                headers: {
-                    'Content-Type': 'application/sdp',
-                },
-                body: offer.sdp,
-            })
-                .then((res) => {
-                    if (res.status !== 201) {
-                        throw new Error('bad status code');
-                    }
-                    // this.eTag = res.headers.get('ETag');
-                    this.eTag = res.headers.get("ETag") || res.headers.get('E-Tag');
-                    return res.text();
-                })
-                .then((sdp) => this.onRemoteAnswer(new RTCSessionDescription({
-                    type: 'answer',
-                    sdp,
-                })))
-                .catch((err) => {
-                    console.log('error: ' + err);
-                    this.scheduleRestart();
+            if (sessionUrl) {
+                fetch(sessionUrl, {
+                    method: 'DELETE',
                 });
+            }
+            sessionUrl = '';
+
+            queuedCandidates = [];
         }
+    };
 
-        onConnectionState() {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            console.log("peer connection state:", this.pc.iceConnectionState);
-
-            switch (this.pc.iceConnectionState) {
-                case "disconnected":
-                    this.scheduleRestart();
-            }
-        }
-
-        onRemoteAnswer(answer) {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            this.pc.setRemoteDescription(new RTCSessionDescription(answer));
-
-            if (this.queuedCandidates.length !== 0) {
-                this.sendLocalCandidates(this.queuedCandidates);
-                this.queuedCandidates = [];
-            }
-        }
-
-        onLocalCandidate(evt) {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            if (evt.candidate !== null) {
-                if (this.eTag === '') {
-                    this.queuedCandidates.push(evt.candidate);
-                } else {
-                    this.sendLocalCandidates([evt.candidate])
+    const sendLocalCandidates = (candidates) => {
+        fetch(sessionUrl + window.location.search, {
+            method: 'PATCH',
+            headers: {
+                'Content-Type': 'application/trickle-ice-sdpfrag',
+                'If-Match': '*',
+            },
+            body: generateSdpFragment(offerData, candidates),
+        })
+            .then((res) => {
+                switch (res.status) {
+                    case 204:
+                        break;
+                    case 404:
+                        throw new Error('stream not found');
+                    default:
+                        throw new Error(`bad status code ${res.status}`);
                 }
-            }
-        }
-
-        sendLocalCandidates(candidates) {
-            fetch(this.wurl, {
-                method: 'PATCH',
-                headers: {
-                    'Content-Type': 'application/trickle-ice-sdpfrag',
-                    'If-Match': this.eTag,
-                },
-
-                body: generateSdpFragment(this.offerData, candidates),
             })
-                .then((res) => {
-                    if (res.status !== 204) {
-                        throw new Error('bad status code');
-                    }
-                })
-                .catch((err) => {
-                    console.log('error: ' + err);
-                    this.scheduleRestart();
-                });
+            .catch((err) => {
+                onError(err.toString());
+            });
+    };
+
+    const onLocalCandidate = (evt) => {
+        if (restartTimeout !== null) {
+            return;
         }
 
-        scheduleRestart() {
-            if (this.restartTimeout !== null) {
-                return;
+        if (evt.candidate !== null) {
+            if (sessionUrl === '') {
+                queuedCandidates.push(evt.candidate);
+            } else {
+                sendLocalCandidates([evt.candidate])
             }
+        }
+    };
 
-            if (this.pc !== null) {
-                this.pc.close();
-                this.pc = null;
-            }
-
-            this.restartTimeout = window.setTimeout(() => {
-                this.restartTimeout = null;
-                this.start();
-            }, restartPause);
-
-            this.eTag = '';
-            this.queuedCandidates = [];
+    const onRemoteAnswer = (sdp) => {
+        if (restartTimeout !== null) {
+            return;
         }
 
-        stop() {
-            if (this.pc) {
-                try {
-                    this.pc.close();
-                } catch (e) {
-                    console.log("Failure close peer connection:" + e);
+        pc.setRemoteDescription(new RTCSessionDescription({
+            type: 'answer',
+            sdp,
+        }))
+            .then(() => {
+                if (queuedCandidates.length !== 0) {
+                    sendLocalCandidates(queuedCandidates);
+                    queuedCandidates = [];
                 }
-                this.pc = null;
-            }
+            })
+            .catch((err) => {
+                onError(err.toString());
+            });
+    };
+
+    const sendOffer = (offer) => {
+        fetch(new URL('whep', window.location.href) + window.location.search, {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/sdp',
+            },
+            body: offer.sdp,
+        })
+            .then((res) => {
+                switch (res.status) {
+                    case 201:
+                        break;
+                    case 404:
+                        throw new Error('stream not found');
+                    case 400:
+                        return res.json().then((e) => { throw new Error(e.error); });
+                    default:
+                        throw new Error(`bad status code ${res.status}`);
+                }
+
+                sessionUrl = new URL(res.headers.get('location'), window.location.href).toString();
+
+                return res.text()
+                    .then((sdp) => onRemoteAnswer(sdp));
+            })
+            .catch((err) => {
+                onError(err.toString());
+            });
+    };
+
+    const createOffer = () => {
+        pc.createOffer()
+            .then((offer) => {
+                offer.sdp = editOffer(offer.sdp);
+                offerData = parseOffer(offer.sdp);
+                pc.setLocalDescription(offer)
+                    .then(() => {
+                        sendOffer(offer);
+                    })
+                    .catch((err) => {
+                        onError(err.toString());
+                    });
+            })
+            .catch((err) => {
+                onError(err.toString());
+            });
+    };
+
+    const onConnectionState = () => {
+        if (restartTimeout !== null) {
+            return;
         }
-    }
+
+        if (pc.iceConnectionState === 'disconnected') {
+            onError('peer connection closed');
+        }
+    };
+
+    const onTrack = (evt) => {
+        setMessage('');
+        video.srcObject = evt.streams[0];
+    };
+
+    const requestICEServers = () => {
+        fetch(new URL('whep', window.location.href) + window.location.search, {
+            method: 'OPTIONS',
+        })
+            .then((res) => {
+                pc = new RTCPeerConnection({
+                    iceServers: linkToIceServers(res.headers.get('Link')),
+                    // https://webrtc.org/getting-started/unified-plan-transition-guide
+                    sdpSemantics: 'unified-plan',
+                });
+
+                const direction = 'sendrecv';
+                pc.addTransceiver('video', { direction });
+                pc.addTransceiver('audio', { direction });
+
+                pc.onicecandidate = (evt) => onLocalCandidate(evt);
+                pc.oniceconnectionstatechange = () => onConnectionState();
+                pc.ontrack = (evt) => onTrack(evt);
+
+                createOffer();
+            })
+            .catch((err) => {
+                onError(err.toString());
+            });
+    };
+
+    const parseBoolString = (str, defaultVal) => {
+        str = (str || '');
+
+        if (['1', 'yes', 'true'].includes(str.toLowerCase())) {
+            return true;
+        }
+        if (['0', 'no', 'false'].includes(str.toLowerCase())) {
+            return false;
+        }
+        return defaultVal;
+    };
+
+    const loadAttributesFromQuery = () => {
+        const params = new URLSearchParams(window.location.search);
+        video.controls = parseBoolString(params.get('controls'), true);
+        video.muted = parseBoolString(params.get('muted'), true);
+        video.autoplay = parseBoolString(params.get('autoplay'), true);
+        video.playsInline = parseBoolString(params.get('playsinline'), true);
+        defaultControls = video.controls;
+    };
+
 
     function realView(whepUrl, videoId) {
         console.log(whepUrl)
diff --git a/ard-work/src/main/resources/templates/test1.html b/ard-work/src/main/resources/templates/test1.html
new file mode 100644
index 0000000..5623b4b
--- /dev/null
+++ b/ard-work/src/main/resources/templates/test1.html
@@ -0,0 +1,1161 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>娴嬭瘯椤�</title>
+    <script th:src="@{/js/jquery-3.6.4.min.js}"></script>
+    <link rel="stylesheet" th:href="@{/css/bootstrap.css}"/>
+    <script th:src="@{/js/bootstrap.js}"></script>
+    <style>
+        .top-buffer {
+            margin-top: 10px;
+        }
+        .container {
+            border: 2px solid #1b6d85;
+            padding: 15px;
+        }
+    </style>
+<body>
+<div class="container">
+    <div class="row">
+        <div class="col-md-5">
+            <div class="row top-buffer">
+                璁惧锛�<select id="selectDev" style="width: 330px;">
+            </select>
+            </div>
+            <div class="row top-buffer">
+                閫氶亾锛�<select id="selectChn" style="width: 330px;">
+            </select>
+            </div>
+            <div class="row top-buffer">
+                <div class="col-md-1 col-md-offset-1">
+                    <button id="up" type="button" class="btn btn-primary">涓�</button>
+                </div>
+                <div class="col-md-6 col-md-offset-2">
+                    <div class="btn-group" role="group">
+                        <button id="controlZoomIn" type="button" class="btn btn-primary">璋冪劍-</button>
+                        <button id="controlZoomOut" type="button" class="btn btn-primary">璋冪劍+</button>
+                    </div>
+                </div>
+            </div>
+            <div class="row ">
+                <div class="col-md-1">
+                    <button id="left" type="button" class="btn btn-primary">宸�</button>
+                </div>
+                <div class="col-md-1 col-md-offset-1">
+                    <button id="right" type="button" class="btn btn-primary">鍙�</button>
+                </div>
+                <div class="col-md-6 col-md-offset-1">
+                    <div class="btn-group" role="group">
+                        <button id="controlFocusNear" type="button" class="btn btn-primary">鑱氱劍-</button>
+                        <button id="controlFocusFar" type="button" class="btn btn-primary">鑱氱劍+</button>
+                    </div>
+                </div>
+            </div>
+            <div class="row ">
+                <div class="col-md-1 col-md-offset-1">
+                    <button id="down" type="button" class="btn btn-primary">涓�</button>
+                </div>
+                <div class="col-md-6 col-md-offset-2">
+                    <div class="btn-group" role="group">
+                        <button id="controlIrisOpen" type="button" class="btn btn-primary">鍏夊湀-</button>
+                        <button id="controlIrisClose" type="button" class="btn btn-primary">鍏夊湀+</button>
+                    </div>
+                </div>
+            </div>
+            <div class="row ">
+                <div class="col-md-10">
+                    <div class="row top-buffer">
+                        <div class="input-group">
+                            <span class="input-group-addon">鐩殑鍧愭爣鍊硷細</span>
+                            <input id="targetPostion" class="form-control" placeholder="鐩殑鍧愭爣"/>
+                            <button id="setTargetPostion" type="button" class="btn btn-default">鎸囧悜鍧愭爣</button>
+                        </div>
+                        <div class="input-group">
+                            <span class="input-group-addon">P鍊硷細</span>
+                            <input id="p" class="form-control" placeholder="璇疯緭鍏鍊�"/>
+                        </div>
+                        <div class="input-group">
+                            <span class="input-group-addon">T鍊硷細</span>
+                            <input id="t" class="form-control" placeholder="璇疯緭鍏鍊�"/>
+                        </div>
+                        <div class="input-group">
+                            <span class="input-group-addon">Z鍊硷細</span>
+                            <input id="z" class="form-control" placeholder="璇疯緭鍏鍊�"/>
+                        </div>
+                    </div>
+                    <div class="row top-buffer">
+                        <div class="btn-group" role="group">
+                            <button id="getPTZ" type="button" class="btn btn-default">鑾峰彇ptz</button>
+                            <button id="setPTZ" type="button" class="btn btn-default">璁剧疆ptz</button>
+                            <button id="setPreset" type="button" class="btn btn-default">璁鹃缃偣</button>
+                            <button id="gotoPreset" type="button" class="btn btn-default">璋冮缃偣</button>
+                            <button id="setZeroPTZ" type="button" class="btn btn-default">璁剧疆闆舵柟浣嶈</button>
+                        </div>
+                    </div>
+                    <div class="row top-buffer">
+                        <div class="btn-group" role="group">
+                            <button id="FocusMode" type="button" class="btn btn-default">鎵嬪姩鑱氱劍</button>
+                            <div id="focusDiv" class="input-group">
+                                <span class="input-group-addon">鑱氱劍鍊硷細</span>
+                                <input id="focus" class="form-control" placeholder="鑱氱劍鍊�"/>
+                            </div>
+                            <button id="getFocusPos" type="button" class="btn btn-default">鑾峰彇鑱氱劍鍊�</button>
+                            <button id="setFocusPos" type="button" class="btn btn-default">璁剧疆鑱氱劍鍊�</button>
+                        </div>
+                    </div>
+                    <div class="row top-buffer">
+                        <div class="btn-group" role="group">
+                            <button id="WiperPwron" type="button" class="btn btn-default">寮�鍚洦鍒�</button>
+                            <button id="Defogcfg" type="button" class="btn btn-default">寮�鍚�忛浘</button>
+                            <button id="Infrarecfg" type="button" class="btn btn-default">寮�鍚孩澶�</button>
+                            <button id="HeateRpwron" type="button" class="btn btn-default">寮�鍚簯鍙板姞鐑�</button>
+                            <button id="CameraDeicing" type="button" class="btn btn-default">寮�鍚暅澶村姞鐑�</button>
+                        </div>
+                    </div>
+                    <div class="row top-buffer">
+                        <div class="btn-group" role="group">
+                            <button id="voice" type="button" class="btn btn-default">寮�濮嬭闊冲璁�</button>
+                            <button id="record" type="button" class="btn btn-default">寮�濮嬪綍鍍�</button>
+                            <button id="saveCutPic" type="button" class="btn btn-default">瀛樺偍鎶撳浘</button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="col-md-6">
+            <div class="row">
+                <div class="row top-buffer">
+                    <button id="preview" type="button" class="btn btn-primary">棰勮</button>
+                    <button id="previewStop" type="button" class="btn btn-primary">鍋滄</button>
+                </div>
+                <div class="row top-buffer">
+                    <video id="video" muted autoplay loop controls
+                           style="width: 100%; height: 360px; object-fit: fill; border: 2px solid #3498db;"/>
+                </div>
+                <div class="row">
+                    <img class="thumbnail" id="imgContainer"
+                         style="width: 100%; height: 360px; border: 2px solid #3498db;"/>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script th:inline="javascript">
+    var cameraId, chanNo,opt, optOpen, optClose, token;
+    window.onload = function () {
+        console.log(RTCRtpReceiver.getCapabilities('video').codecs)
+        opt = {"username": "admin", "password": "admin123"};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json'
+            },
+            url: "../login",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                token = data.token;
+                getDeviceList();// 鑾峰彇璁惧鍒楄〃
+            }
+        })
+        // 鍒濆鍖栧唴瀹�
+        console.log(cameraMap);
+    }
+
+    //鑾峰彇璁惧
+    function getDeviceList() {
+        $.ajax({
+            url: "../cameraSdk/list",
+            type: "get",
+            success: function (data) {
+                console.log(data);
+                var arr = data.data;
+                for (var i = 0; i < arr.length; i++) {
+                    console.log(arr[i].id);
+                    console.log(arr[i].name)
+                    var camera = {
+                        name: arr[i].name,
+                        factory: arr[i].factory,
+                        ipaddr: arr[i].ip,
+                        username: arr[i].username,
+                        password: arr[i].password,
+                        port: arr[i].rtspPort,
+                        longitude: arr[i].longitude,
+                        latitude: arr[i].latitude,
+                        altitude: arr[i].altitude
+                    };
+                    cameraMap.set(arr[i].id, camera);
+                    //鍏堝垱寤哄ソselect閲岄潰鐨刼ption鍏冪礌
+                    var option = $("<option>");
+                    //缁檕ption鐨則ext璧嬪��,杩欏氨鏄綘鐐瑰紑涓嬫媺妗嗚兘澶熺湅鍒扮殑涓滆タ
+                    $(option).val(arr[i].id);
+                    $(option).text(arr[i].name);
+                    //鑾峰彇select 涓嬫媺妗嗗璞�,骞跺皢option娣诲姞杩泂elect
+                    $('#selectDev').append(option);
+                }
+                $("#selectDev").trigger("change");
+            }
+        })
+    }
+
+    //閫夋嫨璁惧
+    $("#selectDev").change(function () {
+        // 鍦ㄨ繖閲屽鐞嗛�夋嫨浜嬩欢
+        var cameraId = $(this).find("option:selected").val();
+        var name = $(this).find("option:selected").text();
+        getChannelList(cameraId);
+        console.log("閫夋嫨浜嗭細" + cameraId + "---" + name);
+    });
+
+    //鑾峰彇閫氶亾
+    function getChannelList(cameraId) {
+        console.log(cameraId)
+        var myEntity = {
+            deviceId: cameraId,
+            pageNum: 1,
+            pageSize: 64
+        }
+        var queryString = $.param(myEntity);
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../device/channel/list?" + queryString,
+            type: "get",
+            success: function (data) {
+                console.log(data);
+                var arr = data.rows;
+                $('#selectChn').empty();
+                for (var i = 0; i < arr.length; i++) {
+                    console.log(arr[i].chanNo);
+                    console.log(arr[i].name);
+                    //鍏堝垱寤哄ソselect閲岄潰鐨刼ption鍏冪礌
+                    var option = document.createElement("option");
+                    //缁檕ption鐨則ext璧嬪��,杩欏氨鏄綘鐐瑰紑涓嬫媺妗嗚兘澶熺湅鍒扮殑涓滆タ
+                    $(option).text(arr[i].name);
+                    $(option).val(arr[i].chanNo);
+                    //鑾峰彇select 涓嬫媺妗嗗璞�,骞跺皢option娣诲姞杩泂elect
+                    $('#selectChn').append(option);
+                }
+            }
+        })
+    }
+
+    //棰勮
+    $('#preview').click(() => {
+        var cameraId = $('#selectDev option:selected').val();
+        var chanNo = $('#selectChn option:selected').val();
+        console.log(cameraId + " " + chanNo)
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../vtdu/media/" + cameraId + "_" + chanNo,
+            type: "get",
+            dataType: "json",
+            success: function (data) {
+                realView(data.data.webrtcUrl + "/", "video");
+            }
+        })
+    });
+    //鍋滄
+    $('#previewStop').click(() => {
+        webrtcClient.stop();
+    });
+    //浜戝彴涓婁笅宸﹀彸
+    $("#up").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 2;
+        var enable = true;
+        commondMethod(url, code, enable);
+
+    })
+    $("#up").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 2;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    $("#down").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 8;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#down").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 8;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    $("#left").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 4;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#left").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 4;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    $("#right").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 6;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#right").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 6;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    //鍙樺��
+    $("#controlZoomIn").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 10;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#controlZoomIn").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 10;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    $("#controlZoomOut").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 11;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#controlZoomOut").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 11;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    //鍙樼劍
+    $("#controlFocusNear").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 12;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#controlFocusNear").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 12;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    $("#controlFocusFar").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 13;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#controlFocusFar").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 13;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    //鍏夊湀
+    $("#controlIrisOpen").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 14;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#controlIrisOpen").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 14;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+    $("#controlIrisClose").mousedown(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 15;
+        var enable = true;
+        commondMethod(url, code, enable);
+    })
+    $("#controlIrisClose").mouseup(function () {
+        var url = "../cameraSdk/PTZControlWithSpeed";
+        var code = 15;
+        var enable = false;
+        commondMethod(url, code, enable);
+    })
+
+    $("#setPreset").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "presetIndex": 1};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/setPreset",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    })
+    $("#gotoPreset").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "presetIndex": 1};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/gotoPreset",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    })
+    $("#getPTZ").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/getPTZ",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (datas) {
+                console.log(datas);
+                $("#p").val(datas.data.p);
+                $("#t").val(datas.data.t);
+                $("#z").val(datas.data.z);
+            }
+        })
+    })
+    $("#setPTZ").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        var p = $('#p').val();
+        var t = $('#t').val();
+        var z = $('#z').val();
+        //瀹氫箟涓�涓甫鏈塎ap瀛楁鐨勫疄浣撳璞�
+        var myEntity = {
+            chanNo: chanNo,
+            cameraId: cameraId,
+            ptzMap: {
+                p: p,
+                t: t,
+                z: z
+            }
+        };
+        console.log(opt)
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/setPTZ",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(myEntity),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    })
+    $("#setTargetPostion").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        var camera = cameraMap.get(cameraId);
+        var camP = camera.longitude + ',' + camera.latitude + ',' + camera.altitude;
+        var targetP = $('#targetPostion').val();
+        var arr = targetP.split(",");
+        arr = arr.map(item => parseFloat(item));
+        //瀹氫箟涓�涓甫鏈塎ap瀛楁鐨勫疄浣撳璞�
+        var myEntity = {
+            chanNo: chanNo,
+            cameraId: cameraId,
+            targetPosition: arr
+        };
+        console.log(myEntity)
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/setTargetPosition",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(myEntity),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    })
+    $("#setZeroPTZ").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/setZeroPTZ",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    })
+    $("#WiperPwron").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "enable": true, "code": 16};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/PTZControlWithSpeed",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    })
+    var defogflag = true;
+    $("#Defogcfg").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true};
+        optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false};
+        if (defogflag) {
+            $(this).text("鍏抽棴閫忛浘");
+            defogflag = false;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/defogcfg",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optOpen),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        } else {
+            $(this).text("寮�鍚�忛浘");
+            defogflag = true;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/defogcfg",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optClose),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        }
+
+    })
+    var infrareflag = true;
+    $("#Infrarecfg").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true};
+        optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false};
+        if (infrareflag) {
+            $(this).text("鍏抽棴绾㈠");
+            infrareflag = false;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/infrarecfg",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optOpen),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        } else {
+
+            $(this).text("寮�鍚孩澶�");
+            infrareflag = true;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/infrarecfg",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optClose),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        }
+    })
+    var focusModeflag = true;
+    $("#FocusMode").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true};
+        optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false};
+        if (focusModeflag) {
+            $(this).text("鑷姩鑱氱劍");
+            focusModeflag = false;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/focusMode",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optOpen),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        } else {
+            $(this).text("鎵嬪姩鑱氱劍");
+            focusModeflag = true;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/focusMode",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optClose),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        }
+    })
+    $("#getFocusPos").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/getFocusPos",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (datas) {
+                console.log(datas);
+                $("#focus").val(datas.data);
+            }
+        })
+    })
+    var heateRpwronflag = true;
+    $("#HeateRpwron").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true};
+        optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false};
+        if (heateRpwronflag) {
+            $(this).text("鍏抽棴浜戝彴鍔犵儹");
+            heateRpwronflag = false;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/heateRpwron",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optOpen),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        } else {
+            $(this).text("寮�鍚簯鍙板姞鐑�");
+            heateRpwronflag = true;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/heateRpwron",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optClose),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        }
+    })
+    var CameraDeicingflag = true;
+    $("#CameraDeicing").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true};
+        optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false};
+        if (CameraDeicingflag) {
+            $(this).text("鍏抽棴闀滃ご鍔犵儹");
+            CameraDeicingflag = false;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/cameraDeicing",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optOpen),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        } else {
+            $(this).text("寮�鍚暅澶村姞鐑�");
+            CameraDeicingflag = true;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/cameraDeicing",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optClose),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        }
+    })
+    $("#realCutPic").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/captureJPEGPicture",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data.data);
+                $("#imgContainer").attr("src", "data:image/png;base64," + data.data);
+            }
+        })
+    })
+    $("#saveCutPic").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../cameraSdk/picCutCate",
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data.data);
+                setTimeout(() => {
+                    $('#imgContainer').attr('src', data.data);
+                }, 1000)
+
+            }
+        })
+    })
+    var recordflag = true;
+    $("#record").click(function () {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true};
+        optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false};
+        if (recordflag) {
+            $(this).text("鍋滄褰曞儚");
+            recordflag = false;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/record",
+                type: "post",
+                dataType: "json",
+                data: JSON.stringify(optOpen),
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        } else {
+            $(this).text("寮�濮嬪綍鍍�");
+            recordflag = true;
+            $.ajax({
+                headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json',
+                    'Authorization': token
+                },
+                url: "../cameraSdk/record",
+                dataType: "json",
+                data: JSON.stringify(optClose),
+                type: "post",
+                success: function (data) {
+                    console.log(data);
+                }
+            })
+        }
+    })
+
+    var cameraMap = new Map();
+
+    /*浜戝彴鍏叡鏂规硶*/
+    function commondMethod(url, code, enable) {
+        cameraId = $('#selectDev option:selected').val();
+        chanNo = $('#selectChn option:selected').val();
+        opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 4, "enable": enable, "code": code};
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: url,
+            type: "post",
+            dataType: "json",
+            data: JSON.stringify(opt),
+            success: function (data) {
+                console.log(data);
+            }
+        })
+    }
+
+    $('video').click(function (e) {
+        var cameraId = $('#selectDev option:selected').val();
+        var chanNo = $('#selectChn option:selected').val();
+        console.log(cameraId + " " + chanNo)
+        $.ajax({
+            headers: {
+                'Accept': 'application/json',
+                'Content-Type': 'application/json',
+                'Authorization': token
+            },
+            url: "../vtdu/media/" + cameraId + "_" + chanNo,
+            type: "get",
+            dataType: "json",
+            success: function (data) {
+                realView(data.data.webrtcUrl + "/", e.target.id);
+            }
+        })
+    });
+
+    let webrtcClient;
+    //whep鎿嶄綔鏂规硶
+    const restartPause = 2000;
+    const unquoteCredential = (v) => (
+        JSON.parse(`"${v}"`)
+    );
+    const linkToIceServers = (links) => (
+        (links !== null) ? links.split(', ').map((link) => {
+            const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
+            const ret = {
+                urls: [m[1]],
+            };
+
+            if (m[3] !== undefined) {
+                ret.username = unquoteCredential(m[3]);
+                ret.credential = unquoteCredential(m[4]);
+                ret.credentialType = "password";
+            }
+
+            return ret;
+        }) : []
+    );
+    const parseOffer = (offer) => {
+        const ret = {
+            iceUfrag: '',
+            icePwd: '',
+            medias: [],
+        };
+
+        for (const line of offer.split('\r\n')) {
+            if (line.startsWith('m=')) {
+                ret.medias.push(line.slice('m='.length));
+            } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) {
+                ret.iceUfrag = line.slice('a=ice-ufrag:'.length);
+            } else if (ret.icePwd === '' && line.startsWith('a=ice-pwd:')) {
+                ret.icePwd = line.slice('a=ice-pwd:'.length);
+            }
+        }
+
+        return ret;
+    };
+    const generateSdpFragment = (offerData, candidates) => {
+        const candidatesByMedia = {};
+        for (const candidate of candidates) {
+            const mid = candidate.sdpMLineIndex;
+            if (candidatesByMedia[mid] === undefined) {
+                candidatesByMedia[mid] = [];
+            }
+            candidatesByMedia[mid].push(candidate);
+        }
+
+        let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n'
+            + 'a=ice-pwd:' + offerData.icePwd + '\r\n';
+
+        let mid = 0;
+
+        for (const media of offerData.medias) {
+            if (candidatesByMedia[mid] !== undefined) {
+                frag += 'm=' + media + '\r\n'
+                    + 'a=mid:' + mid + '\r\n';
+
+                for (const candidate of candidatesByMedia[mid]) {
+                    frag += 'a=' + candidate.candidate + '\r\n';
+                }
+            }
+            mid++;
+        }
+
+        return frag;
+    }
+
+    class WHEPClient {
+        constructor(whepUrl, videoId) {
+            this.video = videoId;
+            this.wurl = new URL('whep', whepUrl);
+            this.pc = null;
+            this.restartTimeout = null;
+            this.eTag = '';
+            this.queuedCandidates = [];
+            this.start();
+        }
+
+        start() {
+            console.log("requesting ICE servers");
+            fetch(this.wurl, {
+                method: 'OPTIONS',
+            })
+                .then((res) => this.onIceServers(res))
+                .catch((err) => {
+                    console.log('error: ' + err);
+                    this.scheduleRestart();
+                });
+        }
+
+        onIceServers(res) {
+            this.pc = new RTCPeerConnection({
+                iceServers: linkToIceServers(res.headers.get('Link')),
+            });
+
+            const direction = "sendrecv";
+            this.pc.addTransceiver("video", {direction});
+            this.pc.addTransceiver("audio", {direction});
+
+            this.pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
+            this.pc.oniceconnectionstatechange = () => this.onConnectionState();
+
+            this.pc.ontrack = (evt) => {
+                console.log("new track:", evt.track.kind);
+                document.getElementById(this.video).srcObject = evt.streams[0];
+            };
+
+            this.pc.createOffer()
+                .then((offer) => this.onLocalOffer(offer));
+        }
+
+        onLocalOffer(offer) {
+            this.offerData = parseOffer(offer.sdp);
+            this.pc.setLocalDescription(offer);
+
+            console.log("sending offer");
+            console.log(this.wurl);
+            fetch(this.wurl, {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/sdp',
+                },
+                body: offer.sdp,
+            })
+                .then((res) => {
+                    if (res.status !== 201) {
+                        throw new Error('bad status code');
+                    }
+                    // this.eTag = res.headers.get('ETag');
+                    this.eTag = res.headers.get("ETag") || res.headers.get('E-Tag');
+                    return res.text();
+                })
+                .then((sdp) => this.onRemoteAnswer(new RTCSessionDescription({
+                    type: 'answer',
+                    sdp,
+                })))
+                .catch((err) => {
+                    console.log('error: ' + err);
+                    this.scheduleRestart();
+                });
+        }
+
+        onConnectionState() {
+            if (this.restartTimeout !== null) {
+                return;
+            }
+
+            console.log("peer connection state:", this.pc.iceConnectionState);
+
+            switch (this.pc.iceConnectionState) {
+                case "disconnected":
+                    this.scheduleRestart();
+            }
+        }
+
+        onRemoteAnswer(answer) {
+            if (this.restartTimeout !== null) {
+                return;
+            }
+
+            this.pc.setRemoteDescription(new RTCSessionDescription(answer));
+
+            if (this.queuedCandidates.length !== 0) {
+                this.sendLocalCandidates(this.queuedCandidates);
+                this.queuedCandidates = [];
+            }
+        }
+
+        onLocalCandidate(evt) {
+            if (this.restartTimeout !== null) {
+                return;
+            }
+
+            if (evt.candidate !== null) {
+                if (this.eTag === '') {
+                    this.queuedCandidates.push(evt.candidate);
+                } else {
+                    this.sendLocalCandidates([evt.candidate])
+                }
+            }
+        }
+
+        sendLocalCandidates(candidates) {
+            fetch(this.wurl, {
+                method: 'PATCH',
+                headers: {
+                    'Content-Type': 'application/trickle-ice-sdpfrag',
+                    'If-Match': this.eTag,
+                },
+
+                body: generateSdpFragment(this.offerData, candidates),
+            })
+                .then((res) => {
+                    if (res.status !== 204) {
+                        throw new Error('bad status code');
+                    }
+                })
+                .catch((err) => {
+                    console.log('error: ' + err);
+                    this.scheduleRestart();
+                });
+        }
+
+        scheduleRestart() {
+            if (this.restartTimeout !== null) {
+                return;
+            }
+
+            if (this.pc !== null) {
+                this.pc.close();
+                this.pc = null;
+            }
+
+            this.restartTimeout = window.setTimeout(() => {
+                this.restartTimeout = null;
+                this.start();
+            }, restartPause);
+
+            this.eTag = '';
+            this.queuedCandidates = [];
+        }
+
+        stop() {
+            if (this.pc) {
+                try {
+                    this.pc.close();
+                } catch (e) {
+                    console.log("Failure close peer connection:" + e);
+                }
+                this.pc = null;
+            }
+        }
+    }
+
+    function realView(whepUrl, videoId) {
+        console.log(whepUrl)
+        webrtcClient = new WHEPClient(whepUrl, videoId);
+    }
+</script>
+</body>
+</html>
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SyncTask.java
index 4e7d4ff..0b089e4 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SyncTask.java
@@ -84,28 +84,24 @@
 
     /**
      * @Author 鍒樿嫃涔�
-     * @Description 鍚屾nvr閫氶亾鐘舵�佸疄鏃跺悓姝ユ祦濯掍綋
+     * @Description 鍚屾閫氶亾鐘舵�佸疄鏃跺悓姝ユ祦濯掍綋
      * @Date 2024/7/9 16:01
      */
-    public void nvrChannelState() {
+    public void syncChannelState() {
+        log.warn("瀹氭椂鍚屾閫氶亾浠诲姟寮�濮�");
         //鑾峰彇鎵�鏈塶vr璁惧
         List<ArdCameras> ardCamerasList = iArdCamerasService.selectArdCamerasListNoDataScope(new ArdCameras());
         if (ardCamerasList.size() > 0) {
             ardCamerasList.stream()
-                    .filter(ardCameras -> ardCameras.getGdtype().equals("2"))
                     .forEach(ardCameras -> {
-                        //閫氳繃SDK鑾峰彇NVR褰撳墠閫氶亾
+                        //閫氳繃SDK鑾峰彇NVR瀹為檯閫氶亾
                         CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(ardCameras.getFactory());
                         List<ArdChannel> ardChannelList = cameraSDK.getChannels(ardCameras);
                         //鍚屾閫氶亾琛�
                         ArdChannel ardChannel=new ArdChannel();
                         ardChannel.setDeviceId(ardCameras.getId());
                         List<ArdChannel> ardChannelListDb = ardChannelService.selectArdChannelList(ardChannel);
-                        ardChannelService.asyncChannel(ardChannelListDb,ardChannelList);
-                        //鍚屾娴佸獟浣撹〃
-
-                        //鍚屾娴佸獟浣揳pi
-
+                        ardChannelService.asyncChannel(ardCameras,ardChannelListDb,ardChannelList);
                     });
         }
     }
@@ -142,9 +138,10 @@
      * 鍒樿嫃涔�
      * 2023/10/13 14:13:53
      */
-    public void vtdu() {
+    public void syncVtdu() {
+        log.warn("瀹氭椂鍚屾娴佸獟浣撲换鍔″紑濮�");
         List<String> nameList = mediaService.getNameList();
         List<Vtdu> vtduList = vtduService.selectVtduList(new Vtdu());
         vtduService.asyncVtdu(vtduList,nameList);
     }
-}
+}
\ No newline at end of file
diff --git a/server/mediamtx/mediamtx.yml b/server/mediamtx/mediamtx.yml
index cbe09e8..81ca38d 100644
--- a/server/mediamtx/mediamtx.yml
+++ b/server/mediamtx/mediamtx.yml
@@ -225,17 +225,17 @@
 webrtcICEInterfaces: []
 # List of public IP addresses that are to be used as a host.
 # This is used typically for servers that are behind 1:1 D-NAT.
-webrtcICEHostNAT1To1IPs: []
+webrtcICEHostNAT1To1IPs: [192.168.1.227]
 # Address of a ICE UDP listener in format host:port.
 # If filled, ICE traffic will pass through a single UDP port,
 # allowing the deployment of the server inside a container or behind a NAT.
-webrtcICEUDPMuxAddress:
+webrtcICEUDPMuxAddress: :8189
 # Address of a ICE TCP listener in format host:port.
 # If filled, ICE traffic will pass through a single TCP port,
 # allowing the deployment of the server inside a container or behind a NAT.
 # Using this setting forces usage of the TCP protocol, which is not
 # optimal for WebRTC.
-webrtcICETCPMuxAddress: 192.168.1.227:19302
+webrtcICETCPMuxAddress:
 
 ###############################################
 # Global settings -> SRT

--
Gitblit v1.9.3