Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 9cd84bd

Browse files
authored
[camera] Fix iOS rotation issue (#3591)
* Fix iOS rotation issue * Fix orientation issues on iOS * Merged with master and added test * Test RotationBox turns according to device orientation * Fix formatting * Removed merge conflict tags from CHANGELOG * Fix license header in test
1 parent 9548bc2 commit 9cd84bd

File tree

5 files changed

+324
-56
lines changed

5 files changed

+324
-56
lines changed

packages/camera/camera/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.8.1
2+
3+
* Solved a rotation issue on iOS which caused the default preview to be displayed as landscape right instead of portrait.
4+
15
## 0.8.0
26

37
* Stable null safety release.

packages/camera/camera/ios/Classes/CameraPlugin.m

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ @interface FLTCam : NSObject <FlutterTexture,
345345

346346
@implementation FLTCam {
347347
dispatch_queue_t _dispatchQueue;
348+
UIDeviceOrientation _deviceOrientation;
348349
}
349350
// Format used for video and image streaming.
350351
FourCharCode videoFormat = kCVPixelFormatType_32BGRA;
@@ -353,6 +354,7 @@ @implementation FLTCam {
353354
- (instancetype)initWithCameraName:(NSString *)cameraName
354355
resolutionPreset:(NSString *)resolutionPreset
355356
enableAudio:(BOOL)enableAudio
357+
orientation:(UIDeviceOrientation)orientation
356358
dispatchQueue:(dispatch_queue_t)dispatchQueue
357359
error:(NSError **)error {
358360
self = [super init];
@@ -370,6 +372,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
370372
_exposureMode = ExposureModeAuto;
371373
_focusMode = FocusModeAuto;
372374
_lockedCaptureOrientation = UIDeviceOrientationUnknown;
375+
_deviceOrientation = orientation;
373376

374377
NSError *localError = nil;
375378
_captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice
@@ -389,10 +392,11 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
389392
AVCaptureConnection *connection =
390393
[AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports
391394
output:_captureVideoOutput];
395+
392396
if ([_captureDevice position] == AVCaptureDevicePositionFront) {
393397
connection.videoMirrored = YES;
394398
}
395-
connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
399+
396400
[_captureSession addInputWithNoConnections:_captureVideoInput];
397401
[_captureSession addOutputWithNoConnections:_captureVideoOutput];
398402
[_captureSession addConnection:connection];
@@ -406,6 +410,8 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
406410
[_motionManager startAccelerometerUpdates];
407411

408412
[self setCaptureSessionPreset:_resolutionPreset];
413+
[self updateOrientation];
414+
409415
return self;
410416
}
411417

@@ -417,6 +423,40 @@ - (void)stop {
417423
[_captureSession stopRunning];
418424
}
419425

426+
- (void)setDeviceOrientation:(UIDeviceOrientation)orientation {
427+
if (_deviceOrientation == orientation) {
428+
return;
429+
}
430+
431+
_deviceOrientation = orientation;
432+
[self updateOrientation];
433+
}
434+
435+
- (void)updateOrientation {
436+
if (_isRecording) {
437+
return;
438+
}
439+
440+
UIDeviceOrientation orientation = (_lockedCaptureOrientation != UIDeviceOrientationUnknown)
441+
? _lockedCaptureOrientation
442+
: _deviceOrientation;
443+
444+
[self updateOrientation:orientation forCaptureOutput:_capturePhotoOutput];
445+
[self updateOrientation:orientation forCaptureOutput:_captureVideoOutput];
446+
}
447+
448+
- (void)updateOrientation:(UIDeviceOrientation)orientation
449+
forCaptureOutput:(AVCaptureOutput *)captureOutput {
450+
if (!captureOutput) {
451+
return;
452+
}
453+
454+
AVCaptureConnection *connection = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
455+
if (connection && connection.isVideoOrientationSupported) {
456+
connection.videoOrientation = [self getVideoOrientationForDeviceOrientation:orientation];
457+
}
458+
}
459+
420460
- (void)captureToFile:(FlutterResult)result API_AVAILABLE(ios(10)) {
421461
AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings];
422462
if (_resolutionPreset == max) {
@@ -437,18 +477,6 @@ - (void)captureToFile:(FlutterResult)result API_AVAILABLE(ios(10)) {
437477
return;
438478
}
439479

440-
AVCaptureConnection *connection = [_capturePhotoOutput connectionWithMediaType:AVMediaTypeVideo];
441-
442-
if (connection) {
443-
if (_lockedCaptureOrientation != UIDeviceOrientationUnknown) {
444-
connection.videoOrientation =
445-
[self getVideoOrientationForDeviceOrientation:_lockedCaptureOrientation];
446-
} else {
447-
connection.videoOrientation =
448-
[self getVideoOrientationForDeviceOrientation:[[UIDevice currentDevice] orientation]];
449-
}
450-
}
451-
452480
[_capturePhotoOutput capturePhotoWithSettings:settings
453481
delegate:[[FLTSavePhotoDelegate alloc] initWithPath:path
454482
result:result]];
@@ -812,9 +840,11 @@ - (void)startVideoRecordingWithResult:(FlutterResult)result {
812840
- (void)stopVideoRecordingWithResult:(FlutterResult)result {
813841
if (_isRecording) {
814842
_isRecording = NO;
843+
815844
if (_videoWriter.status != AVAssetWriterStatusUnknown) {
816845
[_videoWriter finishWritingWithCompletionHandler:^{
817846
if (self->_videoWriter.status == AVAssetWriterStatusCompleted) {
847+
[self updateOrientation];
818848
result(self->_videoRecordingPath);
819849
self->_videoRecordingPath = nil;
820850
} else {
@@ -854,12 +884,18 @@ - (void)lockCaptureOrientationWithResult:(FlutterResult)result
854884
result(getFlutterError(e));
855885
return;
856886
}
857-
_lockedCaptureOrientation = orientation;
887+
888+
if (_lockedCaptureOrientation != orientation) {
889+
_lockedCaptureOrientation = orientation;
890+
[self updateOrientation];
891+
}
892+
858893
result(nil);
859894
}
860895

861896
- (void)unlockCaptureOrientationWithResult:(FlutterResult)result {
862897
_lockedCaptureOrientation = UIDeviceOrientationUnknown;
898+
[self updateOrientation];
863899
result(nil);
864900
}
865901

@@ -1101,6 +1137,7 @@ - (BOOL)setupWriterForPath:(NSString *)path {
11011137
if (_enableAudio && !_isAudioSetup) {
11021138
[self setUpCaptureSessionForAudio];
11031139
}
1140+
11041141
_videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL
11051142
fileType:AVFileTypeMPEG4
11061143
error:&error];
@@ -1109,11 +1146,9 @@ - (BOOL)setupWriterForPath:(NSString *)path {
11091146
[_methodChannel invokeMethod:errorMethod arguments:error.description];
11101147
return NO;
11111148
}
1112-
NSDictionary *videoSettings = [NSDictionary
1113-
dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey,
1114-
[NSNumber numberWithInt:_previewSize.width], AVVideoWidthKey,
1115-
[NSNumber numberWithInt:_previewSize.height], AVVideoHeightKey,
1116-
nil];
1149+
1150+
NSDictionary *videoSettings = [_captureVideoOutput
1151+
recommendedVideoSettingsForAssetWriterWithOutputFileType:AVFileTypeMPEG4];
11171152
_videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
11181153
outputSettings:videoSettings];
11191154

@@ -1124,14 +1159,7 @@ - (BOOL)setupWriterForPath:(NSString *)path {
11241159
}];
11251160

11261161
NSParameterAssert(_videoWriterInput);
1127-
CGFloat rotationDegrees;
1128-
if (_lockedCaptureOrientation != UIDeviceOrientationUnknown) {
1129-
rotationDegrees = [self getRotationFromDeviceOrientation:_lockedCaptureOrientation];
1130-
} else {
1131-
rotationDegrees = [self getRotationFromDeviceOrientation:[UIDevice currentDevice].orientation];
1132-
}
11331162

1134-
_videoWriterInput.transform = CGAffineTransformMakeRotation(rotationDegrees * M_PI / 180);
11351163
_videoWriterInput.expectsMediaDataInRealTime = YES;
11361164

11371165
// Add the audio input
@@ -1194,21 +1222,6 @@ - (void)setUpCaptureSessionForAudio {
11941222
}
11951223
}
11961224
}
1197-
1198-
- (int)getRotationFromDeviceOrientation:(UIDeviceOrientation)orientation {
1199-
switch (orientation) {
1200-
case UIDeviceOrientationPortraitUpsideDown:
1201-
return 270;
1202-
case UIDeviceOrientationLandscapeRight:
1203-
return 180;
1204-
case UIDeviceOrientationLandscapeLeft:
1205-
return 0;
1206-
case UIDeviceOrientationPortrait:
1207-
default:
1208-
return 90;
1209-
};
1210-
}
1211-
12121225
@end
12131226

12141227
@interface CameraPlugin ()
@@ -1257,7 +1270,13 @@ - (void)startOrientationListener {
12571270

12581271
- (void)orientationChanged:(NSNotification *)note {
12591272
UIDevice *device = note.object;
1260-
[self sendDeviceOrientation:device.orientation];
1273+
UIDeviceOrientation orientation = device.orientation;
1274+
1275+
if (_camera) {
1276+
[_camera setDeviceOrientation:orientation];
1277+
}
1278+
1279+
[self sendDeviceOrientation:orientation];
12611280
}
12621281

12631282
- (void)sendDeviceOrientation:(UIDeviceOrientation)orientation {
@@ -1318,6 +1337,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re
13181337
FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName
13191338
resolutionPreset:resolutionPreset
13201339
enableAudio:[enableAudio boolValue]
1340+
orientation:[[UIDevice currentDevice] orientation]
13211341
dispatchQueue:_dispatchQueue
13221342
error:&error];
13231343

packages/camera/camera/lib/src/camera_preview.dart

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// found in the LICENSE file.
44

55
import 'package:camera/camera.dart';
6-
import 'package:camera_platform_interface/camera_platform_interface.dart';
76
import 'package:flutter/foundation.dart';
87
import 'package:flutter/material.dart';
98
import 'package:flutter/services.dart';
@@ -29,23 +28,23 @@ class CameraPreview extends StatelessWidget {
2928
child: Stack(
3029
fit: StackFit.expand,
3130
children: [
32-
RotatedBox(
33-
quarterTurns: _getQuarterTurns(),
34-
child:
35-
CameraPlatform.instance.buildPreview(controller.cameraId),
36-
),
31+
_wrapInRotatedBox(child: controller.buildPreview()),
3732
child ?? Container(),
3833
],
3934
),
4035
)
4136
: Container();
4237
}
4338

44-
DeviceOrientation _getApplicableOrientation() {
45-
return controller.value.isRecordingVideo
46-
? controller.value.recordingOrientation!
47-
: (controller.value.lockedCaptureOrientation ??
48-
controller.value.deviceOrientation);
39+
Widget _wrapInRotatedBox({required Widget child}) {
40+
if (defaultTargetPlatform != TargetPlatform.android) {
41+
return child;
42+
}
43+
44+
return RotatedBox(
45+
quarterTurns: _getQuarterTurns(),
46+
child: child,
47+
);
4948
}
5049

5150
bool _isLandscape() {
@@ -54,13 +53,19 @@ class CameraPreview extends StatelessWidget {
5453
}
5554

5655
int _getQuarterTurns() {
57-
int platformOffset = defaultTargetPlatform == TargetPlatform.iOS ? 1 : 0;
5856
Map<DeviceOrientation, int> turns = {
5957
DeviceOrientation.portraitUp: 0,
6058
DeviceOrientation.landscapeLeft: 1,
6159
DeviceOrientation.portraitDown: 2,
6260
DeviceOrientation.landscapeRight: 3,
6361
};
64-
return turns[_getApplicableOrientation()]! + platformOffset;
62+
return turns[_getApplicableOrientation()]!;
63+
}
64+
65+
DeviceOrientation _getApplicableOrientation() {
66+
return controller.value.isRecordingVideo
67+
? controller.value.recordingOrientation!
68+
: (controller.value.lockedCaptureOrientation ??
69+
controller.value.deviceOrientation);
6570
}
6671
}

packages/camera/camera/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: camera
22
description: A Flutter plugin for getting information about and controlling the
33
camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
44
and streaming image buffers to dart.
5-
version: 0.8.0
5+
version: 0.8.1
66
homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera
77

88
dependencies:

0 commit comments

Comments
 (0)