ard-work/src/main/java/com/ruoyi/alarm/global/service/impl/QueueTaskExecutor.java
@@ -47,6 +47,7 @@ IArdAlarmExternalService ardAlarmExternalService; @Resource IArdAlarmAccessService ardAlarmAccessService; public void processTask(GuideTask guideTask) { try { CameraCmd cmd = new CameraCmd(); ard-work/src/main/java/com/ruoyi/device/camera/controller/CameraSdkController.java
@@ -286,6 +286,7 @@ @ApiOperation(value = "è·åèç¦æ¨¡å¼", notes = "1æå¨2èªå¨") @PostMapping("/getFocusMode") @ApiOperationSupport(includeParameters = {"cmd.cameraId", "cmd.chanNo"}) public @ResponseBody AjaxResult getFocusMode(@RequestBody CameraCmd cmd) { cmd.setOperator(SecurityUtils.getUserId()); @@ -328,7 +329,7 @@ @ApiOperation("è·åç¸æºæ¶è®¾åæ°") @PostMapping("/getGisInfo") @Log(title = "è·åç¸æºæ¶è®¾åæ°", businessType = BusinessType.CONTROL) @ApiOperationSupport(includeParameters = {"cmd.cameraId", "cmd.chanNo", "cmd.enable"}) @ApiOperationSupport(includeParameters = {"cmd.cameraId", "cmd.chanNo"}) public @ResponseBody AjaxResult getGisInfo(@RequestBody CameraCmd cmd) { cmd.setOperator(SecurityUtils.getUserId()); ard-work/src/main/java/com/ruoyi/device/camera/domain/CameraCmd.java
@@ -2,13 +2,21 @@ import io.swagger.annotations.ApiModel; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.bind.DefaultValue; import java.util.Map; @Data @NoArgsConstructor @ApiModel(description = "sdkå½ä»¤å®ä½ç±»") public class CameraCmd { public CameraCmd(String cameraId, Integer chanNo) { this.cameraId = cameraId; this.chanNo = chanNo; } /*å½ä»¤æ è¯*/ String cmdType; /*ç¸æºID*/ @@ -26,7 +34,7 @@ /*èç¦å¼*/ Integer dwFocusPos; /*PTZå¼*/ Map<String,Double>ptzMap; Map<String, Double> ptzMap; /*ç®æ ç»çº¬åº¦*/ double[] targetPosition; ard-work/src/main/java/com/ruoyi/device/camera/service/impl/CameraSdkServiceImpl.java
@@ -315,7 +315,7 @@ if (factory.equals("1")) { result = hikClientService.getFocusMode(cmd); } else if (factory.equals("2")) { result = dhClientService.getFocusMode(cmd); } } } catch (Exception ex) { @@ -659,7 +659,7 @@ if (factory.equals("1")) { map = hikClientService.getGisInfo(cmd); } else if (factory.equals("2")) { map = dhClientService.getGisInfo(cmd); } } } catch (Exception ex) { ard-work/src/main/java/com/ruoyi/device/dhsdk/lib/enumeration/EM_FOCUS_LIMIT_SELECT_MODE.java
@@ -1,43 +1,43 @@ package com.ruoyi.device.dhsdk.lib.enumeration; /** * @author 291189 * @description èç¦æéå¯¹åºæä¸¾ * @date 2022/11/01 11:16:54 */ /** * @author 291189 * @description èç¦æéå¯¹åºæä¸¾ * @date 2022/11/01 11:16:54 */ public enum EM_FOCUS_LIMIT_SELECT_MODE { /** Manual èªå¨ */ EM_FOCUS_LIMIT_MODE_MANUAL(0," Manual èªå¨"), /** Auto æå¨ */ EM_FOCUS_LIMIT_MODE_AUTO(1," Auto æå¨"), /** /** * Manual èªå¨ */ EM_FOCUS_LIMIT_MODE_MANUAL(0, " Manual èªå¨"), /** * Auto æå¨ */ EM_FOCUS_LIMIT_MODE_AUTO(1, " Auto æå¨"), /** * */ EM_FOCUS_LIMIT_MODE_INVALI(2, "æ æ"); */ EM_FOCUS_LIMIT_MODE_INVALI(2,"æ æ"); private int value; private int value; private String note; private String note; public String getNote() { public String getNote() { return note; } public int getValue() { public int getValue() { return value; } EM_FOCUS_LIMIT_SELECT_MODE(int givenValue, String note) { EM_FOCUS_LIMIT_SELECT_MODE(int givenValue, String note) { this.value = givenValue; this.note = note; } public static String getNoteByValue(int givenValue) { public static String getNoteByValue(int givenValue) { for (EM_FOCUS_LIMIT_SELECT_MODE enumType : EM_FOCUS_LIMIT_SELECT_MODE.values()) { if (givenValue == enumType.getValue()) { return enumType.getNote(); @@ -46,7 +46,7 @@ return null; } public static int getValueByNote(String givenNote) { public static int getValueByNote(String givenNote) { for (EM_FOCUS_LIMIT_SELECT_MODE enumType : EM_FOCUS_LIMIT_SELECT_MODE.values()) { if (givenNote.equals(enumType.getNote())) { return enumType.getValue(); @@ -55,7 +55,7 @@ return -1; } public static EM_FOCUS_LIMIT_SELECT_MODE getEnum(int value) { public static EM_FOCUS_LIMIT_SELECT_MODE getEnum(int value) { for (EM_FOCUS_LIMIT_SELECT_MODE e : EM_FOCUS_LIMIT_SELECT_MODE.values()) { if (e.getValue() == value) return e; ard-work/src/main/java/com/ruoyi/device/dhsdk/lib/structure/CFG_VIDEO_IN_FOCUS.java
@@ -3,28 +3,28 @@ import com.ruoyi.device.dhsdk.lib.NetSDKLib; /** * @author 291189 * @description èç¦è®¾ç½®åºæ¬ä¿¡æ¯åå * @date 2022/11/01 11:16:54 */ /** * @author 291189 * @description èç¦è®¾ç½®åºæ¬ä¿¡æ¯åå * @date 2022/11/01 11:16:54 */ public class CFG_VIDEO_IN_FOCUS extends NetSDKLib.SdkStructure { /** ééå· */ public int nChannelIndex; /** é 置使ç¨ä¸ªæ° */ public int nVideoInFocusRealNum; /** ééèç¦é ç½®åå ä¿¡æ¯ */ public CFG_VIDEO_IN_FOCUS_UNIT[] stVideoInFocusUnit=new CFG_VIDEO_IN_FOCUS_UNIT[32]; /** * ééå· */ public int nChannelIndex; /** * é 置使ç¨ä¸ªæ° */ public int nVideoInFocusRealNum; /** * ééèç¦é ç½®åå ä¿¡æ¯ */ public CFG_VIDEO_IN_FOCUS_UNIT[] stVideoInFocusUnit = new CFG_VIDEO_IN_FOCUS_UNIT[32]; public CFG_VIDEO_IN_FOCUS(){ for(int i=0;i<stVideoInFocusUnit.length;i++){ stVideoInFocusUnit[i]=new CFG_VIDEO_IN_FOCUS_UNIT(); } } public CFG_VIDEO_IN_FOCUS() { for (int i = 0; i < stVideoInFocusUnit.length; i++) { stVideoInFocusUnit[i] = new CFG_VIDEO_IN_FOCUS_UNIT(); } } } ard-work/src/main/java/com/ruoyi/device/dhsdk/module/ConfigModule.java
@@ -32,6 +32,21 @@ } /** * æ¥è¯¢è¿ç¨è®¾å¤ç¶æ */ public static boolean queryRemotDevState(NetSDKLib.LLong hLoginHandle, int nChn, int nType, NetSDKLib.SdkStructure stuInfo) { IntByReference intRetLen = new IntByReference(); stuInfo.write(); if (!netsdk.CLIENT_QueryRemotDevState(hLoginHandle, nType, nChn, stuInfo.getPointer(), stuInfo.size(), intRetLen, 3000)) { System.err.println("Config Failed!" + ToolKits.getErrorCodePrint()); return false; } stuInfo.read(); return true; } /** * è·åå个é ç½® * * @param hLoginHandle ç»é奿 @@ -69,7 +84,7 @@ int nBufferLen = 2 * 1024 * 1024; byte[] strBuffer = new byte[nBufferLen]; cmdObject.write(); boolean bRet = netsdk.CLIENT_QueryNewSystemInfo(hLoginHandle, strCmd, nChn, strBuffer, cmdObject.size(), error,3000); boolean bRet = netsdk.CLIENT_QueryNewSystemInfo(hLoginHandle, strCmd, nChn, strBuffer, cmdObject.size(), error, 3000); if (bRet) { cmdObject.read(); } else { @@ -78,7 +93,20 @@ } return result; } // è·åé ç½® public static boolean GetConfig(NetSDKLib.LLong hLoginHandle, int nChn,int type,Structure cmdObject) { boolean result = false; // è·å cmdObject.write(); if (netsdk.CLIENT_GetConfig(hLoginHandle, type, nChn, cmdObject.getPointer(), cmdObject.size(), 4000, null)) { cmdObject.read(); result=true; } else { System.err.println("GetConfig Failed!" + getErrorCodePrint()); result=false; } return result; } /** * 设置å个é ç½® * @@ -112,4 +140,5 @@ return result; } } ard-work/src/main/java/com/ruoyi/device/dhsdk/service/IDhClientService.java
@@ -55,8 +55,12 @@ boolean gotoPreset(CameraCmd cmd); //设置é¢ç½®ä½ boolean setPreset(CameraCmd cmd); //èç¦æ¨¡å¼ //设置èç¦æ¨¡å¼ boolean controlFocusMode(CameraCmd cmd); //è·åèç¦æ¨¡å¼ String getFocusMode(CameraCmd cmd); //éé¾ boolean controlDefogcfg(CameraCmd cmd); //çº¢å¤ @@ -67,4 +71,6 @@ boolean setFocusPos(CameraCmd cmd); //è·åç æµåç¼©åæ° Map<String, Object> getVideoCompressionCfg(CameraCmd cmd); //è·åGISä¿¡æ¯æ°æ® Map<String, Object> getGisInfo(CameraCmd cmd); } ard-work/src/main/java/com/ruoyi/device/dhsdk/service/impl/DhClientServiceImpl.java
@@ -3,31 +3,25 @@ import com.ruoyi.common.annotation.SdkOperate; import com.ruoyi.common.utils.file.FileUtils; import com.ruoyi.common.utils.file.MimeTypeUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.camera.service.IArdCamerasService; import com.ruoyi.device.channel.domain.ArdChannel; import com.ruoyi.device.channel.service.IArdChannelService; import com.ruoyi.device.dhsdk.common.Res; import com.ruoyi.device.dhsdk.lib.NetSDKLib; import com.ruoyi.device.dhsdk.lib.NetSDKLib.LLong; import com.ruoyi.device.dhsdk.lib.enumeration.EM_FOCUS_LIMIT_SELECT_MODE; import com.ruoyi.device.dhsdk.lib.enumeration.EM_NEW_CONFIG; import com.ruoyi.device.dhsdk.lib.enumeration.NET_EM_CFG_OPERATE_TYPE; import com.ruoyi.device.dhsdk.lib.structure.CFG_VIDEO_IN_FOCUS; import com.ruoyi.device.dhsdk.lib.structure.CFG_VIDEO_IN_FOCUS_UNIT; import com.ruoyi.device.dhsdk.lib.structure.NET_ENCODE_VIDEO_INFO; import com.ruoyi.device.dhsdk.lib.structure.DH_OUT_PTZ_VIEW_RANGE_STATUS; import com.ruoyi.device.dhsdk.module.*; import com.ruoyi.device.dhsdk.service.IDhClientService; import com.ruoyi.device.hiksdk.common.GlobalVariable; import com.ruoyi.device.hiksdk.sdk.HCNetSDK; import com.ruoyi.media.domain.Vtdu; import com.ruoyi.media.service.IVtduService; import com.ruoyi.media.service.impl.VtduServiceImpl; import com.ruoyi.utils.gis.GisUtil; import com.ruoyi.utils.minio.MinioUtil; import com.ruoyi.utils.tools.ArdTool; import com.sun.jna.Pointer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -150,11 +144,18 @@ if (vtdu != null) { vtduService.deleteVtduByName(name); } //æ·»å å°æµåªä½ CameraCmd cmd = new CameraCmd(camera.getId(), channel.getChanNo()); Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd); vtdu = new Vtdu(); if (videoCompressionCfg.get("videoEncType").equals("æ åh264")) { vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç } else { vtdu.setIsCode("1");//é»è®¤è½¬ç } vtdu.setRtspSource(rtspSource); vtdu.setName(camera.getId() + "_" + channel.getChanNo()); vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç vtdu.setMode("1");//é»è®¤CPU软解ç vtdu.setCameraId(camera.getId()); vtduService.insertVtdu(vtdu); @@ -312,7 +313,8 @@ if (b) { DecimalFormat df = new DecimalFormat("0.0");//设置ä¿ç使° String nPTZPan = df.format((float) dh_ptz_location_info.nPTZPan / 10); String nPTZTilt = df.format((float) dh_ptz_location_info.nPTZTilt / 10); float t = (float) dh_ptz_location_info.nPTZTilt / 10; String nPTZTilt = df.format(t < 0 ? t + 360 : t); String nPTZZoom = df.format((float) dh_ptz_location_info.nPTZZoom); ptzMap.put("p", nPTZPan); ptzMap.put("t", nPTZTilt); @@ -648,10 +650,12 @@ cfg_video_in_focus.stVideoInFocusUnit[i].nFocusLimit = 10000;//èç¦æéå¼, å使¯«ç±³ if (enable) {//èç¦æ¨¡å¼, 0-å ³é, 1-è¾ å©èç¦, 2-èªå¨èç¦, 3-åèªå¨èç¦, 4-æå¨èç¦ cfg_video_in_focus.stVideoInFocusUnit[i].nMode = 4;//æå¨èç¦ cfg_video_in_focus.stVideoInFocusUnit[i].emFocusMode = 1;//èç¦æéManual cfg_video_in_focus.stVideoInFocusUnit[i].emFocusMode = 0;//èç¦æéManual log.debug("å½å为æå¨èç¦æ¨¡å¼"); } else { cfg_video_in_focus.stVideoInFocusUnit[i].nMode = 2;//èªå¨èç¦ cfg_video_in_focus.stVideoInFocusUnit[i].emFocusMode = 0;//èç¦æéAuto cfg_video_in_focus.stVideoInFocusUnit[i].emFocusMode = 1;//èç¦æéAuto log.debug("å½å为èªå¨èç¦æ¨¡å¼"); } } cfg_video_in_focus.nChannelIndex = chanNo - 1; @@ -666,6 +670,49 @@ } } @Override public String getFocusMode(CameraCmd cmd) { String mode = ""; String cameraId = cmd.getCameraId(); Integer chanNo = cmd.getChanNo(); if (!GlobalVariable.loginMap.containsKey(cameraId)) { return ""; } LLong loginId = (LLong) GlobalVariable.loginMap.get(cameraId); try { NET_VIDEOIN_FOCUSMODE_INFO focusModeInfo = new NET_VIDEOIN_FOCUSMODE_INFO(); int emCfgOpType = NET_EM_CFG_OPERATE_TYPE.NET_EM_CFG_VIDEOIN_FOCUSMODE; boolean bool = ConfigModule.GetConfig(loginId, chanNo - 1, emCfgOpType, focusModeInfo); if (!bool) { log.error("è·å失败,请ç¨åéè¯" + getErrorCodePrint()); } System.out.println("é 置类å:" + focusModeInfo.emCfgType); // å ·ä½ä¿¡æ¯ï¼åèåºéçæä¸¾ System.out.println("èç¦æ¨¡å¼:" + focusModeInfo.emFocusMode); switch (focusModeInfo.emFocusMode) { case 0: mode = "å ³é"; break; case 1: mode = "è¾ å©èç¦"; break; case 2: mode = "èªå¨èç¦"; break; case 3: mode = "åèªå¨èç¦"; break; case 4: mode = "æå¨èç¦"; break; } } catch (Exception ex) { log.error("è·åèç¦æ¨¡å¼å¼å¸¸:" + ex.getMessage()); } return mode; } //éé¾ @Override public boolean controlDefogcfg(CameraCmd cmd) { @@ -677,9 +724,9 @@ } LLong loginId = (LLong) GlobalVariable.loginMap.get(cameraId); try { EM_NEW_CONFIG config = EM_NEW_CONFIG.CFG_CMD_VIDEOINDEFOG; String command = EM_NEW_CONFIG.CFG_CMD_VIDEOINDEFOG.getValue(); //CFG_VIDEOINDEFOG_LIST cfg_videoindefog_list=new CFG_VIDEOINDEFOG_LIST(); boolean bool = ConfigModule.SetDevConfig(loginId, chanNo - 1, config.getValue(), null); boolean bool = ConfigModule.SetDevConfig(loginId, chanNo - 1, command, null); if (!bool) { log.error("æ§å¶å¤±è´¥,请ç¨åéè¯" + getErrorCodePrint()); } @@ -836,6 +883,32 @@ return map; } //è·åGISä¿¡æ¯æ°æ® @Override public Map<String, Object> getGisInfo(CameraCmd cmd) { Map<String, Object> map = new HashMap<>(); try { String cameraId = cmd.getCameraId(); Integer chanNo = cmd.getChanNo(); if (!GlobalVariable.loginMap.containsKey(cameraId)) { return null; } LLong loginId = (LLong) GlobalVariable.loginMap.get(cameraId); DH_OUT_PTZ_VIEW_RANGE_STATUS dh_out_ptz_view_range_status = new DH_OUT_PTZ_VIEW_RANGE_STATUS(); boolean b = ConfigModule.queryDevState(loginId, NET_DEVSTATE_PTZ_VIEW_RANGE, dh_out_ptz_view_range_status); if (b) { float nAngelH = (float) dh_out_ptz_view_range_status.nAngelH / 10; float nAngelV = (float) dh_out_ptz_view_range_status.nAngelV / 10; map = getPtz(cmd);//è·åptz map.put("fHorFieldAngle", nAngelH);// æ°´å¹³è§åºè§ map.put("fVerFieldAngle", nAngelV);// åç´è§åºè§ } } catch (Exception ex) { log.error("è·åäºå°å¯è§åå¼å¸¸" + ex.getMessage()); } return map; } // è®¾å¤æçº¿åè°: å½è®¾å¤åºç°æçº¿æ¶ï¼SDKä¼è°ç¨è¯¥å½æ° private static class DisConnect implements NetSDKLib.fDisConnect { public void invoke(LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) { ard-work/src/main/java/com/ruoyi/device/hiksdk/sdk/LoginResultCallBack.java
@@ -12,11 +12,9 @@ import com.ruoyi.device.hiksdk.service.IHikClientService; import com.ruoyi.media.domain.Vtdu; import com.ruoyi.media.service.IVtduService; import com.ruoyi.utils.forest.MediaClient; import com.sun.jna.Pointer; import lombok.extern.slf4j.Slf4j; import javax.annotation.Resource; import java.util.Comparator; import java.util.List; import java.util.concurrent.PriorityBlockingQueue; ard-work/src/main/java/com/ruoyi/device/hiksdk/service/IHikClientService.java
@@ -84,6 +84,7 @@ //è·åptzèå´ Map<String, Object> getPtzScope(CameraCmd cmd); //设置ptz boolean setPtz(CameraCmd cmd); //è®¾ç½®é¶æ¹ä½è§ ard-work/src/main/java/com/ruoyi/device/hiksdk/service/impl/HikClientServiceImpl.java
@@ -25,7 +25,9 @@ import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.Base64; import javax.annotation.Resource; import java.io.*; @@ -109,6 +111,7 @@ * @å建æ¶é´ 2023/1/17 16:12 * @ä¿®æ¹äººåå ¶å®ä¿¡æ¯ */ @Async public void syncLogin(ArdCameras camera) { // åå§å if (!hCNetSDK.NET_DVR_Init()) { @@ -188,7 +191,13 @@ vtdu = new Vtdu(); vtdu.setRtspSource(rtspSource); vtdu.setName(camera.getId() + "_" + channel.getChanNo()); vtdu.setIsCode("0");//é»è®¤ä¸è½¬ç 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); ard-work/src/main/java/com/ruoyi/inspect/service/impl/ArdVideoInspectTaskServiceImpl.java
@@ -8,6 +8,7 @@ import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.camera.mapper.ArdCamerasMapper; import com.ruoyi.device.camera.service.ICameraSdkService; import com.ruoyi.device.hiksdk.service.IHikClientService; import com.ruoyi.inspect.domain.ArdVideoInspectRecord; import com.ruoyi.inspect.mapper.ArdVideoInspectRecordMapper; @@ -44,7 +45,7 @@ @Resource private ArdCamerasMapper ardCamerasMapper; @Resource private IHikClientService hikClientService; private ICameraSdkService cameraSdkService; @Resource private ArdCamerasMapper camerasMapper; @@ -455,10 +456,10 @@ cmd.setTargetPosition(targetPositon); cmd.setOperator("sys_patrol_inspect"); cmd.setExpired(step.getRecordingTime() * 60); boolean setTargetPosition = hikClientService.guideTargetPosition(cmd); boolean setTargetPosition = cameraSdkService.guideTargetPosition(cmd); if (setTargetPosition) { /*æ§å¶ç¸æºå·¡æ£æåï¼å¼å§å½å*/ hikClientService.recordStart(cmd); cameraSdkService.recordStart(cmd); } else { /*æ§å¶å¤±è´¥,å½åæ¥éª¤å¯å¨æ¶é´ç½®null*/ ardVideoInspectTask.setCurrentStepStartTime(""); @@ -513,7 +514,7 @@ cmd.setTargetPosition(targetPositon); cmd.setOperator("sys_patrol_inspect"); cmd.setExpired(step.getRecordingTime() * 60); boolean setTargetPosition = hikClientService.guideTargetPosition(cmd); boolean setTargetPosition = cameraSdkService.guideTargetPosition(cmd); if (!setTargetPosition) { /*æ§å¶å¤±è´¥,å½åæ¥éª¤å¯å¨æ¶é´ç½®null*/ ardVideoInspectTask.setCurrentStepStartTime(""); @@ -552,7 +553,7 @@ cmd.setOperator("sys_patrol_inspect"); cmd.setRecordBucketName("record"); cmd.setRecordObjectName("inspect_" + IdUtils.fastSimpleUUID()); String url = hikClientService.recordStopToMinio(cmd); String url = cameraSdkService.recordStopToMinio(cmd); /*æå ¥å·¡æ£è®°å½*/ ArdVideoInspectRecord ardVideoInspectRecord = new ArdVideoInspectRecord(); ardVideoInspectRecord.setStepId(step.getId()); ard-work/src/main/java/com/ruoyi/media/domain/Conf.java
@@ -15,122 +15,11 @@ @Data public class Conf { private String source; @JsonProperty("sourceFingerprint") private String sourcefingerprint; @JsonProperty("sourceOnDemand") private boolean sourceondemand; @JsonProperty("sourceOnDemandStartTimeout") private String sourceondemandstarttimeout; @JsonProperty("sourceOnDemandCloseAfter") private String sourceondemandcloseafter; @JsonProperty("publishUser") private String publishuser; @JsonProperty("publishPass") private String publishpass; @JsonProperty("publishIPs") private List<String> publiships; @JsonProperty("readUser") private String readuser; @JsonProperty("readPass") private String readpass; @JsonProperty("readIPs") private List<String> readips; @JsonProperty("disablePublisherOverride") private boolean disablepublisheroverride; private String fallback; @JsonProperty("sourceProtocol") private String sourceprotocol; @JsonProperty("sourceAnyPortEnable") private boolean sourceanyportenable; @JsonProperty("rtspRangeType") private String rtsprangetype; @JsonProperty("rtspRangeStart") private String rtsprangestart; @JsonProperty("sourceRedirect") private String sourceredirect; @JsonProperty("rpiCameraCamID") private int rpicameracamid; @JsonProperty("rpiCameraWidth") private int rpicamerawidth; @JsonProperty("rpiCameraHeight") private int rpicameraheight; @JsonProperty("rpiCameraHFlip") private boolean rpicamerahflip; @JsonProperty("rpiCameraVFlip") private boolean rpicameravflip; @JsonProperty("rpiCameraBrightness") private int rpicamerabrightness; @JsonProperty("rpiCameraContrast") private int rpicameracontrast; @JsonProperty("rpiCameraSaturation") private int rpicamerasaturation; @JsonProperty("rpiCameraSharpness") private int rpicamerasharpness; @JsonProperty("rpiCameraExposure") private String rpicameraexposure; @JsonProperty("rpiCameraAWB") private String rpicameraawb; @JsonProperty("rpiCameraDenoise") private String rpicameradenoise; @JsonProperty("rpiCameraShutter") private int rpicamerashutter; @JsonProperty("rpiCameraMetering") private String rpicamerametering; @JsonProperty("rpiCameraGain") private int rpicameragain; @JsonProperty("rpiCameraEV") private int rpicameraev; @JsonProperty("rpiCameraROI") private String rpicameraroi; @JsonProperty("rpiCameraTuningFile") private String rpicameratuningfile; @JsonProperty("rpiCameraMode") private String rpicameramode; @JsonProperty("rpiCameraFPS") private int rpicamerafps; @JsonProperty("rpiCameraIDRPeriod") private int rpicameraidrperiod; @JsonProperty("rpiCameraBitrate") private int rpicamerabitrate; @JsonProperty("rpiCameraProfile") private String rpicameraprofile; @JsonProperty("rpiCameraLevel") private String rpicameralevel; @JsonProperty("rpiCameraAfMode") private String rpicameraafmode; @JsonProperty("rpiCameraAfRange") private String rpicameraafrange; @JsonProperty("rpiCameraAfSpeed") private String rpicameraafspeed; @JsonProperty("rpiCameraLensPosition") private int rpicameralensposition; @JsonProperty("rpiCameraAfWindow") private String rpicameraafwindow; @JsonProperty("rpiCameraTextOverlayEnable") private boolean rpicameratextoverlayenable; @JsonProperty("rpiCameraTextOverlay") private String rpicameratextoverlay; @JsonProperty("runOnInit") private String runoninit; @JsonProperty("runOnInitRestart") private boolean runoninitrestart; @JsonProperty("runOnDemand") private String runondemand; @JsonProperty("runOnDemandRestart") private boolean runondemandrestart; @JsonProperty("runOnDemandStartTimeout") private String runondemandstarttimeout; @JsonProperty("runOnDemandCloseAfter") private String runondemandcloseafter; @JsonProperty("runOnReady") private String runonready; @JsonProperty("runOnReadyRestart") private boolean runonreadyrestart; @JsonProperty("runOnRead") private String runonread; @JsonProperty("runOnReadRestart") private boolean runonreadrestart; @JsonProperty("maxReaders") private boolean sourceOnDemand; private String sourceProtocol; private Integer maxReaders; private String runOnDemand; private boolean runOnDemandRestart; private String runOnDemandStartTimeout; private String runOnDemandCloseAfter; } ard-work/src/main/java/com/ruoyi/media/domain/Items.java
@@ -19,18 +19,9 @@ public class Items { private String name; private String confName; private Conf conf; private Source source; private List<Readers> readers; private boolean sourceReady; private List<String> tracks; private String mode; private String id; private Date created; private String remoteAddr; private String state; private long bytesReceived; private long bytesSent; private Long bytesReceived; } ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
@@ -1,9 +1,6 @@ package com.ruoyi.media.service; import com.dtflys.forest.annotation.Var; import com.ruoyi.media.domain.*; import java.lang.reflect.MalformedParameterizedTypeException; import java.util.List; import java.util.Map; ard-work/src/main/java/com/ruoyi/media/service/IMediaV2Service.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,65 @@ package com.ruoyi.media.service; import com.ruoyi.media.domain.*; import java.util.List; import java.util.Map; public interface IMediaV2Service { /** * å¢å è·¯å¾ * name åç§° * rtspPath rtspå°å * mode 模å¼ï¼gpu硬解ç /cpu软解ç * isCode æ¯å¦è½¬ç * åèä¹ * 2023/8/12 13:56:52 */ Map<String, String> addPath(String name, String sourceUrl, String mode, String isCode); /** * ä¿®æ¹è·¯å¾ * name åç§° * rtspPath rtspå°å * mode 模å¼ï¼å®æ¶/æé * isCode æ¯å¦è½¬ç * åèä¹ * 2023/8/12 13:56:52 */ Map<String, String> editPath(String name, String sourceUrl, String mode, String isCode); StreamInfo getPathInfo(String name); void removePath(String[] names); void removePath(String name); List<StreamInfo> paths(); List<String> getNameList(); boolean checkNameExist(String name); RtspSession getRtspSessionById(String sessionId); WebrtcSession getWebrtcSessionById(String sessionId); RtmpSession getRtmpSessionById(String sessionId); List<StreamInfo> getPushStreamList(); List<StreamInfo> getPullStreamList(); Boolean kickRtspSession(String sessionId); Boolean kickRtmpSession(String sessionId); Boolean kickWebrtcSession(String sessionId); /** * é ç½®æµåªä½åæ° * åèä¹ * 2023/10/13 15:17:57 */ public String setConfig(Config config); } ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
@@ -1,31 +1,18 @@ package com.ruoyi.media.service.impl; import com.alibaba.fastjson2.JSONObject; import com.dtflys.forest.Forest; import com.dtflys.forest.exceptions.ForestNetworkException; import com.dtflys.forest.exceptions.ForestRuntimeException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.media.domain.*; import com.ruoyi.media.mapper.VtduMapper; import com.ruoyi.media.service.IMediaService; import com.ruoyi.utils.forest.MediaClient; import com.ruoyi.utils.tools.ArdTool; import com.ruoyi.utils.process.CmdUtils; import com.sun.jna.Platform; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.io.File; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -65,34 +52,27 @@ String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; Conf mediaInfo = new Conf(); Conf conf = new Conf(); String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/"; if (isCode.equals("1")) { mediaInfo.setSource("publisher"); conf.setSource("publisher"); //é»è®¤è½¯è§£ç String cmd = "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; if (mode.equals("0")) {//硬解ç cmd = "ffmpeg -hwaccel cuvid -c:v hevc_cuvid -rtsp_transport tcp -i " + sourceUrl + " -c:v h264_nvenc -r 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -bf 0 -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; } mediaInfo.setRunondemand(cmd); mediaInfo.setRunondemandrestart(true); mediaInfo.setRunondemandcloseafter("5s"); conf.setRunOnDemand(cmd); conf.setRunOnDemandRestart(true); conf.setRunOnDemandCloseAfter("5s"); } else { mediaInfo.setSource(sourceUrl); mediaInfo.setSourceondemand(true); conf.setSource(sourceUrl); conf.setSourceOnDemand(true); } mediaInfo.setMaxReaders(100); mediaInfo.setSourceprotocol("tcp"); conf.setMaxReaders(100); conf.setSourceProtocol("tcp"); List<String> nameList = new ArrayList<>(); String paths = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(paths, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { nameList.add(item.getName()); } if (!nameList.contains(name)) { mediaClient.addPath(name, mediaInfo); if (!checkNameExist(name)) { mediaClient.addPath(name, conf); } Map<String, String> map = new HashMap<>(); map.put("rtspUrl", rtspUrl); @@ -109,26 +89,27 @@ String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; Conf mediaInfo = new Conf(); Conf conf = new Conf(); String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/"; if (isCode.equals("1")) { mediaInfo.setSource("publisher"); conf.setSource("publisher"); //é»è®¤è½¯è§£ç String cmd = "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; if (mode.equals("0")) {//硬解ç cmd = "ffmpeg -hwaccel cuvid -c:v hevc_cuvid -rtsp_transport tcp -i " + sourceUrl + " -c:v h264_nvenc -r 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -bf 0 -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; } mediaInfo.setRunondemand(cmd); mediaInfo.setRunondemandrestart(true); mediaInfo.setRunondemandcloseafter("5s"); conf.setRunOnDemand(cmd); conf.setRunOnDemandRestart(true); conf.setRunOnDemandCloseAfter("5s"); } else { mediaInfo.setSource(sourceUrl); conf.setSource(sourceUrl); conf.setSourceOnDemand(true); } mediaInfo.setMaxReaders(100); mediaInfo.setSourceprotocol("tcp"); conf.setMaxReaders(100); conf.setSourceProtocol("tcp"); if (checkNameExist(name)) { mediaClient.editPath(name, mediaInfo); mediaClient.editPath(name, conf); } map.put("rtspUrl", rtspUrl); @@ -142,18 +123,14 @@ @Override public StreamInfo getPathInfo(String name) { Items item = mediaClient.getPathInfo(name); Conf conf = mediaClient.getPathInfo(name); StreamInfo info = new StreamInfo(); //ID info.setName(name); String runOn; if (StringUtils.isNotEmpty(item.getConf().getRunondemand())) { runOn = item.getConf().getRunondemand(); String runOn = ""; if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); info.setMode("0"); } else { //runOn = item.getConf().getRunonready(); runOn = item.getConf().getRunoninit(); info.setMode("1"); } //RTSPæºå°å Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); @@ -161,7 +138,7 @@ info.setRtspSource(matcher.group()); info.setIsCode("1"); } else { info.setRtspSource(item.getConf().getSource()); info.setRtspSource(conf.getSource()); info.setIsCode("0"); } return info; @@ -170,14 +147,15 @@ @Override public void removePath(String[] names) { for (String name : names) { if(checkNameExist(name)) { mediaClient.removePath(name);} if (checkNameExist(name)) { mediaClient.removePath(name); } } } @Override public void removePath(String name) { if(checkNameExist(name)) { if (checkNameExist(name)) { mediaClient.removePath(name); } } @@ -193,9 +171,11 @@ //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathInfo(name); String runOn = ""; if (StringUtils.isNotEmpty(item.getConf().getRunondemand())) { runOn = item.getConf().getRunondemand(); if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); } //RTSPæºå°å Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); @@ -203,7 +183,7 @@ info.setRtspSource(matcher.group()); info.setIsCode("1"); } else { info.setRtspSource(item.getConf().getSource()); info.setRtspSource(conf.getSource()); info.setIsCode("0"); } //ä¼ è¾åè®® @@ -216,7 +196,6 @@ } return pathInfoList; } @Override public RtspSession getRtspSessionById(String sessionId) { @@ -255,6 +234,9 @@ //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathInfo(name); //RTMPææ¾å°å String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; info.setRtmpUrl(rtmpUrl); @@ -291,17 +273,17 @@ } //RTSPæºå°å String runOn = ""; if (StringUtils.isNotEmpty(item.getConf().getRunondemand())) { runOn = item.getConf().getRunondemand(); if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); } Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); if (matcher.find()) { info.setRtspSource(matcher.group()); } else { info.setRtspSource(item.getConf().getSource()); info.setRtspSource(conf.getSource()); } //ä¼ è¾åè®® info.setProtocol(item.getConf().getSourceprotocol()); info.setProtocol(conf.getSourceProtocol()); //ææµæ°é List<Readers> readers = item.getReaders(); info.setNum(readers.size()); @@ -329,8 +311,9 @@ //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathInfo(name); //ä¼ è¾åè®® info.setProtocol(item.getConf().getSourceprotocol()); info.setProtocol(conf.getSourceProtocol()); String type = reader.getType(); switch (type) { @@ -461,17 +444,11 @@ @Override public List<String> getNameList() { List<String> nameList = new ArrayList<>(); try { String paths = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(paths, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { nameList.add(item.getName()); } } catch (Exception ex) { log.error(ex.getMessage()); String paths = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(paths, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { nameList.add(item.getName()); } return nameList; } ard-work/src/main/java/com/ruoyi/media/service/impl/MediaV2ServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,490 @@ package com.ruoyi.media.service.impl; import com.alibaba.fastjson2.JSONObject; import com.dtflys.forest.exceptions.ForestNetworkException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.media.domain.*; import com.ruoyi.media.service.IMediaService; import com.ruoyi.media.service.IMediaV2Service; import com.ruoyi.utils.forest.MediaClient; import com.ruoyi.utils.tools.ArdTool; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @Description: æµåªä½ä¸å¡ * @ClassName: MediaService * @Author: åèä¹ * @Date: 2023å¹´07æ13æ¥9:28 * @Version: 1.0 **/ @Service @Slf4j(topic = "cmd") @Order(2) public class MediaV2ServiceImpl implements IMediaV2Service { @Resource MediaClient mediaClient; @Value("${mediamtx.host}") String mediamtxHost; /** * æ·»å æµåªä½ * name ç¸æºID * sourceUrl rtspå°å * isCode 0-ä¸è½¬ç 1-转ç * mode 0-gpu硬解ç 1-cpu软解ç * <p> * åèä¹ * 2023/10/12 9:03:41 */ @Override public Map<String, String> addPath(String name, String sourceUrl, String mode, String isCode) { String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name; String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; Conf conf = new Conf(); String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/"; if (isCode.equals("1")) { conf.setSource("publisher"); //é»è®¤è½¯è§£ç String cmd = "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; if (mode.equals("0")) {//硬解ç cmd = "ffmpeg -hwaccel cuvid -c:v hevc_cuvid -rtsp_transport tcp -i " + sourceUrl + " -c:v h264_nvenc -r 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -bf 0 -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; } conf.setRunOnDemand(cmd); conf.setRunOnDemandRestart(true); conf.setRunOnDemandCloseAfter("5s"); } else { conf.setSource(sourceUrl); conf.setRunOnDemandRestart(true); } conf.setMaxReaders(100); conf.setSourceProtocol("tcp"); List<String> nameList = new ArrayList<>(); String paths = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(paths, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { nameList.add(item.getName()); } if (!nameList.contains(name)) { mediaClient.addPath(name, conf); } Map<String, String> map = new HashMap<>(); map.put("rtspUrl", rtspUrl); map.put("rtmpUrl", rtmpUrl); map.put("webrtcUrl", webrtcUrl); return map; } @Override public Map<String, String> editPath(String name, String sourceUrl, String mode, String isCode) { Map<String, String> map = new HashMap<>(); try { String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name; String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; Conf mediaInfo = new Conf(); String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/"; if (isCode.equals("1")) { mediaInfo.setSource("publisher"); //é»è®¤è½¯è§£ç String cmd = "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; if (mode.equals("0")) {//硬解ç cmd = "ffmpeg -hwaccel cuvid -c:v hevc_cuvid -rtsp_transport tcp -i " + sourceUrl + " -c:v h264_nvenc -r 25 -g 60 -sc_threshold 0 -threads 6 -b:v 2048k -bf 0 -acodec opus -strict -2 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; } mediaInfo.setRunOnDemand(cmd); mediaInfo.setRunOnDemandRestart(true); mediaInfo.setRunOnDemandCloseAfter("5s"); } else { mediaInfo.setSource(sourceUrl); } mediaInfo.setMaxReaders(100); mediaInfo.setSourceProtocol("tcp"); if (checkNameExist(name)) { mediaClient.editPath(name, mediaInfo); } map.put("rtspUrl", rtspUrl); map.put("rtmpUrl", rtmpUrl); map.put("webrtcUrl", webrtcUrl); } catch (ForestNetworkException ex) { log.error(ex.getMessage()); } return map; } @Override public StreamInfo getPathInfo(String name) { Conf conf = mediaClient.getPathInfo(name); StreamInfo info = new StreamInfo(); //ID info.setName(name); String runOn=""; if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); } //RTSPæºå°å Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); if (matcher.find()) { info.setRtspSource(matcher.group()); info.setIsCode("1"); } else { info.setRtspSource(conf.getSource()); info.setIsCode("0"); } return info; } @Override public void removePath(String[] names) { for (String name : names) { if(checkNameExist(name)) { mediaClient.removePath(name);} } } @Override public void removePath(String name) { if(checkNameExist(name)) { mediaClient.removePath(name); } } @Override public List<StreamInfo> paths() { String list = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(list, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); List<StreamInfo> pathInfoList = new ArrayList<>(); for (Items item : items) { StreamInfo info = new StreamInfo(); //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathInfo(name); String runOn = ""; if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); } //RTSPæºå°å Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); if (matcher.find()) { info.setRtspSource(matcher.group()); info.setIsCode("1"); } else { info.setRtspSource(conf.getSource()); info.setIsCode("0"); } //ä¼ è¾åè®® matcher = Pattern.compile("-rtsp_transport\\s+(\\w+)").matcher(runOn); if (matcher.find()) { info.setProtocol(matcher.group(1)); } pathInfoList.add(info); } return pathInfoList; } @Override public RtspSession getRtspSessionById(String sessionId) { String list = mediaClient.getRtspsessionById(sessionId); RtspSession rtspSession = JSONObject.parseObject(list, RtspSession.class); return rtspSession; } @Override public WebrtcSession getWebrtcSessionById(String sessionId) { String list = mediaClient.getWebrtcsessionById(sessionId); WebrtcSession webrtcSession = JSONObject.parseObject(list, WebrtcSession.class); return webrtcSession; } @Override public RtmpSession getRtmpSessionById(String sessionId) { String list = mediaClient.getRtmpsessionById(sessionId); RtmpSession rtmpSession = JSONObject.parseObject(list, RtmpSession.class); return rtmpSession; } /** * è·åæ¨æµå表 * åèä¹ * 2023/8/29 9:37:05 */ @Override public List<StreamInfo> getPushStreamList() { List<StreamInfo> PushStreamInfoList = new ArrayList<>(); String list = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(list, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { StreamInfo info = new StreamInfo(); //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathInfo(name); //RTMPææ¾å°å String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name; info.setRtmpUrl(rtmpUrl); //RTSPææ¾å°å String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name; info.setRtspUrl(rtspUrl); //WEBRTCææ¾å°å String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name; info.setWebrtcUrl(webrtcUrl); Source source = item.getSource(); if (source == null || source.getId().equals("")) { //ä¼è¯ID info.setId("0"); //ä¸è¡æµé long bytesReceived = item.getBytesReceived(); String formatReceivedSize = ArdTool.formatFileSize(bytesReceived); info.setUpTraffic(formatReceivedSize); } else { RtspSession rtspSession = getRtspSessionById(source.getId()); //ä¼è¯ID info.setId(rtspSession.getId()); //å¼å§æ¨æµæ¶é´ info.setBeginTime(rtspSession.getCreated()); //ä¸è¡æµé long bytesReceived = rtspSession.getBytesReceived(); String formatReceivedSize = ArdTool.formatFileSize(bytesReceived); info.setUpTraffic(formatReceivedSize); //ä¸è¡æµé long bytesSent = rtspSession.getBytesSent(); String formatSentSize = ArdTool.formatFileSize(bytesSent); info.setDownTraffic(formatSentSize); //æ¨æµæå¡å¨ info.setRemoteAddr(rtspSession.getRemoteAddr()); } //RTSPæºå°å String runOn = ""; if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); } Matcher matcher = Pattern.compile("rtsp://[^\\s\"]+").matcher(runOn); if (matcher.find()) { info.setRtspSource(matcher.group()); } else { info.setRtspSource(conf.getSource()); } //ä¼ è¾åè®® info.setProtocol(conf.getSourceProtocol()); //ææµæ°é List<Readers> readers = item.getReaders(); info.setNum(readers.size()); PushStreamInfoList.add(info); } return PushStreamInfoList; } /** * è·åææµå表 * åèä¹ * 2023/8/29 9:37:05 */ @Override public List<StreamInfo> getPullStreamList() { List<StreamInfo> PullStreamInfoList = new ArrayList<>(); String list = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(list, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { List<Readers> readers = item.getReaders(); for (Readers reader : readers) { StreamInfo info = new StreamInfo(); //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathInfo(name); //ä¼ è¾åè®® info.setProtocol(conf.getSourceProtocol()); String type = reader.getType(); switch (type) { case "rtmpConn": info.setSessionType("rtmp"); //webrtcææ¾å°å String url = "rtmp://" + mediamtxHost + ":1935/" + name; info.setRtspUrl(url); RtmpSession rtmpSession = getRtmpSessionById(reader.getId()); //ä¼è¯ID info.setId(rtmpSession.getId()); //å¼å§ææµæ¶é´ info.setBeginTime(rtmpSession.getCreated()); //ä¸è¡æµé long bytesReceived = rtmpSession.getBytesReceived(); String formatReceivedSize = ArdTool.formatFileSize(bytesReceived); info.setUpTraffic(formatReceivedSize); //ä¸è¡æµé long bytesSent = rtmpSession.getBytesSent(); String formatSentSize = ArdTool.formatFileSize(bytesSent); info.setDownTraffic(formatSentSize); //ææµæå¡å¨ info.setRemoteAddr(rtmpSession.getRemoteAddr()); PullStreamInfoList.add(info); break; case "webRTCSession": info.setSessionType("webrtc"); //webrtcææ¾å°å url = "http://" + mediamtxHost + ":8889/" + name; info.setRtspUrl(url); WebrtcSession webrtcSession = getWebrtcSessionById(reader.getId()); //ä¼è¯ID info.setId(webrtcSession.getId()); //å¼å§ææµæ¶é´ info.setBeginTime(webrtcSession.getCreated()); //ä¸è¡æµé bytesReceived = webrtcSession.getBytesReceived(); formatReceivedSize = ArdTool.formatFileSize(bytesReceived); info.setUpTraffic(formatReceivedSize); //ä¸è¡æµé bytesSent = webrtcSession.getBytesSent(); formatSentSize = ArdTool.formatFileSize(bytesSent); info.setDownTraffic(formatSentSize); //ææµæå¡å¨ info.setRemoteAddr(webrtcSession.getRemoteAddr()); PullStreamInfoList.add(info); break; case "rtspSession": info.setSessionType("rtsp"); //RTSPææ¾å°å String rtspUrl = "rtsp://" + mediamtxHost + ":8554/" + name; info.setRtspUrl(rtspUrl); RtspSession rtspSession = getRtspSessionById(reader.getId()); //ä¼è¯ID info.setId(rtspSession.getId()); //å¼å§ææµæ¶é´ info.setBeginTime(rtspSession.getCreated()); //ä¸è¡æµé bytesReceived = rtspSession.getBytesReceived(); formatReceivedSize = ArdTool.formatFileSize(bytesReceived); info.setUpTraffic(formatReceivedSize); //ä¸è¡æµé bytesSent = rtspSession.getBytesSent(); formatSentSize = ArdTool.formatFileSize(bytesSent); info.setDownTraffic(formatSentSize); //ææµæå¡å¨ info.setRemoteAddr(rtspSession.getRemoteAddr()); PullStreamInfoList.add(info); break; } } } Comparator<StreamInfo> comparator = Comparator.comparing(streamInfo -> streamInfo.getBeginTime()); // 使ç¨Collections.sortæ¹æ³è¿è¡æåº Collections.sort(personList, comparator); Collections.sort(PullStreamInfoList, comparator.reversed()); return PullStreamInfoList; } /** * 踢åºrtspä¼è¯ * åèä¹ * 2023/8/29 9:37:05 */ @Override public Boolean kickRtspSession(String sessionId) { try { mediaClient.kickRtspSessions(sessionId); return true; } catch (Exception ex) { return false; } } /** * 踢åºrtmpä¼è¯ * åèä¹ * 2023/8/29 9:37:05 */ @Override public Boolean kickRtmpSession(String sessionId) { try { mediaClient.kickRtmpSessions(sessionId); return true; } catch (Exception ex) { return false; } } /** * 踢åºwebrtcä¼è¯ * åèä¹ * 2023/8/29 9:37:05 */ @Override public Boolean kickWebrtcSession(String sessionId) { try { mediaClient.kickWebrtcSessions(sessionId); return true; } catch (Exception ex) { return false; } } /** * è·åæµåªä½nameå表 * åèä¹ * 2023/10/13 14:19:07 */ @Override public List<String> getNameList() { List<String> nameList = new ArrayList<>(); try { String paths = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(paths, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { nameList.add(item.getName()); } } catch (Exception ex) { log.error(ex.getMessage()); } return nameList; } /** * æ£æ¥åç§°æ¯å¦åå¨ * åèä¹ * 2023/10/19 15:18:45 */ @Override public boolean checkNameExist(String name) { boolean result = false; List<String> nameList = getNameList(); if (nameList.contains(name)) { result = true; } return result; } /** * é ç½®æµåªä½åæ° */ @Override public String setConfig(Config config) { return mediaClient.setConfig(config); } } ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java
@@ -1,21 +1,11 @@ package com.ruoyi.media.service.impl; import java.util.HashMap; import java.util.List; import java.util.Map; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.camera.service.ICameraSdkService; import com.ruoyi.media.service.IMediaService; import com.ruoyi.utils.forest.MediaClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import com.ruoyi.media.mapper.VtduMapper; import com.ruoyi.media.domain.Vtdu; @@ -30,7 +20,7 @@ * @date 2023-08-29 */ @Service @Slf4j @Slf4j(topic = "sdk") public class VtduServiceImpl implements IVtduService { @Resource private VtduMapper vtduMapper; @@ -68,7 +58,7 @@ */ @Override public int insertVtdu(Vtdu vtdu) { log.info("æµåªä½ã" + vtdu.getName() + "ãééæ·»å "); 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")); @@ -91,7 +81,7 @@ @Override public int updateVtdu(Vtdu vtdu) { log.info("æµåªä½ã" + vtdu.getName() + "ãééæ´æ°"); 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()); @@ -112,6 +102,9 @@ */ @Override public int deleteVtduByNames(String[] names) { for (String name : names) { log.debug("æµåªä½ã" + name + "ãééå é¤"); } mediaService.removePath(names); return vtduMapper.deleteVtduByNames(names); } @@ -124,6 +117,7 @@ */ @Override public int deleteVtduByName(String name) { log.debug("æµåªä½ã" + name + "ãééå é¤"); mediaService.removePath(name); return vtduMapper.deleteVtduByName(name); } ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
@@ -1,7 +1,6 @@ package com.ruoyi.utils.forest; import com.dtflys.forest.annotation.*; import com.dtflys.forest.callback.OnError; import com.ruoyi.media.domain.Conf; import com.ruoyi.media.domain.Config; import com.ruoyi.media.domain.Items; @@ -13,7 +12,7 @@ * @Date: 2023å¹´07æ06æ¥9:51 * @Version: 1.0 **/ @BaseRequest(baseURL = "http://#{mediamtx.host}:9997/v2") @BaseRequest(baseURL = "http://#{mediamtx.host}:9997/v3") public interface MediaClient { /** * å¢å è·¯å¾ @@ -24,20 +23,20 @@ /** * ä¿®æ¹è·¯å¾ */ @Post(url = "/config/paths/edit/{name}", async = true) @Patch(url = "/config/paths/patch/{name}") public String editPath(@Var("name") String name, @JSONBody Conf body); /** * ç§»é¤è·¯å¾ */ @Post("/config/paths/remove/{name}") @Delete("/config/paths/delete/{name}") public String removePath(@Var("name") String name); /** * è·åè·¯å¾è¯¦æ */ @Get("/paths/get/{name}") public Items getPathInfo(@Var("name") String name); @Get("/config/paths/get/{name}") public Conf getPathInfo(@Var("name") String name); /** * æ¥è¯¢ææè·¯å¾ @@ -50,12 +49,6 @@ */ @Get("/rtspsessions/list") public String rtspsessions(); /** * æ¥è¯¢æærtspè¿æ¥ */ @Get("/rtspconns/list") public String rtspconns(); /** * æsessionIdæ¥è¯¢rtspä¼è¯ ard-work/src/main/java/com/ruoyi/utils/forest/MediaClientV2.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,100 @@ 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; /** * @Description: mediamtxæµåªä½å®¢æ·ç«¯ * @ClassName: client * @Author: åèä¹ * @Date: 2023å¹´07æ06æ¥9:51 * @Version: 1.0 **/ @BaseRequest(baseURL = "http://#{mediamtx.host}:9997/v2") public interface MediaClientV2 { /** * å¢å è·¯å¾ */ @Post(url = "/config/paths/add/{name}") public String addPath(@Var("name") String name, @JSONBody Conf body); /** * ä¿®æ¹è·¯å¾ */ @Post(url = "/config/paths/edit/{name}", async = true) public String editPath(@Var("name") String name, @JSONBody Conf body); /** * ç§»é¤è·¯å¾ */ @Post("/config/paths/remove/{name}") public String removePath(@Var("name") String name); /** * è·åè·¯å¾è¯¦æ */ @Get("/paths/get/{name}") public Items getPathInfo(@Var("name") String name); /** * æ¥è¯¢ææè·¯å¾ */ @Get("/paths/list") public String paths(); /** * æ¥è¯¢æærtspä¼è¯ */ @Get("/rtspsessions/list") public String rtspsessions(); /** * æ¥è¯¢æærtspè¿æ¥ */ @Get("/rtspconns/list") public String rtspconns(); /** * æsessionIdæ¥è¯¢rtspä¼è¯ */ @Get("/rtspsessions/get/{sessionId}") public String getRtspsessionById(@Var("sessionId") String sessionId); /** * æsessionIdæ¥è¯¢webrtcä¼è¯ */ @Get("/webrtcsessions/get/{sessionId}") public String getWebrtcsessionById(@Var("sessionId") String sessionId); /** * æsessionIdæ¥è¯¢rtmpä¼è¯ */ @Get("/rtmpconns/get/{sessionId}") public String getRtmpsessionById(@Var("sessionId") String sessionId); /** * æsessionIdå é¤rtspä¼è¯ */ @Post("/rtspsessions/kick/{sessionId}") public String kickRtspSessions(@Var("sessionId") String sessionId); /** * æsessionIdå é¤rtmpè¿æ¥ */ @Post("/rtmpconns/kick/{sessionId}") public String kickRtmpSessions(@Var("sessionId") String sessionId); /** * æsessionIdå é¤webrtcä¼è¯ */ @Post("/webrtcsessions/kick/{sessionId}") public String kickWebrtcSessions(@Var("sessionId") String sessionId); /** * é ç½®æµåªä½åæ° */ @Post("/config/set") public String setConfig(@JSONBody Config config); } ard-work/src/main/resources/templates/preview.html
@@ -190,6 +190,9 @@ console.log("requesting ICE servers"); fetch(this.wurl, { method: 'OPTIONS', headers: { 'Referer': this.wurl, }, }) .then((res) => this.onIceServers(res)) .catch((err) => { @@ -229,6 +232,7 @@ method: 'POST', headers: { 'Content-Type': 'application/sdp', 'Referer': this.wurl, }, body: offer.sdp, }) @@ -296,7 +300,9 @@ headers: { 'Content-Type': 'application/trickle-ice-sdpfrag', 'If-Match': this.eTag, 'Referer': this.wurl, }, body: generateSdpFragment(this.offerData, candidates), }) .then((res) => { lib/mediamtx/LICENSE
@@ -19,3 +19,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. internal/core/hls.min.js is Copyright (c) Dailymotion and is protected by its own license (Apache License, Version 2.0) available at https://github.com/video-dev/hls.js/blob/master/LICENSE lib/mediamtx/mediamtx.exeBinary files differ
lib/mediamtx/mediamtx.yml
@@ -1,8 +1,12 @@ ############################################### # Global settings # Settings in this section are applied anywhere. ############################################### # General settings # Global settings -> General # Sets the verbosity of the program; available values are "error", "warn", "info", "debug". # Verbosity of the program; available values are "error", "warn", "info", "debug". logLevel: info # Destinations of log messages; available values are "stdout", "file" and "syslog". logDestinations: [stdout] @@ -40,29 +44,33 @@ # Enable the HTTP API. api: yes # Address of the API listener. apiAddress: 192.168.1.227:9997 apiAddress: 112.98.126.2:9997 # Enable Prometheus-compatible metrics. metrics: no # Address of the metrics listener. metricsAddress: 127.0.0.1:9998 metricsAddress: 112.98.126.2:9998 # Enable pprof-compatible endpoint to monitor performances. pprof: no # Address of the pprof listener. pprofAddress: 127.0.0.1:9999 pprofAddress: 112.98.126.2:9999 # Command to run when a client connects to the server. # Prepend ./ to run an executable in the current folder (example: "./ffmpeg") # This is terminated with SIGINT when a client disconnects from the server. # The following environment variables are available: # * RTSP_PORT: RTSP server port # * MTX_CONN_TYPE: connection type # * MTX_CONN_ID: connection ID runOnConnect: # Restart the command if it exits. runOnConnectRestart: no # Command to run when a client disconnects from the server. # Environment variables are the same of runOnConnect. runOnDisconnect: ############################################### # RTSP settings # Global settings -> RTSP # Allow publishing and reading streams with the RTSP protocol. rtsp: yes @@ -77,19 +85,19 @@ # Available values are "no", "strict", "optional". encryption: "no" # Address of the TCP/RTSP listener. This is needed only when encryption is "no" or "optional". rtspAddress: :7554 rtspAddress: :8554 # Address of the TCP/TLS/RTSPS listener. This is needed only when encryption is "strict" or "optional". rtspsAddress: :7322 rtspsAddress: :8322 # Address of the UDP/RTP listener. This is needed only when "udp" is in protocols. rtpAddress: :7000 rtpAddress: :8000 # Address of the UDP/RTCP listener. This is needed only when "udp" is in protocols. rtcpAddress: :7001 rtcpAddress: :8001 # IP range of all UDP-multicast listeners. This is needed only when "multicast" is in protocols. multicastIPRange: 224.1.0.0/16 # Port of all UDP-multicast/RTP listeners. This is needed only when "multicast" is in protocols. multicastRTPPort: 7002 multicastRTPPort: 8002 # Port of all UDP-multicast/RTCP listeners. This is needed only when "multicast" is in protocols. multicastRTCPPort: 7003 multicastRTCPPort: 8003 # Path to the server key. This is needed only when encryption is "strict" or "optional". # This can be generated with: # openssl genrsa -out server.key 2048 @@ -102,7 +110,7 @@ authMethods: [basic] ############################################### # RTMP settings # Global settings -> RTMP # Allow publishing and reading streams with the RTMP protocol. rtmp: yes @@ -122,7 +130,7 @@ rtmpServerCert: server.crt ############################################### # HLS settings # Global settings -> HLS # Allow reading streams with the HLS protocol. hls: no @@ -178,7 +186,7 @@ hlsDirectory: '' ############################################### # WebRTC settings # Global settings -> WebRTC # Allow publishing and reading streams with the WebRTC protocol. webrtc: yes @@ -207,27 +215,30 @@ # needed when server and clients are on different LANs. # TURN/TURNS servers are needed when a direct connection between server and # clients is not possible. All traffic is routed through them. - url: stun:stun.l.google.com:19302 - url: stun:112.98.126.2:3478 # if user is "AUTH_SECRET", then authentication is secret based. # the secret must be inserted into the password field. username: '' password: '' username: 'admin' password: '123456' # List of interfaces that will be used to gather IPs to send # to the counterpart to establish a connection. 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: [192.168.1.227] webrtcICEHostNAT1To1IPs: [112.98.126.2] # 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: 192.168.1.227:8189 webrtcICEUDPMuxAddress: # 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:8189 webrtcICETCPMuxAddress: 112.98.126.2:1234 ############################################### # SRT settings # Global settings -> SRT # Allow publishing and reading streams with the SRT protocol. srt: yes @@ -235,246 +246,311 @@ srtAddress: :8890 ############################################### # Default path settings # Settings in "pathDefaults" are applied anywhere, # unless they are overridden in "paths". pathDefaults: ############################################### # Default path settings -> General # Source of the stream. This can be: # * publisher -> the stream is provided by a RTSP, RTMP, WebRTC or SRT client # * rtsp://existing-url -> the stream is pulled from another RTSP server / camera # * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS # * rtmp://existing-url -> the stream is pulled from another RTMP server / camera # * rtmps://existing-url -> the stream is pulled from another RTMP server / camera with RTMPS # * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera # * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera with HTTPS # * udp://ip:port -> the stream is pulled with UDP, by listening on the specified IP and port # * srt://existing-url -> the stream is pulled from another SRT server / camera # * whep://existing-url -> the stream is pulled from another WebRTC server / camera # * wheps://existing-url -> the stream is pulled from another WebRTC server / camera with HTTPS # * redirect -> the stream is provided by another path or server # * rpiCamera -> the stream is provided by a Raspberry Pi Camera source: publisher # If the source is a URL, and the source certificate is self-signed # or invalid, you can provide the fingerprint of the certificate in order to # validate it anyway. It can be obtained by running: # openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt # openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':' sourceFingerprint: # If the source is a URL, it will be pulled only when at least # one reader is connected, saving bandwidth. sourceOnDemand: no # If sourceOnDemand is "yes", readers will be put on hold until the source is # ready or until this amount of time has passed. sourceOnDemandStartTimeout: 10s # If sourceOnDemand is "yes", the source will be closed when there are no # readers connected and this amount of time has passed. sourceOnDemandCloseAfter: 10s # Maximum number of readers. Zero means no limit. maxReaders: 0 # SRT encryption passphrase require to read from this path srtReadPassphrase: ############################################### # Default path settings -> Recording # Record streams to disk. record: no # Path of recording segments. # Extension is added automatically. # Available variables are %path (path name), %Y %m %d %H %M %S %f (time in strftime format) recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f # Format of recorded segments. # Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS). recordFormat: fmp4 # fMP4 segments are concatenation of small MP4 files (parts), each with this duration. # MPEG-TS segments are concatenation of 188-bytes packets, flushed to disk with this period. # When a system failure occurs, the last part gets lost. # Therefore, the part duration is equal to the RPO (recovery point objective). recordPartDuration: 100ms # Minimum duration of each segment. recordSegmentDuration: 1h # Delete segments after this timespan. # Set to 0s to disable automatic deletion. recordDeleteAfter: 24h ############################################### # Default path settings -> Authentication # Username required to publish. # SHA256-hashed values can be inserted with the "sha256:" prefix. publishUser: # Password required to publish. # SHA256-hashed values can be inserted with the "sha256:" prefix. publishPass: # IPs or networks (x.x.x.x/24) allowed to publish. publishIPs: [] # Username required to read. # SHA256-hashed values can be inserted with the "sha256:" prefix. readUser: # password required to read. # SHA256-hashed values can be inserted with the "sha256:" prefix. readPass: # IPs or networks (x.x.x.x/24) allowed to read. readIPs: [] ############################################### # Default path settings -> Publisher source (when source is "publisher") # allow another client to disconnect the current publisher and publish in its place. overridePublisher: yes # if no one is publishing, redirect readers to this path. # It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL. fallback: # SRT encryption passphrase required to publish to this path srtPublishPassphrase: ############################################### # Default path settings -> RTSP source (when source is a RTSP or a RTSPS URL) # protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp". sourceProtocol: automatic # support sources that don't provide server ports or use random server ports. This is a security issue # and must be used only when interacting with sources that require it. sourceAnyPortEnable: no # range header to send to the source, in order to start streaming from the specified offset. # available values: # * clock: Absolute time # * npt: Normal Play Time # * smpte: SMPTE timestamps relative to the start of the recording rtspRangeType: # available values: # * clock: UTC ISO 8601 combined date and time string, e.g. 20230812T120000Z # * npt: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" # * smpte: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" rtspRangeStart: ############################################### # Default path settings -> Redirect source (when source is "redirect") # RTSP URL which clients will be redirected to. sourceRedirect: ############################################### # Default path settings -> Raspberry Pi Camera source (when source is "rpiCamera") # ID of the camera rpiCameraCamID: 0 # width of frames rpiCameraWidth: 1920 # height of frames rpiCameraHeight: 1080 # flip horizontally rpiCameraHFlip: false # flip vertically rpiCameraVFlip: false # brightness [-1, 1] rpiCameraBrightness: 0 # contrast [0, 16] rpiCameraContrast: 1 # saturation [0, 16] rpiCameraSaturation: 1 # sharpness [0, 16] rpiCameraSharpness: 1 # exposure mode. # values: normal, short, long, custom rpiCameraExposure: normal # auto-white-balance mode. # values: auto, incandescent, tungsten, fluorescent, indoor, daylight, cloudy, custom rpiCameraAWB: auto # denoise operating mode. # values: off, cdn_off, cdn_fast, cdn_hq rpiCameraDenoise: "off" # fixed shutter speed, in microseconds. rpiCameraShutter: 0 # metering mode of the AEC/AGC algorithm. # values: centre, spot, matrix, custom rpiCameraMetering: centre # fixed gain rpiCameraGain: 0 # EV compensation of the image [-10, 10] rpiCameraEV: 0 # Region of interest, in format x,y,width,height rpiCameraROI: # whether to enable HDR on Raspberry Camera 3. rpiCameraHDR: false # tuning file rpiCameraTuningFile: # sensor mode, in format [width]:[height]:[bit-depth]:[packing] # bit-depth and packing are optional. rpiCameraMode: # frames per second rpiCameraFPS: 30 # period between IDR frames rpiCameraIDRPeriod: 60 # bitrate rpiCameraBitrate: 1000000 # H264 profile rpiCameraProfile: main # H264 level rpiCameraLevel: '4.1' # Autofocus mode # values: auto, manual, continuous rpiCameraAfMode: auto # Autofocus range # values: normal, macro, full rpiCameraAfRange: normal # Autofocus speed # values: normal, fast rpiCameraAfSpeed: normal # Lens position (for manual autofocus only), will be set to focus to a specific distance # calculated by the following formula: d = 1 / value # Examples: 0 moves the lens to infinity. # 0.5 moves the lens to focus on objects 2m away. # 2 moves the lens to focus on objects 50cm away. rpiCameraLensPosition: 0.0 # Specifies the autofocus window, in the form x,y,width,height where the coordinates # are given as a proportion of the entire image. rpiCameraAfWindow: # enables printing text on each frame. rpiCameraTextOverlayEnable: false # text that is printed on each frame. # format is the one of the strftime() function. rpiCameraTextOverlay: '%Y-%m-%d %H:%M:%S - MediaMTX' ############################################### # Default path settings -> Hooks # Command to run when this path is initialized. # This can be used to publish a stream when the server is launched. # This is terminated with SIGINT when the program closes. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. runOnInit: # Restart the command if it exits. runOnInitRestart: no # Command to run when this path is requested by a reader. # This can be used to publish a stream on demand. # This is terminated with SIGINT when the path is not requested anymore. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. runOnDemand: # Restart the command if it exits. runOnDemandRestart: no # Readers will be put on hold until the runOnDemand command starts publishing # or until this amount of time has passed. runOnDemandStartTimeout: 10s # The command will be closed when there are no # readers connected and this amount of time has passed. runOnDemandCloseAfter: 10s # Command to run when the stream is ready to be read, whenever it is # published by a client or pulled from a server / camera. # This is terminated with SIGINT when the stream is not ready anymore. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. # * MTX_SOURCE_TYPE: source type # * MTX_SOURCE_ID: source ID runOnReady: # Restart the command if it exits. runOnReadyRestart: no # Command to run when the stream is not available anymore. # Environment variables are the same of runOnReady. runOnNotReady: # Command to run when a client starts reading. # This is terminated with SIGINT when a client stops reading. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. # * MTX_READER_TYPE: reader type # * MTX_READER_ID: reader ID runOnRead: # Restart the command if it exits. runOnReadRestart: no # Command to run when a client stops reading. # Environment variables are the same of runOnRead. runOnUnread: # Command to run when a recording segment is created. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. # * MTX_SEGMENT_PATH: segment file path runOnRecordSegmentCreate: # Command to run when a recording segment is complete. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. # * MTX_SEGMENT_PATH: segment file path runOnRecordSegmentComplete: ############################################### # Path settings # These settings are path-dependent, and the map key is the name of the path. # Settings in "paths" are applied to specific paths, and the map key # is the name of the path. # Any setting in "pathDefaults" can be overridden here. # It's possible to use regular expressions by using a tilde as prefix, # for example "~^(test1|test2)$" will match both "test1" and "test2", # for example "~^prefix" will match all paths that start with "prefix". # Settings under the path "all" are applied to all paths that do not match # another entry. paths: all: ############################################### # General path settings # example: # my_camera: # source: rtsp://my_camera # Source of the stream. This can be: # * publisher -> the stream is provided by a RTSP, RTMP, WebRTC or SRT client # * rtsp://existing-url -> the stream is pulled from another RTSP server / camera # * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS # * rtmp://existing-url -> the stream is pulled from another RTMP server / camera # * rtmps://existing-url -> the stream is pulled from another RTMP server / camera with RTMPS # * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera # * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera with HTTPS # * udp://ip:port -> the stream is pulled with UDP, by listening on the specified IP and port # * srt://existing-url -> the stream is pulled from another SRT server / camera # * whep://existing-url -> the stream is pulled from another WebRTC server / camera # * wheps://existing-url -> the stream is pulled from another WebRTC server / camera with HTTPS # * redirect -> the stream is provided by another path or server # * rpiCamera -> the stream is provided by a Raspberry Pi Camera source: publisher # If the source is a URL, and the source certificate is self-signed # or invalid, you can provide the fingerprint of the certificate in order to # validate it anyway. It can be obtained by running: # openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt # openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':' sourceFingerprint: # If the source is a URL, it will be pulled only when at least # one reader is connected, saving bandwidth. sourceOnDemand: no # If sourceOnDemand is "yes", readers will be put on hold until the source is # ready or until this amount of time has passed. sourceOnDemandStartTimeout: 10s # If sourceOnDemand is "yes", the source will be closed when there are no # readers connected and this amount of time has passed. sourceOnDemandCloseAfter: 10s # Maximum number of readers. Zero means no limit. maxReaders: 0 ############################################### # Authentication path settings # Username required to publish. # SHA256-hashed values can be inserted with the "sha256:" prefix. publishUser: # Password required to publish. # SHA256-hashed values can be inserted with the "sha256:" prefix. publishPass: # IPs or networks (x.x.x.x/24) allowed to publish. publishIPs: [] # Username required to read. # SHA256-hashed values can be inserted with the "sha256:" prefix. readUser: # password required to read. # SHA256-hashed values can be inserted with the "sha256:" prefix. readPass: # IPs or networks (x.x.x.x/24) allowed to read. readIPs: [] ############################################### # Publisher path settings (when source is "publisher") # allow another client to disconnect the current publisher and publish in its place. overridePublisher: yes # if no one is publishing, redirect readers to this path. # It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL. fallback: ############################################### # RTSP path settings (when source is a RTSP or a RTSPS URL) # protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp". sourceProtocol: automatic # support sources that don't provide server ports or use random server ports. This is a security issue # and must be used only when interacting with sources that require it. sourceAnyPortEnable: no # range header to send to the source, in order to start streaming from the specified offset. # available values: # * clock: Absolute time # * npt: Normal Play Time # * smpte: SMPTE timestamps relative to the start of the recording rtspRangeType: # available values: # * clock: UTC ISO 8601 combined date and time string, e.g. 20230812T120000Z # * npt: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" # * smpte: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" rtspRangeStart: ############################################### # Redirect path settings (when source is "redirect") # RTSP URL which clients will be redirected to. sourceRedirect: ############################################### # Raspberry Pi Camera path settings (when source is "rpiCamera") # ID of the camera rpiCameraCamID: 0 # width of frames rpiCameraWidth: 1920 # height of frames rpiCameraHeight: 1080 # flip horizontally rpiCameraHFlip: false # flip vertically rpiCameraVFlip: false # brightness [-1, 1] rpiCameraBrightness: 0 # contrast [0, 16] rpiCameraContrast: 1 # saturation [0, 16] rpiCameraSaturation: 1 # sharpness [0, 16] rpiCameraSharpness: 1 # exposure mode. # values: normal, short, long, custom rpiCameraExposure: normal # auto-white-balance mode. # values: auto, incandescent, tungsten, fluorescent, indoor, daylight, cloudy, custom rpiCameraAWB: auto # denoise operating mode. # values: off, cdn_off, cdn_fast, cdn_hq rpiCameraDenoise: "off" # fixed shutter speed, in microseconds. rpiCameraShutter: 0 # metering mode of the AEC/AGC algorithm. # values: centre, spot, matrix, custom rpiCameraMetering: centre # fixed gain rpiCameraGain: 0 # EV compensation of the image [-10, 10] rpiCameraEV: 0 # Region of interest, in format x,y,width,height rpiCameraROI: # whether to enable HDR on Raspberry Camera 3. rpiCameraHDR: false # tuning file rpiCameraTuningFile: # sensor mode, in format [width]:[height]:[bit-depth]:[packing] # bit-depth and packing are optional. rpiCameraMode: # frames per second rpiCameraFPS: 30 # period between IDR frames rpiCameraIDRPeriod: 60 # bitrate rpiCameraBitrate: 1000000 # H264 profile rpiCameraProfile: main # H264 level rpiCameraLevel: '4.1' # Autofocus mode # values: auto, manual, continuous rpiCameraAfMode: auto # Autofocus range # values: normal, macro, full rpiCameraAfRange: normal # Autofocus speed # values: normal, fast rpiCameraAfSpeed: normal # Lens position (for manual autofocus only), will be set to focus to a specific distance # calculated by the following formula: d = 1 / value # Examples: 0 moves the lens to infinity. # 0.5 moves the lens to focus on objects 2m away. # 2 moves the lens to focus on objects 50cm away. rpiCameraLensPosition: 0.0 # Specifies the autofocus window, in the form x,y,width,height where the coordinates # are given as a proportion of the entire image. rpiCameraAfWindow: # enables printing text on each frame. rpiCameraTextOverlayEnable: false # text that is printed on each frame. # format is the one of the strftime() function. rpiCameraTextOverlay: '%Y-%m-%d %H:%M:%S - MediaMTX' ############################################### # External commands path settings # Command to run when this path is initialized. # This can be used to publish a stream and keep it always opened. # Prepend ./ to run an executable in the current folder (example: "./ffmpeg") # This is terminated with SIGINT when the program closes. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. runOnInit: # Restart the command if it exits. runOnInitRestart: no # Command to run when this path is requested. # This can be used to publish a stream on demand. # Prepend ./ to run an executable in the current folder (example: "./ffmpeg") # This is terminated with SIGINT when the path is not requested anymore. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. runOnDemand: # Restart the command if it exits. runOnDemandRestart: no # Readers will be put on hold until the runOnDemand command starts publishing # or until this amount of time has passed. runOnDemandStartTimeout: 10s # The command will be closed when there are no # readers connected and this amount of time has passed. runOnDemandCloseAfter: 10s # Command to run when the stream is ready to be read, whether it is # published by a client or pulled from a server / camera. # Prepend ./ to run an executable in the current folder (example: "./ffmpeg") # This is terminated with SIGINT when the stream is not ready anymore. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. runOnReady: # Restart the command if it exits. runOnReadyRestart: no # Command to run when a clients starts reading. # Prepend ./ to run an executable in the current folder (example: "./ffmpeg") # This is terminated with SIGINT when a client stops reading. # The following environment variables are available: # * MTX_PATH: path name # * RTSP_PORT: RTSP server port # * G1, G2, ...: regular expression groups, if path name is # a regular expression. runOnRead: # Restart the command if it exits. runOnReadRestart: no # Settings under path "all_others" are applied to all paths that # do not match another entry. all_others: ruoyi-admin/src/main/resources/logback.xml
@@ -107,6 +107,19 @@ <pattern>${log.pattern}</pattern> </encoder> </appender> <!--dhSdkæ¥å¿è¾åº--> <appender name="sdk" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/sdk.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--æå¤©åæ»daily--> <fileNamePattern>${log.path}/sdk.%d{yyyy-MM-dd}.log</fileNamePattern> <!--æ¥å¿æå¤§çåå²60天--> <maxHistory>60</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> </appender> <!--minioæ¥å¿è¾åº--> <appender name="minio" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/minio.log</file> @@ -262,6 +275,10 @@ <logger name="dhSdk" level="INFO"> <appender-ref ref="dhSdk"/> </logger> <!--sdkæ¥å¿--> <logger name="sdk" level="INFO"> <appender-ref ref="sdk"/> </logger> <!--mqttæ¥å¿--> <logger name="mqtt" level="INFO"> <appender-ref ref="mqtt"/> ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/SdkOperateAspect.java
@@ -29,7 +29,7 @@ */ @Aspect @Component @Slf4j(topic = "hikSdk") @Slf4j(topic = "sdk") public class SdkOperateAspect { @Resource ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/PushTask.java
@@ -9,11 +9,14 @@ import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.device.camera.domain.CameraCmd; import com.ruoyi.device.camera.service.ICameraSdkService; import com.ruoyi.device.hiksdk.common.GlobalVariable; import com.ruoyi.device.hiksdk.service.IHikClientService; import com.ruoyi.utils.websocket.util.WebSocketUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.*; import static com.ruoyi.utils.websocket.util.WebSocketUtils.ONLINE_USER_SESSIONS; @@ -28,6 +31,12 @@ @Slf4j public class PushTask { @Resource RedisCache redisCache; @Resource ICameraSdkService cameraSdkService; @Resource IGlobalAlarmService globalAlarmService; /** * @æè¿° 宿¶æ¨éæææ¥è¦çç¹ä½æ°é * @åæ° [] @@ -38,7 +47,6 @@ */ public void globalAlarmCountPush() { try { IGlobalAlarmService globalAlarmService = SpringUtils.getBean(IGlobalAlarmService.class); Map<String, Object> stringIntegerMap = globalAlarmService.selectAlarmLogsCount(); if (ONLINE_USER_SESSIONS.size() > 0) { WebSocketUtils.sendMessageAll(stringIntegerMap); @@ -59,8 +67,6 @@ */ public void ptzPush() { try { RedisCache redisCache = SpringUtils.getBean(RedisCache.class); IHikClientService hikClientService = SpringUtils.getBean(IHikClientService.class); List<Map<String, Object>> list = new ArrayList<>(); List<Object> Objects = redisCache.getListKey(CacheConstants.CAMERA_LIST_KEY); if (Objects.size() > 0) { @@ -70,7 +76,6 @@ { continue; } //æ¨é大å çµ if(!"1".equals(camera.getGdtype())) { continue; @@ -80,12 +85,12 @@ cmd.setChanNo(1); cmd.setOperator(camera.getOperatorId()); //æ¨éå¨çº¿çç¸æº boolean onLine = hikClientService.isOnLine(cmd); boolean onLine = cameraSdkService.isOnLine(cmd); if(!onLine) { continue; } Map<String, Object> ptz = hikClientService.getGisInfo(cmd); Map<String, Object> ptz = cameraSdkService.getGisInfo(cmd); if (StringUtils.isNull(ptz)) { continue; }