写点什么

爱奇艺直播 WebAssembly 优化之路

  • 2019-10-31
  • 本文字数:3679 字

    阅读完需:约 12 分钟

爱奇艺直播WebAssembly优化之路

WebAssembly 技术简介

近几年,WebAssembly 技术非常火,可以说是成为了 JavaScript 一个新的转折点。JavaScript 自 1995 年诞生之日起,其性能问题就被大家诟病。直到 2008 年,很多浏览器加入了即时编译器,JavaScript 也开始引入 JITs,再加上 Google 等厂商对其的大力优化,其性能提升了 10 倍不止。由此,JavaScript 也开始跳出了浏览器的范围,在各个领域崭露头脚,比如后台使用的 Node.js 和桌面端使用的 Electron 等。


JIT 技术简而言之是在 JavaScript 解释执行时将常用的二进制代码块暂存下来,在下一次解释执行相同的代码块的时候可以直接运行暂存的二进制代码块,节约了解释的时间。那能不能将所有 JavaScript 代码一次性都编译成二进制,提升运行效率呢?WebAssembly 的出现回答了这个问题。


在 WebAssembly 出现之前,JavaScript 是浏览器里可以运行的唯一的编程语言。而 WebAssembly 技术使浏览器运行别的语言编写的程序变成了可能。目前可以使用 C、C++、Rust、Go、Java、C# 编译器(还有更多)来创建 wasm 模块。浏览器在运行时将 wasm 模块放在专有的虚拟机中运行。由于是二进制的文件,运行效率比解释执行的 JavaScript 脚本要高很多,因此,很多前端开发者也把 WebAssembly 技术视作下一代的前端技术。


目前 WebAssembly 的兼容性如下图所示:



可以看到,在新版本上,主流浏览器不管是在 PC 端还是移动端都支持了 WebAssembly,而且各大浏览器厂商还在持续支持此项技术,相信不久就会得到非常普遍的应用。

WebAssembly 和直播,不一样的火花

一直以来,爱奇艺生产的直播流有 mp4 和 flv 两种格式,但 Html5 的 video 标签原生只支持 mp4 的播放,如何解决 flv 格式在网页端播放的问题就摆在了所有人的面前。一般来说 flv 格式在网页端播放有以下几种解决方案:


1、使用 flash 播放器插件


不过因为性能和安全等各种问题,各大浏览器已经逐渐弱化了这种方式,Chrome 也将在 2020 年左右停止对 flash player 的支持,所以现在基本很少有人用了。


2、网页对 flv 格式的视频解码


使用 canvas 渲染图像,使用 audio 播放声音,相当于网页端做一个播放器,这也是可行的。但各大浏览器厂商对原生 video 控件会针对不同的平台做硬件加速渲染的优化,如果自己渲染的话,硬件加速这块便也需要自己做,这样会耗费极大的人力,并且效果也很难和浏览器原生的硬件加速相比。


3、在网页端将 flv 格式转成 mp4 格式然后使用原生播放器


这也是目前使用得最多的方案。这样既可以播放 flv 的直播流,也可以将渲染丢给原生播放器去做,充分发挥原生播放器的优化能力。


爱奇艺直播使用的就是第三种方式,当 flv 的直播流到达前端时,使用 JavaScript 将 flv 转换成 mp4,再交给原生播放器。但由于 JavaScript 运行效率较低,这部分的性能一直都令人不太满意,所以决定引入 WebAssembly 技术,看看是否能带来不一样的提升。现在打开任意的爱奇艺直播间,在后面输入__enablewasm__=true,就能打开 WebAssembly 转码模式,如下图所示:



体验上,两种模式都能满足流畅观看直播的需求。由此可见,WebAssembly 模块可以很完美地替换原来的 JavaScript 所写的转码模块。下面来看一下如何接入 WebAssembly。

接入 WebAssembly 的步骤

使用 WebAssembly 非常简单,总的来说,分为以下几步:


1、使用 c 编写 flv 转 mp4 的代码


首先定义 WebAssembly 被 js 调用的接口文件:



如果想在被编译成 wasm 文件后可以被 JavaScript 调用,就需要在可以被外部调用的函数前使用 EM_PORT_API 来标识,这样在后面编译的时候 WebAssembly 就会将此函数作为可被 JavaScript 调用的方法抛出。


然后还需定义一些 WebAssembly 调用 JavaScript 的接口,如下面所示:



主要是通知 js 转换 mp4 流的头部信息和已经转好的部分流的缓存区地址等,实际调用的代码需要使用 EM_ASM_()函数包起来,里面填上调用的 JavaScript 方法名和带的参数。


定义好接口后就是转码实现的部分了,这里涉及到 flv 和 mp4 格式的相关知识(对这两种格式不太了解的同学可以自行阅读相关文档)。整体转码采用 flv 和 mp4 双缓存区的模式,流程如下图所示:



a. JavaScript 获取到直播流后将流存入 flv 缓存区;


b. JavaScript 通知 WebAssembly 缓存区首地址和进度;


c. WebAssembly 请求缓存区数据;


d. WebAssembly 进行转码;


e. 将转好的 mp4 片段存入 mp4 缓存区;


f. WebAssembly 通知 JavaScript 转码进度;


最后由 JavaScript 通知原生播放器直接播放 mp4 缓存区的视频流。


2、使用 emcc 编译出 flv2Mp4.js 和 flv2Mp4.wasm


首先需要安装 emscripten 环境,安装和配置的具体步骤可以参考 emscripten 的官网


安装完成后就可以使用 emcc 命令编译 c 文件了,使用命令 emcc main.c -s TOTAL_MEMORY=268435456 -g -o flvToMp4.js,最终可以得到两个文件,flvToMp4.js 和 flvToMp4.wasm。


其中 flvToMp4.wasm 是实际转码的 code,flvToMp4.js 相当于接口文件,播放器可以通过引入 flvToMp4.js 来加载 wasm 文件和调用 wasm 文件中的二进制代码。


通过阅读 flvToMp4.js,我们可以发现自动生成初始化 WebAssembly 的相关代码,获取 WebAssembly 的二进制文件后,调用了 WebAssembly.instantiate(),初始化了 WebAssembly,并且在获取或加载 wasm 文件失败后还能再次重试。



通过查看 getBinaryPromise()方法也可以看到下载的过程。



使用自动生成的代码,就基本可以不用管加载 wasm 文件等问题,非常方便。


3、对接编译好的 wasm 文件


因为转码是高 cpu 的工作,所以将其放入 web_worker 中运行,这样不会阻碍主线程的渲染。



worker 创建后将事件和主线程对应绑定,也即绑定前面定义的 wasm 调用 JavaScript 的几个方法:



上面就是 WebAssembly 通知 JavaScript 的相关消息接口的定义,到此已经完成了整个转码过程的全部接口定义,全部流程就如下面的时序图所展示。



由时序图可以看到播放器在收到流时就会初始化 WebAssembly 模块,初始化完成后进入转码阶段,通知 WebAssembly 进行转码并存入缓存区,再通知播放器播放。

使用 WebAssembly 实际性能对比

体验上能保持一致,那实际性能上有多少提升呢?还是要用数据说话。爱奇艺直播团队先后使用代码打点和浏览器自带的性能监测工具实时监测数据的方式来测试 WebAssembly 的实际使用性能。


1、直播流转码效率情况


首先,测试使用 WebAssembly 实际转码的速度。分别使用原来 JavaScript 所开发的转码模块和使用 WebAssembly 的转码模块进行转换,在实际直播间转换 flv 流数据包的前后进行打点计时,最终得到的数据如下所示:



前后各挑取了 30 包 flv 数据,表中第二列是每一包转码耗时,第三列是包的大小,在表的最后统计了总包的大小和总耗时,由此计算出未开启 WebAssembly 和开启 WebAssembly 的平均传输速率分别为 35305.6 字节/s 和 46608.1 字节/s。可以看出,WebAssembly 开启后转码速度的提升还是非常明显的。


2、运行时浏览器资源消耗情况


WebAssembly 实际应用在直播间中,能给直播间带来什么样的提升呢?最明显的是 cpu 占用率的下降。这一点可以通过使用 Chrome 浏览器自带的 Performance monitor 对使用 WebAssembly 前后的资源消耗做对比来证明。



如上图所示,可以在开发者工具 More tools 中找到 Performance monitor。通过这个工具,可以大概得到平时运行时的 cpu 占用率。下面两张图分别显示稳定播放时未开启 WebAssembly 和开启 WebAssembly 的 cpu 占用率情况:


  • 未开启 WebAssembly



  • 开启 WebAssembly



从图中可以大致看到,未开启 WebAssembly 时,cpu 占用率基本稳定在 7%左右,而开启 WebAssembly 之后,cpu 占用率能稳定在 5%上下,由此可以估算出大约有 10%-20%的提升(注:使用测试机型为 macbookpro 2018 款,cpu i7 2.2 GHz,不同机器测试出的性能可能有差别,波动状况也不完全一致,但在不同平台开启 WebAssembly 基本都能获得不同程度性能的提升)。

WebAssembly 未来的更多可能

使用 WebAssembly 转码还只是 WebAssembly 的一个非常小的用处,爱奇艺直播团队还将使用 WebAssembly 技术实现更多有趣、有价值的功能,比如:


  • c++项目的移植。现在很多图像相关的项目、算法都是由 c++编写的,如果想在浏览器上运行,以前就只能使用 JavaScript 重写一遍;而现在,通过 WebAssembly,只需要极小的改动,就使其能在浏览器上跑起来。

  • 算法的加密。由于 WebAssembly 编译成的 wasm 是二进制文件,反编译的成本很高,部分保密性比较强的算法会使用 WebAssembly 技术。

  • H.265 编码格式的支持。H.265 编码方式凭借其出色的压缩比,被越来越多的产品所应用,但目前各主流浏览器原生还不支持 H.265 的硬解。但是也可以根据同样的思路,使用 WebAssembly 将 H.265 的流转化为 H.264 的流,然后再使用原生播放器播放,最终达到 Web 端播放 H.265 流的效果,这样可以极大地降低带宽成本。


得益于性能上的提升,WebAssembly 开始在各个领域崭露头脚,今后,爱奇艺直播团队也将尝试使用 WebAsssembly 实现更多的功能来优化爱奇艺的直播体验。


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


原文链接


https://mp.weixin.qq.com/s/LRGNOuFwHXALs_lhPyN3Zw


2019-10-31 08:003665

评论

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

如何解决Python项目在VScode中跨文件夹导包问题

IT蜗壳-Tango

IT蜗壳教学 2月月更

大数据开发之运维面试题汇总分享

@零度

面试题 大数据运维

如何让代码代码管理变的更安全更高效 | 云效开发篇

阿里云云效

阿里云 DevOps 云原生 代码管理 Codeup

探秘 k8e:极简 Kubernetes 发行版

张晓辉

Kubernetes 云原生

推荐学java——Spring与web项目

逆锋起笔

Spring web idea web web项目 idea spring

“1天一朵云”,这是如何做到的?

华为云开发者联盟

数字化转型 软件架构 华为云 华为云Stack 全栈云平台

百度APP视频播放中的解码优化

百度Geek说

百度 性能优化 视频

永中软件加入龙蜥社区,共建开源新生态

OpenAnolis小助手

Linux 开源 永中

7

Geek_59dec2

【架构实战营】模块七:命题作业

wgl

「架构实战营」

面试突击20:进程和线程有什么区别?

王磊

设备接入服务,看完这篇给你整的明明白白

华为云开发者联盟

物联网 IoT 华为云 设备接入服务 设备

模块七作业

whoami

「架构实战营」

不断突破,稳中求进——我的移动端跨平台开发技术回顾与展望| 社区征文

No Silver Bullet

跨平台 移动端 新春征文 2月月更

在阿里,我们如何管理代码分支?

阿里云云效

阿里云 DevOps 云原生 研发 分支管理

Ceph核心概念解读

总想做点什么

分布式存储 Ceph 云存储 云计算,

Java11特性-效能翻倍的HttpClient

蜜糖的代码注释

Java 后端技术 2月月更

Kubernetes HPA 基于 Prometheus 自定义指标的可控弹性伸缩

张晓辉

Kubernetes 弹性 HPA

【高并发】一文解密诡异并发问题的第一个幕后黑手——可见性问题

冰河

Java 并发编程 多线程 异步编程 精通高并发系列

鸿蒙轻内核源码分析:文件系统LittleFS

华为云开发者联盟

鸿蒙 文件系统 Flash LittleFS LiteOS-M内核

从原理到操作,让你在 APISIX 中代理 Dubbo 服务更便捷

API7.ai 技术团队

微服务网关

阿里巴巴DevOps文化浅谈

阿里云云效

阿里巴巴 阿里云 DevOps 云原生 研发

java开发之Spring集成MyBatis知识分享

@零度

Java spring mybatis

Java 字符串常见的操作

编程江湖

腾讯员工怒怼加班文化事件:大厂的内卷,小厂有什么资格?

菜根老谭

预约小程序开发笔记一:背景与技术方案的选型

CC同学

百度爱番番实时CDP建设实践

百度Geek说

后端

前端开发之js栈内存和堆内存的区别

@零度

JAVA开发 堆内存 栈内存

分配任务的人才是团队的核心成员

panda

管理 限界上下文 任务分配

盘点冬奥会上的那些“黑科技”,你也可以掌握它!

博文视点Broadview

Go 学习路线(2022)

AlwaysBeta

Go golang Go 语言

爱奇艺直播WebAssembly优化之路_文化 & 方法_直播技术团队_InfoQ精选文章