SRS流媒体服务器实战:从Docker部署到WebRTC低延迟生产配置
最近给一个在线教学项目做实时音视频方案,刚开始图省事直接租云直播服务,结果带宽加上存储,一个月大几千块,业务还没跑起来成本先崩了。后来索性把目光转向开源流媒体服务,最终锁定了 SRS(Simple Realtime Server)。这东西确实能打,单机几路到上千路并发都能扛,而且覆盖 RTMP、HLS、HTTP-FLV、WebRTC、SRT 这些主流协议,配合 Docker 部署基本十分钟就能把一个可用的实时音视频流媒体平台跑起来。这篇文章就按我实际操作的顺序,把从环境准备、容器启动、推流拉流联调,到生产环境鉴权与调优的过程完整记录下来,给正准备自建流媒体服务的朋友一份可以直接照做的参考。
1. 自建流媒体平台,为什么我选了 SRS
1.1 SRS 到底能做什么
SRS 全称 Simple Realtime Server,是一个开源的实时视频服务器,也是目前流媒体领域活跃度很高的开源项目之一。我最初是被它的协议覆盖范围吸引的:输入端支持 RTMP、SRT、WebRTC 推流,输出端支持 RTMP、HLS、HTTP-FLV、WebRTC、SRT,基本覆盖了常见直播场景的所有需求。
比如我现在的在线教学项目,老师和学生之间的画面需要低延迟,课堂录制要转成 HLS 方便课后回看,同时还要给外部系统提供 HTTP-FLV 拉流接口。这些需求用 SRS 一个服务就能全部满足。
除了协议,SRS 还内置了不少生产级能力:
- HTTP API 与 Web 控制台,方便查看在线流、踢流、监控状态
- 推流与播放鉴权,支持 secret 方式,也支持 HTTP 回调做自定义鉴权
- DVR 录制,能把直播流直接录成文件
- 转码能力,内置 FFmpeg 引擎,可做分辨率、码率转换
- 源站集群与边缘集群,通过 edge 模式做负载分担
对于中小团队来说,这些能力已经足够覆盖从 MVP 到生产环境的完整链路。
1.2 对比 nginx-rtmp、MediaMTX、ZLMediaKit,优势在哪
自建流媒体服务不止 SRS 一个选择,我也简单对比过其他方案。这里放一张我实测后的横向对比表:
| 方案 | 协议支持 | 上手难度 | 集群与高级能力 | 维护活跃度 | 适用场景 |
|---|---|---|---|---|---|
| nginx-rtmp | RTMP、HLS | 低 | 基本没有 | 一般 | 简单直播、临时测试 |
| MediaMTX | RTSP、RTMP、HLS、WebRTC 等 | 低 | 较弱 | 高 | 摄像头接入、嵌入式 |
| ZLMediaKit | RTSP、RTMP、HLS、WebRTC | 中 | 较强 | 高 | 公网视频平台、GB28181 |
| SRS | RTMP、HLS、HTTP-FLV、WebRTC、SRT | 中 | 强 | 高 | 大规模直播、生产级服务 |
这里说下我的选型思路。nginx-rtmp 最大的问题是功能简单,没有 HTTP-FLV 和 WebRTC 支持,团队如果后面要做低延迟方案,基本得重来。MediaMTX 胜在轻量,但集群、录制、鉴权这类偏业务的场景需要自己拼装。ZLMediaKit 能力很强,不过配置和二次开发门槛偏高。相比之下,SRS 的 Docker 镜像做得非常规范,默认配置就能跑通全链路,遇到问题官方文档和社区讨论也容易搜到答案。对于我这种希望快速上线、后续又能从容扩展的团队来说,SRS 是综合性价比最高的选择。
2. 部署前的准备:Docker 环境、镜像加速与端口规划
2.1 Docker 安装的几个关键点
SRS 官方提供了非常成熟的 Docker 镜像,所以部署的第一步是先把 Docker 环境准备好。如果服务器上已经装好了 Docker,这一步可以直接跳过。
在 Linux 服务器上,我比较推荐用官方安装脚本:
curl -fsSL https://get.docker.com | bash -s docker
安装完成后执行
systemctl enable --now docker
设置开机自启,再用
docker version
确认服务正常。
如果你是在 Windows 或 macOS 上先做本地验证,通常用 Docker Desktop。这里有个高频问题,Windows 下 Docker Desktop 启动时会报 "virtualization support not detected",就是因为本机没有开启硬件虚拟化。处理步骤如下:
- 重启电脑进入 BIOS / UEFI,开启 Intel VT-x 或 AMD-V
- 在 Windows 功能中勾选“适用于 Linux 的 Windows 子系统”(WSL2)和“虚拟机平台”
-
以管理员身份执行
wsl --update,再启动 Docker Desktop
不夸张地说,这个“虚拟化未检测到”的问题是 Docker Desktop 新手遇到最多的拦路虎,但原理其实很简单,就是虚拟化能力没打开,按上面的步骤排查基本都能解决。
2.2 镜像下载慢,先配镜像加速器
Docker 安装好之后,很多同学会直接去拉镜像,结果发现速度感人。这个问题不是网络问题,而是默认镜像源在海外的原因。解决办法是给 Docker 配置镜像加速器。
在 Linux 上,编辑
/etc/docker/daemon.json
:
{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
保存后重启 Docker:
sudo systemctl restart docker
Windows 和 macOS 的 Docker Desktop 则在 Settings -> Docker Engine 里修改 JSON 配置,然后点击 Apply & Restart。
配好镜像加速之后,再拉取
ossrs/srs
镜像,速度会明显改善。这一步虽然简单,但建议在开始所有操作之前先搞定,否则后面每次拉镜像都要等半天。
2.3 端口与目录规划
部署之前还有一个容易忽略的点:端口规划。SRS 默认会用到这几个端口,我整理成了表格:
| 端口 | 协议 | 用途 |
|---|---|---|
| 1935 | TCP | RTMP / SRT 推流与拉流 |
| 1985 | TCP | HTTP API,管理接口与回调 |
| 8080 | TCP | HTTP 服务,HLS、HTTP-FLV、WebRTC 信令 |
| 8000 | UDP | WebRTC 媒体传输(RTP/RTCP) |
如果服务器上有防火墙,记得放行这些端口。比如 CentOS/RHEL 系执行:
sudo firewall-cmd --permanent --add-port=1935/tcp
sudo firewall-cmd --permanent --add-port=1985/tcp
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=8000/udp
sudo firewall-cmd --reload
目录规划上,建议在宿主机建一个工作目录,比如
/opt/srs
,下面分
conf
和
objs
两个子目录。
conf
用来放自定义配置文件,
objs
用来挂载录制文件、HLS 切片和日志。这样容器升级或重建时,数据不会丢。
3. 从零启动 SRS 容器
3.1 先跑通最简单的启动命令
第一次验证 SRS 是否可用,不建议直接上复杂配置,用官方默认镜像跑起来就行:
docker run --rm -p 1935:1935 -p 1985:1985 -p 8080:8080 \
-p 8000:8000/udp --name srs ossrs/srs:5
启动参数说明一下:
-
--rm:容器停止后自动删除,适合临时测试 -
-p 1935:1935:接收 RTMP 推流 -
-p 1985:1985:暴露 HTTP API -
-p 8080:8080:提供 HTTP 播放服务 -
-p 8000:8000/udp:WebRTC 媒体传输必须使用 UDP
容器启动后,打开浏览器访问
http://服务器IP:8080/console/
,能看到 SRS 管理控制台。再执行
curl http://服务器IP:1985/api/v1/versions
,如果返回类似下面的 JSON,说明服务已经正常:
{
"code": 0,
"data": {
"version": "5.0.xxx",
"server": "SRS/5.0.xxx"
}
}
这一步跑通后,SRS 就已经具备完整的推拉流能力了。默认配置里已经开启了 HTTP-FLV、HLS 和 WebRTC,所以接下来可以直接进行推流验证。
3.2 用 Docker Compose 管理生产级部署
docker run
适合快速验证,但生产环境里不好维护,尤其是重启策略、环境变量、目录挂载这些参数,写在命令行里既不直观也容易出错。我建议切换到 Docker Compose。
先创建目录结构:
mkdir -p /opt/srs/conf /opt/srs/objs
然后编写
docker-compose.yml
:
services:
srs:
image: ossrs/srs:5
container_name: srs
restart: unless-stopped
ports:
- "1935:1935"
- "1985:1985"
- "8080:8080"
- "8000:8000/udp"
environment:
- TZ=Asia/Shanghai
- SRS_CANDIDATE=127.0.0.1
volumes:
- /opt/srs/conf/srs.conf:/usr/local/srs/conf/srs.conf
- /opt/srs/objs:/usr/local/srs/objs
这里我重点说下两个容易踩坑的地方:
第一,
SRS_CANDIDATE
这个环境变量。WebRTC 在建立连接时,需要把自己的公网地址告诉浏览器,
SRS_CANDIDATE
就是用来指定这个地址的。如果只是本机测试,填
127.0.0.1
就行;如果部署在有公网 IP 的服务器上,一定要改成服务器的公网 IP,否则浏览器 WebRTC 播放会一直连接失败。
第二,
/opt/srs/objs
这个挂载目录。SRS 的 HLS 切片、DVR 录制文件默认都写在
/usr/local/srs/objs/nginx/html
下,通过挂载目录,这些文件会落到宿主机,方便后续做回源、备份或由 Nginx 直接托管。
一切就绪后执行:
docker compose up -d
docker compose logs -f srs
logs -f
会实时输出 SRS 的日志,后面排查推流问题时非常有用。
3.3 SRS 核心配置逐行解读
挂载的
srs.conf
是决定 SRS 行为的关键文件。下面是我最常用的一套基础配置,也是官方默认配置的近亲,我把每段的作用都标出来:
listen 1935; # RTMP 监听端口
max_connections 1000; # 最大连接数
srs_log_tank console; # 日志输出到控制台
http_api {
enabled on; # 开启 HTTP API
}
http_server {
enabled on;
listen 8080; # 控制台与 HLS 服务
}
rtc_server {
enabled on; # 开启 WebRTC
}
vhost __defaultVhost__ {
hls {
enabled on; # 开启 HLS 切片
}
http_remux {
enabled on; # 开启 HTTP-FLV
}
webrtc {
enabled on; # 开启 WebRTC 播放
}
}
几个关键点:
-
max_connections决定了单机并发上限,默认 1000 对于中小业务足够了 -
vhost __defaultVhost__是一个默认虚拟主机,如果没有特殊业务需求,所有流都走这个 vhost -
http_remux提供了 HTTP-FLV 流,它比 RTMP 更适合在浏览器和移动端播放 -
hls默认按 10 秒一个切片生成 m3u8 索引文件,HLS 延迟相对高,但对弱网兼容性最好
修改配置后,不需要重启容器,执行
docker exec -it srs kill -1 1
就能热加载配置。不过要注意,热加载虽然方便,但部分参数(如端口变更)还是需要完整重启才能生效,稳妥起见,生产环境我一般还是直接
docker compose restart
。
4. 推流拉流全流程联调
4.1 用 OBS 推流验证 RTMP 链路
服务跑起来之后,第一件事就是验证推流链路。我用的是 OBS Studio,它免费且跨平台,用来做推流测试非常顺手。
OBS 里配置推流地址:
- 打开“设置” -> “直播”
- 服务选择“自定义”
-
服务器填写
rtmp://服务器IP:1935/live -
串流密钥随便填一个,比如
stream1 - 点击“开始直播”
这里的
.live
是应用名,
stream1
是流 ID。推流地址最终会解析成
rtmp://服务器IP:1935/live/stream1
。
开始推流后,回到服务器执行:
docker logs -f srs
看到类似
rtmp: stream service stream1, play...
的日志,说明推流成功。如果日志只显示 TCP 连接建立,没有出现流信息,多半是推流地址格式不对,检查一下
live
和
stream1
有没有写错。
4.2 播放端验证:HLS、HTTP-FLV、WebRTC
推流成功只是第一步,关键还要验证拉流。SRS 对同一条流会同时输出多种协议,播放地址分别是:
-
RTMP:
rtmp://服务器IP:1935/live/stream1 -
HLS:
http://服务器IP:8080/live/stream1.m3u8 -
HTTP-FLV:
http://服务器IP:8080/live/stream1.flv -
WebRTC:
webrtc://服务器IP:8080/live/stream1
验证时我习惯分三步走:
先用 VLC 播放 RTMP 地址,确认最基本的链路通。VLC 打开网络串流,输入
rtmp://服务器IP:1935/live/stream1
,能出画面就说明推流和 RTMP 播放都没问题。
接着验证 HLS。在浏览器里直接访问
http://服务器IP:8080/live/stream1.m3u8
,这是一个 m3u8 索引文件,内容会列出视频分片路径。看到分片列表后,再到手机或电脑上用支持 HLS 的播放器播放同一条地址。HLS 兼容性最好,几乎所有的播放器都支持,适合做回放和弱网场景。
最后验证 WebRTC。最方便的方式是打开 SRS 管理控制台
http://服务器IP:8080/console/
,进入 WebRTC 播放页面,输入
webrtc://服务器IP:8080/live/stream1
,点击播放。如果控制台用不了,也可以直接用 SRS 自带的演示播放器页面。
4.3 不同协议的延迟差异
联调的时候,顺便可以感受一下不同协议在延迟上的差异。我实测的一组参考数据:
| 协议 | 端到端延迟 | 适用场景 |
|---|---|---|
| HLS | 10~30 秒 | 录播回放、弱网兼容 |
| RTMP | 2~5 秒 | 常规直播,搭配 Flash 或原生播放器 |
| HTTP-FLV | 2~5 秒 | 浏览器直播,延迟低,兼容性好 |
| WebRTC | 0.3~1 秒 | 视频会议、连麦、低延迟互动 |
HLS 延迟高的原因在于切片机制,播放器至少需要加载两到三个切片才能开始播放,每个切片默认 10 秒,所以延迟很容易上到十秒以上。RTMP 和 HTTP-FLV 走的是长连接流式传输,播放器接收到数据即刻渲染,延迟就低很多。WebRTC 是基于 UDP 的实时传输,不做 TCP 重传那些冗余逻辑,所以能压到亚秒级。
实际业务中,如果教室连麦和老师直播同时进行,我会让老师端用 WebRTC 推流,学生端用 HTTP-FLV 或 WebRTC 拉流,这样延迟控制在可接受范围内。如果需要课后回放,再依赖 HLS 的切片能力。
5. 生产化配置:鉴权、录制、WebRTC 调优与集群
5.1 推流与播放鉴权:别把你的流裸奔出去
默认配置下,SRS 是没有任何鉴权的。也就是说,只要知道服务器地址和流名,任何人都能推流或拉流,这在公网环境下完全不可接受。上线前务必先做鉴权。
SRS 最简单的鉴权方式是配置 secret。编辑
srs.conf
:
vhost __defaultVhost__ {
play {
secret play123;
}
publish {
secret pub123;
}
}
配置后,推流地址需要带上 secret:
rtmp://服务器IP:1935/live/stream1?secret=pub123
播放地址同理,需要带
?secret=play123
。这种方式适合内部系统,简单高效,但 secret 会被放在 URL 里,安全性有限。
更安全的方案是走 HTTP 回调鉴权。SRS 在收到推流或播放请求时,可以向你的后端服务发起 HTTP 请求,由后端决定是否允许。核心配置如下:
vhost __defaultVhost__ {
http_hooks {
enabled on;
on_publish http://后端服务地址/api/srs/on_publish;
on_play http://后端服务地址/api/srs/on_play;
}
}
后端收到回调后,检查请求里的参数(如流名、token),返回 HTTP 200 表示允许,返回非 200 则拒绝。这种方案灵活很多,可以把鉴权逻辑和业务系统整合起来,适合对外提供服务。
5.2 DVR 录制:把直播流变成可回看的文件
在线教学这个场景,课程回放是刚需。SRS 内置的 DVR 功能就是为了这个场景设计的。在 vhost 里添加:
vhost __defaultVhost__ {
dvr {
enabled on;
dvr_path ./objs/nginx/html/record/[app]/[stream]/[2006][01][02]_[15][04][05].flv;
dvr_plan segment;
}
}
dvr_path
里的时间占位符很有意思,
[2006]
表示年份,
[01]
表示月份,以此类推,生成的录制文件会自动按时间归类,回看时非常好找。
dvr_plan segment
表示按切片方式录制,每段录制文件时长有一定间隔,方便管理。
录制文件默认生成在挂载目录
/opt/srs/objs/nginx/html/record
下。如果需要对外提供回看,可以直接用 Nginx 把这目录托管成静态文件,也可以让 SRS 的 HTTP 服务直接访问。
5.3 WebRTC 低延迟调优的实战经验
WebRTC 是 SRS 里最有价值的能力之一,但也是调优坑最多的部分。除了前面提到的
SRS_CANDIDATE
配置,还有几个细节需要注意。
第一,浏览器限制。Chrome 等浏览器只有在 HTTPS 或 localhost 环境下才允许调用摄像头和麦克风,所以如果 WebRTC 页面要走公网访问,前面必须套一层 HTTPS。我一般用 Nginx 反代 8080 端口,再挂上证书解决。
第二,GOP 长度。WebRTC 的延迟和编码器的关键帧间隔强相关。OBS 推流时,把“关键帧间隔”设为 1 秒,SRS 侧做播放时,把 GOP 缓存关闭:
vhost __defaultVhost__ {
play {
gop_cache off;
}
}
默认情况下,SRS 为了秒开会缓存一个 GOP,也就是从上一个关键帧开始的数据。这对低延迟场景反而是负担,因为播放器必须等下一个关键帧才能起播,延迟就高了。关闭
gop_cache
后,首帧抵达就可能渲染,延迟自然降下来。
第三,UDP 端口连通性。WebRTC 媒体流走的是 UDP 8000 端口,前面已经强调过了,这里再提醒一次:云服务器默认的安全组和防火墙往往只放行 TCP 端口,UDP 很容易被漏掉。如果 WebRTC 连不上,优先检查 UDP 端口是否真正对公网开放。
5.4 边缘集群:从单机到多节点的扩展思路
当并发量上来之后,单机 SRS 可能扛不住,或者业务需要做地域就近接入,这时候就要考虑集群了。SRS 的集群采用 Origin + Edge 架构,思路跟 CDN 很像:源站负责接收推流并存储录制,边缘节点负责接收播放请求,如果没有本地流,就去源站回源。
Edge 节点配置很简单:
vhost __defaultVhost__ {
mode remote;
origin 10.0.0.10:1935;
}
origin
指向源站 SRS 的地址。这样,播放端只需要连接边缘节点的 IP,边缘节点会自动从源站拉流并缓存,同一路流被大量播放时,边缘节点只回源一次,大大减轻源站压力。
当然了,集群模式下 Docker Compose 就不太够用了,建议引入 Docker Swarm 或 Kubernetes 做编排,让容器在有多个节点的集群中自动调度。这个属于后续演进方向,前期先把单节点跑稳,再逐步扩展会更务实。
6. 踩坑实录:高频问题与排查方法速查
6.1 容器启动失败类问题
问题一:端口占用
docker run
或
docker compose up
提示
Error starting userland proxy: listen tcp4 0.0.0.0:1935: bind: address already in use
。这就是 1935 端口被其他程序占了。排查命令:
sudo lsof -i :1935
找到占用进程,关掉它,或者把 SRS 的宿主机端口映射改掉,比如
-p 19350:1935
,播放地址对应改成
rtmp://服务器IP:19350/live/stream1
。
问题二:Windows 下 Docker Desktop 无法启动
前面提到过,典型的报错是
Docker Desktop failed to start because virtualisation support wasn't detected
。本质就是 BIOS 没有开启虚拟化,或者 WSL2 没装好。反复确认 BIOS 里的 Intel VT-x/AMD-V 已开启,Windows 功能里勾选了“虚拟机平台”和“适用于 Linux 的 Windows 子系统”,然后执行
wsl --update
升级 WSL 内核。
6.2 推流和播放异常类问题
问题三:能推流但 VLC 拉流黑屏
RTMP 推流成功,但 VLC 播放 RTMP 地址一直黑屏。这种问题常见于 OBS 的编码设置和 SRS 默认配置不兼容。试着把 OBS 的视频编码器从硬件编码(NVENC/QuickSync)切换为 x264 软件编码,输出分辨率和帧率设置为常见的 1920x1080@30 或 1280x720@30,再重推。不少解码库对高帧率或特殊色彩格式支持不好,换成通用参数往往就正常了。
问题四:HLS 播放地址打不开
HLS 拉流时浏览器返回 404。先确认
hls
配置已经开启,然后看一下挂载目录里是否生成了 m3u8 文件:
ls /opt/srs/objs/nginx/html/live/
如果文件都不存在,说明 HLS 切片没生成,很大概率是配置热加载时 hls 模块没生效。稳妥做法是
docker compose restart
重启容器。
6.3 WebRTC 连接问题
问题五:WebRTC 一直连接中或直接失败
这个问题的排查顺序我建议按三步走:
第一步,验证
SRS_CANDIDATE
。在容器里执行:
docker exec srs env | grep CANDIDATE
看是否指向了可被浏览器访问到的公网 IP。如果填的是内网 IP,浏览器在公网环境下无法连通,必然失败。
第二步,验证 UDP 端口。在播放端电脑上执行:
nc -u 服务器IP 8000
如果能通,说明 UDP 端口放行了。如果没反应,去云控制台安全组和服务器防火墙检查 8000/udp 放行规则。
第三步,验证 HTTPS。浏览器访问 WebRTC 页面时,地址必须是 HTTPS,否则摄像头和麦克风权限会被拒绝。用 Nginx 反代并配置证书可以解决这个问题。
这套排查流程我每次遇到 WebRTC 问题都会走一遍,基本能定位 80% 以上的故障原因。剩下的就是编码参数和带宽问题了,需要结合业务再做针对性调优。
最后分享一个经验:在部署 SRS 时,最忌讳一上来就改一堆高级配置,先把最基础的 RTMP 推流跑通,再逐步叠加 HLS、HTTP-FLV、WebRTC,最后再上鉴权和集群。每加一个功能,就验证一个环节,出问题时能快速判断是新配置引入的还是原有链路的问题。流媒体链条比较长,推流端、服务端、播放端任何一端出问题都会导致异常,分层排查的习惯能帮你省下大把时间。
更多推荐
所有评论(0)