[camera] Expand CameraImage DTO with properties for lens aperture, exposure time and ISO. (#4256)
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index 68188d6..73cce2c 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.1
+
+* Added `lensAperture`, `sensorExposureTime` and `sensorSensitivity` properties to the `CameraImage` dto.
+
## 0.9.0
* Complete rewrite of Android plugin to fix many capture, focus, flash, orientation and exposure issues.
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
index 4724d22..43479ac 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
@@ -61,6 +61,7 @@
import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature;
import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature;
import io.flutter.plugins.camera.media.MediaRecorderBuilder;
+import io.flutter.plugins.camera.types.CameraCaptureProperties;
import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;
import io.flutter.view.TextureRegistry.SurfaceTextureEntry;
import java.io.File;
@@ -130,6 +131,8 @@
/** Holds the current capture timeouts */
private CaptureTimeoutsWrapper captureTimeouts;
+ /** Holds the last known capture properties */
+ private CameraCaptureProperties captureProps;
private MethodChannel.Result flutterResult;
@@ -158,7 +161,8 @@
// Create capture callback.
captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000);
- cameraCaptureCallback = CameraCaptureCallback.create(this, captureTimeouts);
+ captureProps = new CameraCaptureProperties();
+ cameraCaptureCallback = CameraCaptureCallback.create(this, captureTimeouts, captureProps);
startBackgroundThread();
}
@@ -1042,6 +1046,11 @@
imageBuffer.put("height", img.getHeight());
imageBuffer.put("format", img.getFormat());
imageBuffer.put("planes", planes);
+ imageBuffer.put("lensAperture", this.captureProps.getLastLensAperture());
+ imageBuffer.put("sensorExposureTime", this.captureProps.getLastSensorExposureTime());
+ Integer sensorSensitivity = this.captureProps.getLastSensorSensitivity();
+ imageBuffer.put(
+ "sensorSensitivity", sensorSensitivity == null ? null : (double) sensorSensitivity);
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> imageStreamSink.success(imageBuffer));
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java
index 21dcb60..805f182 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java
@@ -11,6 +11,7 @@
import android.hardware.camera2.TotalCaptureResult;
import android.util.Log;
import androidx.annotation.NonNull;
+import io.flutter.plugins.camera.types.CameraCaptureProperties;
import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;
/**
@@ -22,13 +23,16 @@
private final CameraCaptureStateListener cameraStateListener;
private CameraState cameraState;
private final CaptureTimeoutsWrapper captureTimeouts;
+ private final CameraCaptureProperties captureProps;
private CameraCaptureCallback(
@NonNull CameraCaptureStateListener cameraStateListener,
- @NonNull CaptureTimeoutsWrapper captureTimeouts) {
+ @NonNull CaptureTimeoutsWrapper captureTimeouts,
+ @NonNull CameraCaptureProperties captureProps) {
cameraState = CameraState.STATE_PREVIEW;
this.cameraStateListener = cameraStateListener;
this.captureTimeouts = captureTimeouts;
+ this.captureProps = captureProps;
}
/**
@@ -41,8 +45,9 @@
*/
public static CameraCaptureCallback create(
@NonNull CameraCaptureStateListener cameraStateListener,
- @NonNull CaptureTimeoutsWrapper captureTimeouts) {
- return new CameraCaptureCallback(cameraStateListener, captureTimeouts);
+ @NonNull CaptureTimeoutsWrapper captureTimeouts,
+ @NonNull CameraCaptureProperties captureProps) {
+ return new CameraCaptureCallback(cameraStateListener, captureTimeouts, captureProps);
}
/**
@@ -67,6 +72,16 @@
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
+ // Update capture properties
+ if (result instanceof TotalCaptureResult) {
+ Float lensAperture = result.get(CaptureResult.LENS_APERTURE);
+ Long sensorExposureTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+ Integer sensorSensitivity = result.get(CaptureResult.SENSOR_SENSITIVITY);
+ this.captureProps.setLastLensAperture(lensAperture);
+ this.captureProps.setLastSensorExposureTime(sensorExposureTime);
+ this.captureProps.setLastSensorSensitivity(sensorSensitivity);
+ }
+
if (cameraState != CameraState.STATE_PREVIEW) {
Log.d(
TAG,
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java
new file mode 100644
index 0000000..68177f4
--- /dev/null
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java
@@ -0,0 +1,67 @@
+// 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;
+
+public class CameraCaptureProperties {
+
+ private Float lastLensAperture;
+ private Long lastSensorExposureTime;
+ private Integer lastSensorSensitivity;
+
+ /**
+ * Gets the last known lens aperture. (As f-stop value)
+ *
+ * @return the last known lens aperture. (As f-stop value)
+ */
+ public Float getLastLensAperture() {
+ return lastLensAperture;
+ }
+
+ /**
+ * Sets the last known lens aperture. (As f-stop value)
+ *
+ * @param lastLensAperture - The last known lens aperture to set. (As f-stop value)
+ */
+ public void setLastLensAperture(Float lastLensAperture) {
+ this.lastLensAperture = lastLensAperture;
+ }
+
+ /**
+ * Gets the last known sensor exposure time in nanoseconds.
+ *
+ * @return the last known sensor exposure time in nanoseconds.
+ */
+ public Long getLastSensorExposureTime() {
+ return lastSensorExposureTime;
+ }
+
+ /**
+ * Sets the last known sensor exposure time in nanoseconds.
+ *
+ * @param lastSensorExposureTime - The last known sensor exposure time to set, in nanoseconds.
+ */
+ public void setLastSensorExposureTime(Long lastSensorExposureTime) {
+ this.lastSensorExposureTime = lastSensorExposureTime;
+ }
+
+ /**
+ * Gets the last known sensor sensitivity in ISO arithmetic units.
+ *
+ * @return the last known sensor sensitivity in ISO arithmetic units.
+ */
+ public Integer getLastSensorSensitivity() {
+ return lastSensorSensitivity;
+ }
+
+ /**
+ * Sets the last known sensor sensitivity in ISO arithmetic units.
+ *
+ * @param lastSensorSensitivity - The last known sensor sensitivity to set, in ISO arithmetic
+ * units.
+ */
+ public void setLastSensorSensitivity(Integer lastSensorSensitivity) {
+ this.lastSensorSensitivity = lastSensorSensitivity;
+ }
+}
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java
index 4964aef..934aff8 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java
@@ -17,6 +17,7 @@
import android.hardware.camera2.CaptureResult.Key;
import android.hardware.camera2.TotalCaptureResult;
import io.flutter.plugins.camera.CameraCaptureCallback.CameraCaptureStateListener;
+import io.flutter.plugins.camera.types.CameraCaptureProperties;
import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;
import io.flutter.plugins.camera.types.Timeout;
import io.flutter.plugins.camera.utils.TestUtils;
@@ -40,6 +41,7 @@
private CaptureRequest mockCaptureRequest;
private CaptureResult mockPartialCaptureResult;
private CaptureTimeoutsWrapper mockCaptureTimeouts;
+ private CameraCaptureProperties mockCaptureProps;
private TotalCaptureResult mockTotalCaptureResult;
private MockedStatic<Timeout> mockedStaticTimeout;
private Timeout mockTimeout;
@@ -83,6 +85,7 @@
mockTotalCaptureResult = mock(TotalCaptureResult.class);
mockTimeout = mock(Timeout.class);
mockCaptureTimeouts = mock(CaptureTimeoutsWrapper.class);
+ mockCaptureProps = mock(CameraCaptureProperties.class);
when(mockCaptureTimeouts.getPreCaptureFocusing()).thenReturn(mockTimeout);
when(mockCaptureTimeouts.getPreCaptureMetering()).thenReturn(mockTimeout);
@@ -95,7 +98,8 @@
mockedStaticTimeout.when(() -> Timeout.create(1000)).thenReturn(mockTimeout);
cameraCaptureCallback =
- CameraCaptureCallback.create(mockCaptureStateListener, mockCaptureTimeouts);
+ CameraCaptureCallback.create(
+ mockCaptureStateListener, mockCaptureTimeouts, mockCaptureProps);
}
@Override
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java
new file mode 100644
index 0000000..75a5b25
--- /dev/null
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java
@@ -0,0 +1,72 @@
+// 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;
+
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import io.flutter.plugins.camera.types.CameraCaptureProperties;
+import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class CameraCaptureCallbackTest {
+
+ private CameraCaptureCallback cameraCaptureCallback;
+ private CameraCaptureProperties mockCaptureProps;
+
+ @Before
+ public void setUp() {
+ CameraCaptureCallback.CameraCaptureStateListener mockCaptureStateListener =
+ mock(CameraCaptureCallback.CameraCaptureStateListener.class);
+ CaptureTimeoutsWrapper mockCaptureTimeouts = mock(CaptureTimeoutsWrapper.class);
+ mockCaptureProps = mock(CameraCaptureProperties.class);
+ cameraCaptureCallback =
+ CameraCaptureCallback.create(
+ mockCaptureStateListener, mockCaptureTimeouts, mockCaptureProps);
+ }
+
+ @Test
+ public void onCaptureProgressed_doesNotUpdateCameraCaptureProperties() {
+ CameraCaptureSession mockSession = mock(CameraCaptureSession.class);
+ CaptureRequest mockRequest = mock(CaptureRequest.class);
+ CaptureResult mockResult = mock(CaptureResult.class);
+
+ cameraCaptureCallback.onCaptureProgressed(mockSession, mockRequest, mockResult);
+
+ verify(mockCaptureProps, never()).setLastLensAperture(anyFloat());
+ verify(mockCaptureProps, never()).setLastSensorExposureTime(anyLong());
+ verify(mockCaptureProps, never()).setLastSensorSensitivity(anyInt());
+ }
+
+ @Test
+ public void onCaptureCompleted_updatesCameraCaptureProperties() {
+ CameraCaptureSession mockSession = mock(CameraCaptureSession.class);
+ CaptureRequest mockRequest = mock(CaptureRequest.class);
+ TotalCaptureResult mockResult = mock(TotalCaptureResult.class);
+ when(mockResult.get(CaptureResult.LENS_APERTURE)).thenReturn(1.0f);
+ when(mockResult.get(CaptureResult.SENSOR_EXPOSURE_TIME)).thenReturn(2L);
+ when(mockResult.get(CaptureResult.SENSOR_SENSITIVITY)).thenReturn(3);
+
+ cameraCaptureCallback.onCaptureCompleted(mockSession, mockRequest, mockResult);
+
+ verify(mockCaptureProps, times(1)).setLastLensAperture(1.0f);
+ verify(mockCaptureProps, times(1)).setLastSensorExposureTime(2L);
+ verify(mockCaptureProps, times(1)).setLastSensorSensitivity(3);
+ }
+}
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m
index d88eb45..ea03ce5 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera/ios/Classes/CameraPlugin.m
@@ -661,6 +661,11 @@
imageBuffer[@"height"] = [NSNumber numberWithUnsignedLong:imageHeight];
imageBuffer[@"format"] = @(videoFormat);
imageBuffer[@"planes"] = planes;
+ imageBuffer[@"lensAperture"] = [NSNumber numberWithFloat:[_captureDevice lensAperture]];
+ Float64 exposureDuration = CMTimeGetSeconds([_captureDevice exposureDuration]);
+ Float64 nsExposureDuration = 1000000000 * exposureDuration;
+ imageBuffer[@"sensorExposureTime"] = [NSNumber numberWithInt:nsExposureDuration];
+ imageBuffer[@"sensorSensitivity"] = [NSNumber numberWithFloat:[_captureDevice ISO]];
_imageStreamHandler.eventSink(imageBuffer);
diff --git a/packages/camera/camera/lib/src/camera_image.dart b/packages/camera/camera/lib/src/camera_image.dart
index 411c7e8..43fa763 100644
--- a/packages/camera/camera/lib/src/camera_image.dart
+++ b/packages/camera/camera/lib/src/camera_image.dart
@@ -100,6 +100,9 @@
: format = ImageFormat._fromPlatformData(data['format']),
height = data['height'],
width = data['width'],
+ lensAperture = data['lensAperture'],
+ sensorExposureTime = data['sensorExposureTime'],
+ sensorSensitivity = data['sensorSensitivity'],
planes = List<Plane>.unmodifiable(data['planes']
.map((dynamic planeData) => Plane._fromPlatformData(planeData)));
@@ -125,4 +128,15 @@
///
/// The number of planes is determined by the format of the image.
final List<Plane> planes;
+
+ /// The aperture settings for this image.
+ ///
+ /// Represented as an f-stop value.
+ final double? lensAperture;
+
+ /// The sensor exposure time for this image in nanoseconds.
+ final int? sensorExposureTime;
+
+ /// The sensor sensitivity in standard ISO arithmetic units.
+ final double? sensorSensitivity;
}
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index a7c6a61..08d1e3e 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
and streaming image buffers to dart.
repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.9.0
+version: 0.9.1
environment:
sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/camera/camera/test/camera_image_test.dart b/packages/camera/camera/test/camera_image_test.dart
index 2d827d9..85d613f 100644
--- a/packages/camera/camera/test/camera_image_test.dart
+++ b/packages/camera/camera/test/camera_image_test.dart
@@ -18,6 +18,9 @@
'format': 35,
'height': 1,
'width': 4,
+ 'lensAperture': 1.8,
+ 'sensorExposureTime': 9991324,
+ 'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
@@ -41,6 +44,9 @@
'format': 875704438,
'height': 1,
'width': 4,
+ 'lensAperture': 1.8,
+ 'sensorExposureTime': 9991324,
+ 'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
@@ -61,6 +67,9 @@
'format': 35,
'height': 1,
'width': 4,
+ 'lensAperture': 1.8,
+ 'sensorExposureTime': 9991324,
+ 'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
@@ -81,6 +90,9 @@
'format': 1111970369,
'height': 1,
'width': 4,
+ 'lensAperture': 1.8,
+ 'sensorExposureTime': 9991324,
+ 'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
@@ -98,6 +110,9 @@
'format': null,
'height': 1,
'width': 4,
+ 'lensAperture': 1.8,
+ 'sensorExposureTime': 9991324,
+ 'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),