Title: [276870] trunk/Source/WebCore
Revision
276870
Author
[email protected]
Date
2021-04-30 19:11:51 -0700 (Fri, 30 Apr 2021)

Log Message

[Cocoa] Calling into -[AVPlayerItem currentTime] is very expensive
https://bugs.webkit.org/show_bug.cgi?id=225254

Reviewed by Eric Carlson.

Calling into -currentTime is an expensive operation that synchronously calls a shared
background thread, and so can block for potentially long periods of time. Instead,
AVPlayerItem offers an API which will push currentTime changes on a specified dispatch
queue. We can use that API to occasionally update a cached view of the item's currentTime
and combine that cached value with other cached states to accurately calculate an
approximation of the currentTime during playback.

* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::cancelLoad):
(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer):
(WebCore::MediaPlayerPrivateAVFoundationObjC::currentMediaTime const):
(WebCore::MediaPlayerPrivateAVFoundationObjC::currentMediaTimeDidChange):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setRateDouble):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setPlayerRate):
(WebCore::MediaPlayerPrivateAVFoundationObjC::timeControlStatusDidChange):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (276869 => 276870)


--- trunk/Source/WebCore/ChangeLog	2021-05-01 01:22:48 UTC (rev 276869)
+++ trunk/Source/WebCore/ChangeLog	2021-05-01 02:11:51 UTC (rev 276870)
@@ -1,3 +1,27 @@
+2021-04-30  Jer Noble  <[email protected]>
+
+        [Cocoa] Calling into -[AVPlayerItem currentTime] is very expensive
+        https://bugs.webkit.org/show_bug.cgi?id=225254
+
+        Reviewed by Eric Carlson.
+
+        Calling into -currentTime is an expensive operation that synchronously calls a shared
+        background thread, and so can block for potentially long periods of time. Instead,
+        AVPlayerItem offers an API which will push currentTime changes on a specified dispatch
+        queue. We can use that API to occasionally update a cached view of the item's currentTime
+        and combine that cached value with other cached states to accurately calculate an
+        approximation of the currentTime during playback.
+
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::cancelLoad):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::currentMediaTime const):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::currentMediaTimeDidChange):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::setRateDouble):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::setPlayerRate):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::timeControlStatusDidChange):
+
 2021-04-30  Cameron McCormack  <[email protected]>
 
         Defend further against a PlatformCALayer's owner becoming null in PlatformCALayerCocoa::drawLayerContents.

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


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h	2021-05-01 01:22:48 UTC (rev 276869)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h	2021-05-01 02:11:51 UTC (rev 276870)
@@ -321,6 +321,8 @@
     void setPreferredDynamicRangeMode(DynamicRangeMode) final;
     void audioOutputDeviceChanged() final;
 
+    void currentMediaTimeDidChange(MediaTime&&) const;
+
     RetainPtr<AVURLAsset> m_avAsset;
     RetainPtr<AVPlayer> m_avPlayer;
     RetainPtr<AVPlayerItem> m_avPlayerItem;
@@ -384,6 +386,7 @@
 
     RetainPtr<AVPlayerItemMetadataCollector> m_metadataCollector;
     RetainPtr<AVPlayerItemMetadataOutput> m_metadataOutput;
+    RetainPtr<id> m_currentTimeObserver;
 
     mutable RetainPtr<NSArray> m_cachedSeekableRanges;
     mutable RetainPtr<NSArray> m_cachedLoadedRanges;
@@ -391,6 +394,10 @@
     RetainPtr<NSArray> m_currentMetaData;
     FloatSize m_cachedPresentationSize;
     MediaTime m_cachedDuration;
+    mutable MediaTime m_cachedCurrentMediaTime;
+    mutable Optional<WallTime> m_wallClockAtCachedCurrentTime;
+    mutable int m_timeControlStatusAtCachedCurrentTime { 0 };
+    mutable double m_requestedRateAtCachedCurrentTime { 0 };
     RefPtr<SharedBuffer> m_keyID;
     double m_cachedRate { 0 };
     bool m_requestedPlaying { false };

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


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm	2021-05-01 01:22:48 UTC (rev 276869)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm	2021-05-01 02:11:51 UTC (rev 276870)
@@ -518,6 +518,11 @@
 #if !PLATFORM(IOS_FAMILY)
         [m_avPlayer setOutputContext:nil];
 #endif
+
+        if (m_currentTimeObserver)
+            [m_avPlayer removeTimeObserver:m_currentTimeObserver.get()];
+        m_currentTimeObserver = nil;
+
         m_avPlayer = nil;
     }
 
@@ -1100,6 +1105,12 @@
     }
 #endif
 
+    ASSERT(!m_currentTimeObserver);
+    m_currentTimeObserver = [m_avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 10) queue:dispatch_get_main_queue() usingBlock:[weakThis = makeWeakPtr(*this)] (CMTime time) {
+        if (weakThis)
+            weakThis->currentMediaTimeDidChange(PAL::toMediaTime(time));
+    }];
+
     setDelayCallbacks(false);
 }
 
@@ -1428,13 +1439,30 @@
     if (!metaDataAvailable() || !m_avPlayerItem)
         return MediaTime::zeroTime();
 
-    CMTime itemTime = [m_avPlayerItem.get() currentTime];
-    if (CMTIME_IS_NUMERIC(itemTime))
-        return std::max(PAL::toMediaTime(itemTime), MediaTime::zeroTime());
+    if (!m_wallClockAtCachedCurrentTime)
+        currentMediaTimeDidChange(toMediaTime([m_avPlayerItem.get() currentTime]));
+    ASSERT(m_wallClockAtCachedCurrentTime);
 
-    return MediaTime::zeroTime();
+    auto itemTime = m_cachedCurrentMediaTime;
+    if (!itemTime.isFinite())
+        return MediaTime::zeroTime();
+
+    if (m_timeControlStatusAtCachedCurrentTime == AVPlayerTimeControlStatusPlaying) {
+        auto elapsedMediaTime = (WallTime::now() - *m_wallClockAtCachedCurrentTime) * m_requestedRateAtCachedCurrentTime;
+        itemTime += MediaTime::createWithDouble(elapsedMediaTime.seconds());
+    }
+
+    return std::min(std::max(itemTime, MediaTime::zeroTime()), m_cachedDuration);
 }
 
+void MediaPlayerPrivateAVFoundationObjC::currentMediaTimeDidChange(WTF::MediaTime&& time) const
+{
+    m_cachedCurrentMediaTime = time;
+    m_wallClockAtCachedCurrentTime = WallTime::now();
+    m_timeControlStatusAtCachedCurrentTime = m_cachedTimeControlStatus;
+    m_requestedRateAtCachedCurrentTime = m_requestedRate;
+}
+
 void MediaPlayerPrivateAVFoundationObjC::seekToTime(const MediaTime& time, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance)
 {
     // setCurrentTime generates several event callbacks, update afterwards.
@@ -1508,6 +1536,7 @@
     m_requestedRate = rate;
     if (m_requestedPlaying)
         setPlayerRate(rate);
+    m_wallClockAtCachedCurrentTime = WTF::nullopt;
 }
 
 void MediaPlayerPrivateAVFoundationObjC::setPlayerRate(double rate)
@@ -1521,6 +1550,8 @@
     m_cachedTimeControlStatus = [m_avPlayer timeControlStatus];
     setShouldObserveTimeControlStatus(true);
     setDelayCallbacks(false);
+
+    m_wallClockAtCachedCurrentTime = WTF::nullopt;
 }
 
 double MediaPlayerPrivateAVFoundationObjC::rate() const
@@ -3345,6 +3376,7 @@
 
     m_cachedTimeControlStatus = timeControlStatus;
     rateChanged();
+    m_wallClockAtCachedCurrentTime = WTF::nullopt;
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
     if (!isCurrentPlaybackTargetWireless())
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to