Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
14f4b51
Initial commit of camera changes
MarcoSavaglia Jun 19, 2023
ee2338c
Continuing work on aspect ratio and capture mode
MarcoSavaglia Jun 22, 2023
4ef8483
Finalising capture mode
MarcoSavaglia Jun 28, 2023
6fc2977
Clean up
MarcoSavaglia Jun 30, 2023
6585905
Refactor resolution selection
MarcoSavaglia Jun 30, 2023
f351124
Finalise the resolution selection
MarcoSavaglia Jul 4, 2023
61af8d3
Cleaning up work
MarcoSavaglia Jul 4, 2023
ae90b2e
Finalise clea up
MarcoSavaglia Jul 4, 2023
df8b338
Update dependencies
MarcoSavaglia Jul 5, 2023
8e36a18
Update camera_web plugin usage
MarcoSavaglia Jul 5, 2023
99b768f
Remove unnecessary preview size setting
MarcoSavaglia Jul 5, 2023
2784002
Update package dependencies
MarcoSavaglia Sep 27, 2023
41964b1
Fix Android compile issue
MarcoSavaglia Sep 27, 2023
ff40209
Further clean up
MarcoSavaglia Sep 27, 2023
5d7a27c
Make changes for contribution guide
MarcoSavaglia Sep 28, 2023
203ab04
Move capture mode out of the initialisation
MarcoSavaglia Nov 3, 2023
baf1054
Fix some tests
MarcoSavaglia Nov 3, 2023
076f40c
Re-add setCaptureMode
MarcoSavaglia Nov 3, 2023
b830aea
Fix analyze issues
MarcoSavaglia Nov 6, 2023
7e186da
Fix analyze issues in camerax
MarcoSavaglia Nov 6, 2023
c7e9d15
Fix analyzer info warnings
MarcoSavaglia Nov 6, 2023
e6d2630
Fix tests
MarcoSavaglia Nov 6, 2023
327f449
Address review comments
MarcoSavaglia Nov 24, 2023
7560e62
Fix failing analyze
MarcoSavaglia Nov 24, 2023
675b841
Start on iOS native/integration tests
MarcoSavaglia Dec 1, 2023
e67bca6
Finalise iOS test and start Android tests
MarcoSavaglia Dec 4, 2023
72a4cb2
Revert change to QueueUtils.h
MarcoSavaglia Dec 4, 2023
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.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.
Expand Down
5 changes: 5 additions & 0 deletions packages/camera/camera/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
1 change: 1 addition & 0 deletions packages/camera/camera/lib/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export 'package:camera_platform_interface/camera_platform_interface.dart'
CameraDescription,
CameraException,
CameraLensDirection,
CaptureMode,
FlashMode,
ExposureMode,
FocusMode,
Expand Down
37 changes: 36 additions & 1 deletion packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -72,6 +73,7 @@ class CameraValue {
deviceOrientation: DeviceOrientation.portraitUp,
isPreviewPaused: false,
description: description,
captureMode: CaptureMode.video,
);

/// True after [CameraController.initialize] has completed successfully.
Expand Down Expand Up @@ -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
Expand All @@ -171,6 +176,7 @@ class CameraValue {
bool? isPreviewPaused,
CameraDescription? description,
Optional<DeviceOrientation>? previewPauseOrientation,
CaptureMode? captureMode,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
Expand Down Expand Up @@ -198,6 +204,7 @@ class CameraValue {
previewPauseOrientation: previewPauseOrientation == null
? this.previewPauseOrientation
: previewPauseOrientation.orNull,
captureMode: captureMode ?? this.captureMode,
);
}

Expand All @@ -219,7 +226,8 @@ class CameraValue {
'recordingOrientation: $recordingOrientation, '
'isPreviewPaused: $isPreviewPaused, '
'previewPausedOrientation: $previewPauseOrientation, '
'description: $description)';
'description: $description, '
'captureMode: $captureMode)';
}
}

Expand Down Expand Up @@ -343,6 +351,8 @@ class CameraController extends ValueNotifier<CameraValue> {
.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
Expand Down Expand Up @@ -529,6 +539,12 @@ class CameraController extends ValueNotifier<CameraValue> {
'startVideoRecording was called when a recording is already started.',
);
}
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.',
);
}

Function(CameraImageData image)? streamCallback;
if (onAvailable != null) {
Expand Down Expand Up @@ -610,6 +626,13 @@ class CameraController extends ValueNotifier<CameraValue> {
'resumeVideoRecording was called when no video is recording.',
);
}
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.',
);
}

try {
await CameraPlatform.instance.resumeVideoRecording(_cameraId);
value = value.copyWith(isRecordingPaused: false);
Expand Down Expand Up @@ -682,6 +705,18 @@ class CameraController extends ValueNotifier<CameraValue> {
}
}

/// Sets the capture mode for the camera.
Future<void> 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
Expand Down
15 changes: 10 additions & 5 deletions 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 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"
Expand All @@ -21,10 +21,10 @@ 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: ^0.10.9
camera_avfoundation: ^0.9.14
camera_platform_interface: ^2.5.3
camera_web: ^0.3.2
flutter:
sdk: flutter
flutter_plugin_android_lifecycle: ^2.0.2
Expand All @@ -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}}
3 changes: 3 additions & 0 deletions packages/camera/camera/test/camera_preview_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ class FakeController extends ValueNotifier<CameraValue>
@override
Future<void> setDescription(CameraDescription description) async {}

@override
Future<void> setCaptureMode(CaptureMode captureMode) async {}

@override
CameraDescription get description => value.description;
}
Expand Down
54 changes: 54 additions & 0 deletions packages/camera/camera/test/camera_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ CameraInitializedEvent get mockOnCameraInitializedEvent =>
true,
FocusMode.auto,
true,
CaptureMode.video,
);

DeviceOrientationChangedEvent get mockOnDeviceOrientationChangedEvent =>
Expand Down Expand Up @@ -1401,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<Size?>.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<CameraException>().having(
(CameraException error) => error.description,
'TEST_ERROR',
'This is a test error message',
)));
});
});
}

Expand Down Expand Up @@ -1554,6 +1601,13 @@ class MockCameraPlatform extends Mock
Invocation.method(#setExposureOffset, <Object?>[cameraId, offset]),
returnValue: Future<double>.value(1.0),
) as Future<double>;

@override
Future<Size?> setCaptureMode(int? cameraId, CaptureMode mode) async =>
super.noSuchMethod(
Invocation.method(#setCaptureMode, <Object?>[cameraId, mode]),
returnValue: Future<Size?>.value(const Size(680, 480)),
) as Future<Size?>;
}

class MockCameraDescription extends CameraDescription {
Expand Down
4 changes: 3 additions & 1 deletion packages/camera/camera/test/camera_value_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void main() {
focusPointSupported: true,
previewPauseOrientation: DeviceOrientation.portraitUp,
description: FakeController.fakeDescription,
captureMode: CaptureMode.video,
);

expect(cameraValue, isA<CameraValue>());
Expand Down Expand Up @@ -144,10 +145,11 @@ void main() {
isPreviewPaused: true,
previewPauseOrientation: DeviceOrientation.portraitUp,
description: FakeController.fakeDescription,
captureMode: CaptureMode.video,
);

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)');
});
});
}
4 changes: 4 additions & 0 deletions packages/camera/camera_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
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.CaptureMode;
import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;
import io.flutter.view.TextureRegistry.SurfaceTextureEntry;
import java.io.File;
Expand All @@ -68,6 +69,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executors;

@FunctionalInterface
Expand Down Expand Up @@ -207,7 +209,12 @@ public Camera(
this.resolutionPreset = resolutionPreset;
this.cameraFeatures =
CameraFeatures.init(
cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset);
cameraFeatureFactory,
cameraProperties,
activity,
dartMessenger,
resolutionPreset,
CaptureMode.video);

// Create capture callback.
captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000);
Expand Down Expand Up @@ -326,7 +333,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) {
Expand Down Expand Up @@ -409,7 +417,6 @@ private void createCaptureSession(
resolutionFeature.getPreviewSize().getHeight());
Surface flutterSurface = new Surface(surfaceTexture);
previewRequestBuilder.addTarget(flutterSurface);

List<Surface> remainingSurfaces = Arrays.asList(surfaces);
if (templateType != CameraDevice.TEMPLATE_PREVIEW) {
// If it is not preview mode, add all surfaces as targets
Expand Down Expand Up @@ -502,7 +509,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");
Expand Down Expand Up @@ -972,6 +979,36 @@ 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<String, Integer> previewSize = new HashMap<String, Integer>();
previewSize.put("previewWidth", resolutionFeature.getPreviewSize().getWidth());
previewSize.put("previewHeight", resolutionFeature.getPreviewSize().getHeight());
result.success(previewSize);
}

/**
* Sets new focus point from dart.
*
Expand Down Expand Up @@ -1309,7 +1346,12 @@ public void setDescriptionWhileRecording(
cameraProperties = properties;
cameraFeatures =
CameraFeatures.init(
cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset);
cameraFeatureFactory,
cameraProperties,
activity,
dartMessenger,
resolutionPreset,
cameraFeatures.getResolution().getCaptureMode());
cameraFeatures.setAutoFocus(
cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true));
try {
Expand Down
Loading