Skip to content

Commit

Permalink
[iOS] MediaSessionManager continues to monitor wireless targets when …
Browse files Browse the repository at this point in the history
…a page is no longer visible

https://bugs.webkit.org/show_bug.cgi?id=259084
rdar://107220858

Reviewed by Jer Noble.

When a page contains a visible media element with a playback target availability listener (e.g.,
webkitplaybacktargetavailabilitychanged), MediaSessionManager starts to monitor for playback
targets. On iOS, this is implemented by asking AVRouteDetector to monitor for AirPlay routes,
increasing the system's power consumption.

MediaElementSession attempted to stop monitoring when its HTML element becomes invisible, but this
only occurred when m_clientDataBufferingTimer fired. However, while this timer was always scheduled
when the media element became invisible, in some cases it would be stopped before firing by code
calling updateClientDataBuffering() directly. In these cases MediaSessionManager would not be told
to stop monitoring target availability, increasing the system's power consumption for an arbitrary
amount of time.

Fixed this by calling PlatformMediaSessionManager::configureWirelessTargetMonitoring() whenever
MediaElementSession::updateClientDataBuffering() is called, whether directly or by
m_clientDataBufferingTimer firing. While here, also renamed configureWireLessTargetMonitoring to
configureWirelessTargetMonitoring.

* LayoutTests/TestExpectations: Skipped wireless-route-monitoring.html.
* LayoutTests/media/wireless-route-monitoring-expected.txt: Added.
* LayoutTests/media/wireless-route-monitoring.html: Added.
* LayoutTests/platform/ios/TestExpectations: Expected wireless-route-monitoring.html to pass on iOS.
* Source/WebCore/html/MediaElementSession.cpp:
(WebCore::MediaElementSession::clientDataBufferingTimerFired):
(WebCore::MediaElementSession::updateClientDataBuffering):
(WebCore::MediaElementSession::setHasPlaybackTargetAvailabilityListeners):
* Source/WebCore/platform/audio/PlatformMediaSessionManager.h:
(WebCore::PlatformMediaSessionManager::configureWirelessTargetMonitoring):
(WebCore::PlatformMediaSessionManager::isMonitoringWirelessTargets const):
(WebCore::PlatformMediaSessionManager::configureWireLessTargetMonitoring): Renamed to configureWirelessTargetMonitoring.
* Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h:
* Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm:
(WebCore::MediaSessionManageriOS::isMonitoringWirelessTargets const):
(WebCore::MediaSessionManageriOS::configureWirelessTargetMonitoring):
(WebCore::MediaSessionManageriOS::configureWireLessTargetMonitoring): Renamed to configureWirelessTargetMonitoring.
* Source/WebCore/testing/Internals.cpp:
(WebCore::Internals::suspendAllMediaBuffering):
(WebCore::Internals::isMonitoringWirelessRoutes const):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:

Canonical link: https://commits.webkit.org/265965@main
  • Loading branch information
aestes committed Jul 11, 2023
1 parent 62ac852 commit ecb0f69
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 14 deletions.
1 change: 1 addition & 0 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ media/encrypted-media/encrypted-media-append-encrypted-unencrypted.html [ Skip ]
media/media-source/media-source-append-play-corrupt-aac.html [ Skip ]
media/modern-media-controls/ios-inline-media-controls/touch [ Skip ]
media/modern-media-controls/overflow-support [ Skip ]
media/wireless-route-monitoring.html [ Skip ]
http/tests/media/modern-media-controls/overflow-support [ Skip ]
media/modern-media-controls/tracks-support [ Skip ]
fast/text-autosizing [ Skip ]
Expand Down
10 changes: 10 additions & 0 deletions LayoutTests/media/wireless-route-monitoring-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

EXPECTED (internals.isMonitoringWirelessRoutes == 'false') OK
RUN(video.src = findMediaFile("video", "content/test"))
EVENT(canplaythrough)
EXPECTED (internals.isMonitoringWirelessRoutes == 'true') OK
RUN(internals.setPageVisibility(false))
RUN(internals.suspendAllMediaBuffering())
EXPECTED (internals.isMonitoringWirelessRoutes == 'false') OK
END OF TEST

36 changes: 36 additions & 0 deletions LayoutTests/media/wireless-route-monitoring.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<script src=media-file.js></script>
<script src=video-test.js></script>
<script>
window.addEventListener('load', async event => {
if (!window.internals) {
failTest('This test requires window.internals.');
return;
}

findMediaElement();

testExpected('internals.isMonitoringWirelessRoutes', false);

video.addEventListener('webkitplaybacktargetavailabilitychanged', event => {});
run('video.src = findMediaFile("video", "content/test")');
await waitFor(video, 'canplaythrough');

testExpected('internals.isMonitoringWirelessRoutes', true);

run('internals.setPageVisibility(false)');
run('internals.suspendAllMediaBuffering()');

testExpected('internals.isMonitoringWirelessRoutes', false);

endTest();
});
</script>
</head>

<body>
<video></video>
</body>
</html>
1 change: 1 addition & 0 deletions LayoutTests/platform/ios/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ imported/w3c/web-platform-tests/speech-api/ [ Pass ]
http/tests/media/fairplay [ Pass ]
media/track/track-description-cue.html [ Pass ]
media/track/track-extended-descriptions.html [ Pass ]
media/wireless-route-monitoring.html [ Pass ]

# mac-only IPC test
ipc/webpageproxy-correctionpanel-no-crash.html [ Skip ]
Expand Down
10 changes: 5 additions & 5 deletions Source/WebCore/html/MediaElementSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,6 @@ void MediaElementSession::clientDataBufferingTimerFired()

updateClientDataBuffering();

#if PLATFORM(IOS_FAMILY)
PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
#endif

if (state() != Playing || !m_element.elementIsHidden())
return;

Expand All @@ -341,6 +337,10 @@ void MediaElementSession::updateClientDataBuffering()
m_clientDataBufferingTimer.stop();

m_element.setBufferingPolicy(preferredBufferingPolicy());

#if PLATFORM(IOS_FAMILY)
PlatformMediaSessionManager::sharedManager().configureWirelessTargetMonitoring();
#endif
}

void MediaElementSession::addBehaviorRestriction(BehaviorRestrictions restrictions)
Expand Down Expand Up @@ -770,7 +770,7 @@ void MediaElementSession::setHasPlaybackTargetAvailabilityListeners(bool hasList

#if PLATFORM(IOS_FAMILY)
m_hasPlaybackTargetAvailabilityListeners = hasListeners;
PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
PlatformMediaSessionManager::sharedManager().configureWirelessTargetMonitoring();
#else
UNUSED_PARAM(hasListeners);
m_element.document().playbackTargetPickerClientStateDidChange(*this, m_element.mediaState());
Expand Down
10 changes: 4 additions & 6 deletions Source/WebCore/platform/audio/PlatformMediaSessionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef PlatformMediaSessionManager_h
#define PlatformMediaSessionManager_h
#pragma once

#include "MediaUniqueIdentifier.h"
#include "PlatformMediaSession.h"
Expand Down Expand Up @@ -140,9 +139,10 @@ class PlatformMediaSessionManager
virtual void sessionCanProduceAudioChanged();

#if PLATFORM(IOS_FAMILY)
virtual void configureWireLessTargetMonitoring() { }
virtual void configureWirelessTargetMonitoring() { }
#endif
virtual bool hasWirelessTargetsAvailable() { return false; }
virtual bool isMonitoringWirelessTargets() const { return false; }

virtual void setCurrentSession(PlatformMediaSession&);
PlatformMediaSession* currentSession() const;
Expand Down Expand Up @@ -255,6 +255,4 @@ class PlatformMediaSessionManager
#endif
};

}

#endif // PlatformMediaSessionManager_h
} // namespace WebCore
3 changes: 2 additions & 1 deletion Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class MediaSessionManageriOS
virtual ~MediaSessionManageriOS();

bool hasWirelessTargetsAvailable() override;
bool isMonitoringWirelessTargets() const override;
static WEBCORE_EXPORT void providePresentingApplicationPID();

using MediaSessionHelperClient::weakPtrFactory;
Expand All @@ -66,7 +67,7 @@ class MediaSessionManageriOS
void resetRestrictions() final;
#endif

void configureWireLessTargetMonitoring() final;
void configureWirelessTargetMonitoring() final;
void providePresentingApplicationPIDIfNecessary() final;
bool sessionWillBeginPlayback(PlatformMediaSession&) final;
void sessionWillEndPlayback(PlatformMediaSession&, DelayCallingUpdateNowPlaying) final;
Expand Down
8 changes: 6 additions & 2 deletions Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@
return MediaSessionHelper::sharedHelper().isExternalOutputDeviceAvailable();
}

void MediaSessionManageriOS::configureWireLessTargetMonitoring()
bool MediaSessionManageriOS::isMonitoringWirelessTargets() const
{
return m_isMonitoringWirelessRoutes;
}

void MediaSessionManageriOS::configureWirelessTargetMonitoring()
{
#if !PLATFORM(WATCHOS)
bool requiresMonitoring = anyOfSessions([] (auto& session) {
Expand Down Expand Up @@ -229,5 +234,4 @@

} // namespace WebCore


#endif // PLATFORM(IOS_FAMILY)
18 changes: 18 additions & 0 deletions Source/WebCore/testing/Internals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4741,6 +4741,19 @@ void Internals::endAudioSessionInterruption()
#endif
}

void Internals::suspendAllMediaBuffering()
{
auto frame = this->frame();
if (!frame)
return;

auto page = frame->page();
if (!page)
return;

page->suspendAllMediaBuffering();
}

#endif // ENABLE(VIDEO)

#if ENABLE(WEB_AUDIO)
Expand Down Expand Up @@ -4939,6 +4952,11 @@ void Internals::mockMediaPlaybackTargetPickerDismissPopup()
}
#endif

bool Internals::isMonitoringWirelessRoutes() const
{
return PlatformMediaSessionManager::sharedManager().isMonitoringWirelessTargets();
}

ExceptionOr<Ref<MockPageOverlay>> Internals::installMockPageOverlay(PageOverlayType type)
{
Document* document = contextDocument();
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/testing/Internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ class Internals final : public RefCounted<Internals>, private ContextDestruction
bool isPlayerMuted(const HTMLMediaElement&) const;
void beginAudioSessionInterruption();
void endAudioSessionInterruption();
void suspendAllMediaBuffering();
#endif

#if ENABLE(WIRELESS_PLAYBACK_TARGET)
Expand All @@ -797,6 +798,8 @@ class Internals final : public RefCounted<Internals>, private ContextDestruction
void mockMediaPlaybackTargetPickerDismissPopup();
#endif

bool isMonitoringWirelessRoutes() const;

#if ENABLE(WEB_AUDIO)
void setAudioContextRestrictions(AudioContext&, StringView restrictionsString);
void useMockAudioDestinationCocoa();
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/testing/Internals.idl
Original file line number Diff line number Diff line change
Expand Up @@ -861,12 +861,15 @@ typedef (FetchRequest or FetchResponse) FetchObject;
[Conditional=VIDEO] undefined activeAudioRouteDidChange(boolean shouldPause);
[Conditional=VIDEO] undefined beginAudioSessionInterruption();
[Conditional=VIDEO] undefined endAudioSessionInterruption();
[Conditional=VIDEO] undefined suspendAllMediaBuffering();

[Conditional=WIRELESS_PLAYBACK_TARGET] undefined setMockMediaPlaybackTargetPickerEnabled(boolean enabled);
[Conditional=WIRELESS_PLAYBACK_TARGET] undefined setMockMediaPlaybackTargetPickerState(DOMString deviceName, DOMString deviceState);
[Conditional=WIRELESS_PLAYBACK_TARGET] undefined mockMediaPlaybackTargetPickerDismissPopup();
[Conditional=MEDIA_RECORDER] undefined setCustomPrivateRecorderCreator();

readonly attribute boolean isMonitoringWirelessRoutes;

[Conditional=WEB_AUDIO] undefined useMockAudioDestinationCocoa();

[Conditional=WEB_RTC] undefined emulateRTCPeerConnectionPlatformEvent(RTCPeerConnection connection, DOMString action);
Expand Down

0 comments on commit ecb0f69

Please sign in to comment.