From 49bedcce2ac42fa34da012ee3fc8e9230886064a Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: 星期五, 12 七月 2024 15:28:24 +0800
Subject: [PATCH] 优化:流媒体
---
ard-work/src/main/java/com/ruoyi/media/domain/RtmpSessions.java | 21
ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/controller/HikSdkController.java | 1
ard-work/src/main/resources/templates/test.html | 550 ++++----------
ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java | 93 +-
ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSessions.java | 20
ard-work/src/main/java/com/ruoyi/media/domain/RtspSessions.java | 21
ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java | 7
ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java | 4
ard-work/src/main/java/com/ruoyi/device/camera/controller/CameraSdkController.java | 2
ard-work/src/main/resources/templates/preview.html | 3
ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java | 9
/dev/null | 1161 ---------------------------------
ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java | 17
ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java | 167 ++--
ard-work/src/main/java/com/ruoyi/media/domain/RtspSession.java | 7
server/mediamtx/mediamtx.yml | 2
16 files changed, 390 insertions(+), 1,695 deletions(-)
diff --git a/ard-work/src/main/java/com/ruoyi/device/camera/controller/CameraSdkController.java b/ard-work/src/main/java/com/ruoyi/device/camera/controller/CameraSdkController.java
index e1e3ec5..5ca32d2 100644
--- a/ard-work/src/main/java/com/ruoyi/device/camera/controller/CameraSdkController.java
+++ b/ard-work/src/main/java/com/ruoyi/device/camera/controller/CameraSdkController.java
@@ -23,13 +23,11 @@
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
-import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
/**
* @Description: 鐩告満閫氱敤SDK鎺ュ彛
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 6d89b58..c5d6bba 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
@@ -2,16 +2,14 @@
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.constant.HttpStatus;
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.domain.*;
import com.ruoyi.media.service.IMediaService;
import com.ruoyi.media.service.IVtduService;
import io.swagger.annotations.Api;
@@ -107,33 +105,21 @@
}
/**
- * 绉婚櫎鎷夋祦
+ * 绉婚櫎webrtc鎷夋祦
*/
- @ApiOperation("绉婚櫎鎷夋祦")
- @ApiOperationSupport(order =4 )
+ @ApiOperation("绉婚櫎webrtc鎷夋祦")
+ @ApiOperationSupport(order = 4)
@PreAuthorize("@ss.hasPermi('media:stream:remove')")
@DeleteMapping("/{sessionId}")
- public AjaxResult removePullStreamSession(@PathVariable String sessionId) {
- List<StreamInfo> pullStreamList = mediaService.getPullStreamList(1,1000);
- StreamInfo streamInfo = pullStreamList.stream()
- .filter(object -> object.getId().equals(sessionId))
- .collect(Collectors.toList()).get(0);
- switch (streamInfo.getSessionType()) {
- case "rtsp":
- return AjaxResult.success(mediaService.kickRtspSession(sessionId));
- case "webrtc":
- return AjaxResult.success(mediaService.kickWebrtcSession(sessionId));
- case "rtmp":
- return AjaxResult.success(mediaService.kickRtmpSession(sessionId));
- }
- return AjaxResult.error();
+ public AjaxResult removeWebrtcPullStreamSession(@PathVariable String sessionId) {
+ return AjaxResult.success(mediaService.kickWebrtcSession(sessionId));
}
/**
* 鑾峰彇閫氶亾璇︾粏淇℃伅
*/
@ApiOperation("鑾峰彇閫氶亾璇︾粏淇℃伅")
- @ApiOperationSupport(order =4 )
+ @ApiOperationSupport(order = 4)
@GetMapping(value = "/{name}")
public AjaxResult getInfo(@PathVariable("name") String name) {
return success(mediaService.getPathInfo(name));
@@ -145,30 +131,18 @@
@GetMapping("/path/list")
@ApiOperation("鑾峰彇褰撳墠閫氶亾鍒楄〃")
@ApiOperationSupport(order = 5)
- public TableDataInfo getPaths(Integer pageNum,Integer pageSize) {
+ public TableDataInfo getPaths(Integer pageNum, Integer pageSize) {
startPage();
- return getDataTable(mediaService.paths(pageNum,pageSize));
+ return getDataTable(mediaService.paths(pageNum, pageSize));
}
/**
* 鎸塈D鏌ヨ鎷夋祦璇︽儏
*/
- @GetMapping("/getRtspSessionById")
- @ApiOperation("鎸塈D鏌ヨ鎷夋祦璇︽儏")
- public AjaxResult getRtspSessionById(String sessionId) {
- List<StreamInfo> pullStreamList = mediaService.getPullStreamList(1,1000);
- StreamInfo streamInfo = pullStreamList.stream()
- .filter(object -> object.getId().equals(sessionId))
- .collect(Collectors.toList()).get(0);
- switch (streamInfo.getSessionType()) {
- case "rtsp":
- return AjaxResult.success(mediaService.getRtspSessionById(sessionId));
- case "rtmp":
- return AjaxResult.success(mediaService.getRtmpSessionById(sessionId));
- case "webrtc":
- return AjaxResult.success(mediaService.getWebrtcSessionById(sessionId));
- }
- return AjaxResult.error();
+ @GetMapping("/getWebrtcSessionById")
+ @ApiOperation("鎸塈D鏌ヨwebrtc鎷夋祦璇︽儏")
+ public AjaxResult getWebrtcSessionById(String sessionId) {
+ return AjaxResult.success(mediaService.getWebrtcSessionById(sessionId));
}
/**
@@ -178,21 +152,42 @@
@GetMapping("/pushList")
@ApiOperation("鑾峰彇鎺ㄦ祦鍒楄〃")
@ApiOperationSupport(order = 6)
- public TableDataInfo getPushStreamList(Integer pageNum,Integer pageSize) {
- startPage();
- return getDataTable(mediaService.getPushStreamList(pageNum,pageSize));
+ public TableDataInfo getPushStreamList(Integer pageNum, Integer pageSize) {
+ TableDataInfo tableDataInfo = mediaService.getPushStreamList(pageNum, pageSize);
+ return tableDataInfo;
}
/**
- * 鑾峰彇鎷夋祦鍒楄〃
+ * 鑾峰彇WebRtc鎷夋祦鍒楄〃
*/
@PreAuthorize("@ss.hasPermi('media:stream:list')")
- @GetMapping("/pullList")
- @ApiOperation("鑾峰彇鎷夋祦鍒楄〃")
+ @GetMapping("/getPullWebrtcStreamList")
+ @ApiOperation("鑾峰彇WebRtc鎷夋祦鍒楄〃")
@ApiOperationSupport(order = 7)
- public TableDataInfo getPullStreamList(Integer pageNum,Integer pageSize) {
- startPage();
- return getDataTable(mediaService.getPullStreamList(pageNum,pageSize));
+ public TableDataInfo getPullWebrtcStreamList(Integer pageNum, Integer pageSize) {
+ return mediaService.getPullWebrtcStreamList(pageNum, pageSize);
}
+ /**
+ * 鑾峰彇rtsp鎷夋祦鍒楄〃
+ */
+ @PreAuthorize("@ss.hasPermi('media:stream:list')")
+ @GetMapping("/getPullRtspStreamList")
+ @ApiOperation("鑾峰彇rtsp鎷夋祦鍒楄〃")
+ @ApiOperationSupport(order = 8)
+ public TableDataInfo getPullRtspStreamList(Integer pageNum, Integer pageSize) {
+ return mediaService.getPullRtspStreamList(pageNum, pageSize);
+
+ }
+
+ /**
+ * 鑾峰彇rtmp鎷夋祦鍒楄〃
+ */
+ @PreAuthorize("@ss.hasPermi('media:stream:list')")
+ @GetMapping("/getPullRtmpStreamList")
+ @ApiOperation("鑾峰彇rtmp鎷夋祦鍒楄〃")
+ @ApiOperationSupport(order = 9)
+ public TableDataInfo getPullRtmpStreamList(Integer pageNum, Integer pageSize) {
+ return mediaService.getPullRtmpStreamList(pageNum, pageSize);
+ }
}
diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java b/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java
index c9906d3..e32642e 100644
--- a/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java
+++ b/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java
@@ -20,4 +20,8 @@
private String state;
private long bytesReceived;
private long bytesSent;
+ private String upStream;
+ private String downStream;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date beginTime;
}
diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSessions.java b/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSessions.java
new file mode 100644
index 0000000..06cb51c
--- /dev/null
+++ b/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSessions.java
@@ -0,0 +1,21 @@
+package com.ruoyi.media.domain;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @ClassName:Sessions
+ * @Description:
+ * @Author:ard
+ * @Date:2024骞�07鏈�12鏃�13:34
+ * @Version:1.0
+ **/
+@Data
+public class RtmpSessions {
+ private int itemCount;
+
+ private int pageCount;
+
+ private List<RtmpSession> items;
+}
diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/RtspSession.java b/ard-work/src/main/java/com/ruoyi/media/domain/RtspSession.java
index 629baac..4697b86 100644
--- a/ard-work/src/main/java/com/ruoyi/media/domain/RtspSession.java
+++ b/ard-work/src/main/java/com/ruoyi/media/domain/RtspSession.java
@@ -14,11 +14,16 @@
*/
@Data
public class RtspSession {
- private String name;
private String id;
+ private String path;
private Date created;
private String remoteAddr;
private String state;
+ private String transport;
private long bytesReceived;
private long bytesSent;
+ private String upStream;
+ private String downStream;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date beginTime;
}
diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/RtspSessions.java b/ard-work/src/main/java/com/ruoyi/media/domain/RtspSessions.java
new file mode 100644
index 0000000..2cccc3c
--- /dev/null
+++ b/ard-work/src/main/java/com/ruoyi/media/domain/RtspSessions.java
@@ -0,0 +1,21 @@
+package com.ruoyi.media.domain;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @ClassName:Sessions
+ * @Description:
+ * @Author:ard
+ * @Date:2024骞�07鏈�12鏃�13:34
+ * @Version:1.0
+ **/
+@Data
+public class RtspSessions {
+ private int itemCount;
+
+ private int pageCount;
+
+ private List<RtspSession> items;
+}
diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java b/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java
index b66dbbe..5451888 100644
--- a/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java
+++ b/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java
@@ -13,11 +13,18 @@
**/
@Data
public class WebrtcSession {
- private String name;
private String id;
+ private String path;
private Date created;
private String remoteAddr;
+ private String peerConnectionEstablished;
+ private String localCandidate;
+ private String remoteCandidate;
private String state;
private long bytesReceived;
private long bytesSent;
+ private String upStream;
+ private String downStream;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date beginTime;
}
diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSessions.java b/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSessions.java
new file mode 100644
index 0000000..8873a72
--- /dev/null
+++ b/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSessions.java
@@ -0,0 +1,20 @@
+package com.ruoyi.media.domain;
+
+import lombok.Data;
+import java.util.List;
+
+/**
+ * @ClassName:Sessions
+ * @Description:
+ * @Author:ard
+ * @Date:2024骞�07鏈�12鏃�13:34
+ * @Version:1.0
+ **/
+@Data
+public class WebrtcSessions {
+ private int itemCount;
+
+ private int pageCount;
+
+ private List<WebrtcSession> items;
+}
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 9c0602e..087c11b 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
@@ -1,5 +1,6 @@
package com.ruoyi.media.service;
+import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.media.domain.*;
import java.util.List;
import java.util.Map;
@@ -45,9 +46,11 @@
RtmpSession getRtmpSessionById(String sessionId);
- List<StreamInfo> getPushStreamList(Integer pageNum,Integer pageSize);
+ TableDataInfo getPushStreamList(Integer pageNum,Integer pageSize);
- List<StreamInfo> getPullStreamList(Integer pageNum,Integer pageSize);
+ TableDataInfo getPullWebrtcStreamList(Integer pageNum, Integer pageSize);
+ TableDataInfo getPullRtspStreamList(Integer pageNum, Integer pageSize);
+ TableDataInfo getPullRtmpStreamList(Integer pageNum, Integer pageSize);
Boolean kickRtspSession(String sessionId);
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 0079687..3e3a269 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
@@ -3,6 +3,8 @@
import com.alibaba.fastjson2.JSONObject;
import com.dtflys.forest.exceptions.ForestNetworkException;
import com.dtflys.forest.exceptions.ForestRuntimeException;
+import com.ruoyi.common.constant.HttpStatus;
+import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.media.domain.*;
import com.ruoyi.media.service.IMediaService;
@@ -10,6 +12,7 @@
import com.ruoyi.utils.tools.ArdTool;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
@@ -243,9 +246,12 @@
* 2023/8/29 9:37:05
*/
@Override
- public List<StreamInfo> getPushStreamList(Integer pageNum, Integer pageSize) {
+ public TableDataInfo getPushStreamList(Integer pageNum, Integer pageSize) {
+ TableDataInfo tableDataInfo = new TableDataInfo();
List<StreamInfo> PushStreamInfoList = new ArrayList<>();
Paths paths = mediaClient.paths(pageNum - 1, pageSize);
+ int itemCount = paths.getItemCount();
+ tableDataInfo.setTotal(itemCount);
List<Items> items = paths.getItems();
for (Items item : items) {
StreamInfo info = new StreamInfo();
@@ -309,105 +315,82 @@
PushStreamInfoList.add(info);
}
- return PushStreamInfoList;
+ tableDataInfo.setRows(PushStreamInfoList);
+ tableDataInfo.setCode(HttpStatus.SUCCESS);
+ tableDataInfo.setMsg("鏌ヨ鎴愬姛");
+ return tableDataInfo;
}
/**
- * 鑾峰彇鎷夋祦鍒楄〃
+ * 鑾峰彇webrtc鎷夋祦鍒楄〃
* 鍒樿嫃涔�
* 2023/8/29 9:37:05
*/
@Override
- public List<StreamInfo> getPullStreamList(Integer pageNum, Integer pageSize) {
- List<StreamInfo> PullStreamInfoList = new ArrayList<>();
- Paths paths = mediaClient.paths(pageNum - 1, pageSize);
- 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":
- 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;
+ public TableDataInfo getPullWebrtcStreamList(Integer pageNum, Integer pageSize) {
+ TableDataInfo tableDataInfo = new TableDataInfo();
+ WebrtcSessions WebrtcSessions = mediaClient.webrtcsessions(pageNum-1, pageSize);
+ List<WebrtcSession> webrtcsessions = WebrtcSessions.getItems();
+ webrtcsessions.stream().forEach(
+ webrtcSession -> {
+ webrtcSession.setUpStream(ArdTool.formatFileSize(webrtcSession.getBytesReceived()));
+ webrtcSession.setDownStream(ArdTool.formatFileSize(webrtcSession.getBytesSent()));
+ webrtcSession.setBeginTime(webrtcSession.getCreated());
}
- }
- }
- Comparator<StreamInfo> comparator = Comparator.comparing(streamInfo -> streamInfo.getBeginTime()); // 浣跨敤Collections.sort鏂规硶杩涜鎺掑簭 Collections.sort(personList, comparator);
- Collections.sort(PullStreamInfoList, comparator.reversed());
- return PullStreamInfoList;
+ );
+ tableDataInfo.setTotal(WebrtcSessions.getItemCount());
+ tableDataInfo.setRows(webrtcsessions);
+ tableDataInfo.setCode(HttpStatus.SUCCESS);
+ tableDataInfo.setMsg("鏌ヨ鎴愬姛");
+ return tableDataInfo;
+ }
+
+ /**
+ * 鑾峰彇rtmp鎷夋祦鍒楄〃
+ * 鍒樿嫃涔�
+ * 2023/8/29 9:37:05
+ */
+ @Override
+ public TableDataInfo getPullRtmpStreamList(Integer pageNum, Integer pageSize) {
+ TableDataInfo tableDataInfo = new TableDataInfo();
+ RtmpSessions rtmpSessions = mediaClient.rtmpsessions(pageNum-1, pageSize);
+ List<RtmpSession> webrtcsessions = rtmpSessions.getItems();
+ webrtcsessions.stream().forEach(
+ webrtcSession -> {
+ webrtcSession.setUpStream(ArdTool.formatFileSize(webrtcSession.getBytesReceived()));
+ webrtcSession.setDownStream(ArdTool.formatFileSize(webrtcSession.getBytesSent()));
+ webrtcSession.setBeginTime(webrtcSession.getCreated());
+ }
+ );
+ tableDataInfo.setTotal(rtmpSessions.getItemCount());
+ tableDataInfo.setRows(webrtcsessions);
+ tableDataInfo.setCode(HttpStatus.SUCCESS);
+ tableDataInfo.setMsg("鏌ヨ鎴愬姛");
+ return tableDataInfo;
+ }
+
+ /**
+ * 鑾峰彇rtsp鎷夋祦鍒楄〃
+ * 鍒樿嫃涔�
+ * 2023/8/29 9:37:05
+ */
+ @Override
+ public TableDataInfo getPullRtspStreamList(Integer pageNum, Integer pageSize) {
+ TableDataInfo tableDataInfo = new TableDataInfo();
+ RtspSessions rtspSessions = mediaClient.rtspsessions(pageNum-1, pageSize);
+ List<RtspSession> webrtcsessions = rtspSessions.getItems();
+ webrtcsessions.stream().forEach(
+ webrtcSession -> {
+ webrtcSession.setUpStream(ArdTool.formatFileSize(webrtcSession.getBytesReceived()));
+ webrtcSession.setDownStream(ArdTool.formatFileSize(webrtcSession.getBytesSent()));
+ webrtcSession.setBeginTime(webrtcSession.getCreated());
+ }
+ );
+ tableDataInfo.setTotal(rtspSessions.getItemCount());
+ tableDataInfo.setRows(webrtcsessions);
+ tableDataInfo.setCode(HttpStatus.SUCCESS);
+ tableDataInfo.setMsg("鏌ヨ鎴愬姛");
+ return tableDataInfo;
}
/**
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 e441bc0..1f37783 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
@@ -4,6 +4,7 @@
import com.ruoyi.media.domain.*;
import java.nio.file.Path;
+import java.util.List;
/**
* @Description: mediamtx娴佸獟浣撳鎴风
@@ -47,8 +48,20 @@
/**
* 鏌ヨ鎵�鏈塺tsp浼氳瘽
*/
- @Get("/rtspsessions/list")
- public String rtspsessions();
+ @Get("/rtspsessions/list?page={pageNum}&itemsPerPage={pageSize}")
+ public RtspSessions rtspsessions(@Var("pageNum") Integer pageNum, @Var("pageSize") Integer pageSize);
+
+ /**
+ * 鏌ヨ鎵�鏈墂ebrt浼氳瘽
+ */
+ @Get("/webrtcsessions/list?page={pageNum}&itemsPerPage={pageSize}")
+ public WebrtcSessions webrtcsessions(@Var("pageNum") Integer pageNum, @Var("pageSize") Integer pageSize);
+
+ /**
+ * 鏌ヨ鎵�鏈塺tmp浼氳瘽
+ */
+ @Get("/rtmpsessions/list?page={pageNum}&itemsPerPage={pageSize}")
+ public RtmpSessions rtmpsessions(@Var("pageNum") Integer pageNum, @Var("pageSize") Integer pageSize);
/**
* 鎸塻essionId鏌ヨrtsp浼氳瘽
diff --git a/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/controller/HikSdkController.java b/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/controller/HikSdkController.java
index e90b089..e70ba37 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/controller/HikSdkController.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/controller/HikSdkController.java
@@ -18,7 +18,6 @@
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
diff --git a/ard-work/src/main/resources/templates/preview.html b/ard-work/src/main/resources/templates/preview.html
index 273683e..003d22d 100644
--- a/ard-work/src/main/resources/templates/preview.html
+++ b/ard-work/src/main/resources/templates/preview.html
@@ -101,7 +101,7 @@
<button class="toggle-button" onclick="changeGrid(7, 7)">7x7</button>
<button class="toggle-button" onclick="changeGrid(8, 8)">8x8</button>
<button class="toggle-button" onclick="changeGrid(9, 9)">9x9</button>
- <input id="videoUrl" type="text" value="http://192.168.1.227:8889/164/" style="width: 250px"/>
+ <input id="videoUrl" type="text" value="http://192.168.1.227:8889/0d1c9f80a7b4480c8b401ba6b140b581_1/" style="width: 250px"/>
</div>
</div>
</div>
@@ -242,6 +242,7 @@
}
// this.eTag = res.headers.get('ETag');
this.eTag = res.headers.get("ETag") || res.headers.get('E-Tag');
+ this.wurl = new URL(res.headers.get('location'), this.wurl.origin).toString();
return res.text();
})
.then((sdp) => this.onRemoteAnswer(new RTCSessionDescription({
diff --git a/ard-work/src/main/resources/templates/test.html b/ard-work/src/main/resources/templates/test.html
index 4c4dc19..6284aaf 100644
--- a/ard-work/src/main/resources/templates/test.html
+++ b/ard-work/src/main/resources/templates/test.html
@@ -10,7 +10,6 @@
.top-buffer {
margin-top: 10px;
}
-
.container {
border: 2px solid #1b6d85;
padding: 15px;
@@ -143,7 +142,7 @@
</div>
</div>
<script th:inline="javascript">
- var cameraId, chanNo, opt, optOpen, optClose, token;
+ var cameraId, chanNo,opt, optOpen, optClose, token;
window.onload = function () {
console.log(RTCRtpReceiver.getCapabilities('video').codecs)
opt = {"username": "admin", "password": "admin123"};
@@ -879,7 +878,7 @@
function commondMethod(url, code, enable) {
cameraId = $('#selectDev option:selected').val();
chanNo = $('#selectChn option:selected').val();
- opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "enable": enable, "code": code};
+ opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 4, "enable": enable, "code": code};
$.ajax({
headers: {
'Accept': 'application/json',
@@ -910,30 +909,17 @@
type: "get",
dataType: "json",
success: function (data) {
- realView(data.data.webrtcUrl + "/");
+ realView(data.data.webrtcUrl + "/", e.target.id);
}
})
});
let webrtcClient;
//whep鎿嶄綔鏂规硶
- const retryPause = 2000;
-
- const video = document.getElementById('video');
-
- let nonAdvertisedCodecs = [];
- let pc = null;
- let restartTimeout = null;
- let sessionUrl = '';
- let offerData = '';
- let queuedCandidates = [];
- let defaultControls = false;
- let whepUrl = '';
-
+ 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);
@@ -944,21 +930,20 @@
if (m[3] !== undefined) {
ret.username = unquoteCredential(m[3]);
ret.credential = unquoteCredential(m[4]);
- ret.credentialType = 'password';
+ ret.credentialType = "password";
}
return ret;
}) : []
);
-
- const parseOffer = (sdp) => {
+ const parseOffer = (offer) => {
const ret = {
iceUfrag: '',
icePwd: '',
medias: [],
};
- for (const line of sdp.split('\r\n')) {
+ 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:')) {
@@ -970,131 +955,7 @@
return ret;
};
-
- 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 generateSdpFragment = (offerData, candidates) => {
const candidatesByMedia = {};
for (const candidate of candidates) {
const mid = candidate.sdpMLineIndex;
@@ -1104,12 +965,12 @@
candidatesByMedia[mid].push(candidate);
}
- let frag = 'a=ice-ufrag:' + od.iceUfrag + '\r\n'
- + 'a=ice-pwd:' + od.icePwd + '\r\n';
+ let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n'
+ + 'a=ice-pwd:' + offerData.icePwd + '\r\n';
let mid = 0;
- for (const media of od.medias) {
+ for (const media of offerData.medias) {
if (candidatesByMedia[mid] !== undefined) {
frag += 'm=' + media + '\r\n'
+ 'a=mid:' + mid + '\r\n';
@@ -1122,256 +983,181 @@
}
return frag;
- };
+ }
- const loadStream = () => {
- console.log('loadStream');
- requestICEServers();
- };
+ 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 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;
+ 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');
}
- 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);
+ // this.eTag = res.headers.get('ETag');
+ this.eTag = res.headers.get("ETag") || res.headers.get('E-Tag');
+ this.wurl = new URL(res.headers.get('location'), this.wurl.origin).toString();
+ 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 === '') {
+ console.log("222222222222222222222")
+ this.queuedCandidates.push(evt.candidate);
+ } else {
+ console.log("333333333333333333333")
+ 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');
}
- 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) => {
- resolve(false);
- })
- .finally(() => {
- pc.close();
+ console.log('error: ' + err);
+ this.scheduleRestart();
});
- })
- );
+ }
- 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 onError = (err) => {
- if (restartTimeout === null) {
-
- if (pc !== null) {
- pc.close();
- pc = null;
+ scheduleRestart() {
+ if (this.restartTimeout !== null) {
+ return;
}
- restartTimeout = window.setTimeout(() => {
- restartTimeout = null;
- loadStream();
- }, retryPause);
-
- if (this.sessionUrl) {
- fetch(this.sessionUrl, {
- method: 'DELETE',
- });
+ if (this.pc !== null) {
+ this.pc.close();
+ this.pc = null;
}
- this.sessionUrl = '';
- queuedCandidates = [];
+ this.restartTimeout = window.setTimeout(() => {
+ this.restartTimeout = null;
+ this.start();
+ }, restartPause);
+
+ this.eTag = '';
+ this.queuedCandidates = [];
}
- };
- const sendLocalCandidates = (candidates) => {
- fetch(new URL('whep', this.whepUrl), {
- 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}`);
+ stop() {
+ if (this.pc) {
+ try {
+ this.pc.close();
+ } catch (e) {
+ console.log("Failure close peer connection:" + e);
}
- })
- .catch((err) => {
- onError(err.toString());
- });
- };
-
- const onLocalCandidate = (evt) => {
- if (restartTimeout !== null) {
- return;
- }
-
- if (evt.candidate !== null) {
- if (this.sessionUrl === '') {
- queuedCandidates.push(evt.candidate);
- } else {
- //sendLocalCandidates([evt.candidate])
+ this.pc = null;
}
}
- };
+ }
- const onRemoteAnswer = (sdp) => {
- if (restartTimeout !== null) {
- return;
- }
- pc.setRemoteDescription(new RTCSessionDescription({
- type: 'answer',
- sdp,
- }))
- .then(() => {
- if (queuedCandidates.length !== 0) {
- sendLocalCandidates(queuedCandidates);
- queuedCandidates = [];
- }
- })
- .catch((err) => {
- onError(err.toString());
- });
- };
-
- const sendOffer = (offer) => {
- fetch(new URL('whep', this.whepUrl), {
- 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}`);
- }
-
- this.sessionUrl = new URL(res.headers.get('location'), new URL(this.whepUrl).origin).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) => {
-
- video.srcObject = evt.streams[0];
- };
-
- const requestICEServers = () => {
- fetch(new URL('whep', this.whepUrl), {
- 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 init = () => {
- getNonAdvertisedCodecs();
- };
-
- function realView(whepUrl) {
- console.log(whepUrl)
- this.whepUrl = whepUrl
- init();
+ function realView(whepUrl, videoId) {
+ webrtcClient = new WHEPClient(whepUrl, videoId);
}
</script>
</body>
diff --git a/ard-work/src/main/resources/templates/test1.html b/ard-work/src/main/resources/templates/test1.html
deleted file mode 100644
index 5623b4b..0000000
--- a/ard-work/src/main/resources/templates/test1.html
+++ /dev/null
@@ -1,1161 +0,0 @@
-<!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/server/mediamtx/mediamtx.yml b/server/mediamtx/mediamtx.yml
index 81ca38d..9427049 100644
--- a/server/mediamtx/mediamtx.yml
+++ b/server/mediamtx/mediamtx.yml
@@ -222,7 +222,7 @@
password: ''
# List of interfaces that will be used to gather IPs to send
# to the counterpart to establish a connection.
-webrtcICEInterfaces: []
+webrtcICEInterfaces: [192.168.1.1]
# 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: [192.168.1.227]
--
Gitblit v1.9.3