• flutter 使用texture实现Windows渲染视频



    前言

    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,
    ))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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');
      });
      });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    c++

    //此texture_id 关联到界面上的Texture即可。
    texture_id = texture_registrar_->RegisterTexture(texture);
    //本地方法返回texture_id
    result->Success(flutter::EncodableValue(texture_id));
    
    • 1
    • 2
    • 3
    • 4

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注:texture_registrar的获取方法为:

    //定义TextureRegistrar对象
    static flutter::TextureRegistrar* texture_registrar_;
    //插件注册代码,这里的插件名为ffplayplugin,此处为官方生成代码
    void FfplayPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) {
        // 略 此处为官方生成代码
        //获取TextureRegistrar对象
    	texture_registrar_ = registrar->texture_registrar();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二、示例

    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),
      ),
      );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    定义一个插件我这里是fflay_plugin。
    fflay_plugin.cpp
    相关对象的定义

    static flutter::TextureRegistrar* texture_registrar_;
    class PlayData 
    {
    public:
    	Play* play;
    	FlutterDesktopPixelBuffer flutter_pixel_buffer;
    	int64_t texture_id;
    };
    static std::map<int64_t, PlayData*> playMap;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    插件注册代码

    //插件注册代码,这里的插件名为ffplayplugin,此处为官方生成代码
    void FfplayPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) 
    {
        // 略 此处为官方生成代码
        //获取TextureRegistrar对象
    	texture_registrar_ = registrar->texture_registrar();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
    	flutter::TextureVariant* texture_ = new flutter::TextureVariant(flutter::PixelBufferTexture(
    		[=](size_t width, size_t height) -> const FlutterDesktopPixelBuffer* {
    			return &pd->flutter_pixel_buffer;
    		}));
    	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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    效果预览

    在这里插入图片描述


    总结

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

  • 相关阅读:
    C++ std::array的原理和语法
    为啥$p(w|D)=p(y|X,w)$?
    top100-贪心算法
    移动端中如何做单位的自适应
    hadoop异常java.net.ConnectException 拒绝连接
    C++标准输入输出及命名空间
    Android Studio 快捷键及使用技巧汇总
    写给新生们-常用软件及一些学习上的建议
    【C++】STL详解(十一)—— unordered_set、unordered_map的介绍及使用
    仅靠阿里 P9 分享的 Redis 工作手册,拿到 60W 年薪 Offer
  • 原文地址:https://blog.csdn.net/u013113678/article/details/125492286