Video sometimes flickers when playing to AppleTV
[WebKit-https.git] / Source / WebCore / platform / audio / ios / AudioSessionIOS.mm
1 /*
2  * Copyright (C) 2013-2014 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)
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/RetainPtr.h>
36 #import <wtf/SoftLinking.h>
37
38 SOFT_LINK_FRAMEWORK(AVFoundation)
39 SOFT_LINK_CLASS(AVFoundation, AVAudioSession)
40
41 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryAmbient, NSString *)
42 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategorySoloAmbient, NSString *)
43 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryPlayback, NSString *)
44 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryRecord, NSString *)
45 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryPlayAndRecord, NSString *)
46 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryAudioProcessing, NSString *)
47 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionModeDefault, NSString *)
48 SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionModeVideoChat, NSString *)
49
50 #define AVAudioSession getAVAudioSessionClass()
51 #define AVAudioSessionCategoryAmbient getAVAudioSessionCategoryAmbient()
52 #define AVAudioSessionCategorySoloAmbient getAVAudioSessionCategorySoloAmbient()
53 #define AVAudioSessionCategoryPlayback getAVAudioSessionCategoryPlayback()
54 #define AVAudioSessionCategoryRecord getAVAudioSessionCategoryRecord()
55 #define AVAudioSessionCategoryPlayAndRecord getAVAudioSessionCategoryPlayAndRecord()
56 #define AVAudioSessionCategoryAudioProcessing getAVAudioSessionCategoryAudioProcessing()
57 #define AVAudioSessionModeDefault getAVAudioSessionModeDefault()
58 #define AVAudioSessionModeVideoChat getAVAudioSessionModeVideoChat()
59
60 namespace WebCore {
61
62 #if !LOG_DISABLED
63 static const char* categoryName(AudioSession::CategoryType category)
64 {
65 #define CASE(category) case AudioSession::category: return #category
66     switch (category) {
67         CASE(None);
68         CASE(AmbientSound);
69         CASE(SoloAmbientSound);
70         CASE(MediaPlayback);
71         CASE(RecordAudio);
72         CASE(PlayAndRecord);
73         CASE(AudioProcessing);
74     }
75     
76     ASSERT_NOT_REACHED();
77     return "";
78 }
79 #endif
80
81 class AudioSessionPrivate {
82 public:
83     AudioSessionPrivate(AudioSession*);
84     AudioSession::CategoryType m_categoryOverride;
85 };
86
87 AudioSessionPrivate::AudioSessionPrivate(AudioSession*)
88     : m_categoryOverride(AudioSession::None)
89 {
90 }
91
92 AudioSession::AudioSession()
93     : m_private(std::make_unique<AudioSessionPrivate>(this))
94 {
95 }
96
97 AudioSession::~AudioSession()
98 {
99 }
100
101 void AudioSession::setCategory(CategoryType newCategory)
102 {
103     LOG(Media, "AudioSession::setCategory() - category = %s", categoryName(newCategory));
104
105     if (categoryOverride() && categoryOverride() != newCategory) {
106         LOG(Media, "AudioSession::setCategory() - override set, NOT changing");
107         return;
108     }
109
110     NSString *categoryString;
111     NSString *categoryMode = AVAudioSessionModeDefault;
112     AVAudioSessionCategoryOptions options = 0;
113     AVAudioSessionRouteSharingPolicy policy = AVAudioSessionRouteSharingPolicyDefault;
114
115     switch (newCategory) {
116     case AmbientSound:
117         categoryString = AVAudioSessionCategoryAmbient;
118         break;
119     case SoloAmbientSound:
120         categoryString = AVAudioSessionCategorySoloAmbient;
121         break;
122     case MediaPlayback:
123         categoryString = AVAudioSessionCategoryPlayback;
124         policy = AVAudioSessionRouteSharingPolicyLongForm;
125         break;
126     case RecordAudio:
127         categoryString = AVAudioSessionCategoryRecord;
128         break;
129     case PlayAndRecord:
130         categoryString = AVAudioSessionCategoryPlayAndRecord;
131         categoryMode = AVAudioSessionModeVideoChat;
132         options |= AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowBluetoothA2DP | AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowAirPlay;
133         break;
134     case AudioProcessing:
135         categoryString = AVAudioSessionCategoryAudioProcessing;
136         break;
137     case None:
138         return;
139     }
140
141     NSError *error = nil;
142     [[AVAudioSession sharedInstance] setCategory:categoryString mode:categoryMode routeSharingPolicy:policy options:options error:&error];
143 #if !PLATFORM(IOS_SIMULATOR) && !ENABLE(MINIMAL_SIMULATOR)
144     ASSERT(!error);
145 #endif
146 }
147
148 AudioSession::CategoryType AudioSession::category() const
149 {
150     NSString *categoryString = [[AVAudioSession sharedInstance] category];
151     if ([categoryString isEqual:AVAudioSessionCategoryAmbient])
152         return AmbientSound;
153     if ([categoryString isEqual:AVAudioSessionCategorySoloAmbient])
154         return SoloAmbientSound;
155     if ([categoryString isEqual:AVAudioSessionCategoryPlayback])
156         return MediaPlayback;
157     if ([categoryString isEqual:AVAudioSessionCategoryRecord])
158         return RecordAudio;
159     if ([categoryString isEqual:AVAudioSessionCategoryPlayAndRecord])
160         return PlayAndRecord;
161     if ([categoryString isEqual:AVAudioSessionCategoryAudioProcessing])
162         return AudioProcessing;
163     return None;
164 }
165
166 RouteSharingPolicy AudioSession::routeSharingPolicy() const
167 {
168     static_assert(static_cast<size_t>(RouteSharingPolicy::Default) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyDefault), "RouteSharingPolicy::Default is not AVAudioSessionRouteSharingPolicyDefault as expected");
169     static_assert(static_cast<size_t>(RouteSharingPolicy::LongForm) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyLongForm), "RouteSharingPolicy::LongForm is not AVAudioSessionRouteSharingPolicyLongForm as expected");
170     static_assert(static_cast<size_t>(RouteSharingPolicy::Independent) == static_cast<size_t>(AVAudioSessionRouteSharingPolicyIndependent), "RouteSharingPolicy::Independent is not AVAudioSessionRouteSharingPolicyIndependent as expected");
171
172     AVAudioSessionRouteSharingPolicy policy = [[AVAudioSession sharedInstance] routeSharingPolicy];
173     ASSERT(static_cast<RouteSharingPolicy>(policy) <= RouteSharingPolicy::Independent);
174     return static_cast<RouteSharingPolicy>(policy);
175 }
176
177 String AudioSession::routingContextUID() const
178 {
179 #if !PLATFORM(IOS_SIMULATOR) && !ENABLE(MINIMAL_SIMULATOR) && !PLATFORM(WATCHOS)
180     return [[AVAudioSession sharedInstance] routingContextUID];
181 #else
182     return emptyString();
183 #endif
184 }
185
186 void AudioSession::setCategoryOverride(CategoryType category)
187 {
188     if (m_private->m_categoryOverride == category)
189         return;
190
191     m_private->m_categoryOverride = category;
192     setCategory(category);
193 }
194
195 AudioSession::CategoryType AudioSession::categoryOverride() const
196 {
197     return m_private->m_categoryOverride;
198 }
199
200 float AudioSession::sampleRate() const
201 {
202     return [[AVAudioSession sharedInstance] sampleRate];
203 }
204
205 size_t AudioSession::bufferSize() const
206 {
207     return [[AVAudioSession sharedInstance] IOBufferDuration] * sampleRate();
208 }
209
210 size_t AudioSession::numberOfOutputChannels() const
211 {
212     return [[AVAudioSession sharedInstance] outputNumberOfChannels];
213 }
214
215 bool AudioSession::tryToSetActive(bool active)
216 {
217     NSError *error = nil;
218     [[AVAudioSession sharedInstance] setActive:active withOptions:active ? 0 : AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
219     return !error;
220 }
221
222 size_t AudioSession::preferredBufferSize() const
223 {
224     return [[AVAudioSession sharedInstance] preferredIOBufferDuration] * sampleRate();
225 }
226
227 void AudioSession::setPreferredBufferSize(size_t bufferSize)
228 {
229     NSError *error = nil;
230     float duration = bufferSize / sampleRate();
231     [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:duration error:&error];
232     ASSERT(!error);
233 }
234
235 }
236
237 #endif // USE(AUDIO_SESSION) && PLATFORM(IOS)