网络知识 娱乐 flutter 使用texture实现Windows渲染视频

flutter 使用texture实现Windows渲染视频

文章目录

  • 前言
  • 一、如何实现?
    • 1、定义Texture控件
    • 2、创建Texture对象
    • 3、关联TextureId
    • 4、写入rgba
  • 二、示例
    • 1.使用ffmpeg解码播放
  • 总结


前言

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));
}

效果预览

在这里插入图片描述


总结

以上就是今天要讲的内容,flutter在Windows上渲染视频,熟悉c++的话代码不算难,但是主要问题是没有资料,几乎唯一的方法就是阅读源码,这就对开发效率造成了阻碍。总的来说,使用texture渲染跟界面兼容性好,但是性能一般,毕竟使用rgba渲染限制了硬解的优化可能,不过对于一般使用场景还是适用的。