import 'package:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'frame_analyzer.dart'; /// camera 插件封装:后摄图像流(对应 Kotlin CameraController)。 class AppCameraController { final List cameras; CameraController? controller; /// 图像流回调实际触发次数(诊断用,与 analyzer 帧计数区分) int streamCallbacks = 0; AppCameraController._(this.cameras); static Future create() async { final cameras = await availableCameras(); if (cameras.isEmpty) return null; return AppCameraController._(cameras); } bool get isInitialized => controller?.value.isInitialized ?? false; CameraController get currentController => controller ?? (throw StateError('camera not initialized')); /// 图像流送达时的旋转角(传感器 → 竖屏显示所需的顺时针旋转)。 /// 与 CameraX rotationDegrees 同公式;预览本身由平台旋转,检测框 overlay /// 用同一角度映射即可对齐。 int get rotationDegrees { final c = controller; if (c == null) return 0; final deviceDegrees = switch (c.value.deviceOrientation) { DeviceOrientation.portraitUp => 0, DeviceOrientation.landscapeLeft => 90, DeviceOrientation.portraitDown => 180, DeviceOrientation.landscapeRight => 270, }; final sensor = c.description.sensorOrientation; final isFront = c.description.lensDirection == CameraLensDirection.front; final degrees = (isFront ? sensor + deviceDegrees : sensor - deviceDegrees) % 360; return degrees < 0 ? degrees + 360 : degrees; } Future start(FrameAnalyzer analyzer) async { await stop(); final desc = cameras.firstWhere( (c) => c.lensDirection == CameraLensDirection.back, orElse: () => cameras.first); // iOS 用默认 bgra8888(420v 在部分 iOS 版本上视频输出静默不送帧), // Android 用 yuv420 多平面。 final fmt = defaultTargetPlatform == TargetPlatform.iOS ? ImageFormatGroup.bgra8888 : ImageFormatGroup.yuv420; final c = CameraController(desc, ResolutionPreset.high, enableAudio: false, imageFormatGroup: fmt); controller = c; await c.initialize(); // 相机(重新)启动后重置运动/背景参考与抽帧节流,避免旧场景残留 analyzer.reset(); analyzer.worker?.reset(); debugPrint('[camera] initialized, starting image stream'); try { await c.startImageStream((image) { streamCallbacks++; try { analyzer.analyze(image, rotationDegrees); } catch (e, st) { debugPrint('[camera] analyze error: $e\n$st'); analyzer.recordStreamError('analyze: $e'); } }); debugPrint('[camera] startImageStream ok'); } catch (e, st) { debugPrint('[camera] startImageStream FAILED: $e\n$st'); analyzer.recordStreamError('startImageStream: $e'); rethrow; } } Future stop() async { final c = controller; if (c == null) return; controller = null; try { await c.stopImageStream(); } catch (_) {} await c.dispose(); } }