Title: [255581] trunk/Source
Revision
255581
Author
eric.carl...@apple.com
Date
2020-02-03 12:19:44 -0800 (Mon, 03 Feb 2020)

Log Message

[macOS] AirPlay sometimes stops after 60 minutes of playback
https://bugs.webkit.org/show_bug.cgi?id=207056
Source/WebCore:

<rdar://problem/53649508>

Reviewed by Jer Noble.

No new tests, this only reproduces when playing to an AirPlay device.

AVPlayerItem.tracks is empty during AirPlay. If AirPlay is activated immediately
after the item is created, as is typically the case when switching from an MSE to
a url based player, MediaPlayerPrivateAVFoundationObjC doesn't know if the AVPlayerItem
has audio or video so the state reported to the WebMediaSessionManager is incorrect.
AirPlay can't actually be active if an item doesn't have audio or video, so always claim
to have both during AirPlay.

Converted WebMediaSessionManager logging from debug-only to runtime to make it easier
to diagnose problems in the future.

* Modules/mediasession/WebMediaSessionManager.cpp:
(WebCore::mediaProducerStateString):
(WebCore::WebMediaSessionLogger::create):
(WebCore::WebMediaSessionLogger::WebMediaSessionLogger):
(WebCore::WebMediaSessionLogger::log const):
(WebCore::WebMediaSessionManager::logger):
(WebCore::WebMediaSessionManager::alwaysOnLoggingAllowed const):
(WebCore::WebMediaSessionManager::setMockMediaPlaybackTargetPickerEnabled):
(WebCore::WebMediaSessionManager::setMockMediaPlaybackTargetPickerState):
(WebCore::WebMediaSessionManager::mockMediaPlaybackTargetPickerDismissPopup):
(WebCore::WebMediaSessionManager::addPlaybackTargetPickerClient):
(WebCore::WebMediaSessionManager::removePlaybackTargetPickerClient):
(WebCore::WebMediaSessionManager::removeAllPlaybackTargetPickerClients):
(WebCore::WebMediaSessionManager::showPlaybackTargetPicker):
(WebCore::WebMediaSessionManager::clientStateDidChange):
(WebCore::WebMediaSessionManager::setPlaybackTarget):
(WebCore::WebMediaSessionManager::externalOutputDeviceAvailableDidChange):
(WebCore::WebMediaSessionManager::playbackTargetPickerWasDismissed):
(WebCore::WebMediaSessionManager::configurePlaybackTargetClients):
(WebCore::WebMediaSessionManager::configurePlaybackTargetMonitoring):
(WebCore::WebMediaSessionManager::configureWatchdogTimer):
(WebCore::WebMediaSessionManager::watchdogTimerFired):
(WebCore::ClientState::logAlways const): Deleted.
* Modules/mediasession/WebMediaSessionManager.h:
* Modules/mediasession/WebMediaSessionManagerClient.h:
(WebCore::WebMediaSessionManagerClient::alwaysOnLoggingAllowed):
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::mediaState const):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::hasVideo const):
(WebCore::MediaPlayerPrivateAVFoundationObjC::hasAudio const):

Source/WebKit:

Reviewed by Jer Noble.

* UIProcess/WebPageProxy.cpp:
* UIProcess/WebPageProxy.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (255580 => 255581)


--- trunk/Source/WebCore/ChangeLog	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebCore/ChangeLog	2020-02-03 20:19:44 UTC (rev 255581)
@@ -1,3 +1,56 @@
+2020-02-03  Eric Carlson  <eric.carl...@apple.com>
+
+        [macOS] AirPlay sometimes stops after 60 minutes of playback
+        https://bugs.webkit.org/show_bug.cgi?id=207056
+        <rdar://problem/53649508>
+
+        Reviewed by Jer Noble.
+
+        No new tests, this only reproduces when playing to an AirPlay device.
+
+        AVPlayerItem.tracks is empty during AirPlay. If AirPlay is activated immediately
+        after the item is created, as is typically the case when switching from an MSE to
+        a url based player, MediaPlayerPrivateAVFoundationObjC doesn't know if the AVPlayerItem
+        has audio or video so the state reported to the WebMediaSessionManager is incorrect.
+        AirPlay can't actually be active if an item doesn't have audio or video, so always claim
+        to have both during AirPlay.
+
+        Converted WebMediaSessionManager logging from debug-only to runtime to make it easier
+        to diagnose problems in the future.
+
+        * Modules/mediasession/WebMediaSessionManager.cpp:
+        (WebCore::mediaProducerStateString):
+        (WebCore::WebMediaSessionLogger::create):
+        (WebCore::WebMediaSessionLogger::WebMediaSessionLogger):
+        (WebCore::WebMediaSessionLogger::log const):
+        (WebCore::WebMediaSessionManager::logger):
+        (WebCore::WebMediaSessionManager::alwaysOnLoggingAllowed const):
+        (WebCore::WebMediaSessionManager::setMockMediaPlaybackTargetPickerEnabled):
+        (WebCore::WebMediaSessionManager::setMockMediaPlaybackTargetPickerState):
+        (WebCore::WebMediaSessionManager::mockMediaPlaybackTargetPickerDismissPopup):
+        (WebCore::WebMediaSessionManager::addPlaybackTargetPickerClient):
+        (WebCore::WebMediaSessionManager::removePlaybackTargetPickerClient):
+        (WebCore::WebMediaSessionManager::removeAllPlaybackTargetPickerClients):
+        (WebCore::WebMediaSessionManager::showPlaybackTargetPicker):
+        (WebCore::WebMediaSessionManager::clientStateDidChange):
+        (WebCore::WebMediaSessionManager::setPlaybackTarget):
+        (WebCore::WebMediaSessionManager::externalOutputDeviceAvailableDidChange):
+        (WebCore::WebMediaSessionManager::playbackTargetPickerWasDismissed):
+        (WebCore::WebMediaSessionManager::configurePlaybackTargetClients):
+        (WebCore::WebMediaSessionManager::configurePlaybackTargetMonitoring):
+        (WebCore::WebMediaSessionManager::configureWatchdogTimer):
+        (WebCore::WebMediaSessionManager::watchdogTimerFired):
+        (WebCore::ClientState::logAlways const): Deleted.
+        * Modules/mediasession/WebMediaSessionManager.h:
+        * Modules/mediasession/WebMediaSessionManagerClient.h:
+        (WebCore::WebMediaSessionManagerClient::alwaysOnLoggingAllowed):
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::mediaState const):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::hasVideo const):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::hasAudio const):
+
 2020-02-03  youenn fablet  <you...@apple.com>
 
         Do not copy feature policy in isFeaturePolicyAllowedByDocumentAndAllOwners

Modified: trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp (255580 => 255581)


--- trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp	2020-02-03 20:19:44 UTC (rev 255581)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -32,6 +32,8 @@
 #include "Logging.h"
 #include "MediaPlaybackTargetPickerMock.h"
 #include "WebMediaSessionManagerClient.h"
+#include <wtf/Algorithms.h>
+#include <wtf/Logger.h>
 #include <wtf/text/StringBuilder.h>
 
 namespace WebCore {
@@ -38,6 +40,12 @@
 
 static const Seconds taskDelayInterval { 100_ms };
 
+#undef LOGIDENTIFIER
+#define LOGIDENTIFIER __func__
+
+#undef ALWAYS_LOG
+#define ALWAYS_LOG logger().logAlways
+
 struct ClientState {
     WTF_MAKE_STRUCT_FAST_ALLOCATED;
 
@@ -66,56 +74,106 @@
     return value & flags;
 }
 
-#if !LOG_DISABLED
-static String mediaProducerStateString(MediaProducer::MediaStateFlags flags)
+String mediaProducerStateString(MediaProducer::MediaStateFlags flags)
 {
     StringBuilder string;
     if (flags & MediaProducer::IsPlayingAudio)
-        string.append("IsPlayingAudio + ");
+        string.append("IsPlayingAudio+");
     if (flags & MediaProducer::IsPlayingVideo)
-        string.append("IsPlayingVideo + ");
+        string.append("IsPlayingVideo+");
     if (flags & MediaProducer::IsPlayingToExternalDevice)
-        string.append("IsPlayingToExternalDevice + ");
+        string.append("IsPlayingToExternalDevice+");
     if (flags & MediaProducer::HasPlaybackTargetAvailabilityListener)
-        string.append("HasPlaybackTargetAvailabilityListener + ");
+        string.append("HasTargetAvailabilityListener+");
     if (flags & MediaProducer::RequiresPlaybackTargetMonitoring)
-        string.append("RequiresPlaybackTargetMonitoring + ");
+        string.append("RequiresTargetMonitoring+");
     if (flags & MediaProducer::ExternalDeviceAutoPlayCandidate)
-        string.append("ExternalDeviceAutoPlayCandidate + ");
+        string.append("ExternalDeviceAutoPlayCandidate+");
     if (flags & MediaProducer::DidPlayToEnd)
-        string.append("DidPlayToEnd + ");
+        string.append("DidPlayToEnd+");
     if (flags & MediaProducer::HasAudioOrVideo)
-        string.append("HasAudioOrVideo + ");
+        string.append("HasAudioOrVideo+");
     if (string.isEmpty())
         string.append("IsNotPlaying");
     else
-        string.resize(string.length() - 2);
+        string.resize(string.length() - 1);
 
-    return string.toString();
+    return makeString(" { ", string.toString(), " }");
 }
-#endif
 
+class WebMediaSessionLogger {
+    WTF_MAKE_NONCOPYABLE(WebMediaSessionLogger);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+
+    static std::unique_ptr<WebMediaSessionLogger> create(WebMediaSessionManager& manager)
+    {
+        return makeUnique<WebMediaSessionLogger>(manager);
+    }
+
+    template<typename... Arguments>
+    inline void logAlways(const char* methodName, ClientState* state, const Arguments&... arguments) const
+    {
+        if (!state->client.alwaysOnLoggingAllowed())
+            return;
+
+        m_logger->logAlways(LogMedia, makeString("WebMediaSessionManager::", methodName, ' '), state->contextId, state->flags, arguments...);
+    }
+
+    template<typename... Arguments>
+    inline void logAlways(const char* methodName, const Arguments&... arguments) const
+    {
+        if (!m_manager.alwaysOnLoggingAllowed())
+            return;
+
+        m_logger->logAlways(LogMedia, makeString("WebMediaSessionManager::", methodName, ' '), arguments...);
+    }
+
+private:
+    friend std::unique_ptr<WebMediaSessionLogger> std::make_unique<WebMediaSessionLogger>(WebMediaSessionManager&);
+    WebMediaSessionLogger(WebMediaSessionManager& manager)
+        : m_manager(manager)
+        , m_logger(Logger::create(this))
+    {
+    }
+
+    WebMediaSessionManager& m_manager;
+    Ref<Logger> m_logger;
+};
+
+WebMediaSessionLogger& WebMediaSessionManager::logger()
+{
+    if (!m_logger)
+        m_logger = WebMediaSessionLogger::create(*this);
+
+    return *m_logger;
+}
+
+bool WebMediaSessionManager::alwaysOnLoggingAllowed() const
+{
+    return allOf(m_clientState, [] (auto& state) {
+        return state->client.alwaysOnLoggingAllowed();
+    });
+}
+
 void WebMediaSessionManager::setMockMediaPlaybackTargetPickerEnabled(bool enabled)
 {
-    LOG(Media, "WebMediaSessionManager::setMockMediaPlaybackTargetPickerEnabled - enabled = %i", (int)enabled);
-
     if (m_mockPickerEnabled == enabled)
         return;
 
+    ALWAYS_LOG(LOGIDENTIFIER);
     m_mockPickerEnabled = enabled;
 }
 
 void WebMediaSessionManager::setMockMediaPlaybackTargetPickerState(const String& name, MediaPlaybackTargetContext::State state)
 {
-    LOG(Media, "WebMediaSessionManager::setMockMediaPlaybackTargetPickerState - name = %s, state = %i", name.utf8().data(), (int)state);
-
+    ALWAYS_LOG(LOGIDENTIFIER);
     mockPicker().setState(name, state);
 }
 
 void WebMediaSessionManager::mockMediaPlaybackTargetPickerDismissPopup()
 {
-    LOG(Media, "WebMediaSessionManager::mockMediaPlaybackTargetPickerDismissPopup");
-
+    ALWAYS_LOG(LOGIDENTIFIER);
     mockPicker().dismissPopup();
 }
 
@@ -150,8 +208,7 @@
     if (index != notFound)
         return 0;
 
-    LOG(Media, "WebMediaSessionManager::addPlaybackTargetPickerClient(%p + %llu)", &client, contextId);
-
+    ALWAYS_LOG(LOGIDENTIFIER, contextId);
     m_clientState.append(makeUnique<ClientState>(client, contextId));
 
     if (m_externalOutputDeviceAvailable || m_playbackTarget)
@@ -167,7 +224,7 @@
     if (index == notFound)
         return;
 
-    LOG(Media, "WebMediaSessionManager::removePlaybackTargetPickerClient(%p + %llu)", &client, contextId);
+    ALWAYS_LOG(LOGIDENTIFIER, m_clientState[index].get());
 
     m_clientState.remove(index);
     scheduleDelayedTask(TargetMonitoringConfigurationTask | TargetClientsConfigurationTask);
@@ -178,11 +235,11 @@
     if (m_clientState.isEmpty())
         return;
 
-    LOG(Media, "WebMediaSessionManager::removeAllPlaybackTargetPickerClients(%p)", &client);
-
     for (size_t i = m_clientState.size(); i > 0; --i) {
-        if (&m_clientState[i - 1]->client == &client)
+        if (&m_clientState[i - 1]->client == &client) {
+            ALWAYS_LOG(LOGIDENTIFIER, m_clientState[i - 1].get());
             m_clientState.remove(i - 1);
+        }
     }
     scheduleDelayedTask(TargetMonitoringConfigurationTask | TargetClientsConfigurationTask);
 }
@@ -200,8 +257,9 @@
         state->previouslyRequestedPicker = state == clientRequestingPicker;
     }
 
+    ALWAYS_LOG(LOGIDENTIFIER, m_clientState[index].get());
+
     bool hasActiveRoute = flagsAreSet(m_clientState[index]->flags, MediaProducer::IsPlayingToExternalDevice);
-    LOG(Media, "WebMediaSessionManager::showPlaybackTargetPicker(%p + %llu) - hasActiveRoute = %i", &client, contextId, (int)hasActiveRoute);
     targetPicker().showPlaybackTargetPicker(FloatRect(rect), hasActiveRoute, useDarkAppearance);
 }
 
@@ -217,7 +275,7 @@
     if (newFlags == oldFlags)
         return;
 
-    LOG(Media, "WebMediaSessionManager::clientStateDidChange(%p + %llu) - new flags = %s, old flags = %s", &client, contextId, mediaProducerStateString(newFlags).utf8().data(), mediaProducerStateString(oldFlags).utf8().data());
+    ALWAYS_LOG(LOGIDENTIFIER, m_clientState[index].get(), "new flags = ", newFlags);
 
     changedClientState->flags = newFlags;
 
@@ -240,20 +298,26 @@
         if (state == changedClientState)
             continue;
 
-        if (flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice) && flagsAreSet(state->flags, MediaProducer::IsPlayingVideo))
+        if (flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice) && flagsAreSet(state->flags, MediaProducer::IsPlayingVideo)) {
+            ALWAYS_LOG(LOGIDENTIFIER, state.get(), " returning early");
             return;
+        }
     }
 
     // Do not begin playing to the device unless playback has just started.
-    if (!flagsAreSet(newFlags, MediaProducer::IsPlayingVideo) || flagsAreSet(oldFlags, MediaProducer::IsPlayingVideo))
+    if (!flagsAreSet(newFlags, MediaProducer::IsPlayingVideo) || flagsAreSet(oldFlags, MediaProducer::IsPlayingVideo)) {
+        ALWAYS_LOG(LOGIDENTIFIER, "returning early, playback didn't just start");
         return;
+    }
 
     for (auto& state : m_clientState) {
         if (state == changedClientState)
             continue;
+        ALWAYS_LOG(LOGIDENTIFIER, state.get(), " calling setShouldPlayToPlaybackTarget(false)");
         state->client.setShouldPlayToPlaybackTarget(state->contextId, false);
     }
 
+    ALWAYS_LOG(LOGIDENTIFIER, changedClientState.get(), " calling setShouldPlayToPlaybackTarget(true)");
     changedClientState->client.setShouldPlayToPlaybackTarget(changedClientState->contextId, true);
 
     if (index && m_clientState.size() > 1)
@@ -262,6 +326,7 @@
 
 void WebMediaSessionManager::setPlaybackTarget(Ref<MediaPlaybackTarget>&& target)
 {
+    ALWAYS_LOG(LOGIDENTIFIER, "has active route = ", target->hasActiveRoute());
     m_playbackTarget = WTFMove(target);
     m_targetChanged = true;
     scheduleDelayedTask(TargetClientsConfigurationTask);
@@ -269,8 +334,7 @@
 
 void WebMediaSessionManager::externalOutputDeviceAvailableDidChange(bool available)
 {
-    LOG(Media, "WebMediaSessionManager::externalOutputDeviceAvailableDidChange - clients = %zu, available = %i", m_clientState.size(), (int)available);
-
+    ALWAYS_LOG(LOGIDENTIFIER, available);
     m_externalOutputDeviceAvailable = available;
     for (auto& state : m_clientState)
         state->client.externalOutputDeviceAvailableDidChange(state->contextId, available);
@@ -278,6 +342,7 @@
 
 void WebMediaSessionManager::playbackTargetPickerWasDismissed()
 {
+    ALWAYS_LOG(LOGIDENTIFIER);
     m_playbackTargetPickerDismissed = true;
     scheduleDelayedTask(TargetClientsConfigurationTask);
 }
@@ -310,7 +375,7 @@
     for (size_t i = 0; i < m_clientState.size(); ++i) {
         auto& state = m_clientState[i];
 
-        LOG(Media, "WebMediaSessionManager::configurePlaybackTargetClients %zu - client (%p + %llu) requestedPicker = %i, flags = %s", i, &state->client, state->contextId, state->requestedPicker, mediaProducerStateString(state->flags).utf8().data());
+        ALWAYS_LOG(LOGIDENTIFIER, state.get(), ", requestedPicker = ", state->requestedPicker);
 
         if ((m_targetChanged || m_playbackTargetPickerDismissed) && state->requestedPicker)
             indexOfClientThatRequestedPicker = i;
@@ -329,8 +394,6 @@
     if (indexOfClientWillPlayToTarget == notFound && haveActiveRoute && flagsAreSet(m_clientState[0]->flags, MediaProducer::ExternalDeviceAutoPlayCandidate) && !flagsAreSet(m_clientState[0]->flags, MediaProducer::IsPlayingVideo))
         indexOfClientWillPlayToTarget = 0;
 
-    LOG(Media, "WebMediaSessionManager::configurePlaybackTargetClients - indexOfClientWillPlayToTarget = %zu", indexOfClientWillPlayToTarget);
-
     for (size_t i = 0; i < m_clientState.size(); ++i) {
         auto& state = m_clientState[i];
 
@@ -337,21 +400,27 @@
         if (m_playbackTarget)
             state->client.setPlaybackTarget(state->contextId, *m_playbackTarget.copyRef());
 
-        if (i != indexOfClientWillPlayToTarget || !haveActiveRoute)
+        if (i != indexOfClientWillPlayToTarget || !haveActiveRoute) {
+            ALWAYS_LOG(LOGIDENTIFIER, state.get(), " calling setShouldPlayToPlaybackTarget(false)");
             state->client.setShouldPlayToPlaybackTarget(state->contextId, false);
+        }
 
-        if (state->requestedPicker && m_playbackTargetPickerDismissed)
+        if (state->requestedPicker && m_playbackTargetPickerDismissed) {
+            ALWAYS_LOG(LOGIDENTIFIER, state.get(), " calling playbackTargetPickerWasDismissed");
             state->client.playbackTargetPickerWasDismissed(state->contextId);
+        }
 
         state->configurationRequired = false;
-        if ((m_targetChanged || m_playbackTargetPickerDismissed))
+        if (m_targetChanged || m_playbackTargetPickerDismissed)
             state->requestedPicker = false;
     }
 
     if (haveActiveRoute && indexOfClientWillPlayToTarget != notFound) {
         auto& state = m_clientState[indexOfClientWillPlayToTarget];
-        if (!flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice))
+        if (!flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice)) {
+            ALWAYS_LOG(LOGIDENTIFIER, state.get(), " calling setShouldPlayToPlaybackTarget(true)");
             state->client.setShouldPlayToPlaybackTarget(state->contextId, true);
+        }
     }
 
     m_targetChanged = false;
@@ -364,6 +433,7 @@
     bool hasAvailabilityListener = false;
     bool haveClientWithMedia = false;
     for (auto& state : m_clientState) {
+        ALWAYS_LOG(LOGIDENTIFIER, state.get());
         if (state->flags & MediaProducer::RequiresPlaybackTargetMonitoring) {
             monitoringRequired = true;
             break;
@@ -374,39 +444,17 @@
             haveClientWithMedia = true;
     }
 
-    LOG(Media, "WebMediaSessionManager::configurePlaybackTargetMonitoring - monitoringRequired = %i", static_cast<int>(monitoringRequired || (hasAvailabilityListener && haveClientWithMedia)));
-
-    if (monitoringRequired || (hasAvailabilityListener && haveClientWithMedia))
+    if (monitoringRequired || (hasAvailabilityListener && haveClientWithMedia)) {
+        ALWAYS_LOG(LOGIDENTIFIER, "starting monitoring");
         targetPicker().startingMonitoringPlaybackTargets();
-    else
+    } else {
+        ALWAYS_LOG(LOGIDENTIFIER, "stopping monitoring");
         targetPicker().stopMonitoringPlaybackTargets();
+    }
 }
 
-#if !LOG_DISABLED
-String WebMediaSessionManager::toString(ConfigurationTasks tasks)
-{
-    StringBuilder string;
-    if (tasks & InitialConfigurationTask)
-        string.append("InitialConfigurationTask + ");
-    if (tasks & TargetClientsConfigurationTask)
-        string.append("TargetClientsConfigurationTask + ");
-    if (tasks & TargetMonitoringConfigurationTask)
-        string.append("TargetMonitoringConfigurationTask + ");
-    if (tasks & WatchdogTimerConfigurationTask)
-        string.append("WatchdogTimerConfigurationTask + ");
-    if (string.isEmpty())
-        string.append("NoTask");
-    else
-        string.resize(string.length() - 2);
-    
-    return string.toString();
-}
-#endif
-
 void WebMediaSessionManager::scheduleDelayedTask(ConfigurationTasks tasks)
 {
-    LOG(Media, "WebMediaSessionManager::scheduleDelayedTask - %s", toString(tasks).utf8().data());
-
     m_taskFlags |= tasks;
     m_taskTimer.startOneShot(taskDelayInterval);
 }
@@ -413,8 +461,6 @@
 
 void WebMediaSessionManager::taskTimerFired()
 {
-    LOG(Media, "WebMediaSessionManager::taskTimerFired - tasks = %s", toString(m_taskFlags).utf8().data());
-
     if (m_taskFlags & InitialConfigurationTask)
         configureNewClients();
     if (m_taskFlags & TargetClientsConfigurationTask)
@@ -443,7 +489,12 @@
     static const Seconds watchdogTimerIntervalAfterPlayingToEnd { 8_min };
 
     if (!m_playbackTarget || !m_playbackTarget->hasActiveRoute()) {
-        m_watchdogTimer.stop();
+        if (m_watchdogTimer.isActive()) {
+            ALWAYS_LOG(LOGIDENTIFIER, "stopping timer");
+            m_currentWatchdogInterval = { };
+            m_watchdogTimer.stop();
+        }
+
         return;
     }
 
@@ -450,6 +501,9 @@
     bool stopTimer = false;
     bool didPlayToEnd = false;
     for (auto& state : m_clientState) {
+
+        ALWAYS_LOG(LOGIDENTIFIER, state.get(), " playedToEnd = ", state->playedToEnd);
+
         if (flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice) && flagsAreSet(state->flags, MediaProducer::IsPlayingVideo))
             stopTimer = true;
         if (state->playedToEnd)
@@ -458,15 +512,15 @@
     }
 
     if (stopTimer) {
+        ALWAYS_LOG(LOGIDENTIFIER, "stopping timer");
         m_currentWatchdogInterval = { };
         m_watchdogTimer.stop();
-        LOG(Media, "WebMediaSessionManager::configureWatchdogTimer - timer stopped");
     } else {
         Seconds interval = didPlayToEnd ? watchdogTimerIntervalAfterPlayingToEnd : watchdogTimerIntervalAfterPausing;
         if (interval != m_currentWatchdogInterval || !m_watchdogTimer.isActive()) {
             m_watchdogTimer.startOneShot(interval);
-            LOG(Media, "WebMediaSessionManager::configureWatchdogTimer - timer scheduled for %.0f seconds", interval.value());
         }
+        ALWAYS_LOG(LOGIDENTIFIER, "timer scheduled for ", interval.value(), " seconds");
         m_currentWatchdogInterval = interval;
     }
 }
@@ -473,10 +527,10 @@
 
 void WebMediaSessionManager::watchdogTimerFired()
 {
-    LOG(Media, "WebMediaSessionManager::watchdogTimerFired");
     if (!m_playbackTarget)
         return;
 
+    ALWAYS_LOG(LOGIDENTIFIER);
     targetPicker().invalidatePlaybackTargets();
 }
 

Modified: trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h (255580 => 255581)


--- trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h	2020-02-03 20:19:44 UTC (rev 255581)
@@ -39,6 +39,7 @@
 
 struct ClientState;
 class IntRect;
+class WebMediaSessionLogger;
 class WebMediaSessionManagerClient;
 
 class WebMediaSessionManager : public MediaPlaybackTargetPicker::Client {
@@ -57,6 +58,8 @@
     WEBCORE_EXPORT void showPlaybackTargetPicker(WebMediaSessionManagerClient&, uint64_t, const IntRect&, bool, bool);
     WEBCORE_EXPORT void clientStateDidChange(WebMediaSessionManagerClient&, uint64_t, WebCore::MediaProducer::MediaStateFlags);
 
+    bool alwaysOnLoggingAllowed() const;
+
 protected:
     WebMediaSessionManager();
     virtual ~WebMediaSessionManager();
@@ -96,6 +99,8 @@
 
     void watchdogTimerFired();
 
+    WebMediaSessionLogger& logger();
+
     RunLoop::Timer<WebMediaSessionManager> m_taskTimer;
     RunLoop::Timer<WebMediaSessionManager> m_watchdogTimer;
 
@@ -103,6 +108,7 @@
     RefPtr<MediaPlaybackTarget> m_playbackTarget;
     std::unique_ptr<WebCore::MediaPlaybackTargetPickerMock> m_pickerOverride;
     ConfigurationTasks m_taskFlags { NoTask };
+    std::unique_ptr<WebMediaSessionLogger> m_logger;
     Seconds m_currentWatchdogInterval;
     bool m_externalOutputDeviceAvailable { false };
     bool m_targetChanged { false };
@@ -110,6 +116,18 @@
     bool m_mockPickerEnabled { false };
 };
 
+String mediaProducerStateString(WebCore::MediaProducer::MediaStateFlags);
+
 } // namespace WebCore
 
+namespace WTF {
+
+template<typename> struct LogArgument;
+
+template<> struct LogArgument<WebCore::MediaProducer::MediaStateFlags> {
+    static String toString(WebCore::MediaProducer::MediaStateFlags flags) { return WebCore::mediaProducerStateString(flags); }
+};
+
+} // namespace WTF
+
 #endif // ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h (255580 => 255581)


--- trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h	2020-02-03 20:19:44 UTC (rev 255581)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,6 +41,7 @@
     virtual void externalOutputDeviceAvailableDidChange(uint64_t, bool) = 0;
     virtual void setShouldPlayToPlaybackTarget(uint64_t, bool) = 0;
     virtual void playbackTargetPickerWasDismissed(uint64_t) = 0;
+    virtual bool alwaysOnLoggingAllowed() { return false; }
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h (255580 => 255581)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h	2020-02-03 20:19:44 UTC (rev 255581)
@@ -301,6 +301,8 @@
 #endif
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
+    bool hasVideo() const final;
+    bool hasAudio() const final;
     bool isCurrentPlaybackTargetWireless() const override;
     String wirelessPlaybackTargetName() const override;
     MediaPlayer::WirelessPlaybackTargetType wirelessPlaybackTargetType() const override;

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm (255580 => 255581)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm	2020-02-03 20:19:44 UTC (rev 255581)
@@ -2621,6 +2621,27 @@
 }
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
+// AVPlayerItem.tracks is empty during AirPlay so if AirPlay is activated immediately
+// after the item is created, we don't know if it has audio or video and state reported
+// to the WebMediaSessionManager is incorrect. AirPlay can't actually be active is an item
+// doesn't have audio or video, so lie during AirPlay.
+
+bool MediaPlayerPrivateAVFoundationObjC::hasVideo() const
+{
+    if (isCurrentPlaybackTargetWireless())
+        return true;
+
+    return MediaPlayerPrivateAVFoundation::hasVideo();
+}
+
+bool MediaPlayerPrivateAVFoundationObjC::hasAudio() const
+{
+    if (isCurrentPlaybackTargetWireless())
+        return true;
+
+    return MediaPlayerPrivateAVFoundation::hasAudio();
+}
+
 bool MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless() const
 {
     bool wirelessTarget = false;

Modified: trunk/Source/WebKit/ChangeLog (255580 => 255581)


--- trunk/Source/WebKit/ChangeLog	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebKit/ChangeLog	2020-02-03 20:19:44 UTC (rev 255581)
@@ -1,3 +1,13 @@
+2020-02-03  Eric Carlson  <eric.carl...@apple.com>
+
+        [macOS] AirPlay sometimes stops after 60 minutes of playback
+        https://bugs.webkit.org/show_bug.cgi?id=207056
+
+        Reviewed by Jer Noble.
+
+        * UIProcess/WebPageProxy.cpp:
+        * UIProcess/WebPageProxy.h:
+
 2020-02-03  Sam Weinig  <wei...@apple.com>
 
         Start splitting platform specific overrides of default enable behavior into their own files

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (255580 => 255581)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-02-03 19:41:41 UTC (rev 255580)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-02-03 20:19:44 UTC (rev 255581)
@@ -1474,10 +1474,11 @@
     void mockMediaPlaybackTargetPickerDismissPopup();
 
     // WebMediaSessionManagerClient
-    void setPlaybackTarget(uint64_t, Ref<WebCore::MediaPlaybackTarget>&&) override;
-    void externalOutputDeviceAvailableDidChange(uint64_t, bool) override;
-    void setShouldPlayToPlaybackTarget(uint64_t, bool) override;
-    void playbackTargetPickerWasDismissed(uint64_t) override;
+    void setPlaybackTarget(uint64_t, Ref<WebCore::MediaPlaybackTarget>&&) final;
+    void externalOutputDeviceAvailableDidChange(uint64_t, bool) final;
+    void setShouldPlayToPlaybackTarget(uint64_t, bool) final;
+    void playbackTargetPickerWasDismissed(uint64_t) final;
+    bool alwaysOnLoggingAllowed() final { return isAlwaysOnLoggingAllowed(); }
 #endif
 
     void didChangeBackgroundColor();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to