ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

camera_windows 示例应用解读:平台实现测试应用的手动测试、集成测试与底层实现原理

camera_windows 示例应用解读:平台实现测试应用的手动测试、集成测试与底层实现原理 camera_windows 示例应用解读平台实现测试应用的手动测试、集成测试与底层实现原理【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址: https://gitcode.com/gh_mirrors/pl/plugins本指南以 Flutter 官方插件仓库中 camera_windows 示例应用 的定位文档为核心讲解这个名为 Platform Implementation Test App 的示例项目为什么存在、与普通camera使用示例的本质区别、它如何通过camera_platform_interface直接驱动 Windows 相机平台实现以及配套的自动化集成测试与原生层Media Foundation实现链路。读完本文你将理解如何运行和扩展这个测试应用来验证 camera_windows 插件的各项能力并掌握在 Windows 平台上使用相机 API 的能力边界。一、先明确定位这是平台实现测试应用不是使用示例示例目录中的 README.md 开头就给出了明确的定位声明这是一个用于对该平台实现camera_windows进行手动测试和自动化集成测试的测试应用它并不打算演示该包的常规使用方法因为插件客户端的预期用法是使用面向应用的camera包。这一点非常关键它划定了三条边界服务对象是平台实现包本示例针对的是 camera_windows 这个 Windows 平台实现而非 camera 这个面向最终开发者的统一 API 包。不示范业务集成示例直接调用平台接口层 APICameraPlatform.instance而不是camera包提供的CameraController高层抽象因此它不能作为普通 App 集成相机的模板。目标读者明确除非你正在修改 camera_windows 实现包例如为其新增功能、修复 Bug 或调整原生层行为否则这个示例与你大概率不相关。把这段定位文字与仓库结构对照即可印证示例应用直接依赖 camera_platform_interface 与 camera_windows后者通过 path 依赖指向插件本体而普通使用示例则会依赖camera主包。二、示例应用的工程结构示例应用位于 packages/camera/camera_windows/example其顶层结构如下路径作用lib/main.dart手动测试 UI枚举相机、初始化、预览、拍照、录像、切换分辨率等integration_test/camera_test.dart自动化集成测试用例test_driver/integration_test.dartflutter drive模式下的测试入口pubspec.yaml依赖与工程声明windows/Windows RunnerCMake 工程供示例在本机运行依赖配置的关键点example/pubspec.yaml 中两处值得注意dependencies: camera_platform_interface: ^2.1.2 camera_windows: # When depending on this package from a real application you should use: # camera_windows: ^x.y.z path: ../使用path: ../指向插件本体示例应用与插件源码在同一个仓库内通过相对路径依赖当前工作区版本的插件保证测试的是正在修改的代码文件中的注释也明确提示真实应用接入时应使用版本约束如camera_windows: ^x.y.z并参见 Dart 官方关于版本约束的说明。直接依赖camera_platform_interface这是平台实现测试应用区别于使用示例的最直接证据——测试代码绕过高层camera包直接与平台接口交互从而能逐一验证平台实现的每个方法。dev_dependencies 中引入integration_test与flutter_test用于承载 integration_test/camera_test.dart 中的自动化用例。三、手动测试示例 UI 覆盖的平台 API 全景lib/main.dart 是一个状态管理清晰的 Material 应用其 UI 依次提供相机信息文本、可用相机重查按钮、分辨率预设下拉框、音频开关、创建/释放相机、拍照、暂停/恢复预览、录制视频、定时录制 5 秒、切换相机等控件。下面按平台 API 的调用链拆解它覆盖的能力并与 Dart 端平台实现 一一对应。1. 枚举可用相机availableCameras_fetchCameras()调用CameraPlatform.instance.availableCameras()返回ListCameraDescription若列表为空则提示No available cameras否则取_cameraIndex % cameras.length索引处的相机并展示其name。若抛出PlatformException则以code: message形式显示失败原因。对应实现CameraWindows.availableCameras()camera_windows.dart通过 MethodChannelplugins.flutter.io/camera_windows调用availableCameras方法把原生返回的name、lensFacing、sensorOrientation映射为CameraDescription其中lensFacing字符串由parseCameraLensDirection解析为front/back/external三种枚举。2. 创建并初始化相机createCamera / initializeCamera_initializeCamera()展示了测试应用的标准做法final CameraDescription camera _cameras[cameraIndex]; cameraId await CameraPlatform.instance.createCamera( camera, _resolutionPreset, enableAudio: _recordAudio, ); _errorStreamSubscription CameraPlatform.instance .onCameraError(cameraId) .listen(_onCameraError); _cameraClosingStreamSubscription CameraPlatform.instance .onCameraClosing(cameraId) .listen(_onCameraClosing); final FutureCameraInitializedEvent initialized CameraPlatform.instance.onCameraInitialized(cameraId).first; await CameraPlatform.instance.initializeCamera(cameraId); final CameraInitializedEvent event await initialized; _previewSize Size(event.previewWidth, event.previewHeight);要点createCamera返回一个cameraIdint作为后续所有操作句柄。若未指定resolutionPresetDart 层会把null序列化后传给原生原生侧选择最高可用分辨率camera_windows.dart 注释明确说明。事件订阅必须在initializeCamera之前完成示例先订阅onCameraError、onCameraClosing再通过onCameraInitialized(cameraId).first预取初始化事件最后才调用initializeCamera并 await 初始化结果拿到previewWidth/previewHeight。这正是平台事件流驱动的典型时序也解释了为什么camera_windows.dart中使用广播型StreamControllercameraEventStreamController并在initializeCamera内部向流中添加CameraInitializedEvent。初始化失败时示例会尝试dispose(cameraId)清理句柄并把_initialized等状态复位同时展示Failed to initialize camera: code: description。3. 预览buildPreview 与 Texture_buildPreview()直接返回CameraPlatform.instance.buildPreview(_cameraId)UI 中用AspectRatio宽高比来自初始化事件与BoxConstraints(maxHeight: 500)约束预览控件并在下方显示实际预览分辨率文本。对应实现camera_windows.dartoverride Widget buildPreview(int cameraId) { return Texture(textureId: cameraId); }Windows 实现把cameraId直接映射为 FlutterTexture的textureId。原生侧则由TextureHandler配合 Flutter 的TextureRegistrar注册纹理将 Media Foundation 采集到的预览帧推送到 GPU 纹理上详见 windows/texture_handler.cpp、windows/preview_handler.cpp。这解释了为什么示例中创建相机与预览是分离的两步textureId实际由原生create流程分配并回传。4. 拍照takePicture_takePicture()调用takePicture(_cameraId)后通过 SnackBar 显示返回的XFile路径Picture captured to: ...。原生侧GetFilePathForPicture()camera_plugin.cpp调用SHGetKnownFolderPath(FOLDERID_Pictures, KF_FLAG_CREATE, ...)获取系统图片文件夹并以PhotoCapture_ 时间戳含毫秒保证文件名唯一.jpeg的格式生成保存路径。5. 录像startVideoRecording / stopVideoRecording 与定时录制UI 提供两种录像方式_toggleRecord()无时长限制的起停录制停止时拿到XFile并显示保存路径_recordTimed(5)调用startVideoRecording(_cameraId, maxVideoDuration: Duration(seconds: 5))并监听onVideoRecordedEvent(_cameraId).first由原生侧在达到时长后自动停止并通过video_recorded事件回传VideoRecordedEvent含文件路径与时长。对应实现startVideoRecording内部委托startVideoCapturing(VideoCaptureOptions(...))通过maxVideoDuration?.inMilliseconds传给原生camera_windows.dart。原生侧视频保存路径使用FOLDERID_VideosVideoCapture_ 时间戳 .mp4kVideoCaptureExtension见 camera_plugin.cpp。video_recorded与camera_closing、error等事件统一由handleCameraMethodCall解析并注入广播流camera_windows.dart。6. 暂停/恢复预览pausePreview / resumePreview_togglePreview()在两种状态间切换对应pausePreview/resumePreview两个方法均以cameraId为参数。注意这两个方法与takePicture、stopVideoRecording一样最终走的是 MethodChannel 的pausePreview/resumePreview调用camera_plugin.cpp 中的kPausePreview、kResumePreview。7. 切换相机与分辨率/音频变更时的重建策略_switchCamera()轮询选择下一个CameraDescription若相机已初始化则先_disposeCurrentCamera()再重新枚举、重新初始化。_onResolutionChange()/_onAudioChange()修改_resolutionPreset/_recordAudio后若相机已初始化同样先 dispose 再初始化——因为createCamera的分辨率与音频参数是创建期绑定的Windows 实现不支持运行时热切换分辨率。这与 camera_windows.dart 中onCameraResolutionChanged返回空流的实现互相印证Windows API 不会在捕获过程中自动改变分辨率因此平台层从不发送该事件。8. 释放dispose_disposeCurrentCamera()调用CameraPlatform.instance.dispose(_cameraId)并复位全部状态_initialized、_cameraId、_previewSize、_recording、_previewPaused等。Dart 实现在 dispose 后还会移除对应cameraId的专属 MethodChannelplugins.flutter.io/camera_windows/camera$cameraId避免残留监听camera_windows.dart。四、自动化集成测试integration_test 与 flutter drive除手动 UI 外示例还附带自动化集成测试 integration_test/camera_test.dart其文件头注释特别说明这些集成测试目前并未覆盖大部分功能与代码路径因为只有测试环境中存在一台或多台相机时才能测试覆盖更全面的原生单元测试位于插件原生实现一侧即 windows/test 下的camera_plugin_test.cpp、camera_test.cpp、capture_controller_test.cpp等。测试用例主要验证未创建相机时的错误路径group(initializeCamera, () { testWidgets(throws exception if camera is not created, (WidgetTester _) async { final CameraPlatform camera CameraPlatform.instance; expect(() async camera.initializeCamera(1234), throwsA(isACameraException())); }); });initializeCamera对未创建的相机抛出CameraExceptiontakePicture、startVideoRecording、stopVideoRecording、pausePreview、resumePreview对未创建的相机抛出PlatformExceptiononDeviceOrientationChanged发出初始的DeviceOrientationChangedEvent(DeviceOrientation.landscapeRight)——该用例直接对应 camera_windows.dart 中强制设备方向为横屏的兜底实现TODO 注释指向设备方向检测尚未实现。运行方式基于示例的目录结构# 在 example 目录下以 Windows 设备为目标运行集成测试 flutter test integration_test/camera_test.dart -d windows # 或使用 flutter drive 模式对应 test_driver/integration_test.dart flutter drive --drivertest_driver/integration_test.dart \ --targetintegration_test/camera_test.dart -d windowstest_driver/integration_test.dart 仅一行integrationDriver()调用是flutter drive模式的标准入口integration_test相关基础设施的说明可参见 integration_test 目录。五、错误处理onCameraError 与只能释放的现实camera_windows 插件 README 的 Error handling 一节是示例之外最重要的配套说明相机错误可以通过平台实现的onCameraError方法监听监听错误非常重要在某些情况下释放dispose相机是重置状态的唯一途径。示例 main.dart 的_onCameraError演示了标准处理流程void _onCameraError(CameraErrorEvent event) { if (mounted) { _scaffoldMessengerKey.currentState?.showSnackBar( SnackBar(content: Text(Error: ${event.description}))); // Dispose camera on camera error as it can not be used anymore. _disposeCurrentCamera(); _fetchCameras(); } }错误事件同样由原生层通过error方法经专属相机 Channel 上报handleCameraMethodCall将其封装为CameraErrorEvent(cameraId, description)camera_windows.dart。此外根据 CHANGELOG 0.2.0 版本记录CameraException.code在权限被拒绝时为CameraAccessDenied在捕获过程中出错时为camera_error可作为业务侧错误分支的判别依据。六、底层链路MethodChannel 与原生方法分发把示例应用调用的每个 API 串联起来就得到完整的 Windows 平台实现链路Dart 层camera_windows.dart 中的CameraWindows extends CameraPlatform通过plugins.flutter.io/camera_windows这一全局 MethodChannel 与原生通信每个相机创建后还会建立专属 Channelplugins.flutter.io/camera_windows/camera$cameraId用于回传事件。原生层windows/camera_plugin.cpp 的CameraPlugin::HandleMethodCall按方法名分发availableCameras、create、initialize、takePicture、startVideoRecording、stopVideoRecording、pausePreview、resumePreview、dispose等调用kCreateMethod、kTakePictureMethod等常量定义于文件顶部。采集引擎原生侧基于 Windows Media Foundationmfapi.h/mfidl.h枚举视频捕获设备MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME、MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK并通过 capture_controller.cpp、photo_handler.cpp、record_handler.cpp、preview_handler.cpp 分别管理预览、拍照与录像会话。七、Windows 平台的能力边界当前未实现与受限功能camera_windows 插件 README 用整节 Missing features on the Windows platform 明确列出开发中插件的能力边界测试应用的存在正是为了在这些能力落地前后持续验证。这些限制同样能从 camera_windows.dart 的代码直接确认能力状态代码/源码依据设备方向检测未实现onDeviceOrientationChanged()强制返回landscapeRightcamera_windows.dart锁定/解锁捕获方向未实现lockCaptureOrientation/unlockCaptureOrientation抛出UnimplementedError视频录制暂停/恢复不支持Win32 API 限制pauseVideoRecording/resumeVideoRecording抛出UnsupportedErrorcamera_windows.dart曝光模式/偏移未实现setExposureMode/setExposureOffset抛出UnimplementedErrorgetMinExposureOffset返回 0.0、getMaxExposureOffset返回 0.0、getExposureOffsetStepSize返回 1.0 仅为兼容占位曝光点不支持Win32 API 限制setExposurePoint抛出UnsupportedError含参数范围断言x/y 需在 0~1对焦模式/对焦点未实现/不支持setFocusMode抛出UnimplementedErrorsetFocusPoint抛出UnsupportedError变焦未实现getMinZoomLevel/getMaxZoomLevel返回 1.0 占位setZoomLevel抛出UnimplementedError闪光灯模式未实现setFlashMode抛出UnimplementedError帧流式输出image streaming未实现startVideoCapturing在传入streamCallback/streamOptions时抛出UnimplementedErrorcamera_windows.dart这些UnimplementedError/UnsupportedError与占位返回值是示例测试应用 UI 中未提供对应控件的直接原因也是后续为插件贡献功能的入口点各方法上方均有 TODO 注释标注。需要特别说明插件 README 明确标注该插件仍在开发中上述限制清单以当前仓库代码为准能力会随版本迭代变化。八、与 camera 主包的关系及真实接入方式最后回到定位问题为什么示例不示范真实接入因为 camera_windows不是camera插件的 endorsed官方背书实现也就是说在 pubspec 中仅声明camera依赖不会自动引入 Windows 实现必须显式添加camera_windows。真实应用的接入方式是dependencies: camera: ^x.y.z # 面向应用的统一 API camera_windows: ^0.2.0 # 显式声明 Windows 平台实现当前为未背书实现之后仍使用camera包暴露的高层 API如CameraController进行开发camera包通过camera_platform_interface在 Windows 上路由到CameraWindows其registerWith()将CameraPlatform.instance设为CameraWindows()见 camera_windows.dart。而对于需要验证或修改平台实现本身的开发者本示例则提供了完整的三层验证手段手动验证运行示例 UI逐一操作枚举、初始化、预览、拍照、录像、暂停预览、切分辨率、切相机直观检查每个平台 API 的行为自动化验证执行 integration_test/camera_test.dart 中的错误路径用例以及 windows/test 下覆盖更细的原生单元测试源码级验证对照 lib/camera_windows.dart 与 windows/camera_plugin.cpp 确认 Dart 与原生两侧的方法契约一致。围绕 camera_windows 示例应用 的这份说明既是该插件质量保障体系手动 集成 单元测试的入口也是理解 Flutter Federated Plugins 架构中平台实现包与测试应用如何组织的典型范例。【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址: https://gitcode.com/gh_mirrors/pl/plugins创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表