Flutter 用Texture控件在Windows平台实现视频渲染

Posted 施强教育科技

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 用Texture控件在Windows平台实现视频渲染相关的知识,希望对你有一定的参考价值。

提示:阅读此文章之前需要有C++开发经验,知道如何利用channel在C++和Dart之间做通信。

前言

一、PlatformView与Texture是什么?

二、使用步骤

1.在Flutter需要显示视频的地方声明Texture组件

2.在Windows插件代码里面创建TextureRenderer类

3.Flutter通过channel调用Windows插件创建Texture

4.Windows插件部分C++更新视频RGBA,并通知flutter刷新界面

三、运行效果演示

总结


前言

        Flutter渲染视频在移动端比较容易,可以用PlatformView,Texture等,但在Windows平台无法使用PlatformView,原因是Windows的每一个窗口都是一个窗口句柄(HWND),如果强行的增加一 个PlatformView,那以后想在PlatformView所占据的位置弹出其他Flutter控件,会被遮盖,因此Windows平台视频渲染只能用Texture,效率并不像网上提到的那么并。目前Windows平台的示例代码几乎搜索不到,笔者在对接声网和网易两家公司RTC SDK的过程中,成功渲染了两家公司的多媒体播放器和RTC提供的视频,确定了Windows平台使用Texture渲染视频的方法,写下此文章,希望对后来的开发者有些许帮助。


一、PlatformView与Texture是什么?

PlatformView主要适用于原生已经很成熟的组件,嵌入到Flutter中,节省开发时间,例如WebView,视频播放器等。由于Windows窗口机制的原因,在Windows平台并不支持PlatformView。

Texture是一个颜色数据缓存区,只要平台将缓存区更新后,通知Flutter刷新界面即可。例如视频渲染,只需要将当前视频帧的RGBA缓存区拷贝到Texture的缓存区,然后Flutter刷新界面即可实现视频渲染,这种方式更dart。

二、使用步骤

1.在Flutter需要显示视频的地方声明Texture组件

代码如下:

 Container(
  color: Colors.black,
    child: Texture(textureId: (widget.viewModel.textureId))
);

2.在Windows插件代码里面创建TextureRenderer类

代码如下:

class TextureRenderer 
public:
    TextureRenderer(flutter::BinaryMessenger* messenger,
        flutter::TextureRegistrar* registrar);
bool TextureRenderer::onRenderVideoFrame(unsigned int uid, agora::media::IVideoFrameObserver::VideoFrame& videoFrame);

private:
    flutter::BinaryMessenger* messenger_ = nullptr;
    flutter::TextureRegistrar* registrar_ = nullptr;
    std::unique_ptr<TextureRenderer> fullScreenTextureRenderer_;    //全员看他纹理
    std::map<int64_t, std::unique_ptr<TextureRenderer>> renderers_; //通用的直播渲染纹理



TextureRenderer::TextureRenderer(flutter::BinaryMessenger *messenger,
                                 flutter::TextureRegistrar *registrar)
    : registrar_(registrar), 
      texture_(PixelBufferTexture(std::bind(&TextureRenderer::CopyPixelBuffer,
                                            this, std::placeholders::_1,
                                            std::placeholders::_2))),
      uid_(0), pixel_buffer_(new FlutterDesktopPixelBuffernullptr, 0, 0) 
    texture_id_ = registrar->RegisterTexture(&texture_);
    channel_ = std::make_unique<MethodChannel<EncodableValue>>(
      messenger,
      "strong_live_player/texture_render_" + std::to_string(texture_id_),
      &flutter::StandardMethodCodec::GetInstance());
    channel_->SetMethodCallHandler([this](const auto &call, auto result) 
    this->HandleMethodCall(call, std::move(result));
  );

3.Flutter通过channel调用Windows插件创建Texture

Flutter层面通过channel调用C++的createTextureRender函数后,会创建上面提到的第二步提到的TextureRender类实例,并与第一步提到的Flutter层面的Texture控件绑定。

PlayerViewModel init(var callback) 
    AgoraMediaPlayerKit.channel.invokeMethod('createTextureRender', 
    ).then((value) 
      textureId = value;
      _channel = MethodChannel('agora_media_player_kit/texture_render_$value');
      _channel?.setMethodCallHandler(_platformCallHandler);
      callback();
      refresh();
    );
    return this;
  

 4.Windows插件部分C++更新视频RGBA,并通知flutter刷新界面

这一步是收到播放器的视频帧回调后,通知Flutter层面进行渲染。所做的工作就是把视频帧的RGBA缓存区拷贝到Flutter底层提供的pixel_buffer。 并通知刷新,至此,视频渲染工作完成。缓存区的大小为 视频宽度 * 视频高度 * 4,之所以要乘以4,是因为每个颜色点由一个32位整数组成,每个颜色点的每一BIT排列顺序为 RGBA,分别代表红、绿、蓝、透明度,值为0~255。 代码如下:


bool TextureRenderer::onRenderVideoFrame(unsigned int uid, agora::media::IVideoFrameObserver::VideoFrame& videoFrame)

	std::lock_guard<std::mutex> lock_guard(mutex_);
	if (pixel_buffer_->width != videoFrame.width ||
		pixel_buffer_->height != videoFrame.height) 
		if (pixel_buffer_->buffer) 
			delete[] pixel_buffer_->buffer;
		
		pixel_buffer_->buffer = new uint8_t[videoFrame.width * videoFrame.height * 4];
	
	memcpy((void*)pixel_buffer_->buffer, videoFrame.yBuffer,
		videoFrame.width * videoFrame.height * 4);
	pixel_buffer_->width = videoFrame.width;
	pixel_buffer_->height = videoFrame.height;
	registrar_->MarkTextureFrameAvailable(texture_id_);
	return true;

三、运行效果演示

20220924-100136


总结

以上就是今天要讲的内容,Flutter在Windows上使用Texture进行视频渲染,对于不懂C++的人来说可能有点难懂。代码只有最关键的部分,如果有疑问,可以回复留言交流。

flutter widget:Row

参考技术A row : 在水平方向上排列子widget的列表.

Row 是继承 Flex => MultiChildRenderObjectWidget => RenderObjectWidget => Widget .
所以 Row 具有动态布局的特点,可以让子控件展开以填充可水平的可用空间,用一个 Expanded 的控件包裹子控件。
Row 不支持滚动(通常认为一行的子控件超过可以容纳空间是错误的)。如果有一行控件控件比较多,希望可以滑动的时候,可以采用 ListView 。
同样,如果是垂直方向方面和 Row 对应的是 Column 。
小demo

这里需要注意的地方:
因为在 Row 里面有text,但是因为text的长度过长,一行肯定是放不下的,所以这里考虑用Expanded包裹一个Text,这样文字会自动换行,填充剩余部分。

效果图如下

修改下Row的 内容:

效果如下:可见多个Expanded 具有平分剩余空间的功能。和android 的weight 属性很相像。

以上是关于Flutter 用Texture控件在Windows平台实现视频渲染的主要内容,如果未能解决你的问题,请参考以下文章

flutter获取屏幕宽高和某个控件 的宽高

max 8 render to texture 出错

flutter widget:Row

texture

Flutter Plugin的坑

Unity笔记UGUI的ImageRawImage控件