⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 239857 in webkit


Ignore:
Timestamp:
Jan 10, 2019, 8:46:23 PM (8 years ago)
Author:
jer.noble@apple.com
Message:

<video> elements do not enter 'paused' state when playing to end over AirPlay
​https://bugs.webkit.org/show_bug.cgi?id=193295
<rdar://problem/46708670>

Reviewed by Eric Carlson.

Adopt the -[AVPlayer timeControlStatus] API, which reports whether the AVPlayer is paused, playing, or blocked waiting
for more data before playing. AirPlay devices report this state back from the remote device, and this allows the
MediaPlayerPrivateAVFoundationObjC to differentiate between user-generated pauses and simple stalling.

Adopting this API allows us to remove the heuristic from rateChanged() which inteprets a rate change when the
readyState > HAVE_ENOUGH as an intentional pause.

Drive-by fix: MediaPlayerPrivateAVFoundation had some code to delay calling platformPlay()
until the first frame became available. But this code was entirely undermined by the previous
behavior of setRate(). Fixing setRate()/setRateDouble() to only start playback if playback was
actually requested started making this code work for the first time, and broke some API tests.
Thus, we're removing this previously dead code.

  • platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:

(WebCore::MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation):
(WebCore::MediaPlayerPrivateAVFoundation::play):
(WebCore::MediaPlayerPrivateAVFoundation::pause):
(WebCore::MediaPlayerPrivateAVFoundation::rateChanged):
(WebCore::MediaPlayerPrivateAVFoundation::updateStates):

  • platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
  • platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
  • platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:

(WebCore::MediaPlayerPrivateAVFoundationObjC::cancelLoad):
(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer):
(WebCore::MediaPlayerPrivateAVFoundationObjC::didEnd):
(WebCore::MediaPlayerPrivateAVFoundationObjC::platformPlay):
(WebCore::MediaPlayerPrivateAVFoundationObjC::platformPause):
(WebCore::MediaPlayerPrivateAVFoundationObjC::seekToTime):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setRateDouble):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setPlayerRate):
(WebCore::MediaPlayerPrivateAVFoundationObjC::timeControlStatusDidChange):
(WebCore::MediaPlayerPrivateAVFoundationObjC::setShouldObserveTimeControlStatus):
(-[WebCoreAVFMovieObserver observeValueForKeyPath:ofObject:change:context:]):

Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r239854 r239857  
     12019-01-10  Jer Noble  <jer.noble@apple.com>
     2
     3        <video> elements do not enter 'paused' state when playing to end over AirPlay
     4        https://bugs.webkit.org/show_bug.cgi?id=193295
     5        <rdar://problem/46708670>
     6
     7        Reviewed by Eric Carlson.
     8
     9        Adopt the -[AVPlayer timeControlStatus] API, which reports whether the AVPlayer is paused, playing, or blocked waiting
     10        for more data before playing. AirPlay devices report this state back from the remote device, and this allows the
     11        MediaPlayerPrivateAVFoundationObjC to differentiate between user-generated pauses and simple stalling.
     12
     13        Adopting this API allows us to remove the heuristic from rateChanged() which inteprets a rate change when the
     14        readyState > HAVE_ENOUGH as an intentional pause.
     15
     16        Drive-by fix: MediaPlayerPrivateAVFoundation had some code to delay calling platformPlay()
     17        until the first frame became available. But this code was entirely undermined by the previous
     18        behavior of setRate(). Fixing setRate()/setRateDouble() to only start playback if playback was
     19        actually requested started making this code work for the first time, and broke some API tests.
     20        Thus, we're removing this previously dead code.
     21
     22        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
     23        (WebCore::MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation):
     24        (WebCore::MediaPlayerPrivateAVFoundation::play):
     25        (WebCore::MediaPlayerPrivateAVFoundation::pause):
     26        (WebCore::MediaPlayerPrivateAVFoundation::rateChanged):
     27        (WebCore::MediaPlayerPrivateAVFoundation::updateStates):
     28        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
     29        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
     30        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
     31        (WebCore::MediaPlayerPrivateAVFoundationObjC::cancelLoad):
     32        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer):
     33        (WebCore::MediaPlayerPrivateAVFoundationObjC::didEnd):
     34        (WebCore::MediaPlayerPrivateAVFoundationObjC::platformPlay):
     35        (WebCore::MediaPlayerPrivateAVFoundationObjC::platformPause):
     36        (WebCore::MediaPlayerPrivateAVFoundationObjC::seekToTime):
     37        (WebCore::MediaPlayerPrivateAVFoundationObjC::setRateDouble):
     38        (WebCore::MediaPlayerPrivateAVFoundationObjC::setPlayerRate):
     39        (WebCore::MediaPlayerPrivateAVFoundationObjC::timeControlStatusDidChange):
     40        (WebCore::MediaPlayerPrivateAVFoundationObjC::setShouldObserveTimeControlStatus):
     41        (-[WebCoreAVFMovieObserver observeValueForKeyPath:ofObject:change:context:]):
     42
    1432019-01-10  Myles C. Maxfield  <mmaxfield@apple.com>
    244
  • trunk/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp

    r238815 r239857  
    8080    , m_ignoreLoadStateChanges(false)
    8181    , m_haveReportedFirstVideoFrame(false)
    82     , m_playWhenFramesAvailable(false)
    8382    , m_inbandTrackConfigurationPending(false)
    8483    , m_characteristicsChanged(false)
    … …  
    217216{
    218217    ALWAYS_LOG(LOGIDENTIFIER);
    219 
    220     // If the file has video, don't request playback until the first frame of video is ready to display
    221     // or the audio may start playing before we can render video.
    222     if (!m_cachedHasVideo || hasAvailableVideoFrame())
    223         platformPlay();
    224     else {
    225         INFO_LOG(LOGIDENTIFIER, "waiting for first video frame");
    226         m_playWhenFramesAvailable = true;
    227     }
     218    platformPlay();
    228219}
    229220
    … …  
    231222{
    232223    ALWAYS_LOG(LOGIDENTIFIER);
    233     m_playWhenFramesAvailable = false;
    234224    platformPause();
    235225}
    … …  
    577567    setNetworkState(newNetworkState);
    578568    setReadyState(newReadyState);
    579 
    580     if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) {
    581         m_playWhenFramesAvailable = false;
    582         platformPlay();
    583     }
    584569}
    585570
    … …  
    624609void MediaPlayerPrivateAVFoundation::rateChanged()
    625610{
    626 
    627 #if ENABLE(WIRELESS_PLAYBACK_TARGET) && PLATFORM(IOS_FAMILY)
    628     if (isCurrentPlaybackTargetWireless() && playerItemStatus() >= MediaPlayerAVPlayerItemStatusPlaybackBufferFull) {
    629         double rate = this->rate();
    630         if (rate != requestedRate()) {
    631             m_player->handlePlaybackCommand(rate ? PlatformMediaSession::PlayCommand : PlatformMediaSession::PauseCommand);
    632             return;
    633         }
    634     }
    635 #endif
    636 
    637611    m_player->rateChanged();
    638612}
  • trunk/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h

    r232613 r239857  
    370370    bool m_ignoreLoadStateChanges;
    371371    bool m_haveReportedFirstVideoFrame;
    372     bool m_playWhenFramesAvailable;
    373372    bool m_inbandTrackConfigurationPending;
    374373    bool m_characteristicsChanged;
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h

    r239427 r239857  
    8181    void setAsset(RetainPtr<id>&&);
    8282    void tracksChanged() override;
     83    void didEnd() override;
    8384
    8485#if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
    … …  
    114115    void durationDidChange(const MediaTime&);
    115116    void rateDidChange(double);
     117    void timeControlStatusDidChange(int);
    116118    void metadataDidArrive(const RetainPtr<NSArray>&, const MediaTime&);
    117119    void firstFrameAvailableDidChange(bool);
    … …  
    182184    void setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode) override;
    183185    void videoFullscreenStandbyChanged() override;
     186    void setPlayerRate(double);
    184187
    185188#if PLATFORM(IOS_FAMILY)
    … …  
    334337
    335338    bool performTaskAtMediaTime(WTF::Function<void()>&&, MediaTime) final;
     339    void setShouldObserveTimeControlStatus(bool);
    336340
    337341    WeakPtrFactory<MediaPlayerPrivateAVFoundationObjC> m_weakPtrFactory;
    … …  
    416420    RefPtr<SharedBuffer> m_keyID;
    417421    double m_cachedRate;
     422    bool m_requestedPlaying { false };
     423    double m_requestedRate { 1.0 };
     424    int m_cachedTimeControlStatus { 0 };
    418425    mutable long long m_cachedTotalBytes;
    419426    unsigned m_pendingStatusChanges;
    … …  
    429436    bool m_cachedCanPlayFastReverse;
    430437    bool m_muted { false };
     438    bool m_shouldObserveTimeControlStatus { false };
    431439    mutable Optional<bool> m_tracksArePlayable;
    432440#if ENABLE(WIRELESS_PLAYBACK_TARGET)
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm

    r239535 r239857  
    588588            [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
    589589
     590        setShouldObserveTimeControlStatus(false);
     591
    590592        [m_avPlayer replaceCurrentItemWithPlayerItem:nil];
    591593        m_avPlayer = nil;
    … …  
    10121014        [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
    10131015
     1016    setShouldObserveTimeControlStatus(true);
     1017
    10141018#if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
    10151019    [m_avPlayer.get() setAppliesMediaSelectionCriteriaAutomatically:NO];
    … …  
    12661270#endif
    12671271
     1272void MediaPlayerPrivateAVFoundationObjC::didEnd()
     1273{
     1274    m_requestedPlaying = false;
     1275    MediaPlayerPrivateAVFoundation::didEnd();
     1276}
     1277
    12681278void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
    12691279{
    … …  
    12811291        return;
    12821292
    1283     setDelayCallbacks(true);
    1284     m_cachedRate = requestedRate();
    1285     [m_avPlayer.get() setRate:requestedRate()];
    1286     setDelayCallbacks(false);
     1293    m_requestedPlaying = true;
     1294    setPlayerRate(m_requestedRate);
    12871295}
    12881296
    … …  
    12931301        return;
    12941302
    1295     setDelayCallbacks(true);
    1296     m_cachedRate = 0;
    1297     [m_avPlayer.get() setRate:0];
    1298     setDelayCallbacks(false);
     1303    m_requestedPlaying = false;
     1304    setPlayerRate(0);
    12991305}
    13001306
    … …  
    13541360    auto weakThis = makeWeakPtr(*this);
    13551361
     1362    setShouldObserveTimeControlStatus(false);
    13561363    [m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
    13571364        callOnMainThread([weakThis, finished] {
    … …  
    13601367                return;
    13611368
     1369            _this->setShouldObserveTimeControlStatus(true);
    13621370            _this->seekCompleted(finished);
    13631371        });
    … …  
    14071415void MediaPlayerPrivateAVFoundationObjC::setRateDouble(double rate)
    14081416{
     1417    m_requestedRate = rate;
     1418    if (m_requestedPlaying)
     1419        setPlayerRate(rate);
     1420}
     1421
     1422void MediaPlayerPrivateAVFoundationObjC::setPlayerRate(double rate)
     1423{
    14091424    setDelayCallbacks(true);
    14101425    m_cachedRate = rate;
    1411     [m_avPlayer.get() setRate:rate];
     1426    setShouldObserveTimeControlStatus(false);
     1427    [m_avPlayer setRate:rate];
     1428    m_cachedTimeControlStatus = [m_avPlayer timeControlStatus];
     1429    setShouldObserveTimeControlStatus(true);
    14121430    setDelayCallbacks(false);
    14131431}
    … …  
    32353253}
    32363254
     3255void MediaPlayerPrivateAVFoundationObjC::timeControlStatusDidChange(int timeControlStatus)
     3256{
     3257    if (m_cachedTimeControlStatus == timeControlStatus)
     3258        return;
     3259
     3260    if (!m_shouldObserveTimeControlStatus)
     3261        return;
     3262
     3263    m_cachedTimeControlStatus = timeControlStatus;
     3264
     3265#if ENABLE(WIRELESS_PLAYBACK_TARGET)
     3266    if (!isCurrentPlaybackTargetWireless())
     3267        return;
     3268
     3269    bool playerIsPlaying = m_cachedTimeControlStatus != AVPlayerTimeControlStatusPaused;
     3270    if (playerIsPlaying != m_requestedPlaying)
     3271        player()->handlePlaybackCommand(playerIsPlaying ? PlatformMediaSession::PlayCommand : PlatformMediaSession::PauseCommand);
     3272#endif
     3273}
     3274
    32373275#if ENABLE(WIRELESS_PLAYBACK_TARGET)
    32383276
    … …  
    33093347    }];
    33103348    return true;
     3349}
     3350
     3351void MediaPlayerPrivateAVFoundationObjC::setShouldObserveTimeControlStatus(bool shouldObserve)
     3352{
     3353    if (shouldObserve == m_shouldObserveTimeControlStatus)
     3354        return;
     3355
     3356    m_shouldObserveTimeControlStatus = shouldObserve;
     3357    if (shouldObserve)
     3358        [m_avPlayer addObserver:m_objcObserver.get() forKeyPath:@"timeControlStatus" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
     3359    else
     3360        [m_avPlayer removeObserver:m_objcObserver.get() forKeyPath:@"timeControlStatus"];
    33113361}
    33123362
    … …  
    34743524            if ([keyPath isEqualToString:@"rate"])
    34753525                player->rateDidChange([newValue doubleValue]);
     3526            else if ([keyPath isEqualToString:@"timeControlStatus"])
     3527                player->timeControlStatusDidChange([newValue intValue]);
    34763528#if ENABLE(WIRELESS_PLAYBACK_TARGET)
    34773529            else if ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"allowsExternalPlayback"])
Note: See TracChangeset for help on using the changeset viewer.