最近咱们希望Flutter技能在桌面端的运用能有所突破,所以笔者跨进了本不了解的桌面端运用领域。今天给大家分享下咱们是怎么让Flutter怎么辨认外接windows10媒体设备,而且完结视频流烘托和摄影;从官方插件外界纹路platformVielinuxw实践,都尝试了一遍,最终选择了webRtc,整个预研手机淘宝进程好事多磨,学到了很多常识!

需求布景

需求是在win10和Android9的设备上支撑外接摄像头,可以进行实时拍摄,做一个类似相机的运用。
从技能流程上来分析,咱们需求辨认出相机设备,拿到媒体流信息然后做烘托(烘托机制一般经过外接纹路Texture去完结),最终捕获帧进行摄影/录制。Fandroid是什么系统lutter中,任何目标烘托后自然能拿android手机到RanderObject,只要有RanderObject这个实在linux的烘托目标,咱们就能进行相片的存储。
以上流程,理论上库现已帮咱们做好,可是桌面端的生态,往往没那么简略~~~

一、官方Plugin

Android端运用camera,windows运用camera_windows。官方的库对于内置相机的支撑做的很不错,直接引证后在手机和普通电脑上作用都很好;可是两个库都是明确不支撑外接设备,见issus-1、isLinuxsus-android的drawable类2,优先级分别是P4、P5,明显官方以为这些问题优先级不高。
而纵观整手机怎么定位别人手机个Flutter生态对USB外设的支撑,并没有一个官方的库,pub上的基本也是良莠不齐,大多只支撑单一渠道。

完结原理

  • Android端的cameralinux常用命令插件,运用原生Camera2 Api,经过TextureRegistry创立纹路,然后Flutter用Texture进行制作。
  1. 创立相机实例,返回textureId
// camera_android-0.9.8+3libsrcandroid_camera.dart
@override
Future<int> createCamera(
  CameraDescription cameraDescription,
  ResolutionPreset? resolutionPreset, {
  bool enableAudio = false,
}) async {
  try {
    final Map<String, dynamic>? reply = await _channel
        .invokeMapMethod<String, dynamic>('create', <String, dynamic>{
      'cameraName': cameraDescription.name,
      'resolutionPreset': resolutionPreset != null
          ? _serializeResolutionPreset(resolutionPreset)
          : null,
      'enableAudio': enableAudio,
    });
    return reply!['cameraId']! as int;
  } on PlatformException catch (e) {
    throw CameraException(e.code, e.message);
  }
}
  1. 预览控件返回Flutter Texturelinux重启命令 Widget,与原生返回的纹路id形成绑定,然后接纳纹路信linux系统息然后制作
// camera_android-0.9.8+3libsrcandroid_camera.dart
@override
Widget buildPreview(int cameraId) {
  return Texture(textureId: cameraId);
}
  1. Android端经过TextureRegistwindows更新有必要吗ry创立createSurfaceTexture,把textureId返回到Dart层。
// camera_android-0.9.8+3androidsrcmainjavaioflutterpluginscameraMethodCallHandlerImpl.java
private void instantiateCamera(MethodCall call, Result result) throws CameraAccessException {
  String cameraName = call.argument("cameraName");
  String preset = call.argument("resolutionPreset");
  boolean enableAudio = call.argument("enableAudio");
  TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture =
      textureRegistry.createSurfaceTexture();
  DartMessenger dartMessenger =
      new DartMessenger(
          messenger, flutterSurfaceTexture.id(), new Handler(Looper.getMainLooper()));
  CameraProperties cameraProperties =
      new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity));
  ResolutionPreset resolutionPreset = ResolutionPreset.valueOf(preset);
  camera =
      new Camera(
          activity,
          flutterSurfaceTexture,
          new CameraFeatureFactoryImpl(),
          dartMessenger,
          cameraProperties,
          resolutionPreset,
          enableAudio);
  Map<String, Object> reply = new HashMap<>();
  reply.put("cameraId", flutterSurfaceTexture.id());
  result.success(reply);
}

值得一提的是Flutter3.0后,官方的原生linux虚拟机制作方法现已抛弃了VirtualDisplay,拥抱TextureLayer,功能上现已优化了不少,让Flandroid是什么手机牌子utter的音视频烘托才能提升了不少。 但问题就是在instantlinux虚拟机iateCamera之前,官方在Camera2的完结上,没有对外界设备进行处理,然后查找不到对应的外接相机接口类型

  • Windows端的完结彻底相同,都是经过Texture做烘托,原因也是获取相机列表的时分没有做外接设备的完结,这里不在赘述。

解决计windows许可证即将过期怎么办

根据多端的camera接口做处理,把外设设备的逻手机搬家辑加上,应该就可以了。 在Texture纹路这块官方的完结是没有问题的。
当然这个思路我目前只停留在理论层面,并未真实去完结,原因如下:

  1. 两个库android手机都是设计原生常识,咱们维护成本会很大;
  2. 官方的库维护的很频频,后面更多优化还得看官方,很有android平板电脑价格可能哪个版别就得悉数推翻重新来一遍。

二、PlatformView

明确一个观点,这个计划不可落地。预研这个计linux系统安装划的原因是咱们自身现已有原生的代码接口是什么封装,根据CameraX的Android完结,我只需求在Plugin上注册下视图即可,具体完结代码如下:

  1. 注册视图
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    val key: String = "camera"
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "camera_plugin")
    channel.setMethodCallHandler(this)
    CameraInfoManager.getCameraInfoList().forEach {
        Log.d(TAG, "onAttachedToEngine: $it")
    }
    // 注册视图
    flutterPluginBinding.platformViewRegistry.registerViewFactory(
        key,
        CameraFactory(flutterPluginBinding.binaryMessenger)
    )
}
  1. 视图工厂
class CameraFactory(private val messenger: BinaryMessenger) :
    PlatformViewFactory(StandardMessageCodec.INSTANCE) {
    override fun create(context: Context?, id: Int, args: Any?): PlatformView {
        return CameraPlatformView(context!!)
    }
}
  1. 引进CameraX视图
class CameraPreView(context: Context, attrs: AttributeSet?) :
    LinearLayout(context, attrs), LifecycleOwner {
    private var camera: PreviewView
    private val mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
    init {
        val view: View = LayoutInflater.from(context).inflate(R.layout.layout_camera_preview, this)
        camera = view.findViewById(R.id.camera_preview_view)
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }
    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
        // 这里是封装好的CameraX预览视图
        CameraXPreview
            .bindLifecycle(this)
            .setPreviewView(camera)
            .setCameraId(0)
            .startPreview(context)
    }
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    }
    override fun getLifecycle(): Lifecycle = mLifecycleRegistry
}

问题清楚明了,Flutter引擎发动白屏300ms,视图同步产生延时,内存平均新windows11有必要升级吗增20M+,而且接口英文视图生命周期没法同手机耗电速度过快怎么办,都是致命问题。
根据上面的实践,Windows上咱们没有再做尝试了,Fail接口自动化

三、webRtc

上面两种计划都以失败告终后,大接口英文佬提到了webRlinux必学的60个命令tc,从基础协议出发,往往能解决核心问题。所以flutter_webrtc上场,WebRTC供给音视频的收集、编解码、网络传输、显示等功用,而且还支撑跨渠道:接口crc错误计数windows,linux,mac,android,现已被纳入被纳入W3C推荐标准。webRtc开发文档

  • 引证fllinux必学的60个命令utter_webrlinux系统tc这个库,其烘托原理依旧是外接纹路,运用linux重启命令方法查看官方的example实例即可;
  • 手机号最旺财的尾数点在完结摄影功用,摄影无非就是进行帧捕获,Android现已完结:
  final videoTrack = localStream!
      .getVideoTracks()
      .firstWhere((track) => track.kind == 'video');
  final frame = await videoTrack.captureFrame();
  // 运用image.memory即可烘托
  frameList = frame.asUint8List();
  • 而windows端很遗憾,还没有完结摄影功用,见issus;所以我想到了曲线救国,经过截取屏幕来保存图像由所以运用Texture烘托,通常的RenderRepaintBoundary+GlobalKey是没办法拿到RanderObject的!
    幸亏插件供给了截取屏幕的方法,也算完结曲线救国了。
try {
  var sources = await desktopCapturer.getSources(types: [SourceType.Window]);
  DesktopCapturerSource capture =
      sources.firstWhere((element) => element.name == 'my_camera');
  // 运用image.memory即可烘托
  frameList = capture.thumbnail;
  return;
} catch (e) {
  print(e.toString());
}

写在最终

到此,崎岖的外接相机预研之路告一段落。可是功能比起原生,真的差了一截,这让咱们意识到,在官方不支撑外接设备之前,针对此类需求接口是什么,还是少用Flutter来完结。
Flutter桌面运用尽管发布了Stable版别,但接口说句实话生态确实比移动端差了不少,这意味着咱们需求一起建设这接口英文个生态,可是趋势起来了,咱们也愿意社区共建!
另外插个题外话,关于上面windows截取屏幕的需求,其实是有issus未封闭的,7月1号下午刚参加了issue的讨论,傍晚作者就拉了pull requesAndroidt,而且更了一版,解了当务之急啊Windows!!!
怎么说呢,开源万岁,Respect!

Flutter 桌面端实践之辨认外接媒体设备

Flutter 桌面端实践之辨认外接媒体设备