浏览器根本不支持 RTMP:所谓“RTMP 在线播放器”到底在播什么
关于我
我是 EZ 在线工具网 的站长,这个站上的播放器和转换工具都是我们自己写的。下面要拆的 FLV/RTMP 在线播放器 就是其中之一,所以这不是转述别人的科普,是我们的代码。
结论先甩出来
浏览器里没有 RTMP 这个东西。不是兼容性差,是压根没有。
不信,打开控制台把这几个名字挨个敲一遍:
typeof fetch // 'function' HTTP/1.1、2、3 都走这条
typeof XMLHttpRequest // 'function' 老接口,还活着
typeof WebSocket // 'function' ws:// wss://
typeof RTCPeerConnection // 'function' WebRTC
typeof RTMPConnection // 'undefined' ← 就这个RTMP 是跑在 TCP 1935 端口上的长连接协议。浏览器不给你裸 TCP 的能力,也没打算给。所以“网页直连 RTMP”这句话,从技术上就不成立。
RTMP 是谁的东西
Adobe 定的,更早是 Macromedia。规范到 2009 年才公开,整套东西是三个部件咬在一起的:TCP 长连接、握手加 chunk 分片、再加一个能解析它的运行时,也就是 Flash Player。
后半截已经没了。Flash 在 2020 年底停止支持,2021 年起主流浏览器把插件通道全拆了。RTMP 唯一的宿主就这么消失。
那浏览器为什么不自己实现一个?划不来。等于往浏览器里塞一套私有协议栈:握手、chunk 流、AMF 序列化,还要兼容各家魔改过的实现。推流本来就是服务端的事,播流又有更省事的出路。
浏览器只开了三个口子
| 管道 | 能装什么 | 一般延迟 | 烦人的地方 |
|---|---|---|---|
| HTTP 分段拉 | HLS(TS 或 fMP4 分片)、DASH | 6-30 秒,LL-HLS 能压到 2-5 秒 | 分片和缓冲定死的下限,砍不下去 |
| HTTP 长连接流 | HTTP-FLV、HTTP-TS | 1-3 秒 | 服务端得一直吐,中间挂个代理就可能开始攒数据 |
| WebSocket | 自己定义的二进制协议 | 看你写 | 没有播放器生态,拆包全靠自己 |
| WebRTC | RTP/RTCP over SRTP | 0.2-1 秒 | 信令、NAT 穿透、运维成本 |
表格里没有 RTMP,一行都没有。你在网页上看过的所有直播,底下都是这四条里的一条。
flv.js 不播 RTMP,它只换壳
B 站开源的 flv.js 被叫了很多年“网页 RTMP 播放器”,我们站也把它放在 RTMP 播放器里用。这个名字的误会太大了。
<video> 能直接吃的数据只有两种容器:ISO BMFF(就是 fMP4)和 WebM。TS 不行,FLV 也不行。视频标签不管你想喂什么,它只认这两个。
flv.js 干的是中间那层活:走 HTTP 把 FLV 字节流拉下来,按 FLV 的 Tag 结构拆开,抠出 H.264 的 NALU 和 AAC 音频帧,现场重封成 fMP4,最后通过 MSE 的 appendBuffer() 丢给 <video>。
这活儿叫 transmux,转封装。不解码也不编码,只是把盒子换了一个。所以它 CPU 占用低得离谱,画质一点不掉,延迟也小。代价是输入端只认 HTTP-FLV,它没有能力自己去连 1935。
同一套“只换壳不重编码”的路子,我们在 M3U8 转 MP4 的拆解 里也走过一遍,那次换的是另一层壳。
名字里带 RTMP 的,其实就三类
真转协议的。 用户填 RTMP,后端用 FFmpeg 拉流转成 HLS 或 HTTP-FLV 再吐出来。延迟和服务器账单都堆在服务端,商业方案基本都这么干,也只有这样才能真的接住一个 rtmp:// 地址。
只吃 HTTP-FLV、名字里塞 RTMP 骗搜索的。 我们站属于这类。它不连 RTMP,只是顺手把 rtmp:// 猜成对应的 http-flv 地址。那为什么还叫 RTMP?因为大家搜的就是这个词,我改个名字等于把自己藏起来。这点我坦白。
浏览器插件和桌面客户端。 这类是真在跑 RTMP,但要装东西,跟“在线”没关系。
下次看到“免安装、在线、直接播 RTMP”的宣传,就问一句:协议在哪儿转的?答不上来的,多半是第二类。
把我们自己的代码拖出来看
播放器拿到 rtmp:// 开头的地址,走的是这么三行:
const url = new URL(rtmpUrl.replace('rtmp://', 'https://'));
url.pathname += '.flv';
return url.href;赌的是服务端在同一台机器的 80/443 上也开了 HTTP-FLV,路径规则就是把流名加个 .flv。腾讯云、阿里云的直播域名都吃这套,代码里塞的那个示例地址就是这么来的。
这里有个坑必须说清楚:端口不会跟着改。 你填 rtmp://1.2.3.4:1935/live/demo,改写出来的是 https://1.2.3.4:1935/live/demo.flv。浏览器拿着 HTTPS 去敲 1935 端口,对面在那儿只会讲 RTMP,握手当场失败。更烦的是报错文案会让你以为是跨域,然后你花一晚上去查 CORS,最后发现是自己填错了地址。
两种正确填法:服务端确实在 443 上开了 HTTP-FLV,把端口去掉;不确定的话,直接填完整的 https://域名/live/xxx.flv,让它原样请求。
播不了的时候别猜。开 DevTools 的 Network 面板,看那条请求真实用的什么协议、什么端口、响应头写了什么。这一步能解决八成“播不了”。
参数为什么这么配
创建播放器时传的配置:
flvjs.createPlayer({
type: 'flv', isLive: true, cors: true, withCredentials: false
}, {
enableWorker: false,
lazyLoad: true, lazyLoadMaxDuration: 3 * 60,
liveBufferLatencyChasing: true,
liveBufferLatencyMaxLatency: 2,
liveBufferLatencyMinRemain: 1
})liveBufferLatencyChasing: true 是低延迟那口气。播放器会盯着缓冲区里积压了多少,超过 liveBufferLatencyMaxLatency(设的 2 秒)就偷偷加速追帧,追到只剩 liveBufferLatencyMinRemain(1 秒)就收手。追太狠会卡,不追延迟就涨,延迟和流畅度就卡在这一对数字上。
lazyLoad 那两行是给点播型 FLV 兜的,lazyLoadMaxDuration: 180 意思是别一上来就把整个文件拉完,用户还没看到的位置没必要占带宽。
enableWorker: false 是我们踩出来的。转封装本身很轻,再开一个 worker 去搬数据,反而容易和主线程抢资源;页面上还有别的活在跑,直接放手在主线程更稳。
cors: true 配 withCredentials: false:允许跨域拉流,但不带 Cookie。这样服务端只要给个 Access-Control-Allow-Origin 就能播,不用去处理凭据模式下通配符不能用那堆麻烦。
面板上那个延迟读数不是我估的,是 flv.js 的 STATISTICS_INFO 事件报出来的 delay。
延迟这数字,谁承诺谁心虚
上面表里的区间是典型值,不是协议保证值。同样一条流,换台服务器能差出三倍。真正说话算数的是这几个:
- GOP 长度。2 秒一个关键帧,延迟就别想低于 2 秒。
- 服务端缓冲策略。有的服务器为了抗抖动主动攒 3 秒再发,前端怎么追都追不回来。
- 追帧阈值。就是上面那对
MaxLatency和MinRemain。 - 中间链路。CDN 或者反向代理有没有做二次缓冲,这条最容易被漏掉。
想拿到自己环境里的真实数字,办法很土:同一路流连播 5 分钟,每 10 秒记一次延迟读数,算平均值和最差值。别拿刚点开那一秒的数字当结论,那个数字永远很好看。
想自己搭一个能播的
三件事:
- OBS 推
rtmp://你的服务器/live/流名 - 服务端既收 RTMP,又吐 HTTP-FLV。nginx 得编译
nginx-http-flv-module,SRS 自带http_remux - 播放端填
http://你的服务器/live/流名.flv
卡人的永远是第 2 步。nginx 默认那套配置只收 RTMP,网页端一定播不了。从编译到跑通的全过程我们单独写过一篇——手把手搭建 Nginx-RTMP 流媒体服务器,重点就是那个“收流 + 输出”的双配置,只配一半必然白忙。
我们做不到的部分
- iPhone 上基本别指望。iOS 的 Safari 长期没开 MSE,而 flv.js 整条链路都架在 MSE 上。iPhone 只能走原生 HLS 或者 WebRTC。iPadOS 有些版本反倒可以,所以别拿一台设备就下结论。
- 跨域头是硬要求。flv.js 用 fetch 拉流,服务端得给
Access-Control-Allow-Origin。开了防盗链还要把来源域名加进白名单,不然控制台给你的那句报错会把你往坑里带。 - 只认 FLV over HTTP。HEVC 封的 HTTP-FLV、或者别人自己拼的 MP4 流,它都吃不下。
- 我们的工具不替你转协议。它就是纯浏览器端的播放器,服务端没开 HTTP-FLV,它也救不了你。这事在 隐私政策 里也写了:所有解析都在你自己的浏览器里跑,我们看不到你的流。
就这样
手上已经有 HTTP-FLV 或者 RTMP 原始地址的,直接丢进 FLV/RTMP 在线播放器,能出画面就说明服务端配对了。
播不出来、源站只有 HLS 的,换 M3U8 在线播放器,路更短。搞不清手里那个地址属于哪一类,先看 .m3u8 文件到底是什么,一分钟能判断出来。
至于浏览器直连 RTMP 这件事,等哪天它愿意把裸 TCP 的权限交出来再说。目前一点迹象都没有。
📌 相关文章
- 手把手搭建 Nginx-RTMP 流媒体服务器(Ubuntu/Windows) —— 想跑通“推 RTMP、播 HTTP-FLV”,这篇的配置直接抄
- 使用 B 站开源的 flv.js:网页无插件播放 RTMP/FLV —— 只想看 flv.js 的 API 怎么调,看那几段代码就够
- 零基础:用网页播放器轻松观看 FLV/RTMP 直播 —— 不写代码,就想把地址填进去看画面
- .m3u8 是什么格式?HLS 原理与打开方法 —— 播不了的备选方案,先把 HLS 这套搞明白
- TS 合并失败、无法播放怎么办?7 类常见问题排查 —— 排查思路和本文同源:先看真实请求,再谈猜测
🚀 推荐阅读
- 一文搞懂 HLS.js 使用指南与最佳实践 —— HLS 那边的参数怎么配,对照着看更有感觉
- H.264 vs H.265 vs AV1:三大编码深度对比 —— “不重编码”到底省了多少,看完就有答案
- M3U8 转 MP4 失败?黑屏、加密、403 全攻略 —— 同一套“先看 Network 面板”的排查方法论
- Mac/Win 通用:把网页 M3U8 视频流保存为本地 MP4 —— 流能播了,下一步通常是想存下来