1 /*
2  *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #import <AVFoundation/AVFoundation.h>
12 #import <Foundation/Foundation.h>
13 
14 #import "RTCMacros.h"
15 
16 NS_ASSUME_NONNULL_BEGIN
17 
18 extern NSString *const kRTCAudioSessionErrorDomain;
19 /** Method that requires lock was called without lock. */
20 extern NSInteger const kRTCAudioSessionErrorLockRequired;
21 /** Unknown configuration error occurred. */
22 extern NSInteger const kRTCAudioSessionErrorConfiguration;
23 
24 @class RTCAudioSession;
25 @class RTCAudioSessionConfiguration;
26 
27 // Surfaces AVAudioSession events. WebRTC will listen directly for notifications
28 // from AVAudioSession and handle them before calling these delegate methods,
29 // at which point applications can perform additional processing if required.
30 RTC_OBJC_EXPORT
31 @protocol RTCAudioSessionDelegate <NSObject>
32 
33 @optional
34 /** Called on a system notification thread when AVAudioSession starts an
35  *  interruption event.
36  */
37 - (void)audioSessionDidBeginInterruption:(RTCAudioSession *)session;
38 
39 /** Called on a system notification thread when AVAudioSession ends an
40  *  interruption event.
41  */
42 - (void)audioSessionDidEndInterruption:(RTCAudioSession *)session
43                    shouldResumeSession:(BOOL)shouldResumeSession;
44 
45 /** Called on a system notification thread when AVAudioSession changes the
46  *  route.
47  */
48 - (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
49                             reason:(AVAudioSessionRouteChangeReason)reason
50                      previousRoute:(AVAudioSessionRouteDescription *)previousRoute;
51 
52 /** Called on a system notification thread when AVAudioSession media server
53  *  terminates.
54  */
55 - (void)audioSessionMediaServerTerminated:(RTCAudioSession *)session;
56 
57 /** Called on a system notification thread when AVAudioSession media server
58  *  restarts.
59  */
60 - (void)audioSessionMediaServerReset:(RTCAudioSession *)session;
61 
62 // TODO(tkchin): Maybe handle SilenceSecondaryAudioHintNotification.
63 
64 - (void)audioSession:(RTCAudioSession *)session didChangeCanPlayOrRecord:(BOOL)canPlayOrRecord;
65 
66 /** Called on a WebRTC thread when the audio device is notified to begin
67  *  playback or recording.
68  */
69 - (void)audioSessionDidStartPlayOrRecord:(RTCAudioSession *)session;
70 
71 /** Called on a WebRTC thread when the audio device is notified to stop
72  *  playback or recording.
73  */
74 - (void)audioSessionDidStopPlayOrRecord:(RTCAudioSession *)session;
75 
76 /** Called when the AVAudioSession output volume value changes. */
77 - (void)audioSession:(RTCAudioSession *)audioSession didChangeOutputVolume:(float)outputVolume;
78 
79 /** Called when the audio device detects a playout glitch. The argument is the
80  *  number of glitches detected so far in the current audio playout session.
81  */
82 - (void)audioSession:(RTCAudioSession *)audioSession
83     didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches;
84 
85 /** Called when the audio session is about to change the active state.
86  */
87 - (void)audioSession:(RTCAudioSession *)audioSession willSetActive:(BOOL)active;
88 
89 /** Called after the audio session sucessfully changed the active state.
90  */
91 - (void)audioSession:(RTCAudioSession *)audioSession didSetActive:(BOOL)active;
92 
93 /** Called after the audio session failed to change the active state.
94  */
95 - (void)audioSession:(RTCAudioSession *)audioSession
96     failedToSetActive:(BOOL)active
97                 error:(NSError *)error;
98 
99 @end
100 
101 /** This is a protocol used to inform RTCAudioSession when the audio session
102  *  activation state has changed outside of RTCAudioSession. The current known use
103  *  case of this is when CallKit activates the audio session for the application
104  */
105 RTC_OBJC_EXPORT
106 @protocol RTCAudioSessionActivationDelegate <NSObject>
107 
108 /** Called when the audio session is activated outside of the app by iOS. */
109 - (void)audioSessionDidActivate:(AVAudioSession *)session;
110 
111 /** Called when the audio session is deactivated outside of the app by iOS. */
112 - (void)audioSessionDidDeactivate:(AVAudioSession *)session;
113 
114 @end
115 
116 /** Proxy class for AVAudioSession that adds a locking mechanism similar to
117  *  AVCaptureDevice. This is used to that interleaving configurations between
118  *  WebRTC and the application layer are avoided.
119  *
120  *  RTCAudioSession also coordinates activation so that the audio session is
121  *  activated only once. See |setActive:error:|.
122  */
123 RTC_OBJC_EXPORT
124 @interface RTCAudioSession : NSObject <RTCAudioSessionActivationDelegate>
125 
126 /** Convenience property to access the AVAudioSession singleton. Callers should
127  *  not call setters on AVAudioSession directly, but other method invocations
128  *  are fine.
129  */
130 @property(nonatomic, readonly) AVAudioSession *session;
131 
132 /** Our best guess at whether the session is active based on results of calls to
133  *  AVAudioSession.
134  */
135 @property(nonatomic, readonly) BOOL isActive;
136 /** Whether RTCAudioSession is currently locked for configuration. */
137 @property(nonatomic, readonly) BOOL isLocked;
138 
139 /** If YES, WebRTC will not initialize the audio unit automatically when an
140  *  audio track is ready for playout or recording. Instead, applications should
141  *  call setIsAudioEnabled. If NO, WebRTC will initialize the audio unit
142  *  as soon as an audio track is ready for playout or recording.
143  */
144 @property(nonatomic, assign) BOOL useManualAudio;
145 
146 /** This property is only effective if useManualAudio is YES.
147  *  Represents permission for WebRTC to initialize the VoIP audio unit.
148  *  When set to NO, if the VoIP audio unit used by WebRTC is active, it will be
149  *  stopped and uninitialized. This will stop incoming and outgoing audio.
150  *  When set to YES, WebRTC will initialize and start the audio unit when it is
151  *  needed (e.g. due to establishing an audio connection).
152  *  This property was introduced to work around an issue where if an AVPlayer is
153  *  playing audio while the VoIP audio unit is initialized, its audio would be
154  *  either cut off completely or played at a reduced volume. By preventing
155  *  the audio unit from being initialized until after the audio has completed,
156  *  we are able to prevent the abrupt cutoff.
157  */
158 @property(nonatomic, assign) BOOL isAudioEnabled;
159 
160 // Proxy properties.
161 @property(readonly) NSString *category;
162 @property(readonly) AVAudioSessionCategoryOptions categoryOptions;
163 @property(readonly) NSString *mode;
164 @property(readonly) BOOL secondaryAudioShouldBeSilencedHint;
165 @property(readonly) AVAudioSessionRouteDescription *currentRoute;
166 @property(readonly) NSInteger maximumInputNumberOfChannels;
167 @property(readonly) NSInteger maximumOutputNumberOfChannels;
168 @property(readonly) float inputGain;
169 @property(readonly) BOOL inputGainSettable;
170 @property(readonly) BOOL inputAvailable;
171 @property(readonly, nullable) NSArray<AVAudioSessionDataSourceDescription *> *inputDataSources;
172 @property(readonly, nullable) AVAudioSessionDataSourceDescription *inputDataSource;
173 @property(readonly, nullable) NSArray<AVAudioSessionDataSourceDescription *> *outputDataSources;
174 @property(readonly, nullable) AVAudioSessionDataSourceDescription *outputDataSource;
175 @property(readonly) double sampleRate;
176 @property(readonly) double preferredSampleRate;
177 @property(readonly) NSInteger inputNumberOfChannels;
178 @property(readonly) NSInteger outputNumberOfChannels;
179 @property(readonly) float outputVolume;
180 @property(readonly) NSTimeInterval inputLatency;
181 @property(readonly) NSTimeInterval outputLatency;
182 @property(readonly) NSTimeInterval IOBufferDuration;
183 @property(readonly) NSTimeInterval preferredIOBufferDuration;
184 
185 /**
186  When YES, calls to -setConfiguration:error: and -setConfiguration:active:error: ignore errors in
187  configuring the audio session's "preferred" attributes (e.g. preferredInputNumberOfChannels).
188  Typically, configurations to preferred attributes are optimizations, and ignoring this type of
189  configuration error allows code flow to continue along the happy path when these optimization are
190  not available. The default value of this property is NO.
191  */
192 @property(nonatomic) BOOL ignoresPreferredAttributeConfigurationErrors;
193 
194 /** Default constructor. */
195 + (instancetype)sharedInstance;
196 - (instancetype)init NS_UNAVAILABLE;
197 
198 /** Adds a delegate, which is held weakly. */
199 - (void)addDelegate:(id<RTCAudioSessionDelegate>)delegate;
200 /** Removes an added delegate. */
201 - (void)removeDelegate:(id<RTCAudioSessionDelegate>)delegate;
202 
203 /** Request exclusive access to the audio session for configuration. This call
204  *  will block if the lock is held by another object.
205  */
206 - (void)lockForConfiguration;
207 /** Relinquishes exclusive access to the audio session. */
208 - (void)unlockForConfiguration;
209 
210 /** If |active|, activates the audio session if it isn't already active.
211  *  Successful calls must be balanced with a setActive:NO when activation is no
212  *  longer required. If not |active|, deactivates the audio session if one is
213  *  active and this is the last balanced call. When deactivating, the
214  *  AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation option is passed to
215  *  AVAudioSession.
216  */
217 - (BOOL)setActive:(BOOL)active error:(NSError **)outError;
218 
219 // The following methods are proxies for the associated methods on
220 // AVAudioSession. |lockForConfiguration| must be called before using them
221 // otherwise they will fail with kRTCAudioSessionErrorLockRequired.
222 
223 - (BOOL)setCategory:(NSString *)category
224         withOptions:(AVAudioSessionCategoryOptions)options
225               error:(NSError **)outError;
226 - (BOOL)setMode:(NSString *)mode error:(NSError **)outError;
227 - (BOOL)setInputGain:(float)gain error:(NSError **)outError;
228 - (BOOL)setPreferredSampleRate:(double)sampleRate error:(NSError **)outError;
229 - (BOOL)setPreferredIOBufferDuration:(NSTimeInterval)duration error:(NSError **)outError;
230 - (BOOL)setPreferredInputNumberOfChannels:(NSInteger)count error:(NSError **)outError;
231 - (BOOL)setPreferredOutputNumberOfChannels:(NSInteger)count error:(NSError **)outError;
232 - (BOOL)overrideOutputAudioPort:(AVAudioSessionPortOverride)portOverride error:(NSError **)outError;
233 - (BOOL)setPreferredInput:(AVAudioSessionPortDescription *)inPort error:(NSError **)outError;
234 - (BOOL)setInputDataSource:(AVAudioSessionDataSourceDescription *)dataSource
235                      error:(NSError **)outError;
236 - (BOOL)setOutputDataSource:(AVAudioSessionDataSourceDescription *)dataSource
237                       error:(NSError **)outError;
238 @end
239 
240 @interface RTCAudioSession (Configuration)
241 
242 /** Applies the configuration to the current session. Attempts to set all
243  *  properties even if previous ones fail. Only the last error will be
244  *  returned.
245  *  |lockForConfiguration| must be called first.
246  */
247 - (BOOL)setConfiguration:(RTCAudioSessionConfiguration *)configuration error:(NSError **)outError;
248 
249 /** Convenience method that calls both setConfiguration and setActive.
250  *  |lockForConfiguration| must be called first.
251  */
252 - (BOOL)setConfiguration:(RTCAudioSessionConfiguration *)configuration
253                   active:(BOOL)active
254                    error:(NSError **)outError;
255 
256 @end
257 
258 NS_ASSUME_NONNULL_END
259