| <!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: 20px; | 
|         } | 
|     </style> | 
| <body> | 
| <div class="container"> | 
|     <div class="row "> | 
|         <div class="col-md-12"> | 
|             相机id:<select id="select" style="width: 330px;"> | 
|         </select> | 
|         </div> | 
|     </div> | 
|     <div class="row"> | 
|         <div class="col-md-5"> | 
|             <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="请输入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"> | 
|   | 
|     var cameraId, opt, optOpen, optClose, token; | 
|     window.onload = function () { | 
|         console.log(RTCRtpReceiver.getCapabilities('video').codecs) | 
|         $.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); | 
|                     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里面的option元素 | 
|                     var option = document.createElement("option"); | 
|                     //给option的text赋值,这就是你点开下拉框能够看到的东西 | 
|                     $(option).text(arr[i].id); | 
|                     //获取select 下拉框对象,并将option添加进select | 
|                     $('#select').append(option); | 
|                 } | 
|             } | 
|         }) | 
|         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; | 
|             } | 
|         }) | 
|         // 初始化内容 | 
|         console.log(cameraMap); | 
|     } | 
|     //云台上下左右 | 
|     $("#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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1}; | 
|         $.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 = $('#select option:selected').val(); | 
|         var p = $('#p').val(); | 
|         var t = $('#t').val(); | 
|         var z = $('#z').val(); | 
|         //定义一个带有Map字段的实体对象 | 
|         var myEntity = { | 
|             chanNo: 1, | 
|             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 = $('#select option:selected').val(); | 
|         var camera = cameraMap.get(cameraId); | 
|         var camP = camera.longitude + ',' + camera.latitude + ',' + camera.altitude; | 
|         var targetP = $('#targetPostion').val(); | 
|         //定义一个带有Map字段的实体对象 | 
|         var myEntity = { | 
|             chanNo: 1, | 
|             cameraId: cameraId, | 
|             targetPosition: targetP, | 
|             camPosition: camP, | 
|         }; | 
|         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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1}; | 
|         $.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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         optOpen = {"cameraId": cameraId, "chanNo": 1, "enable": true}; | 
|         optClose = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         optOpen = {"cameraId": cameraId, "chanNo": 1, "enable": true}; | 
|         optClose = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         optOpen = {"cameraId": cameraId, "chanNo": 1, "enable": true}; | 
|         optClose = {"cameraId": cameraId, "chanNo": 1, "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 = $('#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); | 
|             } | 
|         }) | 
|     }) | 
|     var heateRpwronflag = true; | 
|     $("#HeateRpwron").click(function () { | 
|         cameraId = $('#select option:selected').val(); | 
|         optOpen = {"cameraId": cameraId, "chanNo": 1, "enable": true}; | 
|         optClose = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         optOpen = {"cameraId": cameraId, "chanNo": 1, "enable": true}; | 
|         optClose = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1}; | 
|         $.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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1}; | 
|         $.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 = $('#select option:selected').val(); | 
|         optOpen = {"cameraId": cameraId, "chanNo": 1, "enable": true}; | 
|         optClose = {"cameraId": cameraId, "chanNo": 1, "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 = $('#select option:selected').val(); | 
|         opt = {"cameraId": cameraId, "chanNo": 1, "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 = $('#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); | 
|             } | 
|         }) | 
|     }); | 
|   | 
|     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> |