Changeset 286907 in webkit
- Timestamp:
- Dec 11, 2021, 12:47:27 AM (5 years ago)
- Location:
- trunk/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
-
trunk/Source/WebCore/ChangeLog
r286905 r286907 1 2021-12-11 Jer Noble <jer.noble@apple.com> 2 3 [Cocoa] -[AVPlayerItem liveUpdateInterval] can hang the main thread for ~60ms 4 https://bugs.webkit.org/show_bug.cgi?id=234131 5 6 Reviewed by Eric Carlson. 7 8 Direct property access of AVFoundation objects can take tens of milliseconds to return 9 a value, even for simple properties. This impacts scrolling responsiveness. 10 11 -liveUpdateInterval is not KVO-observable, but only changes when -seekableTimeRanges does 12 as well. Query and cache that property during KVO of -seekableTimeRanges. 13 14 * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h: 15 * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm: 16 (WebCore::MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesLastModifiedTime const): 17 (WebCore::MediaPlayerPrivateAVFoundationObjC::liveUpdateInterval const): 18 (WebCore::MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange): 19 (-[WebCoreAVFMovieObserver observeValueForKeyPath:ofObject:change:context:]): 20 1 21 2021-12-10 Simon Fraser <simon.fraser@apple.com> 2 22 -
trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
r285993 r286907 54 54 typedef struct __CVBuffer *CVPixelBufferRef; 55 55 typedef NSString *AVMediaCharacteristic; 56 typedef double NSTimeInterval; 56 57 57 58 namespace WebCore { … … 102 103 void playbackBufferFullDidChange(bool); 103 104 void loadedTimeRangesDidChange(RetainPtr<NSArray>&&); 104 void seekableTimeRangesDidChange(RetainPtr<NSArray>&& );105 void seekableTimeRangesDidChange(RetainPtr<NSArray>&&, NSTimeInterval, NSTimeInterval); 105 106 void tracksDidChange(const RetainPtr<NSArray>&); 106 107 void hasEnabledAudioDidChange(bool); … … 462 463 bool m_isGatheringVideoFrameMetadata { false }; 463 464 std::optional<VideoFrameMetadata> m_videoFrameMetadata; 465 mutable std::optional<NSTimeInterval> m_cachedSeekableTimeRangesLastModifiedTime; 466 mutable std::optional<NSTimeInterval> m_cachedLiveUpdateInterval; 464 467 }; 465 468 -
trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
r286772 r286907 103 103 #import <wtf/OSObjectPtr.h> 104 104 #import <wtf/URL.h> 105 #import <wtf/WorkQueue.h> 105 106 #import <wtf/cocoa/VectorCocoa.h> 106 107 #import <wtf/text/CString.h> … … 184 185 WeakPtr<MediaPlayerPrivateAVFoundationObjC> m_player; 185 186 int m_delayCallbacks; 187 RefPtr<WorkQueue> m_backgroundQueue; 186 188 } 187 189 -(id)initWithPlayer:(WeakPtr<MediaPlayerPrivateAVFoundationObjC>&&)callback; … … 1678 1680 { 1679 1681 #if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST) 1680 return [m_avPlayerItem seekableTimeRangesLastModifiedTime]; 1682 if (!m_cachedSeekableTimeRangesLastModifiedTime) 1683 m_cachedSeekableTimeRangesLastModifiedTime = [m_avPlayerItem seekableTimeRangesLastModifiedTime]; 1684 return *m_cachedSeekableTimeRangesLastModifiedTime; 1681 1685 #else 1682 1686 return 0; … … 1687 1691 { 1688 1692 #if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST) 1689 return [m_avPlayerItem liveUpdateInterval]; 1693 if (!m_cachedLiveUpdateInterval) 1694 m_cachedLiveUpdateInterval = [m_avPlayerItem liveUpdateInterval]; 1695 return *m_cachedLiveUpdateInterval; 1690 1696 #else 1691 1697 return 0; … … 3328 3334 } 3329 3335 3330 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray>&& seekableRanges )3336 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray>&& seekableRanges, NSTimeInterval seekableTimeRangesLastModifiedTime, NSTimeInterval liveUpdateInterval) 3331 3337 { 3332 3338 m_cachedSeekableRanges = WTFMove(seekableRanges); 3339 m_cachedSeekableTimeRangesLastModifiedTime = seekableTimeRangesLastModifiedTime; 3340 m_cachedLiveUpdateInterval = liveUpdateInterval; 3333 3341 3334 3342 seekableTimeRangesChanged(); … … 3780 3788 return nil; 3781 3789 m_player = WTFMove(player); 3790 m_backgroundQueue = WorkQueue::create("WebCoreAVFMovieObserver Background Queue"); 3782 3791 return self; 3783 3792 } … … 3831 3840 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context 3832 3841 { 3833 ensureOnMainThread([self, strongSelf = retainPtr(self), keyPath = retainPtr(keyPath), change = retainPtr(change), object = retainPtr(object), context]() mutable { 3834 if (!m_player) 3835 return; 3836 3837 m_player->queueTaskOnEventLoop([player = m_player, keyPath = WTFMove(keyPath), change = WTFMove(change), object = WTFMove(object), context] { 3838 if (!player) 3842 auto queueTaskOnEventLoopWithPlayer = [self, strongSelf = retainPtr(self)] (Function<void(MediaPlayerPrivateAVFoundationObjC&)>&& function) mutable { 3843 ensureOnMainThread([self, strongSelf = WTFMove(strongSelf), function = WTFMove(function)] () mutable { 3844 if (!m_player) 3839 3845 return; 3840 3846 3841 ScriptDisallowedScope::InMainThread scriptDisallowedScope; 3842 3843 id newValue = [change valueForKey:NSKeyValueChangeNewKey]; 3844 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; 3845 bool shouldLogValue = !willChange; 3846 3847 if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) { 3848 if ([keyPath isEqualToString:@"readyForDisplay"]) 3849 player->firstFrameAvailableDidChange([newValue boolValue]); 3850 } 3851 3852 if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) { 3853 if ([keyPath isEqualToString:@"enabled"]) 3854 player->trackEnabledDidChange([newValue boolValue]); 3855 } 3856 3857 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) { 3858 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3859 player->playbackLikelyToKeepUpWillChange(); 3860 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3861 player->playbackBufferEmptyWillChange(); 3862 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3863 player->playbackBufferFullWillChange(); 3864 } 3865 3866 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) { 3867 // A value changed for an AVPlayerItem 3868 if ([keyPath isEqualToString:@"status"]) 3869 player->playerItemStatusDidChange([newValue intValue]); 3870 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3871 player->playbackLikelyToKeepUpDidChange([newValue boolValue]); 3872 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3873 player->playbackBufferEmptyDidChange([newValue boolValue]); 3874 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3875 player->playbackBufferFullDidChange([newValue boolValue]); 3876 else if ([keyPath isEqualToString:@"asset"]) { 3877 player->setAsset(RetainPtr<id>(newValue)); 3878 shouldLogValue = false; 3879 } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) 3880 player->loadedTimeRangesDidChange(RetainPtr<NSArray>(newValue)); 3881 else if ([keyPath isEqualToString:@"seekableTimeRanges"]) 3882 player->seekableTimeRangesDidChange(RetainPtr<NSArray>(newValue)); 3883 else if ([keyPath isEqualToString:@"tracks"]) { 3884 player->tracksDidChange(RetainPtr<NSArray>(newValue)); 3885 shouldLogValue = false; 3886 } else if ([keyPath isEqualToString:@"hasEnabledAudio"]) 3887 player->hasEnabledAudioDidChange([newValue boolValue]); 3888 else if ([keyPath isEqualToString:@"presentationSize"]) 3889 player->presentationSizeDidChange(FloatSize([newValue sizeValue])); 3890 else if ([keyPath isEqualToString:@"duration"]) 3891 player->durationDidChange(PAL::toMediaTime([newValue CMTimeValue])); 3892 else if ([keyPath isEqualToString:@"canPlayFastReverse"]) 3893 player->canPlayFastReverseDidChange([newValue boolValue]); 3894 else if ([keyPath isEqualToString:@"canPlayFastForward"]) 3895 player->canPlayFastForwardDidChange([newValue boolValue]); 3896 } 3897 3898 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) { 3899 // A value changed for an AVPlayer. 3900 if ([keyPath isEqualToString:@"rate"]) 3901 player->rateDidChange([newValue doubleValue]); 3902 else if ([keyPath isEqualToString:@"timeControlStatus"]) 3903 player->timeControlStatusDidChange([newValue intValue]); 3847 m_player->queueTaskOnEventLoop([player = m_player, function = WTFMove(function)] { 3848 if (!player) 3849 return; 3850 3851 ScriptDisallowedScope::InMainThread scriptDisallowedScope; 3852 function(*player); 3853 }); 3854 }); 3855 }; 3856 3857 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && [keyPath isEqualToString:@"seekableTimeRanges"]) { 3858 // -liveUpdateInterval and -seekableTimeRangesLastModifiedTime are not KVO observable, but may also hang when queried. 3859 // Query their values here on a background thread, and pass to the main thread for caching. 3860 id newValue = [change valueForKey:NSKeyValueChangeNewKey]; 3861 auto seekableTimeRanges = RetainPtr<NSArray> { newValue }; 3862 3863 m_backgroundQueue->dispatch([seekableTimeRanges = WTFMove(seekableTimeRanges), playerItem = RetainPtr<AVPlayerItem> { object }, queueTaskOnEventLoopWithPlayer] () mutable { 3864 auto seekableTimeRangesLastModifiedTime = [playerItem seekableTimeRangesLastModifiedTime]; 3865 auto liveUpdateInterval = [playerItem liveUpdateInterval]; 3866 queueTaskOnEventLoopWithPlayer([seekableTimeRanges = WTFMove(seekableTimeRanges), seekableTimeRangesLastModifiedTime, liveUpdateInterval] (auto& player) mutable { 3867 player.seekableTimeRangesDidChange(WTFMove(seekableTimeRanges), seekableTimeRangesLastModifiedTime, liveUpdateInterval); 3868 }); 3869 }); 3870 } 3871 3872 queueTaskOnEventLoopWithPlayer([keyPath = RetainPtr { keyPath }, change = RetainPtr { change }, object = RetainPtr { object }, context] (auto& player) mutable { 3873 id newValue = [change valueForKey:NSKeyValueChangeNewKey]; 3874 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; 3875 bool shouldLogValue = !willChange; 3876 3877 if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) { 3878 if ([keyPath isEqualToString:@"readyForDisplay"]) 3879 player.firstFrameAvailableDidChange([newValue boolValue]); 3880 } 3881 3882 if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) { 3883 if ([keyPath isEqualToString:@"enabled"]) 3884 player.trackEnabledDidChange([newValue boolValue]); 3885 } 3886 3887 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) { 3888 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3889 player.playbackLikelyToKeepUpWillChange(); 3890 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3891 player.playbackBufferEmptyWillChange(); 3892 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3893 player.playbackBufferFullWillChange(); 3894 } 3895 3896 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) { 3897 // A value changed for an AVPlayerItem 3898 if ([keyPath isEqualToString:@"status"]) 3899 player.playerItemStatusDidChange([newValue intValue]); 3900 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) 3901 player.playbackLikelyToKeepUpDidChange([newValue boolValue]); 3902 else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) 3903 player.playbackBufferEmptyDidChange([newValue boolValue]); 3904 else if ([keyPath isEqualToString:@"playbackBufferFull"]) 3905 player.playbackBufferFullDidChange([newValue boolValue]); 3906 else if ([keyPath isEqualToString:@"asset"]) { 3907 player.setAsset(RetainPtr<id>(newValue)); 3908 shouldLogValue = false; 3909 } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) 3910 player.loadedTimeRangesDidChange(RetainPtr<NSArray>(newValue)); 3911 else if ([keyPath isEqualToString:@"tracks"]) { 3912 player.tracksDidChange(RetainPtr<NSArray>(newValue)); 3913 shouldLogValue = false; 3914 } else if ([keyPath isEqualToString:@"hasEnabledAudio"]) 3915 player.hasEnabledAudioDidChange([newValue boolValue]); 3916 else if ([keyPath isEqualToString:@"presentationSize"]) 3917 player.presentationSizeDidChange(FloatSize([newValue sizeValue])); 3918 else if ([keyPath isEqualToString:@"duration"]) 3919 player.durationDidChange(PAL::toMediaTime([newValue CMTimeValue])); 3920 else if ([keyPath isEqualToString:@"canPlayFastReverse"]) 3921 player.canPlayFastReverseDidChange([newValue boolValue]); 3922 else if ([keyPath isEqualToString:@"canPlayFastForward"]) 3923 player.canPlayFastForwardDidChange([newValue boolValue]); 3924 } 3925 3926 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) { 3927 // A value changed for an AVPlayer. 3928 if ([keyPath isEqualToString:@"rate"]) 3929 player.rateDidChange([newValue doubleValue]); 3930 else if ([keyPath isEqualToString:@"timeControlStatus"]) 3931 player.timeControlStatusDidChange([newValue intValue]); 3904 3932 #if ENABLE(WIRELESS_PLAYBACK_TARGET) 3905 else if ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"allowsExternalPlayback"])3906 player->playbackTargetIsWirelessDidChange();3933 else if ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"allowsExternalPlayback"]) 3934 player.playbackTargetIsWirelessDidChange(); 3907 3935 #endif 3908 3936 #if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) 3909 else if ([keyPath isEqualToString:@"outputObscuredDueToInsufficientExternalProtection"]) 3910 player->outputObscuredDueToInsufficientExternalProtectionChanged([newValue boolValue]); 3911 #endif 3912 } 3913 3914 if (player->logger().willLog(player->logChannel(), WTFLogLevel::Debug) && !([keyPath isEqualToString:@"loadedTimeRanges"] || [keyPath isEqualToString:@"seekableTimeRanges"])) { 3915 auto identifier = Logger::LogSiteIdentifier("MediaPlayerPrivateAVFoundation", "observeValueForKeyPath", player->logIdentifier()); 3916 3917 if (shouldLogValue) { 3918 if ([keyPath isEqualToString:@"duration"]) 3919 player->logger().debug(player->logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", PAL::toMediaTime([newValue CMTimeValue])); 3920 else { 3921 RetainPtr<NSString> valueString = adoptNS([[NSString alloc] initWithFormat:@"%@", newValue]); 3922 player->logger().debug(player->logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", [valueString UTF8String]); 3923 } 3924 } else 3925 player->logger().debug(player->logChannel(), identifier, willChange ? "will" : "did", " change '", [keyPath UTF8String], "'"); 3926 } 3927 }); 3937 else if ([keyPath isEqualToString:@"outputObscuredDueToInsufficientExternalProtection"]) 3938 player.outputObscuredDueToInsufficientExternalProtectionChanged([newValue boolValue]); 3939 #endif 3940 } 3941 3942 if (player.logger().willLog(player.logChannel(), WTFLogLevel::Debug) && !([keyPath isEqualToString:@"loadedTimeRanges"] || [keyPath isEqualToString:@"seekableTimeRanges"])) { 3943 auto identifier = Logger::LogSiteIdentifier("MediaPlayerPrivateAVFoundation", "observeValueForKeyPath", player.logIdentifier()); 3944 3945 if (shouldLogValue) { 3946 if ([keyPath isEqualToString:@"duration"]) 3947 player.logger().debug(player.logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", PAL::toMediaTime([newValue CMTimeValue])); 3948 else { 3949 RetainPtr<NSString> valueString = adoptNS([[NSString alloc] initWithFormat:@"%@", [newValue description]]); 3950 player.logger().debug(player.logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", [valueString UTF8String]); 3951 } 3952 } else 3953 player.logger().debug(player.logChannel(), identifier, willChange ? "will" : "did", " change '", [keyPath UTF8String], "'"); 3954 } 3928 3955 }); 3929 3956 }
Note:
See TracChangeset
for help on using the changeset viewer.