最近给一个在线教学项目做实时音视频方案,刚开始图省事直接租云直播服务,结果带宽加上存储,一个月大几千块,业务还没跑起来成本先崩了。后来索性把目光转向开源流媒体服务,最终锁定了 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",就是因为本机没有开启硬件虚拟化。处理步骤如下:

  1. 重启电脑进入 BIOS / UEFI,开启 Intel VT-x 或 AMD-V
  2. 在 Windows 功能中勾选“适用于 Linux 的 Windows 子系统”(WSL2)和“虚拟机平台”
  3. 以管理员身份执行 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 里配置推流地址:

  1. 打开“设置” -> “直播”
  2. 服务选择“自定义”
  3. 服务器填写 rtmp://服务器IP:1935/live
  4. 串流密钥随便填一个,比如 stream1
  5. 点击“开始直播”

这里的 .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,最后再上鉴权和集群。每加一个功能,就验证一个环节,出问题时能快速判断是新配置引入的还是原有链路的问题。流媒体链条比较长,推流端、服务端、播放端任何一端出问题都会导致异常,分层排查的习惯能帮你省下大把时间。

Logo

邀请您加入社区

更多推荐