Changeset 286984 in webkit
- Timestamp:
- Dec 13, 2021, 2:50:09 PM (5 years ago)
- Location:
- branches/safari-612-branch/Source/WebCore
- Files:
-
- 3 edited
-
ChangeLog (modified) (1 diff)
-
platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h (modified) (3 diffs)
-
platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/safari-612-branch/Source/WebCore/ChangeLog
r286977 r286984 1 2021-12-13 Alan Coon <alancoon@apple.com> 2 3 Cherry-pick r286907. rdar://problem/86307593 4 5 [Cocoa] -[AVPlayerItem liveUpdateInterval] can hang the main thread for ~60ms 6 https://bugs.webkit.org/show_bug.cgi?id=234131 7 8 Reviewed by Eric Carlson. 9 10 Direct property access of AVFoundation objects can take tens of milliseconds to return 11 a value, even for simple properties. This impacts scrolling responsiveness. 12 13 -liveUpdateInterval is not KVO-observable, but only changes when -seekableTimeRanges does 14 as well. Query and cache that property during KVO of -seekableTimeRanges. 15 16 * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h: 17 * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm: 18 (WebCore::MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesLastModifiedTime const): 19 (WebCore::MediaPlayerPrivateAVFoundationObjC::liveUpdateInterval const): 20 (WebCore::MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange): 21 (-[WebCoreAVFMovieObserver observeValueForKeyPath:ofObject:change:context:]): 22 23 git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286907 268f45cc-cd09-0410-ab3c-d52691b4dbfc 24 25 2021-12-11 Jer Noble <jer.noble@apple.com> 26 27 [Cocoa] -[AVPlayerItem liveUpdateInterval] can hang the main thread for ~60ms 28 https://bugs.webkit.org/show_bug.cgi?id=234131 29 30 Reviewed by Eric Carlson. 31 32 Direct property access of AVFoundation objects can take tens of milliseconds to return 33 a value, even for simple properties. This impacts scrolling responsiveness. 34 35 -liveUpdateInterval is not KVO-observable, but only changes when -seekableTimeRanges does 36 as well. Query and cache that property during KVO of -seekableTimeRanges. 37 38 * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h: 39 * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm: 40 (WebCore::MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesLastModifiedTime const): 41 (WebCore::MediaPlayerPrivateAVFoundationObjC::liveUpdateInterval const): 42 (WebCore::MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange): 43 (-[WebCoreAVFMovieObserver observeValueForKeyPath:ofObject:change:context:]): 44 1 45 2021-12-13 Alan Coon <alancoon@apple.com> 2 46 -
branches/safari-612-branch/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
r286237 r286984 51 51 typedef struct CGImage *CGImageRef; 52 52 typedef struct __CVBuffer *CVPixelBufferRef; 53 typedef double NSTimeInterval; 53 54 54 55 namespace WebCore { … … 97 98 void playbackBufferFullDidChange(bool); 98 99 void loadedTimeRangesDidChange(RetainPtr<NSArray>&&); 99 void seekableTimeRangesDidChange(RetainPtr<NSArray>&& );100 void seekableTimeRangesDidChange(RetainPtr<NSArray>&&, NSTimeInterval, NSTimeInterval); 100 101 void tracksDidChange(const RetainPtr<NSArray>&); 101 102 void hasEnabledAudioDidChange(bool); … … 442 443 bool m_waitForVideoOutputMediaDataWillChangeTimedOut { false }; 443 444 bool m_haveBeenAskedToPaint { false }; 445 mutable std::optional<NSTimeInterval> m_cachedSeekableTimeRangesLastModifiedTime; 446 mutable std::optional<NSTimeInterval> m_cachedLiveUpdateInterval; 444 447 }; 445 448 -
branches/safari-612-branch/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
r286237 r286984 98 98 #import <wtf/OSObjectPtr.h> 99 99 #import <wtf/URL.h> 100 #import <wtf/WorkQueue.h> 100 101 #import <wtf/cocoa/VectorCocoa.h> 101 102 #import <wtf/text/CString.h> … … 175 176 WeakPtr<MediaPlayerPrivateAVFoundationObjC> m_player; 176 177 int m_delayCallbacks; 178 RefPtr<WorkQueue> m_backgroundQueue; 177 179 } 178 180 -(id)initWithPlayer:(WeakPtr<MediaPlayerPrivateAVFoundationObjC>&&)callback; … … 1609 1611 { 1610 1612 #if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST) 1611 return [m_avPlayerItem seekableTimeRangesLastModifiedTime]; 1613 if (!m_cachedSeekableTimeRangesLastModifiedTime) 1614 m_cachedSeekableTimeRangesLastModifiedTime = [m_avPlayerItem seekableTimeRangesLastModifiedTime]; 1615 return *m_cachedSeekableTimeRangesLastModifiedTime; 1612 1616 #else 1613 1617 return 0; … … 1618 1622 { 1619 1623 #if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST) 1620 return [m_avPlayerItem liveUpdateInterval]; 1624 if (!m_cachedLiveUpdateInterval) 1625 m_cachedLiveUpdateInterval = [m_avPlayerItem liveUpdateInterval]; 1626 return *m_cachedLiveUpdateInterval; 1621 1627 #else 1622 1628 return 0; … … 3209 3215 } 3210 3216 3211 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray>&& seekableRanges )3217 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray>&& seekableRanges, NSTimeInterval seekableTimeRangesLastModifiedTime, NSTimeInterval liveUpdateInterval) 3212 3218 { 3213 3219 m_cachedSeekableRanges = WTFMove(seekableRanges); 3220 m_cachedSeekableTimeRangesLastModifiedTime = seekableTimeRangesLastModifiedTime; 3221 m_cachedLiveUpdateInterval = liveUpdateInterval; 3214 3222 3215 3223 seekableTimeRangesChanged(); … … 3659 3667 return nil; 3660 3668 m_player = WTFMove(player); 3669 m_backgroundQueue = WorkQueue::create("WebCoreAVFMovieObserver Background Queue"); 3661 3670 return self; 3662 3671 } … … 3696 3705 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context 3697 3706 { 3698 ensureOnMainThread([self, strongSelf = retainPtr(self), keyPath = retainPtr(keyPath), change = retainPtr(change), object = retainPtr(object), context]() mutable { 3699 if (!m_player) 3700 return; 3701 3702 m_player->queueTaskOnEventLoop([player = m_player, keyPath = WTFMove(keyPath), change = WTFMove(change), object = WTFMove(object), context] { 3703 if (!player) 3707 auto queueTaskOnEventLoopWithPlayer = [self, strongSelf = retainPtr(self)] (Function<void(MediaPlayerPrivateAVFoundationObjC&)>&& function) mutable { 3708 ensureOnMainThread([self, strongSelf = WTFMove(strongSelf), function = WTFMove(function)] () mutable { 3709 if (!m_player) 3704 3710 return; 3705 3711 3706 ScriptDisallowedScope::InMainThread scriptDisallowedScope; 3707 3708 id newValue = [change valueForKey:NSKeyValueChangeNewKey]; 3709 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; 3710 bool shouldLogValue = !willChange; 3711 3712 if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) { 3713 if ([keyPath isEqualToString:@"readyForDisplay"]) 3714 player->firstFrameAvailableDidChange([newValue boolValue]); 3715 } 3716 3717 if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) { 3718 if ([keyPath isEqualToString:@"enabled"]) 3719 player->trackEnabledDidChange([newValue boolValue]); 3720 } 3721 3722 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) { 3723 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3724 player->playbackLikelyToKeepUpWillChange(); 3725 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3726 player->playbackBufferEmptyWillChange(); 3727 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3728 player->playbackBufferFullWillChange(); 3729 } 3730 3731 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) { 3732 // A value changed for an AVPlayerItem 3733 if ([keyPath isEqualToString:@"status"]) 3734 player->playerItemStatusDidChange([newValue intValue]); 3735 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3736 player->playbackLikelyToKeepUpDidChange([newValue boolValue]); 3737 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3738 player->playbackBufferEmptyDidChange([newValue boolValue]); 3739 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3740 player->playbackBufferFullDidChange([newValue boolValue]); 3741 else if ([keyPath isEqualToString:@"asset"]) { 3742 player->setAsset(RetainPtr<id>(newValue)); 3743 shouldLogValue = false; 3744 } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) 3745 player->loadedTimeRangesDidChange(RetainPtr<NSArray>(newValue)); 3746 else if ([keyPath isEqualToString:@"seekableTimeRanges"]) 3747 player->seekableTimeRangesDidChange(RetainPtr<NSArray>(newValue)); 3748 else if ([keyPath isEqualToString:@"tracks"]) { 3749 player->tracksDidChange(RetainPtr<NSArray>(newValue)); 3750 shouldLogValue = false; 3751 } else if ([keyPath isEqualToString:@"hasEnabledAudio"]) 3752 player->hasEnabledAudioDidChange([newValue boolValue]); 3753 else if ([keyPath isEqualToString:@"presentationSize"]) 3754 player->presentationSizeDidChange(FloatSize([newValue sizeValue])); 3755 else if ([keyPath isEqualToString:@"duration"]) 3756 player->durationDidChange(PAL::toMediaTime([newValue CMTimeValue])); 3757 else if ([keyPath isEqualToString:@"canPlayFastReverse"]) 3758 player->canPlayFastReverseDidChange([newValue boolValue]); 3759 else if ([keyPath isEqualToString:@"canPlayFastForward"]) 3760 player->canPlayFastForwardDidChange([newValue boolValue]); 3761 } 3762 3763 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) { 3764 // A value changed for an AVPlayer. 3765 if ([keyPath isEqualToString:@"rate"]) 3766 player->rateDidChange([newValue doubleValue]); 3767 else if ([keyPath isEqualToString:@"timeControlStatus"]) 3768 player->timeControlStatusDidChange([newValue intValue]); 3712 m_player->queueTaskOnEventLoop([player = m_player, function = WTFMove(function)] { 3713 if (!player) 3714 return; 3715 3716 ScriptDisallowedScope::InMainThread scriptDisallowedScope; 3717 function(*player); 3718 }); 3719 }); 3720 }; 3721 3722 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && [keyPath isEqualToString:@"seekableTimeRanges"]) { 3723 // -liveUpdateInterval and -seekableTimeRangesLastModifiedTime are not KVO observable, but may also hang when queried. 3724 // Query their values here on a background thread, and pass to the main thread for caching. 3725 id newValue = [change valueForKey:NSKeyValueChangeNewKey]; 3726 auto seekableTimeRanges = RetainPtr<NSArray> { newValue }; 3727 3728 m_backgroundQueue->dispatch([seekableTimeRanges = WTFMove(seekableTimeRanges), playerItem = RetainPtr<AVPlayerItem> { object }, queueTaskOnEventLoopWithPlayer] () mutable { 3729 auto seekableTimeRangesLastModifiedTime = [playerItem seekableTimeRangesLastModifiedTime]; 3730 auto liveUpdateInterval = [playerItem liveUpdateInterval]; 3731 queueTaskOnEventLoopWithPlayer([seekableTimeRanges = WTFMove(seekableTimeRanges), seekableTimeRangesLastModifiedTime, liveUpdateInterval] (auto& player) mutable { 3732 player.seekableTimeRangesDidChange(WTFMove(seekableTimeRanges), seekableTimeRangesLastModifiedTime, liveUpdateInterval); 3733 }); 3734 }); 3735 } 3736 3737 queueTaskOnEventLoopWithPlayer([keyPath = RetainPtr { keyPath }, change = RetainPtr { change }, object = RetainPtr { object }, context] (auto& player) mutable { 3738 id newValue = [change valueForKey:NSKeyValueChangeNewKey]; 3739 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; 3740 bool shouldLogValue = !willChange; 3741 3742 if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) { 3743 if ([keyPath isEqualToString:@"readyForDisplay"]) 3744 player.firstFrameAvailableDidChange([newValue boolValue]); 3745 } 3746 3747 if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) { 3748 if ([keyPath isEqualToString:@"enabled"]) 3749 player.trackEnabledDidChange([newValue boolValue]); 3750 } 3751 3752 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) { 3753 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3754 player.playbackLikelyToKeepUpWillChange(); 3755 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3756 player.playbackBufferEmptyWillChange(); 3757 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3758 player.playbackBufferFullWillChange(); 3759 } 3760 3761 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) { 3762 // A value changed for an AVPlayerItem 3763 if ([keyPath isEqualToString:@"status"]) 3764 player.playerItemStatusDidChange([newValue intValue]); 3765 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3766 player.playbackLikelyToKeepUpDidChange([newValue boolValue]); 3767 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3768 player.playbackBufferEmptyDidChange([newValue boolValue]); 3769 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3770 player.playbackBufferFullDidChange([newValue boolValue]); 3771 else if ([keyPath isEqualToString:@"asset"]) { 3772 player.setAsset(RetainPtr<id>(newValue)); 3773 shouldLogValue = false; 3774 } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) 3775 player.loadedTimeRangesDidChange(RetainPtr<NSArray>(newValue)); 3776 else if ([keyPath isEqualToString:@"tracks"]) { 3777 player.tracksDidChange(RetainPtr<NSArray>(newValue)); 3778 shouldLogValue = false; 3779 } else if ([keyPath isEqualToString:@"hasEnabledAudio"]) 3780 player.hasEnabledAudioDidChange([newValue boolValue]); 3781 else if ([keyPath isEqualToString:@"presentationSize"]) 3782 player.presentationSizeDidChange(FloatSize([newValue sizeValue])); 3783 else if ([keyPath isEqualToString:@"duration"]) 3784 player.durationDidChange(PAL::toMediaTime([newValue CMTimeValue])); 3785 else if ([keyPath isEqualToString:@"canPlayFastReverse"]) 3786 player.canPlayFastReverseDidChange([newValue boolValue]); 3787 else if ([keyPath isEqualToString:@"canPlayFastForward"]) 3788 player.canPlayFastForwardDidChange([newValue boolValue]); 3789 } 3790 3791 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) { 3792 // A value changed for an AVPlayer. 3793 if ([keyPath isEqualToString:@"rate"]) 3794 player.rateDidChange([newValue doubleValue]); 3795 else if ([keyPath isEqualToString:@"timeControlStatus"]) 3796 player.timeControlStatusDidChange([newValue intValue]); 3769 3797 #if ENABLE(WIRELESS_PLAYBACK_TARGET) 3770 else if ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"allowsExternalPlayback"])3771 player->playbackTargetIsWirelessDidChange();3798 else if ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"allowsExternalPlayback"]) 3799 player.playbackTargetIsWirelessDidChange(); 3772 3800 #endif 3773 3801 #if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) 3774 else if ([keyPath isEqualToString:@"outputObscuredDueToInsufficientExternalProtection"]) 3775 player->outputObscuredDueToInsufficientExternalProtectionChanged([newValue boolValue]); 3776 #endif 3777 } 3778 3779 if (player->logger().willLog(player->logChannel(), WTFLogLevel::Debug) && !([keyPath isEqualToString:@"loadedTimeRanges"] || [keyPath isEqualToString:@"seekableTimeRanges"])) { 3780 auto identifier = Logger::LogSiteIdentifier("MediaPlayerPrivateAVFoundation", "observeValueForKeyPath", player->logIdentifier()); 3781 3782 if (shouldLogValue) { 3783 if ([keyPath isEqualToString:@"duration"]) 3784 player->logger().debug(player->logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", PAL::toMediaTime([newValue CMTimeValue])); 3785 else { 3786 RetainPtr<NSString> valueString = adoptNS([[NSString alloc] initWithFormat:@"%@", newValue]); 3787 player->logger().debug(player->logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", [valueString.get() UTF8String]); 3788 } 3789 } else 3790 player->logger().debug(player->logChannel(), identifier, willChange ? "will" : "did", " change '", [keyPath UTF8String], "'"); 3791 } 3792 }); 3802 else if ([keyPath isEqualToString:@"outputObscuredDueToInsufficientExternalProtection"]) 3803 player.outputObscuredDueToInsufficientExternalProtectionChanged([newValue boolValue]); 3804 #endif 3805 } 3806 3807 if (player.logger().willLog(player.logChannel(), WTFLogLevel::Debug) && !([keyPath isEqualToString:@"loadedTimeRanges"] || [keyPath isEqualToString:@"seekableTimeRanges"])) { 3808 auto identifier = Logger::LogSiteIdentifier("MediaPlayerPrivateAVFoundation", "observeValueForKeyPath", player.logIdentifier()); 3809 3810 if (shouldLogValue) { 3811 if ([keyPath isEqualToString:@"duration"]) 3812 player.logger().debug(player.logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", PAL::toMediaTime([newValue CMTimeValue])); 3813 else { 3814 RetainPtr<NSString> valueString = adoptNS([[NSString alloc] initWithFormat:@"%@", [newValue description]]); 3815 player.logger().debug(player.logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", [valueString UTF8String]); 3816 } 3817 } else 3818 player.logger().debug(player.logChannel(), identifier, willChange ? "will" : "did", " change '", [keyPath UTF8String], "'"); 3819 } 3793 3820 }); 3794 3821 }
Note:
See TracChangeset
for help on using the changeset viewer.