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

* Migrates exposure offset and zoom factor limit getters to Swift.
* Migrates `setImageFileFormat` method to Swift.
* Migrates pause and resume methods to Swift.
* Migrates capture orientation locking methods to Swift.
* Converts `setDeviceOrientation` method to property setter and migrated to Swift.

## 0.9.20+1

* Migrates lifecycle methods (`start`, `stop`, `close`) to Swift.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ final class FLTCamSetDeviceOrientationTests: XCTestCase {
videoSetVideoOrientationCalled = true
}

camera.setDeviceOrientation(.landscapeLeft)
camera.deviceOrientation = .landscapeLeft

XCTAssertTrue(photoSetVideoOrientationCalled)
XCTAssertTrue(videoSetVideoOrientationCalled)
Expand All @@ -75,7 +75,7 @@ final class FLTCamSetDeviceOrientationTests: XCTestCase {

camera.lockCaptureOrientation(FCPPlatformDeviceOrientation.portraitDown)

camera.setDeviceOrientation(.landscapeLeft)
camera.deviceOrientation = .landscapeLeft

XCTAssertTrue(photoSetVideoOrientationCalled)
XCTAssertTrue(videoSetVideoOrientationCalled)
Expand All @@ -89,7 +89,7 @@ final class FLTCamSetDeviceOrientationTests: XCTestCase {
mockPhotoCaptureConnection.setVideoOrientationStub = { _ in XCTFail() }
mockVideoCaptureConnection.setVideoOrientationStub = { _ in XCTFail() }

camera.setDeviceOrientation(.landscapeLeft)
camera.deviceOrientation = .landscapeLeft
}

func testSetDeviceOrientation_doesNotSetOrientations_forDuplicateUpdates() {
Expand All @@ -104,8 +104,8 @@ final class FLTCamSetDeviceOrientationTests: XCTestCase {
videoSetVideoOrientationCallCount += 1
}

camera.setDeviceOrientation(.landscapeRight)
camera.setDeviceOrientation(.landscapeRight)
camera.deviceOrientation = .landscapeRight
camera.deviceOrientation = .landscapeRight

XCTAssertEqual(photoSetVideoOrientationCallCount, 1)
XCTAssertEqual(videoSetVideoOrientationCallCount, 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ final class MockCamera: NSObject, Camera {
var isPreviewPaused: Bool = false
var isStreamingImages: Bool = false

var deviceOrientation: UIDeviceOrientation {
get {
preconditionFailure("Attempted to access unimplemented property: deviceOrientation")
}
set {
setDeviceOrientationStub?(newValue)
}
}

var minimumExposureOffset: CGFloat {
return getMinimumExposureOffsetStub?() ?? 0
}
Expand Down Expand Up @@ -120,10 +129,6 @@ final class MockCamera: NSObject, Camera {
captureToFileStub?(completion)
}

func setDeviceOrientation(_ orientation: UIDeviceOrientation) {
setDeviceOrientationStub?(orientation)
}

func lockCaptureOrientation(_ orientation: FCPPlatformDeviceOrientation) {
lockCaptureOrientationStub?(orientation)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ protocol Camera: FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate,
var isPreviewPaused: Bool { get }
var isStreamingImages: Bool { get }

var deviceOrientation: UIDeviceOrientation { get set }

var minimumAvailableZoomFactor: CGFloat { get }
var maximumAvailableZoomFactor: CGFloat { get }
var minimumExposureOffset: CGFloat { get }
Expand Down Expand Up @@ -61,7 +63,6 @@ protocol Camera: FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate,

func captureToFile(completion: @escaping (_ path: String?, _ error: FlutterError?) -> Void)

func setDeviceOrientation(_ orientation: UIDeviceOrientation)
func lockCaptureOrientation(_ orientation: FCPPlatformDeviceOrientation)
func unlockCaptureOrientation()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ public final class CameraPlugin: NSObject, FlutterPlugin {

self.captureSessionQueue.async { [weak self] in
guard let strongSelf = self else { return }
// `FLTCam.setDeviceOrientation` must be called on capture session queue.
strongSelf.camera?.setDeviceOrientation(orientation)
// `Camera.deviceOrientation` must be set on capture session queue.
strongSelf.camera?.deviceOrientation = orientation
// `CameraPlugin.sendDeviceOrientation` can be called on any queue.
strongSelf.sendDeviceOrientation(orientation)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ import CoreMotion
#endif

final class DefaultCamera: FLTCam, Camera {
override var deviceOrientation: UIDeviceOrientation {
get { super.deviceOrientation }
set {
guard newValue != super.deviceOrientation else { return }

super.deviceOrientation = newValue
updateOrientation()
}
}

var minimumExposureOffset: CGFloat { CGFloat(captureDevice.minExposureTargetBias) }
var maximumExposureOffset: CGFloat { CGFloat(captureDevice.maxExposureTargetBias) }
var minimumAvailableZoomFactor: CGFloat { captureDevice.minAvailableVideoZoomFactor }
var maximumAvailableZoomFactor: CGFloat { captureDevice.maxAvailableVideoZoomFactor }

/// The queue on which `latestPixelBuffer` property is accessed.
/// To avoid unnecessary contention, do not access `latestPixelBuffer` on the `captureSessionQueue`.
private let pixelBufferSynchronizationQueue = DispatchQueue(
Expand Down Expand Up @@ -64,6 +79,33 @@ final class DefaultCamera: FLTCam, Camera {
audioCaptureSession.stopRunning()
}

func pauseVideoRecording() {
isRecordingPaused = true
videoIsDisconnected = true
audioIsDisconnected = true
}

func resumeVideoRecording() {
isRecordingPaused = false
}

func lockCaptureOrientation(_ pigeonOrientation: FCPPlatformDeviceOrientation) {
let orientation = FCPGetUIDeviceOrientationForPigeonDeviceOrientation(pigeonOrientation)
if lockedCaptureOrientation != orientation {
lockedCaptureOrientation = orientation
updateOrientation()
}
}

func unlockCaptureOrientation() {
lockedCaptureOrientation = .unknown
updateOrientation()
}

func setImageFileFormat(_ fileFormat: FCPPlatformImageFileFormat) {
self.fileFormat = fileFormat
}

func setExposureMode(_ mode: FCPPlatformExposureMode) {
exposureMode = mode
applyExposureMode()
Expand Down Expand Up @@ -199,6 +241,14 @@ final class DefaultCamera: FLTCam, Camera {
return CGPoint(x: x, y: y)
}

func pausePreview() {
isPreviewPaused = true
}

func resumePreview() {
isPreviewPaused = false
}

func captureOutput(
_ output: AVCaptureOutput,
didOutput sampleBuffer: CMSampleBuffer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,12 @@ @interface FLTCam () <AVCaptureVideoDataOutputSampleBufferDelegate,
@property(strong, nonatomic) NSString *videoRecordingPath;
@property(assign, nonatomic) BOOL isAudioSetup;

@property(assign, nonatomic) UIDeviceOrientation lockedCaptureOrientation;
@property(nonatomic) CMMotionManager *motionManager;
/// All FLTCam's state access and capture session related operations should be on run on this queue.
@property(strong, nonatomic) dispatch_queue_t captureSessionQueue;
/// The queue on which captured photos (not videos) are written to disk.
/// Videos are written to disk by `videoAdaptor` on an internal queue managed by AVFoundation.
@property(strong, nonatomic) dispatch_queue_t photoIOQueue;
@property(assign, nonatomic) UIDeviceOrientation deviceOrientation;
/// A wrapper for CMVideoFormatDescriptionGetDimensions.
/// Allows for alternate implementations in tests.
@property(nonatomic, copy) VideoDimensionsForFormat videoDimensionsForFormat;
Expand Down Expand Up @@ -202,19 +200,6 @@ - (void)setVideoFormat:(OSType)videoFormat {
@{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(videoFormat)};
}

- (void)setImageFileFormat:(FCPPlatformImageFileFormat)fileFormat {
_fileFormat = fileFormat;
}

- (void)setDeviceOrientation:(UIDeviceOrientation)orientation {
if (_deviceOrientation == orientation) {
return;
}

_deviceOrientation = orientation;
[self updateOrientation];
}

- (void)updateOrientation {
if (_isRecording) {
return;
Expand Down Expand Up @@ -526,30 +511,6 @@ - (void)stopVideoRecordingWithCompletion:(void (^)(NSString *_Nullable,
}
}

- (void)pauseVideoRecording {
_isRecordingPaused = YES;
_videoIsDisconnected = YES;
_audioIsDisconnected = YES;
}

- (void)resumeVideoRecording {
_isRecordingPaused = NO;
}

- (void)lockCaptureOrientation:(FCPPlatformDeviceOrientation)pigeonOrientation {
UIDeviceOrientation orientation =
FCPGetUIDeviceOrientationForPigeonDeviceOrientation(pigeonOrientation);
if (_lockedCaptureOrientation != orientation) {
_lockedCaptureOrientation = orientation;
[self updateOrientation];
}
}

- (void)unlockCaptureOrientation {
_lockedCaptureOrientation = UIDeviceOrientationUnknown;
[self updateOrientation];
}

- (void)setFlashMode:(FCPPlatformFlashMode)mode
withCompletion:(void (^)(FlutterError *_Nullable))completion {
if (mode == FCPPlatformFlashModeTorch) {
Expand Down Expand Up @@ -595,14 +556,6 @@ - (void)setFlashMode:(FCPPlatformFlashMode)mode
completion(nil);
}

- (void)pausePreview {
_isPreviewPaused = true;
}

- (void)resumePreview {
_isPreviewPaused = false;
}

- (void)setDescriptionWhileRecording:(NSString *)cameraName
withCompletion:(void (^)(FlutterError *_Nullable))completion {
if (!_isRecording) {
Expand Down Expand Up @@ -737,22 +690,6 @@ - (void)setZoomLevel:(CGFloat)zoom withCompletion:(void (^)(FlutterError *_Nulla
completion(nil);
}

- (CGFloat)minimumAvailableZoomFactor {
return _captureDevice.minAvailableVideoZoomFactor;
}

- (CGFloat)maximumAvailableZoomFactor {
return _captureDevice.maxAvailableVideoZoomFactor;
}

- (CGFloat)minimumExposureOffset {
return _captureDevice.minExposureTargetBias;
}

- (CGFloat)maximumExposureOffset {
return _captureDevice.maxExposureTargetBias;
}

- (BOOL)setupWriterForPath:(NSString *)path {
NSError *error = nil;
NSURL *outputURL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ NS_ASSUME_NONNULL_BEGIN
// Format used for video and image streaming.
@property(assign, nonatomic) FourCharCode videoFormat;
@property(assign, nonatomic) FCPPlatformImageFileFormat fileFormat;
@property(readonly, nonatomic) CGFloat minimumAvailableZoomFactor;
@property(readonly, nonatomic) CGFloat maximumAvailableZoomFactor;
@property(readonly, nonatomic) CGFloat minimumExposureOffset;
@property(readonly, nonatomic) CGFloat maximumExposureOffset;

// Properties exposed for the Swift DefaultCamera subclass
@property(nonatomic, nullable) FLTImageStreamHandler *imageStreamHandler;
Expand All @@ -52,15 +48,15 @@ NS_ASSUME_NONNULL_BEGIN
@property(readonly, nonatomic) NSObject<FLTCaptureSession> *videoCaptureSession;
@property(readonly, nonatomic) NSObject<FLTCaptureSession> *audioCaptureSession;
@property(readonly, nonatomic) NSObject<FLTDeviceOrientationProviding> *deviceOrientationProvider;
@property(assign, nonatomic) UIDeviceOrientation lockedCaptureOrientation;
@property(assign, nonatomic) UIDeviceOrientation deviceOrientation;

/// Initializes an `FLTCam` instance with the given configuration.
/// @param error report to the caller if any error happened creating the camera.
- (instancetype)initWithConfiguration:(FLTCamConfiguration *)configuration error:(NSError **)error;

- (void)setDeviceOrientation:(UIDeviceOrientation)orientation;
- (void)captureToFileWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion;
- (void)setImageFileFormat:(FCPPlatformImageFileFormat)fileFormat;
/// Starts recording a video with an optional streaming messenger.
/// If the messenger is non-nil then it will be called for each
/// captured frame, allowing streaming concurrently with recording.
Expand All @@ -70,16 +66,9 @@ NS_ASSUME_NONNULL_BEGIN
messengerForStreaming:(nullable NSObject<FlutterBinaryMessenger> *)messenger;
- (void)stopVideoRecordingWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion;
- (void)pauseVideoRecording;
- (void)resumeVideoRecording;
- (void)lockCaptureOrientation:(FCPPlatformDeviceOrientation)orientation
NS_SWIFT_NAME(lockCaptureOrientation(_:));
- (void)unlockCaptureOrientation;
- (void)setFlashMode:(FCPPlatformFlashMode)mode
withCompletion:(void (^)(FlutterError *_Nullable))completion;

- (void)pausePreview;
- (void)resumePreview;
- (void)setDescriptionWhileRecording:(NSString *)cameraName
withCompletion:(void (^)(FlutterError *_Nullable))completion;

Expand All @@ -89,6 +78,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setZoomLevel:(CGFloat)zoom withCompletion:(void (^)(FlutterError *_Nullable))completion;
- (void)setUpCaptureSessionForAudioIfNeeded;

// Methods exposed for the Swift DefaultCamera subclass
- (void)updateOrientation;

@end

NS_ASSUME_NONNULL_END
2 changes: 1 addition & 1 deletion packages/camera/camera_avfoundation/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_avfoundation
description: iOS implementation of the camera plugin.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.9.20+1
version: 0.9.20+2

environment:
sdk: ^3.6.0
Expand Down