背景

在Flutter开发中需要用到视频播放的功能,Flutter对视频播放的支持并不是很友好所以Google提供了TextureLayer让Flutter端能够使用原生端的渲染,这样我们原生端很多优秀的视频播放组件能够在Flutter程序上使用了

Texture的创建

Texture是Platform端创建的,创建是会生成一个textureId,textureId可以映射获取到Texture

  • Androaid端FlutterRenderer创建SurfaceTexture
  @Overridepublic SurfaceTextureEntry createSurfaceTexture() {Log.v(TAG, "Creating a SurfaceTexture.");//创建SurfaceTexturefinal SurfaceTexture surfaceTexture = new SurfaceTexture(0);surfaceTexture.detachFromGLContext();final SurfaceTextureRegistryEntry entry =new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture);Log.v(TAG, "New SurfaceTexture ID: " + entry.id());// 映射 textureId/entry.id() 和 SurfaceTexture的关系registerTexture(entry.id(), entry.textureWrapper());return entry;}
  • Flutter Engine中 platform_view_android_jni_impl.cc
static void RegisterTexture(JNIEnv* env,jobject jcaller,jlong shell_holder,jlong texture_id,jobject surface_texture) {ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterExternalTexture(static_cast<int64_t>(texture_id),                        //fml::jni::JavaObjectWeakGlobalRef(env, surface_texture)  //);
}
  • android_shell_holder.cc
fml::WeakPtr<PlatformViewAndroid> AndroidShellHolder::GetPlatformView() {FML_DCHECK(platform_view_);return platform_view_;
}
  • platform_view_android.cc
void PlatformViewAndroid::RegisterExternalTexture(int64_t texture_id,const fml::jni::JavaObjectWeakGlobalRef& surface_texture) {//AndroidExternalTextureGL 即 TextureRegisterTexture(std::make_shared<AndroidExternalTextureGL>(texture_id, surface_texture, std::move(jni_facade_)));
}
  • platform_view.cc
void PlatformView::RegisterTexture(std::shared_ptr<flutter::Texture> texture) {delegate_.OnPlatformViewRegisterTexture(std::move(texture));
}
  • shell.cc
// |PlatformView::Delegate|
void Shell::OnPlatformViewRegisterTexture(std::shared_ptr<flutter::Texture> texture) {FML_DCHECK(is_setup_);FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());task_runners_.GetRasterTaskRunner()->PostTask([rasterizer = rasterizer_->GetWeakPtr(), texture] {if (rasterizer) {if (auto* registry = rasterizer->GetTextureRegistry()) {registry->RegisterTexture(texture);}}});
}
  • texture.cc
void TextureRegistry::RegisterTexture(std::shared_ptr<Texture> texture) {if (!texture) {return;}mapping_[texture->Id()] = texture;
}

通过上述流程Flutter Engine层最终会把SurfaceTexture存到mapping_中

Texture的获取

Texture的获取是在Flutter端,通过textureId获取到mapping_中保存的Texture并且创建出一个TextureLayer映射到Flutter framework层

  • Flutter端TextureLayer
  @overridevoid addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {final Rect shiftedRect = layerOffset == Offset.zero ? rect : rect.shift(layerOffset);builder.addTexture(textureId,offset: shiftedRect.topLeft,width: shiftedRect.width,height: shiftedRect.height,freeze: freeze,filterQuality: filterQuality,);}
  • SceneBuilder
  void _addTexture(double dx, double dy, double width, double height, int textureId, bool freeze,int filterQuality) native 'SceneBuilder_addTexture'; // 调用Engine中的方法
  • scene_builder.cc
void SceneBuilder::addTexture(double dx,double dy,double width,double height,int64_t textureId,bool freeze,int filterQualityIndex) {auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);auto layer = std::make_unique<flutter::TextureLayer>(SkPoint::Make(dx, dy), SkSize::Make(width, height), textureId, freeze,sampling);AddLayer(std::move(layer));
}
  • texture_layer.cc
//GPU线程绘制时会调用该方法
void TextureLayer::Paint(PaintContext& context) const {TRACE_EVENT0("flutter", "TextureLayer::Paint");FML_DCHECK(needs_painting(context));//获取texture_registry中注册好的texturestd::shared_ptr<Texture> texture =context.texture_registry.GetTexture(texture_id_);if (!texture) {TRACE_EVENT_INSTANT0("flutter", "null texture");return;}texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_,context.gr_context, sampling_);
}

Texture的使用

Texture是封装的TextureLayer,通过上述流程分析后再来使用TextureLayer就比较简单了,可以通过MethodChannel的方式让Platform端创建一个Texture,最终返回一个textureId到Flutter端,Flutter端通过textureId的映射获取到Flutter Engine层创建好的Texture并包装成一个TextureLayer返回到Flutter framework层。

  1. 创建MethodChannel
  2. 创建SurfaceTexture
  3. 获取textureId创建Texture

以下是以获取摄像头预览为例:

  • Android 端示例代码
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Handler;
import android.util.Log;
import android.view.Surface;import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;import java.util.Arrays;import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.TextureRegistry;public class MainActivity extends FlutterActivity {public static final String TAG = "MainActivity";MethodChannel channel;private Handler backgroundHandler;@Overridepublic void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {super.configureFlutterEngine(flutterEngine);backgroundHandler = new Handler();channel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "flutter/texture/channel");channel.setMethodCallHandler((call, result) -> {switch (call.method) {case "createTexture":createTexture(flutterEngine,result);break;}});}private void createTexture(FlutterEngine flutterEngine,MethodChannel.Result result) {CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {result.success(-1);ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},1);return;}TextureRegistry.SurfaceTextureEntry entry =flutterEngine.getRenderer().createSurfaceTexture();SurfaceTexture surfaceTexture = entry.surfaceTexture();Surface surface = new Surface(surfaceTexture);try {cameraManager.openCamera("0",new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice device) {result.success(entry.id());CaptureRequest.Builder previewRequestBuilder = null;try {previewRequestBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);previewRequestBuilder.addTarget(surface);startPreview(result,device,surface,previewRequestBuilder,backgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {}@Overridepublic void onError(@NonNull CameraDevice cameraDevice, int errorCode) {Log.i(TAG, "open | onError");result.success(-1);}},backgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}private void startPreview(MethodChannel.Result result,CameraDevice device, Surface surface, CaptureRequest.Builder previewRequestBuilder, Handler backgroundHandler) {try {device.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {Log.i(TAG, "startPreview");try {session.setRepeatingRequest(previewRequestBuilder.build(),null,backgroundHandler );} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {Log.i(TAG, "startPreview Failed");}},backgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}
}
  • Flutter端示例代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';MethodChannel methodChannel = MethodChannel('flutter/texture/channel');void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {// This widget is the root of your application.@overrideWidget 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('createTexture').then((value) {textureId = value;setState(() {print('textureId ==== $textureId');});});}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[if (textureId > -1)Container(width: 300,height: 400,child: Texture(textureId: textureId,),)],),),floatingActionButton: FloatingActionButton(onPressed: _createTexture,tooltip: 'createTexture',child: Icon(Icons.add),), );}
}

上述代码在Android并未创建View,而是创建了SurfaceTexture与camera绑定后通过Texture的形式在Flutter端显示

Texture和PlatformView的区别

PlatformView是Flutter中嵌套Platform中的View,如:TextView。它们的区别在Texture是渲染层的东西,而PlatformView本质是一个View它拥有View所有的属性。

总结

本文通过源码分析以及简单的摄像头预览示例讲解了Flutter外接纹理的原理和使用方式,希望能够帮助部分刚接触Flutter开发的同学加深对Flutter外接纹理的认识。

Flutter 外接纹理相关推荐

  1. 在Flutter中嵌入Native组件的正确姿势

    引言 在漫长的从Native向Flutter过渡的混合工程时期,要想平滑地过渡,在Flutter中使用Native中较为完善的控件会是一个很好的选择.本文希望向大家介绍AndroidView的使用方式 ...

  2. 重磅发布 | 承载亿级流量的开发框架,闲鱼Flutter技术解析与实战大公开

    简介: 闲鱼是国内最早接触使用 Flutter 的团队,经过多次研讨验证并大规模上线,在App性能.稳定性.开发效率上收益甚多.现在,闲鱼将这个过程中的一手实践知识和技术沉淀,整理成册 --<F ...

  3. 《Flutter in action》开放下载!闲鱼Flutter企业级实践精选

    复制链接到浏览器 https://yq.aliyun.com/download/3792?utm_content=g_1000081730 下载. 闲鱼是国内最早使用Flutter的团队,也是Flut ...

  4. 一个优秀的可定制化Flutter相册组件,看这一篇就够了

    背景 在做图片.视频相关功能的时候,相册是一个绕不开的话题,因为大家基本都有从相册获取图片或者视频的需求.最直接的方式是调用系统相册接口,基本功能是满足的,一些高级功能就不行了,例如自定义UI.多选图 ...

  5. 燃烧我的卡路里 ---- Flutter瘦内存瘦包之图片组件

    作者:闲鱼技术-炉军 背景 在电商类APP里,图片到现在为止仍然是最重要的信息承载媒介,不得不说逛淘宝的过程,其实就是一个看图片的过程.而商品详情页中的图片,通常是页面中内存占用最多的内容,占用了整个 ...

  6. Android 11 Bata 正式发布!闲鱼最新升级版 Flutter 技术电子书开放下载

    点击"开发者技术前线",选择"星标" 在看|星标|留言,  真爱 回复"666",获取一份技术人专属大礼包 Android 11 Bata发 ...

  7. Flutter 1.17重磅发布​!闲鱼最新升级版 Flutter 技术电子书开放下载!

    近期Flutter 1.17发布,该版本包含大量修复内容,解决了自 1.12 稳定版本以来报告的 6,339 个问题,这么大的数字也是前所未有的.如此大的进展很大一部分归功于我们与 Nevercode ...

  8. Flutter 凉了吗?

    点击"开发者技术前线",选择"星标????" 在看|星标|留言,  真爱 作者 | Eric Grandt出品 | CSDN(ID:CSDNnews) 原文:h ...

  9. Flutter开始支持Windows了

    点击"开发者技术前线",选择"星标????" 在看|星标|留言,  真爱 本周,Flutter 团队发布了 Flutter Windows 的首个 Alpha ...

最新文章

  1. 算法----摆动序列
  2. 查看Tomcat版本及多版本切换
  3. 网络知识:各种缓存核心知识整理,值得收藏!
  4. java opencsv_用opencsv文件读写CSV文件
  5. 家用、商用、工业交换机的用途与区别
  6. CentOS(八)--crontab命令的使用方法
  7. linux创建文件内容三行,shell之创建文件及内容的方法示例
  8. easyUI按钮图表对照大全
  9. 第三天:完善数据层(controller)真正对接数据库Mysql
  10. 离散数学经典教材及资料(整理)
  11. C语言例题:数字重组
  12. 一个Keil工程包含的内容
  13. 嵩天python测验_北理 嵩天老师 Python程序设计 测验易错题总结
  14. 拓扑排序算法(1.0版)
  15. 2021泰迪杯数据分析技能赛A题:Python实现通讯产品销售和盈利能力分析(含原始数据)
  16. 分析 BAT 互联网巨头在大数据方向布局及大数据未来发展趋势
  17. 英语基础语法(九)-被动语态
  18. http常用请求头与响应头字段详解
  19. 鹏孚隆冲刺创业板上市:计划募资约7亿元,部分收入来自海外
  20. Common-pool源码编译——小例子

热门文章

  1. GAMS系列分享12—GAMS基础知识——模型和求解
  2. 如何实现PDF转Word
  3. 马来西亚吉隆坡召开的2010OpenWebAsia大会
  4. 自己做一个属于自己的论坛html网站
  5. Android系统启动流程完整分析(一)
  6. 苹果手机怎么设置来电铃声?4个步骤,快速学会
  7. 强大的全文本搜索工具——AnyTXT Searcher
  8. asp.net师电子化信息库的设计与实现(源代码+论文)ASP.NET汽车销售管理系统的设计与开发(源代码+论文)
  9. 曙光服务器怎么进入bios_在安装电脑系统进入bios界面时应该如何设置?怎么在bios界面把硬盘格式化?求电脑高手帮个忙!谢谢了… 曙光电脑进入bios...
  10. Vue CLI 3结合Lerna进行UI框架设计