写点什么

从 Flash 到 MP4,爱奇艺奇秀直播礼物特效精进之路

  • 2020-12-27
  • 本文字数:3425 字

    阅读完需:约 11 分钟

从Flash到MP4,爱奇艺奇秀直播礼物特效精进之路

虚拟礼物是主播和观众互动的重要道具,也是主播很大一部分的收入来源。当刷满足够礼物数量时,直播间就会飞出炫酷的礼物动效,它既能刺激主播更好地直播,也能满足用户在虚拟世界的荣誉感,越有诚意的礼物会触发越精彩的礼物动效,这也加深了主播和用户之间的羁绊。


随着直播平台的发展,设计人员不断创作出精美的礼物动效,相应的,动效加载时长和文件体积逐渐变大,而开发人员要需要能够吃下这些负担,在保证动效播放的高效和顺滑的同时,不给用户机器带来负担也不给设计人员的创作带去局限,这便是我们本文需要考虑的几个难点。本文将以奇秀 PCweb 端为例,从动效效果、性能、研发效率、兼容性等多角度谈谈礼物动效的发展。


演进之路


礼物动效的实现格式可以有很多种,不同文件格式之间各有利弊,从业务角度出发选取相对较优的方案是我们的目的。整条演进路上,我将带领大家从 Flash 时代开始了解多种动效格式,希望在了解多种方案后,你能找到适合自己业务的较优方案。


动效格式


●过去的王者:Flash


●动图:GIF、APNG、Webp


●开源和自研:Lottie、SVGA、IXD(自研)


●视频:MP4


Flash 时代


奇秀平台刚开始上线的时候,视频行业在网页端还是 Flash 的天下,基于 Flash 的流媒体播放器几乎是唯一选择,拿它实现动效制作也是很自然的事情,对设计师来说使用 Adobe Flash CC 导出 Flash 动画也是常规操作。


没多久,Flash 式微,html5 技术飞速发展,替换 Flash 变成迫切需要做的事情。彼时团队内部出现两种实现方向:


●APNG 动图


●自研新的动效方案


APNG 动图


APNG(Animated Portable Network Graphics)诞生于 2004 年,是一个基于 png 的位图动画格式,扩展方法类似主要用于网页的 GIF 89a,仍对传统 PNG 保留向下兼容,2017 年主流浏览器几乎都已经支持 APNG。由于当时平台上移动端一直是使用图片序列生成的 APNG/Webp 作为动效资源,为了保持多端的统一,PCweb 端也开始尝试使用动图作为后续的动效格式。



综合对比文件大小、效果以及浏览器兼容性,选择使用 APNG 作为新的动效格式。单纯使用<img>标签直接展示 APNG 会遇到如下几个问题:


●小部分浏览器不支持播放 APNG


●为了动效播放连贯性,需要预加载 APNG


●APNG 播放开始、过程、结束等时间点不受控


为了解决这几个问题,我们开始研究 APNG 的文件格式结构,包括 PNG 的结构和扩展的 acTL(动画控制块)、fcTL(帧控制块)、fdAT(帧数据块)。



基于对 APNG 的了解,对一些开源方案进行了调研测试,最终决定使用 apng-js 帮助我们解决上述几个问题。使用 apng-js 解析 APNG,可以得到 APNG 的很多信息,包括宽高、帧数、播放时长,以及播放过程中的每一帧画面,有了这些再配合 canvas 便可以将 APNG 绘制和控制起来。下面是设计的播放器参数的结构:


{     src: string, // 动效地址    selector: string            // dom容器    playStart: () => void, // 动画开始    playProgress: (index: number, totalTime: number, remainTime: number) => void, // 动画过程    playEnd: () => void, // 动画结束};
复制代码


上述设计以及播放、暂停、重播等使用方法,基本满足对 APNG 播放的控制,且在后续业务中也可直接实现支持音效礼物,只要在 start 处读配置播放对应的音频资源,end 处删除音频即可。为了提高动效播放的连贯性,在解析 APNG 后会将解析出的数据存入 indexedDB,后面在播放同一动效时便可省去下载和格式解析的耗时。


这种方案对于设计来说也很方便,AE 做好动画后,使用格式工具和插件直接导出动图即可。但是后面也发现了该方案的弊端,平台对动效的要求越来越高,越酷炫的动效体积越大,很多复杂动效甚至达到 5、6M。


自研 IXD


后 Flash 时代,另一部分同学开始了动效自研的路子。APNG 的本质是对 PNG 格式的扩展,除了向下兼容的 IDAT(图像数据块)块外,多了很多 fdAT(帧数据块),每一块都是完整的图像,每多一帧就会做一张位图的大小。如果每一数据块不是完整位图,而是图集素材,且数据块表示素材的位置、宽高、缩放等各种信息,那么素材复用率会极高,再辅以对应的解析,那么整体文件大小也会降低很多。



按照该思路去实现,封装了自研的 IXD 格式,原本 10M 的 PNG 序列可以被拆解压缩到 1M 以内,动效效果几乎没有收到影响。


对于使用端,只需要支持二进制数据和 PNG 位图的处理,再按照设定的格式解析数据块动态去渲染每一帧即可。对于开发人员,我们同步配套了 web 端 SDK。


为了方便设计师生产,也配套开发了动效编辑器,将素材导入到工具中,通过复用素材和组合效果的方式,支持叠加多个图层,最终导出 IXD 文件。对于有动态数据的动效,也支持变量占位,开发人员按照占位符取值,使得同一动效不同用户看到的各不一样。



同期市面上也出现了 Airbnb 的 Lottie 和 YY 的 SVGA 方案:


●Lottie 需要 AE 安装 Bodymovin 插件导出 JSON 文件给开发同学使用,有兴趣的同学可以去了解下。


●SVGA 也提供了 AE 插件,它做的事情其实和 IXD 类似。SVGA 从 AE 源文件里提取素材,并将素材在时间轴里的表现(位移、旋转、缩放等)导出,各端 parser 负责将这些还原到画布上。它比 IXD 优秀的点在于,提供了 IOS、Android 端的解析 SDK。


目前我们有 APNG 方案和 IXD 方案,包括市面上的 SVGA 方案,从播放可控到文件大小都有了一定的解决方案,似乎已经可以告一段落了。但这几个方案依旧有各自的缺陷:


MP4


有没有既能尽量保证 AE 动效,也能保证文件体积小的方案呢?有,用 MP4!肯定会有人以为我在开玩笑,MP4 都不支持透明,如何能够用来承担直播动效呢。我们先了解下透明是什么:图像的透明使用 Alpha 通道表示,即 RGBA 里的 A,该通道是一个 8 位灰度通道,由 256 级灰度来记录图像中的透明信息。说白了透明和 RGB 一样都是颜色信息,虽然 MP4 不支持透明通道,只要想办法给它展示出来就好了吧!还是 canvas 技术,只要我们可以用 canvas 把透明视频画出来就可以了。


通过和设计同学沟通,也参考了网络上的文章和讨论,最终决定将动效以上下同步的两块视频拼接而成。上面的视频保留原始的 RGB 信息,下面的视频将 A 信息用黑白来表示,当 MP4 播放的时候分别读取上下两块视频进行拼接,得到完整了 RGBA 像素信息,然后绘制在目标画布上。



代码很简单,canvas 入门的同学应该都可以写出来:


// 绘制视频buffer.drawImage(video, 0, 0, width, height * 2);// 读取上下视频图像数据let imageData = buffer.getImageData(0, 0, width, height).data;let alphaData = buffer.getImageData(0, height, width, height).data;// 塞alpha通道for (let i = 3, len = imageData.length; i < len; i = i + 4) {    imageData[i] = alphaData[i-1];}// 绘制canvasoutput.putImageData(image, 0, 0, 0, 0, width, height);
复制代码


本以为一切都特别顺利,结果进行性能测试发现动效绘制时 CPU 暴涨,根本无法投入生产。原因其实看代码也能发现,在视频播放过程中密集遍历处理像素点,计算频率太高,所以 CPU 暴涨。既然单纯 canvas2d 不行,我们就打算试试性能更好的 WebGL。WebGL(Web Graphics Library)基于 OpenGL,可以为 HTML5 Canvas 提供硬件 3D 加速渲染,也可以借助系统显卡来在浏览器里更流畅地展示 3D 场景和模型。WebGL 里的概念很多,有 scene、camera、texture 等,这里不会一一介绍,有兴趣的同学可以去了解下。在实现中为了便捷,我们借助 Three.js 来实现功能,利用 THREE.ShaderMaterial 自定义的着色器去处理,以下是核心代码:


gl_FragColor = vec4(    texture2D(texture, vec2(vUv.x, 0.5 + vUv.y/2.)).rgb,    texture2D(texture, vec2(vUv.x, vUv.y/2.)).r);
复制代码


经过性能测试和对比,动效播放时的 CPU 使用率显著降低:



总结


通过对以上各种方案的分析,从实际效果、资源大小、成本的角度我们得出以下表格:



我们的场景主要是直播间的礼物动效,暂时没有什么动效的用户交互需要,因此我们选择让设计同学充分发挥才能的 MP4 方案,设计同学无需学习第三方插件,也不用考虑资源大小而被迫抽帧导致降低动效质量。


我们坚持认为在礼物动效制作方面,MP4 的是未来的大趋势,视频编解码技术也在不断发展,H.265、H.266,视频文件体积将会不断缩小,用户等待的时长减少和带宽节省都是很不错的收益。


后续我们将继续深入 MP4 方案,在格式解析和动态素材占位上下功夫,尝试参照 IXD 的方案将配置信息写到文件中,创建一个新的 BOX 塞入 MP4 文件里,播放时候先取这个 BOX 读取配置信息,然后和后续的视频帧播放时结合起来达到实现动态素材的效果。也将继续研究 WebGL,寻求更加高效的渲染方式,争取进一步提高播放性能。


本文转载自:爱奇艺技术产品团队(ID:iQIYI-TP)

原文链接:从Flash到MP4,爱奇艺奇秀直播礼物特效精进之路

2020-12-27 07:002510

评论

发布
暂无评论
发现更多内容

webpack模块化的原理

Geek_02d948

webpack

Red Hat Releases OptaPlanner 8

积木思维

被翻译成 12 种语言的深度学习名著重磅升级,75%新内容,让你豁然开朗!

图灵社区

Python 深度学习

令人头疼的Javascript隐式&强制转换

hellocoder2029

JavaScript

为什么最近关注“元数据”的人突然增多?

雨果

元数据 元数据管理 元数据管理平台

Vue组件入门(一)组件定义及使用

Augus

Vue3 10月月更

一文了解 Python 中的对象比较方法 is 和 ==及其本质

宇宙之一粟

Python 10月月更

理解NodeJS多进程

coder2028

node.js

详解CAN总线:CAN总线报文格式—遥控帧

不脱发的程序猿

汽车电子 CAN总线报文格式 详解CAN总线 CAN遥控帧

小程序容器技术,或许是提升智能设备安全等级的又一解决方案

Speedoooo

浏览器安全 小程序容器

webpack热更新原理(面试大概率会问)

Geek_02d948

webpack

我为什么要使用 Tauri 构建 macOS 磁盘清理工具 PrettyClean

NinetyH

tauri ​Rust

后端Java随机比大小游戏实战讲解

魏铁锤

10月月更

详解CAN总线:CAN总线报文格式—错误帧

不脱发的程序猿

汽车电子 CAN总线报文格式 详解CAN总线 CAN错误帧

详解CAN总线:CAN总线报文格式—过载帧

不脱发的程序猿

汽车电子 CAN总线报文格式 详解CAN总线 CAN过载帧

实验精神终将胜利:量子纠缠的祛魅七十年

脑极体

爬虫练习题(五)

张立梵

Python. 10月月更 爬虫案例

说说Nodejs高并发的原理

coder2028

node.js

一文读懂Js中的this指向

hellocoder2029

JavaScript

【愚公系列】2022年10月 Go教学课程 024-函数

愚公搬代码

10月月更

数据治理,七种常犯的错误

雨果

数据治理

数字化转型最核心的是数据吗?不,是数据思维

雨果

数字化转型 数据思维

深度理解NodeJS事件循环

coder2028

node.js

星汉未来 | 云原生薪火计划——开源大使招募

星汉未来

开源 开发者 云原生 星汉未来 FinOps

时间旅行调试与自动化测试的支持

iofod jude

前端 低代码

js作用域、作用域链和它的一些优化

hellocoder2029

JavaScript

详解CAN总线:CAN总线报文格式—帧间隔

不脱发的程序猿

汽车电子 CAN总线报文格式 详解CAN总线 CAN帧间隔

阳振坤:分布式技术引领关系数据库发展

OceanBase 数据库

DataOps核心理念是什么?包括哪些模块?

雨果

DataOps

被翻译成 12 种语言的深度学习名著重磅升级,75%新内容,让你豁然开朗!

图灵教育

Python 深度学习

从手动测试到自动化测试(上)

FunTester

从Flash到MP4,爱奇艺奇秀直播礼物特效精进之路_架构_爱奇艺技术产品团队_InfoQ精选文章