Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[camera] Implement pausePreview and resumePreview #260

Merged
merged 2 commits into from Nov 25, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 9 additions & 2 deletions packages/camera/CHANGELOG.md
Expand Up @@ -4,8 +4,15 @@

## 0.2.0

* Apply new external texture APIs
* Apply new external texture APIs.

## 0.2.1

* Fix a freezing preview issue
* Fix a freezing preview issue.

## 0.3.0

* Update camera to 0.9.4.
* Implement `pausePreview` and `resumePreview`.
* Update the example app and integration_test.
* Remove the unused test driver.
14 changes: 7 additions & 7 deletions packages/camera/README.md
Expand Up @@ -4,13 +4,11 @@ The Tizen implementation of [`camera`](https://github.com/flutter/plugins/tree/m

## Supported devices

This plugin is an experimental plug-in for the future

- Nothing
This plugin is currently experimental and does not support any devices.

## Required privileges

To use this plugin, add below lines under the `<manifest>` section in your `tizen-manifest.xml` file,
To use this plugin, add below lines under the `<manifest>` section in your `tizen-manifest.xml` file.

```xml
<privileges>
Expand All @@ -25,19 +23,21 @@ This package is not an _endorsed_ implementation of `camera`. Therefore, you hav

```yaml
dependencies:
camera: ^0.8.1
camera_tizen: ^0.2.1
camera: ^0.9.4
camera_tizen: ^0.3.0
```

Then you can import `camera` in your Dart code:

```dart
import 'package:camera/camera.dart';
```

For detailed usage, see https://github.com/flutter/plugins/tree/master/packages/camera/camera#example.

## Notes
CameraPreview currently does not support other platforms except Android and iOS. Therefor the camera preview to orient properly, you have to modify the `camera_preview.dart`.

For the camera preview to rotate correctly, you have to modify the `camera_preview.dart` file as follows.

```dart
Widget _wrapInRotatedBox({required Widget child}) {
Expand Down
21 changes: 3 additions & 18 deletions packages/camera/example/integration_test/camera_test.dart
Expand Up @@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// TODO(mvanbeusekom): Remove this once flutter_driver supports null safety.
// https://github.com/flutter/flutter/issues/71379
// @dart = 2.9
import 'dart:async';
import 'dart:io';
import 'dart:ui';
Expand All @@ -17,7 +14,7 @@ import 'package:video_player/video_player.dart';
import 'package:integration_test/integration_test.dart';

void main() {
Directory testDir;
late Directory testDir;

IntegrationTestWidgetsFlutterBinding.ensureInitialized();

Expand All @@ -30,18 +27,6 @@ void main() {
await testDir.delete(recursive: true);
});

// final Map<ResolutionPreset, Size> presetExpectedSizes =
// <ResolutionPreset, Size>{
// ResolutionPreset.low:
// Platform.isAndroid ? const Size(240, 320) : const Size(288, 352),
// ResolutionPreset.medium:
// Platform.isAndroid ? const Size(480, 720) : const Size(480, 640),
// ResolutionPreset.high: const Size(720, 1280),
// ResolutionPreset.veryHigh: const Size(1080, 1920),
// ResolutionPreset.ultraHigh: const Size(2160, 3840),
// // Don't bother checking for max here since it could be anything.
// };

final Map<ResolutionPreset, Size> presetExpectedSizes =
<ResolutionPreset, Size>{
ResolutionPreset.medium: const Size(480, 720),
Expand All @@ -63,7 +48,7 @@ void main() {
// whether the image is exactly the desired resolution.
Future<bool> testCaptureImageResolution(
CameraController controller, ResolutionPreset preset) async {
final Size expectedSize = presetExpectedSizes[preset];
final Size expectedSize = presetExpectedSizes[preset]!;
print(
'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}');

Expand Down Expand Up @@ -108,7 +93,7 @@ void main() {
// whether the image is exactly the desired resolution.
Future<bool> testCaptureVideoResolution(
CameraController controller, ResolutionPreset preset) async {
final Size expectedSize = presetExpectedSizes[preset];
final Size expectedSize = presetExpectedSizes[preset]!;
print(
'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}');

Expand Down
134 changes: 101 additions & 33 deletions packages/camera/example/lib/main.dart
Expand Up @@ -8,6 +8,7 @@ import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

Expand Down Expand Up @@ -68,7 +69,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addObserver(this);
_ambiguate(WidgetsBinding.instance)?.addObserver(this);

_flashModeControlRowAnimationController = AnimationController(
duration: const Duration(milliseconds: 300),
Expand Down Expand Up @@ -98,7 +99,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>

@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
_ambiguate(WidgetsBinding.instance)?.removeObserver(this);
_flashModeControlRowAnimationController.dispose();
_exposureModeControlRowAnimationController.dispose();
super.dispose();
Expand Down Expand Up @@ -231,7 +232,14 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
? Container()
: SizedBox(
child: (localVideoController == null)
? Image.file(File(imageFile!.path))
? (
// The captured image on the web contains a network-accessible URL
// pointing to a location within the browser. It may be displayed
// either with Image.network or Image.memory after loading the image
// bytes to memory.
kIsWeb
? Image.network(imageFile!.path)
: Image.file(File(imageFile!.path)))
: Container(
child: Center(
child: AspectRatio(
Expand Down Expand Up @@ -267,17 +275,24 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
color: Colors.blue,
onPressed: controller != null ? onFlashModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.exposure),
color: Colors.blue,
onPressed:
controller != null ? onExposureModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed: controller != null ? onFocusModeButtonPressed : null,
),
// The exposure and focus mode are currently not supported on the web.
...(!kIsWeb
? [
IconButton(
icon: Icon(Icons.exposure),
color: Colors.blue,
onPressed: controller != null
? onExposureModeButtonPressed
: null,
),
IconButton(
icon: Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed:
controller != null ? onFocusModeButtonPressed : null,
)
]
: []),
IconButton(
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
color: Colors.blue,
Expand Down Expand Up @@ -399,6 +414,13 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
onSetExposureModeButtonPressed(ExposureMode.locked)
: null,
),
TextButton(
child: Text('RESET OFFSET'),
style: styleLocked,
onPressed: controller != null
? () => controller!.setExposureOffset(0.0)
: null,
),
],
),
Center(
Expand Down Expand Up @@ -530,7 +552,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 @@ -597,12 +628,14 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
if (controller != null) {
await controller!.dispose();
}

final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.medium,
kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium,
enableAudio: enableAudio,
imageFormatGroup: ImageFormatGroup.jpeg,
);

controller = cameraController;

// If the controller is updated then update the UI.
Expand All @@ -617,12 +650,17 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
try {
await cameraController.initialize();
await Future.wait([
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value),
// The exposure mode is currently not supported on the web.
...(!kIsWeb
? [
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value)
]
: []),
cameraController
.getMaxZoomLevel()
.then((value) => _maxAvailableZoom = value),
Expand Down Expand Up @@ -690,16 +728,20 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
}

void onCaptureOrientationLockButtonPressed() async {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
try {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
}
}
} on CameraException catch (e) {
_showCameraException(e);
}
}

Expand Down Expand Up @@ -741,6 +783,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 Expand Up @@ -881,8 +940,10 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
return;
}

final VideoPlayerController vController =
VideoPlayerController.file(File(videoFile!.path));
final VideoPlayerController vController = kIsWeb
? VideoPlayerController.network(videoFile!.path)
: VideoPlayerController.file(File(videoFile!.path));

videoPlayerListener = () {
if (videoController != null && videoController!.value.size != null) {
// Refreshing the state to update video player with the correct ratio.
Expand Down Expand Up @@ -951,3 +1012,10 @@ Future<void> main() async {
}
runApp(CameraApp());
}

/// This allows a value of type T or T? to be treated as a value of type T?.
///
/// We use this so that APIs that have become non-nullable can still be used
/// with `!` and `?` on the stable branch.
// TODO(ianh): Remove this once we roll stable in late 2021.
T? _ambiguate<T>(T? value) => value;
7 changes: 3 additions & 4 deletions packages/camera/example/pubspec.yaml
@@ -1,22 +1,21 @@
name: camera_example
description: Demonstrates how to use the camera plugin.
publish_to: "none"

dependencies:
camera: ^0.8.1
camera: ^0.9.4
camera_tizen:
path: ../
flutter:
sdk: flutter
path_provider: ^2.0.0
path_provider_tizen:
path: ../../path_provider
video_player: ^2.0.0
video_player: ^2.1.4
video_player_tizen:
path: ../../video_player

dev_dependencies:
flutter_driver:
sdk: flutter
flutter_test:
sdk: flutter
integration_test:
Expand Down