ard-work/src/main/java/com/ruoyi/alarmpoints/tube/controller/ArdTubesDetailsController.java
@@ -120,6 +120,7 @@ String message = ardTubesDetailsService.importArdTubesDetails(tubesDetailsList, updateSupport, operName,tubeId); return success(message); } @PostMapping("/importTemplate") @ApiOperation(value = "管线详情模板下载接口") public void importTemplate(HttpServletResponse response) ard-work/src/main/java/com/ruoyi/alarmpoints/well/controller/ArdAlarmpointsWellController.java
@@ -152,9 +152,9 @@ @ApiOperation("导入井") public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { ExcelUtil<ArdAlarmpointsWell> util = new ExcelUtil<ArdAlarmpointsWell>(ArdAlarmpointsWell.class); List<ArdAlarmpointsWell> userList = util.importExcel(file.getInputStream()); List<ArdAlarmpointsWell> wellList = util.importExcel(file.getInputStream()); String operName = getUsername(); String message = ardAlarmpointsWellService.importUser(userList, updateSupport, operName); String message = ardAlarmpointsWellService.importWell(wellList, updateSupport, operName); return success(message); } ard-work/src/main/java/com/ruoyi/alarmpoints/well/mapper/ArdAlarmpointsWellMapper.java
@@ -62,7 +62,13 @@ * @return 结果 */ public int updateArdAlarmpointsWell(ArdAlarmpointsWell ardAlarmpointsWell); /** * 修改井管理按井号 * * @param ardAlarmpointsWell 井管理 * @return 结果 */ public int updateArdAlarmpointsWellByWellId(ArdAlarmpointsWell ardAlarmpointsWell); /** * 删除井管理 * ard-work/src/main/java/com/ruoyi/alarmpoints/well/service/IArdAlarmpointsWellService.java
@@ -60,7 +60,13 @@ * @return 结果 */ public int updateArdAlarmpointsWell(ArdAlarmpointsWell ardAlarmpointsWell); /** * 修改井管理按井号 * * @param ardAlarmpointsWell 井管理 * @return 结果 */ public int updateArdAlarmpointsWellByWellId(ArdAlarmpointsWell ardAlarmpointsWell); /** * 批量删除井管理 * @@ -84,7 +90,7 @@ * @param operName 操作用户 * @return 结果 */ public String importUser(List<ArdAlarmpointsWell> ardAlarmpointsWellList, Boolean isUpdateSupport, String operName); public String importWell(List<ArdAlarmpointsWell> ardAlarmpointsWellList, Boolean isUpdateSupport, String operName); /** * 校验用户是否有数据权限 * ard-work/src/main/java/com/ruoyi/alarmpoints/well/service/impl/ArdAlarmpointsWellServiceImpl.java
@@ -126,7 +126,13 @@ ardAlarmpointsWell.setUpdateTime(DateUtils.getNowDate()); return ardAlarmpointsWellMapper.updateArdAlarmpointsWell(ardAlarmpointsWell); } @Override @Transactional public int updateArdAlarmpointsWellByWellId(ArdAlarmpointsWell ardAlarmpointsWell) { ardAlarmpointsWell.setUpdateBy(SecurityUtils.getUsername()); ardAlarmpointsWell.setUpdateTime(DateUtils.getNowDate()); return ardAlarmpointsWellMapper.updateArdAlarmpointsWellByWellId(ardAlarmpointsWell); } /** * 批量删除井管理 * @@ -150,7 +156,7 @@ } @Override public String importUser(List<ArdAlarmpointsWell> ardAlarmpointsWellList, Boolean isUpdateSupport, String operName) { public String importWell(List<ArdAlarmpointsWell> ardAlarmpointsWellList, Boolean isUpdateSupport, String operName) { if (StringUtils.isNull(ardAlarmpointsWellList) || ardAlarmpointsWellList.size() == 0) { throw new ServiceException("导入井数据不能为空!"); } @@ -163,9 +169,9 @@ //获取当前登录用户id String userId = SecurityUtils.getUserId(); well.setUserId(userId); // 验证是否存在这个用户 ArdAlarmpointsWell u = ardAlarmpointsWellMapper.selectArdAlarmpointsWellByWellId(well.getWellId()); if (StringUtils.isNull(u)) { // 验证是否存在这个井 ArdAlarmpointsWell w = ardAlarmpointsWellMapper.selectArdAlarmpointsWellByWellId(well.getWellId()); if (StringUtils.isNull(w)) { BeanValidators.validateWithException(validator, well); well.setCreateBy(operName); this.insertArdAlarmpointsWell(well); @@ -173,10 +179,9 @@ successMsg.append("<br/>" + successNum + "、井号 " + well.getWellId() + " 导入成功"); } else if (isUpdateSupport) { BeanValidators.validateWithException(validator, well); checkWellAllowed(well); checkWellDataScope(well.getUserId()); well.setUpdateBy(operName); this.updateArdAlarmpointsWell(well); this.updateArdAlarmpointsWellByWellId(well); successNum++; successMsg.append("<br/>" + successNum + "、井号 " + well.getWellId() + " 更新成功"); } else { ard-work/src/main/java/com/ruoyi/device/camera/controller/ArdCamerasController.java
@@ -3,6 +3,7 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.ruoyi.alarmpoints.well.domain.ArdAlarmpointsWell; import com.ruoyi.common.constant.CameraConstants; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; @@ -27,6 +28,7 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.core.page.TableDataInfo; import org.springframework.web.multipart.MultipartFile; import java.util.*; @@ -137,6 +139,27 @@ return toAjax(ardCamerasService.deleteArdCamerasByIds(ids)); } @Log(title = "导入相机设备", businessType = BusinessType.IMPORT) @PreAuthorize("@ss.hasPermi('device:cameras:import')") @PostMapping("/importData") @ApiOperation("导入相机设备") public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { ExcelUtil<ArdCameras> util = new ExcelUtil<ArdCameras>(ArdCameras.class); List<ArdCameras> camerasList = util.importExcel(file.getInputStream()); String operName = getUsername(); String message = ardCamerasService.importCameras(camerasList, updateSupport, operName); return success(message); } @PostMapping("/importTemplate") @ApiOperation("相机设备导入模板") public void importTemplate(HttpServletResponse response) { ExcelUtil<ArdCameras> util = new ExcelUtil<ArdCameras>(ArdCameras.class); util.importTemplateExcel(response, "相机设备数据"); } @GetMapping("/options") @ApiOperation("选择相机数据") public List options(ArdCameras ardCameras) { ard-work/src/main/java/com/ruoyi/device/camera/service/IArdCamerasService.java
@@ -5,6 +5,7 @@ import java.util.Map; import java.util.TreeMap; import com.ruoyi.alarmpoints.well.domain.ArdAlarmpointsWell; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.scheduling.domian.SchedulingParam; @@ -71,6 +72,30 @@ */ public int deleteArdCamerasById(String id); /** * 导入相机设备信息 * * @param ardCamerasList 相机设备数据列表 * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 * @param operName 操作用户 * @return 结果 */ public String importCameras(List<ArdCameras> ardCamerasList, Boolean isUpdateSupport, String operName); /** * 校验相机是否允许操作 * * @param ardCameras 相机信息 */ public void checkCameraAllowed(ArdCameras ardCameras); /** * 校验用户是否有数据权限 * * @param userId 用户id */ public void checkCameraDataScope(String userId); public List findOptions(ArdCameras ardCameras); /** * @描述 获取本部门以下的所有相机和部门 ard-work/src/main/java/com/ruoyi/device/camera/service/impl/ArdCamerasServiceImpl.java
@@ -3,13 +3,18 @@ import java.util.*; import java.util.stream.Collectors; import com.ruoyi.alarmpoints.well.domain.ArdAlarmpointsWell; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.CameraConstants; import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.bean.BeanValidators; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.channel.domain.ArdChannel; @@ -25,10 +30,12 @@ import com.ruoyi.system.mapper.SysDeptMapper; import com.ruoyi.utils.gis.Point; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Validator; /** * 相机设备Service业务层处理 @@ -50,7 +57,8 @@ private ArdChannelMapper ardChannelMapper; @Resource private IVtduService vtduService; @Autowired protected Validator validator; @PostConstruct public void loadCameras() { @@ -200,6 +208,84 @@ return i; } @Override public String importCameras(List<ArdCameras> ardCamerasList, Boolean isUpdateSupport, String operName) { if (StringUtils.isNull(ardCamerasList) || ardCamerasList.size() == 0) { throw new ServiceException("导入井数据不能为空!"); } int successNum = 0; int failureNum = 0; StringBuilder successMsg = new StringBuilder(); StringBuilder failureMsg = new StringBuilder(); for (ArdCameras camera : ardCamerasList) { try { //获取当前登录用户id String userId = SecurityUtils.getUserId(); camera.setUserId(userId); // 验证是否存在这个用户 ArdCameras u = ardCamerasMapper.selectArdCamerasById(camera.getId()); if (StringUtils.isNull(u)) { BeanValidators.validateWithException(validator, camera); camera.setCreateBy(operName); this.insertArdCameras(camera); successNum++; successMsg.append("<br/>" + successNum + "、相机ID " + camera.getId() + " 导入成功"); } else if (isUpdateSupport) { BeanValidators.validateWithException(validator, camera); checkCameraDataScope(camera.getUserId()); camera.setUpdateBy(operName); this.updateArdCameras(camera); successNum++; successMsg.append("<br/>" + successNum + "、相机ID " + camera.getId() + " 更新成功"); } else { failureNum++; failureMsg.append("<br/>" + failureNum + "、相机ID " + camera.getId() + " 已存在"); } } catch (Exception e) { failureNum++; String msg = "<br/>" + failureNum + "、相机ID " + camera.getId() + " 导入失败:"; failureMsg.append(msg + e.getMessage()); log.error(msg, e); } } if (failureNum > 0) { failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); throw new ServiceException(failureMsg.toString()); } else { successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); } return successMsg.toString(); } /** * 校验相机是否允许操作 * * @param ardCameras 相机信息 */ @Override public void checkCameraAllowed(ArdCameras ardCameras) { if (StringUtils.isNotNull(ardCameras.getId())) { throw new ServiceException("不允许操作井"); } } /** * 校验用户是否有数据权限 * * @param userId 用户id */ @Override public void checkCameraDataScope(String userId) { if (!SysUser.isAdmin(SecurityUtils.getUserId())) { ArdCameras camera = new ArdCameras(); camera.setUserId(userId); List<ArdCameras> cameras = SpringUtils.getAopProxy(this).selectArdCamerasList(camera); if (StringUtils.isEmpty(cameras)) { throw new ServiceException("没有权限访问井数据!"); } } } public List findOptions(ArdCameras ardCameras) { List<ArdCameras> options = ardCamerasMapper.findOptions(ardCameras); for (ArdCameras camera : ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DhClientServiceImpl.java
@@ -896,7 +896,7 @@ // 设备断线回调: 当设备出现断线时,SDK会调用该函数 private static class DisConnect implements NetSDKLib.fDisConnect { public void invoke(LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) { log.warn("Device[" + pchDVRIP + "] Port[" + nDVRPort + "] DisConnect!"); log.warn("Device[" + pchDVRIP + ":" + nDVRPort + "] DisConnect!"); } } @@ -904,7 +904,7 @@ private static class HaveReConnect implements NetSDKLib.fHaveReConnect { @Override public void invoke(LLong lLoginID, String pchDVRIP, int nDVRPort, Pointer dwUser) { log.warn("ReConnect Device[" + pchDVRIP + "] Port[" + nDVRPort + "]"); log.warn("ReConnect Device[" + pchDVRIP + ":" + nDVRPort + "]"); } } ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/ExceptionCallBack.java
@@ -22,19 +22,19 @@ Integer port = camera.getPort(); switch (dwType) { case EXCEPTION_EXCHANGE: log.warn("Device[" + ip + "] Port[" + port + "]用户交互时异常"); log.warn("Device[" + ip + ":" + port + "]用户交互时异常"); break; case EXCEPTION_PREVIEW: log.warn("Device[" + ip + "] Port[" + port + "]网络预览异常"); log.warn("Device[" + ip + ":" + port + "]网络预览异常"); break; case EXCEPTION_RECONNECT: log.warn("Device[" + ip + "] Port[" + port + "]预览时重连"); log.warn("Device[" + ip + ":" + port + "]预览时重连"); break; case RELOGIN_SUCCESS: log.warn("Device[" + ip + "] Port[" + port + "]用户重登陆成功"); log.warn("Device[" + ip + ":" + port + "]用户重登陆成功"); break; case EXCEPTION_RELOGIN: log.warn("Device[" + ip + "] Port[" + port + "]用户重登陆"); log.warn("Device[" + ip + ":" + port + "]用户重登陆"); break; } } ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikClientServiceImpl.java
@@ -1094,7 +1094,7 @@ public boolean controlInfrarecfg(CameraCmd cmd) { String cameraId = cmd.getCameraId(); boolean enable = cmd.isEnable(); Integer channelNum = cmd.getChanNo(); Integer chanNo = cmd.getChanNo(); if (!GlobalVariable.loginMap.containsKey(cameraId)) { return false; } @@ -1102,17 +1102,17 @@ NET_DVR_CAMERAPARAMCFG_EX struDayNigh = new NET_DVR_CAMERAPARAMCFG_EX(); Pointer point = struDayNigh.getPointer(); IntByReference ibrBytesReturned = new IntByReference(0); boolean b_GetCameraParam = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_CCDPARAMCFG_EX, channelNum, point, struDayNigh.size(), ibrBytesReturned); boolean b_GetCameraParam = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_CCDPARAMCFG_EX, chanNo, point, struDayNigh.size(), ibrBytesReturned); if (!b_GetCameraParam) { log.error("获取前端参数失败,错误码:" + hCNetSDK.NET_DVR_GetLastError()); } struDayNigh.read(); log.debug("是否开启夜视:" + struDayNigh.struDayNight.byDayNightFilterType); String current = struDayNigh.struDayNight.byDayNightFilterType == 1 ? "开启" : "关闭"; log.debug("当前状态:" + current); NET_DVR_DAYNIGHT daynight = new NET_DVR_DAYNIGHT(); if (enable) { daynight.byDayNightFilterType = 1;//夜晚 } else { daynight.byDayNightFilterType = 0;//白天 } @@ -1120,12 +1120,15 @@ daynight.byDayNightFilterTime = 60; struDayNigh.struDayNight = daynight; struDayNigh.write(); boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_CCDPARAMCFG_EX, channelNum, point, struDayNigh.size()); boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_CCDPARAMCFG_EX, chanNo, point, struDayNigh.size()); if (!bool) { int code = hCNetSDK.NET_DVR_GetLastError(); log.error("设置夜视失败,请稍后重试" + code); } else { log.debug("设置夜视成功"); } return bool; } ard-work/src/main/resources/mapper/alarmpoints/ArdAlarmpointsWellMapper.xml
@@ -185,7 +185,35 @@ </trim> where id = #{id} </update> <update id="updateArdAlarmpointsWellByWellId" parameterType="ArdAlarmpointsWell"> update ard_alarmpoints_well <trim prefix="SET" suffixOverrides=","> <if test="wellId != null">well_id = #{wellId},</if> <if test="wellNumber != null">well_number = #{wellNumber},</if> <if test="oilProduction != null">oil_production = #{oilProduction},</if> <if test="wellBlock != null">well_block = #{wellBlock},</if> <if test="productionDate != null">production_date = #{productionDate},</if> <if test="displacementMode != null">displacement_mode = #{displacementMode},</if> <if test="surroundingEnvironment != null">surrounding_environment = #{surroundingEnvironment},</if> <if test="wellType != null">well_type = #{wellType},</if> <if test="installedLoad != null">installed_load = #{installedLoad},</if> <if test="meteringStation != null">metering_station = #{meteringStation},</if> <if test="transferStation != null">transfer_station = #{transferStation},</if> <if test="dehydrationStation != null">dehydration_station = #{dehydrationStation},</if> <if test="runStatus != null">run_status = #{runStatus},</if> <if test="longitude != null">longitude = #{longitude},</if> <if test="latitude != null">latitude = #{latitude},</if> <if test="altitude != null">altitude = #{altitude},</if> <if test="deptId != null">dept_id = #{deptId},</if> <if test="userId != null">user_id = #{userId},</if> <if test="createBy != null">create_by = #{createBy},</if> <if test="createTime != null">create_time = #{createTime},</if> <if test="updateBy != null">update_by = #{updateBy},</if> <if test="updateTime != null">update_time = #{updateTime},</if> <if test="cameraId != null">camera_id = #{cameraId},</if> </trim> where well_id = #{wellId} </update> <delete id="deleteArdAlarmpointsWellById" parameterType="String"> delete from ard_alarmpoints_well ard-work/src/main/resources/templates/test.html
@@ -4,28 +4,33 @@ <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"> <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-4 col-md-offset-3"> <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> @@ -39,7 +44,7 @@ <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="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> @@ -50,7 +55,7 @@ <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="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> @@ -58,7 +63,7 @@ </div> </div> <div class="row"> <div class="col-md-6"> <div class="col-md-10"> <div class="row top-buffer"> <div class="input-group"> <span class="input-group-addon">目的坐标值:</span> @@ -84,7 +89,6 @@ <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> @@ -112,25 +116,27 @@ <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> </div> </div> <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"> <div class="row top-buffer"> <video id="video" muted autoplay loop controls style="width: 800px; height: 100%; object-fit: fill;"/> <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> <script th:inline="javascript" th:type="module"> </div> <script th:inline="javascript"> var cameraId, opt, optOpen, optClose, token; window.onload = function () { @@ -175,7 +181,6 @@ dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data); token = data.token; } }) @@ -656,7 +661,8 @@ $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/cameraDeicing", type: "post", @@ -790,67 +796,264 @@ }) } 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); $.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"; } //预览大华相机 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); return ret; }) : [] ); const parseOffer = (offer) => { const ret = { iceUfrag: '', icePwd: '', medias: [], }; webRtcServer.connect(rtspUrl, null, option, null); videoMap.set(elem, webRtcServer); 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); } } //预览宇视相机 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); 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); } function closeVideo(id, webrtc) { webrtc.disconnect(); videoMap.delete(id); 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++; } //页面退出时销毁 window.onbeforeunload = function () { webRtcServer.disconnect(); 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>