1.rtsp视频流网页播放概述
需求:当我们通过ONVIF协议,获取到了摄像头的rtsp流地址(长这样:rtsp://admin:123456789@192.168.9.16:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif)后,通过vlc播放器,我们可以查看监控视频内容,可是,我们应该如何在网页上查看视频内容呢?因为现在的浏览器都不支持rtsp流,因此我所选用的解决方案便是推流 + 转码
(1)转码推流工具ffmpeg(安装教程详见:https://www.cnblogs.com/h2285409/p/16982120.html),安装好之后,便可使用命令 ffmpeg -re -rtsp_transport tcp -i rtsp://admin:123456789@192.168.9.16:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif -c:v copy -c:a copy -f flv rtmp://127.0.0.1/live/16 将我们的rtsp视频流转码并推至流媒体服务器上,在这个命令中含有两个URL,前面的是我们的rtsp流地址,而后面的URL便是我们流媒体服务器的地址,以及一个-f参数,指定了我们视频流转码后的格式为flv
(2)流媒体服务器,主要调研了2款,一是整合了Rtmp模块的Nginx,二是SRS视频服务器,而我所选用的是SRS(官方文档:http://ossrs.net/lts/zh-cn/),在使用ffmpeg推流上SRS后,便可直接从SRS获得webRTC流地址(如本例:webrtc://192.168.4.23/live/16 ),为什么选用webRTC呢,因为它的延迟很低(也可以根据自己的需求选择其他的如http-flv或hls),然后,前端再通过webRTC播放器便可直接在页面上播放该直播流
SRS与ffmpeg参考:https://blog.csdn.net/diyangxia/article/details/120172920
ffmpeg进阶参考:https://segmentfault.com/a/1190000039782685
webRTC参考:https://www.cnblogs.com/ziyue7575/p/13927894.html
2.rtsp推流转码以及关闭推流相关代码实现
@Service
@Slf4j
public class MonitorServiceImpl {
//ffmpeg安装路径
@Value("${ffmpegPath}")
private String ffmpegPathPrefix;
//srs视频服务器地址
@Value("${srsAddress}")
private String srsAddress;
//srs-http-api端口,默认为1985
@Value("${srsHttpApiPort}")
private String srsHttpApiPort;
@Resource
private MonitorMapper monitorMapper;
@Resource
private RestTemplate restTemplate;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
private ConcurrentMap<String, TranscodeModel> transcodeMap = new ConcurrentHashMap<>();
private CopyOnWriteArrayList<ProcessOutputModel> processOutputList = new CopyOnWriteArrayList<>();
/**
* 进行推流转码
*/
public String transcode(String id) {
boolean success = false;
try {
if(transcodeMap.putIfAbsent(id, new TranscodeModel()) == null) {
Ipc ipc = monitorMapper.getIpcInfoById(id);
if(ipc == null) {
throw new RuntimeException("id为" + id + "的ipc不存在");
}
String rtspUrls = ipc.getRtspUrls();
if(!StringUtils.hasText(rtspUrls)) {
try {
//如果流地址不存在,则输入ipc的ip地址,端口号,帐号,密码以及传输协议进行获取
rtspUrls = OnvifUtil.getRTSPUrl(ipc.getIp(), ipc.getPort(), ipc.getAccount(), ipc.getPassword(), TransportProtocol.TCP);
} catch (Exception e) {
throw new RuntimeException("获取id为 " + id + " 的ipc的rtsp流地址失败,请检查该ipc是否开启或其配置信息是否正确");
}
}
if (!StringUtils.hasText(rtspUrls)) {
throw new RuntimeException("获取id为 " + id + "的ipc的rtsp流地址失败,请检查该摄像机是否开启或其配置信息是否正确");
}
//一般情况下获取到的ipc的rtsp流地址存在多个(主码流,辅码流或第三方码流等),我这里用了#号将它们进行分隔
String[] allRtspUrl = rtspUrls.split("#");
for (String rtspUrl : allRtspUrl) {
//使用ffprobe来获取流的一些信息,比如该流当前是否可用(在线),它的编码格式,分辨率以及fps等基本信息 将ffmpeg与ffprobe置于同一目录下
String requestStreamInfoCommand = String.format("%sffprobe %s", this.ffmpegPathPrefix, rtspUrl);
//用于判断该ipc是否在线
boolean online = true;
//用于判断该获取流信息的进程是否应该结束
long lastReadTime = System.currentTimeMillis();
log.info("准备请求流 {} 的信息,其命令为 {}", rtspUrl, requestStreamInfoCommand);
Process requestStreamInfoProcess