如标题所示。关键词:SRS,辅码流,浏览器,多路预览。效果如图,在一张浏览器页面,同时展示9个摄像头的预览画面。

怎么做到的?
一句话概括,就是:
用SRS拉取摄像头的辅码流(子码流),处理成浏览器可以直接播放的HTTP-FLV格式(如http://172.16.10.124:18080/live/cam20922.flv)。
摄像头的视频流有主码流、辅码流(子码流),主码流分辨率高,高清,用于观察、大屏展示、存储和回放;而辅码流低分辨率,视频质量较低,适合预览,或在手机中播放。换言之,主码流占带宽多,子码流占带宽少,要想在一个页面同时预览多个摄像头画面,必须用子码流。其实按摄像头厂家的说法,就算用子码流,同时显示9路摄像头,也是非常吃力的。
我们项目中,摄像头是注册到一个国标视频平台(国产开源的wvp-GB28181-pro),浏览器用的是它转码后的播放地址。但这个地址并没有分成主码流和子码流两种,要么是主码流,要么是子码流。由于国标的视频流用于正常播放,所以拉取子码流的话,就要另想办法,于是想到用SRS处理子码流。
下面是详细介绍。
一、部署SRS
SRS(Simple Realtime Server)是一个简单高效的实时视频服务器,支持RTMP、WebRTC、HLS、HTTP-FLV、SRT等多种实时流媒体协议。它也是国产开源项目。
我的想法是,采用SRS,拉取19个摄像头的子码流。摄像头提供的是RTSP协议的视频流,SRS支持RTSP,可以拉取和转成浏览器支持的HTTP-FLV格式。但是,如果摄像头提供的流是H.265格式的话,浏览器目前基本播放不了,需要转成H.264。SRS需要调用ffmpeg来完成这个转换工作。ffmpeg是一个法国程序员发起的音视频工具集,应用广泛,我看好多地方都用它。比如上面提到的国标视频平台wvp-GB28181-pro,底层用的也是ffmpeg。
我们服务器是国产的UOS 1070e,CPU海光的,也是AMD架构。采用docker镜像来部署SRS。该镜像内置了ffmpeg。但是官方文档却没有提到内置ffmpeg,同时官方文档也没有介绍我这个需求的解决方案,即如何拉取摄像头的RTSP视频流,并转化成HTTP-FLV,全靠问AI和摸索了。你看看它文档写的,不知道的还以为它镜像里根本没有ffmpeg,需要自己去下载。而且手动调用ffmpeg的做法,我认为是玩具级别的,根本不实用。万一有异常发生,ffmpeg就退出了,如何维护这个重启机制?简直不知所谓。

闲话休提,以下是SRS的部署及设置步骤。
1、部署SRS
1)拉取镜像
bash
sudo docker pull registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
采用docker确实是有助于简化安装。尤其是对于我们来说,生产服务器无法上网,可以在开发环境将docker镜像导出,再在服务器导入。
2)创建docker容器
将配置文件所在目录conf外挂到宿主机
bash
sudo docker run --restart=always -d -it --name srs \
-p 1935:1935 -p 1985:1985 -p 18080:8080 \
-v /home/work/docker/srs/conf:/usr/local/srs/conf \
registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
2、设置拉流
配置一个 ingest 拉取 RTSP 流。编辑conf/docker.conf。注意不是srs.conf,而是docker.conf!
以下是向一个摄像头拉流,其余摄像头类似。有多少个摄像头就配多少个ingest。
bash
ingest cam20112 {
enabled on;
input {
type stream;
url rtsp://192.168.201.12:554/stream_1;
}
ffmpeg /usr/local/srs/objs/ffmpeg/bin/ffmpeg;#注意要写绝对路径
engine {
enabled on;
output rtmp://127.0.0.1/live/cam20112;#把流"注册"进 SRS 的直播系统
ff_opts { #ffmpeg处理参数
-i [input];
-c:v libx264; #转化为H.264
-preset ultrafast;
-profile:v baseline;
-level 3.1;
-g 30;
-b:v 512k; # 目标码率
-maxrate 512k;# 最大码率(匹配摄像头)
-bufsize 1024k;
-vf "scale=640:360";#进一步降低分辨率
-r 15;
-an; #禁用音频
-f flv;
}
}
}
这句 output rtmp://127.0.0.1/live/cam20112; 把流"注册"进 SRS 的直播系统。SRS 有一个核心特性:只要有一条 RTMP 流进入系统(无论来源是推流还是 ingest 拉流),它会自动提供多种协议的输出,其中包括浏览器支持的HTTP-FLV。如上例,SRS运行以后,我们就可以在VLC里播放地址http://宿主机IP:18080/live/cam20112.flv 了。8080是SRS的http端口,但我们创建容器的时候映射到了宿主机的18080。

以下是一个完整的docker.conf示例。里面只处理了一个摄像头,增加摄像头处理就增加ingest即可。
bash
# docker config for srs.
# @see full.conf for detail config.
listen 1935;
max_connections 1000;
# For docker, please use docker logs to manage the logs of SRS.
# See https://docs.docker.com/config/containers/logging/
srs_log_tank console;
daemon off;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
rtc_server {
enabled on;
listen 8000;
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
candidate $CANDIDATE;
}
vhost __defaultVhost__ {
hls {
enabled on;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
rtc {
enabled on;
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
rtmp_to_rtc on;
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
rtc_to_rtmp on;
}
ingest cam20112 {
enabled on;
input {
type stream;
url rtsp://192.168.201.12:554/stream_1;
}
ffmpeg /usr/local/srs/objs/ffmpeg/bin/ffmpeg;
engine {
enabled on;
output rtmp://127.0.0.1/live/cam20112;
ff_opts {
-i [input];
-c:v libx264;
-preset ultrafast;
-profile:v baseline;
-level 3.1;
-g 30;
-b:v 512k;
-maxrate 512k;
-bufsize 1024k;
-vf "scale=640:360";
-r 15;
-an;
-f flv;
}
}
}
#可继续增加ingest
}
二、设置摄像头辅码流
摄像头本身也应该设置一下辅码流,这样利于提升传输效率。

三、nginx转发
如上所述,SRS启动以后,我们就可以播放类似http://宿主机IP:18080/live/cam20112.flv 的视频地址了。但是,如果我们站点是https的,只能播放同样是https的网址,不能直接播放http的网址,需要使用nginx转发一下,转成https的URL。
bash
# 已知本网站支持https
location /prv/ {
#http://172.16.10.124:18080/live/cam20922.flv
proxy_pass http://172.17.0.10:8080/live/;#172.17.0.10是SRS的docker容器地址
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
注意代转发的地址有讲究。已知 nginx 和 SRS 都采用docker部署。如前所述,虽然SRS已经将8080映射到了宿主机的18080,但nginx转发的时候,并不能使用宿主机的ip来访问SRS的流。不知道为什么,我的服务器里,docker容器可以互相访问。但我记得我没有创建docker网络,创建每个docker容器时也没有显式指定使用docker网络,但是生产服务器的docker容器确实可以互相访问。其中SRS的docker地址是172.17.0.10,所以在nginx的配置文件中,直接转发SRS的地址:
bash
proxy_pass http://172.17.0.10:8080/live/;#172.17.0.10是SRS的docker容器地址
五、浏览器播放
虽然摄像头的辅码流一再降低质量,但是用浏览器打开预览页面,9个摄像头只能出来6个。刷新、切换页面都是如此。后来找到原因了,是浏览器的限制。现代浏览器对同一域名(host)的 HTTP/1.1 并发连接数限制为 6 个(Chrome/Firefox/Edge 等均如此)。由于我使用了同一个nginx对flv流进行转发,所有 FLV 流都来自同一个域名:https://172.16.10.124/prv/...,因此,最多只能同时建立 6 个 TCP 连接,也就最多播放 6 路视频,第 7 个及以后的请求会被排队阻塞,直到前面某个连接关闭或超时。
真是岂有此理。我于是在生产服务器增加了1个nginx(端口跟第一个nginx不同)做转发;播放时,将摄像头分成2组,一组播放用nginx1转发的地址,另一组播放nginx2转发的地址,果然9个都出来了。