package com.ard.vtdu.service.impl; import com.ard.common.constant.HttpStatus; import com.ard.common.core.page.TableDataInfo; import com.ard.vtdu.client.MediaClient; import com.ard.vtdu.config.MediamtxConfig; import com.ard.vtdu.domain.*; import com.ard.vtdu.service.IArdMediaService; import com.ard.vtdu.util.ArdTool; import com.dtflys.forest.exceptions.ForestRuntimeException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Date; import java.util.List; 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 public class ArdArdMediaServiceImpl implements IArdMediaService { @Resource MediaClient mediaClient; @Resource private MediamtxConfig mediamtxConfig; /** * @return * @Author 刘苏义 * @Description 添加流媒体 * name 相机ID * sourceUrl rtsp推流地址 * isCode 0-不转码 1-转码 * mode 1-gpu硬解码 0-cpu软解码 * @Date 2024/7/3 8:45 * @Param */ @Override public ArdVtdu addPath(ArdVtdu ardVtdu) { try { String rtspUrl = "rtsp://" + mediamtxConfig.getPlayHost() + ":8554/" + ardVtdu.getName(); String rtmpUrl = "rtmp://" + mediamtxConfig.getPlayHost() + ":1935/" + ardVtdu.getName(); String protocol = mediamtxConfig.isHttps() ? "https://" : "http://"; String webrtcUrl = protocol + mediamtxConfig.getPlayHost() + ":8889/" + ardVtdu.getName(); Conf conf = new Conf(); conf.setSource(ardVtdu.getRtspSource()); conf.setRunOnDemandRestart(false); conf.setRunOnDemandCloseAfter("5s"); conf.setMaxReaders(100); conf.setSourceProtocol("tcp"); if (!checkNameExist(ardVtdu.getName())) { mediaClient.addPath(ardVtdu.getName(), conf); } else { mediaClient.editPath(ardVtdu.getName(), conf); } ardVtdu.setCameraId(ardVtdu.getName().split("_")[0]); ardVtdu.setRtspUrl(rtspUrl); ardVtdu.setRtmpUrl(rtmpUrl); ardVtdu.setWebrtcUrl(webrtcUrl); return ardVtdu; } catch (ForestRuntimeException ex) { log.error("添加流媒体异常:" + ex.getMessage()); } return null; } @Override public ArdVtdu editPath(ArdVtdu ardVtdu) { try { String rtspUrl = "rtsp://" + mediamtxConfig.getPlayHost() + ":8554/" + ardVtdu.getName(); String rtmpUrl = "rtmp://" + mediamtxConfig.getPlayHost() + ":1935/" + ardVtdu.getName(); String protocol = mediamtxConfig.isHttps() ? "https://" : "http://"; String webrtcUrl = protocol + mediamtxConfig.getPlayHost() + ":8889/" + ardVtdu.getName(); Conf conf = new Conf(); conf.setSource(ardVtdu.getRtspSource()); conf.setName(ardVtdu.getName()); conf.setRunOnDemand(""); conf.setRunOnDemandRestart(false); conf.setRunOnDemandCloseAfter("5s"); conf.setMaxReaders(100); conf.setSourceProtocol("tcp"); if (checkNameExist(ardVtdu.getName())) { mediaClient.editPath(ardVtdu.getName(), conf); } ardVtdu.setRtspUrl(rtspUrl); ardVtdu.setRtmpUrl(rtmpUrl); ardVtdu.setWebrtcUrl(webrtcUrl); return ardVtdu; } catch (ForestRuntimeException ex) { log.error("修改流媒体异常:" + ex.getMessage()); } return null; } @Override public void removePath(String[] names) { try { for (String name : names) { if (checkNameExist(name)) { mediaClient.removePath(name); } } } catch (Exception ex) { log.error("批量移除流媒体异常:" + ex.getMessage()); } } @Override public void removePath(String name) { try { if (checkNameExist(name)) { mediaClient.removePath(name); } } catch (ForestRuntimeException ex) { log.error("移除流媒体异常:" + ex.getMessage()); } } @Override public List paths(Integer pageNum, Integer pageSize) { Paths paths = mediaClient.paths(pageNum - 1, pageSize); List items = paths.getItems(); List pathInfoList = new ArrayList<>(); for (Item item : items) { StreamInfo info = new StreamInfo(); //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathConfig(name); String runOn = ""; if (StringUtils.isNotEmpty(conf.getRunOnDemand())) { runOn = conf.getRunOnDemand(); } //传输协议 Matcher 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) { RtspSession rtspSession = mediaClient.getRtspsessionById(sessionId); return rtspSession; } @Override public WebrtcSession getWebrtcSessionById(String sessionId) { WebrtcSession webrtcSession = mediaClient.getWebrtcsessionById(sessionId); return webrtcSession; } @Override public RtmpSession getRtmpSessionById(String sessionId) { RtmpSession rtmpSession = mediaClient.getRtmpsessionById(sessionId); return rtmpSession; } /** * 获取推流列表 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public TableDataInfo getPushStreamList(Integer pageNum, Integer pageSize) { TableDataInfo tableDataInfo = new TableDataInfo(); List PushStreamInfoList = new ArrayList<>(); Paths paths = mediaClient.paths(pageNum - 1, pageSize); int itemCount = paths.getItemCount(); tableDataInfo.setTotal(itemCount); List items = paths.getItems(); for (Item item : items) { StreamInfo info = new StreamInfo(); //ID String name = item.getName(); info.setName(name); Conf conf = mediaClient.getPathConfig(name); //RTMP播放地址 String rtmpUrl = "rtmp://" + mediamtxConfig.getPlayHost() + ":1935/" + name; info.setRtmpUrl(rtmpUrl); //RTSP播放地址 String rtspUrl = "rtsp://" + mediamtxConfig.getPlayHost() + ":8554/" + name; info.setRtspUrl(rtspUrl); //WEBRTC播放地址 String protocol = mediamtxConfig.isHttps() ? "https://" : "http://"; String webrtcUrl = protocol + mediamtxConfig.getPlayHost() + ":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.setDownTraffic(formatReceivedSize); //上行流量 long bytesSent = item.getBytesSent(); String formatSentdSize = ArdTool.formatFileSize(bytesSent); info.setUpTraffic(formatSentdSize); info.setBeginTime(item.getReadyTime()); } 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 = item.getReaders(); info.setNum(readers.size()); PushStreamInfoList.add(info); } tableDataInfo.setRows(PushStreamInfoList); tableDataInfo.setCode(HttpStatus.SUCCESS); tableDataInfo.setMsg("查询成功"); return tableDataInfo; } /** * 获取webrtc拉流列表 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public TableDataInfo getPullWebrtcStreamList(Integer pageNum, Integer pageSize) { TableDataInfo tableDataInfo = new TableDataInfo(); WebrtcSessions WebrtcSessions = mediaClient.webrtcsessions(pageNum - 1, pageSize); List items = WebrtcSessions.getItems(); items.forEach( webrtcSession -> { webrtcSession.setUpStream(ArdTool.formatFileSize(webrtcSession.getBytesReceived())); webrtcSession.setDownStream(ArdTool.formatFileSize(webrtcSession.getBytesSent())); webrtcSession.setBeginTime(webrtcSession.getCreated()); } ); tableDataInfo.setTotal(WebrtcSessions.getItemCount()); tableDataInfo.setRows(items); tableDataInfo.setCode(HttpStatus.SUCCESS); tableDataInfo.setMsg("查询成功"); return tableDataInfo; } /** * 获取rtmp拉流列表 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public TableDataInfo getPullRtmpStreamList(Integer pageNum, Integer pageSize) { TableDataInfo tableDataInfo = new TableDataInfo(); RtmpSessions rtmpSessions = mediaClient.rtmpsessions(pageNum - 1, pageSize); List items = rtmpSessions.getItems(); items.stream().forEach( webrtcSession -> { webrtcSession.setUpStream(ArdTool.formatFileSize(webrtcSession.getBytesReceived())); webrtcSession.setDownStream(ArdTool.formatFileSize(webrtcSession.getBytesSent())); webrtcSession.setBeginTime(webrtcSession.getCreated()); } ); tableDataInfo.setTotal(rtmpSessions.getItemCount()); tableDataInfo.setRows(items); tableDataInfo.setCode(HttpStatus.SUCCESS); tableDataInfo.setMsg("查询成功"); return tableDataInfo; } /** * 获取rtsp拉流列表 * 刘苏义 * 2023/8/29 9:37:05 */ @Override public TableDataInfo getPullRtspStreamList(Integer pageNum, Integer pageSize) { TableDataInfo tableDataInfo = new TableDataInfo(); RtspSessions rtspSessions = mediaClient.rtspsessions(pageNum - 1, pageSize); List items = rtspSessions.getItems(); items.stream().forEach( webrtcSession -> { webrtcSession.setUpStream(ArdTool.formatFileSize(webrtcSession.getBytesReceived())); webrtcSession.setDownStream(ArdTool.formatFileSize(webrtcSession.getBytesSent())); webrtcSession.setBeginTime(webrtcSession.getCreated()); } ); tableDataInfo.setTotal(rtspSessions.getItemCount()); tableDataInfo.setRows(items); tableDataInfo.setCode(HttpStatus.SUCCESS); tableDataInfo.setMsg("查询成功"); return tableDataInfo; } /** * 踢出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 getNameList() { List nameList = new ArrayList<>(); try { Paths paths = mediaClient.paths(0, 1000); List items = paths.getItems(); for (Item item : items) { nameList.add(item.getName()); } } catch (Exception ex) { log.error("获取流媒体name列表异常:" + ex.getMessage()); } return nameList; } /** * 检查name是否存在 * 刘苏义 * 2023/10/19 15:18:45 */ @Override public boolean checkNameExist(String name) { boolean result = false; List nameList = getNameList(); if (nameList.contains(name)) { result = true; } return result; } /** * 清空所有流媒体信息 * * @author 刘苏义 * @date 2024/8/22 10:18 */ @Override public void clearAll() { List nameList = getNameList(); nameList.stream().forEach(name -> { removePath(name); }); } /** * 开启录像 */ @Override public String setRecord(Conf conf) { return mediaClient.editPath(conf.getName(), conf); } /** * 获取全部录像分段列表 */ @Override public Paths recordingList(Integer pageNum, Integer pageSize) { Paths paths = mediaClient.recordingList(pageNum - 1, pageSize); paths.getItems().forEach(item -> { item.getSegments().forEach(segment -> { // 使用 OffsetDateTime 进行解析 OffsetDateTime offsetDateTime = OffsetDateTime.parse(segment.getStart()); // 转换为 java.util.Date Date date = Date.from(offsetDateTime.toInstant()); segment.setStartTime(date); }); }); return paths; } /** * 指定路径名获取录像分段列表 */ @Override public Item recordingListByName(String name) { Item item = mediaClient.recordingListByName(name); item.getSegments().forEach(segment -> { // 使用 OffsetDateTime 进行解析 OffsetDateTime offsetDateTime = OffsetDateTime.parse(segment.getStart()); // 转换为 java.util.Date Date date = Date.from(offsetDateTime.toInstant()); segment.setStartTime(date); }); return item; } /** * 通过路径名和分段录像开始时间删除录像分段 */ @Override public String deleteSegment(String name, String start) { return mediaClient.deleteSegment(name, start); } //获取通道配置 @Override public Conf getPathConfig(String name) { return mediaClient.getPathConfig(name); } //获取所有通道配置列表 @Override public PathConfig getPathConfigs(Integer pageNum, Integer pageSize) { return mediaClient.getPathConfigs(pageNum - 1, pageSize); } }