Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -130,6 +131,8 @@ class Camera

/** Holds the current capture timeouts */
private CaptureTimeoutsWrapper captureTimeouts;
/** Holds the last known capture properties */
private CameraCaptureProperties captureProps;

private MethodChannel.Result flutterResult;

Expand Down Expand Up @@ -158,7 +161,8 @@ public Camera(

// Create capture callback.
captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000);
cameraCaptureCallback = CameraCaptureCallback.create(this, captureTimeouts);
captureProps = new CameraCaptureProperties();
cameraCaptureCallback = CameraCaptureCallback.create(this, captureTimeouts, captureProps);

startBackgroundThread();
}
Expand Down Expand Up @@ -1042,6 +1046,11 @@ private void setImageStreamImageAvailableListener(final EventChannel.EventSink i
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -22,13 +23,16 @@ class CameraCaptureCallback extends CaptureCallback {
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;
}

/**
Expand All @@ -41,8 +45,9 @@ private CameraCaptureCallback(
*/
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);
}

/**
Expand All @@ -67,6 +72,16 @@ private void process(CaptureResult result) {
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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,6 +41,7 @@ public class CameraCaptureCallbackStatesTest extends TestCase {
private CaptureRequest mockCaptureRequest;
private CaptureResult mockPartialCaptureResult;
private CaptureTimeoutsWrapper mockCaptureTimeouts;
private CameraCaptureProperties mockCaptureProps;
private TotalCaptureResult mockTotalCaptureResult;
private MockedStatic<Timeout> mockedStaticTimeout;
private Timeout mockTimeout;
Expand Down Expand Up @@ -83,6 +85,7 @@ protected void setUp() throws Exception {
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);

Expand All @@ -95,7 +98,8 @@ protected void setUp() throws Exception {
mockedStaticTimeout.when(() -> Timeout.create(1000)).thenReturn(mockTimeout);

cameraCaptureCallback =
CameraCaptureCallback.create(mockCaptureStateListener, mockCaptureTimeouts);
CameraCaptureCallback.create(
mockCaptureStateListener, mockCaptureTimeouts, mockCaptureProps);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
5 changes: 5 additions & 0 deletions packages/camera/camera/ios/Classes/CameraPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,11 @@ - (void)captureOutput:(AVCaptureOutput *)output
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);

Expand Down
14 changes: 14 additions & 0 deletions packages/camera/camera/lib/src/camera_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ class CameraImage {
: 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)));

Expand All @@ -125,4 +128,15 @@ class CameraImage {
///
/// 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;
}
2 changes: 1 addition & 1 deletion packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Flutter plugin for getting information about and controlling the
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"
Expand Down
15 changes: 15 additions & 0 deletions packages/camera/camera/test/camera_image_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ void main() {
'format': 35,
'height': 1,
'width': 4,
'lensAperture': 1.8,
'sensorExposureTime': 9991324,
'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
Expand All @@ -41,6 +44,9 @@ void main() {
'format': 875704438,
'height': 1,
'width': 4,
'lensAperture': 1.8,
'sensorExposureTime': 9991324,
'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
Expand All @@ -61,6 +67,9 @@ void main() {
'format': 35,
'height': 1,
'width': 4,
'lensAperture': 1.8,
'sensorExposureTime': 9991324,
'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
Expand All @@ -81,6 +90,9 @@ void main() {
'format': 1111970369,
'height': 1,
'width': 4,
'lensAperture': 1.8,
'sensorExposureTime': 9991324,
'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
Expand All @@ -98,6 +110,9 @@ void main() {
'format': null,
'height': 1,
'width': 4,
'lensAperture': 1.8,
'sensorExposureTime': 9991324,
'sensorSensitivity': 92.0,
'planes': [
{
'bytes': Uint8List.fromList([1, 2, 3, 4]),
Expand Down