Flutter视频渲染系列
第一章 Android使用Texture渲染视频
第二章 Windows使用Texture渲染视频(本章)
第三章 Linux使用Texture渲染视频
第四章 全平台FFI+CustomPainter渲染视频
第五章 Windows使用Native窗口渲染视频
第六章 桌面端使用texture_rgba_renderer渲染视频
文章目录
前言
flutter渲染视频的方法有多种,比如texture、platformview、ffi,其中texture是通过flutter自己提供的一个texture对象与dart界面关联后进行渲染,很容易搜索到android和ios的相关资料,但是windows却几乎找不到。通过查看一些开源库的代码,找出了再windows使用texture渲染的方法,在这里做一个简单的介绍。
一、如何实现?
1、定义Texture控件
在界面中定义一个Texture
Container(
width: 640,
height: 360,
child: Texture(
textureId: textureId,
))
2、创建Texture对象
在Windows本地代码中创建一个Texture对象。
int64_t texture_id;
FlutterDesktopPixelBuffer flutter_pixel_buffer;
memset(&flutter_pixel_buffer, 0, sizeof(flutter_pixel_buffer));
flutter::TextureVariant* texture = new flutter::TextureVariant(flutter::PixelBufferTexture([&](size_t width, size_t height) {
//回调返回视频数据
return &flutter_pixel_buffer;
}));
//此texture_id 关联到界面上的Texture即可。
texture_id = texture_registrar_->RegisterTexture(texture);
3、关联TextureId
dart
int textureId = -1;
if (textureId < 0) {
//调用本地方法获取textureId
methodChannel.invokeMethod('startPreview',<String,dynamic>{'path':'test.mov'}).then((value) {
textureId = value;
setState(() {
print('textureId ==== $textureId');
});
});
}
c++
//此texture_id 关联到界面上的Texture即可。
texture_id = texture_registrar_->RegisterTexture(texture);
//本地方法返回texture_id
result->Success(flutter::EncodableValue(texture_id));
4、写入rgba
flutter_pixel_buffer.width = width;
flutter_pixel_buffer.height = height;
//data[0]为一帧rgba数据
flutter_pixel_buffer.buffer = data[0];
//调用此方法后,会出发texture的回调
texture_registrar->MarkTextureFrameAvailable(texture_id);
注:texture_registrar的获取方法为:
//定义TextureRegistrar对象
static flutter::TextureRegistrar* texture_registrar_;
//插件注册代码,这里的插件名为ffplayplugin,此处为官方生成代码
void FfplayPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) {
// 略 此处为官方生成代码
//获取TextureRegistrar对象
texture_registrar_ = registrar->texture_registrar();
}
二、示例
1.使用ffmpeg解码播放
main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
MethodChannel methodChannel = MethodChannel('ffplay_plugin');
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
int textureId = -1;
Future<void> _createTexture() async {
print('textureId = $textureId');
//调用本地方法播放视频
if (textureId < 0) {
methodChannel.invokeMethod('startPreview',<String,dynamic>{'path':'https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv'}).then((value) {
textureId = value;
setState(() {
print('textureId ==== $textureId');
});
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
//控件布局
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (textureId > -1)
ClipRect (
child: Container(
width: 640,
height: 360,
child: Texture(
textureId: textureId,
)),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _createTexture,
tooltip: 'createTexture',
child: Icon(Icons.add),
),
);
}
}
定义一个插件我这里是fflay_plugin。
fflay_plugin.cpp
相关对象的定义
static flutter::TextureRegistrar* texture_registrar_;
class PlayData
{
public:
//Play中封装的ffmepg的操作
Play* play;
FlutterDesktopPixelBuffer flutter_pixel_buffer;
int64_t texture_id;
};
static std::map<int64_t, PlayData*> playMap;
插件注册代码
//插件注册代码,这里的插件名为ffplayplugin,此处为官方生成代码
void FfplayPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar)
{
// 略 此处为官方生成代码
//获取TextureRegistrar对象
texture_registrar_ = registrar->texture_registrar();
}
methodChannel部分
if (method_call.method_name().compare("startPreview") == 0)
{
PlayData* pd = new PlayData;
memset(&pd->flutter_pixel_buffer, 0, sizeof(pd->flutter_pixel_buffer));
//创建播放器
pd->play = new Play;
//创建texture
flutter::TextureVariant* texture_ = new flutter::TextureVariant(flutter::PixelBufferTexture(
[=](size_t width, size_t height) -> const FlutterDesktopPixelBuffer* {
return &pd->flutter_pixel_buffer;
}));
//注册texture
pd->texture_id = texture_registrar_->RegisterTexture(texture_);
playMap[pd->texture_id] = pd;
//播放视频回调
pd->play->Display = [=](unsigned char* data[8], int linesize[8], int width, int height, AVPixelFormat format) {
//设置视频数据
pd->flutter_pixel_buffer.width = width;
pd->flutter_pixel_buffer.height = height;
pd->flutter_pixel_buffer.buffer = data[0];
//通知渲染
texture_registrar_->MarkTextureFrameAvailable(pd->texture_id);
};
//获取参数
flutter::EncodableMap arguments = std::get<flutter::EncodableMap>(*method_call.arguments());
auto path = std::get<std::string>(arguments[flutter::EncodableValue("path")]);
//开始播放
pd->play->Start(path.c_str(), AV_PIX_FMT_RGBA);
//设置返回值
result->Success(flutter::EncodableValue(pd->texture_id));
}
效果预览
三、完整代码
https://download.csdn.net/download/u013113678/87075729
包含完整代码的flutter项目,版本3.0.4、3.3.8都成功运行,目录说明如下。
总结
以上就是今天要讲的内容,flutter在Windows上渲染视频,熟悉c++的话代码不算难,但是主要问题是没有资料,几乎唯一的方法就是阅读源码,这就对开发效率造成了阻碍。总的来说,使用texture渲染跟界面兼容性好,但是性能一般,毕竟使用rgba渲染限制了硬解的优化可能,不过对于一般使用场景还是适用的。