点击上方“LiveVideoStack”关注我们
Flutter是近两年大火的跨终端框架,实时音视频因为疫情的缘故也越来越融入到人们的日常工作生活中,如线上会议、在线教育等。两者结合起来可以碰撞起什么样的火花呢?利用Flutter实时音视频SDK,我们可以快速开发一个跨平台的会议、娱乐、教育等APP。LiveVideoStackCon 2021北京站邀请到腾讯云高级工程师——牛赞,为我们分享利用Flutter如何进行实时音视频渲染,并深入底层,优化视频渲染的性能。
文 | 牛赞
整理 | LiveVideoStack
我来自腾讯云音视频,本次分享主题是音视频前端跨平台技术应用。
我在2015年加入腾讯,先后负责过王者荣耀、英雄联盟竞猜、QQ会员等业务,目前负责腾讯云实时音视频TRTC前端技术研发工作。
1. 跨平台技术
首先为什么需要跨平台框架?因为它在理想情况下可以实现一次开发,多端运行,组件互用,提升效率。对于管理者来说,可以降低人力成本,不用分别组建IOS和Android团队。对于开发者来说,可以降低学习成本,只需理解一套跨平台框架即可实现双端开发,提升自我业务价值。
跨平台技术发展几大阶段如下:
第一阶段——Hybrid APP,核心原理是封装原生接口并暴露于JS。业务运行在外部应用中,背靠前端庞大生态,开发迭代速度非常快。不足之处是能力受限于调节层,扩展性较弱,在Webview中体现的性能较差。
第二阶段——2015年,Facebook推出ReactNative,核心改变是摒弃了低效的Webview渲染,转为Native控件,交由系统进行绘制。优势在于用户能够使用前端开发体系(庞大的React体系),且因其渲染交于系统绘制,所以性能优于Webview。但缺点是在渲染时需要和Native通信,当用户处于通信频繁场景时,处理不佳时会导致卡顿。ReactNative底层使用JS语言,只能使用JIT即时编译,其性能和Native端存在一定差距。
第三阶段——2018年,Google推出Flutter,一套代码可以同时构建多平台应用,它支持热重载从而能够高效进行开发工作,其底层使用Dart语音,同时支持JIT编译和AOT编译,自带渲染引擎skia,性能基本可以达到原生水平。
对比Flutter和ReactNative的热度趋势,数据来源于statisa、github、stack overflow。可以发现Flutter热度趋势已超过ReactNative,在跨平台领域后发制人,是目前最热门的跨平台技术方案。
图中是Flutter的架构,绿色部分是Flutter的Framework,是一个Dart实现的UI SDK,从上到下包括两大组件库、基础组件库、图形绘制、手势识别、动画等功能,其中两大组件库分别实现了基于IOS及Material UI风格的UI组件,使UI开发更加灵活。蓝色部分是Flutter的核心Engine,实现Flutter渲染引擎、Dart虚拟机、Platform通信通道、时间通知、插件架构等功能。
Platform通信通道特性应用于SDK接口的封装,还用于Flutter和Native异步消息传递,整个过程中消息的发送及响应都使用异步方法从而避免阻塞UI界面。Flutter引擎已经完成了桥接通道,用户只需在通信层编写底层的IOS/Android代码就可以在Flutter Dart中直接使用。
2. TRTC Flutter SDK架构设计
图中是Flutter SDK架构,SDK基于原生IOS/Android进行封装,能够直接对齐原生SDK,最大程度封用已有能力如音视频采集、编码解码等。
设计框架过程中,我们做到了以下几点:
对数据通信能力进行优化:由于Flutter和原生SDK进行通信的消息通道只支持简单的如基础类型技术,优化数据通信能力能够使其支持更多复杂的技术类型。
将实验层设计得更具扩展性:考虑到Flutter后续会支持更多平台,此设计便于未来扩展更多平台。
聚合美颜、设备、音频相关API:以便开发者使用腾讯云的API,更加易用。
优化视频渲染能力:GPU性能基本达到原生SDK水平。
上段提到了Flutter通信和原生通信仅支持基本的数据类型,这会带来以下几点挑战:
如何实现复杂的类结构体传输?
图片如何高效在Flutter和原生SDK之间传输?
Flutter没有原生平台类似的系统view组件,如何渲染视频?
API接口繁多,如何助力开发者快速接入?
下文将分别对这四个问题进行详细探讨。
Flutter本质是Dart调用Native的接口,并异步返回Native的数据。原生SDK存在大量类结构体的类型定义,如进房接口存在TRTC Params定义了应用ID、用户ID、用户密钥等相关信息,由于原有消息通道不支持传递这种类结构体,所以我们对数据通信能力进行了升级。
首先将Flutter定义的类结构体转为Map对象,对其进行JSON序列化,底层消息通道会将传输数据高效序列化为二进制传输。通信层收到数据后进行反序列化,转为对应JAVA类结构体后即可传输给原生SDK。
类结构体可以对参数进行约束、类型校验、便于开发者早期发现问题,提升易用性。
直播场景中有时需要给视频打上水印(如左上图右下角的熊猫水印),直播过程中给视频设置水印等接口需要把Flutter项目定义的图片资源传给原生SDK。Flutter有自带的一套图片资源管理机制,而底层SDK只能识别一个Bitmap位图对象,问题在于Flutter没有Bitmap这种数据类型,那么如何把Flutter项目的图片资源转成原生SDK需要的Bitmap呢?
首先利用应用程序的文档目录(Flutter和Android都可访问),上端提到Flutter自带一套图片资源管理机制,所以做法是在Flutter层拷贝其图片资源到文档目录,再将图片文件地址传输到通信层,通信层收到地址后解析为Android原生SDK所需要的Bitmap位图对象。虽然通过文档目录传递文件路径的方式能够实现共享,但当是由1张100kb的图片进行测试时,发现拷贝文件耗时较高,于是我们思考是否能够舍弃拷贝文件过程从而使图片传输过程更加高效。
通过查看Flutter programme的源码发现它的图片资源文件被打包在原生资源包下面,而且Flutter暴露的API能够使通信层拿到资源路径,这就可以直接将Flutter图片的Asset资源地址传递至通信层,通信层拿到地址后通过调用Flutter提供的AssetManager的API直接读取对象并转为Android所需要的Bitmap位图对象。
网络图片可以采取预下载的方式提前下载至文档目录从而实现网络图片的传递。
如果利用Flutter定义的通信机制以实现在Flutter里进行渲染,需要将摄像头采集的每一帧画面数据都从原生传输到Flutter中,而图像帧数据通过消息通道实时传输必然会引起CPU及GPU性能的巨大消耗。
为此,Flutter提供了以下两种视频渲染方案:
外界纹理:可以将原生端OpenGLl图像数据共享给Flutter进行渲染。需要原生SDK提供视频帧图像数据回调接口,实现较为复杂。
PlatformView:主要适用于Flutter中不太容易实现的组件,如Webview、视频播放器、地图等,给Flutter提供了嵌入Android和IOS平台原生view的能力。在之前Flutter技术设施尚不成熟时,PlatformView也为其注入了强大的生命力,在Native端不易实现的组件都可以通过PlatformView方案嵌入原生平台view中。
原生SDK提供了视频渲染view组件,我们只需利用PlatformView的能力,将Native端的视频view嵌入Flutter中即可。PlatformView在Android端其实是AndroidView,图片下方的第一行参数ViewType用于唯一标识Widget,用于和Android的View建立关联。
实现PlatformView并不复杂,简单了解官方文档后可以利用公式及官方提供的框架代码构造出PlatformView。
我们先尝试了容易实现的PlatformView方案,在视频渲染封装好后,对输出的视频画面进行性能测试,使用oppo的一部低端机进行测试,当房间有6个用户的时候,第二屏画面渲染异常(右侧2图)。
利用腾讯云的PerfDog性能狗进行性能分析,发现GPU占用异常高,于是继续开展了一些列优化措施。
首先优化视频列表,默认Flutter的ListView不支持懒加载,我们将其替换为ListView.builder,测试开始时,懒加载未生效且默认支持了预加载,Flutter底层默认预加载250像素以外的区域,考虑到视频渲染的增加对GPU的负荷很大,于是摒弃了预加载能力,更进一步地对非可视区域视频进行回收,当滑动到第二屏时就停止第一屏视频的拉流渲染。
优化视频列表后,GPU占用从72%下降到50%左右,视频画面能够正常渲染显示。
第一阶段优化结束后,我们没有就此止步。因为上线优化的Flutter之后,客户会对比其与Native端的差距,数据显示CPU、内存跟Android原生的占用差不多。
然而Flutter的GPU对比原生性能差距比较明显,达到了15%。
接着我们仔细对照了PlatformView的实现原理,发现对于Android来说,在虚拟显示模式下,其底层也是使用的外接纹理进行渲染,中间多了一块图形缓冲区,在Native端渲染好的视频View的每一个像素都流经这块图形缓冲区,转为图像纹理数据后在SurfaceTexture(Flutter提供的画板)上进行绘制,最终Flutter根据画板数据渲染出完整视频。
在以上环节中,性能的主要消耗点在于图形缓冲区,因为已在Native端渲染好的视频会重新经过这块区域绘制到SurfaceTexture中,造成了显存和绘图性能的严重浪费。针对此问题的优化思路是将拿到的SDK裸数据接口通过OpenGL直接输出到SurfaceTexture画板上,而非利用Native端的系统view组件。
最终视频渲染的架构如图所示,远端用户进房时,本机通过云服务接收到进房信号,比如很多人在一个房间中,此时有新用户进房,本机需要渲染新用户,首先发送拉流指令,安卓原生SDK一帧帧地回调视频帧纹理数据,再通过OpenGL绘制到SurfaceTexture画板中,Flutter最终拿到通信层返回的Texture ID(原生侧绘图数据对应的ID),通过此ID,Flutter能够在GPU中找到并使用相应绘图数据,最后由Flutter的引擎进行渲染。
Flutter优化后的GPU性能提升了约10%,基本能达到Android原生SDK水平。
原始的SDK、API繁多,光是Flutter API就有100多个。开发者和客户理解学习API的成本很高,接入耗时较久。于是我们建设了一系列场景化方案,客户可以寻找契合自身的业务场景,参考源码实现,提升接入效率。
TRTC应用的场景包括音视频通话、多人会议、在线教育、互动直播、语音聊天室、狼人杀、在线医疗、在线K歌等。
设计场景的方案过程中主要采取UI和场景SDK分离模式,客户能够直接参考UI界面进行开发,也可以使用封装好的场景SDK个性化定制UI。场景开发后台采用了腾讯云函数服务,降低客户接入门槛,所有组件都无服务器化,无需运维,节省人力成本。底层依赖TRTC SDK进行音视频传输、IM SDK提供信令及群聊能力。
接着介绍一些已经实现的应用场景。
语音通话场景中,选择呼叫用户发送通话请求,对方接受后即可建立音视频通话连接,类似于微信音视频通话功能。互动直播包括互动连麦、主播PK、低延迟观看,弹幕聊天等。延迟能够控制在300ms以内,直播过程中提供高级美颜如瘦脸、微脸,图中可以明显看到微脸操作后的效果对比。视频会议适合交流工作。语音沙龙,如年初热度很高的ClubHouse,用户能够加入感兴趣的话题房间,在房间中,由嘉宾发言,房间里的其他听众旁听,听众如果想要发言,可以举手申请成为嘉宾,之后提问或发言。在线教育场景中,老师能够选择语音、视频、屏幕分享等授课方式。
结合在线教育场景,简单介绍一下常见SDK的实现理念。该SDK主要针对在线教育场景中使用实时音视频及通信能力的二次封装,在封装基本的音视频聊天及屏幕分享能力的同时,还分装了老师提问,学生举手,老师邀请学生上台回答,回答完毕等能力。客户如果不满意场景化默认实现的UI,可以使用场景化SDK个性化开发。场景化SDK的接口基本不超过30个,语意也更加场景化。客户对接原始API,周期需要2-3个月;使用场景SDK进行对接,周期仅有1个月;复用场景化方案包括UI组件库,最快1周就能上线项目。
目前已经有越来越多的公司在新项目中尝试使用Flutter,这里列举的都是比较典型的使用Flutter的用户,其中有做互动直播场景的日本直播平台yell live、币安、腾讯游戏青少年直播;做教育的潭州教育、力拓飞远,还有做音视频通话的智联招聘等客户。
如果新业务有音视频方面的需求,那Flutter可以是一个非常不错的选择。
3. Flutter音视频未来展望
目前Flutter主要应用在移动端iOS/Android双端,Flutter愿景是成为一个多端运行的UI框架,能够支持不仅仅是移动端,还包括Web端和桌面端(MacOS/Windows),Flutter官方预计年底会正式支持桌面端,我们团队已经将Beta阶段的桌面端融合进TRTC音视频能力中,并开放了对MacOS/Windows的支持,功能上能够支持音视频通话部分,还缺失屏幕共享等能力,我们后续会补齐。
FlutterWeb与FlutterNative的整体架构相似,它们都共用Framework层,核心区别在于FlutterWeb重写了engine层,利用DOM/Canvas对齐了FlutterNative的UI渲染能力,使得Flutter编写的UI能够在浏览器上正常展示。虽然FlutterWeb在年初正式开放对Web的支持,但仍存在以下问题:
构建产物简陋,目前所有文件都打包为main.dart.js,不可避免导致图片文件非常大,基本为1-2mb,另外还缺少js拆包,文件hash等工作,影响页面加载性能。
由于FlutterWeb自身实现了一套页面滚动机制,页面滚动过程中,会频繁计算位置信息,引起滚动区域的重新渲染,最终导致页面滚动性能较差。
目前Flutter官方建议是FlutterWeb端适合以下三种场景:
使用Flutter构建的渐进式Web应用程序;
单页应用程序;
将现有的移动应用程序发布到web上。
Flutter不适用于web端常见的以文档为中心的瀑布流式的场景。
目前我们的SDK在dev测试版上也开放了对Web的支持,跟Native的对比多了一层Web兼容层,主要为了兼容Flutter Native API设计,实际上Web和Native的通信并不依赖于消息通道。
WebApi和NativeApi差异比较大,所以我们在Web通信层也做了大量的逻辑去抹平这里的差异。底层的WebSDK是基于WebRTC的实时音视频通话解决方案,目前主要支持Chrome58+和Safari浏览器。
未来,Flutter对桌面端/web端的支持会越来越好,一套框架打通全平台非常值得期待。
4. Web端音视频能力畅想
Web端的⾳视频能⼒也在不断的进化,浏览器已经变成⼀个完备的多媒体引擎。重点介绍一下浏览器带来的三个新特性:编码层面,可以应用webcodecs做低延迟编解码,动态控制编码的关键帧、编码码率;传输部分,使用WebTransport提供灵活可控的高性能UDP传输能力;WebAssenbly使我们可以复用C++写的复杂算法,在浏览器端可以使用WebAssenbly将C++复杂算法编译为浏览器可运行的代码,从而在浏览器端实现音频降噪、回声消除等能力。
基于腾讯20多年的音视频积累,进⼀步扩展浏览器的能⼒之后,在浏览器端再造一个自定义的RTC技术引擎,这样做的好处有很多:
能复用一套TRTC的技术栈,一套C++的代码可以复用在多个平台。我们在Native端积累了很多最佳实践可以同步用在Web端。
更可控的RTC QOS调控能力,比如在直播场景下,可以牺牲一定的延迟来换取直播的清晰度。
更丰富的使用场景,底层技术也可以复用到直播推流SDK和播放器SDK。
下一代Web的RTC引擎预计明年正式对外,大家可以期待一下。
在目前视频会议产品中,虚拟背景已经成为了标配能力。举个例子,在视频会议中,背景可能是家中,不太正式,这时可以选择合适的背景图替换背景。利用WebGpu/WebgGl的图形渲染能力、TensorFlow的机器学习能力和WebAssembly的多线程计算能力实现Web版本SDK的人像分割能力。
此外,我们还做了大量的优化,将模型文件精简到1M以内。我们还提供了丰富的API帮助用户在性能和效果之间达到很好的平衡。
结合浏览器新特性,还可以开发很多功能,通过shader算法打造WebAr引擎,适用于直播过程中美颜美妆、趣味贴纸等场景。
在日常的企业直播中,OBS已经成为了企业直播的标配。当浏览器的能力提升之后,一个Web版本的OBS成为了可能,它能带来以下优势:
多采集源的支持,能同时支持多人通话直播;
所见即所得的效果,可通过拖动改变布局;
操作简单,打开网页即能直播。
Web OBS能替代OBS80%的能力,可以应用在企业直播、在线直播等场景。
以上是本次的分享,谢谢!
扫描图中二维码或点击阅读原文
了解大会更多信息
喜欢我们的内容就点个“在看”吧!