Define media buffering policy
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 May 2019 22:00:32 +0000 (22:00 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 May 2019 22:00:32 +0000 (22:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196979
<rdar://problem/28383861>

Reviewed by Jer Noble.

Source/WebCore:

Test: MediaBufferingPolicy API test.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
(WebCore::HTMLMediaElement::suspend):
(WebCore::HTMLMediaElement::resume):
(WebCore::HTMLMediaElement::createMediaPlayer):
(WebCore::HTMLMediaElement::setBufferingPolicy):
(WebCore::HTMLMediaElement::purgeBufferedDataIfPossible):
(WebCore::HTMLMediaElement::bufferingPolicy const):
(WebCore::HTMLMediaElement::setShouldBufferData): Deleted.
* html/HTMLMediaElement.h:
(WebCore::HTMLMediaElement::shouldBufferData const): Deleted.
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::updateClientDataBuffering):
(WebCore::MediaElementSession::preferredBufferingPolicy const):
(WebCore::MediaElementSession::dataBufferingPermitted const): Deleted.
* html/MediaElementSession.h:
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::setBufferingPolicy):
(WebCore::convertEnumerationToString):
(WebCore::MediaPlayer::setShouldBufferData): Deleted.
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerEnums.h:
(WTF::LogArgument<WebCore::MediaPlayerEnums::BufferingPolicy>::toString):
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::setBufferingPolicy):
(WebCore::MediaPlayerPrivateInterface::setShouldBufferData): Deleted.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setBufferingPolicy):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setShouldBufferData): Deleted.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setBufferingPolicy):
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::flushAndRemoveVideoSampleBuffers): Deleted.
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setShouldBufferData): Deleted.
* testing/Internals.cpp:
(WebCore::Internals::elementShouldBufferData):
(WebCore::Internals::elementBufferingPolicy):
* testing/Internals.h:
* testing/Internals.idl:

Source/WebCore/PAL:

* pal/spi/mac/AVFoundationSPI.h:

Source/WTF:

* wtf/Platform.h:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/MediaBufferingPolicy.mm: Added.
(waitUntilBufferingPolicyIsEqualTo):
(TEST):

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

23 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/Platform.h
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/spi/mac/AVFoundationSPI.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/html/MediaElementSession.cpp
Source/WebCore/html/MediaElementSession.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerEnums.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaBufferingPolicy.mm [new file with mode: 0644]

index ecb9552..6cb8134 100644 (file)
@@ -1,3 +1,13 @@
+2019-05-07  Eric Carlson  <eric.carlson@apple.com>
+
+        Define media buffering policy
+        https://bugs.webkit.org/show_bug.cgi?id=196979
+        <rdar://problem/28383861>
+
+        Reviewed by Jer Noble.
+
+        * wtf/Platform.h:
+
 2019-05-07  Robin Morisset  <rmorisset@apple.com>
 
         WTF::BitVector should have an isEmpty() method
index 34559fa..84636ca 100644 (file)
 #define HAVE_APP_LINKS_WITH_ISENABLED 1
 #define HAVE_ROUTE_SHARING_POLICY_LONG_FORM_VIDEO 1
 #endif
+
+#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 130000) || (PLATFORM(WATCHOS) && __WATCH_OS_VERSION_MIN_REQUIRED >= 60000) || (PLATFORM(APPLETV) && __TV_OS_VERSION_MIN_REQUIRED >= 130000)
+#define HAVE_AVPLAYER_RESOURCE_CONSERVATION_LEVEL 1
+#endif
+
index d7e9383..291e2ca 100644 (file)
@@ -1,3 +1,55 @@
+2019-05-07  Eric Carlson  <eric.carlson@apple.com>
+
+        Define media buffering policy
+        https://bugs.webkit.org/show_bug.cgi?id=196979
+        <rdar://problem/28383861>
+
+        Reviewed by Jer Noble.
+
+        Test: MediaBufferingPolicy API test.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement):
+        (WebCore::HTMLMediaElement::suspend):
+        (WebCore::HTMLMediaElement::resume):
+        (WebCore::HTMLMediaElement::createMediaPlayer):
+        (WebCore::HTMLMediaElement::setBufferingPolicy):
+        (WebCore::HTMLMediaElement::purgeBufferedDataIfPossible):
+        (WebCore::HTMLMediaElement::bufferingPolicy const):
+        (WebCore::HTMLMediaElement::setShouldBufferData): Deleted.
+        * html/HTMLMediaElement.h:
+        (WebCore::HTMLMediaElement::shouldBufferData const): Deleted.
+        * html/MediaElementSession.cpp:
+        (WebCore::MediaElementSession::updateClientDataBuffering):
+        (WebCore::MediaElementSession::preferredBufferingPolicy const):
+        (WebCore::MediaElementSession::dataBufferingPermitted const): Deleted.
+        * html/MediaElementSession.h:
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::setBufferingPolicy):
+        (WebCore::convertEnumerationToString):
+        (WebCore::MediaPlayer::setShouldBufferData): Deleted.
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerEnums.h:
+        (WTF::LogArgument<WebCore::MediaPlayerEnums::BufferingPolicy>::toString):
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::setBufferingPolicy):
+        (WebCore::MediaPlayerPrivateInterface::setShouldBufferData): Deleted.
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::setBufferingPolicy):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::setShouldBufferData): Deleted.
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setBufferingPolicy):
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::flushAndRemoveVideoSampleBuffers): Deleted.
+        (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::setShouldBufferData): Deleted.
+        * testing/Internals.cpp:
+        (WebCore::Internals::elementShouldBufferData):
+        (WebCore::Internals::elementBufferingPolicy):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2019-05-07  Alex Christensen  <achristensen@webkit.org>
 
         Add SPI to set a list of hosts to which to send custom header fields cross-origin
index 8224b2e..a542ea5 100644 (file)
@@ -1,3 +1,13 @@
+2019-05-07  Eric Carlson  <eric.carlson@apple.com>
+
+        Define media buffering policy
+        https://bugs.webkit.org/show_bug.cgi?id=196979
+        <rdar://problem/28383861>
+
+        Reviewed by Jer Noble.
+
+        * pal/spi/mac/AVFoundationSPI.h:
+
 2019-05-04  Alex Christensen  <achristensen@webkit.org>
 
         Revert r244953 and r244954 because they broke internal builds.
index ff6bf80..f1bebf8 100644 (file)
@@ -324,3 +324,16 @@ NS_ASSUME_NONNULL_BEGIN
 NS_ASSUME_NONNULL_END
 #endif
 
+#if !USE(APPLE_INTERNAL_SDK) && HAVE(AVPLAYER_RESOURCE_CONSERVATION_LEVEL)
+@interface AVPlayer (AVPlayerPrivate)
+
+@property (nonatomic) AVPlayerResourceConservationLevel resourceConservationLevelWhilePaused;
+
+typedef NS_ENUM(NSInteger, AVPlayerResourceConservationLevel) {
+    AVPlayerResourceConservationLevelNone                                 = 0,
+    AVPlayerResourceConservationLevelReduceReadAhead                      = 1,
+    AVPlayerResourceConservationLevelReuseActivePlayerResources           = 2,
+    AVPlayerResourceConservationLevelRecycleBuffer                        = 3,
+};
+@end
+#endif
index e3093f2..6d87bdd 100644 (file)
@@ -449,7 +449,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     , m_completelyLoaded(false)
     , m_havePreparedToPlay(false)
     , m_parsingInProgress(createdByParser)
-    , m_shouldBufferData(true)
     , m_elementIsHidden(document.hidden())
     , m_creatingControls(false)
     , m_receivedLayoutSizeChanged(false)
@@ -5779,7 +5778,7 @@ void HTMLMediaElement::suspend(ReasonForSuspension reason)
     case ReasonForSuspension::PageCache:
         stopWithoutDestroyingMediaPlayer();
         m_asyncEventQueue.suspend();
-        setShouldBufferData(false);
+        setBufferingPolicy(BufferingPolicy::MakeResourcesPurgeable);
         m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePageConsentToResumeMedia);
         break;
     case ReasonForSuspension::PageWillBeSuspended:
@@ -5798,14 +5797,13 @@ void HTMLMediaElement::resume()
 
     m_asyncEventQueue.resume();
 
-    setShouldBufferData(true);
-
     if (!m_mediaSession->pageAllowsPlaybackAfterResuming())
         document().addMediaCanStartListener(*this);
     else
         setPausedInternal(false);
 
     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequirePageConsentToResumeMedia);
+    m_mediaSession->updateBufferingPolicy();
 
     if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED && !m_resumeTaskQueue.hasPendingTask()) {
         // Restart the load if it was aborted in the middle by moving the document to the page cache.
@@ -6705,7 +6703,7 @@ void HTMLMediaElement::createMediaPlayer()
 #endif
 
     m_player = MediaPlayer::create(*this);
-    m_player->setShouldBufferData(m_shouldBufferData);
+    m_player->setBufferingPolicy(m_bufferingPolicy);
     schedulePlaybackControlsManagerUpdate();
 
 #if ENABLE(WEB_AUDIO)
@@ -7882,20 +7880,23 @@ bool HTMLMediaElement::doesHaveAttribute(const AtomicString& attribute, AtomicSt
     return true;
 }
 
-void HTMLMediaElement::setShouldBufferData(bool shouldBuffer)
+void HTMLMediaElement::setBufferingPolicy(BufferingPolicy policy)
 {
-    if (shouldBuffer == m_shouldBufferData)
+    if (policy == m_bufferingPolicy)
         return;
 
-    m_shouldBufferData = shouldBuffer;
+    INFO_LOG(LOGIDENTIFIER, policy);
+
+    m_bufferingPolicy = policy;
     if (m_player)
-        m_player->setShouldBufferData(shouldBuffer);
+        m_player->setBufferingPolicy(policy);
 }
 
 void HTMLMediaElement::purgeBufferedDataIfPossible()
 {
-#if PLATFORM(IOS_FAMILY)
-    if (!MemoryPressureHandler::singleton().isUnderMemoryPressure() && m_mediaSession->dataBufferingPermitted())
+    INFO_LOG(LOGIDENTIFIER);
+
+    if (!MemoryPressureHandler::singleton().isUnderMemoryPressure() && m_mediaSession->preferredBufferingPolicy() == BufferingPolicy::Default)
         return;
 
     if (isPlayingToExternalTarget()) {
@@ -7903,13 +7904,7 @@ void HTMLMediaElement::purgeBufferedDataIfPossible()
         return;
     }
 
-    // This is called to relieve memory pressure. Turning off buffering causes the media playback
-    // daemon to release memory associated with queued-up video frames.
-    // We turn it back on right away, but new frames won't get loaded unless playback is resumed.
-    INFO_LOG(LOGIDENTIFIER, "toggling data buffering");
-    setShouldBufferData(false);
-    setShouldBufferData(true);
-#endif
+    setBufferingPolicy(BufferingPolicy::PurgeResources);
 }
 
 bool HTMLMediaElement::canSaveMediaData() const
@@ -8131,6 +8126,11 @@ void HTMLMediaElement::setInActiveDocument(bool inActiveDocument)
     m_mediaSession->inActiveDocumentChanged();
 }
 
+HTMLMediaElementEnums::BufferingPolicy HTMLMediaElement::bufferingPolicy() const
+{
+    return m_bufferingPolicy;    
+}
+
 }
 
 #endif
index 2296c66..a091626 100644 (file)
@@ -268,13 +268,14 @@ public:
 
     WEBCORE_EXPORT void play() override;
     WEBCORE_EXPORT void pause() override;
-    void setShouldBufferData(bool);
-    WEBCORE_EXPORT bool shouldBufferData() const { return m_shouldBufferData; }
     WEBCORE_EXPORT void fastSeek(double);
     double minFastReverseRate() const;
     double maxFastForwardRate() const;
 
-    void purgeBufferedDataIfPossible();
+    using HTMLMediaElementEnums::BufferingPolicy;
+    void setBufferingPolicy(BufferingPolicy);
+    WEBCORE_EXPORT BufferingPolicy bufferingPolicy() const;
+    WEBCORE_EXPORT void purgeBufferedDataIfPossible();
 
 // captions
     WEBCORE_EXPORT bool webkitHasClosedCaptions() const;
@@ -1072,6 +1073,8 @@ private:
     ScanType m_scanType { Scan };
     ScanDirection m_scanDirection { Forward };
 
+    BufferingPolicy m_bufferingPolicy { BufferingPolicy::Default };
+
     bool m_firstTimePlaying : 1;
     bool m_playing : 1;
     bool m_isWaitingUntilMediaCanStart : 1;
@@ -1099,7 +1102,6 @@ private:
     bool m_completelyLoaded : 1;
     bool m_havePreparedToPlay : 1;
     bool m_parsingInProgress : 1;
-    bool m_shouldBufferData : 1;
     bool m_elementIsHidden : 1;
     bool m_elementWasRemovedFromDOM : 1;
     bool m_creatingControls : 1;
index 7c3d009..ec48a24 100644 (file)
@@ -216,7 +216,7 @@ void MediaElementSession::updateClientDataBuffering()
     if (m_clientDataBufferingTimer.isActive())
         m_clientDataBufferingTimer.stop();
 
-    m_element.setShouldBufferData(dataBufferingPermitted());
+    m_element.setBufferingPolicy(preferredBufferingPolicy());
 }
 
 void MediaElementSession::addBehaviorRestriction(BehaviorRestrictions restrictions)
@@ -356,29 +356,29 @@ bool MediaElementSession::dataLoadingPermitted() const
     return true;
 }
 
-bool MediaElementSession::dataBufferingPermitted() const
+MediaPlayer::BufferingPolicy MediaElementSession::preferredBufferingPolicy() const
 {
     if (isSuspended())
-        return false;
+        return MediaPlayer::BufferingPolicy::MakeResourcesPurgeable;
 
     if (bufferingSuspended())
-        return false;
+        return MediaPlayer::BufferingPolicy::LimitReadAhead;
 
     if (state() == PlatformMediaSession::Playing)
-        return true;
+        return MediaPlayer::BufferingPolicy::Default;
 
     if (shouldOverrideBackgroundLoadingRestriction())
-        return true;
+        return MediaPlayer::BufferingPolicy::Default;
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
     if (m_shouldPlayToPlaybackTarget)
-        return true;
+        return MediaPlayer::BufferingPolicy::Default;
 #endif
 
     if (m_elementIsHiddenUntilVisibleInViewport || m_elementIsHiddenBecauseItWasRemovedFromDOM || m_element.elementIsHidden())
-        return false;
+        return MediaPlayer::BufferingPolicy::MakeResourcesPurgeable;
 
-    return true;
+    return MediaPlayer::BufferingPolicy::Default;
 }
 
 bool MediaElementSession::fullscreenPermitted() const
index 0fa0760..440e5b2 100644 (file)
@@ -73,7 +73,7 @@ public:
     SuccessOr<MediaPlaybackDenialReason> playbackPermitted() const;
     bool autoplayPermitted() const;
     bool dataLoadingPermitted() const;
-    bool dataBufferingPermitted() const;
+    MediaPlayer::BufferingPolicy preferredBufferingPolicy() const;
     bool fullscreenPermitted() const;
     bool pageAllowsDataLoading() const;
     bool pageAllowsPlaybackAfterResuming() const;
@@ -104,6 +104,7 @@ public:
     void suspendBuffering() override;
     void resumeBuffering() override;
     bool bufferingSuspended() const;
+    void updateBufferingPolicy() { scheduleClientDataBufferingCheck(); }
 
     // Restrictions to modify default behaviors.
     enum BehaviorRestrictionFlags : unsigned {
index 8bff2d7..5a704b3 100644 (file)
@@ -563,9 +563,9 @@ void MediaPlayer::pause()
     m_private->pause();
 }
 
-void MediaPlayer::setShouldBufferData(bool shouldBuffer)
+void MediaPlayer::setBufferingPolicy(BufferingPolicy policy)
 {
-    m_private->setShouldBufferData(shouldBuffer);
+    m_private->setBufferingPolicy(policy);
 }
 
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
@@ -1640,6 +1640,22 @@ String convertEnumerationToString(MediaPlayerEnums::SupportsType enumerationValu
     return values[static_cast<size_t>(enumerationValue)];
 }
 
+String convertEnumerationToString(MediaPlayerEnums::BufferingPolicy enumerationValue)
+{
+    static const NeverDestroyed<String> values[] = {
+        MAKE_STATIC_STRING_IMPL("Default"),
+        MAKE_STATIC_STRING_IMPL("LimitReadAhead"),
+        MAKE_STATIC_STRING_IMPL("MakeResourcesPurgeable"),
+        MAKE_STATIC_STRING_IMPL("PurgeResources"),
+    };
+    static_assert(!static_cast<size_t>(MediaPlayerEnums::BufferingPolicy::Default), "MediaPlayerEnums::Default is not 0 as expected");
+    static_assert(static_cast<size_t>(MediaPlayerEnums::BufferingPolicy::LimitReadAhead) == 1, "MediaPlayerEnums::LimitReadAhead is not 1 as expected");
+    static_assert(static_cast<size_t>(MediaPlayerEnums::BufferingPolicy::MakeResourcesPurgeable) == 2, "MediaPlayerEnums::MakeResourcesPurgeable is not 2 as expected");
+    static_assert(static_cast<size_t>(MediaPlayerEnums::BufferingPolicy::PurgeResources) == 3, "MediaPlayerEnums::PurgeResources is not 3 as expected");
+    ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));
+    return values[static_cast<size_t>(enumerationValue)];
+}
+
 }
 
 #endif
index efc15ba..3e1263b 100644 (file)
@@ -312,7 +312,9 @@ public:
     void prepareToPlay();
     void play();
     void pause();
-    void setShouldBufferData(bool);
+
+    using MediaPlayerEnums::BufferingPolicy;
+    void setBufferingPolicy(BufferingPolicy);
 
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
     // Represents synchronous exceptions that can be thrown from the Encrypted Media methods.
index 950e0ed..0195c69 100644 (file)
@@ -43,12 +43,20 @@ public:
         VideoFullscreenModePictureInPicture = 1 << 1,
     };
     typedef uint32_t VideoFullscreenMode;
+
+    enum class BufferingPolicy {
+        Default,
+        LimitReadAhead,
+        MakeResourcesPurgeable,
+        PurgeResources,
+    };
 };
 
 WTF::String convertEnumerationToString(MediaPlayerEnums::ReadyState);
 WTF::String convertEnumerationToString(MediaPlayerEnums::NetworkState);
 WTF::String convertEnumerationToString(MediaPlayerEnums::Preload);
 WTF::String convertEnumerationToString(MediaPlayerEnums::SupportsType);
+WTF::String convertEnumerationToString(MediaPlayerEnums::BufferingPolicy);
 
 } // namespace WebCore
 
@@ -74,4 +82,12 @@ struct LogArgument<WebCore::MediaPlayerEnums::NetworkState> {
     }
 };
 
+template <>
+struct LogArgument<WebCore::MediaPlayerEnums::BufferingPolicy> {
+    static WTF::String toString(const WebCore::MediaPlayerEnums::BufferingPolicy policy)
+    {
+        return convertEnumerationToString(policy);
+    }
+};
+
 }; // namespace WTF
index 9700c05..c79d480 100644 (file)
@@ -68,7 +68,7 @@ public:
 
     virtual void play() = 0;
     virtual void pause() = 0;    
-    virtual void setShouldBufferData(bool) { }
+    virtual void setBufferingPolicy(MediaPlayer::BufferingPolicy) { }
 
     virtual bool supportsPictureInPicture() const { return false; }
     virtual bool supportsFullscreen() const { return false; }
index e1b12dc..7587ad5 100644 (file)
@@ -121,7 +121,7 @@ public:
     void canPlayFastReverseDidChange(bool);
     void canPlayFastForwardDidChange(bool);
 
-    void setShouldBufferData(bool) override;
+    void setBufferingPolicy(MediaPlayerEnums::BufferingPolicy) override;
 
 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
     void outputMediaDataWillChange(AVPlayerItemVideoOutput*);
@@ -424,11 +424,11 @@ private:
     mutable long long m_cachedTotalBytes;
     unsigned m_pendingStatusChanges;
     int m_cachedItemStatus;
+    MediaPlayer::BufferingPolicy m_bufferingPolicy { MediaPlayer::BufferingPolicy::Default };
     bool m_cachedLikelyToKeepUp;
     bool m_cachedBufferEmpty;
     bool m_cachedBufferFull;
     bool m_cachedHasEnabledAudio;
-    bool m_shouldBufferData;
     bool m_cachedIsReadyForDisplay;
     bool m_haveBeenAskedToCreateLayer;
     bool m_cachedCanPlayFastForward;
index e7ebcc4..5afd8cb 100644 (file)
@@ -348,7 +348,6 @@ MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC(MediaPlay
     , m_cachedBufferEmpty(false)
     , m_cachedBufferFull(false)
     , m_cachedHasEnabledAudio(false)
-    , m_shouldBufferData(true)
     , m_cachedIsReadyForDisplay(false)
     , m_haveBeenAskedToCreateLayer(false)
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
@@ -379,7 +378,7 @@ MediaPlayerPrivateAVFoundationObjC::~MediaPlayerPrivateAVFoundationObjC()
 
 void MediaPlayerPrivateAVFoundationObjC::cancelLoad()
 {
-    INFO_LOG(LOGIDENTIFIER);
+    ALWAYS_LOG(LOGIDENTIFIER);
     tearDownVideoRendering();
 
     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
@@ -543,7 +542,7 @@ void MediaPlayerPrivateAVFoundationObjC::createAVPlayerLayer()
     updateVideoLayerGravity();
     [m_videoLayer setContentsScale:player()->client().mediaPlayerContentsScale()];
     IntSize defaultSize = snappedIntRect(player()->client().mediaPlayerContentBoxRect()).size();
-    INFO_LOG(LOGIDENTIFIER);
+    ALWAYS_LOG(LOGIDENTIFIER);
 
     m_videoFullscreenLayerManager->setVideoLayer(m_videoLayer.get(), defaultSize);
 
@@ -558,7 +557,7 @@ void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer()
     if (!m_videoLayer)
         return;
 
-    INFO_LOG(LOGIDENTIFIER);
+    ALWAYS_LOG(LOGIDENTIFIER);
 
     [m_videoLayer removeObserver:m_objcObserver.get() forKeyPath:@"readyForDisplay"];
     [m_videoLayer setPlayer:nil];
@@ -698,7 +697,7 @@ void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const URL& url)
     if (m_avAsset)
         return;
 
-    INFO_LOG(LOGIDENTIFIER);
+    ALWAYS_LOG(LOGIDENTIFIER);
 
     setDelayCallbacks(true);
 
@@ -835,7 +834,7 @@ void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
     if (m_avPlayer)
         return;
 
-    INFO_LOG(LOGIDENTIFIER);
+    ALWAYS_LOG(LOGIDENTIFIER);
 
     setDelayCallbacks(true);
 
@@ -884,7 +883,7 @@ void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
     if (m_avPlayerItem)
         return;
 
-    INFO_LOG(LOGIDENTIFIER);
+    ALWAYS_LOG(LOGIDENTIFIER);
 
     setDelayCallbacks(true);
 
@@ -1112,20 +1111,22 @@ void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
     
 void MediaPlayerPrivateAVFoundationObjC::platformPlay()
 {
-    INFO_LOG(LOGIDENTIFIER);
     if (!metaDataAvailable())
         return;
 
+    ALWAYS_LOG(LOGIDENTIFIER);
+
     m_requestedPlaying = true;
     setPlayerRate(m_requestedRate);
 }
 
 void MediaPlayerPrivateAVFoundationObjC::platformPause()
 {
-    INFO_LOG(LOGIDENTIFIER);
     if (!metaDataAvailable())
         return;
 
+    ALWAYS_LOG(LOGIDENTIFIER);
+
     m_requestedPlaying = false;
     setPlayerRate(0);
 }
@@ -1215,6 +1216,8 @@ void MediaPlayerPrivateAVFoundationObjC::setVolume(float volume)
     if (!m_avPlayer)
         return;
 
+    ALWAYS_LOG(LOGIDENTIFIER, volume);
+
     [m_avPlayer.get() setVolume:volume];
 #endif
 }
@@ -1224,7 +1227,7 @@ void MediaPlayerPrivateAVFoundationObjC::setMuted(bool muted)
     if (m_muted == muted)
         return;
 
-    INFO_LOG(LOGIDENTIFIER, muted);
+    ALWAYS_LOG(LOGIDENTIFIER, muted);
 
     m_muted = muted;
 
@@ -1241,7 +1244,7 @@ void MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(bool closedCap
     if (!metaDataAvailable())
         return;
 
-    INFO_LOG(LOGIDENTIFIER, closedCaptionsVisible);
+    ALWAYS_LOG(LOGIDENTIFIER, closedCaptionsVisible);
 }
 
 void MediaPlayerPrivateAVFoundationObjC::setRateDouble(double rate)
@@ -2859,19 +2862,45 @@ void MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange(bool)
     updateStates();
 }
 
-void MediaPlayerPrivateAVFoundationObjC::setShouldBufferData(bool shouldBuffer)
+void MediaPlayerPrivateAVFoundationObjC::setBufferingPolicy(MediaPlayer::BufferingPolicy policy)
 {
-    INFO_LOG(LOGIDENTIFIER, shouldBuffer);
+    ALWAYS_LOG(LOGIDENTIFIER, policy);
 
-    if (m_shouldBufferData == shouldBuffer)
+    if (m_bufferingPolicy == policy)
         return;
 
-    m_shouldBufferData = shouldBuffer;
+    m_bufferingPolicy = policy;
     
     if (!m_avPlayer)
         return;
 
-    setAVPlayerItem(shouldBuffer ? m_avPlayerItem.get() : nil);
+#if HAVE(AVPLAYER_RESOURCE_CONSERVATION_LEVEL)
+    static_assert(static_cast<size_t>(MediaPlayer::BufferingPolicy::Default) == AVPlayerResourceConservationLevelNone, "MediaPlayer::BufferingPolicy::Default is not AVPlayerResourceConservationLevelNone as expected");
+    static_assert(static_cast<size_t>(MediaPlayer::BufferingPolicy::LimitReadAhead) == AVPlayerResourceConservationLevelReduceReadAhead, "MediaPlayer::BufferingPolicy::LimitReadAhead is not AVPlayerResourceConservationLevelReduceReadAhead as expected");
+    static_assert(static_cast<size_t>(MediaPlayer::BufferingPolicy::MakeResourcesPurgeable) == AVPlayerResourceConservationLevelReuseActivePlayerResources, "MediaPlayer::BufferingPolicy::MakeResourcesPurgeable is not AVPlayerResourceConservationLevelReuseActivePlayerResources as expected");
+    static_assert(static_cast<size_t>(MediaPlayer::BufferingPolicy::PurgeResources) == AVPlayerResourceConservationLevelRecycleBuffer, "MediaPlayer::BufferingPolicy::PurgeResources is not AVPlayerResourceConservationLevelRecycleBuffer as expected");
+
+    if ([m_avPlayer respondsToSelector:@selector(setResourceConservationLevelWhilePaused:)]) {
+        m_avPlayer.get().resourceConservationLevelWhilePaused = static_cast<AVPlayerResourceConservationLevel>(policy);
+        updateStates();
+        return;
+    }
+#endif
+
+    switch (policy) {
+    case MediaPlayer::BufferingPolicy::Default:
+        setAVPlayerItem(m_avPlayerItem.get());
+        break;
+    case MediaPlayer::BufferingPolicy::LimitReadAhead:
+    case MediaPlayer::BufferingPolicy::MakeResourcesPurgeable:
+        setAVPlayerItem(nil);
+        break;
+    case MediaPlayer::BufferingPolicy::PurgeResources:
+        setAVPlayerItem(nil);
+        setAVPlayerItem(m_avPlayerItem.get());
+        break;
+    }
+
     updateStates();
 }
 
index 7959411..ebdaeec 100644 (file)
@@ -154,7 +154,6 @@ private:
     
     void enqueueVideoSample(MediaStreamTrackPrivate&, MediaSample&);
     void enqueueCorrectedVideoSample(MediaSample&);
-    void flushAndRemoveVideoSampleBuffers();
     void requestNotificationWhenReadyForVideoData();
 
     void paint(GraphicsContext&, const FloatRect&) override;
@@ -174,7 +173,7 @@ private:
 
     bool ended() const override { return m_ended; }
 
-    void setShouldBufferData(bool) override;
+    void setBufferingPolicy(MediaPlayer::BufferingPolicy) override;
 
     MediaPlayer::ReadyState currentReadyState();
     void updateReadyState();
index 3c74b5e..ca3b113 100644 (file)
@@ -457,11 +457,6 @@ void MediaPlayerPrivateMediaStreamAVFObjC::flushRenderers()
         [m_sampleBufferDisplayLayer flush];
 }
 
-void MediaPlayerPrivateMediaStreamAVFObjC::flushAndRemoveVideoSampleBuffers()
-{
-    [m_sampleBufferDisplayLayer flushAndRemoveImage];
-}
-
 void MediaPlayerPrivateMediaStreamAVFObjC::ensureLayers()
 {
     if (m_sampleBufferDisplayLayer)
@@ -1149,10 +1144,10 @@ void MediaPlayerPrivateMediaStreamAVFObjC::setNetworkState(MediaPlayer::NetworkS
     m_player->networkStateChanged();
 }
 
-void MediaPlayerPrivateMediaStreamAVFObjC::setShouldBufferData(bool shouldBuffer)
+void MediaPlayerPrivateMediaStreamAVFObjC::setBufferingPolicy(MediaPlayer::BufferingPolicy policy)
 {
-    if (!shouldBuffer)
-        flushAndRemoveVideoSampleBuffers();
+    if (policy != MediaPlayer::BufferingPolicy::Default)
+        [m_sampleBufferDisplayLayer flushAndRemoveImage];
 }
 
 void MediaPlayerPrivateMediaStreamAVFObjC::scheduleDeferredTask(Function<void ()>&& function)
index 3b7614b..524e3b3 100644 (file)
@@ -3501,9 +3501,25 @@ void Internals::endSimulatedHDCPError(HTMLMediaElement& element)
 
 bool Internals::elementShouldBufferData(HTMLMediaElement& element)
 {
-    return element.shouldBufferData();
+    return element.bufferingPolicy() < MediaPlayer::BufferingPolicy::LimitReadAhead;
 }
 
+String Internals::elementBufferingPolicy(HTMLMediaElement& element)
+{
+    switch (element.bufferingPolicy()) {
+    case MediaPlayer::BufferingPolicy::Default:
+        return "Default";
+    case MediaPlayer::BufferingPolicy::LimitReadAhead:
+        return "LimitReadAhead";
+    case MediaPlayer::BufferingPolicy::MakeResourcesPurgeable:
+        return "MakeResourcesPurgeable";
+    case MediaPlayer::BufferingPolicy::PurgeResources:
+        return "PurgeResources";
+    }
+
+    ASSERT_NOT_REACHED();
+    return "UNKNOWN";
+}
 #endif
 
 bool Internals::isSelectPopupVisible(HTMLSelectElement& element)
index 7dc5276..e1f35f0 100644 (file)
@@ -540,6 +540,7 @@ public:
     void endSimulatedHDCPError(HTMLMediaElement&);
 
     bool elementShouldBufferData(HTMLMediaElement&);
+    String elementBufferingPolicy(HTMLMediaElement&);
 #endif
 
     bool isSelectPopupVisible(HTMLSelectElement&);
index 5c7b236..c7a340b 100644 (file)
@@ -528,6 +528,7 @@ enum CompositingPolicy {
     [Conditional=VIDEO] void endSimulatedHDCPError(HTMLMediaElement media);
 
     [Conditional=VIDEO] boolean elementShouldBufferData(HTMLMediaElement media);
+    [Conditional=VIDEO] DOMString elementBufferingPolicy(HTMLMediaElement media);
 
     [Conditional=LEGACY_ENCRYPTED_MEDIA] void initializeMockCDM();
     [Conditional=ENCRYPTED_MEDIA] MockCDMFactory registerMockCDM();
index d804c79..355d536 100644 (file)
@@ -1,3 +1,16 @@
+2019-05-07  Eric Carlson  <eric.carlson@apple.com>
+
+        Define media buffering policy
+        https://bugs.webkit.org/show_bug.cgi?id=196979
+        <rdar://problem/28383861>
+
+        Reviewed by Jer Noble.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/MediaBufferingPolicy.mm: Added.
+        (waitUntilBufferingPolicyIsEqualTo):
+        (TEST):
+
 2019-05-07  Alex Christensen  <achristensen@webkit.org>
 
         Add SPI to set a list of hosts to which to send custom header fields cross-origin
index ead63ce..578b61a 100644 (file)
@@ -32,6 +32,7 @@
                0799C3491EBA2D7B003B7532 /* UserMediaDisabled.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */; };
                0799C34B1EBA3301003B7532 /* disableGetUserMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */; };
                07C046CA1E4262A8007201E7 /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C046C91E42573E007201E7 /* CARingBuffer.cpp */; };
+               07CC7DFE2266330900E39181 /* MediaBufferingPolicy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07CC7DFD2266330800E39181 /* MediaBufferingPolicy.mm */; };
                07CD32F62065B5430064A4BE /* AVFoundationPreference.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07CD32F52065B5420064A4BE /* AVFoundationPreference.mm */; };
                07CE1CF31F06A7E000BF89F5 /* GetUserMediaNavigation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07CE1CF21F06A7E000BF89F5 /* GetUserMediaNavigation.mm */; };
                07E1F6A21FFC44FA0096C7EC /* getDisplayMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 07E1F6A11FFC44F90096C7EC /* getDisplayMedia.html */; };
                076E507E1F45031E006E9F5A /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = "<group>"; };
                0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = disableGetUserMedia.html; sourceTree = "<group>"; };
                07C046C91E42573E007201E7 /* CARingBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CARingBuffer.cpp; sourceTree = "<group>"; };
+               07CC7DFD2266330800E39181 /* MediaBufferingPolicy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaBufferingPolicy.mm; sourceTree = "<group>"; };
                07CD32F52065B5420064A4BE /* AVFoundationPreference.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AVFoundationPreference.mm; sourceTree = "<group>"; };
                07CD32F72065B72A0064A4BE /* video.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = video.html; sourceTree = "<group>"; };
                07CE1CF21F06A7E000BF89F5 /* GetUserMediaNavigation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GetUserMediaNavigation.mm; sourceTree = "<group>"; };
                                46C519D81D355A7300DAA51A /* LocalStorageNullEntries.mm */,
                                8C10AF96206467770018FD90 /* LocalStoragePersistence.mm */,
                                7A6A2C6F1DCCF87B00C0D085 /* LocalStorageQuirkTest.mm */,
+                               07CC7DFD2266330800E39181 /* MediaBufferingPolicy.mm */,
                                5165FE03201EE617009F7EC3 /* MessagePortProviders.mm */,
                                51CD1C6A1B38CE3600142CA5 /* ModalAlerts.mm */,
                                1ABC3DED1899BE6D004F0626 /* Navigation.mm */,
                                7A6A2C701DCCFA8C00C0D085 /* LocalStorageQuirkTest.mm in Sources */,
                                076E507F1F4513D6006E9F5A /* Logging.cpp in Sources */,
                                CE1866491F72E8F100A0CAB6 /* MarkedText.cpp in Sources */,
+                               07CC7DFE2266330900E39181 /* MediaBufferingPolicy.mm in Sources */,
                                CDA315981ED53651009F60D3 /* MediaPlaybackSleepAssertion.mm in Sources */,
                                CDC9442E1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm in Sources */,
                                7CCE7EB21A411A5100447C4C /* MemoryCacheAddImageToCacheIOS.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaBufferingPolicy.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaBufferingPolicy.mm
new file mode 100644 (file)
index 0000000..6c2d7b6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+
+#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
+
+static void waitUntilBufferingPolicyIsEqualTo(WKWebView* webView, const char* expected)
+{
+    NSString* observed;
+    int tries = 0;
+    do {
+        observed = [webView stringByEvaluatingJavaScript:@"window.internals.elementBufferingPolicy(document.querySelector('video'))"];
+        if ([observed isEqualToString:@(expected)])
+            break;
+
+        TestWebKitAPI::Util::sleep(0.1);
+    } while (++tries <= 100);
+
+    EXPECT_WK_STREQ(expected, observed);
+}
+
+TEST(WebKit, MediaBufferingPolicy)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
+    configuration.get().processPool = (WKProcessPool *)context.get();
+    configuration.get()._mediaDataLoadsAutomatically = YES;
+    configuration.get().mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
+
+    __block bool isPlaying = false;
+    [webView performAfterReceivingMessage:@"playing" action:^() { isPlaying = true; }];
+
+    [webView synchronouslyLoadTestPageNamed:@"video-with-audio"];
+    TestWebKitAPI::Util::run(&isPlaying);
+
+    waitUntilBufferingPolicyIsEqualTo(webView.get(), "Default");
+
+    [webView stringByEvaluatingJavaScript:@"document.querySelector('video').pause()"];
+
+    // Suspending the process also forces a memory warning, which should purge whatever possible ASAP.
+    [webView _processWillSuspendImminentlyForTesting];
+    waitUntilBufferingPolicyIsEqualTo(webView.get(), "PurgeResources");
+
+    // And should switch back to default when buffering is allowed.
+    [webView _processDidResumeForTesting];
+    waitUntilBufferingPolicyIsEqualTo(webView.get(), "Default");
+
+    // All resources should be marked as purgeable when the document is hidden.
+#if PLATFORM(MAC)
+    [webView.get().window setIsVisible:NO];
+#else
+    webView.get().window.hidden = YES;
+#endif
+    waitUntilBufferingPolicyIsEqualTo(webView.get(), "MakeResourcesPurgeable");
+
+#if PLATFORM(MAC)
+    [webView.get().window setIsVisible:YES];
+#else
+    webView.get().window.hidden = NO;
+#endif
+
+    // Policy should go back to default when playback starts.
+    isPlaying = false;
+    [webView stringByEvaluatingJavaScript:@"go()"];
+    TestWebKitAPI::Util::run(&isPlaying);
+
+    waitUntilBufferingPolicyIsEqualTo(webView.get(), "Default");
+}
+
+#endif // WK_HAVE_C_SPI