| | |
| | | <meta charset="UTF-8"> |
| | | <title>测试页</title> |
| | | <script th:src="@{/js/jquery-3.6.4.min.js}"></script> |
| | | <script th:src="@{/js/adapter.min.js}"></script> |
| | | <script th:src="@{/js/webrtcstreamer.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: 20px; |
| | | } |
| | | </style> |
| | | <body> |
| | | <div class="container"> |
| | | <div class="row "> |
| | | <div class="dropdown"> |
| | | 相机id:<select id="select"> |
| | | <div class="col-md-12"> |
| | | 相机id:<select id="select" style="width: 330px;"> |
| | | </select> |
| | | </div> |
| | | </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-4 col-md-offset-3"> |
| | | <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-4 col-md-offset-2"> |
| | | <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-4 col-md-offset-3"> |
| | | <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-6"> |
| | | <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="请输入P值"/> |
| | | </div> |
| | | <div class="input-group"> |
| | | <span class="input-group-addon">T值:</span> |
| | | <input id="t" class="form-control" placeholder="请输入T值"/> |
| | | </div> |
| | | <div class="input-group"> |
| | | <span class="input-group-addon">Z值:</span> |
| | | <input id="z" class="form-control" placeholder="请输入Z值"/> |
| | | </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="getZeroPTZ" 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="realCutPic" type="button" class="btn btn-default">实时抓图</button> |
| | | <button id="saveCutPic" type="button" class="btn btn-default">存储抓图</button> |
| | | </div> |
| | | </div> |
| | | <div class="row top-buffer"> |
| | | <div class="col-md-6"> |
| | | <img class="thumbnail" id="imgContainer" style="width: 500px; height: 300px;"/> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="col-md-1"/> |
| | | <div class="col-md-5"> |
| | | <div class="row top-buffer"> |
| | | <video id="video" muted autoplay loop controls style="width: 800px; height: 100%; object-fit: fill;"/> |
| | | <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="请输入P值"/> |
| | | </div> |
| | | <div class="input-group"> |
| | | <span class="input-group-addon">T值:</span> |
| | | <input id="t" class="form-control" placeholder="请输入T值"/> |
| | | </div> |
| | | <div class="input-group"> |
| | | <span class="input-group-addon">Z值:</span> |
| | | <input id="z" class="form-control" placeholder="请输入Z值"/> |
| | | </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"> |
| | | <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" th:type="module"> |
| | | <script th:inline="javascript"> |
| | | |
| | | var cameraId, opt, optOpen, optClose, token; |
| | | window.onload = function () { |
| | |
| | | dataType: "json", |
| | | data: JSON.stringify(opt), |
| | | success: function (data) { |
| | | console.log(data); |
| | | token = data.token; |
| | | } |
| | | }) |
| | |
| | | cameraId = $('#select option:selected').val(); |
| | | opt = {"cameraId": cameraId, "chanNo": 1}; |
| | | $.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); |
| | | } |
| | | }) |
| | | 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 () { |
| | |
| | | $.ajax({ |
| | | headers: { |
| | | 'Accept': 'application/json', |
| | | 'Content-Type': 'application/json' |
| | | 'Content-Type': 'application/json', |
| | | 'Authorization': token |
| | | }, |
| | | url: "../cameraSdk/cameraDeicing", |
| | | type: "post", |
| | |
| | | console.log(data.data); |
| | | setTimeout(() => { |
| | | $('#imgContainer').attr('src', data.data); |
| | | }, 1000 ) |
| | | }, 1000) |
| | | |
| | | } |
| | | }) |
| | |
| | | }) |
| | | } |
| | | |
| | | let webRtcServer = null; |
| | | let videoMap = new Map(); |
| | | $('video').click(function (e) { |
| | | let ID = e.target.id;//获取当前点击事件的元素 |
| | | console.log(ID); |
| | | if (videoMap.get(ID) != null) { |
| | | closeVideo(ID, videoMap.get(ID)); |
| | | } else { |
| | | var cameraId = $('#select option:selected').val(); |
| | | let camera = cameraMap.get(cameraId); |
| | | console.log(camera); |
| | | if (camera.factory == "3") { |
| | | realViewYs("127.0.0.1", ID, camera.username, camera.password, camera.ipaddr, camera.port); |
| | | } else if (camera.factory == "2") { |
| | | realViewDh("127.0.0.1", ID, camera.username, camera.password, camera.ipaddr, camera.port); |
| | | } else { |
| | | realViewHik("127.0.0.1", ID, camera.username, camera.password, camera.ipaddr, camera.port); |
| | | var cameraId = $('#select option:selected').val(); |
| | | $.ajax({ |
| | | headers: { |
| | | 'Accept': 'application/json', |
| | | 'Content-Type': 'application/json', |
| | | 'Authorization': token |
| | | }, |
| | | url: "../vtdu/media/" + cameraId + "_" + 1, |
| | | type: "get", |
| | | dataType: "json", |
| | | success: function (data) { |
| | | realView(data.data.webrtcUrl + "/", e.target.id); |
| | | } |
| | | } |
| | | }) |
| | | }); |
| | | |
| | | //预览海康相机 |
| | | function realViewHik(serverip, elem, username, password, ipaddr, port) { |
| | | // webRtcServer = new WebRtcStreamer(elem, "http://" + serverip + ":8000"); |
| | | webRtcServer = new WebRtcStreamer(elem, "http://192.168.1.227:8000"); |
| | | let rtspUrl = "rtsp://" + username + ":" + password + "@" + ipaddr + ":" + port + "/ch1/main/av_stream"; |
| | | let option = "rtptransport=tcp"; |
| | | console.log("rtsp地址:" + rtspUrl); |
| | | webRtcServer.connect(rtspUrl, null, option, null); |
| | | videoMap.set(elem, webRtcServer); |
| | | 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; |
| | | } |
| | | |
| | | //预览大华相机 |
| | | function realViewDh(serverip, elem, username, password, ipaddr, port) { |
| | | webRtcServer = new WebRtcStreamer(elem, "http://" + serverip + ":8000"); |
| | | let rtspUrl = "rtsp://" + username + ":" + password + "@" + ipaddr + ":" + port + "/cam/realmonitor?channel=1&subtype=0"; |
| | | let option = "rtptransport=tcp"; |
| | | console.log("rtsp地址:" + rtspUrl); |
| | | 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(); |
| | | } |
| | | |
| | | webRtcServer.connect(rtspUrl, null, option, null); |
| | | videoMap.set(elem, webRtcServer); |
| | | 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 realViewYs(serverip, elem, username, password, ipaddr, port) { |
| | | webRtcServer = new WebRtcStreamer(elem, "http://" + serverip + ":8000"); |
| | | let rtspUrl = "rtsp://" + username + ":" + password + "@" + ipaddr + ":" + port + "/media/video1/multicast"; |
| | | console.log("rtsp地址:" + rtspUrl); |
| | | let option = "rtptransport=tcp"; |
| | | webRtcServer.connect(rtspUrl, null, option, null); |
| | | videoMap.set(elem, webRtcServer); |
| | | } |
| | | |
| | | function closeVideo(id, webrtc) { |
| | | webrtc.disconnect(); |
| | | videoMap.delete(id); |
| | | } |
| | | |
| | | //页面退出时销毁 |
| | | window.onbeforeunload = function () { |
| | | webRtcServer.disconnect(); |
| | | function realView(whepUrl, videoId) { |
| | | console.log(whepUrl) |
| | | webrtcClient = new WHEPClient(whepUrl, videoId); |
| | | } |
| | | </script> |
| | | </body> |