Multiple videos (with audios) with autoplay & playinline not working. Only one video...
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 May 2019 20:56:56 +0000 (20:56 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 May 2019 20:56:56 +0000 (20:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193312
<rdar://problem/47189864>

Reviewed by Jer Noble.

Source/WebCore:

Allow all MediaStream backed video elements to play together.
Any non MediaStream backed video will stop all MediaStream backed video elements.
Conversely, all non MediaStream backed videos will stop when playing one MediaStream backed video.

Refactor PlatformMediaSessionManager as the way to iterate through sessions
is not safe when pausing a session: if playing, the session will be moved in the array of sessions.

To handle this, copy the list of sessions before iterating through them.
For extra safety, make sessions WeakPtr.

Add routines for the case of filtering with a predicate taking a const session.
In that case, we do not copy the vector but iterate through it as a small optimization.

Test: webrtc/concurrentVideoPlayback.html

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager):
(WebCore::HTMLMediaElement::hasMediaStreamSource const):
* html/HTMLMediaElement.h:
* platform/audio/PlatformMediaSession.cpp:
(WebCore::PlatformMediaSession::activeAudioSessionRequired const):
(WebCore::PlatformMediaSession::canPlayConcurrently const):
(WebCore::PlatformMediaSession::activeAudioSessionRequired): Deleted.
* platform/audio/PlatformMediaSession.h:
(WebCore::PlatformMediaSessionClient::hasMediaStreamSource const):
* platform/audio/PlatformMediaSessionManager.cpp:
(WebCore::PlatformMediaSessionManager::has const):
(WebCore::PlatformMediaSessionManager::activeAudioSessionRequired const):
(WebCore::PlatformMediaSessionManager::canProduceAudio const):
(WebCore::PlatformMediaSessionManager::count const):
(WebCore::PlatformMediaSessionManager::beginInterruption):
(WebCore::PlatformMediaSessionManager::endInterruption):
(WebCore::PlatformMediaSessionManager::addSession):
(WebCore::PlatformMediaSessionManager::removeSession):
(WebCore::PlatformMediaSessionManager::sessionWillBeginPlayback):
(WebCore::PlatformMediaSessionManager::sessionWillEndPlayback):
(WebCore::PlatformMediaSessionManager::setCurrentSession):
(WebCore::PlatformMediaSessionManager::currentSession const):
(WebCore::PlatformMediaSessionManager::applicationWillBecomeInactive):
(WebCore::PlatformMediaSessionManager::applicationDidBecomeActive):
(WebCore::PlatformMediaSessionManager::applicationDidEnterBackground):
(WebCore::PlatformMediaSessionManager::applicationWillEnterForeground):
(WebCore::PlatformMediaSessionManager::systemWillSleep):
(WebCore::PlatformMediaSessionManager::systemDidWake):
(WebCore::PlatformMediaSessionManager::stopAllMediaPlaybackForDocument):
(WebCore::PlatformMediaSessionManager::stopAllMediaPlaybackForProcess):
(WebCore::PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument):
(WebCore::PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument):
(WebCore::PlatformMediaSessionManager::suspendAllMediaBufferingForDocument):
(WebCore::PlatformMediaSessionManager::resumeAllMediaBufferingForDocument):
(WebCore::PlatformMediaSessionManager::currentSessionsMatching const):
(WebCore::PlatformMediaSessionManager::forEachMatchingSession):
(WebCore::PlatformMediaSessionManager::forEachMatchingSession const):
(WebCore::PlatformMediaSessionManager::forEachSession):
(WebCore::PlatformMediaSessionManager::anyOfSessions const):
(): Deleted.
(WebCore::PlatformMediaSessionManager::applicationWillBecomeInactive const): Deleted.
(WebCore::PlatformMediaSessionManager::applicationDidBecomeActive const): Deleted.
(WebCore::PlatformMediaSessionManager::applicationDidEnterBackground const): Deleted.
(WebCore::PlatformMediaSessionManager::applicationWillEnterForeground const): Deleted.
(WebCore::PlatformMediaSessionManager::forEachSession const): Deleted.
(WebCore::PlatformMediaSessionManager::findSession const): Deleted.
* platform/audio/PlatformMediaSessionManager.h:
(WebCore::PlatformMediaSessionManager::anyOfSessions const): Deleted.
* platform/audio/cocoa/MediaSessionManagerCocoa.mm:
(MediaSessionManagerCocoa::updateSessionState):
(MediaSessionManagerCocoa::beginInterruption):
* platform/audio/ios/MediaSessionManagerIOS.mm:
(WebCore::MediaSessionManageriOS::configureWireLessTargetMonitoring):
(WebCore::MediaSessionManageriOS::externalOutputDeviceAvailableDidChange):

LayoutTests:

* webrtc/concurrentVideoPlayback-expected.txt: Added.
* webrtc/concurrentVideoPlayback.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245712 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
LayoutTests/ChangeLog
LayoutTests/webrtc/concurrentVideoPlayback-expected.txt [new file with mode: 0644]
LayoutTests/webrtc/concurrentVideoPlayback.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/platform/audio/PlatformMediaSession.cpp
Source/WebCore/platform/audio/PlatformMediaSession.h
Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp
Source/WebCore/platform/audio/PlatformMediaSessionManager.h
Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm
Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm

index a1fd2a0..cfa8159 100644 (file)
@@ -1,3 +1,14 @@
+2019-05-23  Youenn Fablet  <youenn@apple.com>
+
+        Multiple videos (with audios) with autoplay & playinline not working. Only one video play at a time.
+        https://bugs.webkit.org/show_bug.cgi?id=193312
+        <rdar://problem/47189864>
+
+        Reviewed by Jer Noble.
+
+        * webrtc/concurrentVideoPlayback-expected.txt: Added.
+        * webrtc/concurrentVideoPlayback.html: Added.
+
 2019-05-23  Shawn Roberts  <sroberts@apple.com>
 
         Updating test expectations for failing tests.
diff --git a/LayoutTests/webrtc/concurrentVideoPlayback-expected.txt b/LayoutTests/webrtc/concurrentVideoPlayback-expected.txt
new file mode 100644 (file)
index 0000000..7ec7dfb
--- /dev/null
@@ -0,0 +1,7 @@
+  
+
+PASS Basic audio/video exchange 
+PASS Play MediaStream backed streams concurrently 
+PASS Play regular video content should pause MediaStream backed video elements 
+PASS Play MediaStream backed video elements should pause regular video content 
+
diff --git a/LayoutTests/webrtc/concurrentVideoPlayback.html b/LayoutTests/webrtc/concurrentVideoPlayback.html
new file mode 100644 (file)
index 0000000..59610c4
--- /dev/null
@@ -0,0 +1,80 @@
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Testing concurrent video playing</title>
+        <script src="../resources/testharness.js"></script>
+        <script src="../resources/testharnessreport.js"></script>
+        <script src="../media/media-file.js"></script>
+    </head>
+    <body>
+        <video id="video1" autoplay controls></video>
+        <video id="video2" autoplay controls></video>
+        <video id="video3" autoplay controls></video>
+        <script src ="routines.js"></script>
+        <script>
+promise_test(async (test) => {
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    localStream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+
+    const remoteStream = await new Promise((resolve, reject) => {
+        createConnections((firstConnection) => {
+            firstConnection.addTrack(localStream.getVideoTracks()[0], localStream);
+            firstConnection.addTrack(localStream.getAudioTracks()[0], localStream);
+        }, (secondConnection) => {
+            secondConnection.ontrack = (trackEvent) => {
+                resolve(trackEvent.streams[0]);
+            };
+        });
+        setTimeout(() => reject("Test timed out"), 5000);
+    });
+
+    video1.srcObject = localStream;
+    video2.srcObject = remoteStream;
+}, "Basic audio/video exchange");
+
+promise_test(async (test) => {
+    await video1.play();
+    await video2.play();
+
+    assert_false(video1.paused, "video1 paused");
+    assert_false(video2.paused, "video2 paused");
+}, "Play MediaStream backed streams concurrently");
+
+promise_test(async (test) => {
+
+    if (window.internals)
+        internals.setMediaSessionRestrictions('videoaudio', 'ConcurrentPlaybackNotPermitted');
+
+    video3.src = findMediaFile('video', '../media/content/audio-tracks');
+    await video3.play();
+
+    let counter = 0;
+    while (!video1.paused && ++counter < 20)
+        await new Promise(resolve => setTimeout(resolve, 50));
+
+    assert_true(video1.paused, "video1 paused");
+    assert_true(video2.paused, "video2 paused");
+    assert_false(video3.paused, "video3 paused");
+}, "Play regular video content should pause MediaStream backed video elements");
+
+promise_test(async (test) => {
+    await video1.play();
+
+    assert_true(video3.paused, "video3 paused");
+
+    await video2.play();
+
+    let counter = 0;
+    while (!video3.paused && ++counter < 20)
+        await new Promise(resolve => setTimeout(resolve, 50));
+
+    assert_false(video1.paused, "video1 paused");
+    assert_false(video2.paused, "video2 paused");
+    assert_true(video3.paused, "video3 paused");
+}, "Play MediaStream backed video elements should pause regular video content");
+        </script>
+    </body>
+</html>
index b68d71e..7d7184a 100644 (file)
@@ -1,3 +1,82 @@
+2019-05-23  Youenn Fablet  <youenn@apple.com>
+
+        Multiple videos (with audios) with autoplay & playinline not working. Only one video play at a time.
+        https://bugs.webkit.org/show_bug.cgi?id=193312
+        <rdar://problem/47189864>
+
+        Reviewed by Jer Noble.
+
+        Allow all MediaStream backed video elements to play together.
+        Any non MediaStream backed video will stop all MediaStream backed video elements.
+        Conversely, all non MediaStream backed videos will stop when playing one MediaStream backed video.
+
+        Refactor PlatformMediaSessionManager as the way to iterate through sessions
+        is not safe when pausing a session: if playing, the session will be moved in the array of sessions.
+
+        To handle this, copy the list of sessions before iterating through them.
+        For extra safety, make sessions WeakPtr.
+
+        Add routines for the case of filtering with a predicate taking a const session.
+        In that case, we do not copy the vector but iterate through it as a small optimization.
+
+        Test: webrtc/concurrentVideoPlayback.html
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager):
+        (WebCore::HTMLMediaElement::hasMediaStreamSource const):
+        * html/HTMLMediaElement.h:
+        * platform/audio/PlatformMediaSession.cpp:
+        (WebCore::PlatformMediaSession::activeAudioSessionRequired const):
+        (WebCore::PlatformMediaSession::canPlayConcurrently const):
+        (WebCore::PlatformMediaSession::activeAudioSessionRequired): Deleted.
+        * platform/audio/PlatformMediaSession.h:
+        (WebCore::PlatformMediaSessionClient::hasMediaStreamSource const):
+        * platform/audio/PlatformMediaSessionManager.cpp:
+        (WebCore::PlatformMediaSessionManager::has const):
+        (WebCore::PlatformMediaSessionManager::activeAudioSessionRequired const):
+        (WebCore::PlatformMediaSessionManager::canProduceAudio const):
+        (WebCore::PlatformMediaSessionManager::count const):
+        (WebCore::PlatformMediaSessionManager::beginInterruption):
+        (WebCore::PlatformMediaSessionManager::endInterruption):
+        (WebCore::PlatformMediaSessionManager::addSession):
+        (WebCore::PlatformMediaSessionManager::removeSession):
+        (WebCore::PlatformMediaSessionManager::sessionWillBeginPlayback):
+        (WebCore::PlatformMediaSessionManager::sessionWillEndPlayback):
+        (WebCore::PlatformMediaSessionManager::setCurrentSession):
+        (WebCore::PlatformMediaSessionManager::currentSession const):
+        (WebCore::PlatformMediaSessionManager::applicationWillBecomeInactive):
+        (WebCore::PlatformMediaSessionManager::applicationDidBecomeActive):
+        (WebCore::PlatformMediaSessionManager::applicationDidEnterBackground):
+        (WebCore::PlatformMediaSessionManager::applicationWillEnterForeground):
+        (WebCore::PlatformMediaSessionManager::systemWillSleep):
+        (WebCore::PlatformMediaSessionManager::systemDidWake):
+        (WebCore::PlatformMediaSessionManager::stopAllMediaPlaybackForDocument):
+        (WebCore::PlatformMediaSessionManager::stopAllMediaPlaybackForProcess):
+        (WebCore::PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument):
+        (WebCore::PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument):
+        (WebCore::PlatformMediaSessionManager::suspendAllMediaBufferingForDocument):
+        (WebCore::PlatformMediaSessionManager::resumeAllMediaBufferingForDocument):
+        (WebCore::PlatformMediaSessionManager::currentSessionsMatching const):
+        (WebCore::PlatformMediaSessionManager::forEachMatchingSession):
+        (WebCore::PlatformMediaSessionManager::forEachMatchingSession const):
+        (WebCore::PlatformMediaSessionManager::forEachSession):
+        (WebCore::PlatformMediaSessionManager::anyOfSessions const):
+        (): Deleted.
+        (WebCore::PlatformMediaSessionManager::applicationWillBecomeInactive const): Deleted.
+        (WebCore::PlatformMediaSessionManager::applicationDidBecomeActive const): Deleted.
+        (WebCore::PlatformMediaSessionManager::applicationDidEnterBackground const): Deleted.
+        (WebCore::PlatformMediaSessionManager::applicationWillEnterForeground const): Deleted.
+        (WebCore::PlatformMediaSessionManager::forEachSession const): Deleted.
+        (WebCore::PlatformMediaSessionManager::findSession const): Deleted.
+        * platform/audio/PlatformMediaSessionManager.h:
+        (WebCore::PlatformMediaSessionManager::anyOfSessions const): Deleted.
+        * platform/audio/cocoa/MediaSessionManagerCocoa.mm:
+        (MediaSessionManagerCocoa::updateSessionState):
+        (MediaSessionManagerCocoa::beginInterruption):
+        * platform/audio/ios/MediaSessionManagerIOS.mm:
+        (WebCore::MediaSessionManageriOS::configureWireLessTargetMonitoring):
+        (WebCore::MediaSessionManageriOS::externalOutputDeviceAvailableDidChange):
+
 2019-05-23  Saam barati  <sbarati@apple.com>
 
         [WHLSL] Add a helper for in-place AST mutation
index d6d3529..814066c 100644 (file)
@@ -1694,7 +1694,7 @@ void Document::allowsMediaDocumentInlinePlaybackChanged()
 void Document::stopAllMediaPlayback()
 {
     if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
-        platformMediaSessionManager->stopAllMediaPlaybackForDocument(this);
+        platformMediaSessionManager->stopAllMediaPlaybackForDocument(*this);
 }
 
 void Document::suspendAllMediaPlayback()
index cb84587..f00d81c 100644 (file)
@@ -657,19 +657,17 @@ HTMLMediaElement::~HTMLMediaElement()
 }
 RefPtr<HTMLMediaElement> HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose purpose)
 {
-    auto allSessions = PlatformMediaSessionManager::sharedManager().currentSessionsMatching([] (const PlatformMediaSession& session) {
-        return is<MediaElementSession>(session);
-    });
-
     Vector<MediaElementSessionInfo> candidateSessions;
     bool atLeastOneNonCandidateMayBeConfusedForMainContent = false;
-    for (auto& session : allSessions) {
-        auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(*session), purpose);
+    PlatformMediaSessionManager::sharedManager().forEachMatchingSession([](auto& session) {
+        return is<MediaElementSession>(session);
+    }, [&](auto& session) {
+        auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(session), purpose);
         if (mediaElementSessionInfo.canShowControlsManager)
             candidateSessions.append(mediaElementSessionInfo);
         else if (mediaSessionMayBeConfusedWithMainContent(mediaElementSessionInfo, purpose))
             atLeastOneNonCandidateMayBeConfusedForMainContent = true;
-    }
+    });
 
     if (!candidateSessions.size())
         return nullptr;
@@ -8137,6 +8135,15 @@ HTMLMediaElementEnums::BufferingPolicy HTMLMediaElement::bufferingPolicy() const
     return m_bufferingPolicy;    
 }
 
+bool HTMLMediaElement::hasMediaStreamSource() const
+{
+#if ENABLE(MEDIA_STREAM)
+    return hasMediaStreamSrcObject();
+#else
+    return false;
+#endif
+}
+
 }
 
 #endif
index c15dc24..2fff363 100644 (file)
@@ -892,6 +892,7 @@ private:
     bool shouldOverrideBackgroundLoadingRestriction() const override;
     bool canProduceAudio() const final;
     bool processingUserGestureForMedia() const final;
+    bool hasMediaStreamSource() const final;
 
     void pageMutedStateDidChange() override;
 
index ba36379..e02394a 100644 (file)
@@ -342,7 +342,7 @@ PlatformMediaSession::DisplayType PlatformMediaSession::displayType() const
     return m_client.displayType();
 }
 
-bool PlatformMediaSession::activeAudioSessionRequired()
+bool PlatformMediaSession::activeAudioSessionRequired() const
 {
     if (mediaType() == PlatformMediaSession::None)
         return false;
@@ -388,6 +388,11 @@ void PlatformMediaSession::clientCharacteristicsChanged()
     PlatformMediaSessionManager::sharedManager().clientCharacteristicsChanged(*this);
 }
 
+bool PlatformMediaSession::canPlayConcurrently(const PlatformMediaSession& otherSession) const
+{
+    return m_client.hasMediaStreamSource() && otherSession.m_client.hasMediaStreamSource();
+}
+
 #if !RELEASE_LOG_DISABLED
 WTFLogChannel& PlatformMediaSession::logChannel() const
 {
index 469824d..9c35117 100644 (file)
@@ -29,6 +29,7 @@
 #include "Timer.h"
 #include <wtf/LoggerHelper.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
@@ -42,8 +43,9 @@ class MediaPlaybackTarget;
 class PlatformMediaSessionClient;
 
 class PlatformMediaSession
+    : public CanMakeWeakPtr<PlatformMediaSession>
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
-    : public MediaPlaybackTargetClient
+    , public MediaPlaybackTargetClient
 #endif
 #if !RELEASE_LOG_DISABLED
     , private LoggerHelper
@@ -170,7 +172,7 @@ public:
     virtual bool requiresPlaybackTargetRouteMonitoring() const { return false; }
 #endif
 
-    bool activeAudioSessionRequired();
+    bool activeAudioSessionRequired() const;
     bool canProduceAudio() const;
     void canProduceAudioChanged();
 
@@ -189,6 +191,8 @@ public:
     WTFLogChannel& logChannel() const final;
 #endif
 
+    bool canPlayConcurrently(const PlatformMediaSession&) const;
+
 protected:
     PlatformMediaSessionClient& client() const { return m_client; }
 
@@ -253,6 +257,8 @@ public:
 
     virtual bool processingUserGestureForMedia() const = 0;
 
+    virtual bool hasMediaStreamSource() const { return false; }
+
 protected:
     virtual ~PlatformMediaSessionClient() = default;
 };
index 665c38c..426a674 100644 (file)
@@ -79,21 +79,21 @@ bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) cons
 {
     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
 
-    return anyOfSessions([type] (PlatformMediaSession& session, size_t) {
+    return anyOfSessions([type] (auto& session) {
         return session.mediaType() == type;
     });
 }
 
 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
 {
-    return anyOfSessions([] (PlatformMediaSession& session, size_t) {
+    return anyOfSessions([] (auto& session) {
         return session.activeAudioSessionRequired();
     });
 }
 
 bool PlatformMediaSessionManager::canProduceAudio() const
 {
-    return anyOfSessions([] (PlatformMediaSession& session, size_t) {
+    return anyOfSessions([] (auto& session) {
         return session.canProduceAudio();
     });
 }
@@ -103,7 +103,7 @@ int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) con
     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
 
     int count = 0;
-    for (auto* session : m_sessions) {
+    for (const auto& session : m_sessions) {
         if (session->mediaType() == type)
             ++count;
     }
@@ -116,7 +116,7 @@ void PlatformMediaSessionManager::beginInterruption(PlatformMediaSession::Interr
     ALWAYS_LOG(LOGIDENTIFIER);
 
     m_interrupted = true;
-    forEachSession([type] (PlatformMediaSession& session, size_t) {
+    forEachSession([type] (auto& session) {
         session.beginInterruption(type);
     });
     updateSessionState();
@@ -127,7 +127,7 @@ void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInter
     ALWAYS_LOG(LOGIDENTIFIER);
 
     m_interrupted = false;
-    forEachSession([flags] (PlatformMediaSession& session, size_t) {
+    forEachSession([flags] (auto& session) {
         session.endInterruption(flags);
     });
 }
@@ -135,8 +135,7 @@ void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInter
 void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
 {
     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
-    
-    m_sessions.append(&session);
+    m_sessions.append(makeWeakPtr(session));
     if (m_interrupted)
         session.setState(PlatformMediaSession::Interrupted);
 
@@ -156,15 +155,12 @@ void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
 void PlatformMediaSessionManager::removeSession(PlatformMediaSession& session)
 {
     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
-    
+
     size_t index = m_sessions.find(&session);
     if (index == notFound)
         return;
 
-    if (m_iteratingOverSessions)
-        m_sessions.at(index) = nullptr;
-    else
-        m_sessions.remove(index);
+    m_sessions.remove(index);
 
     if (m_sessions.isEmpty() || std::all_of(m_sessions.begin(), m_sessions.end(), std::logical_not<void>())) {
         m_remoteCommandListener = nullptr;
@@ -233,15 +229,16 @@ bool PlatformMediaSessionManager::sessionWillBeginPlayback(PlatformMediaSession&
     if (m_interrupted)
         endInterruption(PlatformMediaSession::NoFlags);
 
-    forEachSession([&] (PlatformMediaSession& oneSession, size_t) {
-        if (&oneSession == &session)
-            return;
-        if (oneSession.mediaType() == sessionType
-            && restrictions & ConcurrentPlaybackNotPermitted
-            && oneSession.state() == PlatformMediaSession::Playing)
+    if (restrictions & ConcurrentPlaybackNotPermitted) {
+        forEachMatchingSession([&session, sessionType](auto& oneSession) {
+            return &oneSession != &session
+                && oneSession.mediaType() == sessionType
+                && oneSession.state() == PlatformMediaSession::Playing
+                && !oneSession.canPlayConcurrently(session);
+        }, [](auto& oneSession) {
             oneSession.pauseSession();
-    });
-
+        });
+    }
     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning true");
     return true;
 }
@@ -255,25 +252,27 @@ void PlatformMediaSessionManager::sessionWillEndPlayback(PlatformMediaSession& s
     
     size_t pausingSessionIndex = notFound;
     size_t lastPlayingSessionIndex = notFound;
-    anyOfSessions([&] (PlatformMediaSession& oneSession, size_t i) {
+    for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
+        const auto& oneSession = *m_sessions[i];
         if (&oneSession == &session) {
             pausingSessionIndex = i;
-            return false;
+            break;
         }
         if (oneSession.state() == PlatformMediaSession::Playing) {
             lastPlayingSessionIndex = i;
-            return false;
+            break;
         }
-        return oneSession.state() != PlatformMediaSession::Playing;
-    });
+        if (oneSession.state() != PlatformMediaSession::Playing)
+            break;
+    }
     if (lastPlayingSessionIndex == notFound || pausingSessionIndex == notFound)
         return;
     
     if (pausingSessionIndex > lastPlayingSessionIndex)
         return;
-    
+
     m_sessions.remove(pausingSessionIndex);
-    m_sessions.insert(lastPlayingSessionIndex, &session);
+    m_sessions.append(makeWeakPtr(session));
     
     ALWAYS_LOG(LOGIDENTIFIER, "session moved from index ", pausingSessionIndex, " to ", lastPlayingSessionIndex);
 }
@@ -296,7 +295,7 @@ void PlatformMediaSessionManager::setCurrentSession(PlatformMediaSession& sessio
         return;
 
     m_sessions.remove(index);
-    m_sessions.insert(0, &session);
+    m_sessions.insert(0, makeWeakPtr(session));
     if (m_remoteCommandListener)
         m_remoteCommandListener->updateSupportedCommands();
     
@@ -308,40 +307,32 @@ PlatformMediaSession* PlatformMediaSessionManager::currentSession() const
     if (!m_sessions.size())
         return nullptr;
 
-    return m_sessions[0];
-}
-
-Vector<PlatformMediaSession*> PlatformMediaSessionManager::currentSessionsMatching(const WTF::Function<bool(const PlatformMediaSession&)>& filter)
-{
-    Vector<PlatformMediaSession*> matchingSessions;
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (filter(session))
-            matchingSessions.append(&session);
-    });
-    return matchingSessions;
+    return m_sessions[0].get();
 }
 
-void PlatformMediaSessionManager::applicationWillBecomeInactive() const
+void PlatformMediaSessionManager::applicationWillBecomeInactive()
 {
     ALWAYS_LOG(LOGIDENTIFIER);
 
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted)
-            session.beginInterruption(PlatformMediaSession::ProcessInactive);
+    forEachMatchingSession([&](auto& session) {
+        return m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted;
+    }, [](auto& session) {
+        session.beginInterruption(PlatformMediaSession::ProcessInactive);
     });
 }
 
-void PlatformMediaSessionManager::applicationDidBecomeActive() const
+void PlatformMediaSessionManager::applicationDidBecomeActive()
 {
     ALWAYS_LOG(LOGIDENTIFIER);
 
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted)
-            session.endInterruption(PlatformMediaSession::MayResumePlaying);
+    forEachMatchingSession([&](auto& session) {
+        return m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted;
+    }, [](auto& session) {
+        session.endInterruption(PlatformMediaSession::MayResumePlaying);
     });
 }
 
-void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUnderLock) const
+void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUnderLock)
 {
     ALWAYS_LOG(LOGIDENTIFIER, "suspendedUnderLock: ", suspendedUnderLock);
 
@@ -350,7 +341,7 @@ void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUn
 
     m_isApplicationInBackground = true;
 
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
+    forEachSession([&] (auto& session) {
         if (suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted)
             session.beginInterruption(PlatformMediaSession::SuspendedUnderLock);
         else if (m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
@@ -358,7 +349,7 @@ void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUn
     });
 }
 
-void PlatformMediaSessionManager::applicationWillEnterForeground(bool suspendedUnderLock) const
+void PlatformMediaSessionManager::applicationWillEnterForeground(bool suspendedUnderLock)
 {
     ALWAYS_LOG(LOGIDENTIFIER, "suspendedUnderLock: ", suspendedUnderLock);
 
@@ -367,9 +358,10 @@ void PlatformMediaSessionManager::applicationWillEnterForeground(bool suspendedU
 
     m_isApplicationInBackground = false;
 
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if ((suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted) || m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
-            session.endInterruption(PlatformMediaSession::MayResumePlaying);
+    forEachMatchingSession([&](auto& session) {
+        return (suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted) || m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted;
+    }, [](auto& session) {
+        session.endInterruption(PlatformMediaSession::MayResumePlaying);
     });
 }
 
@@ -438,7 +430,7 @@ void PlatformMediaSessionManager::systemWillSleep()
     if (m_interrupted)
         return;
 
-    forEachSession([] (PlatformMediaSession& session, size_t) {
+    forEachSession([] (auto& session) {
         session.beginInterruption(PlatformMediaSession::SystemSleep);
     });
 }
@@ -448,7 +440,7 @@ void PlatformMediaSessionManager::systemDidWake()
     if (m_interrupted)
         return;
 
-    forEachSession([] (PlatformMediaSession& session, size_t) {
+    forEachSession([] (auto& session) {
         session.endInterruption(PlatformMediaSession::MayResumePlaying);
     });
 }
@@ -458,91 +450,91 @@ void PlatformMediaSessionManager::audioOutputDeviceChanged()
     updateSessionState();
 }
 
-void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document* document)
+void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document& document)
 {
-    forEachSession([document] (PlatformMediaSession& session, size_t) {
-        if (session.client().hostingDocument() == document)
-            session.pauseSession();
+    forEachDocumentSession(document, [](auto& session) {
+        session.pauseSession();
     });
 }
 
 void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
 {
-    forEachSession([] (PlatformMediaSession& session, size_t) {
+    forEachSession([] (auto& session) {
         session.stopSession();
     });
 }
 
 void PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument(const Document& document)
 {
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (session.client().hostingDocument() == &document)
-            session.beginInterruption(PlatformMediaSession::PlaybackSuspended);
+    forEachDocumentSession(document, [](auto& session) {
+        session.beginInterruption(PlatformMediaSession::PlaybackSuspended);
     });
 }
 
 void PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument(const Document& document)
 {
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (session.client().hostingDocument() == &document)
-            session.endInterruption(PlatformMediaSession::MayResumePlaying);
+    forEachDocumentSession(document, [](auto& session) {
+        session.endInterruption(PlatformMediaSession::MayResumePlaying);
     });
 }
 
 void PlatformMediaSessionManager::suspendAllMediaBufferingForDocument(const Document& document)
 {
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (session.client().hostingDocument() == &document)
-            session.suspendBuffering();
+    forEachDocumentSession(document, [](auto& session) {
+        session.suspendBuffering();
     });
 }
 
 void PlatformMediaSessionManager::resumeAllMediaBufferingForDocument(const Document& document)
 {
-    forEachSession([&] (PlatformMediaSession& session, size_t) {
-        if (session.client().hostingDocument() == &document)
-            session.resumeBuffering();
+    forEachDocumentSession(document, [](auto& session) {
+        session.resumeBuffering();
     });
 }
 
-void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&, size_t)>& predicate) const
+Vector<WeakPtr<PlatformMediaSession>> PlatformMediaSessionManager::sessionsMatching(const WTF::Function<bool(const PlatformMediaSession&)>& filter) const
 {
-    ++m_iteratingOverSessions;
-
-    for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
-        auto session = m_sessions[i];
-        if (!session)
-            continue;
-        predicate(*session, i);
+    Vector<WeakPtr<PlatformMediaSession>> matchingSessions;
+    for (auto& session : m_sessions) {
+        if (filter(*session))
+            matchingSessions.append(session);
     }
-
-    --m_iteratingOverSessions;
-    if (!m_iteratingOverSessions)
-        m_sessions.removeAll(nullptr);
+    return matchingSessions;
 }
 
-PlatformMediaSession* PlatformMediaSessionManager::findSession(const Function<bool(PlatformMediaSession&, size_t)>& predicate) const
+void PlatformMediaSessionManager::forEachMatchingSession(const Function<bool(const PlatformMediaSession&)>& predicate, const Function<void(PlatformMediaSession&)>& callback)
 {
-    ++m_iteratingOverSessions;
-
-    PlatformMediaSession* foundSession = nullptr;
-    for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
-        auto session = m_sessions[i];
-        if (!session)
-            continue;
+    for (auto& session : sessionsMatching(predicate)) {
+        ASSERT(session);
+        if (session)
+            callback(*session);
+    }
+}
 
-        if (!predicate(*session, i))
-            continue;
+void PlatformMediaSessionManager::forEachDocumentSession(const Document& document, const Function<void(PlatformMediaSession&)>& callback)
+{
+    forEachMatchingSession([&document](auto& session) {
+        return session.client().hostingDocument() == &document;
+    }, [&callback](auto& session) {
+        callback(session);
+    });
+}
 
-        foundSession = session;
-        break;
+void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&)>& callback)
+{
+    auto sessions = m_sessions;
+    for (auto& session : sessions) {
+        ASSERT(session);
+        if (session)
+            callback(*session);
     }
+}
 
-    --m_iteratingOverSessions;
-    if (!m_iteratingOverSessions)
-        m_sessions.removeAll(nullptr);
-
-    return foundSession;
+bool PlatformMediaSessionManager::anyOfSessions(const Function<bool(const PlatformMediaSession&)>& predicate) const
+{
+    return WTF::anyOf(m_sessions, [&predicate](const auto& session) {
+        return predicate(*session);
+    });
 }
 
 static bool& deactivateAudioSession()
index 98f1f5e..3685307 100644 (file)
@@ -81,14 +81,14 @@ public:
     WEBCORE_EXPORT virtual void beginInterruption(PlatformMediaSession::InterruptionType);
     WEBCORE_EXPORT void endInterruption(PlatformMediaSession::EndInterruptionFlags);
 
-    WEBCORE_EXPORT void applicationWillBecomeInactive() const;
-    WEBCORE_EXPORT void applicationDidBecomeActive() const;
-    WEBCORE_EXPORT void applicationWillEnterForeground(bool suspendedUnderLock) const;
-    WEBCORE_EXPORT void applicationDidEnterBackground(bool suspendedUnderLock) const;
+    WEBCORE_EXPORT void applicationWillBecomeInactive();
+    WEBCORE_EXPORT void applicationDidBecomeActive();
+    WEBCORE_EXPORT void applicationWillEnterForeground(bool suspendedUnderLock);
+    WEBCORE_EXPORT void applicationDidEnterBackground(bool suspendedUnderLock);
     WEBCORE_EXPORT void processWillSuspend();
     WEBCORE_EXPORT void processDidResume();
 
-    void stopAllMediaPlaybackForDocument(const Document*);
+    void stopAllMediaPlaybackForDocument(const Document&);
     WEBCORE_EXPORT void stopAllMediaPlaybackForProcess();
 
     void suspendAllMediaPlaybackForDocument(const Document&);
@@ -127,10 +127,10 @@ public:
     void setCurrentSession(PlatformMediaSession&);
     PlatformMediaSession* currentSession() const;
 
-    Vector<PlatformMediaSession*> currentSessionsMatching(const WTF::Function<bool(const PlatformMediaSession&)>&);
-
     void sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession&);
 
+    void forEachMatchingSession(const Function<bool(const PlatformMediaSession&)>& predicate, const Function<void(PlatformMediaSession&)>& matchingCallback);
+
 protected:
     friend class PlatformMediaSession;
     explicit PlatformMediaSessionManager();
@@ -138,9 +138,9 @@ protected:
     void addSession(PlatformMediaSession&);
     virtual void removeSession(PlatformMediaSession&);
 
-    void forEachSession(const Function<void(PlatformMediaSession&, size_t)>&) const;
-    PlatformMediaSession* findSession(const Function<bool(PlatformMediaSession&, size_t)>&) const;
-    bool anyOfSessions(const Function<bool(PlatformMediaSession&, size_t)>& predicate) const { return findSession(predicate); }
+    void forEachSession(const Function<void(PlatformMediaSession&)>&);
+    void forEachDocumentSession(const Document&, const Function<void(PlatformMediaSession&)>&);
+    bool anyOfSessions(const Function<bool(const PlatformMediaSession&)>&) const;
 
     AudioHardwareListener* audioHardwareListener() { return m_audioHardwareListener.get(); }
 
@@ -171,8 +171,10 @@ private:
     void systemWillSleep() override;
     void systemDidWake() override;
 
+    Vector<WeakPtr<PlatformMediaSession>> sessionsMatching(const Function<bool(const PlatformMediaSession&)>&) const;
+
     SessionRestrictions m_restrictions[PlatformMediaSession::MediaStreamCapturingAudio + 1];
-    mutable Vector<PlatformMediaSession*> m_sessions;
+    mutable Vector<WeakPtr<PlatformMediaSession>> m_sessions;
     std::unique_ptr<RemoteCommandListener> m_remoteCommandListener;
     std::unique_ptr<PAL::SystemSleepListener> m_systemSleepListener;
     RefPtr<AudioHardwareListener> m_audioHardwareListener;
@@ -185,7 +187,6 @@ private:
     bool m_interrupted { false };
     mutable bool m_isApplicationInBackground { false };
     bool m_willIgnoreSystemInterruptions { false };
-    mutable int m_iteratingOverSessions { 0 };
     bool m_processIsSuspended { false };
 
 #if USE(AUDIO_SESSION)
index 374d4cb..ee22386 100644 (file)
@@ -97,7 +97,7 @@ void MediaSessionManagerCocoa::updateSessionState()
         return;
 
     bool hasAudibleAudioOrVideoMediaType = false;
-    forEachSession([&hasAudibleAudioOrVideoMediaType] (PlatformMediaSession& session, size_t) mutable {
+    forEachSession([&hasAudibleAudioOrVideoMediaType] (auto& session) mutable {
         auto type = session.mediaType();
         if ((type == PlatformMediaSession::VideoAudio || type == PlatformMediaSession::Audio) && session.canProduceAudio() && session.hasPlayedSinceLastInterruption())
             hasAudibleAudioOrVideoMediaType = true;
@@ -122,7 +122,7 @@ void MediaSessionManagerCocoa::updateSessionState()
 void MediaSessionManagerCocoa::beginInterruption(PlatformMediaSession::InterruptionType type)
 {
     if (type == PlatformMediaSession::InterruptionType::SystemInterruption) {
-        forEachSession([] (PlatformMediaSession& session, size_t) {
+        forEachSession([] (auto& session) {
             session.clearHasPlayedSinceLastInterruption();
         });
     }
index e1a9fb4..0043904 100644 (file)
@@ -146,7 +146,7 @@ bool MediaSessionManageriOS::hasWirelessTargetsAvailable()
 void MediaSessionManageriOS::configureWireLessTargetMonitoring()
 {
 #if HAVE(MEDIA_PLAYER) && !PLATFORM(WATCHOS)
-    bool requiresMonitoring = anyOfSessions([] (PlatformMediaSession& session, size_t) {
+    bool requiresMonitoring = anyOfSessions([] (auto& session) {
         return session.requiresPlaybackTargetRouteMonitoring();
     });
 
@@ -187,7 +187,7 @@ void MediaSessionManageriOS::externalOutputDeviceAvailableDidChange()
     bool haveTargets = [m_objcObserver hasWirelessTargetsAvailable];
     ALWAYS_LOG(LOGIDENTIFIER, haveTargets);
 
-    forEachSession([haveTargets] (PlatformMediaSession& session, size_t) {
+    forEachSession([haveTargets] (auto& session) {
         session.externalOutputDeviceAvailableDidChange(haveTargets);
     });