[camera]manages ios camera's orientation states on capture session queue (#4748)
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index f846774..c05ea24 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.4+11
+
+* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions.
+
## 0.9.4+10
* iOS performance improvement by moving file writing from the main queue to a background IO queue.
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m
index efd65a4..50f3416 100644
--- a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m
@@ -49,6 +49,28 @@
OCMVerifyAll(mockMessenger);
}
+- (void)testOrientationUpdateMustBeOnCaptureSessionQueue {
+ XCTestExpectation *queueExpectation = [self
+ expectationWithDescription:@"Orientation update must happen on the capture session queue"];
+
+ CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil];
+ const char *captureSessionQueueSpecific = "capture_session_queue";
+ dispatch_queue_set_specific(camera.captureSessionQueue, captureSessionQueueSpecific,
+ (void *)captureSessionQueueSpecific, NULL);
+ FLTCam *mockCam = OCMClassMock([FLTCam class]);
+ camera.camera = mockCam;
+ OCMStub([mockCam setDeviceOrientation:UIDeviceOrientationLandscapeLeft])
+ .andDo(^(NSInvocation *invocation) {
+ if (dispatch_get_specific(captureSessionQueueSpecific)) {
+ [queueExpectation fulfill];
+ }
+ });
+
+ [camera orientationChanged:
+ [self createMockNotificationForOrientation:UIDeviceOrientationLandscapeLeft]];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
- (void)rotate:(UIDeviceOrientation)deviceOrientation
expectedChannelOrientation:(NSString *)channelOrientation
cameraPlugin:(CameraPlugin *)cameraPlugin
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m
index a0adb70..97e9954 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera/ios/Classes/CameraPlugin.m
@@ -17,7 +17,6 @@
@interface CameraPlugin ()
@property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry;
@property(readonly, nonatomic) NSObject<FlutterBinaryMessenger> *messenger;
-@property(readonly, nonatomic) FLTCam *camera;
@property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel;
@end
@@ -69,11 +68,12 @@
return;
}
- if (_camera) {
- [_camera setDeviceOrientation:orientation];
- }
-
- [self sendDeviceOrientation:orientation];
+ dispatch_async(self.captureSessionQueue, ^{
+ // `FLTCam::setDeviceOrientation` must be called on capture session queue.
+ [self.camera setDeviceOrientation:orientation];
+ // `CameraPlugin::sendDeviceOrientation` can be called on any queue.
+ [self sendDeviceOrientation:orientation];
+ });
}
- (void)sendDeviceOrientation:(UIDeviceOrientation)orientation {
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h
index e36b4ab..826b050 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h
+++ b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h
@@ -5,14 +5,18 @@
// This header is available in the Test module. Import via "@import camera.Test;"
#import <camera/CameraPlugin.h>
+#import <camera/FLTCam.h>
#import <camera/FLTThreadSafeFlutterResult.h>
/// Methods exposed for unit testing.
@interface CameraPlugin ()
-// All FLTCam's state access and capture session related operations should be on run on this queue.
+/// All FLTCam's state access and capture session related operations should be on run on this queue.
@property(nonatomic, strong) dispatch_queue_t captureSessionQueue;
+/// An internal camera object that manages camera's state and performs camera operations.
+@property(nonatomic, strong) FLTCam *camera;
+
/// Inject @p FlutterTextureRegistry and @p FlutterBinaryMessenger for unit testing.
- (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry
messenger:(NSObject<FlutterBinaryMessenger> *)messenger
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index 4516e55..f522608 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
Dart.
repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.9.4+10
+version: 0.9.4+11
environment:
sdk: ">=2.14.0 <3.0.0"