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

[camera]refactor flash mode and add unit test #4695

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
## NEXT

* Minor iOS internal code cleanup related to flash mode.

## 0.9.4+8

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

## 0.9.4+7

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

## 0.9.4+6

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; };
E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; };
E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */; };
E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; };
E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; };
E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; };
Expand Down Expand Up @@ -81,6 +82,7 @@
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>"; };
E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = "<group>"; };
E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = "<group>"; };
E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTFlashModeTests.m; sourceTree = "<group>"; };
E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = "<group>"; };
E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = "<group>"; };
E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -125,6 +127,7 @@
F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */,
F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */,
E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */,
E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */,
);
path = RunnerTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -388,6 +391,7 @@
buildActionMask = 2147483647;
files = (
03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */,
E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */,
033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */,
03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */,
E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.

@import camera.Test;
@import AVFoundation;
#import <XCTest/XCTest.h>

@interface FLTFlashModeTests : XCTestCase

@end

@implementation FLTFlashModeTests

- (void)testFLTGetFLTFlashModeForString {
XCTAssertEqual(FLTFlashModeOff, FLTGetFLTFlashModeForString(@"off"));
XCTAssertEqual(FLTFlashModeAuto, FLTGetFLTFlashModeForString(@"auto"));
XCTAssertEqual(FLTFlashModeAlways, FLTGetFLTFlashModeForString(@"always"));
XCTAssertEqual(FLTFlashModeTorch, FLTGetFLTFlashModeForString(@"torch"));
XCTAssertThrows(FLTGetFLTFlashModeForString(@"unkwown"));
}

- (void)testFLTGetAVCaptureFlashModeForFLTFlashMode {
XCTAssertEqual(AVCaptureFlashModeOff, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeOff));
XCTAssertEqual(AVCaptureFlashModeAuto, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAuto));
XCTAssertEqual(AVCaptureFlashModeOn, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAlways));
XCTAssertEqual(-1, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeTorch));
}

@end
59 changes: 9 additions & 50 deletions packages/camera/camera/ios/Classes/CameraPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "FLTThreadSafeFlutterResult.h"
#import "FLTThreadSafeMethodChannel.h"
#import "FLTThreadSafeTextureRegistry.h"
#import "FlashMode.h"

@interface FLTSavePhotoDelegate : NSObject <AVCapturePhotoCaptureDelegate>
@property(readonly, nonatomic) NSString *path;
Expand Down Expand Up @@ -113,34 +114,6 @@ - (void)captureOutput:(AVCapturePhotoOutput *)output
}
@end

// Mirrors FlashMode in flash_mode.dart
typedef enum {
FlashModeOff,
FlashModeAuto,
FlashModeAlways,
FlashModeTorch,
} FlashMode;

static FlashMode getFlashModeForString(NSString *mode) {
if ([mode isEqualToString:@"off"]) {
return FlashModeOff;
} else if ([mode isEqualToString:@"auto"]) {
return FlashModeAuto;
} else if ([mode isEqualToString:@"always"]) {
return FlashModeAlways;
} else if ([mode isEqualToString:@"torch"]) {
return FlashModeTorch;
} else {
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSURLErrorUnknown
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"Unknown flash mode %@", mode]
}];
@throw error;
}
}

static OSType getVideoFormatFromString(NSString *videoFormatString) {
if ([videoFormatString isEqualToString:@"bgra8888"]) {
return kCVPixelFormatType_32BGRA;
Expand All @@ -152,20 +125,6 @@ static OSType getVideoFormatFromString(NSString *videoFormatString) {
}
}

static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode(FlashMode mode) {
switch (mode) {
case FlashModeOff:
return AVCaptureFlashModeOff;
case FlashModeAuto:
return AVCaptureFlashModeAuto;
case FlashModeAlways:
return AVCaptureFlashModeOn;
case FlashModeTorch:
default:
return -1;
}
}

// Mirrors ExposureMode in camera.dart
typedef enum {
ExposureModeAuto,
Expand Down Expand Up @@ -349,7 +308,7 @@ @interface FLTCam : NSObject <FlutterTexture,
@property(assign, nonatomic) ResolutionPreset resolutionPreset;
@property(assign, nonatomic) ExposureMode exposureMode;
@property(assign, nonatomic) FocusMode focusMode;
@property(assign, nonatomic) FlashMode flashMode;
@property(assign, nonatomic) FLTFlashMode flashMode;
@property(assign, nonatomic) UIDeviceOrientation lockedCaptureOrientation;
@property(assign, nonatomic) CMTime lastVideoSampleTime;
@property(assign, nonatomic) CMTime lastAudioSampleTime;
Expand Down Expand Up @@ -386,7 +345,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
_captureSessionQueue = captureSessionQueue;
_captureSession = [[AVCaptureSession alloc] init];
_captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName];
_flashMode = _captureDevice.hasFlash ? FlashModeAuto : FlashModeOff;
_flashMode = _captureDevice.hasFlash ? FLTFlashModeAuto : FLTFlashModeOff;
_exposureMode = ExposureModeAuto;
_focusMode = FocusModeAuto;
_lockedCaptureOrientation = UIDeviceOrientationUnknown;
Expand Down Expand Up @@ -488,7 +447,7 @@ - (void)captureToFile:(FLTThreadSafeFlutterResult *)result API_AVAILABLE(ios(10)
[settings setHighResolutionPhotoEnabled:YES];
}

AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(_flashMode);
AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(_flashMode);
if (avFlashMode != -1) {
[settings setFlashMode:avFlashMode];
}
Expand Down Expand Up @@ -933,14 +892,14 @@ - (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result
}

- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr {
FlashMode mode;
FLTFlashMode mode;
@try {
mode = getFlashModeForString(modeStr);
mode = FLTGetFLTFlashModeForString(modeStr);
} @catch (NSError *e) {
[result sendError:e];
return;
}
if (mode == FlashModeTorch) {
if (mode == FLTFlashModeTorch) {
if (!_captureDevice.hasTorch) {
[result sendErrorWithCode:@"setFlashModeFailed"
message:@"Device does not support torch mode"
Expand All @@ -965,7 +924,7 @@ - (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSStri
details:nil];
return;
}
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(mode);
AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(mode);
if (![_capturePhotoOutput.supportedFlashModes
containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) {
[result sendErrorWithCode:@"setFlashModeFailed"
Expand Down Expand Up @@ -1281,7 +1240,7 @@ - (BOOL)setupWriterForPath:(NSString *)path {
[_audioOutput setSampleBufferDelegate:self queue:_captureSessionQueue];
}

if (_flashMode == FlashModeTorch) {
if (_flashMode == FLTFlashModeTorch) {
[self.captureDevice lockForConfiguration:nil];
[self.captureDevice setTorchMode:AVCaptureTorchModeOn];
[self.captureDevice unlockForConfiguration];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ framework module camera {

explicit module Test {
header "CameraPlugin_Test.h"
header "FlashMode.h"
}
}
32 changes: 32 additions & 0 deletions packages/camera/camera/ios/Classes/FlashMode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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.

#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
* Represents camera's flash mode. Mirrors `FlashMode` enum in flash_mode.dart.
*/
typedef NS_ENUM(NSInteger, FLTFlashMode) {
FLTFlashModeOff,
FLTFlashModeAuto,
FLTFlashModeAlways,
FLTFlashModeTorch,
};

/**
* Gets FLTFlashMode from its string representation.
* @param mode a string representation of the FLTFlashMode.
*/
extern FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode);

/**
* Gets AVCaptureFlashMode from FLTFlashMode.
* @param mode flash mode.
*/
extern AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode);

NS_ASSUME_NONNULL_END
39 changes: 39 additions & 0 deletions packages/camera/camera/ios/Classes/FlashMode.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.

#import "FlashMode.h"

FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode) {
if ([mode isEqualToString:@"off"]) {
return FLTFlashModeOff;
} else if ([mode isEqualToString:@"auto"]) {
return FLTFlashModeAuto;
} else if ([mode isEqualToString:@"always"]) {
return FLTFlashModeAlways;
} else if ([mode isEqualToString:@"torch"]) {
return FLTFlashModeTorch;
} else {
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSURLErrorUnknown
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"Unknown flash mode %@", mode]
}];
@throw error;
}
}

AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode) {
switch (mode) {
case FLTFlashModeOff:
return AVCaptureFlashModeOff;
case FLTFlashModeAuto:
return AVCaptureFlashModeAuto;
case FLTFlashModeAlways:
return AVCaptureFlashModeOn;
case FLTFlashModeTorch:
default:
return -1;
}
}