Flutter视频渲染系列
第一章 Android使用Texture渲染视频
第二章 Windows使用Texture渲染视频
第三章 Linux使用Texture渲染视频
第四章 全平台FFI+CustomPainter渲染视频
第五章 Windows使用Native窗口渲染视频(本章)
文章目录
- Flutter视频渲染系列
- 前言
- 一、有何优势?
- 1、支持任意渲染技术
- 2、解决空域问题
- 二、如何实现
- 1、添加flutter_native_view 插件
- (1)添加依赖
- (2)添加native初始化代码
- (3)添加dart初始化代码
- 使用示例
- 2、创建win32窗口
- (1)、问题
- ①Windows消息循环在主线程
- ②flutter ui线程不是主线程
- (2)、解决方法
- ①Isolate运行消息循环
- ②PostThreadMessage通信
- ③消息循环中创建窗口
- 3、封装成Native控件
- 4、ffi使用句柄渲染视频
- 三、完整代码
- 四、效果预览
- 总结
前言
使用flutter在Windows上渲染视频,目前掌握的至少有2种方法(第二章、第四章),但是性能都不是最佳,而且需要将数据转换成rgba才能渲染。如果能使用win32窗口,直接通过句柄渲染,那就可以达到与原生Windows渲染使用一致的效果,可以使用sdl渲染yuv,以及dxva2解码后d3d9表面直接渲染到窗口全程在gpu上操作。本文将介绍如何在flutter程序嵌入win32窗口并且渲染视频。但是需要注意的是目前的实现只支持win10及以上版本。
一、有何优势?
1、支持任意渲染技术
因为是win32窗口,我们拿到hWnd句柄后可以有多种渲染视频的方法,比如使用sdl渲染yuv、或者opengl渲染yuv、还可以使用d3d9渲染yuv,以及最佳性能的dxva2+d3d9渲染也能使用。
2、解决空域问题
空域问题是两种不同的渲染技术在同个窗口不能兼容,只能相互覆盖。本文的实现方法不存在此问题,嵌入的win32窗口上面可以渲染flutter界面。
二、如何实现
1、添加flutter_native_view 插件
flutter_native_view 实现了win32窗口嵌入的容器。只需要提供窗口句柄,flutter_native_view就能将窗口作为控件加入到flutter界面。
具体添加方法使用方法需要查看https://pub-web.flutter-io.cn/packages/flutter_native_view
添加插件有一定的步骤请仔细阅读上述链接的指引,或flutter_native_view: ^0.0.2 版本的添加示例如下
(1)添加依赖
在pubspec.yaml添加依赖
flutter_native_view: ^0.0.2
(2)添加native初始化代码
在Windows主函数添加初始化代码如下如图:
#include "flutter_native_view/flutter_native_view_plugin.h"
flutternativeview::NativeViewContainer::GetInstance()->Create();
(3)添加dart初始化代码
在main.dart添加初始化代码
import 'package:flutter_native_view/flutter_native_view.dart';
WidgetsFlutterBinding.ensureInitialized();
await FlutterNativeView.ensureInitialized();
使用示例
创建controller,关联窗口句柄
final controller = NativeViewController(
//需要嵌入的窗口句柄
handle: hwnd,
hitTestBehavior: HitTestBehavior.translucent,
);
NativeView关联controller
//作为控件放在需要的地方
LayoutBuilder(
builder: (context, constraints) = > NativeView(
controller: controller,
width : constraints.maxWidth,
height : constraints.maxHeight,
),
),
2、创建win32窗口
需要自己实现win32窗口的创建,使用win32插件可以全程用dart实现。
具体的实现就是调用win32 api,这里就省,就说几个关键点吧。
win32: ^5.0.3
import 'package:win32/win32.dart';
(1)、问题
①Windows消息循环在主线程
②flutter ui线程不是主线程
flutter ui线程是dart代码运行的线程,但是和主线程不是同一个线程。所以创建win32窗口时不能在dart ui线程,否则界面会卡死。
(2)、解决方法
本文使用的是比较简单的实现方式,全部用dart实现,单独开一个线程跑消息循环,并管理子窗口的创建销毁。要求较高的朋友,可以尝试实现在main.cpp的消息循环管理子窗口,或者通过钩子完全用dart实现主线程创建窗口。
①Isolate运行消息循环
isolateFuture函数,就是运行isolate的封装,略。_receivePort是ReceivePort
final sendPort = _receivePort.sendPort;
isolateFuture(() {
//发送当前线程id给ui线程
sendPort.send([msgID, GetCurrentThreadId()]);
final msg = calloc.allocate<MSG>(sizeOf<MSG>());
while (true) {
GetMessage(msg, NULL, 0, 0);
TranslateMessage(msg);
DispatchMessage(msg);
}
calloc.free(msg);
});
②PostThreadMessage通信
//通过消息循环的线程id发送消息
PostThreadMessage(_nativeWindowMsgLoopThread, WM_USER + 1, 0, 0);
③消息循环中创建窗口
if (msg.ref.message == WM_USER + 1) {
final userData =
calloc.allocate<WindowUserData>(sizeOf<WindowUserData>());
final hwnd = CreateWindowEx(
0,
className,
windowName,
0,
0,
0,
0,
0,
0,
0,
0,
userData,
);
//将窗口句柄发送回ui线程
sendPort.send([msg.ref.wParam, hwnd]);
}
3、封装成Native控件
将上述的内容结合起来封装成一个可以直接使用的控件,控件实现略。
/// Windows原生窗口控件。依赖于flutter_native_view插件。
/// 可以当成普通控件使用,不会有空域问题,窗口区域上面可以显示flutter控件。
class WindowsNativeWidget extends StatelessWidget {
final Color color;//窗口背景色
final Function(int)? onHandleCreated;//句柄创建的回调
WindowsNativeWidget({this.onHandleCreated, this.color = Colors.white});
}
4、ffi使用句柄渲染视频
通过封装的Native控件拿到窗口句柄,我们只需要通过ffi设置给c++,然后就可以直接使用窗口句柄渲染视频了。比如使用sdl或者opengl、或者d3d渲染都可以。
三、完整代码
使用ffmpeg+sdl显示视频示例代项目码。
https://download.csdn.net/download/u013113678/88072835
注:示例代码的视频解码渲染在dll中不可见,而且也不是本文的关键,只要能拿到hWnd,渲染方式因人而异。
四、效果预览
嵌入的win32窗口(视频框)上可以显示flutter控件
和控件一样跟随窗口移动
i7 8750h核显使用dxva2解码播放1080p 30fps h264视频的性能。
总结
以上就是今天要讲述的内容,对于flutter在Windows上的视频渲染优化,这是一个很重要的方法。有了win32窗口就相当于支持Windows的所有视频渲染方式,笔者之前做的dxva2+d3d9解码渲染就可以直接复用了,而且性能直接拉满。但是当前实现并不算非常完美,配置较麻烦、背景渲染还有似乎点小问题、对于无边框窗口需要修改插件,而且只支持win10。总的来说,是可以在项目中使用的,需要的只是在此基础上继续优化。