Skip to content

Commit 9cda6e1

Browse files
committed
Migrate FLTCamConfiguration, FLTCamMediaSettingsAVWrapper, FLTCaptureOutput, FLTCapturePhotoOutput, FLTCaptureVideoDataOutput to Swift
1 parent affa244 commit 9cda6e1

22 files changed

+297
-529
lines changed

packages/camera/camera_avfoundation/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.9.21+5
2+
3+
* Migrates `FLTCamConfiguration`, `FLTCamMediaSettingsAVWrapper` classes to Swift.
4+
* Migrates `FLTCaptureOutput`, `FLTCapturePhotoOutput`, `FLTCaptureVideoDataOutput` protocols to Swift.
5+
16
## 0.9.21+4
27

38
* Migrates `updateOrientation` and `setCaptureSessionPreset` methods to Swift.

packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ private final class TestMediaSettingsAVWrapper: FLTCamMediaSettingsAVWrapper {
106106
}
107107

108108
override func recommendedVideoSettingsForAssetWriter(
109-
withFileType fileType: AVFileType, for output: FLTCaptureVideoDataOutput
109+
withFileType fileType: AVFileType, for output: CaptureVideoDataOutput
110110
) -> [String: Any]? {
111111
return [:]
112112
}

packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ final class FLTCamSetFlashModeTests: XCTestCase {
8888
let (camera, mockDevice, mockCapturePhotoOutput) = createCamera()
8989

9090
mockCapturePhotoOutput.supportedFlashModes = [
91-
NSNumber(value: AVCaptureDevice.FlashMode.auto.rawValue)
91+
AVCaptureDevice.FlashMode.auto
9292
]
9393

9494
mockDevice.hasFlash = true

packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import camera_avfoundation
5+
@testable import camera_avfoundation
66

77
// Import Objective-C part of the implementation when SwiftPM is used.
88
#if canImport(camera_avfoundation_objc)
@@ -11,11 +11,11 @@ import camera_avfoundation
1111

1212
/// Mock implementation of `FLTCapturePhotoOutput` protocol which allows injecting a custom
1313
/// implementation.
14-
final class MockCapturePhotoOutput: NSObject, FLTCapturePhotoOutput {
14+
final class MockCapturePhotoOutput: NSObject, CapturePhotoOutput {
1515
var avOutput = AVCapturePhotoOutput()
1616
var availablePhotoCodecTypes: [AVVideoCodecType] = []
17-
var highResolutionCaptureEnabled = false
18-
var supportedFlashModes: [NSNumber] = []
17+
var isHighResolutionCaptureEnabled = false
18+
var supportedFlashModes: [AVCaptureDevice.FlashMode] = []
1919

2020
// Stub that is called when the corresponding public method is called.
2121
var capturePhotoWithSettingsStub:

packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import camera_avfoundation
5+
@testable import camera_avfoundation
66

77
// Import Objective-C part of the implementation when SwiftPM is used.
88
#if canImport(camera_avfoundation_objc)
@@ -11,11 +11,10 @@ import camera_avfoundation
1111

1212
/// Mock implementation of `FLTCaptureVideoDataOutput` protocol which allows injecting a custom
1313
/// implementation.
14-
class MockCaptureVideoDataOutput: NSObject, FLTCaptureVideoDataOutput {
15-
14+
class MockCaptureVideoDataOutput: NSObject, CaptureVideoDataOutput {
1615
var avOutput = AVCaptureVideoDataOutput()
1716
var alwaysDiscardsLateVideoFrames = false
18-
var videoSettings: [String: Any] = [:]
17+
var videoSettings: [String: Any]! = [:]
1918

2019
var connectionWithMediaTypeStub: ((AVMediaType) -> FLTCaptureConnection?)?
2120

packages/camera/camera_avfoundation/example/ios/RunnerTests/SampleBufferTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private class FakeMediaSettingsAVWrapper: FLTCamMediaSettingsAVWrapper {
6060
}
6161

6262
override func recommendedVideoSettingsForAssetWriter(
63-
withFileType fileType: AVFileType, for output: FLTCaptureVideoDataOutput
63+
withFileType fileType: AVFileType, for output: CaptureVideoDataOutput
6464
) -> [String: Any]? {
6565
return [:]
6666
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2013 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import AVFoundation
6+
import CoreMedia
7+
import Foundation
8+
import UIKit
9+
10+
// Import Objective-C part of the implementation when SwiftPM is used.
11+
#if canImport(camera_avfoundation_objc)
12+
import camera_avfoundation_objc
13+
#endif
14+
15+
/// Factory block returning an FLTCaptureDevice.
16+
/// Used in tests to inject a device into DefaultCamera.
17+
typealias CaptureDeviceFactory = (String) -> FLTCaptureDevice
18+
19+
typealias AudioCaptureDeviceFactory = () -> FLTCaptureDevice
20+
21+
typealias CaptureSessionFactory = () -> FLTCaptureSession
22+
23+
typealias AssetWriterFactory = (URL, AVFileType, inout NSError?) -> FLTAssetWriter?
24+
25+
typealias InputPixelBufferAdaptorFactory = (FLTAssetWriterInput, [String: Any]?) -> FLTAssetWriterInputPixelBufferAdaptor
26+
27+
/// Determines the video dimensions (width and height) for a given capture device format.
28+
/// Used in tests to mock CMVideoFormatDescriptionGetDimensions.
29+
typealias VideoDimensionsForFormat = (FLTCaptureDeviceFormat) -> CMVideoDimensions
30+
31+
/// A configuration object that centralizes dependencies for `DefaultCamera`.
32+
class FLTCamConfiguration {
33+
var mediaSettings: FCPPlatformMediaSettings
34+
var mediaSettingsWrapper: FLTCamMediaSettingsAVWrapper
35+
var captureSessionQueue: DispatchQueue
36+
var videoCaptureSession: FLTCaptureSession
37+
var audioCaptureSession: FLTCaptureSession
38+
var captureDeviceFactory: CaptureDeviceFactory
39+
let audioCaptureDeviceFactory: AudioCaptureDeviceFactory
40+
let captureDeviceInputFactory: FLTCaptureDeviceInputFactory
41+
var assetWriterFactory: AssetWriterFactory
42+
var inputPixelBufferAdaptorFactory: InputPixelBufferAdaptorFactory
43+
var videoDimensionsForFormat: VideoDimensionsForFormat
44+
var deviceOrientationProvider: FLTDeviceOrientationProviding
45+
let initialCameraName: String
46+
var orientation: UIDeviceOrientation
47+
48+
init(
49+
mediaSettings: FCPPlatformMediaSettings,
50+
mediaSettingsWrapper: FLTCamMediaSettingsAVWrapper,
51+
captureDeviceFactory: @escaping CaptureDeviceFactory,
52+
audioCaptureDeviceFactory: @escaping AudioCaptureDeviceFactory,
53+
captureSessionFactory: @escaping CaptureSessionFactory,
54+
captureSessionQueue: DispatchQueue,
55+
captureDeviceInputFactory: FLTCaptureDeviceInputFactory,
56+
initialCameraName: String
57+
) {
58+
self.mediaSettings = mediaSettings
59+
self.mediaSettingsWrapper = mediaSettingsWrapper
60+
self.captureDeviceFactory = captureDeviceFactory
61+
self.audioCaptureDeviceFactory = audioCaptureDeviceFactory
62+
self.captureSessionQueue = captureSessionQueue
63+
self.videoCaptureSession = captureSessionFactory()
64+
self.audioCaptureSession = captureSessionFactory()
65+
self.captureDeviceInputFactory = captureDeviceInputFactory
66+
self.initialCameraName = initialCameraName
67+
self.orientation = UIDevice.current.orientation
68+
self.deviceOrientationProvider = FLTDefaultDeviceOrientationProvider()
69+
70+
self.videoDimensionsForFormat = { format in
71+
return CMVideoFormatDescriptionGetDimensions(format.formatDescription)
72+
}
73+
74+
self.assetWriterFactory = { url, fileType, error in
75+
return FLTDefaultAssetWriter(url: url, fileType: fileType, error: &error)
76+
}
77+
78+
self.inputPixelBufferAdaptorFactory = { assetWriterInput, sourcePixelBufferAttributes in
79+
let adaptor = AVAssetWriterInputPixelBufferAdaptor(
80+
assetWriterInput: assetWriterInput.input,
81+
sourcePixelBufferAttributes: sourcePixelBufferAttributes
82+
)
83+
return FLTDefaultAssetWriterInputPixelBufferAdaptor(adaptor: adaptor)
84+
}
85+
}
86+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2013 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import AVFoundation
6+
import Foundation
7+
8+
// Import Objective-C part of the implementation when SwiftPM is used.
9+
#if canImport(camera_avfoundation_objc)
10+
import camera_avfoundation_objc
11+
#endif
12+
13+
/// A protocol which is a direct passthrough to `AVCaptureOutput`. It exists to allow mocking
14+
/// `AVCaptureOutput` in tests.
15+
protocol CaptureOutput {
16+
/// Returns a connection with the specified media type, or nil if no such connection exists.
17+
func connection(withMediaType mediaType: AVMediaType) -> FLTCaptureConnection?
18+
}
19+
20+
/// A protocol which is a direct passthrough to `AVCaptureVideoDataOutput`. It exists to allow
21+
/// mocking `AVCaptureVideoDataOutput` in tests.
22+
protocol CaptureVideoDataOutput: CaptureOutput {
23+
/// The underlying instance of `AVCaptureVideoDataOutput`.
24+
var avOutput: AVCaptureVideoDataOutput { get }
25+
26+
/// Corresponds to the `alwaysDiscardsLateVideoFrames` property of `AVCaptureVideoDataOutput`
27+
var alwaysDiscardsLateVideoFrames: Bool { get set }
28+
29+
/// Corresponds to the `videoSettings` property of `AVCaptureVideoDataOutput`
30+
var videoSettings: [String: Any]! { get set }
31+
32+
/// Corresponds to the `setSampleBufferDelegate` method of `AVCaptureVideoDataOutput`
33+
func setSampleBufferDelegate(
34+
_ sampleBufferDelegate: AVCaptureVideoDataOutputSampleBufferDelegate?,
35+
queue sampleBufferCallbackQueue: DispatchQueue?
36+
)
37+
}
38+
39+
extension AVCaptureVideoDataOutput: CaptureVideoDataOutput {
40+
var avOutput: AVCaptureVideoDataOutput {
41+
return self
42+
}
43+
44+
func connection(withMediaType mediaType: AVMediaType) -> FLTCaptureConnection? {
45+
guard let connection = connection(with: mediaType) else { return nil }
46+
return FLTDefaultCaptureConnection(connection: connection)
47+
}
48+
}
49+
50+
/// A protocol which is a direct passthrough to `AVCapturePhotoOutput`. It exists to allow mocking
51+
/// `AVCapturePhotoOutput` in tests.
52+
protocol CapturePhotoOutput: CaptureOutput {
53+
/// The underlying instance of `AVCapturePhotoOutput`.
54+
var avOutput: AVCapturePhotoOutput { get }
55+
56+
/// Corresponds to the `availablePhotoCodecTypes` property of `AVCapturePhotoOutput`
57+
var availablePhotoCodecTypes: [AVVideoCodecType] { get }
58+
59+
/// Corresponds to the `isHighResolutionCaptureEnabled` property of `AVCapturePhotoOutput`
60+
var isHighResolutionCaptureEnabled: Bool { get set }
61+
62+
/// Corresponds to the `supportedFlashModes` property of `AVCapturePhotoOutput`
63+
var supportedFlashModes: [AVCaptureDevice.FlashMode] { get }
64+
65+
/// Corresponds to the `capturePhotoWithSettings` method of `AVCapturePhotoOutput`
66+
func capturePhoto(with settings: AVCapturePhotoSettings, delegate: AVCapturePhotoCaptureDelegate)
67+
}
68+
69+
/// Make AVCapturePhotoOutput conform to FLTCapturePhotoOutput protocol directly
70+
extension AVCapturePhotoOutput: CapturePhotoOutput {
71+
var avOutput: AVCapturePhotoOutput {
72+
return self
73+
}
74+
75+
func connection(withMediaType mediaType: AVMediaType) -> FLTCaptureConnection? {
76+
guard let connection = connection(with: mediaType) else { return nil }
77+
return FLTDefaultCaptureConnection(connection: connection)
78+
}
79+
}

packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ final class DefaultCamera: NSObject, Camera {
6262

6363
private(set) var captureDevice: FLTCaptureDevice
6464
// Setter exposed for tests.
65-
var captureVideoOutput: FLTCaptureVideoDataOutput
65+
var captureVideoOutput: CaptureVideoDataOutput
6666
// Setter exposed for tests.
67-
var capturePhotoOutput: FLTCapturePhotoOutput
67+
var capturePhotoOutput: CapturePhotoOutput
6868
private var captureVideoInput: FLTCaptureInput
6969

7070
private var videoWriter: FLTAssetWriter?
@@ -135,13 +135,12 @@ final class DefaultCamera: NSObject, Camera {
135135
captureDevice: FLTCaptureDevice,
136136
videoFormat: FourCharCode,
137137
captureDeviceInputFactory: FLTCaptureDeviceInputFactory
138-
) throws -> (FLTCaptureInput, FLTCaptureVideoDataOutput, AVCaptureConnection) {
138+
) throws -> (FLTCaptureInput, CaptureVideoDataOutput, AVCaptureConnection) {
139139
// Setup video capture input.
140140
let captureVideoInput = try captureDeviceInputFactory.deviceInput(with: captureDevice)
141141

142142
// Setup video capture output.
143-
let captureVideoOutput = FLTDefaultCaptureVideoDataOutput(
144-
captureVideoOutput: AVCaptureVideoDataOutput())
143+
let captureVideoOutput = AVCaptureVideoDataOutput()
145144
captureVideoOutput.videoSettings = [
146145
kCVPixelBufferPixelFormatTypeKey as String: videoFormat
147146
]
@@ -176,8 +175,8 @@ final class DefaultCamera: NSObject, Camera {
176175
captureDevice = captureDeviceFactory(configuration.initialCameraName)
177176
flashMode = captureDevice.hasFlash ? .auto : .off
178177

179-
capturePhotoOutput = FLTDefaultCapturePhotoOutput(photoOutput: AVCapturePhotoOutput())
180-
capturePhotoOutput.highResolutionCaptureEnabled = true
178+
capturePhotoOutput = AVCapturePhotoOutput()
179+
capturePhotoOutput.isHighResolutionCaptureEnabled = true
181180

182181
videoCaptureSession.automaticallyConfiguresApplicationAudioSession = false
183182
audioCaptureSession.automaticallyConfiguresApplicationAudioSession = false
@@ -748,7 +747,7 @@ final class DefaultCamera: NSObject, Camera {
748747
}
749748

750749
private func updateOrientation(
751-
_ orientation: UIDeviceOrientation, forCaptureOutput captureOutput: FLTCaptureOutput
750+
_ orientation: UIDeviceOrientation, forCaptureOutput captureOutput: CaptureOutput
752751
) {
753752
if let connection = captureOutput.connection(withMediaType: .video),
754753
connection.isVideoOrientationSupported
@@ -989,7 +988,7 @@ final class DefaultCamera: NSObject, Camera {
989988
return
990989
}
991990
let avFlashMode = FCPGetAVCaptureFlashModeForPigeonFlashMode(mode)
992-
guard capturePhotoOutput.supportedFlashModes.contains(NSNumber(value: avFlashMode.rawValue))
991+
guard capturePhotoOutput.supportedFlashModes.contains(avFlashMode)
993992
else {
994993
completion(
995994
FlutterError(

0 commit comments

Comments
 (0)