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