ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

RTMP协议全解析:为何仍是直播推流事实标准与实战搭建

RTMP协议全解析:为何仍是直播推流事实标准与实战搭建 Flash Player在2020年底正式停止维护很多人以为Flash生态里的那些技术也一起进了坟墓。但有个例外一直活得好好的就是今天要聊的RTMPReal Time Messaging Protocol实时消息传输协议。不信你去看看直播行业后端是怎么把画面推到CDN的或者翻翻各大云厂商直播服务的接入文档RTMP仍然是出场率最高的推流协议。它确实是上世纪的东西但直到今天做直播开发、流媒体运维、甚至是用OBS自己开播的人都躲不开它。这篇文章我会把RTMP讲透它到底解决了什么问题为什么Flash没了它还在消息是怎么在TCP连接上流动的然后带你把一套推拉流环境从零搭起来最后聊聊什么场景下该选它、什么场景下该换别的协议。想学视频开发或者搞直播业务的看完这篇应该能直接上手干活了。1. Flash早就死了RTMP为什么还活着1.1 RTMP的诞生与Flash时代RTMP是Macromedia在2002年前后搞出来的私有协议后来Adobe把Macromedia收了协议就成了Adobe家的东西。当时它的定位非常清晰给Flash Player和Flash Media Server之间传输音视频数据用。你回忆一下当年网页上看视频、玩Flash小游戏的体验那些视频用的底层传输协议大部分就是RTMP。它的设计目标在当年很超前一是低延迟数据到了就推不用等一个文件攒完整二是支持双向交互客户端可以给服务器发命令服务器也能回推消息三是基于TCP的可靠传输不乱序、不丢包。这些特性放到今天的互动直播场景里依然不过时。1.2 Flash凉了推流侧的RTMP没凉Flash Player都停了RTMP为什么没跟着死原因其实不复杂它在推流这个环节已经形成了事实标准。用一句话说清楚推流和拉流的区别推流是主播端把画面声音送进服务器拉流是观众端从服务器取画面声音。Flash的死影响的是浏览器端拉流播放RTMP不再被浏览器原生支持所以拉流侧现在普遍换成了HLS或者HTTP-FLV。但推流侧的生态早就被RTMP占据了这个惯性实在太大OBS等主流的直播推流软件默认的输出协议就是RTMP市面上几乎所有的专业编码器、无人机图传、运动相机固件里都内置了RTMP推流功能各大云厂商的直播服务接流节点统一支持RTMP接入成本极低无数存量系统、文档、SDK都是围绕RTMP推流设计的这就形成了一个很微妙的局面浏览器端的播放早已抛弃RTMP但所有把画面送进互联网的操作都还在用这个老古董协议。1.3 推流这个环节为什么绕不开RTMP你可能会问推流侧的技术也都更新换代了凭什么大家还是用RTMP我的理解是推流这个动作对通用性的要求远高于对先进性的要求。推流端不只是运行在用户电脑上的软件还可能是摄像头、无人机、工业设备、嵌入式开发板比如ESP32-CAM这类小设备。这些设备的算力有限不可能内置太复杂的协议栈。RTMP基于TCP可靠、稳定、CPU开销小对比现在很火的WebRTC那一大套ICE/SRTP/DTLS流程RTMP的实现就简单太多了。对于硬件厂商来说烧一个RTMP推流库进固件成本低、兼容性好没有理由改。对CDN和云厂商来说也是这样接流节点需要接纳各种推流端用一个大家都支持的协议做最大公约数,能省掉海量的兼容性问题。所以你现在看到的直播链路绝大多数依然是这条摄像头/软件推流端 → RTMP推流 → 云端接入 → 转封装分发HLS/HTTP-FLV/WebRTC → 观众播放。RTMP虽然只管了第一段路但这一段路恰恰是绕不开的。2. RTMP的握手、命令与消息块低延迟的秘密2.1 从一次完整的RTMP连接说起RTMP基于TCP默认端口是1935也支持封装在HTTP里的RTMPT端口80和带TLS加密的RTMPS端口443。看协议序号就明白了连接层用TCP保证可靠传输协议层自己定义了握手、命令和数据格式。一次完整的RTMP推流连接可以拆成几大步TCP三次握手建立底层连接RTMP握手客户端发C0C1服务器回S0S1S2客户端再发C2确认双方协议版本和随机数据。握手包里有时间戳和随机字节主要目的是确认双方都按照RTMP协议工作connect命令客户端发一个AMF格式的connect命令参数包括app名称、tcUrl就是完整的推流地址等请求连到某个应用上createStream命令连接成功后客户端再发createStream命令创建一个用于传输音视频的Stream推流或播放推流端发publish命令播放端发play命令然后就进入音视频数据的正常传输阶段音视频数据传输视频帧和音频帧被打成Message再拆成Chunk流持续发送我之前第一次抓RTMP包的时候最大的感受是这个协议聊天的频次非常密集。每次推流建连光是命令消息就有好几个往返不像现在很多协议一个HTTP请求就完事了。但要理解RTMP设计了这些步骤是为了支持复杂的交互和状态管理而且这些命令在长连接里只发生一次后续传输成本就非常低了。2.2 消息块Chunk设计头压缩与多流复用RTMP的数据传输单位是Message可以简单理解成一个逻辑数据包里面装着音频帧、视频帧或者命令数据。但这些Message真正在网络上发送时会被切成比较小的Chunk块。为什么要切因为RTMP是长连接一条TCP连接上可能同时跑着音频流、视频流、控制命令流Chunk机制允许这些不同类型的Message交错发送避免一个大视频帧把网络堵住让其他数据干等。TP层提供的字节流没有天然的报文边界所以每个Chunk都会带上一个Basic Header用于标识这个Chunk属于哪个Chunk Stream ID接收端根据ID把碎片重组回Message。你可以想象成快递仓库里不同订单的商品放在同一个传送带上每个快件上贴着订单号到了目的地再分拣归类。RTMP还有一个细节特别有意思就是变长Chunk头Chunk Header。默认的Chunk size是128字节但连接双方可以通过Set Chunk Size消息协商把块大小调大。我实测下来推流时把chunk_size配置到4096或更大大分辨率视频帧的传输效率会有明显提升CPU占用也低一些。这个优化在nginx-rtmp的配置里一行就能搞定后面实操环节会提到。2.3 命令消息与AMF格式RTMP的命令消息用的是AMFAction Message Format来序列化。AMF是Adobe定义的一套二进制序列化格式功能上类似现在的JSON能表达数字、字符串、对象、数组等类型但编码方式是二进制的比JSON紧凑。RTMP的命令消息类型号是20数据消息是18。拿connect命令举个例子消息体里会有命令名connect事务IDTransaction ID用来把请求和响应配对命令对象一堆键值对包含app应用名比如live、type、flashVer、tcUrl等打开Wireshark去看RTMP的抓包你会发现这些字段一目了然。AMF其实是很老的格式了但胜在稳定被Flash整个生态大量使用所以RTMP一直沿用了下来。推流中常见的publish命令、play命令、deleteStream命令都是通过AMF格式拼装传输的。2.4 RTMP低延迟的真相聊RTMP必然绕不开延迟问题。RTMP把延迟控制在2到5秒这个量级核心原因是它没有切片这个动作。对比一下HLSHLS会把直播流切成一段一段的TS文件每段通常2到6秒播放器要下载完一个切片才能播切片下载播放器缓冲叠起来延迟轻松到6秒以上配置不好甚至30秒。而RTMP是长连接直接推流服务器收到数据后可以立刻转发给播放端中间没有等切片的时间。同时服务器通常还会配合GOP Cache关键帧缓存机制。新观众进入直播间时如果直接播当前帧画面会花屏因为解码依赖前面的I帧。服务器缓存一个GOP从I帧开始的一组画面新观众进来时把缓存的数据先发过去播放器就能从关键帧开始正常解码。nginx-rtmp里开这个功能就是一行配置但很多新手会漏掉导致拉流黑屏或者花屏。3. 从零搭一套RTMP推拉流环境nginx-rtmp、ffmpeg与OBS实测理论讲再多不如亲手跑通一条链路。这一节我带你在本地把RTMP服务器、推流端、播放端全部跑起来。整个过程只需要一台Linux机器虚拟机也OK、一个ffmpeg、一个播放器不需要额外花钱。3.1 准备环境与安装nginx-rtmp最简单的方式是用nginx的官方rtmp模块包。以Ubuntu/Debian为例sudo apt update sudo apt install libnginx-mod-rtmp ffmpeg ffplay装完检查一下模块是否加载了nginx -V 21 | grep rtmp # 输出结果里能看到 --add-module../nginx-rtmp-module 之类的字样就说明OK如果系统源里没有这个模块包那就走编译安装路线。编译的时候记得装好依赖包build-essential、libpcre3-dev、libssl-dev、zlib1g-dev然后按标准流程操作即可wget http://nginx.org/download/nginx-1.24.0.tar.gz git clone https://github.com/arut/nginx-rtmp-module.git tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.0 ./configure --add-module../nginx-rtmp-module --with-http_ssl_module make sudo make install编译安装后nginx默认在/usr/local/nginx下启动方式为sudo /usr/local/nginx/sbin/nginx注意不要和系统自带的nginx冲突。3.2 踩坑点nginx.conf里最容易配错的三个细节网上配RTMP服务器的教程很多但我发现新手特别容易在nginx.conf上翻车主要栽在三个地方。第一rtmp配置块写错了位置。rtmp块必须和http块平级不能塞在http块里面。正确的位置是在http块外面单独一层worker_processes auto; events { worker_connections 1024; } http { # HTTP 配置保持默认即可 } rtmp { server { listen 1935; chunk_size 4096; application live { live on; record off; gop_cache on; } } }第二防火墙忘了放行1935端口。RTMP默认走1935/TCP装好服务后要在防火墙里放行不然从外部推流一定会失败。命令参考# UFW 防火墙 sudo ufw allow 1935/tcp # firewalld sudo firewall-cmd --add-port1935/tcp --permanent sudo firewall-cmd --reload第三没有配置chunk_size和gop_cache。chunk_size默认是4096也行但如果你的视频码率很高可以调大。gop_cache我强烈建议打开不然后面拉流测试大概率看到黑屏。配置完成后先测试语法再重启nginx -t sudo systemctl reload nginx # 或者 sudo nginx -s reload验证服务器起来了没ss -tlnp | grep 1935 # 看到 LISTEN 状态就说明RTMP服务已经在线3.3 用ffmpeg推流参数背后的含义有了服务器推流这个动作其实就是一个ffmpeg命令的事。先拿一个本地视频文件推流测试ffmpeg -re -i /path/to/video.mp4 -c copy -f flv rtmp://127.0.0.1/live/test这里有两个参数值得展开说。-re是让ffmpeg按原视频的帧率速度读取文件不是极速读完而是模拟直播时的实时推送节奏没有它会瞬间把整个视频推完测试就失去了意义。-c copy是流复制直接拷贝文件的音视频编码数据不做转码CPU占用极低适合网络和链路测试。如果是从摄像头采集推流就要转码了命令变成ffmpeg -f v4l2 -framerate 30 -video_size 1280x720 -i /dev/video0 \ -c:v libx264 -preset veryfast -tune zerolatency \ -c:a aac -f flv rtmp://127.0.0.1/live/test注意Linux下摄像头设备是/dev/video0macOS上类似的是0:0这种设备标识。编码参数方面preset veryfast走速度路线tune zerolatency是针对低延迟场景优化这两个参数在直播推流里几乎是标配。推流开始后控制台会滚动输出帧率、码率、时间戳这些信息。看到frame和fps稳定增长说明推流基本没问题了。3.4 用ffplay和OBS验证整条链路拉流测试用ffplay一行命令就能搞定ffplay rtmp://127.0.0.1/live/test弹出窗口看到画面、听到声音全链路就通了。这时候再用ffprobe看一眼流信息会更放心ffprobe rtmp://127.0.0.1/live/test # 输出里能看到视频编码、分辨率、音频采样率等信息用OBS走一遍推流也很方便适合模拟真实主播端的配置。在OBS设置里选择自定义流媒体服务器服务器地址填rtmp://你的IP/live串流密钥填一个自定义的流名比如test点开始推流即可。关键是要把IP换成实际地址注意OBS推流一般不用加完整的流路径因为OBS会把密钥自动拼接上去。调试过程中如果遇到问题可以参考这张排查表现象可能原因解决办法握手超时、连接拒绝防火墙未放行1935放行端口检查监听状态推流端连接被拒nginx rtmp配置错误nginx -t 检查配置重启服务播放端黑屏/花屏gop_cache未开启rtmp application里设置gop_cache on音画不同步推流端时间戳问题检查原文件时间戳或统一编码器时间基准推流画面卡顿上传带宽不够降低码率检查网络4. 网上流传的RTMP测试地址能测出什么又测不出什么4.1 为什么大家都搜RTMP测试地址搜索引擎里rtmp测试地址这个关键词热度一直很高我太能理解这种需求了。做流媒体开发或者直播运维的人经常会遇到一个尴尬场景要验证播放器SDK、测试解码器、确认网络环境到某些公网节点通不通手头却没有一个稳定的直播流。从零搭一个本地推流环境当然最好但很多情况下你没有服务器权限或者只是临时验证一下播放端的兼容性这时候公共测试地址就是最快的选择。网上流传比较广的一个测试地址是rtmp://camlive.iqilu.com/live/streamdelivery1这是某个省级媒体平台开放出来的公共直播流被各大技术社区当作RTMP联调测试用。类似的公共流其实有不少但很多都因为服务器下线或访问控制策略变更而失效了能稳定存活的不多。4.2 用公共测试地址做连通性验证拿到测试地址后第一件事是验证它是否还活着。用ffprobe做一次检查流信息的操作最直接ffprobe rtmp://camlive.iqilu.com/live/streamdelivery1如果输出里能看到Stream #0:0和Stream #0:1这样的音视频流信息说明这个地址还能用。如果卡在连接阶段或者报错Timeout/Handshake failed那就要换一个测试地址了。确认流存活后再去验证播放端。ffplay直接拉流试播ffplay rtmp://camlive.iqilu.com/live/streamdelivery1播放器弹出窗口、画面正常渲染说明你的网络出口到该CDN节点的链路是通的RTMP握手、流交互、音视频解码流程全部正常。如果你在做播放器SDK的兼容性测试这就是一个很好的回归验证环境。4.3 公共测试地址的局限但这类公共地址的局限也很明显我建议你别对它期望太高。它只能测拉流不能测推流。公共流是媒体平台推给所有人的你没有权限往这个地址推数据所以推流测试必须自己搭服务器。它不稳定随时可能失效。公共流是媒体平台顺带开放的不是专门为开发者准备的测试服务。人家调整线路、改编码、下架节目你的测试环境就跟着挂了。我见过不少人把公共地址写死在项目配置里结果某天线上播放器全部连不上排查半天才发现是公共测试地址失效了。它测不了延迟和弱网场景。公共流的服务器距离不可控你测出的延迟值没有参考意义。真要测自己的播放器指标还是得用自己的源。所以我的建议是公共测试地址用于临时冒烟验证可以但凡是正经的开发调试老老实实搭一套本地nginx-rtmp环境成本低、可控性强、能复现问题这才是正道。我自己在团队里就要求所有客户端开发必须用本地环境做联调公共地址只允许在给外部演示的时候用一下。5. RTMP的短板和它的替代者们5.1 RTMP真正的短板在哪里不管你多喜欢RTMP它的短板是客观存在的。第一个也是最大的问题基于TCP弱网表现差。TCP的拥塞控制和丢包重传机制在弱网环境下会导致延迟急剧升高甚至卡顿。手机网络不稳的时候RTMP推流经常出现断断续续的情况就是这个原因。相比之下基于UDP的SRT和WebRTC在抗丢包方面要灵活得多。第二个问题是播放端兼容性差。浏览器已经不支持RTMP原生播放了你需要把流转成HTTP-FLV配合flv.js或者HLS才能在网页上看。移动端App倒是可以用原生RTMP播放但也不是所有播放器都内置了RTMP解码器集成成本不低。第三个问题是默认不带加密。RTMP是明文协议RTMPS相当于加了TLS的RTMP但不是所有服务器和客户端都支持。做直播特别是付费直播时链路加密是刚需RTMPS能覆盖一部分场景但生态不如标准的HTTPS拉流那么无缝。还有一个小坑1935端口在很多企业内网和校园网络里被防火墙策略挡着遇到这种情况想走RTMP就得用RTMPT封装在80端口绕行但RTMPT的性能损耗比较明显也不是长久之计。5.2 主流替代协议对照这些年在直播分发侧HLS和HTTP-FLV已经占据了主流位置在超低延迟场景WebRTC异军突起在弱网传输场景SRT表现亮眼。它们的核心差异可以用一张表说清楚协议传输层延迟浏览器兼容适用场景RTMPTCP2-5秒差推流建链、低延迟拉流需转封装HTTP-FLVTCP/HTTP2-5秒好flv.jsWeb端低延迟直播播放HLSHTTP6-30秒原生支持大规模点播/直播、兼容性优先WebRTCUDP1秒原生支持互动连麦、实时音视频会议SRTUDP与RTMP相近一般跨国传输、弱网环境、公网矩阵注意几个容易理解的为什么HLS延迟高是因为切片和文件下载的机制决定的但它的好处是可以通过普通HTTP静态文件服务分发天然过CDN、天然支持缓存手机浏览器和系统播放器都能直接播大规模直播场景优势巨大。WebRTC走UDP能实现秒开和毫秒级互动但它要求公网协商ICE通道架设和维护的复杂度比RTMP高一个量级目前主要用在连麦和会议场景。5.3 我的选型建议具体到不同场景我通常的建议是推流侧继续用RTMP没有之一。除非你在做纯Web端的互动直播那直接用WebRTC推流否则RTMP推流的稳定性和兼容性依然是最好的选择。国内云厂商直播产品的标准接入方式也都保留了RTMP。拉流侧优先根据播放端选择。Web端追求低延迟用HTTP-FLVflv.js追求兼容性用HLSApp端直接用RTMP协议或者转成HLS都行如果做的是互动连麦级别的低延迟只有WebRTC能接得住。弱网传输往SRT方向考虑。这协议在公网跨地区传输、4G/5G弱网环境下比RTMP抗造得多很多硬件厂商和实验性项目已经在用SRT做推流了。但生态成熟度和接入便捷性目前还不如RTMP。**我个人的体会是**选协议永远不是要找一个最好的而是要找一个当前场景最省事、长期最稳定的。RTMP不先进也不优雅但它足够成熟、足够通用在推流端依然是事实标准。与其天天纠结要不要换新协议不如先把RTMP链路玩明白——你懂了TCP长连接、懂了Chunk流、懂了AMF命令回头再去学WebRTC或者SRT会发现很多底层思路是相通的学习成本低一大截。最后再分享一个小技巧抓RTMP包调试的时候可以在Wireshark里打开Analysis-Decode As协议选RTMPWireshark会把消息类型、事务ID、AMF命令全部解析成可读形式。我第一次用这招定位推流失败问题时不到十分钟就从抓包里看出了是app名称不匹配导致的连接被拒。做流媒体调试学会看抓包有时候比看日志效率高得多。
返回列表