ZLMediaKit实现海康监控摄像头实时播放+RTSP流浏览器可播放转码方案调研+获取视频截图
这篇文章适合想要把海康的rtsp转码成浏览器可播放的格式,要求只是播放出来,有视频截图(可选),又不太了解音视频大坑的前端同学(服务自己部署就好了,不需要后端介入)。
支持通过接口访问,非常友好,也不需要在前端项目里写多余的东西,只需要有个rtsp原始流,接口推流->获取拉流参数->flv播放,结束。
需求背景
我司的合作公司采购了海康的摄像头做室内外监控,需要实现在内网大屏上播放海康视频流,因为不是直接采买,没有直接对接海康的技术人员,自己摸索了大半个月方案。
我的系统:MAC
播放端:Windows电视
服务器:Ubuntu
难点:
- 内网不方便调试
- 没有海康的技术对接,官方文档不清晰
- 音视频技术栈过多过杂,坑点巨多
- 内网因为种种原因不支持https(后来通过询问知道的)
- 需求不清晰明确
最终解决方案
ZLMediaKit
安装启动过程
参考这一篇文章: blog.csdn.net/qq_53200007…
相关命令
以守护进程启动: ./MediaServer -d &
// 如果554端口被占用
查找554端口的使用进程 sudo lsof -i :554
// 杀死554端口的进程
sudo kill -9 <进程ID>
配置文件
参见: 配置文件详解
要修改的项稍微有点多。
如果是通过docker安装的,配置文件在conf里,如果非docker,配置文件的路径在release/你的系统/Debug
下。
RTSP转其他流需要修改的配置项
[rtc]
externIP=改成你自己的服务器ip
[rtsp]
directProxy=0
跑通DEMO
重要: 按照API说明文档,先在ApiFox或者ApiPost等调试工具中访问通了,确保服务可用.
- ZLM全面支持H265/H264/AAC/G711/OPUS,检查一下视频编码是否在其中。
- 确定流可用,可通过VLC播放器进行播放。(VLC->添加流->来自网络->rtsp流直接播放不需要改配置)
- 拿到秘钥secret,在配置文件前几行。
- 确保服务在运行中,随时监控日志。
- stream需要是唯一的值,推荐时间戳。
主要的接口就是通过/index/api/addStreamProxy
推流,成功之后可以通过webRTC拉取,也可以flv拉取。我们的服务器不支持https,所以使用flv拉取。
出现如下响应就是推流成功了,如果没成功,检查一下配置文件,看一下错误日志,多搜搜gitHub的issue,也可以评论区提问。
把下面的代码复制到HTML文件中就可以看到画面啦~
<script src="https://cdn.bootcss.com/flv.js/1.5.0/flv.min.js"></script>
<video id="videoElement"></video>
<button class="btn">播放</button>
<script>
if (flvjs.isSupported()) {
var videoElement = document.getElementById('videoElement');
var flvPlayer = flvjs.createPlayer({
type: 'flv',
url: 'http://这是服务的主机地址/这里写app的值/这里写stream的值.live.flv'
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
document.querySelector('.btn').addEventListener('click', () => {
flvPlayer.play();
})
}
</script>
可通用Hook(拿去就能用)
- 这里的stream是流的唯一标识,我选择了用dayJS产生的时间戳,也可以用其他标识。
- 这里的容器我写死了document.getElementById('videoElement'),也可以改成通过ref传进来的方式。
- rtsp地址可能有特殊字符,所以我进行了编码。
import axios from 'axios';
import dayjs from 'dayjs';
const useFlvVideo = (
rtspUrl,
serverConfig = {
serverSecret: '',
serverUrl: '',
app: 'live'
}
) => {
const { serverSecret, serverUrl, app } = serverConfig;
let flvPlayer = null;
let stream = null;
const checkParams = () => {
if (!serverSecret) {
throw new Error('serverSecret is required');
}
if (!serverUrl) {
throw new Error('serverUrl is required');
}
if (!app) {
throw new Error('app is required');
}
if (!rtspUrl) {
throw new Error('rtspUrl is required');
}
};
checkParams();
const checkFlvIsSupported = () => {
return window?.flvjs.isSupported();
};
const startPlay = async () => {
stream = dayjs().valueOf();
await addStreamProxy();
await startVideoPlay();
return stream;
};
const addStreamProxy = async () => {
const url = `http://${serverUrl}/index/api/addStreamProxy`;
const result = await axios.get(url, {
params: {
secret: serverSecret,
vhost: serverUrl,
app,
url: encodeURI(rtspUrl?.value),
stream: stream
}
});
console.log(result, 'result');
};
const startVideoPlay = () => {
var videoElement = document.getElementById('videoElement');
flvPlayer = window?.flvjs.createPlayer({
type: 'flv',
url: `http://${serverUrl}/${app}/${stream}.live.flv`
});
console.log('videoElement', videoElement, flvPlayer, `http://${serverUrl}/${app}/${stream}.live.flv`);
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvPlayer.play();
};
const stopPlay = async () => {
const url = `http://${serverUrl}/index/api/close_streams`;
const result = await axios.get(url, {
params: {
secret: serverSecret,
vhost: serverUrl,
app,
stream,
force: true
}
});
flvPlayer.stop();
console.log(result, 'result');
};
return {
startPlay,
stopPlay,
flvIsSupported: checkFlvIsSupported()
};
};
export default useFlvVideo;
坑n+1: 接口报错500问题
发现偶现推流接口500问题,经过测试发现超过3个流就不行了,看到这个issue, 结!论!是!这个服务器只支持3路。
访问/index/api/getMediaList
接口查看流列表。
那么
- 可以关闭不需要转码的协议避免浪费资源
- 因为每次打开都是新的推流,所以可以在不看的时候调用
/index/api/close_streams
关闭流。 - 开启无人观看自动关闭配置。酌情修改配置时间(可以根据播放器超时时间)。
- 还可以不用时间戳去开启流,用视频的唯一标识,这样多个客户端可以拉同一个流。(不过这不解决根本问题)
坑n+2: ZLM视频实时截图
截图的API是这一个:视频截图
如果出现了截图返回默认图片的问题,解法如下
- 修改配置文件截图FFmpeg模板 加上-rtsp_transport tcp
- 核对配置文件里的FFmpeg 路径 MAC可以用命令
where FFmpeg
来查找位置 - 只能使用已经被推送的流 我使用addStreamProxy返回结果的路径就可以,其他的没经过推送的流就不行(比如我的服务器只支持3路,那就只能截这3路。。)
外网调试解决方案
花生壳内网穿透
内网不方便调试,每次都需要把包丢上去看效果,这里用了花生壳,有1G试用流量限制,穿透设置TCP协议,554端口,穿透出来的流能通过VLC播放器播放就行。
和向日葵同一家厂商,远程调试必备。
我的思路
一开始,我接到的需求是,摄像头采集视频后会经过一台外网算法服务器计算之后再推出来,但是在VLC播放器太卡了,需要看看为什么卡顿。于是我去简单了解了音视频播放原理:
猜测可能是 1.带宽不够 2.传输过程损耗太大 3.经过算法服务器处理之后有损耗, 需求就变成了“春花同学,你开发一个流媒体服务器!我要一个流媒体服务器!”,尝试进一步沟通失败后,我的目标变成了流媒体服务器。
发现前端可用的流媒体服务器真的不多,基本只有media-soup和node-media-server,前任前段用node-media-serve写了一个简单的DEMO,就是把mp4推成rtsp(推流),再转成flv(拉流),面对摄像头的复杂场景真的没法用。但是我知道了ffmpeg是可以实现推流和转码的。我用media-soup也实现了一遍这个DEMO,资料非常少,跑是跑通了,还是一头雾水。
后来去了现场之后发现现场的视频播放延迟在正常范围,讨论之后决定把服务器布到现场去,需求就变成了“把这个RTSP在浏览器播放”。于是我研究了一番,尝试使用webrtc-stream,也是一番折腾,本地跑通了,但是局域网内跑不通,gitHub上也没有找到合适的解决办法。
这时候又得知短期内用的只有海康摄像头,询问海康智能客服得到对接指南,海康提供的SDK和DEMO只能在Windows上用,Linux上没什么能迅速跑通的方案,内嵌到VUE项目中的方案,文档较为复杂麻烦。Chrome的session禁止共享安全策略等等问题,尝试过了均不能内嵌。一般解决方案都是跳转到海康的预览页面。
最后尝试ZLM方案成功。
其他方案
尝试的其他解决方案
node-media-server
media-soup
webrtc-stream(跑通本地)
海康的SDK
FFmpeg
总结
- 应该找一个方案一直做下去到走通为止
- 环境要想办法提前了解(比如不能用https,比如所有摄像头都是海康的,比如可以使用插件)
- 想办法挖掘真正的需求,需求工期都过一半了,我才知道原来要在大屏上用,且只需要考虑海康的摄像头,且可以用插件。
后端Leader从 “它太卡了!我要它不卡!” 到 “我要一个流媒体服务器!我不管我就要!” 到 “不然你用原来的那个DEMO!” (那个DEMO非常简单根本完全不能用)再到 “你自己写一个推拉流逻辑吧”
最后使用ZLM方案部署出问题 “你看看,那咋没有呢”
就。
还是要有自己的节奏,很多时候你听到的需求并不是真正的需求,连提出需求的人也不知道真正的需求是什么,虽然走了很多弯路,也学到了很多东西,这是在不混乱的团队里得不到的体验。