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 1 commit
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 functions to pause and resume the camera preview

## 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 @@ -125,6 +125,8 @@ class Camera
private MediaRecorder mediaRecorder;
/** True when recording video. */
private boolean recordingVideo;
/** True when the preview is paused. */
private boolean pausedPreview;

private File captureFile;

Expand Down Expand Up @@ -424,8 +426,10 @@ private void refreshPreviewCaptureSession(
}

try {
captureSession.setRepeatingRequest(
previewRequestBuilder.build(), cameraCaptureCallback, backgroundHandler);
if (!pausedPreview) {
captureSession.setRepeatingRequest(
previewRequestBuilder.build(), cameraCaptureCallback, backgroundHandler);
}

if (onSuccessCallback != null) {
onSuccessCallback.run();
Expand Down Expand Up @@ -830,33 +834,36 @@ public void setFocusMode(final Result result, @NonNull FocusMode newMode) {
* For focus mode an extra step of actually locking/unlocking the
* focus has to be done, in order to ensure it goes into the correct state.
*/
switch (newMode) {
case locked:
// Perform a single focus trigger.
lockAutoFocus();
if (captureSession == null) {
Log.i(TAG, "[unlockAutoFocus] captureSession null, returning");
return;
}

// Set AF state to idle again.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);

try {
captureSession.setRepeatingRequest(
previewRequestBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
if (result != null) {
result.error("setFocusModeFailed", "Error setting focus mode: " + e.getMessage(), null);
if (!pausedPreview) {
switch (newMode) {
case locked:
// Perform a single focus trigger.
if (captureSession == null) {
Log.i(TAG, "[unlockAutoFocus] captureSession null, returning");
return;
}
lockAutoFocus();

// Set AF state to idle again.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);

try {
captureSession.setRepeatingRequest(
previewRequestBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
if (result != null) {
result.error(
"setFocusModeFailed", "Error setting focus mode: " + e.getMessage(), null);
}
return;
}
return;
}
break;
case auto:
// Cancel current AF trigger and set AF to idle again.
unlockAutoFocus();
break;
break;
case auto:
// Cancel current AF trigger and set AF to idle again.
unlockAutoFocus();
break;
}
}

if (result != null) {
Expand Down Expand Up @@ -962,6 +969,19 @@ public void unlockCaptureOrientation() {
cameraFeatures.getSensorOrientation().unlockCaptureOrientation();
}

/** Pause the preview from dart. */
public void pausePreview() throws CameraAccessException {
this.pausedPreview = true;
this.captureSession.stopRepeating();
}

/** Resume the preview from dart. */
public void resumePreview() throws CameraAccessException {
this.pausedPreview = false;
this.refreshPreviewCaptureSession(
null, (code, message) -> dartMessenger.sendCameraErrorEvent(message));
}

public void startPreview() throws CameraAccessException {
if (pictureImageReader == null || pictureImageReader.getSurface() == null) return;
Log.i(TAG, "startPreview");
Expand Down Expand Up @@ -1018,8 +1038,8 @@ public void onError(String errorCode, String errorMessage) {
private void setImageStreamImageAvailableListener(final EventChannel.EventSink imageStreamSink) {
imageStreamReader.setOnImageAvailableListener(
reader -> {
// Use acquireNextImage since image reader is only for one image.
Image img = reader.acquireNextImage();
// Use acquireNextImage since image reader is only for one image.
if (img == null) return;

List<Map<String, Object>> planes = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,26 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
}
break;
}
case "pausePreview":
{
try {
camera.pausePreview();
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "resumePreview":
{
try {
camera.resumePreview();
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "dispose":
{
if (camera != null) {
Expand Down
28 changes: 27 additions & 1 deletion packages/camera/camera/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
cameraController.value.isRecordingVideo
? onStopButtonPressed
: null,
)
),
IconButton(
icon: const Icon(Icons.pause_presentation),
color:
cameraController != null && cameraController.value.isPreviewPaused
? Colors.red
: Colors.blue,
onPressed:
cameraController == null ? null : onPausePreviewButtonPressed,
),
],
);
}
Expand Down Expand Up @@ -747,6 +756,23 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
});
}

Future<void> onPausePreviewButtonPressed() async {
final CameraController? cameraController = controller;

if (cameraController == null || !cameraController.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
return;
}

if (cameraController.value.isPreviewPaused) {
await cameraController.resumePreview();
} else {
await cameraController.pausePreview();
}

if (mounted) setState(() {});
}

void onPauseButtonPressed() {
pauseVideoRecording().then((_) {
if (mounted) setState(() {});
Expand Down
19 changes: 18 additions & 1 deletion packages/camera/camera/ios/Classes/CameraPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ @interface FLTCam : NSObject <FlutterTexture,
@property(assign, nonatomic) BOOL audioIsDisconnected;
@property(assign, nonatomic) BOOL isAudioSetup;
@property(assign, nonatomic) BOOL isStreamingImages;
@property(assign, nonatomic) BOOL isPreviewPaused;
@property(assign, nonatomic) ResolutionPreset resolutionPreset;
@property(assign, nonatomic) ExposureMode exposureMode;
@property(assign, nonatomic) FocusMode focusMode;
Expand Down Expand Up @@ -1030,6 +1031,16 @@ - (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureD
[captureDevice unlockForConfiguration];
}

- (void)pausePreviewWithResult:(FlutterResult)result {
_isPreviewPaused = true;
result(nil);
}

- (void)resumePreviewWithResult:(FlutterResult)result {
_isPreviewPaused = false;
result(nil);
}

- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
x:(double)x
y:(double)y {
Expand Down Expand Up @@ -1427,7 +1438,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re

__weak CameraPlugin *weakSelf = self;
_camera.onFrameAvailable = ^{
[weakSelf.registry textureFrameAvailable:cameraId];
if (![weakSelf.camera isPreviewPaused]) {
[weakSelf.registry textureFrameAvailable:cameraId];
}
};
FlutterMethodChannel *methodChannel = [FlutterMethodChannel
methodChannelWithName:[NSString stringWithFormat:@"flutter.io/cameraPlugin/camera%lu",
Expand Down Expand Up @@ -1514,6 +1527,10 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re
y = ((NSNumber *)call.arguments[@"y"]).doubleValue;
}
[_camera setFocusPointWithResult:result x:x y:y];
} else if ([@"pausePreview" isEqualToString:call.method]) {
[_camera pausePreviewWithResult:result];
} else if ([@"resumePreview" isEqualToString:call.method]) {
[_camera resumePreviewWithResult:result];
} else {
result(FlutterMethodNotImplemented);
}
Expand Down
48 changes: 47 additions & 1 deletion packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class CameraValue {
required this.deviceOrientation,
this.lockedCaptureOrientation,
this.recordingOrientation,
this.isPreviewPaused = false,
this.previewPauseOrientation,
}) : _isRecordingPaused = isRecordingPaused;

/// Creates a new camera controller state for an uninitialized controller.
Expand All @@ -63,6 +65,7 @@ class CameraValue {
focusMode: FocusMode.auto,
focusPointSupported: false,
deviceOrientation: DeviceOrientation.portraitUp,
isPreviewPaused: false,
);

/// True after [CameraController.initialize] has completed successfully.
Expand All @@ -79,6 +82,12 @@ class CameraValue {

final bool _isRecordingPaused;

/// True when the preview widget has been paused manually.
final bool isPreviewPaused;

/// Set to the orientation the preview was paused in, if it is currently paused.
final DeviceOrientation? previewPauseOrientation;

/// True when camera [isRecordingVideo] and recording is paused.
bool get isRecordingPaused => isRecordingVideo && _isRecordingPaused;

Expand Down Expand Up @@ -150,6 +159,8 @@ class CameraValue {
DeviceOrientation? deviceOrientation,
Optional<DeviceOrientation>? lockedCaptureOrientation,
Optional<DeviceOrientation>? recordingOrientation,
bool? isPreviewPaused,
Optional<DeviceOrientation>? previewPauseOrientation,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
Expand All @@ -172,6 +183,10 @@ class CameraValue {
recordingOrientation: recordingOrientation == null
? this.recordingOrientation
: recordingOrientation.orNull,
isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused,
previewPauseOrientation: previewPauseOrientation == null
? this.previewPauseOrientation
: previewPauseOrientation.orNull,
);
}

Expand All @@ -190,7 +205,9 @@ class CameraValue {
'focusPointSupported: $focusPointSupported, '
'deviceOrientation: $deviceOrientation, '
'lockedCaptureOrientation: $lockedCaptureOrientation, '
'recordingOrientation: $recordingOrientation)';
'recordingOrientation: $recordingOrientation, '
'isPreviewPaused: $isPreviewPaused, '
'previewPausedOrientation: $previewPauseOrientation)';
}
}

Expand Down Expand Up @@ -325,6 +342,35 @@ class CameraController extends ValueNotifier<CameraValue> {
await CameraPlatform.instance.prepareForVideoRecording();
}

/// Pauses the current camera preview
Future<void> pausePreview() async {
if (value.isPreviewPaused) {
return;
}
try {
await CameraPlatform.instance.pausePreview(_cameraId);
value = value.copyWith(
isPreviewPaused: true,
previewPauseOrientation: Optional.of(this.value.deviceOrientation));
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
}

/// Resumes the current camera preview
Future<void> resumePreview() async {
if (!value.isPreviewPaused) {
return;
}
try {
await CameraPlatform.instance.resumePreview(_cameraId);
value = value.copyWith(
isPreviewPaused: false, previewPauseOrientation: Optional.absent());
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
}

/// Captures an image and returns the file where it was saved.
///
/// Throws a [CameraException] if the capture fails.
Expand Down
3 changes: 2 additions & 1 deletion packages/camera/camera/lib/src/camera_preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ class CameraPreview extends StatelessWidget {
DeviceOrientation _getApplicableOrientation() {
return controller.value.isRecordingVideo
? controller.value.recordingOrientation!
: (controller.value.lockedCaptureOrientation ??
: (controller.value.previewPauseOrientation ??
controller.value.lockedCaptureOrientation ??
controller.value.deviceOrientation);
}
}
4 changes: 2 additions & 2 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 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 All @@ -20,7 +20,7 @@ flutter:
pluginClass: CameraPlugin

dependencies:
camera_platform_interface: ^2.0.0
camera_platform_interface: ^2.1.0
flutter:
sdk: flutter
pedantic: ^1.10.0
Expand Down
6 changes: 6 additions & 0 deletions packages/camera/camera/test/camera_preview_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ class FakeController extends ValueNotifier<CameraValue>

@override
Future<void> unlockCaptureOrientation() async {}

@override
Future<void> pausePreview() async {}

@override
Future<void> resumePreview() async {}
}

void main() {
Expand Down
Loading