教育App Flutter点播组件实战
教育从去年开始接入Flutter,今年上半年重构腾讯课堂和企鹅辅导iPad端,80%代码都采用Flutter实现,对于教育最重要的点播功能同样也需要迁移到Flutter上进行渲染。
目前正在研究的实现渲染的方案主要有2种形式PlatformView和Texture Widget。下面文章就先大概讲述一下Flutter的渲染框架原理和实现,然后会对这两种方案进行对比分析。
Flutter渲染框架和原理
Flutter的框架主要包括Framework和Engine两层,应用是基于Framework层开发的,Framework负责渲染中的Build,Layout,Paint,生成Layer等。Engine层是C++实现的渲染引擎,负责把Framework生成的Layer组合,生成纹理,然后通过OpenGL接口向GPU提交渲染数据。
Flutter:Framework的最底层,提供工具类和方法 Painting :封装了Flutter Engine提供的绘制接口,主要是为了在绘制控件等固定样式的图形时提供更直观、更方便的接口 Animation :动画相关的类 Gesture :提供了手势识别相关的功能,包括触摸事件类定义和多种内置的手势识别器 Rendering:渲染库,Flutter的控件树在实际显示时会转换成对应的渲染对象(RenderObject)树来实现布局和绘制操作
渲染原理
当GPU发出Vsync信号时,会执行Dart代码绘制新UI,Dart会被执行为Layer Tree,然后经过Compositor合成后交由Skia引擎渲染处理为GPU数据,最后通过GL/Vulkan发给GPU,具体流程如下:
当需要更新UI的时候,Framework通知Engine,Engine会等到下个Vsync信号到达的时候通知Framework,Framework进行animations,build,layout,compositing,paint,最后生成layer提交给Engine。Engine再把layer进行组合,生成纹理,最后通过OpenGL接口提交数据给GPU,具体流程如下:
接下来分别分析一下两个方案的各自的特点以及使用的方式。
PlatformView
PlatformView是Flutter官方在1.0版本推出的组件,以解决开发者想在Flutter中嵌入Android和iOS平台原生View的Widget。例如想嵌入地图、视频播放器等原生组件,对于想尝试Flutter,但是又想低成本的迁移复杂组件的团队,可以尝试PlatformView,在 Dart 中的类对应到 iOS 和 Android 平台分别是UIKitView和AndroidView。
那么PlatformView在点播功能中应该怎么实现,如下图所示:
其中的ARMPlatformView代表业务View。
Dart层
1.创建关联类
关联类的作用是Native和Dart侧的桥梁,其中id需要和Native获取对应。
2.创建Callback
3.创建Widget布局
Native层
1.注册ViewFactory
2.注册Plugin
3.ViewFactory实现
4.View实现
Texture Widget
基于纹理实现视频渲染,Flutter官方提供的video_player则是通过这种方式实现的,以iOS为例,Native需要提供一个CVPixelBufferRef给Texture Widget,具体实现流程如下图所示:
其中的ARMTexture是业务提供CVPixelBufferRef,具体实现步骤主要是 1.继承FlutterTexture 2.管理已注册textures集合
3.获得textureId
4.重写
返回CVPixelBufferRef 5.通知Texture获取CVPixelBufferRef
Dart层
Native层
1.注册Plugin
2.管理Texture/获取TextureId
3.重写copyPixelBuffer
4.调用textureFrameAvailable
这里是需要主动调用的,告诉TextureRegistry更新画面。
性能对比
播放同一段MP4视频PlatformView和Texture Widget性能对比, Texture Widget 性能相对差一些,分析主要原因是因为 CVPixelBufferRef 提供给Flutter的Texture,Native到Flutter会经过GPU->CPU->GPU的拷贝过程 ,1.0版本数据对比如下:
遇到的问题
Flutter 播放 器接入到课堂iPad中采用的是Texure的方案,在实现PlatformView和 Tex ture Widget 两个方案的时候,主要遇到了以下几个问题。
PlatformView内存增长问题
课堂在连续播放视频之后,出现内存暴增问题,主要原因是OpenGL 操作都需要设置[EAGLContext setCurrentContext:context_] , 在IOSGLRenderTarget析构的时候,没有设置context上下文。
课堂直播场景退出之后,前面Flutter页面出现黑屏
课堂直播课退出之后回到上一个Flutter页面出现页面黑屏,直播视频渲染也是采用OpenGL,当直播退出的时候不仅需要设置context,还需要清空帧缓冲区,重置纹理,销毁代码如下:
Texture Widget内存相比PlatformView性能相对差一些
主要原因是因为 CVPixelBufferRef 提供给Flutter的Texture,Native到Flutter会经过GPU->CPU->GPU的拷贝过程,所以我们将Native生成TextureID->拷贝生成PixelBuffer->生成新的TextureID改为直接通过Native生成TextureID->渲染,减少多次拷贝引起的内存问题,经过优化 Tex ture Widget 的整体性能优于 PlatformView。 2.0优化后数据对比如下:
总结
目前基于教育自研的播放器ARMPlayer的Flutter播放器Plugin已经在腾讯课堂iPad中使用,采用优化后的Texture Widget方案,Texture Widget是官方推荐的方式,不管是视频,还是图片都可以用Texture,方便扩展,同时通过纹理形式贴到LayerTree上保证平台无关,多端可复用,优化后的Texture Widget性能也优于PlatformView,Flutter播放器Plugin是教育客户端中台(大前端和点播)结合的一个新的尝试。