b9b079d9e1fd7ffffc2085337a0331f1f277058d
[WebKit-https.git] / Source / WebCore / platform / audio / PlatformMediaSessionManager.cpp
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 #include "config.h"
27 #include "PlatformMediaSessionManager.h"
28
29 #include "AudioSession.h"
30 #include "Document.h"
31 #include "Logging.h"
32 #include "PlatformMediaSession.h"
33
34 namespace WebCore {
35
36 #if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
37
38 #if !PLATFORM(COCOA)
39 static PlatformMediaSessionManager* platformMediaSessionManager = nullptr;
40
41 PlatformMediaSessionManager& PlatformMediaSessionManager::sharedManager()
42 {
43     if (!platformMediaSessionManager)
44         platformMediaSessionManager = new PlatformMediaSessionManager;
45     return *platformMediaSessionManager;
46 }
47
48 PlatformMediaSessionManager* PlatformMediaSessionManager::sharedManagerIfExists()
49 {
50     return platformMediaSessionManager;
51 }
52 #endif // !PLATFORM(COCOA)
53
54 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
55 {
56     if (auto existingManager = PlatformMediaSessionManager::sharedManagerIfExists())
57         existingManager->scheduleUpdateNowPlayingInfo();
58 }
59
60 PlatformMediaSessionManager::PlatformMediaSessionManager()
61     : m_systemSleepListener(PAL::SystemSleepListener::create(*this))
62 {
63     resetRestrictions();
64 }
65
66 void PlatformMediaSessionManager::resetRestrictions()
67 {
68     m_restrictions[PlatformMediaSession::Video] = NoRestrictions;
69     m_restrictions[PlatformMediaSession::Audio] = NoRestrictions;
70     m_restrictions[PlatformMediaSession::VideoAudio] = NoRestrictions;
71     m_restrictions[PlatformMediaSession::WebAudio] = NoRestrictions;
72     m_restrictions[PlatformMediaSession::MediaStreamCapturingAudio] = NoRestrictions;
73 }
74
75 bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) const
76 {
77     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
78
79     return anyOfSessions([type] (PlatformMediaSession& session, size_t) {
80         return session.mediaType() == type;
81     });
82 }
83
84 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
85 {
86     return anyOfSessions([] (PlatformMediaSession& session, size_t) {
87         return session.activeAudioSessionRequired();
88     });
89 }
90
91 bool PlatformMediaSessionManager::canProduceAudio() const
92 {
93     return anyOfSessions([] (PlatformMediaSession& session, size_t) {
94         return session.canProduceAudio();
95     });
96 }
97
98 int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) const
99 {
100     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
101
102     int count = 0;
103     for (auto* session : m_sessions) {
104         if (session->mediaType() == type)
105             ++count;
106     }
107
108     return count;
109 }
110
111 void PlatformMediaSessionManager::beginInterruption(PlatformMediaSession::InterruptionType type)
112 {
113     LOG(Media, "PlatformMediaSessionManager::beginInterruption");
114
115     m_interrupted = true;
116     forEachSession([type] (PlatformMediaSession& session, size_t) {
117         session.beginInterruption(type);
118     });
119     updateSessionState();
120 }
121
122 void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInterruptionFlags flags)
123 {
124     LOG(Media, "PlatformMediaSessionManager::endInterruption");
125
126     m_interrupted = false;
127     forEachSession([flags] (PlatformMediaSession& session, size_t) {
128         session.endInterruption(flags);
129     });
130 }
131
132 void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
133 {
134     LOG(Media, "PlatformMediaSessionManager::addSession - %p", &session);
135     
136     m_sessions.append(&session);
137     if (m_interrupted)
138         session.setState(PlatformMediaSession::Interrupted);
139
140     if (!m_remoteCommandListener)
141         m_remoteCommandListener = RemoteCommandListener::create(*this);
142
143     if (!m_audioHardwareListener)
144         m_audioHardwareListener = AudioHardwareListener::create(*this);
145
146     updateSessionState();
147 }
148
149 void PlatformMediaSessionManager::removeSession(PlatformMediaSession& session)
150 {
151     LOG(Media, "PlatformMediaSessionManager::removeSession - %p", &session);
152     
153     size_t index = m_sessions.find(&session);
154     if (index == notFound)
155         return;
156
157     if (m_iteratingOverSessions)
158         m_sessions.at(index) = nullptr;
159     else
160         m_sessions.remove(index);
161
162     if (m_sessions.isEmpty() || std::all_of(m_sessions.begin(), m_sessions.end(), std::logical_not<void>())) {
163         m_remoteCommandListener = nullptr;
164         m_audioHardwareListener = nullptr;
165 #if USE(AUDIO_SESSION)
166         if (m_becameActive && shouldDeactivateAudioSession()) {
167             AudioSession::sharedSession().tryToSetActive(false);
168             m_becameActive = false;
169         }
170 #endif
171     }
172
173     updateSessionState();
174 }
175
176 void PlatformMediaSessionManager::addRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
177 {
178     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
179     m_restrictions[type] |= restriction;
180 }
181
182 void PlatformMediaSessionManager::removeRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
183 {
184     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
185     m_restrictions[type] &= ~restriction;
186 }
187
188 PlatformMediaSessionManager::SessionRestrictions PlatformMediaSessionManager::restrictions(PlatformMediaSession::MediaType type)
189 {
190     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
191     return m_restrictions[type];
192 }
193
194 bool PlatformMediaSessionManager::sessionWillBeginPlayback(PlatformMediaSession& session)
195 {
196     LOG(Media, "PlatformMediaSessionManager::sessionWillBeginPlayback - %p", &session);
197     
198     setCurrentSession(session);
199
200     PlatformMediaSession::MediaType sessionType = session.mediaType();
201     SessionRestrictions restrictions = m_restrictions[sessionType];
202     if (session.state() == PlatformMediaSession::Interrupted && restrictions & InterruptedPlaybackNotPermitted)
203         return false;
204
205 #if USE(AUDIO_SESSION)
206     if (activeAudioSessionRequired() && !AudioSession::sharedSession().tryToSetActive(true))
207         return false;
208
209     m_becameActive = true;
210 #endif
211
212     if (m_interrupted)
213         endInterruption(PlatformMediaSession::NoFlags);
214
215     forEachSession([&] (PlatformMediaSession& oneSession, size_t) {
216         if (&oneSession == &session)
217             return;
218         if (oneSession.mediaType() == sessionType
219             && restrictions & ConcurrentPlaybackNotPermitted
220             && oneSession.state() == PlatformMediaSession::Playing)
221             oneSession.pauseSession();
222     });
223
224     return true;
225 }
226     
227 void PlatformMediaSessionManager::sessionWillEndPlayback(PlatformMediaSession& session)
228 {
229     LOG(Media, "PlatformMediaSessionManager::sessionWillEndPlayback - %p", &session);
230     
231     if (m_sessions.size() < 2)
232         return;
233     
234     size_t pausingSessionIndex = notFound;
235     size_t lastPlayingSessionIndex = notFound;
236     anyOfSessions([&] (PlatformMediaSession& oneSession, size_t i) {
237         if (&oneSession == &session) {
238             pausingSessionIndex = i;
239             return false;
240         }
241         if (oneSession.state() == PlatformMediaSession::Playing) {
242             lastPlayingSessionIndex = i;
243             return false;
244         }
245         return oneSession.state() != PlatformMediaSession::Playing;
246     });
247     if (lastPlayingSessionIndex == notFound || pausingSessionIndex == notFound)
248         return;
249     
250     if (pausingSessionIndex > lastPlayingSessionIndex)
251         return;
252     
253     m_sessions.remove(pausingSessionIndex);
254     m_sessions.insert(lastPlayingSessionIndex, &session);
255     
256     LOG(Media, "PlatformMediaSessionManager::sessionWillEndPlayback - session moved from index %zu to %zu", pausingSessionIndex, lastPlayingSessionIndex);
257 }
258
259 void PlatformMediaSessionManager::sessionStateChanged(PlatformMediaSession&)
260 {
261     updateSessionState();
262 }
263
264 void PlatformMediaSessionManager::setCurrentSession(PlatformMediaSession& session)
265 {
266     LOG(Media, "PlatformMediaSessionManager::setCurrentSession - %p", &session);
267     
268     if (m_sessions.size() < 2)
269         return;
270     
271     size_t index = m_sessions.find(&session);
272     ASSERT(index != notFound);
273     if (!index || index == notFound)
274         return;
275
276     m_sessions.remove(index);
277     m_sessions.insert(0, &session);
278     if (m_remoteCommandListener)
279         m_remoteCommandListener->updateSupportedCommands();
280     
281     LOG(Media, "PlatformMediaSessionManager::setCurrentSession - session moved from index %zu to 0", index);
282 }
283     
284 PlatformMediaSession* PlatformMediaSessionManager::currentSession() const
285 {
286     if (!m_sessions.size())
287         return nullptr;
288
289     return m_sessions[0];
290 }
291
292 Vector<PlatformMediaSession*> PlatformMediaSessionManager::currentSessionsMatching(const WTF::Function<bool(const PlatformMediaSession&)>& filter)
293 {
294     Vector<PlatformMediaSession*> matchingSessions;
295     forEachSession([&] (PlatformMediaSession& session, size_t) {
296         if (filter(session))
297             matchingSessions.append(&session);
298     });
299     return matchingSessions;
300 }
301
302 void PlatformMediaSessionManager::applicationWillBecomeInactive() const
303 {
304     LOG(Media, "PlatformMediaSessionManager::applicationWillBecomeInactive");
305
306     forEachSession([&] (PlatformMediaSession& session, size_t) {
307         if (m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted)
308             session.beginInterruption(PlatformMediaSession::ProcessInactive);
309     });
310 }
311
312 void PlatformMediaSessionManager::applicationDidBecomeActive() const
313 {
314     LOG(Media, "PlatformMediaSessionManager::applicationDidBecomeActive");
315
316     forEachSession([&] (PlatformMediaSession& session, size_t) {
317         if (m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted)
318             session.endInterruption(PlatformMediaSession::MayResumePlaying);
319     });
320 }
321
322 void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUnderLock) const
323 {
324     LOG(Media, "PlatformMediaSessionManager::applicationDidEnterBackground - suspendedUnderLock(%d)", suspendedUnderLock);
325
326     if (m_isApplicationInBackground)
327         return;
328
329     m_isApplicationInBackground = true;
330
331     forEachSession([&] (PlatformMediaSession& session, size_t) {
332         if (suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted)
333             session.beginInterruption(PlatformMediaSession::SuspendedUnderLock);
334         else if (m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
335             session.beginInterruption(PlatformMediaSession::EnteringBackground);
336     });
337 }
338
339 void PlatformMediaSessionManager::applicationWillEnterForeground(bool suspendedUnderLock) const
340 {
341     LOG(Media, "PlatformMediaSessionManager::applicationWillEnterForeground - suspendedUnderLock(%d)", suspendedUnderLock);
342
343     if (!m_isApplicationInBackground)
344         return;
345
346     m_isApplicationInBackground = false;
347
348     forEachSession([&] (PlatformMediaSession& session, size_t) {
349         if ((suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted) || m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
350             session.endInterruption(PlatformMediaSession::MayResumePlaying);
351     });
352 }
353
354 void PlatformMediaSessionManager::sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession& session)
355 {
356     if (!m_isApplicationInBackground || !(m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted))
357         return;
358
359     if (session.state() != PlatformMediaSession::Interrupted)
360         session.beginInterruption(PlatformMediaSession::EnteringBackground);
361 }
362
363 void PlatformMediaSessionManager::sessionCanProduceAudioChanged(PlatformMediaSession&)
364 {
365     updateSessionState();
366 }
367
368 void PlatformMediaSessionManager::didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType command, const PlatformMediaSession::RemoteCommandArgument* argument)
369 {
370     PlatformMediaSession* activeSession = currentSession();
371     if (!activeSession || !activeSession->canReceiveRemoteControlCommands())
372         return;
373     activeSession->didReceiveRemoteControlCommand(command, argument);
374 }
375
376 bool PlatformMediaSessionManager::supportsSeeking() const
377 {
378     PlatformMediaSession* activeSession = currentSession();
379     if (!activeSession)
380         return false;
381     return activeSession->supportsSeeking();
382 }
383
384 void PlatformMediaSessionManager::systemWillSleep()
385 {
386     if (m_interrupted)
387         return;
388
389     forEachSession([] (PlatformMediaSession& session, size_t) {
390         session.beginInterruption(PlatformMediaSession::SystemSleep);
391     });
392 }
393
394 void PlatformMediaSessionManager::systemDidWake()
395 {
396     if (m_interrupted)
397         return;
398
399     forEachSession([] (PlatformMediaSession& session, size_t) {
400         session.endInterruption(PlatformMediaSession::MayResumePlaying);
401     });
402 }
403
404 void PlatformMediaSessionManager::audioOutputDeviceChanged()
405 {
406     updateSessionState();
407 }
408
409 void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document* document)
410 {
411     forEachSession([document] (PlatformMediaSession& session, size_t) {
412         if (session.client().hostingDocument() == document)
413             session.pauseSession();
414     });
415 }
416
417 void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
418 {
419     forEachSession([] (PlatformMediaSession& session, size_t) {
420         session.stopSession();
421     });
422 }
423
424 void PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument(const Document& document)
425 {
426     forEachSession([&] (PlatformMediaSession& session, size_t) {
427         if (session.client().hostingDocument() == &document)
428             session.beginInterruption(PlatformMediaSession::PlaybackSuspended);
429     });
430 }
431
432 void PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument(const Document& document)
433 {
434     forEachSession([&] (PlatformMediaSession& session, size_t) {
435         if (session.client().hostingDocument() == &document)
436             session.endInterruption(PlatformMediaSession::MayResumePlaying);
437     });
438 }
439
440 void PlatformMediaSessionManager::suspendAllMediaBufferingForDocument(const Document& document)
441 {
442     forEachSession([&] (PlatformMediaSession& session, size_t) {
443         if (session.client().hostingDocument() == &document)
444             session.suspendBuffering();
445     });
446 }
447
448 void PlatformMediaSessionManager::resumeAllMediaBufferingForDocument(const Document& document)
449 {
450     forEachSession([&] (PlatformMediaSession& session, size_t) {
451         if (session.client().hostingDocument() == &document)
452             session.resumeBuffering();
453     });
454 }
455
456 void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&, size_t)>& predicate) const
457 {
458     ++m_iteratingOverSessions;
459
460     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
461         auto session = m_sessions[i];
462         if (!session)
463             continue;
464         predicate(*session, i);
465     }
466
467     --m_iteratingOverSessions;
468     if (!m_iteratingOverSessions)
469         m_sessions.removeAll(nullptr);
470 }
471
472 PlatformMediaSession* PlatformMediaSessionManager::findSession(const Function<bool(PlatformMediaSession&, size_t)>& predicate) const
473 {
474     ++m_iteratingOverSessions;
475
476     PlatformMediaSession* foundSession = nullptr;
477     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
478         auto session = m_sessions[i];
479         if (!session)
480             continue;
481
482         if (!predicate(*session, i))
483             continue;
484
485         foundSession = session;
486         break;
487     }
488
489     --m_iteratingOverSessions;
490     if (!m_iteratingOverSessions)
491         m_sessions.removeAll(nullptr);
492
493     return foundSession;
494 }
495
496 static bool& deactivateAudioSession()
497 {
498     static bool deactivate;
499     return deactivate;
500 }
501
502 bool PlatformMediaSessionManager::shouldDeactivateAudioSession()
503 {
504     return deactivateAudioSession();
505 }
506
507 void PlatformMediaSessionManager::setShouldDeactivateAudioSession(bool deactivate)
508 {
509     deactivateAudioSession() = deactivate;
510 }
511
512 #else // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
513
514 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
515 {
516
517 }
518
519 #endif // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
520
521 } // namespace WebCore