Unreviewed, rolling out r244627.
[WebKit-https.git] / Source / WebCore / platform / audio / ios / AudioSessionIOS.mm
1 /*
2  * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "AudioSession.h"
28
29 #if USE(AUDIO_SESSION) && PLATFORM(IOS_FAMILY)
30
31 #import "Logging.h"
32 #import <AVFoundation/AVAudioSession.h>
33 #import <objc/runtime.h>
34 #import <pal/spi/mac/AVFoundationSPI.h>
35 #import <wtf/OSObjectPtr.h>
36 #import <wtf/RetainPtr.h>
37 #import <wtf/SoftLinking.h>
38
39 SOFT_LINK_FRAMEWORK(AVFoundation)
40 SOFT_LINK_CLASS(AVFoundation, AVAudioSession)
41
42 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryAmbient, NSString *)
43 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategorySoloAmbient, NSString *)
44 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryPlayback, NSString *)
45 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryRecord, NSString *)
46 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryPlayAndRecord, NSString *)
47 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryAudioProcessing, NSString *)
48 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionModeDefault, NSString *)
49 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionModeVideoChat, NSString *)
50
51 #define AVAudioSession getAVAudioSessionClass()
52 #define AVAudioSessionCategoryAmbient getAVAudioSessionCategoryAmbient()
53 #define AVAudioSessionCategorySoloAmbient getAVAudioSessionCategorySoloAmbient()
54 #define AVAudioSessionCategoryPlayback getAVAudioSessionCategoryPlayback()
55 #define AVAudioSessionCategoryRecord getAVAudioSessionCategoryRecord()
56 #define AVAudioSessionCategoryPlayAndRecord getAVAudioSessionCategoryPlayAndRecord()
57 #define AVAudioSessionCategoryAudioProcessing getAVAudioSessionCategoryAudioProcessing()
58 #define AVAudioSessionModeDefault getAVAudioSessionModeDefault()
59 #define AVAudioSessionModeVideoChat getAVAudioSessionModeVideoChat()
60
61 namespace WebCore {
62
63 #if !LOG_DISABLED
64 static const char* categoryName(AudioSession::CategoryType category)
65 {
66 #define CASE(category) case AudioSession::category: return #category
67     switch (category) {
68         CASE(None);
69         CASE(AmbientSound);
70         CASE(SoloAmbientSound);
71         CASE(MediaPlayback);
72         CASE(RecordAudio);
73         CASE(PlayAndRecord);
74         CASE(AudioProcessing);
75     }
76     
77     ASSERT_NOT_REACHED();
78     return "";
79 }
80 #endif
81
82 class AudioSessionPrivate {
83 public:
84     AudioSessionPrivate(AudioSession*);
85     AudioSession::CategoryType m_categoryOverride;
86     OSObjectPtr<dispatch_queue_t> m_dispatchQueue;
87 };
88
89 AudioSessionPrivate::AudioSessionPrivate(AudioSession*)
90     : m_categoryOverride(AudioSession::None)
91 {
92 }
93
94 AudioSession::AudioSession()
95     : m_private(std::make_unique<AudioSessionPrivate>(this))
96 {
97 }
98
99 AudioSession::~AudioSession()
100 {
101 }
102
103 void AudioSession::setCategory(CategoryType newCategory, RouteSharingPolicy policy)
104 {
105 #if !HAVE(ROUTE_SHARING_POLICY_LONG_FORM_VIDEO)
106     if (policy == RouteSharingPolicy::LongFormVideo)
107         policy = RouteSharingPolicy::LongFormAudio;
108 #endif
109
110     LOG(Media, "AudioSession::setCategory() - category = %s", categoryName(newCategory));
111
112     if (categoryOverride() && categoryOverride() != newCategory) {
113         LOG(Media, "AudioSession::setCategory() - override set, NOT changing");
114         return;
115     }
116
117     NSString *categoryString;
118     NSString *categoryMode = AVAudioSessionModeDefault;
119     AVAudioSessionCategoryOptions options = 0;
120
121     switch (newCategory) {
122     case AmbientSound:
123         categoryString = AVAudioSessionCategoryAmbient;
124         break;
125     case SoloAmbientSound:
126         categoryString = AVAudioSessionCategorySoloAmbient;
127         break;
128     case MediaPlayback:
129         categoryString = AVAudioSessionCategoryPlayback;
130         break;
131     case RecordAudio:
132         categoryString = AVAudioSessionCategoryRecord;
133         break;
134     case PlayAndRecord:
135         categoryString = AVAudioSessionCategoryPlayAndRecord;
136         categoryMode = AVAudioSessionModeVideoChat;
137         options |= AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowBluetoothA2DP | AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowAirPlay;
138         break;
139     case AudioProcessing:
140         categoryString = AVAudioSessionCategoryAudioProcessing;
141         break;
142     case None:
143         categoryString = AVAudioSessionCategoryAmbient;
144         break;
145     }
146
147     NSError *error = nil;
148     [[AVAudioSession sharedInstance] setCategory:categoryString mode:categoryMode routeSharingPolicy:static_cast<AVAudioSessionRouteSharingPolicy>(policy) options:options error:&error];
149 #if !PLATFORM(IOS_FAMILY_SIMULATOR) && !PLATFORM(IOSMAC)
150     ASSERT(!error);
151 #endif
152 }
153
154 AudioSession::CategoryType AudioSession::category() const
155 {
156     NSString *categoryString = [[AVAudioSession sharedInstance] category];
157     if ([categoryString isEqual:AVAudioSessionCategoryAmbient])
158         return AmbientSound;
159     if ([categoryString isEqual:AVAudioSessionCategorySoloAmbient])
160         return SoloAmbientSound;
161     if ([categoryString isEqual:AVAudioSessionCategoryPlayback])
162         return MediaPlayback;
163     if ([categoryString isEqual:AVAudioSessionCategoryRecord])
164         return RecordAudio;
165     if ([categoryString isEqual:AVAudioSessionCategoryPlayAndRecord])
166         return PlayAndRecord;
167     if ([categoryString isEqual:AVAudioSessionCategoryAudioProcessing])
168         return AudioProcessing;
169     return None;
170 }
171
172 RouteSharingPolicy AudioSession::routeSharingPolicy() const
173 {
174     static_assert(static_cast<size_t>(RouteSharingPolicy::Default) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyDefault), "RouteSharingPolicy::Default is not AVAudioSessionRouteSharingPolicyDefault as expected");
175 #if HAVE(ROUTE_SHARING_POLICY_LONG_FORM_VIDEO)
176     static_assert(static_cast<size_t>(RouteSharingPolicy::LongFormAudio) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyLongFormAudio), "RouteSharingPolicy::LongFormAudio is not AVAudioSessionRouteSharingPolicyLongFormAudio as expected");
177     static_assert(static_cast<size_t>(RouteSharingPolicy::LongFormVideo) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyLongFormVideo), "RouteSharingPolicy::LongFormVideo is not AVAudioSessionRouteSharingPolicyLongFormVideo as expected");
178 #else
179 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
180     static_assert(static_cast<size_t>(RouteSharingPolicy::LongFormAudio) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyLongForm), "RouteSharingPolicy::LongFormAudio is not AVAudioSessionRouteSharingPolicyLongForm as expected");
181 ALLOW_DEPRECATED_DECLARATIONS_END
182 #endif
183     static_assert(static_cast<size_t>(RouteSharingPolicy::Independent) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyIndependent), "RouteSharingPolicy::Independent is not AVAudioSessionRouteSharingPolicyIndependent as expected");
184
185     AVAudioSessionRouteSharingPolicy policy = [[AVAudioSession sharedInstance] routeSharingPolicy];
186     ASSERT(static_cast<RouteSharingPolicy>(policy) <= RouteSharingPolicy::LongFormVideo);
187     return static_cast<RouteSharingPolicy>(policy);
188 }
189
190 String AudioSession::routingContextUID() const
191 {
192 #if !PLATFORM(IOS_FAMILY_SIMULATOR) && !PLATFORM(IOSMAC) && !PLATFORM(WATCHOS)
193     return [[AVAudioSession sharedInstance] routingContextUID];
194 #else
195     return emptyString();
196 #endif
197 }
198
199 void AudioSession::setCategoryOverride(CategoryType category)
200 {
201     if (m_private->m_categoryOverride == category)
202         return;
203
204     m_private->m_categoryOverride = category;
205     setCategory(category, RouteSharingPolicy::Default);
206 }
207
208 AudioSession::CategoryType AudioSession::categoryOverride() const
209 {
210     return m_private->m_categoryOverride;
211 }
212
213 float AudioSession::sampleRate() const
214 {
215     return [[AVAudioSession sharedInstance] sampleRate];
216 }
217
218 size_t AudioSession::bufferSize() const
219 {
220     return [[AVAudioSession sharedInstance] IOBufferDuration] * sampleRate();
221 }
222
223 size_t AudioSession::numberOfOutputChannels() const
224 {
225     return [[AVAudioSession sharedInstance] outputNumberOfChannels];
226 }
227
228 bool AudioSession::tryToSetActiveInternal(bool active)
229 {
230     __block NSError* error = nil;
231
232     if (!m_private->m_dispatchQueue)
233         m_private->m_dispatchQueue = adoptOSObject(dispatch_queue_create("AudioSession Activation Queue", DISPATCH_QUEUE_SERIAL));
234
235     // We need to deactivate the session on another queue because the AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation option
236     // means that AVAudioSession may synchronously unduck previously ducked clients. Activation needs to complete before this method
237     // returns, so do it synchronously on the same serial queue.
238     if (active) {
239         dispatch_sync(m_private->m_dispatchQueue.get(), ^{
240             [[AVAudioSession sharedInstance] setActive:YES withOptions:0 error:&error];
241         });
242
243         return !error;
244     }
245
246     dispatch_async(m_private->m_dispatchQueue.get(), ^{
247         [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
248     });
249
250     return true;
251 }
252
253 size_t AudioSession::preferredBufferSize() const
254 {
255     return [[AVAudioSession sharedInstance] preferredIOBufferDuration] * sampleRate();
256 }
257
258 void AudioSession::setPreferredBufferSize(size_t bufferSize)
259 {
260     NSError *error = nil;
261     float duration = bufferSize / sampleRate();
262     [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:duration error:&error];
263     ASSERT(!error);
264 }
265
266 }
267
268 #endif // USE(AUDIO_SESSION) && PLATFORM(IOS_FAMILY)