基于Docker部署SRS流媒体服务器:从RTMP推流到WebRTC低延迟直播的完整实践
上个月朋友公司内部直播项目找到我,需求一句话讲完:会议室的活动要推到全公司,手机电脑都能看,延迟别太夸张。我第一反应不是去翻云厂商的直播套餐,而是先想到了SRS。这个从RTMP单机服务一路做到WebRTC、HLS、HTTP-FLV全支持的开源流媒体服务器,社区活跃、文档也全,加上Docker部署SRS就三条命令的事,基本能满足大多数中小团队的实时音视频流媒体场景。这篇文章把我从拉镜像到真正跑通推流、拉流、录制、WebRTC的完整过程记录下来,包含我踩过的坑和最后在用的配置,适合刚接触流媒体服务、想快速私有化部署一套直播服务的开发者参考。
1. 先弄明白SRS和Docker这个组合为什么靠谱
1.1 流媒体服务本身的复杂度比想象中大
很多人以为流媒体服务器就是"把一份视频流转发给很多人看",上手才知道完全不是这么回事。RTMP负责推流接入,HLS要切片生成m3u8索引,HTTP-FLV要解决低延迟分发,WebRTC要处理ICE信令和UDP传输,每个协议都有自己的时序和格式要求。更别提推流鉴权、录制、时移、转码这些附加能力,用纯手工方式从Nginx加模块开始搭,光是编译依赖就能耗掉一整天。
我在做这个内部直播项目之前,也考虑过直接用云厂商的直播服务,但需求里有一条"录制文件要留在我们自己的服务器上,方便后续做剪辑复用",这就意味着要么买云的存储和转码服务,要么自建一套。云方案链路长、账单细,对一个小项目来说反而累赘。SRS正好卡在这个位置:单进程部署,配置文件写明白就能跑,协议覆盖广,官方文档里每个功能都有对应的配置片段,不需要到处找资料拼凑。
1.2 SRS到底解决了什么问题
SRS的全称是Simple Realtime Server,定位是高性能、高实时的流媒体服务器。它最早只做RTMP,后来逐步把HTTP-FLV、HLS、WebRTC、SRT这些主流协议都收了进来。最直观的价值是:你只需要维护一个服务进程、一份配置文件,就能同时提供推流接入、拉流分发、录制存储等多组能力。
我按自己实际使用中的理解整理了一个协议对照表,方便你根据场景选:
| 协议 | 典型用途 | 播放端支持 | 延迟量级 |
|---|---|---|---|
| RTMP | 推流上传、老牌播放器拉流 | VLC、FFmpeg、Flash遗留设备 | 1~3秒 |
| HTTP-FLV | 浏览器/移动端低延迟直播 | flv.js、西瓜播放器等 | 1~3秒 |
| HLS | 点播、大规模直播分发 | 几乎所有浏览器、iOS原生 | 5~20秒 |
| WebRTC | 音视频通话、互动直播 | 浏览器原生 | 0.2~0.5秒 |
这个表是我长期实践下来的经验值,不是官方承诺值。实际延迟还受推流端编码参数、网络带宽和播放端缓冲设置影响,但量级不会差太多。做内部直播、在线教学这类场景,HTTP-FLV和WebRTC基本够用;做活动录播回放,HLS最省心。
1.3 用Docker包一层有什么收益,又有什么代价
SRS本身支持直接从源码编译,官方也提供了编译脚本,但对不熟悉C++构建链的人来说,编译时间、依赖冲突、系统库版本问题都很劝退。用Docker之后,这些问题基本被隔离在镜像层了。我这边从拉镜像到服务起来,前后不到五分钟,这在裸机上几乎不可能。
容器化的另一个好处是可重复性。同一份srs.conf配合同一个镜像tag,在开发机、测试机、生产机上的行为是一致的,不会出现"我本地能跑,服务器上起不来"的经典事故。遇到需要升级的情况,换一个镜像tag重启容器即可,回滚也方便。
当然代价也有。如果你要编译SRS的自定义模块或修改底层依赖,容器内的环境会更受限,需要自己额外处理构建流程。但对绝大多数使用场景来说,用官方镜像跑标准功能已经足够。我的建议是:先容器化跑通业务,再根据实际需要决定是否深入定制。
2. 从零部署:三条命令跑通SRS
2.1 第一条命令直接验证服务可用
SRS官方维护了Docker镜像,最简单的方式是直接运行:
docker run --rm -it -p 1935:1935 -p 1985:1985 -p 8080:8080 -p 8000:8000/udp \
-v $PWD/srs.conf:/usr/local/srs/conf/srs.conf \
ossrs/srs:5
如果只是验证服务能不能起来,不需要挂载配置,直接跑:
docker run --rm -it -p 1935:1935 -p 1985:1985 -p 8080:8080 -p 8000:8000/udp ossrs/srs:5
几个端口别搞混:1935是RTMP推拉流端口,1985是HTTP API端口,8080是HTTP服务端口,用来访问SRS自带的网页和拉取HTTP-FLV/HLS流,8000是WebRTC需要的UDP端口。启动成功后,打开
http://localhost:8080/players/
,就能看到SRS自带的播放器演示页。
默认配置下可以直接测试RTMP和HTTP-FLV,但HLS在默认配置里是关闭的,想开得用自定义配置。这也是我建议你第一时间把配置文件变成自己可控文件的原因,默认配置只是让你确认服务能用,真正干活还得按业务来调。
2.2 把配置从容器里拿出来的正确姿势
刚开始用SRS容器时,我习惯直接进容器改配置,后来发现这是给自己挖坑。容器一删,改的东西全没了,而且每次都要重新进容器操作,效率很低。正确做法是把宿主机上的配置文件挂载进容器,这样改配置、重启容器就能生效,还能做版本管理。
先看一下容器里的默认配置长什么样:
docker run --rm --entrypoint cat ossrs/srs:5 /usr/local/srs/conf/srs.conf
拿到默认内容后,在宿主机上创建自己的
srs.conf
,然后用
-v $PWD/srs.conf:/usr/local/srs/conf/srs.conf
挂载进去。这样容器里用的是你宿主机上的配置,改完配置只需要:
docker restart srs
挂载时注意路径要写绝对路径,相对路径在某些环境下会产生诡异问题。如果你还想把录制的文件也保留在宿主机上,可以再加一个目录挂载:
-v $PWD/srs-data:/usr/local/srs/objs/nginx/html
这样HLS切片和DVR录制文件都会落在宿主机当前目录的
srs-data
下,方便直接查看和备份。
2.3 版本选型:SRS 5还是4
SRS 5是当前的主线版本,也是官方文档主推的版本。相比4.x,它在WebRTC、GB28181、SRT等协议支持上更完整,配置语法也更统一。如果项目是今年新起的,直接用5就好。
选版本时有一个经验:tag不要用
latest
,生产环境一定要锁版本。
ossrs/srs:5
虽然会跟随5.x的小版本更新,但至少比latest可控。我自己的习惯是进一步锁定到具体小版本,比如
ossrs/srs:5.0
,确保镜像内容可预测,避免某天重拉镜像后行为变化导致线上问题。
还有一点,SRS 4不止有代码,官方也维护了一个比较稳定的镜像tag
ossrs/srs:4
。如果你的项目里有老模块、老回调逻辑是照着4.x写的,先用4也可以,但新项目建议直接5,少走迁移的弯路。
3. 推流和拉流都跑通了,平台才算真的能用
3.1 FFmpeg推流:先确认你的媒体流格式
容器起来、端口开放,接下来就是推流。FFmpeg是测试阶段最顺手的推流工具,命令结构很简单:
ffmpeg -re -i test.mp4 -c copy -f flv rtmp://127.0.0.1:1935/live/livestream
这里有一个坑要提前讲:
-c copy
的含义是流复制,不重新编码,速度很快,但要求源视频的编码格式是SRS能直接分发的H.264+AAC。如果源文件是MPEG-4编码或者音频是MP3,
-c copy
出来的流在大部分播放器里会黑屏或没声音,这时候需要转码:
ffmpeg -re -i test.mp4 -c:v libx264 -preset veryfast -tune zerolatency \
-c:a aac -f flv rtmp://127.0.0.1:1935/live/livestream
-re
参数的意思是按原始帧率读取文件,模拟实时推流,不加它FFmpeg会以最快速度推完整个文件,服务端收到的就是"秒送全片",根本达不到直播效果。
推流时URL里的
/live/livestream
要拆开理解:
live
是SRS里的应用名(默认vhost下叫live),
livestream
是流的标识,可以随便取,但拉流时要用同一个名字。我习惯用日期加场景命名,比如
live/activity_20250110
,方便后期排查。
3.2 拉流:VLC、ffplay和浏览器三种方式选哪个
推流推上去了,拉流的姿势主要有三种,我分别说下实际体验。
VLC是最省事的播放器,打开网络串流输入
rtmp://127.0.0.1:1935/live/livestream
,如果路径没错基本秒开。VLC也能播
http://127.0.0.1:8080/live/livestream.flv
和
http://127.0.0.1:8080/live/livestream.m3u8
,适合拿来快速验证协议是否可用。
ffplay适合在命令行环境快速测流,命令:
ffplay -fflags nobuffer -i rtmp://127.0.0.1:1935/live/livestream
-fflags nobuffer
可以降低播放缓冲,延迟更小,但画面可能会更卡。这个就是测试用,不用纠结参数,能看到画面就行。
浏览器场景则要看协议选播放插件:HTTP-FLV用flv.js封装,HLS用hls.js,WebRTC直接原生播放。SRS自带的播放页
http://localhost:8080/players/
里已经把几种播放器都集成好了,填上流地址就能测,省去了自己写页面的麻烦。我实际做项目时,会把SRS播放页当第一排查工具:流推不上去或者拉不下来,先在这个页面里挨个协议试一遍,哪个能出画面就说明对应协议链路是通的。
三种方式选哪个取决于用户端环境,我自己的选择原则是:内部工具类页面优先HTTP-FLV,延迟低且浏览器兼容性好;需要原生iOS Safari播放就上HLS;互动场景必须WebRTC。
3.3 把HLS和DVR录制一起开了
HLS在SRS里配置很简单,在vhost里加一段:
vhost __defaultVhost__ {
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
}
hls_fragment
是切片时长,我习惯设5秒,兼顾延迟和切片数量;
hls_window
是保留窗口,30秒意味着播放器最多往回看30秒的直播内容。开完之后,拉流地址变成
http://127.0.0.1:8080/live/livestream.m3u8
,打开后会发现目录下自动生成了多个
.ts
切片文件。
DVR录制用于把推流内容直接落盘为FLV文件,配置方式:
dvr {
enabled on;
dvr_path ./objs/nginx/html/[app]/[stream].[timestamp].flv;
plan session;
}
plan session
表示每次推流会生成一个独立文件,文件以推流开始时间为命名,这样物料的归档非常清晰。录制文件直接在
srs-data
目录下,配合上一步的目录挂载,宿主机上随时能拿到成片,不需要进容器翻文件。
开过录制之后要注意磁盘占用,一场两小时的720p直播,FLV文件大概在1.5GB到2GB,如果活动密度高,建议给录制目录单独做磁盘配额或定时清理策略。
4. WebRTC低延迟场景:默认能出流,但正式用必须调
4.1 SRS的WebRTC链路到底是怎样转的
WebRTC和RTMP/HLS走的是完全不同的传输链路。前者基于UDP,使用SRTP加密媒体流,由ICE协议负责打通两端网络,而SRS在这条链路上的角色不是简单的媒体转发,它要同时处理信令层面的SDP协商、ICE candidate的交换,以及媒体层面的SRTP包转发。
SRS内置了WebRTC网关,默认监听
8000/udp
。浏览器推流时,会先通过HTTP接口做SDP Offer/Answer交换,协商完成后媒体包直接走8000端口这个UDP通道。所以你在Docker部署时如果漏了
-p 8000:8000/udp
这个映射,WebRTC推拉流大概率失败,但RTMP和HTTP-FLV却一切正常,很有迷惑性。
4.2 局域网部署先改candidate
SRS默认配置里
rtc_server
的candidate是127.0.0.1,也就是说它告诉浏览器"你往这个地址发媒体包"。如果你在容器所在的宿主机上测试,这个默认值没问题;但局域网内其他机器要播放时,浏览器拿到的candidate指向回环地址,根本路由不到你的SRS服务器,表现就是WebRTC一直连接失败。
解决办法是把candidate改成SRS服务器的实际IP:
rtc_server {
enabled on;
listen 8000;
candidate 192.168.1.100;
}
这里的IP要填客户端能够访问到的地址。如果SRS部署在内网服务器,就填服务器的内网IP;如果部署在云主机且客户端从公网访问,就要填公网IP或绑定到弹性IP上。改完重启容器,再打开播放页测试,能出画面就说明candidate对了。
我还遇到过一种情况:服务器有多网卡,candidate填了一个内网IP,但客户端和服务器在另一个网段,导致媒体包发不回来。排查时可以在客户端那边抓包看有没有收到来自SRS的UDP包,没收到基本就是candidate指向的地址不可达。
4.3 外层加HTTPS和端口放行
WebRTC用于浏览器推流时,尤其涉及摄像头和麦克风,浏览器要求页面必须处于安全上下文,也就是HTTPS或localhost。内网测试环境用http还能将就,一旦部署到公网或者跨部门访问,就必须给SRS的播放页面套上HTTPS。
实操中有两种做法:一种是在SRS前面加Nginx反向代理,由Nginx终结SSL证书,再把对应请求转发给SRS的8080端口;另一种是直接在SRS的http_server层做SSL配置,但证书管理不如反代灵活。我推荐用反代,证书续期、域名变更、限流配置都集中在Nginx,SRS容器保持干净。
还有安全组和防火墙这一层,不只是TCP的443、1935、8080要放行,UDP的8000必须单独加规则。很多云安全组默认只放行TCP,UDP是被忽略的,结果WebRTC推流一直卡在连接阶段,折腾半天发现是安全组规则漏了。
5. 部署中曝光率最高的坑,附排查链路
5.1 镜像拉不动?先配加速器再谈别的
Docker官方镜像仓库在国内的访问速度一直不稳定,刚接触Docker的人很容易卡在拉镜像这一步,甚至误以为是Docker安装有问题。排查顺序其实是先配置镜像加速器,再重试拉取。
以Linux环境为例,编辑
/etc/docker/daemon.json
:
{
"registry-mirrors": [
"https://xxx.mirror.aliyuncs.com"
]
}
xxx
那一段需要登录阿里云容器镜像服务控制台,在"镜像加速器"页面获取你账号专属的地址。配置完执行
sudo systemctl restart docker
,再拉一次镜像速度就正常了。Windows上的Docker Desktop在Settings的Docker Engine选项里改同样的JSON配置即可。
有一点要说明,镜像加速器只管Docker Hub镜像的拉取加速,和网络代理是两码事,不要混为一谈。如果配置了加速器后拉镜像仍然超时,检查一下
daemon.json
的JSON格式是否合法,以及Docker服务是否真的重启成功了。
5.2 推流失败?按这个顺序排
推流失败是流媒体部署里最常见的故障,我总结了一套固定排查顺序,遇到问题就按这个走。
第一,确认容器状态和端口映射。执行
docker ps
看SRS容器是否在运行,
docker port srs
看端口映射是否符合预期。经常有朋友反映"我容器起来了但推流失败",结果一看8000端口没映射,WebRTC当然不工作。
第二,看SRS自身日志。容器里SRS默认会把日志打到控制台,执行
docker logs -f srs
就能看到实时输出。推流失败时,日志里一般会有
parse uri failed
、
no vhost
、
auth failed
这些关键词,根据报错去查配置,比瞎猜快得多。
第三,验证端口连通性。在另一台机器上执行
telnet 127.0.0.1 1935
,确认RTMP端口是否真的能从外部访问。如果telnet不通,优先检查宿主机防火墙和安全组,而不是怀疑SRS本身。
第四,核对推流地址。SRS默认vhost是
__defaultVhost__
,应用层路径用
live
,如果你推流时写的是
rtmp://127.0.0.1:1935/myapp/mystream
,而SRS里没有名为
myapp
的vhost或应用配置,服务端会直接拒绝。排查时可以把推流地址改回
rtmp://127.0.0.1:1935/live/test
试试,如果通了,就是路径或配置的问题。
5.3 Windows的Docker Desktop虚拟化报错
Windows上跑Docker Desktop,最常见的启动报错是这个:
Docker Desktop failed to start because virtualisation support wasn't detected
。这句话翻译过来是"没检测到虚拟化支持",实际原因通常是BIOS里虚拟化没开,或者Windows功能组件不完整。
排查步骤按顺序来:先打开任务管理器,在"性能"标签里看CPU的虚拟化状态是否是"已启用"。如果显示未启用,需要重启进BIOS,找到Intel VT-x或AMD-V选项打开。
如果虚拟化已启用,那就是Windows功能层的问题。在"启用或关闭Windows功能"里勾选"适用于Linux的Windows子系统"和"虚拟机平台",然后重启系统。重启后再启动Docker Desktop,基本就能正常起来。
还有一种情况是旧电脑CPU不支持所需的虚拟化指令,或者系统版本过旧无法装WSL2,这时无论怎么设置都很难让新版Docker Desktop工作。我的建议是别花太多时间纠结,直接换一台新机器,或者改用纯Linux服务器部署,这条路更稳。
5.4 端口冲突和UDP端口丢失
端口冲突在部署时很常见,8080端口经常被其他Web服务占用。SRS的HTTP服务端口其实是可以改的,在配置里调整
http_server
的
listen
字段就行,同时记得改Docker的端口映射关系。比如改成9080:
http_server {
enabled on;
listen 9080;
dir ./objs/nginx/html;
}
Docker映射改成
-p 9080:9080
,访问地址就变成
http://127.0.0.1:9080/players/
。
UDP端口丢失这个问题更容易被忽略。部分云平台的控制台默认只让配置TCP端口转发,UDP端口需要单独添加。你可以在容器内用一个简单方式验证UDP端口是否真的可用:执行
docker exec srs netstat -ulnp | grep 8000
,看看SRS进程有没有监听8000。如果监听正常,但外部访问不通,那基本可以断定是安全组或防火墙没放行UDP。
6. 可以直接抄走的生产化配置
6.1 一份能落地的srs.conf
把前面讲到的功能汇总,我目前项目里在用的就是这样一份配置:
listen 1935;
max_connections 1000;
daemon off;
srs_log_tank console;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
rtc_server {
enabled on;
listen 8000;
candidate 192.168.1.100;
}
vhost __defaultVhost__ {
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
dvr {
enabled on;
dvr_path ./objs/nginx/html/[app]/[stream].[timestamp].flv;
plan session;
}
}
这份配置做了几件事:打开HTTP API方便做状态监控;打开HLS实现浏览器兼容播放;打开DVR把每次推流自动录制为FLV文件;WebRTC的candidate设置为实际服务器IP。字段含义基本都能见名知意,改IP和端口后就可以直接用。
6.2 用docker-compose固化部署
单机用
docker run
够用,但如果你的服务器需要经常重建、或者想统一管理多个容器,用docker-compose更合适。我一般放一个
docker-compose.yml
:
services:
srs:
image: ossrs/srs:5
container_name: srs-live
restart: always
ports:
- "1935:1935"
- "1985:1985"
- "8080:8080"
- "8000:8000/udp"
volumes:
- ./srs.conf:/usr/local/srs/conf/srs.conf
- ./srs-data:/usr/local/srs/objs/nginx/html
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
restart: always
保证了机器重启后服务自动拉起来,
logging
配置避免容器日志无限增长撑爆磁盘。到这一步,整个部署就变成了两条命令的事:
docker compose up -d
docker compose logs -f
6.3 后续进阶:鉴权回调与状态监控
如果你不满足于"能推能拉",想加一层鉴权,SRS的HTTP回调是标准做法。在vhost里配置
http_hooks
,推流开始和结束时SRS会向你的业务服务器发一个HTTP请求,你可以在回调里校验推流密钥、记录推流时长:
http_hooks {
enabled on;
on_publish http://127.0.0.1:8080/api/auth/publish;
on_unpublish http://127.0.0.1:8080/api/auth/unpublish;
}
业务服务器收到请求后,返回HTTP 200就放行,返回非200就拒绝推流。这个方案实现简单,不需要改SRS源码,也能覆盖大多数内部的权限需求。
状态监控则可以定期调SRS的HTTP API,比如
curl http://127.0.0.1:1985/api/v1/summaries
能拿到当前在线流数、带宽等信息,配合Prometheus等监控工具定期采集,就能在推流异常时第一时间告警,而不是等用户反馈才发现服务器挂了。
在我实际运行这套服务的几个月里,最深的体会是:SRS的默认配置只是一个起点,真正让它适合业务的是配置文件里那些按需打开的功能模块。Docker把部署门槛降到了很低,但流媒体链路的调试能力还是要靠日志、端口排查和协议理解一点点积累。希望这份记录能帮你少走一些我走过的弯路。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)