Title: [280720] trunk
Revision
280720
Author
[email protected]
Date
2021-08-06 02:15:49 -0700 (Fri, 06 Aug 2021)

Log Message

MediaPlayerPrivateMediaStreamAVFObjC should skip enqueuing frames when not visible
https://bugs.webkit.org/show_bug.cgi?id=228816
<rdar://81077972>

Reviewed by Eric Carlson.

Source/WebCore:

Do not create layers when not needed.
This prevents enqueuing frames in a AVSampleBufferDisplayLayer that will only buffer them, thus breaking camera capture/video decoding.
We do this by not calling ensureLayers when getting a new track. Instead we react upon player renderingCanBeAccelerated value.

It is also unnecessary and takes CPU cycles to enqueue frames when the video element is not visible.
HTMLMediaElement and RenderVideo thus pass to MediaPlayer a new flag telling whether the video element is visible in the view port.
MediaPlayerPrivateMediaStreamAVFObjC will then skip enqueueing frames if not needed.

Add getter and internals API to cover these changes.

Test: fast/mediastream/mediaPlayer-visibility.html

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::isVisibleInViewportChanged):
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::loadWithNextMediaEngine):
(WebCore::MediaPlayer::setVisibleInViewport):
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::setVisibleInViewport):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::enqueueVideoSample):
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setVisible):
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setVisibleInViewport):
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::checkSelectedVideoTrack):
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::paintCurrentFrameInContext):
* rendering/RenderVideo.cpp:
(WebCore::RenderVideo::updatePlayer):
* testing/Internals.cpp:
(WebCore::Internals::isPlayerVisibleInViewport const):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

* fast/mediastream/mediaPlayer-visibility-expected.txt: Added.
* fast/mediastream/mediaPlayer-visibility.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (280719 => 280720)


--- trunk/LayoutTests/ChangeLog	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/LayoutTests/ChangeLog	2021-08-06 09:15:49 UTC (rev 280720)
@@ -1,3 +1,14 @@
+2021-08-06  Youenn Fablet  <[email protected]>
+
+        MediaPlayerPrivateMediaStreamAVFObjC should skip enqueuing frames when not visible
+        https://bugs.webkit.org/show_bug.cgi?id=228816
+        <rdar://81077972>
+
+        Reviewed by Eric Carlson.
+
+        * fast/mediastream/mediaPlayer-visibility-expected.txt: Added.
+        * fast/mediastream/mediaPlayer-visibility.html: Added.
+
 2021-08-05  Ryan Haddad  <[email protected]>
 
         Unreviewed test garening, skip test that is unsupported on WK1.

Added: trunk/LayoutTests/fast/mediastream/mediaPlayer-visibility-expected.txt (0 => 280720)


--- trunk/LayoutTests/fast/mediastream/mediaPlayer-visibility-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/mediaPlayer-visibility-expected.txt	2021-08-06 09:15:49 UTC (rev 280720)
@@ -0,0 +1,6 @@
+
+
+PASS Check default media stream player visibility
+PASS Check media stream player visibility with CSS
+PASS Check media stream player visibility of unattached media element
+

Added: trunk/LayoutTests/fast/mediastream/mediaPlayer-visibility.html (0 => 280720)


--- trunk/LayoutTests/fast/mediastream/mediaPlayer-visibility.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/mediaPlayer-visibility.html	2021-08-06 09:15:49 UTC (rev 280720)
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<body>
+<video id="video1" autoplay playsInline></video>
+<video id="video2" autoplay playsInline style="visibility:hidden"></video>
+<script src=""
+<script src=""
+<script>
+
+promise_test(async() => {
+    if (!window.internals)
+       return Promise.reject("Test requires internals API");
+
+    const stream = await navigator.mediaDevices.getUserMedia({video : true});
+    video1.srcObject = stream;
+    await video1.play();
+
+    assert_true(internals.isPlayerVisibleInViewport(video1), "video1");
+}, "Check default media stream player visibility");
+
+promise_test(async() => {
+   if (!window.internals)
+        return Promise.reject("Test requires internals API");
+
+    const stream = await navigator.mediaDevices.getUserMedia({video : true});
+    video2.srcObject = stream;
+    await video2.play();
+
+    assert_false(internals.isPlayerVisibleInViewport(video2), "video2");
+
+    video2.style.visibility = "visible";
+    await new Promise(resolve => setTimeout(resolve, 0));
+    assert_true(internals.isPlayerVisibleInViewport(video2), "video2 2");
+
+    video2.style.visibility = "hidden";
+    await new Promise(resolve => setTimeout(resolve, 0));
+    assert_false(internals.isPlayerVisibleInViewport(video2), "video2 3");
+}, "Check media stream player visibility with CSS");
+
+promise_test(async() => {
+    if (!window.internals)
+        return Promise.reject("Test requires internals API");
+
+    const video3 = document.createElement("video");
+    const stream = await navigator.mediaDevices.getUserMedia({video : true});
+    video3.srcObject = stream;
+    video3.playsInline = true;
+    await video3.play();
+
+    assert_false(internals.isPlayerVisibleInViewport(video3), "video3");
+
+    document.body.appendChild(video3);
+    await video3.play();
+    await new Promise(resolve => setTimeout(resolve, 0));
+    assert_false(internals.isPlayerVisibleInViewport(video3), "video3 2");
+}, "Check media stream player visibility of unattached media element");
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (280719 => 280720)


--- trunk/Source/WebCore/ChangeLog	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/ChangeLog	2021-08-06 09:15:49 UTC (rev 280720)
@@ -1,3 +1,45 @@
+2021-08-06  Youenn Fablet  <[email protected]>
+
+        MediaPlayerPrivateMediaStreamAVFObjC should skip enqueuing frames when not visible
+        https://bugs.webkit.org/show_bug.cgi?id=228816
+        <rdar://81077972>
+
+        Reviewed by Eric Carlson.
+
+        Do not create layers when not needed.
+        This prevents enqueuing frames in a AVSampleBufferDisplayLayer that will only buffer them, thus breaking camera capture/video decoding.
+        We do this by not calling ensureLayers when getting a new track. Instead we react upon player renderingCanBeAccelerated value.
+
+        It is also unnecessary and takes CPU cycles to enqueue frames when the video element is not visible.
+        HTMLMediaElement and RenderVideo thus pass to MediaPlayer a new flag telling whether the video element is visible in the view port.
+        MediaPlayerPrivateMediaStreamAVFObjC will then skip enqueueing frames if not needed.
+
+        Add getter and internals API to cover these changes.
+
+        Test: fast/mediastream/mediaPlayer-visibility.html
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::isVisibleInViewportChanged):
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::loadWithNextMediaEngine):
+        (WebCore::MediaPlayer::setVisibleInViewport):
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::setVisibleInViewport):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::enqueueVideoSample):
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setVisible):
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setVisibleInViewport):
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::checkSelectedVideoTrack):
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::paintCurrentFrameInContext):
+        * rendering/RenderVideo.cpp:
+        (WebCore::RenderVideo::updatePlayer):
+        * testing/Internals.cpp:
+        (WebCore::Internals::isPlayerVisibleInViewport const):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2021-08-05  Cameron McCormack  <[email protected]>
 
         Stop tracking form elements with FormController

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (280719 => 280720)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2021-08-06 09:15:49 UTC (rev 280720)
@@ -6744,6 +6744,7 @@
     m_player->setPreferredDynamicRangeMode(m_overrideDynamicRangeMode.value_or(preferredDynamicRangeMode(document().view())));
     m_player->setMuted(effectiveMuted());
     m_player->setVisible(!m_elementIsHidden);
+    m_player->setVisibleInViewport(isVisibleInViewport());
     schedulePlaybackControlsManagerUpdate();
 
 #if ENABLE(WEB_AUDIO)
@@ -8071,6 +8072,8 @@
 
 void HTMLMediaElement::isVisibleInViewportChanged()
 {
+    if (m_player)
+        m_player->setVisibleInViewport(isVisibleInViewport());
     queueTaskKeepingObjectAlive(*this, TaskSource::MediaElement, [this] {
         if (isContextStopped())
             return;

Modified: trunk/Source/WebCore/platform/graphics/MediaPlayer.cpp (280719 => 280720)


--- trunk/Source/WebCore/platform/graphics/MediaPlayer.cpp	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayer.cpp	2021-08-06 09:15:49 UTC (rev 280720)
@@ -575,6 +575,8 @@
             client().mediaPlayerEngineUpdated();
             if (m_visible)
                 m_private->setVisible(m_visible);
+            if (m_visibleInViewport)
+                m_private->setVisibleInViewport(m_visibleInViewport);
             m_private->prepareForPlayback(m_privateBrowsing, m_preload, m_preservesPitch, m_shouldPrepareToRender);
         }
     }
@@ -1011,6 +1013,12 @@
     m_private->setVisibleForCanvas(visible);
 }
 
+void MediaPlayer::setVisibleInViewport(bool visible)
+{
+    m_visibleInViewport = visible;
+    m_private->setVisibleInViewport(visible);
+}
+
 MediaPlayer::Preload MediaPlayer::preload() const
 {
     return m_preload;

Modified: trunk/Source/WebCore/platform/graphics/MediaPlayer.h (280719 => 280720)


--- trunk/Source/WebCore/platform/graphics/MediaPlayer.h	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayer.h	2021-08-06 09:15:49 UTC (rev 280720)
@@ -360,6 +360,9 @@
     void setVisible(bool);
     void setVisibleForCanvas(bool);
 
+    void setVisibleInViewport(bool);
+    bool isVisibleInViewport() const { return m_visibleInViewport; }
+
     void prepareToPlay();
     void play();
     void pause();
@@ -699,6 +702,7 @@
     Preload m_preload { Preload::Auto };
     double m_volume { 1 };
     bool m_visible { false };
+    bool m_visibleInViewport { false };
     bool m_muted { false };
     bool m_preservesPitch { true };
     bool m_privateBrowsing { false };

Modified: trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h (280719 => 280720)


--- trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h	2021-08-06 09:15:49 UTC (rev 280720)
@@ -98,6 +98,7 @@
 
     virtual void setVisible(bool) = 0;
     virtual void setVisibleForCanvas(bool visible) { setVisible(visible); }
+    virtual void setVisibleInViewport(bool) { }
 
     virtual float duration() const { return 0; }
     virtual double durationDouble() const { return duration(); }

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h (280719 => 280720)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h	2021-08-06 09:15:49 UTC (rev 280720)
@@ -122,6 +122,7 @@
 
     void setVisible(bool) final;
     void setVisibleForCanvas(bool) final;
+    void setVisibleInViewport(bool) final;
 
     MediaTime durationMediaTime() const override;
     MediaTime currentMediaTime() const override;
@@ -143,7 +144,7 @@
     void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&) override;
     bool metaDataAvailable() const { return m_mediaStreamPrivate && m_readyState >= MediaPlayer::ReadyState::HaveMetadata; }
 
-    void acceleratedRenderingStateChanged() override;
+    void acceleratedRenderingStateChanged() final { updateLayersAsNeeded(); }
     bool supportsAcceleratedRendering() const override { return true; }
 
     bool hasSingleSecurityOrigin() const override { return true; }
@@ -170,6 +171,7 @@
     void scheduleDeferredTask(Function<void ()>&&);
 
     void layersAreInitialized(IntSize, bool);
+    void updateLayersAsNeeded();
 
     enum DisplayMode {
         None,
@@ -272,7 +274,8 @@
     bool m_muted { false };
     bool m_ended { false };
     bool m_hasEverEnqueuedVideoFrame { false };
-    bool m_visible { false };
+    bool m_isPageVisible { false };
+    bool m_isVisibleInViewPort { false };
     bool m_haveSeenMetadata { false };
     bool m_waitingForFirstImage { false };
 };

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm (280719 => 280720)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm	2021-08-06 09:15:49 UTC (rev 280720)
@@ -252,7 +252,7 @@
 
 void MediaPlayerPrivateMediaStreamAVFObjC::enqueueVideoSample(MediaSample& sample)
 {
-    if (!m_visible)
+    if (!m_isPageVisible || !m_isVisibleInViewPort)
         return;
 
     if (!m_sampleBufferDisplayLayerLock.tryLock())
@@ -614,12 +614,12 @@
     return !m_audioTrackMap.isEmpty();
 }
 
-void MediaPlayerPrivateMediaStreamAVFObjC::setVisible(bool visible)
+void MediaPlayerPrivateMediaStreamAVFObjC::setVisible(bool isVisible)
 {
-    if (m_visible == visible)
+    if (m_isPageVisible == isVisible)
         return;
 
-    m_visible = visible;
+    m_isPageVisible = isVisible;
     flushRenderers();
 }
 
@@ -627,6 +627,11 @@
 {
 }
 
+void MediaPlayerPrivateMediaStreamAVFObjC::setVisibleInViewport(bool isVisible)
+{
+    m_isVisibleInViewPort = isVisible;
+}
+
 MediaTime MediaPlayerPrivateMediaStreamAVFObjC::durationMediaTime() const
 {
     return MediaTime::positiveInfiniteTime();
@@ -861,7 +866,9 @@
         if (m_displayMode == None)
             m_waitingForFirstImage = true;
     }
-    ensureLayers();
+
+    updateLayersAsNeeded();
+
     if (m_sampleBufferDisplayLayer) {
         if (!m_activeVideoTrack)
             m_sampleBufferDisplayLayer->clearEnqueuedSamples();
@@ -998,7 +1005,7 @@
     context.drawNativeImage(*image, imageRect.size(), transformedDestRect, imageRect);
 }
 
-void MediaPlayerPrivateMediaStreamAVFObjC::acceleratedRenderingStateChanged()
+void MediaPlayerPrivateMediaStreamAVFObjC::updateLayersAsNeeded()
 {
     if (m_player->renderingCanBeAccelerated())
         ensureLayers();

Modified: trunk/Source/WebCore/rendering/RenderVideo.cpp (280719 => 280720)


--- trunk/Source/WebCore/rendering/RenderVideo.cpp	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/rendering/RenderVideo.cpp	2021-08-06 09:15:49 UTC (rev 280720)
@@ -282,6 +282,7 @@
     IntRect videoBounds = videoBox(); 
     mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
     mediaPlayer->setVisible(!videoElement().elementIsHidden());
+    mediaPlayer->setVisibleInViewport(videoElement().isVisibleInViewport());
     mediaPlayer->setShouldMaintainAspectRatio(style().objectFit() != ObjectFit::Fill);
 }
 

Modified: trunk/Source/WebCore/testing/Internals.cpp (280719 => 280720)


--- trunk/Source/WebCore/testing/Internals.cpp	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/testing/Internals.cpp	2021-08-06 09:15:49 UTC (rev 280720)
@@ -4420,6 +4420,13 @@
 {
     return element.isDisablingSleep();
 }
+
+bool Internals::isPlayerVisibleInViewport(HTMLMediaElement& element) const
+{
+    auto player = element.player();
+    return player && player->isVisibleInViewport();
+}
+
 #endif // ENABLE(VIDEO)
 
 #if ENABLE(WEB_AUDIO)

Modified: trunk/Source/WebCore/testing/Internals.h (280719 => 280720)


--- trunk/Source/WebCore/testing/Internals.h	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/testing/Internals.h	2021-08-06 09:15:49 UTC (rev 280720)
@@ -713,6 +713,7 @@
     ExceptionOr<void> postRemoteControlCommand(const String&, float argument);
     void activeAudioRouteDidChange(bool shouldPause);
     bool elementIsBlockingDisplaySleep(HTMLMediaElement&) const;
+    bool isPlayerVisibleInViewport(HTMLMediaElement&) const;
 #endif
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)

Modified: trunk/Source/WebCore/testing/Internals.idl (280719 => 280720)


--- trunk/Source/WebCore/testing/Internals.idl	2021-08-06 03:30:45 UTC (rev 280719)
+++ trunk/Source/WebCore/testing/Internals.idl	2021-08-06 09:15:49 UTC (rev 280720)
@@ -773,6 +773,7 @@
     [Conditional=VIDEO] undefined simulateSystemSleep();
     [Conditional=VIDEO] undefined simulateSystemWake();
     [Conditional=VIDEO] boolean elementIsBlockingDisplaySleep(HTMLMediaElement element);
+    [Conditional=VIDEO] boolean isPlayerVisibleInViewport(HTMLMediaElement element);
 
     MockPageOverlay installMockPageOverlay(PageOverlayType type);
     DOMString pageOverlayLayerTreeAsText(optional unsigned short flags = 0);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to