Multiple videos (with audios) with autoplay & playinline not working. Only one video...
[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 #if !RELEASE_LOG_DISABLED
63     , m_logger(AggregateLogger::create(this))
64 #endif
65 {
66     resetRestrictions();
67 }
68
69 void PlatformMediaSessionManager::resetRestrictions()
70 {
71     m_restrictions[PlatformMediaSession::Video] = NoRestrictions;
72     m_restrictions[PlatformMediaSession::Audio] = NoRestrictions;
73     m_restrictions[PlatformMediaSession::VideoAudio] = NoRestrictions;
74     m_restrictions[PlatformMediaSession::WebAudio] = NoRestrictions;
75     m_restrictions[PlatformMediaSession::MediaStreamCapturingAudio] = NoRestrictions;
76 }
77
78 bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) const
79 {
80     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
81
82     return anyOfSessions([type] (auto& session) {
83         return session.mediaType() == type;
84     });
85 }
86
87 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
88 {
89     return anyOfSessions([] (auto& session) {
90         return session.activeAudioSessionRequired();
91     });
92 }
93
94 bool PlatformMediaSessionManager::canProduceAudio() const
95 {
96     return anyOfSessions([] (auto& session) {
97         return session.canProduceAudio();
98     });
99 }
100
101 int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) const
102 {
103     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
104
105     int count = 0;
106     for (const auto& session : m_sessions) {
107         if (session->mediaType() == type)
108             ++count;
109     }
110
111     return count;
112 }
113
114 void PlatformMediaSessionManager::beginInterruption(PlatformMediaSession::InterruptionType type)
115 {
116     ALWAYS_LOG(LOGIDENTIFIER);
117
118     m_interrupted = true;
119     forEachSession([type] (auto& session) {
120         session.beginInterruption(type);
121     });
122     updateSessionState();
123 }
124
125 void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInterruptionFlags flags)
126 {
127     ALWAYS_LOG(LOGIDENTIFIER);
128
129     m_interrupted = false;
130     forEachSession([flags] (auto& session) {
131         session.endInterruption(flags);
132     });
133 }
134
135 void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
136 {
137     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
138     m_sessions.append(makeWeakPtr(session));
139     if (m_interrupted)
140         session.setState(PlatformMediaSession::Interrupted);
141
142     if (!m_remoteCommandListener)
143         m_remoteCommandListener = RemoteCommandListener::create(*this);
144
145     if (!m_audioHardwareListener)
146         m_audioHardwareListener = AudioHardwareListener::create(*this);
147
148 #if !RELEASE_LOG_DISABLED
149     m_logger->addLogger(session.logger());
150 #endif
151
152     updateSessionState();
153 }
154
155 void PlatformMediaSessionManager::removeSession(PlatformMediaSession& session)
156 {
157     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
158
159     size_t index = m_sessions.find(&session);
160     if (index == notFound)
161         return;
162
163     m_sessions.remove(index);
164
165     if (m_sessions.isEmpty() || std::all_of(m_sessions.begin(), m_sessions.end(), std::logical_not<void>())) {
166         m_remoteCommandListener = nullptr;
167         m_audioHardwareListener = nullptr;
168 #if USE(AUDIO_SESSION)
169         if (m_becameActive && shouldDeactivateAudioSession()) {
170             AudioSession::sharedSession().tryToSetActive(false);
171             m_becameActive = false;
172         }
173 #endif
174     }
175
176 #if !RELEASE_LOG_DISABLED
177     m_logger->removeLogger(session.logger());
178 #endif
179
180     updateSessionState();
181 }
182
183 void PlatformMediaSessionManager::addRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
184 {
185     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
186     m_restrictions[type] |= restriction;
187 }
188
189 void PlatformMediaSessionManager::removeRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
190 {
191     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
192     m_restrictions[type] &= ~restriction;
193 }
194
195 PlatformMediaSessionManager::SessionRestrictions PlatformMediaSessionManager::restrictions(PlatformMediaSession::MediaType type)
196 {
197     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
198     return m_restrictions[type];
199 }
200
201 bool PlatformMediaSessionManager::sessionWillBeginPlayback(PlatformMediaSession& session)
202 {
203     setCurrentSession(session);
204
205     PlatformMediaSession::MediaType sessionType = session.mediaType();
206     SessionRestrictions restrictions = m_restrictions[sessionType];
207     if (session.state() == PlatformMediaSession::Interrupted && restrictions & InterruptedPlaybackNotPermitted) {
208         ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning false because session.state() is Interrupted, and InterruptedPlaybackNotPermitted");
209         return false;
210     }
211
212     if (m_processIsSuspended) {
213         ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning false because process is suspended");
214         return false;
215     }
216
217 #if USE(AUDIO_SESSION)
218     if (activeAudioSessionRequired()) {
219         if (!AudioSession::sharedSession().tryToSetActive(true)) {
220             ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning false failed to set active AudioSession");
221             return false;
222         }
223
224         ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " sucessfully activated AudioSession");
225         m_becameActive = true;
226     }
227 #endif
228
229     if (m_interrupted)
230         endInterruption(PlatformMediaSession::NoFlags);
231
232     if (restrictions & ConcurrentPlaybackNotPermitted) {
233         forEachMatchingSession([&session, sessionType](auto& oneSession) {
234             return &oneSession != &session
235                 && oneSession.mediaType() == sessionType
236                 && oneSession.state() == PlatformMediaSession::Playing
237                 && !oneSession.canPlayConcurrently(session);
238         }, [](auto& oneSession) {
239             oneSession.pauseSession();
240         });
241     }
242     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning true");
243     return true;
244 }
245     
246 void PlatformMediaSessionManager::sessionWillEndPlayback(PlatformMediaSession& session)
247 {
248     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
249     
250     if (m_sessions.size() < 2)
251         return;
252     
253     size_t pausingSessionIndex = notFound;
254     size_t lastPlayingSessionIndex = notFound;
255     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
256         const auto& oneSession = *m_sessions[i];
257         if (&oneSession == &session) {
258             pausingSessionIndex = i;
259             break;
260         }
261         if (oneSession.state() == PlatformMediaSession::Playing) {
262             lastPlayingSessionIndex = i;
263             break;
264         }
265         if (oneSession.state() != PlatformMediaSession::Playing)
266             break;
267     }
268     if (lastPlayingSessionIndex == notFound || pausingSessionIndex == notFound)
269         return;
270     
271     if (pausingSessionIndex > lastPlayingSessionIndex)
272         return;
273
274     m_sessions.remove(pausingSessionIndex);
275     m_sessions.append(makeWeakPtr(session));
276     
277     ALWAYS_LOG(LOGIDENTIFIER, "session moved from index ", pausingSessionIndex, " to ", lastPlayingSessionIndex);
278 }
279
280 void PlatformMediaSessionManager::sessionStateChanged(PlatformMediaSession&)
281 {
282     updateSessionState();
283 }
284
285 void PlatformMediaSessionManager::setCurrentSession(PlatformMediaSession& session)
286 {
287     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
288     
289     if (m_sessions.size() < 2)
290         return;
291     
292     size_t index = m_sessions.find(&session);
293     ASSERT(index != notFound);
294     if (!index || index == notFound)
295         return;
296
297     m_sessions.remove(index);
298     m_sessions.insert(0, makeWeakPtr(session));
299     if (m_remoteCommandListener)
300         m_remoteCommandListener->updateSupportedCommands();
301     
302     ALWAYS_LOG(LOGIDENTIFIER, "session moved from index ", index, " to 0");
303 }
304     
305 PlatformMediaSession* PlatformMediaSessionManager::currentSession() const
306 {
307     if (!m_sessions.size())
308         return nullptr;
309
310     return m_sessions[0].get();
311 }
312
313 void PlatformMediaSessionManager::applicationWillBecomeInactive()
314 {
315     ALWAYS_LOG(LOGIDENTIFIER);
316
317     forEachMatchingSession([&](auto& session) {
318         return m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted;
319     }, [](auto& session) {
320         session.beginInterruption(PlatformMediaSession::ProcessInactive);
321     });
322 }
323
324 void PlatformMediaSessionManager::applicationDidBecomeActive()
325 {
326     ALWAYS_LOG(LOGIDENTIFIER);
327
328     forEachMatchingSession([&](auto& session) {
329         return m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted;
330     }, [](auto& session) {
331         session.endInterruption(PlatformMediaSession::MayResumePlaying);
332     });
333 }
334
335 void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUnderLock)
336 {
337     ALWAYS_LOG(LOGIDENTIFIER, "suspendedUnderLock: ", suspendedUnderLock);
338
339     if (m_isApplicationInBackground)
340         return;
341
342     m_isApplicationInBackground = true;
343
344     forEachSession([&] (auto& session) {
345         if (suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted)
346             session.beginInterruption(PlatformMediaSession::SuspendedUnderLock);
347         else if (m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
348             session.beginInterruption(PlatformMediaSession::EnteringBackground);
349     });
350 }
351
352 void PlatformMediaSessionManager::applicationWillEnterForeground(bool suspendedUnderLock)
353 {
354     ALWAYS_LOG(LOGIDENTIFIER, "suspendedUnderLock: ", suspendedUnderLock);
355
356     if (!m_isApplicationInBackground)
357         return;
358
359     m_isApplicationInBackground = false;
360
361     forEachMatchingSession([&](auto& session) {
362         return (suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted) || m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted;
363     }, [](auto& session) {
364         session.endInterruption(PlatformMediaSession::MayResumePlaying);
365     });
366 }
367
368 void PlatformMediaSessionManager::processWillSuspend()
369 {
370     if (m_processIsSuspended)
371         return;
372     m_processIsSuspended = true;
373
374 #if USE(AUDIO_SESSION)
375     if (m_becameActive && shouldDeactivateAudioSession()) {
376         AudioSession::sharedSession().tryToSetActive(false);
377         ALWAYS_LOG(LOGIDENTIFIER, "tried to set inactive AudioSession");
378         m_becameActive = false;
379     }
380 #endif
381 }
382
383 void PlatformMediaSessionManager::processDidResume()
384 {
385     if (!m_processIsSuspended)
386         return;
387     m_processIsSuspended = false;
388
389 #if USE(AUDIO_SESSION)
390     if (!m_becameActive && activeAudioSessionRequired()) {
391         m_becameActive = AudioSession::sharedSession().tryToSetActive(true);
392         ALWAYS_LOG(LOGIDENTIFIER, "tried to set active AudioSession, ", m_becameActive ? "succeeded" : "failed");
393     }
394 #endif
395 }
396
397
398 void PlatformMediaSessionManager::sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession& session)
399 {
400     if (!m_isApplicationInBackground || !(m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted))
401         return;
402
403     if (session.state() != PlatformMediaSession::Interrupted)
404         session.beginInterruption(PlatformMediaSession::EnteringBackground);
405 }
406
407 void PlatformMediaSessionManager::sessionCanProduceAudioChanged(PlatformMediaSession&)
408 {
409     updateSessionState();
410 }
411
412 void PlatformMediaSessionManager::didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType command, const PlatformMediaSession::RemoteCommandArgument* argument)
413 {
414     PlatformMediaSession* activeSession = currentSession();
415     if (!activeSession || !activeSession->canReceiveRemoteControlCommands())
416         return;
417     activeSession->didReceiveRemoteControlCommand(command, argument);
418 }
419
420 bool PlatformMediaSessionManager::supportsSeeking() const
421 {
422     PlatformMediaSession* activeSession = currentSession();
423     if (!activeSession)
424         return false;
425     return activeSession->supportsSeeking();
426 }
427
428 void PlatformMediaSessionManager::systemWillSleep()
429 {
430     if (m_interrupted)
431         return;
432
433     forEachSession([] (auto& session) {
434         session.beginInterruption(PlatformMediaSession::SystemSleep);
435     });
436 }
437
438 void PlatformMediaSessionManager::systemDidWake()
439 {
440     if (m_interrupted)
441         return;
442
443     forEachSession([] (auto& session) {
444         session.endInterruption(PlatformMediaSession::MayResumePlaying);
445     });
446 }
447
448 void PlatformMediaSessionManager::audioOutputDeviceChanged()
449 {
450     updateSessionState();
451 }
452
453 void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document& document)
454 {
455     forEachDocumentSession(document, [](auto& session) {
456         session.pauseSession();
457     });
458 }
459
460 void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
461 {
462     forEachSession([] (auto& session) {
463         session.stopSession();
464     });
465 }
466
467 void PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument(const Document& document)
468 {
469     forEachDocumentSession(document, [](auto& session) {
470         session.beginInterruption(PlatformMediaSession::PlaybackSuspended);
471     });
472 }
473
474 void PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument(const Document& document)
475 {
476     forEachDocumentSession(document, [](auto& session) {
477         session.endInterruption(PlatformMediaSession::MayResumePlaying);
478     });
479 }
480
481 void PlatformMediaSessionManager::suspendAllMediaBufferingForDocument(const Document& document)
482 {
483     forEachDocumentSession(document, [](auto& session) {
484         session.suspendBuffering();
485     });
486 }
487
488 void PlatformMediaSessionManager::resumeAllMediaBufferingForDocument(const Document& document)
489 {
490     forEachDocumentSession(document, [](auto& session) {
491         session.resumeBuffering();
492     });
493 }
494
495 Vector<WeakPtr<PlatformMediaSession>> PlatformMediaSessionManager::sessionsMatching(const WTF::Function<bool(const PlatformMediaSession&)>& filter) const
496 {
497     Vector<WeakPtr<PlatformMediaSession>> matchingSessions;
498     for (auto& session : m_sessions) {
499         if (filter(*session))
500             matchingSessions.append(session);
501     }
502     return matchingSessions;
503 }
504
505 void PlatformMediaSessionManager::forEachMatchingSession(const Function<bool(const PlatformMediaSession&)>& predicate, const Function<void(PlatformMediaSession&)>& callback)
506 {
507     for (auto& session : sessionsMatching(predicate)) {
508         ASSERT(session);
509         if (session)
510             callback(*session);
511     }
512 }
513
514 void PlatformMediaSessionManager::forEachDocumentSession(const Document& document, const Function<void(PlatformMediaSession&)>& callback)
515 {
516     forEachMatchingSession([&document](auto& session) {
517         return session.client().hostingDocument() == &document;
518     }, [&callback](auto& session) {
519         callback(session);
520     });
521 }
522
523 void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&)>& callback)
524 {
525     auto sessions = m_sessions;
526     for (auto& session : sessions) {
527         ASSERT(session);
528         if (session)
529             callback(*session);
530     }
531 }
532
533 bool PlatformMediaSessionManager::anyOfSessions(const Function<bool(const PlatformMediaSession&)>& predicate) const
534 {
535     return WTF::anyOf(m_sessions, [&predicate](const auto& session) {
536         return predicate(*session);
537     });
538 }
539
540 static bool& deactivateAudioSession()
541 {
542     static bool deactivate;
543     return deactivate;
544 }
545
546 bool PlatformMediaSessionManager::shouldDeactivateAudioSession()
547 {
548     return deactivateAudioSession();
549 }
550
551 void PlatformMediaSessionManager::setShouldDeactivateAudioSession(bool deactivate)
552 {
553     deactivateAudioSession() = deactivate;
554 }
555
556 #else // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
557
558 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
559 {
560
561 }
562
563 #endif // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
564
565 #if !RELEASE_LOG_DISABLED
566 WTFLogChannel& PlatformMediaSessionManager::logChannel() const
567 {
568     return LogMedia;
569 }
570 #endif
571
572 } // namespace WebCore