前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >K歌礼物视频动画 web 端实践及性能优化回顾

K歌礼物视频动画 web 端实践及性能优化回顾

作者头像
QQ音乐技术团队
发布2020-08-17 11:40:15
2.5K1
发布2020-08-17 11:40:15
举报
文章被收录于专栏:QQ音乐技术团队的专栏

K 歌移动客户端19年在直播间中上线了视频礼物资源动画能力,使用特制的视频资源加通道导出和混合 (基于企鹅电竞vapx方案),支持了细腻的视频动画素材播放渲染,同时解决了直接播放视频背景无法透明的问题。

‍‍‍‍‍‍在随后的新 pc 主播端项目中我们对直播工具进行重构 (主界面 UI 基于 web 完成),礼物动画部分由于当时没有 web 版本的 sdk,为了复用线上已有的动画资源以及和移动端保持对齐的效果,web 端通过 video + canvas/webgl 实现进行了支持。

此文回顾整理一下之前的实现流程与细节。

0. 业务流程

首先基于线上方案,上架一个动画资源的整体的流程为以下几步:

  1. 将多个不同视频样本上传到配置平台,同时填写配置 (类型/方向/尺寸等);
  2. 后台根据配置生成生成礼物编号入库,将视频发到 CDN 上架;
  3. 前端通过后台接口可拉到礼物资源所对应的视频地址与配置参数;
  4. 前端触发播放礼物动画。

下面主要讲解渲染播放方面的实现。

1. 实现逻辑

从方案和动画资源来看,为了解决背景透明的问题,视频文件都包含了两个部分:原动画部分以及单独导出的 alpha 通道。只是尺寸和方向不同。

因此逐帧将两个部分的 rgb 分别取出,进行通道混合,就能实现透明背景的画面。 具体来讲,假设资源在某一帧某一点的 rgb 分别为: 原片部分: rgb(R, G, B) alpha部分: rgb(A, A, A) 混合后的动画相应位置就是: rgba(R, G, B, A)

2. 最简方案

首先视频一般用 <video> 播放。结合上面这个角度讲,自然先想到了使用 canvas:让 video 隐藏播放,同时在播放过程中逐帧 drawImage 到画布,读取 ImageData,按照位置取出两部分,混合后重新 putImageData 显示。

共使用到两个 canvas 画布,一个用来离屏读写 imageData, 计算后放到另一个真实看到的画布。

这样第一版就快速实现了。单个 demo 来看是 Ok 的。

但是接下来仔细测试,还有不少优化空间:

3. 加载问题

首先尝试多个动画同时渲染,调低网速,会发现动画跟随缓冲而卡顿。(这里为了方便实验关闭了缓存)

从 network 来看,同时加载播放多个线上视频,并行占用带宽,播放缓冲会导致 video 暂停,实际结果就是 fps下降了。礼物动画这种场景本身不应该出现播放中的等待。因此需要支持加载完整个视频后再本地播放。

这里改为使用 xhr2 将视频完全下载后转为 blob 再放到 video 让其能够一次顺畅播完。

修改后的效果。整体首次播放比刚刚要顺畅了。

但也有代价,就是增加了加载准备时间。后续可以通过离线缓存和空闲时预加载来弥补和提升。

视频动画资源通常很大,单个在2-5m左右甚至更多,一些高频礼物如果实时下载延迟会比较大,没有缓存反复下载也会导致带宽消耗浪费。因此也加上了 service worker 进行资源的持久化。策略使用为 CacheFirst (基于workbox)。冷启动空闲时也可以手动预加载部分资源。

4. CPU消耗

这时继续再多增加同屏个数来测试,下面翻一倍增加到 8 个,同时反复多次循环重复播放,发现性能大幅下降了,非常卡顿。

重复播放时资源都有了,这次肯定不是加载问题。这时打开 performance monitor,发现 cpu 消耗非常高,基本都是 100%。

于是通过录制 performance 来分析,发现瓶颈主要在 canvas 的 getImageData / drawImage 以及 pixelData 的遍历计算上。这里对 CPU 的消耗太高了。 

这里 demo 单个视频是 1440x1152,等于每一帧要 get 出 6635520 个 pixelData (pixel * rgba)。遍历计算 1658880 次结果色值。n个动画再乘以n,计算量非常大,导致高负载,fps也相应降低。 

另外这里高频的绘图场景,直觉上应该是 GPU 的长项才对。但通过系统监控看到GPU在打开前后负载没太大的变化 (在20-30%间波动)。能否想办法发挥 GPU 的能力?

因此重新思考方案,看能否找到其他合适的方案可以代替 ImageData 操作和计算。

5. 更换 WebGL

按照前面的设想 (尝试将消耗转移和利用 GPU),于是考虑使用 WebGL 来看看能否实现。

理论上就是每帧两个部分的对应区域叠加混合。刚开始凭直觉找了一圈 Blend 和 composite 的方案不合适。后来想起 ImageData、 <image /> 这些是可以作为 texture 纹理在 WebGL 中使用的。

那 <video /> 能否当做纹理?查阅文档果然也可以。然后思路就来了:我们知道纹理是可以互相叠加的,在渲染过程中着色器可以清楚的表达如何去处理最后的色值。那理论上我们就可以直接把整个 video 作为纹理,取不同的区域去参与渲染计算和叠加。

根据这个逻辑,梳理一下代码实现。首先创建程序和挂载着色器:

顶点着色器 (上面的Shaders.vertex) 里把坐标和变量声明:

创建两个坐标变量 AlphaCoord 和 ColorCoord,分别代表两个区域的位置 (gl很啰嗦,已省略部分非关键代码):

再来看看片段着色器 (前面传入的Shaders.fragment) 。根据前文的逻辑,带入坐标,分别从两个区域各取出 rgb 和 alpha,合成新的color:

三行就搞定了。就和我们自己计算 rgb 一样,只不过是手动 CPU 计算变成了编译到 GPU 运算。

最后逐帧使用 video 创建纹理并渲染:

经过编码和调试,成功跑起来后,再次打开 performance,cpu 峰值和均值都下降了(90-100% 到 20-30%):

fps也提升了3-4倍(4-5 到 20左右)。证明思路是ok的。

对比此时的系统负载 GPU 比原先增加15%(从30%到45%)。CPU从60%左右下降到20-30%。

再降到同屏 4-5 个的情况下,可以稳定在60fps,足够承载业务场景。

6. 总结

打开了 WebGL 的宝盒,到此后续还有没有更多优化空间?比如冷启动预缓冲时间的缩短;移动端的适配,卡顿检测等等。另外还有没有比 video 纹理叠加更高效率的方式,或者更大胆的想法,能否 MSE 或 WASM 跳过 video 直接到 WebGL?更多细节还有待后续研究。

腾讯音乐全民k歌招聘客户端、web前端、后台开发,点击查看原文投递简历!或邮箱联系: godjliu@tencent.com

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯音乐技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档