ard-work/src/main/java/com/ruoyi/alarmpoints/elecwall/controller/ArdWallController.java
@@ -2,6 +2,9 @@ import java.util.List; import javax.servlet.http.HttpServletResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -27,6 +30,7 @@ * @author ard * @date 2023-07-20 */ @Api(tags = "电子围栏管理") @RestController @RequestMapping("/alarmpoints/wall") public class ArdWallController extends BaseController @@ -37,6 +41,7 @@ /** * 查询电子围栏管理列表 */ @ApiOperation("查询电子围栏管理列表") @PreAuthorize("@ss.hasPermi('alarmpoints:wall:list')") @GetMapping("/list") public TableDataInfo list(ArdWall ardWall) @@ -62,6 +67,7 @@ /** * 获取电子围栏管理详细信息 */ @ApiOperation("获取电子围栏管理详细信息") @PreAuthorize("@ss.hasPermi('alarmpoints:wall:query')") @GetMapping(value = "/{id}") public AjaxResult getInfo(@PathVariable("id") String id) @@ -72,6 +78,7 @@ /** * 新增电子围栏管理 */ @ApiOperation("新增电子围栏管理") @PreAuthorize("@ss.hasPermi('alarmpoints:wall:add')") @Log(title = "电子围栏管理", businessType = BusinessType.INSERT) @PostMapping @@ -83,6 +90,7 @@ /** * 修改电子围栏管理 */ @ApiOperation("修改电子围栏管理") @PreAuthorize("@ss.hasPermi('alarmpoints:wall:edit')") @Log(title = "电子围栏管理", businessType = BusinessType.UPDATE) @PutMapping @@ -94,6 +102,7 @@ /** * 删除电子围栏管理 */ @ApiOperation("删除电子围栏管理") @PreAuthorize("@ss.hasPermi('alarmpoints:wall:remove')") @Log(title = "电子围栏管理", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
@@ -2,15 +2,15 @@ import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.device.camera.domain.ArdCameras; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.media.domain.StreamInfo; import com.ruoyi.media.domain.Vtdu; import com.ruoyi.media.service.IMediaService; import com.ruoyi.media.service.IVtduService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; @@ -19,7 +19,6 @@ import javax.annotation.Resource; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @Description: @@ -35,11 +34,16 @@ public class MediaController extends BaseController { @Resource private IMediaService mediaService; @Resource private IVtduService vtduService; /** * 增加通道 */ @PostMapping() @ApiOperation("增加转码") @ApiOperation("增加通道") @PreAuthorize("@ss.hasPermi('media:stream:add')") @ApiOperationSupport(includeParameters = {"streamInfo.name", "streamInfo.rtspSource", "streamInfo.mode"}) @ApiOperationSupport(includeParameters = {"streamInfo.name", "streamInfo.rtspSource", "streamInfo.mode", "streamInfo.isCode"}, order = 1) public AjaxResult addPath(@RequestBody StreamInfo streamInfo) { if (StringUtils.isEmpty(streamInfo.getName())) { return AjaxResult.error("通道名称不能为空"); @@ -48,107 +52,135 @@ return AjaxResult.error("rtsp地址不能为空"); } String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode()); Vtdu vtdu = new Vtdu(); vtdu.setName(streamInfo.getName()); vtdu.setRtspUrl(streamInfo.getRtspSource()); vtdu.setIsCode(streamInfo.getIsCode()); vtdu.setCodeType(streamInfo.getMode()); vtduService.insertVtdu(vtdu); return AjaxResult.success(rtsp); } /** * 修改通道 */ @ApiOperation("修改通道") @ApiOperationSupport(includeParameters = {"streamInfo.name", "streamInfo.rtspSource", "streamInfo.mode", "streamInfo.isCode"}, order = 2) @PreAuthorize("@ss.hasPermi('media:stream:edit')") @PutMapping public AjaxResult edit(@RequestBody StreamInfo streamInfo) { mediaService.removePath(new String[]{streamInfo.getName()}); vtduService.deleteVtduByName(streamInfo.getName()); String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode()); Vtdu vtdu = new Vtdu(); vtdu.setName(streamInfo.getName()); vtdu.setRtspUrl(streamInfo.getRtspSource()); vtdu.setIsCode(streamInfo.getIsCode()); vtdu.setCodeType(streamInfo.getMode()); vtduService.insertVtdu(vtdu); return AjaxResult.success(rtsp); } /** * 获取转码详细信息 * 移除通道 */ @ApiOperation("获取转码详细信息") @DeleteMapping("/path/{names}") @PreAuthorize("@ss.hasPermi('media:stream:remove')") @ApiOperation("移除通道") @ApiOperationSupport(order = 3) public AjaxResult removePath(@PathVariable String[] names) { mediaService.removePath(names); vtduService.deleteVtduByNames(names); return AjaxResult.success(); } /** * 移除拉流 */ @ApiOperation("移除拉流") @ApiOperationSupport(order =4 ) @PreAuthorize("@ss.hasPermi('media:stream:remove')") @DeleteMapping("/{id}") public AjaxResult removePullStreamSession(@PathVariable String sessionId) { List<StreamInfo> pullStreamList = mediaService.getPullStreamList(); StreamInfo streamInfo = pullStreamList.stream() .filter(object -> object.getId().equals(sessionId)) .collect(Collectors.toList()).get(0); switch (streamInfo.getSessionType()) { case "rtsp": return AjaxResult.success(mediaService.kickRtspSession(sessionId)); case "webrtc": return AjaxResult.success(mediaService.kickWebrtcSession(sessionId)); case "rtmp": return AjaxResult.success(mediaService.kickRtmpSession(sessionId)); } return AjaxResult.error(); } /** * 获取通道详细信息 */ @ApiOperation("获取通道详细信息") @ApiOperationSupport(order =4 ) @GetMapping(value = "/{name}") public AjaxResult getInfo(@PathVariable("name") String name) { return success(mediaService.getPathInfo(name)); } /** * 修改转码 * 获取当前通道列表 */ @ApiOperation("修改转码") @PreAuthorize("@ss.hasPermi('media:stream:edit')") @PutMapping public AjaxResult edit(@RequestBody StreamInfo streamInfo) { mediaService.removePath(new String[]{streamInfo.getName()}); String rtsp = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode()); return AjaxResult.success(rtsp); } @DeleteMapping("/path/{names}") @PreAuthorize("@ss.hasPermi('media:stream:remove')") @ApiOperation("移除转码") public AjaxResult removePath(@PathVariable String[] names) { mediaService.removePath(names); return AjaxResult.success(); } @GetMapping("/path/list") @ApiOperation("获取当前转码列表") @ApiOperation("获取当前通道列表") @ApiOperationSupport(order = 5) public TableDataInfo getPaths() { startPage(); return getDataTable(mediaService.paths()); } @GetMapping("/getRtspSessions") @ApiOperation("获取rtsp会话列表") public AjaxResult getRtspSessions() { return AjaxResult.success(mediaService.rtspsessions()); } @GetMapping("/getRtspConns") @ApiOperation("获取rtsp连接列表") public AjaxResult getRtspConns() { return AjaxResult.success(mediaService.rtspconns()); } /** * 按ID查询拉流详情 */ @GetMapping("/getRtspSessionById") @ApiOperation("按ID查询会话") @ApiOperation("按ID查询拉流详情") public AjaxResult getRtspSessionById(String sessionId) { return AjaxResult.success(mediaService.getRtspSessionById(sessionId)); List<StreamInfo> pullStreamList = mediaService.getPullStreamList(); StreamInfo streamInfo = pullStreamList.stream() .filter(object -> object.getId().equals(sessionId)) .collect(Collectors.toList()).get(0); switch (streamInfo.getSessionType()) { case "rtsp": return AjaxResult.success(mediaService.getRtspSessionById(sessionId)); case "rtmp": return AjaxResult.success(mediaService.getRtmpSessionById(sessionId)); case "webrtc": return AjaxResult.success(mediaService.getWebrtcSessionById(sessionId)); } return AjaxResult.error(); } @GetMapping("/getPushStreams") @ApiOperation("获取推流信息") public AjaxResult getPushStreams() { return AjaxResult.success(mediaService.getPushStreams()); } @GetMapping("/getPullStreams") @ApiOperation("获取拉流信息") public AjaxResult getPullStreams() { return AjaxResult.success(mediaService.getPullStreams()); } /** * 获取推流列表 */ @PreAuthorize("@ss.hasPermi('media:stream:list')") @GetMapping("/pushList") @ApiOperation("获取推流列表") @ApiOperationSupport(order = 6) public TableDataInfo getPushStreamList() { startPage(); return getDataTable(mediaService.getPushStreamList()); } /** * 删除流媒体拉流 */ @PreAuthorize("@ss.hasPermi('media:stream:remove')") @DeleteMapping("/{id}") public AjaxResult removePullStreamSession(@PathVariable String id) { List<StreamInfo> pullStreamList = mediaService.getPullStreamList(); StreamInfo streamInfo = pullStreamList.stream() .filter(object -> object.getId().equals(id)) .collect(Collectors.toList()).get(0); switch (streamInfo.getSessionType()) { case "rtsp": return AjaxResult.success(mediaService.kickRtspSession(id)); case "webrtc": return AjaxResult.success(mediaService.kickWebrtcSession(id)); case "rtmp": return AjaxResult.success(mediaService.kickRtmpSession(id)); } return AjaxResult.error(); } /** * 获取拉流列表 */ @PreAuthorize("@ss.hasPermi('media:stream:list')") @GetMapping("/pullList") @ApiOperation("获取拉流列表") @ApiOperationSupport(order = 7) public TableDataInfo getPullStreamList() { startPage(); return getDataTable(mediaService.getPullStreamList()); ard-work/src/main/java/com/ruoyi/media/controller/VtduController.java
对比新文件 @@ -0,0 +1,104 @@ package com.ruoyi.media.controller; import java.util.List; import javax.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.media.domain.Vtdu; import com.ruoyi.media.service.IVtduService; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.core.page.TableDataInfo; /** * 流媒体管理Controller * * @author ard * @date 2023-08-29 */ @RestController @RequestMapping("/vtdu/media") public class VtduController extends BaseController { @Autowired private IVtduService vtduService; /** * 查询流媒体管理列表 */ @PreAuthorize("@ss.hasPermi('vtdu:media:list')") @GetMapping("/list") public TableDataInfo list(Vtdu vtdu) { startPage(); List<Vtdu> list = vtduService.selectVtduList(vtdu); return getDataTable(list); } /** * 导出流媒体管理列表 */ @PreAuthorize("@ss.hasPermi('vtdu:media:export')") @Log(title = "流媒体管理", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, Vtdu vtdu) { List<Vtdu> list = vtduService.selectVtduList(vtdu); ExcelUtil<Vtdu> util = new ExcelUtil<Vtdu>(Vtdu.class); util.exportExcel(response, list, "流媒体管理数据"); } /** * 获取流媒体管理详细信息 */ @PreAuthorize("@ss.hasPermi('vtdu:media:query')") @GetMapping(value = "/{name}") public AjaxResult getInfo(@PathVariable("name") String name) { return success(vtduService.selectVtduByName(name)); } /** * 新增流媒体管理 */ @PreAuthorize("@ss.hasPermi('vtdu:media:add')") @Log(title = "流媒体管理", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody Vtdu vtdu) { return toAjax(vtduService.insertVtdu(vtdu)); } /** * 修改流媒体管理 */ @PreAuthorize("@ss.hasPermi('vtdu:media:edit')") @Log(title = "流媒体管理", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody Vtdu vtdu) { return toAjax(vtduService.updateVtdu(vtdu)); } /** * 删除流媒体管理 */ @PreAuthorize("@ss.hasPermi('vtdu:media:remove')") @Log(title = "流媒体管理", businessType = BusinessType.DELETE) @DeleteMapping("/{names}") public AjaxResult remove(@PathVariable String[] names) { return toAjax(vtduService.deleteVtduByNames(names)); } } ard-work/src/main/java/com/ruoyi/media/domain/Vtdu.java
对比新文件 @@ -0,0 +1,45 @@ package com.ruoyi.media.domain; import lombok.Data; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.core.domain.BaseEntity; /** * 流媒体管理对象 vtdu * * @author ard * @date 2023-08-29 */ @Data public class Vtdu extends BaseEntity { private static final long serialVersionUID = 1L; /** * 主键名称 */ @Excel(name = "名称") private String name; /** * rtsp源地址 */ @Excel(name = "rtsp源地址") private String rtspUrl; /** * 是否转码 */ @Excel(name = "是否转码") private String isCode; /** * 转码模式 */ @Excel(name = "转码模式") private String codeType; } ard-work/src/main/java/com/ruoyi/media/mapper/VtduMapper.java
对比新文件 @@ -0,0 +1,60 @@ package com.ruoyi.media.mapper; import java.util.List; import com.ruoyi.media.domain.Vtdu; /** * 流媒体管理Mapper接口 * * @author ard * @date 2023-08-29 */ public interface VtduMapper { /** * 查询流媒体管理 * * @param name 流媒体管理主键 * @return 流媒体管理 */ public Vtdu selectVtduByName(String name); /** * 查询流媒体管理列表 * * @param vtdu 流媒体管理 * @return 流媒体管理集合 */ public List<Vtdu> selectVtduList(Vtdu vtdu); /** * 新增流媒体管理 * * @param vtdu 流媒体管理 * @return 结果 */ public int insertVtdu(Vtdu vtdu); /** * 修改流媒体管理 * * @param vtdu 流媒体管理 * @return 结果 */ public int updateVtdu(Vtdu vtdu); /** * 删除流媒体管理 * * @param name 流媒体通道名称 * @return 结果 */ public int deleteVtduByName(String name); /** * 批量删除流媒体管理 * * @param names 需要删除的数据主键集合 * @return 结果 */ public int deleteVtduByNames(String[] names); } ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
@@ -14,30 +14,27 @@ * 刘苏义 * 2023/8/12 13:56:52 */ public String addPath(String name, String rtspPath, String mode, String isCode); String addPath(String name, String rtspPath, String mode, String isCode); StreamInfo getPathInfo(String name); public void removePath(String[] names); void removePath(String[] names); public List<StreamInfo> paths(); public List<Items> rtspconns(); public List<Items> rtspsessions(); List<StreamInfo> paths(); RtspSession getRtspSessionById(String sessionId); WebrtcSession getWebrtcSessionById(String sessionId); RtmpSession getRtmpSessionById(String sessionId); List<RtspSession> getPushStreams(); List<RtspSession> getPullStreams(); 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); } ard-work/src/main/java/com/ruoyi/media/service/IVtduService.java
对比新文件 @@ -0,0 +1,61 @@ package com.ruoyi.media.service; import java.util.List; import com.ruoyi.media.domain.Vtdu; /** * 流媒体管理Service接口 * * @author ard * @date 2023-08-29 */ public interface IVtduService { /** * 查询流媒体管理 * * @param name 流媒体管理主键 * @return 流媒体管理 */ public Vtdu selectVtduByName(String name); /** * 查询流媒体管理列表 * * @param vtdu 流媒体管理 * @return 流媒体管理集合 */ public List<Vtdu> selectVtduList(Vtdu vtdu); /** * 新增流媒体管理 * * @param vtdu 流媒体管理 * @return 结果 */ public int insertVtdu(Vtdu vtdu); /** * 修改流媒体管理 * * @param vtdu 流媒体管理 * @return 结果 */ public int updateVtdu(Vtdu vtdu); /** * 批量删除流媒体管理 * * @param names 需要删除的流媒体管理主键集合 * @return 结果 */ public int deleteVtduByNames(String[] names); /** * 删除流媒体管理信息 * * @param name 流媒体管理主键 * @return 结果 */ public int deleteVtduByName(String name); } ard-work/src/main/java/com/ruoyi/media/service/impl/MediaService.java
@@ -2,7 +2,9 @@ import com.alibaba.fastjson2.JSONObject; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.uuid.IdUtils; 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; @@ -33,13 +35,16 @@ @Service @Slf4j(topic = "cmd") public class MediaService implements IMediaService { @Resource VtduMapper vtduMapper; @Resource MediaClient mediaClient; @Value("${mediamtx.host}") String mediamtxHost; @Value("${mediamtx.enabled}") Boolean mediamtxEnabled; @Value("${mediamtx.software_decoding}") Boolean softwareDecoding; String processName = "mediamtx.exe"; @@ -64,6 +69,10 @@ // CmdUtils.commandStart(command); } } List<Vtdu> vtduList = vtduMapper.selectVtduList(new Vtdu()); for (Vtdu v : vtduList) { addPath(v.getName(), v.getRtspUrl(), v.getCodeType(), v.getIsCode()); } } @PreDestroy @@ -81,18 +90,25 @@ public String addPath(String name, String rtspPath, String mode, String isCode) { String rtspUrl = "rtsp://" + mediamtxHost + ":7554/" + name; Conf mediaInfo = new Conf(); String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/"); //-vcodec libx264 //指定视频编码器为 libx264,使用 H.264 编码格式进行视频压缩 //-preset ultrafast //--preset的参数主要调节编码速度和质量的平衡,有ultrafast(转码速度最快,视频往往也最模糊)、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项,从快到慢 //-r 25 //设置输出视频的帧率为 25 帧/秒 //-rtsp_transport tcp //这个选项告诉 FFmpeg 使用 TCP 作为 RTSP 的传输协议 //-threads 4: 指定要使用的线程数为 4。//这允许 FFmpeg 在多核处理器上使用多个线程来进行视频编码,以加快速度。 // -i //用于指定输入媒体文件或输入流的地址 // -bf 0 禁用B帧,因为webrtc在网页调用时控制台一直输出 WebRTC doesn’t support H264 streams with B-frames //-f rtsp //这个选项告诉 FFmpeg 输出为 RTSP 格式。 //CPU软解码编码 String cmd = "ffmpeg -rtsp_transport tcp -i \"" + rtspPath + "\" -vcodec libx264 -preset:v ultrafast -r 25 -threads 4 -b:v 4096k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; //String cmd = rootPath + "/lib/mediamtx/" +"ffmpeg -rtsp_transport tcp -i " + rtspPath + " -vcodec libx264 -preset:v ultrafast -r 25 -threads 4 -b:v 2048k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; //GPU硬解码编码 -hwaccel cuvid -c:v h264_cuvid 使用cuda解码 -c:v h264_nvenc 使用cuda编码 //String cmd = "ffmpeg -hwaccel cuvid -c:v h264_cuvid -rtsp_transport udp -i \"" + rtspPath + "\" -c:v h264_nvenc -r 25 -threads 4 -b:v 4096k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; //String cmd = rootPath + "/lib/mediamtx/" + "ffmpeg -hwaccel cuvid -c:v h264_cuvid -rtsp_transport udp -i " + rtspPath + " -c:v h264_nvenc -r 25 -threads 4 -b:v 2048k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; if (isCode.equals("1")) { String cmd = rootPath + "/lib/mediamtx/" + "ffmpeg -rtsp_transport tcp -i " + rtspPath + " -vcodec libx264 -preset:v ultrafast -r 25 -threads 4 -b:v 2048k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; if (!softwareDecoding) { cmd = rootPath + "/lib/mediamtx/" + "ffmpeg -hwaccel cuvid -c:v h264_cuvid -rtsp_transport udp -i " + rtspPath + " -c:v h264_nvenc -r 25 -threads 4 -b:v 2048k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH"; } if (mode.equals("1")) { mediaInfo.setRunondemand(cmd); mediaInfo.setRunondemandrestart(true); @@ -188,19 +204,6 @@ return pathInfoList; } @Override public List<Items> rtspconns() { String list = mediaClient.rtspconns(); JsonsRoot jsonsRoot = JSONObject.parseObject(list, JsonsRoot.class); return jsonsRoot.getItems(); } @Override public List<Items> rtspsessions() { String list = mediaClient.rtspsessions(); JsonsRoot jsonsRoot = JSONObject.parseObject(list, JsonsRoot.class); return jsonsRoot.getItems(); } @Override public RtspSession getRtspSessionById(String sessionId) { @@ -223,39 +226,11 @@ return rtmpSession; } @Override public List<RtspSession> getPushStreams() { List<RtspSession> rtspSessions = new ArrayList<>(); String list = mediaClient.paths(); JsonsRoot jsonsRoot = JSONObject.parseObject(list, JsonsRoot.class); List<Items> items = jsonsRoot.getItems(); for (Items item : items) { Source source = item.getSource(); RtspSession rtspSession = getRtspSessionById(source.getId()); rtspSession.setName(item.getName()); rtspSessions.add(rtspSession); } return rtspSessions; } @Override public List<RtspSession> getPullStreams() { List<RtspSession> rtspSessions = 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) { RtspSession rtspSession = getRtspSessionById(reader.getId()); rtspSession.setName(item.getName()); rtspSessions.add(rtspSession); } } return rtspSessions; } /** * 获取推流列表 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public List<StreamInfo> getPushStreamList() { List<StreamInfo> PushStreamInfoList = new ArrayList<>(); @@ -329,6 +304,11 @@ return PushStreamInfoList; } /** * 获取拉流列表 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public List<StreamInfo> getPullStreamList() { List<StreamInfo> PullStreamInfoList = new ArrayList<>(); @@ -380,12 +360,12 @@ //开始拉流时间 info.setBeginTime(webrtcSession.getCreated()); //上行流量 bytesReceived = webrtcSession.getBytesReceived(); formatReceivedSize = ArdTool.formatFileSize(bytesReceived); bytesReceived = webrtcSession.getBytesReceived(); formatReceivedSize = ArdTool.formatFileSize(bytesReceived); info.setUpTraffic(formatReceivedSize); //下行流量 bytesSent = webrtcSession.getBytesSent(); formatSentSize = ArdTool.formatFileSize(bytesSent); bytesSent = webrtcSession.getBytesSent(); formatSentSize = ArdTool.formatFileSize(bytesSent); info.setDownTraffic(formatSentSize); //拉流服务器 info.setRemoteAddr(webrtcSession.getRemoteAddr()); @@ -421,6 +401,11 @@ return PullStreamInfoList; } /** * 踢出rtsp会话 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public Boolean kickRtspSession(String sessionId) { try { @@ -431,6 +416,11 @@ } } /** * 踢出rtmp会话 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public Boolean kickRtmpSession(String sessionId) { try { @@ -441,6 +431,11 @@ } } /** * 踢出webrtc会话 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public Boolean kickWebrtcSession(String sessionId) { try { ard-work/src/main/java/com/ruoyi/media/service/impl/VtduServiceImpl.java
对比新文件 @@ -0,0 +1,88 @@ package com.ruoyi.media.service.impl; import java.util.List; import com.ruoyi.common.utils.uuid.IdUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.media.mapper.VtduMapper; import com.ruoyi.media.domain.Vtdu; import com.ruoyi.media.service.IVtduService; /** * 流媒体管理Service业务层处理 * * @author ard * @date 2023-08-29 */ @Service public class VtduServiceImpl implements IVtduService { @Autowired private VtduMapper vtduMapper; /** * 查询流媒体管理 * * @param name 流媒体管理主键 * @return 流媒体管理 */ @Override public Vtdu selectVtduByName(String name) { return vtduMapper.selectVtduByName(name); } /** * 查询流媒体管理列表 * * @param vtdu 流媒体管理 * @return 流媒体管理 */ @Override public List<Vtdu> selectVtduList(Vtdu vtdu) { return vtduMapper.selectVtduList(vtdu); } /** * 新增流媒体管理 * * @param vtdu 流媒体管理 * @return 结果 */ @Override public int insertVtdu(Vtdu vtdu) { return vtduMapper.insertVtdu(vtdu); } /** * 修改流媒体管理 * * @param vtdu 流媒体管理 * @return 结果 */ @Override public int updateVtdu(Vtdu vtdu) { return vtduMapper.updateVtdu(vtdu); } /** * 批量删除流媒体管理 * * @param names 需要删除的数据主键集合 * @return 结果 */ @Override public int deleteVtduByNames(String[] names) { return vtduMapper.deleteVtduByNames(names); } /** * 删除流媒体管理 * * @param name 流媒体通道名称 * @return 结果 */ @Override public int deleteVtduByName(String name) { return vtduMapper.deleteVtduByName(name); } } ard-work/src/main/resources/mapper/vtdu/VtduMapper.xml
对比新文件 @@ -0,0 +1,69 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.media.mapper.VtduMapper"> <resultMap type="Vtdu" id="VtduResult"> <result property="name" column="name" /> <result property="rtspUrl" column="rtsp_url" /> <result property="isCode" column="is_code" /> <result property="codeType" column="code_type" /> </resultMap> <sql id="selectVtduVo"> select name, rtsp_url, is_code, code_type from vtdu </sql> <select id="selectVtduList" parameterType="Vtdu" resultMap="VtduResult"> <include refid="selectVtduVo"/> <where> <if test="name != null and name != ''"> and name like '%'||#{name}||'%'</if> <if test="rtspUrl != null and rtspUrl != ''"> and rtsp_url = #{rtspUrl}</if> <if test="isCode != null and isCode != ''"> and is_code = #{isCode}</if> <if test="codeType != null and codeType != ''"> and code_type = #{codeType}</if> </where> </select> <select id="selectVtduByName" parameterType="String" resultMap="VtduResult"> <include refid="selectVtduVo"/> where name = #{name} </select> <insert id="insertVtdu" parameterType="Vtdu"> insert into vtdu <trim prefix="(" suffix=")" suffixOverrides=","> <if test="name != null">name,</if> <if test="rtspUrl != null">rtsp_url,</if> <if test="isCode != null">is_code,</if> <if test="codeType != null">code_type,</if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="name != null">#{name},</if> <if test="rtspUrl != null">#{rtspUrl},</if> <if test="isCode != null">#{isCode},</if> <if test="codeType != null">#{codeType},</if> </trim> </insert> <update id="updateVtdu" parameterType="Vtdu"> update vtdu <trim prefix="SET" suffixOverrides=","> <if test="rtspUrl != null">rtsp_url = #{rtspUrl},</if> <if test="isCode != null">is_code = #{isCode},</if> <if test="codeType != null">code_type = #{codeType},</if> </trim> where name = #{name} </update> <delete id="deleteVtduByName" parameterType="String"> delete from vtdu where name = #{name} </delete> <delete id="deleteVtduByNames" parameterType="String"> delete from vtdu where name in <foreach item="name" collection="array" open="(" separator="," close=")"> #{name} </foreach> </delete> </mapper> ard-work/src/main/resources/templates/mediaMTX.html
@@ -10,7 +10,7 @@ .video-container { display: inline-block; vertical-align: top; width: 25%; /* 六个视频平均分配一行的宽度 */ width: 33%; /* 3个视频平均分配一行的宽度 */ /*padding: 2px; !* 可以根据需要调整内边距 *!*/ box-sizing: border-box; } @@ -42,7 +42,16 @@ </div> </div> <script th:inline="javascript"> const restartPause = 2000; var chanMap = new Map(); window.onload = function () { chanMap.set("video1", "http://127.0.0.1:8889/164/"); chanMap.set("video2", "http://127.0.0.1:8889/164/"); chanMap.set("video3", "http://127.0.0.1:8889/164/"); chanMap.set("video4", "http://127.0.0.1:8889/165/"); chanMap.set("video5", "http://127.0.0.1:8889/165/"); console.log(chanMap); } const linkToIceServers = (links) => ( (links !== null) ? links.split(', ').map((link) => { const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i); @@ -260,22 +269,37 @@ this.eTag = ''; this.queuedCandidates = []; } stop() { if (this.pc) { try { this.pc.close(); } catch (e) { console.log("Failure close peer connection:" + e); } this.pc = null; } } } let videoMap = new Map(); $('video').click(function (e) { let ID = e.target.id;//获取当前点击事件的元素 console.log(ID); console.log(videoMap); if (videoMap.get(ID) != null) { closeVideo(ID, videoMap.get(ID)); closeVideo(ID); } else { let client = new WHEPClient("http://127.0.0.1:8889/165/", ID); let stream = chanMap.get(ID); let client = new WHEPClient(stream, ID); videoMap.set(ID, client); } }); function closeVideo(id) { let elementById = document.getElementById(id); elementById.pause(); console.log("关闭" + id) let client = videoMap.get(id); client.stop(id); videoMap.delete(id); } </script>