Skip to content
Closed
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
4 changes: 4 additions & 0 deletions packages/camera/camera_avfoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.21+5

* Adds accurate values for `lensType` in `CameraDescription`.

## 0.9.21+4

* Migrates `updateOrientation` and `setCaptureSessionPreset` methods to Swift.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,38 +28,62 @@ final class AvailableCamerasTest: XCTestCase {
)
}

private func fakeNativeCameraList() -> [MockCaptureDevice] {
var cameras: [MockCaptureDevice] = []

let wideAngleCamera = MockCaptureDevice()
wideAngleCamera.uniqueID = "0"
wideAngleCamera.position = .back
wideAngleCamera.deviceType = .builtInWideAngleCamera

let frontFacingCamera = MockCaptureDevice()
frontFacingCamera.uniqueID = "1"
frontFacingCamera.position = .front
frontFacingCamera.deviceType = .builtInWideAngleCamera

let ultraWideCamera = MockCaptureDevice()
ultraWideCamera.uniqueID = "2"
ultraWideCamera.position = .back
ultraWideCamera.deviceType = .builtInUltraWideCamera

let telephotoCamera = MockCaptureDevice()
telephotoCamera.uniqueID = "3"
telephotoCamera.position = .back
telephotoCamera.deviceType = .builtInTelephotoCamera

// the order of `cameras` is important. It must match the order of the
// discoveryDevices list used by availableCameras()
cameras = [
wideAngleCamera, frontFacingCamera, telephotoCamera, ultraWideCamera,
]

return cameras
}

func testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone() {
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer)
let expectation = self.expectation(description: "Result finished")

mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in
// iPhone 13 Cameras:
let wideAngleCamera = MockCaptureDevice()
wideAngleCamera.uniqueID = "0"
wideAngleCamera.position = .back

let frontFacingCamera = MockCaptureDevice()
frontFacingCamera.uniqueID = "1"
frontFacingCamera.position = .front

let ultraWideCamera = MockCaptureDevice()
ultraWideCamera.uniqueID = "2"
ultraWideCamera.position = .back

let telephotoCamera = MockCaptureDevice()
telephotoCamera.uniqueID = "3"
telephotoCamera.position = .back
// We'll stub the discovery session and return this list of fake cameras
let nativeCameras = fakeNativeCameraList()

var requiredTypes: [AVCaptureDevice.DeviceType] = [
.builtInWideAngleCamera, .builtInTelephotoCamera, .builtInUltraWideCamera,
]
var cameras = [wideAngleCamera, frontFacingCamera, telephotoCamera, ultraWideCamera]
// The order of expectedDeviceTypesToBeRequested is important. We will use
// this in our discovery session stub to confirm that availableCameras()
// requests the correct DeviceTypes in the correct order.
var expectedDeviceTypesToBeRequested: [AVCaptureDevice.DeviceType] = [
.builtInWideAngleCamera,
.builtInTelephotoCamera,
.builtInUltraWideCamera,
]

XCTAssertEqual(deviceTypes, requiredTypes)
mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in
// confirm that availableCameras() made the
// expected call to our discovery stub
XCTAssertEqual(deviceTypes, expectedDeviceTypesToBeRequested)
XCTAssertEqual(mediaType, .video)
XCTAssertEqual(position, .unspecified)
return cameras
return nativeCameras
}

var resultValue: [FCPPlatformCameraDescription]?
Expand All @@ -71,7 +95,7 @@ final class AvailableCamerasTest: XCTestCase {
waitForExpectations(timeout: 30, handler: nil)

// Verify the result.
XCTAssertEqual(resultValue?.count, 4)
XCTAssertEqual(resultValue?.count, nativeCameras.count)
}

func testAvailableCamerasShouldReturnTwoCamerasOnDualCameraIPhone() {
Expand All @@ -89,14 +113,8 @@ final class AvailableCamerasTest: XCTestCase {
frontFacingCamera.uniqueID = "1"
frontFacingCamera.position = .front

var requiredTypes: [AVCaptureDevice.DeviceType] = [
.builtInWideAngleCamera, .builtInTelephotoCamera, .builtInUltraWideCamera,
]
let cameras = [wideAngleCamera, frontFacingCamera]

XCTAssertEqual(deviceTypes, requiredTypes)
XCTAssertEqual(mediaType, .video)
XCTAssertEqual(position, .unspecified)
return cameras
}

Expand All @@ -122,14 +140,8 @@ final class AvailableCamerasTest: XCTestCase {
unspecifiedCamera.uniqueID = "0"
unspecifiedCamera.position = .unspecified

var requiredTypes: [AVCaptureDevice.DeviceType] = [
.builtInWideAngleCamera, .builtInTelephotoCamera, .builtInUltraWideCamera,
]
let cameras = [unspecifiedCamera]

XCTAssertEqual(deviceTypes, requiredTypes)
XCTAssertEqual(mediaType, .video)
XCTAssertEqual(position, .unspecified)
return cameras
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class MockCaptureDevice: NSObject, FLTCaptureDevice {

var uniqueID = ""
var position = AVCaptureDevice.Position.unspecified
var deviceType = AVCaptureDevice.DeviceType.builtInWideAngleCamera

var activeFormat: FLTCaptureDeviceFormat {
get {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,23 @@ extension CameraPlugin: FCPCameraApi {
lensFacing = .external
}

var lensType: FCPPlatformCameraLensType

switch device.deviceType {
case .builtInWideAngleCamera:
lensType = .builtInWideAngleCamera
case .builtInTelephotoCamera:
lensType = .builtInTelephotoCamera
case .builtInUltraWideCamera:
lensType = .builtInUltraWideCamera
default:
lensType = .unknown
}
Comment on lines +163 to +172

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better forward-compatibility, it's recommended to use @unknown default instead of default when switching over non-frozen enums like AVCaptureDevice.DeviceType. This will cause the compiler to issue a warning if a new case is added in a future SDK version, reminding us to handle it explicitly. This pattern is already used for device.position in this same file.

Suggested change
switch device.deviceType {
case .builtInWideAngleCamera:
lensType = .builtInWideAngleCamera
case .builtInTelephotoCamera:
lensType = .builtInTelephotoCamera
case .builtInUltraWideCamera:
lensType = .builtInUltraWideCamera
default:
lensType = .unknown
}
switch device.deviceType {
case .builtInWideAngleCamera:
lensType = .builtInWideAngleCamera
case .builtInTelephotoCamera:
lensType = .builtInTelephotoCamera
case .builtInUltraWideCamera:
lensType = .builtInUltraWideCamera
@unknown default:
lensType = .unknown
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I'd agree with this suggestion, but in this specific case I think it's better to leave the "old" style default in place. Switching to @unknown default would require naming each of the current 15 values here. That would include some deprecated values, and also some non-camera devices like microphones and depth sensors. This might be something for closer consideration in a later PR.

But I'd welcome other perspectives from the reviewers.


let cameraDescription = FCPPlatformCameraDescription.make(
withName: device.uniqueID,
lensDirection: lensFacing
lensDirection: lensFacing,
lensType: lensType
)
reply.append(cameraDescription)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ - (AVCaptureDevicePosition)position {
return self.device.position;
}

// lens type
- (AVCaptureDeviceType)deviceType {
return self.device.deviceType;
}

// Format/Configuration
- (NSObject<FLTCaptureDeviceFormat> *)activeFormat {
return [[FLTDefaultCaptureDeviceFormat alloc] initWithFormat:self.device.activeFormat];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN
// Position/Orientation
@property(nonatomic, readonly) AVCaptureDevicePosition position;

// Device type
@property(nonatomic, readonly) AVCaptureDeviceType deviceType;

// Format/Configuration
@property(nonatomic, retain) NSObject<FLTCaptureDeviceFormat> *activeFormat;
@property(nonatomic, readonly) NSArray<NSObject<FLTCaptureDeviceFormat> *> *formats;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v22.4.2), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon

#import <Foundation/Foundation.h>
Expand Down Expand Up @@ -114,6 +114,19 @@ typedef NS_ENUM(NSUInteger, FCPPlatformResolutionPreset) {
- (instancetype)initWithValue:(FCPPlatformResolutionPreset)value;
@end

typedef NS_ENUM(NSUInteger, FCPPlatformCameraLensType) {
FCPPlatformCameraLensTypeBuiltInWideAngleCamera = 0,
FCPPlatformCameraLensTypeBuiltInUltraWideCamera = 1,
FCPPlatformCameraLensTypeBuiltInTelephotoCamera = 2,
FCPPlatformCameraLensTypeUnknown = 3,
};

/// Wrapper for FCPPlatformCameraLensType to allow for nullability.
@interface FCPPlatformCameraLensTypeBox : NSObject
@property(nonatomic, assign) FCPPlatformCameraLensType value;
- (instancetype)initWithValue:(FCPPlatformCameraLensType)value;
@end

@class FCPPlatformCameraDescription;
@class FCPPlatformCameraState;
@class FCPPlatformMediaSettings;
Expand All @@ -124,11 +137,13 @@ typedef NS_ENUM(NSUInteger, FCPPlatformResolutionPreset) {
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithName:(NSString *)name
lensDirection:(FCPPlatformCameraLensDirection)lensDirection;
lensDirection:(FCPPlatformCameraLensDirection)lensDirection
lensType:(FCPPlatformCameraLensType)lensType;
/// The name of the camera device.
@property(nonatomic, copy) NSString *name;
/// The direction the camera is facing.
@property(nonatomic, assign) FCPPlatformCameraLensDirection lensDirection;
@property(nonatomic, assign) FCPPlatformCameraLensType lensType;
@end

@interface FCPPlatformCameraState : NSObject
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v22.4.2), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon

#import "./include/camera_avfoundation/messages.g.h"
Expand Down Expand Up @@ -120,6 +120,16 @@ - (instancetype)initWithValue:(FCPPlatformResolutionPreset)value {
}
@end

@implementation FCPPlatformCameraLensTypeBox
- (instancetype)initWithValue:(FCPPlatformCameraLensType)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
@end

@interface FCPPlatformCameraDescription ()
+ (FCPPlatformCameraDescription *)fromList:(NSArray<id> *)list;
+ (nullable FCPPlatformCameraDescription *)nullableFromList:(NSArray<id> *)list;
Expand Down Expand Up @@ -152,10 +162,12 @@ + (nullable FCPPlatformSize *)nullableFromList:(NSArray<id> *)list;

@implementation FCPPlatformCameraDescription
+ (instancetype)makeWithName:(NSString *)name
lensDirection:(FCPPlatformCameraLensDirection)lensDirection {
lensDirection:(FCPPlatformCameraLensDirection)lensDirection
lensType:(FCPPlatformCameraLensType)lensType {
FCPPlatformCameraDescription *pigeonResult = [[FCPPlatformCameraDescription alloc] init];
pigeonResult.name = name;
pigeonResult.lensDirection = lensDirection;
pigeonResult.lensType = lensType;
return pigeonResult;
}
+ (FCPPlatformCameraDescription *)fromList:(NSArray<id> *)list {
Expand All @@ -164,6 +176,8 @@ + (FCPPlatformCameraDescription *)fromList:(NSArray<id> *)list {
FCPPlatformCameraLensDirectionBox *boxedFCPPlatformCameraLensDirection =
GetNullableObjectAtIndex(list, 1);
pigeonResult.lensDirection = boxedFCPPlatformCameraLensDirection.value;
FCPPlatformCameraLensTypeBox *boxedFCPPlatformCameraLensType = GetNullableObjectAtIndex(list, 2);
pigeonResult.lensType = boxedFCPPlatformCameraLensType.value;
return pigeonResult;
}
+ (nullable FCPPlatformCameraDescription *)nullableFromList:(NSArray<id> *)list {
Expand All @@ -173,6 +187,7 @@ + (nullable FCPPlatformCameraDescription *)nullableFromList:(NSArray<id> *)list
return @[
self.name ?: [NSNull null],
[[FCPPlatformCameraLensDirectionBox alloc] initWithValue:self.lensDirection],
[[FCPPlatformCameraLensTypeBox alloc] initWithValue:self.lensType],
];
}
@end
Expand Down Expand Up @@ -356,15 +371,21 @@ - (nullable id)readValueOfType:(UInt8)type {
: [[FCPPlatformResolutionPresetBox alloc]
initWithValue:[enumAsNumber integerValue]];
}
case 137:
return [FCPPlatformCameraDescription fromList:[self readValue]];
case 137: {
NSNumber *enumAsNumber = [self readValue];
return enumAsNumber == nil
? nil
: [[FCPPlatformCameraLensTypeBox alloc] initWithValue:[enumAsNumber integerValue]];
}
case 138:
return [FCPPlatformCameraState fromList:[self readValue]];
return [FCPPlatformCameraDescription fromList:[self readValue]];
case 139:
return [FCPPlatformMediaSettings fromList:[self readValue]];
return [FCPPlatformCameraState fromList:[self readValue]];
case 140:
return [FCPPlatformPoint fromList:[self readValue]];
return [FCPPlatformMediaSettings fromList:[self readValue]];
case 141:
return [FCPPlatformPoint fromList:[self readValue]];
case 142:
return [FCPPlatformSize fromList:[self readValue]];
default:
return [super readValueOfType:type];
Expand Down Expand Up @@ -408,20 +429,24 @@ - (void)writeValue:(id)value {
FCPPlatformResolutionPresetBox *box = (FCPPlatformResolutionPresetBox *)value;
[self writeByte:136];
[self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])];
} else if ([value isKindOfClass:[FCPPlatformCameraDescription class]]) {
} else if ([value isKindOfClass:[FCPPlatformCameraLensTypeBox class]]) {
FCPPlatformCameraLensTypeBox *box = (FCPPlatformCameraLensTypeBox *)value;
[self writeByte:137];
[self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])];
} else if ([value isKindOfClass:[FCPPlatformCameraDescription class]]) {
[self writeByte:138];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformCameraState class]]) {
[self writeByte:138];
[self writeByte:139];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformMediaSettings class]]) {
[self writeByte:139];
[self writeByte:140];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformPoint class]]) {
[self writeByte:140];
[self writeByte:141];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformSize class]]) {
[self writeByte:141];
[self writeByte:142];
[self writeValue:[value toList]];
} else {
[super writeValue:value];
Expand Down
Loading