自建流媒体服务器:nginx-rtmp 推流与回放配置实录

nginx-rtmp 流媒体服务器架构封面图

为什么很多直播方案最后都败在推流这一步

如果你尝试过把一场直播稳定推到观众面前,大概率遇到过:画面卡成幻灯片、延迟十秒起步、观众端间歇性黑屏。这些问题多数不是摄像头或播放器的问题,而是缺一台可控的流媒体服务器。教你用 nginx-rtmp 在一台 VPS(虚拟专用服务器)上搭建属于自己的流媒体服务器,是解决这类问题最直接的路径:把推流的接收、转封装和分发都握在自己手里,延迟、画质、并发上限都由你自己定义。

整套方案只有三个角色:推流端(OBS 或 FFmpeg)把流推到服务器的 1935 端口;nginx-rtmp 接收并转成 HLS 切片;观众通过浏览器拉取回放地址。下面按真实搭建顺序逐步展开,每步都给出可复制的配置。

环境准备与 nginx-rtmp 模块编译

nginx 主线版本默认不带 RTMP 模块,必须额外编译。建议选一台带宽充足的 VPS 主机(虚拟专用服务器),系统以 Ubuntu 22.04 或 Debian 12 为例,先装齐编译依赖:

sudo apt update
sudo apt install -y build-essential libpcre3-dev libssl-dev zlib1g-dev git
wget https://nginx.org/download/nginx-1.26.2.tar.gz
git clone https://github.com/arut/nginx-rtmp-module.git
tar -xzf nginx-1.26.2.tar.gz && cd nginx-1.26.2
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module
make && sudo make install

编译后 nginx 位于 /usr/local/nginx/sbin/nginx。两个易踩的坑:--add-module 必须指向 nginx-rtmp-module 源码目录,写错会在 configure 阶段报错;若服务器已跑着发行版自带 nginx,先 sudo systemctl stop nginx 停掉,避免抢 80 端口。编译大约需要 2-5 分钟,完成后执行 /usr/local/nginx/sbin/nginx -V,输出里能看到 --add-module=../nginx-rtmp-module 就说明模块已生效。

核心 rtmp 配置:直播应用与回放应用分开建

nginx-rtmp 的所有行为都由 rtmp {} 块驱动。下面这份配置把“直播转发”和“点播回放”拆成两个 application,是目前社区验证过最稳的结构:

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
            live on;
            record off;
            # 推流鉴权:只允许携带正确密钥的推流
            on_publish http://127.0.0.1:8080/auth;
        }

        application vod {
            play /var/mp4/vod;
        }
    }
}

配套还需要在 http {} 块里加一段 HLS 输出,观众端才能用普通浏览器观看:

location /hls {
    types {
        application/vnd.apple.mpegurl m3u8;
        video/mp2t ts;
    }
    root /var/www;
    add_header Cache-Control no-cache;
    add_header Access-Control-Allow-Origin *;
}

rtmp 侧的 live application 补上这几行,把切片写进 /var/www/hls:

hls on;
hls_path /var/www/hls;
hls_fragment 3;
hls_playlist_length 60;

参数直接影响体验:hls_fragment 3 即每片 3 秒,观众端延迟约 15-25 秒;降到 1 秒可压到 6-10 秒,但请求频率翻三倍,小带宽机器容易打满。hls_playlist_length 60 控制播放列表只保留 60 秒历史,避免磁盘被切片文件慢慢吃满。目录权限别遗漏:sudo mkdir -p /var/www/hls /var/mp4/vod && sudo chown -R nobody /var/www/hls,否则 nginx 写不进切片,表现为推流正常但 /hls 下没有 m3u8。

改完配置先 /usr/local/nginx/sbin/nginx -t 做语法检查,再启动。ss -tlnp | grep 1935 能看到监听,说明 RTMP 服务就位。

推流端配置:OBS 与 FFmpeg 两条路都通

推流地址格式为 rtmp://服务器IP:1935/live/流名称。OBS 中选“自定义”服务,服务器填 rtmp://203.0.113.10:1935/live,串流密钥填 test01 即可开播;命令行用 FFmpeg 一条命令搞定:

ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -maxrate 2500k \
  -bufsize 5000k -g 50 -c:a aac -b:a 128k \
  -f flv rtmp://203.0.113.10:1935/live/test01

-re 让 FFmpeg 按原始帧率读文件,去掉后文件几秒被灌完,观众端表现为飞速快放;-g 50 设关键帧间隔,25fps 下 2 秒一个,与 HLS 切片对齐后卡顿明显减少。推流成功的判断标准:nginx 日志出现 publish 字样,且 /var/www/hls/test01/ 开始生成 .ts 切片和 index.m3u8。

浏览器打开 http://203.0.113.10/hls/test01/index.m3u8,若下载到的文本含 #EXTINF 行,说明服务端输出正常,剩下交给播放器:VLC 输入该地址,或用 hls.js 内嵌网页。

录制回放:把直播流自动落盘成 MP4

直播结束后想把内容存成点播文件,record 指令一行就能做到。在 live application 里把 record off 换成:

record all;
record_path /var/recordings;
record_unique on;
record_suffix .flv;
exec_record_done ffmpeg -i $path -c copy /var/mp4/vod/$basename.mp4;

含义:每路推流完整录制到 /var/recordings,文件名自动加时间戳防覆盖;推流结束后触发 exec_record_done 钩子,用 FFmpeg 把 FLV 无损转封装成 MP4 放进点播目录。-c copy 是流复制不重新编码,2 核服务器转 2 小时录像只要十几秒;需要压缩体积或加水印时换成 -c:v libx264 -crf 28 -preset fast,但转码时长会变成视频长度的 0.5-1 倍,建议夜里用 cron 批量处理。

录好的 MP4 通过 vod application 播放:ffmpeg -re -i local.mp4 -c copy -f flv rtmp://203.0.113.10:1935/vod/demo.mp4 不适用于点播,正确姿势是直接访问 rtmp://203.0.113.10:1935/vod/demo.mp4,nginx 会按需把 MP4 以 RTMP 流形式吐给播放器。更常用的 HTTP 点播再加一个 location /vod/ 指向 MP4 目录,支持拖动进度条,体验接近专业点播站。

带宽与并发:一台服务器到底能扛多少观众

流媒体服务器最贵的资源不是 CPU 而是带宽(出口网络传输能力)。HLS 分发是一对多模型,消耗的带宽等于码率 × 观众数。以 2500kbps 的流为例:10 个观众约消耗 25Mbps 出口流量,100 个观众就是 250Mbps,已超过多数入门级 VPS 的端口能力。容量对照如下:

推流码率 观众 10 人 观众 50 人 观众 200 人
1500kbps 15Mbps 75Mbps 300Mbps
2500kbps 25Mbps 125Mbps 500Mbps
4000kbps 40Mbps 200Mbps 800Mbps

对照这张表不难得出选型结论:预期 50 人以内的小型内部直播,一台 100Mbps 端口的 VPS 足够;如果观众规模会破百,要么选 独立服务器 拿到 Gbit 级端口,要么在 nginx 前面再挂一层 CDN(内容分发网络)分流 HLS 切片。这种内容分发网络的成本通常只有自建带宽的三分之一,代价是切片缓存策略需要额外调试。CPU 反而不用担心:nginx-rtmp 默认不做转码,纯转发场景下单核就能扛数百路并发,吃 CPU 的只有 exec_record_done 转码那几分钟。

小规模直播与大规模直播的出口流量消耗对比示意

常见故障排查与上线前检查

把这套服务跑起来之后,日常遇到的问题基本集中在四类,按出现频率从高到低排:

  • 推流连不上:先用 telnet 服务器IP 1935 测端口连通,通不了九成是防火墙没放行(sudo ufw allow 1935/tcp),端口通但推流失败则查 on_publish 鉴权返回是否为 HTTP 200。
  • 有画面但持续转圈:典型原因是切片目录权限不对或 hls_path 与 location /hls 的 root 不一致,ls /var/www/hls/流名称/ 里能看到最新 .ts 文件即服务端正常,问题在播放端。
  • 延迟越看越大:观众端网络抖动导致播放缓冲堆积,让播放器做丢帧追帧(hls.js 开启 liveSyncDuration),或直接降低 hls_fragment 重建播放列表。
  • 磁盘几天就满:HLS 旧切片没被清理,加一个每 10 分钟执行一次的 cron:find /var/www/hls -name "*.ts" -mmin +10 -delete。

推流故障排查流程示意:从推流端到鉴权再到切片分发与播放

上线前再做一轮完整验收:推一路 1080p 流挂 24 小时,检查 nginx -s reload 后正在推的流是否会断(会,reload 会中断 RTMP 长连接,建议停播窗口操作);观察 df -h 确认录制目录没有异常膨胀;用 4G 网络的手机实际拉一次流,模拟最差的观众环境。这三步都过了,这套服务才算真正能交付。

写在最后:从能跑到稳定的下一步

回顾整个流程:编译带 rtmp 模块的 nginx、拆分 live 与 vod 两个应用、OBS/FFmpeg 推流、record 自动录制回放、按带宽表规划容量、按清单排障——六步走完,你已经拥有一套完全自主可控的流媒体服务器,总成本可能不到每月一百元。建议下一步把 on_publish 鉴权对接你自己的用户系统,把推流权限收进登录态;如果后续要对外提供大规模观看,推荐优先评估内容分发网络分流而不是堆服务器。如果你需要一台带宽扎实、可以随时升级端口的机器来承载这套服务,可以了解下 Hostease 的 VPS 与独立服务器方案,搭配 服务器优化 与 网站性能加速 相关内容,能把推流与分发链路调得更稳。

发表评论