[camera] Use startVideoCapturing and expose concurrent stream/record (#6815)
* Use startVideoCapturing and expose concurrent stream/record
This uses the new startVideoCapturing implementation, that supports concurrent stream/record.
* Ran dart formatter
* retrigger checks
* Account for version bump
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index 84c7559..d19a528 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.10.2
+
+* Implements option to also stream when recording a video.
+
## 0.10.1
* Remove usage of deprecated quiver Optional type.
diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart
index b05e61b..b201074 100644
--- a/packages/camera/camera/lib/src/camera_controller.dart
+++ b/packages/camera/camera/lib/src/camera_controller.dart
@@ -452,12 +452,6 @@
assert(defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS);
_throwIfNotInitialized('stopImageStream');
- if (value.isRecordingVideo) {
- throw CameraException(
- 'A video recording is already started.',
- 'stopImageStream was called while a video is being recorded.',
- );
- }
if (!value.isStreamingImages) {
throw CameraException(
'No camera is streaming images',
@@ -476,9 +470,13 @@
/// Start a video recording.
///
+ /// You may optionally pass an [onAvailable] callback to also have the
+ /// video frames streamed to this callback.
+ ///
/// The video is returned as a [XFile] after calling [stopVideoRecording].
/// Throws a [CameraException] if the capture fails.
- Future<void> startVideoRecording() async {
+ Future<void> startVideoRecording(
+ {onLatestImageAvailable? onAvailable}) async {
_throwIfNotInitialized('startVideoRecording');
if (value.isRecordingVideo) {
throw CameraException(
@@ -486,18 +484,21 @@
'startVideoRecording was called when a recording is already started.',
);
}
- if (value.isStreamingImages) {
- throw CameraException(
- 'A camera has started streaming images.',
- 'startVideoRecording was called while a camera was streaming images.',
- );
+
+ Function(CameraImageData image)? streamCallback;
+ if (onAvailable != null) {
+ streamCallback = (CameraImageData imageData) {
+ onAvailable(CameraImage.fromPlatformInterface(imageData));
+ };
}
try {
- await CameraPlatform.instance.startVideoRecording(_cameraId);
+ await CameraPlatform.instance.startVideoCapturing(
+ VideoCaptureOptions(_cameraId, streamCallback: streamCallback));
value = value.copyWith(
isRecordingVideo: true,
isRecordingPaused: false,
+ isStreamingImages: onAvailable != null,
recordingOrientation:
value.lockedCaptureOrientation ?? value.deviceOrientation);
} on PlatformException catch (e) {
@@ -516,6 +517,11 @@
'stopVideoRecording was called when no video is recording.',
);
}
+
+ if (value.isStreamingImages) {
+ stopImageStream();
+ }
+
try {
final XFile file =
await CameraPlatform.instance.stopVideoRecording(_cameraId);
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index 33d704e..f8b23bf 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
Dart.
repository: https://github.com/flutter/plugins/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.1
+version: 0.10.2
environment:
sdk: ">=2.14.0 <3.0.0"
@@ -21,10 +21,10 @@
default_package: camera_web
dependencies:
- camera_android: ^0.10.0
- camera_avfoundation: ^0.9.7+1
- camera_platform_interface: ^2.2.0
- camera_web: ^0.3.0
+ camera_android: ^0.10.1
+ camera_avfoundation: ^0.9.9
+ camera_platform_interface: ^2.3.2
+ camera_web: ^0.3.1
flutter:
sdk: flutter
flutter_plugin_android_lifecycle: ^2.0.2
diff --git a/packages/camera/camera/test/camera_image_stream_test.dart b/packages/camera/camera/test/camera_image_stream_test.dart
index a9320e4..29b5cce 100644
--- a/packages/camera/camera/test/camera_image_stream_test.dart
+++ b/packages/camera/camera/test/camera_image_stream_test.dart
@@ -130,28 +130,6 @@
);
});
- test('stopImageStream() throws $CameraException when recording videos',
- () async {
- final CameraController cameraController = CameraController(
- const CameraDescription(
- name: 'cam',
- lensDirection: CameraLensDirection.back,
- sensorOrientation: 90),
- ResolutionPreset.max);
- await cameraController.initialize();
-
- await cameraController.startImageStream((CameraImage image) => null);
- cameraController.value =
- cameraController.value.copyWith(isRecordingVideo: true);
- expect(
- cameraController.stopImageStream,
- throwsA(isA<CameraException>().having(
- (CameraException error) => error.description,
- 'A video recording is already started.',
- 'stopImageStream was called while a video is being recorded.',
- )));
- });
-
test('stopImageStream() throws $CameraException when not streaming images',
() async {
final CameraController cameraController = CameraController(
@@ -185,6 +163,39 @@
expect(mockPlatform.streamCallLog,
<String>['onStreamedFrameAvailable', 'listen', 'cancel']);
});
+
+ test('startVideoRecording() can stream images', () async {
+ final CameraController cameraController = CameraController(
+ const CameraDescription(
+ name: 'cam',
+ lensDirection: CameraLensDirection.back,
+ sensorOrientation: 90),
+ ResolutionPreset.max);
+
+ await cameraController.initialize();
+
+ cameraController.startVideoRecording(
+ onAvailable: (CameraImage image) => null);
+
+ expect(
+ mockPlatform.streamCallLog.contains('startVideoCapturing with stream'),
+ isTrue);
+ });
+
+ test('startVideoRecording() by default does not stream', () async {
+ final CameraController cameraController = CameraController(
+ const CameraDescription(
+ name: 'cam',
+ lensDirection: CameraLensDirection.back,
+ sensorOrientation: 90),
+ ResolutionPreset.max);
+
+ await cameraController.initialize();
+
+ cameraController.startVideoRecording();
+
+ expect(mockPlatform.streamCallLog.contains('startVideoCapturing'), isTrue);
+ });
}
class MockStreamingCameraPlatform extends MockCameraPlatform {
@@ -203,6 +214,24 @@
return _streamController!.stream;
}
+ @override
+ Future<XFile> startVideoRecording(int cameraId,
+ {Duration? maxVideoDuration}) {
+ streamCallLog.add('startVideoRecording');
+ return super
+ .startVideoRecording(cameraId, maxVideoDuration: maxVideoDuration);
+ }
+
+ @override
+ Future<void> startVideoCapturing(VideoCaptureOptions options) {
+ if (options.streamCallback == null) {
+ streamCallLog.add('startVideoCapturing');
+ } else {
+ streamCallLog.add('startVideoCapturing with stream');
+ }
+ return super.startVideoCapturing(options);
+ }
+
void _onFrameStreamListen() {
streamCallLog.add('listen');
}
diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart
index 546f4e9..7c43787 100644
--- a/packages/camera/camera/test/camera_preview_test.dart
+++ b/packages/camera/camera/test/camera_preview_test.dart
@@ -97,7 +97,8 @@
Future<void> startImageStream(onLatestImageAvailable onAvailable) async {}
@override
- Future<void> startVideoRecording() async {}
+ Future<void> startVideoRecording(
+ {onLatestImageAvailable? onAvailable}) async {}
@override
Future<void> stopImageStream() async {}
diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart
index 2138f2d..44a48d1 100644
--- a/packages/camera/camera/test/camera_test.dart
+++ b/packages/camera/camera/test/camera_test.dart
@@ -335,30 +335,6 @@
)));
});
- test(
- 'startVideoRecording() throws $CameraException when already streaming images',
- () async {
- final CameraController cameraController = CameraController(
- const CameraDescription(
- name: 'cam',
- lensDirection: CameraLensDirection.back,
- sensorOrientation: 90),
- ResolutionPreset.max);
-
- await cameraController.initialize();
-
- cameraController.value =
- cameraController.value.copyWith(isStreamingImages: true);
-
- expect(
- cameraController.startVideoRecording(),
- throwsA(isA<CameraException>().having(
- (CameraException error) => error.description,
- 'A camera has started streaming images.',
- 'startVideoRecording was called while a camera was streaming images.',
- )));
- });
-
test('getMaxZoomLevel() throws $CameraException when uninitialized',
() async {
final CameraController cameraController = CameraController(
@@ -1458,6 +1434,12 @@
Future<XFile>.value(mockVideoRecordingXFile);
@override
+ Future<void> startVideoCapturing(VideoCaptureOptions options) {
+ return startVideoRecording(options.cameraId,
+ maxVideoDuration: options.maxDuration);
+ }
+
+ @override
Future<void> lockCaptureOrientation(
int? cameraId, DeviceOrientation? orientation) async =>
super.noSuchMethod(Invocation.method(