| 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); | 
|     } | 
| } |