From 14f4b51bf968de796170f504310a9a13e551bef8 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 19 Jun 2023 16:26:04 +0930 Subject: [PATCH 01/27] Initial commit of camera changes --- packages/camera/camera/example/pubspec.yaml | 5 +++ .../camera/lib/src/camera_controller.dart | 31 +++++++++++++++++- .../camera_android/example/pubspec.yaml | 5 +++ .../example/pubspec.yaml | 5 +++ .../camera_avfoundation/example/pubspec.yaml | 5 +++ .../camera_avfoundation/ios/Classes/FLTCam.h | 4 +++ .../lib/src/avfoundation_camera.dart | 9 +++++- .../method_channel/method_channel_camera.dart | 9 +++++- .../platform_interface/camera_platform.dart | 8 ++++- .../lib/src/types/aspect_ratio_preset.dart | 32 +++++++++++++++++++ .../lib/src/types/capture_mode.dart | 32 +++++++++++++++++++ .../lib/src/types/types.dart | 2 ++ .../camera/camera_web/example/pubspec.yaml | 5 +++ .../camera_windows/example/pubspec.yaml | 5 +++ 14 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart create mode 100644 packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 3b23ee596ded..ddeaebf989d9 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera: {path: ../../../camera/camera}, camera_android: {path: ../../../camera/camera_android}, camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_web: {path: ../../../camera/camera_web}} diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 2e8d0df8d748..58728491bb64 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -234,7 +234,9 @@ class CameraController extends ValueNotifier { /// Creates a new camera controller in an uninitialized state. CameraController( CameraDescription description, - this.resolutionPreset, { + this.resolutionPreset, + this.captureMode, + this.aspectRatioPreset, { this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(description)); @@ -242,6 +244,9 @@ class CameraController extends ValueNotifier { /// The properties of the camera device controlled by this controller. CameraDescription get description => value.description; + /// The capture mode this controller should be using. + final CaptureMode captureMode; + /// The resolution this controller is targeting. /// /// This resolution preset is not guaranteed to be available on the device, @@ -250,6 +255,15 @@ class CameraController extends ValueNotifier { /// See also: [ResolutionPreset]. final ResolutionPreset resolutionPreset; + /// The aspect ratio this controller is targeting. + /// + /// This aspect ratio preset is not guaranteed to be available on the device + /// for the selected resolution or capture mode. + /// if unavailable a different aspect ratio will be used. + /// + /// See also: [AspectRatioPreset]. + final AspectRatioPreset aspectRatioPreset; + /// Whether to include audio when recording a video. final bool enableAudio; @@ -316,6 +330,8 @@ class CameraController extends ValueNotifier { _cameraId = await CameraPlatform.instance.createCamera( description, resolutionPreset, + captureMode, + aspectRatioPreset, enableAudio: enableAudio, ); @@ -529,6 +545,12 @@ class CameraController extends ValueNotifier { 'startVideoRecording was called when a recording is already started.', ); } + if (captureMode == CaptureMode.photo) { + throw CameraException( + 'The camera is configured for still images.', + 'startVideoRecording was called when the camera was configured for still images.', + ); + } Function(CameraImageData image)? streamCallback; if (onAvailable != null) { @@ -610,6 +632,13 @@ class CameraController extends ValueNotifier { 'resumeVideoRecording was called when no video is recording.', ); } + if (captureMode == CaptureMode.photo) { + throw CameraException( + 'The camera is configured for still images.', + 'resumeVideoRecording was called when the camera was configured for still images.', + ); + } + try { await CameraPlatform.instance.resumeVideoRecording(_cameraId); value = value.copyWith(isRecordingPaused: false); diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 40f0eedbd821..0f4bb709e663 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -32,3 +32,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_android: {path: ../../../camera/camera_android}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 663a58b8470b..bec47e4077c9 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -28,3 +28,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_android_camerax: {path: ../../../camera/camera_android_camerax}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 3680ea2419c1..39393cae940c 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index fbf4ef4882cb..dc5ec4fc65d4 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, copy) void (^onFrameAvailable)(void); @property(nonatomic) FLTThreadSafeMethodChannel *methodChannel; @property(assign, nonatomic) FLTResolutionPreset resolutionPreset; +@property(assign, nonatomic) FLTCaptureMode captureMode; +@property(assign, nonatomic) FLTAspectRatioPreset aspectRatioPreset; @property(assign, nonatomic) FLTExposureMode exposureMode; @property(assign, nonatomic) FLTFocusMode focusMode; @property(assign, nonatomic) FLTFlashMode flashMode; @@ -40,6 +42,8 @@ NS_ASSUME_NONNULL_BEGIN /// @param error report to the caller if any error happened creating the camera. - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset + captureMode:(NSString *)captureMode + aspectRatioPreset:(NSString *)aspectRatioPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 33f550b3211b..b5baf39fe78f 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -94,7 +94,9 @@ class AVFoundationCamera extends CameraPlatform { @override Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, { + ResolutionPreset? resolutionPreset, + CaptureMode? captureMode, + AspectRatioPreset? aspectRatioPreset, { bool enableAudio = false, }) async { try { @@ -104,6 +106,11 @@ class AVFoundationCamera extends CameraPlatform { 'resolutionPreset': resolutionPreset != null ? _serializeResolutionPreset(resolutionPreset) : null, + 'captureMode': + captureMode != null ? serializeCaptureMode(captureMode) : null, + 'aspectRatioPreset': aspectRatioPreset != null + ? serializeAspectRatioPreset(aspectRatioPreset) + : null, 'enableAudio': enableAudio, }); diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 35ece04fdf52..287b6c648f4c 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -86,7 +86,9 @@ class MethodChannelCamera extends CameraPlatform { @override Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, { + ResolutionPreset? resolutionPreset, + CaptureMode? captureMode, + AspectRatioPreset? aspectRatioPreset, { bool enableAudio = false, }) async => createCameraWithSettings( @@ -110,6 +112,11 @@ class MethodChannelCamera extends CameraPlatform { 'fps': mediaSettings.fps, 'videoBitrate': mediaSettings.videoBitrate, 'audioBitrate': mediaSettings.audioBitrate, + 'captureMode': + captureMode != null ? serializeCaptureMode(captureMode) : null, + 'aspectRatioPreset': aspectRatioPreset != null + ? serializeAspectRatioPreset(aspectRatioPreset) + : null, 'enableAudio': mediaSettings.enableAudio, }); diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index b74cb4203324..3b043fce1110 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -49,7 +49,9 @@ abstract class CameraPlatform extends PlatformInterface { /// Creates an uninitialized camera instance and returns the cameraId. Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, { + ResolutionPreset? resolutionPreset, + CaptureMode? captureMode, + AspectRatioPreset? aspectRatioPreset, { bool enableAudio = false, }) { throw UnimplementedError('createCamera() is not implemented.'); @@ -273,6 +275,10 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('setZoomLevel() is not implemented.'); } + Future setCaptureMode(int cameraId, CaptureMode mode) { + throw UnimplementedError('setCaptureMode() is not implemented.'); + } + /// Pause the active preview on the current frame for the selected camera. Future pausePreview(int cameraId) { throw UnimplementedError('pausePreview() is not implemented.'); diff --git a/packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart b/packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart new file mode 100644 index 000000000000..1878e82e1ec4 --- /dev/null +++ b/packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart @@ -0,0 +1,32 @@ +enum AspectRatioPreset { + /// 4:3 + /// Only supported in photo capture mode. + standard, + + /// 16:9 + widescreen, +} + +/// Returns the aspect ratio preset as a String. +/// +String serializeAspectRatioPreset(AspectRatioPreset aspectRatioPreset) { + switch (aspectRatioPreset) { + case AspectRatioPreset.standard: + return 'standard'; + case AspectRatioPreset.widescreen: + return 'widescreen'; + } +} + +/// Returns the aspect ratio preset for a given String. +/// +AspectRatioPreset deserializeAspectRatioPreset(String str) { + switch (str) { + case 'standard': + return AspectRatioPreset.standard; + case 'widescreen': + return AspectRatioPreset.widescreen; + default: + throw ArgumentError('"$str" is not a valid AspectRatioPreset value'); + } +} diff --git a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart new file mode 100644 index 000000000000..39b69a28a9de --- /dev/null +++ b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart @@ -0,0 +1,32 @@ +/// The mode of capture to use when taking a picture or video. +enum CaptureMode { + /// Capture a photo. + photo, + + /// Capture a video, however this allows the user to take photos while recording. + video, +} + +/// Returns the capture mode as a String. +/// +String serializeCaptureMode(CaptureMode captureMode) { + switch (captureMode) { + case CaptureMode.photo: + return 'photo'; + case CaptureMode.video: + return 'video'; + } +} + +/// Returns the capture mode for a given String. +/// +CaptureMode deserializeCaptureMode(String str) { + switch (str) { + case 'photo': + return CaptureMode.photo; + case 'video': + return CaptureMode.video; + default: + throw ArgumentError('"$str" is not a valid CaptureMode value'); + } +} diff --git a/packages/camera/camera_platform_interface/lib/src/types/types.dart b/packages/camera/camera_platform_interface/lib/src/types/types.dart index f9a81559d680..dc3e8d64b531 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/types.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/types.dart @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +export 'aspect_ratio_preset.dart'; export 'camera_description.dart'; export 'camera_exception.dart'; export 'camera_image_data.dart'; +export 'capture_mode.dart'; export 'exposure_mode.dart'; export 'flash_mode.dart'; export 'focus_mode.dart'; diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index fef2a773262b..3789f7f7189d 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -20,3 +20,8 @@ dev_dependencies: integration_test: sdk: flutter mocktail: 0.3.0 + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_web: {path: ../../../camera/camera_web}} diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index 1dd6f634e95a..e722c62d9b30 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -27,3 +27,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_windows: {path: ../../../camera/camera_windows}} From ee2338ca05ca82eef8eed930ea6abe88df2bd38a Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Thu, 22 Jun 2023 12:33:56 +0930 Subject: [PATCH 02/27] Continuing work on aspect ratio and capture mode --- packages/camera/camera/lib/camera.dart | 1 + packages/camera/camera/pubspec.yaml | 12 ++- .../io/flutter/plugins/camera/Camera.java | 5 + .../lib/src/android_camera.dart | 9 +- packages/camera/camera_android/pubspec.yaml | 3 +- .../ios/Classes/CameraPlugin.m | 4 + .../ios/Classes/CameraProperties.h | 32 ++++++ .../camera_avfoundation/ios/Classes/FLTCam.m | 98 ++++++++++++++++++- .../camera/camera_avfoundation/pubspec.yaml | 3 +- packages/camera/camera_web/pubspec.yaml | 3 +- 10 files changed, 158 insertions(+), 12 deletions(-) diff --git a/packages/camera/camera/lib/camera.dart b/packages/camera/camera/lib/camera.dart index 900c2633a5d7..6189aeb55253 100644 --- a/packages/camera/camera/lib/camera.dart +++ b/packages/camera/camera/lib/camera.dart @@ -7,6 +7,7 @@ export 'package:camera_platform_interface/camera_platform_interface.dart' CameraDescription, CameraException, CameraLensDirection, + CaptureMode, FlashMode, ExposureMode, FocusMode, diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index c246e4c232ab..02f0597c72cb 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -21,10 +21,14 @@ flutter: default_package: camera_web dependencies: - camera_android: ^0.10.7 - camera_avfoundation: ^0.9.13 - camera_platform_interface: ^2.5.0 - camera_web: ^0.3.1 + camera_android: + path: ../../camera/camera_android + camera_avfoundation: + path: ../../camera/camera_avfoundation + camera_platform_interface: + path: ../../camera/camera_platform_interface + camera_web: + path: ../../camera/camera_web flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 5e526975172e..0f990fad5efd 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -112,6 +112,11 @@ class Camera private final SurfaceTextureEntry flutterTexture; private final ResolutionPreset resolutionPreset; + + private final CaptureMode captureMode; + + private final AspectRatioPreset aspectRatioPreset; + private final boolean enableAudio; private final Context applicationContext; final DartMessenger dartMessenger; diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index eca1003247c6..9e93bf0d679f 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -94,7 +94,9 @@ class AndroidCamera extends CameraPlatform { @override Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, { + ResolutionPreset? resolutionPreset, + CaptureMode? captureMode, + AspectRatioPreset? aspectRatioPreset, { bool enableAudio = false, }) async { try { @@ -104,6 +106,11 @@ class AndroidCamera extends CameraPlatform { 'resolutionPreset': resolutionPreset != null ? _serializeResolutionPreset(resolutionPreset) : null, + 'captureMode': + captureMode != null ? serializeCaptureMode(captureMode) : null, + 'aspectRatioPreset': aspectRatioPreset != null + ? serializeAspectRatioPreset(aspectRatioPreset) + : null, 'enableAudio': enableAudio, }); diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index f920e2336fa2..61f8e159facf 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -19,7 +19,8 @@ flutter: dartPluginClass: AndroidCamera dependencies: - camera_platform_interface: ^2.5.0 + camera_platform_interface: + path: ../../camera/camera_platform_interface flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index 874f37b6c4fc..225a104f0826 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -304,10 +304,14 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea NSString *cameraName = createMethodCall.arguments[@"cameraName"]; NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; + NSString *captureMode = createMethodCall.arguments[@"captureMode"]; + NSString *aspectRatioPreset = createMethodCall.arguments[@"aspectRatioPreset"]; NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; NSError *error; FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName resolutionPreset:resolutionPreset + captureMode: captureMode + aspectRatioPreset: aspectRatioPreset enableAudio:[enableAudio boolValue] orientation:[[UIDevice currentDevice] orientation] captureSessionQueue:strongSelf.captureSessionQueue diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h index 4d0818dc8164..ff0f767e3268 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h @@ -120,6 +120,38 @@ typedef NS_ENUM(NSInteger, FLTResolutionPreset) { */ extern FLTResolutionPreset FLTGetFLTResolutionPresetForString(NSString *preset); +#pragma mark - capture mode + +/** + * Represents camera's capture mode. Mirrors CaptureMode in camera.dart. + */ +typedef NS_ENUM(NSInteger, FLTCaptureMode) { + FLTCaptureModePhoto, + FLTCaptureModeVideo, +}; + +/** + * Gets FLTCaptureMode from its string representation. + * @param mode a string representation of FLTCaptureMode. + */ +extern FLTCaptureMode FLTGetFLTCaptureModeForString(NSString *mode); + +#pragma mark - aspect ratio preset + +/** + * Represents camera's aspect ratio preset. Mirrors AspectRatioPreset in camera.dart. + */ +typedef NS_ENUM(NSInteger, FLTAspectRatioPreset) { + FLTAspectRatioPresetStandard, + FLTAspectRatioPresetWideScreen, +}; + +/** + * Gets FLTAspectRatioPreset from its string representation. + * @param preset a string representation of FLTAspectRatioPreset. + */ +extern FLTAspectRatioPreset FLTGetFLTAspectRatioPresetForString(NSString *preset); + #pragma mark - video format /** diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 1f9404033204..a66c63262ccd 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -94,12 +94,16 @@ @implementation FLTCam - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset + captureMode: (NSString *)captureMode + aspectRatioPreset: (NSString *)aspectRatioPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error { return [self initWithCameraName:cameraName resolutionPreset:resolutionPreset + captureMode: (NSString *)captureMode + aspectRatioPreset: (NSString *)aspectRatioPreset enableAudio:enableAudio orientation:orientation videoCaptureSession:[[AVCaptureSession alloc] init] @@ -110,6 +114,8 @@ - (instancetype)initWithCameraName:(NSString *)cameraName - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset + captureMode: (NSString *)captureMode + aspectRatioPreset: (NSString *)aspectRatioPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation videoCaptureSession:(AVCaptureSession *)videoCaptureSession @@ -168,7 +174,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName _motionManager = [[CMMotionManager alloc] init]; [_motionManager startAccelerometerUpdates]; - if (![self setCaptureSessionPreset:_resolutionPreset withError:error]) { + if (![self setCaptureSessionWithResolutionPreset:_resolutionPreset withCaptureMode:_captureMode withAspectRatio:_aspectRatioPreset withError:error]) { return nil; } [self updateOrientation]; @@ -345,7 +351,8 @@ - (NSString *)getTemporaryFilePathWithExtension:(NSString *)extension return file; } -- (BOOL)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { + +- (BOOL)setCaptureSessionForWideScreen:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { switch (resolutionPreset) { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: @@ -357,8 +364,8 @@ - (BOOL)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset withError: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) { _videoCaptureSession.sessionPreset = AVCaptureSessionPresetHigh; _previewSize = - CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, - _captureDevice.activeFormat.highResolutionStillImageDimensions.height); + CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, + _captureDevice.activeFormat.highResolutionStillImageDimensions.height); break; } case FLTResolutionPresetVeryHigh: @@ -399,7 +406,90 @@ - (BOOL)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset withError: return NO; } } +} + +- (void)setCaptureSessionForStandard:(FLTResolutionPreset)resolutionPreset { + switch (resolutionPreset) { + case FLTResolutionPresetMax: + case FLTResolutionPresetUltraHigh: + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetPhoto]) { + _videoCaptureSession.sessionPreset = AVCaptureSessionPresetPhoto; + break; + } + case FLTResolutionPresetVeryHigh: + // Selects the appropriate 1080p resolution to match the desired aspect ratio. + for (AVCaptureDeviceFormat *format in _captureDevice.formats) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + if (dimensions.height == 1080) { + if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { + if ([_captureDevice lockForConfiguration:nil]) { + _captureDevice.activeFormat = format; + [_captureDevice unlockForConfiguration]; + //_videoCaptureSession.sessionPreset = AVCaptureSessionPresetInputPriority; + break; + } + } + } + } + case FLTResolutionPresetHigh: + // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio. + for (AVCaptureDeviceFormat *format in _captureDevice.formats) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + if (dimensions.height == 720 || dimensions.height == 768) { + if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { + if ([_captureDevice lockForConfiguration:nil]) { + _captureDevice.activeFormat = format; + [_captureDevice unlockForConfiguration]; + break; + } + } + } + } + // Most device format resolutions lower than 720/768p are generally 4:3 resolutions. Meaning the aspect ratio + // is the same regardless of the preset chosen. + case FLTResolutionPresetMedium: + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) { + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset640x480; + break; + } + case FLTResolutionPresetLow: + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset352x288]) { + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset352x288; + break; + } + default: + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { + _videoCaptureSession.sessionPreset = AVCaptureSessionPresetLow; + } else { + NSError *error = + [NSError errorWithDomain:NSCocoaErrorDomain + code:NSURLErrorUnknown + userInfo:@{ + NSLocalizedDescriptionKey : + @"No capture session available for current capture session." + }]; + @throw error; + } + } +} + +- (void)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPreset + withCaptureMode: (FLTCaptureMode) captureMode + withAspectRatio: (FLTAspectRatioPreset) aspectRatioPreset { + NSArray *formats = _captureDevice.formats; + for (AVCaptureDeviceFormat *format in formats) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + NSLog(@"format: %@, resolution: %d x %d", format, dimensions.width, dimensions.height); + } + if (captureMode == FLTCaptureModeVideo || aspectRatioPreset == FLTAspectRatioPresetWideScreen) { + [self setCaptureSessionForWideScreen:resolutionPreset]; + } else if (captureMode == FLTCaptureModePhoto) { + [self setCaptureSessionForStandard:resolutionPreset]; + } + CMVideoDimensions previewDimensions = CMVideoFormatDescriptionGetDimensions(_captureDevice.activeFormat.formatDescription); + _previewSize = CGSizeMake(previewDimensions.width, previewDimensions.height); _audioCaptureSession.sessionPreset = _videoCaptureSession.sessionPreset; + NSLog(@"preview resolution: %d x %d", previewDimensions.width, previewDimensions.height); return YES; } diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 3de45b68908e..a96a0e4d9f55 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -17,7 +17,8 @@ flutter: dartPluginClass: AVFoundationCamera dependencies: - camera_platform_interface: ^2.4.0 + camera_platform_interface: + path: ../../camera/camera_platform_interface flutter: sdk: flutter stream_transform: ^2.0.0 diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index f2f8f7caec4d..19154483cd33 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -17,7 +17,8 @@ flutter: fileName: camera_web.dart dependencies: - camera_platform_interface: ^2.3.1 + camera_platform_interface: + path: ../../camera/camera_platform_interface flutter: sdk: flutter flutter_web_plugins: From 4ef848358ae18313f117691f317813954e3c168d Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 28 Jun 2023 21:17:48 +0930 Subject: [PATCH 03/27] Finalising capture mode --- .../camera/lib/src/camera_controller.dart | 21 ++---- .../io/flutter/plugins/camera/Camera.java | 41 ++++++++--- .../plugins/camera/CameraProperties.java | 3 + .../plugins/camera/CameraPropertiesImpl.java | 7 ++ .../plugins/camera/CameraRegionUtils.java | 5 +- .../plugins/camera/MethodCallHandlerImpl.java | 4 ++ .../camera/features/CameraFeatureFactory.java | 6 +- .../features/CameraFeatureFactoryImpl.java | 11 +-- .../camera/features/CameraFeatures.java | 8 ++- .../features/fpsrange/FpsRangeFeature.java | 20 ++++-- .../resolution/ResolutionFeature.java | 70 ++++++++++++++++--- .../camera/media/ImageStreamReader.java | 3 +- .../plugins/camera/types/CaptureMode.java | 11 +++ .../example/lib/camera_controller.dart | 3 + .../lib/src/android_camera.dart | 13 ++-- .../lib/src/android_camera_camerax.dart | 1 + .../example/lib/camera_controller.dart | 11 +++ .../ios/Classes/CameraPlugin.m | 2 - .../ios/Classes/CameraProperties.h | 16 ----- .../camera_avfoundation/ios/Classes/FLTCam.h | 2 - .../camera_avfoundation/ios/Classes/FLTCam.m | 41 +++++------ .../lib/src/avfoundation_camera.dart | 11 +-- .../method_channel/method_channel_camera.dart | 13 ++-- .../platform_interface/camera_platform.dart | 8 ++- .../lib/src/types/aspect_ratio_preset.dart | 32 --------- .../lib/src/types/capture_mode.dart | 32 ++------- .../lib/src/types/resolution_preset.dart | 4 +- .../lib/src/types/types.dart | 1 - .../camera/camera_web/lib/src/camera_web.dart | 1 + 29 files changed, 224 insertions(+), 177 deletions(-) create mode 100644 packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java delete mode 100644 packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 58728491bb64..0510614aab0e 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -234,9 +234,8 @@ class CameraController extends ValueNotifier { /// Creates a new camera controller in an uninitialized state. CameraController( CameraDescription description, - this.resolutionPreset, - this.captureMode, - this.aspectRatioPreset, { + this.resolutionPreset, { + this.captureMode = CaptureMode.video, this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(description)); @@ -244,7 +243,9 @@ class CameraController extends ValueNotifier { /// The properties of the camera device controlled by this controller. CameraDescription get description => value.description; - /// The capture mode this controller should be using. + /// The capture mode this controller should be using. Currently only includes + /// photo or video, but may be expanded in the future to include slow motion, + /// panoramic, etc. final CaptureMode captureMode; /// The resolution this controller is targeting. @@ -255,15 +256,6 @@ class CameraController extends ValueNotifier { /// See also: [ResolutionPreset]. final ResolutionPreset resolutionPreset; - /// The aspect ratio this controller is targeting. - /// - /// This aspect ratio preset is not guaranteed to be available on the device - /// for the selected resolution or capture mode. - /// if unavailable a different aspect ratio will be used. - /// - /// See also: [AspectRatioPreset]. - final AspectRatioPreset aspectRatioPreset; - /// Whether to include audio when recording a video. final bool enableAudio; @@ -330,8 +322,7 @@ class CameraController extends ValueNotifier { _cameraId = await CameraPlatform.instance.createCamera( description, resolutionPreset, - captureMode, - aspectRatioPreset, + captureMode: captureMode, enableAudio: enableAudio, ); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 0f990fad5efd..d938a442b4df 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -12,6 +12,7 @@ import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; @@ -60,6 +61,7 @@ import io.flutter.plugins.camera.media.MediaRecorderBuilder; import io.flutter.plugins.camera.types.CameraCaptureProperties; import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; +import io.flutter.plugins.camera.types.CaptureMode; import io.flutter.view.TextureRegistry.SurfaceTextureEntry; import java.io.File; import java.io.IOException; @@ -112,11 +114,7 @@ class Camera private final SurfaceTextureEntry flutterTexture; private final ResolutionPreset resolutionPreset; - private final CaptureMode captureMode; - - private final AspectRatioPreset aspectRatioPreset; - private final boolean enableAudio; private final Context applicationContext; final DartMessenger dartMessenger; @@ -197,6 +195,7 @@ public Camera( final DartMessenger dartMessenger, final CameraProperties cameraProperties, final ResolutionPreset resolutionPreset, + final CaptureMode captureMode, final boolean enableAudio) { if (activity == null) { @@ -210,9 +209,10 @@ public Camera( this.cameraProperties = cameraProperties; this.cameraFeatureFactory = cameraFeatureFactory; this.resolutionPreset = resolutionPreset; + this.captureMode = captureMode; this.cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, captureMode); // Create capture callback. captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000); @@ -281,6 +281,8 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { public void open(String imageFormatGroup) throws CameraAccessException { this.imageFormatGroup = imageFormatGroup; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + Log.d(TAG, "captureSize: " + resolutionFeature.getCaptureSize().toString()); + Log.d(TAG, "previewSize: " + resolutionFeature.getPreviewSize().toString()); if (!resolutionFeature.checkIsSupported()) { // Tell the user that the camera they are trying to open is not supported, @@ -405,16 +407,19 @@ private void createCaptureSession( // Create a new capture builder. previewRequestBuilder = cameraDevice.createCaptureRequest(templateType); + Log.d(TAG, "scalar1" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + Log.d(TAG, "zoom1" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); + // Build Flutter surface to render to. ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture(); + Log.i(TAG, "previewSize: " + resolutionFeature.getPreviewSize().toString()); surfaceTexture.setDefaultBufferSize( resolutionFeature.getPreviewSize().getWidth(), resolutionFeature.getPreviewSize().getHeight()); Surface flutterSurface = new Surface(surfaceTexture); previewRequestBuilder.addTarget(flutterSurface); - List remainingSurfaces = Arrays.asList(surfaces); if (templateType != CameraDevice.TEMPLATE_PREVIEW) { // If it is not preview mode, add all surfaces as targets @@ -428,12 +433,19 @@ private void createCaptureSession( previewRequestBuilder.addTarget(surface); } } + Log.d(TAG, "scalar2" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cameraProperties.getSensorInfoActiveArraySize()); + Log.d(TAG, "hardcoded" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + Log.d(TAG, "zoom2" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); // Update camera regions. Size cameraBoundaries = CameraRegionUtils.getCameraBoundaries(cameraProperties, previewRequestBuilder); cameraFeatures.getExposurePoint().setCameraBoundaries(cameraBoundaries); cameraFeatures.getFocusPoint().setCameraBoundaries(cameraBoundaries); + Log.i(TAG, "cameraBoundaires: " +cameraBoundaries.toString()); + Log.d(TAG, "scalar3" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + Log.d(TAG, "zoom3" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); // Prepare the callback. CameraCaptureSession.StateCallback callback = @@ -452,6 +464,8 @@ public void onConfigured(@NonNull CameraCaptureSession session) { Log.i(TAG, "Updating builder settings"); updateBuilderSettings(previewRequestBuilder); + Log.d(TAG, "scalar4" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + Log.d(TAG, "zoom4" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); refreshPreviewCaptureSession( onSuccessCallback, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); @@ -542,7 +556,10 @@ private void startCapture(boolean record, boolean stream) throws CameraAccessExc Runnable successCallback = null; if (record) { surfaces.add(mediaRecorder.getSurface()); - successCallback = () -> mediaRecorder.start(); + successCallback = () -> { Log.d(TAG, "scalar6" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + Log.d(TAG, "zoom5" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); + + mediaRecorder.start();}; } if (stream && imageStreamReader != null) { surfaces.add(imageStreamReader.getSurface()); @@ -613,6 +630,14 @@ private void runPrecaptureSequence() { CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); +// previewRequestBuilder.set( +// CaptureRequest.SCALER_CROP_REGION, +// +// +// ); + + Log.d(TAG, "scalar" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); + // Trigger one capture to start AE sequence. captureSession.capture( previewRequestBuilder.build(), cameraCaptureCallback, backgroundHandler); @@ -1314,7 +1339,7 @@ public void setDescriptionWhileRecording( cameraProperties = properties; cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, captureMode); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java index 2c3eda880deb..1d5b4ceba8e0 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java @@ -273,4 +273,7 @@ public interface CameraProperties { */ @NonNull int[] getAvailableNoiseReductionModes(); + + @NonNull + Size[] getAvailableOutputSizes(int format); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java index dc8e9b736f2c..abe07c6db910 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java @@ -164,4 +164,11 @@ public int[] getAvailableNoiseReductionModes() { return cameraCharacteristics.get( CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES); } + + @NonNull + @Override + public Size[] getAvailableOutputSizes(int imageFormat) { + return cameraCharacteristics.get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(imageFormat); + } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java index df9b06b85515..55c686118962 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java @@ -9,6 +9,7 @@ import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; import android.util.Size; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.systemchannels.PlatformChannel; @@ -46,9 +47,11 @@ && supportsDistortionCorrection(cameraProperties)) { } else { rect = cameraProperties.getSensorInfoActiveArraySize(); } - + Log.i("boundaries", "rect" + rect.toString()); return SizeFactory.create(rect.width(), rect.height()); } else { + Log.i("boundaries", "rect" + cameraProperties.getSensorInfoPixelArraySize().toString()); + // No distortion correction support. return cameraProperties.getSensorInfoPixelArraySize(); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index aad62bbaba85..028fe6ae88e0 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -23,6 +23,7 @@ import io.flutter.plugins.camera.features.exposurelock.ExposureMode; import io.flutter.plugins.camera.features.flash.FlashMode; import io.flutter.plugins.camera.features.resolution.ResolutionPreset; +import io.flutter.plugins.camera.types.CaptureMode; import io.flutter.view.TextureRegistry; import java.util.HashMap; import java.util.Map; @@ -387,6 +388,7 @@ void stopListening() { private void instantiateCamera(MethodCall call, Result result) throws CameraAccessException { String cameraName = call.argument("cameraName"); String preset = call.argument("resolutionPreset"); + String mode = call.argument("captureMode"); boolean enableAudio = call.argument("enableAudio"); TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture = @@ -397,6 +399,7 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce CameraProperties cameraProperties = new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); ResolutionPreset resolutionPreset = ResolutionPreset.valueOf(preset); + CaptureMode captureMode = CaptureMode.valueOf(mode); camera = new Camera( @@ -406,6 +409,7 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce dartMessenger, cameraProperties, resolutionPreset, + captureMode, enableAudio); Map reply = new HashMap<>(); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java index 573bf7b5bf45..5d965cd766c6 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java @@ -20,6 +20,7 @@ import io.flutter.plugins.camera.features.resolution.ResolutionPreset; import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature; import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; +import io.flutter.plugins.camera.types.CaptureMode; /** * Factory for creating the supported feature implementation controlling different aspects of the @@ -82,7 +83,8 @@ AutoFocusFeature createAutoFocusFeature( ResolutionFeature createResolutionFeature( @NonNull CameraProperties cameraProperties, @NonNull ResolutionPreset initialSetting, - @NonNull String cameraName); + @NonNull String cameraName, + @NonNull CaptureMode captureMode); /** * Creates a new instance of the focus point feature. @@ -106,7 +108,7 @@ FocusPointFeature createFocusPointFeature( * @return newly created instance of the FpsRangeFeature class. */ @NonNull - FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties); + FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties, @NonNull CaptureMode captureMode); /** * Creates a new instance of the sensor orientation feature. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java index a15a0dca4017..32edb32d1ee5 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java @@ -20,6 +20,7 @@ import io.flutter.plugins.camera.features.resolution.ResolutionPreset; import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature; import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; +import io.flutter.plugins.camera.types.CaptureMode; /** * Implementation of the {@link CameraFeatureFactory} interface creating the supported feature @@ -58,8 +59,9 @@ public FlashFeature createFlashFeature(@NonNull CameraProperties cameraPropertie public ResolutionFeature createResolutionFeature( @NonNull CameraProperties cameraProperties, @NonNull ResolutionPreset initialSetting, - @NonNull String cameraName) { - return new ResolutionFeature(cameraProperties, initialSetting, cameraName); + @NonNull String cameraName, + @NonNull CaptureMode captureMode) { + return new ResolutionFeature(cameraProperties, initialSetting, cameraName, captureMode); } @NonNull @@ -72,8 +74,9 @@ public FocusPointFeature createFocusPointFeature( @NonNull @Override - public FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties) { - return new FpsRangeFeature(cameraProperties); + public FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties, + @NonNull CaptureMode captureMode) { + return new FpsRangeFeature(cameraProperties, captureMode); } @NonNull diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java index 6c8c1f17a2f2..03926bcb2dae 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java @@ -20,6 +20,7 @@ import io.flutter.plugins.camera.features.resolution.ResolutionPreset; import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature; import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; +import io.flutter.plugins.camera.types.CaptureMode; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -49,7 +50,8 @@ public static CameraFeatures init( @NonNull CameraProperties cameraProperties, @NonNull Activity activity, @NonNull DartMessenger dartMessenger, - @NonNull ResolutionPreset resolutionPreset) { + @NonNull ResolutionPreset resolutionPreset, + @NonNull CaptureMode captureMode) { CameraFeatures cameraFeatures = new CameraFeatures(); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, false)); @@ -67,12 +69,12 @@ public static CameraFeatures init( cameraFeatures.setFlash(cameraFeatureFactory.createFlashFeature(cameraProperties)); cameraFeatures.setFocusPoint( cameraFeatureFactory.createFocusPointFeature(cameraProperties, sensorOrientationFeature)); - cameraFeatures.setFpsRange(cameraFeatureFactory.createFpsRangeFeature(cameraProperties)); + cameraFeatures.setFpsRange(cameraFeatureFactory.createFpsRangeFeature(cameraProperties, captureMode)); cameraFeatures.setNoiseReduction( cameraFeatureFactory.createNoiseReductionFeature(cameraProperties)); cameraFeatures.setResolution( cameraFeatureFactory.createResolutionFeature( - cameraProperties, resolutionPreset, cameraProperties.getCameraName())); + cameraProperties, resolutionPreset, cameraProperties.getCameraName(), captureMode)); cameraFeatures.setZoomLevel(cameraFeatureFactory.createZoomLevelFeature(cameraProperties)); return cameraFeatures; } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java index 408e7a16b564..fa1ee575b9f9 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java @@ -6,12 +6,14 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CaptureRequest; +import android.util.Log; import android.util.Range; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.plugins.camera.CameraProperties; import io.flutter.plugins.camera.DeviceInfo; import io.flutter.plugins.camera.features.CameraFeature; +import io.flutter.plugins.camera.types.CaptureMode; /** * Controls the frames per seconds (FPS) range configuration on the {@link android.hardware.camera2} @@ -20,14 +22,16 @@ public class FpsRangeFeature extends CameraFeature> { private static final Range MAX_PIXEL4A_RANGE = new Range<>(30, 30); @Nullable private Range currentSetting; - + @NonNull private CaptureMode captureMode; /** * Creates a new instance of the {@link FpsRangeFeature}. * * @param cameraProperties Collection of characteristics for the current camera device. */ - public FpsRangeFeature(@NonNull CameraProperties cameraProperties) { + public FpsRangeFeature(@NonNull CameraProperties cameraProperties, + @NonNull CaptureMode captureMode) { super(cameraProperties); + this.captureMode = captureMode; if (isPixel4A()) { // HACK: There is a bug in the Pixel 4A where it cannot support 60fps modes @@ -39,12 +43,18 @@ public FpsRangeFeature(@NonNull CameraProperties cameraProperties) { currentSetting = MAX_PIXEL4A_RANGE; } else { Range[] ranges = cameraProperties.getControlAutoExposureAvailableTargetFpsRanges(); + // print all ranges + // + for (Range range : ranges) { + Log.d("FpsRangeFeature", "FpsRangeFeature: " + range.getLower()+ " " + range.getUpper()); + } if (ranges != null) { for (Range range : ranges) { int upper = range.getUpper(); - - if (upper >= 10) { + // When in photo mode, the upper bound is 30 fps or the aspect ratio/resolution will be + // changed by the camera session. + if (upper >= 10 && (upper < 60 || captureMode != CaptureMode.photo)) { if (currentSetting == null || upper > currentSetting.getUpper()) { currentSetting = range; } @@ -89,7 +99,7 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { if (!checkIsSupported()) { return; } - + Log.d("FpsRangeFeature", "updateBuilder: " + currentSetting); requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, currentSetting); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index 57ceddb39630..e9d8f1bd1855 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -6,10 +6,12 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.graphics.ImageFormat; import android.hardware.camera2.CaptureRequest; import android.media.CamcorderProfile; import android.media.EncoderProfiles; import android.os.Build; +import android.util.Log; import android.util.Size; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -17,6 +19,7 @@ import io.flutter.plugins.camera.CameraProperties; import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; +import io.flutter.plugins.camera.types.CaptureMode; import java.util.List; /** @@ -33,6 +36,7 @@ public class ResolutionFeature extends CameraFeature { private EncoderProfiles recordingProfile; @NonNull private ResolutionPreset currentSetting; private int cameraId; + @NonNull private CaptureMode captureMode; /** * Creates a new instance of the {@link ResolutionFeature}. @@ -44,16 +48,18 @@ public class ResolutionFeature extends CameraFeature { public ResolutionFeature( @NonNull CameraProperties cameraProperties, @NonNull ResolutionPreset resolutionPreset, - @NonNull String cameraName) { + @NonNull String cameraName, + @NonNull CaptureMode captureMode) { super(cameraProperties); this.currentSetting = resolutionPreset; + this.captureMode = captureMode; try { this.cameraId = Integer.parseInt(cameraName, 10); } catch (NumberFormatException e) { this.cameraId = -1; return; } - configureResolution(resolutionPreset, cameraId); + configureResolution(resolutionPreset, cameraId, captureMode, cameraProperties.getAvailableOutputSizes(ImageFormat.PRIVATE)); } /** @@ -108,7 +114,9 @@ public ResolutionPreset getValue() { @Override public void setValue(@NonNull ResolutionPreset value) { this.currentSetting = value; - configureResolution(currentSetting, cameraId); + // TODO + Log.d("RESOLUTION", "setValue: " + value.toString()); + configureResolution(currentSetting, cameraId, captureMode, cameraProperties.getAvailableOutputSizes(ImageFormat.PRIVATE)); } @Override @@ -121,13 +129,42 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { // No-op: when setting a resolution there is no need to update the request builder. } + static int gcd (int a, int b) { + return (b == 0) ? a : gcd (b, a%b); + } + @VisibleForTesting - static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset) + static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { if (preset.ordinal() > ResolutionPreset.high.ordinal()) { preset = ResolutionPreset.high; } - if (SdkCapabilityChecker.supportsEncoderProfiles()) { + if (captureMode == CaptureMode.photo) { + // Using max resolution for the preview is not a good use of system resources. + // Limiting the max resolution used for the preview to 1080p. is a good balance. + // When the capture mode is photo, the preview size should be a 4:3 aspect ratio. + Size selectedPreviewResolution = null; + int currentHighestPixel = 0; + for (int i = 0; i < availableOutputSizes.length; i++) { + // calculate aspect ratio and prints as 4:3 or 16:9, etc. + int factor = gcd(availableOutputSizes[i].getWidth(), availableOutputSizes[i].getHeight()); + int widthRatio = availableOutputSizes[i].getWidth() / factor; + int heightRatio = availableOutputSizes[i].getHeight() / factor; + + Log.d("RESOLUTION", availableOutputSizes[i].toString() + "Aspect Ratio: " + widthRatio + ":" + heightRatio); + if (widthRatio == 4 && heightRatio == 3 && availableOutputSizes[i].getHeight() == 1080) { + if (availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight() > currentHighestPixel) { + selectedPreviewResolution = availableOutputSizes[i]; + currentHighestPixel = availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight(); + } + } + } + if (selectedPreviewResolution != null) { + Log.d( "RESOLUTION PICKED", selectedPreviewResolution.toString() ); + return selectedPreviewResolution; + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { EncoderProfiles profile = getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset); List videoProfiles = profile.getVideoProfiles(); @@ -262,11 +299,27 @@ public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPrese } } - private void configureResolution(ResolutionPreset resolutionPreset, int cameraId) + private void configureResolution(ResolutionPreset resolutionPreset, int cameraId, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { if (!checkIsSupported()) { return; } + // Attempt to select the highest resolution from the available ones when in photo mode. + if (captureMode == CaptureMode.photo) { + Size actualMaxResolution = null; + int maxMegapixel = 0; + for (int i = 0; i < availableOutputSizes.length; i++) { + Log.d("RESOLUTION", availableOutputSizes[i].toString()); + if (availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight() > maxMegapixel) { + actualMaxResolution = availableOutputSizes[i]; + maxMegapixel = availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight(); + } + } + Log.d( "RESOLUTION PICKED", actualMaxResolution.toString()); + captureSize = actualMaxResolution; + } + + if (captureSize == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { boolean captureSizeCalculated = false; if (SdkCapabilityChecker.supportsEncoderProfiles()) { @@ -278,12 +331,11 @@ private void configureResolution(ResolutionPreset resolutionPreset, int cameraId EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0); if (defaultVideoProfile != null) { - captureSizeCalculated = true; captureSize = new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight()); } } - if (!captureSizeCalculated) { + if (captureSize == null) { recordingProfile = null; CamcorderProfile camcorderProfile = getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, resolutionPreset); @@ -292,6 +344,6 @@ private void configureResolution(ResolutionPreset resolutionPreset, int cameraId new Size(recordingProfileLegacy.videoFrameWidth, recordingProfileLegacy.videoFrameHeight); } - previewSize = computeBestPreviewSize(cameraId, resolutionPreset); + previewSize = computeBestPreviewSize(cameraId, resolutionPreset, captureMode, availableOutputSizes); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java index 1a9cf18307f9..eca1fbac7ed0 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java @@ -9,6 +9,7 @@ import android.media.ImageReader; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.view.Surface; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -104,7 +105,7 @@ public void onImageAvailable( } else { imageBuffer.put("planes", parsePlanesForYuvOrJpeg(image)); } - + Log.d("ImageStreamReader", "width: " + image.getWidth()); imageBuffer.put("width", image.getWidth()); imageBuffer.put("height", image.getHeight()); imageBuffer.put("format", dartImageFormat); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java new file mode 100644 index 000000000000..46eb869b7f7f --- /dev/null +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camera.types; + +// Mirrors camera.dart +public enum CaptureMode { + photo, + video, +} diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 9ab06140170a..e445b906e1af 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -174,6 +174,7 @@ class CameraController extends ValueNotifier { CameraController( CameraDescription cameraDescription, this.resolutionPreset, { + this.captureMode = CaptureMode.video, this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(cameraDescription)); @@ -189,6 +190,8 @@ class CameraController extends ValueNotifier { /// See also: [ResolutionPreset]. final ResolutionPreset resolutionPreset; + final CaptureMode captureMode; + /// Whether to include audio when recording a video. final bool enableAudio; diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 9e93bf0d679f..cf43f9533d9a 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -94,9 +94,8 @@ class AndroidCamera extends CameraPlatform { @override Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, - CaptureMode? captureMode, - AspectRatioPreset? aspectRatioPreset, { + ResolutionPreset? resolutionPreset, { + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { @@ -106,11 +105,7 @@ class AndroidCamera extends CameraPlatform { 'resolutionPreset': resolutionPreset != null ? _serializeResolutionPreset(resolutionPreset) : null, - 'captureMode': - captureMode != null ? serializeCaptureMode(captureMode) : null, - 'aspectRatioPreset': aspectRatioPreset != null - ? serializeAspectRatioPreset(aspectRatioPreset) - : null, + 'captureMode': captureMode.name, 'enableAudio': enableAudio, }); @@ -189,6 +184,8 @@ class AndroidCamera extends CameraPlatform { return _cameraEvents(cameraId).whereType(); } + /// TODO investigate if this is needed for capturemode + @override Stream onCameraClosing(int cameraId) { return _cameraEvents(cameraId).whereType(); diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 044778fc20f2..14c94912dbf2 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -219,6 +219,7 @@ class AndroidCameraCameraX extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { // Must obtain proper permissions before attempting to access a camera. diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 36b9ac68cfdd..d65563201202 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -174,6 +174,7 @@ class CameraController extends ValueNotifier { CameraController( CameraDescription cameraDescription, this.resolutionPreset, { + this.captureMode = CaptureMode.video, this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(cameraDescription)); @@ -189,6 +190,15 @@ class CameraController extends ValueNotifier { /// See also: [ResolutionPreset]. final ResolutionPreset resolutionPreset; + /// The mode this controller should operate in. + /// + /// This capture mode determines whether the capture session is optimized for + /// video recording or photo capture. + /// + /// Defaults to [CaptureMode.video] as the camera plugin configuration is + /// currently geared towards video recording. + final CaptureMode captureMode; + /// Whether to include audio when recording a video. final bool enableAudio; @@ -226,6 +236,7 @@ class CameraController extends ValueNotifier { _cameraId = await CameraPlatform.instance.createCamera( description, resolutionPreset, + captureMode: captureMode, enableAudio: enableAudio, ); diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index 225a104f0826..d6fdd96f93a9 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -305,13 +305,11 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea NSString *cameraName = createMethodCall.arguments[@"cameraName"]; NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; NSString *captureMode = createMethodCall.arguments[@"captureMode"]; - NSString *aspectRatioPreset = createMethodCall.arguments[@"aspectRatioPreset"]; NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; NSError *error; FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName resolutionPreset:resolutionPreset captureMode: captureMode - aspectRatioPreset: aspectRatioPreset enableAudio:[enableAudio boolValue] orientation:[[UIDevice currentDevice] orientation] captureSessionQueue:strongSelf.captureSessionQueue diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h index ff0f767e3268..d8536518855b 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h @@ -136,22 +136,6 @@ typedef NS_ENUM(NSInteger, FLTCaptureMode) { */ extern FLTCaptureMode FLTGetFLTCaptureModeForString(NSString *mode); -#pragma mark - aspect ratio preset - -/** - * Represents camera's aspect ratio preset. Mirrors AspectRatioPreset in camera.dart. - */ -typedef NS_ENUM(NSInteger, FLTAspectRatioPreset) { - FLTAspectRatioPresetStandard, - FLTAspectRatioPresetWideScreen, -}; - -/** - * Gets FLTAspectRatioPreset from its string representation. - * @param preset a string representation of FLTAspectRatioPreset. - */ -extern FLTAspectRatioPreset FLTGetFLTAspectRatioPresetForString(NSString *preset); - #pragma mark - video format /** diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index dc5ec4fc65d4..74750a1ebd5f 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -26,7 +26,6 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic) FLTThreadSafeMethodChannel *methodChannel; @property(assign, nonatomic) FLTResolutionPreset resolutionPreset; @property(assign, nonatomic) FLTCaptureMode captureMode; -@property(assign, nonatomic) FLTAspectRatioPreset aspectRatioPreset; @property(assign, nonatomic) FLTExposureMode exposureMode; @property(assign, nonatomic) FLTFocusMode focusMode; @property(assign, nonatomic) FLTFlashMode flashMode; @@ -43,7 +42,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset captureMode:(NSString *)captureMode - aspectRatioPreset:(NSString *)aspectRatioPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index a66c63262ccd..ad32e2b9d76f 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -95,7 +95,6 @@ @implementation FLTCam - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset captureMode: (NSString *)captureMode - aspectRatioPreset: (NSString *)aspectRatioPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue @@ -103,7 +102,6 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return [self initWithCameraName:cameraName resolutionPreset:resolutionPreset captureMode: (NSString *)captureMode - aspectRatioPreset: (NSString *)aspectRatioPreset enableAudio:enableAudio orientation:orientation videoCaptureSession:[[AVCaptureSession alloc] init] @@ -115,7 +113,6 @@ - (instancetype)initWithCameraName:(NSString *)cameraName - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset captureMode: (NSString *)captureMode - aspectRatioPreset: (NSString *)aspectRatioPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation videoCaptureSession:(AVCaptureSession *)videoCaptureSession @@ -174,7 +171,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName _motionManager = [[CMMotionManager alloc] init]; [_motionManager startAccelerometerUpdates]; - if (![self setCaptureSessionWithResolutionPreset:_resolutionPreset withCaptureMode:_captureMode withAspectRatio:_aspectRatioPreset withError:error]) { + if (![self setCaptureSessionWithResolutionPreset:_resolutionPreset withCaptureMode:_captureMode withError:error]) { return nil; } [self updateOrientation]; @@ -352,7 +349,7 @@ - (NSString *)getTemporaryFilePathWithExtension:(NSString *)extension } -- (BOOL)setCaptureSessionForWideScreen:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { +- (BOOL)setCaptureSessionForVideo:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { switch (resolutionPreset) { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: @@ -408,7 +405,7 @@ - (BOOL)setCaptureSessionForWideScreen:(FLTResolutionPreset)resolutionPreset wit } } -- (void)setCaptureSessionForStandard:(FLTResolutionPreset)resolutionPreset { +- (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { switch (resolutionPreset) { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: @@ -417,22 +414,21 @@ - (void)setCaptureSessionForStandard:(FLTResolutionPreset)resolutionPreset { break; } case FLTResolutionPresetVeryHigh: - // Selects the appropriate 1080p resolution to match the desired aspect ratio. + // Selects the appropriate 1080p resolution to match the desired aspect ratio for photos. for (AVCaptureDeviceFormat *format in _captureDevice.formats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - if (dimensions.height == 1080) { - if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { - if ([_captureDevice lockForConfiguration:nil]) { - _captureDevice.activeFormat = format; - [_captureDevice unlockForConfiguration]; - //_videoCaptureSession.sessionPreset = AVCaptureSessionPresetInputPriority; - break; - } - } + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + if (dimensions.height == 1080) { + if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { + if ([_captureDevice lockForConfiguration:nil]) { + _captureDevice.activeFormat = format; + [_captureDevice unlockForConfiguration]; + break; + } } + } } case FLTResolutionPresetHigh: - // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio. + // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio for photos. for (AVCaptureDeviceFormat *format in _captureDevice.formats) { CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); if (dimensions.height == 720 || dimensions.height == 768) { @@ -474,17 +470,16 @@ - (void)setCaptureSessionForStandard:(FLTResolutionPreset)resolutionPreset { } - (void)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPreset - withCaptureMode: (FLTCaptureMode) captureMode - withAspectRatio: (FLTAspectRatioPreset) aspectRatioPreset { + withCaptureMode: (FLTCaptureMode) captureMode { NSArray *formats = _captureDevice.formats; for (AVCaptureDeviceFormat *format in formats) { CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); NSLog(@"format: %@, resolution: %d x %d", format, dimensions.width, dimensions.height); } - if (captureMode == FLTCaptureModeVideo || aspectRatioPreset == FLTAspectRatioPresetWideScreen) { - [self setCaptureSessionForWideScreen:resolutionPreset]; + if (captureMode == FLTCaptureModeVideo) { + [self setCaptureSessionForVideo:resolutionPreset]; } else if (captureMode == FLTCaptureModePhoto) { - [self setCaptureSessionForStandard:resolutionPreset]; + [self setCaptureSessionForPhoto:resolutionPreset]; } CMVideoDimensions previewDimensions = CMVideoFormatDescriptionGetDimensions(_captureDevice.activeFormat.formatDescription); _previewSize = CGSizeMake(previewDimensions.width, previewDimensions.height); diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index b5baf39fe78f..4b62a972c3a9 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -94,9 +94,8 @@ class AVFoundationCamera extends CameraPlatform { @override Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, - CaptureMode? captureMode, - AspectRatioPreset? aspectRatioPreset, { + ResolutionPreset? resolutionPreset, { + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { @@ -106,11 +105,7 @@ class AVFoundationCamera extends CameraPlatform { 'resolutionPreset': resolutionPreset != null ? _serializeResolutionPreset(resolutionPreset) : null, - 'captureMode': - captureMode != null ? serializeCaptureMode(captureMode) : null, - 'aspectRatioPreset': aspectRatioPreset != null - ? serializeAspectRatioPreset(aspectRatioPreset) - : null, + 'captureMode': captureMode.name, 'enableAudio': enableAudio, }); diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 287b6c648f4c..423986dc50fa 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -86,9 +86,8 @@ class MethodChannelCamera extends CameraPlatform { @override Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, - CaptureMode? captureMode, - AspectRatioPreset? aspectRatioPreset, { + ResolutionPreset? resolutionPreset, { + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async => createCameraWithSettings( @@ -112,11 +111,7 @@ class MethodChannelCamera extends CameraPlatform { 'fps': mediaSettings.fps, 'videoBitrate': mediaSettings.videoBitrate, 'audioBitrate': mediaSettings.audioBitrate, - 'captureMode': - captureMode != null ? serializeCaptureMode(captureMode) : null, - 'aspectRatioPreset': aspectRatioPreset != null - ? serializeAspectRatioPreset(aspectRatioPreset) - : null, + 'captureMode': captureMode.name, 'enableAudio': mediaSettings.enableAudio, }); @@ -195,6 +190,8 @@ class MethodChannelCamera extends CameraPlatform { return _cameraEvents(cameraId).whereType(); } + /// Investigate TODO here for events for capturemode + @override Stream onCameraClosing(int cameraId) { return _cameraEvents(cameraId).whereType(); diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index 3b043fce1110..ade2b3cf4697 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -49,9 +49,8 @@ abstract class CameraPlatform extends PlatformInterface { /// Creates an uninitialized camera instance and returns the cameraId. Future createCamera( CameraDescription cameraDescription, - ResolutionPreset? resolutionPreset, - CaptureMode? captureMode, - AspectRatioPreset? aspectRatioPreset, { + ResolutionPreset? resolutionPreset, { + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) { throw UnimplementedError('createCamera() is not implemented.'); @@ -275,6 +274,9 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('setZoomLevel() is not implemented.'); } + /// Set the capture mode for the selected camera. + /// + /// See [CaptureMode] for more information. Future setCaptureMode(int cameraId, CaptureMode mode) { throw UnimplementedError('setCaptureMode() is not implemented.'); } diff --git a/packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart b/packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart deleted file mode 100644 index 1878e82e1ec4..000000000000 --- a/packages/camera/camera_platform_interface/lib/src/types/aspect_ratio_preset.dart +++ /dev/null @@ -1,32 +0,0 @@ -enum AspectRatioPreset { - /// 4:3 - /// Only supported in photo capture mode. - standard, - - /// 16:9 - widescreen, -} - -/// Returns the aspect ratio preset as a String. -/// -String serializeAspectRatioPreset(AspectRatioPreset aspectRatioPreset) { - switch (aspectRatioPreset) { - case AspectRatioPreset.standard: - return 'standard'; - case AspectRatioPreset.widescreen: - return 'widescreen'; - } -} - -/// Returns the aspect ratio preset for a given String. -/// -AspectRatioPreset deserializeAspectRatioPreset(String str) { - switch (str) { - case 'standard': - return AspectRatioPreset.standard; - case 'widescreen': - return AspectRatioPreset.widescreen; - default: - throw ArgumentError('"$str" is not a valid AspectRatioPreset value'); - } -} diff --git a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart index 39b69a28a9de..a2356997522a 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart @@ -1,4 +1,10 @@ -/// The mode of capture to use when taking a picture or video. +/// The mode the controller should operate in. +/// +/// This capture mode determines whether the capture session is optimized for +/// video recording or photo capture. +/// +/// Defaults to [CaptureMode.video] as the camera plugin configuration is +/// currently geared towards video recording. enum CaptureMode { /// Capture a photo. photo, @@ -6,27 +12,3 @@ enum CaptureMode { /// Capture a video, however this allows the user to take photos while recording. video, } - -/// Returns the capture mode as a String. -/// -String serializeCaptureMode(CaptureMode captureMode) { - switch (captureMode) { - case CaptureMode.photo: - return 'photo'; - case CaptureMode.video: - return 'video'; - } -} - -/// Returns the capture mode for a given String. -/// -CaptureMode deserializeCaptureMode(String str) { - switch (str) { - case 'photo': - return CaptureMode.photo; - case 'video': - return CaptureMode.video; - default: - throw ArgumentError('"$str" is not a valid CaptureMode value'); - } -} diff --git a/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart b/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart index fcb6b83bbf14..7ed8c71e7b81 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart @@ -21,6 +21,8 @@ enum ResolutionPreset { /// 2160p (3840x2160 on Android and iOS, 4096x2160 on Web) ultraHigh, - /// The highest resolution available. + /// The highest resolution available. When CaptureMode is video, this is the same as [ultraHigh]. + /// When CaptureMode is photo, this is the maximum resolution of the camera which is typically + /// a 4:3 resolution. max, } diff --git a/packages/camera/camera_platform_interface/lib/src/types/types.dart b/packages/camera/camera_platform_interface/lib/src/types/types.dart index dc3e8d64b531..349fa7735d83 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/types.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/types.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export 'aspect_ratio_preset.dart'; export 'camera_description.dart'; export 'camera_exception.dart'; export 'camera_image_data.dart'; diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index be41e7b9a874..633828b79a81 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -196,6 +196,7 @@ class CameraPlugin extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { From 6fc2977eb323ddcb82db6dda122bf3abbb3ff34d Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 30 Jun 2023 15:18:09 +0930 Subject: [PATCH 04/27] Clean up --- .../plugins/camera/CameraProperties.java | 8 ++++ .../camera_avfoundation/ios/Classes/FLTCam.m | 40 ++++++++++--------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java index 1d5b4ceba8e0..75be9ca99b68 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java @@ -274,6 +274,14 @@ public interface CameraProperties { @NonNull int[] getAvailableNoiseReductionModes(); + /** + * Returns a list of available output sizes for a particular format. + * + *

By default maps to the @see android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP key. + * + * @param format int The format for which available output sizes will be returned. + * @return Size[] List of available output sizes for a particular format. + */ @NonNull Size[] getAvailableOutputSizes(int format); } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index ad32e2b9d76f..b6d5ecccfc01 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -410,33 +410,37 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetPhoto]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPresetPhoto; - break; + _videoCaptureSession.sessionPreset = AVCaptureSessionPresetPhoto; + break; } case FLTResolutionPresetVeryHigh: // Selects the appropriate 1080p resolution to match the desired aspect ratio for photos. - for (AVCaptureDeviceFormat *format in _captureDevice.formats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - if (dimensions.height == 1080) { - if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { - if ([_captureDevice lockForConfiguration:nil]) { - _captureDevice.activeFormat = format; - [_captureDevice unlockForConfiguration]; - break; + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionInputPriority]) { + for (AVCaptureDeviceFormat *format in _captureDevice.formats) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + if (dimensions.height == 1080) { + if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { + if ([_captureDevice lockForConfiguration:nil]) { + _captureDevice.activeFormat = format; + [_captureDevice unlockForConfiguration]; + return; + } } } } } case FLTResolutionPresetHigh: // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio for photos. - for (AVCaptureDeviceFormat *format in _captureDevice.formats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - if (dimensions.height == 720 || dimensions.height == 768) { - if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { - if ([_captureDevice lockForConfiguration:nil]) { - _captureDevice.activeFormat = format; - [_captureDevice unlockForConfiguration]; - break; + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionInputPriority]) { + for (AVCaptureDeviceFormat *format in _captureDevice.formats) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + if (dimensions.height == 720 || dimensions.height == 768) { + if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { + if ([_captureDevice lockForConfiguration:nil]) { + _captureDevice.activeFormat = format; + [_captureDevice unlockForConfiguration]; + return; + } } } } From 658590587d44a2fd112a7508b059568a69310334 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 30 Jun 2023 16:32:02 +0930 Subject: [PATCH 05/27] Refactor resolution selection --- .../resolution/ResolutionFeature.java | 169 ++++++++++++------ 1 file changed, 111 insertions(+), 58 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index e9d8f1bd1855..bb8008ad93ac 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -30,26 +30,30 @@ * required to configure the resolution using the {@link android.hardware.camera2} API. */ public class ResolutionFeature extends CameraFeature { - @Nullable private Size captureSize; - @Nullable private Size previewSize; + @Nullable + private Size captureSize; + @Nullable + private Size previewSize; private CamcorderProfile recordingProfileLegacy; private EncoderProfiles recordingProfile; - @NonNull private ResolutionPreset currentSetting; + @NonNull + private ResolutionPreset currentSetting; private int cameraId; - @NonNull private CaptureMode captureMode; + @NonNull + private CaptureMode captureMode; /** * Creates a new instance of the {@link ResolutionFeature}. * * @param cameraProperties Collection of characteristics for the current camera device. * @param resolutionPreset Platform agnostic enum containing resolution information. - * @param cameraName Camera identifier of the camera for which to configure the resolution. + * @param cameraName Camera identifier of the camera for which to configure the resolution. */ public ResolutionFeature( - @NonNull CameraProperties cameraProperties, - @NonNull ResolutionPreset resolutionPreset, - @NonNull String cameraName, - @NonNull CaptureMode captureMode) { + @NonNull CameraProperties cameraProperties, + @NonNull ResolutionPreset resolutionPreset, + @NonNull String cameraName, + @NonNull CaptureMode captureMode) { super(cameraProperties); this.currentSetting = resolutionPreset; this.captureMode = captureMode; @@ -129,44 +133,24 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { // No-op: when setting a resolution there is no need to update the request builder. } - static int gcd (int a, int b) { - return (b == 0) ? a : gcd (b, a%b); + static int gcd(int a, int b) { + return (b == 0) ? a : gcd(b, a % b); } @VisibleForTesting static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) - throws IndexOutOfBoundsException { + throws IndexOutOfBoundsException { + // Using max resolution for the preview is not a good use of system resources. + // Limiting the max resolution used for the preview to 1080p. is a good balance. if (preset.ordinal() > ResolutionPreset.high.ordinal()) { preset = ResolutionPreset.high; } if (captureMode == CaptureMode.photo) { - // Using max resolution for the preview is not a good use of system resources. - // Limiting the max resolution used for the preview to 1080p. is a good balance. - // When the capture mode is photo, the preview size should be a 4:3 aspect ratio. - Size selectedPreviewResolution = null; - int currentHighestPixel = 0; - for (int i = 0; i < availableOutputSizes.length; i++) { - // calculate aspect ratio and prints as 4:3 or 16:9, etc. - int factor = gcd(availableOutputSizes[i].getWidth(), availableOutputSizes[i].getHeight()); - int widthRatio = availableOutputSizes[i].getWidth() / factor; - int heightRatio = availableOutputSizes[i].getHeight() / factor; - - Log.d("RESOLUTION", availableOutputSizes[i].toString() + "Aspect Ratio: " + widthRatio + ":" + heightRatio); - if (widthRatio == 4 && heightRatio == 3 && availableOutputSizes[i].getHeight() == 1080) { - if (availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight() > currentHighestPixel) { - selectedPreviewResolution = availableOutputSizes[i]; - currentHighestPixel = availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight(); - } - } - } - if (selectedPreviewResolution != null) { - Log.d( "RESOLUTION PICKED", selectedPreviewResolution.toString() ); - return selectedPreviewResolution; - } + return getBestAvailableCameraSizeForResolutionPreset(preset, availableOutputSizes); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { EncoderProfiles profile = - getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset); + getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset); List videoProfiles = profile.getVideoProfiles(); EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0); @@ -178,7 +162,7 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, Captur // TODO(camsim99): Suppression is currently safe because legacy code is used as a fallback for SDK < S. // This should be removed when reverting that fallback behavior: https://github.com/flutter/flutter/issues/119668. CamcorderProfile profile = - getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, preset); + getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, preset); return new Size(profile.videoFrameWidth, profile.videoFrameHeight); } @@ -187,21 +171,21 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, Captur * ResolutionPreset}. Supports SDK < 31. * * @param cameraId Camera identifier which indicates the device's camera for which to select a - * {@link android.media.CamcorderProfile}. - * @param preset The {@link ResolutionPreset} for which is to be translated to a {@link - * android.media.CamcorderProfile}. + * {@link android.media.CamcorderProfile}. + * @param preset The {@link ResolutionPreset} for which is to be translated to a {@link + * android.media.CamcorderProfile}. * @return The best possible {@link android.media.CamcorderProfile} that matches the supplied - * {@link ResolutionPreset}. + * {@link ResolutionPreset}. */ @TargetApi(Build.VERSION_CODES.R) // All of these cases deliberately fall through to get the best available profile. @SuppressWarnings({"fallthrough", "deprecation"}) @NonNull public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPresetLegacy( - int cameraId, @NonNull ResolutionPreset preset) { + int cameraId, @NonNull ResolutionPreset preset) { if (cameraId < 0) { throw new AssertionError( - "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); + "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); } switch (preset) { @@ -240,7 +224,7 @@ public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPres return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); } else { throw new IllegalArgumentException( - "No capture session available for current capture session."); + "No capture session available for current capture session."); } } } @@ -250,10 +234,10 @@ public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPres @SuppressWarnings("fallthrough") @NonNull public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPreset( - int cameraId, @NonNull ResolutionPreset preset) { + int cameraId, @NonNull ResolutionPreset preset) { if (cameraId < 0) { throw new AssertionError( - "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); + "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); } String cameraIdString = Integer.toString(cameraId); @@ -295,10 +279,89 @@ public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPrese } throw new IllegalArgumentException( - "No capture session available for current capture session."); + "No capture session available for current capture session."); + } + } + + // All of these cases deliberately fall through to get the best available camera profile. + @SuppressWarnings("fallthrough") + @NonNull + public static Size getBestAvailableCameraSizeForResolutionPreset(@NonNull ResolutionPreset preset, Size[] availableOutputSizes) { + Size selectedSize = null; + switch (preset) { + case max: + selectedSize = selectPhotoCaptureSize(null, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + // fall through + case ultraHigh: + selectedSize = selectPhotoCaptureSize(2160, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + // fall through + case veryHigh: + selectedSize = selectPhotoCaptureSize(1080, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + // fall through + case high: + selectedSize = selectPhotoCaptureSize(720, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + // fall through + case medium: + selectedSize = selectPhotoCaptureSize(480, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + // fall through + case low: + selectedSize = selectPhotoCaptureSize(240, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + // fall through + default: + // default to 720p as a fallback as it should be available on most devices as an option. + selectedSize = selectPhotoCaptureSize(720, availableOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + + throw new IllegalArgumentException( + "No capture session available for current capture session."); } } + private static Size selectPhotoCaptureSize(Integer resolutionWidth, Size[] availableOutputSizes) { + // When the capture mode is photo, the preview size should be a 4:3 aspect ratio. + Size selectedPreviewResolution = null; + int currentHighestPixel = 0; + for (int i = 0; i < availableOutputSizes.length; i++) { + int factor = gcd(availableOutputSizes[i].getWidth(), availableOutputSizes[i].getHeight()); + int widthRatio = availableOutputSizes[i].getWidth() / factor; + int heightRatio = availableOutputSizes[i].getHeight() / factor; + + Log.d("RESOLUTION", availableOutputSizes[i].toString() + "Aspect Ratio: " + widthRatio + ":" + heightRatio); + // When no resolutionWidth is provided, the highest resolution should be selected. + if ((Math.abs((double) availableOutputSizes[i].getWidth() / availableOutputSizes[i].getHeight() - (double)4 / 3) < 0.01 && resolutionWidth != null && availableOutputSizes[i].getHeight() == resolutionWidth) || resolutionWidth == null) { + if (availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight() > currentHighestPixel) { + selectedPreviewResolution = availableOutputSizes[i]; + currentHighestPixel = availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight(); + } + } + } + if (selectedPreviewResolution != null) { + Log.d("RESOLUTION PICKED", selectedPreviewResolution.toString()); + } + return selectedPreviewResolution; + } + + private void configureResolution(ResolutionPreset resolutionPreset, int cameraId, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { if (!checkIsSupported()) { @@ -306,17 +369,7 @@ private void configureResolution(ResolutionPreset resolutionPreset, int cameraId } // Attempt to select the highest resolution from the available ones when in photo mode. if (captureMode == CaptureMode.photo) { - Size actualMaxResolution = null; - int maxMegapixel = 0; - for (int i = 0; i < availableOutputSizes.length; i++) { - Log.d("RESOLUTION", availableOutputSizes[i].toString()); - if (availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight() > maxMegapixel) { - actualMaxResolution = availableOutputSizes[i]; - maxMegapixel = availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight(); - } - } - Log.d( "RESOLUTION PICKED", actualMaxResolution.toString()); - captureSize = actualMaxResolution; + captureSize = getBestAvailableCameraSizeForResolutionPreset(resolutionPreset, availableOutputSizes); } if (captureSize == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { From f35112499a39c296272708100ec2de45c3a7c9b6 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Tue, 4 Jul 2023 11:32:58 +0930 Subject: [PATCH 06/27] Finalise the resolution selection --- .../camera/camera/lib/src/camera_preview.dart | 4 +- .../io/flutter/plugins/camera/Camera.java | 26 +------- .../features/fpsrange/FpsRangeFeature.java | 6 -- .../resolution/ResolutionFeature.java | 60 +++++++++---------- .../camera_avfoundation/ios/Classes/FLTCam.m | 4 +- 5 files changed, 33 insertions(+), 67 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index 53f9034dc858..f22558f9057b 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -44,9 +44,9 @@ class CameraPreview extends StatelessWidget { } Widget _wrapInRotatedBox({required Widget child}) { - if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { + //if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { return child; - } + // } return RotatedBox( quarterTurns: _getQuarterTurns(), diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index d938a442b4df..657bd553ef91 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -281,8 +281,6 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { public void open(String imageFormatGroup) throws CameraAccessException { this.imageFormatGroup = imageFormatGroup; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); - Log.d(TAG, "captureSize: " + resolutionFeature.getCaptureSize().toString()); - Log.d(TAG, "previewSize: " + resolutionFeature.getPreviewSize().toString()); if (!resolutionFeature.checkIsSupported()) { // Tell the user that the camera they are trying to open is not supported, @@ -407,9 +405,6 @@ private void createCaptureSession( // Create a new capture builder. previewRequestBuilder = cameraDevice.createCaptureRequest(templateType); - Log.d(TAG, "scalar1" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); - Log.d(TAG, "zoom1" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); - // Build Flutter surface to render to. ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); @@ -433,19 +428,13 @@ private void createCaptureSession( previewRequestBuilder.addTarget(surface); } } - Log.d(TAG, "scalar2" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cameraProperties.getSensorInfoActiveArraySize()); - Log.d(TAG, "hardcoded" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); - Log.d(TAG, "zoom2" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); // Update camera regions. Size cameraBoundaries = CameraRegionUtils.getCameraBoundaries(cameraProperties, previewRequestBuilder); cameraFeatures.getExposurePoint().setCameraBoundaries(cameraBoundaries); cameraFeatures.getFocusPoint().setCameraBoundaries(cameraBoundaries); - Log.i(TAG, "cameraBoundaires: " +cameraBoundaries.toString()); - Log.d(TAG, "scalar3" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); - Log.d(TAG, "zoom3" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); // Prepare the callback. CameraCaptureSession.StateCallback callback = @@ -464,8 +453,6 @@ public void onConfigured(@NonNull CameraCaptureSession session) { Log.i(TAG, "Updating builder settings"); updateBuilderSettings(previewRequestBuilder); - Log.d(TAG, "scalar4" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); - Log.d(TAG, "zoom4" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); refreshPreviewCaptureSession( onSuccessCallback, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); @@ -556,10 +543,7 @@ private void startCapture(boolean record, boolean stream) throws CameraAccessExc Runnable successCallback = null; if (record) { surfaces.add(mediaRecorder.getSurface()); - successCallback = () -> { Log.d(TAG, "scalar6" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); - Log.d(TAG, "zoom5" + previewRequestBuilder.get(CaptureRequest.CONTROL_ZOOM_RATIO).toString()); - - mediaRecorder.start();}; + successCallback = () -> mediaRecorder.start(); } if (stream && imageStreamReader != null) { surfaces.add(imageStreamReader.getSurface()); @@ -630,14 +614,6 @@ private void runPrecaptureSequence() { CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); -// previewRequestBuilder.set( -// CaptureRequest.SCALER_CROP_REGION, -// -// -// ); - - Log.d(TAG, "scalar" + previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION).toString()); - // Trigger one capture to start AE sequence. captureSession.capture( previewRequestBuilder.build(), cameraCaptureCallback, backgroundHandler); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java index fa1ee575b9f9..0220a33644d6 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java @@ -43,11 +43,6 @@ public FpsRangeFeature(@NonNull CameraProperties cameraProperties, currentSetting = MAX_PIXEL4A_RANGE; } else { Range[] ranges = cameraProperties.getControlAutoExposureAvailableTargetFpsRanges(); - // print all ranges - // - for (Range range : ranges) { - Log.d("FpsRangeFeature", "FpsRangeFeature: " + range.getLower()+ " " + range.getUpper()); - } if (ranges != null) { for (Range range : ranges) { @@ -99,7 +94,6 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { if (!checkIsSupported()) { return; } - Log.d("FpsRangeFeature", "updateBuilder: " + currentSetting); requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, currentSetting); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index bb8008ad93ac..46e8cc9f3aa9 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -20,6 +20,8 @@ import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; import io.flutter.plugins.camera.types.CaptureMode; + +import java.util.ArrayList; import java.util.List; /** @@ -118,8 +120,6 @@ public ResolutionPreset getValue() { @Override public void setValue(@NonNull ResolutionPreset value) { this.currentSetting = value; - // TODO - Log.d("RESOLUTION", "setValue: " + value.toString()); configureResolution(currentSetting, cameraId, captureMode, cameraProperties.getAvailableOutputSizes(ImageFormat.PRIVATE)); } @@ -133,10 +133,6 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { // No-op: when setting a resolution there is no need to update the request builder. } - static int gcd(int a, int b) { - return (b == 0) ? a : gcd(b, a % b); - } - @VisibleForTesting static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { @@ -287,77 +283,77 @@ public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPrese @SuppressWarnings("fallthrough") @NonNull public static Size getBestAvailableCameraSizeForResolutionPreset(@NonNull ResolutionPreset preset, Size[] availableOutputSizes) { + List availableStandardOutputSizes = new ArrayList<>(); + for (Size outputSize : availableOutputSizes) { + if ((Math.abs((double) outputSize.getWidth() / outputSize.getHeight() - (double)4 / 3) < 0.01)) { + availableStandardOutputSizes.add(outputSize); + } + } Size selectedSize = null; switch (preset) { case max: - selectedSize = selectPhotoCaptureSize(null, availableOutputSizes); + selectedSize = selectPhotoCaptureSize(null, availableStandardOutputSizes); if (selectedSize != null) { return selectedSize; } // fall through case ultraHigh: - selectedSize = selectPhotoCaptureSize(2160, availableOutputSizes); + selectedSize = selectPhotoCaptureSize(2160, availableStandardOutputSizes); if (selectedSize != null) { return selectedSize; } // fall through case veryHigh: - selectedSize = selectPhotoCaptureSize(1080, availableOutputSizes); + selectedSize = selectPhotoCaptureSize(1080, availableStandardOutputSizes); if (selectedSize != null) { return selectedSize; } // fall through case high: - selectedSize = selectPhotoCaptureSize(720, availableOutputSizes); + // Both 768 and 720 are common HD picture heights. + selectedSize = selectPhotoCaptureSize(768, availableStandardOutputSizes); + if (selectedSize != null) { + return selectedSize; + } + selectedSize = selectPhotoCaptureSize(720, availableStandardOutputSizes); if (selectedSize != null) { return selectedSize; } // fall through case medium: - selectedSize = selectPhotoCaptureSize(480, availableOutputSizes); + selectedSize = selectPhotoCaptureSize(480, availableStandardOutputSizes); if (selectedSize != null) { return selectedSize; } // fall through case low: - selectedSize = selectPhotoCaptureSize(240, availableOutputSizes); + selectedSize = selectPhotoCaptureSize(240, availableStandardOutputSizes); if (selectedSize != null) { return selectedSize; } // fall through default: - // default to 720p as a fallback as it should be available on most devices as an option. - selectedSize = selectPhotoCaptureSize(720, availableOutputSizes); - if (selectedSize != null) { - return selectedSize; + // default to lowest available 4:3 resolution. + if (availableStandardOutputSizes.size() > 0) { + return availableStandardOutputSizes.get(availableStandardOutputSizes.size() - 1); } - throw new IllegalArgumentException( "No capture session available for current capture session."); } } - private static Size selectPhotoCaptureSize(Integer resolutionWidth, Size[] availableOutputSizes) { - // When the capture mode is photo, the preview size should be a 4:3 aspect ratio. + private static Size selectPhotoCaptureSize(Integer resolutionWidth, List availableStandardOutputSizes) { Size selectedPreviewResolution = null; int currentHighestPixel = 0; - for (int i = 0; i < availableOutputSizes.length; i++) { - int factor = gcd(availableOutputSizes[i].getWidth(), availableOutputSizes[i].getHeight()); - int widthRatio = availableOutputSizes[i].getWidth() / factor; - int heightRatio = availableOutputSizes[i].getHeight() / factor; - - Log.d("RESOLUTION", availableOutputSizes[i].toString() + "Aspect Ratio: " + widthRatio + ":" + heightRatio); + for (Size standardOutputSize : availableStandardOutputSizes) { // When no resolutionWidth is provided, the highest resolution should be selected. - if ((Math.abs((double) availableOutputSizes[i].getWidth() / availableOutputSizes[i].getHeight() - (double)4 / 3) < 0.01 && resolutionWidth != null && availableOutputSizes[i].getHeight() == resolutionWidth) || resolutionWidth == null) { - if (availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight() > currentHighestPixel) { - selectedPreviewResolution = availableOutputSizes[i]; - currentHighestPixel = availableOutputSizes[i].getWidth() * availableOutputSizes[i].getHeight(); + if ((resolutionWidth != null && standardOutputSize.getHeight() == resolutionWidth) || resolutionWidth == null) { + if (standardOutputSize.getWidth() * standardOutputSize.getHeight() > currentHighestPixel) { + selectedPreviewResolution = standardOutputSize; + currentHighestPixel = standardOutputSize.getWidth() * standardOutputSize.getHeight(); } } } - if (selectedPreviewResolution != null) { - Log.d("RESOLUTION PICKED", selectedPreviewResolution.toString()); - } return selectedPreviewResolution; } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index b6d5ecccfc01..fefdd049395e 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -415,7 +415,7 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { } case FLTResolutionPresetVeryHigh: // Selects the appropriate 1080p resolution to match the desired aspect ratio for photos. - if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionInputPriority]) { + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetInputPriority]) { for (AVCaptureDeviceFormat *format in _captureDevice.formats) { CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); if (dimensions.height == 1080) { @@ -431,7 +431,7 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { } case FLTResolutionPresetHigh: // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio for photos. - if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionInputPriority]) { + if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetInputPriority]) { for (AVCaptureDeviceFormat *format in _captureDevice.formats) { CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); if (dimensions.height == 720 || dimensions.height == 768) { From 61af8d385391aec5fc3f28438ab2a4f0eaa7039a Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Tue, 4 Jul 2023 13:20:10 +0930 Subject: [PATCH 07/27] Cleaning up work --- .../io/flutter/plugins/camera/Camera.java | 1 - .../plugins/camera/CameraRegionUtils.java | 3 --- .../features/fpsrange/FpsRangeFeature.java | 1 + .../resolution/ResolutionFeature.java | 21 ++++++++++--------- .../camera/media/ImageStreamReader.java | 1 - .../lib/src/android_camera.dart | 2 -- .../camera_avfoundation/ios/Classes/FLTCam.m | 5 ----- .../method_channel/method_channel_camera.dart | 2 -- .../lib/src/types/resolution_preset.dart | 8 ++++--- 9 files changed, 17 insertions(+), 27 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 657bd553ef91..2890897fd135 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -409,7 +409,6 @@ private void createCaptureSession( // Build Flutter surface to render to. ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture(); - Log.i(TAG, "previewSize: " + resolutionFeature.getPreviewSize().toString()); surfaceTexture.setDefaultBufferSize( resolutionFeature.getPreviewSize().getWidth(), resolutionFeature.getPreviewSize().getHeight()); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java index 55c686118962..ab27f8c161ab 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java @@ -47,11 +47,8 @@ && supportsDistortionCorrection(cameraProperties)) { } else { rect = cameraProperties.getSensorInfoActiveArraySize(); } - Log.i("boundaries", "rect" + rect.toString()); return SizeFactory.create(rect.width(), rect.height()); } else { - Log.i("boundaries", "rect" + cameraProperties.getSensorInfoPixelArraySize().toString()); - // No distortion correction support. return cameraProperties.getSensorInfoPixelArraySize(); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java index 0220a33644d6..12e78f1cb8bb 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java @@ -94,6 +94,7 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { if (!checkIsSupported()) { return; } + requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, currentSetting); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index 46e8cc9f3aa9..d6fa6ad31368 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -50,6 +50,7 @@ public class ResolutionFeature extends CameraFeature { * @param cameraProperties Collection of characteristics for the current camera device. * @param resolutionPreset Platform agnostic enum containing resolution information. * @param cameraName Camera identifier of the camera for which to configure the resolution. + * @param captureMode Capture mode to configure the appropriate resolution and aspect ratio. */ public ResolutionFeature( @NonNull CameraProperties cameraProperties, @@ -135,7 +136,7 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { @VisibleForTesting static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) - throws IndexOutOfBoundsException { + throws IndexOutOfBoundsException { // Using max resolution for the preview is not a good use of system resources. // Limiting the max resolution used for the preview to 1080p. is a good balance. if (preset.ordinal() > ResolutionPreset.high.ordinal()) { @@ -146,7 +147,7 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, Captur } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { EncoderProfiles profile = - getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset); + getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset); List videoProfiles = profile.getVideoProfiles(); EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0); @@ -158,7 +159,7 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, Captur // TODO(camsim99): Suppression is currently safe because legacy code is used as a fallback for SDK < S. // This should be removed when reverting that fallback behavior: https://github.com/flutter/flutter/issues/119668. CamcorderProfile profile = - getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, preset); + getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, preset); return new Size(profile.videoFrameWidth, profile.videoFrameHeight); } @@ -178,10 +179,10 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, Captur @SuppressWarnings({"fallthrough", "deprecation"}) @NonNull public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPresetLegacy( - int cameraId, @NonNull ResolutionPreset preset) { + int cameraId, @NonNull ResolutionPreset preset) { if (cameraId < 0) { throw new AssertionError( - "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); + "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); } switch (preset) { @@ -220,7 +221,7 @@ public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPres return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); } else { throw new IllegalArgumentException( - "No capture session available for current capture session."); + "No capture session available for current capture session."); } } } @@ -230,10 +231,10 @@ public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPres @SuppressWarnings("fallthrough") @NonNull public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPreset( - int cameraId, @NonNull ResolutionPreset preset) { + int cameraId, @NonNull ResolutionPreset preset) { if (cameraId < 0) { throw new AssertionError( - "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); + "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers."); } String cameraIdString = Integer.toString(cameraId); @@ -275,7 +276,7 @@ public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPrese } throw new IllegalArgumentException( - "No capture session available for current capture session."); + "No capture session available for current capture session."); } } @@ -338,7 +339,7 @@ public static Size getBestAvailableCameraSizeForResolutionPreset(@NonNull Resolu return availableStandardOutputSizes.get(availableStandardOutputSizes.size() - 1); } throw new IllegalArgumentException( - "No capture session available for current capture session."); + "No capture session available for current capture session."); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java index eca1fbac7ed0..ee4073c593ab 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java @@ -105,7 +105,6 @@ public void onImageAvailable( } else { imageBuffer.put("planes", parsePlanesForYuvOrJpeg(image)); } - Log.d("ImageStreamReader", "width: " + image.getWidth()); imageBuffer.put("width", image.getWidth()); imageBuffer.put("height", image.getHeight()); imageBuffer.put("format", dartImageFormat); diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index cf43f9533d9a..45c599b14f5e 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -184,8 +184,6 @@ class AndroidCamera extends CameraPlatform { return _cameraEvents(cameraId).whereType(); } - /// TODO investigate if this is needed for capturemode - @override Stream onCameraClosing(int cameraId) { return _cameraEvents(cameraId).whereType(); diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index fefdd049395e..e3455fd42e78 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -476,10 +476,6 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { - (void)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPreset withCaptureMode: (FLTCaptureMode) captureMode { NSArray *formats = _captureDevice.formats; - for (AVCaptureDeviceFormat *format in formats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - NSLog(@"format: %@, resolution: %d x %d", format, dimensions.width, dimensions.height); - } if (captureMode == FLTCaptureModeVideo) { [self setCaptureSessionForVideo:resolutionPreset]; } else if (captureMode == FLTCaptureModePhoto) { @@ -488,7 +484,6 @@ - (void)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPre CMVideoDimensions previewDimensions = CMVideoFormatDescriptionGetDimensions(_captureDevice.activeFormat.formatDescription); _previewSize = CGSizeMake(previewDimensions.width, previewDimensions.height); _audioCaptureSession.sessionPreset = _videoCaptureSession.sessionPreset; - NSLog(@"preview resolution: %d x %d", previewDimensions.width, previewDimensions.height); return YES; } diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 423986dc50fa..423abb0596d1 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -190,8 +190,6 @@ class MethodChannelCamera extends CameraPlatform { return _cameraEvents(cameraId).whereType(); } - /// Investigate TODO here for events for capturemode - @override Stream onCameraClosing(int cameraId) { return _cameraEvents(cameraId).whereType(); diff --git a/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart b/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart index 7ed8c71e7b81..dc9fef89cfb2 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart @@ -5,6 +5,8 @@ /// Affect the quality of video recording and image capture: /// /// If a preset is not available on the camera being used a preset of lower quality will be selected automatically. +/// If the [CaptureMode] is [CaptureMode.photo] the selected resolution will be a 4:3 resolution. +/// If the [CaptureMode] is [CaptureMode.video] the selected resolution will generally be a 16:9 resolution. enum ResolutionPreset { /// 352x288 on iOS, 240p (320x240) on Android and Web low, @@ -12,13 +14,13 @@ enum ResolutionPreset { /// 480p (640x480 on iOS, 720x480 on Android and Web) medium, - /// 720p (1280x720) + /// 720p or 768p (1280x720, 1024x768 or 960x720) high, - /// 1080p (1920x1080) + /// 1080p (1920x1080 or 1440x1080) veryHigh, - /// 2160p (3840x2160 on Android and iOS, 4096x2160 on Web) + /// 2160p (3840x2160 on Android and iOS, 4096x2160 on Web or 2880x2160) ultraHigh, /// The highest resolution available. When CaptureMode is video, this is the same as [ultraHigh]. From ae90b2ed4035994596b1d58c053c0ac3bab7656a Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Tue, 4 Jul 2023 14:10:37 +0930 Subject: [PATCH 08/27] Finalise clea up --- packages/camera/camera/lib/src/camera_preview.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index f22558f9057b..53f9034dc858 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -44,9 +44,9 @@ class CameraPreview extends StatelessWidget { } Widget _wrapInRotatedBox({required Widget child}) { - //if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { + if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { return child; - // } + } return RotatedBox( quarterTurns: _getQuarterTurns(), From df8b338d37e2b04e0cdf14ae868a78fbf1a3a2af Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 5 Jul 2023 13:18:16 +0930 Subject: [PATCH 09/27] Update dependencies --- packages/camera/camera/pubspec.yaml | 12 ++++-------- packages/camera/camera_android/example/pubspec.yaml | 5 ----- packages/camera/camera_android/pubspec.yaml | 3 +-- .../camera_android_camerax/example/pubspec.yaml | 5 ----- .../camera/camera_avfoundation/example/pubspec.yaml | 5 ----- packages/camera/camera_avfoundation/pubspec.yaml | 3 +-- packages/camera/camera_web/example/pubspec.yaml | 5 ----- packages/camera/camera_web/pubspec.yaml | 3 +-- 8 files changed, 7 insertions(+), 34 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 02f0597c72cb..e6f77349c532 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -21,14 +21,10 @@ flutter: default_package: camera_web dependencies: - camera_android: - path: ../../camera/camera_android - camera_avfoundation: - path: ../../camera/camera_avfoundation - camera_platform_interface: - path: ../../camera/camera_platform_interface - camera_web: - path: ../../camera/camera_web + camera_android: ^0.10.9 + camera_avfoundation: ^0.9.14 + camera_platform_interface: ^2.5.2 + camera_web: ^0.3.1+4 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 0f4bb709e663..40f0eedbd821 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -32,8 +32,3 @@ dev_dependencies: flutter: uses-material-design: true - -# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. -# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins -dependency_overrides: - {camera_android: {path: ../../../camera/camera_android}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 61f8e159facf..524b8dcf408f 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -19,8 +19,7 @@ flutter: dartPluginClass: AndroidCamera dependencies: - camera_platform_interface: - path: ../../camera/camera_platform_interface + camera_platform_interface: ^2.5.2 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index bec47e4077c9..663a58b8470b 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -28,8 +28,3 @@ dev_dependencies: flutter: uses-material-design: true - -# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. -# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins -dependency_overrides: - {camera_android_camerax: {path: ../../../camera/camera_android_camerax}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 39393cae940c..3680ea2419c1 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -30,8 +30,3 @@ dev_dependencies: flutter: uses-material-design: true - -# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. -# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins -dependency_overrides: - {camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index a96a0e4d9f55..a219ed62cd6a 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -17,8 +17,7 @@ flutter: dartPluginClass: AVFoundationCamera dependencies: - camera_platform_interface: - path: ../../camera/camera_platform_interface + camera_platform_interface: ^2.5.2 flutter: sdk: flutter stream_transform: ^2.0.0 diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 3789f7f7189d..fef2a773262b 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -20,8 +20,3 @@ dev_dependencies: integration_test: sdk: flutter mocktail: 0.3.0 - -# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. -# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins -dependency_overrides: - {camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_web: {path: ../../../camera/camera_web}} diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 19154483cd33..f2f8f7caec4d 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -17,8 +17,7 @@ flutter: fileName: camera_web.dart dependencies: - camera_platform_interface: - path: ../../camera/camera_platform_interface + camera_platform_interface: ^2.3.1 flutter: sdk: flutter flutter_web_plugins: From 8e36a18db0bf52a3a9ac0f3b0268efd4532b6aea Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 5 Jul 2023 14:14:28 +0930 Subject: [PATCH 10/27] Update camera_web plugin usage --- packages/camera/camera/example/pubspec.yaml | 5 ----- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/pubspec.yaml | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index ddeaebf989d9..3b23ee596ded 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -30,8 +30,3 @@ dev_dependencies: flutter: uses-material-design: true - -# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. -# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins -dependency_overrides: - {camera: {path: ../../../camera/camera}, camera_android: {path: ../../../camera/camera_android}, camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_web: {path: ../../../camera/camera_web}} diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index e6f77349c532..a869e064b8c5 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: camera_android: ^0.10.9 camera_avfoundation: ^0.9.14 camera_platform_interface: ^2.5.2 - camera_web: ^0.3.1+4 + camera_web: ^0.3.2 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index f2f8f7caec4d..7e56cffbefce 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -17,7 +17,7 @@ flutter: fileName: camera_web.dart dependencies: - camera_platform_interface: ^2.3.1 + camera_platform_interface: ^2.5.2 flutter: sdk: flutter flutter_web_plugins: From 99b768f8b83d57d7a4e8771f7ea07218253038c5 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 5 Jul 2023 14:30:34 +0930 Subject: [PATCH 11/27] Remove unnecessary preview size setting --- .../android/src/main/java/io/flutter/plugins/camera/Camera.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 2890897fd135..6e63dc766b91 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -427,7 +427,6 @@ private void createCaptureSession( previewRequestBuilder.addTarget(surface); } } - previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cameraProperties.getSensorInfoActiveArraySize()); // Update camera regions. Size cameraBoundaries = From 2784002ce8f2d91308a746dd6ca3f6e7c833e99a Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 27 Sep 2023 12:41:59 +0930 Subject: [PATCH 12/27] Update package dependencies --- packages/camera/camera/pubspec.yaml | 4 ++-- packages/camera/camera_android/pubspec.yaml | 4 ++-- .../lib/src/android_camera_camerax.dart | 1 - packages/camera/camera_avfoundation/pubspec.yaml | 4 ++-- packages/camera/camera_web/lib/src/camera_web.dart | 1 - packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/example/pubspec.yaml | 5 ----- 7 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index a869e064b8c5..e2158c42615d 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.5+5 +version: 0.11.0 environment: sdk: ">=2.19.0 <4.0.0" @@ -23,7 +23,7 @@ flutter: dependencies: camera_android: ^0.10.9 camera_avfoundation: ^0.9.14 - camera_platform_interface: ^2.5.2 + camera_platform_interface: ^2.5.3 camera_web: ^0.3.2 flutter: sdk: flutter diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 524b8dcf408f..1801e1d8a38d 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.8+13 +version: 0.10.9 environment: sdk: ">=2.19.0 <4.0.0" @@ -19,7 +19,7 @@ flutter: dartPluginClass: AndroidCamera dependencies: - camera_platform_interface: ^2.5.2 + camera_platform_interface: ^2.5.3 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 14c94912dbf2..044778fc20f2 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -219,7 +219,6 @@ class AndroidCameraCameraX extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { // Must obtain proper permissions before attempting to access a camera. diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index a219ed62cd6a..2f9e8d57d10b 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.13+7 +version: 0.9.14 environment: sdk: ">=2.19.0 <4.0.0" @@ -17,7 +17,7 @@ flutter: dartPluginClass: AVFoundationCamera dependencies: - camera_platform_interface: ^2.5.2 + camera_platform_interface: ^2.5.3 flutter: sdk: flutter stream_transform: ^2.0.0 diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 633828b79a81..be41e7b9a874 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -196,7 +196,6 @@ class CameraPlugin extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 7e56cffbefce..f2f8f7caec4d 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -17,7 +17,7 @@ flutter: fileName: camera_web.dart dependencies: - camera_platform_interface: ^2.5.2 + camera_platform_interface: ^2.3.1 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index e722c62d9b30..1dd6f634e95a 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -27,8 +27,3 @@ dev_dependencies: flutter: uses-material-design: true - -# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. -# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins -dependency_overrides: - {camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_windows: {path: ../../../camera/camera_windows}} From 41964b177a61173907376f1dbf4ba977cfc85254 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 27 Sep 2023 13:03:24 +0930 Subject: [PATCH 13/27] Fix Android compile issue --- .../resolution/ResolutionFeature.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index d6fa6ad31368..5347f904615e 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -138,7 +138,7 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { // Using max resolution for the preview is not a good use of system resources. - // Limiting the max resolution used for the preview to 1080p. is a good balance. + // Limiting the max resolution used for the preview to 720p. is a good balance. if (preset.ordinal() > ResolutionPreset.high.ordinal()) { preset = ResolutionPreset.high; } @@ -370,18 +370,19 @@ private void configureResolution(ResolutionPreset resolutionPreset, int cameraId } if (captureSize == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - boolean captureSizeCalculated = false; + boolean captureSizeCalculated = false; - if (SdkCapabilityChecker.supportsEncoderProfiles()) { - recordingProfileLegacy = null; - recordingProfile = - getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset); - List videoProfiles = recordingProfile.getVideoProfiles(); + if (SdkCapabilityChecker.supportsEncoderProfiles()) { + recordingProfileLegacy = null; + recordingProfile = + getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset); + List videoProfiles = recordingProfile.getVideoProfiles(); - EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0); + EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0); - if (defaultVideoProfile != null) { - captureSize = new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight()); + if (defaultVideoProfile != null) { + captureSize = new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight()); + } } } From ff402098e2ffd6f499096b058e76c2ceeb57f8e7 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Wed, 27 Sep 2023 14:26:03 +0930 Subject: [PATCH 14/27] Further clean up --- .../main/java/io/flutter/plugins/camera/CameraRegionUtils.java | 2 +- .../plugins/camera/features/resolution/ResolutionFeature.java | 2 +- packages/camera/camera_web/lib/src/camera_web.dart | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java index ab27f8c161ab..8aecc4eff885 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java @@ -9,7 +9,6 @@ import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; import android.util.Size; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.systemchannels.PlatformChannel; @@ -47,6 +46,7 @@ && supportsDistortionCorrection(cameraProperties)) { } else { rect = cameraProperties.getSensorInfoActiveArraySize(); } + return SizeFactory.create(rect.width(), rect.height()); } else { // No distortion correction support. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index 5347f904615e..b3909d0903ad 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -138,7 +138,7 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { // Using max resolution for the preview is not a good use of system resources. - // Limiting the max resolution used for the preview to 720p. is a good balance. + // Limiting the max resolution used for the preview to 720p is a good balance. if (preset.ordinal() > ResolutionPreset.high.ordinal()) { preset = ResolutionPreset.high; } diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index be41e7b9a874..8b87618aadc5 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -196,6 +196,8 @@ class CameraPlugin extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { + // Capture Mode is not currently supported. + CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { From 5d7a27ca4c05be9210cfbe91907359b866d9a79d Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Thu, 28 Sep 2023 16:01:52 +0930 Subject: [PATCH 15/27] Make changes for contribution guide --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/example/pubspec.yaml | 5 +++++ packages/camera/camera/pubspec.yaml | 5 +++++ packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/example/pubspec.yaml | 5 +++++ packages/camera/camera_android/pubspec.yaml | 5 +++++ packages/camera/camera_android_camerax/example/pubspec.yaml | 5 +++++ packages/camera/camera_android_camerax/pubspec.yaml | 5 +++++ packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ packages/camera/camera_avfoundation/example/pubspec.yaml | 5 +++++ packages/camera/camera_avfoundation/pubspec.yaml | 5 +++++ packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++++ packages/camera/camera_web/example/pubspec.yaml | 5 +++++ packages/camera/camera_web/pubspec.yaml | 5 +++++ packages/camera/camera_windows/example/pubspec.yaml | 5 +++++ packages/camera/camera_windows/pubspec.yaml | 5 +++++ 16 files changed, 76 insertions(+) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 81e8fd45b17d..77e675566d9f 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.6 + +* Adds `CaptureMode` as a parameter to allow configuring the camera for taking photos or videos. `CaptureMode.photo` selects an appropriate 4:3 aspect ratio depending on the `ResolutionPreset`. + ## 0.10.5+5 * Fixes bug where old camera resources were not disposed when switching between camera descriptions. diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 3b23ee596ded..c4780584ec80 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera: {path: ../../../camera/camera}, camera_android: {path: ../../../camera/camera_android}, camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index e2158c42615d..31fef79756b2 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -39,3 +39,8 @@ dev_dependencies: topics: - camera + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_android: {path: ../../camera/camera_android}, camera_avfoundation: {path: ../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index acaa35064435..9d8740bcd8b7 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.9 + +* Adds `CaptureMode` as a parameter to allow configuring the camera for taking photos or videos. `CaptureMode.photo` selects an appropriate 4:3 aspect ratio depending on the `ResolutionPreset`. + ## 0.10.8+13 * Updates annotations lib to 1.7.0. diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 40f0eedbd821..0f4bb709e663 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -32,3 +32,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_android: {path: ../../../camera/camera_android}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 1801e1d8a38d..5ded6046a930 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -32,3 +32,8 @@ dev_dependencies: topics: - camera + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 663a58b8470b..a4963ffa6670 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -28,3 +28,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index d7500c09d3ae..275e2dc1c021 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -36,3 +36,8 @@ dev_dependencies: topics: - camera + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index cac28692b704..f01b00e91738 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.14 + +* Adds `CaptureMode` as a parameter to allow configuring the camera for taking photos or videos. `CaptureMode.photo` selects an appropriate 4:3 aspect ratio depending on the `ResolutionPreset`. + ## 0.9.13+7 * Fixes inverted orientation strings. diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 3680ea2419c1..39393cae940c 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 2f9e8d57d10b..e541bcdba2b4 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -29,3 +29,8 @@ dev_dependencies: topics: - camera + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index eaac4bc7ecc8..b02896e2a168 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.1 + +* Adds `CaptureMode` as a parameter to allow configuring the camera for taking photos or videos. `CaptureMode.photo` selects an appropriate 4:3 aspect ratio depending on the `ResolutionPreset`. + ## 2.6.0 * Adds support to control video fps and bitrate. See `CameraPlatform.createCameraWithSettings`. diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index fef2a773262b..05cc94f79033 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -20,3 +20,8 @@ dev_dependencies: integration_test: sdk: flutter mocktail: 0.3.0 + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index f2f8f7caec4d..f77e54853169 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: topics: - camera + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index 1dd6f634e95a..e4d35f006680 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -27,3 +27,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../../camera/camera_platform_interface}} diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index c78633fbe2f4..24ae342aed2d 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: topics: - camera + +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins +dependency_overrides: + {camera_platform_interface: {path: ../../camera/camera_platform_interface}} From 203ab048c07a8560114e439cf00fc72983aabaa6 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 3 Nov 2023 11:14:42 +1030 Subject: [PATCH 16/27] Move capture mode out of the initialisation --- .../camera/lib/src/camera_controller.dart | 35 ++- packages/camera/camera/pubspec.yaml | 6 +- .../camera/camera/test/camera_value_test.dart | 2 + .../io/flutter/plugins/camera/Camera.java | 39 +++- .../flutter/plugins/camera/DartMessenger.java | 7 +- .../plugins/camera/MethodCallHandlerImpl.java | 16 +- .../plugins/camera/types/CaptureMode.java | 28 ++- .../lib/src/android_camera.dart | 18 ++ .../test/android_camera_test.dart | 9 + .../example/lib/camera_controller.dart | 1 - .../ios/Classes/CameraPlugin.m | 5 +- .../ios/Classes/CameraProperties.h | 9 + .../ios/Classes/CameraProperties.m | 25 ++ .../camera_avfoundation/ios/Classes/FLTCam.h | 2 +- .../camera_avfoundation/ios/Classes/FLTCam.m | 80 +++++-- .../lib/src/avfoundation_camera.dart | 20 +- .../test/avfoundation_camera_test.dart | 10 + .../lib/src/events/camera_event.dart | 12 +- .../method_channel/method_channel_camera.dart | 13 +- .../platform_interface/camera_platform.dart | 3 +- .../lib/src/types/capture_mode.dart | 22 ++ .../test/events/camera_event_test.dart | 216 ++++++++++++++++-- .../method_channel_camera_test.dart | 29 +++ .../integration_test/camera_web_test.dart | 1 + .../camera/camera_web/lib/src/camera_web.dart | 4 +- .../camera_windows/lib/camera_windows.dart | 1 + 26 files changed, 534 insertions(+), 79 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 0510614aab0e..0aa6da06d60a 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -50,6 +50,7 @@ class CameraValue { required this.focusPointSupported, required this.deviceOrientation, required this.description, + required this.captureMode, this.lockedCaptureOrientation, this.recordingOrientation, this.isPreviewPaused = false, @@ -72,6 +73,7 @@ class CameraValue { deviceOrientation: DeviceOrientation.portraitUp, isPreviewPaused: false, description: description, + captureMode: CaptureMode.video, ); /// True after [CameraController.initialize] has completed successfully. @@ -148,6 +150,9 @@ class CameraValue { /// The properties of the camera device controlled by this controller. final CameraDescription description; + /// The current capture mode. + final CaptureMode captureMode; + /// Creates a modified copy of the object. /// /// Explicitly specified fields get the specified value, all other fields get @@ -171,6 +176,7 @@ class CameraValue { bool? isPreviewPaused, CameraDescription? description, Optional? previewPauseOrientation, + CaptureMode? captureMode, }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, @@ -198,6 +204,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, + captureMode: captureMode ?? this.captureMode, ); } @@ -219,7 +226,8 @@ class CameraValue { 'recordingOrientation: $recordingOrientation, ' 'isPreviewPaused: $isPreviewPaused, ' 'previewPausedOrientation: $previewPauseOrientation, ' - 'description: $description)'; + 'description: $description, ' + 'captureMode: $captureMode )'; } } @@ -235,7 +243,6 @@ class CameraController extends ValueNotifier { CameraController( CameraDescription description, this.resolutionPreset, { - this.captureMode = CaptureMode.video, this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(description)); @@ -243,11 +250,6 @@ class CameraController extends ValueNotifier { /// The properties of the camera device controlled by this controller. CameraDescription get description => value.description; - /// The capture mode this controller should be using. Currently only includes - /// photo or video, but may be expanded in the future to include slow motion, - /// panoramic, etc. - final CaptureMode captureMode; - /// The resolution this controller is targeting. /// /// This resolution preset is not guaranteed to be available on the device, @@ -322,7 +324,6 @@ class CameraController extends ValueNotifier { _cameraId = await CameraPlatform.instance.createCamera( description, resolutionPreset, - captureMode: captureMode, enableAudio: enableAudio, ); @@ -350,6 +351,8 @@ class CameraController extends ValueNotifier { .then((CameraInitializedEvent event) => event.exposureMode), focusMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusMode), + captureMode: await initializeCompleter.future + .then((CameraInitializedEvent event) => event.captureMode), exposurePointSupported: await initializeCompleter.future.then( (CameraInitializedEvent event) => event.exposurePointSupported), focusPointSupported: await initializeCompleter.future @@ -536,7 +539,7 @@ class CameraController extends ValueNotifier { 'startVideoRecording was called when a recording is already started.', ); } - if (captureMode == CaptureMode.photo) { + if (value.captureMode == CaptureMode.photo) { throw CameraException( 'The camera is configured for still images.', 'startVideoRecording was called when the camera was configured for still images.', @@ -623,7 +626,7 @@ class CameraController extends ValueNotifier { 'resumeVideoRecording was called when no video is recording.', ); } - if (captureMode == CaptureMode.photo) { + if (value.captureMode == CaptureMode.photo) { throw CameraException( 'The camera is configured for still images.', 'resumeVideoRecording was called when the camera was configured for still images.', @@ -702,6 +705,18 @@ class CameraController extends ValueNotifier { } } + /// Sets the capture mode for + Future setCaptureMode(CaptureMode mode) async { + _throwIfNotInitialized('setCaptureMode'); + try { + final Size? previewSize = + await CameraPlatform.instance.setCaptureMode(_cameraId, mode); + value = value.copyWith(captureMode: mode, previewSize: previewSize); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + /// Sets the exposure point for automatically determining the exposure value. /// /// Supplying a `null` value will reset the exposure point to it's default diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 31fef79756b2..3abb52ef373b 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -21,9 +21,9 @@ flutter: default_package: camera_web dependencies: - camera_android: ^0.10.9 - camera_avfoundation: ^0.9.14 - camera_platform_interface: ^2.5.3 + camera_android: ^0.10.8 + camera_avfoundation: ^0.9.13 + camera_platform_interface: ^2.5.2 camera_web: ^0.3.2 flutter: sdk: flutter diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index dbb1ddcbf789..3acf02976d59 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -28,6 +28,7 @@ void main() { focusPointSupported: true, previewPauseOrientation: DeviceOrientation.portraitUp, description: FakeController.fakeDescription, + captureMode: CaptureMode.video, ); expect(cameraValue, isA()); @@ -144,6 +145,7 @@ void main() { isPreviewPaused: true, previewPauseOrientation: DeviceOrientation.portraitUp, description: FakeController.fakeDescription, + captureMode: CaptureMode.video, ); expect(cameraValue.toString(), diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 6e63dc766b91..5923c54a5686 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -53,6 +53,7 @@ import io.flutter.plugins.camera.features.flash.FlashFeature; import io.flutter.plugins.camera.features.flash.FlashMode; import io.flutter.plugins.camera.features.focuspoint.FocusPointFeature; +import io.flutter.plugins.camera.features.fpsrange.FpsRangeFeature; import io.flutter.plugins.camera.features.resolution.ResolutionFeature; import io.flutter.plugins.camera.features.resolution.ResolutionPreset; import io.flutter.plugins.camera.features.sensororientation.DeviceOrientationManager; @@ -70,6 +71,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.Executors; @FunctionalInterface @@ -114,7 +116,6 @@ class Camera private final SurfaceTextureEntry flutterTexture; private final ResolutionPreset resolutionPreset; - private final CaptureMode captureMode; private final boolean enableAudio; private final Context applicationContext; final DartMessenger dartMessenger; @@ -195,7 +196,6 @@ public Camera( final DartMessenger dartMessenger, final CameraProperties cameraProperties, final ResolutionPreset resolutionPreset, - final CaptureMode captureMode, final boolean enableAudio) { if (activity == null) { @@ -209,10 +209,9 @@ public Camera( this.cameraProperties = cameraProperties; this.cameraFeatureFactory = cameraFeatureFactory; this.resolutionPreset = resolutionPreset; - this.captureMode = captureMode; this.cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, captureMode); + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, CaptureMode.video); // Create capture callback. captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000); @@ -331,7 +330,8 @@ public void onOpened(@NonNull CameraDevice device) { cameraFeatures.getExposureLock().getValue(), cameraFeatures.getAutoFocus().getValue(), cameraFeatures.getExposurePoint().checkIsSupported(), - cameraFeatures.getFocusPoint().checkIsSupported()); + cameraFeatures.getFocusPoint().checkIsSupported(), + resolutionFeature.getCaptureMode()); } } catch (Exception e) { if (BuildConfig.DEBUG) { @@ -506,7 +506,7 @@ private void createCaptureSession( cameraDevice.createCaptureSession(surfaces, callback, backgroundHandler); } - // Send a repeating request to refresh capture session. + // Send a repeating request to refresh capture session. void refreshPreviewCaptureSession( @Nullable Runnable onSuccessCallback, @NonNull ErrorCallback onErrorCallback) { Log.i(TAG, "refreshPreviewCaptureSession"); @@ -976,6 +976,31 @@ public void setFocusMode(final Result result, @NonNull FocusMode newMode) { } } + /** + * Sets new capture mode from dart + * + * @param result Flutter result. + * @param newMode New mode. + */ + public void setCaptureMode(@NonNull final Result result, @NonNull CaptureMode newMode) { + // Re-initalises the camera with the appropriate capture mode. + stopAndReleaseCamera(); + cameraFeatures = + CameraFeatures.init( + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, newMode); + try { + open(imageFormatGroup); + } catch (CameraAccessException e) { + result.error("setDescriptionWhileRecordingFailed", e.getMessage(), null); + } + + final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + Map previewSize = new HashMap(); + previewSize.put("previewWidth", resolutionFeature.getPreviewSize().getWidth()); + previewSize.put("previewHeight", resolutionFeature.getPreviewSize().getHeight()); + result.success(previewSize); + } + /** * Sets new focus point from dart. * @@ -1313,7 +1338,7 @@ public void setDescriptionWhileRecording( cameraProperties = properties; cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, captureMode); + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, cameraFeatures.getResolution().getCaptureMode()); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java index e2b2ef80820d..7aa694c9c4f9 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java @@ -13,6 +13,7 @@ import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.camera.features.autofocus.FocusMode; import io.flutter.plugins.camera.features.exposurelock.ExposureMode; +import io.flutter.plugins.camera.types.CaptureMode; import java.util.HashMap; import java.util.Map; @@ -95,6 +96,7 @@ public void sendDeviceOrientationChangeEvent( * @param focusMode describes the current focus mode that is set on the camera. * @param exposurePointSupported indicates if the camera supports setting an exposure point. * @param focusPointSupported indicates if the camera supports setting a focus point. + * @param captureMode describes the current capture mode that is set on the camera. */ void sendCameraInitializedEvent( Integer previewWidth, @@ -102,13 +104,15 @@ void sendCameraInitializedEvent( ExposureMode exposureMode, FocusMode focusMode, Boolean exposurePointSupported, - Boolean focusPointSupported) { + Boolean focusPointSupported, + CaptureMode captureMode) { assert (previewWidth != null); assert (previewHeight != null); assert (exposureMode != null); assert (focusMode != null); assert (exposurePointSupported != null); assert (focusPointSupported != null); + assert (captureMode != null); this.send( CameraEventType.INITIALIZED, new HashMap() { @@ -119,6 +123,7 @@ void sendCameraInitializedEvent( put("focusMode", focusMode.toString()); put("exposurePointSupported", exposurePointSupported); put("focusPointSupported", focusPointSupported); + put("captureMode", captureMode.toString()); } }); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 028fe6ae88e0..be3581d4185c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -375,6 +375,21 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) result.success(null); break; } + case "setCaptureMode": + { + String modeStr = call.argument("mode"); + CaptureMode mode = CaptureMode.getValueForString(modeStr); + if (mode == null) { + result.error("setCaptureModeFailed", "Unknown capture mode " + modeStr, null); + return; + } + try { + camera.setCaptureMode(result, mode); + } catch (Exception e) { + handleException(e, result); + } + break; + } default: result.notImplemented(); break; @@ -409,7 +424,6 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce dartMessenger, cameraProperties, resolutionPreset, - captureMode, enableAudio); Map reply = new HashMap<>(); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java index 46eb869b7f7f..6778c6f57dc7 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java @@ -4,8 +4,30 @@ package io.flutter.plugins.camera.types; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + // Mirrors camera.dart public enum CaptureMode { - photo, - video, -} + photo("photo"), + video("video"); + + private final String strValue; + + CaptureMode(String strValue) { + this.strValue = strValue; + } + + @Nullable + public static CaptureMode getValueForString(@NonNull String modeStr) { + for (CaptureMode value : values()) { + if (value.strValue.equals(modeStr)) return value; + } + return null; + } + + @Override + public String toString() { + return strValue; + } +} \ No newline at end of file diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 45c599b14f5e..8de553ca42e6 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -371,6 +371,23 @@ class AndroidCamera extends CameraPlatform { }, ); + @override + Future setCaptureMode(int cameraId, CaptureMode mode) async { + final Map? reply = await + _channel.invokeMethod>( + 'setCaptureMode', + { + 'cameraId': cameraId, + 'mode': serializeCaptureMode(mode), + }, + ); + if (reply == null) { + return null; + } + return Size( + (reply['previewWidth']! as int).toDouble(), (reply['previewHeight']! as int).toDouble()); + } + @override Future setExposurePoint(int cameraId, Point? point) { assert(point == null || point.x >= 0 && point.x <= 1); @@ -599,6 +616,7 @@ class AndroidCamera extends CameraPlatform { arguments['exposurePointSupported']! as bool, deserializeFocusMode(arguments['focusMode']! as String), arguments['focusPointSupported']! as bool, + deserializeCaptureMode(arguments['captureMode']! as String), )); break; case 'resolution_changed': diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index fb6f6a71639a..73cfa1a4eaa5 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -204,6 +204,7 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, )); await initializeFuture; @@ -249,6 +250,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + )); await initializeFuture; @@ -297,6 +300,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + )); await initializeFuture; }); @@ -317,6 +322,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + ); await camera.handleCameraMethodCall( MethodCall('initialized', event.toJson()), cameraId); @@ -470,6 +477,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + ), ); await initializeFuture; diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index d65563201202..4553d4c008d5 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -236,7 +236,6 @@ class CameraController extends ValueNotifier { _cameraId = await CameraPlatform.instance.createCamera( description, resolutionPreset, - captureMode: captureMode, enableAudio: enableAudio, ); diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index d6fdd96f93a9..4f58a830ce22 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -179,6 +179,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call @"exposurePointSupported" : @([_camera.captureDevice isExposurePointOfInterestSupported]), @"focusPointSupported" : @([_camera.captureDevice isFocusPointOfInterestSupported]), + @"captureMode" : FLTGetStringForFLTCaptureMode([_camera captureMode]), }]; [self sendDeviceOrientation:[UIDevice currentDevice].orientation]; [_camera start]; @@ -225,6 +226,8 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call y = ((NSNumber *)call.arguments[@"y"]).doubleValue; } [_camera setExposurePointWithResult:result x:x y:y]; + } else if ([@"setCaptureMode" isEqualToString:call.method]) { + [_camera setCaptureModeWithResult:result mode:call.arguments[@"mode"]]; } else if ([@"getMinExposureOffset" isEqualToString:call.method]) { [result sendSuccessWithData:@(_camera.captureDevice.minExposureTargetBias)]; } else if ([@"getMaxExposureOffset" isEqualToString:call.method]) { @@ -304,12 +307,10 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea NSString *cameraName = createMethodCall.arguments[@"cameraName"]; NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; - NSString *captureMode = createMethodCall.arguments[@"captureMode"]; NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; NSError *error; FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName resolutionPreset:resolutionPreset - captureMode: captureMode enableAudio:[enableAudio boolValue] orientation:[[UIDevice currentDevice] orientation] captureSessionQueue:strongSelf.captureSessionQueue diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h index d8536518855b..199e5fc187ee 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h @@ -128,6 +128,9 @@ extern FLTResolutionPreset FLTGetFLTResolutionPresetForString(NSString *preset); typedef NS_ENUM(NSInteger, FLTCaptureMode) { FLTCaptureModePhoto, FLTCaptureModeVideo, + // This should never occur; it indicates an unknown value was received over + // the platform channel. + FLTCaptureModeInvalid, }; /** @@ -136,6 +139,12 @@ typedef NS_ENUM(NSInteger, FLTCaptureMode) { */ extern FLTCaptureMode FLTGetFLTCaptureModeForString(NSString *mode); +/** + * Gets a string representation of capture mode. + * @param mode capture mode + */ +extern NSString *FLTGetStringForFLTCaptureMode(FLTCaptureMode mode); + #pragma mark - video format /** diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m index 69daa515e1a9..8f32878465a7 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m @@ -59,6 +59,31 @@ FLTExposureMode FLTGetFLTExposureModeForString(NSString *mode) { } } +#pragma mark - capture mode + +NSString *FLTGetStringForFLTCaptureMode(FLTCaptureMode mode) { + switch (mode) { + case FLTCaptureModePhoto: + return @"photo"; + case FLTCaptureModeVideo: + return @"video"; + case FLTCaptureModeInvalid: + // This value should never actually be used. + return nil; + } + return nil; +} + +FLTCaptureMode FLTGetFLTCaptureModeForString(NSString *mode) { + if ([mode isEqualToString:@"photo"]) { + return FLTCaptureModePhoto; + } else if ([mode isEqualToString:@"video"]) { + return FLTCaptureModeVideo; + } else { + return FLTCaptureModeInvalid; + } +} + #pragma mark - focus mode NSString *FLTGetStringForFLTFocusMode(FLTFocusMode mode) { diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index 74750a1ebd5f..852fb6a54a28 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -41,7 +41,6 @@ NS_ASSUME_NONNULL_BEGIN /// @param error report to the caller if any error happened creating the camera. - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset - captureMode:(NSString *)captureMode enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue @@ -69,6 +68,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result; - (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; - (void)setExposureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; +- (void)setCaptureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; - (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; - (void)applyFocusMode; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index e3455fd42e78..43cdaf1480ee 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -94,14 +94,12 @@ @implementation FLTCam - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset - captureMode: (NSString *)captureMode enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error { return [self initWithCameraName:cameraName resolutionPreset:resolutionPreset - captureMode: (NSString *)captureMode enableAudio:enableAudio orientation:orientation videoCaptureSession:[[AVCaptureSession alloc] init] @@ -112,7 +110,6 @@ - (instancetype)initWithCameraName:(NSString *)cameraName - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset - captureMode: (NSString *)captureMode enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation videoCaptureSession:(AVCaptureSession *)videoCaptureSession @@ -142,6 +139,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; _flashMode = _captureDevice.hasFlash ? FLTFlashModeAuto : FLTFlashModeOff; _exposureMode = FLTExposureModeAuto; + _captureMode = FLTCaptureModeVideo; _focusMode = FLTFocusModeAuto; _lockedCaptureOrientation = UIDeviceOrientationUnknown; _deviceOrientation = orientation; @@ -403,9 +401,10 @@ - (BOOL)setCaptureSessionForVideo:(FLTResolutionPreset)resolutionPreset withErro return NO; } } + return YES; } -- (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { +- (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { switch (resolutionPreset) { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: @@ -423,7 +422,7 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { if ([_captureDevice lockForConfiguration:nil]) { _captureDevice.activeFormat = format; [_captureDevice unlockForConfiguration]; - return; + return YES; } } } @@ -439,7 +438,7 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { if ([_captureDevice lockForConfiguration:nil]) { _captureDevice.activeFormat = format; [_captureDevice unlockForConfiguration]; - return; + return YES; } } } @@ -461,25 +460,30 @@ - (void)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset { if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { _videoCaptureSession.sessionPreset = AVCaptureSessionPresetLow; } else { - NSError *error = - [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : - @"No capture session available for current capture session." - }]; - @throw error; + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSURLErrorUnknown + userInfo:@{ + NSLocalizedDescriptionKey : + @"No capture session available for current capture session." + }]; + return NO; } } + return YES; } -- (void)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPreset - withCaptureMode: (FLTCaptureMode) captureMode { - NSArray *formats = _captureDevice.formats; +- (BOOL)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPreset + withCaptureMode: (FLTCaptureMode) captureMode + withError:(NSError **)error { + // NSArray *formats = _captureDevice.formats; if (captureMode == FLTCaptureModeVideo) { - [self setCaptureSessionForVideo:resolutionPreset]; + if (![self setCaptureSessionForVideo:resolutionPreset withError:error]) { + return NO; + } } else if (captureMode == FLTCaptureModePhoto) { - [self setCaptureSessionForPhoto:resolutionPreset]; + if (![self setCaptureSessionForPhoto:resolutionPreset withError:error]) { + return NO; + } } CMVideoDimensions previewDimensions = CMVideoFormatDescriptionGetDimensions(_captureDevice.activeFormat.formatDescription); _previewSize = CGSizeMake(previewDimensions.width, previewDimensions.height); @@ -938,6 +942,44 @@ - (void)applyExposureMode { [_captureDevice unlockForConfiguration]; } +- (void)setCaptureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { + FLTCaptureMode mode = FLTGetFLTCaptureModeForString(modeStr); + if (mode == FLTCaptureModeInvalid) { + [result sendError:[NSError errorWithDomain:NSCocoaErrorDomain + code:NSURLErrorUnknown + userInfo:@{ + NSLocalizedDescriptionKey : [NSString + stringWithFormat:@"Unknown capture mode %@", modeStr] + }]]; + return; + } + NSError *error; + _captureMode = mode; + [self setCaptureSessionWithResolutionPreset:_resolutionPreset withCaptureMode:_captureMode withError: &error]; + [result sendSuccessWithData:@{ + @"previewWidth" : @(_previewSize.width), + @"previewHeight" : @(_previewSize.height), + }]; +} + +// - (void)applyCaptureMode { +// [_captureDevice lockForConfiguration:nil]; +// switch (_captureMode) { +// case FLTCaptureModePhoto: +// [_captureDevice setCaptureMode:AVCaptureCaptureModePhoto]; +// break; +// case FLTCaptureModeVideo: +// [_captureDevice setCaptureMode:AVCaptureCaptureModeVideo]; +// break; +// case FLTCaptureModeInvalid: +// // This state is not intended to be reachable; it exists only for error handling during +// // message deserialization. +// NSAssert(false, @""); +// break; +// } +// [_captureDevice unlockForConfiguration]; +// } + - (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { FLTFocusMode mode = FLTGetFLTFocusModeForString(modeStr); if (mode == FLTFocusModeInvalid) { diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 4b62a972c3a9..306d1204ea52 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -95,7 +95,6 @@ class AVFoundationCamera extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { @@ -105,7 +104,6 @@ class AVFoundationCamera extends CameraPlatform { 'resolutionPreset': resolutionPreset != null ? _serializeResolutionPreset(resolutionPreset) : null, - 'captureMode': captureMode.name, 'enableAudio': enableAudio, }); @@ -377,6 +375,23 @@ class AVFoundationCamera extends CameraPlatform { }, ); + @override + Future setCaptureMode(int cameraId, CaptureMode mode) async { + final Map? reply = await + _channel.invokeMethod>( + 'setCaptureMode', + { + 'cameraId': cameraId, + 'mode': serializeCaptureMode(mode), + }, + ); + if (reply == null) { + return null; + } + return Size( + reply['previewWidth']! as double, reply['previewHeight']! as double); + } + @override Future setExposurePoint(int cameraId, Point? point) { assert(point == null || point.x >= 0 && point.x <= 1); @@ -605,6 +620,7 @@ class AVFoundationCamera extends CameraPlatform { arguments['exposurePointSupported']! as bool, deserializeFocusMode(arguments['focusMode']! as String), arguments['focusPointSupported']! as bool, + deserializeCaptureMode(arguments['captureMode']! as String), )); break; case 'resolution_changed': diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index 17b06cddc1ae..5d91459c5bd1 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -205,6 +205,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + )); await initializeFuture; @@ -250,6 +252,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + )); await initializeFuture; @@ -298,6 +302,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + )); await initializeFuture; }); @@ -318,6 +324,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + ); await camera.handleCameraMethodCall( MethodCall('initialized', event.toJson()), cameraId); @@ -472,6 +480,8 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, + ), ); await initializeFuture; diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index 1c76c341d9b1..0003d6366685 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -58,6 +58,7 @@ class CameraInitializedEvent extends CameraEvent { this.exposurePointSupported, this.focusMode, this.focusPointSupported, + this.captureMode, ); /// Converts the supplied [Map] to an instance of the [CameraInitializedEvent] @@ -70,6 +71,7 @@ class CameraInitializedEvent extends CameraEvent { (json['exposurePointSupported'] as bool?) ?? false, focusMode = deserializeFocusMode(json['focusMode']! as String), focusPointSupported = (json['focusPointSupported'] as bool?) ?? false, + captureMode = (json['captureMode'] != null) ? deserializeCaptureMode(json['captureMode']! as String) : CaptureMode.video, super(json['cameraId']! as int); /// The width of the preview in pixels. @@ -84,6 +86,9 @@ class CameraInitializedEvent extends CameraEvent { /// The default focus mode final FocusMode focusMode; + // The default capture mode + final CaptureMode captureMode; + /// Whether setting exposure points is supported. final bool exposurePointSupported; @@ -100,6 +105,7 @@ class CameraInitializedEvent extends CameraEvent { 'exposurePointSupported': exposurePointSupported, 'focusMode': serializeFocusMode(focusMode), 'focusPointSupported': focusPointSupported, + 'captureMode': serializeCaptureMode(captureMode), }; @override @@ -113,8 +119,9 @@ class CameraInitializedEvent extends CameraEvent { exposureMode == other.exposureMode && exposurePointSupported == other.exposurePointSupported && focusMode == other.focusMode && - focusPointSupported == other.focusPointSupported; - + focusPointSupported == other.focusPointSupported && + captureMode == other.captureMode; + @override int get hashCode => Object.hash( super.hashCode, @@ -124,6 +131,7 @@ class CameraInitializedEvent extends CameraEvent { exposurePointSupported, focusMode, focusPointSupported, + captureMode, ); } diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 423abb0596d1..534fbb73745f 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -87,7 +87,6 @@ class MethodChannelCamera extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async => createCameraWithSettings( @@ -111,7 +110,6 @@ class MethodChannelCamera extends CameraPlatform { 'fps': mediaSettings.fps, 'videoBitrate': mediaSettings.videoBitrate, 'audioBitrate': mediaSettings.audioBitrate, - 'captureMode': captureMode.name, 'enableAudio': mediaSettings.enableAudio, }); @@ -400,6 +398,16 @@ class MethodChannelCamera extends CameraPlatform { ); } + @override + Future setCaptureMode(int cameraId, CaptureMode mode) => + _channel.invokeMethod( + 'setCaptureMode', + { + 'cameraId': cameraId, + 'mode': serializeCaptureMode(mode), + }, + ); + @override Future getMinExposureOffset(int cameraId) async { final double? minExposureOffset = await _channel.invokeMethod( @@ -601,6 +609,7 @@ class MethodChannelCamera extends CameraPlatform { arguments['exposurePointSupported']! as bool, deserializeFocusMode(arguments['focusMode']! as String), arguments['focusPointSupported']! as bool, + deserializeCaptureMode(arguments['captureMode']! as String), )); break; case 'resolution_changed': diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index ade2b3cf4697..5cacf8628183 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -50,7 +50,6 @@ abstract class CameraPlatform extends PlatformInterface { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) { throw UnimplementedError('createCamera() is not implemented.'); @@ -277,7 +276,7 @@ abstract class CameraPlatform extends PlatformInterface { /// Set the capture mode for the selected camera. /// /// See [CaptureMode] for more information. - Future setCaptureMode(int cameraId, CaptureMode mode) { + Future setCaptureMode(int cameraId, CaptureMode mode) { throw UnimplementedError('setCaptureMode() is not implemented.'); } diff --git a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart index a2356997522a..734e03e1ea56 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart @@ -12,3 +12,25 @@ enum CaptureMode { /// Capture a video, however this allows the user to take photos while recording. video, } + +/// Returns the capture mode as a string +String serializeCaptureMode(CaptureMode captureMode) { + switch (captureMode) { + case CaptureMode.photo: + return 'photo'; + case CaptureMode.video: + return 'video'; + } +} + +/// Returns the capture mode for a given string. +CaptureMode deserializeCaptureMode(String captureMode) { + switch (captureMode) { + case 'photo': + return CaptureMode.photo; + case 'video': + return CaptureMode.video; + default: + throw ArgumentError('"$captureMode" is not a valid CaptureMode value'); + } +} \ No newline at end of file diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 074f203bea21..6985e490f896 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:math'; + import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -10,8 +12,8 @@ void main() { group('CameraInitializedEvent tests', () { test('Constructor should initialize all properties', () { - const CameraInitializedEvent event = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + const CameraInitializedEvent event = CameraInitializedEvent(1, 1024, 640, + ExposureMode.auto, true, FocusMode.auto, true, CaptureMode.video); expect(event.cameraId, 1); expect(event.previewWidth, 1024); @@ -20,6 +22,7 @@ void main() { expect(event.focusMode, FocusMode.auto); expect(event.exposurePointSupported, true); expect(event.focusPointSupported, true); + expect(event.captureMode, CaptureMode.video); }); test('fromJson should initialize all properties', () { @@ -45,7 +48,15 @@ void main() { test('toJson should return a map with all fields', () { const CameraInitializedEvent event = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); final Map jsonMap = event.toJson(); @@ -57,49 +68,130 @@ void main() { expect(jsonMap['exposurePointSupported'], true); expect(jsonMap['focusMode'], 'auto'); expect(jsonMap['focusPointSupported'], true); + expect(jsonMap['captureMode'], 'video'); }); test('equals should return true if objects are the same', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, true); }); test('equals should return false if cameraId is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 2, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 2, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, false); }); test('equals should return false if previewWidth is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 2048, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 2048, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, false); }); test('equals should return false if previewHeight is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 1024, 980, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 980, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, false); }); test('equals should return false if exposureMode is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.locked, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.locked, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, false); }); @@ -107,34 +199,115 @@ void main() { test('equals should return false if exposurePointSupported is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, false, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + false, + FocusMode.auto, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, false); }); test('equals should return false if focusMode is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.locked, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.locked, + true, + CaptureMode.video, + ); expect(firstEvent == secondEvent, false); }); test('equals should return false if focusPointSupported is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); const CameraInitializedEvent secondEvent = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, false); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + false, + CaptureMode.video, + ); + + expect(firstEvent == secondEvent, false); + }); + + test('equals should return false if captureMode is different', () { + const CameraInitializedEvent firstEvent = CameraInitializedEvent( + 1, + 1024, + 640, + ExposureMode.locked, + true, + FocusMode.locked, + true, + CaptureMode.video, + ); + const CameraInitializedEvent secondEvent = CameraInitializedEvent( + 1, + 1024, + 640, + ExposureMode.locked, + true, + FocusMode.locked, + true, + CaptureMode.photo, + ); expect(firstEvent == secondEvent, false); }); test('hashCode should match hashCode of all properties', () { const CameraInitializedEvent event = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + 1, + 1024, + 640, + ExposureMode.auto, + true, + FocusMode.auto, + true, + CaptureMode.video, + ); final int expectedHashCode = Object.hash( event.cameraId.hashCode, event.previewWidth, @@ -142,7 +315,8 @@ void main() { event.exposureMode, event.exposurePointSupported, event.focusMode, - event.focusPointSupported); + event.focusPointSupported, + event.captureMode); expect(event.hashCode, expectedHashCode); }); diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index 8159cdfe26f5..f280502b3eb0 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -212,6 +212,7 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, )); await initializeFuture; @@ -263,6 +264,7 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, )); await initializeFuture; @@ -317,6 +319,7 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, )); await initializeFuture; }); @@ -337,6 +340,7 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, ); await camera.handleCameraMethodCall( MethodCall('initialized', event.toJson()), cameraId); @@ -494,6 +498,7 @@ void main() { true, FocusMode.auto, true, + CaptureMode.video, ), ); await initializeFuture; @@ -808,6 +813,30 @@ void main() { ]); }); + test('Should set the capture mode', () async{ + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: {'setCaptureMode': null}, + ); + + // Act + await camera.setCaptureMode(cameraId, CaptureMode.video); + await camera.setCaptureMode(cameraId, CaptureMode.photo); + + // Assert + expect(channel.log, [ + isMethodCall('setCaptureMode', arguments: { + 'cameraId': cameraId, + 'mode': 'video' + }), + isMethodCall('setCaptureMode', arguments: { + 'cameraId': cameraId, + 'mode': 'photo' + }), + ]); + }); + test('Should get the min exposure offset', () async { // Arrange final MethodChannelMock channel = MethodChannelMock( diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 6c73bcf76bdc..b3672a95abe1 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -2375,6 +2375,7 @@ void main() { false, FocusMode.auto, false, + CaptureMode.video, ), ), ); diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 8b87618aadc5..b036de54d8f7 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -196,8 +196,6 @@ class CameraPlugin extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - // Capture Mode is not currently supported. - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { @@ -318,6 +316,8 @@ class CameraPlugin extends CameraPlatform { // TODO(bselwe): Add support for focus mode and point (https://github.com/flutter/flutter/issues/86858). FocusMode.auto, false, + // TODO: Add support for capture modes. + CaptureMode.video, ), ); } on html.DomException catch (e) { diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart index 4b0c1586f433..adc60a2a0e71 100644 --- a/packages/camera/camera_windows/lib/camera_windows.dart +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -126,6 +126,7 @@ class CameraWindows extends CameraPlatform { false, FocusMode.auto, false, + CaptureMode.video, ), ); } From baf105456362ae97dbd413430ed8c4902ed51763 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 3 Nov 2023 11:30:00 +1030 Subject: [PATCH 17/27] Fix some tests --- packages/camera/camera/pubspec.yaml | 6 +++--- packages/camera/camera/test/camera_test.dart | 1 + .../camera_android/test/android_camera_test.dart | 12 ++++-------- .../test/avfoundation_camera_test.dart | 15 +++++---------- 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 3abb52ef373b..31fef79756b2 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -21,9 +21,9 @@ flutter: default_package: camera_web dependencies: - camera_android: ^0.10.8 - camera_avfoundation: ^0.9.13 - camera_platform_interface: ^2.5.2 + camera_android: ^0.10.9 + camera_avfoundation: ^0.9.14 + camera_platform_interface: ^2.5.3 camera_web: ^0.3.2 flutter: sdk: flutter diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index ec111ed85949..90aa385f2455 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -36,6 +36,7 @@ CameraInitializedEvent get mockOnCameraInitializedEvent => true, FocusMode.auto, true, + CaptureMode.video, ); DeviceOrientationChangedEvent get mockOnDeviceOrientationChangedEvent => diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 73cfa1a4eaa5..2b00ce204fc9 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -250,8 +250,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, )); await initializeFuture; @@ -300,8 +299,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, )); await initializeFuture; }); @@ -322,8 +320,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, ); await camera.handleCameraMethodCall( MethodCall('initialized', event.toJson()), cameraId); @@ -477,8 +474,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, ), ); await initializeFuture; diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index 5d91459c5bd1..49634fb5dee2 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -205,8 +205,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, )); await initializeFuture; @@ -252,8 +251,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, )); await initializeFuture; @@ -302,8 +300,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, )); await initializeFuture; }); @@ -324,8 +321,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, ); await camera.handleCameraMethodCall( MethodCall('initialized', event.toJson()), cameraId); @@ -480,8 +476,7 @@ void main() { true, FocusMode.auto, true, - CaptureMode.video, - + CaptureMode.video, ), ); await initializeFuture; From 076f40cb3d041a33f15d80aac5ea7ce438e62b6c Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 3 Nov 2023 11:41:07 +1030 Subject: [PATCH 18/27] Re-add setCaptureMode --- .../camera/features/resolution/ResolutionFeature.java | 9 +++++++++ packages/camera/camera_platform_interface/pubspec.yaml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index b3909d0903ad..7d480f895ba3 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -105,6 +105,15 @@ public Size getCaptureSize() { return this.captureSize; } + /** + * Gets the current capture mode for the configured resolution. + * + * @return The current capture mode. + */ + public CaptureMode getCaptureMode() { + return this.captureMode; + } + @NonNull @Override public String getDebugName() { diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index b0c3a1d8bff0..370480718b1d 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.6.0 +version: 2.6.1 environment: sdk: ">=2.19.0 <4.0.0" From b830aeaf71c111c8575495936e6c4f3916244253 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 6 Nov 2023 10:55:22 +1030 Subject: [PATCH 19/27] Fix analyze issues --- packages/camera/camera/test/camera_preview_test.dart | 3 +++ .../camera_platform_interface/lib/src/events/camera_event.dart | 2 +- .../camera_platform_interface/lib/src/types/capture_mode.dart | 2 +- .../test/events/camera_event_test.dart | 2 -- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index c73e1816445c..edd30aebc69c 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -120,6 +120,9 @@ class FakeController extends ValueNotifier @override Future setDescription(CameraDescription description) async {} + @override + Future setCaptureMode(CaptureMode captureMode) async {} + @override CameraDescription get description => value.description; } diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index 0003d6366685..fb9a96243438 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -86,7 +86,7 @@ class CameraInitializedEvent extends CameraEvent { /// The default focus mode final FocusMode focusMode; - // The default capture mode + /// The default capture mode final CaptureMode captureMode; /// Whether setting exposure points is supported. diff --git a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart index 734e03e1ea56..d19075af2f07 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart @@ -33,4 +33,4 @@ CaptureMode deserializeCaptureMode(String captureMode) { default: throw ArgumentError('"$captureMode" is not a valid CaptureMode value'); } -} \ No newline at end of file +} diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 6985e490f896..bdcd3ca69876 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:math'; - import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; From 7e186da29b8b7da642f1343ffe29b9589e27dbf3 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 6 Nov 2023 11:08:10 +1030 Subject: [PATCH 20/27] Fix analyze issues in camerax --- .../lib/src/android_camera_camerax.dart | 6 ++++- .../test/android_camera_camerax_test.dart | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 044778fc20f2..9bd136f2530d 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -314,6 +314,8 @@ class AndroidCameraCameraX extends CameraPlatform { const FocusMode focusMode = FocusMode.auto; const bool focusPointSupported = false; + const CaptureMode captureMode = CaptureMode.video; + cameraEventStreamController.add(CameraInitializedEvent( cameraId, previewResolutionInfo.width.toDouble(), @@ -321,7 +323,9 @@ class AndroidCameraCameraX extends CameraPlatform { exposureMode, exposurePointSupported, focusMode, - focusPointSupported)); + focusPointSupported, + captureMode, + )); } /// Releases the resources of the accessed camera. diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index 1b2b7c50a84f..3a8052116434 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -485,13 +485,15 @@ void main() { // https://github.com/flutter/flutter/issues/120467 final CameraInitializedEvent testCameraInitializedEvent = CameraInitializedEvent( - cameraId, - resolutionWidth.toDouble(), - resolutionHeight.toDouble(), - ExposureMode.auto, - false, - FocusMode.auto, - false); + cameraId, + resolutionWidth.toDouble(), + resolutionHeight.toDouble(), + ExposureMode.auto, + false, + FocusMode.auto, + false, + CaptureMode.video, + ); camera.processCameraProvider = mockProcessCameraProvider; @@ -552,7 +554,14 @@ void main() { final StreamQueue streamQueue = StreamQueue(eventStream); const CameraInitializedEvent testEvent = CameraInitializedEvent( - cameraId, 320, 80, ExposureMode.auto, false, FocusMode.auto, false); + cameraId, + 320, + 80, + ExposureMode.auto, + false, + FocusMode.auto, + false, + CaptureMode.video,); camera.cameraEventStreamController.add(testEvent); From c7e9d15d24052e63a464b0f637c43bec010745b5 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 6 Nov 2023 11:16:54 +1030 Subject: [PATCH 21/27] Fix analyzer info warnings --- .../camera_android/example/lib/camera_controller.dart | 3 --- .../example/lib/camera_controller.dart | 10 ---------- packages/camera/camera_web/lib/src/camera_web.dart | 1 - 3 files changed, 14 deletions(-) diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index e445b906e1af..9ab06140170a 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -174,7 +174,6 @@ class CameraController extends ValueNotifier { CameraController( CameraDescription cameraDescription, this.resolutionPreset, { - this.captureMode = CaptureMode.video, this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(cameraDescription)); @@ -190,8 +189,6 @@ class CameraController extends ValueNotifier { /// See also: [ResolutionPreset]. final ResolutionPreset resolutionPreset; - final CaptureMode captureMode; - /// Whether to include audio when recording a video. final bool enableAudio; diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 4553d4c008d5..36b9ac68cfdd 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -174,7 +174,6 @@ class CameraController extends ValueNotifier { CameraController( CameraDescription cameraDescription, this.resolutionPreset, { - this.captureMode = CaptureMode.video, this.enableAudio = true, this.imageFormatGroup, }) : super(CameraValue.uninitialized(cameraDescription)); @@ -190,15 +189,6 @@ class CameraController extends ValueNotifier { /// See also: [ResolutionPreset]. final ResolutionPreset resolutionPreset; - /// The mode this controller should operate in. - /// - /// This capture mode determines whether the capture session is optimized for - /// video recording or photo capture. - /// - /// Defaults to [CaptureMode.video] as the camera plugin configuration is - /// currently geared towards video recording. - final CaptureMode captureMode; - /// Whether to include audio when recording a video. final bool enableAudio; diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index b036de54d8f7..7fc9bf634b5f 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -316,7 +316,6 @@ class CameraPlugin extends CameraPlatform { // TODO(bselwe): Add support for focus mode and point (https://github.com/flutter/flutter/issues/86858). FocusMode.auto, false, - // TODO: Add support for capture modes. CaptureMode.video, ), ); From e6d2630c20e16fc2338464b4fb3719825ff8a2f4 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 6 Nov 2023 11:35:02 +1030 Subject: [PATCH 22/27] Fix tests --- .../camera/lib/src/camera_controller.dart | 2 +- .../camera/camera/test/camera_value_test.dart | 2 +- .../camera_android/lib/src/android_camera.dart | 2 -- .../test/android_camera_camerax_test.dart | 17 +++++++++-------- .../test/events/camera_event_test.dart | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 0aa6da06d60a..b40e2146bd72 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -227,7 +227,7 @@ class CameraValue { 'isPreviewPaused: $isPreviewPaused, ' 'previewPausedOrientation: $previewPauseOrientation, ' 'description: $description, ' - 'captureMode: $captureMode )'; + 'captureMode: $captureMode)'; } } diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index 3acf02976d59..167390ad2be1 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -149,7 +149,7 @@ void main() { ); expect(cameraValue.toString(), - 'CameraValue(isRecordingVideo: false, isInitialized: false, errorDescription: null, previewSize: Size(10.0, 10.0), isStreamingImages: false, flashMode: FlashMode.auto, exposureMode: ExposureMode.auto, focusMode: FocusMode.auto, exposurePointSupported: true, focusPointSupported: true, deviceOrientation: DeviceOrientation.portraitUp, lockedCaptureOrientation: DeviceOrientation.portraitUp, recordingOrientation: DeviceOrientation.portraitUp, isPreviewPaused: true, previewPausedOrientation: DeviceOrientation.portraitUp, description: CameraDescription(, CameraLensDirection.back, 0))'); + 'CameraValue(isRecordingVideo: false, isInitialized: false, errorDescription: null, previewSize: Size(10.0, 10.0), isStreamingImages: false, flashMode: FlashMode.auto, exposureMode: ExposureMode.auto, focusMode: FocusMode.auto, exposurePointSupported: true, focusPointSupported: true, deviceOrientation: DeviceOrientation.portraitUp, lockedCaptureOrientation: DeviceOrientation.portraitUp, recordingOrientation: DeviceOrientation.portraitUp, isPreviewPaused: true, previewPausedOrientation: DeviceOrientation.portraitUp, description: CameraDescription(, CameraLensDirection.back, 0), captureMode: CaptureMode.video)'); }); }); } diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 8de553ca42e6..d8e16961928a 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -95,7 +95,6 @@ class AndroidCamera extends CameraPlatform { Future createCamera( CameraDescription cameraDescription, ResolutionPreset? resolutionPreset, { - CaptureMode captureMode = CaptureMode.video, bool enableAudio = false, }) async { try { @@ -105,7 +104,6 @@ class AndroidCamera extends CameraPlatform { 'resolutionPreset': resolutionPreset != null ? _serializeResolutionPreset(resolutionPreset) : null, - 'captureMode': captureMode.name, 'enableAudio': enableAudio, }); diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index 3a8052116434..4be184302eec 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -554,14 +554,15 @@ void main() { final StreamQueue streamQueue = StreamQueue(eventStream); const CameraInitializedEvent testEvent = CameraInitializedEvent( - cameraId, - 320, - 80, - ExposureMode.auto, - false, - FocusMode.auto, - false, - CaptureMode.video,); + cameraId, + 320, + 80, + ExposureMode.auto, + false, + FocusMode.auto, + false, + CaptureMode.video, + ); camera.cameraEventStreamController.add(testEvent); diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index bdcd3ca69876..54be6724cf55 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -58,7 +58,7 @@ void main() { final Map jsonMap = event.toJson(); - expect(jsonMap.length, 7); + expect(jsonMap.length, 8); expect(jsonMap['cameraId'], 1); expect(jsonMap['previewWidth'], 1024); expect(jsonMap['previewHeight'], 640); From 327f4499989a9e6898aaed62d26c9e713b7ee9bf Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 24 Nov 2023 22:19:30 +1030 Subject: [PATCH 23/27] Address review comments --- packages/camera/camera/example/pubspec.yaml | 2 +- .../camera/lib/src/camera_controller.dart | 2 +- packages/camera/camera/test/camera_test.dart | 53 +++++++++++++ .../io/flutter/plugins/camera/Camera.java | 29 +++++-- .../plugins/camera/CameraProperties.java | 9 ++- .../plugins/camera/CameraPropertiesImpl.java | 5 +- .../plugins/camera/CameraRegionUtils.java | 2 +- .../camera/features/CameraFeatureFactory.java | 3 +- .../features/CameraFeatureFactoryImpl.java | 4 +- .../camera/features/CameraFeatures.java | 3 +- .../features/fpsrange/FpsRangeFeature.java | 5 +- .../resolution/ResolutionFeature.java | 76 +++++++++++-------- .../camera/media/ImageStreamReader.java | 1 - .../plugins/camera/types/CaptureMode.java | 2 +- .../lib/src/android_camera.dart | 28 +++---- .../lib/src/android_camera_camerax.dart | 18 ++--- .../lib/src/avfoundation_camera.dart | 28 +++---- .../lib/src/events/camera_event.dart | 10 ++- .../method_channel/method_channel_camera.dart | 4 +- .../lib/src/types/capture_mode.dart | 33 ++++---- .../lib/src/types/resolution_preset.dart | 2 - .../test/events/camera_event_test.dart | 2 +- .../method_channel_camera_test.dart | 6 +- 23 files changed, 200 insertions(+), 127 deletions(-) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index c4780584ec80..ddeaebf989d9 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -34,4 +34,4 @@ flutter: # FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. # See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins dependency_overrides: - {camera: {path: ../../../camera/camera}, camera_android: {path: ../../../camera/camera_android}, camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}} + {camera: {path: ../../../camera/camera}, camera_android: {path: ../../../camera/camera_android}, camera_avfoundation: {path: ../../../camera/camera_avfoundation}, camera_platform_interface: {path: ../../../camera/camera_platform_interface}, camera_web: {path: ../../../camera/camera_web}} diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index b40e2146bd72..7e017132d6c9 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -705,7 +705,7 @@ class CameraController extends ValueNotifier { } } - /// Sets the capture mode for + /// Sets the capture mode for the camera. Future setCaptureMode(CaptureMode mode) async { _throwIfNotInitialized('setCaptureMode'); try { diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 90aa385f2455..80c6b088ee5c 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -1402,6 +1402,52 @@ void main() { 'This is a test error message', ))); }); + + test('setCaptureMode() calls $CameraPlatform', () async { + final CameraController cameraController = CameraController( + const CameraDescription( + name: 'cam', + lensDirection: CameraLensDirection.back, + sensorOrientation: 90), + ResolutionPreset.max); + await cameraController.initialize(); + when(CameraPlatform.instance + .setCaptureMode(mockInitializeCamera, CaptureMode.photo)) + .thenAnswer((_) => Future.value(const Size(640, 480))); + await cameraController.setCaptureMode(CaptureMode.photo); + + verify(CameraPlatform.instance + .setCaptureMode(cameraController.cameraId, CaptureMode.photo)) + .called(1); + }); + + test('setCaptureMode throws $CameraException on $PlatformException', + () async { + final CameraController cameraController = CameraController( + const CameraDescription( + name: 'cam', + lensDirection: CameraLensDirection.back, + sensorOrientation: 90), + ResolutionPreset.max); + await cameraController.initialize(); + + when(CameraPlatform.instance + .setCaptureMode(cameraController.cameraId, CaptureMode.photo)) + .thenThrow( + PlatformException( + code: 'TEST_ERROR', + message: 'This is a test error message', + ), + ); + + expect( + cameraController.setCaptureMode(CaptureMode.photo), + throwsA(isA().having( + (CameraException error) => error.description, + 'TEST_ERROR', + 'This is a test error message', + ))); + }); }); } @@ -1555,6 +1601,13 @@ class MockCameraPlatform extends Mock Invocation.method(#setExposureOffset, [cameraId, offset]), returnValue: Future.value(1.0), ) as Future; + + @override + Future setCaptureMode(int? cameraId, CaptureMode mode) async => + super.noSuchMethod( + Invocation.method(#setCaptureMode, [cameraId, mode]), + returnValue: Future.value(const Size(680, 480)), + ) as Future; } class MockCameraDescription extends CameraDescription { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 5923c54a5686..418fc2c6dc4f 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -12,7 +12,6 @@ import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; @@ -53,7 +52,6 @@ import io.flutter.plugins.camera.features.flash.FlashFeature; import io.flutter.plugins.camera.features.flash.FlashMode; import io.flutter.plugins.camera.features.focuspoint.FocusPointFeature; -import io.flutter.plugins.camera.features.fpsrange.FpsRangeFeature; import io.flutter.plugins.camera.features.resolution.ResolutionFeature; import io.flutter.plugins.camera.features.resolution.ResolutionPreset; import io.flutter.plugins.camera.features.sensororientation.DeviceOrientationManager; @@ -61,8 +59,8 @@ import io.flutter.plugins.camera.media.ImageStreamReader; import io.flutter.plugins.camera.media.MediaRecorderBuilder; import io.flutter.plugins.camera.types.CameraCaptureProperties; -import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; import io.flutter.plugins.camera.types.CaptureMode; +import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; import io.flutter.view.TextureRegistry.SurfaceTextureEntry; import java.io.File; import java.io.IOException; @@ -211,7 +209,12 @@ public Camera( this.resolutionPreset = resolutionPreset; this.cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, CaptureMode.video); + cameraFeatureFactory, + cameraProperties, + activity, + dartMessenger, + resolutionPreset, + CaptureMode.video); // Create capture callback. captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000); @@ -978,7 +981,7 @@ public void setFocusMode(final Result result, @NonNull FocusMode newMode) { /** * Sets new capture mode from dart - * + * * @param result Flutter result. * @param newMode New mode. */ @@ -987,7 +990,12 @@ public void setCaptureMode(@NonNull final Result result, @NonNull CaptureMode ne stopAndReleaseCamera(); cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, newMode); + cameraFeatureFactory, + cameraProperties, + activity, + dartMessenger, + resolutionPreset, + newMode); try { open(imageFormatGroup); } catch (CameraAccessException e) { @@ -995,7 +1003,7 @@ public void setCaptureMode(@NonNull final Result result, @NonNull CaptureMode ne } final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); - Map previewSize = new HashMap(); + Map previewSize = new HashMap(); previewSize.put("previewWidth", resolutionFeature.getPreviewSize().getWidth()); previewSize.put("previewHeight", resolutionFeature.getPreviewSize().getHeight()); result.success(previewSize); @@ -1338,7 +1346,12 @@ public void setDescriptionWhileRecording( cameraProperties = properties; cameraFeatures = CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset, cameraFeatures.getResolution().getCaptureMode()); + cameraFeatureFactory, + cameraProperties, + activity, + dartMessenger, + resolutionPreset, + cameraFeatures.getResolution().getCaptureMode()); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java index 75be9ca99b68..c64ade792f26 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java @@ -274,11 +274,12 @@ public interface CameraProperties { @NonNull int[] getAvailableNoiseReductionModes(); - /** + /** * Returns a list of available output sizes for a particular format. - * - *

By default maps to the @see android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP key. - * + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP key. + * * @param format int The format for which available output sizes will be returned. * @return Size[] List of available output sizes for a particular format. */ diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java index abe07c6db910..9fc1b790e275 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java @@ -168,7 +168,8 @@ public int[] getAvailableNoiseReductionModes() { @NonNull @Override public Size[] getAvailableOutputSizes(int imageFormat) { - return cameraCharacteristics.get( - CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(imageFormat); + return cameraCharacteristics + .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) + .getOutputSizes(imageFormat); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java index 8aecc4eff885..df9b06b85515 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java @@ -46,7 +46,7 @@ && supportsDistortionCorrection(cameraProperties)) { } else { rect = cameraProperties.getSensorInfoActiveArraySize(); } - + return SizeFactory.create(rect.width(), rect.height()); } else { // No distortion correction support. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java index 5d965cd766c6..0daef2f04e1f 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java @@ -108,7 +108,8 @@ FocusPointFeature createFocusPointFeature( * @return newly created instance of the FpsRangeFeature class. */ @NonNull - FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties, @NonNull CaptureMode captureMode); + FpsRangeFeature createFpsRangeFeature( + @NonNull CameraProperties cameraProperties, @NonNull CaptureMode captureMode); /** * Creates a new instance of the sensor orientation feature. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java index 32edb32d1ee5..910370e13012 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java @@ -74,8 +74,8 @@ public FocusPointFeature createFocusPointFeature( @NonNull @Override - public FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties, - @NonNull CaptureMode captureMode) { + public FpsRangeFeature createFpsRangeFeature( + @NonNull CameraProperties cameraProperties, @NonNull CaptureMode captureMode) { return new FpsRangeFeature(cameraProperties, captureMode); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java index 03926bcb2dae..469eeafd16b0 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java @@ -69,7 +69,8 @@ public static CameraFeatures init( cameraFeatures.setFlash(cameraFeatureFactory.createFlashFeature(cameraProperties)); cameraFeatures.setFocusPoint( cameraFeatureFactory.createFocusPointFeature(cameraProperties, sensorOrientationFeature)); - cameraFeatures.setFpsRange(cameraFeatureFactory.createFpsRangeFeature(cameraProperties, captureMode)); + cameraFeatures.setFpsRange( + cameraFeatureFactory.createFpsRangeFeature(cameraProperties, captureMode)); cameraFeatures.setNoiseReduction( cameraFeatureFactory.createNoiseReductionFeature(cameraProperties)); cameraFeatures.setResolution( diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java index 12e78f1cb8bb..bd226f7b7c04 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java @@ -6,7 +6,6 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CaptureRequest; -import android.util.Log; import android.util.Range; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,8 +27,8 @@ public class FpsRangeFeature extends CameraFeature> { * * @param cameraProperties Collection of characteristics for the current camera device. */ - public FpsRangeFeature(@NonNull CameraProperties cameraProperties, - @NonNull CaptureMode captureMode) { + public FpsRangeFeature( + @NonNull CameraProperties cameraProperties, @NonNull CaptureMode captureMode) { super(cameraProperties); this.captureMode = captureMode; diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index 7d480f895ba3..e152c4dc4d71 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -11,7 +11,6 @@ import android.media.CamcorderProfile; import android.media.EncoderProfiles; import android.os.Build; -import android.util.Log; import android.util.Size; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -20,7 +19,6 @@ import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; import io.flutter.plugins.camera.types.CaptureMode; - import java.util.ArrayList; import java.util.List; @@ -32,31 +30,27 @@ * required to configure the resolution using the {@link android.hardware.camera2} API. */ public class ResolutionFeature extends CameraFeature { - @Nullable - private Size captureSize; - @Nullable - private Size previewSize; + @Nullable private Size captureSize; + @Nullable private Size previewSize; private CamcorderProfile recordingProfileLegacy; private EncoderProfiles recordingProfile; - @NonNull - private ResolutionPreset currentSetting; + @NonNull private ResolutionPreset currentSetting; private int cameraId; - @NonNull - private CaptureMode captureMode; + @NonNull private CaptureMode captureMode; /** * Creates a new instance of the {@link ResolutionFeature}. * * @param cameraProperties Collection of characteristics for the current camera device. * @param resolutionPreset Platform agnostic enum containing resolution information. - * @param cameraName Camera identifier of the camera for which to configure the resolution. - * @param captureMode Capture mode to configure the appropriate resolution and aspect ratio. + * @param cameraName Camera identifier of the camera for which to configure the resolution. + * @param captureMode Capture mode to configure the appropriate resolution and aspect ratio. */ public ResolutionFeature( - @NonNull CameraProperties cameraProperties, - @NonNull ResolutionPreset resolutionPreset, - @NonNull String cameraName, - @NonNull CaptureMode captureMode) { + @NonNull CameraProperties cameraProperties, + @NonNull ResolutionPreset resolutionPreset, + @NonNull String cameraName, + @NonNull CaptureMode captureMode) { super(cameraProperties); this.currentSetting = resolutionPreset; this.captureMode = captureMode; @@ -66,7 +60,11 @@ public ResolutionFeature( this.cameraId = -1; return; } - configureResolution(resolutionPreset, cameraId, captureMode, cameraProperties.getAvailableOutputSizes(ImageFormat.PRIVATE)); + configureResolution( + resolutionPreset, + cameraId, + captureMode, + cameraProperties.getAvailableOutputSizes(ImageFormat.JPEG)); } /** @@ -107,7 +105,7 @@ public Size getCaptureSize() { /** * Gets the current capture mode for the configured resolution. - * + * * @return The current capture mode. */ public CaptureMode getCaptureMode() { @@ -130,7 +128,11 @@ public ResolutionPreset getValue() { @Override public void setValue(@NonNull ResolutionPreset value) { this.currentSetting = value; - configureResolution(currentSetting, cameraId, captureMode, cameraProperties.getAvailableOutputSizes(ImageFormat.PRIVATE)); + configureResolution( + currentSetting, + cameraId, + captureMode, + cameraProperties.getAvailableOutputSizes(ImageFormat.JPEG)); } @Override @@ -144,7 +146,8 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { } @VisibleForTesting - static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) + static Size computeBestPreviewSize( + int cameraId, ResolutionPreset preset, CaptureMode captureMode, Size[] availableOutputSizes) throws IndexOutOfBoundsException { // Using max resolution for the preview is not a good use of system resources. // Limiting the max resolution used for the preview to 720p is a good balance. @@ -177,11 +180,11 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset, Captur * ResolutionPreset}. Supports SDK < 31. * * @param cameraId Camera identifier which indicates the device's camera for which to select a - * {@link android.media.CamcorderProfile}. - * @param preset The {@link ResolutionPreset} for which is to be translated to a {@link - * android.media.CamcorderProfile}. + * {@link android.media.CamcorderProfile}. + * @param preset The {@link ResolutionPreset} for which is to be translated to a {@link + * android.media.CamcorderProfile}. * @return The best possible {@link android.media.CamcorderProfile} that matches the supplied - * {@link ResolutionPreset}. + * {@link ResolutionPreset}. */ @TargetApi(Build.VERSION_CODES.R) // All of these cases deliberately fall through to get the best available profile. @@ -292,10 +295,12 @@ public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPrese // All of these cases deliberately fall through to get the best available camera profile. @SuppressWarnings("fallthrough") @NonNull - public static Size getBestAvailableCameraSizeForResolutionPreset(@NonNull ResolutionPreset preset, Size[] availableOutputSizes) { + public static Size getBestAvailableCameraSizeForResolutionPreset( + @NonNull ResolutionPreset preset, Size[] availableOutputSizes) { List availableStandardOutputSizes = new ArrayList<>(); for (Size outputSize : availableOutputSizes) { - if ((Math.abs((double) outputSize.getWidth() / outputSize.getHeight() - (double)4 / 3) < 0.01)) { + if ((Math.abs((double) outputSize.getWidth() / outputSize.getHeight() - (double) 4 / 3) + < 0.01)) { availableStandardOutputSizes.add(outputSize); } } @@ -352,12 +357,14 @@ public static Size getBestAvailableCameraSizeForResolutionPreset(@NonNull Resolu } } - private static Size selectPhotoCaptureSize(Integer resolutionWidth, List availableStandardOutputSizes) { + private static Size selectPhotoCaptureSize( + Integer resolutionWidth, List availableStandardOutputSizes) { Size selectedPreviewResolution = null; int currentHighestPixel = 0; for (Size standardOutputSize : availableStandardOutputSizes) { // When no resolutionWidth is provided, the highest resolution should be selected. - if ((resolutionWidth != null && standardOutputSize.getHeight() == resolutionWidth) || resolutionWidth == null) { + if ((resolutionWidth != null && standardOutputSize.getHeight() == resolutionWidth) + || resolutionWidth == null) { if (standardOutputSize.getWidth() * standardOutputSize.getHeight() > currentHighestPixel) { selectedPreviewResolution = standardOutputSize; currentHighestPixel = standardOutputSize.getWidth() * standardOutputSize.getHeight(); @@ -367,15 +374,19 @@ private static Size selectPhotoCaptureSize(Integer resolutionWidth, List a return selectedPreviewResolution; } - - private void configureResolution(ResolutionPreset resolutionPreset, int cameraId, CaptureMode captureMode, Size[] availableOutputSizes) + private void configureResolution( + ResolutionPreset resolutionPreset, + int cameraId, + CaptureMode captureMode, + Size[] availableOutputSizes) throws IndexOutOfBoundsException { if (!checkIsSupported()) { return; } // Attempt to select the highest resolution from the available ones when in photo mode. if (captureMode == CaptureMode.photo) { - captureSize = getBestAvailableCameraSizeForResolutionPreset(resolutionPreset, availableOutputSizes); + captureSize = + getBestAvailableCameraSizeForResolutionPreset(resolutionPreset, availableOutputSizes); } if (captureSize == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -404,6 +415,7 @@ private void configureResolution(ResolutionPreset resolutionPreset, int cameraId new Size(recordingProfileLegacy.videoFrameWidth, recordingProfileLegacy.videoFrameHeight); } - previewSize = computeBestPreviewSize(cameraId, resolutionPreset, captureMode, availableOutputSizes); + previewSize = + computeBestPreviewSize(cameraId, resolutionPreset, captureMode, availableOutputSizes); } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java index ee4073c593ab..5c8d8814e1d7 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReader.java @@ -9,7 +9,6 @@ import android.media.ImageReader; import android.os.Handler; import android.os.Looper; -import android.util.Log; import android.view.Surface; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java index 6778c6f57dc7..6cb90786e4f8 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureMode.java @@ -30,4 +30,4 @@ public static CaptureMode getValueForString(@NonNull String modeStr) { public String toString() { return strValue; } -} \ No newline at end of file +} diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index d8e16961928a..da1a954bef6b 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -371,19 +371,19 @@ class AndroidCamera extends CameraPlatform { @override Future setCaptureMode(int cameraId, CaptureMode mode) async { - final Map? reply = await - _channel.invokeMethod>( - 'setCaptureMode', - { - 'cameraId': cameraId, - 'mode': serializeCaptureMode(mode), - }, - ); - if (reply == null) { - return null; - } - return Size( - (reply['previewWidth']! as int).toDouble(), (reply['previewHeight']! as int).toDouble()); + final Map? reply = + await _channel.invokeMethod>( + 'setCaptureMode', + { + 'cameraId': cameraId, + 'mode': mode.name, + }, + ); + if (reply == null) { + return null; + } + return Size((reply['previewWidth']! as int).toDouble(), + (reply['previewHeight']! as int).toDouble()); } @override @@ -614,7 +614,7 @@ class AndroidCamera extends CameraPlatform { arguments['exposurePointSupported']! as bool, deserializeFocusMode(arguments['focusMode']! as String), arguments['focusPointSupported']! as bool, - deserializeCaptureMode(arguments['captureMode']! as String), + CaptureMode.deserialize(arguments['captureMode']! as String), )); break; case 'resolution_changed': diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 9bd136f2530d..22078a8ad1b2 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -317,15 +317,15 @@ class AndroidCameraCameraX extends CameraPlatform { const CaptureMode captureMode = CaptureMode.video; cameraEventStreamController.add(CameraInitializedEvent( - cameraId, - previewResolutionInfo.width.toDouble(), - previewResolutionInfo.height.toDouble(), - exposureMode, - exposurePointSupported, - focusMode, - focusPointSupported, - captureMode, - )); + cameraId, + previewResolutionInfo.width.toDouble(), + previewResolutionInfo.height.toDouble(), + exposureMode, + exposurePointSupported, + focusMode, + focusPointSupported, + captureMode, + )); } /// Releases the resources of the accessed camera. diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 306d1204ea52..fdf8b36f53b8 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -377,19 +377,19 @@ class AVFoundationCamera extends CameraPlatform { @override Future setCaptureMode(int cameraId, CaptureMode mode) async { - final Map? reply = await - _channel.invokeMethod>( - 'setCaptureMode', - { - 'cameraId': cameraId, - 'mode': serializeCaptureMode(mode), - }, - ); - if (reply == null) { - return null; - } - return Size( - reply['previewWidth']! as double, reply['previewHeight']! as double); + final Map? reply = + await _channel.invokeMethod>( + 'setCaptureMode', + { + 'cameraId': cameraId, + 'mode': mode.name, + }, + ); + if (reply == null) { + return null; + } + return Size( + reply['previewWidth']! as double, reply['previewHeight']! as double); } @override @@ -620,7 +620,7 @@ class AVFoundationCamera extends CameraPlatform { arguments['exposurePointSupported']! as bool, deserializeFocusMode(arguments['focusMode']! as String), arguments['focusPointSupported']! as bool, - deserializeCaptureMode(arguments['captureMode']! as String), + CaptureMode.deserialize(arguments['captureMode']! as String), )); break; case 'resolution_changed': diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index fb9a96243438..cb876e0735a4 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -71,7 +71,9 @@ class CameraInitializedEvent extends CameraEvent { (json['exposurePointSupported'] as bool?) ?? false, focusMode = deserializeFocusMode(json['focusMode']! as String), focusPointSupported = (json['focusPointSupported'] as bool?) ?? false, - captureMode = (json['captureMode'] != null) ? deserializeCaptureMode(json['captureMode']! as String) : CaptureMode.video, + captureMode = (json['captureMode'] != null) + ? CaptureMode.deserialize(json['captureMode']! as String) + : CaptureMode.video, super(json['cameraId']! as int); /// The width of the preview in pixels. @@ -105,7 +107,7 @@ class CameraInitializedEvent extends CameraEvent { 'exposurePointSupported': exposurePointSupported, 'focusMode': serializeFocusMode(focusMode), 'focusPointSupported': focusPointSupported, - 'captureMode': serializeCaptureMode(captureMode), + 'captureMode': captureMode.name, }; @override @@ -119,9 +121,9 @@ class CameraInitializedEvent extends CameraEvent { exposureMode == other.exposureMode && exposurePointSupported == other.exposurePointSupported && focusMode == other.focusMode && - focusPointSupported == other.focusPointSupported && + focusPointSupported == other.focusPointSupported && captureMode == other.captureMode; - + @override int get hashCode => Object.hash( super.hashCode, diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 534fbb73745f..74ae5be67c65 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -404,7 +404,7 @@ class MethodChannelCamera extends CameraPlatform { 'setCaptureMode', { 'cameraId': cameraId, - 'mode': serializeCaptureMode(mode), + 'mode': mode.name, }, ); @@ -609,7 +609,7 @@ class MethodChannelCamera extends CameraPlatform { arguments['exposurePointSupported']! as bool, deserializeFocusMode(arguments['focusMode']! as String), arguments['focusPointSupported']! as bool, - deserializeCaptureMode(arguments['captureMode']! as String), + CaptureMode.deserialize(arguments['captureMode']! as String), )); break; case 'resolution_changed': diff --git a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart index d19075af2f07..6ab9a7cca76c 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + /// The mode the controller should operate in. /// /// This capture mode determines whether the capture session is optimized for @@ -10,27 +14,16 @@ enum CaptureMode { photo, /// Capture a video, however this allows the user to take photos while recording. - video, -} + video; -/// Returns the capture mode as a string -String serializeCaptureMode(CaptureMode captureMode) { - switch (captureMode) { - case CaptureMode.photo: - return 'photo'; - case CaptureMode.video: - return 'video'; - } -} + factory CaptureMode.deserialize(String captureMode) { + switch (captureMode) { + case 'photo': + return CaptureMode.photo; + case 'video': + return CaptureMode.video; + } -/// Returns the capture mode for a given string. -CaptureMode deserializeCaptureMode(String captureMode) { - switch (captureMode) { - case 'photo': - return CaptureMode.photo; - case 'video': - return CaptureMode.video; - default: - throw ArgumentError('"$captureMode" is not a valid CaptureMode value'); + throw ArgumentError('"$captureMode" is not a valid CaptureMode value'); } } diff --git a/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart b/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart index dc9fef89cfb2..48549ddfb2bf 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/resolution_preset.dart @@ -5,8 +5,6 @@ /// Affect the quality of video recording and image capture: /// /// If a preset is not available on the camera being used a preset of lower quality will be selected automatically. -/// If the [CaptureMode] is [CaptureMode.photo] the selected resolution will be a 4:3 resolution. -/// If the [CaptureMode] is [CaptureMode.video] the selected resolution will generally be a 16:9 resolution. enum ResolutionPreset { /// 352x288 on iOS, 240p (320x240) on Android and Web low, diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 54be6724cf55..facb2e7a052f 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -269,7 +269,7 @@ void main() { expect(firstEvent == secondEvent, false); }); - + test('equals should return false if captureMode is different', () { const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index f280502b3eb0..5e46c475d272 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -813,11 +813,11 @@ void main() { ]); }); - test('Should set the capture mode', () async{ + test('Should set the capture mode', () async { // Arrange final MethodChannelMock channel = MethodChannelMock( - channelName: 'plugins.flutter.io/camera', - methods: {'setCaptureMode': null}, + channelName: 'plugins.flutter.io/camera', + methods: {'setCaptureMode': null}, ); // Act From 7560e628b83ce3fcaff4758a07bd317eb0ec72d6 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 24 Nov 2023 22:25:18 +1030 Subject: [PATCH 24/27] Fix failing analyze --- .../camera_platform_interface/lib/src/types/capture_mode.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart index 6ab9a7cca76c..83f252591339 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/capture_mode.dart @@ -16,6 +16,7 @@ enum CaptureMode { /// Capture a video, however this allows the user to take photos while recording. video; + /// Deserializes the [captureMode] string argument to the corresponding CaptureMode enum. factory CaptureMode.deserialize(String captureMode) { switch (captureMode) { case 'photo': From 675b84167788a22c46c7dd373fa6c9fd143c0691 Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Fri, 1 Dec 2023 16:59:05 +1030 Subject: [PATCH 25/27] Start on iOS native/integration tests --- .../example/integration_test/camera_test.dart | 70 +++++++++++++++++-- .../ios/Runner.xcodeproj/project.pbxproj | 26 +++++-- .../ios/RunnerTests/CameraCaptureModeTests.m | 43 ++++++++++++ .../ios/RunnerTests/CameraPropertiesTests.m | 14 ++++ .../example/lib/camera_controller.dart | 15 +++- .../camera_avfoundation/ios/Classes/FLTCam.m | 18 ----- .../test/avfoundation_camera_test.dart | 21 ++++++ 7 files changed, 177 insertions(+), 30 deletions(-) create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 315a34e9f9b1..623297fbd318 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -30,7 +30,7 @@ void main() { await testDir.delete(recursive: true); }); - final Map presetExpectedSizes = + final Map presetVideoExpectedSizes = { ResolutionPreset.low: const Size(288, 352), ResolutionPreset.medium: const Size(480, 640), @@ -40,6 +40,17 @@ void main() { // Don't bother checking for max here since it could be anything. }; + final Map presetPhotoExpectedSizes = + { + ResolutionPreset.low: const Size(288, 352), + ResolutionPreset.medium: const Size(480, 640), + ResolutionPreset.high: const Size(768, 1024), + ResolutionPreset.veryHigh: const Size(1080, 1440), + ResolutionPreset.ultraHigh: const Size(2160, 4096), + // Don't bother checking for max here since it could be anything. + }; + + /// Verify that [actual] has dimensions that are at least as large as /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns /// whether the dimensions exactly match. @@ -53,9 +64,9 @@ void main() { // This tests that the capture is no bigger than the preset, since we have // automatic code to fall back to smaller sizes when we need to. Returns // whether the image is exactly the desired resolution. - Future testCaptureImageResolution( + Future testCaptureImageInVideoModeResolution( CameraController controller, ResolutionPreset preset) async { - final Size expectedSize = presetExpectedSizes[preset]!; + final Size expectedSize = presetVideoExpectedSizes[preset]!; // Take Picture final XFile file = await controller.takePicture(); @@ -80,12 +91,57 @@ void main() { for (final CameraDescription cameraDescription in cameras) { bool previousPresetExactlySupported = true; for (final MapEntry preset - in presetExpectedSizes.entries) { + in presetVideoExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageInVideoModeResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }); + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureImageInPhotoModeResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetPhotoExpectedSizes[preset]!; + + // Take Picture + final XFile file = await controller.takePicture(); + + // Load picture + final File fileImage = File(file.path); + final Image image = await decodeImageFromList(fileImage.readAsBytesSync()); + + // Verify image dimensions are as expected + expect(image, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(image.height.toDouble(), image.width.toDouble())); + } + + testWidgets('Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetVideoExpectedSizes.entries) { final CameraController controller = CameraController(cameraDescription, preset.key); await controller.initialize(); + await controller.setCaptureMode(CaptureMode.photo); final bool presetExactlySupported = - await testCaptureImageResolution(controller, preset.key); + await testCaptureImageInPhotoModeResolution(controller, preset.key); assert(!(!previousPresetExactlySupported && presetExactlySupported), 'The camera took higher resolution pictures at a lower resolution.'); previousPresetExactlySupported = presetExactlySupported; @@ -99,7 +155,7 @@ void main() { // whether the image is exactly the desired resolution. Future testCaptureVideoResolution( CameraController controller, ResolutionPreset preset) async { - final Size expectedSize = presetExpectedSizes[preset]!; + final Size expectedSize = presetVideoExpectedSizes[preset]!; // Take Video await controller.startVideoRecording(); @@ -129,7 +185,7 @@ void main() { for (final CameraDescription cameraDescription in cameras) { bool previousPresetExactlySupported = true; for (final MapEntry preset - in presetExpectedSizes.entries) { + in presetVideoExpectedSizes.entries) { final CameraController controller = CameraController(cameraDescription, preset.key); await controller.initialize(); diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 6006b9f2b4ba..88edce27fec9 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + CD52577A2B19827700ED0301 /* CameraCaptureModeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CD5257792B19827700ED0301 /* CameraCaptureModeTests.m */; }; E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; @@ -89,6 +90,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + CD5257792B19827700ED0301 /* CameraCaptureModeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = CameraCaptureModeTests.m; path = RunnerTests/CameraCaptureModeTests.m; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; @@ -178,6 +180,7 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + CD5257792B19827700ED0301 /* CameraCaptureModeTests.m */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 03BB76692665316900CE5A93 /* RunnerTests */, @@ -417,6 +420,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CD52577A2B19827700ED0301 /* CameraCaptureModeTests.m in Sources */, 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, @@ -495,7 +499,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RunnerTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; @@ -521,7 +529,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RunnerTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -648,7 +660,10 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -670,7 +685,10 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m new file mode 100644 index 000000000000..72beb86a416a --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera_avfoundation; +@import camera_avfoundation.Test; +@import XCTest; +@import AVFoundation; +#import +#import "CameraTestUtils.h" +#import "MockFLTThreadSafeFlutterResult.h" + +@interface CameraCaptureModeTests : XCTestCase +@property(readonly, nonatomic) FLTCam *camera; +@end + +@implementation CameraCaptureModeTests + +- (void)setUp { + dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); + dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, + (void *)FLTCaptureSessionQueueSpecific, NULL); + + _camera = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); + +} + +- (void) testCaptureMode_shouldBeVideoByDefault { + XCTAssertEqual(_camera.captureMode, FLTCaptureModeVideo); +} + +- (void) testCaptureMode_shouldBeVideoAfterSettingVideo { + [_camera setCaptureMode:FLTCaptureModePhoto]; + [_camera setCaptureMode:FLTCaptureModeVideo]; + XCTAssertEqual(_camera.captureMode, FLTCaptureModeVideo); +} + +- (void) testCaptureMode_shouldBePhotoAfterSettingPhoto { + [_camera setCaptureMode:FLTCaptureModePhoto]; + XCTAssertEqual(_camera.captureMode, FLTCaptureModePhoto); +} + +@end \ No newline at end of file diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m index c429d2d9751d..82b482467a85 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m @@ -104,4 +104,18 @@ - (void)testFLTGetStringForUIDeviceOrientation { XCTAssertEqualObjects(@"portraitUp", FLTGetStringForUIDeviceOrientation(-1)); } +#pragma mark - capture mode tests + +- (void)testFLTGetCaptureModeForString { + XCTAssertEqual(FLTCaptureModeVideo, FLTGetFLTCaptureModeForString(@"video")); + XCTAssertEqual(FLTCaptureModePhoto, FLTGetFLTCaptureModeForString(@"photo")); + XCTAssertEqual(FLTCaptureModeInvalid, FLTGetFLTCaptureModeForString(@"unknown")); +} + +- (void)testFLTGetStringForCaptureMode { + XCTAssertEqualObjects(@"video", FLTGetStringForFLTCaptureMode(FLTCaptureModeVideo)); + XCTAssertEqualObjects(@"photo", FLTGetStringForFLTCaptureMode(FLTCaptureModePhoto)); + XCTAssertNil(FLTGetStringForFLTCaptureMode(-1)); +} + @end diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 36b9ac68cfdd..6a8921c46022 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -25,6 +25,7 @@ class CameraValue { required this.focusMode, required this.deviceOrientation, required this.description, + required this.captureMode, this.lockedCaptureOrientation, this.recordingOrientation, this.isPreviewPaused = false, @@ -45,6 +46,7 @@ class CameraValue { deviceOrientation: DeviceOrientation.portraitUp, isPreviewPaused: false, description: description, + captureMode: CaptureMode.video, ); /// True after [CameraController.initialize] has completed successfully. @@ -97,6 +99,9 @@ class CameraValue { /// The properties of the camera device controlled by this controller. final CameraDescription description; + /// The capture mode of the camera device controlled by this controller. + final CaptureMode captureMode; + /// Creates a modified copy of the object. /// /// Explicitly specified fields get the specified value, all other fields get @@ -119,6 +124,7 @@ class CameraValue { bool? isPreviewPaused, CameraDescription? description, Optional? previewPauseOrientation, + CaptureMode? captureMode, }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, @@ -142,6 +148,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, + captureMode: captureMode ?? this.captureMode, ); } @@ -159,7 +166,8 @@ class CameraValue { 'lockedCaptureOrientation: $lockedCaptureOrientation, ' 'recordingOrientation: $recordingOrientation, ' 'isPreviewPaused: $isPreviewPaused, ' - 'previewPausedOrientation: $previewPauseOrientation)'; + 'previewPausedOrientation: $previewPauseOrientation, ' + 'captureMode: $captureMode)'; } } @@ -437,6 +445,11 @@ class CameraController extends ValueNotifier { value = value.copyWith(focusMode: mode); } + Future setCaptureMode(CaptureMode mode) async { + await CameraPlatform.instance.setCaptureMode(_cameraId, mode); + value = value.copyWith(captureMode: mode); + } + /// Releases the resources of this camera. @override Future dispose() async { diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 43cdaf1480ee..5f616d7b903b 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -962,24 +962,6 @@ - (void)setCaptureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSSt }]; } -// - (void)applyCaptureMode { -// [_captureDevice lockForConfiguration:nil]; -// switch (_captureMode) { -// case FLTCaptureModePhoto: -// [_captureDevice setCaptureMode:AVCaptureCaptureModePhoto]; -// break; -// case FLTCaptureModeVideo: -// [_captureDevice setCaptureMode:AVCaptureCaptureModeVideo]; -// break; -// case FLTCaptureModeInvalid: -// // This state is not intended to be reachable; it exists only for error handling during -// // message deserialization. -// NSAssert(false, @""); -// break; -// } -// [_captureDevice unlockForConfiguration]; -// } - - (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { FLTFocusMode mode = FLTGetFLTFocusModeForString(modeStr); if (mode == FLTFocusModeInvalid) { diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index 49634fb5dee2..a7a8f53ab793 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -1150,6 +1150,27 @@ void main() { isMethodCall('stopImageStream', arguments: null), ]); }); + + test('Should set the capture mode',() async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: { + 'setCaptureMode': null, + }, + ); + + // Act + await camera.setCaptureMode(cameraId, CaptureMode.photo); + + // Assert + expect(channel.log, [ + isMethodCall('setCaptureMode', arguments: { + 'cameraId': cameraId, + 'mode': CaptureMode.photo.name + }), + ]); + }); }); } From e67bca60252acc63602a21ecdd03ff2fae59208e Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 4 Dec 2023 11:12:46 +1030 Subject: [PATCH 26/27] Finalise iOS test and start Android tests --- .../fpsrange/FpsRangeFeatureTest.java | 32 +++++++++---- .../resolution/ResolutionFeatureTest.java | 46 +++++++++++++------ .../plugins/camera/types/CaptureModeTest.java | 35 ++++++++++++++ .../example/integration_test/camera_test.dart | 12 ++++- .../example/integration_test/camera_test.dart | 5 +- .../ios/RunnerTests/CameraCaptureModeTests.m | 7 ++- .../example/lib/camera_controller.dart | 1 + .../camera_avfoundation/ios/Classes/FLTCam.m | 41 ++++++++++------- .../ios/Classes/QueueUtils.h | 2 +- .../test/avfoundation_camera_test.dart | 4 +- 10 files changed, 134 insertions(+), 51 deletions(-) create mode 100644 packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/CaptureModeTest.java diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java index 4dc68af94ca3..0d21c2d1d596 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java @@ -16,6 +16,7 @@ import android.util.Range; import io.flutter.plugins.camera.CameraProperties; import io.flutter.plugins.camera.DeviceInfo; +import io.flutter.plugins.camera.types.CaptureMode; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -35,22 +36,30 @@ public void after() { @Test public void ctor_shouldInitializeFpsRangeWithHighestUpperValueFromRangeArray() { - FpsRangeFeature fpsRangeFeature = createTestInstance(); + FpsRangeFeature fpsRangeFeature = createTestInstance(CaptureMode.video); assertEquals(13, (int) fpsRangeFeature.getValue().getUpper()); } @Test public void getDebugName_shouldReturnTheNameOfTheFeature() { - FpsRangeFeature fpsRangeFeature = createTestInstance(); + FpsRangeFeature fpsRangeFeature = createTestInstance(CaptureMode.video); assertEquals("FpsRangeFeature", fpsRangeFeature.getDebugName()); } @Test public void getValue_shouldReturnHighestUpperRangeIfNotSet() { CameraProperties mockCameraProperties = mock(CameraProperties.class); - FpsRangeFeature fpsRangeFeature = createTestInstance(); + FpsRangeFeature fpsRangeFeature = createTestInstance(CaptureMode.video); - assertEquals(13, (int) fpsRangeFeature.getValue().getUpper()); + assertEquals(60, (int) fpsRangeFeature.getValue().getUpper()); + } + + @Test + public void getValue_shouldReturnNoHigherThan60fpsInPhotoMode() { + CameraProperties mockCameraProperties = mock(CameraProperties.class); + FpsRangeFeature fpsRangeFeature = createTestInstance(CaptureMode.photo); + + assertEquals(30, (int) fpsRangeFeature.getValue().getUpper()); } @Test @@ -68,7 +77,7 @@ public void getValue_shouldEchoTheSetValue() { @Test public void checkIsSupported_shouldReturnTrue() { - FpsRangeFeature fpsRangeFeature = createTestInstance(); + FpsRangeFeature fpsRangeFeature = createTestInstance(CaptureMode.video); assertTrue(fpsRangeFeature.checkIsSupported()); } @@ -76,34 +85,37 @@ public void checkIsSupported_shouldReturnTrue() { @SuppressWarnings("unchecked") public void updateBuilder_shouldSetAeTargetFpsRange() { CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class); - FpsRangeFeature fpsRangeFeature = createTestInstance(); + FpsRangeFeature fpsRangeFeature = createTestInstance(CaptureMode.video); fpsRangeFeature.updateBuilder(mockBuilder); verify(mockBuilder).set(eq(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE), any(Range.class)); } - private static FpsRangeFeature createTestInstance() { + private static FpsRangeFeature createTestInstance(CaptureMode captureMode) { @SuppressWarnings("unchecked") Range rangeOne = mock(Range.class); @SuppressWarnings("unchecked") Range rangeTwo = mock(Range.class); @SuppressWarnings("unchecked") Range rangeThree = mock(Range.class); - + @SuppressWarnings("unchecked") + Range rangeFour = mock(Range.class); when(rangeOne.getUpper()).thenReturn(11); when(rangeTwo.getUpper()).thenReturn(12); when(rangeThree.getUpper()).thenReturn(13); + when(rangeFour.getUpper()).thenReturn(60); // Use a wildcard, since `new Range[] {rangeOne, rangeTwo, rangeThree}` // results in a 'Generic array creation' error. @SuppressWarnings("unchecked") - Range[] ranges = (Range[]) new Range[] {rangeOne, rangeTwo, rangeThree}; + Range[] ranges = + (Range[]) new Range[] {rangeOne, rangeTwo, rangeThree, rangeFour}; CameraProperties cameraProperties = mock(CameraProperties.class); when(cameraProperties.getControlAutoExposureAvailableTargetFpsRanges()).thenReturn(ranges); - return new FpsRangeFeature(cameraProperties); + return new FpsRangeFeature(cameraProperties, captureMode); } } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java index fe4dcd795fed..6ce4c2cc60bc 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java @@ -18,6 +18,7 @@ import android.media.EncoderProfiles; import android.util.Size; import io.flutter.plugins.camera.CameraProperties; +import io.flutter.plugins.camera.types.CaptureMode; import java.util.ArrayList; import java.util.List; import org.junit.After; @@ -231,7 +232,8 @@ public void getBestAvailableCamcorderProfileForResolutionPreset_shouldFallThroug @SuppressWarnings("deprecation") @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMaxLegacy() { - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.max); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.max, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P)); } @@ -240,7 +242,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMaxLegacy() @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMax() { before(); - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.max); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.max, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P)); } @@ -249,7 +252,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMax() { @SuppressWarnings("deprecation") @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHighLegacy() { - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.ultraHigh); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.ultraHigh, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P)); } @@ -258,7 +262,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHighLeg @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHigh() { before(); - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.ultraHigh); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.ultraHigh, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P)); } @@ -267,7 +272,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHigh() @SuppressWarnings("deprecation") @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHighLegacy() { - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.veryHigh); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.veryHigh, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P)); } @@ -277,7 +283,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHighLega @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHigh() { before(); - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.veryHigh); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.veryHigh, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P)); } @@ -286,7 +293,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHigh() { @SuppressWarnings("deprecation") @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHighLegacy() { - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.high); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.high, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P)); } @@ -295,7 +303,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHighLegacy() @Test public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHigh() { before(); - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.high); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.high, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P)); } @@ -304,7 +313,8 @@ public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHigh() { @SuppressWarnings("deprecation") @Test public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMediumLegacy() { - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.medium); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.medium, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_480P)); } @@ -313,7 +323,8 @@ public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMediumLegacy @Test public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMedium() { before(); - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.medium); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.medium, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_480P)); } @@ -322,7 +333,8 @@ public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMedium() { @SuppressWarnings("deprecation") @Test public void computeBestPreviewSize_shouldUseQVGAWhenResolutionPresetLowLegacy() { - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.low); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.low, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_QVGA)); } @@ -331,7 +343,8 @@ public void computeBestPreviewSize_shouldUseQVGAWhenResolutionPresetLowLegacy() @Test public void computeBestPreviewSize_shouldUseQVGAWhenResolutionPresetLow() { before(); - ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.low); + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.low, CaptureMode.video, new Size[0]); mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_QVGA)); } @@ -373,10 +386,15 @@ public void computeBestPreviewSize_shouldUseLegacyBehaviorWhenEncoderProfilesNul return mockCamcorderProfile; }); mockedResolutionFeature - .when(() -> ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.max)) + .when( + () -> + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.max, CaptureMode.video, new Size[0])) .thenCallRealMethod(); - Size testPreviewSize = ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.max); + Size testPreviewSize = + ResolutionFeature.computeBestPreviewSize( + 1, ResolutionPreset.max, CaptureMode.video, new Size[0]); assertEquals(testPreviewSize.getWidth(), 10); assertEquals(testPreviewSize.getHeight(), 50); } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/CaptureModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/CaptureModeTest.java new file mode 100644 index 000000000000..ee1d5a3bf28a --- /dev/null +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/CaptureModeTest.java @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camera.types; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class CaptureModeTest { + @Test + public void getValueForString_returnsCorrectValues() { + assertEquals( + "Returns CaptureMode.image for 'photo'", + CaptureMode.getValueForString("photo"), + CaptureMode.photo); + assertEquals( + "Returns CaptureMode.video for 'video'", + CaptureMode.getValueForString("video"), + CaptureMode.video); + } + + @Test + public void getValueForString_returnsNullForNonexistantValue() { + assertEquals( + "Returns null for 'nonexistant'", CaptureMode.getValueForString("nonexistant"), null); + } + + @Test + public void toString_returnsCorrectValue() { + assertEquals("Returns 'image' for CaptureMode.photo", CaptureMode.photo.toString(), "photo"); + assertEquals("Returns 'video' for CaptureMode.video", CaptureMode.video.toString(), "video"); + } +} diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 8d663074df72..9705a4e00405 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -40,6 +40,16 @@ void main() { // Don't bother checking for max here since it could be anything. }; + final Map presetImageExpectedSizes = + { + ResolutionPreset.low: const Size(240, 320), + ResolutionPreset.medium: const Size(480, 720), + ResolutionPreset.high: const Size(768, 1024), + ResolutionPreset.veryHigh: const Size(1080, 1440), + ResolutionPreset.ultraHigh: const Size(2160, 2880), + // Don't bother checking for max here since it could be anything. + }; + /// Verify that [actual] has dimensions that are at least as large as /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns /// whether the dimensions exactly match. @@ -81,7 +91,7 @@ void main() { for (final CameraDescription cameraDescription in cameras) { bool previousPresetExactlySupported = true; for (final MapEntry preset - in presetExpectedSizes.entries) { + in presetImageExpectedSizes.entries) { final CameraController controller = CameraController(cameraDescription, preset.key); await controller.initialize(); diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 623297fbd318..a3422ab17106 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -50,7 +50,6 @@ void main() { // Don't bother checking for max here since it could be anything. }; - /// Verify that [actual] has dimensions that are at least as large as /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns /// whether the dimensions exactly match. @@ -105,7 +104,7 @@ void main() { } }); - // This tests that the capture is no bigger than the preset, since we have + // This tests that the capture is no bigger than the preset, since we have // automatic code to fall back to smaller sizes when we need to. Returns // whether the image is exactly the desired resolution. Future testCaptureImageInPhotoModeResolution( @@ -125,7 +124,7 @@ void main() { expectedSize, Size(image.height.toDouble(), image.width.toDouble())); } - testWidgets('Capture specific image resolutions', + testWidgets('Capture specific image resolutions', (WidgetTester tester) async { final List cameras = await CameraPlatform.instance.availableCameras(); diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m index 72beb86a416a..e48841355f41 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureModeTests.m @@ -22,20 +22,19 @@ - (void)setUp { (void *)FLTCaptureSessionQueueSpecific, NULL); _camera = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); - } -- (void) testCaptureMode_shouldBeVideoByDefault { +- (void)testCaptureMode_shouldBeVideoByDefault { XCTAssertEqual(_camera.captureMode, FLTCaptureModeVideo); } -- (void) testCaptureMode_shouldBeVideoAfterSettingVideo { +- (void)testCaptureMode_shouldBeVideoAfterSettingVideo { [_camera setCaptureMode:FLTCaptureModePhoto]; [_camera setCaptureMode:FLTCaptureModeVideo]; XCTAssertEqual(_camera.captureMode, FLTCaptureModeVideo); } -- (void) testCaptureMode_shouldBePhotoAfterSettingPhoto { +- (void)testCaptureMode_shouldBePhotoAfterSettingPhoto { [_camera setCaptureMode:FLTCaptureModePhoto]; XCTAssertEqual(_camera.captureMode, FLTCaptureModePhoto); } diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 6a8921c46022..13d7c5cb46e8 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -445,6 +445,7 @@ class CameraController extends ValueNotifier { value = value.copyWith(focusMode: mode); } + /// Sets the capture mode for the camera. Future setCaptureMode(CaptureMode mode) async { await CameraPlatform.instance.setCaptureMode(_cameraId, mode); value = value.copyWith(captureMode: mode); diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 5f616d7b903b..07060e6449dd 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -169,7 +169,9 @@ - (instancetype)initWithCameraName:(NSString *)cameraName _motionManager = [[CMMotionManager alloc] init]; [_motionManager startAccelerometerUpdates]; - if (![self setCaptureSessionWithResolutionPreset:_resolutionPreset withCaptureMode:_captureMode withError:error]) { + if (![self setCaptureSessionWithResolutionPreset:_resolutionPreset + withCaptureMode:_captureMode + withError:error]) { return nil; } [self updateOrientation]; @@ -346,8 +348,8 @@ - (NSString *)getTemporaryFilePathWithExtension:(NSString *)extension return file; } - -- (BOOL)setCaptureSessionForVideo:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { +- (BOOL)setCaptureSessionForVideo:(FLTResolutionPreset)resolutionPreset + withError:(NSError **)error { switch (resolutionPreset) { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: @@ -359,8 +361,8 @@ - (BOOL)setCaptureSessionForVideo:(FLTResolutionPreset)resolutionPreset withErro if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) { _videoCaptureSession.sessionPreset = AVCaptureSessionPresetHigh; _previewSize = - CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, - _captureDevice.activeFormat.highResolutionStillImageDimensions.height); + CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, + _captureDevice.activeFormat.highResolutionStillImageDimensions.height); break; } case FLTResolutionPresetVeryHigh: @@ -404,7 +406,8 @@ - (BOOL)setCaptureSessionForVideo:(FLTResolutionPreset)resolutionPreset withErro return YES; } -- (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error { +- (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset + withError:(NSError **)error { switch (resolutionPreset) { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: @@ -416,7 +419,8 @@ - (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset withErro // Selects the appropriate 1080p resolution to match the desired aspect ratio for photos. if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetInputPriority]) { for (AVCaptureDeviceFormat *format in _captureDevice.formats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + CMVideoDimensions dimensions = + CMVideoFormatDescriptionGetDimensions(format.formatDescription); if (dimensions.height == 1080) { if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { if ([_captureDevice lockForConfiguration:nil]) { @@ -429,10 +433,12 @@ - (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset withErro } } case FLTResolutionPresetHigh: - // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio for photos. + // Selects the appropriate 720p or 768p resolution to match the desired aspect ratio for + // photos. if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetInputPriority]) { for (AVCaptureDeviceFormat *format in _captureDevice.formats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + CMVideoDimensions dimensions = + CMVideoFormatDescriptionGetDimensions(format.formatDescription); if (dimensions.height == 720 || dimensions.height == 768) { if (fabs((double)dimensions.width / dimensions.height - (double)4 / 3) < 0.01) { if ([_captureDevice lockForConfiguration:nil]) { @@ -444,8 +450,8 @@ - (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset withErro } } } - // Most device format resolutions lower than 720/768p are generally 4:3 resolutions. Meaning the aspect ratio - // is the same regardless of the preset chosen. + // Most device format resolutions lower than 720/768p are generally 4:3 resolutions. Meaning + // the aspect ratio is the same regardless of the preset chosen. case FLTResolutionPresetMedium: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) { _videoCaptureSession.sessionPreset = AVCaptureSessionPreset640x480; @@ -473,9 +479,9 @@ - (BOOL)setCaptureSessionForPhoto:(FLTResolutionPreset)resolutionPreset withErro } - (BOOL)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPreset - withCaptureMode: (FLTCaptureMode) captureMode - withError:(NSError **)error { - // NSArray *formats = _captureDevice.formats; + withCaptureMode:(FLTCaptureMode)captureMode + withError:(NSError **)error { + // NSArray *formats = _captureDevice.formats; if (captureMode == FLTCaptureModeVideo) { if (![self setCaptureSessionForVideo:resolutionPreset withError:error]) { return NO; @@ -485,7 +491,8 @@ - (BOOL)setCaptureSessionWithResolutionPreset:(FLTResolutionPreset)resolutionPre return NO; } } - CMVideoDimensions previewDimensions = CMVideoFormatDescriptionGetDimensions(_captureDevice.activeFormat.formatDescription); + CMVideoDimensions previewDimensions = + CMVideoFormatDescriptionGetDimensions(_captureDevice.activeFormat.formatDescription); _previewSize = CGSizeMake(previewDimensions.width, previewDimensions.height); _audioCaptureSession.sessionPreset = _videoCaptureSession.sessionPreset; return YES; @@ -955,7 +962,9 @@ - (void)setCaptureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSSt } NSError *error; _captureMode = mode; - [self setCaptureSessionWithResolutionPreset:_resolutionPreset withCaptureMode:_captureMode withError: &error]; + [self setCaptureSessionWithResolutionPreset:_resolutionPreset + withCaptureMode:_captureMode + withError:&error]; [result sendSuccessWithData:@{ @"previewWidth" : @(_previewSize.width), @"previewHeight" : @(_previewSize.height), diff --git a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h index a7e22da716d0..e230a53508fa 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h +++ b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN /// Queue-specific context data to be associated with the capture session queue. -extern const char* FLTCaptureSessionQueueSpecific; +extern const char *FLTCaptureSessionQueueSpecific; /// Ensures the given block to be run on the main queue. /// If caller site is already on the main queue, the block will be run diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index a7a8f53ab793..83929954dea0 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -1150,8 +1150,8 @@ void main() { isMethodCall('stopImageStream', arguments: null), ]); }); - - test('Should set the capture mode',() async { + + test('Should set the capture mode', () async { // Arrange final MethodChannelMock channel = MethodChannelMock( channelName: _channelName, From 72a4cb22600f40104add6da41a881b9d4086b5dd Mon Sep 17 00:00:00 2001 From: Marco Savaglia Date: Mon, 4 Dec 2023 11:21:28 +1030 Subject: [PATCH 27/27] Revert change to QueueUtils.h --- packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h index e230a53508fa..a7e22da716d0 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h +++ b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN /// Queue-specific context data to be associated with the capture session queue. -extern const char *FLTCaptureSessionQueueSpecific; +extern const char* FLTCaptureSessionQueueSpecific; /// Ensures the given block to be run on the main queue. /// If caller site is already on the main queue, the block will be run