Changeset 186361 in webkit


Ignore:
Timestamp:
Jul 6, 2015 11:04:10 AM (9 years ago)
Author:
eric.carlson@apple.com
Message:

[Mac] Inactive AirPlay route should automatically timeout
https://bugs.webkit.org/show_bug.cgi?id=146642
<rdar://problem/21602955>

Automatically clear a media element's AirPlay connection after it has been paused
for 60 minutes, or after 8 minutes if it played to the end before pausing.

Reviewed by Brent Fulgham.

  • Modules/mediasession/WebMediaSessionManager.cpp:

(WebCore::WebMediaSessionManager::WebMediaSessionManager): Initialize m_watchdogTimer.
(WebCore::WebMediaSessionManager::clientStateDidChange): Schedule watchdog timer configuration

if the client started playing or paused.

(WebCore::WebMediaSessionManager::configurePlaybackTargetClients): Schedule watchdog timer configuration.
(WebCore::WebMediaSessionManager::toString): Print watchdog configuration flag.
(WebCore::WebMediaSessionManager::taskTimerFired): Call configureWatchdogTimer.
(WebCore::WebMediaSessionManager::configureWatchdogTimer): New, start or stop watchdog timer.
(WebCore::WebMediaSessionManager::watchdogTimerFired): Stop monitoring for targets, which

clears the route.

  • Modules/mediasession/WebMediaSessionManager.h:
  • html/HTMLMediaElement.cpp:

(WebCore::HTMLMediaElement::mediaState): Set DidPlayToEnd when appropriate.

  • page/MediaProducer.h: Add DidPlayToEnd.
  • platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm:

(WebCore::MediaPlaybackTargetPickerMac::~MediaPlaybackTargetPickerMac): Clear m_client, call

stopMonitoringPlaybackTargets.

(WebCore::MediaPlaybackTargetPickerMac::pendingActionTimerFired): Send a neutered

MediaPlaybackTarget when m_outputDeviceMenuController is NULL.

(WebCore::MediaPlaybackTargetPickerMac::devicePicker): Add logging.
(WebCore::MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets): Clear the menu

controller to cancel the route.

Location:
trunk/Source/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r186330 r186361  
     12015-07-06  Eric Carlson  <eric.carlson@apple.com>
     2
     3        [Mac] Inactive AirPlay route should automatically timeout
     4        https://bugs.webkit.org/show_bug.cgi?id=146642
     5        <rdar://problem/21602955>
     6
     7        Automatically clear a media element's AirPlay connection after it has been paused
     8        for 60 minutes, or after 8 minutes if it played to the end before pausing.
     9
     10        Reviewed by Brent Fulgham.
     11
     12        * Modules/mediasession/WebMediaSessionManager.cpp:
     13        (WebCore::WebMediaSessionManager::WebMediaSessionManager): Initialize m_watchdogTimer.
     14        (WebCore::WebMediaSessionManager::clientStateDidChange): Schedule watchdog timer configuration
     15          if the client started playing or paused.
     16        (WebCore::WebMediaSessionManager::configurePlaybackTargetClients): Schedule watchdog timer configuration.
     17        (WebCore::WebMediaSessionManager::toString): Print watchdog configuration flag.
     18        (WebCore::WebMediaSessionManager::taskTimerFired): Call configureWatchdogTimer.
     19        (WebCore::WebMediaSessionManager::configureWatchdogTimer): New, start or stop watchdog timer.
     20        (WebCore::WebMediaSessionManager::watchdogTimerFired): Stop monitoring for targets, which
     21          clears the route.
     22        * Modules/mediasession/WebMediaSessionManager.h:
     23
     24        * html/HTMLMediaElement.cpp:
     25        (WebCore::HTMLMediaElement::mediaState): Set DidPlayToEnd when appropriate.
     26
     27        * page/MediaProducer.h: Add DidPlayToEnd.
     28
     29        * platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm:
     30        (WebCore::MediaPlaybackTargetPickerMac::~MediaPlaybackTargetPickerMac): Clear m_client, call
     31          stopMonitoringPlaybackTargets.
     32        (WebCore::MediaPlaybackTargetPickerMac::pendingActionTimerFired): Send a neutered
     33          MediaPlaybackTarget when m_outputDeviceMenuController is NULL.
     34        (WebCore::MediaPlaybackTargetPickerMac::devicePicker): Add logging.
     35        (WebCore::MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets): Clear the menu
     36          controller to cancel the route.
     37
    1382015-07-06  Zan Dobersek  <zdobersek@igalia.com>
    239
  • trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp

    r186238 r186361  
    5656    bool requestedPicker { false };
    5757    bool configurationRequired { true };
     58    bool playedToEnd { false };
    5859};
    5960
     
    8889WebMediaSessionManager::WebMediaSessionManager()
    8990    : m_taskTimer(RunLoop::current(), this, &WebMediaSessionManager::taskTimerFired)
     91    , m_watchdogTimer(RunLoop::current(), this, &WebMediaSessionManager::watchdogTimerFired)
    9092{
    9193}
     
    171173    if (!flagsAreSet(oldFlags, MediaProducer::RequiresPlaybackTargetMonitoring) && flagsAreSet(newFlags, MediaProducer::RequiresPlaybackTargetMonitoring))
    172174        scheduleDelayedTask(TargetMonitoringConfigurationTask);
     175
     176    MediaProducer::MediaStateFlags playingToTargetFlags = MediaProducer::IsPlayingToExternalDevice | MediaProducer::IsPlayingVideo;
     177    if ((oldFlags & playingToTargetFlags) != (newFlags & playingToTargetFlags)) {
     178        if (flagsAreSet(oldFlags, MediaProducer::IsPlayingVideo) && !flagsAreSet(newFlags, MediaProducer::IsPlayingVideo) && flagsAreSet(newFlags, MediaProducer::DidPlayToEnd))
     179            changedClientState->playedToEnd = true;
     180        scheduleDelayedTask(WatchdogTimerConfigurationTask);
     181    }
    173182
    174183    if (!m_playbackTarget || !m_playbackTarget->hasActiveRoute())
     
    277286            state->client.setShouldPlayToPlaybackTarget(state->contextId, true);
    278287    }
     288
     289    configureWatchdogTimer();
    279290}
    280291
     
    307318    if (tasks & TargetMonitoringConfigurationTask)
    308319        string.append("TargetMonitoringConfigurationTask + ");
     320    if (tasks & WatchdogTimerConfigurationTask)
     321        string.append("WatchdogTimerConfigurationTask + ");
    309322    if (string.isEmpty())
    310323        string.append("NoTask");
     
    334347    if (m_taskFlags & TargetMonitoringConfigurationTask)
    335348        configurePlaybackTargetMonitoring();
     349    if (m_taskFlags & WatchdogTimerConfigurationTask)
     350        configureWatchdogTimer();
    336351
    337352    m_taskFlags = NoTask;
     
    348363}
    349364
     365void WebMediaSessionManager::configureWatchdogTimer()
     366{
     367    static const double watchdogTimerIntervalAfterPausing = 60 * 60;
     368    static const double watchdogTimerIntervalAfterPlayingToEnd = 8 * 60;
     369
     370    if (!m_playbackTarget || !m_playbackTarget->hasActiveRoute()) {
     371        m_watchdogTimer.stop();
     372        return;
     373    }
     374
     375    bool stopTimer = false;
     376    bool didPlayToEnd = false;
     377    for (auto& state : m_clientState) {
     378        if (flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice) && flagsAreSet(state->flags, MediaProducer::IsPlayingVideo))
     379            stopTimer = true;
     380        if (state->playedToEnd)
     381            didPlayToEnd = true;
     382        state->playedToEnd = false;
     383    }
     384
     385    if (stopTimer) {
     386        m_currentWatchdogInterval = 0;
     387        m_watchdogTimer.stop();
     388        LOG(Media, "WebMediaSessionManager::configureWatchdogTimer - timer stopped");
     389    } else {
     390        double interval = didPlayToEnd ? watchdogTimerIntervalAfterPlayingToEnd : watchdogTimerIntervalAfterPausing;
     391        if (interval != m_currentWatchdogInterval || !m_watchdogTimer.isActive()) {
     392            m_watchdogTimer.startOneShot(interval);
     393            LOG(Media, "WebMediaSessionManager::configureWatchdogTimer - timer scheduled for %.0f", interval);
     394        }
     395        m_currentWatchdogInterval = interval;
     396    }
     397}
     398
     399void WebMediaSessionManager::watchdogTimerFired()
     400{
     401    LOG(Media, "WebMediaSessionManager::watchdogTimerFired");
     402    if (!m_playbackTarget)
     403        return;
     404
     405    targetPicker().stopMonitoringPlaybackTargets();
     406}
     407
    350408} // namespace WebCore
    351409
  • trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h

    r185519 r186361  
    6868    void configureNewClients();
    6969    void configurePlaybackTargetMonitoring();
     70    void configureWatchdogTimer();
    7071
    7172    enum ConfigurationTaskFlags {
     
    7475        TargetClientsConfigurationTask = 1 << 1,
    7576        TargetMonitoringConfigurationTask = 1 << 2,
     77        WatchdogTimerConfigurationTask = 1 << 3,
    7678    };
    7779    typedef unsigned ConfigurationTasks;
     
    8082    void scheduleDelayedTask(ConfigurationTasks);
    8183    void taskTimerFired();
     84
     85    void watchdogTimerFired();
     86
    8287    RunLoop::Timer<WebMediaSessionManager> m_taskTimer;
     88    RunLoop::Timer<WebMediaSessionManager> m_watchdogTimer;
    8389
    8490    Vector<std::unique_ptr<ClientState>> m_clientState;
    8591    RefPtr<MediaPlaybackTarget> m_playbackTarget;
    8692    ConfigurationTasks m_taskFlags { NoTask };
     93    double m_currentWatchdogInterval { 0 };
    8794    bool m_externalOutputDeviceAvailable { false };
    8895};
  • trunk/Source/WebCore/html/HTMLMediaElement.cpp

    r186279 r186361  
    63736373    if (hasActiveVideo && (!requireUserGesture || (hasAudio && !m_initiallyMuted && !loop())) && !m_failedToPlayToWirelessTarget)
    63746374        state |= ExternalDeviceAutoPlayCandidate;
     6375
     6376    if (hasActiveVideo && endedPlayback())
     6377        state |= DidPlayToEnd;
    63756378#endif
    63766379
  • trunk/Source/WebCore/page/MediaProducer.h

    r183613 r186361  
    3838        RequiresPlaybackTargetMonitoring = 1 << 3,
    3939        ExternalDeviceAutoPlayCandidate = 1 << 4,
     40        DidPlayToEnd = 1 << 5,
    4041    };
    4142    typedef unsigned MediaStateFlags;
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm

    r184788 r186361  
    8484MediaPlaybackTargetPickerMac::~MediaPlaybackTargetPickerMac()
    8585{
     86    m_client = nullptr;
    8687    m_pendingActionTimer.stop();
    8788    [m_outputDeviceMenuControllerDelegate clearCallback];
     89
     90    stopMonitoringPlaybackTargets();
     91}
     92
     93void MediaPlaybackTargetPickerMac::pendingActionTimerFired()
     94{
     95    LOG(Media, "MediaPlaybackTargetPickerMac::pendingActionTimerFired - flags = 0x%x", m_pendingActionFlags);
     96
     97    if (!m_client)
     98        return;
     99
     100    PendingActionFlags pendingActions = m_pendingActionFlags;
     101    m_pendingActionFlags = 0;
     102
     103    if (pendingActions & CurrentDeviceDidChange) {
     104        AVOutputContext* context = m_outputDeviceMenuController ? [m_outputDeviceMenuController.get() outputContext] : nullptr;
     105        m_client->setPlaybackTarget(WebCore::MediaPlaybackTargetMac::create(context));
     106    }
     107
     108    if (pendingActions & OutputDeviceAvailabilityChanged)
     109        m_client->externalOutputDeviceAvailableDidChange(devicePicker().externalOutputDeviceAvailable);
     110
     111}
     112
     113void MediaPlaybackTargetPickerMac::availableDevicesDidChange()
     114{
     115    LOG(Media, "MediaPlaybackTargetPickerMac::availableDevicesDidChange - available = %i", (int)devicePicker().externalOutputDeviceAvailable);
     116
     117    if (!m_client)
     118        return;
     119
     120    addPendingAction(OutputDeviceAvailabilityChanged);
     121}
     122
     123AVOutputDeviceMenuControllerType *MediaPlaybackTargetPickerMac::devicePicker()
     124{
     125    if (!getAVOutputDeviceMenuControllerClass())
     126        return nullptr;
     127
     128    if (!m_outputDeviceMenuController) {
     129        LOG(Media, "MediaPlaybackTargetPickerMac::devicePicker - allocating picker");
     130
     131        RetainPtr<AVOutputContextType> context = adoptNS([[getAVOutputContextClass() alloc] init]);
     132        m_outputDeviceMenuController = adoptNS([[getAVOutputDeviceMenuControllerClass() alloc] initWithOutputContext:context.get()]);
     133
     134        [m_outputDeviceMenuController.get() addObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDeviceAvailableKeyName options:NSKeyValueObservingOptionNew context:nullptr];
     135        [m_outputDeviceMenuController.get() addObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDevicePickedKeyName options:NSKeyValueObservingOptionNew context:nullptr];
     136
     137        LOG(Media, "MediaPlaybackTargetPickerMac::devicePicker - allocated menu controller %p", m_outputDeviceMenuController.get());
     138
     139        if (m_outputDeviceMenuController.get().externalOutputDeviceAvailable)
     140            availableDevicesDidChange();
     141    }
     142
     143    return m_outputDeviceMenuController.get();
     144}
     145
     146void MediaPlaybackTargetPickerMac::showPlaybackTargetPicker(const FloatRect& location, bool checkActiveRoute)
     147{
     148    if (!m_client || m_showingMenu)
     149        return;
     150
     151    LOG(Media, "MediaPlaybackTargetPickerMac::showPlaybackTargetPicker - checkActiveRoute = %i", (int)checkActiveRoute);
     152
     153    AVOutputDeviceMenuControllerType *picker = devicePicker();
     154    if (![picker respondsToSelector:@selector(showMenuForRect:appearanceName:allowReselectionOfSelectedOutputDevice:)])
     155        return;
     156
     157    m_showingMenu = true;
     158    if ([picker showMenuForRect:location appearanceName:NSAppearanceNameVibrantLight allowReselectionOfSelectedOutputDevice:!checkActiveRoute]) {
     159        if (!checkActiveRoute)
     160            currentDeviceDidChange();
     161    }
     162    m_showingMenu = false;
     163}
     164
     165void MediaPlaybackTargetPickerMac::addPendingAction(PendingActionFlags action)
     166{
     167    m_pendingActionFlags |= action;
     168    m_pendingActionTimer.startOneShot(pendingActionInterval);
     169}
     170
     171void MediaPlaybackTargetPickerMac::currentDeviceDidChange()
     172{
     173    LOG(Media, "MediaPlaybackTargetPickerMac::currentDeviceDidChange");
     174
     175    if (!m_client)
     176        return;
     177
     178    addPendingAction(CurrentDeviceDidChange);
     179}
     180
     181void MediaPlaybackTargetPickerMac::startingMonitoringPlaybackTargets()
     182{
     183    LOG(Media, "MediaPlaybackTargetPickerMac::startingMonitoringPlaybackTargets");
     184
     185    devicePicker();
     186}
     187
     188void MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets()
     189{
     190    LOG(Media, "MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets");
    88191
    89192    if (m_outputDeviceMenuController) {
     
    92195        m_outputDeviceMenuController = nullptr;
    93196    }
    94 }
    95 
    96 void MediaPlaybackTargetPickerMac::pendingActionTimerFired()
    97 {
    98     LOG(Media, "MediaPlaybackTargetPickerMac::pendingActionTimerFired - flags = 0x%x", m_pendingActionFlags);
    99 
    100     if (!m_outputDeviceMenuController || !m_client)
    101         return;
    102 
    103     PendingActionFlags pendingActions = m_pendingActionFlags;
    104     m_pendingActionFlags = 0;
    105 
    106     if (pendingActions & OutputDeviceAvailabilityChanged)
    107         m_client->externalOutputDeviceAvailableDidChange(devicePicker().externalOutputDeviceAvailable);
    108 
    109     if (pendingActions & CurrentDeviceDidChange) {
    110         AVOutputDeviceMenuControllerType* devicePicker = this->devicePicker();
    111         if (devicePicker)
    112             m_client->setPlaybackTarget(WebCore::MediaPlaybackTargetMac::create([devicePicker outputContext]));
    113     }
    114 }
    115 
    116 void MediaPlaybackTargetPickerMac::availableDevicesDidChange()
    117 {
    118     LOG(Media, "MediaPlaybackTargetPickerMac::availableDevicesDidChange - available = %i", (int)devicePicker().externalOutputDeviceAvailable);
    119 
    120     if (!m_client)
    121         return;
    122 
    123     addPendingAction(OutputDeviceAvailabilityChanged);
    124 }
    125 
    126 AVOutputDeviceMenuControllerType *MediaPlaybackTargetPickerMac::devicePicker()
    127 {
    128     if (!getAVOutputDeviceMenuControllerClass())
    129         return nullptr;
    130 
    131     if (!m_outputDeviceMenuController) {
    132         RetainPtr<AVOutputContextType> context = adoptNS([[getAVOutputContextClass() alloc] init]);
    133         m_outputDeviceMenuController = adoptNS([[getAVOutputDeviceMenuControllerClass() alloc] initWithOutputContext:context.get()]);
    134 
    135         [m_outputDeviceMenuController.get() addObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDeviceAvailableKeyName options:NSKeyValueObservingOptionNew context:nullptr];
    136         [m_outputDeviceMenuController.get() addObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDevicePickedKeyName options:NSKeyValueObservingOptionNew context:nullptr];
    137 
    138         LOG(Media, "MediaPlaybackTargetPickerMac::devicePicker - allocated menu controller %p", m_outputDeviceMenuController.get());
    139 
    140         if (m_outputDeviceMenuController.get().externalOutputDeviceAvailable)
    141             availableDevicesDidChange();
    142     }
    143 
    144     return m_outputDeviceMenuController.get();
    145 }
    146 
    147 void MediaPlaybackTargetPickerMac::showPlaybackTargetPicker(const FloatRect& location, bool checkActiveRoute)
    148 {
    149     if (!m_client || m_showingMenu)
    150         return;
    151 
    152     LOG(Media, "MediaPlaybackTargetPickerMac::showPlaybackTargetPicker - checkActiveRoute = %i", (int)checkActiveRoute);
    153 
    154     AVOutputDeviceMenuControllerType *picker = devicePicker();
    155     if (![picker respondsToSelector:@selector(showMenuForRect:appearanceName:allowReselectionOfSelectedOutputDevice:)])
    156         return;
    157 
    158     m_showingMenu = true;
    159     if ([picker showMenuForRect:location appearanceName:NSAppearanceNameVibrantLight allowReselectionOfSelectedOutputDevice:!checkActiveRoute]) {
    160         if (!checkActiveRoute)
    161             currentDeviceDidChange();
    162     }
    163     m_showingMenu = false;
    164 }
    165 
    166 void MediaPlaybackTargetPickerMac::addPendingAction(PendingActionFlags action)
    167 {
    168     m_pendingActionFlags |= action;
    169     m_pendingActionTimer.startOneShot(pendingActionInterval);
    170 }
    171 
    172 void MediaPlaybackTargetPickerMac::currentDeviceDidChange()
    173 {
    174     LOG(Media, "MediaPlaybackTargetPickerMac::currentDeviceDidChange");
    175 
    176     if (!m_client)
    177         return;
    178 
    179     addPendingAction(CurrentDeviceDidChange);
    180 }
    181 
    182 void MediaPlaybackTargetPickerMac::startingMonitoringPlaybackTargets()
    183 {
    184     LOG(Media, "MediaPlaybackTargetPickerMac::startingMonitoringPlaybackTargets");
    185 
    186     devicePicker();
    187 }
    188 
    189 void MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets()
    190 {
    191     LOG(Media, "MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets");
    192 
    193     // FIXME: update once rdar://21062536 has been fixed.
     197    currentDeviceDidChange();
    194198}
    195199
Note: See TracChangeset for help on using the changeset viewer.