Flutter-Texture外接纹理上实现视频播放
有关于 Flutter Texture 的部分的相关资料都比较少,要么就是封装视频播放器的大佬,要么就是闲鱼,声网这些团队
先放参考文章:
万万没想到——flutter 这样外接纹理
实时渲染不是梦:通过共享内存优化 Flutter 外接纹理的渲染性能
Flutter 实时视频渲染:Texture 与 PlatformView
Android 记录在 macOS 上使用 NDK20 编译 ffmpeg4.2.2 的过程
Android 使用 FFmpeg (二)——视屏流播放简单实现
咱们要做的还是视频播放,我并不是非要去反复造这个轮子,它对我之后的投屏起到了绝大的帮助
现有的视频播放器在安卓端的封装是 ExoPlayer,还有 ijkplayer 在 Flutter 的封装等,ExoPlayer 是硬件解码的播放器
什么是硬件解码?
硬件解码就是利用安卓底层已经有的解码器进行解码,它的性能较高,但兼容较低,是利用 GPU 进行处理的解码器(才入门音视频,任何说得不对的可以指出)
什么是软件解码?
软件解码则使用 CPU 进行解码,兼容性较高,需要在软件额外的添加解码库才能进行解码
需要的环境
我们这次使用软件解码器来进行解码,由于使用软件解码器,我们就需要使用解码的库,这里我使用强大的 FFmpeg,编译与引入安卓部分本篇就不提了,可以参考低调大佬的帖子,这里感谢低调大佬对我提供的帮助(入门 Flutter 看了他挺多帖子,在群里我都不认识 hhh)
首先看一下 Flutter 中 Texture 这个 Widget 的构造函数
1 | const Texture({ |
如上,它只需要一个 TextureId 就能构造这个 widget,我看了绝大部分国内关于 Texture 的介绍,还一点一点研究了目前的视频播放器,得到了在安卓端创建 SurfaceView 的方法
我起初以为这个 Texture id 会是一个 hashcode,最后发现它在一个 app 内就是 0,1,2…,每次创建+1。
由于视频播放解码等都是耗时操作,在安卓会使用到 SurfaceView 这样一个组件,SurfaceView 是独立的线程,也就是说在它内部调用 jni 进行视频解码播放不会影响到其他的 UI 线程,SurfaceView 内部会将自己 View 对应的 Surface 这个对象的实例直接通过 jni 直接传给 native,native 就能直接操作这部分的 UI。
我们先看一下安卓原生怎么来播放视频
自定义 SurfaceView
1 | public class MyVideoView extends SurfaceView { |
FFmpegNativeUtil 类
1 | package com.example.ffmpeg; |
播放视频
1 | public class MainActivity extends AppCompatActivity { |
这里是动态添加了一个 SurfaceView 组件,没有用到 xml,并通过一个按钮来进行调用了他的 Play 方法,传入了需要播放视频的本地路径。
对应的 native
1 | Java_com_example_ffmpeg_FFmpegNativeUtil_videoStreamPlay(JNIEnv *env, jobject instance, |
Flutter 视频播放器在安卓端的实现
创建 SurfaceView 拿到 Texture ID
我们通过一个 Plugin 来实现创建
Android 端
1 | new MethodChannel(getFlutterView(), "VideoCall").setMethodCallHandler((call, result) -> { |
这就是一个简单的 Plugin,里面的代码就只有几行,拿到 Surface 传给 cpp native,通过 result 返回 ID,中间开启了新的线程去对视频进行解码播放,cpp 的 navtive 部分复用了纯安卓播放的 native
Flutter 端
在任何地方初始化这个对应的 Plugin
1 | MethodChannel videoPlugin = const MethodChannel("VideoCall"); |
1 | *** |
Dart 则通过调用这个 Plugin 拿到一个 Texture ID,并刷新 UI。没有做太多的细化处理,当然你自己使用的时候最好加上一些判断语句。
这样就已经实现了 Flutter 简单的视频播放,
看过前面参考的文章,里面有提到这是个 GPU->CPU->GPU 的过程消耗,将安卓的 Surface 数据 copy 到 Flutter 内存中并通过 skia 渲染了出来。
如果通过 ffmpeg 解码,这个时候不去绘制安卓原生的 SurfaceView,直接将数据交给 Flutter engine,再渲染到屏幕,应该就会解决这样的消耗,并且不会依赖平台的组件,不过 Flutter 没有开放内部的 gl 供开发者使用,闲鱼的那篇文章有这样的尝试
为什么我一定要用这样的方式去实现视频播放呢?
目前的已有的视频播放器已能播放本地视频与 url 网络视频,ijkplayer 甚至能兼容一些其他的直播流协议,但并不方便去播放一些自定义协议,如来自 socket 流中的视频
下面是这两天的成果
等我弄得差不多的时候,一定会开源出来
Flutter-Texture外接纹理上实现视频播放
http://blog.nightmare.press/2020/03/23/Flutter-Texture外接纹理上实现视频播放/