做视频业务这几年,开源流媒体几乎是绕不开的选项。不管是给监控摄像头做直播分发,还是给在线课程做点播回看,又或者是给内部系统加一路实时画面,开源的 SRS、ZLMediaKit、MediaMTX 这几个项目,基本能覆盖 80% 以上的需求。这篇文章我会从选型、部署、拉流测试、安全鉴权到开源许可证合规,把整个链路按我实际跑通的顺序讲一遍。你不一定需要全看懂每个源码,但跟着操作下来,至少能自己搭起一套能出画面的流媒体服务。适合刚上手视频方向的开发、运维,也适合想给个人项目加直播能力的独立开发者。
1. 开源流媒体自建方案怎么选:从需求反推技术栈
1.1 为什么自建,而不是直接买云服务
我遇到过不少团队,视频需求一出来就直奔云厂商的直播产品,结果账单和定制化限制撑过三个月就受不了了。云直播的优势确实明显,接入快、转码强、CDN 分发能力成熟,但缺点也很具体:计费复杂、出问题要提工单、协议覆盖不一定满足你的私有场景。
自建开源流媒体适合的场景我总结过几个:
- 内网环境,比如园区、工厂、学校,视频流不能出内网,云直播根本不可行。
- 私有化交付,客户明确要求整套系统部署在客户机房,你不可能把客户摄像头拉到第三方平台。
- 长期规模大、流量稳定,自建服务器带宽成本比按量付费低很多。
- 需要深度定制,比如要改协议、叠加自己的鉴权逻辑、对接内部账号体系。
当然,自建不是“免费”的。服务器成本、带宽成本、日常监控、故障处理都要自己扛。如果只是小规模测试或个人学习,开源项目完全够用;如果是商业项目,要把运维成本、资源规划、合规问题一起算进去,再决定到底自建还是买云。
1.2 主流开源项目对比与我的选型倾向
说到开源流媒体服务器,绕不开这几家:SRS、ZLMediaKit、MediaMTX、Janus,还有偏历史的 Nginx-RTMP 和 Red5。我前几年每个都折腾过,先给一张对比表,再讲我实际项目里怎么搭配。
| 项目 | 主要协议 | 延迟表现 | 许可证 | 适合场景 | 上手难度 |
|---|---|---|---|---|---|
| SRS | RTMP、HTTP-FLV、HLS、WebRTC、SRT | 低,WebRTC 可到几百毫秒 | MIT | 直播分发、录制、回源 | 中等,文档全 |
| ZLMediaKit | RTSP、RTMP、HLS、WebRTC、GB28181 | 低,WebRTC 支持好 | MIT | 摄像头接入、多协议流媒体网关 | 中等,需要自己编译 |
| MediaMTX | RTSP、RTMP、HLS、SRT、WebRTC | 低 | MIT | 轻量转流、调试工具 | 低 |
| Janus | WebRTC 为主 | 低,面向实时通信 | GPL-3.0 | 视频会议、SFU | 高 |
| Nginx-RTMP | RTMP、HLS | 中等 | BSD-2-Clause | 老项目、简单直播 | 中等,已基本停更 |
| Red5 | RTMP 为主 | 中等 | Apache-2.0 | Java 技术栈 | 高 |
我的选型逻辑很简单:
- 做直播分发,优先 SRS。它在中国社区活跃度最高,中文文档和群聊都很多,遇到问题搜得到答案。
- 要从摄像头取流并统一转成多种协议,优先 ZLMediaKit。它对 RTSP、GB28181 这类安防协议支持非常成熟,拉流、转流、推流一把梭。
- 只是临时把一路 RTSP 转给播放器看,MediaMTX 最省事,单二进制文件就能跑。
- 要做连麦、视频会议这种双向实时通信,再考虑 Janus 或 mediasoup。
我实际项目里经常是 SRS 和 ZLMediaKit 同时用:摄像头通过 ZLMediaKit 接入,统一成 RTMP 或 WebRTC 后,再转给 SRS 做公网分发和录制。这套组合的好处是各管一段,出问题隔离得开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 服务器部署实操:SRS 和 ZLMediaKit 我都帮你跑了一遍
2.1 SRS 部署与最小配置
SRS 的部署方式非常灵活,最简单的是直接跑 Docker。我测试环境常用这一条命令:
bash复制docker run --rm -p 1935:1935 -p 1985:1985 -p 8080:8080 \
registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
这样起来之后,RTMP 入口是 1935 端口,HTTP API 是 1985,HTTP 文件服务是 8080。
如果你要改配置或编译扩展,建议还是走源码编译。我一般从 Gitee 拉代码,速度稳定:
bash复制git clone -b 5.0release https://gitee.com/ossrs/srs.git
cd srs/trunk
./configure --full
make -j$(nproc)
./objs/srs -c conf/srs.conf
编译时间取决于机器性能,4 核机器大概十分钟左右。注意 configure 时如果缺依赖,先装一下 gcc、make、pkg-config、libssl-dev 这类基础包。
SRS 默认配置可以直接用,但如果你想验证 HLS 播放,可以看下 conf/srs.conf 里是否开了 HLS。我经常用的一个最小配置长这样:
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;
}
vhost __defaultVhost__ {
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 2;
hls_window 10;
}
}
这里重点说三个字段。hls_fragment 控制每个切片的秒数,2 秒一个切片,配合适当播放器缓冲,端到端延迟可以压到 5 到 10 秒;hls_window 是保留切片窗口,10 秒意味着播放器最多落后 10 秒还能拉到流,更早的切片会被清理;hls_path 指定切片文件落盘目录,后面的 HTTP 服务就是从这个目录读文件。
2.2 ZLMediaKit 的编译与启动
ZLMediaKit 是 C++ 项目,编译前要装好 cmake、gcc/g++、libssl-dev。流程如下:
bash复制git clone https://gitee.com/xia-chu/ZLMediaKit.git
cd ZLMediaKit
git submodule update --init
mkdir build && cd build
cmake ..
make -j$(nproc)
cd release/bin
./MediaServer -c ../config.ini
这里有个特别容易踩的坑:ZLMediaKit 依赖一些子模块,如果直接 clone 后就开始编译,会报找不到头文件。所以 git submodule update --init 这一步必须执行,而且要保证子模块拉全。
启动之后,默认监听端口大致是 RTSP 554、RTMP 1935、HTTP 80。注意:Linux 下普通用户监听 554 会报权限不足,开发和测试环境我习惯把 RTSP 端口改成 10554,改法是编辑 config.ini 里的 port=10554。
ZLMediaKit 的 HTTP API 默认是开放的,生产环境第一件事就是设置 API 鉴权。在 config.ini 里找 api.secret,设置一个强密码,之后所有 HTTP API 请求都要带上 ?secret=你的密码。这一点不夸张地说,很多人裸奔上线,被扫描器盯上只是时间问题。
2.3 用 ffmpeg 推流,验证整条链路
服务起来后,最关键的一步是验证推拉流链路是通的。我建议不要一上来就拿摄像头试,先用 ffmpeg 生成一路自己可控的测试流,排除摄像头和网络因素。
推一路测试视频到 SRS:
bash复制ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=25 \
-f lavfi -i sine=frequency=1000:sample_rate=44100 \
-c:v libx264 -preset ultrafast -tune zerolatency \
-c:a aac -g 50 -f flv rtmp://127.0.0.1:1935/live/test
这条命令里,testsrc 是 ffmpeg 自带的测试画面,sine 是测试音频,-re 表示按真实时间速率读取,避免推流速度过快。-g 50 是设置 GOP 大小为 50 帧,也就是每 2 秒一个关键帧,这个参数直接影响播放器起播速度。
拉流测试推荐用 ffplay,带一组快速启动参数:
bash复制ffplay -fflags nobuffer -analyzeduration 1000000 \
-i rtmp://127.0.0.1:1935/live/test
如果画面声音都正常,说明 RTMP 链路通了。再验证 HLS:
bash复制ffplay http://127.0.0.1:8080/live/test.m3u8
能播放就说明 HLS 切片和 HTTP 分发都没问题。
如果你想让 ZLMediaKit 也参与进来,可以推一路 RTSP:
bash复制ffmpeg -re -i input.mp4 -c copy -f rtsp rtsp://127.0.0.1:10554/live/test
注意 -c copy 是直接复用原文件编码,如果原文件编码不是 H.264/AAC,可能会有兼容性问题,需要先转码。
验证用的流一定自己生成,或者用自己设备的摄像头。网上流传的所谓“公共 RTSP 地址”很多是别人设备和私有资源,用了既不稳定也不礼貌。
3. 拉流地址与延迟调优:让画面在不同端都能秒开
3.1 RTSP、RTMP、HLS、HTTP-FLV、WebRTC 地址长什么样
很多刚接触流媒体的朋友,第一反应是“流地址到底怎么写”。这个答案取决于你用什么服务器、流名是什么 vhost 下的 app/stream。
以我上面搭的 SRS 为例,如果推流到 rtmp://ip:1935/live/test,那么:
| 协议 | 地址示例 | 说明 |
|---|---|---|
| RTMP | rtmp://ip:1935/live/test | 推流和拉流都可以,Flash 时代的标准 |
| HTTP-FLV | http://ip:8080/live/test.flv | 网页播放延迟低,适合浏览器 |
| HLS | http://ip:8080/live/test.m3u8 | 苹果生态和普通网页兼容性最好 |
| WebRTC | webrtc://ip:1935/live/test 配合 SRS 播放器 | 延迟最低,适合会议和连麦 |
如果是 ZLMediaKit,推流地址是 rtsp://ip:10554/live/test 或 rtmp://ip:1935/live/test 后,拉流地址一般对应:
| 协议 | 地址示例 |
|---|---|
| RTSP | rtsp://ip:10554/live/test |
| RTMP | rtmp://ip:1935/live/test |
| HLS | http://ip/live/test/hls.m3u8 |
| FLV | http://ip/live/test.live.flv |
| WebRTC | webrtc://ip:8000/live/test(按实际配置) |
我必须提醒一句:不同版本、不同配置下的 URL 规则可能不一样,一定要以你部署版本的官方 README 和实际日志为准。我见过太多人拿着别人的地址模板往自己服务器上套,结果一直打不开,最后发现版本差异。
3.2 用 ffprobe、curl、VLC 快速验证流地址是否可用
验证流地址是否可用,最直接的工具是 ffprobe。它能在播放前就把流的编码信息、时长、分辨率拉出来。
检查 RTSP 流:
bash复制ffprobe -rtsp_transport tcp -i rtsp://127.0.0.1:10554/live/test -show_streams -show_format
加 -rtsp_transport tcp 是因为 RTSP 默认走 UDP,在跨网络或丢包环境下经常抽风,TCP 更稳。如果这条命令能正常输出流信息,说明拉流链路没问题;如果卡住不动或直接报错,再往下排查。
检查 HLS 流,可以直接用 curl 看 m3u8 文件内容:
bash复制curl -s http://127.0.0.1:8080/live/test.m3u8
正常的切片列表里会有多个 .ts 文件名,如果只看到 #EXTM3U 没有分片,说明流没有推上来或切片还没生成。
播放端测试我一般用 VLC 或 ffplay。VLC 的好处是支持协议广,RTSP、RTMP、HLS 都能直接打开,适合给不懂技术的人看。ffplay 的好处是命令行可控,可以带参数模拟弱网、设置缓冲。
3.3 低延迟配置思路:HLS、HTTP-FLV、WebRTC 分别怎么调
延迟需求是你选择协议的最重要依据。
HLS 延迟主要由切片时长决定,2 秒一个切片,播放器至少会有 2 到 4 秒的额外缓冲。想降低延迟,先把 hls_fragment 调小到 1 或 2,然后把播放器的缓冲调小。如果是网页端用 hls.js,开启 lowLatencyMode 也有帮助。但说实话,HLS 做到极致也就是 5 秒左右,再低不现实。
HTTP-FLV 的低延迟效果比 HLS 好不少,因为它是长连接流式传输,不依赖切片。延迟一般在 1 到 3 秒。配合 flv.js 在浏览器播放,是比较成熟的低延迟方案。
真正要做到几百毫秒,只能上 WebRTC。SRS 5.0 对 WebRTC 支持已经很完善,但有几个硬性条件:服务器需要有公网 IP,或者内网环境里客户端和服务器在同一二层网络;UDP 端口要放行,SRS 的 WebRTC 默认会用到 8000 这个 RTC 端口区间;NAT 穿透依赖 ICE candidate,如果环境复杂,需要手动配置 candidate 为服务器公网 IP。
推流端参数对延迟影响也很大。ffmpeg 推流时,-preset ultrafast 和 -tune zerolatency 能明显减少编码缓冲,-g 控制关键帧间隔,关键帧间隔越大,起播越慢。我常用的推流参数组合:
bash复制ffmpeg -re -i input.mp4 \
-c:v libx264 -preset ultrafast -tune zerolatency \
-c:a aac -g 30 -f flv rtmp://ip:1935/live/test
这里 -g 30 是每 1 秒一个关键帧(假设 30 帧率),适合对起播速度敏感的场景。
4. 流媒体访问控制与公网防护:先鉴权再谈功能
4.1 推流鉴权、播放鉴权与 HLS 加密
流媒体服务最大的安全风险是匿名推流和匿名拉流。匿名推流意味着任何人都可以把垃圾流推到你的服务器上刷流量;匿名拉流意味着你的摄像头画面、直播内容可以被任何人看。这两个问题都要靠鉴权解决。
SRS 的常用做法是配置 HTTP 回调,在推流和播放开始时请求你的业务接口,返回 0 表示拒绝,返回非 0 表示允许。典型配置片段:
conf复制vhost __defaultVhost__ {
http_hooks {
enabled on;
on_publish http://127.0.0.1:8080/check_publish;
on_play http://127.0.0.1:8080/check_play;
}
}
有了这个回调,推流地址可以带上 token 参数,比如 rtmp://ip:1935/live/test?token=xxxxx,业务网关再校验 token 是否合法。需要注意,hook 地址本身不能暴露给外部网络,否则等于把校验逻辑敞开了。
播放防盗链常见的有两种:referer 校验和 token 过期校验。referer 只适合防君子,因为伪造起来太容易;token 方案更靠谱,可以限定过期时间、单次有效、绑定用户。
HLS 内容的加密,SRS 支持 HLS AES-128 加密,在 vhost 里开启 hls_aes,并配置密钥文件路径。这样 m3u8 文件里的切片是加密的,播放时通过 EXT-X-KEY 标签下载密钥,没拿到密钥的人即使复制了流地址和切片也播放不了。如果要保护的是重要版权内容,HLS 加密只解决一部分问题,因为播放器拿到密钥后还是可能被录制,这是流媒体天然的攻防点。
4.2 公网部署时的必要防护清单
把流媒体服务暴露到公网前,我建议按这个清单过一遍:
- 改掉默认端口。1935、554、80 这些端口是扫描器最爱,改成高位随机端口至少能挡住一批脚本。
- 开启 API 鉴权。SRS 的 http_api 里配置
secret,ZLMediaKit 的 config.ini 里配置api.secret,确保管理接口不能被匿名访问。 - 控制来源 IP。如果业务方 IP 固定,用防火墙规则把 1935、554 的访问来源限制到业务网段。
- 使用 Nginx 反向代理做 HTTPS。HLS 和 HTTP-FLV 走 HTTPS,避免明文流量被抓包。
- 日志保留和监控。访问日志、推流日志至少保留 30 天,出现异常能追溯。
我见过最典型的翻车现场是:服务器上直接跑默认配置的 ZLMediaKit,554 和 80 端口全开,API 没有 secret,上线不到十分钟就被扫描工具抓到了。所以我的原则很简单:任何自建流媒体,先鉴权再谈功能,哪怕只是内部测试,也把 secret 先设上。
5. 开源许可证与合规扫描:开源不等于随便用
5.1 主流许可证差异速查
开源流媒体项目基本都是开源许可证,但许可证和许可证之间的权限差别非常大。很多团队直接 git clone 下来编译上线,完全没有读过 License,这是一个风险点。
先看几种最常见的许可证:
| 许可证 | 核心要求 | 商用注意事项 |
|---|---|---|
| MIT | 可自由使用、修改、分发,需保留版权声明 | 基本无限制,但别把原作者署名去掉 |
| Apache-2.0 | 类似 MIT,额外包含专利授权条款 | 需保留 NOTICE 文件,注明修改内容 |
| BSD-2-Clause | 宽松,分条款较多 | 需保留版权和免责声明 |
| GPL-3.0 | 修改或分发衍生作品时,需以 GPL-3.0 开源 | 闭源商用风险高,可能涉及整个项目的开源义务 |
| AGPL-3.0 | 在 GPL-3.0 基础上,网络提供服务也需提供源码 | SaaS 场景尤其要小心 |
| MulanPSL-2.0 | 国内发起的开源许可证,宽松程度类似 Apache-2.0 | 条款相对友好,中文文本更易读 |
回到流媒体项目本身,SRS、ZLMediaKit、MediaMTX 都是 MIT,非常宽松,商用没什么负担。Janus 是 GPL-3.0,第三方组件使用方式和部署方式就要考虑边界。Nginx-RTMP 是 BSD-2-Clause,Red5 是 Apache-2.0。
5.2 商业项目为什么要在意“传染性”
GPL 家族经常被提起的是“传染性”。这个说法很粗暴,但能帮助理解:如果你把 GPL 协议的组件代码直接嵌入到自己的代码里,一起编译、一起分发,那么你的那部分代码可能需要以同样的 GPL 许可证开源。
这里有一个关键边界问题:如果你的组件和 GPL 程序是独立进程,通过 HTTP API、命令行等方式通信,一般不被认定为同一衍生作品,这在开源社区里叫“独立作品”。但实际操作中,这个边界没有一刀切的标准,不同律师可能有不同解读。我的做法是:小型工具能替代的尽量用宽松许可证替代,确实需要 GPL 组件的,单独抽成独立服务,避免混源。
商业公司还要特别留意“版权声明保留”这个隐含义务。MIT 协议虽然自由,但要求版权声明和许可声明必须出现在所有副本中。我见过有团队把源码包装成自研产品,却把别人的 LICENSE 文件删了,一旦被原作者追责,非常被动。
5.3 用自动化工具做依赖合规扫描
手动看许可证在项目小的时候可行,一旦依赖几十上百个,就不现实了。我这边流程一般是这样:
- 维护一份 SBOM(软件物料清单),把项目用到的所有开源组件、版本、许可证记录下来。
- 用自动化扫描工具跑依赖,输出漏洞和许可证风险报告。
- 对高风险项做决策:换替代组件、购买商业授权、整改代码、放弃使用。
工具方面,商业工具 Black Duck 是最常用的,扫描后会自动提示组件漏洞、许可证类型和冲突项。开源方案可以用 OSS Review Toolkit(ORT),配合 FOSSA 的免费层也能覆盖大部分场景。GitHub 和 Gitee 仓库里也有简单的许可证检测提示,能看到依赖的 License 概况。
如果是第一次做合规,建议把扫描结果分成三个优先级:高危漏洞(必须尽快处理)、许可证冲突(需要立即决策)、缺失许可证信息(需要补充记录)。这样不至于拿到几百页报告后不知道从哪下手。
6. 常见问题与排查思路:从黑屏到断流的处理实录
6.1 推流成功但播放黑屏
“ffmpeg 推流没报错,服务器日志也没异常,但播放器就是黑屏”,这个问题出现频率极高。我排查时按顺序看三点:
- 编码格式是否兼容。H.264 + AAC 是兼容性最好的组合;如果是 H.265/HEVC,很多浏览器和播放器不支持;如果是纯视频没有音频,有些播放器也会表现异常。
- 是否存在 B 帧。B 帧会引入解码延迟和乱序,部分播放器处理不好就黑屏或花屏。ffmpeg 推流时加
-bf 0可以禁用 B 帧,代价是压缩率略降。 - 关键帧间隔是否过大。GOP 太大时,播放器要从关键帧开始解码,如果起播时正好错过关键帧,会一直黑屏等待。用
-g控制在 2 到 3 秒内。
验证手段是 ffprobe 拉一下流,看视频流的 codec_name、profile、level 是不是正常。比如:
bash复制ffprobe -show_streams -show_format -of json rtmp://ip:1935/live/test
能正常解析出来,再怀疑播放器;解析不出来,问题在前面几层。
6.2 WebRTC 连不上或秒断
WebRTC 秒断一般不是代码问题,是网络环境问题。最常见的是服务器没有公网 IP,或者有公网 IP 但 UDP 端口没放行。
SRS 的 WebRTC 配置需要明确 candidate。如果服务器只有内网 IP,通过云端 NAT 转发,必须手动配置公网 IP 作为 candidate,否则客户端看到的是内网地址,永远连不回来。
排查时先看 SRS 日志,确认 RTC 端口是否正常绑定,再检查防火墙。很多云厂商安全组默认只放行 TCP,UDP 全部丢弃,WebRTC 就完全跑不通。放行时注意端口范围,SRS 默认 RTC 端口是 8000 起一段 UDP,ZLMediaKit 也类似,具体以配置文件为准。
6.3 录制文件损坏或磁盘写满
SRS 的录制功能默认生成 FLV 文件,不是 MP4。如果项目要回看,需要转封装。常见做法是定时任务把 FLV 转 MP4:
bash复制ffmpeg -i record.flv -c copy record.mp4
如果录制中途断流,FLV 文件可能没有写尾部索引,ffprobe 可能会报错。遇到这种情况,恢复断流的点只能从最后一个关键帧之前开始,数据会有一小段丢失,这是流录制本身的限制。
磁盘写满的预防要做好两级:一是 SRS 的配置里打开 max_sequences 或切片回收策略,限制历史文件数量;二是在系统层面写 crontab 定时清理过期文件。我在生产环境碰到过一次最严重的故障,就是录制目录写满导致推流中断,后来加了一个磁盘水位监控,超过 80% 自动发告警,才算根治。
6.4 一条通用排查路径
从推流端到播放端,我的排查顺序是固定的:
| 排查点 | 常用命令 / 操作 | 判断标准 |
|---|---|---|
| 推流端进程是否正常 | ps 确认 ffmpeg/编码进程在跑 | 进程无异常退出 |
| 服务器端口是否监听 | ss -lntup | grep 1935 | 端口处于 LISTEN 状态 |
| 服务器日志有无错误 | tail -f 日志文件 | 无 fatal、error |
| 拉流端能否解析流 | ffprobe -i 地址 | 正常输出流信息 |
| 播放端缓存设置 | 调小 VLC/ffplay 缓冲 | 起播延迟明显改善 |
如果到第四步还不行,就逐层回退:先确认推流是否成功,再看服务器是否收到流,最后才怀疑播放器。
最后分享一点实操心得
写到这基本把开源流媒体的主干跑了一遍。我个人体会是,这类项目一旦跑通链路,后续扩展反而简单,因为社区和文档都非常成熟。你如果刚开始接触,不要纠结高深调试,先用 SRS 或 ZLMediaKit 配合 ffmpeg 推一路自己生成的测试流,把 RTMP、HLS、HTTP-FLV、WebRTC 都点一遍,再代入自己的摄像头和使用场景。过程中最常翻车的其实不是服务器,而是编码参数和公网端口,把这两点记住,能少走很多弯路。
