写点什么

Flutter 实时音视频实践

  • 2019-11-30
  • 本文字数:3767 字

    阅读完需:约 12 分钟

Flutter 实时音视频实践

前两期 RTC Dev Meetup:Flutter 开发技术专场 的回顾视频已被上传至了 RTC 开发者社区,欢迎大家浏览与交流。

我们整理了其中一个演讲内容——由声网 Agora 高级架构师 张乾泽分享的“Flutter 实时音视频实践”。以下为演讲实录。


我今天给大家介绍的是 Flutter 实时音视频开发实践方面的经验。我叫张乾泽,毕业于英国牛津大学,之前在 SAP 主要担任移动端架构师,2017 年加入声网 Agora,目前是声网 Agora 的高级架构师,主要负责 RTC 技术在娱乐、直播、教育等行业的应用研发工作。


首先我们讲到 RTC 应用,什么样是 RTC 应用呢?RTC 是 Real-time Communications。大家平时在日常生活中已经接触到非常非常多的 RTC 应用,比如说直播,大家平时会用手机看熊猫直播、斗鱼直播,实时看主播的游戏画面、他们唱歌的画面、跳舞的画面。还有教育,通过互联网可以实现远程教育,比如学生和美国的老师可以直接进行实时互动。


在医疗方面,我们最近也看到很多的案例。以前你要到医院挂号排队,等一上午,医生看一下,然后明天再来复诊时重新挂号,又是一个上午的时间过去了。现在有了 RTC 后,你可以通过视频直接在家里看医生,医生在视频中问你一些问题,如果病症简单,你甚至都不需要去医院。


今天我们就拿常见的视频通话的应用,来看看如何基于 Flutter 来开发实现,以及其中会有什么样的困难。

架构逻辑与实现思路

首先讲一下 Flutter 的架构逻辑。如果比较熟悉 Flutter 的朋友可能会知道,它的渲染方式跟 React Native 最不一样的地方就是它对于自己的一些原生控件,没有利用系统本身提供 View。那么开发应用的时候,怎么渲染出来的呢?



它自己里面会建立一个 Layer Tree,Layer Tree 里面是它的一个树状结构,它把数据或者渲染的地方发到 Skia,Skia 得到 GPU 的 Vsync 信号的时候,再把这些数据传给 GPU 统一渲染。完全没有通过类似 UI View 这样的 Native 的 View 的。


在知道这个概念之后,我就开始着手开发这个应用。这个应用因为它本身就提供了一些常用的组件,比如 App 的一个框架,一些按钮和导航栏,Flutter 有现成的组件可以让你直接把这些东西画上去,只需要通过一些非常简单的配置即可。


Flutter 本身也提供了与 Native SDK 的通讯的方式,叫做 Channel Massage,跟 React Native 调用 Native 的思路非常像。所以我们有了声网 Agora SDK,会帮我们处理音频编解码、音频的传输,然后我们可以利用 Flutter 本身的组件把 UI 也画好,好像这个东西就 Work 了。当一切都似乎变得那么美好的时候,我卡在了视频上。Flutter 怎么渲染视频?


在 Native 平台都有系统组件来渲染视频,但 Flutter 没有这样的东西。我现在要把实时视频渲染出来。


于是我查看了 Flutter 的源码,其中有一个组件叫 Video player,这是放视频的。其中有一个 Texture Widget,它在这种模式下提供了一种机制,可以让你进行视频的渲染。

实现思路一:Texture Widget

首先视频是由一帧帧图像组成的。Flutter 的 Texture 提供了一个可以放在 Layer Tree 里的组件,组件中的数据源需要由你通过 Native 端来提供。


在这里以 iOS 为例,iOS 就需要提供 CVPixelBufferRef。这是一个数据,对应的就是视频中的一帧画面。把这个数据作为数据源提供给 Texture Widget,然后 Texture Widget 就可以把你提供的这些数据显示出来,最终就变成了一个视频。



上图是大致的实现架构。首先要在 Dart 上实现一个 Native 的 Plugin。Plugin Registar 是 Plugin 的一个入口,有一个属性叫做 TextureRegistry,你可以在这里注册一个你自己的 Texture 类,这个类可能实现了一个协议叫 Flutter Texture。你实现这个协议过程中有两个方法需要实现:一个方法叫做 CVPixelBufferRef,就是要提供数据源到你的 Texture。另一个方法你需要自己主动去调,叫做 textureFrameAvailable,它的作用是告诉 TextureRegistry 现在可以根据提供的数据来更新画面了,就是我提供数据。


其中有一个问题,现在有 TextureWidget,如果更新数据源的话,它怎么才能知道我要更新哪个 Texture 呢?其实,在注册这 Texture 的时候,它会返回一个 TextureID。你在创建 Texture Widget 的时候需要把这个 TextureID 传进去。这样的话 Texture 的组件就会跟你提供的这个实践进行绑定,知道你提供的数据源就是为这个 Texture 提供的数据。以此类推,我们也可以有很多个 Texture。



回到我们之前的设计来看(如上图),我们这边是一个视频通话应用,我们有很多本地的视频,有很多的远端的视频,这些视频都可以是不同的 Texture Widget,放在 Dart 的页面容器中,然后我们分别为它们提供数据源,进行数据渲染。


是不是感觉现在一切准备就绪了?其实还有一个问题。Flutter 官方的 Github 提供了一些示例代码,但是它提供的功能是让你播放一个静态缓存,我们这里提供的是一个实时的视频流,不是一个视频文件。不可能以读取一个文件的方式,把里面的数据一帧一帧地转化之后,传给 Texture Widget。我们需要有一个方法拿到这些实时的数据,把它作为数据源传给 Texture。



这时候我们就需要使用声网 Agora SDK。首先给大家普及一个概念,大家想象一下平时在看一个直播的时候,直播那边有一个摄像头,他玩一些游戏,他跳一些舞、唱一些歌,是怎样让我在手机里面看到这些数据的呢?总结来讲是一个非常简单的流程,如上图所示。首先主播有一个摄像头,进行数据的采集。这些数据是裸数据。在拿到这些数据后,我们需要进行编码。在编码过程中,我们可以对数据进行处理和格式化,比如说让它有一个特定的分辨率和帧率。直播不会把摄像头的所有的数据都传过来,他会对数据进行压缩、处理,这就是编码的过程。在编码完成后,我们就有一个视频数据,它会通过声网的云服务,快速传输到你的手机上。手机客户端上也有声网的 SDK,在拿到数据后,会把数据进行解码,并提供一个方法,将这些视频数据传到 UI View 上,最终显示为你所看到的视频。



如上图所示,基于刚才的架构,我们添加了一个声网 SDK。SDK 有一个方法叫做 AgoraVideoSink。它提供了一个回调,会把收到的所有视频数据按照你想要的格式传给你。我们在得到回调数据后,再把他传给 Flutter Texture。我们可以设定一个更新频率,每当得到一次回调后,通过 notify 告诉 TextureRegistery 将新的数据更新到 Texture。通过这样的一个过程,我们就能实现一个 Flutter 的实时音视频 App 了。

实现思路二:PlatformView

由于 Texture 会涉及到很多渲染的流程,所以很多人都会觉得它有些复杂。所以在 Flutter 1.0 版本中,Google 给出了一个新的东西,叫做 PlatformView。



它提供了一种方法,让我们可以创建 UI View,并加到 Dart 的 LayerTree 里。在 Dart 中的类对应到 iOS 和 Android 平台分别是 UIKitView 和 AndroidView。


PlatformView 该怎么用呢?在 PluginRegistar 中新增了 ViewFactory,ViewFactory 只有 CreateView 这一个方法需要实现。你可以在这个方法里首先提供一个 Identifier,在实现该方法后,可以返回一个你想要的 PlatformView,并与 Dart 组件绑定在一起。


因为我们的 SDK 支持传递 Native 的 View,然后将视频渲染到上面。如上图,我们基于刚才的架构做一下改动。声网 SDK 中提供了一个 CustomViewFoctory,它的作用就是创建一个原生的 View,然后 SDK 的 AgoraRtcEngine 会与 View 绑定,在收到数据流的时候会自动将它渲染到这个 View 上。因为每个 View 会对应一个 ViewID。那么我们只需要通过 ViewID 就可以得到这个 View,然后把它渲染出来。

性能对比

总结一下,在 Flutter 中实现实时音视频有两种方式,一种是 Texture,另一种是 PlatformView。我们将它们与 Native 的实现方式进行了性能对比,结果如下图所示。



三者相比,Texture 的实现方式性能比较差。PlatformView 的性能与 Native 相近。就目前的 Demo 来看,造成 Texture 的方法性能较差的原因可能有两个:


  • 第一,在收到视频数据之后,会立刻更新 Texture,但这样做的必要性不大,如果进行一些优化的话,还可以提升一些性能;

  • 第二,我们是通过一个回调来获取数据,然后将它提供给 Flutter 的 Texture,这个过程是通过 CPU 来完成的,但通常来讲,渲染的工作需要由 GPU 来做,所以数据从 GPU 到 CPU 又再到 GPU,这个过程非常耗费资源。


总结一下,我个人对于 Texture 和 PlatformView 的优缺点比较。我个人觉得 Texture 这个东西实现“更 Dart”,什么叫“更 Dart”?就是没有很多原生的参与,比如 UI View ,更符合 Dart 的生态环境,在设计上更加纯粹。但是它也有缺点。如果用它做一些事情,不做特殊处理的话,可能很难避免 CPU、GPU 中间数据拷贝,造成性能损失。如果你要用它去做一个地图,那你就得把地图数据渲染放在 Flutter 上,用 Flutter 的逻辑全部重新实现一遍。这个对于大多数开发者来说,对于正在蓬勃发展中的一个生态来说是非常不利的。所以我个人认为这也是 Google 在 Flutter 1.0 加入了 PlatformView 的原因。



使用 PlatformView 有什么优点呢?我刚才说了,它可以把之前实现 Native View 的功能直接放在 Dart 里。对于开发者来说,更加简单易用。也可以避免前者存在在 CPU、GPU 数据拷贝带来的性能损失问题。但问题,它在设计上不那么纯粹,会引入一下不可控的因素。


本文转载自公众号声网 Agora(ID:shengwang-agora)。


原文链接:


https://mp.weixin.qq.com/s/8_epFcHKbaWT0P6IlyeXPg


2019-11-30 13:232019

评论

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

3 条掏心掏肺的建议,新手学习编程必备,快上车!

Java 程序员 后端

容器 & 服务:Helm Charts配置文件分析

程序员架构进阶

容器 Helm 11月日更 chart

AQS-AbstractQueuedSynchronizer源码解析(下)

Java 程序员 后端

AQS源码分析看这一篇就够了

Java 程序员 后端

CAT中实现异步请求的调用链查看

Java 程序员 后端

Prometheus HTTP API 查询(四)Target 和查询结果格式

耳东@Erdong

Prometheus PromQL HTTP API 11月日更

ApplicationContextAware使用理解

Java 程序员 后端

Bootstrap框架快速上手

Java 程序员 后端

CentOS安装rar及用法

Java 程序员 后端

《Kubernetes in action 读书笔记》:Kurbernetes 架构设计

后台技术汇

Kubernetes 11月日更

ConcurrentHashMap锁的前世今生,了解一下

Java 程序员 后端

69-个经典-Spring-面试题和答案详解(下)

Java 程序员 后端

BATJ互联网月薪45K的Java岗面试题首次曝光,掌握这些Offer指定跑不了

Java 程序员 后端

7月份了,再不准备金九银十就晚了,阿里2021最新数据结构与算法面试题手册

Java 程序员 后端

588页!三个通宵学完这份“宝典”,4面斩获字节跳动offer

Java 程序员 后端

as-if-serial规则和happens-before规则的区别

Java 程序员 后端

BS-GX-018 基于SSM实现在校学生考试系统

Java 程序员 后端

Chrome 灵魂插件!爱了爱了!

Java 程序员 后端

4月面试5月成功入职阿里,工作之余整理复盘:面试经历+备战经验分享!

Java 程序员 后端

Alibaba内部Java面试手册,10W字全是精华

Java 程序员 后端

AtomicBoolean介绍与使用

Java 程序员 后端

Centos7搭建maven私服nexus3

Java 程序员 后端

4年Java程序员十面阿里终拿下offer,评级P6+年薪30-40w无股票

Java 程序员 后端

Android C++系列:认识JNI

轻口味

c++ android jni 11月日更

模块三课后作业-详细架构设计文档-外包学生管理系统

断水风春

架构实战营

Chrome上的这些灵魂插件太猛了,碾压其他同款

Java 程序员 后端

【Promise 源码学习】第三篇 - 实现一个简版 Promise

Brave

Promise 11月日更

ARM架构下的Docker环境,OpenJDK官方没有8版本镜像,如何完美解决?

Java 程序员 后端

BIGO Java 三面 + HR 面面经(已意向)

Java 程序员 后端

CentOS7系统中安装JDK8

Java 程序员 后端

构建大型 Vue.js 应用程序文件结构

devpoint

Vue 11月日更

Flutter 实时音视频实践_文化 & 方法_声网_InfoQ精选文章