From 76817b8c752b12030ab285bcb5b2effebfa9a248 Mon Sep 17 00:00:00 2001 From: ‘liusuyi’ <1951119284@qq.com> Date: 星期六, 26 八月 2023 17:29:14 +0800 Subject: [PATCH] 流媒体增加webrtc和rtmp协议推拉流 --- ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java | 24 +++ ard-work/src/main/resources/templates/test.html | 33 +++ ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java | 31 ++- ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java | 24 ++ ard-work/src/main/resources/static/js/WHEPClient.js | 76 +++++++++ ard-work/src/main/java/com/ruoyi/media/domain/StreamInfo.java | 12 + ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java | 10 ard-work/src/main/resources/static/js/negotiateConnectionWithClientOffer.js | 80 ++++++++++ ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java | 24 +++ lib/mediamtx/mediamtx.yml | 4 ard-work/src/main/java/com/ruoyi/media/service/impl/MediaService.java | 157 ++++++++++++++----- 11 files changed, 408 insertions(+), 67 deletions(-) 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 1ec2e16..ca50877 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 @@ -17,6 +17,9 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @Description: @@ -38,17 +41,16 @@ @PreAuthorize("@ss.hasPermi('media:stream:add')") @ApiOperationSupport(includeParameters = {"streamInfo.name", "streamInfo.rtspSource", "streamInfo.mode"}) public AjaxResult addPath(@RequestBody StreamInfo streamInfo) { - if(StringUtils.isEmpty(streamInfo.getName())) - { + if (StringUtils.isEmpty(streamInfo.getName())) { return AjaxResult.error("閫氶亾鍚嶇О涓嶈兘涓虹┖"); } - if(StringUtils.isEmpty(streamInfo.getRtspSource())) - { + if (StringUtils.isEmpty(streamInfo.getRtspSource())) { return AjaxResult.error("rtsp鍦板潃涓嶈兘涓虹┖"); } - String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(),streamInfo.getIsCode()); + String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode()); return AjaxResult.success(rtsp); } + /** * 鑾峰彇杞爜璇︾粏淇℃伅 */ @@ -57,6 +59,7 @@ public AjaxResult getInfo(@PathVariable("name") String name) { return success(mediaService.getPathInfo(name)); } + /** * 淇敼杞爜 */ @@ -65,14 +68,14 @@ @PutMapping public AjaxResult edit(@RequestBody StreamInfo streamInfo) { mediaService.removePath(new String[]{streamInfo.getName()}); - String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(),streamInfo.getIsCode()); + String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode()); return AjaxResult.success(rtsp); } @DeleteMapping("/path/{names}") @PreAuthorize("@ss.hasPermi('media:stream:remove')") @ApiOperation("绉婚櫎杞爜") - public AjaxResult removePath( @PathVariable String[] names) { + public AjaxResult removePath(@PathVariable String[] names) { mediaService.removePath(names); return AjaxResult.success(); } @@ -128,7 +131,19 @@ @PreAuthorize("@ss.hasPermi('media:stream:remove')") @DeleteMapping("/{id}") public AjaxResult removePullStreamSession(@PathVariable String id) { - return AjaxResult.success(mediaService.kickRtspSession(id)); + List<StreamInfo> pullStreamList = mediaService.getPullStreamList(); + StreamInfo streamInfo = pullStreamList.stream() + .filter(object -> object.getId().equals(id)) + .collect(Collectors.toList()).get(0); + switch (streamInfo.getSessionType()) { + case "rtsp": + return AjaxResult.success(mediaService.kickRtspSession(id)); + case "webrtc": + return AjaxResult.success(mediaService.kickWebrtcSession(id)); + case "rtmp": + return AjaxResult.success(mediaService.kickRtmpSession(id)); + } + return AjaxResult.error(); } @PreAuthorize("@ss.hasPermi('media:stream:list')") 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 new file mode 100644 index 0000000..8969464 --- /dev/null +++ b/ard-work/src/main/java/com/ruoyi/media/domain/RtmpSession.java @@ -0,0 +1,24 @@ +package com.ruoyi.media.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @Description: + * @ClassName: WebrtcSession + * @Author: 鍒樿嫃涔� + * @Date: 2023骞�08鏈�26鏃�10:16:21 + **/ +@Data +public class RtmpSession { + private String name; + private String id; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date created; + private String remoteAddr; + private String state; + private long bytesReceived; + private long bytesSent; +} diff --git a/ard-work/src/main/java/com/ruoyi/media/domain/StreamInfo.java b/ard-work/src/main/java/com/ruoyi/media/domain/StreamInfo.java index b705a5c..c605658 100644 --- a/ard-work/src/main/java/com/ruoyi/media/domain/StreamInfo.java +++ b/ard-work/src/main/java/com/ruoyi/media/domain/StreamInfo.java @@ -33,6 +33,14 @@ */ String rtspUrl; /** + * rtmp鎾斁鍦板潃 + */ + String rtmpUrl; + /** + * webrtc鎾斁鍦板潃 + */ + String webrtcUrl; + /** * 浼犺緭鏂瑰紡 */ String protocol; @@ -67,5 +75,9 @@ * 鏄惁杞爜 */ String isCode; + /** + * 濯掍綋绫诲瀷锛坵ebRTCSession/rtspSession锛� + */ + String sessionType; } 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 new file mode 100644 index 0000000..5e27c27 --- /dev/null +++ b/ard-work/src/main/java/com/ruoyi/media/domain/WebrtcSession.java @@ -0,0 +1,24 @@ +package com.ruoyi.media.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @Description: + * @ClassName: WebrtcSession + * @Author: 鍒樿嫃涔� + * @Date: 2023骞�08鏈�26鏃�10:16:21 + **/ +@Data +public class WebrtcSession { + private String name; + private String id; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date created; + private String remoteAddr; + private String state; + private long bytesReceived; + private long bytesSent; +} 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 28b5780..bd99555 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,8 +1,6 @@ package com.ruoyi.media.service; -import com.ruoyi.media.domain.Items; -import com.ruoyi.media.domain.RtspSession; -import com.ruoyi.media.domain.StreamInfo; +import com.ruoyi.media.domain.*; import java.util.List; @@ -29,7 +27,8 @@ public List<Items> rtspsessions(); RtspSession getRtspSessionById(String sessionId); - + WebrtcSession getWebrtcSessionById(String sessionId); + RtmpSession getRtmpSessionById(String sessionId); List<RtspSession> getPushStreams(); List<RtspSession> getPullStreams(); @@ -39,5 +38,6 @@ List<StreamInfo> getPullStreamList(); Boolean kickRtspSession(String sessionId); - + Boolean kickRtmpSession(String sessionId); + Boolean kickWebrtcSession(String sessionId); } diff --git a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaService.java b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaService.java index 578291c..090961e 100644 --- a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaService.java +++ b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaService.java @@ -130,9 +130,7 @@ if (matcher.find()) { info.setRtspSource(matcher.group()); info.setIsCode("1"); - } - else - { + } else { info.setRtspSource(item.getConf().getSource()); info.setIsCode("0"); } @@ -212,6 +210,20 @@ } @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; + } + + @Override public List<RtspSession> getPushStreams() { List<RtspSession> rtspSessions = new ArrayList<>(); @@ -255,11 +267,17 @@ //ID String name = item.getName(); info.setName(name); + //RTMP鎾斁鍦板潃 + String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; + info.setRtmpUrl(rtmpUrl); //RTSP鎾斁鍦板潃 String rtspUrl = "rtsp://" + mediamtxHost + ":7554/" + name; info.setRtspUrl(rtspUrl); + //WEBRTC鎾斁鍦板潃 + String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; + info.setWebrtcUrl(webrtcUrl); Source source = item.getSource(); - if (source==null||source.getId().equals("")) { + if (source == null || source.getId().equals("")) { //浼氳瘽ID info.setId("0"); //涓婅娴侀噺 @@ -296,18 +314,10 @@ Matcher matcher = pattern.matcher(runoninit); if (matcher.find()) { info.setRtspSource(matcher.group()); - } - else - { + } else { info.setRtspSource(item.getConf().getSource()); } //浼犺緭鍗忚 -// regex = "-rtsp_transport\\s+(\\w+)"; -// pattern = Pattern.compile(regex); -// matcher = pattern.matcher(runoninit); -// if (matcher.find()) { -// info.setProtocol(matcher.group(1)); -// } info.setProtocol(item.getConf().getSourceprotocol()); //鎷夋祦鏁伴噺 List<Readers> readers = item.getReaders(); @@ -332,38 +342,81 @@ //ID String name = item.getName(); info.setName(name); - //RTSP鎾斁鍦板潃 - String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name; - info.setRtspUrl(rtspUrl); - RtspSession rtspSession = getRtspSessionById(reader.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); - //浼犺緭鍗忚 -// String runoninit = item.getConf().getRunondemand(); -// String regex = "-rtsp_transport\\s+(\\w+)"; -// Pattern pattern = Pattern.compile(regex); -// Matcher matcher = pattern.matcher(runoninit); -// if (matcher.find()) { -// info.setProtocol(matcher.group(1)); -// } info.setProtocol(item.getConf().getSourceprotocol()); - //鎷夋祦鏈嶅姟鍣� - info.setRemoteAddr(rtspSession.getRemoteAddr()); - PullStreamInfoList.add(info); + + 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); + Comparator<StreamInfo> comparator = Comparator.comparing(streamInfo -> streamInfo.getBeginTime()); // 浣跨敤Collections.sort鏂规硶杩涜鎺掑簭 Collections.sort(personList, comparator); Collections.sort(PullStreamInfoList, comparator.reversed()); return PullStreamInfoList; } @@ -371,7 +424,27 @@ @Override public Boolean kickRtspSession(String sessionId) { try { - mediaClient.kick(sessionId); + mediaClient.kickRtspSessions(sessionId); + return true; + } catch (Exception ex) { + return false; + } + } + + @Override + public Boolean kickRtmpSession(String sessionId) { + try { + mediaClient.kickRtmpSessions(sessionId); + return true; + } catch (Exception ex) { + return false; + } + } + + @Override + public Boolean kickWebrtcSession(String sessionId) { + try { + mediaClient.kickWebrtcSessions(sessionId); return true; } catch (Exception ex) { return false; 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 e87f76c..7103be5 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 @@ -53,11 +53,29 @@ */ @Get("/rtspsessions/get/{sessionId}") String getRtspsessionById(@Var("sessionId") String sessionId); - + /** + * 鎸塻essionId鏌ヨwebrtc浼氳瘽 + */ + @Get("/webrtcsessions/get/{sessionId}") + String getWebrtcsessionById(@Var("sessionId") String sessionId); + /** + * 鎸塻essionId鏌ヨrtmp浼氳瘽 + */ + @Get("/rtmpconns/get/{sessionId}") + String getRtmpsessionById(@Var("sessionId") String sessionId); /** * 鎸塻essionId鍒犻櫎rtsp浼氳瘽 */ @Post("/rtspsessions/kick/{sessionId}") - String kick(@Var("sessionId") String sessionId); - + String kickRtspSessions(@Var("sessionId") String sessionId); + /** + * 鎸塻essionId鍒犻櫎rtmp杩炴帴 + */ + @Post("/rtmpconns/kick/{sessionId}") + String kickRtmpSessions(@Var("sessionId") String sessionId); + /** + * 鎸塻essionId鍒犻櫎webrtc浼氳瘽 + */ + @Post("/webrtcsessions/kick/{sessionId}") + String kickWebrtcSessions(@Var("sessionId") String sessionId); } diff --git a/ard-work/src/main/resources/static/js/WHEPClient.js b/ard-work/src/main/resources/static/js/WHEPClient.js new file mode 100644 index 0000000..f84398b --- /dev/null +++ b/ard-work/src/main/resources/static/js/WHEPClient.js @@ -0,0 +1,76 @@ +import negotiateConnectionWithClientOffer from "./negotiateConnectionWithClientOffer.js"; +/** + * Example implementation of a client that uses WHEP to playback video over WebRTC + * + * https://www.ietf.org/id/draft-murillo-whep-00.html + */ +export default class WHEPClient { + constructor(endpoint, videoElement) { + this.endpoint = endpoint; + this.videoElement = videoElement; + this.stream = new MediaStream(); + /** + * Create a new WebRTC connection, using public STUN servers with ICE, + * allowing the client to disover its own IP address. + * https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Protocols#ice + */ + this.peerConnection = new RTCPeerConnection({ + iceServers: [ + { + urls: "stun:stun.cloudflare.com:3478", + }, + ], + bundlePolicy: "max-bundle", + }); + /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTransceiver */ + this.peerConnection.addTransceiver("video", { + direction: "recvonly", + }); + this.peerConnection.addTransceiver("audio", { + direction: "recvonly", + }); + /** + * When new tracks are received in the connection, store local references, + * so that they can be added to a MediaStream, and to the <video> element. + * + * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/track_event + */ + this.peerConnection.ontrack = (event) => { + const track = event.track; + const currentTracks = this.stream.getTracks(); + const streamAlreadyHasVideoTrack = currentTracks.some( + (track) => track.kind === "video" + ); + const streamAlreadyHasAudioTrack = currentTracks.some( + (track) => track.kind === "audio" + ); + switch (track.kind) { + case "video": + if (streamAlreadyHasVideoTrack) { + break; + } + this.stream.addTrack(track); + break; + case "audio": + if (streamAlreadyHasAudioTrack) { + break; + } + this.stream.addTrack(track); + break; + default: + console.log("got unknown track " + track); + } + }; + this.peerConnection.addEventListener("connectionstatechange", (ev) => { + if (this.peerConnection.connectionState !== "connected") { + return; + } + if (!this.videoElement.srcObject) { + this.videoElement.srcObject = this.stream; + } + }); + this.peerConnection.addEventListener("negotiationneeded", (ev) => { + negotiateConnectionWithClientOffer(this.peerConnection, this.endpoint); + }); + } +} diff --git a/ard-work/src/main/resources/static/js/negotiateConnectionWithClientOffer.js b/ard-work/src/main/resources/static/js/negotiateConnectionWithClientOffer.js new file mode 100644 index 0000000..26c70b6 --- /dev/null +++ b/ard-work/src/main/resources/static/js/negotiateConnectionWithClientOffer.js @@ -0,0 +1,80 @@ +/** + * Performs the actual SDP exchange. + * + * 1. Constructs the client's SDP offer + * 2. Sends the SDP offer to the server, + * 3. Awaits the server's offer. + * + * SDP describes what kind of media we can send and how the server and client communicate. + * + * https://developer.mozilla.org/en-US/docs/Glossary/SDP + * https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#name-protocol-operation + */ +export default async function negotiateConnectionWithClientOffer( + peerConnection, + endpoint +) { + /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer */ + const offer = await peerConnection.createOffer(); + /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription */ + await peerConnection.setLocalDescription(offer); + /** Wait for ICE gathering to complete */ + let ofr = await waitToCompleteICEGathering(peerConnection); + if (!ofr) { + throw Error("failed to gather ICE candidates for offer"); + } + /** + * As long as the connection is open, attempt to... + */ + while (peerConnection.connectionState !== "closed") { + /** + * This response contains the server's SDP offer. + * This specifies how the client should communicate, + * and what kind of media client and server have negotiated to exchange. + */ + let response = await postSDPOffer(endpoint, ofr.sdp); + if (response.status === 201) { + let answerSDP = await response.text(); + await peerConnection.setRemoteDescription( + new RTCSessionDescription({ type: "answer", sdp: answerSDP }) + ); + return response.headers.get("Location"); + } else if (response.status === 405) { + console.error("Update the URL passed into the WHIP or WHEP client"); + } else { + const errorMessage = await response.text(); + console.error(errorMessage); + } + /** Limit reconnection attempts to at-most once every 5 seconds */ + await new Promise((r) => setTimeout(r, 5000)); + } +} +async function postSDPOffer(endpoint, data) { + return await fetch(endpoint, { + method: "POST", + mode: "cors", + headers: { + "content-type": "application/sdp", + }, + body: data, + }); +} +/** + * Receives an RTCPeerConnection and waits until + * the connection is initialized or a timeout passes. + * + * https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#section-4.1 + * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceGatheringState + * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icegatheringstatechange_event + */ +async function waitToCompleteICEGathering(peerConnection) { + return new Promise((resolve) => { + /** Wait at most 1 second for ICE gathering. */ + setTimeout(function () { + resolve(peerConnection.localDescription); + }, 1000); + peerConnection.onicegatheringstatechange = (ev) => + peerConnection.iceGatheringState === "complete" && + resolve(peerConnection.localDescription); + }); +} diff --git a/ard-work/src/main/resources/templates/test.html b/ard-work/src/main/resources/templates/test.html index 15e9981..a55bf5e 100644 --- a/ard-work/src/main/resources/templates/test.html +++ b/ard-work/src/main/resources/templates/test.html @@ -13,6 +13,20 @@ margin-top: 10px; } </style> + <!-- 瀵煎叆 ECMAScript 妯″潡 --> + <script th:type="module" th:src="@{/js/WHEPClient.js}"></script> + <!-- 鍐呰仈鑴氭湰鍧楋紝鍙互浣跨敤妯″潡涓殑鍐呭 --> + <script th:inline="javascript"> + /*<![CDATA[*/ + // 鍦ㄨ繖閲屼娇鐢� WHEPClient 妯″潡涓殑鍐呭 + $("#play").click(function webrtcClient(){ + const url = "http://127.0.0.1:8889/165"; // add the webRTC URL from your live input here + console.log(url) + const videoElement = document.getElementById("remote-video"); + const client = new WHEPClient(url, videoElement); + }) + /*]]>*/ + </script> <body> <div class="container"> <div class="row "> @@ -127,11 +141,15 @@ <div class="row top-buffer"> <video id="video" muted autoplay loop controls style="width: 800px; height: 100%; object-fit: fill;"/> </div> + <div class="row top-buffer"> + <button id="play" type="button" class="btn btn-default" >鎾斁</button> + <video id="remote-video" controls autoplay muted></video> + </div> </div> </div> </div> <script th:inline="javascript"> - var cameraId, opt, token; + var cameraId, opt,optOpen,optClose, token; window.onload = function () { $.ajax({ url: "../hik/list", @@ -461,8 +479,8 @@ var defogflag = true; $("#Defogcfg").click(function () { cameraId = $('#select option:selected').val(); - optOpen = {"cameraId": cameraId, "channelNum": 1, "enable": true}; - optClose = {"cameraId": cameraId, "channelNum": 1, "enable": false}; + optOpen = {"cameraId": cameraId, "channelNum": 1, "enable": true}; + optClose = {"cameraId": cameraId, "channelNum": 1, "enable": false}; if (defogflag) { $(this).text("鍏抽棴閫忛浘"); defogflag = false; @@ -503,8 +521,8 @@ var infrareflag = true; $("#Infrarecfg").click(function () { cameraId = $('#select option:selected').val(); - optOpen = {"cameraId": cameraId, "channelNum": 1, "enable": true}; - optClose = {"cameraId": cameraId, "channelNum": 1, "enable": false}; + optOpen = {"cameraId": cameraId, "channelNum": 1, "enable": true}; + optClose = {"cameraId": cameraId, "channelNum": 1, "enable": false}; if (infrareflag) { $(this).text("鍏抽棴绾㈠"); infrareflag = false; @@ -545,8 +563,8 @@ var focusModeflag = true; $("#FocusMode").click(function () { cameraId = $('#select option:selected').val(); - optOpen = {"cameraId": cameraId, "channelNum": 1, "enable": true}; - optClose = {"cameraId": cameraId, "channelNum": 1, "enable": false}; + optOpen = {"cameraId": cameraId, "channelNum": 1, "enable": true}; + optClose = {"cameraId": cameraId, "channelNum": 1, "enable": false}; if (focusModeflag) { $(this).text("鑷姩鑱氱劍"); focusModeflag = false; @@ -828,5 +846,6 @@ webRtcServer.disconnect(); } </script> + </body> </html> \ No newline at end of file diff --git a/lib/mediamtx/mediamtx.yml b/lib/mediamtx/mediamtx.yml index ae9757b..c26403a 100644 --- a/lib/mediamtx/mediamtx.yml +++ b/lib/mediamtx/mediamtx.yml @@ -105,7 +105,7 @@ # RTMP parameters # Disable support for the RTMP protocol. -rtmpDisable: yes +rtmpDisable: no # Address of the RTMP listener. This is needed only when encryption is "no" or "optional". rtmpAddress: :1935 # Encrypt connections with TLS (RTMPS). @@ -181,7 +181,7 @@ # WebRTC parameters # Disable support for the WebRTC protocol. -webrtcDisable: yes +webrtcDisable: no # Address of the WebRTC listener. webrtcAddress: :8889 # Enable TLS/HTTPS on the WebRTC server. -- Gitblit v1.9.3