全民K歌推流直播Web实践
背景
2020年受到疫情的影响,大众减少了线下娱乐,将更多的时间投入到了线上活动,直播行业迎来了一个小爆发,主播注册数量与线上观众不断增长。同时,在线直播演唱作为一种全新的演出模式,受到广大网友的好评,4月以来TME承办了近20场明星在线演唱会。
随着站外直播场景业务需求逐步增多,K歌直播旧的业务代码无法满足不断增长的产品功能需求和用户体验需求。在此背景下,Web侧急需为推流直播业务提供更加可靠的技术支持。
HLS和HTTP FLV
目前K歌Web使用的直播流格式主要以HLS直播流为主。HLS(HTTP Live Streaming) 是由Apple提出的HTTP流媒体传输协议。其工作原理是服务端把整个流切分成一片片小的媒体流片段,客户端通过下载一个包含源数据的extended M3U(m3u8)playlist文件用于寻找可用的媒体流,随后开始下载格式为MPEG-TS的媒体片段,整体的流程图如下:
移动端 iOS 和 Android 都支持HLS协议,做好视频采集、视频流推流服务之后,便可以直接在H5页面通过 video 标签播放直播流。
<video> <source id="source" src="http://xxxx/video.m3u8" type="application/x-mpegURL" /></video>
HLS局限性
HLS流最大的局限性主要有:
- 在服务端对直播流进行切分处理之后客户端才能拉取到数据,所以整体延迟较高,通常延迟可达到20~30s。
- 由于直播流分片拉取,所以客户端需要频繁地进行http请求,可能会导致播放卡顿。
- 通过video标签进行hls播放,无法很好的在业务层进行定制化操作以及数据监控。
因此,前端还需支持更低延迟也更稳定的直播流,FLV直播流能满足当下需求。
HTTP FLV是将RTMP封装在HTTP协议之上的用于传输flv格式的协议,其传输的http_flv是一个文件大小无上限的http流的文件。相较于HLS,HTTP FLV无需在服务端对直播流进行切片处理,所以具有低延迟的优势,平均延迟只有1~2s。此外,HTTP FLV还具有一定程度上避免防火墙干扰、兼容302跳转、支持HTTPS通道加密等优势。
HTTP FLV局限性
既然HTTP FLV相较于HLS有如此大的优势,直接使用HTTP FLV不是更好吗?
由于HTTP FLV的支持需要依赖于MSE(Media Source Extensions) API 和 fetch+stream API ,而iOS浏览器不支持MSE API,所以flv流无法直接在iOS端播放。
FLV Web支持方案
通过原生的video标签来进行flv流格式播放的方式不可行,是否有其他方法来支持flv在移动端的播放呢?调研发现,现阶段支持移动端播放flv格式的js sdk主要有以下几种,对比其在移动端的能力如下表所示:
| | flv.js | NodePlayer(非开源) | WXInlinePlayer | ffmepgplayer(now直播自研) |
---|---|---|---|---|---|
苹果 | safari | N | Y | Y | Y |
K歌web | N | Y | Y | Y | |
QQ浏览器 | N | N | Y | Y | |
手Q | N | N | Y | Y | |
微信 | N | N | Y | Y | |
安卓 | 默认浏览器 | N | N | Y | Y |
K歌web | N | Y | Y | Y | |
QQ浏览器 | Y | Y | Y | Y | |
手Q | Y | Y | Y | Y | |
微信 | Y | Y | Y | Y |
flv.js工作原理为:通过mse将flv流转码成fmp4给video进行播放,由于其本质还需依赖MSE,所以无法支持iOS。
NodePlayer.js 工作原理:通过ASM.js软解码H.264+AAC流,利用WebGL视频渲染,WebAudio音频播放来实现移动端flv直播流播放。
WXInlinePlayer与ffmpeg-player工作原理基本相似:
- 数据流获取层:利用 XMLHttpRequest 或者 Fetch Api 从云端的HTTP-FLV 流媒体获取直播流数据。
- WASM解码层:利用web worker开启子线程,通过获取视频流metaData信息之后,对视频进行解封装,并将视频流格式化为YUV,将音频流格式化为PCM,进而将转换好的数据回调给渲染层。
- 渲染层:渲染层将获取到的视频数据与音频数据存入渲染缓存池中,WebGL 在 Canvas 上绘制视频画面,同时通过 Web Audio API 播放音频。
WXInlinePlayer和now直播的ffmepg player两者最大的区别在于其针对的目标有所不同。
- 解码库依赖方面
ffmepg player是在web侧复用FFmpeg中的H.265解码模块实现前端解码,整套解码器在依赖h264\acc\flv的同时还依赖了hevc,所以ffmepg player同时支持了H.265与H.264两种格式的视频流。但正因如此,ffmepg player输出的wasm文件体量较大,约1.3M。
WXInlinePlayer提供了可选的三套构建方案:baseline(不使用OpenH264),all(在baseline基础上支持OpenH264)以及h265 (基于OpenH265),开发者可根据需求来选择不同的解码器,最终输出文件体量视构建方案而定。基于OpenH264的解码文件约800K。
- 解码算法方面
WXInlinePlayer使用的主要是SWSFASTBILINEAR 算法。
ffmepg player使用的则是SWSBICUBIC 算法。两者具体算法的差异对比可以参考雷神的性能测试文章《ffmpeg中的swsscale算法性能测试》
- 文件部署方面
ffmepg player从网络性能的角度考虑,将wasm和js胶水代码部署在cdn上,通过fetch 请求web worker cdn的方式来实现项目对sdk的访问。
WXInlinePlayer相对来说更"简单粗暴",直接将wasm和胶水代码转成base64的形式注入到sdk代码中,最终输出为单个的js文件。
总结来说,要想让web端可以很好的支持flv,实现多平台的支持http-flv流播放器,主要开发思路如下:
通过wasm来编译解码器从而实现在前端进行flv格式的解码,输出YUV视频数据以及PCM音频数据,利用webgl渲染YUV,Web Audio API播放PCM音频最终实现flv播放。
技术流程图如下:
为此,K歌web团队封装了一套同时支持hls和flv视频流播放的kg-player SDK。
kg-player SDK
我们对WXInlinePlayer 以及腾讯自研的TCPlayer进行了源码优化,解决了iOS端画音不同步、移动端系统适配以及webgl渲染旋转等问题,将其封装成kg-player SDK。
例如,WXInlinePlayer使用的OpenH264解码模块,在iOS端和Android端有差异化的表现,在iOS端会对首帧进行额外解码,导致第二段音频每次都会包含第一段音频,从而使得画音不同步。对此,我们在逻辑层对iOS端的第一段音频进行记录,在audioSrc.start播放时减去第一段时长,使音频时间轴整体前偏移首段音频的长度,最终使音视频保持同步。
再例如,由于K歌iOS主播端的推流没有对直播源进行旋转,导致原本竖屏的直播流为横屏状态。对此,我们在webgl渲染层面对其进行逻辑封装,通过视频的宽高信息对直播流进行检测,通过矩阵变换视频流进行旋转。
优化后的flv解析流程如下:
kg-player SDK 能够同时支持flv和hls两种格式的视频流播放,支持多码率流地址传入以及流地址切换。通过兼容性测试发现,hls支持大部分移动端设备。而flv由于需要web端支持WASM所以仅支持iOS 12及以上系统, Android 5及以上系统。对此我们在sdk初始化阶段会对当前使用的机型进行判断,针对不同机型进行适配性降级,从而保证直播流能够正常播放。此外,SDK能自动检测浏览器的播放性能,当页面发生卡顿或播放性能较糟糕时(如整体页面fps低于24),会触发降级回调事件,由业务层逻辑判断是否降级到低码率流或从flv切回hls流来保证整体播放性能。
技术架构
K歌Web侧推流直播的整体技术架构图如下:
整个技术架构分为三个模块,基于kg-player sdk的直播流播放模块,基于kg-im sdk的直播间消息模块以及礼物动画渲染模块。
kg-im sdk通过轮询的方式获取直播间的互动消息(礼物消息,评论消息,喇叭消息等),并根据消息池中消息的总数来弹性控制每次拉取消息间隔的时间,进而保证前端能够及时高效拉取消息且不会造成额外的网络开销。同时SDK会对消息进行二次封装,去掉冗余字段,封装成前端更易识别的消息结构体,减少前端业务代码。kg-gift 同时支持视频动画,webgl动画以及传统的css动画来满足直播业务中较为复杂的礼物动画需求。
直播性能数据
hls流与flv流各有优劣,在接入kg-player sdk之后,具体的性能对比数据如下:
首帧渲染时间
| FLV | HLS |
---|---|---|
首帧的平均耗时(ms) | 3421.786 | 1075.669 |
由于flv播放需要通过前端的WASM进行软解码,所以从第一帧传输到渲染上屏需要的时间要略长于hls流播放。
播放卡顿率
在kg-player内会设置心跳检测,其核心逻辑是通过设置timeInterval来进行心跳记录,心跳间隔2秒,2秒内如果出现当前播放时间和上一次心跳时的播放时间一致的情况则标记当前出现卡顿并进行一次"卡顿"上报,否则则进行一次"流畅"上报。
注:Web页面在某些交互场景如微信端点击右上角或播放时切后台等操作,会导致心跳计数误差,上报的卡顿率要略大于实际卡顿率
可以看到FLV流由于不需要切片请求,使得整体的拉流更为流畅,同样的网络状况下,其播放的卡顿率要远低于HLS。
降级
kg-player SDK在检测到页面出现卡顿时会对播放流进行降级,从720p分辨率的流降级到540p,或者从flv降级到hls,由于flv流播放依赖于WebGL渲染,而WebGL渲染又依赖手机硬件性能,所以对于低端机会出现降级情况。
可以看到,整体降级率约为11.49%,这也表明flv在web端的支持有一定的优化空间。
进一步优化
通过wasm进行软解码并通过webgl进行渲染的技术方案,虽然能够很好的解决移动端播放不了flv直播流的问题,但是由于软解码和webgl渲染十分依赖硬件性能,导致很多中低端机型无法很好的播放flv流。
通过对比发现,iOS系统能够很好的解码并播放720p的直播流视频,但是Android端只能勉强支持540p的直播流视频,且在页面进行其他渲染(礼物动画,喇叭广播动画,评论滚动动画)时,由于硬件性能跟不上,Android端有一部分机型会降级到hls直播流,Android端的降级率接近iOS的三倍。
针对上述问题,我们对kg-player SDK进行了进一步的优化,对于Android端这一类性能不稳定的系统,我们回归到通过MSE的方式进行flv直播流的加载,利用现有的sdk如flv.js或者videojs来进行flv直播流加载。使用优化策略后,整体降级率大幅降低,对比图如下:
且优化后无需使用WASM进行软解码,所以首帧平均耗时也得到了降低:
| iOS | Android |
---|---|---|
FLV首帧的平均耗时(ms) | 3181.444 | 981.139 |
除此之外,播放卡顿方面也有进一步的优化空间,通过实时监控页面的卡顿率,我们可以自适应的降级到后台提供的更低分辨率的直播流,使得直播源能够自适应用户网络环境。
上层业务支持
利用推流直播技术基础,我们能够较好的支撑现有的直播业务。目前主要的落地场景包括K歌直播站外H5分享页(日均PV 30W左右)以及TME live直播(场均PV50W左右),在保证前端渲染性能的同时也能给用户提供很好的交互体验。
总结与展望
通过对现有的直播技术方案进行封装优化,我们实现了满足K歌业务需求的技术框架,在此基础上能够很好的支持各个直播项目,也提升了开发效率。
未来我们还将支持更多的Web直播业务场景例如直播歌房等,而K歌直播技术架构也将会不断完善,从整体性能以及用户体验上出发,不断进行优化以便更好的支撑起更多更复杂的直播项目。
整个技术架构的sdk代码也在优化封装中,功能稳定成熟之后也将尝试开源。
原文地址
本文地址:https://blog.csdn.net/colorfulyan/article/details/107251295