Changeset 276870 in webkit


Ignore:
Timestamp:
Apr 30, 2021 7:11:51 PM (3 years ago)
Author:
jer.noble@apple.com
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):

Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r276867 r276870  
     12021-04-30  Jer Noble  <jer.noble@apple.com>
     2
     3        [Cocoa] Calling into -[AVPlayerItem currentTime] is very expensive
     4        https://bugs.webkit.org/show_bug.cgi?id=225254
     5
     6        Reviewed by Eric Carlson.
     7
     8        Calling into -currentTime is an expensive operation that synchronously calls a shared
     9        background thread, and so can block for potentially long periods of time. Instead,
     10        AVPlayerItem offers an API which will push currentTime changes on a specified dispatch
     11        queue. We can use that API to occasionally update a cached view of the item's currentTime
     12        and combine that cached value with other cached states to accurately calculate an
     13        approximation of the currentTime during playback.
     14
     15        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
     16        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
     17        (WebCore::MediaPlayerPrivateAVFoundationObjC::cancelLoad):
     18        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer):
     19        (WebCore::MediaPlayerPrivateAVFoundationObjC::currentMediaTime const):
     20        (WebCore::MediaPlayerPrivateAVFoundationObjC::currentMediaTimeDidChange):
     21        (WebCore::MediaPlayerPrivateAVFoundationObjC::setRateDouble):
     22        (WebCore::MediaPlayerPrivateAVFoundationObjC::setPlayerRate):
     23        (WebCore::MediaPlayerPrivateAVFoundationObjC::timeControlStatusDidChange):
     24
    1252021-04-30  Cameron McCormack  <heycam@apple.com>
    226
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h

    r274021 r276870  
    322322    void audioOutputDeviceChanged() final;
    323323
     324    void currentMediaTimeDidChange(MediaTime&&) const;
     325
    324326    RetainPtr<AVURLAsset> m_avAsset;
    325327    RetainPtr<AVPlayer> m_avPlayer;
     
    385387    RetainPtr<AVPlayerItemMetadataCollector> m_metadataCollector;
    386388    RetainPtr<AVPlayerItemMetadataOutput> m_metadataOutput;
     389    RetainPtr<id> m_currentTimeObserver;
    387390
    388391    mutable RetainPtr<NSArray> m_cachedSeekableRanges;
     
    392395    FloatSize m_cachedPresentationSize;
    393396    MediaTime m_cachedDuration;
     397    mutable MediaTime m_cachedCurrentMediaTime;
     398    mutable Optional<WallTime> m_wallClockAtCachedCurrentTime;
     399    mutable int m_timeControlStatusAtCachedCurrentTime { 0 };
     400    mutable double m_requestedRateAtCachedCurrentTime { 0 };
    394401    RefPtr<SharedBuffer> m_keyID;
    395402    double m_cachedRate { 0 };
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm

    r276621 r276870  
    519519        [m_avPlayer setOutputContext:nil];
    520520#endif
     521
     522        if (m_currentTimeObserver)
     523            [m_avPlayer removeTimeObserver:m_currentTimeObserver.get()];
     524        m_currentTimeObserver = nil;
     525
    521526        m_avPlayer = nil;
    522527    }
     
    11011106#endif
    11021107
     1108    ASSERT(!m_currentTimeObserver);
     1109    m_currentTimeObserver = [m_avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 10) queue:dispatch_get_main_queue() usingBlock:[weakThis = makeWeakPtr(*this)] (CMTime time) {
     1110        if (weakThis)
     1111            weakThis->currentMediaTimeDidChange(PAL::toMediaTime(time));
     1112    }];
     1113
    11031114    setDelayCallbacks(false);
    11041115}
     
    14291440        return MediaTime::zeroTime();
    14301441
    1431     CMTime itemTime = [m_avPlayerItem.get() currentTime];
    1432     if (CMTIME_IS_NUMERIC(itemTime))
    1433         return std::max(PAL::toMediaTime(itemTime), MediaTime::zeroTime());
    1434 
    1435     return MediaTime::zeroTime();
     1442    if (!m_wallClockAtCachedCurrentTime)
     1443        currentMediaTimeDidChange(toMediaTime([m_avPlayerItem.get() currentTime]));
     1444    ASSERT(m_wallClockAtCachedCurrentTime);
     1445
     1446    auto itemTime = m_cachedCurrentMediaTime;
     1447    if (!itemTime.isFinite())
     1448        return MediaTime::zeroTime();
     1449
     1450    if (m_timeControlStatusAtCachedCurrentTime == AVPlayerTimeControlStatusPlaying) {
     1451        auto elapsedMediaTime = (WallTime::now() - *m_wallClockAtCachedCurrentTime) * m_requestedRateAtCachedCurrentTime;
     1452        itemTime += MediaTime::createWithDouble(elapsedMediaTime.seconds());
     1453    }
     1454
     1455    return std::min(std::max(itemTime, MediaTime::zeroTime()), m_cachedDuration);
     1456}
     1457
     1458void MediaPlayerPrivateAVFoundationObjC::currentMediaTimeDidChange(WTF::MediaTime&& time) const
     1459{
     1460    m_cachedCurrentMediaTime = time;
     1461    m_wallClockAtCachedCurrentTime = WallTime::now();
     1462    m_timeControlStatusAtCachedCurrentTime = m_cachedTimeControlStatus;
     1463    m_requestedRateAtCachedCurrentTime = m_requestedRate;
    14361464}
    14371465
     
    15091537    if (m_requestedPlaying)
    15101538        setPlayerRate(rate);
     1539    m_wallClockAtCachedCurrentTime = WTF::nullopt;
    15111540}
    15121541
     
    15221551    setShouldObserveTimeControlStatus(true);
    15231552    setDelayCallbacks(false);
     1553
     1554    m_wallClockAtCachedCurrentTime = WTF::nullopt;
    15241555}
    15251556
     
    33463377    m_cachedTimeControlStatus = timeControlStatus;
    33473378    rateChanged();
     3379    m_wallClockAtCachedCurrentTime = WTF::nullopt;
    33483380
    33493381#if ENABLE(WIRELESS_PLAYBACK_TARGET)
Note: See TracChangeset for help on using the changeset viewer.