Browse Source

ios: refactor AudioMode to use RTCAudioSession

RTCAudioSession is a thin wrapper around AVAudioSession provided by the WebRTC
framework. It makes some use-cases easier, and leads us closer to manual audio
unit management, which we will likely need in the near future.
j8
Saúl Ibarra Corretgé 6 years ago
parent
commit
7e320a5d38
1 changed files with 70 additions and 79 deletions
  1. 70
    79
      ios/sdk/src/AudioMode.m

+ 70
- 79
ios/sdk/src/AudioMode.m View File

@@ -19,6 +19,7 @@
19 19
 
20 20
 #import <React/RCTBridgeModule.h>
21 21
 #import <React/RCTLog.h>
22
+#import <WebRTC/WebRTC.h>
22 23
 
23 24
 typedef enum {
24 25
     kAudioModeDefault,
@@ -26,16 +27,17 @@ typedef enum {
26 27
     kAudioModeVideoCall
27 28
 } JitsiMeetAudioMode;
28 29
 
29
-@interface AudioMode : NSObject<RCTBridgeModule>
30
+@interface AudioMode : NSObject<RCTBridgeModule, RTCAudioSessionDelegate>
30 31
 
31 32
 @property(nonatomic, strong) dispatch_queue_t workerQueue;
32 33
 
33 34
 @end
34 35
 
35 36
 @implementation AudioMode {
36
-    NSString *_avCategory;
37
-    NSString *_avMode;
38
-    JitsiMeetAudioMode _mode;
37
+    JitsiMeetAudioMode activeMode;
38
+    RTCAudioSessionConfiguration *defaultConfig;
39
+    RTCAudioSessionConfiguration *audioCallConfig;
40
+    RTCAudioSessionConfiguration *videoCallConfig;
39 41
 }
40 42
 
41 43
 RCT_EXPORT_MODULE();
@@ -55,24 +57,32 @@ RCT_EXPORT_MODULE();
55 57
 - (instancetype)init {
56 58
     self = [super init];
57 59
     if (self) {
58
-        _avCategory = nil;
59
-        _avMode = nil;
60
-        _mode = kAudioModeDefault;
61
-
62 60
         dispatch_queue_attr_t attributes =
63 61
         dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
64 62
                                                 QOS_CLASS_USER_INITIATED, -1);
65 63
         _workerQueue = dispatch_queue_create("AudioMode.queue", attributes);
66 64
 
67
-        // AVAudioSession is a singleton and other parts of the application such as
68
-        // WebRTC may undo the settings. Make sure that the settings are reapplied
69
-        // upon undoes.
70
-        [[NSNotificationCenter defaultCenter]
71
-             addObserver:self
72
-                selector:@selector(routeChanged:)
73
-                    name:AVAudioSessionRouteChangeNotification
74
-                  object:nil];
65
+        activeMode = kAudioModeDefault;
66
+
67
+        defaultConfig = [[RTCAudioSessionConfiguration alloc] init];
68
+        defaultConfig.category = AVAudioSessionCategoryAmbient;
69
+        defaultConfig.categoryOptions = 0;
70
+        defaultConfig.mode = AVAudioSessionModeDefault;
71
+
72
+        audioCallConfig = [[RTCAudioSessionConfiguration alloc] init];
73
+        audioCallConfig.category = AVAudioSessionCategoryPlayAndRecord;
74
+        audioCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth;
75
+        audioCallConfig.mode = AVAudioSessionModeVoiceChat;
76
+
77
+        videoCallConfig = [[RTCAudioSessionConfiguration alloc] init];
78
+        videoCallConfig.category = AVAudioSessionCategoryPlayAndRecord;
79
+        videoCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth;
80
+        videoCallConfig.mode = AVAudioSessionModeVideoChat;
81
+
82
+        RTCAudioSession *session = [RTCAudioSession sharedInstance];
83
+        [session addDelegate:self];
75 84
     }
85
+
76 86
     return self;
77 87
 }
78 88
 
@@ -81,93 +91,74 @@ RCT_EXPORT_MODULE();
81 91
     return _workerQueue;
82 92
 }
83 93
 
84
-- (void)routeChanged:(NSNotification*)notification {
85
-    NSInteger reason
86
-        = [[notification.userInfo
87
-                valueForKey:AVAudioSessionRouteChangeReasonKey]
88
-            integerValue];
89
-
90
-    switch (reason) {
91
-    case AVAudioSessionRouteChangeReasonCategoryChange: {
92
-        // The category has changed. Check if it's the one we want and adjust as
93
-        // needed. This notification is posted on a secondary thread, so make
94
-        // sure we switch to our worker thread.
95
-        dispatch_async(_workerQueue, ^{
96
-            [self setCategory:self->_avCategory mode:self->_avMode error:nil];
97
-        });
98
-        break;
99
-    }
100
-    default:
101
-        // Do nothing.
102
-        break;
103
-    }
104
-}
105
-
106
-- (BOOL)setCategory:(NSString *)category
107
-               mode:(NSString *)mode
108
-              error:(NSError * _Nullable *)outError {
109
-    AVAudioSession *session = [AVAudioSession sharedInstance];
110
-
111
-    // We don't want to touch the category when setting the default mode.
112
-    // This is to play well with other components which could be integrated
113
-    // into the final application.
114
-    if (_mode == kAudioModeDefault) {
115
-        return YES;
116
-    }
117
-
118
-    // Nothing to do.
119
-    if (category == nil && mode == nil) {
120
-        return YES;
121
-    }
122
-
123
-    if (session.category != category
124
-            && ![session setCategory:category error:outError]) {
125
-        RCTLogError(@"Failed to (re)apply specified AVAudioSession category!");
126
-        return NO;
127
-    }
94
+- (BOOL)setConfig:(RTCAudioSessionConfiguration *)config
95
+            error:(NSError * _Nullable *)outError {
128 96
 
129
-    if (session.mode != mode && ![session setMode:mode error:outError]) {
130
-        RCTLogError(@"Failed to (re)apply specified AVAudioSession mode!");
131
-        return NO;
132
-    }
97
+    RTCAudioSession *session = [RTCAudioSession sharedInstance];
98
+    [session lockForConfiguration];
99
+    BOOL success = [session setConfiguration:config error:outError];
100
+    [session unlockForConfiguration];
133 101
 
134
-    return YES;
102
+    return success;
135 103
 }
136 104
 
105
+#pragma mark - Exported methods
106
+
137 107
 RCT_EXPORT_METHOD(setMode:(int)mode
138 108
                   resolve:(RCTPromiseResolveBlock)resolve
139 109
                    reject:(RCTPromiseRejectBlock)reject) {
140
-    NSString *avCategory = nil;
141
-    NSString *avMode = nil;
110
+    RTCAudioSessionConfiguration *config;
142 111
     NSError *error;
143 112
 
144 113
     switch (mode) {
145 114
     case kAudioModeAudioCall:
146
-        avCategory = AVAudioSessionCategoryPlayAndRecord;
147
-        avMode = AVAudioSessionModeVoiceChat;
115
+        config = audioCallConfig;
148 116
         break;
149 117
     case kAudioModeDefault:
118
+        config = defaultConfig;
150 119
         break;
151 120
     case kAudioModeVideoCall:
152
-        avCategory = AVAudioSessionCategoryPlayAndRecord;
153
-        avMode = AVAudioSessionModeVideoChat;
121
+        config = videoCallConfig;
154 122
         break;
155 123
     default:
156 124
         reject(@"setMode", @"Invalid mode", nil);
157 125
         return;
158 126
     }
159 127
 
160
-    // Save the desired/specified category and mode so that they may be
161
-    // reapplied.
162
-    _avCategory = avCategory;
163
-    _avMode = avMode;
164
-    _mode = mode;
128
+    activeMode = mode;
165 129
 
166
-    if (![self setCategory:avCategory mode:avMode error:&error] || error) {
167
-        reject(@"setMode", error.localizedDescription, error);
168
-    } else {
130
+    if ([self setConfig:config error:&error]) {
169 131
         resolve(nil);
132
+    } else {
133
+        reject(@"setMode", error.localizedDescription, error);
170 134
     }
171 135
 }
172 136
 
137
+#pragma mark - RTCAudioSessionDelegate
138
+
139
+- (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
140
+                            reason:(AVAudioSessionRouteChangeReason)reason
141
+                     previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
142
+    if (reason == AVAudioSessionRouteChangeReasonCategoryChange) {
143
+        // The category has changed. Check if it's the one we want and adjust as
144
+        // needed. This notification is posted on a secondary thread, so make
145
+        // sure we switch to our worker thread.
146
+        dispatch_async(_workerQueue, ^{
147
+            // We don't want to touch the category when in default mode.
148
+            // This is to play well with other components which could be integrated
149
+            // into the final application.
150
+            if (self->activeMode != kAudioModeDefault) {
151
+                NSLog(@"Audio route changed, reapplying RTCAudioSession config");
152
+                RTCAudioSessionConfiguration *config
153
+                    = self->activeMode == kAudioModeAudioCall ? self->audioCallConfig : self->videoCallConfig;
154
+                [self setConfig:config error:nil];
155
+            }
156
+        });
157
+    }
158
+}
159
+
160
+- (void)audioSession:(RTCAudioSession *)audioSession didSetActive:(BOOL)active {
161
+    NSLog(@"[AudioMode] Audio session didSetActive:%d", active);
162
+}
163
+
173 164
 @end

Loading…
Cancel
Save