ard-work/src/main/java/com/ruoyi/device/camera/domain/ArdCameras.java
@@ -178,7 +178,7 @@ /** * ç»å½ID */ private Integer loginId; private Long loginId; /** * å¨çº¿ç¶æ 0-离线 1-å¨çº¿ */ @@ -192,7 +192,7 @@ /** * èµ·å§ééå· */ private Integer startDChan; private Integer startChan; /** * ééæ° */ ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java
@@ -4,6 +4,7 @@ import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.channel.domain.ArdChannel; import com.ruoyi.media.domain.Vtdu; import javax.servlet.http.HttpServletResponse; import java.util.List; @@ -116,4 +117,5 @@ //æ¬å°å½å忢 AjaxResult localRecordStop(CameraCmd cmd); } ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java
@@ -1,6 +1,8 @@ package com.ruoyi.device.channel.service; import java.util.List; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.channel.domain.ArdChannel; /** @@ -65,22 +67,6 @@ * @return ç»æ */ public int deleteArdChannelByDeviceId(String deviceId); /** * @Author åèä¹ * @Description è·å2个ééå表ç交é * @Date 2024/7/10 9:38 * @Param * @return */ public List<ArdChannel> sameList(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList); /** * @Author åèä¹ * @Description å2个ééå表çå·®é * @Date 2024/7/10 9:39 * @Param * @return */ public List<ArdChannel> diffList(List<ArdChannel> firstArrayList, List<ArdChannel> secondArrayList); /** * @Author åèä¹ @@ -89,5 +75,5 @@ * @Param * @return */ public void asyncChannel(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList); public void asyncChannel(ArdCameras ardCameras, List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList); } ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java
@@ -1,9 +1,16 @@ package com.ruoyi.device.channel.service.impl; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.factory.CameraSDK; import com.ruoyi.device.camera.factory.CameraSDKFactory; import com.ruoyi.device.camera.mapper.ArdCamerasMapper; import com.ruoyi.media.mapper.VtduMapper; import com.ruoyi.media.service.IVtduService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.device.channel.mapper.ArdChannelMapper; @@ -22,7 +29,10 @@ public class ArdChannelServiceImpl implements IArdChannelService { @Resource private ArdChannelMapper ardChannelMapper; @Resource private CameraSDKFactory cameraSDKFactory; @Resource private IVtduService vtduService; /** * æ¥è¯¢éé管ç * @@ -101,38 +111,40 @@ return ardChannelMapper.deleteArdChannelByDeviceId(deviceId); } //æ±ä¸¤ä¸ªå¯¹è±¡Listç交é @Override public List<ArdChannel> sameList(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) { List<ArdChannel> resultList = newArrayList.stream() .filter(item -> oldArrayList.stream().map(e -> e.getChanNo()) .collect(Collectors.toList()).contains(item.getChanNo())) .collect(Collectors.toList()); return resultList; } public void asyncChannel(ArdCameras ardCameras,List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) { // å°å表转æ¢ä¸ºæ å°ä»¥æé«æ§è½ Map<Integer, ArdChannel> oldMap = oldArrayList.stream() .collect(Collectors.toMap(ArdChannel::getChanNo, channel -> channel)); Map<Integer, ArdChannel> newMap = newArrayList.stream() .collect(Collectors.toMap(ArdChannel::getChanNo, channel -> channel)); //æ±ä¸¤ä¸ªå¯¹è±¡Listçå·®é @Override public List<ArdChannel> diffList(List<ArdChannel> firstArrayList, List<ArdChannel> secondArrayList) { List<ArdChannel> resultList = firstArrayList.stream() .filter(item -> !secondArrayList.stream().map(e -> e.getChanNo()).collect(Collectors.toList()).contains(item.getChanNo())) .collect(Collectors.toList()); return resultList; } @Override public void asyncChannel(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) { //éè¦æ´æ°çæ°æ®,åæ°é¡ºåºæ³¨æ sameList(oldArrayList, newArrayList).stream().forEach(ardChannel -> { updateArdChannel(ardChannel); // éè¦æ´æ°çæ°æ® newArrayList.stream() .filter(channel -> { ArdChannel oldChannel = oldMap.get(channel.getChanNo()); return oldChannel != null && !oldChannel.getName().equals(channel.getName()); }) .forEach(channel -> { ArdChannel oldChannel = oldMap.get(channel.getChanNo()); channel.setId(oldChannel.getId()); updateArdChannel(channel); }); //éè¦å é¤çæ°æ® diffList(oldArrayList, newArrayList).stream().forEach(ardChannel -> { deleteArdChannelById(ardChannel.getId()); oldArrayList.stream() .filter(channel -> !newMap.containsKey(channel.getChanNo())) .forEach(channel -> { deleteArdChannelById(channel.getId()); vtduService.deleteVtduByName(channel.getDeviceId() + "_" + channel.getChanNo()); }); //éè¦æ°å¢çæ°æ® diffList(newArrayList, oldArrayList).stream().forEach(ardChannel -> { insertArdChannel(ardChannel); newArrayList.stream() .filter(channel -> !oldMap.containsKey(channel.getChanNo())) .forEach(channel -> { insertArdChannel(channel); vtduService.addChanToVtdu(ardCameras, channel); }); } } ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
@@ -4,10 +4,12 @@ import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.media.domain.Config; import com.ruoyi.media.domain.Paths; import com.ruoyi.media.domain.StreamInfo; import com.ruoyi.media.domain.Vtdu; import com.ruoyi.media.service.IMediaService; @@ -112,7 +114,7 @@ @PreAuthorize("@ss.hasPermi('media:stream:remove')") @DeleteMapping("/{sessionId}") public AjaxResult removePullStreamSession(@PathVariable String sessionId) { List<StreamInfo> pullStreamList = mediaService.getPullStreamList(); List<StreamInfo> pullStreamList = mediaService.getPullStreamList(1,1000); StreamInfo streamInfo = pullStreamList.stream() .filter(object -> object.getId().equals(sessionId)) .collect(Collectors.toList()).get(0); @@ -143,9 +145,9 @@ @GetMapping("/path/list") @ApiOperation("è·åå½åééå表") @ApiOperationSupport(order = 5) public TableDataInfo getPaths() { public TableDataInfo getPaths(Integer pageNum,Integer pageSize) { startPage(); return getDataTable(mediaService.paths()); return getDataTable(mediaService.paths(pageNum,pageSize)); } /** @@ -154,7 +156,7 @@ @GetMapping("/getRtspSessionById") @ApiOperation("æIDæ¥è¯¢ææµè¯¦æ ") public AjaxResult getRtspSessionById(String sessionId) { List<StreamInfo> pullStreamList = mediaService.getPullStreamList(); List<StreamInfo> pullStreamList = mediaService.getPullStreamList(1,1000); StreamInfo streamInfo = pullStreamList.stream() .filter(object -> object.getId().equals(sessionId)) .collect(Collectors.toList()).get(0); @@ -176,9 +178,9 @@ @GetMapping("/pushList") @ApiOperation("è·åæ¨æµå表") @ApiOperationSupport(order = 6) public TableDataInfo getPushStreamList() { public TableDataInfo getPushStreamList(Integer pageNum,Integer pageSize) { startPage(); return getDataTable(mediaService.getPushStreamList()); return getDataTable(mediaService.getPushStreamList(pageNum,pageSize)); } /** @@ -188,9 +190,9 @@ @GetMapping("/pullList") @ApiOperation("è·åææµå表") @ApiOperationSupport(order = 7) public TableDataInfo getPullStreamList() { public TableDataInfo getPullStreamList(Integer pageNum,Integer pageSize) { startPage(); return getDataTable(mediaService.getPullStreamList()); return getDataTable(mediaService.getPullStreamList(pageNum,pageSize)); } } ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
@@ -33,7 +33,7 @@ void removePath(String name); List<StreamInfo> paths(); List<StreamInfo> paths(Integer pageNum,Integer pageSize); List<String> getNameList(); @@ -45,9 +45,9 @@ RtmpSession getRtmpSessionById(String sessionId); List<StreamInfo> getPushStreamList(); List<StreamInfo> getPushStreamList(Integer pageNum,Integer pageSize); List<StreamInfo> getPullStreamList(); List<StreamInfo> getPullStreamList(Integer pageNum,Integer pageSize); Boolean kickRtspSession(String sessionId); ard-work/src/main/java/com/ruoyi/media/service/IMediaV2Service.java
ÎļþÒÑɾ³ý ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java
@@ -2,6 +2,7 @@ import java.util.List; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.channel.domain.ArdChannel; import com.ruoyi.media.domain.Vtdu; @@ -103,4 +104,7 @@ * @Param */ public void asyncVtdu(List<Vtdu> vtdus, List<String> names); //æ·»å ééè³æµåªä½ void addChanToVtdu(ArdCameras camera, ArdChannel channel); } ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
@@ -8,6 +8,7 @@ import com.ruoyi.media.service.IMediaService; import com.ruoyi.utils.forest.MediaClient; import com.ruoyi.utils.tools.ArdTool; import io.swagger.models.auth.In; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; @@ -146,6 +147,8 @@ if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); info.setMode("0"); } else { info.setMode("1"); } //RTSPæºå°å Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); @@ -184,8 +187,8 @@ } @Override public List<StreamInfo> paths() { Paths paths = mediaClient.paths(); public List<StreamInfo> paths(Integer pageNum, Integer pageSize) { Paths paths = mediaClient.paths(pageNum - 1, pageSize); List<Items> items = paths.getItems(); List<StreamInfo> pathInfoList = new ArrayList<>(); for (Items item : items) { @@ -221,23 +224,17 @@ @Override public RtspSession getRtspSessionById(String sessionId) { String list = mediaClient.getRtspsessionById(sessionId); RtspSession rtspSession = JSONObject.parseObject(list, RtspSession.class); return rtspSession; return mediaClient.getRtspsessionById(sessionId); } @Override public WebrtcSession getWebrtcSessionById(String sessionId) { String list = mediaClient.getWebrtcsessionById(sessionId); WebrtcSession webrtcSession = JSONObject.parseObject(list, WebrtcSession.class); return webrtcSession; return mediaClient.getWebrtcsessionById(sessionId); } @Override public RtmpSession getRtmpSessionById(String sessionId) { String list = mediaClient.getRtmpsessionById(sessionId); RtmpSession rtmpSession = JSONObject.parseObject(list, RtmpSession.class); return rtmpSession; return mediaClient.getRtmpsessionById(sessionId); } /** @@ -246,9 +243,9 @@ * 2023/8/29 9:37:05 */ @Override public List<StreamInfo> getPushStreamList() { public List<StreamInfo> getPushStreamList(Integer pageNum, Integer pageSize) { List<StreamInfo> PushStreamInfoList = new ArrayList<>(); Paths paths = mediaClient.paths(); Paths paths = mediaClient.paths(pageNum - 1, pageSize); List<Items> items = paths.getItems(); for (Items item : items) { StreamInfo info = new StreamInfo(); @@ -321,9 +318,9 @@ * 2023/8/29 9:37:05 */ @Override public List<StreamInfo> getPullStreamList() { public List<StreamInfo> getPullStreamList(Integer pageNum, Integer pageSize) { List<StreamInfo> PullStreamInfoList = new ArrayList<>(); Paths paths = mediaClient.paths(); Paths paths = mediaClient.paths(pageNum - 1, pageSize); List<Items> items = paths.getItems(); for (Items item : items) { List<Readers> readers = item.getReaders(); @@ -467,7 +464,7 @@ public List<String> getNameList() { List<String> nameList = new ArrayList<>(); try { Paths paths = mediaClient.paths(); Paths paths = mediaClient.paths(0, 1000); List<Items> items = paths.getItems(); for (Items item : items) { nameList.add(item.getName()); ard-work/src/main/java/com/ruoyi/media/service/impl/MediaV2ServiceImpl.java
ÎļþÒÑɾ³ý ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java
@@ -6,9 +6,15 @@ import java.util.stream.Collectors; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.camera.factory.CameraSDK; import com.ruoyi.device.camera.factory.CameraSDKFactory; import com.ruoyi.device.channel.domain.ArdChannel; import com.ruoyi.media.domain.StreamInfo; import com.ruoyi.media.service.IMediaService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.ruoyi.media.mapper.VtduMapper; import com.ruoyi.media.domain.Vtdu; @@ -30,7 +36,10 @@ private VtduMapper vtduMapper; @Resource private IMediaService mediaService; @Resource private CameraSDKFactory cameraSDKFactory; @Value("${mediamtx.host}") String mediamtxHost; /** * æ¥è¯¢æµåªä½ç®¡ç * @@ -39,7 +48,6 @@ */ @Override public Vtdu selectVtduByName(String name) { return vtduMapper.selectVtduByName(name); } @@ -62,17 +70,6 @@ */ @Override public int insertVtdu(Vtdu vtdu) { log.debug("æµåªä½ã" + vtdu.getName() + "ãééæ·»å "); Map<String, String> map = mediaService.addPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode()); vtdu.setRtspUrl(map.get("rtspUrl")); vtdu.setRtmpUrl(map.get("rtmpUrl")); vtdu.setWebrtcUrl(map.get("webrtcUrl")); vtdu.setName(vtdu.getName()); vtdu.setRtspSource(vtdu.getRtspSource()); vtdu.setIsCode(vtdu.getIsCode()); vtdu.setMode(vtdu.getMode()); vtdu.setCreateTime(DateUtils.getNowDate()); return vtduMapper.insertVtdu(vtdu); } @@ -84,7 +81,6 @@ */ @Override public int updateVtdu(Vtdu vtdu) { log.debug("æµåªä½ã" + vtdu.getName() + "ãééæ´æ°"); Map<String, String> map = mediaService.editPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode()); vtdu.setName(vtdu.getName()); vtdu.setRtspSource(vtdu.getRtspSource()); @@ -171,12 +167,10 @@ .collect(Collectors.toList()); return nameList; } else { List<String> nameList = names.stream().filter(item -> !vtdus.stream().map(Vtdu::getName).collect(Collectors.toList()).contains(item)) .collect(Collectors.toList()); return nameList; } } //éè¦æ°å¢çï¼æµåªä½ä¸å°çï¼ @@ -186,18 +180,21 @@ } /** * @return * @Author åèä¹ * @Description 忥æ¬å°vtduåºåæµåªä½ä¸çæ°æ® * @Date 2024/7/10 15:26 * @Param vtdus vtduåºçéå * @Param names æµåªä½çnameéå * @return */ @Override public void asyncVtdu(List<Vtdu> vtdus, List<String> names) { //éè¦æ´æ°çæ°æ®,åæ°é¡ºåºæ³¨æ sameList(vtdus, names).stream().forEach(vtdu -> { StreamInfo streamInfo = mediaService.getPathInfo(vtdu.getName()); if (!streamInfo.getRtspSource().equals(vtdu.getRtspSource())) { mediaService.editPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode()); } }); //éè¦å é¤çæ°æ® diffListToDel(vtdus, names).stream().forEach(name -> { @@ -208,4 +205,45 @@ mediaService.addPath(vtdu.getName(), vtdu.getRtspSource(), vtdu.getMode(), vtdu.getIsCode()); }); } @Override public void addChanToVtdu(ArdCameras camera, ArdChannel channel) { String name = camera.getId() + "_" + channel.getChanNo(); String rtspSource=""; switch (camera.getFactory()) { case "1": rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";break; case "2": rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";break; } //å 餿µåªä½ if (selectVtduByName(name) != null) { deleteVtduByName(name); } //æ·»å å°æµåªä½ CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo()); CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(camera.getFactory()); Map<String, Object> videoCompressionCfg = cameraSDK.getVideoCompressionCfg(cmd); Vtdu vtdu = new Vtdu(); if (videoCompressionCfg.get("videoEncType") != null) { if (videoCompressionCfg.get("videoEncType").equals("æ åh264")) { vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç } else { vtdu.setIsCode("1");//é»è®¤è½¬ç } } else { vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç } vtdu.setRtspSource(rtspSource); vtdu.setName(name); vtdu.setMode("1");//é»è®¤CPU软解ç vtdu.setCameraId(camera.getId()); String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name; String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; vtdu.setRtmpUrl(rtmpUrl); vtdu.setWebrtcUrl(webrtcUrl); vtdu.setRtspUrl(rtspUrl); insertVtdu(vtdu); } } ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
@@ -1,10 +1,7 @@ package com.ruoyi.utils.forest; import com.dtflys.forest.annotation.*; import com.ruoyi.media.domain.Conf; import com.ruoyi.media.domain.Config; import com.ruoyi.media.domain.Items; import com.ruoyi.media.domain.Paths; import com.ruoyi.media.domain.*; import java.nio.file.Path; @@ -44,8 +41,8 @@ /** * æ¥è¯¢ææè·¯å¾ */ @Get("/paths/list") public Paths paths(); @Get("/paths/list?page={pageNum}&itemsPerPage={pageSize}") public Paths paths(@Var("pageNum") Integer pageNum, @Var("pageSize") Integer pageSize); /** * æ¥è¯¢æærtspä¼è¯ @@ -57,19 +54,19 @@ * æsessionIdæ¥è¯¢rtspä¼è¯ */ @Get("/rtspsessions/get/{sessionId}") public String getRtspsessionById(@Var("sessionId") String sessionId); public RtspSession getRtspsessionById(@Var("sessionId") String sessionId); /** * æsessionIdæ¥è¯¢webrtcä¼è¯ */ @Get("/webrtcsessions/get/{sessionId}") public String getWebrtcsessionById(@Var("sessionId") String sessionId); public WebrtcSession getWebrtcsessionById(@Var("sessionId") String sessionId); /** * æsessionIdæ¥è¯¢rtmpä¼è¯ */ @Get("/rtmpconns/get/{sessionId}") public String getRtmpsessionById(@Var("sessionId") String sessionId); public RtmpSession getRtmpsessionById(@Var("sessionId") String sessionId); /** * æsessionIdå é¤rtspä¼è¯ @@ -89,9 +86,4 @@ @Post("/webrtcsessions/kick/{sessionId}") public String kickWebrtcSessions(@Var("sessionId") String sessionId); /** * é ç½®æµåªä½åæ° */ @Post("/config/set") public String setConfig(@JSONBody Config config); } ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/module/ConfigModule.java
@@ -81,10 +81,12 @@ return result; } //æ¥è¯¢ééåç§° public static String getChannelName(NetSDKLib.LLong hLoginHandle,Integer channel) { String channelName = ""; NetSDKLib.AV_CFG_ChannelName channelTitleName = new NetSDKLib.AV_CFG_ChannelName(); if (ToolKits.GetDevConfig(hLoginHandle, channel, NetSDKLib.CFG_CMD_CHANNELTITLE, channelTitleName)) { if (ToolKits.GetDevConfig(hLoginHandle, channel - 1, NetSDKLib.CFG_CMD_CHANNELTITLE, channelTitleName)) { try { channelName = new String(channelTitleName.szName, "GBK"); } catch (Exception e) { @@ -95,6 +97,7 @@ } return channelName; } public static boolean GetDevConfig(NetSDKLib.LLong hLoginHandle, int nChn, String strCmd, Structure cmdObject) { boolean result = true; IntByReference error = new IntByReference(0); @@ -111,6 +114,7 @@ return result; } //æ¥è¯¢ç¸æºç¶æ public static boolean queryCameraState(NetSDKLib.LLong hLoginHandle, Integer chanNum, Integer chanNo) { boolean bRet = false; try { @@ -141,9 +145,9 @@ stOut.read(); ToolKits.GetPointerDataToStructArr(stOut.pCameraStateInfo, arrCameraStatus); // å°Pointeræ·è´å°æ°ç»å å final String[] connectionState = {"æªç¥", "æ£å¨è¿æ¥", "å·²è¿æ¥", "æªè¿æ¥", "ééæªé ç½®,æ ä¿¡æ¯", "ééæé ç½®,ä½è¢«ç¦ç¨"}; log.debug(connectionState[arrCameraStatus[chanNo - 1].emConnectionState]); if (connectionState[arrCameraStatus[chanNo - 1].emConnectionState].equals("å·²è¿æ¥")) { log.debug("éé" + arrCameraStatus[chanNo - 1].nChannel + connectionState[arrCameraStatus[chanNo - 1].emConnectionState]); //log.debug("ééï¼" + arrCameraStatus[chanNo - 1].nChannel + "ç¶æï¼" + connectionState[arrCameraStatus[chanNo - 1].emConnectionState]); bRet = true; } else { bRet = false; @@ -205,6 +209,5 @@ return result; } } ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java
@@ -104,7 +104,7 @@ NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); if (loginId.longValue() <= 0) { camera.setChanNum(0); camera.setLoginId(-1); camera.setLoginId(-1l); camera.setState("0"); ardCamerasService.updateArdCameras(camera); //å é¤ç®¡çéé @@ -112,11 +112,11 @@ log.error("设å¤[" + camera.getIp() + ":" + camera.getPort() + "]ç»å½å¤±è´¥:" + getErrorCodePrint()); return AjaxResult.warn(ErrorCode.getErrorCode(LoginModule.netsdk.CLIENT_GetLastError())); } log.debug("设å¤[" + camera.getIp() + ":" + camera.getPort() + "]ç»å½æå:" + (int) loginId.longValue()); camera.setState("1"); camera.setChanNum(m_stDeviceInfo.byChanNum); camera.setStartDChan(1); camera.setLoginId((int) loginId.longValue()); camera.setStartChan(1); camera.setLoginId(loginId.longValue()); GlobalVariable.loginMap.put(camera.getId(), loginId); //è·åææ°éé List<ArdChannel> ardChannelList = getChannels(camera); @@ -125,12 +125,11 @@ ardChannelList.stream().forEach(channel -> { ardChannelService.insertArdChannel(channel); }); camera.setChanNum(ardChannelList.size()); camera.setChannelList(ardChannelList); ardCamerasService.updateArdCameras(camera); //é ç½®å°æµåªä½ addVtdu(camera); //é鿹鿷»å å°æµåªä½ batchAddVtdu(camera); } ardCamerasService.updateArdCameras(camera); //å建å¼å¯¼éå createGuideQueue(camera); return AjaxResult.success("设å¤ç»å½æå"); @@ -149,7 +148,7 @@ NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); if (loginId.longValue() <= 0) { camera.setChanNum(0); camera.setLoginId(-1); camera.setLoginId(-1l); camera.setState("0"); ardCamerasService.updateArdCameras(camera); //å é¤ç®¡çéé @@ -157,10 +156,11 @@ log.error("设å¤[" + camera.getIp() + ":" + camera.getPort() + "]ç»å½å¤±è´¥:" + getErrorCodePrint()); return AjaxResult.warn(getErrorCodePrint()); } log.debug("设å¤[" + camera.getIp() + ":" + camera.getPort() + "]ç»å½æå:" + loginId); camera.setState("1"); camera.setChanNum(m_stDeviceInfo.byChanNum); camera.setStartDChan(1); camera.setLoginId((int) loginId.longValue()); camera.setStartChan(1); camera.setLoginId((Long) loginId.longValue()); ardCamerasService.updateArdCameras(camera); GlobalVariable.loginMap.put(camera.getId(), loginId); @@ -171,12 +171,11 @@ ardChannelList.stream().forEach(channel -> { ardChannelService.insertArdChannel(channel); }); camera.setChanNum(ardChannelList.size()); camera.setChannelList(ardChannelList); ardCamerasService.updateArdCameras(camera); //é ç½®å°æµåªä½ addVtdu(camera); //é鿹鿷»å å°æµåªä½ batchAddVtdu(camera); } ardCamerasService.updateArdCameras(camera); //å建å¼å¯¼éå createGuideQueue(camera); return AjaxResult.success("ç»å½æå"); @@ -186,35 +185,13 @@ } } //æ·»å å°æµåªä½ private void addVtdu(ArdCameras camera) { //é鿹鿷»å å°æµåªä½ public void batchAddVtdu(ArdCameras camera) { camera.getChannelList().stream().forEach(channel -> { String name = camera.getId() + "_" + channel.getChanNo(); String rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0"; //å 餿µåªä½ if (vtduService.selectVtduByName(name) != null) { vtduService.deleteVtduByName(name); } //æ·»å å°æµåªä½ CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo()); Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd); Vtdu vtdu = new Vtdu(); if (videoCompressionCfg.get("videoEncType") != null) { if (videoCompressionCfg.get("videoEncType").equals("æ åh264")) { vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç } else { vtdu.setIsCode("1");//é»è®¤è½¬ç } } else { vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç } vtdu.setRtspSource(rtspSource); vtdu.setName(name); vtdu.setMode("1");//é»è®¤CPU软解ç vtdu.setCameraId(camera.getId()); vtduService.insertVtdu(vtdu); vtduService.addChanToVtdu(camera, channel); }); } //å建å¼å¯¼éå private void createGuideQueue(ArdCameras camera) { @@ -231,12 +208,14 @@ //è·åéé public List<ArdChannel> getChannels(ArdCameras camera) { if (camera.getLoginId().equals(-1)) { return new ArrayList<>(); } LLong loginId = new LLong(camera.getLoginId()); List<ArdChannel> ardChannelList = new ArrayList<>(); for (int i = 1; i < camera.getChanNum() + 1; i++) { ArdChannel channel = new ArdChannel(); String chanName = ConfigModule.getChannelName(loginId, i - 1).trim(); log.debug("è·åééåç§°ï¼" + chanName); String chanName = ConfigModule.getChannelName(loginId, i).trim(); channel.setName(chanName.equals("") ? "éé" + i : chanName); channel.setDeviceId(camera.getId()); channel.setChanNo(i); ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/lib/LoginResultCallBack.java
@@ -48,10 +48,10 @@ if (dwResult == 1) { GlobalVariable.loginMap.put(camera.getId(), lUserID); log.debug(camera.getIp() + ":" + camera.getPort() + "ç»å½æå"); camera.setLoginId(lUserID); camera.setLoginId((long)lUserID); camera.setState("1"); camera.setChanNum((int) lpDeviceinfo.byChanNum); camera.setStartDChan((int) lpDeviceinfo.byStartDChan); camera.setStartChan((int) lpDeviceinfo.byStartDChan); //è·åææ°éé List<ArdChannel> cameraChannelList = hikClientService.getChannels(camera); if (cameraChannelList.size() > 0) { @@ -89,7 +89,7 @@ } else { log.debug(camera.getIp() + ":" + camera.getPort() + "ç»å½å¤±è´¥"); camera.setChanNum(0); camera.setLoginId(-1); camera.setLoginId(-1l); camera.setState("0"); } ardCamerasService.updateArdCameras(camera); ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java
@@ -125,7 +125,7 @@ if (lUserID < 0) { int errorCode = hCNetSDK.NET_DVR_GetLastError(); camera.setChanNum(0); camera.setLoginId(-1); camera.setLoginId(-1l); camera.setState("0"); //å é¤ç®¡çéé ardChannelService.deleteArdChannelByDeviceId(camera.getId()); @@ -146,7 +146,7 @@ } GlobalVariable.loginMap.put(camera.getId(), lUserID); GlobalVariable.loginCameraMap.put(lUserID, camera); camera.setLoginId(lUserID); camera.setLoginId((long)lUserID); camera.setState("1"); int chanNum = m_strDeviceInfo.struDeviceV30.byChanNum; int startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan + 1; @@ -155,7 +155,7 @@ startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan; } camera.setChanNum(chanNum); camera.setStartDChan(startDchan); camera.setStartChan(startDchan); //è·åææ°éé List<ArdChannel> cameraChannelList = getChannels(camera); if (cameraChannelList.size() > 0) { @@ -164,11 +164,11 @@ ardChannelService.insertArdChannel(channel); }); camera.setChannelList(cameraChannelList); camera.setChanNum(cameraChannelList.size()); ardCamerasService.updateArdCameras(camera); //æ·»å å°æµåªä½ addVtdu(camera); //camera.setChanNum(cameraChannelList.size()); //é鿹鿷»å å°æµåªä½ batchAddVtdu(camera); } ardCamerasService.updateArdCameras(camera); //å建å¼å¯¼éå createGuideQueue(camera); return AjaxResult.success("设å¤ç»å½æå"); @@ -231,7 +231,7 @@ if (lUserID < 0) { int errorCode = hCNetSDK.NET_DVR_GetLastError(); camera.setChanNum(0); camera.setLoginId(-1); camera.setLoginId(-1l); camera.setState("0"); //å é¤ç®¡çéé ardChannelService.deleteArdChannelByDeviceId(camera.getId()); @@ -254,7 +254,7 @@ GlobalVariable.loginMap.put(camera.getId(), lUserID); GlobalVariable.loginCameraMap.put(lUserID, camera); camera.setLoginId(lUserID); camera.setLoginId((long)lUserID); camera.setState("1"); int chanNum = m_strDeviceInfo.struDeviceV30.byChanNum; int startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan + 1; @@ -263,7 +263,7 @@ startDchan = m_strDeviceInfo.struDeviceV30.byStartDChan; } camera.setChanNum(chanNum); camera.setStartDChan(startDchan); camera.setStartChan(startDchan); //è·åææ°éé List<ArdChannel> cameraChannelList = getChannels(camera); if (cameraChannelList.size() > 0) { @@ -272,11 +272,11 @@ ardChannelService.insertArdChannel(channel); }); camera.setChannelList(cameraChannelList); camera.setChanNum(cameraChannelList.size()); ardCamerasService.updateArdCameras(camera); //æ·»å å°æµåªä½ addVtdu(camera); //camera.setChanNum(cameraChannelList.size()); //é鿹鿷»å å°æµåªä½ batchAddVtdu(camera); } ardCamerasService.updateArdCameras(camera); //å建å¼å¯¼éå createGuideQueue(camera); return AjaxResult.success("设å¤ç»å½æå"); @@ -299,36 +299,19 @@ } } //æ·»å å°æµåªä½ private void addVtdu(ArdCameras camera) { //é鿹鿷»å å°æµåªä½ public void batchAddVtdu(ArdCameras camera) { try { camera.getChannelList().stream().forEach(channel->{ String name = camera.getId() + "_" + channel.getChanNo(); String rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream"; //å 餿µåªä½ if (vtduService.selectVtduByName(name) != null) { vtduService.deleteVtduByName(name); } //æ·»å å°æµåªä½ Vtdu vtdu = new Vtdu(); vtdu.setRtspSource(rtspSource); vtdu.setName(name); CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo()); Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd); if (videoCompressionCfg.get("videoEncType").equals("æ åh264")) { vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç } else { vtdu.setIsCode("1");//é»è®¤è½¬ç } vtdu.setMode("1");//é»è®¤CPU软解ç vtdu.setCameraId(camera.getId()); vtduService.insertVtdu(vtdu); vtduService.addChanToVtdu(camera, channel); }); } catch (Exception ex) { log.error("ééæ·»å å°æµåªä½å¼å¸¸ï¼" + ex.getMessage()); } } /** * @æè¿° 注éç»å½ @@ -1848,6 +1831,9 @@ //è·åIPéé public List<ArdChannel> getChannels(ArdCameras camera) { if (camera.getLoginId().equals(-1)) { return new ArrayList<>(); } //è·åéé List<ArdChannel> channelList = new ArrayList<>(); try { @@ -1856,19 +1842,19 @@ m_strIpparaCfg.write(); //lpIpParaConfig æ¥æ¶æ°æ®çç¼å²æé Pointer lpIpParaConfig = m_strIpparaCfg.getPointer(); boolean bRet = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId(), HCNetSDK.NET_DVR_GET_IPPARACFG_V40, 0, lpIpParaConfig, m_strIpparaCfg.size(), ibrBytesReturned); boolean bRet = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId().intValue(), HCNetSDK.NET_DVR_GET_IPPARACFG_V40, 0, lpIpParaConfig, m_strIpparaCfg.size(), ibrBytesReturned); m_strIpparaCfg.read(); //log.debug("èµ·å§æ°åééå·ï¼" + m_strIpparaCfg.dwStartDChan);//m_strIpparaCfg.dwDChanNum for (int iChannum = 0; iChannum < camera.getChanNum(); iChannum++) { ArdChannel channel = new ArdChannel(); int chanNo = iChannum + camera.getStartDChan(); int chanNo = iChannum + camera.getStartChan(); HCNetSDK.NET_DVR_PICCFG_V40 strPicCfg = new HCNetSDK.NET_DVR_PICCFG_V40(); strPicCfg.dwSize = strPicCfg.size(); strPicCfg.write(); Pointer pStrPicCfg = strPicCfg.getPointer(); NativeLong lChannel = new NativeLong(chanNo); IntByReference pInt = new IntByReference(0); boolean b_GetPicCfg = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId(), HCNetSDK.NET_DVR_GET_PICCFG_V40, lChannel.intValue(), pStrPicCfg, strPicCfg.size(), pInt); boolean b_GetPicCfg = hCNetSDK.NET_DVR_GetDVRConfig(camera.getLoginId().intValue(), HCNetSDK.NET_DVR_GET_PICCFG_V40, lChannel.intValue(), pStrPicCfg, strPicCfg.size(), pInt); if (!b_GetPicCfg) { // log.error("è·åå¾ååæ°å¤±è´¥ï¼é误ç ï¼" + hCNetSDK.NET_DVR_GetLastError()); } ard-work/src/main/resources/mapper/device/ArdCamerasMapper.xml
@@ -15,6 +15,7 @@ <result property="gdtype" column="gdtype"/> <result property="factory" column="factory"/> <result property="chanNum" column="channel"/> <result property="startChan" column="start_chan"/> <result property="longitude" column="longitude"/> <result property="latitude" column="latitude"/> <result property="altitude" column="altitude"/> @@ -48,6 +49,7 @@ c.gdtype, c.factory, c.channel, c.start_chan, c.longitude, c.latitude, c.altitude, @@ -86,6 +88,7 @@ <if test="gdtype != null and gdtype != ''">and c.gdtype = #{gdtype}</if> <if test="factory != null and factory != ''">and c.factory = #{factory}</if> <if test="chanNum != null ">and c.channel = #{chanNum}</if> <if test="startChan != null ">and c.start_chan = #{startChan}</if> <if test="longitude != null ">and c.longitude = #{longitude}</if> <if test="latitude != null ">and c.latitude = #{latitude}</if> <if test="altitude != null ">and c.altitude = #{altitude}</if> @@ -123,6 +126,7 @@ <if test="gdtype != null and gdtype != ''">and c.gdtype = #{gdtype}</if> <if test="factory != null and factory != ''">and c.factory = #{factory}</if> <if test="chanNum != null ">and c.channel = #{chanNum}</if> <if test="startChan != null ">and c.start_chan= #{startChan}</if> <if test="longitude != null ">and c.longitude = #{longitude}</if> <if test="latitude != null ">and c.latitude = #{latitude}</if> <if test="altitude != null ">and c.altitude = #{altitude}</if> @@ -163,6 +167,7 @@ <if test="factory != null">factory,</if> <if test="towerId != null">tower_id,</if> <if test="chanNum != null">channel,</if> <if test="startChan != null">start_chan,</if> <if test="longitude != null">longitude,</if> <if test="latitude != null">latitude,</if> <if test="altitude != null">altitude,</if> @@ -197,6 +202,7 @@ <if test="factory != null">#{factory},</if> <if test="towerId != null">#{towerId},</if> <if test="chanNum != null">#{chanNum},</if> <if test="startChan != null">#{startChan},</if> <if test="longitude != null">#{longitude},</if> <if test="latitude != null">#{latitude},</if> <if test="altitude != null">#{altitude},</if> @@ -234,6 +240,7 @@ <if test="factory != null">factory = #{factory},</if> <if test="towerId != null">tower_id = #{towerId},</if> <if test="chanNum != null">channel = #{chanNum},</if> <if test="startChan != null">start_chan = #{startChan},</if> <if test="longitude != null">longitude = #{longitude},</if> <if test="latitude != null">latitude = #{latitude},</if> <if test="altitude != null">altitude = #{altitude},</if> ard-work/src/main/resources/templates/test.html
@@ -916,10 +916,32 @@ let webrtcClient; //whepæä½æ¹æ³ const restartPause = 2000; const retryPause = 2000; const video = document.getElementById('video'); const message = document.getElementById('message'); let nonAdvertisedCodecs = []; let pc = null; let restartTimeout = null; let sessionUrl = ''; let offerData = ''; let queuedCandidates = []; let defaultControls = false; const setMessage = (str) => { if (str !== '') { video.controls = false; } else { video.controls = defaultControls; } message.innerText = str; }; 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); @@ -930,20 +952,21 @@ if (m[3] !== undefined) { ret.username = unquoteCredential(m[3]); ret.credential = unquoteCredential(m[4]); ret.credentialType = "password"; ret.credentialType = 'password'; } return ret; }) : [] ); const parseOffer = (offer) => { const parseOffer = (sdp) => { const ret = { iceUfrag: '', icePwd: '', medias: [], }; for (const line of offer.split('\r\n')) { for (const line of sdp.split('\r\n')) { if (line.startsWith('m=')) { ret.medias.push(line.slice('m='.length)); } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) { @@ -955,7 +978,131 @@ return ret; }; const generateSdpFragment = (offerData, candidates) => { const enableStereoPcmau = (section) => { let lines = section.split('\r\n'); lines[0] += ' 118'; lines.splice(lines.length - 1, 0, 'a=rtpmap:118 PCMU/8000/2'); lines.splice(lines.length - 1, 0, 'a=rtcp-fb:118 transport-cc'); lines[0] += ' 119'; lines.splice(lines.length - 1, 0, 'a=rtpmap:119 PCMA/8000/2'); lines.splice(lines.length - 1, 0, 'a=rtcp-fb:119 transport-cc'); return lines.join('\r\n'); }; const enableMultichannelOpus = (section) => { let lines = section.split('\r\n'); lines[0] += " 112"; lines.splice(lines.length - 1, 0, "a=rtpmap:112 multiopus/48000/3"); lines.splice(lines.length - 1, 0, "a=fmtp:112 channel_mapping=0,2,1;num_streams=2;coupled_streams=1"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:112 transport-cc"); lines[0] += " 113"; lines.splice(lines.length - 1, 0, "a=rtpmap:113 multiopus/48000/4"); lines.splice(lines.length - 1, 0, "a=fmtp:113 channel_mapping=0,1,2,3;num_streams=2;coupled_streams=2"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:113 transport-cc"); lines[0] += " 114"; lines.splice(lines.length - 1, 0, "a=rtpmap:114 multiopus/48000/5"); lines.splice(lines.length - 1, 0, "a=fmtp:114 channel_mapping=0,4,1,2,3;num_streams=3;coupled_streams=2"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:114 transport-cc"); lines[0] += " 115"; lines.splice(lines.length - 1, 0, "a=rtpmap:115 multiopus/48000/6"); lines.splice(lines.length - 1, 0, "a=fmtp:115 channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:115 transport-cc"); lines[0] += " 116"; lines.splice(lines.length - 1, 0, "a=rtpmap:116 multiopus/48000/7"); lines.splice(lines.length - 1, 0, "a=fmtp:116 channel_mapping=0,4,1,2,3,5,6;num_streams=4;coupled_streams=4"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:116 transport-cc"); lines[0] += " 117"; lines.splice(lines.length - 1, 0, "a=rtpmap:117 multiopus/48000/8"); lines.splice(lines.length - 1, 0, "a=fmtp:117 channel_mapping=0,6,1,4,5,2,3,7;num_streams=5;coupled_streams=4"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:117 transport-cc"); return lines.join('\r\n'); }; const enableL16 = (section) => { let lines = section.split('\r\n'); lines[0] += " 120"; lines.splice(lines.length - 1, 0, "a=rtpmap:120 L16/8000/2"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:120 transport-cc"); lines[0] += " 121"; lines.splice(lines.length - 1, 0, "a=rtpmap:121 L16/16000/2"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:121 transport-cc"); lines[0] += " 122"; lines.splice(lines.length - 1, 0, "a=rtpmap:122 L16/48000/2"); lines.splice(lines.length - 1, 0, "a=rtcp-fb:122 transport-cc"); return lines.join('\r\n'); }; const enableStereoOpus = (section) => { let opusPayloadFormat = ''; let lines = section.split('\r\n'); for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('a=rtpmap:') && lines[i].toLowerCase().includes('opus/')) { opusPayloadFormat = lines[i].slice('a=rtpmap:'.length).split(' ')[0]; break; } } if (opusPayloadFormat === '') { return section; } for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('a=fmtp:' + opusPayloadFormat + ' ')) { if (!lines[i].includes('stereo')) { lines[i] += ';stereo=1'; } if (!lines[i].includes('sprop-stereo')) { lines[i] += ';sprop-stereo=1'; } } } return lines.join('\r\n'); }; const editOffer = (sdp) => { const sections = sdp.split('m='); for (let i = 0; i < sections.length; i++) { if (sections[i].startsWith('audio')) { sections[i] = enableStereoOpus(sections[i]); if (nonAdvertisedCodecs.includes('pcma/8000/2')) { sections[i] = enableStereoPcmau(sections[i]); } if (nonAdvertisedCodecs.includes('multiopus/48000/6')) { sections[i] = enableMultichannelOpus(sections[i]); } if (nonAdvertisedCodecs.includes('L16/48000/2')) { sections[i] = enableL16(sections[i]); } break; } } return sections.join('m='); }; const generateSdpFragment = (od, candidates) => { const candidatesByMedia = {}; for (const candidate of candidates) { const mid = candidate.sdpMLineIndex; @@ -965,12 +1112,12 @@ candidatesByMedia[mid].push(candidate); } let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n' + 'a=ice-pwd:' + offerData.icePwd + '\r\n'; let frag = 'a=ice-ufrag:' + od.iceUfrag + '\r\n' + 'a=ice-pwd:' + od.icePwd + '\r\n'; let mid = 0; for (const media of offerData.medias) { for (const media of od.medias) { if (candidatesByMedia[mid] !== undefined) { frag += 'm=' + media + '\r\n' + 'a=mid:' + mid + '\r\n'; @@ -983,59 +1130,161 @@ } 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)); const loadStream = () => { requestICEServers(); }; const supportsNonAdvertisedCodec = (codec, fmtp) => ( new Promise((resolve, reject) => { const pc = new RTCPeerConnection({ iceServers: [] }); pc.addTransceiver('audio', { direction: 'recvonly' }); pc.createOffer() .then((offer) => { if (offer.sdp.includes(' ' + codec)) { // codec is advertised, there's no need to add it manually resolve(false); return; } const sections = offer.sdp.split('m=audio'); const lines = sections[1].split('\r\n'); lines[0] += ' 118'; lines.splice(lines.length - 1, 0, 'a=rtpmap:118 ' + codec); if (fmtp !== undefined) { lines.splice(lines.length - 1, 0, 'a=fmtp:118 ' + fmtp); } sections[1] = lines.join('\r\n'); offer.sdp = sections.join('m=audio'); return pc.setLocalDescription(offer); }) .then(() => { return pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: 'v=0\r\n' + 'o=- 6539324223450680508 0 IN IP4 0.0.0.0\r\n' + 's=-\r\n' + 't=0 0\r\n' + 'a=fingerprint:sha-256 0D:9F:78:15:42:B5:4B:E6:E2:94:3E:5B:37:78:E1:4B:54:59:A3:36:3A:E5:05:EB:27:EE:8F:D2:2D:41:29:25\r\n' + 'm=audio 9 UDP/TLS/RTP/SAVPF 118\r\n' + 'c=IN IP4 0.0.0.0\r\n' + 'a=ice-pwd:7c3bf4770007e7432ee4ea4d697db675\r\n' + 'a=ice-ufrag:29e036dc\r\n' + 'a=sendonly\r\n' + 'a=rtcp-mux\r\n' + 'a=rtpmap:118 ' + codec + '\r\n' + ((fmtp !== undefined) ? 'a=fmtp:118 ' + fmtp + '\r\n' : ''), })); }) .then(() => { resolve(true); }) .catch((err) => { resolve(false); }) .finally(() => { pc.close(); }); }) ); const getNonAdvertisedCodecs = () => { Promise.all([ ['pcma/8000/2'], ['multiopus/48000/6', 'channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2'], ['L16/48000/2'] ].map((c) => supportsNonAdvertisedCodec(c[0], c[1]).then((r) => (r) ? c[0] : false))) .then((c) => c.filter((e) => e !== false)) .then((codecs) => { nonAdvertisedCodecs = codecs; loadStream(); }); }; const onError = (err) => { if (restartTimeout === null) { setMessage(err + ', retrying in some seconds'); if (pc !== null) { pc.close(); pc = null; } onLocalOffer(offer) { this.offerData = parseOffer(offer.sdp); this.pc.setLocalDescription(offer); restartTimeout = window.setTimeout(() => { restartTimeout = null; loadStream(); }, retryPause); console.log("sending offer"); console.log(this.wurl); fetch(this.wurl, { if (sessionUrl) { fetch(sessionUrl, { method: 'DELETE', }); } sessionUrl = ''; queuedCandidates = []; } }; const sendLocalCandidates = (candidates) => { fetch(sessionUrl + window.location.search, { method: 'PATCH', headers: { 'Content-Type': 'application/trickle-ice-sdpfrag', 'If-Match': '*', }, body: generateSdpFragment(offerData, candidates), }) .then((res) => { switch (res.status) { case 204: break; case 404: throw new Error('stream not found'); default: throw new Error(`bad status code ${res.status}`); } }) .catch((err) => { onError(err.toString()); }); }; const onLocalCandidate = (evt) => { if (restartTimeout !== null) { return; } if (evt.candidate !== null) { if (sessionUrl === '') { queuedCandidates.push(evt.candidate); } else { sendLocalCandidates([evt.candidate]) } } }; const onRemoteAnswer = (sdp) => { if (restartTimeout !== null) { return; } pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp, })) .then(() => { if (queuedCandidates.length !== 0) { sendLocalCandidates(queuedCandidates); queuedCandidates = []; } }) .catch((err) => { onError(err.toString()); }); }; const sendOffer = (offer) => { fetch(new URL('whep', window.location.href) + window.location.search, { method: 'POST', headers: { 'Content-Type': 'application/sdp', @@ -1043,114 +1292,107 @@ body: offer.sdp, }) .then((res) => { if (res.status !== 201) { throw new Error('bad status code'); switch (res.status) { case 201: break; case 404: throw new Error('stream not found'); case 400: return res.json().then((e) => { throw new Error(e.error); }); default: throw new Error(`bad status code ${res.status}`); } // this.eTag = res.headers.get('ETag'); this.eTag = res.headers.get("ETag") || res.headers.get('E-Tag'); return res.text(); sessionUrl = new URL(res.headers.get('location'), window.location.href).toString(); return res.text() .then((sdp) => onRemoteAnswer(sdp)); }) .then((sdp) => this.onRemoteAnswer(new RTCSessionDescription({ type: 'answer', sdp, }))) .catch((err) => { console.log('error: ' + err); this.scheduleRestart(); onError(err.toString()); }); } }; onConnectionState() { if (this.restartTimeout !== null) { const createOffer = () => { pc.createOffer() .then((offer) => { offer.sdp = editOffer(offer.sdp); offerData = parseOffer(offer.sdp); pc.setLocalDescription(offer) .then(() => { sendOffer(offer); }) .catch((err) => { onError(err.toString()); }); }) .catch((err) => { onError(err.toString()); }); }; const onConnectionState = () => { if (restartTimeout !== null) { return; } console.log("peer connection state:", this.pc.iceConnectionState); if (pc.iceConnectionState === 'disconnected') { onError('peer connection closed'); } }; switch (this.pc.iceConnectionState) { case "disconnected": this.scheduleRestart(); } } const onTrack = (evt) => { setMessage(''); video.srcObject = evt.streams[0]; }; 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), const requestICEServers = () => { fetch(new URL('whep', window.location.href) + window.location.search, { method: 'OPTIONS', }) .then((res) => { if (res.status !== 204) { throw new Error('bad status code'); } pc = new RTCPeerConnection({ iceServers: linkToIceServers(res.headers.get('Link')), // https://webrtc.org/getting-started/unified-plan-transition-guide sdpSemantics: 'unified-plan', }); const direction = 'sendrecv'; pc.addTransceiver('video', { direction }); pc.addTransceiver('audio', { direction }); pc.onicecandidate = (evt) => onLocalCandidate(evt); pc.oniceconnectionstatechange = () => onConnectionState(); pc.ontrack = (evt) => onTrack(evt); createOffer(); }) .catch((err) => { console.log('error: ' + err); this.scheduleRestart(); onError(err.toString()); }); } }; scheduleRestart() { if (this.restartTimeout !== null) { return; } const parseBoolString = (str, defaultVal) => { str = (str || ''); if (this.pc !== null) { this.pc.close(); this.pc = null; if (['1', 'yes', 'true'].includes(str.toLowerCase())) { return true; } if (['0', 'no', 'false'].includes(str.toLowerCase())) { return false; } return defaultVal; }; this.restartTimeout = window.setTimeout(() => { this.restartTimeout = null; this.start(); }, restartPause); const loadAttributesFromQuery = () => { const params = new URLSearchParams(window.location.search); video.controls = parseBoolString(params.get('controls'), true); video.muted = parseBoolString(params.get('muted'), true); video.autoplay = parseBoolString(params.get('autoplay'), true); video.playsInline = parseBoolString(params.get('playsinline'), true); defaultControls = video.controls; }; 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) ard-work/src/main/resources/templates/test1.html
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,1161 @@ <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>æµè¯é¡µ</title> <script th:src="@{/js/jquery-3.6.4.min.js}"></script> <link rel="stylesheet" th:href="@{/css/bootstrap.css}"/> <script th:src="@{/js/bootstrap.js}"></script> <style> .top-buffer { margin-top: 10px; } .container { border: 2px solid #1b6d85; padding: 15px; } </style> <body> <div class="container"> <div class="row"> <div class="col-md-5"> <div class="row top-buffer"> 设å¤ï¼<select id="selectDev" style="width: 330px;"> </select> </div> <div class="row top-buffer"> ééï¼<select id="selectChn" style="width: 330px;"> </select> </div> <div class="row top-buffer"> <div class="col-md-1 col-md-offset-1"> <button id="up" type="button" class="btn btn-primary">ä¸</button> </div> <div class="col-md-6 col-md-offset-2"> <div class="btn-group" role="group"> <button id="controlZoomIn" type="button" class="btn btn-primary">è°ç¦-</button> <button id="controlZoomOut" type="button" class="btn btn-primary">è°ç¦+</button> </div> </div> </div> <div class="row "> <div class="col-md-1"> <button id="left" type="button" class="btn btn-primary">å·¦</button> </div> <div class="col-md-1 col-md-offset-1"> <button id="right" type="button" class="btn btn-primary">å³</button> </div> <div class="col-md-6 col-md-offset-1"> <div class="btn-group" role="group"> <button id="controlFocusNear" type="button" class="btn btn-primary">èç¦-</button> <button id="controlFocusFar" type="button" class="btn btn-primary">èç¦+</button> </div> </div> </div> <div class="row "> <div class="col-md-1 col-md-offset-1"> <button id="down" type="button" class="btn btn-primary">ä¸</button> </div> <div class="col-md-6 col-md-offset-2"> <div class="btn-group" role="group"> <button id="controlIrisOpen" type="button" class="btn btn-primary">å å-</button> <button id="controlIrisClose" type="button" class="btn btn-primary">å å+</button> </div> </div> </div> <div class="row "> <div class="col-md-10"> <div class="row top-buffer"> <div class="input-group"> <span class="input-group-addon">ç®çåæ å¼ï¼</span> <input id="targetPostion" class="form-control" placeholder="ç®çåæ "/> <button id="setTargetPostion" type="button" class="btn btn-default">æååæ </button> </div> <div class="input-group"> <span class="input-group-addon">På¼ï¼</span> <input id="p" class="form-control" placeholder="请è¾å ¥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"> <button id="preview" type="button" class="btn btn-primary">é¢è§</button> <button id="previewStop" type="button" class="btn btn-primary">忢</button> </div> <div class="row top-buffer"> <video id="video" muted autoplay loop controls style="width: 100%; height: 360px; object-fit: fill; border: 2px solid #3498db;"/> </div> <div class="row"> <img class="thumbnail" id="imgContainer" style="width: 100%; height: 360px; border: 2px solid #3498db;"/> </div> </div> </div> </div> </div> <script th:inline="javascript"> var cameraId, chanNo,opt, optOpen, optClose, token; window.onload = function () { console.log(RTCRtpReceiver.getCapabilities('video').codecs) opt = {"username": "admin", "password": "admin123"}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, url: "../login", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { token = data.token; getDeviceList();// è·å设å¤å表 } }) // åå§åå 容 console.log(cameraMap); } //è·åè®¾å¤ function getDeviceList() { $.ajax({ url: "../cameraSdk/list", type: "get", success: function (data) { console.log(data); var arr = data.data; for (var i = 0; i < arr.length; i++) { console.log(arr[i].id); console.log(arr[i].name) var camera = { name: arr[i].name, factory: arr[i].factory, ipaddr: arr[i].ip, username: arr[i].username, password: arr[i].password, port: arr[i].rtspPort, longitude: arr[i].longitude, latitude: arr[i].latitude, altitude: arr[i].altitude }; cameraMap.set(arr[i].id, camera); //å å建好selectéé¢çoptionå ç´ var option = $("<option>"); //ç»optionçtextèµå¼,è¿å°±æ¯ä½ ç¹å¼ä¸ææ¡è½å¤çå°çä¸è¥¿ $(option).val(arr[i].id); $(option).text(arr[i].name); //è·åselect 䏿æ¡å¯¹è±¡,å¹¶å°optionæ·»å è¿select $('#selectDev').append(option); } $("#selectDev").trigger("change"); } }) } //éæ©è®¾å¤ $("#selectDev").change(function () { // å¨è¿éå¤çéæ©äºä»¶ var cameraId = $(this).find("option:selected").val(); var name = $(this).find("option:selected").text(); getChannelList(cameraId); console.log("éæ©äºï¼" + cameraId + "---" + name); }); //è·åéé function getChannelList(cameraId) { console.log(cameraId) var myEntity = { deviceId: cameraId, pageNum: 1, pageSize: 64 } var queryString = $.param(myEntity); $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../device/channel/list?" + queryString, type: "get", success: function (data) { console.log(data); var arr = data.rows; $('#selectChn').empty(); for (var i = 0; i < arr.length; i++) { console.log(arr[i].chanNo); console.log(arr[i].name); //å å建好selectéé¢çoptionå ç´ var option = document.createElement("option"); //ç»optionçtextèµå¼,è¿å°±æ¯ä½ ç¹å¼ä¸ææ¡è½å¤çå°çä¸è¥¿ $(option).text(arr[i].name); $(option).val(arr[i].chanNo); //è·åselect 䏿æ¡å¯¹è±¡,å¹¶å°optionæ·»å è¿select $('#selectChn').append(option); } } }) } //é¢è§ $('#preview').click(() => { var cameraId = $('#selectDev option:selected').val(); var chanNo = $('#selectChn option:selected').val(); console.log(cameraId + " " + chanNo) $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../vtdu/media/" + cameraId + "_" + chanNo, type: "get", dataType: "json", success: function (data) { realView(data.data.webrtcUrl + "/", "video"); } }) }); //忢 $('#previewStop').click(() => { webrtcClient.stop(); }); //äºå°ä¸ä¸å·¦å³ $("#up").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 2; var enable = true; commondMethod(url, code, enable); }) $("#up").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 2; var enable = false; commondMethod(url, code, enable); }) $("#down").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 8; var enable = true; commondMethod(url, code, enable); }) $("#down").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 8; var enable = false; commondMethod(url, code, enable); }) $("#left").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 4; var enable = true; commondMethod(url, code, enable); }) $("#left").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 4; var enable = false; commondMethod(url, code, enable); }) $("#right").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 6; var enable = true; commondMethod(url, code, enable); }) $("#right").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 6; var enable = false; commondMethod(url, code, enable); }) //åå $("#controlZoomIn").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 10; var enable = true; commondMethod(url, code, enable); }) $("#controlZoomIn").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 10; var enable = false; commondMethod(url, code, enable); }) $("#controlZoomOut").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 11; var enable = true; commondMethod(url, code, enable); }) $("#controlZoomOut").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 11; var enable = false; commondMethod(url, code, enable); }) //åç¦ $("#controlFocusNear").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 12; var enable = true; commondMethod(url, code, enable); }) $("#controlFocusNear").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 12; var enable = false; commondMethod(url, code, enable); }) $("#controlFocusFar").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 13; var enable = true; commondMethod(url, code, enable); }) $("#controlFocusFar").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 13; var enable = false; commondMethod(url, code, enable); }) //å å $("#controlIrisOpen").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 14; var enable = true; commondMethod(url, code, enable); }) $("#controlIrisOpen").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 14; var enable = false; commondMethod(url, code, enable); }) $("#controlIrisClose").mousedown(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 15; var enable = true; commondMethod(url, code, enable); }) $("#controlIrisClose").mouseup(function () { var url = "../cameraSdk/PTZControlWithSpeed"; var code = 15; var enable = false; commondMethod(url, code, enable); }) $("#setPreset").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "presetIndex": 1}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/setPreset", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data); } }) }) $("#gotoPreset").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "presetIndex": 1}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/gotoPreset", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data); } }) }) $("#getPTZ").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/getPTZ", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (datas) { console.log(datas); $("#p").val(datas.data.p); $("#t").val(datas.data.t); $("#z").val(datas.data.z); } }) }) $("#setPTZ").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); var p = $('#p').val(); var t = $('#t').val(); var z = $('#z').val(); //å®ä¹ä¸ä¸ªå¸¦æMapåæ®µçå®ä½å¯¹è±¡ var myEntity = { chanNo: chanNo, cameraId: cameraId, ptzMap: { p: p, t: t, z: z } }; console.log(opt) $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/setPTZ", type: "post", dataType: "json", data: JSON.stringify(myEntity), success: function (data) { console.log(data); } }) }) $("#setTargetPostion").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); var camera = cameraMap.get(cameraId); var camP = camera.longitude + ',' + camera.latitude + ',' + camera.altitude; var targetP = $('#targetPostion').val(); var arr = targetP.split(","); arr = arr.map(item => parseFloat(item)); //å®ä¹ä¸ä¸ªå¸¦æMapåæ®µçå®ä½å¯¹è±¡ var myEntity = { chanNo: chanNo, cameraId: cameraId, targetPosition: arr }; console.log(myEntity) $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/setTargetPosition", type: "post", dataType: "json", data: JSON.stringify(myEntity), success: function (data) { console.log(data); } }) }) $("#setZeroPTZ").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/setZeroPTZ", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data); } }) }) $("#WiperPwron").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 8, "enable": true, "code": 16}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/PTZControlWithSpeed", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data); } }) }) var defogflag = true; $("#Defogcfg").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true}; optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false}; if (defogflag) { $(this).text("å ³ééé¾"); defogflag = false; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/defogcfg", type: "post", dataType: "json", data: JSON.stringify(optOpen), success: function (data) { console.log(data); } }) } else { $(this).text("å¼å¯éé¾"); defogflag = true; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/defogcfg", type: "post", dataType: "json", data: JSON.stringify(optClose), success: function (data) { console.log(data); } }) } }) var infrareflag = true; $("#Infrarecfg").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true}; optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false}; if (infrareflag) { $(this).text("å ³é红å¤"); infrareflag = false; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/infrarecfg", type: "post", dataType: "json", data: JSON.stringify(optOpen), success: function (data) { console.log(data); } }) } else { $(this).text("å¼å¯çº¢å¤"); infrareflag = true; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/infrarecfg", type: "post", dataType: "json", data: JSON.stringify(optClose), success: function (data) { console.log(data); } }) } }) var focusModeflag = true; $("#FocusMode").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true}; optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false}; if (focusModeflag) { $(this).text("èªå¨èç¦"); focusModeflag = false; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/focusMode", type: "post", dataType: "json", data: JSON.stringify(optOpen), success: function (data) { console.log(data); } }) } else { $(this).text("æå¨èç¦"); focusModeflag = true; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/focusMode", type: "post", dataType: "json", data: JSON.stringify(optClose), success: function (data) { console.log(data); } }) } }) $("#getFocusPos").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/getFocusPos", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (datas) { console.log(datas); $("#focus").val(datas.data); } }) }) var heateRpwronflag = true; $("#HeateRpwron").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true}; optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false}; if (heateRpwronflag) { $(this).text("å ³éäºå°å ç"); heateRpwronflag = false; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/heateRpwron", type: "post", dataType: "json", data: JSON.stringify(optOpen), success: function (data) { console.log(data); } }) } else { $(this).text("å¼å¯äºå°å ç"); heateRpwronflag = true; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/heateRpwron", type: "post", dataType: "json", data: JSON.stringify(optClose), success: function (data) { console.log(data); } }) } }) var CameraDeicingflag = true; $("#CameraDeicing").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true}; optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false}; if (CameraDeicingflag) { $(this).text("å ³éé头å ç"); CameraDeicingflag = false; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/cameraDeicing", type: "post", dataType: "json", data: JSON.stringify(optOpen), success: function (data) { console.log(data); } }) } else { $(this).text("å¼å¯é头å ç"); CameraDeicingflag = true; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/cameraDeicing", type: "post", dataType: "json", data: JSON.stringify(optClose), success: function (data) { console.log(data); } }) } }) $("#realCutPic").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/captureJPEGPicture", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data.data); $("#imgContainer").attr("src", "data:image/png;base64," + data.data); } }) }) $("#saveCutPic").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/picCutCate", type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data.data); setTimeout(() => { $('#imgContainer').attr('src', data.data); }, 1000) } }) }) var recordflag = true; $("#record").click(function () { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); optOpen = {"cameraId": cameraId, "chanNo": chanNo, "enable": true}; optClose = {"cameraId": cameraId, "chanNo": chanNo, "enable": false}; if (recordflag) { $(this).text("忢å½å"); recordflag = false; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/record", type: "post", dataType: "json", data: JSON.stringify(optOpen), success: function (data) { console.log(data); } }) } else { $(this).text("å¼å§å½å"); recordflag = true; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../cameraSdk/record", dataType: "json", data: JSON.stringify(optClose), type: "post", success: function (data) { console.log(data); } }) } }) var cameraMap = new Map(); /*äºå°å ¬å ±æ¹æ³*/ function commondMethod(url, code, enable) { cameraId = $('#selectDev option:selected').val(); chanNo = $('#selectChn option:selected').val(); opt = {"cameraId": cameraId, "chanNo": chanNo, "speed": 4, "enable": enable, "code": code}; $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: url, type: "post", dataType: "json", data: JSON.stringify(opt), success: function (data) { console.log(data); } }) } $('video').click(function (e) { var cameraId = $('#selectDev option:selected').val(); var chanNo = $('#selectChn option:selected').val(); console.log(cameraId + " " + chanNo) $.ajax({ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': token }, url: "../vtdu/media/" + cameraId + "_" + chanNo, type: "get", dataType: "json", success: function (data) { realView(data.data.webrtcUrl + "/", e.target.id); } }) }); let webrtcClient; //whepæä½æ¹æ³ const restartPause = 2000; const unquoteCredential = (v) => ( JSON.parse(`"${v}"`) ); const linkToIceServers = (links) => ( (links !== null) ? links.split(', ').map((link) => { const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i); const ret = { urls: [m[1]], }; if (m[3] !== undefined) { ret.username = unquoteCredential(m[3]); ret.credential = unquoteCredential(m[4]); ret.credentialType = "password"; } return ret; }) : [] ); const parseOffer = (offer) => { const ret = { iceUfrag: '', icePwd: '', medias: [], }; for (const line of offer.split('\r\n')) { if (line.startsWith('m=')) { ret.medias.push(line.slice('m='.length)); } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) { ret.iceUfrag = line.slice('a=ice-ufrag:'.length); } else if (ret.icePwd === '' && line.startsWith('a=ice-pwd:')) { ret.icePwd = line.slice('a=ice-pwd:'.length); } } return ret; }; const generateSdpFragment = (offerData, candidates) => { const candidatesByMedia = {}; for (const candidate of candidates) { const mid = candidate.sdpMLineIndex; if (candidatesByMedia[mid] === undefined) { candidatesByMedia[mid] = []; } candidatesByMedia[mid].push(candidate); } let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n' + 'a=ice-pwd:' + offerData.icePwd + '\r\n'; let mid = 0; for (const media of offerData.medias) { if (candidatesByMedia[mid] !== undefined) { frag += 'm=' + media + '\r\n' + 'a=mid:' + mid + '\r\n'; for (const candidate of candidatesByMedia[mid]) { frag += 'a=' + candidate.candidate + '\r\n'; } } mid++; } return frag; } class WHEPClient { constructor(whepUrl, videoId) { this.video = videoId; this.wurl = new URL('whep', whepUrl); this.pc = null; this.restartTimeout = null; this.eTag = ''; this.queuedCandidates = []; this.start(); } start() { console.log("requesting ICE servers"); fetch(this.wurl, { method: 'OPTIONS', }) .then((res) => this.onIceServers(res)) .catch((err) => { console.log('error: ' + err); this.scheduleRestart(); }); } onIceServers(res) { this.pc = new RTCPeerConnection({ iceServers: linkToIceServers(res.headers.get('Link')), }); const direction = "sendrecv"; this.pc.addTransceiver("video", {direction}); this.pc.addTransceiver("audio", {direction}); this.pc.onicecandidate = (evt) => this.onLocalCandidate(evt); this.pc.oniceconnectionstatechange = () => this.onConnectionState(); this.pc.ontrack = (evt) => { console.log("new track:", evt.track.kind); document.getElementById(this.video).srcObject = evt.streams[0]; }; this.pc.createOffer() .then((offer) => this.onLocalOffer(offer)); } onLocalOffer(offer) { this.offerData = parseOffer(offer.sdp); this.pc.setLocalDescription(offer); console.log("sending offer"); console.log(this.wurl); fetch(this.wurl, { method: 'POST', headers: { 'Content-Type': 'application/sdp', }, body: offer.sdp, }) .then((res) => { if (res.status !== 201) { throw new Error('bad status code'); } // this.eTag = res.headers.get('ETag'); this.eTag = res.headers.get("ETag") || res.headers.get('E-Tag'); return res.text(); }) .then((sdp) => this.onRemoteAnswer(new RTCSessionDescription({ type: 'answer', sdp, }))) .catch((err) => { console.log('error: ' + err); this.scheduleRestart(); }); } onConnectionState() { if (this.restartTimeout !== null) { return; } console.log("peer connection state:", this.pc.iceConnectionState); switch (this.pc.iceConnectionState) { case "disconnected": this.scheduleRestart(); } } onRemoteAnswer(answer) { if (this.restartTimeout !== null) { return; } this.pc.setRemoteDescription(new RTCSessionDescription(answer)); if (this.queuedCandidates.length !== 0) { this.sendLocalCandidates(this.queuedCandidates); this.queuedCandidates = []; } } onLocalCandidate(evt) { if (this.restartTimeout !== null) { return; } if (evt.candidate !== null) { if (this.eTag === '') { this.queuedCandidates.push(evt.candidate); } else { this.sendLocalCandidates([evt.candidate]) } } } sendLocalCandidates(candidates) { fetch(this.wurl, { method: 'PATCH', headers: { 'Content-Type': 'application/trickle-ice-sdpfrag', 'If-Match': this.eTag, }, body: generateSdpFragment(this.offerData, candidates), }) .then((res) => { if (res.status !== 204) { throw new Error('bad status code'); } }) .catch((err) => { console.log('error: ' + err); this.scheduleRestart(); }); } scheduleRestart() { if (this.restartTimeout !== null) { return; } if (this.pc !== null) { this.pc.close(); this.pc = null; } this.restartTimeout = window.setTimeout(() => { this.restartTimeout = null; this.start(); }, restartPause); this.eTag = ''; this.queuedCandidates = []; } stop() { if (this.pc) { try { this.pc.close(); } catch (e) { console.log("Failure close peer connection:" + e); } this.pc = null; } } } function realView(whepUrl, videoId) { console.log(whepUrl) webrtcClient = new WHEPClient(whepUrl, videoId); } </script> </body> </html> ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SyncTask.java
@@ -84,28 +84,24 @@ /** * @Author åèä¹ * @Description 忥nvrééç¶æå®æ¶åæ¥æµåªä½ * @Description 忥ééç¶æå®æ¶åæ¥æµåªä½ * @Date 2024/7/9 16:01 */ public void nvrChannelState() { public void syncChannelState() { log.warn("宿¶åæ¥ééä»»å¡å¼å§"); //è·åæænvrè®¾å¤ List<ArdCameras> ardCamerasList = iArdCamerasService.selectArdCamerasListNoDataScope(new ArdCameras()); if (ardCamerasList.size() > 0) { ardCamerasList.stream() .filter(ardCameras -> ardCameras.getGdtype().equals("2")) .forEach(ardCameras -> { //éè¿SDKè·åNVRå½åéé //éè¿SDKè·åNVRå®é éé CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(ardCameras.getFactory()); List<ArdChannel> ardChannelList = cameraSDK.getChannels(ardCameras); //忥éé表 ArdChannel ardChannel=new ArdChannel(); ardChannel.setDeviceId(ardCameras.getId()); List<ArdChannel> ardChannelListDb = ardChannelService.selectArdChannelList(ardChannel); ardChannelService.asyncChannel(ardChannelListDb,ardChannelList); //忥æµåªä½è¡¨ //忥æµåªä½api ardChannelService.asyncChannel(ardCameras,ardChannelListDb,ardChannelList); }); } } @@ -142,7 +138,8 @@ * åèä¹ * 2023/10/13 14:13:53 */ public void vtdu() { public void syncVtdu() { log.warn("宿¶åæ¥æµåªä½ä»»å¡å¼å§"); List<String> nameList = mediaService.getNameList(); List<Vtdu> vtduList = vtduService.selectVtduList(new Vtdu()); vtduService.asyncVtdu(vtduList,nameList); server/mediamtx/mediamtx.yml
@@ -225,17 +225,17 @@ webrtcICEInterfaces: [] # List of public IP addresses that are to be used as a host. # This is used typically for servers that are behind 1:1 D-NAT. webrtcICEHostNAT1To1IPs: [] webrtcICEHostNAT1To1IPs: [192.168.1.227] # Address of a ICE UDP listener in format host:port. # If filled, ICE traffic will pass through a single UDP port, # allowing the deployment of the server inside a container or behind a NAT. webrtcICEUDPMuxAddress: webrtcICEUDPMuxAddress: :8189 # Address of a ICE TCP listener in format host:port. # If filled, ICE traffic will pass through a single TCP port, # allowing the deployment of the server inside a container or behind a NAT. # Using this setting forces usage of the TCP protocol, which is not # optimal for WebRTC. webrtcICETCPMuxAddress: 192.168.1.227:19302 webrtcICETCPMuxAddress: ############################################### # Global settings -> SRT