diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index b8f168c1097..fd730d67b49 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.22+2 + +* Migrates `FLTCamConfiguration`, `FLTCamMediaSettingsAVWrapper` classes to Swift. +* Migrates `FLTCaptureOutput`, `FLTCapturePhotoOutput`, `FLTCaptureVideoDataOutput` protocols to Swift. + ## 0.9.22+1 * Fixes crash on iOS when `enableAudio` is false. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift index 5e085b3a0a2..e83cecfa5fb 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift @@ -35,8 +35,8 @@ final class CameraSessionPresetsTests: XCTestCase { } let configuration = CameraTestUtils.createTestCameraConfiguration() - configuration.captureDeviceFactory = { _ in captureDeviceMock } - configuration.videoDimensionsForFormat = { format in + configuration.videoCaptureDeviceFactory = { _ in captureDeviceMock } + configuration.videoDimensionsConverter = { format in return CMVideoDimensions(width: 1, height: 1) } configuration.videoCaptureSession = videoSessionMock @@ -65,7 +65,7 @@ final class CameraSessionPresetsTests: XCTestCase { configuration.videoCaptureSession = videoSessionMock configuration.mediaSettings = CameraTestUtils.createDefaultMediaSettings( resolutionPreset: FCPPlatformResolutionPreset.max) - configuration.captureDeviceFactory = { _ in MockCaptureDevice() } + configuration.videoCaptureDeviceFactory = { _ in MockCaptureDevice() } let _ = CameraTestUtils.createTestCamera(configuration) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift index f6a1e911fe8..bd5422f7747 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift @@ -106,7 +106,7 @@ private final class TestMediaSettingsAVWrapper: FLTCamMediaSettingsAVWrapper { } override func recommendedVideoSettingsForAssetWriter( - withFileType fileType: AVFileType, for output: FLTCaptureVideoDataOutput + withFileType fileType: AVFileType, for output: CaptureVideoDataOutput ) -> [String: Any]? { return [:] } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift index b25cac06c5d..a716aade6f5 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift @@ -26,8 +26,8 @@ enum CameraTestUtils { enableAudio: true) } - /// Creates a test `FLTCamConfiguration` with a default mock setup. - static func createTestCameraConfiguration() -> FLTCamConfiguration { + /// Creates a test `CameraConfiguration` with a default mock setup. + static func createTestCameraConfiguration() -> CameraConfiguration { let captureSessionQueue = DispatchQueue(label: "capture_session_queue") let videoSessionMock = MockCaptureSession() @@ -56,7 +56,7 @@ enum CameraTestUtils { currentFormat = format } - let configuration = FLTCamConfiguration( + let configuration = CameraConfiguration( mediaSettings: createDefaultMediaSettings( resolutionPreset: FCPPlatformResolutionPreset.medium), mediaSettingsWrapper: FLTCamMediaSettingsAVWrapper(), @@ -72,7 +72,7 @@ enum CameraTestUtils { configuration.audioCaptureSession = audioSessionMock configuration.orientation = .portrait - configuration.assetWriterFactory = { _, _, _ in MockAssetWriter() } + configuration.assetWriterFactory = { _, _ in MockAssetWriter() } configuration.inputPixelBufferAdaptorFactory = { _, _ in MockAssetWriterInputPixelBufferAdaptor() @@ -81,7 +81,7 @@ enum CameraTestUtils { return configuration } - static func createTestCamera(_ configuration: FLTCamConfiguration) -> DefaultCamera { + static func createTestCamera(_ configuration: CameraConfiguration) -> DefaultCamera { return (try? DefaultCamera(configuration: configuration))! } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift index 79063ec9e89..8310bbba606 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift @@ -17,7 +17,7 @@ final class FLTCamExposureTests: XCTestCase { let mockDeviceOrientationProvider = MockDeviceOrientationProvider() let configuration = CameraTestUtils.createTestCameraConfiguration() - configuration.captureDeviceFactory = { _ in mockDevice } + configuration.videoCaptureDeviceFactory = { _ in mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider let camera = CameraTestUtils.createTestCamera(configuration) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift index aa67b742e47..570fd2022b8 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift @@ -18,7 +18,7 @@ final class FLTCamSetFocusModeTests: XCTestCase { let mockDeviceOrientationProvider = MockDeviceOrientationProvider() let configuration = CameraTestUtils.createTestCameraConfiguration() - configuration.captureDeviceFactory = { _ in mockDevice } + configuration.videoCaptureDeviceFactory = { _ in mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider let camera = CameraTestUtils.createTestCamera(configuration) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift index b91a3037027..34f47c09346 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift @@ -18,7 +18,7 @@ final class FLTCamSetFlashModeTests: XCTestCase { let mockCapturePhotoOutput = MockCapturePhotoOutput() let configuration = CameraTestUtils.createTestCameraConfiguration() - configuration.captureDeviceFactory = { _ in mockDevice } + configuration.videoCaptureDeviceFactory = { _ in mockDevice } let camera = CameraTestUtils.createTestCamera(configuration) camera.capturePhotoOutput = mockCapturePhotoOutput @@ -87,9 +87,7 @@ final class FLTCamSetFlashModeTests: XCTestCase { func testSetFlashModeWithNonTorchMode_setsTrochModeOff_ifTorchModeIsEnabled() { let (camera, mockDevice, mockCapturePhotoOutput) = createCamera() - mockCapturePhotoOutput.supportedFlashModes = [ - NSNumber(value: AVCaptureDevice.FlashMode.auto.rawValue) - ] + mockCapturePhotoOutput.supportedFlashModes = [.auto] mockDevice.hasFlash = true // Torch mode is enabled diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift index e693df7df70..b0641fc6daf 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift @@ -17,7 +17,7 @@ final class FLTCamZoomTests: XCTestCase { let mockDevice = MockCaptureDevice() let configuration = CameraTestUtils.createTestCameraConfiguration() - configuration.captureDeviceFactory = { _ in mockDevice } + configuration.videoCaptureDeviceFactory = { _ in mockDevice } let camera = CameraTestUtils.createTestCamera(configuration) return (camera, mockDevice) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift index 76e6d1ddf96..ad8f480f22c 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import camera_avfoundation +@testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. #if canImport(camera_avfoundation_objc) @@ -11,11 +11,11 @@ import camera_avfoundation /// Mock implementation of `FLTCapturePhotoOutput` protocol which allows injecting a custom /// implementation. -final class MockCapturePhotoOutput: NSObject, FLTCapturePhotoOutput { +final class MockCapturePhotoOutput: NSObject, CapturePhotoOutput { var avOutput = AVCapturePhotoOutput() var availablePhotoCodecTypes: [AVVideoCodecType] = [] - var highResolutionCaptureEnabled = false - var supportedFlashModes: [NSNumber] = [] + var isHighResolutionCaptureEnabled = false + var supportedFlashModes: [AVCaptureDevice.FlashMode] = [] // Stub that is called when the corresponding public method is called. var capturePhotoWithSettingsStub: @@ -29,7 +29,7 @@ final class MockCapturePhotoOutput: NSObject, FLTCapturePhotoOutput { capturePhotoWithSettingsStub?(settings, delegate) } - func connection(withMediaType mediaType: AVMediaType) -> FLTCaptureConnection? { + func connection(with mediaType: AVMediaType) -> FLTCaptureConnection? { return connectionWithMediaTypeStub?(mediaType) } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift index 4131beb3e10..98d1296a8b2 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import camera_avfoundation +@testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. #if canImport(camera_avfoundation_objc) @@ -11,15 +11,14 @@ import camera_avfoundation /// Mock implementation of `FLTCaptureVideoDataOutput` protocol which allows injecting a custom /// implementation. -class MockCaptureVideoDataOutput: NSObject, FLTCaptureVideoDataOutput { - +class MockCaptureVideoDataOutput: NSObject, CaptureVideoDataOutput { var avOutput = AVCaptureVideoDataOutput() var alwaysDiscardsLateVideoFrames = false - var videoSettings: [String: Any] = [:] + var videoSettings: [String: Any]! = [:] var connectionWithMediaTypeStub: ((AVMediaType) -> FLTCaptureConnection?)? - func connection(withMediaType mediaType: AVMediaType) -> FLTCaptureConnection? { + func connection(with mediaType: AVMediaType) -> FLTCaptureConnection? { return connectionWithMediaTypeStub?(mediaType) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift index 7f0716faa64..7f8cef124d6 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift @@ -174,7 +174,7 @@ final class PhotoCaptureTests: XCTestCase { key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue) let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureSessionQueue = captureSessionQueue - configuration.captureDeviceFactory = { _ in captureDeviceMock } + configuration.videoCaptureDeviceFactory = { _ in captureDeviceMock } let cam = CameraTestUtils.createTestCamera(configuration) let filePath = "test" diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/SampleBufferTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/SampleBufferTests.swift index 65b15eaa9b8..8a39d55f945 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/SampleBufferTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/SampleBufferTests.swift @@ -60,7 +60,7 @@ private class FakeMediaSettingsAVWrapper: FLTCamMediaSettingsAVWrapper { } override func recommendedVideoSettingsForAssetWriter( - withFileType fileType: AVFileType, for output: FLTCaptureVideoDataOutput + withFileType fileType: AVFileType, for output: CaptureVideoDataOutput ) -> [String: Any]? { return [:] } @@ -87,7 +87,7 @@ final class CameraSampleBufferTests: XCTestCase { enableAudio: true) configuration.mediaSettingsWrapper = FakeMediaSettingsAVWrapper(inputMock: input) - configuration.assetWriterFactory = { url, fileType, error in + configuration.assetWriterFactory = { url, fileType in return assetWriter } configuration.inputPixelBufferAdaptorFactory = { input, settings in diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraConfiguration.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraConfiguration.swift new file mode 100644 index 00000000000..bb58c4617b3 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraConfiguration.swift @@ -0,0 +1,95 @@ +// 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. + +import AVFoundation +import CoreMedia +import UIKit + +// Import Objective-C part of the implementation when SwiftPM is used. +#if canImport(camera_avfoundation_objc) + import camera_avfoundation_objc +#endif + +/// Factory block returning an FLTCaptureDevice. +/// Used in tests to inject a video capture device into DefaultCamera. +typealias VideoCaptureDeviceFactory = (_ cameraName: String) -> FLTCaptureDevice + +typealias AudioCaptureDeviceFactory = () -> FLTCaptureDevice + +typealias CaptureSessionFactory = () -> FLTCaptureSession + +typealias AssetWriterFactory = (_ assetUrl: URL, _ fileType: AVFileType) throws -> FLTAssetWriter + +typealias InputPixelBufferAdaptorFactory = ( + _ input: FLTAssetWriterInput, _ settings: [String: Any]? +) -> + FLTAssetWriterInputPixelBufferAdaptor + +/// Determines the video dimensions (width and height) for a given capture device format. +/// Used in tests to mock CMVideoFormatDescriptionGetDimensions. +typealias VideoDimensionsConverter = (FLTCaptureDeviceFormat) -> CMVideoDimensions + +/// A configuration object that centralizes dependencies for `DefaultCamera`. +class CameraConfiguration { + var mediaSettings: FCPPlatformMediaSettings + var mediaSettingsWrapper: FLTCamMediaSettingsAVWrapper + var captureSessionQueue: DispatchQueue + var videoCaptureSession: FLTCaptureSession + var audioCaptureSession: FLTCaptureSession + var videoCaptureDeviceFactory: VideoCaptureDeviceFactory + let audioCaptureDeviceFactory: AudioCaptureDeviceFactory + let captureDeviceInputFactory: FLTCaptureDeviceInputFactory + var assetWriterFactory: AssetWriterFactory + var inputPixelBufferAdaptorFactory: InputPixelBufferAdaptorFactory + var videoDimensionsConverter: VideoDimensionsConverter + var deviceOrientationProvider: FLTDeviceOrientationProviding + let initialCameraName: String + var orientation: UIDeviceOrientation + + init( + mediaSettings: FCPPlatformMediaSettings, + mediaSettingsWrapper: FLTCamMediaSettingsAVWrapper, + captureDeviceFactory: @escaping VideoCaptureDeviceFactory, + audioCaptureDeviceFactory: @escaping AudioCaptureDeviceFactory, + captureSessionFactory: @escaping CaptureSessionFactory, + captureSessionQueue: DispatchQueue, + captureDeviceInputFactory: FLTCaptureDeviceInputFactory, + initialCameraName: String + ) { + self.mediaSettings = mediaSettings + self.mediaSettingsWrapper = mediaSettingsWrapper + self.videoCaptureDeviceFactory = captureDeviceFactory + self.audioCaptureDeviceFactory = audioCaptureDeviceFactory + self.captureSessionQueue = captureSessionQueue + self.videoCaptureSession = captureSessionFactory() + self.audioCaptureSession = captureSessionFactory() + self.captureDeviceInputFactory = captureDeviceInputFactory + self.initialCameraName = initialCameraName + self.orientation = UIDevice.current.orientation + self.deviceOrientationProvider = FLTDefaultDeviceOrientationProvider() + + self.videoDimensionsConverter = { format in + return CMVideoFormatDescriptionGetDimensions(format.formatDescription) + } + + self.assetWriterFactory = { url, fileType in + var error: NSError? + let writer = FLTDefaultAssetWriter(url: url, fileType: fileType, error: &error) + + if let error = error { + throw error + } + + return writer + } + + self.inputPixelBufferAdaptorFactory = { assetWriterInput, sourcePixelBufferAttributes in + let adaptor = AVAssetWriterInputPixelBufferAdaptor( + assetWriterInput: assetWriterInput.input, + sourcePixelBufferAttributes: sourcePixelBufferAttributes + ) + return FLTDefaultAssetWriterInputPixelBufferAdaptor(adaptor: adaptor) + } + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift index 9f9fc7b3f3d..465db90eb8d 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift @@ -16,7 +16,7 @@ public final class CameraPlugin: NSObject, FlutterPlugin { private let globalEventAPI: FCPCameraGlobalEventApi private let deviceDiscoverer: FLTCameraDeviceDiscovering private let permissionManager: FLTCameraPermissionManager - private let captureDeviceFactory: CaptureDeviceFactory + private let captureDeviceFactory: VideoCaptureDeviceFactory private let captureSessionFactory: CaptureSessionFactory private let captureDeviceInputFactory: FLTCaptureDeviceInputFactory @@ -52,7 +52,7 @@ public final class CameraPlugin: NSObject, FlutterPlugin { globalAPI: FCPCameraGlobalEventApi, deviceDiscoverer: FLTCameraDeviceDiscovering, permissionManager: FLTCameraPermissionManager, - deviceFactory: @escaping CaptureDeviceFactory, + deviceFactory: @escaping VideoCaptureDeviceFactory, captureSessionFactory: @escaping CaptureSessionFactory, captureDeviceInputFactory: FLTCaptureDeviceInputFactory, captureSessionQueue: DispatchQueue @@ -251,7 +251,7 @@ extension CameraPlugin: FCPCameraApi { ) { let mediaSettingsAVWrapper = FLTCamMediaSettingsAVWrapper() - let camConfiguration = FLTCamConfiguration( + let camConfiguration = CameraConfiguration( mediaSettings: settings, mediaSettingsWrapper: mediaSettingsAVWrapper, captureDeviceFactory: captureDeviceFactory, diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CaptureOutput.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CaptureOutput.swift new file mode 100644 index 00000000000..fba320728d6 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CaptureOutput.swift @@ -0,0 +1,79 @@ +// 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. + +import AVFoundation +import Foundation + +// Import Objective-C part of the implementation when SwiftPM is used. +#if canImport(camera_avfoundation_objc) + import camera_avfoundation_objc +#endif + +/// A protocol which is a direct passthrough to `AVCaptureOutput`. It exists to allow mocking +/// `AVCaptureOutput` in tests. +protocol CaptureOutput { + /// Returns a connection with the specified media type, or nil if no such connection exists. + func connection(with mediaType: AVMediaType) -> FLTCaptureConnection? +} + +/// A protocol which is a direct passthrough to `AVCaptureVideoDataOutput`. It exists to allow +/// mocking `AVCaptureVideoDataOutput` in tests. +protocol CaptureVideoDataOutput: CaptureOutput { + /// The underlying instance of `AVCaptureVideoDataOutput`. + var avOutput: AVCaptureVideoDataOutput { get } + + /// Corresponds to the `alwaysDiscardsLateVideoFrames` property of `AVCaptureVideoDataOutput` + var alwaysDiscardsLateVideoFrames: Bool { get set } + + /// Corresponds to the `videoSettings` property of `AVCaptureVideoDataOutput` + var videoSettings: [String: Any]! { get set } + + /// Corresponds to the `setSampleBufferDelegate` method of `AVCaptureVideoDataOutput` + func setSampleBufferDelegate( + _ sampleBufferDelegate: AVCaptureVideoDataOutputSampleBufferDelegate?, + queue sampleBufferCallbackQueue: DispatchQueue? + ) +} + +extension AVCaptureVideoDataOutput: CaptureVideoDataOutput { + var avOutput: AVCaptureVideoDataOutput { + return self + } + + func connection(with mediaType: AVMediaType) -> FLTCaptureConnection? { + guard let connection: AVCaptureConnection = connection(with: mediaType) else { return nil } + return FLTDefaultCaptureConnection(connection: connection) + } +} + +/// A protocol which is a direct passthrough to `AVCapturePhotoOutput`. It exists to allow mocking +/// `AVCapturePhotoOutput` in tests. +protocol CapturePhotoOutput: CaptureOutput { + /// The underlying instance of `AVCapturePhotoOutput`. + var avOutput: AVCapturePhotoOutput { get } + + /// Corresponds to the `availablePhotoCodecTypes` property of `AVCapturePhotoOutput` + var availablePhotoCodecTypes: [AVVideoCodecType] { get } + + /// Corresponds to the `isHighResolutionCaptureEnabled` property of `AVCapturePhotoOutput` + var isHighResolutionCaptureEnabled: Bool { get set } + + /// Corresponds to the `supportedFlashModes` property of `AVCapturePhotoOutput` + var supportedFlashModes: [AVCaptureDevice.FlashMode] { get } + + /// Corresponds to the `capturePhotoWithSettings` method of `AVCapturePhotoOutput` + func capturePhoto(with settings: AVCapturePhotoSettings, delegate: AVCapturePhotoCaptureDelegate) +} + +/// Make AVCapturePhotoOutput conform to FLTCapturePhotoOutput protocol directly +extension AVCapturePhotoOutput: CapturePhotoOutput { + var avOutput: AVCapturePhotoOutput { + return self + } + + func connection(with mediaType: AVMediaType) -> FLTCaptureConnection? { + guard let connection: AVCaptureConnection = connection(with: mediaType) else { return nil } + return FLTDefaultCaptureConnection(connection: connection) + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift index f7bcc20fb45..61da63e8e87 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift @@ -47,7 +47,7 @@ final class DefaultCamera: NSObject, Camera { private let audioCaptureSession: FLTCaptureSession /// A wrapper for AVCaptureDevice creation to allow for dependency injection in tests. - private let captureDeviceFactory: CaptureDeviceFactory + private let videoCaptureDeviceFactory: VideoCaptureDeviceFactory private let audioCaptureDeviceFactory: AudioCaptureDeviceFactory private let captureDeviceInputFactory: FLTCaptureDeviceInputFactory private let assetWriterFactory: AssetWriterFactory @@ -55,16 +55,16 @@ final class DefaultCamera: NSObject, Camera { /// A wrapper for CMVideoFormatDescriptionGetDimensions. /// Allows for alternate implementations in tests. - private let videoDimensionsForFormat: VideoDimensionsForFormat + private let videoDimensionsConverter: VideoDimensionsConverter private let deviceOrientationProvider: FLTDeviceOrientationProviding private let motionManager = CMMotionManager() private(set) var captureDevice: FLTCaptureDevice // Setter exposed for tests. - var captureVideoOutput: FLTCaptureVideoDataOutput + var captureVideoOutput: CaptureVideoDataOutput // Setter exposed for tests. - var capturePhotoOutput: FLTCapturePhotoOutput + var capturePhotoOutput: CapturePhotoOutput private var captureVideoInput: FLTCaptureInput private var videoWriter: FLTAssetWriter? @@ -135,13 +135,12 @@ final class DefaultCamera: NSObject, Camera { captureDevice: FLTCaptureDevice, videoFormat: FourCharCode, captureDeviceInputFactory: FLTCaptureDeviceInputFactory - ) throws -> (FLTCaptureInput, FLTCaptureVideoDataOutput, AVCaptureConnection) { + ) throws -> (FLTCaptureInput, CaptureVideoDataOutput, AVCaptureConnection) { // Setup video capture input. let captureVideoInput = try captureDeviceInputFactory.deviceInput(with: captureDevice) // Setup video capture output. - let captureVideoOutput = FLTDefaultCaptureVideoDataOutput( - captureVideoOutput: AVCaptureVideoDataOutput()) + let captureVideoOutput = AVCaptureVideoDataOutput() captureVideoOutput.videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String: videoFormat ] @@ -159,25 +158,25 @@ final class DefaultCamera: NSObject, Camera { return (captureVideoInput, captureVideoOutput, connection) } - init(configuration: FLTCamConfiguration) throws { + init(configuration: CameraConfiguration) throws { captureSessionQueue = configuration.captureSessionQueue mediaSettings = configuration.mediaSettings mediaSettingsAVWrapper = configuration.mediaSettingsWrapper videoCaptureSession = configuration.videoCaptureSession audioCaptureSession = configuration.audioCaptureSession - captureDeviceFactory = configuration.captureDeviceFactory + videoCaptureDeviceFactory = configuration.videoCaptureDeviceFactory audioCaptureDeviceFactory = configuration.audioCaptureDeviceFactory captureDeviceInputFactory = configuration.captureDeviceInputFactory assetWriterFactory = configuration.assetWriterFactory inputPixelBufferAdaptorFactory = configuration.inputPixelBufferAdaptorFactory - videoDimensionsForFormat = configuration.videoDimensionsForFormat + videoDimensionsConverter = configuration.videoDimensionsConverter deviceOrientationProvider = configuration.deviceOrientationProvider - captureDevice = captureDeviceFactory(configuration.initialCameraName) + captureDevice = videoCaptureDeviceFactory(configuration.initialCameraName) flashMode = captureDevice.hasFlash ? .auto : .off - capturePhotoOutput = FLTDefaultCapturePhotoOutput(photoOutput: AVCapturePhotoOutput()) - capturePhotoOutput.highResolutionCaptureEnabled = true + capturePhotoOutput = AVCapturePhotoOutput() + capturePhotoOutput.isHighResolutionCaptureEnabled = true videoCaptureSession.automaticallyConfiguresApplicationAudioSession = false audioCaptureSession.automaticallyConfiguresApplicationAudioSession = false @@ -215,7 +214,7 @@ final class DefaultCamera: NSObject, Camera { FLTSelectBestFormatForRequestedFrameRate( captureDevice, mediaSettings, - videoDimensionsForFormat) + videoDimensionsConverter) if let framesPerSecond = mediaSettings.framesPerSecond { // Set frame rate with 1/10 precision allowing non-integral values. @@ -302,7 +301,7 @@ final class DefaultCamera: NSObject, Camera { } } - let size = videoDimensionsForFormat(captureDevice.activeFormat) + let size = videoDimensionsConverter(captureDevice.activeFormat) previewSize = CGSize(width: CGFloat(size.width), height: CGFloat(size.height)) audioCaptureSession.sessionPreset = videoCaptureSession.sessionPreset } @@ -319,7 +318,7 @@ final class DefaultCamera: NSObject, Camera { var isBestSubTypePreferred = false for format in captureDevice.formats { - let resolution = videoDimensionsForFormat(format) + let resolution = videoDimensionsConverter(format) let height = UInt(resolution.height) let width = UInt(resolution.width) let pixelCount = height * width @@ -525,13 +524,13 @@ final class DefaultCamera: NSObject, Camera { private func setupWriter(forPath path: String) -> Bool { setUpCaptureSessionForAudioIfNeeded() - var error: NSError? - videoWriter = assetWriterFactory(URL(fileURLWithPath: path), AVFileType.mp4, &error) + let videoWriter: FLTAssetWriter - guard let videoWriter = videoWriter else { - if let error = error { - reportErrorMessage(error.description) - } + do { + videoWriter = try assetWriterFactory(URL(fileURLWithPath: path), .mp4) + self.videoWriter = videoWriter + } catch let error as NSError { + reportErrorMessage(error.description) return false } @@ -748,9 +747,9 @@ final class DefaultCamera: NSObject, Camera { } private func updateOrientation( - _ orientation: UIDeviceOrientation, forCaptureOutput captureOutput: FLTCaptureOutput + _ orientation: UIDeviceOrientation, forCaptureOutput captureOutput: CaptureOutput ) { - if let connection = captureOutput.connection(withMediaType: .video), + if let connection = captureOutput.connection(with: .video), connection.isVideoOrientationSupported { connection.videoOrientation = videoOrientation(forDeviceOrientation: orientation) @@ -989,7 +988,7 @@ final class DefaultCamera: NSObject, Camera { return } let avFlashMode = FCPGetAVCaptureFlashModeForPigeonFlashMode(mode) - guard capturePhotoOutput.supportedFlashModes.contains(NSNumber(value: avFlashMode.rawValue)) + guard capturePhotoOutput.supportedFlashModes.contains(avFlashMode) else { completion( FlutterError( @@ -1031,9 +1030,9 @@ final class DefaultCamera: NSObject, Camera { return } - captureDevice = captureDeviceFactory(cameraName) + captureDevice = videoCaptureDeviceFactory(cameraName) - let oldConnection = captureVideoOutput.connection(withMediaType: .video) + let oldConnection = captureVideoOutput.connection(with: .video) // Stop video capture from the old output. captureVideoOutput.setSampleBufferDelegate(nil, queue: nil) diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/MediaSettingsAVWrapper.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/MediaSettingsAVWrapper.swift new file mode 100644 index 00000000000..b0281ec7aa3 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/MediaSettingsAVWrapper.swift @@ -0,0 +1,107 @@ +// 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. + +import AVFoundation +import CoreMedia +import Foundation + +// Import Objective-C part of the implementation when SwiftPM is used. +#if canImport(camera_avfoundation_objc) + import camera_avfoundation_objc +#endif + +/// An interface for performing media settings operations. +/// +/// xctest-expectation-checking implementation (`TestMediaSettingsAVWrapper`) of this interface can +/// be injected into `camera-avfoundation` plugin allowing to run media-settings tests without any +/// additional mocking of AVFoundation classes. +class FLTCamMediaSettingsAVWrapper { + + /// Requests exclusive access to configure device hardware properties. + /// - Parameter captureDevice: The capture device. + /// - Throws: An error if the device could not be locked for configuration. + /// - Returns: A Bool indicating whether the device was successfully locked for configuration. + func lockDevice(_ captureDevice: FLTCaptureDevice) throws { + return try captureDevice.lockForConfiguration() + } + + /// Release exclusive control over device hardware properties. + /// - Parameter captureDevice: The capture device. + func unlockDevice(_ captureDevice: FLTCaptureDevice) { + captureDevice.unlockForConfiguration() + } + + /// When paired with commitConfiguration, allows a client to batch multiple configuration + /// operations on a running session into atomic updates. + /// - Parameter videoCaptureSession: The video capture session. + func beginConfiguration(for videoCaptureSession: FLTCaptureSession) { + videoCaptureSession.beginConfiguration() + } + + /// When preceded by beginConfiguration, allows a client to batch multiple configuration + /// operations on a running session into atomic updates. + /// - Parameter videoCaptureSession: The video capture session. + func commitConfiguration(for videoCaptureSession: FLTCaptureSession) { + videoCaptureSession.commitConfiguration() + } + + /// Set receiver's current active minimum frame duration (the reciprocal of its max frame rate). + /// - Parameters: + /// - duration: The frame duration. + /// - captureDevice: The capture device + func setMinFrameDuration(_ duration: CMTime, on captureDevice: FLTCaptureDevice) { + captureDevice.activeVideoMinFrameDuration = duration + } + + /// Set receiver's current active maximum frame duration (the reciprocal of its min frame rate). + /// - Parameters: + /// - duration: The frame duration. + /// - captureDevice: The capture device + func setMaxFrameDuration(_ duration: CMTime, on captureDevice: FLTCaptureDevice) { + captureDevice.activeVideoMaxFrameDuration = duration + } + + /// Creates a new input of the audio media type to receive sample buffers for writing to + /// the output file. + /// - Parameter outputSettings: The settings used for encoding the audio appended to the output. + /// - Returns: An instance of `FLTAssetWriterInput`. + func assetWriterAudioInput(withOutputSettings outputSettings: [String: Any]?) + -> FLTAssetWriterInput + { + let input = AVAssetWriterInput(mediaType: .audio, outputSettings: outputSettings) + return FLTDefaultAssetWriterInput(input: input) + } + + /// Creates a new input of the video media type to receive sample buffers for writing to + /// the output file. + /// - Parameter outputSettings: The settings used for encoding the video appended to the output. + /// - Returns: An instance of `FLTAssetWriterInput`. + func assetWriterVideoInput(withOutputSettings outputSettings: [String: Any]?) + -> FLTAssetWriterInput + { + let input = AVAssetWriterInput(mediaType: .video, outputSettings: outputSettings) + return FLTDefaultAssetWriterInput(input: input) + } + + /// Adds an input to the asset writer. + /// - Parameters: + /// - writerInput: The `FLTAssetWriterInput` object to be added. + /// - writer: The `FLTAssetWriter` object. + func addInput(_ writerInput: FLTAssetWriterInput, to writer: FLTAssetWriter) { + writer.add(writerInput.input) + } + + /// Specifies the recommended video settings for `FLTCaptureVideoDataOutput`. + /// - Parameters: + /// - fileType: Specifies the UTI of the file type to be written (see AVMediaFormat.h for a list + /// of file format UTIs). + /// - output: The `FLTCaptureVideoDataOutput` instance. + /// - Returns: A fully populated dictionary of keys and values that are compatible with AVAssetWriter. + func recommendedVideoSettingsForAssetWriter( + withFileType fileType: AVFileType, + for output: CaptureVideoDataOutput + ) -> [String: Any]? { + return output.avOutput.recommendedVideoSettingsForAssetWriter(writingTo: fileType) + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCamConfiguration.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCamConfiguration.m deleted file mode 100644 index 2957ee511f1..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCamConfiguration.m +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -#import "./include/camera_avfoundation/FLTCamConfiguration.h" - -@implementation FLTCamConfiguration - -- (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings - mediaSettingsWrapper:(FLTCamMediaSettingsAVWrapper *)mediaSettingsWrapper - captureDeviceFactory:(CaptureDeviceFactory)captureDeviceFactory - audioCaptureDeviceFactory:(AudioCaptureDeviceFactory)audioCaptureDeviceFactory - captureSessionFactory:(CaptureSessionFactory)captureSessionFactory - captureSessionQueue:(dispatch_queue_t)captureSessionQueue - captureDeviceInputFactory: - (NSObject *)captureDeviceInputFactory - initialCameraName:(NSString *)initialCameraName { - self = [super init]; - if (self) { - _mediaSettings = mediaSettings; - _mediaSettingsWrapper = mediaSettingsWrapper; - _captureSessionQueue = captureSessionQueue; - _videoCaptureSession = captureSessionFactory(); - _audioCaptureSession = captureSessionFactory(); - _captureDeviceFactory = captureDeviceFactory; - _audioCaptureDeviceFactory = audioCaptureDeviceFactory; - _orientation = [[UIDevice currentDevice] orientation]; - _deviceOrientationProvider = [[FLTDefaultDeviceOrientationProvider alloc] init]; - _videoDimensionsForFormat = ^CMVideoDimensions(NSObject *format) { - return CMVideoFormatDescriptionGetDimensions(format.formatDescription); - }; - _captureDeviceInputFactory = captureDeviceInputFactory; - _initialCameraName = initialCameraName; - _assetWriterFactory = ^id(NSURL *url, AVFileType fileType, NSError **error) { - return [[FLTDefaultAssetWriter alloc] initWithURL:url fileType:fileType error:error]; - }; - _inputPixelBufferAdaptorFactory = ^NSObject *( - NSObject *assetWriterInput, - NSDictionary *sourcePixelBufferAttributes) { - return [[FLTDefaultAssetWriterInputPixelBufferAdaptor alloc] - initWithAdaptor:[[AVAssetWriterInputPixelBufferAdaptor alloc] - initWithAssetWriterInput:assetWriterInput.input - sourcePixelBufferAttributes:sourcePixelBufferAttributes]]; - }; - } - return self; -} - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCamMediaSettingsAVWrapper.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCamMediaSettingsAVWrapper.m deleted file mode 100644 index 8e8d818e21a..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCamMediaSettingsAVWrapper.m +++ /dev/null @@ -1,63 +0,0 @@ -// 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. - -#import "./include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h" -#import "./include/camera_avfoundation/FLTAssetWriter.h" -#import "./include/camera_avfoundation/FLTCaptureDevice.h" -#import "./include/camera_avfoundation/FLTCaptureSession.h" - -@implementation FLTCamMediaSettingsAVWrapper - -- (BOOL)lockDevice:(NSObject *)captureDevice - error:(NSError *_Nullable *_Nullable)outError { - return [captureDevice lockForConfiguration:outError]; -} - -- (void)unlockDevice:(NSObject *)captureDevice { - return [captureDevice unlockForConfiguration]; -} - -- (void)beginConfigurationForSession:(NSObject *)videoCaptureSession { - [videoCaptureSession beginConfiguration]; -} - -- (void)commitConfigurationForSession:(NSObject *)videoCaptureSession { - [videoCaptureSession commitConfiguration]; -} - -- (void)setMinFrameDuration:(CMTime)duration onDevice:(NSObject *)captureDevice { - captureDevice.activeVideoMinFrameDuration = duration; -} - -- (void)setMaxFrameDuration:(CMTime)duration onDevice:(NSObject *)captureDevice { - captureDevice.activeVideoMaxFrameDuration = duration; -} - -- (NSObject *)assetWriterAudioInputWithOutputSettings: - (nullable NSDictionary *)outputSettings { - return [[FLTDefaultAssetWriterInput alloc] - initWithInput:[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio - outputSettings:outputSettings]]; -} - -- (NSObject *)assetWriterVideoInputWithOutputSettings: - (nullable NSDictionary *)outputSettings { - return [[FLTDefaultAssetWriterInput alloc] - initWithInput:[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo - outputSettings:outputSettings]]; -} - -- (void)addInput:(NSObject *)writerInput - toAssetWriter:(NSObject *)writer { - [writer addInput:writerInput.input]; -} - -- (nullable NSDictionary *) - recommendedVideoSettingsForAssetWriterWithFileType:(AVFileType)fileType - forOutput: - (NSObject *)output { - return [output.avOutput recommendedVideoSettingsForAssetWriterWithOutputFileType:fileType]; -} - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCapturePhotoOutput.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCapturePhotoOutput.m deleted file mode 100644 index 4ae46834e15..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCapturePhotoOutput.m +++ /dev/null @@ -1,48 +0,0 @@ -// 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. - -#import "./include/camera_avfoundation/FLTCapturePhotoOutput.h" - -@interface FLTDefaultCapturePhotoOutput () -@property(nonatomic, strong) AVCapturePhotoOutput *avOutput; -@end - -@implementation FLTDefaultCapturePhotoOutput - -- (instancetype)initWithPhotoOutput:(AVCapturePhotoOutput *)photoOutput { - self = [super init]; - if (self) { - _avOutput = photoOutput; - } - return self; -} - -- (NSArray *)availablePhotoCodecTypes { - return self.avOutput.availablePhotoCodecTypes; -} - -- (BOOL)highResolutionCaptureEnabled { - return self.avOutput.isHighResolutionCaptureEnabled; -} - -- (void)setHighResolutionCaptureEnabled:(BOOL)enabled { - [self.avOutput setHighResolutionCaptureEnabled:enabled]; -} - -- (void)capturePhotoWithSettings:(AVCapturePhotoSettings *)settings - delegate:(NSObject *)delegate { - [self.avOutput capturePhotoWithSettings:settings delegate:delegate]; -} - -- (nullable NSObject *)connectionWithMediaType: - (nonnull AVMediaType)mediaType { - return [[FLTDefaultCaptureConnection alloc] - initWithConnection:[self.avOutput connectionWithMediaType:mediaType]]; -} - -- (NSArray *)supportedFlashModes { - return self.avOutput.supportedFlashModes; -} - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCaptureVideoDataOutput.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCaptureVideoDataOutput.m deleted file mode 100644 index 1bf98166aa8..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTCaptureVideoDataOutput.m +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -#import "./include/camera_avfoundation/FLTCaptureVideoDataOutput.h" - -@interface FLTDefaultCaptureVideoDataOutput () -@property(nonatomic, strong) AVCaptureVideoDataOutput *avOutput; -@end - -@implementation FLTDefaultCaptureVideoDataOutput - -- (instancetype)initWithCaptureVideoOutput:(AVCaptureVideoDataOutput *)videoOutput { - self = [super init]; - if (self) { - _avOutput = videoOutput; - } - return self; -} - -- (BOOL)alwaysDiscardsLateVideoFrames { - return self.avOutput.alwaysDiscardsLateVideoFrames; -} - -- (void)setAlwaysDiscardsLateVideoFrames:(BOOL)alwaysDiscardsLateVideoFrames { - self.avOutput.alwaysDiscardsLateVideoFrames = alwaysDiscardsLateVideoFrames; -} - -- (NSDictionary *)videoSettings { - return self.avOutput.videoSettings; -} - -- (void)setVideoSettings:(NSDictionary *)videoSettings { - self.avOutput.videoSettings = videoSettings; -} - -- (nullable NSObject *)connectionWithMediaType: - (nonnull AVMediaType)mediaType { - return [[FLTDefaultCaptureConnection alloc] - initWithConnection:[self.avOutput connectionWithMediaType:mediaType]]; -} - -- (void)setSampleBufferDelegate: - (nullable id)sampleBufferDelegate - queue:(nullable dispatch_queue_t)sampleBufferCallbackQueue { - [self.avOutput setSampleBufferDelegate:sampleBufferDelegate queue:sampleBufferCallbackQueue]; -} - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCamConfiguration.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCamConfiguration.h deleted file mode 100644 index d7c816c526e..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCamConfiguration.h +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -@import AVFoundation; -@import Foundation; -@import Flutter; - -#import "CameraProperties.h" -#import "FLTAssetWriter.h" -#import "FLTCamMediaSettingsAVWrapper.h" -#import "FLTCaptureDevice.h" -#import "FLTCaptureSession.h" -#import "FLTDeviceOrientationProviding.h" - -NS_ASSUME_NONNULL_BEGIN - -/// Factory block returning an AVCaptureDevice. -/// Used in tests to inject a device into FLTCam. -typedef NSObject *_Nonnull (^CaptureDeviceFactory)(NSString *); - -typedef NSObject *_Nonnull (^AudioCaptureDeviceFactory)(void); - -typedef NSObject *_Nonnull (^CaptureSessionFactory)(void); - -typedef NSObject *_Nonnull (^AssetWriterFactory)(NSURL *, AVFileType, - NSError *_Nullable *_Nullable); - -typedef NSObject *_Nonnull (^InputPixelBufferAdaptorFactory)( - NSObject *, NSDictionary *_Nullable); - -/// Determines the video dimensions (width and height) for a given capture device format. -/// Used in tests to mock CMVideoFormatDescriptionGetDimensions. -typedef CMVideoDimensions (^VideoDimensionsForFormat)(NSObject *); - -/// A configuration object that centralizes dependencies for `FLTCam`. -@interface FLTCamConfiguration : NSObject - -/// Initializes a new camera configuration with specified media settings and factories. -- (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings - mediaSettingsWrapper:(FLTCamMediaSettingsAVWrapper *)mediaSettingsWrapper - captureDeviceFactory:(CaptureDeviceFactory)captureDeviceFactory - audioCaptureDeviceFactory:(AudioCaptureDeviceFactory)audioCaptureDeviceFactory - captureSessionFactory:(CaptureSessionFactory)captureSessionFactory - captureSessionQueue:(dispatch_queue_t)captureSessionQueue - captureDeviceInputFactory: - (NSObject *)captureDeviceInputFactory - initialCameraName:(NSString *)initialCameraName; - -@property(nonatomic, strong) id deviceOrientationProvider; -@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; -@property(nonatomic, strong) FCPPlatformMediaSettings *mediaSettings; -@property(nonatomic, strong) FLTCamMediaSettingsAVWrapper *mediaSettingsWrapper; -@property(nonatomic, copy) CaptureDeviceFactory captureDeviceFactory; -@property(nonatomic, copy) AudioCaptureDeviceFactory audioCaptureDeviceFactory; -@property(nonatomic, copy) VideoDimensionsForFormat videoDimensionsForFormat; -@property(nonatomic, assign) UIDeviceOrientation orientation; -@property(nonatomic, strong) NSObject *videoCaptureSession; -@property(nonatomic, strong) NSObject *audioCaptureSession; -@property(nonatomic, strong) NSObject *captureDeviceInputFactory; -@property(nonatomic, copy) AssetWriterFactory assetWriterFactory; -@property(nonatomic, copy) InputPixelBufferAdaptorFactory inputPixelBufferAdaptorFactory; -@property(nonatomic, copy) NSString *initialCameraName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h deleted file mode 100644 index 3175573f442..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h +++ /dev/null @@ -1,120 +0,0 @@ -// 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. - -@import AVFoundation; -@import Foundation; - -#import "FLTAssetWriter.h" -#import "FLTCaptureDevice.h" -#import "FLTCaptureSession.h" -#import "FLTCaptureVideoDataOutput.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * @interface FLTCamMediaSettingsAVWrapper - * @abstract An interface for performing media settings operations. - * - * @discussion - * xctest-expectation-checking implementation (`TestMediaSettingsAVWrapper`) of this interface can - * be injected into `camera-avfoundation` plugin allowing to run media-settings tests without any - * additional mocking of AVFoundation classes. - */ -@interface FLTCamMediaSettingsAVWrapper : NSObject - -/** - * @method lockDevice:error: - * @abstract Requests exclusive access to configure device hardware properties. - * @param captureDevice The capture device. - * @param outError The optional error. - * @result A BOOL indicating whether the device was successfully locked for configuration. - */ -- (BOOL)lockDevice:(NSObject *)captureDevice - error:(NSError *_Nullable *_Nullable)outError NS_SWIFT_NAME(lockDevice(_:)); - -/** - * @method unlockDevice: - * @abstract Release exclusive control over device hardware properties. - * @param captureDevice The capture device. - */ -- (void)unlockDevice:(NSObject *)captureDevice NS_SWIFT_NAME(unlockDevice(_:)) ; - -/** - * @method beginConfigurationForSession: - * @abstract When paired with commitConfiguration, allows a client to batch multiple configuration - * operations on a running session into atomic updates. - * @param videoCaptureSession The video capture session. - */ -- (void)beginConfigurationForSession:(NSObject *)videoCaptureSession; - -/** - * @method commitConfigurationForSession: - * @abstract When preceded by beginConfiguration, allows a client to batch multiple configuration - * operations on a running session into atomic updates. - * @param videoCaptureSession The video capture session. - */ -- (void)commitConfigurationForSession:(NSObject *)videoCaptureSession; - -/** - * @method setMinFrameDuration:onDevice: - * @abstract Set receiver's current active minimum frame duration (the reciprocal of its max frame - * rate). - * @param duration The frame duration. - * @param captureDevice The capture device - */ -- (void)setMinFrameDuration:(CMTime)duration onDevice:(NSObject *)captureDevice; - -/** - * @method setMaxFrameDuration:onDevice: - * @abstract Set receiver's current active maximum frame duration (the reciprocal of its min frame - * rate). - * @param duration The frame duration. - * @param captureDevice The capture device - */ -- (void)setMaxFrameDuration:(CMTime)duration onDevice:(NSObject *)captureDevice; - -/** - * @method assetWriterAudioInputWithOutputSettings: - * @abstract Creates a new input of the audio media type to receive sample buffers for writing to - * the output file. - * @param outputSettings The settings used for encoding the audio appended to the output. - * @result An instance of `AVAssetWriterInput`. - */ -- (NSObject *)assetWriterAudioInputWithOutputSettings: - (nullable NSDictionary *)outputSettings; - -/** - * @method assetWriterVideoInputWithOutputSettings: - * @abstract Creates a new input of the video media type to receive sample buffers for writing to - * the output file. - * @param outputSettings The settings used for encoding the video appended to the output. - * @result An instance of `AVAssetWriterInput`. - */ -- (NSObject *)assetWriterVideoInputWithOutputSettings: - (nullable NSDictionary *)outputSettings; - -/** - * @method addInput:toAssetWriter: - * @abstract Adds an input to the asset writer. - * @param writerInput The `AVAssetWriterInput` object to be added. - * @param writer The `AVAssetWriter` object. - */ -- (void)addInput:(NSObject *)writerInput - toAssetWriter:(NSObject *)writer NS_SWIFT_NAME(addInput(_:to:)); - -/** - * @method recommendedVideoSettingsForAssetWriterWithFileType:forOutput: - * @abstract Specifies the recommended video settings for `AVCaptureVideoDataOutput`. - * @param fileType Specifies the UTI of the file type to be written (see AVMediaFormat.h for a list - * of file format UTIs). - * @param output The `FLTCaptureVideoDataOutput` instance. - * @result A fully populated dictionary of keys and values that are compatible with AVAssetWriter. - */ -- (nullable NSDictionary *) - recommendedVideoSettingsForAssetWriterWithFileType:(AVFileType)fileType - forOutput: - (NSObject *)output; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCaptureOutput.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCaptureOutput.h deleted file mode 100644 index 85918cb9fe2..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCaptureOutput.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -@import Foundation; -@import AVFoundation; - -#import "FLTCaptureConnection.h" - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol which is a direct passthrough to `AVCapturePhotoOutput`. It exists to allow mocking -/// `AVCapturePhotoOutput` in tests. -@protocol FLTCaptureOutput - -///// The underlying instance of `AVCapturePhotoOutput`. -//@property(nonatomic, readonly) AVCaptureOutput *avOutput; - -- (nullable NSObject *)connectionWithMediaType:(AVMediaType)mediaType; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCapturePhotoOutput.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCapturePhotoOutput.h deleted file mode 100644 index 861ad40e31f..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCapturePhotoOutput.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -@import Foundation; -@import AVFoundation; - -#import "FLTCaptureOutput.h" - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol which is a direct passthrough to `AVCapturePhotoOutput`. It exists to allow mocking -/// `AVCapturePhotoOutput` in tests. -@protocol FLTCapturePhotoOutput - -/// The underlying instance of `AVCapturePhotoOutput`. -@property(nonatomic, readonly) AVCapturePhotoOutput *avOutput; - -/// Corresponds to the `availablePhotoCodecTypes` property of `AVCapturePhotoOutput` -@property(nonatomic, readonly) NSArray *availablePhotoCodecTypes; - -/// Corresponds to the `highResolutionCaptureEnabled` property of `AVCapturePhotoOutput` -@property(nonatomic, assign) BOOL highResolutionCaptureEnabled; - -/// Corresponds to the `supportedFlashModes` property of `AVCapturePhotoOutput` -@property(nonatomic, readonly) NSArray *supportedFlashModes; - -/// Corresponds to the `capturePhotoWithSettings` method of `AVCapturePhotoOutput` -- (void)capturePhotoWithSettings:(AVCapturePhotoSettings *)settings - delegate:(NSObject *)delegate; - -@end - -/// A default implementation of `FLTCapturePhotoOutput` which wraps an instance of -/// `AVCapturePhotoOutput`. -@interface FLTDefaultCapturePhotoOutput : NSObject - -/// Initializes an instance of `FLTDefaultCapturePhotoOutput` with the given `AVCapturePhotoOutput`. -/// All method and property calls will be forwarded to the given `photoOutput`. -- (instancetype)initWithPhotoOutput:(AVCapturePhotoOutput *)photoOutput; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCaptureVideoDataOutput.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCaptureVideoDataOutput.h deleted file mode 100644 index cc27e0b5dc8..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTCaptureVideoDataOutput.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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. - -@import Foundation; -@import AVFoundation; - -#import "FLTCaptureOutput.h" - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol which is a direct passthrough to `AVCaptureVideoDataOutput`. It exists to allow -/// mocking `AVCaptureVideoDataOutput` in tests. -@protocol FLTCaptureVideoDataOutput - -/// The underlying instance of `AVCaptureVideoDataOutput`. -@property(nonatomic, readonly) AVCaptureVideoDataOutput *avOutput; - -/// Corresponds to the `alwaysDiscardsLateVideoFrames` property of `AVCaptureVideoDataOutput` -@property(nonatomic) BOOL alwaysDiscardsLateVideoFrames; - -/// Corresponds to the `videoSettings` property of `AVCaptureVideoDataOutput` -@property(nonatomic, copy, null_resettable) NSDictionary *videoSettings; - -/// Corresponds to the `setSampleBufferDelegate` method of `AVCaptureVideoDataOutput` -- (void)setSampleBufferDelegate: - (nullable id)sampleBufferDelegate - queue:(nullable dispatch_queue_t)sampleBufferCallbackQueue; - -@end - -/// A default implementation of `FLTCaptureVideoDataOutput` which wraps an instance of -/// `AVCaptureVideoDataOutput`. -@interface FLTDefaultCaptureVideoDataOutput : NSObject - -/// Initializes an instance of `FLTDefaultCaptureVideDataOutput` with the given -/// `AVCaptureVideoDataOutput`. All method and property calls will be forwarded to the given -/// `videoOutput`. -- (instancetype)initWithCaptureVideoOutput:(AVCaptureVideoDataOutput *)videoOutput; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTFormatUtils.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTFormatUtils.h index c4e70fe5f99..ab0097592d2 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTFormatUtils.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTFormatUtils.h @@ -2,12 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FLTCamConfiguration.h" #import "FLTCaptureDevice.h" #import "FLTCaptureDeviceFormat.h" +#import "messages.g.h" NS_ASSUME_NONNULL_BEGIN +/// Determines the video dimensions (width and height) for a given capture device format. +/// Used in tests to mock CMVideoFormatDescriptionGetDimensions. +typedef CMVideoDimensions (^VideoDimensionsForFormat)(NSObject *); + // Finds format with same resolution as current activeFormat in captureDevice for which // bestFrameRateForFormat returned frame rate closest to mediaSettings.framesPerSecond. // Preferred are formats with the same subtype as current activeFormat. Sets this format diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h index d19cd0c5c70..dfbeb2b93ca 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FLTCamConfiguration.h" #import "FLTCameraDeviceDiscovering.h" #import "FLTCameraPermissionManager.h" #import "FLTCaptureDevice.h" diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index f819184512f..04455cf673d 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -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.22+1 +version: 0.9.22+2 environment: sdk: ^3.9.0