From dcf5051eaf66c58ecd1d6e97f183f844f194c191 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 12:36:56 -0600 Subject: [PATCH 001/105] setDescription in Camera platform interface --- .../lib/src/method_channel/method_channel_camera.dart | 10 ++++++++++ .../lib/src/platform_interface/camera_platform.dart | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index f932f253f491..65d4021d6a0f 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -415,6 +415,16 @@ class MethodChannelCamera extends CameraPlatform { ); } + @override + Future setDescription(CameraDescription description) async { + await _channel.invokeMethod( + 'setDescription', + { + 'cameraName': description.name, + }, + ); + } + @override Widget buildPreview(int cameraId) { return Texture(textureId: cameraId); diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index aafeef890f1b..10aa61927dea 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -247,6 +247,11 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('pausePreview() is not implemented.'); } + /// Set the active camera even mid recording + Future setDescription(CameraDescription description) { + throw UnimplementedError('setDescription() is not implemented.'); + } + /// Returns a widget showing a live camera preview. Widget buildPreview(int cameraId) { throw UnimplementedError('buildView() has not been implemented.'); From ffd30f08bd3c02105d9fe0444af5e6436c4109e8 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 14:55:20 -0600 Subject: [PATCH 002/105] example app setup to change description mid recording --- .../example/lib/camera_controller.dart | 24 +++++++++++++------ .../camera_avfoundation/example/lib/main.dart | 20 ++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 5a7a79c8d96c..5fdcbca09378 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -24,6 +24,7 @@ class CameraValue { required this.exposureMode, required this.focusMode, required this.deviceOrientation, + required this.description, this.lockedCaptureOrientation, this.recordingOrientation, this.isPreviewPaused = false, @@ -31,7 +32,7 @@ class CameraValue { }); /// Creates a new camera controller state for an uninitialized controller. - const CameraValue.uninitialized() + const CameraValue.uninitialized(CameraDescription description) : this( isInitialized: false, isRecordingVideo: false, @@ -43,6 +44,7 @@ class CameraValue { focusMode: FocusMode.auto, deviceOrientation: DeviceOrientation.portraitUp, isPreviewPaused: false, + description: description, ); /// True after [CameraController.initialize] has completed successfully. @@ -92,6 +94,9 @@ class CameraValue { /// The orientation of the currently running video recording. final DeviceOrientation? recordingOrientation; + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + /// Creates a modified copy of the object. /// /// Explicitly specified fields get the specified value, all other fields get @@ -113,6 +118,7 @@ class CameraValue { Optional? recordingOrientation, bool? isPreviewPaused, Optional? previewPauseOrientation, + CameraDescription? description, }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, @@ -135,6 +141,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, + description: description ?? this.description, ); } @@ -165,14 +172,11 @@ class CameraValue { class CameraController extends ValueNotifier { /// Creates a new camera controller in an uninitialized state. CameraController( - this.description, + CameraDescription cameraDescription, this.resolutionPreset, { this.enableAudio = true, this.imageFormatGroup, - }) : super(const CameraValue.uninitialized()); - - /// The properties of the camera device controlled by this controller. - final CameraDescription description; + }) : super(CameraValue.uninitialized(cameraDescription)); /// The resolution this controller is targeting. /// @@ -215,7 +219,7 @@ class CameraController extends ValueNotifier { }); _cameraId = await CameraPlatform.instance.createCamera( - description, + value.description, resolutionPreset, enableAudio: enableAudio, ); @@ -274,6 +278,12 @@ class CameraController extends ValueNotifier { previewPauseOrientation: const Optional.absent()); } + /// Sets the focus mode for taking pictures. + Future setDescription(CameraDescription description) async { + await CameraPlatform.instance.setDescription(description); + value = value.copyWith(description: description); + } + /// Captures an image and returns the file where it was saved. /// /// Throws a [CameraException] if the capture fails. diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index 9ebc27e4be5b..6704a84926be 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -124,7 +124,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + onNewCameraSelected(cameraController.value.description); } } @@ -602,12 +602,9 @@ class _CameraExampleHomeState extends State width: 90.0, child: RadioListTile( title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), - groupValue: controller?.description, + groupValue: controller?.value.description, value: cameraDescription, - onChanged: - controller != null && controller!.value.isRecordingVideo - ? null - : onChanged, + onChanged: onChanged, ), ), ); @@ -640,6 +637,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { + + // if we are currently recording then switch the camera description of the controller to flip the camera + final bool isRecording = + controller != null && controller!.value.isRecordingVideo; + if (isRecording) { + controller!.setDescription(cameraDescription); + return; + } + final CameraController? oldController = controller; if (oldController != null) { // `controller` needs to be set to null before getting disposed, @@ -776,7 +782,7 @@ class _CameraExampleHomeState extends State void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller!.description); + onNewCameraSelected(controller!.value.description); } } From bc9e8b9724d5da49a1486a6c714a7fbacc0a855a Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 14:58:09 -0600 Subject: [PATCH 003/105] AVFoundationCamera method call to setDescription --- .../lib/src/avfoundation_camera.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 19054fe5c561..96aa442fa04b 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -487,6 +487,16 @@ class AVFoundationCamera extends CameraPlatform { ); } + @override + Future setDescription(CameraDescription description) async { + await _channel.invokeMethod( + 'setDescription', + { + 'cameraName': description.name, + }, + ); + } + @override Widget buildPreview(int cameraId) { return Texture(textureId: cameraId); From 89accd69be721d4326669cc4ce45f36b10d1e6cf Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 15:10:08 -0600 Subject: [PATCH 004/105] FLTCam setup to setDescription --- .../camera/camera_avfoundation/ios/Classes/CameraPlugin.m | 4 +++- packages/camera/camera_avfoundation/ios/Classes/FLTCam.h | 1 + packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index 628211ac7f7a..12d73e27bf3a 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -256,7 +256,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [_camera pausePreviewWithResult:result]; } else if ([@"resumePreview" isEqualToString:call.method]) { [_camera resumePreviewWithResult:result]; - } else { + } else if([@"setDescription" isEqualToString:call.method]) { + [_camera setDescription:(call.arguments[@"cameraName"]) result:result]; + }else { [result sendNotImplemented]; } } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index 8a5dafaf8354..aeb5edb31eee 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -86,6 +86,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; - (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result; - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)setDescription:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result; - (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; - (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; - (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 90b81adbd84c..da7d32f83390 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -838,6 +838,11 @@ - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result { [result sendSuccess]; } +- (void)setDescription:(NSString *)cameraName + result:(FLTThreadSafeFlutterResult *)result { + +} + - (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation x:(double)x y:(double)y { From e31ba2a293f5e776d44e04a0d271c0465b05a8bd Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 16:02:36 -0600 Subject: [PATCH 005/105] captureSession split into video and audio so we will be able to switch cameras without breaking the audio --- .../example/ios/RunnerTests/CameraTestUtils.m | 13 +- .../camera_avfoundation/ios/Classes/FLTCam.m | 157 ++++++++++++++---- .../ios/Classes/FLTCam_Test.h | 3 +- 3 files changed, 133 insertions(+), 40 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m index 0ae4887eb631..37519037176a 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -11,15 +11,20 @@ OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) .andReturn(inputMock); - id sessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op - OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + id videoSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + id audioSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); return [[FLTCam alloc] initWithCameraName:@"camera" resolutionPreset:@"medium" enableAudio:true orientation:UIDeviceOrientationPortrait - captureSession:sessionMock + videoCaptureSession:videoSessionMock + audioCaptureSession:audioSessionMock captureSessionQueue:captureSessionQueue error:nil]; } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index da7d32f83390..cbc41fb3740a 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -43,7 +43,8 @@ @interface FLTCam () Date: Wed, 21 Sep 2022 16:15:02 -0600 Subject: [PATCH 006/105] renamed setDescription to setDescriptionWhileRecording since it can only be used while recording --- .../example/ios/Runner.xcodeproj/project.pbxproj | 7 ++++--- .../camera_avfoundation/example/lib/camera_controller.dart | 6 +++--- packages/camera/camera_avfoundation/example/lib/main.dart | 2 +- .../camera/camera_avfoundation/ios/Classes/CameraPlugin.m | 4 ++-- packages/camera/camera_avfoundation/ios/Classes/FLTCam.h | 2 +- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 2 +- .../camera_avfoundation/lib/src/avfoundation_camera.dart | 4 ++-- .../lib/src/method_channel/method_channel_camera.dart | 4 ++-- .../lib/src/platform_interface/camera_platform.dart | 4 ++-- 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 03c80d79c578..114ff1a24430 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -290,6 +290,7 @@ }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = W4ZTW5E78A; }; }; }; @@ -637,7 +638,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W4ZTW5E78A; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -659,7 +660,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W4ZTW5E78A; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 5fdcbca09378..fee553260ac4 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -278,9 +278,9 @@ class CameraController extends ValueNotifier { previewPauseOrientation: const Optional.absent()); } - /// Sets the focus mode for taking pictures. - Future setDescription(CameraDescription description) async { - await CameraPlatform.instance.setDescription(description); + /// Sets the description while the camera is recording + Future setDescriptionWhileRecording(CameraDescription description) async { + await CameraPlatform.instance.setDescriptionWhileRecording(description); value = value.copyWith(description: description); } diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index 6704a84926be..50167bd01319 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -642,7 +642,7 @@ class _CameraExampleHomeState extends State final bool isRecording = controller != null && controller!.value.isRecordingVideo; if (isRecording) { - controller!.setDescription(cameraDescription); + controller!.setDescriptionWhileRecording(cameraDescription); return; } diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index 12d73e27bf3a..bcde55efc560 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -256,8 +256,8 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [_camera pausePreviewWithResult:result]; } else if ([@"resumePreview" isEqualToString:call.method]) { [_camera resumePreviewWithResult:result]; - } else if([@"setDescription" isEqualToString:call.method]) { - [_camera setDescription:(call.arguments[@"cameraName"]) result:result]; + } else if([@"setDescriptionWhileRecording" isEqualToString:call.method]) { + [_camera setDescriptionWhileRecording:(call.arguments[@"cameraName"]) result:result]; }else { [result sendNotImplemented]; } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index aeb5edb31eee..05143196d915 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -86,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; - (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result; - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result; -- (void)setDescription:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result; +- (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result; - (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; - (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; - (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index cbc41fb3740a..9de64a12b759 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -852,7 +852,7 @@ - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result { [result sendSuccess]; } -- (void)setDescription:(NSString *)cameraName +- (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result { if(!_isRecording){ diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 96aa442fa04b..106e5717b6f5 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -488,9 +488,9 @@ class AVFoundationCamera extends CameraPlatform { } @override - Future setDescription(CameraDescription description) async { + Future setDescriptionWhileRecording(CameraDescription description) async { await _channel.invokeMethod( - 'setDescription', + 'setDescriptionWhileRecording', { 'cameraName': description.name, }, diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index b0942d51c33a..8e56b1b4c97e 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -482,9 +482,9 @@ class MethodChannelCamera extends CameraPlatform { } @override - Future setDescription(CameraDescription description) async { + Future setDescriptionWhileRecording(CameraDescription description) async { await _channel.invokeMethod( - 'setDescription', + 'setDescriptionWhileRecording', { 'cameraName': description.name, }, diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index e72a5440ae36..dabfc972b668 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -257,8 +257,8 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('pausePreview() is not implemented.'); } - /// Set the active camera even mid recording - Future setDescription(CameraDescription description) { + /// Set the active camera while recording + Future setDescriptionWhileRecording(CameraDescription description) { throw UnimplementedError('setDescription() is not implemented.'); } From 1516e0d6dff16f6663c19665a70019a65170647e Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 16:17:38 -0600 Subject: [PATCH 007/105] integration tests fixed --- .../example/integration_test/camera_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 51eab634c84b..5dd98cd630a0 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -57,7 +57,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); // Take Picture final XFile file = await controller.takePicture(); @@ -103,7 +103,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); // Take Video await controller.startVideoRecording(); From 34b9b031e95286bc97229be7893fd76a02b2e89d Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 16:24:18 -0600 Subject: [PATCH 008/105] set description while recording integration test --- .../example/integration_test/camera_test.dart | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 5dd98cd630a0..d053365f14a9 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -202,6 +202,30 @@ void main() { expect(duration, lessThan(recordingTime - timePaused)); }); + testWidgets('Set description while recording', (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.length < 2) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 500)); + await controller.setDescriptionWhileRecording(cameras[1]); + sleep(const Duration(milliseconds: 500)); + + expect(controller.value.description, cameras[1]); + }); + /// Start streaming with specifying the ImageFormatGroup. Future startStreaming(List cameras, ImageFormatGroup? imageFormatGroup) async { From 97f97339aff8d1eb501df0bc45e07c293b86c4dd Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 16:26:22 -0600 Subject: [PATCH 009/105] throws error if device not recording and setDescriptionWhileRecording is called --- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 9de64a12b759..1cbace73f70b 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -856,7 +856,9 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result { if(!_isRecording){ - // TODO: what happens if we are not recording + [result sendErrorWithCode:@"setDescriptionWhileRecording" + message:@"Device was not recording" + details:nil]; return; } From bca93e2c07920dd9fc79cc6234b35a8eb55b7265 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 21 Sep 2022 16:32:57 -0600 Subject: [PATCH 010/105] set description while recording test --- .../test/avfoundation_camera_test.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index 60109a4172b7..c87d5d542b92 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -669,6 +669,28 @@ void main() { ]); }); + test('Should set the description while recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setDescriptionWhileRecording': null}, + ); + const CameraDescription camera2Description = CameraDescription( + name: 'Test2', + lensDirection: CameraLensDirection.front, + sensorOrientation: 0); + + // Act + await camera.setDescriptionWhileRecording(camera2Description); + + // Assert + expect(channel.log, [ + isMethodCall('setDescriptionWhileRecording', arguments: { + 'cameraName': camera2Description.name, + }), + ]); + }); + test('Should set the flash mode', () async { // Arrange final MethodChannelMock channel = MethodChannelMock( From b999be13d287e5146896ce6c9ee21e4154bd63f3 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 22 Sep 2022 06:52:54 -0600 Subject: [PATCH 011/105] example project setup --- .../example/lib/camera_controller.dart | 24 +++++++++++++------ .../camera_android/example/lib/main.dart | 20 ++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 5a7a79c8d96c..fee553260ac4 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -24,6 +24,7 @@ class CameraValue { required this.exposureMode, required this.focusMode, required this.deviceOrientation, + required this.description, this.lockedCaptureOrientation, this.recordingOrientation, this.isPreviewPaused = false, @@ -31,7 +32,7 @@ class CameraValue { }); /// Creates a new camera controller state for an uninitialized controller. - const CameraValue.uninitialized() + const CameraValue.uninitialized(CameraDescription description) : this( isInitialized: false, isRecordingVideo: false, @@ -43,6 +44,7 @@ class CameraValue { focusMode: FocusMode.auto, deviceOrientation: DeviceOrientation.portraitUp, isPreviewPaused: false, + description: description, ); /// True after [CameraController.initialize] has completed successfully. @@ -92,6 +94,9 @@ class CameraValue { /// The orientation of the currently running video recording. final DeviceOrientation? recordingOrientation; + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + /// Creates a modified copy of the object. /// /// Explicitly specified fields get the specified value, all other fields get @@ -113,6 +118,7 @@ class CameraValue { Optional? recordingOrientation, bool? isPreviewPaused, Optional? previewPauseOrientation, + CameraDescription? description, }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, @@ -135,6 +141,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, + description: description ?? this.description, ); } @@ -165,14 +172,11 @@ class CameraValue { class CameraController extends ValueNotifier { /// Creates a new camera controller in an uninitialized state. CameraController( - this.description, + CameraDescription cameraDescription, this.resolutionPreset, { this.enableAudio = true, this.imageFormatGroup, - }) : super(const CameraValue.uninitialized()); - - /// The properties of the camera device controlled by this controller. - final CameraDescription description; + }) : super(CameraValue.uninitialized(cameraDescription)); /// The resolution this controller is targeting. /// @@ -215,7 +219,7 @@ class CameraController extends ValueNotifier { }); _cameraId = await CameraPlatform.instance.createCamera( - description, + value.description, resolutionPreset, enableAudio: enableAudio, ); @@ -274,6 +278,12 @@ class CameraController extends ValueNotifier { previewPauseOrientation: const Optional.absent()); } + /// Sets the description while the camera is recording + Future setDescriptionWhileRecording(CameraDescription description) async { + await CameraPlatform.instance.setDescriptionWhileRecording(description); + value = value.copyWith(description: description); + } + /// Captures an image and returns the file where it was saved. /// /// Throws a [CameraException] if the capture fails. diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index 9ebc27e4be5b..50167bd01319 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -124,7 +124,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + onNewCameraSelected(cameraController.value.description); } } @@ -602,12 +602,9 @@ class _CameraExampleHomeState extends State width: 90.0, child: RadioListTile( title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), - groupValue: controller?.description, + groupValue: controller?.value.description, value: cameraDescription, - onChanged: - controller != null && controller!.value.isRecordingVideo - ? null - : onChanged, + onChanged: onChanged, ), ), ); @@ -640,6 +637,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { + + // if we are currently recording then switch the camera description of the controller to flip the camera + final bool isRecording = + controller != null && controller!.value.isRecordingVideo; + if (isRecording) { + controller!.setDescriptionWhileRecording(cameraDescription); + return; + } + final CameraController? oldController = controller; if (oldController != null) { // `controller` needs to be set to null before getting disposed, @@ -776,7 +782,7 @@ class _CameraExampleHomeState extends State void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller!.description); + onNewCameraSelected(controller!.value.description); } } From 568af7d36022b427e6032513040bc7d2430aedc2 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 22 Sep 2022 08:41:43 -0600 Subject: [PATCH 012/105] camera preview can be changed while recording --- .../io/flutter/plugins/camera/Camera.java | 180 ++++++++++-------- .../plugins/camera/MethodCallHandlerImpl.java | 12 ++ .../lib/src/android_camera.dart | 10 + 3 files changed, 126 insertions(+), 76 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 3d2df98b60da..5fc3b43f04cb 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -115,13 +115,14 @@ class Camera * Holds all of the camera features/settings and will be used to update the request builder when * one changes. */ - private final CameraFeatures cameraFeatures; + private CameraFeatures cameraFeatures; private final SurfaceTextureEntry flutterTexture; + private final ResolutionPreset resolutionPreset; private final boolean enableAudio; private final Context applicationContext; private final DartMessenger dartMessenger; - private final CameraProperties cameraProperties; + private CameraProperties cameraProperties; private final CameraFeatureFactory cameraFeatureFactory; private final Activity activity; /** A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. */ @@ -211,6 +212,7 @@ public Camera( this.applicationContext = activity.getApplicationContext(); this.cameraProperties = cameraProperties; this.cameraFeatureFactory = cameraFeatureFactory; + this.resolutionPreset = resolutionPreset; this.cameraFeatures = CameraFeatures.init( cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); @@ -274,7 +276,6 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { .build(); } - @SuppressLint("MissingPermission") public void open(String imageFormatGroup) throws CameraAccessException { final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); @@ -310,76 +311,83 @@ public void open(String imageFormatGroup) throws CameraAccessException { imageFormat, 1); + openCameraAndStartPreview(resolutionFeature); // TODO: can we just includet this entire function in this parent one or do we have to bypass pictureImageReader stuff on switch? + } + + @SuppressLint("MissingPermission") + private void openCameraAndStartPreview(ResolutionFeature resolutionFeature) throws CameraAccessException { // Open the camera. CameraManager cameraManager = CameraUtils.getCameraManager(activity); cameraManager.openCamera( - cameraProperties.getCameraName(), - new CameraDevice.StateCallback() { - @Override - public void onOpened(@NonNull CameraDevice device) { - cameraDevice = new DefaultCameraDeviceWrapper(device); - try { - startPreview(); - dartMessenger.sendCameraInitializedEvent( - resolutionFeature.getPreviewSize().getWidth(), - resolutionFeature.getPreviewSize().getHeight(), - cameraFeatures.getExposureLock().getValue(), - cameraFeatures.getAutoFocus().getValue(), - cameraFeatures.getExposurePoint().checkIsSupported(), - cameraFeatures.getFocusPoint().checkIsSupported()); - } catch (CameraAccessException e) { - dartMessenger.sendCameraErrorEvent(e.getMessage()); - close(); - } - } + cameraProperties.getCameraName(), + new CameraDevice.StateCallback() { + @Override + public void onOpened(@NonNull CameraDevice device) { + cameraDevice = new DefaultCameraDeviceWrapper(device); + try { + startPreview(); + + // TODO: do we send this ? + dartMessenger.sendCameraInitializedEvent( + resolutionFeature.getPreviewSize().getWidth(), + resolutionFeature.getPreviewSize().getHeight(), + cameraFeatures.getExposureLock().getValue(), + cameraFeatures.getAutoFocus().getValue(), + cameraFeatures.getExposurePoint().checkIsSupported(), + cameraFeatures.getFocusPoint().checkIsSupported()); + } catch (CameraAccessException e) { + dartMessenger.sendCameraErrorEvent(e.getMessage()); + close(); + } + } - @Override - public void onClosed(@NonNull CameraDevice camera) { - Log.i(TAG, "open | onClosed"); + @Override + public void onClosed(@NonNull CameraDevice camera) { + Log.i(TAG, "open | onClosed"); - // Prevents calls to methods that would otherwise result in IllegalStateException exceptions. - cameraDevice = null; - closeCaptureSession(); - dartMessenger.sendCameraClosingEvent(); - } + // Prevents calls to methods that would otherwise result in IllegalStateException exceptions. + cameraDevice = null; + closeCaptureSession(); + dartMessenger.sendCameraClosingEvent(); + } - @Override - public void onDisconnected(@NonNull CameraDevice cameraDevice) { - Log.i(TAG, "open | onDisconnected"); + @Override + public void onDisconnected(@NonNull CameraDevice cameraDevice) { + Log.i(TAG, "open | onDisconnected"); - close(); - dartMessenger.sendCameraErrorEvent("The camera was disconnected."); - } + close(); + dartMessenger.sendCameraErrorEvent("The camera was disconnected."); + } - @Override - public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { - Log.i(TAG, "open | onError"); - - close(); - String errorDescription; - switch (errorCode) { - case ERROR_CAMERA_IN_USE: - errorDescription = "The camera device is in use already."; - break; - case ERROR_MAX_CAMERAS_IN_USE: - errorDescription = "Max cameras in use"; - break; - case ERROR_CAMERA_DISABLED: - errorDescription = "The camera device could not be opened due to a device policy."; - break; - case ERROR_CAMERA_DEVICE: - errorDescription = "The camera device has encountered a fatal error"; - break; - case ERROR_CAMERA_SERVICE: - errorDescription = "The camera service has encountered a fatal error."; - break; - default: - errorDescription = "Unknown camera error"; - } - dartMessenger.sendCameraErrorEvent(errorDescription); - } - }, - backgroundHandler); + @Override + public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { + Log.i(TAG, "open | onError"); + + close(); + String errorDescription; + switch (errorCode) { + case ERROR_CAMERA_IN_USE: + errorDescription = "The camera device is in use already."; + break; + case ERROR_MAX_CAMERAS_IN_USE: + errorDescription = "Max cameras in use"; + break; + case ERROR_CAMERA_DISABLED: + errorDescription = "The camera device could not be opened due to a device policy."; + break; + case ERROR_CAMERA_DEVICE: + errorDescription = "The camera device has encountered a fatal error"; + break; + case ERROR_CAMERA_SERVICE: + errorDescription = "The camera service has encountered a fatal error."; + break; + default: + errorDescription = "Unknown camera error"; + } + dartMessenger.sendCameraErrorEvent(errorDescription); + } + }, + backgroundHandler); } @VisibleForTesting @@ -1169,17 +1177,7 @@ private void closeCaptureSession() { public void close() { Log.i(TAG, "close"); - if (cameraDevice != null) { - cameraDevice.close(); - cameraDevice = null; - - // Closing the CameraDevice without closing the CameraCaptureSession is recommended - // for quickly closing the camera: - // https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession#close() - captureSession = null; - } else { - closeCaptureSession(); - } + stopAndReleaseCamera(); if (pictureImageReader != null) { pictureImageReader.close(); @@ -1198,6 +1196,36 @@ public void close() { stopBackgroundThread(); } + private void stopAndReleaseCamera() { + if (cameraDevice != null) { + cameraDevice.close(); + cameraDevice = null; + + // Closing the CameraDevice without closing the CameraCaptureSession is recommended + // for quickly closing the camera: + // https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession#close() + captureSession = null; + } else { + closeCaptureSession(); + } + } + + public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { + // TODO: save some camera settings + stopAndReleaseCamera(); + this.cameraProperties = properties; + this.cameraFeatures = + CameraFeatures.init( + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); + // set camera stuff + final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + try { + openCameraAndStartPreview(resolutionFeature); + } catch (CameraAccessException e) { + e.printStackTrace(); // TODO: throw flutter error + } + } + public void dispose() { Log.i(TAG, "dispose"); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 38201e1136c9..b4d0ee89e0c3 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -351,6 +351,18 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) result.success(null); break; } + case "setDescriptionWhileRecording" : { + try { + String cameraName = call.argument("cameraName"); + CameraProperties cameraProperties = + new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); + camera.setDescriptionWhileRecording(result,cameraProperties); + result.success(null); + } catch (Exception e) { + handleException(e, result); + } + break; + } case "dispose": { if (camera != null) { diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 3e9e52af04a7..f35cec69d885 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -482,6 +482,16 @@ class AndroidCamera extends CameraPlatform { ); } + @override + Future setDescriptionWhileRecording(CameraDescription description) async { + await _channel.invokeMethod( + 'setDescriptionWhileRecording', + { + 'cameraName': description.name, + }, + ); + } + @override Widget buildPreview(int cameraId) { return Texture(textureId: cameraId); From ef4f96b4c309898577087649dbd02697c8bf7117 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 22 Sep 2022 09:30:24 -0600 Subject: [PATCH 013/105] camera switches and keeps surface pointed to mediarecorder --- .../io/flutter/plugins/camera/Camera.java | 29 ++++++++++++------- .../plugins/camera/MethodCallHandlerImpl.java | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 5fc3b43f04cb..14451e6d88f6 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -116,6 +116,7 @@ class Camera * one changes. */ private CameraFeatures cameraFeatures; + private String imageFormatGroup; private final SurfaceTextureEntry flutterTexture; private final ResolutionPreset resolutionPreset; @@ -276,7 +277,14 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { .build(); } + // default to opening as image stream public void open(String imageFormatGroup) throws CameraAccessException { + open(imageFormatGroup, false); + } + + @SuppressLint("MissingPermission") + private void open(String imageFormatGroup, boolean openAsVideo) throws CameraAccessException{ + this.imageFormatGroup = imageFormatGroup; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); if (!resolutionFeature.checkIsSupported()) { @@ -311,11 +319,6 @@ public void open(String imageFormatGroup) throws CameraAccessException { imageFormat, 1); - openCameraAndStartPreview(resolutionFeature); // TODO: can we just includet this entire function in this parent one or do we have to bypass pictureImageReader stuff on switch? - } - - @SuppressLint("MissingPermission") - private void openCameraAndStartPreview(ResolutionFeature resolutionFeature) throws CameraAccessException { // Open the camera. CameraManager cameraManager = CameraUtils.getCameraManager(activity); cameraManager.openCamera( @@ -325,7 +328,7 @@ private void openCameraAndStartPreview(ResolutionFeature resolutionFeature) thro public void onOpened(@NonNull CameraDevice device) { cameraDevice = new DefaultCameraDeviceWrapper(device); try { - startPreview(); + startPreview(openAsVideo); // TODO: do we send this ? dartMessenger.sendCameraInitializedEvent( @@ -787,7 +790,7 @@ public void stopVideoRecording(@NonNull final Result result) { } mediaRecorder.reset(); try { - startPreview(); + startPreview(false); } catch (CameraAccessException | IllegalStateException e) { result.error("videoRecordingFailed", e.getMessage(), null); return; @@ -1072,11 +1075,15 @@ public void resumePreview() { null, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); } - public void startPreview() throws CameraAccessException { + public void startPreview(boolean useMediaRecorderSurface) throws CameraAccessException { if (pictureImageReader == null || pictureImageReader.getSurface() == null) return; Log.i(TAG, "startPreview"); - - createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); + if(useMediaRecorderSurface){ + createCaptureSession( + CameraDevice.TEMPLATE_RECORD, mediaRecorder.getSurface()); + }else{ + createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); + } } public void startPreviewWithImageStream(EventChannel imageStreamChannel) @@ -1220,7 +1227,7 @@ public void setDescriptionWhileRecording(@NonNull final Result result, CameraPro // set camera stuff final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); try { - openCameraAndStartPreview(resolutionFeature); + open(imageFormatGroup, true); } catch (CameraAccessException e) { e.printStackTrace(); // TODO: throw flutter error } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index b4d0ee89e0c3..821f0c5ecb40 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -262,7 +262,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) case "stopImageStream": { try { - camera.startPreview(); + camera.startPreview(false); result.success(null); } catch (Exception e) { handleException(e, result); From f36004df77ec5af0d12d28a0201a84f137eb97ff Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 22 Sep 2022 12:12:06 -0600 Subject: [PATCH 014/105] small change to set autofocus when switching while recording --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 14451e6d88f6..ce18b978cfe6 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1220,12 +1220,12 @@ private void stopAndReleaseCamera() { public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { // TODO: save some camera settings stopAndReleaseCamera(); - this.cameraProperties = properties; - this.cameraFeatures = + cameraProperties = properties; + cameraFeatures = CameraFeatures.init( cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); - // set camera stuff - final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + cameraFeatures.setAutoFocus( + cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { open(imageFormatGroup, true); } catch (CameraAccessException e) { From a0301c2c1029a06037b8607bd44ea2e1c4a2a906 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 22 Sep 2022 13:32:08 -0600 Subject: [PATCH 015/105] android video record goes through VideoRenderer to apply matrix after switching camera --- .../io/flutter/plugins/camera/Camera.java | 17 +- .../flutter/plugins/camera/VideoRenderer.java | 333 ++++++++++++++++++ 2 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index ce18b978cfe6..61c94086f47d 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -117,6 +117,7 @@ class Camera */ private CameraFeatures cameraFeatures; private String imageFormatGroup; + private VideoRenderer videoRenderer; private final SurfaceTextureEntry flutterTexture; private final ResolutionPreset resolutionPreset; @@ -254,6 +255,7 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { if (mediaRecorder != null) { mediaRecorder.release(); } + closeRenderer(); final PlatformChannel.DeviceOrientation lockedOrientation = ((SensorOrientationFeature) cameraFeatures.getSensorOrientation()) @@ -275,6 +277,9 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { ? getDeviceOrientationManager().getVideoOrientation() : getDeviceOrientationManager().getVideoOrientation(lockedOrientation)) .build(); + final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + videoRenderer = new VideoRenderer(mediaRecorder.getSurface(),resolutionFeature.getCaptureSize().getWidth(), + resolutionFeature.getCaptureSize().getHeight()); } // default to opening as image stream @@ -764,15 +769,22 @@ public void startVideoRecording(@NonNull Result result) { recordingVideo = true; try { createCaptureSession( - CameraDevice.TEMPLATE_RECORD, () -> mediaRecorder.start(), mediaRecorder.getSurface()); + CameraDevice.TEMPLATE_RECORD, () -> mediaRecorder.start(),videoRenderer.getInputSurface()); result.success(null); - } catch (CameraAccessException e) { + } catch (CameraAccessException | InterruptedException e) { recordingVideo = false; captureFile = null; result.error("videoRecordingFailed", e.getMessage(), null); } } + private void closeRenderer() { + if(videoRenderer != null){ + videoRenderer.close(); + videoRenderer = null; + } + } + public void stopVideoRecording(@NonNull final Result result) { if (!recordingVideo) { result.success(null); @@ -783,6 +795,7 @@ public void stopVideoRecording(@NonNull final Result result) { cameraFeatureFactory.createAutoFocusFeature(cameraProperties, false)); recordingVideo = false; try { + closeRenderer(); captureSession.abortCaptures(); mediaRecorder.stop(); } catch (CameraAccessException | IllegalStateException e) { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java new file mode 100644 index 000000000000..81d2781aabc7 --- /dev/null +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -0,0 +1,333 @@ +package io.flutter.plugins.camera; + +import static android.os.SystemClock.uptimeMillis; + +import android.graphics.SurfaceTexture; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLExt; +import android.opengl.EGLSurface; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.opengl.Matrix; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** Renders video onto texture */ +public class VideoRenderer { + + private static String TAG = "VideoRenderer"; + + private String vertexShaderCode = " precision highp float;\n"+ + " attribute vec3 vertexPosition;\n"+ + " attribute vec2 uvs;\n"+ + " varying vec2 varUvs;\n"+ + " uniform mat4 texMatrix;\n"+ + " uniform mat4 mvp;\n"+ + "\n"+ + " void main()\n"+ + " {\n"+ + " varUvs = (texMatrix * vec4(uvs.x, uvs.y, 0, 1.0)).xy;\n"+ + " gl_Position = mvp * vec4(vertexPosition, 1.0);\n"+ + " }";; + + private String fragmentShaderCode = + " #extension GL_OES_EGL_image_external : require\n"+ + " precision mediump float;\n"+ + "\n"+ + " varying vec2 varUvs;\n"+ + " uniform samplerExternalOES texSampler;\n"+ + "\n"+ + " void main()\n"+ + " {\n"+ + " vec4 c = texture2D(texSampler, varUvs);\n"+ + " gl_FragColor = vec4(c.r, c.g, c.b, c.a);\n"+ + " }"; + + private int[] textureHandles = new int[1]; + + private float[] vertices = new float[]{ + // x, y, z, u, v + -1.0f, -1.0f, 0.0f, 0f, 0f, + -1.0f, 1.0f, 0.0f, 0f, 1f, + 1.0f, 1.0f, 0.0f, 1f, 1f, + 1.0f, -1.0f, 0.0f, 1f, 0f + }; + + private int[] indices = new int[] + + { + 2, 1, 0, 0, 3, 2 + }; + + private int program; + private int vertexHandle = 0; + private int[] bufferHandles = new int[2]; + private int uvsHandle = 0; + private int texMatrixHandle = 0; + private int mvpHandle = 0; + private int samplerHandle = 0; + + private ByteBuffer vertexBuffer; + + private ByteBuffer indexBuffer ; + + EGLDisplay display; + EGLContext context; + EGLSurface surface; + private Thread thread; + private final Surface outputSurface; + private SurfaceTexture inputSurfaceTexture; + private Surface inputSurface; + + private HandlerThread surfaceTextureFrameAvailableHandler; + private final Object surfaceTextureAvailableFrameLock = new Object(); + private Boolean surfaceTextureFrameAvailable = false; + + private final int recordingWidth; + private final int recordingHeight; + private int rotation = 90; + + private final Object lock = new Object(); + + /** Gets surface for input. Blocks until surface is ready */ + public Surface getInputSurface() throws InterruptedException { + synchronized (lock) { + while (inputSurface == null) { + lock.wait(500); + } + } + return inputSurface; + } + + public VideoRenderer(Surface outputSurface, int recordingWidth, int recordingHeight){ + this.outputSurface = outputSurface; + this.recordingHeight = recordingHeight; + this.recordingWidth = recordingWidth; + startOpenGL(); + Log.d(TAG, "VideoRenderer setup complete"); + } + + /** Stop rendering and cleanup resources*/ + public void close(){ + thread.interrupt(); + surfaceTextureFrameAvailableHandler.quitSafely(); + inputSurfaceTexture.release(); + } + + /** Configures openGL. must be called in same thread as draw is called*/ + private void configureOpenGL(){ + synchronized (lock) { + display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (display == EGL14.EGL_NO_DISPLAY) + throw new RuntimeException("eglDisplay == EGL14.EGL_NO_DISPLAY: " + + GLUtils.getEGLErrorString(EGL14.eglGetError())); + + int[] version = new int[2]; + if (!EGL14.eglInitialize(display, version, 0, version, 1)) + throw new RuntimeException("eglInitialize(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); + + int[] attribList = new int[]{ + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, + EGLExt.EGL_RECORDABLE_ANDROID, 1, + EGL14.EGL_NONE + }; + + EGLConfig[] configs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + if (!EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.length, numConfigs, 0)) + throw new RuntimeException(GLUtils.getEGLErrorString(EGL14.eglGetError())); + + int err = EGL14.eglGetError(); + if (err != EGL14.EGL_SUCCESS) + throw new RuntimeException(GLUtils.getEGLErrorString(err)); + + int[] ctxAttribs = new int[]{ + EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, + EGL14.EGL_NONE + }; + context = EGL14.eglCreateContext(display, configs[0], EGL14.EGL_NO_CONTEXT, ctxAttribs, 0); + + err = EGL14.eglGetError(); + if (err != EGL14.EGL_SUCCESS) + throw new RuntimeException(GLUtils.getEGLErrorString(err)); + + int[] surfaceAttribs = new int[]{ + EGL14.EGL_NONE + }; + + surface = EGL14.eglCreateWindowSurface(display, configs[0], outputSurface, surfaceAttribs, 0); + + err = EGL14.eglGetError(); + if (err != EGL14.EGL_SUCCESS) + throw new RuntimeException(GLUtils.getEGLErrorString(err)); + + if (!EGL14.eglMakeCurrent(display, surface, surface, context)) + throw new RuntimeException("eglMakeCurrent(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); + + vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4); + vertexBuffer.order(ByteOrder.nativeOrder()); + vertexBuffer.asFloatBuffer().put(vertices); + vertexBuffer.asFloatBuffer().position(0); + + + indexBuffer = ByteBuffer.allocateDirect(indices.length * 4); + indexBuffer.order(ByteOrder.nativeOrder()); + indexBuffer.asIntBuffer().put(indices); + indexBuffer.position(0); + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + + program = GLES20.glCreateProgram(); + + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glLinkProgram(program); + + vertexHandle = GLES20.glGetAttribLocation(program, "vertexPosition"); + uvsHandle = GLES20.glGetAttribLocation(program, "uvs"); + texMatrixHandle = GLES20.glGetUniformLocation(program, "texMatrix"); + mvpHandle = GLES20.glGetUniformLocation(program, "mvp"); + samplerHandle = GLES20.glGetUniformLocation(program, "texSampler"); + + + // Initialize buffers + GLES20.glGenBuffers(2, bufferHandles, 0); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.length * 4, vertexBuffer, GLES20.GL_DYNAMIC_DRAW); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]); + GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.length * 4, indexBuffer, GLES20.GL_DYNAMIC_DRAW); + + // Init texture that will receive decoded frames + GLES20.glGenTextures(1, textureHandles, 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureHandles[0]); + + inputSurfaceTexture = new SurfaceTexture(getTexId()); + inputSurfaceTexture.setDefaultBufferSize(recordingWidth, recordingHeight); + surfaceTextureFrameAvailableHandler = new HandlerThread("FrameHandlerThread"); + surfaceTextureFrameAvailableHandler.start(); + inputSurface = new Surface(inputSurfaceTexture); + + inputSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + synchronized (surfaceTextureAvailableFrameLock) { + if (surfaceTextureFrameAvailable) + Log.w(TAG, "Frame available before processing other frames. dropping frames"); + surfaceTextureFrameAvailable = true; + surfaceTextureAvailableFrameLock.notifyAll(); + } + + } + }, new Handler(surfaceTextureFrameAvailableHandler.getLooper())); + lock.notifyAll(); + } + + } + + /** Starts and configures Video Renderer */ + private void startOpenGL() { + Log.d(TAG, "Starting OpenGL Thread"); + thread = new Thread() { + @Override + public void run() { + + configureOpenGL(); + + try { + // continuously pull frames from input surface texture and use videoRenderer to modify to correct rotation + while(!Thread.interrupted()) { + + synchronized(surfaceTextureAvailableFrameLock) { + while (!surfaceTextureFrameAvailable) { + surfaceTextureAvailableFrameLock.wait(500); + } + surfaceTextureFrameAvailable = false; + } + + inputSurfaceTexture.updateTexImage(); + + float[] surfaceTextureMatrix = new float[16]; + inputSurfaceTexture.getTransformMatrix(surfaceTextureMatrix); + + draw(recordingWidth,recordingHeight,surfaceTextureMatrix, inputSurfaceTexture.getTimestamp()); + } + } catch (InterruptedException e) { + Log.e(TAG, "thread interrupted while waiting for frames"); // TODO: + } + + } + }; + thread.start(); + } + + public int getTexId(){ + return textureHandles[0]; + } + + public float[] moveMatrix (){ + float[] m = new float[16]; + Matrix.setIdentityM(m, 0); + Matrix.rotateM(m, 0, rotation, 0, 0, 1); + return m; + } + + public void setRotation(int rotation){ + this.rotation = rotation; + } + + private int loadShader(int type, String code) { + + int shader = GLES20.glCreateShader(type); + + GLES20.glShaderSource(shader, code); + GLES20.glCompileShader(shader); + return shader; + } + + public void draw(int viewportWidth,int viewportHeight , float[] texMatrix, long timestamp ) { + + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + GLES20.glClearColor(0f, 0f, 0f, 0f); + + GLES20.glViewport(0, 0, viewportWidth, viewportHeight); + + GLES20.glUseProgram(program); + + // Pass transformations to shader + GLES20.glUniformMatrix4fv(texMatrixHandle, 1, false, texMatrix, 0); + GLES20.glUniformMatrix4fv(mvpHandle, 1, false, moveMatrix(), 0); + + // Prepare buffers with vertices and indices & draw + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]); + + GLES20.glEnableVertexAttribArray(vertexHandle); + GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 4 * 5, 0); + + GLES20.glEnableVertexAttribArray(uvsHandle); + GLES20.glVertexAttribPointer(uvsHandle, 2, GLES20.GL_FLOAT, false, 4 * 5, 3 * 4); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); + + EGLExt.eglPresentationTimeANDROID(display, surface, + uptimeMillis() * 1000000); // TODO: see https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled + EGL14.eglSwapBuffers(display, surface); + } +} From e929814031ccd18abc802692ceb516f4189f1c67 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 08:33:05 -0600 Subject: [PATCH 016/105] switch camera uses VideoRenderer --- .../io/flutter/plugins/camera/Camera.java | 25 ++++++++++++------- .../plugins/camera/MethodCallHandlerImpl.java | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 61c94086f47d..fc3934b944b2 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -117,6 +117,8 @@ class Camera */ private CameraFeatures cameraFeatures; private String imageFormatGroup; + + /** Takes an input/output surface and orients the recording correctly. This is needed because switching cameras while recording causes bad orientation */ private VideoRenderer videoRenderer; private final SurfaceTextureEntry flutterTexture; @@ -333,7 +335,11 @@ private void open(String imageFormatGroup, boolean openAsVideo) throws CameraAcc public void onOpened(@NonNull CameraDevice device) { cameraDevice = new DefaultCameraDeviceWrapper(device); try { - startPreview(openAsVideo); + if(recordingVideo){ + startPreviewWithVideoStream(); + }else{ + startPreview(); + } // TODO: do we send this ? dartMessenger.sendCameraInitializedEvent( @@ -343,7 +349,7 @@ public void onOpened(@NonNull CameraDevice device) { cameraFeatures.getAutoFocus().getValue(), cameraFeatures.getExposurePoint().checkIsSupported(), cameraFeatures.getFocusPoint().checkIsSupported()); - } catch (CameraAccessException e) { + } catch (CameraAccessException | InterruptedException e) { dartMessenger.sendCameraErrorEvent(e.getMessage()); close(); } @@ -803,7 +809,7 @@ public void stopVideoRecording(@NonNull final Result result) { } mediaRecorder.reset(); try { - startPreview(false); + startPreview(); } catch (CameraAccessException | IllegalStateException e) { result.error("videoRecordingFailed", e.getMessage(), null); return; @@ -1088,15 +1094,16 @@ public void resumePreview() { null, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); } - public void startPreview(boolean useMediaRecorderSurface) throws CameraAccessException { + public void startPreview() throws CameraAccessException { if (pictureImageReader == null || pictureImageReader.getSurface() == null) return; Log.i(TAG, "startPreview"); - if(useMediaRecorderSurface){ - createCaptureSession( - CameraDevice.TEMPLATE_RECORD, mediaRecorder.getSurface()); - }else{ createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); - } + } + + private void startPreviewWithVideoStream() throws CameraAccessException, InterruptedException { + if (videoRenderer == null) return; + createCaptureSession( + CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); } public void startPreviewWithImageStream(EventChannel imageStreamChannel) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 821f0c5ecb40..b4d0ee89e0c3 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -262,7 +262,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) case "stopImageStream": { try { - camera.startPreview(false); + camera.startPreview(); result.success(null); } catch (Exception e) { handleException(e, result); From 156f78a95916753796f7e5874a51f99b2bd013bf Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 08:41:51 -0600 Subject: [PATCH 017/105] dont use video renderer until user switches camera while recording --- .../io/flutter/plugins/camera/Camera.java | 19 ++++++++++++------- .../flutter/plugins/camera/VideoRenderer.java | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index fc3934b944b2..762994471386 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -279,9 +279,6 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { ? getDeviceOrientationManager().getVideoOrientation() : getDeviceOrientationManager().getVideoOrientation(lockedOrientation)) .build(); - final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); - videoRenderer = new VideoRenderer(mediaRecorder.getSurface(),resolutionFeature.getCaptureSize().getWidth(), - resolutionFeature.getCaptureSize().getHeight()); } // default to opening as image stream @@ -336,7 +333,7 @@ public void onOpened(@NonNull CameraDevice device) { cameraDevice = new DefaultCameraDeviceWrapper(device); try { if(recordingVideo){ - startPreviewWithVideoStream(); + startPreviewWithVideoRendererStream(); }else{ startPreview(); } @@ -775,9 +772,9 @@ public void startVideoRecording(@NonNull Result result) { recordingVideo = true; try { createCaptureSession( - CameraDevice.TEMPLATE_RECORD, () -> mediaRecorder.start(),videoRenderer.getInputSurface()); + CameraDevice.TEMPLATE_RECORD, () -> mediaRecorder.start(),mediaRecorder.getSurface()); result.success(null); - } catch (CameraAccessException | InterruptedException e) { + } catch (CameraAccessException e) { recordingVideo = false; captureFile = null; result.error("videoRecordingFailed", e.getMessage(), null); @@ -1100,7 +1097,7 @@ public void startPreview() throws CameraAccessException { createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); } - private void startPreviewWithVideoStream() throws CameraAccessException, InterruptedException { + private void startPreviewWithVideoRendererStream() throws CameraAccessException, InterruptedException { if (videoRenderer == null) return; createCaptureSession( CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); @@ -1237,9 +1234,17 @@ private void stopAndReleaseCamera() { } } + private void prepareVideoRenderer() { + if(videoRenderer != null) return; + final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + videoRenderer = new VideoRenderer(mediaRecorder.getSurface(),resolutionFeature.getCaptureSize().getWidth(), + resolutionFeature.getCaptureSize().getHeight()); + } + public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { // TODO: save some camera settings stopAndReleaseCamera(); + prepareVideoRenderer(); cameraProperties = properties; cameraFeatures = CameraFeatures.init( diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 81d2781aabc7..33b123ec68b8 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -94,7 +94,7 @@ public class VideoRenderer { private final int recordingWidth; private final int recordingHeight; - private int rotation = 90; + private int rotation = 0; private final Object lock = new Object(); From 61468a94dd80c6725b829a30c9887eed65d30ceb Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:03:38 -0600 Subject: [PATCH 018/105] rotate based on initial recording direction --- .../io/flutter/plugins/camera/Camera.java | 19 +++++++++++++++++++ .../flutter/plugins/camera/VideoRenderer.java | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 762994471386..1e4cb9270d6c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -121,6 +121,9 @@ class Camera /** Takes an input/output surface and orients the recording correctly. This is needed because switching cameras while recording causes bad orientation */ private VideoRenderer videoRenderer; + /** When we flip the camera we need to know if it aligns with the initial way the camera was facing or not */ + private int initialCameraFacing; + private final SurfaceTextureEntry flutterTexture; private final ResolutionPreset resolutionPreset; private final boolean enableAudio; @@ -766,6 +769,7 @@ public void startVideoRecording(@NonNull Result result) { result.error("videoRecordingFailed", e.getMessage(), null); return; } + initialCameraFacing = cameraProperties.getLensFacing(); // Re-create autofocus feature so it's using video focus mode now. cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); @@ -1099,6 +1103,21 @@ public void startPreview() throws CameraAccessException { private void startPreviewWithVideoRendererStream() throws CameraAccessException, InterruptedException { if (videoRenderer == null) return; + + // get rotation for rendered video + final PlatformChannel.DeviceOrientation lockedOrientation = + ((SensorOrientationFeature) cameraFeatures.getSensorOrientation()) + .getLockedCaptureOrientation(); + int rotation = lockedOrientation== null + ? cameraFeatures.getSensorOrientation().getDeviceOrientationManager().getVideoOrientation() + : cameraFeatures.getSensorOrientation().getDeviceOrientationManager().getVideoOrientation(lockedOrientation); + + + if(cameraProperties.getLensFacing() != initialCameraFacing){ // if we are facing the opposite way than the initial recording we need to flip 180 degrees + rotation = (rotation + 180) % 360; + } + videoRenderer.setRotation(rotation); + createCaptureSession( CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 33b123ec68b8..4574ea98ae73 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -102,7 +102,7 @@ public class VideoRenderer { public Surface getInputSurface() throws InterruptedException { synchronized (lock) { while (inputSurface == null) { - lock.wait(500); + lock.wait(); } } return inputSurface; From aea906721bec993587a82da0c24defb228062a26 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:10:30 -0600 Subject: [PATCH 019/105] VideoRenderer cleanup --- .../flutter/plugins/camera/VideoRenderer.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 4574ea98ae73..cfb025045e84 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -26,7 +26,7 @@ public class VideoRenderer { private static String TAG = "VideoRenderer"; - private String vertexShaderCode = " precision highp float;\n"+ + private final static String vertexShaderCode = " precision highp float;\n"+ " attribute vec3 vertexPosition;\n"+ " attribute vec2 uvs;\n"+ " varying vec2 varUvs;\n"+ @@ -39,7 +39,7 @@ public class VideoRenderer { " gl_Position = mvp * vec4(vertexPosition, 1.0);\n"+ " }";; - private String fragmentShaderCode = + private final static String fragmentShaderCode = " #extension GL_OES_EGL_image_external : require\n"+ " precision mediump float;\n"+ "\n"+ @@ -52,9 +52,9 @@ public class VideoRenderer { " gl_FragColor = vec4(c.r, c.g, c.b, c.a);\n"+ " }"; - private int[] textureHandles = new int[1]; + private final int[] textureHandles = new int[1]; - private float[] vertices = new float[]{ + private final float[] vertices = new float[]{ // x, y, z, u, v -1.0f, -1.0f, 0.0f, 0f, 0f, -1.0f, 1.0f, 0.0f, 0f, 1f, @@ -62,7 +62,7 @@ public class VideoRenderer { 1.0f, -1.0f, 0.0f, 1f, 0f }; - private int[] indices = new int[] + private final int[] indices = new int[] { 2, 1, 0, 0, 3, 2 @@ -70,15 +70,10 @@ public class VideoRenderer { private int program; private int vertexHandle = 0; - private int[] bufferHandles = new int[2]; + private final int[] bufferHandles = new int[2]; private int uvsHandle = 0; private int texMatrixHandle = 0; private int mvpHandle = 0; - private int samplerHandle = 0; - - private ByteBuffer vertexBuffer; - - private ByteBuffer indexBuffer ; EGLDisplay display; EGLContext context; @@ -177,13 +172,13 @@ private void configureOpenGL(){ if (!EGL14.eglMakeCurrent(display, surface, surface, context)) throw new RuntimeException("eglMakeCurrent(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); - vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4); + ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4); vertexBuffer.order(ByteOrder.nativeOrder()); vertexBuffer.asFloatBuffer().put(vertices); vertexBuffer.asFloatBuffer().position(0); - indexBuffer = ByteBuffer.allocateDirect(indices.length * 4); + ByteBuffer indexBuffer = ByteBuffer.allocateDirect(indices.length * 4); indexBuffer.order(ByteOrder.nativeOrder()); indexBuffer.asIntBuffer().put(indices); indexBuffer.position(0); @@ -202,8 +197,6 @@ private void configureOpenGL(){ uvsHandle = GLES20.glGetAttribLocation(program, "uvs"); texMatrixHandle = GLES20.glGetUniformLocation(program, "texMatrix"); mvpHandle = GLES20.glGetUniformLocation(program, "mvp"); - samplerHandle = GLES20.glGetUniformLocation(program, "texSampler"); - // Initialize buffers GLES20.glGenBuffers(2, bufferHandles, 0); From fdf5738d9fd770779505d23e80b2b1f330998279 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:13:32 -0600 Subject: [PATCH 020/105] flutter results for setDescriptionWhileRecording --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 4 ++-- .../java/io/flutter/plugins/camera/MethodCallHandlerImpl.java | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 1e4cb9270d6c..86a7eba6cf58 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1261,7 +1261,6 @@ private void prepareVideoRenderer() { } public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { - // TODO: save some camera settings stopAndReleaseCamera(); prepareVideoRenderer(); cameraProperties = properties; @@ -1273,8 +1272,9 @@ public void setDescriptionWhileRecording(@NonNull final Result result, CameraPro try { open(imageFormatGroup, true); } catch (CameraAccessException e) { - e.printStackTrace(); // TODO: throw flutter error + result.error("setDescriptionWhileRecordingFailed", e.getMessage(), null); } + result.success(null); } public void dispose() { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index b4d0ee89e0c3..ba544ef7b411 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -357,7 +357,6 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) CameraProperties cameraProperties = new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); camera.setDescriptionWhileRecording(result,cameraProperties); - result.success(null); } catch (Exception e) { handleException(e, result); } From d9862f38275e36fe894ffa655a7a330c877f33d5 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:18:44 -0600 Subject: [PATCH 021/105] error if you setDescriptionWhileRecording while device is not recording --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 86a7eba6cf58..e3d59e1c6658 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1261,6 +1261,12 @@ private void prepareVideoRenderer() { } public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { + + if(!recordingVideo){ + result.error("setDescriptionWhileRecording", "Device was not recording", null); + return; + } + stopAndReleaseCamera(); prepareVideoRenderer(); cameraProperties = properties; From a20e5d32f199504b51cd185b7abfe2b08eda752d Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:33:55 -0600 Subject: [PATCH 022/105] android tests --- .../io/flutter/plugins/camera/Camera.java | 2 +- .../io/flutter/plugins/camera/CameraTest.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index e3d59e1c6658..99b9360bd3a4 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1263,7 +1263,7 @@ private void prepareVideoRenderer() { public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { if(!recordingVideo){ - result.error("setDescriptionWhileRecording", "Device was not recording", null); + result.error("setDescriptionWhileRecordingFailed", "Device was not recording", null); return; } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 9a679017ded2..5081e1535c5b 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -602,6 +602,33 @@ public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { verify(mockResult, never()).error(any(), any(), any()); } + @Test + public void setDescriptionWhileRecording() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); + TestUtils.setPrivateField(camera, "recordingVideo", true); + TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer); + + final CameraProperties newCameraProperties = mock(CameraProperties.class); + camera.setDescriptionWhileRecording(mockResult, newCameraProperties); + + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); + } + + @Test + public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + TestUtils.setPrivateField(camera, "recordingVideo", false); + final CameraProperties newCameraProperties = mock(CameraProperties.class); + camera.setDescriptionWhileRecording(mockResult, newCameraProperties); + + verify(mockResult, times(1)).error("setDescriptionWhileRecordingFailed", "Device was not recording", null); + verify(mockResult, never()).success(any()); + } + @Test public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { From 5044d94960d7ec530ccfe6b96edfb7838dff608b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:37:21 -0600 Subject: [PATCH 023/105] integration tests --- .../example/integration_test/camera_test.dart | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 99029fcac605..6fa7ea79d175 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -56,7 +56,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); // Take Picture final XFile file = await controller.takePicture(); @@ -106,7 +106,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); // Take Video await controller.startVideoRecording(); @@ -209,6 +209,30 @@ void main() { expect(duration, lessThan(recordingTime - timePaused)); }); + testWidgets('Set description while recording', (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.length < 2) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 500)); + await controller.setDescriptionWhileRecording(cameras[1]); + sleep(const Duration(milliseconds: 500)); + + expect(controller.value.description, cameras[1]); + }); + testWidgets( 'image streaming', (WidgetTester tester) async { From 667c803616be6cce9e379594dcd3d037406192e9 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 09:39:22 -0600 Subject: [PATCH 024/105] method channel test --- .../test/android_camera_test.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 3e50e6918648..32055cd48693 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -669,6 +669,28 @@ void main() { ]); }); + test('Should set the description while recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setDescriptionWhileRecording': null}, + ); + const CameraDescription camera2Description = CameraDescription( + name: 'Test2', + lensDirection: CameraLensDirection.front, + sensorOrientation: 0); + + // Act + await camera.setDescriptionWhileRecording(camera2Description); + + // Assert + expect(channel.log, [ + isMethodCall('setDescriptionWhileRecording', arguments: { + 'cameraName': camera2Description.name, + }), + ]); + }); + test('Should set the flash mode', () async { // Arrange final MethodChannelMock channel = MethodChannelMock( From 3b45ff57373e35656d94039b9d723fdb82623df9 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 10:03:36 -0600 Subject: [PATCH 025/105] main package tests --- .../example/integration_test/camera_test.dart | 4 +-- packages/camera/camera/example/lib/main.dart | 20 ++++++++----- .../camera/lib/src/camera_controller.dart | 28 +++++++++++++------ .../camera/test/camera_preview_test.dart | 13 +++++---- .../camera/camera/test/camera_value_test.dart | 18 +++++++----- .../example/lib/camera_controller.dart | 2 +- 6 files changed, 55 insertions(+), 30 deletions(-) diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index 557f4858acab..c9158885cf1b 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -56,7 +56,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); // Take Picture final XFile file = await controller.takePicture(); @@ -105,7 +105,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); // Take Video await controller.startVideoRecording(); diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index c5804e7d0624..ca76dcd56276 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -121,7 +121,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + onNewCameraSelected(cameraController.value.description); } } // #enddocregion AppLifecycle @@ -596,12 +596,9 @@ class _CameraExampleHomeState extends State width: 90.0, child: RadioListTile( title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), - groupValue: controller?.description, + groupValue: controller?.value.description, value: cameraDescription, - onChanged: - controller != null && controller!.value.isRecordingVideo - ? null - : onChanged, + onChanged: onChanged, ), ), ); @@ -634,6 +631,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { + + // if we are currently recording then switch the camera description of the controller to flip the camera + final bool isRecording = + controller != null && controller!.value.isRecordingVideo; + if (isRecording) { + controller!.setDescriptionWhileRecording(cameraDescription); + return; + } + final CameraController? oldController = controller; if (oldController != null) { // `controller` needs to be set to null before getting disposed, @@ -768,7 +774,7 @@ class _CameraExampleHomeState extends State void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller!.description); + onNewCameraSelected(controller!.value.description); } } diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 6566e2abc883..618f73e9d4b0 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -48,6 +48,7 @@ class CameraValue { required this.exposurePointSupported, required this.focusPointSupported, required this.deviceOrientation, + required this.description, this.lockedCaptureOrientation, this.recordingOrientation, this.isPreviewPaused = false, @@ -55,7 +56,7 @@ class CameraValue { }) : _isRecordingPaused = isRecordingPaused; /// Creates a new camera controller state for an uninitialized controller. - const CameraValue.uninitialized() + const CameraValue.uninitialized(CameraDescription description) : this( isInitialized: false, isRecordingVideo: false, @@ -69,6 +70,7 @@ class CameraValue { focusPointSupported: false, deviceOrientation: DeviceOrientation.portraitUp, isPreviewPaused: false, + description: description, ); /// True after [CameraController.initialize] has completed successfully. @@ -142,6 +144,10 @@ class CameraValue { /// The orientation of the currently running video recording. final DeviceOrientation? recordingOrientation; + + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + /// Creates a modified copy of the object. /// /// Explicitly specified fields get the specified value, all other fields get @@ -164,6 +170,7 @@ class CameraValue { Optional? recordingOrientation, bool? isPreviewPaused, Optional? previewPauseOrientation, + CameraDescription? description, }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, @@ -190,6 +197,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, + description: description ?? this.description, ); } @@ -210,7 +218,8 @@ class CameraValue { 'lockedCaptureOrientation: $lockedCaptureOrientation, ' 'recordingOrientation: $recordingOrientation, ' 'isPreviewPaused: $isPreviewPaused, ' - 'previewPausedOrientation: $previewPauseOrientation)'; + 'previewPausedOrientation: $previewPauseOrientation, ' + 'description: $description)'; } } @@ -224,14 +233,11 @@ class CameraValue { class CameraController extends ValueNotifier { /// Creates a new camera controller in an uninitialized state. CameraController( - this.description, + CameraDescription description, this.resolutionPreset, { this.enableAudio = true, this.imageFormatGroup, - }) : super(const CameraValue.uninitialized()); - - /// The properties of the camera device controlled by this controller. - final CameraDescription description; + }) : super(CameraValue.uninitialized(description)); /// The resolution this controller is targeting. /// @@ -293,7 +299,7 @@ class CameraController extends ValueNotifier { }); _cameraId = await CameraPlatform.instance.createCamera( - description, + value.description, resolutionPreset, enableAudio: enableAudio, ); @@ -379,6 +385,12 @@ class CameraController extends ValueNotifier { } } + /// Sets the description while the camera is recording + Future setDescriptionWhileRecording(CameraDescription description) async { + await CameraPlatform.instance.setDescriptionWhileRecording(description); + value = value.copyWith(description: description); + } + /// Captures an image and returns the file where it was saved. /// /// Throws a [CameraException] if the capture fails. diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index bedb0ea8e01f..4b11bbdfd481 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -12,7 +12,10 @@ import 'package:quiver/core.dart'; class FakeController extends ValueNotifier implements CameraController { - FakeController() : super(const CameraValue.uninitialized()); + FakeController() : super(const CameraValue.uninitialized(fakeDescription)); + + static const CameraDescription fakeDescription = CameraDescription( + name: '', lensDirection: CameraLensDirection.back, sensorOrientation: 0); @override Future dispose() async { @@ -30,10 +33,6 @@ class FakeController extends ValueNotifier @override void debugCheckIsDisposed() {} - @override - CameraDescription get description => const CameraDescription( - name: '', lensDirection: CameraLensDirection.back, sensorOrientation: 0); - @override bool get enableAudio => false; @@ -117,6 +116,10 @@ class FakeController extends ValueNotifier @override Future resumePreview() async {} + + @override + Future setDescriptionWhileRecording(CameraDescription description) async {} + } void main() { diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index 37168dbd48d7..fe05428fa4bf 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -13,6 +13,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'camera_preview_test.dart'; + void main() { group('camera_value', () { test('Can be created', () { @@ -32,6 +34,7 @@ void main() { recordingOrientation: DeviceOrientation.portraitUp, focusPointSupported: true, previewPauseOrientation: DeviceOrientation.portraitUp, + description: FakeController.fakeDescription ); expect(cameraValue, isA()); @@ -54,7 +57,7 @@ void main() { }); test('Can be created as uninitialized', () { - const CameraValue cameraValue = CameraValue.uninitialized(); + const CameraValue cameraValue = CameraValue.uninitialized(FakeController.fakeDescription); expect(cameraValue, isA()); expect(cameraValue.isInitialized, isFalse); @@ -76,7 +79,7 @@ void main() { }); test('Can be copied with isInitialized', () { - const CameraValue cv = CameraValue.uninitialized(); + const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith(isInitialized: true); expect(cameraValue, isA()); @@ -99,7 +102,7 @@ void main() { }); test('Has aspectRatio after setting size', () { - const CameraValue cv = CameraValue.uninitialized(); + const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith(isInitialized: true, previewSize: const Size(20, 10)); @@ -107,7 +110,7 @@ void main() { }); test('hasError is true after setting errorDescription', () { - const CameraValue cv = CameraValue.uninitialized(); + const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith(errorDescription: 'error'); expect(cameraValue.hasError, isTrue); @@ -115,7 +118,7 @@ void main() { }); test('Recording paused is false when not recording', () { - const CameraValue cv = CameraValue.uninitialized(); + const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith( isInitialized: true, isRecordingVideo: false, @@ -141,10 +144,11 @@ void main() { lockedCaptureOrientation: DeviceOrientation.portraitUp, recordingOrientation: DeviceOrientation.portraitUp, isPreviewPaused: true, - previewPauseOrientation: DeviceOrientation.portraitUp); + previewPauseOrientation: DeviceOrientation.portraitUp, + description: FakeController.fakeDescription,); 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)'); + '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))'); }); }); } diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index fee553260ac4..64e27e2a75f5 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -94,7 +94,7 @@ class CameraValue { /// The orientation of the currently running video recording. final DeviceOrientation? recordingOrientation; - /// The properties of the camera device controlled by this controller. + /// The properties of the camera device controlled by this controller. final CameraDescription description; /// Creates a modified copy of the object. From 5d96ffdbb06f29413a56f27eab06943d09f44394 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 15:19:44 -0600 Subject: [PATCH 026/105] setDescriptionWhileRecording called while no video was recording test --- .../camera/lib/src/camera_controller.dart | 8 +++++++ packages/camera/camera/test/camera_test.dart | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 618f73e9d4b0..a1f54bbef1c0 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -387,6 +387,14 @@ class CameraController extends ValueNotifier { /// Sets the description while the camera is recording Future setDescriptionWhileRecording(CameraDescription description) async { + + if (!value.isRecordingVideo) { + throw CameraException( + 'Video was not being recording', + 'setDescriptionWhileRecording was called while a video was not being recorded', + ); + } + await CameraPlatform.instance.setDescriptionWhileRecording(description); value = value.copyWith(description: description); } diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 3c12648f13b9..a38becc9ef71 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -360,6 +360,30 @@ void main() { ))); }); + test( + 'setDescriptionWhileRecording() throws $CameraException when not currenctly recording', + () async { + final CameraController cameraController = CameraController( + const CameraDescription( + name: 'cam', + lensDirection: CameraLensDirection.back, + sensorOrientation: 90), + ResolutionPreset.max); + + await cameraController.initialize(); + const CameraDescription newDescription = CameraDescription( + name: 'cam2', + lensDirection: CameraLensDirection.front, + sensorOrientation: 90); + expect( + cameraController.setDescriptionWhileRecording(newDescription), + throwsA(isA().having( + (CameraException error) => error.description, + 'Video was not being recording', + 'setDescriptionWhileRecording was called while a video was not being recorded', + ))); + }); + test('getMaxZoomLevel() throws $CameraException when uninitialized', () async { final CameraController cameraController = CameraController( From aa1d0706fb3f25cb59f928e88be0f7c909e5c75b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 15:25:17 -0600 Subject: [PATCH 027/105] integration tests --- .../example/integration_test/camera_test.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index c9158885cf1b..d96ac4c3724d 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -269,6 +269,29 @@ void main() { return _completer.future; } + testWidgets('Set description while recording', (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.length < 2) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 500)); + await controller.setDescriptionWhileRecording(cameras[1]); + sleep(const Duration(milliseconds: 500)); + + expect(controller.value.description, cameras[1]); + }); + testWidgets( 'iOS image streaming with imageFormatGroup', (WidgetTester tester) async { From 858ce2e783033d212008872d0fb39cb1adcc57d9 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 15:58:52 -0600 Subject: [PATCH 028/105] dependency overrides --- packages/camera/camera/pubspec.yaml | 12 ++++++++++++ packages/camera/camera_android/example/pubspec.yaml | 6 ++++++ packages/camera/camera_android/pubspec.yaml | 6 ++++++ packages/camera/camera_android_camerax/pubspec.yaml | 6 ++++++ .../camera/camera_avfoundation/example/pubspec.yaml | 6 ++++++ packages/camera/camera_avfoundation/pubspec.yaml | 6 ++++++ packages/camera/camera_web/example/pubspec.yaml | 6 ++++++ packages/camera/camera_web/pubspec.yaml | 6 ++++++ packages/camera/camera_windows/example/pubspec.yaml | 6 ++++++ packages/camera/camera_windows/pubspec.yaml | 6 ++++++ 10 files changed, 66 insertions(+) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 5a68f6da8036..4db5f93099bb 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -38,3 +38,15 @@ dev_dependencies: mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 video_player: ^2.0.0 + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + 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 diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 5fcc8093d29e..6644cec80fe8 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -31,3 +31,9 @@ dev_dependencies: flutter: uses-material-design: true + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_android: + path: ../../../camera/camera_android diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 9f5a2f2b9c16..35ce8a400528 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -30,3 +30,9 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 9873db1a0121..cfb954c2b634 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -28,3 +28,9 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pigeon: ^3.2.6 + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 55296a6bf4db..2d4ffffd610f 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -31,3 +31,9 @@ dev_dependencies: flutter: uses-material-design: true + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_avfoundation: + path: ../../../camera/camera_avfoundation diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 4b5b67dd8943..63d1ad2c8593 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -28,3 +28,9 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 78ff61a5f883..69d76ffb8666 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -19,3 +19,9 @@ dev_dependencies: integration_test: sdk: flutter mocktail: ^0.3.0 + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_web: + path: ../../../camera/camera_web diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 527b28367eaf..be4847d9720d 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -27,3 +27,9 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index e1d63a3f81e9..f677fd33343e 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -26,3 +26,9 @@ dev_dependencies: flutter: uses-material-design: true + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../../camera/camera_platform_interface diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 403cb459a6f7..1985f8d1056d 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -27,3 +27,9 @@ dev_dependencies: async: ^2.5.0 flutter_test: sdk: flutter + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../camera/camera_platform_interface From 58bdcf6cd805cc4ac22ec2ce59abbd4cec46240c Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 16:02:13 -0600 Subject: [PATCH 029/105] update readme and version --- packages/camera/camera/CHANGELOG.md | 3 ++- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/CHANGELOG.md | 3 ++- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- packages/camera/camera_platform_interface/CHANGELOG.md | 3 ++- packages/camera/camera_platform_interface/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 3 ++- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 3 ++- packages/camera/camera_windows/pubspec.yaml | 2 +- 12 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 67b8b7970bbe..6827388d40c7 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.10.0+2 +* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. ## 0.10.0+1 diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 4db5f93099bb..519a5e1505fe 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing 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.0+1 +version: 0.10.0+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index f2417fb20645..52c35b0afe3c 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+3 + +* Allow camera to be switched while video recording. + ## 0.10.0+2 * Removes call to `join` on the camera's background `HandlerThread`. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 35ce8a400528..06cdb1839d4d 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0+2 +version: 0.10.0+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index ab33c35dd8da..96ec3045b7a7 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.8+6 +* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. ## 0.9.8+5 diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 63d1ad2c8593..c029146a27d3 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.8+5 +version: 0.9.8+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 7411db738483..72ddde15f0c8 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.1 +* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 1c874725e9ce..9cbd51b1bd2d 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ 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 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.0 +version: 2.2.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index c6254ac11d38..4111d2be24da 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.3.0+1 +* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index be4847d9720d..51a46547df23 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.3.0 +version: 0.3.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 10b08874092a..416830db5698 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.1+2 +* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. ## 0.2.1+1 diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 1985f8d1056d..9cc511cd263d 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+1 +version: 0.2.1+2 environment: sdk: ">=2.12.0 <3.0.0" From c29ed5d590e87b5b6d66bb2e8ea2bac411da276b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 23 Sep 2022 16:06:24 -0600 Subject: [PATCH 030/105] removed old TODO --- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index cfb025045e84..cf5501b1babd 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -262,7 +262,7 @@ public void run() { draw(recordingWidth,recordingHeight,surfaceTextureMatrix, inputSurfaceTexture.getTimestamp()); } } catch (InterruptedException e) { - Log.e(TAG, "thread interrupted while waiting for frames"); // TODO: + Log.d(TAG, "thread interrupted while waiting for frames"); } } @@ -320,7 +320,7 @@ public void draw(int viewportWidth,int viewportHeight , float[] texMatrix, long GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); EGLExt.eglPresentationTimeANDROID(display, surface, - uptimeMillis() * 1000000); // TODO: see https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled + uptimeMillis() * 1000000); // Not the perfect solution but works well, see https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices EGL14.eglSwapBuffers(display, surface); } } From 156ad277e35f10d18578983aaafd9501ad2b1acc Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 13:15:41 -0600 Subject: [PATCH 031/105] removed accidental dev team ID commit --- .../example/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 114ff1a24430..59f086e163fa 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -290,7 +290,7 @@ }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = W4ZTW5E78A; + DevelopmentTeam = ""; }; }; }; @@ -638,7 +638,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W4ZTW5E78A; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -660,7 +660,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W4ZTW5E78A; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", From 2dbaccb157f55ee19141322a1caa3c123c0bdcee Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 13:32:28 -0600 Subject: [PATCH 032/105] renamed local variables --- .../camera_avfoundation/ios/Classes/FLTCam.m | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 1cbace73f70b..cea77c956331 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -864,17 +864,17 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; - AVCaptureInput *_oldInput = _captureVideoInput; - AVCaptureVideoDataOutput *_oldOutput = _captureVideoOutput; - AVCaptureConnection *_oldConnection = [_oldOutput connectionWithMediaType:AVMediaTypeVideo]; + AVCaptureInput *oldInput = _captureVideoInput; + AVCaptureVideoDataOutput *oldOutput = _captureVideoOutput; + AVCaptureConnection *oldConnection = [oldOutput connectionWithMediaType:AVMediaTypeVideo]; // stop video capture from old output // this also automatically removes old output's connection from captureSession - [_oldOutput setSampleBufferDelegate:nil queue:nil]; + [oldOutput setSampleBufferDelegate:nil queue:nil]; // get new video input NSError *error = nil; - AVCaptureInput *_newInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice + AVCaptureInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:&error]; if (error) { @@ -882,45 +882,45 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName } // get new output - AVCaptureVideoDataOutput *_newOutput = [AVCaptureVideoDataOutput new]; - _newOutput.videoSettings = _oldOutput.videoSettings; - [_newOutput setAlwaysDiscardsLateVideoFrames:YES]; - [_newOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; + AVCaptureVideoDataOutput *newOutput = [AVCaptureVideoDataOutput new]; + newOutput.videoSettings = oldOutput.videoSettings; + [newOutput setAlwaysDiscardsLateVideoFrames:YES]; + [newOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; AVCaptureConnection *_newConnection = - [AVCaptureConnection connectionWithInputPorts:_newInput.ports - output:_newOutput]; + [AVCaptureConnection connectionWithInputPorts:newInput.ports + output:newOutput]; // set mirrored if needed if ([_captureDevice position] == AVCaptureDevicePositionFront) { _newConnection.videoMirrored = YES; } // keep orientation - if (_oldConnection && _newConnection.isVideoOrientationSupported) { - _newConnection.videoOrientation = _oldConnection.videoOrientation; + if (oldConnection && _newConnection.isVideoOrientationSupported) { + _newConnection.videoOrientation = oldConnection.videoOrientation; } // replace old with new in session [_videoCaptureSession beginConfiguration]; - [_videoCaptureSession removeInput:_oldInput]; - [_videoCaptureSession removeOutput:_oldOutput]; - if(![_videoCaptureSession canAddInput:_newInput]) + [_videoCaptureSession removeInput:oldInput]; + [_videoCaptureSession removeOutput:oldOutput]; + if(![_videoCaptureSession canAddInput:newInput]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video input" details:nil]; - [_videoCaptureSession addInputWithNoConnections:_newInput]; - if(![_videoCaptureSession canAddOutput:_newOutput]) + [_videoCaptureSession addInputWithNoConnections:newInput]; + if(![_videoCaptureSession canAddOutput:newOutput]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video output" details:nil]; - [_videoCaptureSession addOutputWithNoConnections:_newOutput]; + [_videoCaptureSession addOutputWithNoConnections:newOutput]; if(![_videoCaptureSession canAddConnection:_newConnection]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video connection" details:nil]; [_videoCaptureSession addConnection:_newConnection]; - _captureVideoInput = _newInput; - _captureVideoOutput = _newOutput; + _captureVideoInput = newInput; + _captureVideoOutput = newOutput; [_videoCaptureSession commitConfiguration]; [result sendSuccess]; From c17f133115e537faa6fa6fa86ca84bec7404a28f Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 13:34:29 -0600 Subject: [PATCH 033/105] use captureSessionQueue --- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index cea77c956331..d2a3684b75be 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -885,7 +885,7 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName AVCaptureVideoDataOutput *newOutput = [AVCaptureVideoDataOutput new]; newOutput.videoSettings = oldOutput.videoSettings; [newOutput setAlwaysDiscardsLateVideoFrames:YES]; - [newOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; + [newOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; AVCaptureConnection *_newConnection = [AVCaptureConnection connectionWithInputPorts:newInput.ports From bfbdc48a1f3420729d4b2a3485313eaeeb2e1794 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 13:40:56 -0600 Subject: [PATCH 034/105] fixed local variable name --- .../camera/camera_avfoundation/ios/Classes/FLTCam.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index d2a3684b75be..d1d745e39108 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -887,17 +887,17 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName [newOutput setAlwaysDiscardsLateVideoFrames:YES]; [newOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; - AVCaptureConnection *_newConnection = + AVCaptureConnection *newConnection = [AVCaptureConnection connectionWithInputPorts:newInput.ports output:newOutput]; // set mirrored if needed if ([_captureDevice position] == AVCaptureDevicePositionFront) { - _newConnection.videoMirrored = YES; + newConnection.videoMirrored = YES; } // keep orientation - if (oldConnection && _newConnection.isVideoOrientationSupported) { - _newConnection.videoOrientation = oldConnection.videoOrientation; + if (oldConnection && newConnection.isVideoOrientationSupported) { + newConnection.videoOrientation = oldConnection.videoOrientation; } // replace old with new in session @@ -914,11 +914,11 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName message:@"Unable switch video output" details:nil]; [_videoCaptureSession addOutputWithNoConnections:newOutput]; - if(![_videoCaptureSession canAddConnection:_newConnection]) + if(![_videoCaptureSession canAddConnection:newConnection]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video connection" details:nil]; - [_videoCaptureSession addConnection:_newConnection]; + [_videoCaptureSession addConnection:newConnection]; _captureVideoInput = newInput; _captureVideoOutput = newOutput; [_videoCaptureSession commitConfiguration]; From dad170f2c2f285e7a8a971e41c5c4e8b299761ec Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 14:22:51 -0600 Subject: [PATCH 035/105] setupCaptureVideoOutput function --- .../camera_avfoundation/ios/Classes/FLTCam.m | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index d1d745e39108..c18e532ac5fc 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -153,11 +153,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return nil; } - _captureVideoOutput = [AVCaptureVideoDataOutput new]; - _captureVideoOutput.videoSettings = - @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; - [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; - [_captureVideoOutput setSampleBufferDelegate:self queue:captureSessionQueue]; + [self setupCaptureVideoOutput]; AVCaptureConnection *connection = [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports @@ -185,6 +181,14 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return self; } +- (void) setupCaptureVideoOutput { + _captureVideoOutput = [AVCaptureVideoDataOutput new]; + _captureVideoOutput.videoSettings = + @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; + [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; + [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; +} + - (void)start { [_videoCaptureSession startRunning]; [_audioCaptureSession startRunning]; @@ -881,15 +885,11 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName [result sendError:error]; } - // get new output - AVCaptureVideoDataOutput *newOutput = [AVCaptureVideoDataOutput new]; - newOutput.videoSettings = oldOutput.videoSettings; - [newOutput setAlwaysDiscardsLateVideoFrames:YES]; - [newOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; + [self setupCaptureVideoOutput]; AVCaptureConnection *newConnection = [AVCaptureConnection connectionWithInputPorts:newInput.ports - output:newOutput]; + output:_captureVideoOutput]; // set mirrored if needed if ([_captureDevice position] == AVCaptureDevicePositionFront) { newConnection.videoMirrored = YES; @@ -909,18 +909,17 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName message:@"Unable switch video input" details:nil]; [_videoCaptureSession addInputWithNoConnections:newInput]; - if(![_videoCaptureSession canAddOutput:newOutput]) + if(![_videoCaptureSession canAddOutput:_captureVideoOutput]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video output" details:nil]; - [_videoCaptureSession addOutputWithNoConnections:newOutput]; + [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput]; if(![_videoCaptureSession canAddConnection:newConnection]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video connection" details:nil]; [_videoCaptureSession addConnection:newConnection]; _captureVideoInput = newInput; - _captureVideoOutput = newOutput; [_videoCaptureSession commitConfiguration]; [result sendSuccess]; From 4017924459555d5ce1ea82fce5d69eee4c2ca1ff Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 14:29:43 -0600 Subject: [PATCH 036/105] createConnectionWithInput --- .../camera_avfoundation/ios/Classes/FLTCam.m | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index c18e532ac5fc..dfd54117775d 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -154,14 +154,8 @@ - (instancetype)initWithCameraName:(NSString *)cameraName } [self setupCaptureVideoOutput]; + AVCaptureConnection *connection = [self createConnectionWithInput:_captureVideoInput]; - AVCaptureConnection *connection = - [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports - output:_captureVideoOutput]; - - if ([_captureDevice position] == AVCaptureDevicePositionFront) { - connection.videoMirrored = YES; - } [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput]; @@ -181,6 +175,17 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return self; } +- (AVCaptureConnection *) createConnectionWithInput:(AVCaptureInput *) captureVideoInput { + AVCaptureConnection *connection = + [AVCaptureConnection connectionWithInputPorts:captureVideoInput.ports + output:_captureVideoOutput]; + + if ([_captureDevice position] == AVCaptureDevicePositionFront) { + connection.videoMirrored = YES; + } + return connection; +} + - (void) setupCaptureVideoOutput { _captureVideoOutput = [AVCaptureVideoDataOutput new]; _captureVideoOutput.videoSettings = @@ -887,13 +892,7 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName [self setupCaptureVideoOutput]; - AVCaptureConnection *newConnection = - [AVCaptureConnection connectionWithInputPorts:newInput.ports - output:_captureVideoOutput]; - // set mirrored if needed - if ([_captureDevice position] == AVCaptureDevicePositionFront) { - newConnection.videoMirrored = YES; - } + AVCaptureConnection *newConnection = [self createConnectionWithInput:newInput]; // keep orientation if (oldConnection && newConnection.isVideoOrientationSupported) { From 6d4c5a3b46221c67b54ca90e4beef065d459795e Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 14:51:13 -0600 Subject: [PATCH 037/105] simplified configureConnection function to re-use code on switching camera --- .../camera_avfoundation/ios/Classes/FLTCam.m | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index dfd54117775d..5edf3bb5fbd6 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -144,18 +144,12 @@ - (instancetype)initWithCameraName:(NSString *)cameraName // https://github.com/flutter/plugins/pull/4520#discussion_r766335637 _maxStreamingPendingFramesCount = 4; - NSError *localError = nil; - _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice - error:&localError]; - - if (localError) { - *error = localError; - return nil; - } - - [self setupCaptureVideoOutput]; - AVCaptureConnection *connection = [self createConnectionWithInput:_captureVideoInput]; - + NSError *localError = nil; + AVCaptureConnection *connection = [self configureConnection:localError]; + if (localError != nil) { + *error = localError; + return nil; + } [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput]; @@ -175,23 +169,33 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return self; } -- (AVCaptureConnection *) createConnectionWithInput:(AVCaptureInput *) captureVideoInput { - AVCaptureConnection *connection = - [AVCaptureConnection connectionWithInputPorts:captureVideoInput.ports - output:_captureVideoOutput]; - - if ([_captureDevice position] == AVCaptureDevicePositionFront) { - connection.videoMirrored = YES; +- (AVCaptureConnection *) configureConnection:(NSError *)error { + + // setup input + _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice + error:&error]; + + if (error != nil) { + return nil; } - return connection; -} -- (void) setupCaptureVideoOutput { + // setup output _captureVideoOutput = [AVCaptureVideoDataOutput new]; _captureVideoOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; + + + // setup connection + AVCaptureConnection *connection = + [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports + output:_captureVideoOutput]; + if ([_captureDevice position] == AVCaptureDevicePositionFront) { + connection.videoMirrored = YES; + } + + return connection; } - (void)start { @@ -873,41 +877,34 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; - AVCaptureInput *oldInput = _captureVideoInput; - AVCaptureVideoDataOutput *oldOutput = _captureVideoOutput; - AVCaptureConnection *oldConnection = [oldOutput connectionWithMediaType:AVMediaTypeVideo]; + AVCaptureConnection *oldConnection = [_captureVideoOutput connectionWithMediaType:AVMediaTypeVideo]; // stop video capture from old output - // this also automatically removes old output's connection from captureSession - [oldOutput setSampleBufferDelegate:nil queue:nil]; + [_captureVideoOutput setSampleBufferDelegate:nil queue:nil]; - // get new video input + // remove old connections + [_videoCaptureSession beginConfiguration]; + [_videoCaptureSession removeInput:_captureVideoInput]; + [_videoCaptureSession removeOutput:_captureVideoOutput]; + NSError *error = nil; - AVCaptureInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice - error:&error]; - - if (error) { + AVCaptureConnection *newConnection = [self configureConnection:error]; + if (error != nil) { [result sendError:error]; + return; } - [self setupCaptureVideoOutput]; - - AVCaptureConnection *newConnection = [self createConnectionWithInput:newInput]; - // keep orientation if (oldConnection && newConnection.isVideoOrientationSupported) { newConnection.videoOrientation = oldConnection.videoOrientation; } - // replace old with new in session - [_videoCaptureSession beginConfiguration]; - [_videoCaptureSession removeInput:oldInput]; - [_videoCaptureSession removeOutput:oldOutput]; - if(![_videoCaptureSession canAddInput:newInput]) + // add new connections + if(![_videoCaptureSession canAddInput:_captureVideoInput]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video input" details:nil]; - [_videoCaptureSession addInputWithNoConnections:newInput]; + [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; if(![_videoCaptureSession canAddOutput:_captureVideoOutput]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video output" @@ -918,7 +915,6 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName message:@"Unable switch video connection" details:nil]; [_videoCaptureSession addConnection:newConnection]; - _captureVideoInput = newInput; [_videoCaptureSession commitConfiguration]; [result sendSuccess]; From 0ab3548a34c284d5b0c7b5a043c84ab9657b0f79 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 26 Sep 2022 15:00:31 -0600 Subject: [PATCH 038/105] formatting --- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 5edf3bb5fbd6..3f688c704ffc 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -174,7 +174,6 @@ - (AVCaptureConnection *) configureConnection:(NSError *)error { // setup input _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:&error]; - if (error != nil) { return nil; } @@ -186,7 +185,6 @@ - (AVCaptureConnection *) configureConnection:(NSError *)error { [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; - // setup connection AVCaptureConnection *connection = [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports From 28c89f8882a72ff648e1f313fc9460757e39bcc3 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 10:21:32 -0600 Subject: [PATCH 039/105] example project dependency overrides --- packages/camera/camera_android/example/pubspec.yaml | 2 ++ packages/camera/camera_avfoundation/example/pubspec.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 6644cec80fe8..5748a88e3865 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -37,3 +37,5 @@ flutter: dependency_overrides: camera_android: path: ../../../camera/camera_android + camera_platform_interface: + path: ../../../camera/camera_platform_interface diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 2d4ffffd610f..c68a2d58f3e1 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -37,3 +37,5 @@ flutter: dependency_overrides: camera_avfoundation: path: ../../../camera/camera_avfoundation + camera_platform_interface: + path: ../../../camera/camera_platform_interface From 9aa5289923af7d532e78132416d1f1a4c6740c5c Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 10:32:56 -0600 Subject: [PATCH 040/105] fixed versioning --- packages/camera/camera/CHANGELOG.md | 2 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_android/CHANGELOG.md | 2 +- packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/CHANGELOG.md | 2 +- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- packages/camera/camera_platform_interface/CHANGELOG.md | 2 +- packages/camera/camera_platform_interface/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 3 +-- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 3 +-- packages/camera/camera_windows/pubspec.yaml | 2 +- 12 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 6827388d40c7..d8b50e1c01fa 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.10.0+2 +## 0.11.0 * Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 519a5e1505fe..0527446c5879 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing 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.0+2 +version: 0.11.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 52c35b0afe3c..cd979c404bb4 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.10.0+3 +## 0.11.0 * Allow camera to be switched while video recording. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 06cdb1839d4d..06867294ae11 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0+3 +version: 0.11.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 96ec3045b7a7..47f3d6578c8f 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.9.8+6 +## 0.10.0 * Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index c029146a27d3..e58b430889c6 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.8+6 +version: 0.10.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 72ddde15f0c8..d36ef4b7ed90 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.2.1 +## 2.3.0 * Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 9cbd51b1bd2d..7ddc6d561aa4 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ 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 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.1 +version: 2.3.0 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 4111d2be24da..c6254ac11d38 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,6 +1,5 @@ -## 0.3.0+1 +## NEXT -* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 51a46547df23..be4847d9720d 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.3.0+1 +version: 0.3.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 416830db5698..10b08874092a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,6 +1,5 @@ -## 0.2.1+2 +## NEXT -* Allow camera to be switched while video recording. * Updates minimum Flutter version to 2.10. ## 0.2.1+1 diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 9cc511cd263d..1985f8d1056d 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+2 +version: 0.2.1+1 environment: sdk: ">=2.12.0 <3.0.0" From 57f5f77fe8c99c2e01f1f3b94f0f5307fb59259b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 10:37:36 -0600 Subject: [PATCH 041/105] formatting --- packages/camera/camera/example/lib/main.dart | 1 - .../camera/lib/src/camera_controller.dart | 9 +- .../camera/test/camera_preview_test.dart | 8 +- .../camera/camera/test/camera_value_test.dart | 83 ++++++++++--------- .../example/integration_test/camera_test.dart | 2 +- .../example/lib/camera_controller.dart | 7 +- .../camera_android/example/lib/main.dart | 1 - .../lib/src/android_camera.dart | 3 +- .../test/android_camera_test.dart | 9 +- .../example/integration_test/camera_test.dart | 2 +- .../example/lib/camera_controller.dart | 9 +- .../camera_avfoundation/example/lib/main.dart | 1 - .../lib/src/avfoundation_camera.dart | 3 +- .../test/avfoundation_camera_test.dart | 7 +- .../method_channel/method_channel_camera.dart | 5 +- 15 files changed, 79 insertions(+), 71 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index ca76dcd56276..522181660d5c 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -631,7 +631,6 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the camera description of the controller to flip the camera final bool isRecording = controller != null && controller!.value.isRecordingVideo; diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index a1f54bbef1c0..8d7185a6b854 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -144,7 +144,6 @@ class CameraValue { /// The orientation of the currently running video recording. final DeviceOrientation? recordingOrientation; - /// The properties of the camera device controlled by this controller. final CameraDescription description; @@ -197,7 +196,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, - description: description ?? this.description, + description: description ?? this.description, ); } @@ -218,7 +217,7 @@ class CameraValue { 'lockedCaptureOrientation: $lockedCaptureOrientation, ' 'recordingOrientation: $recordingOrientation, ' 'isPreviewPaused: $isPreviewPaused, ' - 'previewPausedOrientation: $previewPauseOrientation, ' + 'previewPausedOrientation: $previewPauseOrientation, ' 'description: $description)'; } } @@ -386,8 +385,8 @@ class CameraController extends ValueNotifier { } /// Sets the description while the camera is recording - Future setDescriptionWhileRecording(CameraDescription description) async { - + Future setDescriptionWhileRecording( + CameraDescription description) async { if (!value.isRecordingVideo) { throw CameraException( 'Video was not being recording', diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index 4b11bbdfd481..2837faee9291 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -14,7 +14,7 @@ class FakeController extends ValueNotifier implements CameraController { FakeController() : super(const CameraValue.uninitialized(fakeDescription)); - static const CameraDescription fakeDescription = CameraDescription( + static const CameraDescription fakeDescription = CameraDescription( name: '', lensDirection: CameraLensDirection.back, sensorOrientation: 0); @override @@ -116,10 +116,10 @@ class FakeController extends ValueNotifier @override Future resumePreview() async {} - - @override - Future setDescriptionWhileRecording(CameraDescription description) async {} + @override + Future setDescriptionWhileRecording( + CameraDescription description) async {} } void main() { diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index fe05428fa4bf..036b8d7f4a0f 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -19,23 +19,22 @@ void main() { group('camera_value', () { test('Can be created', () { const CameraValue cameraValue = CameraValue( - isInitialized: false, - previewSize: Size(10, 10), - isRecordingPaused: false, - isRecordingVideo: false, - isTakingPicture: false, - isStreamingImages: false, - flashMode: FlashMode.auto, - exposureMode: ExposureMode.auto, - exposurePointSupported: true, - focusMode: FocusMode.auto, - deviceOrientation: DeviceOrientation.portraitUp, - lockedCaptureOrientation: DeviceOrientation.portraitUp, - recordingOrientation: DeviceOrientation.portraitUp, - focusPointSupported: true, - previewPauseOrientation: DeviceOrientation.portraitUp, - description: FakeController.fakeDescription - ); + isInitialized: false, + previewSize: Size(10, 10), + isRecordingPaused: false, + isRecordingVideo: false, + isTakingPicture: false, + isStreamingImages: false, + flashMode: FlashMode.auto, + exposureMode: ExposureMode.auto, + exposurePointSupported: true, + focusMode: FocusMode.auto, + deviceOrientation: DeviceOrientation.portraitUp, + lockedCaptureOrientation: DeviceOrientation.portraitUp, + recordingOrientation: DeviceOrientation.portraitUp, + focusPointSupported: true, + previewPauseOrientation: DeviceOrientation.portraitUp, + description: FakeController.fakeDescription); expect(cameraValue, isA()); expect(cameraValue.isInitialized, isFalse); @@ -57,7 +56,8 @@ void main() { }); test('Can be created as uninitialized', () { - const CameraValue cameraValue = CameraValue.uninitialized(FakeController.fakeDescription); + const CameraValue cameraValue = + CameraValue.uninitialized(FakeController.fakeDescription); expect(cameraValue, isA()); expect(cameraValue.isInitialized, isFalse); @@ -79,7 +79,8 @@ void main() { }); test('Can be copied with isInitialized', () { - const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); + const CameraValue cv = + CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith(isInitialized: true); expect(cameraValue, isA()); @@ -102,7 +103,8 @@ void main() { }); test('Has aspectRatio after setting size', () { - const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); + const CameraValue cv = + CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith(isInitialized: true, previewSize: const Size(20, 10)); @@ -110,7 +112,8 @@ void main() { }); test('hasError is true after setting errorDescription', () { - const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); + const CameraValue cv = + CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith(errorDescription: 'error'); expect(cameraValue.hasError, isTrue); @@ -118,7 +121,8 @@ void main() { }); test('Recording paused is false when not recording', () { - const CameraValue cv = CameraValue.uninitialized(FakeController.fakeDescription); + const CameraValue cv = + CameraValue.uninitialized(FakeController.fakeDescription); final CameraValue cameraValue = cv.copyWith( isInitialized: true, isRecordingVideo: false, @@ -129,23 +133,24 @@ void main() { test('toString() works as expected', () { const CameraValue cameraValue = CameraValue( - isInitialized: false, - previewSize: Size(10, 10), - isRecordingPaused: false, - isRecordingVideo: false, - isTakingPicture: false, - 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, - previewPauseOrientation: DeviceOrientation.portraitUp, - description: FakeController.fakeDescription,); + isInitialized: false, + previewSize: Size(10, 10), + isRecordingPaused: false, + isRecordingVideo: false, + isTakingPicture: false, + 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, + previewPauseOrientation: DeviceOrientation.portraitUp, + description: FakeController.fakeDescription, + ); 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))'); diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 6fa7ea79d175..bc842c2e8b92 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -209,7 +209,7 @@ void main() { expect(duration, lessThan(recordingTime - timePaused)); }); - testWidgets('Set description while recording', (WidgetTester tester) async { + testWidgets('Set description while recording', (WidgetTester tester) async { final List cameras = await CameraPlatform.instance.availableCameras(); if (cameras.length < 2) { diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 64e27e2a75f5..5a2332937c90 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -141,7 +141,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, - description: description ?? this.description, + description: description ?? this.description, ); } @@ -278,8 +278,9 @@ class CameraController extends ValueNotifier { previewPauseOrientation: const Optional.absent()); } - /// Sets the description while the camera is recording - Future setDescriptionWhileRecording(CameraDescription description) async { + /// Sets the description while the camera is recording + Future setDescriptionWhileRecording( + CameraDescription description) async { await CameraPlatform.instance.setDescriptionWhileRecording(description); value = value.copyWith(description: description); } diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index 50167bd01319..bf1212eefc3a 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -637,7 +637,6 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the camera description of the controller to flip the camera final bool isRecording = controller != null && controller!.value.isRecordingVideo; diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index f35cec69d885..a390e57fd4c6 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -483,7 +483,8 @@ class AndroidCamera extends CameraPlatform { } @override - Future setDescriptionWhileRecording(CameraDescription description) async { + Future setDescriptionWhileRecording( + CameraDescription description) async { await _channel.invokeMethod( 'setDescriptionWhileRecording', { diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 32055cd48693..79ef006fe822 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -669,7 +669,7 @@ void main() { ]); }); - test('Should set the description while recording', () async { + test('Should set the description while recording', () async { // Arrange final MethodChannelMock channel = MethodChannelMock( channelName: _channelName, @@ -685,9 +685,10 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('setDescriptionWhileRecording', arguments: { - 'cameraName': camera2Description.name, - }), + isMethodCall('setDescriptionWhileRecording', + arguments: { + 'cameraName': camera2Description.name, + }), ]); }); diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index d053365f14a9..7ad6d5ccf285 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -202,7 +202,7 @@ void main() { expect(duration, lessThan(recordingTime - timePaused)); }); - testWidgets('Set description while recording', (WidgetTester tester) async { + testWidgets('Set description while recording', (WidgetTester tester) async { final List cameras = await CameraPlatform.instance.availableCameras(); if (cameras.length < 2) { diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index fee553260ac4..5a2332937c90 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -94,7 +94,7 @@ class CameraValue { /// The orientation of the currently running video recording. final DeviceOrientation? recordingOrientation; - /// The properties of the camera device controlled by this controller. + /// The properties of the camera device controlled by this controller. final CameraDescription description; /// Creates a modified copy of the object. @@ -141,7 +141,7 @@ class CameraValue { previewPauseOrientation: previewPauseOrientation == null ? this.previewPauseOrientation : previewPauseOrientation.orNull, - description: description ?? this.description, + description: description ?? this.description, ); } @@ -278,8 +278,9 @@ class CameraController extends ValueNotifier { previewPauseOrientation: const Optional.absent()); } - /// Sets the description while the camera is recording - Future setDescriptionWhileRecording(CameraDescription description) async { + /// Sets the description while the camera is recording + Future setDescriptionWhileRecording( + CameraDescription description) async { await CameraPlatform.instance.setDescriptionWhileRecording(description); value = value.copyWith(description: description); } diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index 50167bd01319..bf1212eefc3a 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -637,7 +637,6 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the camera description of the controller to flip the camera final bool isRecording = controller != null && controller!.value.isRecordingVideo; diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 106e5717b6f5..d7c67749e9db 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -488,7 +488,8 @@ class AVFoundationCamera extends CameraPlatform { } @override - Future setDescriptionWhileRecording(CameraDescription description) async { + Future setDescriptionWhileRecording( + CameraDescription description) async { await _channel.invokeMethod( 'setDescriptionWhileRecording', { diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index c87d5d542b92..b10f561fafa7 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -685,9 +685,10 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('setDescriptionWhileRecording', arguments: { - 'cameraName': camera2Description.name, - }), + isMethodCall('setDescriptionWhileRecording', + arguments: { + 'cameraName': camera2Description.name, + }), ]); }); diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 8e56b1b4c97e..403466bf3430 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -481,8 +481,9 @@ class MethodChannelCamera extends CameraPlatform { ); } - @override - Future setDescriptionWhileRecording(CameraDescription description) async { + @override + Future setDescriptionWhileRecording( + CameraDescription description) async { await _channel.invokeMethod( 'setDescriptionWhileRecording', { From ccf9e63a159c37c1f3ad02b12559c8fcf0d681b0 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 11:38:59 -0600 Subject: [PATCH 042/105] fixed some ios native tests --- .../ios/Runner.xcodeproj/project.pbxproj | 5 +++-- .../ios/Runner.xcodeproj/project.pbxproj | 6 +++--- .../example/ios/Runner/Info.plist | 2 ++ .../camera_avfoundation/ios/Classes/FLTCam.m | 17 +++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 99433b084f27..e4764a8b42cc 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -174,6 +174,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = W4ZTW5E78A; }; }; }; @@ -406,7 +407,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W4ZTW5E78A; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -428,7 +429,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W4ZTW5E78A; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 59f086e163fa..114ff1a24430 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -290,7 +290,7 @@ }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = ""; + DevelopmentTeam = W4ZTW5E78A; }; }; }; @@ -638,7 +638,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W4ZTW5E78A; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -660,7 +660,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W4ZTW5E78A; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist b/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist index ff2e341a1803..c50ce989f0c2 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist @@ -52,5 +52,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 3f688c704ffc..225eb5b4fe32 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -145,8 +145,8 @@ - (instancetype)initWithCameraName:(NSString *)cameraName _maxStreamingPendingFramesCount = 4; NSError *localError = nil; - AVCaptureConnection *connection = [self configureConnection:localError]; - if (localError != nil) { + AVCaptureConnection *connection = [self configureConnection:&localError]; + if (localError) { *error = localError; return nil; } @@ -169,13 +169,14 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return self; } -- (AVCaptureConnection *) configureConnection:(NSError *)error { +- (AVCaptureConnection *) configureConnection:(NSError **)error { // setup input _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice - error:&error]; - if (error != nil) { - return nil; + error:error]; + + if(error){ + return nil; } // setup output @@ -886,8 +887,8 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName [_videoCaptureSession removeOutput:_captureVideoOutput]; NSError *error = nil; - AVCaptureConnection *newConnection = [self configureConnection:error]; - if (error != nil) { + AVCaptureConnection *newConnection = [self configureConnection:&error]; + if (error) { [result sendError:error]; return; } From 9041121cd65ea7f440bcffa08f4e8fead8bba89e Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 11:45:16 -0600 Subject: [PATCH 043/105] fixed small bug --- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 225eb5b4fe32..5feb24897c91 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -175,7 +175,7 @@ - (AVCaptureConnection *) configureConnection:(NSError **)error { _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:error]; - if(error){ + if(*error){ return nil; } From b03a4a5ed5c57e30487fc55de52137a67400cd09 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 12:03:36 -0600 Subject: [PATCH 044/105] dont emit initialized when switching camera --- .../io/flutter/plugins/camera/Camera.java | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 99b9360bd3a4..9efa496e0eaf 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -284,13 +284,8 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { .build(); } - // default to opening as image stream - public void open(String imageFormatGroup) throws CameraAccessException { - open(imageFormatGroup, false); - } - @SuppressLint("MissingPermission") - private void open(String imageFormatGroup, boolean openAsVideo) throws CameraAccessException{ + public void open(String imageFormatGroup) throws CameraAccessException{ this.imageFormatGroup = imageFormatGroup; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); @@ -335,20 +330,19 @@ private void open(String imageFormatGroup, boolean openAsVideo) throws CameraAcc public void onOpened(@NonNull CameraDevice device) { cameraDevice = new DefaultCameraDeviceWrapper(device); try { + // already recording, since we are flipping the camera we must send it through VideoRenderer to keep correct orientation if(recordingVideo){ startPreviewWithVideoRendererStream(); }else{ startPreview(); + dartMessenger.sendCameraInitializedEvent( + resolutionFeature.getPreviewSize().getWidth(), + resolutionFeature.getPreviewSize().getHeight(), + cameraFeatures.getExposureLock().getValue(), + cameraFeatures.getAutoFocus().getValue(), + cameraFeatures.getExposurePoint().checkIsSupported(), + cameraFeatures.getFocusPoint().checkIsSupported()); } - - // TODO: do we send this ? - dartMessenger.sendCameraInitializedEvent( - resolutionFeature.getPreviewSize().getWidth(), - resolutionFeature.getPreviewSize().getHeight(), - cameraFeatures.getExposureLock().getValue(), - cameraFeatures.getAutoFocus().getValue(), - cameraFeatures.getExposurePoint().checkIsSupported(), - cameraFeatures.getFocusPoint().checkIsSupported()); } catch (CameraAccessException | InterruptedException e) { dartMessenger.sendCameraErrorEvent(e.getMessage()); close(); @@ -1276,7 +1270,7 @@ public void setDescriptionWhileRecording(@NonNull final Result result, CameraPro cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { - open(imageFormatGroup, true); + open(imageFormatGroup); } catch (CameraAccessException e) { result.error("setDescriptionWhileRecordingFailed", e.getMessage(), null); } From cfe8e208fa7d057fa7938dcef3e8f56b3b16220d Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 12:19:58 -0600 Subject: [PATCH 045/105] ios formatting --- .../ios/Classes/CameraPlugin.m | 6 +- .../camera_avfoundation/ios/Classes/FLTCam.h | 3 +- .../camera_avfoundation/ios/Classes/FLTCam.m | 220 +++++++++--------- .../ios/Classes/FLTCam_Test.h | 2 +- 4 files changed, 111 insertions(+), 120 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index bcde55efc560..726bd3a29b65 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -256,9 +256,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [_camera pausePreviewWithResult:result]; } else if ([@"resumePreview" isEqualToString:call.method]) { [_camera resumePreviewWithResult:result]; - } else if([@"setDescriptionWhileRecording" isEqualToString:call.method]) { - [_camera setDescriptionWhileRecording:(call.arguments[@"cameraName"]) result:result]; - }else { + } else if ([@"setDescriptionWhileRecording" isEqualToString:call.method]) { + [_camera setDescriptionWhileRecording:(call.arguments[@"cameraName"]) result:result]; + } else { [result sendNotImplemented]; } } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index 05143196d915..a8e71ddb2fe9 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -86,7 +86,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; - (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result; - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result; -- (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result; +- (void)setDescriptionWhileRecording:(NSString *)cameraName + result:(FLTThreadSafeFlutterResult *)result; - (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; - (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; - (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 5feb24897c91..e58d9f2c7af5 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -102,8 +102,8 @@ - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:resolutionPreset enableAudio:enableAudio orientation:orientation - videoCaptureSession:[[AVCaptureSession alloc] init] - audioCaptureSession:[[AVCaptureSession alloc] init] + videoCaptureSession:[[AVCaptureSession alloc] init] + audioCaptureSession:[[AVCaptureSession alloc] init] captureSessionQueue:captureSessionQueue error:error]; } @@ -112,7 +112,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation - videoCaptureSession:(AVCaptureSession *)videoCaptureSession + videoCaptureSession:(AVCaptureSession *)videoCaptureSession audioCaptureSession:(AVCaptureSession *)audioCaptureSession captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error { @@ -144,12 +144,12 @@ - (instancetype)initWithCameraName:(NSString *)cameraName // https://github.com/flutter/plugins/pull/4520#discussion_r766335637 _maxStreamingPendingFramesCount = 4; - NSError *localError = nil; - AVCaptureConnection *connection = [self configureConnection:&localError]; - if (localError) { - *error = localError; - return nil; - } + NSError *localError = nil; + AVCaptureConnection *connection = [self configureConnection:&localError]; + if (localError) { + *error = localError; + return nil; + } [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput]; @@ -169,42 +169,40 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return self; } -- (AVCaptureConnection *) configureConnection:(NSError **)error { - - // setup input - _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice - error:error]; - - if(*error){ - return nil; - } +- (AVCaptureConnection *)configureConnection:(NSError **)error { + // setup input + _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:error]; - // setup output - _captureVideoOutput = [AVCaptureVideoDataOutput new]; - _captureVideoOutput.videoSettings = - @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; - [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; - [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; - - // setup connection - AVCaptureConnection *connection = - [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports - output:_captureVideoOutput]; - if ([_captureDevice position] == AVCaptureDevicePositionFront) { - connection.videoMirrored = YES; - } - - return connection; + if (*error) { + return nil; + } + + // setup output + _captureVideoOutput = [AVCaptureVideoDataOutput new]; + _captureVideoOutput.videoSettings = + @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; + [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; + [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; + + // setup connection + AVCaptureConnection *connection = + [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports + output:_captureVideoOutput]; + if ([_captureDevice position] == AVCaptureDevicePositionFront) { + connection.videoMirrored = YES; + } + + return connection; } - (void)start { - [_videoCaptureSession startRunning]; - [_audioCaptureSession startRunning]; + [_videoCaptureSession startRunning]; + [_audioCaptureSession startRunning]; } - (void)stop { - [_videoCaptureSession stopRunning]; - [_audioCaptureSession stopRunning]; + [_videoCaptureSession stopRunning]; + [_audioCaptureSession stopRunning]; } - (void)setVideoFormat:(OSType)videoFormat { @@ -345,12 +343,12 @@ - (void)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset { case FLTResolutionPresetMax: case FLTResolutionPresetUltraHigh: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset3840x2160]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPreset3840x2160; + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset3840x2160; _previewSize = CGSizeMake(3840, 2160); break; } if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPresetHigh; + _videoCaptureSession.sessionPreset = AVCaptureSessionPresetHigh; _previewSize = CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, _captureDevice.activeFormat.highResolutionStillImageDimensions.height); @@ -358,31 +356,31 @@ - (void)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset { } case FLTResolutionPresetVeryHigh: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPreset1920x1080; + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset1920x1080; _previewSize = CGSizeMake(1920, 1080); break; } case FLTResolutionPresetHigh: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPreset1280x720; + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset1280x720; _previewSize = CGSizeMake(1280, 720); break; } case FLTResolutionPresetMedium: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPreset640x480; + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset640x480; _previewSize = CGSizeMake(640, 480); break; } case FLTResolutionPresetLow: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset352x288]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPreset352x288; + _videoCaptureSession.sessionPreset = AVCaptureSessionPreset352x288; _previewSize = CGSizeMake(352, 288); break; } default: if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { - _videoCaptureSession.sessionPreset = AVCaptureSessionPresetLow; + _videoCaptureSession.sessionPreset = AVCaptureSessionPresetLow; _previewSize = CGSizeMake(352, 288); } else { NSError *error = @@ -394,9 +392,8 @@ - (void)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset { }]; @throw error; } - } - _audioCaptureSession.sessionPreset = _videoCaptureSession.sessionPreset; + _audioCaptureSession.sessionPreset = _videoCaptureSession.sessionPreset; } - (void)captureOutput:(AVCaptureOutput *)output @@ -615,19 +612,19 @@ - (void)newAudioSample:(CMSampleBufferRef)sampleBuffer { } - (void)close { - [self stop]; - for (AVCaptureInput *input in [_videoCaptureSession inputs]) { - [_videoCaptureSession removeInput:input]; - } - for (AVCaptureOutput *output in [_videoCaptureSession outputs]) { - [_videoCaptureSession removeOutput:output]; - } - for (AVCaptureInput *input in [_audioCaptureSession inputs]) { - [_audioCaptureSession removeInput:input]; - } - for (AVCaptureOutput *output in [_audioCaptureSession outputs]) { - [_audioCaptureSession removeOutput:output]; - } + [self stop]; + for (AVCaptureInput *input in [_videoCaptureSession inputs]) { + [_videoCaptureSession removeInput:input]; + } + for (AVCaptureOutput *output in [_videoCaptureSession outputs]) { + [_videoCaptureSession removeOutput:output]; + } + for (AVCaptureInput *input in [_audioCaptureSession inputs]) { + [_audioCaptureSession removeInput:input]; + } + for (AVCaptureOutput *output in [_audioCaptureSession outputs]) { + [_audioCaptureSession removeOutput:output]; + } } - (void)dealloc { @@ -865,58 +862,52 @@ - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result { } - (void)setDescriptionWhileRecording:(NSString *)cameraName - result:(FLTThreadSafeFlutterResult *)result { - - if(!_isRecording){ - [result sendErrorWithCode:@"setDescriptionWhileRecording" - message:@"Device was not recording" - details:nil]; - return; - } - - _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; - - AVCaptureConnection *oldConnection = [_captureVideoOutput connectionWithMediaType:AVMediaTypeVideo]; - - // stop video capture from old output - [_captureVideoOutput setSampleBufferDelegate:nil queue:nil]; - - // remove old connections - [_videoCaptureSession beginConfiguration]; - [_videoCaptureSession removeInput:_captureVideoInput]; - [_videoCaptureSession removeOutput:_captureVideoOutput]; - - NSError *error = nil; - AVCaptureConnection *newConnection = [self configureConnection:&error]; - if (error) { - [result sendError:error]; - return; - } - - // keep orientation - if (oldConnection && newConnection.isVideoOrientationSupported) { - newConnection.videoOrientation = oldConnection.videoOrientation; - } - - // add new connections - if(![_videoCaptureSession canAddInput:_captureVideoInput]) - [result sendErrorWithCode:@"VideoError" - message:@"Unable switch video input" - details:nil]; - [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; - if(![_videoCaptureSession canAddOutput:_captureVideoOutput]) - [result sendErrorWithCode:@"VideoError" - message:@"Unable switch video output" - details:nil]; - [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput]; - if(![_videoCaptureSession canAddConnection:newConnection]) - [result sendErrorWithCode:@"VideoError" - message:@"Unable switch video connection" - details:nil]; - [_videoCaptureSession addConnection:newConnection]; - [_videoCaptureSession commitConfiguration]; - - [result sendSuccess]; + result:(FLTThreadSafeFlutterResult *)result { + if (!_isRecording) { + [result sendErrorWithCode:@"setDescriptionWhileRecording" + message:@"Device was not recording" + details:nil]; + return; + } + + _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; + + AVCaptureConnection *oldConnection = + [_captureVideoOutput connectionWithMediaType:AVMediaTypeVideo]; + + // stop video capture from old output + [_captureVideoOutput setSampleBufferDelegate:nil queue:nil]; + + // remove old connections + [_videoCaptureSession beginConfiguration]; + [_videoCaptureSession removeInput:_captureVideoInput]; + [_videoCaptureSession removeOutput:_captureVideoOutput]; + + NSError *error = nil; + AVCaptureConnection *newConnection = [self configureConnection:&error]; + if (error) { + [result sendError:error]; + return; + } + + // keep orientation + if (oldConnection && newConnection.isVideoOrientationSupported) { + newConnection.videoOrientation = oldConnection.videoOrientation; + } + + // add new connections + if (![_videoCaptureSession canAddInput:_captureVideoInput]) + [result sendErrorWithCode:@"VideoError" message:@"Unable switch video input" details:nil]; + [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; + if (![_videoCaptureSession canAddOutput:_captureVideoOutput]) + [result sendErrorWithCode:@"VideoError" message:@"Unable switch video output" details:nil]; + [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput]; + if (![_videoCaptureSession canAddConnection:newConnection]) + [result sendErrorWithCode:@"VideoError" message:@"Unable switch video connection" details:nil]; + [_videoCaptureSession addConnection:newConnection]; + [_videoCaptureSession commitConfiguration]; + + [result sendSuccess]; } - (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation @@ -1160,10 +1151,9 @@ - (BOOL)setupWriterForPath:(NSString *)path { } - (void)setUpCaptureSessionForAudio { - // dont setup twice or we will lose the audio - if(_isAudioSetup){ - return; + if (_isAudioSetup) { + return; } NSError *error = nil; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h index 3dacf425f854..bae1a563d509 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h @@ -51,7 +51,7 @@ enableAudio:(BOOL)enableAudio orientation:(UIDeviceOrientation)orientation videoCaptureSession:(AVCaptureSession *)videoCaptureSession - audioCaptureSession:(AVCaptureSession *)audioCaptureSession + audioCaptureSession:(AVCaptureSession *)audioCaptureSession captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error; From 7e338e4aec5f703b53a8599306cfb37d5081c03a Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 12:27:44 -0600 Subject: [PATCH 046/105] dependency overrides for camera/example --- packages/camera/camera/example/pubspec.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 22e275e633fe..a0fcab15786d 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -30,3 +30,8 @@ dev_dependencies: flutter: uses-material-design: true + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_platform_interface: + path: ../../../camera/camera_platform_interface \ No newline at end of file From ce6950c69dcf1e3115e5887ba8080383577ea98f Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 12:39:37 -0600 Subject: [PATCH 047/105] android formatting --- .../io/flutter/plugins/camera/Camera.java | 203 ++++--- .../plugins/camera/MethodCallHandlerImpl.java | 21 +- .../flutter/plugins/camera/VideoRenderer.java | 554 +++++++++--------- 3 files changed, 402 insertions(+), 376 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 9efa496e0eaf..e21727e9ad6e 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -116,12 +116,19 @@ class Camera * one changes. */ private CameraFeatures cameraFeatures; + private String imageFormatGroup; - /** Takes an input/output surface and orients the recording correctly. This is needed because switching cameras while recording causes bad orientation */ + /** + * Takes an input/output surface and orients the recording correctly. This is needed because + * switching cameras while recording causes bad orientation + */ private VideoRenderer videoRenderer; - /** When we flip the camera we need to know if it aligns with the initial way the camera was facing or not */ + /** + * When we flip the camera we need to know if it aligns with the initial way the camera was facing + * or not + */ private int initialCameraFacing; private final SurfaceTextureEntry flutterTexture; @@ -285,7 +292,7 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { } @SuppressLint("MissingPermission") - public void open(String imageFormatGroup) throws CameraAccessException{ + public void open(String imageFormatGroup) throws CameraAccessException { this.imageFormatGroup = imageFormatGroup; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); @@ -324,78 +331,80 @@ public void open(String imageFormatGroup) throws CameraAccessException{ // Open the camera. CameraManager cameraManager = CameraUtils.getCameraManager(activity); cameraManager.openCamera( - cameraProperties.getCameraName(), - new CameraDevice.StateCallback() { - @Override - public void onOpened(@NonNull CameraDevice device) { - cameraDevice = new DefaultCameraDeviceWrapper(device); - try { - // already recording, since we are flipping the camera we must send it through VideoRenderer to keep correct orientation - if(recordingVideo){ - startPreviewWithVideoRendererStream(); - }else{ - startPreview(); - dartMessenger.sendCameraInitializedEvent( - resolutionFeature.getPreviewSize().getWidth(), - resolutionFeature.getPreviewSize().getHeight(), - cameraFeatures.getExposureLock().getValue(), - cameraFeatures.getAutoFocus().getValue(), - cameraFeatures.getExposurePoint().checkIsSupported(), - cameraFeatures.getFocusPoint().checkIsSupported()); - } - } catch (CameraAccessException | InterruptedException e) { - dartMessenger.sendCameraErrorEvent(e.getMessage()); - close(); - } + cameraProperties.getCameraName(), + new CameraDevice.StateCallback() { + @Override + public void onOpened(@NonNull CameraDevice device) { + cameraDevice = new DefaultCameraDeviceWrapper(device); + try { + // already recording, since we are flipping the camera we must send it through + // VideoRenderer to keep correct orientation + if (recordingVideo) { + startPreviewWithVideoRendererStream(); + } else { + startPreview(); + dartMessenger.sendCameraInitializedEvent( + resolutionFeature.getPreviewSize().getWidth(), + resolutionFeature.getPreviewSize().getHeight(), + cameraFeatures.getExposureLock().getValue(), + cameraFeatures.getAutoFocus().getValue(), + cameraFeatures.getExposurePoint().checkIsSupported(), + cameraFeatures.getFocusPoint().checkIsSupported()); } + } catch (CameraAccessException | InterruptedException e) { + dartMessenger.sendCameraErrorEvent(e.getMessage()); + close(); + } + } - @Override - public void onClosed(@NonNull CameraDevice camera) { - Log.i(TAG, "open | onClosed"); - - // Prevents calls to methods that would otherwise result in IllegalStateException exceptions. - cameraDevice = null; - closeCaptureSession(); - dartMessenger.sendCameraClosingEvent(); - } + @Override + public void onClosed(@NonNull CameraDevice camera) { + Log.i(TAG, "open | onClosed"); + + // Prevents calls to methods that would otherwise result in IllegalStateException + // exceptions. + cameraDevice = null; + closeCaptureSession(); + dartMessenger.sendCameraClosingEvent(); + } - @Override - public void onDisconnected(@NonNull CameraDevice cameraDevice) { - Log.i(TAG, "open | onDisconnected"); + @Override + public void onDisconnected(@NonNull CameraDevice cameraDevice) { + Log.i(TAG, "open | onDisconnected"); - close(); - dartMessenger.sendCameraErrorEvent("The camera was disconnected."); - } + close(); + dartMessenger.sendCameraErrorEvent("The camera was disconnected."); + } - @Override - public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { - Log.i(TAG, "open | onError"); - - close(); - String errorDescription; - switch (errorCode) { - case ERROR_CAMERA_IN_USE: - errorDescription = "The camera device is in use already."; - break; - case ERROR_MAX_CAMERAS_IN_USE: - errorDescription = "Max cameras in use"; - break; - case ERROR_CAMERA_DISABLED: - errorDescription = "The camera device could not be opened due to a device policy."; - break; - case ERROR_CAMERA_DEVICE: - errorDescription = "The camera device has encountered a fatal error"; - break; - case ERROR_CAMERA_SERVICE: - errorDescription = "The camera service has encountered a fatal error."; - break; - default: - errorDescription = "Unknown camera error"; - } - dartMessenger.sendCameraErrorEvent(errorDescription); - } - }, - backgroundHandler); + @Override + public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { + Log.i(TAG, "open | onError"); + + close(); + String errorDescription; + switch (errorCode) { + case ERROR_CAMERA_IN_USE: + errorDescription = "The camera device is in use already."; + break; + case ERROR_MAX_CAMERAS_IN_USE: + errorDescription = "Max cameras in use"; + break; + case ERROR_CAMERA_DISABLED: + errorDescription = "The camera device could not be opened due to a device policy."; + break; + case ERROR_CAMERA_DEVICE: + errorDescription = "The camera device has encountered a fatal error"; + break; + case ERROR_CAMERA_SERVICE: + errorDescription = "The camera service has encountered a fatal error."; + break; + default: + errorDescription = "Unknown camera error"; + } + dartMessenger.sendCameraErrorEvent(errorDescription); + } + }, + backgroundHandler); } @VisibleForTesting @@ -770,7 +779,7 @@ public void startVideoRecording(@NonNull Result result) { recordingVideo = true; try { createCaptureSession( - CameraDevice.TEMPLATE_RECORD, () -> mediaRecorder.start(),mediaRecorder.getSurface()); + CameraDevice.TEMPLATE_RECORD, () -> mediaRecorder.start(), mediaRecorder.getSurface()); result.success(null); } catch (CameraAccessException e) { recordingVideo = false; @@ -780,7 +789,7 @@ public void startVideoRecording(@NonNull Result result) { } private void closeRenderer() { - if(videoRenderer != null){ + if (videoRenderer != null) { videoRenderer.close(); videoRenderer = null; } @@ -1092,28 +1101,36 @@ public void resumePreview() { public void startPreview() throws CameraAccessException { if (pictureImageReader == null || pictureImageReader.getSurface() == null) return; Log.i(TAG, "startPreview"); - createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); + createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); } - private void startPreviewWithVideoRendererStream() throws CameraAccessException, InterruptedException { + private void startPreviewWithVideoRendererStream() + throws CameraAccessException, InterruptedException { if (videoRenderer == null) return; // get rotation for rendered video final PlatformChannel.DeviceOrientation lockedOrientation = - ((SensorOrientationFeature) cameraFeatures.getSensorOrientation()) - .getLockedCaptureOrientation(); - int rotation = lockedOrientation== null - ? cameraFeatures.getSensorOrientation().getDeviceOrientationManager().getVideoOrientation() - : cameraFeatures.getSensorOrientation().getDeviceOrientationManager().getVideoOrientation(lockedOrientation); - - - if(cameraProperties.getLensFacing() != initialCameraFacing){ // if we are facing the opposite way than the initial recording we need to flip 180 degrees + ((SensorOrientationFeature) cameraFeatures.getSensorOrientation()) + .getLockedCaptureOrientation(); + int rotation = + lockedOrientation == null + ? cameraFeatures + .getSensorOrientation() + .getDeviceOrientationManager() + .getVideoOrientation() + : cameraFeatures + .getSensorOrientation() + .getDeviceOrientationManager() + .getVideoOrientation(lockedOrientation); + + if (cameraProperties.getLensFacing() + != initialCameraFacing) { // if we are facing the opposite way than the initial recording we + // need to flip 180 degrees rotation = (rotation + 180) % 360; } videoRenderer.setRotation(rotation); - createCaptureSession( - CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); + createCaptureSession(CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); } public void startPreviewWithImageStream(EventChannel imageStreamChannel) @@ -1248,15 +1265,19 @@ private void stopAndReleaseCamera() { } private void prepareVideoRenderer() { - if(videoRenderer != null) return; + if (videoRenderer != null) return; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); - videoRenderer = new VideoRenderer(mediaRecorder.getSurface(),resolutionFeature.getCaptureSize().getWidth(), + videoRenderer = + new VideoRenderer( + mediaRecorder.getSurface(), + resolutionFeature.getCaptureSize().getWidth(), resolutionFeature.getCaptureSize().getHeight()); } - public void setDescriptionWhileRecording(@NonNull final Result result, CameraProperties properties) { + public void setDescriptionWhileRecording( + @NonNull final Result result, CameraProperties properties) { - if(!recordingVideo){ + if (!recordingVideo) { result.error("setDescriptionWhileRecordingFailed", "Device was not recording", null); return; } @@ -1265,10 +1286,10 @@ public void setDescriptionWhileRecording(@NonNull final Result result, CameraPro prepareVideoRenderer(); cameraProperties = properties; cameraFeatures = - CameraFeatures.init( - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); + CameraFeatures.init( + cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); cameraFeatures.setAutoFocus( - cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); + cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { open(imageFormatGroup); } catch (CameraAccessException e) { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index ba544ef7b411..4ef37832812e 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -351,17 +351,18 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) result.success(null); break; } - case "setDescriptionWhileRecording" : { - try { - String cameraName = call.argument("cameraName"); - CameraProperties cameraProperties = - new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); - camera.setDescriptionWhileRecording(result,cameraProperties); - } catch (Exception e) { - handleException(e, result); + case "setDescriptionWhileRecording": + { + try { + String cameraName = call.argument("cameraName"); + CameraProperties cameraProperties = + new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); + camera.setDescriptionWhileRecording(result, cameraProperties); + } catch (Exception e) { + handleException(e, result); + } + break; } - break; - } case "dispose": { if (camera != null) { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index cf5501b1babd..c4a3eae32fc5 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -24,303 +24,307 @@ /** Renders video onto texture */ public class VideoRenderer { - private static String TAG = "VideoRenderer"; - - private final static String vertexShaderCode = " precision highp float;\n"+ - " attribute vec3 vertexPosition;\n"+ - " attribute vec2 uvs;\n"+ - " varying vec2 varUvs;\n"+ - " uniform mat4 texMatrix;\n"+ - " uniform mat4 mvp;\n"+ - "\n"+ - " void main()\n"+ - " {\n"+ - " varUvs = (texMatrix * vec4(uvs.x, uvs.y, 0, 1.0)).xy;\n"+ - " gl_Position = mvp * vec4(vertexPosition, 1.0);\n"+ - " }";; - - private final static String fragmentShaderCode = - " #extension GL_OES_EGL_image_external : require\n"+ - " precision mediump float;\n"+ - "\n"+ - " varying vec2 varUvs;\n"+ - " uniform samplerExternalOES texSampler;\n"+ - "\n"+ - " void main()\n"+ - " {\n"+ - " vec4 c = texture2D(texSampler, varUvs);\n"+ - " gl_FragColor = vec4(c.r, c.g, c.b, c.a);\n"+ - " }"; - - private final int[] textureHandles = new int[1]; - - private final float[] vertices = new float[]{ - // x, y, z, u, v - -1.0f, -1.0f, 0.0f, 0f, 0f, - -1.0f, 1.0f, 0.0f, 0f, 1f, - 1.0f, 1.0f, 0.0f, 1f, 1f, - 1.0f, -1.0f, 0.0f, 1f, 0f - }; - - private final int[] indices = new int[] - - { - 2, 1, 0, 0, 3, 2 - }; - - private int program; - private int vertexHandle = 0; - private final int[] bufferHandles = new int[2]; - private int uvsHandle = 0; - private int texMatrixHandle = 0; - private int mvpHandle = 0; - - EGLDisplay display; - EGLContext context; - EGLSurface surface; - private Thread thread; - private final Surface outputSurface; - private SurfaceTexture inputSurfaceTexture; - private Surface inputSurface; - - private HandlerThread surfaceTextureFrameAvailableHandler; - private final Object surfaceTextureAvailableFrameLock = new Object(); - private Boolean surfaceTextureFrameAvailable = false; - - private final int recordingWidth; - private final int recordingHeight; - private int rotation = 0; - - private final Object lock = new Object(); - - /** Gets surface for input. Blocks until surface is ready */ - public Surface getInputSurface() throws InterruptedException { - synchronized (lock) { - while (inputSurface == null) { - lock.wait(); - } - } - return inputSurface; - } - - public VideoRenderer(Surface outputSurface, int recordingWidth, int recordingHeight){ - this.outputSurface = outputSurface; - this.recordingHeight = recordingHeight; - this.recordingWidth = recordingWidth; - startOpenGL(); - Log.d(TAG, "VideoRenderer setup complete"); + private static String TAG = "VideoRenderer"; + + private static final String vertexShaderCode = + " precision highp float;\n" + + " attribute vec3 vertexPosition;\n" + + " attribute vec2 uvs;\n" + + " varying vec2 varUvs;\n" + + " uniform mat4 texMatrix;\n" + + " uniform mat4 mvp;\n" + + "\n" + + " void main()\n" + + " {\n" + + " varUvs = (texMatrix * vec4(uvs.x, uvs.y, 0, 1.0)).xy;\n" + + " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" + + " }"; + ; + + private static final String fragmentShaderCode = + " #extension GL_OES_EGL_image_external : require\n" + + " precision mediump float;\n" + + "\n" + + " varying vec2 varUvs;\n" + + " uniform samplerExternalOES texSampler;\n" + + "\n" + + " void main()\n" + + " {\n" + + " vec4 c = texture2D(texSampler, varUvs);\n" + + " gl_FragColor = vec4(c.r, c.g, c.b, c.a);\n" + + " }"; + + private final int[] textureHandles = new int[1]; + + private final float[] vertices = + new float[] { + // x, y, z, u, v + -1.0f, -1.0f, 0.0f, 0f, 0f, + -1.0f, 1.0f, 0.0f, 0f, 1f, + 1.0f, 1.0f, 0.0f, 1f, 1f, + 1.0f, -1.0f, 0.0f, 1f, 0f + }; + + private final int[] indices = new int[] {2, 1, 0, 0, 3, 2}; + + private int program; + private int vertexHandle = 0; + private final int[] bufferHandles = new int[2]; + private int uvsHandle = 0; + private int texMatrixHandle = 0; + private int mvpHandle = 0; + + EGLDisplay display; + EGLContext context; + EGLSurface surface; + private Thread thread; + private final Surface outputSurface; + private SurfaceTexture inputSurfaceTexture; + private Surface inputSurface; + + private HandlerThread surfaceTextureFrameAvailableHandler; + private final Object surfaceTextureAvailableFrameLock = new Object(); + private Boolean surfaceTextureFrameAvailable = false; + + private final int recordingWidth; + private final int recordingHeight; + private int rotation = 0; + + private final Object lock = new Object(); + + /** Gets surface for input. Blocks until surface is ready */ + public Surface getInputSurface() throws InterruptedException { + synchronized (lock) { + while (inputSurface == null) { + lock.wait(); + } } - - /** Stop rendering and cleanup resources*/ - public void close(){ - thread.interrupt(); - surfaceTextureFrameAvailableHandler.quitSafely(); - inputSurfaceTexture.release(); + return inputSurface; + } + + public VideoRenderer(Surface outputSurface, int recordingWidth, int recordingHeight) { + this.outputSurface = outputSurface; + this.recordingHeight = recordingHeight; + this.recordingWidth = recordingWidth; + startOpenGL(); + Log.d(TAG, "VideoRenderer setup complete"); + } + + /** Stop rendering and cleanup resources */ + public void close() { + thread.interrupt(); + surfaceTextureFrameAvailableHandler.quitSafely(); + inputSurfaceTexture.release(); + } + + /** Configures openGL. must be called in same thread as draw is called */ + private void configureOpenGL() { + synchronized (lock) { + display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (display == EGL14.EGL_NO_DISPLAY) + throw new RuntimeException( + "eglDisplay == EGL14.EGL_NO_DISPLAY: " + + GLUtils.getEGLErrorString(EGL14.eglGetError())); + + int[] version = new int[2]; + if (!EGL14.eglInitialize(display, version, 0, version, 1)) + throw new RuntimeException( + "eglInitialize(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); + + int[] attribList = + new int[] { + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, + EGLExt.EGL_RECORDABLE_ANDROID, 1, + EGL14.EGL_NONE + }; + + EGLConfig[] configs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + if (!EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.length, numConfigs, 0)) + throw new RuntimeException(GLUtils.getEGLErrorString(EGL14.eglGetError())); + + int err = EGL14.eglGetError(); + if (err != EGL14.EGL_SUCCESS) throw new RuntimeException(GLUtils.getEGLErrorString(err)); + + int[] ctxAttribs = new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE}; + context = EGL14.eglCreateContext(display, configs[0], EGL14.EGL_NO_CONTEXT, ctxAttribs, 0); + + err = EGL14.eglGetError(); + if (err != EGL14.EGL_SUCCESS) throw new RuntimeException(GLUtils.getEGLErrorString(err)); + + int[] surfaceAttribs = new int[] {EGL14.EGL_NONE}; + + surface = EGL14.eglCreateWindowSurface(display, configs[0], outputSurface, surfaceAttribs, 0); + + err = EGL14.eglGetError(); + if (err != EGL14.EGL_SUCCESS) throw new RuntimeException(GLUtils.getEGLErrorString(err)); + + if (!EGL14.eglMakeCurrent(display, surface, surface, context)) + throw new RuntimeException( + "eglMakeCurrent(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); + + ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4); + vertexBuffer.order(ByteOrder.nativeOrder()); + vertexBuffer.asFloatBuffer().put(vertices); + vertexBuffer.asFloatBuffer().position(0); + + ByteBuffer indexBuffer = ByteBuffer.allocateDirect(indices.length * 4); + indexBuffer.order(ByteOrder.nativeOrder()); + indexBuffer.asIntBuffer().put(indices); + indexBuffer.position(0); + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + program = GLES20.glCreateProgram(); + + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glLinkProgram(program); + + vertexHandle = GLES20.glGetAttribLocation(program, "vertexPosition"); + uvsHandle = GLES20.glGetAttribLocation(program, "uvs"); + texMatrixHandle = GLES20.glGetUniformLocation(program, "texMatrix"); + mvpHandle = GLES20.glGetUniformLocation(program, "mvp"); + + // Initialize buffers + GLES20.glGenBuffers(2, bufferHandles, 0); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]); + GLES20.glBufferData( + GLES20.GL_ARRAY_BUFFER, vertices.length * 4, vertexBuffer, GLES20.GL_DYNAMIC_DRAW); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]); + GLES20.glBufferData( + GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.length * 4, indexBuffer, GLES20.GL_DYNAMIC_DRAW); + + // Init texture that will receive decoded frames + GLES20.glGenTextures(1, textureHandles, 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureHandles[0]); + + inputSurfaceTexture = new SurfaceTexture(getTexId()); + inputSurfaceTexture.setDefaultBufferSize(recordingWidth, recordingHeight); + surfaceTextureFrameAvailableHandler = new HandlerThread("FrameHandlerThread"); + surfaceTextureFrameAvailableHandler.start(); + inputSurface = new Surface(inputSurfaceTexture); + + inputSurfaceTexture.setOnFrameAvailableListener( + new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + synchronized (surfaceTextureAvailableFrameLock) { + if (surfaceTextureFrameAvailable) + Log.w(TAG, "Frame available before processing other frames. dropping frames"); + surfaceTextureFrameAvailable = true; + surfaceTextureAvailableFrameLock.notifyAll(); + } + } + }, + new Handler(surfaceTextureFrameAvailableHandler.getLooper())); + lock.notifyAll(); } - - /** Configures openGL. must be called in same thread as draw is called*/ - private void configureOpenGL(){ - synchronized (lock) { - display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); - if (display == EGL14.EGL_NO_DISPLAY) - throw new RuntimeException("eglDisplay == EGL14.EGL_NO_DISPLAY: " - + GLUtils.getEGLErrorString(EGL14.eglGetError())); - - int[] version = new int[2]; - if (!EGL14.eglInitialize(display, version, 0, version, 1)) - throw new RuntimeException("eglInitialize(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); - - int[] attribList = new int[]{ - EGL14.EGL_RED_SIZE, 8, - EGL14.EGL_GREEN_SIZE, 8, - EGL14.EGL_BLUE_SIZE, 8, - EGL14.EGL_ALPHA_SIZE, 8, - EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, - EGLExt.EGL_RECORDABLE_ANDROID, 1, - EGL14.EGL_NONE - }; - - EGLConfig[] configs = new EGLConfig[1]; - int[] numConfigs = new int[1]; - if (!EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.length, numConfigs, 0)) - throw new RuntimeException(GLUtils.getEGLErrorString(EGL14.eglGetError())); - - int err = EGL14.eglGetError(); - if (err != EGL14.EGL_SUCCESS) - throw new RuntimeException(GLUtils.getEGLErrorString(err)); - - int[] ctxAttribs = new int[]{ - EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, - EGL14.EGL_NONE - }; - context = EGL14.eglCreateContext(display, configs[0], EGL14.EGL_NO_CONTEXT, ctxAttribs, 0); - - err = EGL14.eglGetError(); - if (err != EGL14.EGL_SUCCESS) - throw new RuntimeException(GLUtils.getEGLErrorString(err)); - - int[] surfaceAttribs = new int[]{ - EGL14.EGL_NONE - }; - - surface = EGL14.eglCreateWindowSurface(display, configs[0], outputSurface, surfaceAttribs, 0); - - err = EGL14.eglGetError(); - if (err != EGL14.EGL_SUCCESS) - throw new RuntimeException(GLUtils.getEGLErrorString(err)); - - if (!EGL14.eglMakeCurrent(display, surface, surface, context)) - throw new RuntimeException("eglMakeCurrent(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); - - ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4); - vertexBuffer.order(ByteOrder.nativeOrder()); - vertexBuffer.asFloatBuffer().put(vertices); - vertexBuffer.asFloatBuffer().position(0); - - - ByteBuffer indexBuffer = ByteBuffer.allocateDirect(indices.length * 4); - indexBuffer.order(ByteOrder.nativeOrder()); - indexBuffer.asIntBuffer().put(indices); - indexBuffer.position(0); - - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); - int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); - - - program = GLES20.glCreateProgram(); - - GLES20.glAttachShader(program, vertexShader); - GLES20.glAttachShader(program, fragmentShader); - GLES20.glLinkProgram(program); - - vertexHandle = GLES20.glGetAttribLocation(program, "vertexPosition"); - uvsHandle = GLES20.glGetAttribLocation(program, "uvs"); - texMatrixHandle = GLES20.glGetUniformLocation(program, "texMatrix"); - mvpHandle = GLES20.glGetUniformLocation(program, "mvp"); - - // Initialize buffers - GLES20.glGenBuffers(2, bufferHandles, 0); - - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]); - GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.length * 4, vertexBuffer, GLES20.GL_DYNAMIC_DRAW); - - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]); - GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.length * 4, indexBuffer, GLES20.GL_DYNAMIC_DRAW); - - // Init texture that will receive decoded frames - GLES20.glGenTextures(1, textureHandles, 0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureHandles[0]); - - inputSurfaceTexture = new SurfaceTexture(getTexId()); - inputSurfaceTexture.setDefaultBufferSize(recordingWidth, recordingHeight); - surfaceTextureFrameAvailableHandler = new HandlerThread("FrameHandlerThread"); - surfaceTextureFrameAvailableHandler.start(); - inputSurface = new Surface(inputSurfaceTexture); - - inputSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - synchronized (surfaceTextureAvailableFrameLock) { - if (surfaceTextureFrameAvailable) - Log.w(TAG, "Frame available before processing other frames. dropping frames"); - surfaceTextureFrameAvailable = true; - surfaceTextureAvailableFrameLock.notifyAll(); - } - + } + + /** Starts and configures Video Renderer */ + private void startOpenGL() { + Log.d(TAG, "Starting OpenGL Thread"); + thread = + new Thread() { + @Override + public void run() { + + configureOpenGL(); + + try { + // continuously pull frames from input surface texture and use videoRenderer to modify + // to correct rotation + while (!Thread.interrupted()) { + + synchronized (surfaceTextureAvailableFrameLock) { + while (!surfaceTextureFrameAvailable) { + surfaceTextureAvailableFrameLock.wait(500); + } + surfaceTextureFrameAvailable = false; } - }, new Handler(surfaceTextureFrameAvailableHandler.getLooper())); - lock.notifyAll(); - } - - } - - /** Starts and configures Video Renderer */ - private void startOpenGL() { - Log.d(TAG, "Starting OpenGL Thread"); - thread = new Thread() { - @Override - public void run() { - configureOpenGL(); + inputSurfaceTexture.updateTexImage(); - try { - // continuously pull frames from input surface texture and use videoRenderer to modify to correct rotation - while(!Thread.interrupted()) { - - synchronized(surfaceTextureAvailableFrameLock) { - while (!surfaceTextureFrameAvailable) { - surfaceTextureAvailableFrameLock.wait(500); - } - surfaceTextureFrameAvailable = false; - } - - inputSurfaceTexture.updateTexImage(); - - float[] surfaceTextureMatrix = new float[16]; - inputSurfaceTexture.getTransformMatrix(surfaceTextureMatrix); - - draw(recordingWidth,recordingHeight,surfaceTextureMatrix, inputSurfaceTexture.getTimestamp()); - } - } catch (InterruptedException e) { - Log.d(TAG, "thread interrupted while waiting for frames"); - } + float[] surfaceTextureMatrix = new float[16]; + inputSurfaceTexture.getTransformMatrix(surfaceTextureMatrix); + draw( + recordingWidth, + recordingHeight, + surfaceTextureMatrix, + inputSurfaceTexture.getTimestamp()); + } + } catch (InterruptedException e) { + Log.d(TAG, "thread interrupted while waiting for frames"); } + } }; - thread.start(); - } + thread.start(); + } - public int getTexId(){ - return textureHandles[0]; - } + public int getTexId() { + return textureHandles[0]; + } - public float[] moveMatrix (){ - float[] m = new float[16]; - Matrix.setIdentityM(m, 0); - Matrix.rotateM(m, 0, rotation, 0, 0, 1); - return m; - } + public float[] moveMatrix() { + float[] m = new float[16]; + Matrix.setIdentityM(m, 0); + Matrix.rotateM(m, 0, rotation, 0, 0, 1); + return m; + } - public void setRotation(int rotation){ - this.rotation = rotation; - } + public void setRotation(int rotation) { + this.rotation = rotation; + } - private int loadShader(int type, String code) { + private int loadShader(int type, String code) { - int shader = GLES20.glCreateShader(type); + int shader = GLES20.glCreateShader(type); - GLES20.glShaderSource(shader, code); - GLES20.glCompileShader(shader); - return shader; - } + GLES20.glShaderSource(shader, code); + GLES20.glCompileShader(shader); + return shader; + } - public void draw(int viewportWidth,int viewportHeight , float[] texMatrix, long timestamp ) { + public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long timestamp) { - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); - GLES20.glClearColor(0f, 0f, 0f, 0f); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + GLES20.glClearColor(0f, 0f, 0f, 0f); - GLES20.glViewport(0, 0, viewportWidth, viewportHeight); + GLES20.glViewport(0, 0, viewportWidth, viewportHeight); - GLES20.glUseProgram(program); + GLES20.glUseProgram(program); - // Pass transformations to shader - GLES20.glUniformMatrix4fv(texMatrixHandle, 1, false, texMatrix, 0); - GLES20.glUniformMatrix4fv(mvpHandle, 1, false, moveMatrix(), 0); + // Pass transformations to shader + GLES20.glUniformMatrix4fv(texMatrixHandle, 1, false, texMatrix, 0); + GLES20.glUniformMatrix4fv(mvpHandle, 1, false, moveMatrix(), 0); - // Prepare buffers with vertices and indices & draw - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]); - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]); + // Prepare buffers with vertices and indices & draw + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]); - GLES20.glEnableVertexAttribArray(vertexHandle); - GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 4 * 5, 0); + GLES20.glEnableVertexAttribArray(vertexHandle); + GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 4 * 5, 0); - GLES20.glEnableVertexAttribArray(uvsHandle); - GLES20.glVertexAttribPointer(uvsHandle, 2, GLES20.GL_FLOAT, false, 4 * 5, 3 * 4); + GLES20.glEnableVertexAttribArray(uvsHandle); + GLES20.glVertexAttribPointer(uvsHandle, 2, GLES20.GL_FLOAT, false, 4 * 5, 3 * 4); - GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); + GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); - EGLExt.eglPresentationTimeANDROID(display, surface, - uptimeMillis() * 1000000); // Not the perfect solution but works well, see https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices - EGL14.eglSwapBuffers(display, surface); - } + EGLExt.eglPresentationTimeANDROID( + display, + surface, + uptimeMillis() + * 1000000); // Not the perfect solution but works well, see + // https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices + EGL14.eglSwapBuffers(display, surface); + } } From 563329eb85206eaa8c4a2dbc181ee9f733c4da34 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 12:48:49 -0600 Subject: [PATCH 048/105] ios test formatted --- .../example/ios/RunnerTests/CameraTestUtils.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m index 37519037176a..b42aa34e2a17 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -14,7 +14,7 @@ id videoSessionMock = OCMClassMock([AVCaptureSession class]); OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - + id audioSessionMock = OCMClassMock([AVCaptureSession class]); OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); @@ -23,7 +23,7 @@ resolutionPreset:@"medium" enableAudio:true orientation:UIDeviceOrientationPortrait - videoCaptureSession:videoSessionMock + videoCaptureSession:videoSessionMock audioCaptureSession:audioSessionMock captureSessionQueue:captureSessionQueue error:nil]; From 9a32c1ace56239eca535922fc8b0a65607056ed3 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 12:50:02 -0600 Subject: [PATCH 049/105] android tests formatted --- .../test/java/io/flutter/plugins/camera/CameraTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 5081e1535c5b..97301a51b231 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -611,7 +611,7 @@ public void setDescriptionWhileRecording() { TestUtils.setPrivateField(camera, "recordingVideo", true); TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer); - final CameraProperties newCameraProperties = mock(CameraProperties.class); + final CameraProperties newCameraProperties = mock(CameraProperties.class); camera.setDescriptionWhileRecording(mockResult, newCameraProperties); verify(mockResult, times(1)).success(null); @@ -622,10 +622,11 @@ public void setDescriptionWhileRecording() { public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { MethodChannel.Result mockResult = mock(MethodChannel.Result.class); TestUtils.setPrivateField(camera, "recordingVideo", false); - final CameraProperties newCameraProperties = mock(CameraProperties.class); + final CameraProperties newCameraProperties = mock(CameraProperties.class); camera.setDescriptionWhileRecording(mockResult, newCameraProperties); - verify(mockResult, times(1)).error("setDescriptionWhileRecordingFailed", "Device was not recording", null); + verify(mockResult, times(1)) + .error("setDescriptionWhileRecordingFailed", "Device was not recording", null); verify(mockResult, never()).success(any()); } From eb471130f0e832234fa0c9e1e886f5e30a8065ba Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 13:01:38 -0600 Subject: [PATCH 050/105] android format that I missed --- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index c4a3eae32fc5..712b6b3ba31e 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -320,11 +320,8 @@ public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); EGLExt.eglPresentationTimeANDROID( - display, - surface, - uptimeMillis() - * 1000000); // Not the perfect solution but works well, see - // https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices + display, surface, uptimeMillis() * 1000000); // Not the perfect solution but works well, see + // https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices EGL14.eglSwapBuffers(display, surface); } } From f01f63f0058949c489ac99cdd9b0e76ca02929c2 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 13:27:22 -0600 Subject: [PATCH 051/105] other android formatting --- .../java/io/flutter/plugins/camera/VideoRenderer.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 712b6b3ba31e..921f18925054 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -39,7 +39,6 @@ public class VideoRenderer { + " varUvs = (texMatrix * vec4(uvs.x, uvs.y, 0, 1.0)).xy;\n" + " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" + " }"; - ; private static final String fragmentShaderCode = " #extension GL_OES_EGL_image_external : require\n" @@ -58,11 +57,8 @@ public class VideoRenderer { private final float[] vertices = new float[] { - // x, y, z, u, v - -1.0f, -1.0f, 0.0f, 0f, 0f, - -1.0f, 1.0f, 0.0f, 0f, 1f, - 1.0f, 1.0f, 0.0f, 1f, 1f, - 1.0f, -1.0f, 0.0f, 1f, 0f + -1.0f, -1.0f, 0.0f, 0f, 0f, -1.0f, 1.0f, 0.0f, 0f, 1f, 1.0f, 1.0f, 0.0f, 1f, 1f, 1.0f, + -1.0f, 0.0f, 1f, 0f }; private final int[] indices = new int[] {2, 1, 0, 0, 3, 2}; From 4bdac9d6214235e92fcc775f85b54ae5bfa82b16 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 13:32:47 -0600 Subject: [PATCH 052/105] final formatting with flutter tool --- .../android/src/main/java/io/flutter/plugins/camera/Camera.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index e21727e9ad6e..03166a32eec1 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1125,7 +1125,7 @@ private void startPreviewWithVideoRendererStream() if (cameraProperties.getLensFacing() != initialCameraFacing) { // if we are facing the opposite way than the initial recording we - // need to flip 180 degrees + // need to flip 180 degrees rotation = (rotation + 180) % 360; } videoRenderer.setRotation(rotation); From 68d8c8fab7314d6cf0f9c8df04d8ecd964348072 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 13:43:44 -0600 Subject: [PATCH 053/105] formatted android again --- .../src/main/java/io/flutter/plugins/camera/VideoRenderer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 921f18925054..1b0f93c08e80 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -17,7 +17,6 @@ import android.os.HandlerThread; import android.util.Log; import android.view.Surface; - import java.nio.ByteBuffer; import java.nio.ByteOrder; From a001e0278ca596e1a1804069a7a044dac220a9bc Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 27 Sep 2022 13:51:01 -0600 Subject: [PATCH 054/105] android license in new file --- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 1b0f93c08e80..8ac1d07fd4bd 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -1,3 +1,7 @@ +// 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 android.os.SystemClock.uptimeMillis; @@ -316,7 +320,7 @@ public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long EGLExt.eglPresentationTimeANDROID( display, surface, uptimeMillis() * 1000000); // Not the perfect solution but works well, see - // https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices + // https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices and see if the timestamp can be incorporated EGL14.eglSwapBuffers(display, surface); } } From 322bdf5486f05d217e7680a7b816b6eb60dff4cd Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 28 Sep 2022 09:49:28 -0600 Subject: [PATCH 055/105] update-excerpts ran --- packages/camera/camera/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 4d7c3d90791a..db2e41b4f4cd 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -75,7 +75,7 @@ void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + onNewCameraSelected(cameraController.value.description); } } ``` From d9aaba05dc639acd81a9aaddd8c73c4b69a815ae Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 29 Sep 2022 13:57:03 -0600 Subject: [PATCH 056/105] fixed changelog --- packages/camera/camera/CHANGELOG.md | 2 +- packages/camera/camera_android/CHANGELOG.md | 2 +- packages/camera/camera_avfoundation/CHANGELOG.md | 2 +- packages/camera/camera_platform_interface/CHANGELOG.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index bc2c9eab63c6..740a419e34b6 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.11.0 -* Allow camera to be switched while video recording. +* Allows camera to be switched while video recording. ## 0.10.0+2 diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index cd979c404bb4..cc1241d7d565 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.11.0 -* Allow camera to be switched while video recording. +* Allows camera to be switched while video recording. ## 0.10.0+2 diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 47f3d6578c8f..233b5cb93aa4 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.10.0 -* Allow camera to be switched while video recording. +* Allows camera to be switched while video recording. * Updates minimum Flutter version to 2.10. ## 0.9.8+5 diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index ccc819597f15..37cb562c636b 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.3.0 -* Allow camera to be switched while video recording. +* Allows camera to be switched while video recording. ## 2.2.1 From a9bb9f866507ffc524595612a2b17860ebff90ff Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 29 Sep 2022 13:57:47 -0600 Subject: [PATCH 057/105] removed development team --- .../camera/example/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- .../example/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index e4764a8b42cc..d20263b65a6e 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -174,7 +174,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = W4ZTW5E78A; + DevelopmentTeam = ""; }; }; }; @@ -407,7 +407,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W4ZTW5E78A; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -429,7 +429,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W4ZTW5E78A; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 114ff1a24430..59f086e163fa 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -290,7 +290,7 @@ }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = W4ZTW5E78A; + DevelopmentTeam = ""; }; }; }; @@ -638,7 +638,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W4ZTW5E78A; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -660,7 +660,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W4ZTW5E78A; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", From 9e40600d572de4721d2b054def7ab2819dea9349 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 29 Sep 2022 14:02:47 -0600 Subject: [PATCH 058/105] renames configureConnection to createConnection --- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index e58d9f2c7af5..ab94eb97ad80 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -145,7 +145,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName _maxStreamingPendingFramesCount = 4; NSError *localError = nil; - AVCaptureConnection *connection = [self configureConnection:&localError]; + AVCaptureConnection *connection = [self createConnection:&localError]; if (localError) { *error = localError; return nil; @@ -169,7 +169,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName return self; } -- (AVCaptureConnection *)configureConnection:(NSError **)error { +- (AVCaptureConnection *)createConnection:(NSError **)error { // setup input _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:error]; @@ -884,7 +884,7 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName [_videoCaptureSession removeOutput:_captureVideoOutput]; NSError *error = nil; - AVCaptureConnection *newConnection = [self configureConnection:&error]; + AVCaptureConnection *newConnection = [self createConnection:&error]; if (error) { [result sendError:error]; return; From 5a55b7a2e02a7c62c7638524a11966414ebaeebb Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 3 Oct 2022 07:22:34 -0600 Subject: [PATCH 059/105] renames unimplemented error message --- .../lib/src/platform_interface/camera_platform.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index 4053420ad5b0..b4e461be1679 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -260,7 +260,7 @@ abstract class CameraPlatform extends PlatformInterface { /// Set the active camera while recording Future setDescriptionWhileRecording(CameraDescription description) { - throw UnimplementedError('setDescription() is not implemented.'); + throw UnimplementedError('setDescriptionWhileRecording() is not implemented.'); } /// Returns a widget showing a live camera preview. From ad5d62f101678bdb3a317d423ce79df0fdf9a646 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 3 Oct 2022 07:29:19 -0600 Subject: [PATCH 060/105] renames setDescriptionWhileRecording error to match android and the other errors --- packages/camera/camera_avfoundation/ios/Classes/FLTCam.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index ab94eb97ad80..5fdecaddd174 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -864,7 +864,7 @@ - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result { - (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FLTThreadSafeFlutterResult *)result { if (!_isRecording) { - [result sendErrorWithCode:@"setDescriptionWhileRecording" + [result sendErrorWithCode:@"setDescriptionWhileRecordingFailed" message:@"Device was not recording" details:nil]; return; From cb368492a9dddd40cf07fbee75298f7a67c9133b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 3 Oct 2022 07:39:16 -0600 Subject: [PATCH 061/105] fixes formatting --- .../lib/src/platform_interface/camera_platform.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index b4e461be1679..0a398a2f03e2 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -260,7 +260,8 @@ abstract class CameraPlatform extends PlatformInterface { /// Set the active camera while recording Future setDescriptionWhileRecording(CameraDescription description) { - throw UnimplementedError('setDescriptionWhileRecording() is not implemented.'); + throw UnimplementedError( + 'setDescriptionWhileRecording() is not implemented.'); } /// Returns a widget showing a live camera preview. From 94c0be3db6e5dea180612047362a40b778075b79 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 3 Oct 2022 14:24:56 -0600 Subject: [PATCH 062/105] removes override dependencies from camera_web and camera_windows --- packages/camera/camera_web/example/pubspec.yaml | 6 ------ packages/camera/camera_web/pubspec.yaml | 6 ------ packages/camera/camera_windows/example/pubspec.yaml | 6 ------ packages/camera/camera_windows/pubspec.yaml | 6 ------ 4 files changed, 24 deletions(-) diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index a7e2db29f1b5..e82bbe392ceb 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -22,9 +22,3 @@ dev_dependencies: integration_test: sdk: flutter mocktail: ^0.3.0 - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_web: - path: ../../../camera/camera_web diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 51a46547df23..ef9c45c71796 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -27,9 +27,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index b3477b801784..80ce958a0e84 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -27,9 +27,3 @@ dev_dependencies: flutter: uses-material-design: true - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../../camera/camera_platform_interface diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 9cc511cd263d..1eab9fa108ef 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -27,9 +27,3 @@ dev_dependencies: async: ^2.5.0 flutter_test: sdk: flutter - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../camera/camera_platform_interface From f8824ed5e526fa043dbe55eda8da313a28a9a5be Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 3 Oct 2022 14:39:30 -0600 Subject: [PATCH 063/105] removes camera_web override dependency in camera package --- packages/camera/camera/pubspec.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 35aedc151ded..068763938a1e 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -49,5 +49,3 @@ dependency_overrides: path: ../../camera/camera_avfoundation camera_platform_interface: path: ../../camera/camera_platform_interface - camera_web: - path: ../../camera/camera_web From 5d3bc8a6b3a73be79fb9accd2f9439dd1f225220 Mon Sep 17 00:00:00 2001 From: Braden Bagby <33461698+BradenBagby@users.noreply.github.com> Date: Thu, 20 Oct 2022 11:25:04 -0600 Subject: [PATCH 064/105] Update packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java Co-authored-by: Camille Simon <43054281+camsim99@users.noreply.github.com> --- .../android/src/main/java/io/flutter/plugins/camera/Camera.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 03166a32eec1..78d13b2ed5f1 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -121,7 +121,7 @@ class Camera /** * Takes an input/output surface and orients the recording correctly. This is needed because - * switching cameras while recording causes bad orientation + * switching cameras while recording causes the wrong orientation. */ private VideoRenderer videoRenderer; From 54e062d068eb1cbd3ccf0f9c51ba12de202b3562 Mon Sep 17 00:00:00 2001 From: Braden Bagby <33461698+BradenBagby@users.noreply.github.com> Date: Thu, 20 Oct 2022 18:23:04 -0600 Subject: [PATCH 065/105] Update packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java Co-authored-by: Camille Simon <43054281+camsim99@users.noreply.github.com> --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 78d13b2ed5f1..a438f0757aab 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -126,8 +126,8 @@ class Camera private VideoRenderer videoRenderer; /** - * When we flip the camera we need to know if it aligns with the initial way the camera was facing - * or not + * Whether or not the camera aligns with the initial way the camera was facing + * if the camera was flipped. */ private int initialCameraFacing; From ca2880ffd597a5246cd4fae966674245b3c36644 Mon Sep 17 00:00:00 2001 From: Braden Bagby <33461698+BradenBagby@users.noreply.github.com> Date: Thu, 20 Oct 2022 18:23:32 -0600 Subject: [PATCH 066/105] Update packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java Co-authored-by: Camille Simon <43054281+camsim99@users.noreply.github.com> --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index a438f0757aab..9a7b0f174f8f 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -337,8 +337,8 @@ public void open(String imageFormatGroup) throws CameraAccessException { public void onOpened(@NonNull CameraDevice device) { cameraDevice = new DefaultCameraDeviceWrapper(device); try { - // already recording, since we are flipping the camera we must send it through - // VideoRenderer to keep correct orientation + // Since we are already recording, we are flipping the camera and must + // send it through the VideoRenderer to keep correct orientation. if (recordingVideo) { startPreviewWithVideoRendererStream(); } else { From f2e1569a67c41e6e5615755af75ca1e0acb87a43 Mon Sep 17 00:00:00 2001 From: Braden Bagby <33461698+BradenBagby@users.noreply.github.com> Date: Thu, 20 Oct 2022 18:23:45 -0600 Subject: [PATCH 067/105] Update packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java Co-authored-by: Camille Simon <43054281+camsim99@users.noreply.github.com> --- .../src/main/java/io/flutter/plugins/camera/Camera.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 9a7b0f174f8f..74a211065077 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1124,8 +1124,9 @@ private void startPreviewWithVideoRendererStream() .getVideoOrientation(lockedOrientation); if (cameraProperties.getLensFacing() - != initialCameraFacing) { // if we are facing the opposite way than the initial recording we - // need to flip 180 degrees + != initialCameraFacing) { + // If we are facing the opposite way than the initial recording, + // we need to flip 180 degrees. rotation = (rotation + 180) % 360; } videoRenderer.setRotation(rotation); From 89b7c2feb4e1860af5f5c27ac840782a1c26faed Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 21 Oct 2022 07:33:53 -0600 Subject: [PATCH 068/105] reformats camera.java --- .../main/java/io/flutter/plugins/camera/Camera.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 74a211065077..2fca0bb55fee 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -126,8 +126,8 @@ class Camera private VideoRenderer videoRenderer; /** - * Whether or not the camera aligns with the initial way the camera was facing - * if the camera was flipped. + * Whether or not the camera aligns with the initial way the camera was facing if the camera was + * flipped. */ private int initialCameraFacing; @@ -1123,10 +1123,9 @@ private void startPreviewWithVideoRendererStream() .getDeviceOrientationManager() .getVideoOrientation(lockedOrientation); - if (cameraProperties.getLensFacing() - != initialCameraFacing) { - // If we are facing the opposite way than the initial recording, - // we need to flip 180 degrees. + if (cameraProperties.getLensFacing() != initialCameraFacing) { + // If we are facing the opposite way than the initial recording, + // we need to flip 180 degrees. rotation = (rotation + 180) % 360; } videoRenderer.setRotation(rotation); From 619773da943bfcb425590c07c7a2618feadc9cb8 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 21 Oct 2022 07:42:58 -0600 Subject: [PATCH 069/105] VideoRenderer uses surface texture timestamp instead of current system time --- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 8ac1d07fd4bd..caec58e7d14b 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -4,7 +4,6 @@ package io.flutter.plugins.camera; -import static android.os.SystemClock.uptimeMillis; import android.graphics.SurfaceTexture; import android.opengl.EGL14; @@ -318,9 +317,7 @@ public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); - EGLExt.eglPresentationTimeANDROID( - display, surface, uptimeMillis() * 1000000); // Not the perfect solution but works well, see - // https://stackoverflow.com/questions/63467704/mediarecorder-surface-input-with-opengl-issue-if-audio-recording-is-enabled TODO: try on more devices and see if the timestamp can be incorporated + EGLExt.eglPresentationTimeANDROID(display, surface, timestamp); EGL14.eglSwapBuffers(display, surface); } } From 5e364732aa1f5b510300ae434741f9bef0b204a1 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 21 Oct 2022 07:43:51 -0600 Subject: [PATCH 070/105] formats VideoRenderer.java --- .../src/main/java/io/flutter/plugins/camera/VideoRenderer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index caec58e7d14b..4af720118a04 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -4,7 +4,6 @@ package io.flutter.plugins.camera; - import android.graphics.SurfaceTexture; import android.opengl.EGL14; import android.opengl.EGLConfig; From 973b1f887f29ed3ce52f7dd6b8fb4a8563589d9a Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 21 Oct 2022 07:45:43 -0600 Subject: [PATCH 071/105] fixes comments in VideoRenderer.java --- .../java/io/flutter/plugins/camera/VideoRenderer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 4af720118a04..27b09bde7617 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -22,7 +22,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -/** Renders video onto texture */ +/** Renders video onto texture. */ public class VideoRenderer { private static String TAG = "VideoRenderer"; @@ -89,7 +89,7 @@ public class VideoRenderer { private final Object lock = new Object(); - /** Gets surface for input. Blocks until surface is ready */ + /** Gets surface for input. Blocks until surface is ready. */ public Surface getInputSurface() throws InterruptedException { synchronized (lock) { while (inputSurface == null) { @@ -107,14 +107,14 @@ public VideoRenderer(Surface outputSurface, int recordingWidth, int recordingHei Log.d(TAG, "VideoRenderer setup complete"); } - /** Stop rendering and cleanup resources */ + /** Stop rendering and cleanup resources. */ public void close() { thread.interrupt(); surfaceTextureFrameAvailableHandler.quitSafely(); inputSurfaceTexture.release(); } - /** Configures openGL. must be called in same thread as draw is called */ + /** Configures openGL. Must be called in same thread as draw is called. */ private void configureOpenGL() { synchronized (lock) { display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); @@ -226,7 +226,7 @@ public void onFrameAvailable(SurfaceTexture surfaceTexture) { } } - /** Starts and configures Video Renderer */ + /** Starts and configures Video Renderer. */ private void startOpenGL() { Log.d(TAG, "Starting OpenGL Thread"); thread = From 8bd178eb90948b81fd5fb4ec1ab19eff938438b5 Mon Sep 17 00:00:00 2001 From: Braden Bagby <33461698+BradenBagby@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:54:17 -0600 Subject: [PATCH 072/105] Update packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart Co-authored-by: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> --- .../lib/src/platform_interface/camera_platform.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index 0a398a2f03e2..ea7c6a9936c1 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -258,7 +258,7 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('pausePreview() is not implemented.'); } - /// Set the active camera while recording + /// Set the active camera while recording. Future setDescriptionWhileRecording(CameraDescription description) { throw UnimplementedError( 'setDescriptionWhileRecording() is not implemented.'); From 08e158b07559e4f2f6bbaa422056b3a3efe16a4b Mon Sep 17 00:00:00 2001 From: Braden Bagby <33461698+BradenBagby@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:54:27 -0600 Subject: [PATCH 073/105] Update packages/camera/camera/lib/src/camera_controller.dart Co-authored-by: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> --- packages/camera/camera/lib/src/camera_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 181098bf193d..ace6c8fed8fd 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -385,7 +385,7 @@ class CameraController extends ValueNotifier { } } - /// Sets the description while the camera is recording + /// Sets the description while the camera is recording. Future setDescriptionWhileRecording( CameraDescription description) async { if (!value.isRecordingVideo) { From 1f2635edaceece70233865a71e7ac1a683032de6 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 31 Oct 2022 11:13:25 -0600 Subject: [PATCH 074/105] renames error typo --- packages/camera/camera/lib/src/camera_controller.dart | 2 +- packages/camera/camera/test/camera_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index ace6c8fed8fd..d4e2fc366dea 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -390,7 +390,7 @@ class CameraController extends ValueNotifier { CameraDescription description) async { if (!value.isRecordingVideo) { throw CameraException( - 'Video was not being recording', + 'Video was not being recorded', 'setDescriptionWhileRecording was called while a video was not being recorded', ); } diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index a38becc9ef71..b875ff028f85 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -379,7 +379,7 @@ void main() { cameraController.setDescriptionWhileRecording(newDescription), throwsA(isA().having( (CameraException error) => error.description, - 'Video was not being recording', + 'Video was not being recorded', 'setDescriptionWhileRecording was called while a video was not being recorded', ))); }); From 70da5ef8dfe3da93d81242281a7146f4eb220d27 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 1 Nov 2022 11:56:33 -0600 Subject: [PATCH 075/105] frees shaders after program linking --- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 27b09bde7617..468388e24975 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -183,6 +183,9 @@ private void configureOpenGL() { GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); + deleteShader(vertexShader); + deleteShader(fragmentShader); + vertexHandle = GLES20.glGetAttribLocation(program, "vertexPosition"); uvsHandle = GLES20.glGetAttribLocation(program, "uvs"); texMatrixHandle = GLES20.glGetUniformLocation(program, "texMatrix"); @@ -291,6 +294,10 @@ private int loadShader(int type, String code) { return shader; } + private void deleteShader(int shader) { + GLES20.glDeleteShader(shader); + } + public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long timestamp) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); From aec423df0dd0967628cbe2e2b01ed1e35047394b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 1 Nov 2022 12:45:42 -0600 Subject: [PATCH 076/105] handles eglSwapBuffers errors --- .../java/io/flutter/plugins/camera/Camera.java | 14 +++++++++++++- .../io/flutter/plugins/camera/VideoRenderer.java | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 2fca0bb55fee..ebc93a1c6ccb 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1267,11 +1267,23 @@ private void stopAndReleaseCamera() { private void prepareVideoRenderer() { if (videoRenderer != null) return; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); + + // handle videoRenderer errors + Thread.UncaughtExceptionHandler videoRendererUncaughtExceptionHandler = + new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread thread, Throwable ex) { + dartMessenger.sendCameraErrorEvent( + "Failed to process frames after camera was flipped."); + } + }; + videoRenderer = new VideoRenderer( mediaRecorder.getSurface(), resolutionFeature.getCaptureSize().getWidth(), - resolutionFeature.getCaptureSize().getHeight()); + resolutionFeature.getCaptureSize().getHeight(), + videoRendererUncaughtExceptionHandler); } public void setDescriptionWhileRecording( diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 468388e24975..154cdd770733 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -89,6 +89,8 @@ public class VideoRenderer { private final Object lock = new Object(); + private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler; + /** Gets surface for input. Blocks until surface is ready. */ public Surface getInputSurface() throws InterruptedException { synchronized (lock) { @@ -99,10 +101,15 @@ public Surface getInputSurface() throws InterruptedException { return inputSurface; } - public VideoRenderer(Surface outputSurface, int recordingWidth, int recordingHeight) { + public VideoRenderer( + Surface outputSurface, + int recordingWidth, + int recordingHeight, + Thread.UncaughtExceptionHandler uncaughtExceptionHandler) { this.outputSurface = outputSurface; this.recordingHeight = recordingHeight; this.recordingWidth = recordingWidth; + this.uncaughtExceptionHandler = uncaughtExceptionHandler; startOpenGL(); Log.d(TAG, "VideoRenderer setup complete"); } @@ -267,6 +274,7 @@ public void run() { } } }; + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); thread.start(); } @@ -324,6 +332,9 @@ public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); EGLExt.eglPresentationTimeANDROID(display, surface, timestamp); - EGL14.eglSwapBuffers(display, surface); + if (!EGL14.eglSwapBuffers(display, surface)) { + throw new RuntimeException( + "eglSwapBuffers()" + GLUtils.getEGLErrorString(EGL14.eglGetError())); + } } } From 53d3e1dacdb2e086ec0c7e34e0050605c0061a6f Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 1 Nov 2022 13:28:21 -0600 Subject: [PATCH 077/105] extension check guards eglPresentationTimeANDROID --- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 154cdd770733..0cd1250bbb18 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -135,6 +135,11 @@ private void configureOpenGL() { throw new RuntimeException( "eglInitialize(): " + GLUtils.getEGLErrorString(EGL14.eglGetError())); + String eglExtensions = EGL14.eglQueryString(display, EGL14.EGL_EXTENSIONS); + if (!eglExtensions.contains("EGL_ANDROID_presentation_time")) + throw new RuntimeException( + "cannot configure OpenGL. missing EGL_ANDROID_presentation_time"); + int[] attribList = new int[] { EGL14.EGL_RED_SIZE, 8, From 98969d10baab6e4fada99beeefb094d83c3b84ec Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 7 Nov 2022 16:23:45 -0700 Subject: [PATCH 078/105] cleans openGL resources --- .../java/io/flutter/plugins/camera/VideoRenderer.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 0cd1250bbb18..6e3b4d4d26d7 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -118,9 +118,18 @@ public VideoRenderer( public void close() { thread.interrupt(); surfaceTextureFrameAvailableHandler.quitSafely(); + cleanupOpenGL(); inputSurfaceTexture.release(); } + private void cleanupOpenGL() { + GLES20.glDeleteBuffers(2, bufferHandles, 0); + GLES20.glDeleteTextures(1, textureHandles, 0); + EGL14.eglDestroyContext(display, context); + EGL14.eglDestroySurface(display, surface); + GLES20.glDeleteProgram(program); + } + /** Configures openGL. Must be called in same thread as draw is called. */ private void configureOpenGL() { synchronized (lock) { From f2c487668927c136c24e203ebbde69e196b5a8ed Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 14 Nov 2022 10:26:00 -0700 Subject: [PATCH 079/105] reverted timestamp to use uptimeMillis() --- .../io/flutter/plugins/camera/VideoRenderer.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 6e3b4d4d26d7..cc06ae8976ba 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -4,6 +4,8 @@ package io.flutter.plugins.camera; +import static android.os.SystemClock.uptimeMillis; + import android.graphics.SurfaceTexture; import android.opengl.EGL14; import android.opengl.EGLConfig; @@ -277,11 +279,7 @@ public void run() { float[] surfaceTextureMatrix = new float[16]; inputSurfaceTexture.getTransformMatrix(surfaceTextureMatrix); - draw( - recordingWidth, - recordingHeight, - surfaceTextureMatrix, - inputSurfaceTexture.getTimestamp()); + draw(recordingWidth, recordingHeight, surfaceTextureMatrix); } } catch (InterruptedException e) { Log.d(TAG, "thread interrupted while waiting for frames"); @@ -320,7 +318,7 @@ private void deleteShader(int shader) { GLES20.glDeleteShader(shader); } - public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long timestamp) { + public void draw(int viewportWidth, int viewportHeight, float[] texMatrix) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glClearColor(0f, 0f, 0f, 0f); @@ -345,7 +343,7 @@ public void draw(int viewportWidth, int viewportHeight, float[] texMatrix, long GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0); - EGLExt.eglPresentationTimeANDROID(display, surface, timestamp); + EGLExt.eglPresentationTimeANDROID(display, surface, uptimeMillis() * 1000000); if (!EGL14.eglSwapBuffers(display, surface)) { throw new RuntimeException( "eglSwapBuffers()" + GLUtils.getEGLErrorString(EGL14.eglGetError())); From 8145162d7db5d56150342f9a21c57aa134ce4a3b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 28 Nov 2022 16:02:20 -0700 Subject: [PATCH 080/105] Tests for startPreviewWithVideoRendererStream --- .../io/flutter/plugins/camera/Camera.java | 54 ++++++++++--------- .../io/flutter/plugins/camera/CameraTest.java | 54 +++++++++++++++++++ 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index ebc93a1c6ccb..505cda9b978c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -337,20 +337,16 @@ public void open(String imageFormatGroup) throws CameraAccessException { public void onOpened(@NonNull CameraDevice device) { cameraDevice = new DefaultCameraDeviceWrapper(device); try { - // Since we are already recording, we are flipping the camera and must - // send it through the VideoRenderer to keep correct orientation. - if (recordingVideo) { - startPreviewWithVideoRendererStream(); - } else { - startPreview(); - dartMessenger.sendCameraInitializedEvent( + startPreview(); + if (!recordingVideo) // only send initialization if we werent already recording and switching cameras + dartMessenger.sendCameraInitializedEvent( resolutionFeature.getPreviewSize().getWidth(), resolutionFeature.getPreviewSize().getHeight(), cameraFeatures.getExposureLock().getValue(), cameraFeatures.getAutoFocus().getValue(), cameraFeatures.getExposurePoint().checkIsSupported(), cameraFeatures.getFocusPoint().checkIsSupported()); - } + } catch (CameraAccessException | InterruptedException e) { dartMessenger.sendCameraErrorEvent(e.getMessage()); close(); @@ -1098,7 +1094,17 @@ public void resumePreview() { null, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); } - public void startPreview() throws CameraAccessException { + public void startPreview() throws CameraAccessException, InterruptedException { + // if we are already recording, we are flipping the camera and must + // send it through the VideoRenderer to keep correct orientation. + if (recordingVideo) { + startPreviewWithVideoRendererStream(); + } else { + startRegularPreview(); + } + } + + private void startRegularPreview() throws CameraAccessException { if (pictureImageReader == null || pictureImageReader.getSurface() == null) return; Log.i(TAG, "startPreview"); createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); @@ -1112,23 +1118,21 @@ private void startPreviewWithVideoRendererStream() final PlatformChannel.DeviceOrientation lockedOrientation = ((SensorOrientationFeature) cameraFeatures.getSensorOrientation()) .getLockedCaptureOrientation(); - int rotation = - lockedOrientation == null - ? cameraFeatures - .getSensorOrientation() - .getDeviceOrientationManager() - .getVideoOrientation() - : cameraFeatures - .getSensorOrientation() - .getDeviceOrientationManager() - .getVideoOrientation(lockedOrientation); - - if (cameraProperties.getLensFacing() != initialCameraFacing) { - // If we are facing the opposite way than the initial recording, - // we need to flip 180 degrees. - rotation = (rotation + 180) % 360; + DeviceOrientationManager orientationManager = + cameraFeatures.getSensorOrientation().getDeviceOrientationManager(); + if (orientationManager != null) { + int rotation = + lockedOrientation == null + ? orientationManager.getVideoOrientation() + : orientationManager.getVideoOrientation(lockedOrientation); + + if (cameraProperties.getLensFacing() != initialCameraFacing) { + // If we are facing the opposite way than the initial recording, + // we need to flip 180 degrees. + rotation = (rotation + 180) % 360; + } + videoRenderer.setRotation(rotation); } - videoRenderer.setRotation(rotation); createCaptureSession(CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 97301a51b231..f91b908e14b5 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -618,6 +618,60 @@ public void setDescriptionWhileRecording() { verify(mockResult, never()).error(any(), any(), any()); } + @Test + public void startPreview_shouldPullStreamFromVideoRenderer() + throws InterruptedException, CameraAccessException { + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + Size mockSize = mock(Size.class); + TestUtils.setPrivateField(camera, "recordingVideo", true); + TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + + TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = + (TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture"); + ResolutionFeature resolutionFeature = + (ResolutionFeature) + TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature"); + + when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.startPreview(); + verify(mockVideoRenderer, times(1)) + .getInputSurface(); // stream pulled from videoRenderer's surface. + } + + @Test + public void startPreview_shouldPullStreamFromImageReader() + throws InterruptedException, CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + Size mockSize = mock(Size.class); + ImageReader mockImageReader = mock(ImageReader.class); + TestUtils.setPrivateField(camera, "recordingVideo", false); + TestUtils.setPrivateField(camera, "pictureImageReader", mockImageReader); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + + TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = + (TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture"); + ResolutionFeature resolutionFeature = + (ResolutionFeature) + TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature"); + + when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.startPreview(); + verify(mockImageReader, times(1)) + .getSurface(); // stream pulled from regular imageReader's surface. + } + @Test public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { MethodChannel.Result mockResult = mock(MethodChannel.Result.class); From a8e0a72a86a0f3360334e1131edd6fcf104c6c8e Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 28 Nov 2022 16:13:02 -0700 Subject: [PATCH 081/105] fixes exception not being caught --- .../android/src/main/java/io/flutter/plugins/camera/Camera.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 505cda9b978c..7df6d202769c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -810,7 +810,7 @@ public void stopVideoRecording(@NonNull final Result result) { mediaRecorder.reset(); try { startPreview(); - } catch (CameraAccessException | IllegalStateException e) { + } catch (CameraAccessException | IllegalStateException | InterruptedException e) { result.error("videoRecordingFailed", e.getMessage(), null); return; } From ea284131600b0f5c2e9aee7bc6d690b6d9e42ec3 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 29 Nov 2022 08:51:18 -0700 Subject: [PATCH 082/105] tests for correct rotation to be set --- .../io/flutter/plugins/camera/Camera.java | 17 +++++++----- .../io/flutter/plugins/camera/CameraTest.java | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 7df6d202769c..0c5f62742b64 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1120,19 +1120,22 @@ private void startPreviewWithVideoRendererStream() .getLockedCaptureOrientation(); DeviceOrientationManager orientationManager = cameraFeatures.getSensorOrientation().getDeviceOrientationManager(); + + int rotation = 0; if (orientationManager != null) { - int rotation = + rotation = lockedOrientation == null ? orientationManager.getVideoOrientation() : orientationManager.getVideoOrientation(lockedOrientation); + } - if (cameraProperties.getLensFacing() != initialCameraFacing) { - // If we are facing the opposite way than the initial recording, - // we need to flip 180 degrees. - rotation = (rotation + 180) % 360; - } - videoRenderer.setRotation(rotation); + if (cameraProperties.getLensFacing() != initialCameraFacing) { + + // If we are facing the opposite way than the initial recording, + // we need to flip 180 degrees. + rotation = (rotation + 180) % 360; } + videoRenderer.setRotation(rotation); createCaptureSession(CameraDevice.TEMPLATE_RECORD, videoRenderer.getInputSurface()); } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index f91b908e14b5..9a6f7dc20d22 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -672,6 +672,33 @@ public void startPreview_shouldPullStreamFromImageReader() .getSurface(); // stream pulled from regular imageReader's surface. } + @Test + public void startPreview_shouldFlipRotation() throws InterruptedException, CameraAccessException { + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + Size mockSize = mock(Size.class); + TestUtils.setPrivateField(camera, "recordingVideo", true); + TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer); + TestUtils.setPrivateField(camera, "initialCameraFacing", CameraMetadata.LENS_FACING_BACK); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + + TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = + (TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture"); + ResolutionFeature resolutionFeature = + (ResolutionFeature) + TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature"); + + when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + when(mockCameraProperties.getLensFacing()).thenReturn(CameraMetadata.LENS_FACING_FRONT); + + camera.startPreview(); + verify(mockVideoRenderer, times(1)).setRotation(180); + } + @Test public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { MethodChannel.Result mockResult = mock(MethodChannel.Result.class); From a12e3f4a196898ad65d40029ac3341b24f2af697 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Sat, 3 Dec 2022 20:53:22 -0700 Subject: [PATCH 083/105] fixes versioning --- packages/camera/camera/CHANGELOG.md | 2 +- packages/camera/camera/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 20248d64b0d4..04978a42d0ee 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.11.0 +## 0.10.1 * Allows camera to be switched while video recording. diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 6e593a93d98f..3371ed223041 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing 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.11.0 +version: 0.10.1 environment: sdk: ">=2.14.0 <3.0.0" From 98a493bf96520b0f4be9b40fe63cf0984e61225f Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Sat, 3 Dec 2022 21:04:40 -0700 Subject: [PATCH 084/105] tests method channel setDescriptionWhileRecording --- .../method_channel_camera_test.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index 60f42fd4af4a..751e1269efd9 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -573,6 +573,29 @@ void main() { ]); }); + test('Should set description while recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: {'setDescriptionWhileRecording': null}, + ); + + // Act + const CameraDescription cameraDescription = CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0); + await camera.setDescriptionWhileRecording(cameraDescription); + + // Assert + expect(channel.log, [ + isMethodCall('setDescriptionWhileRecording', + arguments: { + 'cameraName': cameraDescription.name + }), + ]); + }); + test('Should pass maxVideoDuration when starting recording a video', () async { // Arrange From 9f3fc4942c536baf3489158133c98f021e4232c8 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 5 Dec 2022 10:12:37 -0700 Subject: [PATCH 085/105] adds forwarding getter on CameraController to its value's description --- packages/camera/camera/README.md | 2 +- .../camera/camera/example/integration_test/camera_test.dart | 6 +++--- packages/camera/camera/example/lib/main.dart | 6 +++--- packages/camera/camera/lib/src/camera_controller.dart | 5 ++++- .../example/integration_test/camera_test.dart | 6 +++--- .../camera_android/example/lib/camera_controller.dart | 5 ++++- packages/camera/camera_android/example/lib/main.dart | 6 +++--- .../example/integration_test/camera_test.dart | 6 +++--- .../camera_avfoundation/example/lib/camera_controller.dart | 5 ++++- packages/camera/camera_avfoundation/example/lib/main.dart | 6 +++--- 10 files changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index db2e41b4f4cd..4d7c3d90791a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -75,7 +75,7 @@ void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.value.description); + onNewCameraSelected(cameraController.description); } } ``` diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index 95424d38e7ae..ed2d64e21eee 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -56,7 +56,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Picture final XFile file = await controller.takePicture(); @@ -105,7 +105,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Video await controller.startVideoRecording(); @@ -289,7 +289,7 @@ void main() { await controller.setDescriptionWhileRecording(cameras[1]); sleep(const Duration(milliseconds: 500)); - expect(controller.value.description, cameras[1]); + expect(controller.description, cameras[1]); }); testWidgets( diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index f1c53a5e3a1e..f38f358c4290 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -121,7 +121,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.value.description); + onNewCameraSelected(cameraController.description); } } // #enddocregion AppLifecycle @@ -596,7 +596,7 @@ class _CameraExampleHomeState extends State width: 90.0, child: RadioListTile( title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), - groupValue: controller?.value.description, + groupValue: controller?.description, value: cameraDescription, onChanged: onChanged, ), @@ -773,7 +773,7 @@ class _CameraExampleHomeState extends State void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller!.value.description); + onNewCameraSelected(controller!.description); } } diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index d4e2fc366dea..e543ed5cc2c6 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -239,6 +239,9 @@ class CameraController extends ValueNotifier { this.imageFormatGroup, }) : super(CameraValue.uninitialized(description)); + /// The properties of the camera device controlled by this controller. + CameraDescription get description => value.description; + /// The resolution this controller is targeting. /// /// This resolution preset is not guaranteed to be available on the device, @@ -299,7 +302,7 @@ class CameraController extends ValueNotifier { }); _cameraId = await CameraPlatform.instance.createCamera( - value.description, + description, resolutionPreset, enableAudio: enableAudio, ); diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index b2d362b028a7..0b9011d30c3d 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -56,7 +56,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Picture final XFile file = await controller.takePicture(); @@ -106,7 +106,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Video await controller.startVideoRecording(); @@ -230,7 +230,7 @@ void main() { await controller.setDescriptionWhileRecording(cameras[1]); sleep(const Duration(milliseconds: 500)); - expect(controller.value.description, cameras[1]); + expect(controller.description, cameras[1]); }); testWidgets( diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 6366f5aaca3f..23046aa1e02b 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -178,6 +178,9 @@ class CameraController extends ValueNotifier { this.imageFormatGroup, }) : super(CameraValue.uninitialized(cameraDescription)); + /// The properties of the camera device controlled by this controller. + CameraDescription get description => value.description; + /// The resolution this controller is targeting. /// /// This resolution preset is not guaranteed to be available on the device, @@ -219,7 +222,7 @@ class CameraController extends ValueNotifier { }); _cameraId = await CameraPlatform.instance.createCamera( - value.description, + description, resolutionPreset, enableAudio: enableAudio, ); diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index bf1212eefc3a..16b1b3140a76 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -124,7 +124,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.value.description); + onNewCameraSelected(cameraController.description); } } @@ -602,7 +602,7 @@ class _CameraExampleHomeState extends State width: 90.0, child: RadioListTile( title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), - groupValue: controller?.value.description, + groupValue: controller?.description, value: cameraDescription, onChanged: onChanged, ), @@ -781,7 +781,7 @@ class _CameraExampleHomeState extends State void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller!.value.description); + onNewCameraSelected(controller!.description); } } diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 6dd003b76f97..bbe825af488c 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -57,7 +57,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Picture final XFile file = await controller.takePicture(); @@ -103,7 +103,7 @@ void main() { CameraController controller, ResolutionPreset preset) async { final Size expectedSize = presetExpectedSizes[preset]!; print( - 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.value.description.name}'); + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Video await controller.startVideoRecording(); @@ -223,7 +223,7 @@ void main() { await controller.setDescriptionWhileRecording(cameras[1]); sleep(const Duration(milliseconds: 500)); - expect(controller.value.description, cameras[1]); + expect(controller.description, cameras[1]); }); /// Start streaming with specifying the ImageFormatGroup. diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 6366f5aaca3f..23046aa1e02b 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -178,6 +178,9 @@ class CameraController extends ValueNotifier { this.imageFormatGroup, }) : super(CameraValue.uninitialized(cameraDescription)); + /// The properties of the camera device controlled by this controller. + CameraDescription get description => value.description; + /// The resolution this controller is targeting. /// /// This resolution preset is not guaranteed to be available on the device, @@ -219,7 +222,7 @@ class CameraController extends ValueNotifier { }); _cameraId = await CameraPlatform.instance.createCamera( - value.description, + description, resolutionPreset, enableAudio: enableAudio, ); diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index bf1212eefc3a..16b1b3140a76 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -124,7 +124,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.value.description); + onNewCameraSelected(cameraController.description); } } @@ -602,7 +602,7 @@ class _CameraExampleHomeState extends State width: 90.0, child: RadioListTile( title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), - groupValue: controller?.value.description, + groupValue: controller?.description, value: cameraDescription, onChanged: onChanged, ), @@ -781,7 +781,7 @@ class _CameraExampleHomeState extends State void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller!.value.description); + onNewCameraSelected(controller!.description); } } From 2c5e38f9035e7738cee96baa1fb53bcc6866846f Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 5 Dec 2022 10:23:14 -0700 Subject: [PATCH 086/105] dummy commit to fix github test's not finding commit hash --- packages/camera/camera/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index f38f358c4290..c19b035864fd 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -631,7 +631,7 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the camera description of the controller to flip the camera + // if we are currently recording then switch the cameraDescription of the controller to flip the camera final bool isRecording = controller != null && controller!.value.isRecordingVideo; if (isRecording) { From 3a8fd893a4abc608826e1ae16f291cf8584b2881 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 5 Dec 2022 10:37:23 -0700 Subject: [PATCH 087/105] adds override description for FakeController in camera tests --- packages/camera/camera/test/camera_preview_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index 2837faee9291..bad9c0bf9995 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -120,6 +120,9 @@ class FakeController extends ValueNotifier @override Future setDescriptionWhileRecording( CameraDescription description) async {} + + @override + CameraDescription get description => value.description; } void main() { From e52dfcccfde34f927cacf835bbe50bb3f58ab2fb Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 5 Dec 2022 10:46:02 -0700 Subject: [PATCH 088/105] fixes versioning for avfoundation and android --- packages/camera/camera_android/CHANGELOG.md | 2 +- packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/CHANGELOG.md | 2 +- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 40cf6c9efbce..7d3e076deff3 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.11.0 +## 0.10.1 * Allows camera to be switched while video recording. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 06867294ae11..d125dbf51d2a 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.11.0 +version: 0.10.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index a64f57a7c1d9..a8069a395650 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.10.0 +## 0.9.9 * Allows camera to be switched while video recording. diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index e58b430889c6..f0c0ab9282f6 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0 +version: 0.9.9 environment: sdk: ">=2.14.0 <3.0.0" From 0c70970884d366b767cd453c051fdfe32bcbca80 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Tue, 6 Dec 2022 07:10:42 -0700 Subject: [PATCH 089/105] fixes versioning --- packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- packages/camera/camera_platform_interface/CHANGELOG.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index dd7eaa216e9a..6d86c07ef6a1 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android 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" diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 3d6ea756f41a..eac2795c1d29 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.9 +version: 0.9.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index eb851ab2dab5..279dc73e6a34 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.4 +## 2.3.3 * Allows camera to be switched while video recording. ## 2.3.2 From d49dcf77d44fee5a46bc28da96ab7e9163c59a0b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Fri, 9 Dec 2022 07:24:20 -0700 Subject: [PATCH 090/105] fixes pubspec versions --- packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- packages/camera/camera_platform_interface/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index dd7eaa216e9a..6d86c07ef6a1 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android 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" diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 3d6ea756f41a..eac2795c1d29 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.9 +version: 0.9.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index cb21a6c7e09c..54383cd853ee 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ 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 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.3.2 +version: 2.3.3 environment: sdk: '>=2.12.0 <3.0.0' From e95eea90566ccdc8035fc1faaf251b7ea6bf7ec8 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 14:57:32 -0700 Subject: [PATCH 091/105] ios setDescription --- .../example/integration_test/camera_test.dart | 2 +- .../example/lib/camera_controller.dart | 16 ++++++++---- .../camera_avfoundation/example/lib/main.dart | 26 ++++++------------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index d8a2ad81c848..1fc61adfdfe1 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -216,7 +216,7 @@ void main() { await controller.startVideoRecording(); sleep(const Duration(milliseconds: 500)); - await controller.setDescriptionWhileRecording(cameras[1]); + await controller.setDescription(cameras[1]); sleep(const Duration(milliseconds: 500)); expect(controller.description, cameras[1]); diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 0f4f312431d1..65c2d86b666c 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -204,7 +204,9 @@ class CameraController extends ValueNotifier { int get cameraId => _cameraId; /// Initializes the camera on the device. - Future initialize() async { + Future initialize() => _initializeWithDescription(description); + + Future _initializeWithDescription(CameraDescription description) async { final Completer initializeCompleter = Completer(); @@ -236,6 +238,7 @@ class CameraController extends ValueNotifier { value = value.copyWith( isInitialized: true, + description: description, previewSize: await initializeCompleter.future .then((CameraInitializedEvent event) => Size( event.previewWidth, @@ -275,10 +278,13 @@ class CameraController extends ValueNotifier { } /// Sets the description while the camera is recording - Future setDescriptionWhileRecording( - CameraDescription description) async { - await CameraPlatform.instance.setDescriptionWhileRecording(description); - value = value.copyWith(description: description); + Future setDescription(CameraDescription description) async { + if (value.isRecordingVideo) { + await CameraPlatform.instance.setDescriptionWhileRecording(description); + value = value.copyWith(description: description); + } else { + await _initializeWithDescription(description); + } } /// Captures an image and returns the file where it was saved. diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index a26326421d0b..364a6ea1d5a0 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -121,7 +121,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + _initializeCameraController(cameraController.description); } } @@ -634,25 +634,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the camera description of the controller to flip the camera - final bool isRecording = - controller != null && controller!.value.isRecordingVideo; - if (isRecording) { - controller!.setDescriptionWhileRecording(cameraDescription); - return; - } - - final CameraController? oldController = controller; - if (oldController != null) { - // `controller` needs to be set to null before getting disposed, - // to avoid a race condition when we use the controller that is being - // disposed. This happens when camera permission dialog shows up, - // which triggers `didChangeAppLifecycleState`, which disposes and - // re-creates the controller. - controller = null; - await oldController.dispose(); + if (controller != null) { + return controller!.setDescription(cameraDescription); + } else { + return _initializeCameraController(cameraDescription); } + } + Future _initializeCameraController( + CameraDescription cameraDescription) async { final CameraController cameraController = CameraController( cameraDescription, kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, From e0a5fbb2ab44087580da5e25250131bb50a60245 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:10:18 -0700 Subject: [PATCH 092/105] setDescription --- .../example/integration_test/camera_test.dart | 2 +- packages/camera/camera/example/lib/main.dart | 26 ++++++------------- .../camera/lib/src/camera_controller.dart | 26 +++++++++++-------- packages/camera/camera/test/camera_test.dart | 24 ----------------- 4 files changed, 24 insertions(+), 54 deletions(-) diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index e4009ba49f99..b7e0fa83fab5 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -282,7 +282,7 @@ void main() { await controller.startVideoRecording(); sleep(const Duration(milliseconds: 500)); - await controller.setDescriptionWhileRecording(cameras[1]); + await controller.setDescription(cameras[1]); sleep(const Duration(milliseconds: 500)); expect(controller.description, cameras[1]); diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index ffe7dfbfe24a..ee0c3479eed9 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -118,7 +118,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + _initializeCameraController(cameraController.description); } } // #enddocregion AppLifecycle @@ -628,25 +628,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the cameraDescription of the controller to flip the camera - final bool isRecording = - controller != null && controller!.value.isRecordingVideo; - if (isRecording) { - controller!.setDescriptionWhileRecording(cameraDescription); - return; - } - - final CameraController? oldController = controller; - if (oldController != null) { - // `controller` needs to be set to null before getting disposed, - // to avoid a race condition when we use the controller that is being - // disposed. This happens when camera permission dialog shows up, - // which triggers `didChangeAppLifecycleState`, which disposes and - // re-creates the controller. - controller = null; - await oldController.dispose(); + if (controller != null) { + return controller!.setDescription(cameraDescription); + } else { + return _initializeCameraController(cameraDescription); } + } + Future _initializeCameraController( + CameraDescription cameraDescription) async { final CameraController cameraController = CameraController( cameraDescription, kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 9b87c45b1ec9..e0ed8c8985e8 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -277,7 +277,12 @@ class CameraController extends ValueNotifier { /// Initializes the camera on the device. /// /// Throws a [CameraException] if the initialization fails. - Future initialize() async { + Future initialize() => _initializeWithDescription(description); + + /// Initializes the camera on the device with the specified description. + /// + /// Throws a [CameraException] if the initialization fails. + Future _initializeWithDescription(CameraDescription description) async { if (_isDisposed) { throw CameraException( 'Disposed CameraController', @@ -316,6 +321,7 @@ class CameraController extends ValueNotifier { value = value.copyWith( isInitialized: true, + description: description, previewSize: await initializeCompleter.future .then((CameraInitializedEvent event) => Size( event.previewWidth, @@ -382,17 +388,15 @@ class CameraController extends ValueNotifier { } /// Sets the description while the camera is recording. - Future setDescriptionWhileRecording( - CameraDescription description) async { - if (!value.isRecordingVideo) { - throw CameraException( - 'Video was not being recorded', - 'setDescriptionWhileRecording was called while a video was not being recorded', - ); + /// + /// Throws a [CameraException] if setting the description fails. + Future setDescription(CameraDescription description) async { + if (value.isRecordingVideo) { + await CameraPlatform.instance.setDescriptionWhileRecording(description); + value = value.copyWith(description: description); + } else { + await _initializeWithDescription(description); } - - await CameraPlatform.instance.setDescriptionWhileRecording(description); - value = value.copyWith(description: description); } /// Captures an image and returns the file where it was saved. diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 61a4c7ba2ade..2138f2d055a5 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -359,30 +359,6 @@ void main() { ))); }); - test( - 'setDescriptionWhileRecording() throws $CameraException when not currenctly recording', - () async { - final CameraController cameraController = CameraController( - const CameraDescription( - name: 'cam', - lensDirection: CameraLensDirection.back, - sensorOrientation: 90), - ResolutionPreset.max); - - await cameraController.initialize(); - const CameraDescription newDescription = CameraDescription( - name: 'cam2', - lensDirection: CameraLensDirection.front, - sensorOrientation: 90); - expect( - cameraController.setDescriptionWhileRecording(newDescription), - throwsA(isA().having( - (CameraException error) => error.description, - 'Video was not being recorded', - 'setDescriptionWhileRecording was called while a video was not being recorded', - ))); - }); - test('getMaxZoomLevel() throws $CameraException when uninitialized', () async { final CameraController cameraController = CameraController( From 9fdba259ef35014eb433eec1bd8d27e89e1c9d94 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:15:18 -0700 Subject: [PATCH 093/105] android setDescription --- .../example/integration_test/camera_test.dart | 2 +- .../example/lib/camera_controller.dart | 18 ++++++++----- .../camera_android/example/lib/main.dart | 26 ++++++------------- .../example/lib/camera_controller.dart | 2 +- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index ff4e244122d9..c31e36077025 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -223,7 +223,7 @@ void main() { await controller.startVideoRecording(); sleep(const Duration(milliseconds: 500)); - await controller.setDescriptionWhileRecording(cameras[1]); + await controller.setDescription(cameras[1]); sleep(const Duration(milliseconds: 500)); expect(controller.description, cameras[1]); diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 5d25ec1da41b..3a20e1dc5671 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -204,7 +204,9 @@ class CameraController extends ValueNotifier { int get cameraId => _cameraId; /// Initializes the camera on the device. - Future initialize() async { + Future initialize() => _initializeWithDescription(description); + + Future _initializeWithDescription(CameraDescription description) async { final Completer initializeCompleter = Completer(); @@ -236,6 +238,7 @@ class CameraController extends ValueNotifier { value = value.copyWith( isInitialized: true, + description: description, previewSize: await initializeCompleter.future .then((CameraInitializedEvent event) => Size( event.previewWidth, @@ -274,11 +277,14 @@ class CameraController extends ValueNotifier { value = value.copyWith(isPreviewPaused: false); } - /// Sets the description while the camera is recording - Future setDescriptionWhileRecording( - CameraDescription description) async { - await CameraPlatform.instance.setDescriptionWhileRecording(description); - value = value.copyWith(description: description); + /// Sets the description of the camera + Future setDescription(CameraDescription description) async { + if (value.isRecordingVideo) { + await CameraPlatform.instance.setDescriptionWhileRecording(description); + value = value.copyWith(description: description); + } else { + await _initializeWithDescription(description); + } } /// Captures an image and returns the file where it was saved. diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index a26326421d0b..364a6ea1d5a0 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -121,7 +121,7 @@ class _CameraExampleHomeState extends State if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + _initializeCameraController(cameraController.description); } } @@ -634,25 +634,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - // if we are currently recording then switch the camera description of the controller to flip the camera - final bool isRecording = - controller != null && controller!.value.isRecordingVideo; - if (isRecording) { - controller!.setDescriptionWhileRecording(cameraDescription); - return; - } - - final CameraController? oldController = controller; - if (oldController != null) { - // `controller` needs to be set to null before getting disposed, - // to avoid a race condition when we use the controller that is being - // disposed. This happens when camera permission dialog shows up, - // which triggers `didChangeAppLifecycleState`, which disposes and - // re-creates the controller. - controller = null; - await oldController.dispose(); + if (controller != null) { + return controller!.setDescription(cameraDescription); + } else { + return _initializeCameraController(cameraDescription); } + } + Future _initializeCameraController( + CameraDescription cameraDescription) async { final CameraController cameraController = CameraController( cameraDescription, kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 65c2d86b666c..e19bb496a21f 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -277,7 +277,7 @@ class CameraController extends ValueNotifier { value = value.copyWith(isPreviewPaused: false); } - /// Sets the description while the camera is recording + /// Sets the description of the camera Future setDescription(CameraDescription description) async { if (value.isRecordingVideo) { await CameraPlatform.instance.setDescriptionWhileRecording(description); From ff17835b4b7253a2abe682960b2b54b9299b063b Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:17:08 -0700 Subject: [PATCH 094/105] formatting --- packages/camera/camera/lib/src/camera_controller.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index e0ed8c8985e8..589b3ad3a319 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -280,7 +280,7 @@ class CameraController extends ValueNotifier { Future initialize() => _initializeWithDescription(description); /// Initializes the camera on the device with the specified description. - /// + /// /// Throws a [CameraException] if the initialization fails. Future _initializeWithDescription(CameraDescription description) async { if (_isDisposed) { @@ -387,8 +387,8 @@ class CameraController extends ValueNotifier { } } - /// Sets the description while the camera is recording. - /// + /// Sets the description of the camera + /// /// Throws a [CameraException] if setting the description fails. Future setDescription(CameraDescription description) async { if (value.isRecordingVideo) { From 02b847aa1bbf97a5d9415f4570d66b9ee30591ac Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:26:03 -0700 Subject: [PATCH 095/105] revert --- .../example/ios/Runner.xcodeproj/project.pbxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 59f086e163fa..c63d00860204 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -290,7 +290,6 @@ }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = ""; }; }; }; From 8330cc918f08cb405571b6a6ca25e1e47651eecb Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:31:59 -0700 Subject: [PATCH 096/105] nits and reverts --- .../example/ios/Runner.xcodeproj/project.pbxproj | 1 - .../camera_avfoundation/ios/Classes/FLTCam.m | 16 ++++++++-------- .../src/platform_interface/camera_platform.dart | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index d20263b65a6e..99433b084f27 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -174,7 +174,6 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = ""; }; }; }; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index fec06ac5ada1..d5247e00382e 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -170,21 +170,21 @@ - (instancetype)initWithCameraName:(NSString *)cameraName } - (AVCaptureConnection *)createConnection:(NSError **)error { - // setup input + // Setup video capture input. _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:error]; if (*error) { return nil; } - // setup output + // Setup video capture output. _captureVideoOutput = [AVCaptureVideoDataOutput new]; _captureVideoOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; - // setup connection + // Setup video capture connection. AVCaptureConnection *connection = [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports output:_captureVideoOutput]; @@ -884,10 +884,10 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName AVCaptureConnection *oldConnection = [_captureVideoOutput connectionWithMediaType:AVMediaTypeVideo]; - // stop video capture from old output + // Stop video capture from the old output. [_captureVideoOutput setSampleBufferDelegate:nil queue:nil]; - // remove old connections + // Remove the old video capture connections. [_videoCaptureSession beginConfiguration]; [_videoCaptureSession removeInput:_captureVideoInput]; [_videoCaptureSession removeOutput:_captureVideoOutput]; @@ -899,12 +899,12 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName return; } - // keep orientation + // Keep the same orientation the old connections had. if (oldConnection && newConnection.isVideoOrientationSupported) { newConnection.videoOrientation = oldConnection.videoOrientation; } - // add new connections + // Add the new connections to the session. if (![_videoCaptureSession canAddInput:_captureVideoInput]) [result sendErrorWithCode:@"VideoError" message:@"Unable switch video input" details:nil]; [_videoCaptureSession addInputWithNoConnections:_captureVideoInput]; @@ -1160,7 +1160,7 @@ - (BOOL)setupWriterForPath:(NSString *)path { } - (void)setUpCaptureSessionForAudio { - // dont setup twice or we will lose the audio + // Don't setup audio twice or we will lose the audio. if (_isAudioSetup) { return; } diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index 3b3abd8ea7bd..b43629d4e0c3 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -269,7 +269,7 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('pausePreview() is not implemented.'); } - /// Set the active camera while recording. + /// Sets the active camera while recording. Future setDescriptionWhileRecording(CameraDescription description) { throw UnimplementedError( 'setDescriptionWhileRecording() is not implemented.'); From fbf2a23e44a1551788bc2fe82c3e0b85b37b479d Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:40:44 -0700 Subject: [PATCH 097/105] nits --- .../java/io/flutter/plugins/camera/Camera.java | 7 +++---- .../io/flutter/plugins/camera/VideoRenderer.java | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 4513bc7e1cac..b2a90b8b290c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1097,8 +1097,7 @@ public void resumePreview() { } public void startPreview() throws CameraAccessException, InterruptedException { - // if we are already recording, we are flipping the camera and must - // send it through the VideoRenderer to keep correct orientation. + // If recording is already in progress, the camera is being flipped, so send it through the VideoRenderer to keep the correct orientation. if (recordingVideo) { startPreviewWithVideoRendererStream(); } else { @@ -1133,8 +1132,8 @@ private void startPreviewWithVideoRendererStream() if (cameraProperties.getLensFacing() != initialCameraFacing) { - // If we are facing the opposite way than the initial recording, - // we need to flip 180 degrees. + // If the new camera is facing the opposite way than the initial recording, + // the rotation should be flipped 180 degrees. rotation = (rotation + 180) % 360; } videoRenderer.setRotation(rotation); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index cc06ae8976ba..8dc135716010 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -24,7 +24,20 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -/** Renders video onto texture. */ +/** + * Renders video onto texture after performing a matrix rotation on each frame. + * + *

VideoRenderer is needed because when switching between cameras mid recording, the orientation + * of the recording from the new camera usually becomes flipped. MediaRecorder has + * setOrientationHint, but that cannot be called mid recording and therefore isn't useful. Android + * Camera2 has no setDisplayOrientation on the camera itself as it is supposed to 'just work' (see + * https://stackoverflow.com/questions/33479004/what-is-the-camera2-api-equivalent-of-setdisplayorientation). + * Therefore it cannot be used to set the camera's orientation either. + * + *

This leaves the solution to be routing the recording through a surface texture and performing + * a matrix transformation on it manually to get the correct orientation. This only happens when + * setDescription is called mid video recording. + */ public class VideoRenderer { private static String TAG = "VideoRenderer"; From fa33e4fffedf5c90446827791e09bfccf824106c Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:45:13 -0700 Subject: [PATCH 098/105] fixes README --- packages/camera/camera/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 86b0355b8bcc..709b5913da2a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -75,7 +75,7 @@ void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); + _initializeCameraController(cameraController.description); } } ``` From 998a366ea08905f3073afdf8fbc9de36aeb6fbff Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:50:09 -0700 Subject: [PATCH 099/105] fixes other comments --- packages/camera/camera/lib/src/camera_controller.dart | 2 +- .../main/java/io/flutter/plugins/camera/VideoRenderer.java | 4 ++-- .../camera/camera_android/example/lib/camera_controller.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 589b3ad3a319..d30947712a7b 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -387,7 +387,7 @@ class CameraController extends ValueNotifier { } } - /// Sets the description of the camera + /// Sets the description of the camera. /// /// Throws a [CameraException] if setting the description fails. Future setDescription(CameraDescription description) async { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index 8dc135716010..b7128373b101 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -276,8 +276,8 @@ public void run() { configureOpenGL(); try { - // continuously pull frames from input surface texture and use videoRenderer to modify - // to correct rotation + // Continuously pull frames from input surface texture and use videoRenderer to modify + // to correct rotation. while (!Thread.interrupted()) { synchronized (surfaceTextureAvailableFrameLock) { diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 3a20e1dc5671..f9615ec310d0 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -277,7 +277,7 @@ class CameraController extends ValueNotifier { value = value.copyWith(isPreviewPaused: false); } - /// Sets the description of the camera + /// Sets the description of the camera. Future setDescription(CameraDescription description) async { if (value.isRecordingVideo) { await CameraPlatform.instance.setDescriptionWhileRecording(description); From 1eeaacc9451053f505b4662416119c3f1580a0c1 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Wed, 11 Jan 2023 15:52:35 -0700 Subject: [PATCH 100/105] fixes setDescription override in camera_preview_test --- packages/camera/camera/test/camera_preview_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index ad719389f670..cb072fe9168d 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -117,7 +117,7 @@ class FakeController extends ValueNotifier Future resumePreview() async {} @override - Future setDescriptionWhileRecording( + Future setDescription( CameraDescription description) async {} @override From a380c4d74effbc9a4930663e4ce957e5e0846917 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Thu, 12 Jan 2023 09:00:04 -0700 Subject: [PATCH 101/105] set description test --- .../example/integration_test/camera_test.dart | 20 ++++++++++++++++++ .../camera/test/camera_preview_test.dart | 3 +-- .../example/integration_test/camera_test.dart | 21 +++++++++++++++++++ .../example/integration_test/camera_test.dart | 21 +++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index b7e0fa83fab5..5338270828e0 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -288,6 +288,26 @@ void main() { expect(controller.description, cameras[1]); }); + testWidgets('Set description', (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.length < 2) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + sleep(const Duration(milliseconds: 500)); + await controller.setDescription(cameras[1]); + sleep(const Duration(milliseconds: 500)); + + expect(controller.description, cameras[1]); + }); + testWidgets( 'iOS image streaming with imageFormatGroup', (WidgetTester tester) async { diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index cb072fe9168d..37cd1af98334 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -117,8 +117,7 @@ class FakeController extends ValueNotifier Future resumePreview() async {} @override - Future setDescription( - CameraDescription description) async {} + Future setDescription(CameraDescription description) async {} @override CameraDescription get description => value.description; diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index c31e36077025..517a50d02cc5 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -229,6 +229,27 @@ void main() { expect(controller.description, cameras[1]); }); + testWidgets('Set description', (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.length < 2) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + sleep(const Duration(milliseconds: 500)); + await controller.setDescription(cameras[1]); + sleep(const Duration(milliseconds: 500)); + + expect(controller.description, cameras[1]); + }); + testWidgets( 'image streaming', (WidgetTester tester) async { diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 1fc61adfdfe1..5a6935a90114 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -222,6 +222,27 @@ void main() { expect(controller.description, cameras[1]); }); + testWidgets('Set description', (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.length < 2) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + sleep(const Duration(milliseconds: 500)); + await controller.setDescription(cameras[1]); + sleep(const Duration(milliseconds: 500)); + + expect(controller.description, cameras[1]); + }); + /// Start streaming with specifying the ImageFormatGroup. Future startStreaming(List cameras, ImageFormatGroup? imageFormatGroup) async { From 2fe002480e1c5f9dfb83372aa6ef730e2c464ab5 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 30 Jan 2023 15:11:56 -0700 Subject: [PATCH 102/105] versions --- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fb4ae277d111..4b7d3cb2f006 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing 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.3 +version: 0.10.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 4fec87e137de..0a99c969d37f 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.3 +version: 0.10.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 9dbbd5a4dff3..1a85946ec277 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.11 +version: 0.9.12 environment: sdk: ">=2.14.0 <3.0.0" From d1a2a94870260684f7939472b27dfa0f5d198e89 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 6 Feb 2023 11:05:22 -0700 Subject: [PATCH 103/105] removes changes on platform_interface_changes --- packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++-- packages/camera/camera_platform_interface/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 1a9a8e307242..b51eb9c78a43 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.0 +* Allows camera to be switched while video recording. * Updates minimum Flutter version to 3.0. ## 2.3.4 @@ -8,7 +9,6 @@ ## 2.3.3 -* Allows camera to be switched while video recording. * Updates code for stricter lint checks. ## 2.3.2 diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index ff024c0404ad..4cdb2855a156 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ 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 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.3.4 +version: 2.4.0 environment: sdk: '>=2.12.0 <3.0.0' From e74c2af22dda30ba9960f3c1ba74e3d8cd728153 Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 6 Feb 2023 11:09:23 -0700 Subject: [PATCH 104/105] points all packages to platform interface version 2.4 --- packages/camera/camera/pubspec.yaml | 12 +----------- packages/camera/camera_android/pubspec.yaml | 8 +------- packages/camera/camera_avfoundation/pubspec.yaml | 8 +------- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 4b7d3cb2f006..630d842071c8 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -23,7 +23,7 @@ flutter: dependencies: camera_android: ^0.10.1 camera_avfoundation: ^0.9.9 - camera_platform_interface: ^2.3.2 + camera_platform_interface: ^2.4.0 camera_web: ^0.3.1 flutter: sdk: flutter @@ -38,13 +38,3 @@ dev_dependencies: mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 video_player: ^2.0.0 - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_android: - path: ../../camera/camera_android - camera_avfoundation: - path: ../../camera/camera_avfoundation - camera_platform_interface: - path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 0a99c969d37f..c6ce575f2a93 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -18,7 +18,7 @@ flutter: dartPluginClass: AndroidCamera dependencies: - camera_platform_interface: ^2.3.1 + camera_platform_interface: ^2.4.0 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 @@ -30,9 +30,3 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 1a85946ec277..78c9156a7b79 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -17,7 +17,7 @@ flutter: dartPluginClass: AVFoundationCamera dependencies: - camera_platform_interface: ^2.3.1 + camera_platform_interface: ^2.4.0 flutter: sdk: flutter stream_transform: ^2.0.0 @@ -28,9 +28,3 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../camera/camera_platform_interface From 49e91c2cd00192be309d9739efcd786eca89231a Mon Sep 17 00:00:00 2001 From: BradenBagby Date: Mon, 6 Feb 2023 11:24:26 -0700 Subject: [PATCH 105/105] points to the new platform interface --- packages/camera/camera/example/pubspec.yaml | 5 ----- packages/camera/camera/pubspec.yaml | 8 ++++++++ packages/camera/camera_android/example/pubspec.yaml | 4 +--- packages/camera/camera_android_camerax/pubspec.yaml | 5 ----- packages/camera/camera_avfoundation/example/pubspec.yaml | 4 +--- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 2d3f87ddeee7..e63024076fef 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -30,8 +30,3 @@ dev_dependencies: flutter: uses-material-design: true - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../../camera/camera_platform_interface \ No newline at end of file diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 630d842071c8..12ed3f80aa1e 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -38,3 +38,11 @@ dev_dependencies: mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 video_player: ^2.0.0 + + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + camera_android: + path: ../../camera/camera_android + camera_avfoundation: + path: ../../camera/camera_avfoundation diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index b6e55d93f102..7a802a2a3a2d 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - camera_platform_interface: ^2.3.1 + camera_platform_interface: ^2.4.0 flutter: sdk: flutter path_provider: ^2.0.0 @@ -38,5 +38,3 @@ flutter: dependency_overrides: camera_android: path: ../../../camera/camera_android - camera_platform_interface: - path: ../../../camera/camera_platform_interface diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index cfb954c2b634..f57a0ec7752c 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -29,8 +29,3 @@ dev_dependencies: mockito: ^5.1.0 pigeon: ^3.2.6 - -# FOR TESTING ONLY. DO NOT MERGE. -dependency_overrides: - camera_platform_interface: - path: ../../camera/camera_platform_interface diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index de071eaf11af..436e7c5662e7 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - camera_platform_interface: ^2.2.0 + camera_platform_interface: ^2.4.0 flutter: sdk: flutter path_provider: ^2.0.0 @@ -38,5 +38,3 @@ flutter: dependency_overrides: camera_avfoundation: path: ../../../camera/camera_avfoundation - camera_platform_interface: - path: ../../../camera/camera_platform_interface