Skip to content

Commit bcbc9a7

Browse files
authored
feat(ios): Add ios config with audio categories. #396
2 parents 809a5e4 + ae59cd0 commit bcbc9a7

File tree

7 files changed

+91
-11
lines changed

7 files changed

+91
-11
lines changed

record/example/ios/Runner/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import UIKit
22
import Flutter
33

4-
@UIApplicationMain
4+
@main
55
@objc class AppDelegate: FlutterAppDelegate {
66
override func application(
77
_ application: UIApplication,

record_darwin/darwin/Classes/RecordConfig.swift

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import AVFoundation
2+
13
public enum AudioEncoder: String {
24
case aacLc = "aacLc"
35
case aacEld = "aacEld"
@@ -19,6 +21,7 @@ public class RecordConfig {
1921
let autoGain: Bool
2022
let echoCancel: Bool
2123
let noiseSuppress: Bool
24+
let iosConfig: IosConfig?
2225

2326
init(encoder: String,
2427
bitRate: Int,
@@ -27,7 +30,8 @@ public class RecordConfig {
2730
device: Device? = nil,
2831
autoGain: Bool = false,
2932
echoCancel: Bool = false,
30-
noiseSuppress: Bool = false
33+
noiseSuppress: Bool = false,
34+
iosConfig: IosConfig? = nil
3135
) {
3236
self.encoder = encoder
3337
self.bitRate = bitRate
@@ -37,27 +41,58 @@ public class RecordConfig {
3741
self.autoGain = autoGain
3842
self.echoCancel = echoCancel
3943
self.noiseSuppress = noiseSuppress
44+
self.iosConfig = iosConfig
4045
}
4146
}
4247

4348
public class Device {
4449
let id: String
4550
let label: String
46-
51+
4752
init(id: String, label: String) {
4853
self.id = id
4954
self.label = label
5055
}
51-
56+
5257
init(map: [String: Any]) {
5358
self.id = map["id"] as! String
5459
self.label = map["label"] as! String
5560
}
56-
61+
5762
func toMap() -> [String: Any] {
5863
return [
5964
"id": id,
6065
"label": label
6166
]
6267
}
6368
}
69+
70+
struct IosConfig {
71+
let audioCategories: [AVAudioSession.CategoryOptions]
72+
73+
init(map: [String: Any]) {
74+
let comps = map["audioCategories"] as? String
75+
let options: [AVAudioSession.CategoryOptions]? = comps?.split(separator: ",").compactMap {
76+
switch $0 {
77+
case "mixWithOthers":
78+
.mixWithOthers
79+
case "duckOthers":
80+
.duckOthers
81+
case "allowBluetooth":
82+
.allowBluetooth
83+
case "defaultToSpeaker":
84+
.defaultToSpeaker
85+
case "interruptSpokenAudioAndMixWithOthers":
86+
.interruptSpokenAudioAndMixWithOthers
87+
case "allowBluetoothA2DP":
88+
.allowBluetoothA2DP
89+
case "allowAirPlay":
90+
.allowAirPlay
91+
case "overrideMutedMicrophoneInterruption":
92+
if #available(iOS 14.5, *) { .overrideMutedMicrophoneInterruption } else { nil }
93+
default: nil
94+
}
95+
}
96+
self.audioCategories = options ?? []
97+
}
98+
}

record_darwin/darwin/Classes/SwiftRecordPlugin.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,12 @@ public class SwiftRecordPlugin: NSObject, FlutterPlugin {
184184
if let deviceMap = args["device"] as? [String : Any] {
185185
device = Device(map: deviceMap)
186186
}
187-
187+
188+
var iosConfig: IosConfig? = nil
189+
if let iosConfigMap = args["iosConfig"] as? [String : Any] {
190+
iosConfig = IosConfig(map: iosConfigMap)
191+
}
192+
188193
let config = RecordConfig(
189194
encoder: encoder,
190195
bitRate: args["bitRate"] as? Int ?? 128000,
@@ -193,7 +198,8 @@ public class SwiftRecordPlugin: NSObject, FlutterPlugin {
193198
device: device,
194199
autoGain: args["autoGain"] as? Bool ?? false,
195200
echoCancel: args["echoCancel"] as? Bool ?? false,
196-
noiseSuppress: args["noiseSuppress"] as? Bool ?? false
201+
noiseSuppress: args["noiseSuppress"] as? Bool ?? false,
202+
iosConfig: iosConfig
197203
)
198204

199205
return config

record_darwin/ios/Classes/RecorderIOS.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func listInputs() throws -> [Device] {
1313
func listInputDevices() throws -> [AVAudioSessionPortDescription]? {
1414
let audioSession = AVAudioSession.sharedInstance()
1515
let options: AVAudioSession.CategoryOptions = [.defaultToSpeaker, .allowBluetooth]
16-
16+
1717
do {
1818
try audioSession.setCategory(.playAndRecord, options: options)
1919
} catch {
@@ -46,10 +46,9 @@ private func setInput(_ config: RecordConfig) throws {
4646
extension AudioRecordingDelegate {
4747
func initAVAudioSession(config: RecordConfig) throws {
4848
let audioSession = AVAudioSession.sharedInstance()
49-
let options: AVAudioSession.CategoryOptions = [.defaultToSpeaker, .allowBluetooth, .allowBluetoothA2DP]
50-
49+
5150
do {
52-
try audioSession.setCategory(.playAndRecord, options: options)
51+
try audioSession.setCategory(.playAndRecord, options: AVAudioSession.CategoryOptions(config.iosConfig?.audioCategories ?? []))
5352
} catch {
5453
throw RecorderError.error(message: "Failed to start recording", details: "setCategory: \(error.localizedDescription)")
5554
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// Constants that specify optional audio behaviors.
2+
/// https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions
3+
enum IosAudioCategories {
4+
mixWithOthers,
5+
duckOthers,
6+
allowBluetooth,
7+
defaultToSpeaker,
8+
/// available from iOS 9.0
9+
interruptSpokenAudioAndMixWithOthers,
10+
/// available from iOS 10.0
11+
allowBluetoothA2DP,
12+
/// available from iOS 10.0
13+
allowAirPlay,
14+
/// available from iOS 14.5
15+
overrideMutedMicrophoneInterruption
16+
}

record_platform_interface/lib/src/types/record_config.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class RecordConfig {
5151
/// Android specific configuration.
5252
final AndroidRecordConfig androidConfig;
5353

54+
/// iOS specific audioCategories
55+
final IosRecordConfig iosConfig;
56+
5457
const RecordConfig({
5558
this.encoder = AudioEncoder.aacLc,
5659
this.bitRate = 128000,
@@ -61,6 +64,7 @@ class RecordConfig {
6164
this.echoCancel = false,
6265
this.noiseSuppress = false,
6366
this.androidConfig = const AndroidRecordConfig(),
67+
this.iosConfig = const IosRecordConfig(),
6468
});
6569

6670
Map<String, dynamic> toMap() {
@@ -74,6 +78,7 @@ class RecordConfig {
7478
'echoCancel': echoCancel,
7579
'noiseSuppress': noiseSuppress,
7680
'androidConfig': androidConfig.toMap(),
81+
'iosConfig': iosConfig.toMap(),
7782
};
7883
}
7984
}
@@ -108,3 +113,21 @@ class AndroidRecordConfig {
108113
};
109114
}
110115
}
116+
117+
///
118+
class IosRecordConfig {
119+
120+
/// Constants that specify optional audio behaviors.
121+
/// https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions
122+
final List<IosAudioCategories> audioCategories;
123+
124+
const IosRecordConfig({
125+
this.audioCategories = const [IosAudioCategories.defaultToSpeaker, IosAudioCategories.allowBluetooth, IosAudioCategories.allowBluetoothA2DP]
126+
});
127+
Map<String, dynamic> toMap() {
128+
return {
129+
"audioCategories": audioCategories.map((e) => e.name).join(',')
130+
};
131+
}
132+
133+
}

record_platform_interface/lib/src/types/types.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export 'package:record_platform_interface/src/types/audio_encoder.dart';
33
export 'package:record_platform_interface/src/types/input_device.dart';
44
export 'package:record_platform_interface/src/types/record_config.dart';
55
export 'package:record_platform_interface/src/types/record_state.dart';
6+
export 'package:record_platform_interface/src/types/ios_audio_categories.dart';

0 commit comments

Comments
 (0)