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() + ":" + mediamtxConfig.getRtspPort() + "/" + ardVtdu.getName();
|
String rtmpUrl =
|
"rtmp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtmpPort() + "/" + ardVtdu.getName();
|
String protocol = mediamtxConfig.isHttps() ? "https://" : "http://";
|
String webrtcUrl =
|
protocol + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getWebrtcPort() + "/" + 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.removePath(ardVtdu.getName());
|
}
|
mediaClient.addPath(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() + ":" + mediamtxConfig.getRtspPort() + "/" + ardVtdu.getName();
|
String rtmpUrl =
|
"rtmp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtmpPort() + "/" + ardVtdu.getName();
|
String protocol = mediamtxConfig.isHttps() ? "https://" : "http://";
|
String webrtcUrl =
|
protocol + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getWebrtcPort() + "/" + 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<StreamInfo> paths(Integer pageNum, Integer pageSize) {
|
Paths paths = mediaClient.paths(pageNum - 1, pageSize);
|
List<Item> items = paths.getItems();
|
List<StreamInfo> 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<StreamInfo> PushStreamInfoList = new ArrayList<>();
|
Paths paths = mediaClient.paths(pageNum - 1, pageSize);
|
int itemCount = paths.getItemCount();
|
tableDataInfo.setTotal(itemCount);
|
List<Item> items = paths.getItems();
|
for (Item item : items) {
|
StreamInfo info = new StreamInfo();
|
//ID
|
String name = item.getName();
|
info.setName(name);
|
Conf conf = mediaClient.getPathConfig(name);
|
//源
|
info.setRtspSource(conf.getSource());
|
//RTMP播放地址
|
String rtmpUrl = "rtmp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtmpPort() + "/" + name;
|
info.setRtmpUrl(rtmpUrl);
|
//RTSP播放地址
|
String rtspUrl = "rtsp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtspPort() + "/" + name;
|
info.setRtspUrl(rtspUrl);
|
//WEBRTC播放地址
|
String protocol = mediamtxConfig.isHttps() ? "https://" : "http://";
|
String webrtcUrl =
|
protocol + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getWebrtcPort() + "/" + name;
|
info.setWebrtcUrl(webrtcUrl);
|
|
//上行流量
|
long bytesReceived = item.getBytesReceived();
|
String formatReceivedSize = ArdTool.formatFileSize(bytesReceived);
|
info.setUpTraffic(formatReceivedSize);
|
//下行流量
|
long bytesSent = item.getBytesSent();
|
String formatSentSize = ArdTool.formatFileSize(bytesSent);
|
info.setDownTraffic(formatSentSize);
|
//传输协议
|
info.setProtocol(conf.getSourceProtocol());
|
//拉流数量
|
List<Readers> readers = item.getReaders();
|
//开始时间
|
info.setBeginTime(item.getReadyTime());
|
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<WebrtcSession> 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<RtmpSession> 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<RtspSession> 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<String> getNameList() {
|
List<String> nameList = new ArrayList<>();
|
try {
|
Paths paths = mediaClient.paths(0, 1000);
|
List<Item> 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<String> nameList = getNameList();
|
if (nameList.contains(name)) {
|
result = true;
|
}
|
return result;
|
}
|
|
/**
|
* 清空所有流媒体信息
|
*
|
* @author 刘苏义
|
* @date 2024/8/22 10:18
|
*/
|
@Override
|
public void clearAll() {
|
List<String> 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);
|
}
|
|
}
|