Skip to content
Open
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
1 change: 1 addition & 0 deletions packages/camera/camera_avfoundation/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy <sanekyy@gmail.com>
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
Rui Craveiro <ruicraveiro@squarealfa.com>
6 changes: 6 additions & 0 deletions packages/camera/camera_avfoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.10.0

* Adds video stabilization.
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a bit more detail? Maybe make a reference to the AVCaptureVideoStabilizationMode api you added.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know how much more detail it makes sense to add to the CHANGELOG, so I just added a mention to the 2 methods added to the Dart classes. Let me know if you need even more details.

- Adds getSupportedVideoStabilizationModes method.
- Adds setVideoStabilizationMode method.

## 0.9.23+2

* Code refactor related to Swift pigeon's generated struct MediaSettings being immutable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ final class MockCamera: NSObject, Camera {
var setDescriptionWhileRecordingStub: ((String, ((FlutterError?) -> Void)?) -> Void)?
var startImageStreamStub: ((FlutterBinaryMessenger, (FlutterError?) -> Void) -> Void)?
var stopImageStreamStub: (() -> Void)?
var setVideoStabilizationModeStub:
((FCPPlatformVideoStabilizationMode, (FlutterError?) -> Void) -> Void)?
var getIsVideoStabilizationModeSupportedStub: ((FCPPlatformVideoStabilizationMode) -> Bool)?

var dartAPI: FCPCameraEventApi? {
get {
Expand Down Expand Up @@ -188,6 +191,16 @@ final class MockCamera: NSObject, Camera {
resumePreviewStub?()
}

func setVideoStabilizationMode(
_ mode: FCPPlatformVideoStabilizationMode, withCompletion: @escaping (FlutterError?) -> Void
) {
setVideoStabilizationModeStub?(mode, withCompletion)
}

func isVideoStabilizationModeSupported(_ mode: FCPPlatformVideoStabilizationMode) -> Bool {
return getIsVideoStabilizationModeSupportedStub?(mode) ?? false
}

func setDescriptionWhileRecording(
_ cameraName: String,
withCompletion completion: @escaping (FlutterError?) -> Void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ final class MockCaptureConnection: NSObject, CaptureConnection {
var inputPorts: [AVCaptureInput.Port] = []
var isVideoMirroringSupported = false
var isVideoOrientationSupported = false
var preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.off
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ class MockCaptureDevice: NSObject, CaptureDevice {

var iso: Float { 0 }

func isVideoStabilizationModeSupported(_ videoStabilizationMode: AVCaptureVideoStabilizationMode)
-> Bool
{
return false
}

func lockForConfiguration() throws {
try lockForConfigurationStub?()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ protocol Camera: FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate,

func setZoomLevel(_ zoom: CGFloat, withCompletion: @escaping (_ error: FlutterError?) -> Void)

func setVideoStabilizationMode(
_ mode: FCPPlatformVideoStabilizationMode,
withCompletion: @escaping (_ error: FlutterError?) -> Void)

func isVideoStabilizationModeSupported(_ mode: FCPPlatformVideoStabilizationMode) -> Bool

func setFlashMode(
_ mode: FCPPlatformFlashMode,
withCompletion: @escaping (_ error: FlutterError?) -> Void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,29 @@ extension CameraPlugin: FCPCameraApi {
}
}

public func setVideoStabilizationMode(
_ mode: FCPPlatformVideoStabilizationMode, completion: @escaping (FlutterError?) -> Void
) {
captureSessionQueue.async { [weak self] in
self?.camera?.setVideoStabilizationMode(mode, withCompletion: completion)
}
}

public func isVideoStabilizationModeSupported(
_ mode: FCPPlatformVideoStabilizationMode,
completion: @escaping (NSNumber?, FlutterError?) -> Void
) {
captureSessionQueue.async { [weak self] in

if let camera = self?.camera {
let isSupported = camera.isVideoStabilizationModeSupported(mode)
completion(NSNumber(value: isSupported), nil)
} else {
completion(nil, nil)
}
}
}

public func pausePreview(completion: @escaping (FlutterError?) -> Void) {
captureSessionQueue.async { [weak self] in
self?.camera?.pausePreview()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,27 @@ func getPixelFormat(for imageFormat: FCPPlatformImageFormatGroup) -> OSType {
return kCVPixelFormatType_32BGRA
}
}

/// Gets video stabilization mode from its Pigeon representation.
/// videoStabilizationMode - the Pigeon video stabilization mode.
func getAvCaptureVideoStabilizationMode(
_ videoStabilizationMode: FCPPlatformVideoStabilizationMode
) -> AVCaptureVideoStabilizationMode {

switch videoStabilizationMode {
case .off:
return .off
case .standard:
return .standard
case .cinematic:
return .cinematic
case .cinematicExtended:
if #available(iOS 13.0, *) {
return .cinematicExtended
} else {
return .cinematic
}
@unknown default:
return .off
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ protocol CaptureConnection: NSObjectProtocol {

/// Corresponds to the `supportsVideoOrientation` property of `AVCaptureConnection`
var isVideoOrientationSupported: Bool { get }

/// Corresponds to the preferredVideoStabilizationMode property of `AVCaptureConnection`
var preferredVideoStabilizationMode: AVCaptureVideoStabilizationMode { get set }

}

extension AVCaptureConnection: CaptureConnection {}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ protocol CaptureDevice: NSObjectProtocol {
var minAvailableVideoZoomFactor: CGFloat { get }
var videoZoomFactor: CGFloat { get set }

// Video Stabilization
func isVideoStabilizationModeSupported(_ videoStabilizationMode: AVCaptureVideoStabilizationMode)
-> Bool

// Camera Properties
var lensAperture: Float { get }
var exposureDuration: CMTime { get }
Expand Down Expand Up @@ -97,6 +101,13 @@ extension AVCaptureDevice: CaptureDevice {
}

var flutterFormats: [CaptureDeviceFormat] { formats }

func isVideoStabilizationModeSupported(_ videoStabilizationMode: AVCaptureVideoStabilizationMode)
-> Bool
{
return self.activeFormat.isVideoStabilizationModeSupported(videoStabilizationMode)
}

}

extension AVCaptureInput: CaptureInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,35 @@ final class DefaultCamera: NSObject, Camera {
completion(nil)
}

func setVideoStabilizationMode(
_ mode: FCPPlatformVideoStabilizationMode,
withCompletion completion: @escaping (FlutterError?) -> Void
) {
let stabilizationMode = getAvCaptureVideoStabilizationMode(mode)

guard captureDevice.isVideoStabilizationModeSupported(stabilizationMode) else {
completion(
FlutterError(
code: "VIDEO_STABILIZATION_ERROR",
message: "Unavailable video stabilization mode.",
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick - maybe something like

FlutterError(
        code: "VIDEO_STABILIZATION_ERROR",
        message: "The requested stabilization mode is not supported.",
        details: [
            "requested_mode": stabilizationMode.rawValue,
        ]
)

details: [
"requested_mode": stabilizationMode.rawValue
]
)
)
return
}
if let connection = captureVideoOutput.connection(with: .video) {
connection.preferredVideoStabilizationMode = stabilizationMode
}
completion(nil)
}

func isVideoStabilizationModeSupported(_ mode: FCPPlatformVideoStabilizationMode) -> Bool {
let stabilizationMode = getAvCaptureVideoStabilizationMode(mode)
return captureDevice.isVideoStabilizationModeSupported(stabilizationMode)
}

func setFlashMode(
_ mode: FCPPlatformFlashMode,
withCompletion completion: @escaping (FlutterError?) -> Void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ typedef NS_ENUM(NSUInteger, FCPPlatformResolutionPreset) {
- (instancetype)initWithValue:(FCPPlatformResolutionPreset)value;
@end

typedef NS_ENUM(NSUInteger, FCPPlatformVideoStabilizationMode) {
FCPPlatformVideoStabilizationModeOff = 0,
FCPPlatformVideoStabilizationModeStandard = 1,
FCPPlatformVideoStabilizationModeCinematic = 2,
FCPPlatformVideoStabilizationModeCinematicExtended = 3,
};

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

@class FCPPlatformCameraDescription;
@class FCPPlatformCameraState;
@class FCPPlatformMediaSettings;
Expand Down Expand Up @@ -283,6 +296,13 @@ NSObject<FlutterMessageCodec> *FCPGetMessagesCodec(void);
- (void)getMaximumZoomLevel:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
/// Sets the zoom factor.
- (void)setZoomLevel:(double)zoom completion:(void (^)(FlutterError *_Nullable))completion;
/// Sets the video stabilization mode.
- (void)setVideoStabilizationMode:(FCPPlatformVideoStabilizationMode)mode
completion:(void (^)(FlutterError *_Nullable))completion;
/// Gets if the given video stabilization mode is supported.
- (void)isVideoStabilizationModeSupported:(FCPPlatformVideoStabilizationMode)mode
completion:(void (^)(NSNumber *_Nullable,
FlutterError *_Nullable))completion;
/// Pauses streaming of preview frames.
- (void)pausePreviewWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Resumes a previously paused preview stream.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ - (instancetype)initWithValue:(FCPPlatformResolutionPreset)value {
}
@end

@implementation FCPPlatformVideoStabilizationModeBox
- (instancetype)initWithValue:(FCPPlatformVideoStabilizationMode)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 @@ -377,15 +387,21 @@ - (nullable id)readValueOfType:(UInt8)type {
: [[FCPPlatformResolutionPresetBox alloc]
initWithValue:[enumAsNumber integerValue]];
}
case 138:
return [FCPPlatformCameraDescription fromList:[self readValue]];
case 138: {
NSNumber *enumAsNumber = [self readValue];
return enumAsNumber == nil ? nil
: [[FCPPlatformVideoStabilizationModeBox alloc]
initWithValue:[enumAsNumber integerValue]];
}
case 139:
return [FCPPlatformCameraState fromList:[self readValue]];
return [FCPPlatformCameraDescription fromList:[self readValue]];
case 140:
return [FCPPlatformMediaSettings fromList:[self readValue]];
return [FCPPlatformCameraState fromList:[self readValue]];
case 141:
return [FCPPlatformPoint fromList:[self readValue]];
return [FCPPlatformMediaSettings fromList:[self readValue]];
case 142:
return [FCPPlatformPoint fromList:[self readValue]];
case 143:
return [FCPPlatformSize fromList:[self readValue]];
default:
return [super readValueOfType:type];
Expand Down Expand Up @@ -433,20 +449,24 @@ - (void)writeValue:(id)value {
FCPPlatformResolutionPresetBox *box = (FCPPlatformResolutionPresetBox *)value;
[self writeByte:137];
[self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])];
} else if ([value isKindOfClass:[FCPPlatformCameraDescription class]]) {
} else if ([value isKindOfClass:[FCPPlatformVideoStabilizationModeBox class]]) {
FCPPlatformVideoStabilizationModeBox *box = (FCPPlatformVideoStabilizationModeBox *)value;
[self writeByte:138];
[self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])];
} else if ([value isKindOfClass:[FCPPlatformCameraDescription class]]) {
[self writeByte:139];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformCameraState class]]) {
[self writeByte:139];
[self writeByte:140];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformMediaSettings class]]) {
[self writeByte:140];
[self writeByte:141];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformPoint class]]) {
[self writeByte:141];
[self writeByte:142];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[FCPPlatformSize class]]) {
[self writeByte:142];
[self writeByte:143];
[self writeValue:[value toList]];
} else {
[super writeValue:value];
Expand Down Expand Up @@ -1141,6 +1161,63 @@ void SetUpFCPCameraApiWithSuffix(id<FlutterBinaryMessenger> binaryMessenger,
[channel setMessageHandler:nil];
}
}
/// Sets the video stabilization mode.
{
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
initWithName:[NSString stringWithFormat:@"%@%@",
@"dev.flutter.pigeon.camera_avfoundation."
@"CameraApi.setVideoStabilizationMode",
messageChannelSuffix]
binaryMessenger:binaryMessenger
codec:FCPGetMessagesCodec()];
if (api) {
NSCAssert([api respondsToSelector:@selector(setVideoStabilizationMode:completion:)],
@"FCPCameraApi api (%@) doesn't respond to "
@"@selector(setVideoStabilizationMode:completion:)",
api);
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
NSArray<id> *args = message;
FCPPlatformVideoStabilizationModeBox *boxedFCPPlatformVideoStabilizationMode =
GetNullableObjectAtIndex(args, 0);
FCPPlatformVideoStabilizationMode arg_mode = boxedFCPPlatformVideoStabilizationMode.value;
[api setVideoStabilizationMode:arg_mode
completion:^(FlutterError *_Nullable error) {
callback(wrapResult(nil, error));
}];
}];
} else {
[channel setMessageHandler:nil];
}
}
/// Gets if the given video stabilization mode is supported.
{
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
initWithName:[NSString stringWithFormat:@"%@%@",
@"dev.flutter.pigeon.camera_avfoundation."
@"CameraApi.isVideoStabilizationModeSupported",
messageChannelSuffix]
binaryMessenger:binaryMessenger
codec:FCPGetMessagesCodec()];
if (api) {
NSCAssert([api respondsToSelector:@selector(isVideoStabilizationModeSupported:completion:)],
@"FCPCameraApi api (%@) doesn't respond to "
@"@selector(isVideoStabilizationModeSupported:completion:)",
api);
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
NSArray<id> *args = message;
FCPPlatformVideoStabilizationModeBox *boxedFCPPlatformVideoStabilizationMode =
GetNullableObjectAtIndex(args, 0);
FCPPlatformVideoStabilizationMode arg_mode = boxedFCPPlatformVideoStabilizationMode.value;
[api isVideoStabilizationModeSupported:arg_mode
completion:^(NSNumber *_Nullable output,
FlutterError *_Nullable error) {
callback(wrapResult(output, error));
}];
}];
} else {
[channel setMessageHandler:nil];
}
}
/// Pauses streaming of preview frames.
{
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
Expand Down
Loading