Title: [170336] trunk
Revision
170336
Author
jer.no...@apple.com
Date
2014-06-23 17:08:25 -0700 (Mon, 23 Jun 2014)

Log Message

HTMLMediaElement seek algorithm should allow cancelling previous seeks.
https://bugs.webkit.org/show_bug.cgi?id=134116

Reviewed by Eric Carlson.

Source/WebCore:
Test: media/video-seek-double.html

Fulfill the requirement of the §4.7.14.9 seeking algorithm to do steps 5+ asynchronously and cancel
previous instances of the algorithm.

For the html/ parts of the algorithm, implement this by adding a seek timer, which when fired will
issue steps 5-12. MediaPlayerPrivateAVFoundation will already coalesce multiple seek operations, so nothing
additional needs be done there.  However, MediaPlayerPrivateMediaSourceAVFObjC needs to implement the same
pending seek logic in case additional seeks were issued after the html/ algorithm reached step 12.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement): Initialize the new m_seekTimer.
(WebCore::HTMLMediaElement::parseAttribute): Clear the seek timer.
(WebCore::HTMLMediaElement::seekInternal): Differentiate between DOM seeks and internal seeks.
(WebCore::HTMLMediaElement::seekWithTolerance:) Split into the seekTimerFired() method.
(WebCore::HTMLMediaElement::seekTimerFired:) Added, split from seekWithTolerance().
* html/HTMLMediaElement.h:
* html/MediaController.cpp:
(MediaController::bringElementUpToSpeed): Call seekInternal().
(WebCore::HTMLMediaElement::PendingSeek::PendingSeek): Added convenience struct for storing seek requests.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::PendingSeek::PendingSeek): Ditto.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::MediaPlayerPrivateMediaSourceAVFObjC): Modify the time jumped
    handler to not clear the m_seeking flag if another seek request is pending.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekWithTolerance):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekInternal):

LayoutTests:
* media/video-seek-double-expected.txt: Added.
* media/video-seek-double.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (170335 => 170336)


--- trunk/LayoutTests/ChangeLog	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/LayoutTests/ChangeLog	2014-06-24 00:08:25 UTC (rev 170336)
@@ -1,3 +1,13 @@
+2014-06-20  Jer Noble  <jer.no...@apple.com>
+
+        HTMLMediaElement seek algorithm should allow cancelling previous seeks.
+        https://bugs.webkit.org/show_bug.cgi?id=134116
+
+        Reviewed by Eric Carlson.
+
+        * media/video-seek-double-expected.txt: Added.
+        * media/video-seek-double.html: Added.
+
 2014-06-19  Jeffrey Pfau  <jp...@apple.com>
 
         Database process crashes when multiple transactions attempt to run at once

Added: trunk/LayoutTests/media/video-seek-double-expected.txt (0 => 170336)


--- trunk/LayoutTests/media/video-seek-double-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/video-seek-double-expected.txt	2014-06-24 00:08:25 UTC (rev 170336)
@@ -0,0 +1,13 @@
+Test that seeking twice in the same run loop will cancel the first seek.
+
+
+EVENT(canplaythrough)
+RUN(video.currentTime = 2)
+RUN(video.currentTime = 0.5)
+
+EVENT(seeked)
+EXPECTED (video.currentTime == '0.5') OK
+RUN(video.play())
+EVENT(play)
+END OF TEST
+

Added: trunk/LayoutTests/media/video-seek-double.html (0 => 170336)


--- trunk/LayoutTests/media/video-seek-double.html	                        (rev 0)
+++ trunk/LayoutTests/media/video-seek-double.html	2014-06-24 00:08:25 UTC (rev 170336)
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+<head>
+    <script src=""
+    <script src=""
+    <script>
+        function runTest()
+        {
+            findMediaElement();
+            waitForEvent('canplaythrough', canplaythrough);
+            video.src = "" 'content/test');
+        }
+
+        function canplaythrough() 
+        {
+            waitForEventOnce('seeked', seeked );
+            run('video.currentTime = 2');
+            run('video.currentTime = 0.5');
+            consoleWrite('');
+        }
+
+        function seeked()
+        { 
+            testExpected('video.currentTime', 0.5);
+            waitForEventAndFail('seeked');
+            waitForEventAndEnd('play');
+            run('video.play()');
+         }
+    </script>
+</head>
+<body _onload_='runTest()'>
+    <p>Test that seeking twice in the same run loop will cancel the first seek.
+     </p>
+    <video controls></video>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (170335 => 170336)


--- trunk/Source/WebCore/ChangeLog	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/Source/WebCore/ChangeLog	2014-06-24 00:08:25 UTC (rev 170336)
@@ -1,3 +1,38 @@
+2014-06-20  Jer Noble  <jer.no...@apple.com>
+
+        HTMLMediaElement seek algorithm should allow cancelling previous seeks.
+        https://bugs.webkit.org/show_bug.cgi?id=134116
+
+        Reviewed by Eric Carlson.
+
+        Test: media/video-seek-double.html
+
+        Fulfill the requirement of the §4.7.14.9 seeking algorithm to do steps 5+ asynchronously and cancel
+        previous instances of the algorithm.
+
+        For the html/ parts of the algorithm, implement this by adding a seek timer, which when fired will
+        issue steps 5-12. MediaPlayerPrivateAVFoundation will already coalesce multiple seek operations, so nothing
+        additional needs be done there.  However, MediaPlayerPrivateMediaSourceAVFObjC needs to implement the same
+        pending seek logic in case additional seeks were issued after the html/ algorithm reached step 12.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement): Initialize the new m_seekTimer.
+        (WebCore::HTMLMediaElement::parseAttribute): Clear the seek timer.
+        (WebCore::HTMLMediaElement::seekInternal): Differentiate between DOM seeks and internal seeks.
+        (WebCore::HTMLMediaElement::seekWithTolerance:) Split into the seekTimerFired() method.
+        (WebCore::HTMLMediaElement::seekTimerFired:) Added, split from seekWithTolerance().
+        * html/HTMLMediaElement.h:
+        * html/MediaController.cpp:
+        (MediaController::bringElementUpToSpeed): Call seekInternal().
+        (WebCore::HTMLMediaElement::PendingSeek::PendingSeek): Added convenience struct for storing seek requests.
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::PendingSeek::PendingSeek): Ditto.
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::MediaPlayerPrivateMediaSourceAVFObjC): Modify the time jumped
+            handler to not clear the m_seeking flag if another seek request is pending.
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekWithTolerance):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekInternal):
+
 2014-06-23  Timothy Horton  <timothy_hor...@apple.com>
 
         [WK2] Use the page background color instead of white when swipe snapshots were purged (134218)

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (170335 => 170336)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2014-06-24 00:08:25 UTC (rev 170336)
@@ -256,6 +256,7 @@
     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
     , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
     , m_scanTimer(this, &HTMLMediaElement::scanTimerFired)
+    , m_seekTimer(this, &HTMLMediaElement::seekTimerFired)
     , m_playedTimeRanges()
     , m_asyncEventQueue(*this)
     , m_playbackRate(1.0f)
@@ -2316,17 +2317,23 @@
     double negativeTolerance = delta >= 0 ? delta : std::numeric_limits<double>::infinity();
     double positiveTolerance = delta < 0 ? -delta : std::numeric_limits<double>::infinity();
 
-    seekWithTolerance(time, negativeTolerance, positiveTolerance);
+    seekWithTolerance(time, negativeTolerance, positiveTolerance, true);
 }
 
 void HTMLMediaElement::seek(double time)
 {
     LOG(Media, "HTMLMediaElement::seek(%f)", time);
-    seekWithTolerance(time, 0, 0);
+    seekWithTolerance(time, 0, 0, true);
 }
 
-void HTMLMediaElement::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance)
+void HTMLMediaElement::seekInternal(double time)
 {
+    LOG(Media, "HTMLMediaElement::seekInternal(%f)", time);
+    seekWithTolerance(time, 0, 0, false);
+}
+
+void HTMLMediaElement::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance, bool fromDOM)
+{
     // 4.8.10.9 Seeking
 
     // 1 - Set the media element's show poster flag to false.
@@ -2347,16 +2354,38 @@
     // 3 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
     // already running. Abort that other instance of the algorithm without waiting for the step that
     // it is running to complete.
-    // Nothing specific to be done here.
+    if (m_seeking) {
+        m_seekTimer.stop();
+        m_pendingSeek = nullptr;
+    }
 
     // 4 - Set the seeking IDL attribute to true.
     // The flag will be cleared when the engine tells us the time has actually changed.
     m_seeking = true;
+    if (m_playing) {
+        if (m_lastSeekTime < now)
+            addPlayedRange(m_lastSeekTime, now);
+    }
+    m_lastSeekTime = time;
 
     // 5 - If the seek was in response to a DOM method call or setting of an IDL attribute, then continue
     // the script. The remainder of these steps must be run asynchronously.
-    // Nothing to be done here.
+    m_pendingSeek = std::make_unique<PendingSeek>(now, time, negativeTolerance, positiveTolerance);
+    if (fromDOM)
+        m_seekTimer.startOneShot(0);
+    else
+        seekTimerFired(m_seekTimer);
+}
 
+void HTMLMediaElement::seekTimerFired(Timer<HTMLMediaElement>&)
+{
+    ASSERT(m_pendingSeek);
+    double now = m_pendingSeek->now;
+    double time = m_pendingSeek->targetTime;
+    double negativeTolerance = m_pendingSeek->negativeTolerance;
+    double positiveTolerance = m_pendingSeek->positiveTolerance;
+    m_pendingSeek = nullptr;
+
     // 6 - If the new playback position is later than the end of the media resource, then let it be the end 
     // of the media resource instead.
     time = std::min(time, duration());
@@ -2373,7 +2402,7 @@
 #if !LOG_DISABLED
     double mediaTime = m_player->mediaTimeForTimeValue(time);
     if (time != mediaTime)
-        LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
+        LOG(Media, "HTMLMediaElement::seekTimerFired(%f) - media timeline equivalent is %f", time, mediaTime);
 #endif
     time = m_player->mediaTimeForTimeValue(time);
 
@@ -2406,12 +2435,8 @@
     }
     time = seekableRanges->nearest(time);
 
-    if (m_playing) {
-        if (m_lastSeekTime < now)
-            addPlayedRange(m_lastSeekTime, now);
-    }
+    m_sentEndEvent = false;
     m_lastSeekTime = time;
-    m_sentEndEvent = false;
 
     // 10 - Queue a task to fire a simple event named seeking at the element.
     scheduleEvent(eventNames().seekingEvent);
@@ -2562,7 +2587,7 @@
     if (m_mediaController)
         return;
 
-    seek(time);
+    seekInternal(time);
 }
 
 void HTMLMediaElement::setCurrentTime(double time, ExceptionCode& ec)
@@ -2728,7 +2753,7 @@
         scheduleDelayedAction(LoadMediaResource);
 
     if (endedPlayback())
-        seek(0);
+        seekInternal(0);
 
     if (m_mediaController)
         m_mediaController->bringElementUpToSpeed(this);
@@ -4075,7 +4100,7 @@
             // then seek to the earliest possible position of the media resource and abort these steps when the direction of
             // playback is forwards,
             if (now >= dur)
-                seek(0);
+                seekInternal(0);
         } else if ((now <= 0 && playbackRate < 0) || (now >= dur && playbackRate > 0)) {
             // If the media element does not have a current media controller, and the media element
             // has still ended playback and paused is false,
@@ -4146,7 +4171,7 @@
     double now = currentTime();
     double dur = duration();
     if (now > dur)
-        seek(dur);
+        seekInternal(dur);
 
     endProcessingMediaPlayerCallback();
 }

Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (170335 => 170336)


--- trunk/Source/WebCore/html/HTMLMediaElement.h	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h	2014-06-24 00:08:25 UTC (rev 170336)
@@ -576,12 +576,14 @@
     void progressEventTimerFired(Timer<HTMLMediaElement>&);
     void playbackProgressTimerFired(Timer<HTMLMediaElement>&);
     void scanTimerFired(Timer<HTMLMediaElement>&);
+    void seekTimerFired(Timer<HTMLMediaElement>&);
     void startPlaybackProgressTimer();
     void startProgressEventTimer();
     void stopPeriodicTimers();
 
     void seek(double time);
-    void seekWithTolerance(double time, double negativeTolerance, double positiveTolerance);
+    void seekInternal(double time);
+    void seekWithTolerance(double time, double negativeTolerance, double positiveTolerance, bool fromDOM);
     void finishSeek();
     void checkIfSeekNeeded();
     void addPlayedRange(double start, double end);
@@ -701,6 +703,7 @@
     Timer<HTMLMediaElement> m_progressEventTimer;
     Timer<HTMLMediaElement> m_playbackProgressTimer;
     Timer<HTMLMediaElement> m_scanTimer;
+    Timer<HTMLMediaElement> m_seekTimer;
     RefPtr<TimeRanges> m_playedTimeRanges;
     GenericEventQueue m_asyncEventQueue;
 
@@ -714,6 +717,21 @@
 
     RefPtr<MediaError> m_error;
 
+    struct PendingSeek {
+        PendingSeek(double now, double targetTime, double negativeTolerance, double positiveTolerance)
+            : now(now)
+            , targetTime(targetTime)
+            , negativeTolerance(negativeTolerance)
+            , positiveTolerance(positiveTolerance)
+        {
+        }
+        double now;
+        double targetTime;
+        double negativeTolerance;
+        double positiveTolerance;
+    };
+    std::unique_ptr<PendingSeek> m_pendingSeek;
+
     double m_volume;
     bool m_volumeInitialized;
     double m_lastSeekTime;

Modified: trunk/Source/WebCore/html/MediaController.cpp (170335 => 170336)


--- trunk/Source/WebCore/html/MediaController.cpp	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/Source/WebCore/html/MediaController.cpp	2014-06-24 00:08:25 UTC (rev 170336)
@@ -479,7 +479,7 @@
     // When the user agent is to bring a media element up to speed with its new media controller,
     // it must seek that media element to the MediaController's media controller position relative
     // to the media element's timeline.
-    element->seek(currentTime());
+    element->seekInternal(currentTime());
 }
 
 bool MediaController::isBlocked() const

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h (170335 => 170336)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h	2014-06-24 00:08:25 UTC (rev 170336)
@@ -64,7 +64,7 @@
     void setReadyState(MediaPlayer::ReadyState);
     void setNetworkState(MediaPlayer::NetworkState);
 
-    void seekInternal(double, double, double);
+    void seekInternal();
     void setLoadingProgresssed(bool flag) { m_loadingProgressed = flag; }
     void setHasAvailableVideoFrame(bool flag) { m_hasAvailableVideoFrame = flag; }
     void durationChanged();
@@ -166,6 +166,19 @@
 
     friend class MediaSourcePrivateAVFObjC;
 
+    struct PendingSeek {
+        PendingSeek(const MediaTime& targetTime, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold)
+            : targetTime(targetTime)
+            , negativeThreshold(negativeThreshold)
+            , positiveThreshold(positiveThreshold)
+        {
+        }
+        MediaTime targetTime;
+        MediaTime negativeThreshold;
+        MediaTime positiveThreshold;
+    };
+    std::unique_ptr<PendingSeek> m_pendingSeek;
+
     MediaPlayer* m_player;
     WeakPtrFactory<MediaPlayerPrivateMediaSourceAVFObjC> m_weakPtrFactory;
     RefPtr<MediaSourcePrivateClient> m_mediaSource;

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm (170335 => 170336)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm	2014-06-24 00:03:15 UTC (rev 170335)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm	2014-06-24 00:08:25 UTC (rev 170336)
@@ -153,9 +153,12 @@
         if (!weakThis)
             return;
 
-        if (m_seeking)
+        if (m_seeking && !m_pendingSeek)
             m_seeking = false;
         m_player->timeChanged();
+
+        if (m_pendingSeek)
+            seekInternal();
     }];
 }
 
@@ -424,23 +427,31 @@
 {
     m_seeking = true;
     auto weakThis = createWeakPtr();
-    callOnMainThread([weakThis, time, negativeThreshold, positiveThreshold]{
+    m_pendingSeek = std::make_unique<PendingSeek>(MediaTime::createWithDouble(time), MediaTime::createWithDouble(negativeThreshold), MediaTime::createWithDouble(positiveThreshold));
+
+    callOnMainThread([weakThis] {
         if (!weakThis)
             return;
-        weakThis.get()->seekInternal(time, negativeThreshold, positiveThreshold);
+        weakThis.get()->seekInternal();
     });
 }
 
-void MediaPlayerPrivateMediaSourceAVFObjC::seekInternal(double time, double negativeThreshold, double positiveThreshold)
+void MediaPlayerPrivateMediaSourceAVFObjC::seekInternal()
 {
+    std::unique_ptr<PendingSeek> pendingSeek;
+    pendingSeek.swap(m_pendingSeek);
+
+    if (!pendingSeek)
+        return;
+
     if (!m_mediaSourcePrivate)
         return;
 
     MediaTime seekTime;
-    if (!negativeThreshold && !positiveThreshold)
-        seekTime = MediaTime::createWithDouble(time);
+    if (pendingSeek->negativeThreshold != MediaTime::zeroTime() && pendingSeek->positiveThreshold != MediaTime::zeroTime())
+        seekTime = pendingSeek->targetTime;
     else
-        seekTime = m_mediaSourcePrivate->fastSeekTimeForMediaTime(MediaTime::createWithDouble(time), MediaTime::createWithDouble(positiveThreshold), MediaTime::createWithDouble(negativeThreshold));
+        seekTime = m_mediaSourcePrivate->fastSeekTimeForMediaTime(pendingSeek->targetTime, pendingSeek->positiveThreshold, pendingSeek->negativeThreshold);
 
     [m_synchronizer setRate:(m_playing ? m_rate : 0) time:toCMTime(seekTime)];
     m_mediaSourcePrivate->seekToTime(seekTime);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to