Skip to content

Commit 3cc18d9

Browse files
authored
[camera]refactor flash mode and add unit test (flutter#4695)
1 parent e282dfb commit 3cc18d9

File tree

7 files changed

+120
-51
lines changed

7 files changed

+120
-51
lines changed

packages/camera/camera/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
## NEXT
2+
3+
* Minor iOS internal code cleanup related to flash mode.
4+
15
## 0.9.4+8
26

37
* Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS.
48

59
## 0.9.4+7
610

711
* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition.
8-
* Minor iOS internal code cleanup.
12+
* Minor iOS internal code cleanup related to dispatch queue.
913

1014
## 0.9.4+6
1115

packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
2323
E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; };
2424
E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; };
25+
E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */; };
2526
E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; };
2627
E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; };
2728
E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; };
@@ -81,6 +82,7 @@
8182
A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
8283
E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = "<group>"; };
8384
E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = "<group>"; };
85+
E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTFlashModeTests.m; sourceTree = "<group>"; };
8486
E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = "<group>"; };
8587
E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = "<group>"; };
8688
E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = "<group>"; };
@@ -125,6 +127,7 @@
125127
F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */,
126128
F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */,
127129
E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */,
130+
E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */,
128131
);
129132
path = RunnerTests;
130133
sourceTree = "<group>";
@@ -388,6 +391,7 @@
388391
buildActionMask = 2147483647;
389392
files = (
390393
03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */,
394+
E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */,
391395
033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */,
392396
03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */,
393397
E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@import camera.Test;
6+
@import AVFoundation;
7+
#import <XCTest/XCTest.h>
8+
9+
@interface FLTFlashModeTests : XCTestCase
10+
11+
@end
12+
13+
@implementation FLTFlashModeTests
14+
15+
- (void)testFLTGetFLTFlashModeForString {
16+
XCTAssertEqual(FLTFlashModeOff, FLTGetFLTFlashModeForString(@"off"));
17+
XCTAssertEqual(FLTFlashModeAuto, FLTGetFLTFlashModeForString(@"auto"));
18+
XCTAssertEqual(FLTFlashModeAlways, FLTGetFLTFlashModeForString(@"always"));
19+
XCTAssertEqual(FLTFlashModeTorch, FLTGetFLTFlashModeForString(@"torch"));
20+
XCTAssertThrows(FLTGetFLTFlashModeForString(@"unkwown"));
21+
}
22+
23+
- (void)testFLTGetAVCaptureFlashModeForFLTFlashMode {
24+
XCTAssertEqual(AVCaptureFlashModeOff, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeOff));
25+
XCTAssertEqual(AVCaptureFlashModeAuto, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAuto));
26+
XCTAssertEqual(AVCaptureFlashModeOn, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAlways));
27+
XCTAssertEqual(-1, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeTorch));
28+
}
29+
30+
@end

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

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#import "FLTThreadSafeFlutterResult.h"
1515
#import "FLTThreadSafeMethodChannel.h"
1616
#import "FLTThreadSafeTextureRegistry.h"
17+
#import "FlashMode.h"
1718

1819
@interface FLTSavePhotoDelegate : NSObject <AVCapturePhotoCaptureDelegate>
1920
@property(readonly, nonatomic) NSString *path;
@@ -113,34 +114,6 @@ - (void)captureOutput:(AVCapturePhotoOutput *)output
113114
}
114115
@end
115116

116-
// Mirrors FlashMode in flash_mode.dart
117-
typedef enum {
118-
FlashModeOff,
119-
FlashModeAuto,
120-
FlashModeAlways,
121-
FlashModeTorch,
122-
} FlashMode;
123-
124-
static FlashMode getFlashModeForString(NSString *mode) {
125-
if ([mode isEqualToString:@"off"]) {
126-
return FlashModeOff;
127-
} else if ([mode isEqualToString:@"auto"]) {
128-
return FlashModeAuto;
129-
} else if ([mode isEqualToString:@"always"]) {
130-
return FlashModeAlways;
131-
} else if ([mode isEqualToString:@"torch"]) {
132-
return FlashModeTorch;
133-
} else {
134-
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
135-
code:NSURLErrorUnknown
136-
userInfo:@{
137-
NSLocalizedDescriptionKey : [NSString
138-
stringWithFormat:@"Unknown flash mode %@", mode]
139-
}];
140-
@throw error;
141-
}
142-
}
143-
144117
static OSType getVideoFormatFromString(NSString *videoFormatString) {
145118
if ([videoFormatString isEqualToString:@"bgra8888"]) {
146119
return kCVPixelFormatType_32BGRA;
@@ -152,20 +125,6 @@ static OSType getVideoFormatFromString(NSString *videoFormatString) {
152125
}
153126
}
154127

155-
static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode(FlashMode mode) {
156-
switch (mode) {
157-
case FlashModeOff:
158-
return AVCaptureFlashModeOff;
159-
case FlashModeAuto:
160-
return AVCaptureFlashModeAuto;
161-
case FlashModeAlways:
162-
return AVCaptureFlashModeOn;
163-
case FlashModeTorch:
164-
default:
165-
return -1;
166-
}
167-
}
168-
169128
// Mirrors ExposureMode in camera.dart
170129
typedef enum {
171130
ExposureModeAuto,
@@ -349,7 +308,7 @@ @interface FLTCam : NSObject <FlutterTexture,
349308
@property(assign, nonatomic) ResolutionPreset resolutionPreset;
350309
@property(assign, nonatomic) ExposureMode exposureMode;
351310
@property(assign, nonatomic) FocusMode focusMode;
352-
@property(assign, nonatomic) FlashMode flashMode;
311+
@property(assign, nonatomic) FLTFlashMode flashMode;
353312
@property(assign, nonatomic) UIDeviceOrientation lockedCaptureOrientation;
354313
@property(assign, nonatomic) CMTime lastVideoSampleTime;
355314
@property(assign, nonatomic) CMTime lastAudioSampleTime;
@@ -386,7 +345,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
386345
_captureSessionQueue = captureSessionQueue;
387346
_captureSession = [[AVCaptureSession alloc] init];
388347
_captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName];
389-
_flashMode = _captureDevice.hasFlash ? FlashModeAuto : FlashModeOff;
348+
_flashMode = _captureDevice.hasFlash ? FLTFlashModeAuto : FLTFlashModeOff;
390349
_exposureMode = ExposureModeAuto;
391350
_focusMode = FocusModeAuto;
392351
_lockedCaptureOrientation = UIDeviceOrientationUnknown;
@@ -488,7 +447,7 @@ - (void)captureToFile:(FLTThreadSafeFlutterResult *)result API_AVAILABLE(ios(10)
488447
[settings setHighResolutionPhotoEnabled:YES];
489448
}
490449

491-
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(_flashMode);
450+
AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(_flashMode);
492451
if (avFlashMode != -1) {
493452
[settings setFlashMode:avFlashMode];
494453
}
@@ -933,14 +892,14 @@ - (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result
933892
}
934893

935894
- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr {
936-
FlashMode mode;
895+
FLTFlashMode mode;
937896
@try {
938-
mode = getFlashModeForString(modeStr);
897+
mode = FLTGetFLTFlashModeForString(modeStr);
939898
} @catch (NSError *e) {
940899
[result sendError:e];
941900
return;
942901
}
943-
if (mode == FlashModeTorch) {
902+
if (mode == FLTFlashModeTorch) {
944903
if (!_captureDevice.hasTorch) {
945904
[result sendErrorWithCode:@"setFlashModeFailed"
946905
message:@"Device does not support torch mode"
@@ -965,7 +924,7 @@ - (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSStri
965924
details:nil];
966925
return;
967926
}
968-
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(mode);
927+
AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(mode);
969928
if (![_capturePhotoOutput.supportedFlashModes
970929
containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) {
971930
[result sendErrorWithCode:@"setFlashModeFailed"
@@ -1281,7 +1240,7 @@ - (BOOL)setupWriterForPath:(NSString *)path {
12811240
[_audioOutput setSampleBufferDelegate:self queue:_captureSessionQueue];
12821241
}
12831242

1284-
if (_flashMode == FlashModeTorch) {
1243+
if (_flashMode == FLTFlashModeTorch) {
12851244
[self.captureDevice lockForConfiguration:nil];
12861245
[self.captureDevice setTorchMode:AVCaptureTorchModeOn];
12871246
[self.captureDevice unlockForConfiguration];

packages/camera/camera/ios/Classes/CameraPlugin.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ framework module camera {
66

77
explicit module Test {
88
header "CameraPlugin_Test.h"
9+
header "FlashMode.h"
910
}
1011
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import <AVFoundation/AVFoundation.h>
6+
#import <Foundation/Foundation.h>
7+
8+
NS_ASSUME_NONNULL_BEGIN
9+
10+
/**
11+
* Represents camera's flash mode. Mirrors `FlashMode` enum in flash_mode.dart.
12+
*/
13+
typedef NS_ENUM(NSInteger, FLTFlashMode) {
14+
FLTFlashModeOff,
15+
FLTFlashModeAuto,
16+
FLTFlashModeAlways,
17+
FLTFlashModeTorch,
18+
};
19+
20+
/**
21+
* Gets FLTFlashMode from its string representation.
22+
* @param mode a string representation of the FLTFlashMode.
23+
*/
24+
extern FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode);
25+
26+
/**
27+
* Gets AVCaptureFlashMode from FLTFlashMode.
28+
* @param mode flash mode.
29+
*/
30+
extern AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode);
31+
32+
NS_ASSUME_NONNULL_END
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import "FlashMode.h"
6+
7+
FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode) {
8+
if ([mode isEqualToString:@"off"]) {
9+
return FLTFlashModeOff;
10+
} else if ([mode isEqualToString:@"auto"]) {
11+
return FLTFlashModeAuto;
12+
} else if ([mode isEqualToString:@"always"]) {
13+
return FLTFlashModeAlways;
14+
} else if ([mode isEqualToString:@"torch"]) {
15+
return FLTFlashModeTorch;
16+
} else {
17+
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
18+
code:NSURLErrorUnknown
19+
userInfo:@{
20+
NSLocalizedDescriptionKey : [NSString
21+
stringWithFormat:@"Unknown flash mode %@", mode]
22+
}];
23+
@throw error;
24+
}
25+
}
26+
27+
AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode) {
28+
switch (mode) {
29+
case FLTFlashModeOff:
30+
return AVCaptureFlashModeOff;
31+
case FLTFlashModeAuto:
32+
return AVCaptureFlashModeAuto;
33+
case FLTFlashModeAlways:
34+
return AVCaptureFlashModeOn;
35+
case FLTFlashModeTorch:
36+
default:
37+
return -1;
38+
}
39+
}

0 commit comments

Comments
 (0)