From 67f25b5da1ef86f76c2778f72f3dda575651a407 Mon Sep 17 00:00:00 2001 From: David Martos Date: Wed, 19 Mar 2025 15:37:20 +0100 Subject: [PATCH 1/7] Set camera feature range when recording --- .../io/flutter/plugins/camera/Camera.java | 56 +++++++++++-------- .../flutter/plugins/camera/CameraPlugin.java | 21 ------- 2 files changed, 32 insertions(+), 45 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 c6eeb65545b..d57057d00c4 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 @@ -238,30 +238,6 @@ public Camera( dartMessenger, videoCaptureSettings.resolutionPreset); - Integer recordingFps = null; - - if (videoCaptureSettings.fps != null && videoCaptureSettings.fps.intValue() > 0) { - recordingFps = videoCaptureSettings.fps; - } else { - - if (SdkCapabilityChecker.supportsEncoderProfiles()) { - EncoderProfiles encoderProfiles = getRecordingProfile(); - if (encoderProfiles != null && encoderProfiles.getVideoProfiles().size() > 0) { - recordingFps = encoderProfiles.getVideoProfiles().get(0).getFrameRate(); - } - } else { - CamcorderProfile camcorderProfile = getRecordingProfileLegacy(); - recordingFps = null != camcorderProfile ? camcorderProfile.videoFrameRate : null; - } - } - - if (recordingFps != null && recordingFps.intValue() > 0) { - - final FpsRangeFeature fpsRange = new FpsRangeFeature(cameraProperties); - fpsRange.setValue(new Range(recordingFps, recordingFps)); - this.cameraFeatures.setFpsRange(fpsRange); - } - // Create capture callback. captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000); captureProps = new CameraCaptureProperties(); @@ -339,6 +315,32 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { .build(); } + private void setFpsCameraFeature(CameraProperties cameraProperties) { + Integer recordingFps = null; + + if (videoCaptureSettings.fps != null && videoCaptureSettings.fps.intValue() > 0) { + recordingFps = videoCaptureSettings.fps; + } else { + + if (SdkCapabilityChecker.supportsEncoderProfiles()) { + EncoderProfiles encoderProfiles = getRecordingProfile(); + if (encoderProfiles != null && encoderProfiles.getVideoProfiles().size() > 0) { + recordingFps = encoderProfiles.getVideoProfiles().get(0).getFrameRate(); + } + } else { + CamcorderProfile camcorderProfile = getRecordingProfileLegacy(); + recordingFps = null != camcorderProfile ? camcorderProfile.videoFrameRate : null; + } + } + + if (recordingFps != null && recordingFps.intValue() > 0) { + + final FpsRangeFeature fpsRange = new FpsRangeFeature(cameraProperties); + fpsRange.setValue(new Range(recordingFps, recordingFps)); + this.cameraFeatures.setFpsRange(fpsRange); + } + } + @SuppressLint("MissingPermission") public void open(String imageFormatGroup) throws CameraAccessException { this.imageFormatGroup = imageFormatGroup; @@ -859,6 +861,9 @@ public void stopVideoRecording(@NonNull final Result result) { // Re-create autofocus feature so it's using continuous capture focus mode now. cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, false)); + // Reset to non recording fps range (the default) + cameraFeatures.setFpsRange(cameraFeatureFactory.createFpsRangeFeature(cameraProperties)); + recordingVideo = false; try { closeRenderer(); @@ -1263,6 +1268,8 @@ void prepareRecording(@NonNull Result result) { // Re-create autofocus feature so it's using video focus mode now. cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); + // Update camera features with the desired fps range + setFpsCameraFeature(cameraProperties); } private void setStreamHandler(EventChannel imageStreamChannel) { @@ -1388,6 +1395,7 @@ public void setDescriptionWhileRecording( videoCaptureSettings.resolutionPreset); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); + setFpsCameraFeature(cameraProperties); try { open(imageFormatGroup); } catch (CameraAccessException e) { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java index 308d4283aa2..aff225e2c35 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java @@ -19,9 +19,6 @@ * *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. * See {@code io.flutter.plugins.camera.MainActivity} for an example. - * - *

Call {@link #registerWith(io.flutter.plugin.common.PluginRegistry.Registrar)} to register an - * implementation of this that uses the stable {@code io.flutter.plugin.common} package. */ public final class CameraPlugin implements FlutterPlugin, ActivityAware { @@ -36,24 +33,6 @@ public final class CameraPlugin implements FlutterPlugin, ActivityAware { */ public CameraPlugin() {} - /** - * Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common} - * package. - * - *

Calling this automatically initializes the plugin. However plugins initialized this way - * won't react to changes in activity or context, unlike {@link CameraPlugin}. - */ - @SuppressWarnings("deprecation") - public static void registerWith( - @NonNull io.flutter.plugin.common.PluginRegistry.Registrar registrar) { - CameraPlugin plugin = new CameraPlugin(); - plugin.maybeStartListening( - registrar.activity(), - registrar.messenger(), - registrar::addRequestPermissionsResultListener, - registrar.view()); - } - @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { this.flutterPluginBinding = binding; From 90863818c660f55ca4d7acbb2f2a843ebf4d4e13 Mon Sep 17 00:00:00 2001 From: David Martos Date: Wed, 19 Mar 2025 16:21:49 +0100 Subject: [PATCH 2/7] update tests --- .../plugins/camera/CameraTest_getRecordingProfileTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java index c5a97a19aca..c67b0f3346c 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java @@ -86,10 +86,7 @@ public void getRecordingProfileLegacy() { CamcorderProfile actualRecordingProfile = camera.getRecordingProfileLegacy(); - // First time: getRecordingProfileLegacy() is called in `before()` when - // camera constructor tries to determine default recording Fps. - // Second time: in this test case. - verify(mockResolutionFeature, times(2)).getRecordingProfileLegacy(); + verify(mockResolutionFeature, times(1)).getRecordingProfileLegacy(); assertEquals(mockCamcorderProfile, actualRecordingProfile); } @@ -104,7 +101,7 @@ public void getRecordingProfile() { EncoderProfiles actualRecordingProfile = camera.getRecordingProfile(); - verify(mockResolutionFeature, times(2)).getRecordingProfile(); + verify(mockResolutionFeature, times(1)).getRecordingProfile(); assertEquals(mockRecordingProfile, actualRecordingProfile); } From 0452839b5fa2b9d3fda0beede5f7e1f1e33bd15a Mon Sep 17 00:00:00 2001 From: David Martos Date: Thu, 20 Mar 2025 11:08:36 +0100 Subject: [PATCH 3/7] format --- .../android/src/main/java/io/flutter/plugins/camera/Camera.java | 2 +- 1 file changed, 1 insertion(+), 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 7078e74795a..f0bd031d008 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 @@ -321,7 +321,7 @@ private void setFpsCameraFeature(CameraProperties cameraProperties) { } if (recordingFps != null && recordingFps.intValue() > 0) { - + final FpsRangeFeature fpsRange = new FpsRangeFeature(cameraProperties); fpsRange.setValue(new Range(recordingFps, recordingFps)); this.cameraFeatures.setFpsRange(fpsRange); From da5281b7b99c4a979496c47d1ab37c02124b9f29 Mon Sep 17 00:00:00 2001 From: David Martos Date: Thu, 20 Mar 2025 11:09:17 +0100 Subject: [PATCH 4/7] rename method --- .../main/java/io/flutter/plugins/camera/Camera.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 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 f0bd031d008..c228856b46e 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 @@ -302,7 +302,12 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { .build(); } - private void setFpsCameraFeature(CameraProperties cameraProperties) { + /** + * Updates the FpsRange camera features with the appropriate FPS range. It sets + * the minimum and maximum fps range to the same value, as that's what is recommended + * for video recording. + */ + private void setFpsCameraFeatureForRecording(CameraProperties cameraProperties) { Integer recordingFps = null; if (videoCaptureSettings.fps != null && videoCaptureSettings.fps.intValue() > 0) { @@ -1258,7 +1263,7 @@ void prepareRecording() { cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); // Update camera features with the desired fps range - setFpsCameraFeature(cameraProperties); + setFpsCameraFeatureForRecording(cameraProperties); } private void setStreamHandler(EventChannel imageStreamChannel) { @@ -1382,7 +1387,7 @@ public void setDescriptionWhileRecording(CameraProperties properties) { videoCaptureSettings.resolutionPreset); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); - setFpsCameraFeature(cameraProperties); + setFpsCameraFeatureForRecording(cameraProperties); try { open(imageFormatGroup); } catch (CameraAccessException e) { From 983cda3e1665e7033ce8a224780367dae0435b72 Mon Sep 17 00:00:00 2001 From: David Martos Date: Thu, 20 Mar 2025 11:17:41 +0100 Subject: [PATCH 5/7] bump version --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 60b8e6cd0a4..139b9bc1e9d 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.10+2 + +* Don't set the FPS range unless video recording. It can cause dark image previews on some devices becuse the auto exposure algorithm is more constrained after fixing a min & max FPS value. + ## 0.10.10+1 * Updates compileSdk 34 to flutter.compileSdkVersion. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 33163076f68..f813858fd62 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.10+1 +version: 0.10.10+2 environment: sdk: ^3.6.0 From 79cb4e7979614198ac5f6d53f281f563cf1c0add Mon Sep 17 00:00:00 2001 From: David Martos Date: Thu, 20 Mar 2025 12:14:33 +0100 Subject: [PATCH 6/7] format --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 5 ++--- 1 file changed, 2 insertions(+), 3 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 c228856b46e..9b5aa049b44 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 @@ -303,9 +303,8 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { } /** - * Updates the FpsRange camera features with the appropriate FPS range. It sets - * the minimum and maximum fps range to the same value, as that's what is recommended - * for video recording. + * Updates the FpsRange camera features with the appropriate FPS range. It sets the minimum and + * maximum fps range to the same value, as that's what is recommended for video recording. */ private void setFpsCameraFeatureForRecording(CameraProperties cameraProperties) { Integer recordingFps = null; From 3fea37a83c40d49d3157315674993fd9b4d84700 Mon Sep 17 00:00:00 2001 From: David Martos Date: Mon, 24 Mar 2025 18:37:14 +0100 Subject: [PATCH 7/7] update changelog --- packages/camera/camera_android/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 139b9bc1e9d..e5e99ce8283 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.10.10+2 -* Don't set the FPS range unless video recording. It can cause dark image previews on some devices becuse the auto exposure algorithm is more constrained after fixing a min & max FPS value. +* Don't set the FPS range unless video recording. It can cause dark image previews on some devices becuse the auto exposure algorithm is more constrained after fixing the min/max FPS range to the same value. This change has the side effect that providing the `fps` parameter will not affect the camera preview when not video recording. And if you need a lower frame rate in your image streaming handler, you can skip frames by checking the time it passed since the last frame. ## 0.10.10+1