很多人第一次接触"直播服务器"这个词,第一反应是那些商业直播平台的推流地址、控制台、付费带宽。但当你只是想在公司内网做一个活动直播、给远程课程搭一个在线教室、或者把某个监控画面送给团队看时,真正的需求其实很朴素——我要一个能接收推流、转发给多个观看者的程序。自己搭直播服务器,本质上就是把这个程序跑起来,然后填对几个地址。这篇文章我打算从直播链路的基本逻辑讲起,再到选型对比、实际部署、推流拉流全流程,最后把我在真实使用中踩过的坑一并说出来。适合想自建直播服务、又不想一上来就被复杂概念劝退的朋友。
1. 直播链路的核心:推流、分发、播放到底谁在干活
1.1 一条直播流的完整旅程
很多人以为直播就是"编码器把画面发出去,播放器能收到",中间那块其实是个容易被忽略的关键环节。一条典型的直播流要经过三个角色:推流端(OBS、手机、摄像头)、流媒体服务器(我们这篇文章的主角)、播放端(VLC、浏览器、小程序)。
推流端把画面和声音压缩成流,通过RTMP协议推到服务器;服务器不是简单地把字节转手,它会做接收、校验、转封装、分发这一系列动作;播放端再从服务器按需拉取。简单理解,直播服务器像一个快递中转站:各家推流端是把包裹送进站的货车,服务器是中转场的分拣线,播放端是来取件的人。货车只有一辆没关系,分拣线可以同时供很多人取件。
这个模型能解释很多实际问题:为什么推流端只有一个,观看端却可以几百人?因为服务器在中间做了分发。为什么换了播放协议,延迟差别很大?因为中转站对包裹的处理方式不同,有的直接放行(低延迟),有的重新打包(高延迟)。理解了这个角色划分,后面所有配置都不会再觉得混乱。
1.2 协议怎么选:RTMP、HTTP-FLV、HLS、WebRTC
直播服务器就像中转站,但它和不同运输车队(协议)打交道的成本不一样。我直接用四个最常见的协议来说明差异。
- RTMP:Adobe当年为Flash直播设计的协议,至今仍是推流端的事实标准。TCP长连接,延迟可以做到1-3秒。OBS、FFmpeg推流几乎都走它。播放端也能拉RTMP,但不是所有浏览器都支持,所以现在更常用于推流。
- HTTP-FLV:把RTMP里的视频数据用HTTP方式传输,浏览器里通过flv.js这类播放器就能播放。延迟通常在2-5秒,比RTMP稍高一点,但在浏览器场景里非常实用。
- HLS:苹果提出的协议,服务器把流切成一小段一小段的ts文件,播放器按顺序拉取。兼容性最强(iOS、Android、PC浏览器都行),但延迟通常有10-30秒,因为切片本身就有时间损耗。
- WebRTC:浏览器之间点对点音视频通信的标准,用在直播分发上延迟可以低到几百毫秒。缺点是部署相对复杂,需要HTTPS环境。
我打个比方:RTMP是直达班车,适合从出发地运到中转站;HTTP-FLV是普通快递,不快但很好使;HLS是挂号信——安全稳妥,就是要多等几天;WebRTC是同城闪送,快得能感觉到实时,但价格和门槛也高。
日常使用中,推流几乎固定用RTMP,播放端则按场景选。要兼容性就HLS,要低延迟就用HTTP-FLV,追求极致低延迟且能接受配置成本就上WebRTC。大部分内网直播场景,我在播放端用HLS或HTTP-FLV就够了。
2. 自建方案选型:为什么我最终常用SRS
2.1 三条常见的路:Nginx-RTMP、MediaMTX、SRS
说到自建直播服务器,很多人查到过的方案无非三类:用Nginx加RTMP模块、用MediaMTX、用SRS。这三条路我都实际跑过,分别说一下我的感受。
- Nginx + nginx-rtmp-module:这是老牌经典方案。Nginx本身是HTTP服务器,加个RTMP模块就能转直播。问题是官方主仓库已经把这个模块移出了维护列表,现在要用基本都得自己编译。我试过编译,依赖一堆,升级麻烦,而且它默认只处理RTMP,HLS功能要自己配,HTTP-FLV支持也比较弱。适合本来就深度使用Nginx且不介意折腾的人。
- MediaMTX:它更偏向"接入各类摄像头RTSP流"和"协议转换"。一个二进制文件就能跑,配置文件是YAML格式,非常简洁。但它的定位是轻量网关,不是完整的直播分发系统:没有内置的Web管理面,鉴权、录制、集群等能力都不如专业直播服务器。如果只是把几路RTSP摄像头转成HLS给网页看,它很舒服;要做一场有观众、有录制、有权限控制的直播,会感觉缺了很多东西。
- SRS(Simple Realtime Server):这是国产开源项目,从最早的Flash直播一路做到现在的WebRTC直播,专门解决"直播服务器"这一件事。它用C++写成,单进程就能完成RTMP接入、HLS切片、HTTP-FLV分发、WebRTC进出,默认配置几乎开箱即用。内置HTTP API和Web控制台,推流鉴权、录像、转码、集群这些都有,文档和社区在直播服务器里算非常活跃的。我第一次用SRS时,下载压缩包、解压、启动,三分钟就把流推起来了,这是其他方案没有给过我的体验。
方案对比如下:
| 方案 | 上手难度 | 协议支持 | 扩展能力 | 维护活跃度 | 适合场景 |
|---|---|---|---|---|---|
| Nginx-RTMP | 中高(需编译) | RTMP、HLS基础 | 较弱 | 低 | 已有Nginx体系 |
| MediaMTX | 低 | RTSP、RTMP、HLS、WebRTC | 中等 | 高 | 摄像头接入、协议转换 |
| SRS | 低 | RTMP、HLS、HTTP-FLV、WebRTC | 强(鉴权/录制/集群) | 高 | 正式直播分发 |
2.2 不同场景怎么选
如果你要做的是一件"正式能对外用"的直播服务,SRS基本是首选。它把"接收推流→切片→分发到播放器"这条链路的每一环都做了,而且每一步都有日志和API可以排查。哪怕是新手,跟着默认配置用起来也不会有太多认知负担。
反过来,如果你已经有Nginx在负责网站,只是偶尔转一下流,那Nginx-RTMP也还凑合。如果你只是想把设备上的RTSP流转给网页看,MediaMTX更轻。但在这篇文章的语境下,我默认你想要的是一台能长时间运行、有人推流有人观看、出了事还能查日志的直播服务器——这个定位SRS最合适。
顺便说一句,很多人会纠结"要不要先把FFmpeg学了再搭服务器"。我的建议是没必要。FFmpeg在直播里的作用是转码和推流,但服务器本身不依赖它;你可以先用OBS推流,FFmpeg等有需求再学。
3. 三分钟跑起来:SRS的Docker与二进制部署
3.1 最省事的启动方式:Docker
如果你的机器上有Docker,SRS启动真的就是一条命令的事。我用Docker跑SRS的基本命令如下:
# 创建配置目录并持久化(可选,但建议) mkdir -p /data/srs docker run -d --name srs \ -p 1935:1935 \ -p 1985:1985 \ -p 8080:8080 \ -v /data/srs:/usr/local/srs/objs/nginx/html \ ossrs/srs:6这里有三个端口要记住:
- 1935:RTMP协议入口,推流和RTMP拉流都用它。
- 1985:SRS的HTTP API端口,用来查状态、拉流信息。
- 8080:HTTP服务端口,HLS切片、HTTP-FLV播放地址都由它对外提供。
-v参数把容器里的HTML目录(也就是HLS切片的输出目录)映射到宿主机,这样即使容器删除,录像和切片数据也还在。我习惯先建好宿主机目录再挂载,因为容器会在目录不存在时自动建目录,权限又不一定是你想要的。
如果ossrs/srs:6镜像拉取较慢,可以用Docker配置镜像加速器,或者直接用后面说的二进制方式。
3.2 不依赖Docker的二进制启动
有些服务器上没有Docker,或者你对容器转来转去的方式不放心,那就用二进制包。去SRS的GitHub Releases页面下载Linux版压缩包,然后解压、启动:
wget https://github.com/ossrs/srs/releases/download/v6.0.186/srs-6.0.186.tar.gz tar xf srs-6.0.186.tar.gz cd srs-6.0.186 ./objs/srs -c conf/srs.conf看到日志里出现start server successfully之类的字样就说明起来了。二进制的目录结构很清晰:conf放配置、objs放可执行文件和日志。我个人的习惯是把它放到/usr/local/srs下,方便统一管理。
二进制方式的好处是依赖少、性能好、排障直观,日志就在objs/srs.log里,问题定位非常直接。
3.3 srs.conf里那几个必须看懂的关键项
SRS默认配置可以直接跑,但既然要长期使用,我还是建议把关键项过一遍。简化后的核心配置大概是这样的:
listen 1935; # RTMP入口 max_connections 1000; # 最大连接数 daemon on; # 后台运行 srs_log_tank console; # 日志输出到控制台 http_api { enabled on; listen 1985; # HTTP API端口 } http_server { enabled on; listen 8080; # HTTP服务端口(HLS、FLV、播放页) dir ./objs/nginx/html; # 静态文件与切片目录 } vhost __defaultVhost__ { hls { enabled on; # 开启HLS切片 hls_path ./objs/nginx/html; hls_fragment 10; # 每个切片时长(秒) hls_window 60; # 保留窗口时长(秒) } }vhost的概念可以理解为一个虚拟的直播房间组。默认的__defaultVhost__会接收所有到达这个IP的RTMP流,对单机场景完全够用。hls_fragment控制切片时长,直接影响HLS延迟——后面讲延迟坑时会再提到。
对于docker映射出来的端口,因为容器内部配置用的还是这几个默认端口,所以你不需要改配置,知道它们干什么是为了排查问题。
3.4 启动后先确认三件事
服务器启动后,我建议立刻做三件事确认它真的活着。
- 查API:访问
http://你的服务器IP:1985/api/v1/summaries,如果返回一段JSON且里面有"status": "running"或类似的状态字段,说明API正常。这一步用浏览器或curl都行。 - 看默认页:访问
http://你的服务器IP:8080,能出现SRS的提示页或目录列表,说明HTTP服务正常。 - 本地连RTMP端口:在同一台机器执行
telnet 127.0.0.1 1935,能连通就说明RTMP入口在监听。
这三件事全部通过之后,再去做推流配置,否则后面排查起来会把网络问题和服务问题混在一起。我发现很多人第一次搭完去问"为什么推流失败",结果连服务都没起来,白白浪费半小时。
4. 推流和播放实战:从OBS到多终端同时观看
4.1 OBS推流:复制粘贴三个字段
服务端就绪后,推流端用OBS(Open Broadcaster Software)来做是最顺手的。打开OBS的设置,找到"直播"(或"推流"),选择自定义服务,然后填写三个信息:
- 服务器:
rtmp://你的服务器IP/live - 串流密钥:
myroom(这个名字可以随便取,相当于房间名,播放地址里会用到) - 没有单独的"应用名"字段,
live已经含在服务器URL里了
填好后点击开始推流。此时OBS的推流状态会从"正在连接"变成"直播中",同时日志里会出现"发送时间"和"丢帧"的数据。如果丢帧持续增加,优先检查网络带宽和服务器CPU,而不是急着重推。
值得说明的是,为什么服务器地址是rtmp://IP/live而不是rtmp://IP?因为SRS默认把VT应用配置在live这个名字下。流密钥myroom就是播放地址里的房间标识,它决定了播放端用什么URL来找这条流。同一个密钥下可以同时有多个播放器观看,服务器会自动转发。
4.2 播放端:三种方式对比实测
推流成功后,我在内网分别用三种方式实测了播放,效果如下:
- FFplay拉RTMP(最直接):
这是延迟最低的播放方式之一,画面基本和推流端同步,适合快速验证流是否正常。ffplay rtmp://你的服务器IP/live/myroom - VLC播放:打开VLC,选择"媒体-打开网络串流",输入
rtmp://你的服务器IP/live/myroom。移动端VLC也可以,但要在网络串流里手动输地址。 - 浏览器HLS播放:在Chrome/Safari/Edge任一浏览器地址栏输入:
HLS切片是SRS默认开启的,所以这个URL能直接在浏览器里以http://你的服务器IP:8080/live/myroom.m3u8video标签播放(Safari原生支持,Chrome也需要用hls.js或者直接访问页面的话SRS有对应的示例播放器页面)。
如果SRS自带的示例播放页可用,也可以直接访问http://你的服务器IP:8080/players/srs_player.html,在里面选择协议、填写流地址,点播放就行。这在排查"到底是我的播放器问题还是流问题"时非常省事。
4.3 同一个房间,多人观看的资源账
很多人关心"一台服务器能支持多少人同时看"。这个问题其实取决于你的码率和带宽,而不是服务器软件本身。直播服务器不转码时,它做的只是把一份进来的流复制成多份出去,CPU开销很小,真正的瓶颈在网络带宽。
举个实际例子:推流端设了4Mbps码率(1080p),有20个人同时观看,那么服务器至少需要4Mbps × 20 = 80Mbps的出站带宽。如果只有100Mbps的带宽,强行支撑30人就会出现播放卡顿。
所以做直播活动前,先算清楚这笔账:
| 观看人数 | 1080p(4Mbps) | 720p(2Mbps) | 480p(1Mbps) |
|---|---|---|---|
| 10人 | 40Mbps | 20Mbps | 10Mbps |
| 50人 | 200Mbps | 100Mbps | 50Mbps |
| 100人 | 400Mbps | 200Mbps | 100Mbps |
如果带宽有限,可以在服务器侧用FFmpeg转出多路不同清晰度的流,或者提前让推流端压到合适的分辨率。这个账没算清,活动当天网络崩了再想补救就来不及了。
5. 真实环境里最容易踩的五个坑
5.1 坑一:端口和服务看着都正常,但别人连不上
我第一次部署完,服务端日志一切正常,本机也能播放,但局域网里的另一台电脑就是连不上。一查,问题出在防火墙。
SRS需要对外开放1935(RTMP)、1985(API)、8080(HTTP),无论你用的是云服务器还是内网机器,都要先把这三个端口放行。云服务器要去安全组里加规则;Linux本机可以用firewall-cmd或ufw放行。排查的时候,先在播放端机器上执行telnet 服务器IP 1935:
telnet 192.168.1.10 1935如果卡住不动或者报连接失败,绝大概率是端口没放行。我建议先把三个端口一次性放行再测试:
# 以firewalld为例 firewall-cmd --permanent --add-port=1935/tcp firewall-cmd --permanent --add-port=1985/tcp firewall-cmd --permanent --add-port=8080/tcp firewall-cmd --reload这个坑太基础了,但栽的人特别多。因为服务端日志和本机播放成功会给你一种"服务正常"的错觉,而问题根本不在服务,在网络入口。
5.2 坑二:流地址能出,播放端却黑屏转圈
有一次我在浏览器里访问HLS地址,能看到返回的.m3u8文件,但播放器一直转圈。检查了半天,发现HLS切片目录根本没有生成最新的.ts文件。
这类问题的排查路径是固定的。先用curl直接访问一下切片地址:
curl -v http://你的服务器IP:8080/live/myroom.m3u8如果返回的是空文件或者404,那问题在服务器侧;如果切片能正常列出,再去检查播放器。
我碰到过的实际情况有两种:一种是因为服务器时间和服务启动顺序的问题导致切片没有落盘,重启SRS后恢复正常;另一种是推流端用的是可变码率(VBR),画面静止时码率过低,HLS切片迟迟不产生新的分片。把OBS里码率设定为固定码率(CBR)后问题就消失了。所以先看切片有没有产生,再谈播放器配置,别一上来就怀疑播放器。
5.3 坑三:画面模糊,我却先怀疑服务器
直播画面模糊,大多数人第一反应是"服务器是不是压缩太狠了"。这个思路完全错了。SRS默认是不转码的,它不改变画质,你的画面模糊大概率是推流端参数没设好。
OBS里影响画质的两个核心参数是分辨率和码率。同样分辨率下,码率太低就会出现马赛克、边缘发虚。我常用的参数如下:
| 分辨率 | 推荐码率 | 适用场景 |
|---|---|---|
| 1080p | 5000-8000 Kbps | 游戏直播、画面运动多 |
| 720p | 2500-4500 Kbps | 课堂、会议画面相对静止 |
| 480p | 1000-2000 Kbps | 带宽受限或观看端以手机为主 |
如果你是用FFmpeg推流,命令里-b:v就是码率,-preset选medium或slow能让同样码率下画质更好一点点。检查顺序应当是:先看推流端的码率是不是跑得起来,再看服务器CPU和带宽是否够用,最后才轮到服务器配置。
5.4 坑四:延迟大得没法用,到底怎么调
第一次用HLS播放时,我对着画面愣了几秒才反应过来流是延迟的,而且比我想象中严重得多。这是因为HLS切片的机制:服务器把流切成一段段几秒长的文件,播放器至少要等下完一个切片才能开始播。默认切片10秒,再加上缓冲,延迟十几秒到半分钟都是正常的。
如果你对延迟有要求,办法有两条:
- 用HTTP-FLV代替HLS:在浏览器里用flv.js配合HTTP-FLV地址(
http://IP:8080/live/myroom.flv),延迟可以压到2-5秒。这是当前网页端低延迟最实用的方案。 - 调低HLS切片时长:把
srs.conf里的hls_fragment从10改成2-3,延迟会下降,但切片数量变多、播放时对服务器和客户端的压力也变大,还可能因为切片太碎导致首屏加载更卡。
RTMP播放的延迟本来就很低,如果你想做互动直播,优先考虑HTTP-FLV而不是继续折腾HLS。每次调整完延迟,记得重新推流测试,因为切片参数对已存在的流不一定立即生效。
5.5 坑五:HTTPS页面里FLV播放器一片空白
这个坑特别隐蔽。我后来做了个带HTTPS的页面,想在上面内嵌flv.js播放器,结果播放器区域一直是黑的,控制台报错信息提示"Mixed Content"。
浏览器安全策略规定:HTTPS页面里不能加载HTTP资源。如果你的播放页面是https://,而HTTP-FLV流地址是http://IP:8080/live/myroom.flv,浏览器会直接拦截。解决办法要么是把流地址也改到HTTPS(给SRS配置证书并开启HTTPS监听),要么放弃在HTTPS页面上直接嵌HTTP流,改走HLS(某些浏览器的策略对HLS相对宽容但不绝对)。
我在实际项目中最终为SRS配了HTTPS来同时支持WebRTC和HTTP-FLV播放,如果只是内网临时用,直接用HTTP页面也能绕开这个限制。这个坑的价值在于提醒你:直播服务器不只是后端程序,它和浏览器的安全策略是强耦合的,选播放方式前先确认页面协议和流地址协议一致。
6. 还没完:鉴权、录制、低延迟扩展
6.1 给直播服务器加一把锁
默认配置下,任何人只要知道你的服务器IP和端口,都可以往live应用里推流或拉流。这在公网环境下等于门户大开。最实用的做法是用SRS的回调机制:配置一个HTTP地址,SRS在收到推流请求时先回调它,返回“允许”才继续,否则拒推。
我用Node.js写过一个极简的验证服务,代码大概是:
// 伪代码示例:on_publish回调验证token const http = require('http'); http.createServer((req, res) => { const query = new URL(req.url, 'http://localhost').searchParams; const token = query.get('token'); if (token === 'myroom_token') { res.writeHead(200); res.end('0'); // 0表示允许推流 } else { res.writeHead(200); res.end('1'); // 非0表示拒绝 } }).listen(8081);SRS的配置里加上:
vhost __defaultVhost__ { hooks { enabled on; on_publish http://127.0.0.1:8081/api/v1/hooks; } }然后推流地址就可以是rtmp://IP/live/myroom?token=myroom_token。OBS的"串流密钥"里填上myroom?token=myroom_token即可。不光是推流,看流也可以加类似限制,SRS的鉴权体系可以做到很细。至少把on_publish加上,能挡住一大半误入的推流。
6.2 自动录制回放
直播结束想留档,SRS可以直接在服务端录制。配置起来很轻:
vhost __defaultVhost__ { dvr { enabled on; dvr_path ./objs/nginx/html/[app]/[stream].[timestamp].flv; } }这样每一条推流都会在服务端录制成FLV文件,路径里的[timestamp]让文件名不重复。录制产生的文件可以直接放到OBS里播放,也可以让FFmpeg转成MP4。我办过几次活动直播,录完直接用这个FLV文件留档,省去了单独开录屏软件的麻烦。
注意录制文件是按流生成的,如果推流中途断过线,会生成多个文件,不要问我是怎么知道的。
6.3 对延迟敏感的WebRTC玩法
如果你要做的是远程操作画面、线上问答这类对实时性要求极高的场景,SRS也能支持WebRTC播放。原理是让SRS开启WebRTC网关,播放端通过浏览器直接建立近实时的音视频通道,延迟能压到几百毫秒。
它需要你额外配置HTTPS证书和WebRTC相关端口。流程大概是:给SRS配置证书和rtc_server端口,然后在播放端使用SRS的WebRTC播放器SDK,把流地址改为WebRTC格式。配置门槛比RTMP和HLS高不少,但带来的实时性提升也很明显。我个人的使用建议是:一般直播用HTTP-FLV足够,别一上来就上WebRTC,先满足需求再谈性能。
我实际跑过几次完整流程后最大的体会是:搭SRS本身真不是难点,真正的功夫都在那些“小事”上——端口通不通、码率配没配、浏览器安不安全策略、录制文件在哪。与其背各种参数,不如先跑通RTMP加HLS这条默认链路,再根据自己的场景慢慢加鉴权、录制和低延迟方案。最后分享一个小技巧:把服务器IP固定下来,或者在内网DNS里给它一个稳定名字,推流地址永远用名而不用IP,这样换设备、换网络时配置不用来回改。