add first version

This commit is contained in:
2026-03-02 22:57:35 +08:00
parent d517195df7
commit d9631fbb80
17 changed files with 1631 additions and 93 deletions

View File

@@ -0,0 +1,158 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;
import '../utils/app_config.dart';
class CameraService extends ChangeNotifier {
CameraController? _controller;
List<CameraDescription> _cameras = [];
bool _isInitialized = false;
bool _isStreaming = false;
Timer? _captureTimer;
CameraController? get controller => _controller;
bool get isInitialized => _isInitialized;
bool get isStreaming => _isStreaming;
/// 初始化相机
Future<void> initialize() async {
try {
_cameras = await availableCameras();
if (_cameras.isEmpty) {
print('没有可用的相机');
return;
}
// 使用后置摄像头
final camera = _cameras.firstWhere(
(camera) => camera.lensDirection == CameraLensDirection.back,
orElse: () => _cameras.first,
);
_controller = CameraController(
camera,
ResolutionPreset.medium, // 中等分辨率,平衡质量和性能
enableAudio: false,
imageFormatGroup: ImageFormatGroup.jpeg,
);
await _controller!.initialize();
_isInitialized = true;
notifyListeners();
print('相机初始化成功');
} catch (e) {
print('相机初始化失败: $e');
_isInitialized = false;
}
}
/// 拍摄单张照片
Future<Uint8List?> takePicture() async {
if (!_isInitialized || _controller == null) return null;
try {
final XFile image = await _controller!.takePicture();
final bytes = await image.readAsBytes();
// 压缩图像
final compressedBytes = await _compressImage(bytes);
return compressedBytes;
} catch (e) {
print('拍照失败: $e');
return null;
}
}
/// 开始实时捕获(定时拍照)
void startStreaming(Function(Uint8List) onImageCaptured) {
if (_isStreaming) return;
_isStreaming = true;
notifyListeners();
_captureTimer = Timer.periodic(AppConfig.captureInterval, (timer) async {
if (!_isStreaming) {
timer.cancel();
return;
}
final imageBytes = await takePicture();
if (imageBytes != null) {
onImageCaptured(imageBytes);
}
});
print('开始实时捕获,间隔: ${AppConfig.captureInterval.inMilliseconds}ms');
}
/// 停止实时捕获
void stopStreaming() {
_isStreaming = false;
_captureTimer?.cancel();
_captureTimer = null;
notifyListeners();
print('停止实时捕获');
}
/// 压缩图像
Future<Uint8List> _compressImage(Uint8List bytes) async {
return await compute(_compressImageIsolate, bytes);
}
/// 在独立 Isolate 中压缩图像(避免阻塞 UI
static Uint8List _compressImageIsolate(Uint8List bytes) {
// 解码图像
img.Image? image = img.decodeImage(bytes);
if (image == null) return bytes;
// 调整大小
if (image.width > AppConfig.imageMaxWidth ||
image.height > AppConfig.imageMaxHeight) {
image = img.copyResize(
image,
width: AppConfig.imageMaxWidth,
height: AppConfig.imageMaxHeight,
);
}
// 压缩为 JPEG
final compressed = img.encodeJpg(image, quality: AppConfig.imageQuality);
print('图像压缩: ${bytes.length} bytes -> ${compressed.length} bytes');
return Uint8List.fromList(compressed);
}
/// 切换摄像头
Future<void> switchCamera() async {
if (_cameras.length < 2) return;
final currentLens = _controller?.description.lensDirection;
final newCamera = _cameras.firstWhere(
(camera) => camera.lensDirection != currentLens,
orElse: () => _cameras.first,
);
await _controller?.dispose();
_controller = CameraController(
newCamera,
ResolutionPreset.medium,
enableAudio: false,
imageFormatGroup: ImageFormatGroup.jpeg,
);
await _controller!.initialize();
notifyListeners();
}
/// 释放资源
@override
void dispose() {
stopStreaming();
_controller?.dispose();
super.dispose();
}
}