YAOTU INSIGHTS

音视频协议选型:RTMP、HLS、HTTP-FLV、WebRTC对比与接入

音视频协议选型:RTMP、HLS、HTTP-FLV、WebRTC对比与接入
1. 做了几个音视频项目之后发现协议选择才是第一个分水岭前一阵帮一个客户对接萤石开放平台的音视频能力具体需求很简单一堆网络摄像头要把实时画面接到一个跨平台的业务系统里PC端、手机端、Web端都要看得到。听起来不难但项目从启动到第一帧画面真正稳定跑起来比预期多花了两周。回头看最耗时间的不是PPT上写的那行“对接实现视频查看功能”而是在第一周反复换协议、改方案、重新联调。几乎所有坑都源自同一个问题协议选型没有在一开始想清楚。这篇文章想把这些经验和判断逻辑整理出来重点围绕萤石开放平台提供的几种标准协议——RTMP、HLS、HTTP-FLV、WebRTC聊聊它们的差异、取舍、接入要点和实际使用中容易踩的坑。适合两类人看一类是刚接到类似需求正在纠结“到底该用哪个协议”的开发者另一类是已经用某种协议把功能跑通了但被延迟、并发、兼容性问题折磨得想重构的同行。先给一个结论没有“最好的协议”只有“当前需求下最不差的协议”。选型不是看技术榜单而是把延迟、并发、终端类型、运维成本这几个变量放到一起做权衡。下面我把每个协议的性质、萤石平台侧的开放方式和我的实测数据摆出来你拿着自己的需求对号入座即可。2. 四个协议的本质差别延迟、兼容性和并发逻辑完全不同音视频传输链路从摄像头采集到屏幕渲染协议决定的是数据怎么封、怎么传、怎么解。萤石开放平台在服务端已经帮你把编码、封装、分发做了对外开放的协议拉流地址就像四个入口数据源头一样但每条路的传输方式和最终体验完全不同。2.1 RTMP直播的老将推流端的主力RTMP是Macromedia在Flash时代定义的实时消息传输协议基于TCP长连接数据在传输过程中按消息块chunk拆分。它最核心的特点是延迟低端到端一般在1到3秒当年互动直播大多是靠它撑起来的。但这个协议有个致命短板对浏览器原生播放不友好传统上必须依赖Flash播放器。Flash退出历史舞台之后纯浏览器播放RTMP基本死路一条。现在RTMP在多数项目里只干一件事——作为推流端协议把摄像头、编码器的流推到服务器再由服务器转成其他协议分发给播放端。对接萤石开放平台时如果你要从设备端主动推流到自己的服务器做二次分发RTMP仍然是比较稳的推流选择因为长期演化的生态里服务端对RTMP接收端的支持最成熟几乎所有流媒体服务都原生接得住。但如果你是想在Web页面直接放RTMP地址趁早打消念头选型上它只能当上游不能当终端交付。2.2 HLS苹果发明的切片方案兼容性最强但延迟偏高HLSHTTP Live Streaming是Apple主导的流媒体协议核心思想是“边下边播”服务端把连续的视频流切成一个个小的TS切片文件播放器通过不断请求更新的m3u8索引文件来找到最新切片并连续播放。它跑在标准HTTP之上所以防火墙友好、CDN亲和力极高这是它最大的工程优势。也正因如此HLS的延迟天然比较高。切片长度一般是2到6秒加上播放器自身缓冲、索引请求间隔实际端到端延迟通常在6到30秒这个区间。你对着监控画面喊一声对方在另一端半天才有反应这种体验在“实时互动”场景下是不能忍的但在“看录像、看现场情况、大并发直播”场景里完全够用。我实际测试萤石平台的HLS拉流在局域网内延迟大约3到8秒公网链路下大概8到15秒取决于网络抖动和播放器缓冲策略。**适合的场景很明确终端类型杂、网络环境不可控、对实时性要求不高但要保证“能放出来”。**iOS的Safari、Android的ExoPlayer、Web的hls.js都对HLS支持良好基本是兼容性兜底方案。2.3 HTTP-FLV低延迟、易接入Web播放的务实选择FLV本来是一个封装格式而HTTP-FLV就是把FLV数据封装在HTTP响应流里播放器从头到尾读一个HTTP流持续解析数据帧。它属于“渐进式流式传输”服务端把流以块的形式推给播放器播放器可以一边收一边播延迟一般在1到5秒。对比HLSHTTP-FLV的延迟优势明显对比RTMP它又能被现代播放器方案直接支持比如用WASM技术解码flv.js这是很长一段时间里低延迟Web直播的标准做法。缺点是FLV格式本身只支持H.264和AAC编码如果你后续要上HEVC、AV1这条路就要打折扣。萤石开放平台也提供了FLV拉流地址我这边实测在桌面浏览器用flv.js播放延迟稳定在2到4秒画面起播时间大约1到3秒比HLS的起播体验爽快很多。一句话能接受写几行Web播放器代码、又要低延迟的场景HTTP-FLV非常合适。2.4 WebRTC实时通信天花板延迟最低但环境要求最苛刻WebRTCWeb Real-Time Communication是为实时通信设计的协议集合包含媒体传输、加密、NAT穿透等一整套机制。它的目标不是“播流”而是“通话级”的实时交互端到端延迟能做到500毫秒以内实测萤石平台的WebRTC拉流在稳定网络下延迟约为1秒左右几乎可以认为是实时的。但低延迟的代价是复杂度。WebRTC需要信令协商、ICE穿透、DTLS加密对网络环境、HTTPS页面、浏览器兼容性都有要求。虽然现代浏览器基本都原生支持WebRTC但它在复杂的公网NAT环境下经常出现连接建立失败、媒体协商超时等问题尤其在移动端弱网下成功率会明显下降。**选WebRTC的场景非常明确对实时性有硬需求比如双向对讲、远程操控、需要近乎无延时的监控画面同时客户端环境可控自己开发的App或授权浏览器。**如果你只是做一个大屏展示墙、几十路画面轮播没必要选WebRTC——低延迟的红利无法体现却要承担穿透和协商的复杂度。2.5 四个协议放一起看维度RTMPHLSHTTP-FLVWebRTC端到端延迟1-3秒6-30秒1-5秒0.2-1秒Web原生播放不支持需Flash支持hls.js/Safari原生支持flv.js浏览器原生移动端支持差好较好较好防火墙壁垒低长连接高HTTP高HTTP需要穿透协商CDN/分发友好度一般极好好一般协议复杂度中低低高典型使用阶段推流源全终端兼容交付低延迟Web播放实时互动这张表可以贴在项目文档第一页。每次纠结选型时先问两个问题延迟能不能接受终端到底有哪些。答案基本就在表格的交叉点上。3. 萤石开放平台的音频视频协议对接形态不只是给一个地址那么简单说清楚协议差异后再看萤石平台具体怎么把这些协议开放出来。很多人第一次对接会以为“拿个播放地址直接塞进播放器就行”实际上整个链路涉及鉴权、地址拼接、有效期控制、码流选择等环节任何一个没搞对画面都出不来。3.1 accessToken机制所有拉流地址的前置条件萤石开放平台的安全模型里accessToken是访问资源的钥匙。你可以把它理解为“临时通行证”控制台创建应用后用AppKey和Secret去换取token这个token默认有效期一般是24小时过期后需要重新获取或刷新。拉流地址里往往直接嵌着token——比如直播地址的构成是“开放平台域名 特定路径 accessToken 设备序列号 通道号 其他参数”。这意味着token一旦泄露别人拿着就能看你的画面token一旦过期你辛苦拼好的URL立刻失效。实际项目中我习惯建一层“代理刷新”机制不在前端暴露真实token。由后端定时刷新accessToken并缓存再按需拼装播放地址下发给前端前端只拿到短期有效的完整URL。这样既避免token在前端暴露也能统一管理有效期。很多人的第一坑就是嫌麻烦把token硬编码在页面上结果第二天视频全黑查半天才发现是token过期了。3.2 不同协议地址的形态差异我以今年接入的实际经验说一下大致形态具体拼接规则以平台控制台文档为准不同版本可能有调整HLS地址一般是.m3u8结尾的URL例如https://hls.某域名.com/openlive/{token}.m3u8的变体播放器直接请求这个索引文件再拉取切片。HTTP-FLV地址是.flv结尾的URL例如https://flv.某域名.com/openlive/{token}.flvflv.js的flvjs.createPlayer({type:flv, url:...})直接可以读。RTMP地址以rtmp://开头主要用于推流到自建服务器或导入到自研播放器。WebRTC地址不是简单URL通常需要先请求一个“播放会话创建”接口换取SDP信息再做协商播放。这一点很容易被忽略同一路监控画面在平台上可以拿到不同协议的播放地址但码流类型主码流/子码流可能对应不同通道参数。主码流清晰度高、码率高适合大屏展示子码流分辨率低、码率低适合多路小窗预览。接入时如果默认在主码流上开很多路带宽会很快被打满。这里有具体的量化经验720P主码流一路按2到3Mbps算10路就是20到30Mbps的上行消耗很多客户的内网带宽未必撑得住所以多路场景优先切子码流。3.3 平台侧的分发调度能力另一个容易被低估的点是萤石平台本身有边缘节点和分发调度能力不同协议走的调度路径不完全一样。HLS因为切片模式天然适合CDN缓存多级节点下对源站压力小HTTP-FLV是长连接流式分发节点间的带宽占用和控制逻辑更敏感。如果你的项目要支撑很大的并发观看比如几百路同时访问HLS在服务端压力上会明显优于HTTP-FLV和RTMP。这个判断逻辑很实际协议选型不只是播放端体验问题也是服务端成本问题。低延迟协议往往意味着更高的长连接占用、更差的缓存能力、更大的节点压力。没有实时性要求的场景硬上低延迟协议等同拿钱换了一个用户感知不明显的指标。4. 从需求反推协议一个模拟项目X的选型全过程技术参数说得再多不如跟着一个具体的项目走一遍。这里用一个完全虚构的案例来演示决策过程某公司要给自己园区的一套安防监控系统加“Web实时预览”功能约50路摄像头使用方有三个群体——大屏值班人员3台大屏、管理层电脑约20台PC、外出人员的手机手机浏览器不装App。接手时我第一反应是WebRTC毕竟监控画面强调实时而且平台也支持。但把需求列成清单后结论完全变了。4.1 需求维度拆解先按四个维度给需求打分延迟要求值班人员主要看“有没有异常”5秒以上的延迟完全可以接受对讲功能暂不涉及。延迟权重低。终端兼容PC浏览器、手机浏览器都要能放不装插件不能强制App。兼容性权重极高。并发规模50路摄像头同时观看人数最多20人左右。并发规模中等不需要海量CDN分发但对服务器破连接稳定性有要求。运维成本客户没有专职流媒体运维不想自建流媒体服务希望“尽量少出问题”。这一轮WebRTC就被否了单看延迟它最强但它要求HTTPS、信令服务、穿透协调而且20个观众分布在不同的网络环境移动端WebRTC在弱网下的表现很不稳定。为了一个“用不上”的低延迟引入一堆不可控因素不划算。4.2 第二轮在HLS和HTTP-FLV之间选剩下两个候选分歧在延迟和兼容性。如果纯选HLS兼容性最好Safari抖音直接放m3u8但延迟可能到8到20秒而且hls.js在部分安卓WebView上有内存方面的问题长时间挂着容易卡顿得做定时重连策略。如果纯选HTTP-FLV延迟2到4秒非常舒服桌面Chrome用flv.js表现得很好但iOS Safari不原生支持flv也要引入flv.js的WASM方案起播速度在移动端偶尔会有4到5秒的黑屏等待。最后的方案是双协议动态选择PC大屏和大部分手机Chrome浏览器优先用HTTP-FLV拿低延迟遇到iOS Safari或需要跨网络高兼容的场合降级到HLS。这个方案不复杂播放器侧写一个协议探测函数先尝试flv拉流失败自动切m3u8即可。4.3 最终落地效果实施后PC端延迟约3秒手机Safari延迟约8秒没有一个“完全打不开”的终端。值班大屏用得最满意因为他们确实需要“看见人走过去”而不是“看录像”管理层的手机偶尔看一次现场8秒延迟根本感知不到。这个案例想表达的核心选型逻辑是三个问题延迟指标是不是产品核心体验是才值得为低延迟引入复杂度。终端类型是谁说了算无法控制的浏览器环境先保兼容自研App环境优先低延迟。你打算花多少精力维护协议越复杂排障成本越高小团队不要贪新技术。顺着这三个问题走大部分项目的协议选型都能在半个小时内定下来而不是靠感觉。5. 接入实操从获取凭证到画面首帧播放选型定了之后接入过程是有固定套路的。我以Web接入HTTP-FLV为例把完整链路走一遍这是很多人卡壳的地方。5.1 创建应用并换取accessToken第一步是在萤石开放平台控制台创建应用拿到AppKey和Secret。这个环节常被忽略的一个细节是密钥信息只在创建时完整展示一次后续只能重置不会让你再次查看。我见过有人把Secret截图存聊天记录里结果复制粘贴时多了一个空格鉴权请求一直报错。正确做法是存到服务器环境变量或密钥管理配置中不要出现在前端代码里。换取accessToken的接口调用我用一段极简伪代码演示一下流程import requests app_key 你的AppKey secret 你的Secret resp requests.post( https://open.ys7.com/api/lapp/token/get, json{appKey: app_key, appSecret: secret}, timeout10 ) data resp.json() access_token data[data][accessToken] # 默认有效期为24小时自行在服务端缓存并定时刷新这里有几个必须注意的响应状态接口返回码是200不代表业务成功很多开放平台接口有独立的业务码200只是HTTP层面通要检查业务数据里的code字段再做后续逻辑。我看到不少初学者只判断resp.status_code 200结果拿着空的token拼了半天的地址白白浪费一下午。5.2 获取设备信息并拼接播放地址拿到token后需要根据设备序列号和通道号去查询播放地址。萤石的播放地址有两个关键参数deviceSerial设备序列号和channelNo通道号多路摄像头从1开始。获取直播地址接口会返回多个协议类型的地址你也可以根据需要自己拼。由于地址可能包含token和有效期参数实际开发中我的建议是后端从接口返回里拿地址不要前端手写拼接。原因是地址格式可能在平台版本更新中微调你手写的拼接逻辑今天能用明天平台调整参数名就失效。而接口返回的地址大概率是平台最认可、最稳定的形态。拿到地址后验证环节也很关键先用VLC或者ffplay试播一次确认流能出画面再写到前端代码里。这一步能帮你把“代码问题”和“流问题”快速分开。我见过前端调试了半天最后发现是拉流地址本身在接口返回里断行了多了一个看不见的换行符。5.3 播放器侧的基本接入模式Web端用flv.js播放HTTP-FLV流的代码大致如下import flvjs from flv.js; const videoElement document.getElementById(video); if (flvjs.isSupported()) { const flvPlayer flvjs.createPlayer( { type: flv, url: 你的flv地址, isLive: true }, { enableStashBuffer: true, stashInitialSize: 128 } ); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); }这里值得展开的是isLive: true和enableStashBuffer两个配置。直播流没有结束标志拉流后数据是持续流入的播放器如果不声明isLive: true内部缓冲管理逻辑会按点播去做可能一直缓存不播放画面迟迟不出。stashInitialSize表示内部缓冲的初始大小数值越大起播越慢但抗抖动能力越强弱网环境适当调大低延迟环境调小。默认值一般够用特殊网络再动它。播放断流重连是另一个核心点。HTTP-FLV长连接在网络切换、服务端重启后会断开需要监听MEDIA_ERROR事件和flvPlayer.on(error)回调做自动重连。这个重连必须做指数退避不能每秒钟狂连否则会把服务端打死。我一般用1秒、2秒、4秒、8秒这样的递增策略最多重试5次后提示用户手动刷新。6. 实测中的坑与排障思路这些比协议本身更值得记接入过程中最有价值的往往不是“正常流程”而是那些来回折腾才定位的问题。挑几个我最常遇到的、且跑不掉的坑出来讲按排查链路走一遍你能少走很多弯路。6.1 HLS画面卡顿但没报错问题出在播放器缓冲策略现象HLS流能播但每隔十几秒就卡一下画面恢复后时间轴会跳。排查链路是我第一个完整记录的案例很典型。第一步查看网络请求发现TS切片的加载时间正常第二步查看服务端状态确认没有拉流超限第三步换了一个播放器组件问题消失。这时候才定位到问题根源播放器默认缓冲策略会把所有已请求的切片攒到一定数量后才播而HLS的切片索引更新有一个固定周期攒得越多端到端延迟越大同时旧切片在队列里堆积会引发播放器主动丢帧表现为“卡一下、跳一下”。这个问题的通用解法是把播放器的缓冲时长设短并让播放器按“最新切片”而不是“队列中间切片”播放。用hls.js时可以把maxBufferLength从默认的30秒调到6到10秒liveSyncDurationCount控制在2个切片以内。对监控场景宁可偶尔起播慢一点也不要攒一堆旧数据造成错觉“我看到的到底是哪一刻的画面”6.2 WebRTC连接成功率不稳定先查HTTPS和时钟现象WebRTC在局域网、办公室网络下秒开但在部分公网环境下一直黑屏偶尔能出画面。这个坑排查了整整一天。先确认页面是不是HTTPS——WebRTC的安全策略强制要求加密上下文HTTP页面下getUserMedia等接口会被浏览器禁用信令和数据通道也会出问题。但普遍排查到“页面是HTTPS”时问题依旧存在。继续看浏览器控制台发现ICE候选收集阶段卡住说明UDP穿透失败。WebRTC在复杂NAT下会尝试STUN/TURN如果没有可用的TURN服务器对称型NAT环境基本连不通。这一步很容易被忽略你以为浏览器原生支持WebRTC就是“零成本”实际上跨公网互动必须自建或购买TURN转发服务这是WebRTC方案里隐藏的硬成本。另外还有一个不起眼的坑电脑系统时钟不准。WebRTC的证书和ICE机制依赖时间戳校验系统时间偏了几分钟都会导致握手失败。这个排查点写在WebRTC的规范文档里但在实际运维中几乎没人第一时间想到。6.3 accessToken过期导致画面集体变黑且错误提示不直观这是最常见的线上事故。接入初期我把token固化在服务端内存里没有做主动刷新结果第二天10点整所有画面同时变黑前端播放器报错信息五花八门有的是“网络连接失败”有的是“解码错误”没有一个报“鉴权过期”。排查链路是先用控制台手动换新token拼地址试播能播再用旧token试播报错。这才确认是token过期。这个事的教训是两条一是token必须由后端统一维护加一个“拉流前检查token剩余有效期”的逻辑短于一定时间预先刷新二是播放器的错误处理要区分“流不存在”“鉴权失败”“网络断开”等类型分别给不同提示。不要把所有错误统一弹“播放失败”否则用户看到的就是误导性的黑屏。6.4 多路播放时带宽估算失误导致全部画面一起卡某项目一次接入32路画面全部用的主码流720P每路2.5Mbps左右理论上只需要80Mbps带宽客户说办公室是千兆内网应该没问题。实测却卡成幻灯片。后来抓包发现瓶颈不在内网带宽而在Nginx和播放器之间的并发连接处理。32路HLS同时拉流每路会建立多个并行连接请求TS切片瞬时连接数冲到一两百服务器的连接数限制被打破大量请求排队。这暴露了一个认知误区带宽够不代表并发够协议族的连接模型决定了压力上限。解法也很直接多路场景全部切到子码流分辨率降到D1甚至更低单路码率压到512Kbps以内同时给拉流服务器加连接数限制和HTTP keep-alive优化防止播放器疯狂重建连接。做完了之后32路同时预览CPU占用和延迟都恢复正常。说起最后一点我特别想强调协议选型和参数调优本质上都是在“成本、兼容、延迟”这个三角里做取舍没有一劳永逸的方案。一个项目里先定协议再去处理鉴权、缓冲、并发这些细节点就不会被表象问题拖住。拿你当下的设备列表和终端清单对着上面那张表圈出候选方案基本可以直接写代码了。