Title: [150066] trunk
Revision
150066
Author
kbal...@webkit.org
Date
2013-05-14 05:49:06 -0700 (Tue, 14 May 2013)

Log Message

[GStreamer] cannot seek after video finished
https://bugs.webkit.org/show_bug.cgi?id=114044

Patch by Balazs Kelemen <b.kele...@sisa.samsung.com> on 2013-04-30
Reviewed by Philippe Normand.

Source/WebCore:

Test: media/video-seek-after-end.html

Reland without wrong assertion. If seek is called after didEnd the pipeline
state will not be in GST_STATE_NULL yet but it is not a problem because we handle that.

Rework the seeking logic to be able to seek after reseting the pipeline.
In addition to solve the actual problem this patch supposed to make seeking
more robust and correct.
The previous implementation tried to hide the complexity of asynchronous operations
on the pipeline. It did not handle the GST_MESSAGE_ASYNC_DONE message from the bus
but instead reported the seek as finished when it saw an asynchronous pending state
(GST_STATE_CHANGE_ASYNC) which could happen way before the seek is really done.
Now we pay attention to the GST_MESSAGE_ASYNC_DONE message to track the status of seeks.
Seeks are not the only operations executed asynchronously, changing the pipeling state is
similar. It means a seek can overlap with onother ongoing asynchronous operation.
This change address this by introducing an invariant for seeks, which is that we only request
a seek if there are no other ongoing asynchronous operations and the pipeline state is either
paused or playing (which is recommended anyway according to GStreamer's documentation).
This way we can be sure that the time when we get the next GST_MESSAGE_ASYNC_DONE message the
seek has been completed.

* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
(WebCore::toGstClockTime): Factored time conversation into a helper.
(WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer):

(WebCore::MediaPlayerPrivateGStreamer::playbackPosition): The position might not be available
if the pipeline still has a pending state. As a workaround, if we are right after a seek we can
use the seek time. Avoiding this situation would be possible by not allowing any asynchronous
operation to overlap. I believe it would add a lot more complexity so I decided to rather introduce
this workaround. Otherwise those overlapping operations are handled fine by GStreamer.

(WebCore::MediaPlayerPrivateGStreamer::prepareToPlay): Do not reset internal state variables.
This function called when there is an intent to restart playback but it does not actually restart it.
(WebCore::MediaPlayerPrivateGStreamer::currentTime): Just removed a staling newline.
(WebCore::MediaPlayerPrivateGStreamer::seek): Take a look to the pipeline state and act upon that.
If there is an ongoing asynchronous operation make the seek pending, otherwise do it now.
Now we handle overlapping seeks as well because I saw that it can happen in some tests.
Added an early return for live streams as it doesn't makes sense to try seeking in them.

(WebCore::MediaPlayerPrivateGStreamer::handleMessage): Handle GST_MESSAGE_ASYNC_DONE and some refactoring.
(WebCore::MediaPlayerPrivateGStreamer::asyncStateChangeDone):
(WebCore::MediaPlayerPrivateGStreamer::updateStates): Only handle seeks in the pending case, the rest is
now handled in asyncStateChangeDone.
(WebCore::MediaPlayerPrivateGStreamer::cacheDuration): Do not reset the m_mediaDurationKnown if the pipeline
has an asynchronous pending state because it would fail. It does actually happen when we get a duration message
after restarting the pipeline and it would result in restarting playback from the start. It seems to be a bug
in GStreamer that it sends the duration message too early. Also sanitized this function by merging redundant branches.
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
(MediaPlayerPrivateGStreamer):

LayoutTests:

* media/video-seek-after-end-expected.txt: Added.
* media/video-seek-after-end.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (150065 => 150066)


--- trunk/LayoutTests/ChangeLog	2013-05-14 12:27:03 UTC (rev 150065)
+++ trunk/LayoutTests/ChangeLog	2013-05-14 12:49:06 UTC (rev 150066)
@@ -1,3 +1,13 @@
+2013-04-30  Balazs Kelemen  <b.kele...@sisa.samsung.com>
+
+        [GStreamer] cannot seek after video finished
+        https://bugs.webkit.org/show_bug.cgi?id=114044
+
+        Reviewed by Philippe Normand.
+
+        * media/video-seek-after-end-expected.txt: Added.
+        * media/video-seek-after-end.html: Added.
+
 2013-05-14  Zalan Bujtas  <za...@apple.com>
 
         Ellipsis text is placed to wrong position, when the truncated text is fully cut off in RTL direction.

Added: trunk/LayoutTests/media/video-seek-after-end-expected.txt (0 => 150066)


--- trunk/LayoutTests/media/video-seek-after-end-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/video-seek-after-end-expected.txt	2013-05-14 12:49:06 UTC (rev 150066)
@@ -0,0 +1,9 @@
+Test that we can seek after reached the end of the video.
+
+EVENT(playing)
+EVENT(ended)
+EVENT(seeked)
+EVENT(playing)
+EXPECTED (video.currentTime.toFixed(2) == '3') OK
+END OF TEST
+

Added: trunk/LayoutTests/media/video-seek-after-end.html (0 => 150066)


--- trunk/LayoutTests/media/video-seek-after-end.html	                        (rev 0)
+++ trunk/LayoutTests/media/video-seek-after-end.html	2013-05-14 12:49:06 UTC (rev 150066)
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+        <script>
+            function start()
+            {
+                findMediaElement();
+
+                var countPlaying = 0;
+                waitForEvent('playing', function() {
+                    if (++countPlaying > 2) {
+                        logResult(false, 'FAIL: playing fired more then twice.');
+                        endTest();
+                        return;
+                    }
+                    var seekTarget = 3;
+                    if (countPlaying == 2) {
+                        testExpected('video.currentTime.toFixed(2)', seekTarget, '==');
+                        endTest();
+                        return;
+                    }
+
+                    waitForEventOnce('ended', function() {
+                        video.currentTime = seekTarget;
+                        waitForEventOnce('seeked', function() { video.play(); })
+                    });
+
+                    video.currentTime = video.duration - 0.2;
+                });
+
+                video.src = "" 'content/test');
+                video.play();
+
+                setTimeout(function() {
+                    logResult(false, 'FAIL: timeout');
+                    endTest();
+                }, 1000);
+            }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+        <p>Test that we can seek after reached the end of the video.</p>
+        <video controls></video>
+    </body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (150065 => 150066)


--- trunk/Source/WebCore/ChangeLog	2013-05-14 12:27:03 UTC (rev 150065)
+++ trunk/Source/WebCore/ChangeLog	2013-05-14 12:49:06 UTC (rev 150066)
@@ -1,3 +1,60 @@
+2013-04-30  Balazs Kelemen  <b.kele...@sisa.samsung.com>
+
+        [GStreamer] cannot seek after video finished
+        https://bugs.webkit.org/show_bug.cgi?id=114044
+
+        Reviewed by Philippe Normand.
+
+        Test: media/video-seek-after-end.html
+
+        Reland without wrong assertion. If seek is called after didEnd the pipeline
+        state will not be in GST_STATE_NULL yet but it is not a problem because we handle that.
+
+        Rework the seeking logic to be able to seek after reseting the pipeline.
+        In addition to solve the actual problem this patch supposed to make seeking
+        more robust and correct.
+        The previous implementation tried to hide the complexity of asynchronous operations
+        on the pipeline. It did not handle the GST_MESSAGE_ASYNC_DONE message from the bus
+        but instead reported the seek as finished when it saw an asynchronous pending state
+        (GST_STATE_CHANGE_ASYNC) which could happen way before the seek is really done.
+        Now we pay attention to the GST_MESSAGE_ASYNC_DONE message to track the status of seeks.
+        Seeks are not the only operations executed asynchronously, changing the pipeling state is
+        similar. It means a seek can overlap with onother ongoing asynchronous operation.
+        This change address this by introducing an invariant for seeks, which is that we only request
+        a seek if there are no other ongoing asynchronous operations and the pipeline state is either
+        paused or playing (which is recommended anyway according to GStreamer's documentation).
+        This way we can be sure that the time when we get the next GST_MESSAGE_ASYNC_DONE message the
+        seek has been completed.
+
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
+        (WebCore::toGstClockTime): Factored time conversation into a helper.
+        (WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer):
+
+        (WebCore::MediaPlayerPrivateGStreamer::playbackPosition): The position might not be available
+        if the pipeline still has a pending state. As a workaround, if we are right after a seek we can
+        use the seek time. Avoiding this situation would be possible by not allowing any asynchronous
+        operation to overlap. I believe it would add a lot more complexity so I decided to rather introduce
+        this workaround. Otherwise those overlapping operations are handled fine by GStreamer.
+
+        (WebCore::MediaPlayerPrivateGStreamer::prepareToPlay): Do not reset internal state variables.
+        This function called when there is an intent to restart playback but it does not actually restart it.
+        (WebCore::MediaPlayerPrivateGStreamer::currentTime): Just removed a staling newline.
+        (WebCore::MediaPlayerPrivateGStreamer::seek): Take a look to the pipeline state and act upon that.
+        If there is an ongoing asynchronous operation make the seek pending, otherwise do it now.
+        Now we handle overlapping seeks as well because I saw that it can happen in some tests.
+        Added an early return for live streams as it doesn't makes sense to try seeking in them.
+
+        (WebCore::MediaPlayerPrivateGStreamer::handleMessage): Handle GST_MESSAGE_ASYNC_DONE and some refactoring.
+        (WebCore::MediaPlayerPrivateGStreamer::asyncStateChangeDone):
+        (WebCore::MediaPlayerPrivateGStreamer::updateStates): Only handle seeks in the pending case, the rest is
+        now handled in asyncStateChangeDone.
+        (WebCore::MediaPlayerPrivateGStreamer::cacheDuration): Do not reset the m_mediaDurationKnown if the pipeline
+        has an asynchronous pending state because it would fail. It does actually happen when we get a duration message
+        after restarting the pipeline and it would result in restarting playback from the start. It seems to be a bug
+        in GStreamer that it sends the duration message too early. Also sanitized this function by merging redundant branches.
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
+        (MediaPlayerPrivateGStreamer):
+
 2013-05-14  Zalan Bujtas  <za...@apple.com>
 
         Ellipsis text is placed to wrong position, when the truncated text is fully cut off in RTL direction.

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp (150065 => 150066)


--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp	2013-05-14 12:27:03 UTC (rev 150065)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp	2013-05-14 12:49:06 UTC (rev 150066)
@@ -138,6 +138,18 @@
     player->handlePluginInstallerResult(result);
 }
 
+static GstClockTime toGstClockTime(float time)
+{
+    // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to
+    // round the microseconds so no floating point precision is lost and we can perform an accurate seek.
+    float seconds;
+    float microSeconds = modf(time, &seconds) * 1000000;
+    GTimeVal timeValue;
+    timeValue.tv_sec = static_cast<glong>(seconds);
+    timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
+    return GST_TIMEVAL_TO_TIME(timeValue);
+}
+
 void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object)
 {
     if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
@@ -199,6 +211,8 @@
     , m_resetPipeline(false)
     , m_paused(true)
     , m_seeking(false)
+    , m_seekIsPending(false)
+    , m_timeOfOverlappingSeek(-1)
     , m_buffering(false)
     , m_playbackRate(1)
     , m_errorOccured(false)
@@ -333,30 +347,26 @@
             return m_seekTime;
         if (m_mediaDuration)
             return m_mediaDuration;
+        return 0;
     }
 
-    float ret = 0.0f;
+    // Position is only available if no async state change is going on and the state is either paused or playing.
+    gint64 position = GST_CLOCK_TIME_NONE;
+    GstQuery* query= gst_query_new_position(GST_FORMAT_TIME);
+    if (gst_element_query(m_playBin.get(), query))
+        gst_query_parse_position(query, 0, &position);
 
-    GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
-    if (!gst_element_query(m_playBin.get(), query)) {
-        LOG_MEDIA_MESSAGE("Position query failed...");
-        gst_query_unref(query);
-        return ret;
-    }
+    float result = 0.0f;
+    if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE)
+        result = static_cast<double>(position) / GST_SECOND;
+    else if (m_canFallBackToLastFinishedSeekPositon)
+        result = m_seekTime;
 
-    gint64 position;
-    gst_query_parse_position(query, 0, &position);
-
-    // Position is available only if the pipeline is not in GST_STATE_NULL or
-    // GST_STATE_READY state.
-    if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
-        ret = static_cast<double>(position) / GST_SECOND;
-
     LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
 
     gst_query_unref(query);
 
-    return ret;
+    return result;
 }
 
 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
@@ -387,9 +397,6 @@
 
 void MediaPlayerPrivateGStreamer::prepareToPlay()
 {
-    m_isEndReached = false;
-    m_seeking = false;
-
     if (m_delayingLoad) {
         m_delayingLoad = false;
         commitLoad();
@@ -467,7 +474,6 @@
         return 0.0f;
 
     return playbackPosition();
-
 }
 
 void MediaPlayerPrivateGStreamer::seek(float time)
@@ -478,35 +484,51 @@
     if (m_errorOccured)
         return;
 
-    LOG_MEDIA_MESSAGE("Seek attempt to %f secs", time);
+    LOG_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time);
 
     // Avoid useless seeking.
     if (time == currentTime())
         return;
 
-    // Extract the integer part of the time (seconds) and the
-    // fractional part (microseconds). Attempt to round the
-    // microseconds so no floating point precision is lost and we can
-    // perform an accurate seek.
-    float seconds;
-    float microSeconds = modf(time, &seconds) * 1000000;
-    GTimeVal timeValue;
-    timeValue.tv_sec = static_cast<glong>(seconds);
-    timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
+    if (isLiveStream())
+        return;
 
-    GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue);
-    LOG_MEDIA_MESSAGE("Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime));
+    GstClockTime clockTime = toGstClockTime(time);
+    LOG_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
 
-    if (!gst_element_seek(m_playBin.get(), m_player->rate(),
-            GST_FORMAT_TIME,
-            (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
-            GST_SEEK_TYPE_SET, clockTime,
-            GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
-        LOG_MEDIA_MESSAGE("Seek to %f failed", time);
-    else {
-        m_seeking = true;
-        m_seekTime = time;
+    if (m_seeking) {
+        if (m_seekIsPending) {
+            m_seekTime = time;
+            return;
+        }
+        m_timeOfOverlappingSeek = time;
     }
+
+    GstState state;
+    GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0);
+    if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
+        LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
+        return;
+    }
+    if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) {
+        m_seekIsPending = true;
+        if (m_isEndReached) {
+            LOG_MEDIA_MESSAGE("[Seek] reset pipeline");
+            m_resetPipeline = true;
+            changePipelineState(GST_STATE_PAUSED);
+        }
+    } else {
+        // We can seek now.
+        if (!gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
+            GST_SEEK_TYPE_SET, clockTime, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
+            LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time);
+            return;
+        }
+    }
+
+    m_seeking = true;
+    m_seekTime = time;
+    m_isEndReached = false;
 }
 
 bool MediaPlayerPrivateGStreamer::paused() const
@@ -680,6 +702,8 @@
     const GstStructure* structure = gst_message_get_structure(message);
     GstState requestedState, currentState;
 
+    m_canFallBackToLastFinishedSeekPositon = false;
+
     if (structure) {
         const gchar* messageTypeName = gst_structure_get_name(structure);
 
@@ -691,7 +715,10 @@
         }
     }
 
-    LOG_MEDIA_MESSAGE("Message received from element %s", GST_MESSAGE_SRC_NAME(message));
+    // We ignore state changes from internal elements. They are forwarded to playbin2 anyway.
+    bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get());
+
+    LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
     switch (GST_MESSAGE_TYPE(message)) {
     case GST_MESSAGE_ERROR:
         if (m_resetPipeline)
@@ -729,33 +756,26 @@
             loadingFailed(error);
         break;
     case GST_MESSAGE_EOS:
-        LOG_MEDIA_MESSAGE("End of Stream");
         didEnd();
         break;
-    case GST_MESSAGE_STATE_CHANGED:
-        // Ignore state changes if load is delayed (preload=none). The
-        // player state will be updated once commitLoad() is called.
-        if (m_delayingLoad) {
-            LOG_MEDIA_MESSAGE("Media load has been delayed. Ignoring state changes for now");
+    case GST_MESSAGE_ASYNC_DONE:
+        if (!messageSourceIsPlaybin || m_delayingLoad)
             break;
-        }
+        asyncStateChangeDone();
+        break;
+    case GST_MESSAGE_STATE_CHANGED: {
+        if (!messageSourceIsPlaybin || m_delayingLoad)
+            break;
+        updateStates();
 
-        // Ignore state changes from internal elements. They are
-        // forwarded to playbin2 anyway.
-        if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get())) {
-            updateStates();
+        // Construct a filename for the graphviz dot file output.
+        GstState newState;
+        gst_message_parse_state_changed(message, &currentState, &newState, 0);
+        CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8();
+        GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
 
-            // Construct a filename for the graphviz dot file output.
-            GstState newState;
-            gst_message_parse_state_changed(message, &currentState, &newState, 0);
-
-            CString dotFileName = String::format("webkit-video.%s_%s",
-                gst_element_state_get_name(currentState),
-                gst_element_state_get_name(newState)).utf8();
-
-            GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
-        }
         break;
+    }
     case GST_MESSAGE_BUFFERING:
         processBufferingStats(message);
         break;
@@ -764,8 +784,8 @@
 #else
     case GST_MESSAGE_DURATION:
 #endif
-        LOG_MEDIA_MESSAGE("Duration changed");
-        durationChanged();
+        if (messageSourceIsPlaybin)
+            durationChanged();
         break;
     case GST_MESSAGE_REQUEST_STATE:
         gst_message_parse_request_state(message, &requestedState);
@@ -1033,6 +1053,32 @@
         gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
 }
 
+void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
+{
+    if (!m_playBin || m_errorOccured)
+        return;
+
+    if (m_seeking) {
+        if (m_seekIsPending)
+            updateStates();
+        else {
+            LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime);
+            m_seeking = false;
+            if (m_timeOfOverlappingSeek != -1) {
+                seek(m_timeOfOverlappingSeek);
+                m_timeOfOverlappingSeek = -1;
+                return;
+            }
+
+            // The pipeline can still have a pending state. In this case a position query will fail.
+            // Right now we can use m_seekTime as a fallback.
+            m_canFallBackToLastFinishedSeekPositon = true;
+            timeChanged();
+        }
+    } else
+        updateStates();
+}
+
 void MediaPlayerPrivateGStreamer::updateStates()
 {
     if (!m_playBin)
@@ -1046,16 +1092,12 @@
     GstState state;
     GstState pending;
 
-    GstStateChangeReturn ret = gst_element_get_state(m_playBin.get(),
-        &state, &pending, 250 * GST_NSECOND);
+    GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND);
 
-    bool shouldUpdateAfterSeek = false;
     bool shouldUpdatePlaybackState = false;
-    switch (ret) {
+    switch (getStateResult) {
     case GST_STATE_CHANGE_SUCCESS:
-        LOG_MEDIA_MESSAGE("State: %s, pending: %s",
-            gst_element_state_get_name(state),
-            gst_element_state_get_name(pending));
+        LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
 
         m_resetPipeline = state <= GST_STATE_READY;
 
@@ -1132,11 +1174,6 @@
             m_changingRate = false;
         }
 
-        if (m_seeking) {
-            shouldUpdateAfterSeek = true;
-            m_seeking = false;
-        }
-
         if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
             shouldUpdatePlaybackState = true;
             LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state));
@@ -1144,9 +1181,7 @@
 
         break;
     case GST_STATE_CHANGE_ASYNC:
-        LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s",
-            gst_element_state_get_name(state),
-            gst_element_state_get_name(pending));
+        LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
         // Change in progress
 
         // On-disk buffering was attempted but the media is live. This
@@ -1164,24 +1199,13 @@
             gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
         }
 
-        if (!isLiveStream() && !m_buffering)
-            return;
-
-        if (m_seeking) {
-            shouldUpdateAfterSeek = true;
-            m_seeking = false;
-        }
         break;
     case GST_STATE_CHANGE_FAILURE:
-        LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s",
-            gst_element_state_get_name(state),
-            gst_element_state_get_name(pending));
+        LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
         // Change failed
         return;
     case GST_STATE_CHANGE_NO_PREROLL:
-        LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s",
-            gst_element_state_get_name(state),
-            gst_element_state_get_name(pending));
+        LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
 
         if (state == GST_STATE_READY)
             m_readyState = MediaPlayer::HaveNothing;
@@ -1193,42 +1217,38 @@
         } else if (state == GST_STATE_PLAYING)
             m_paused = false;
 
-        if (m_seeking) {
-            shouldUpdateAfterSeek = true;
-            m_seeking = false;
-            if (!m_paused)
-                gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
-        } else if (!m_paused)
+        if (!m_paused)
             gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
 
         m_networkState = MediaPlayer::Loading;
         break;
     default:
-        LOG_MEDIA_MESSAGE("Else : %d", ret);
+        LOG_MEDIA_MESSAGE("Else : %d", getStateResult);
         break;
     }
 
     m_requestedState = GST_STATE_VOID_PENDING;
 
-    if (seeking())
-        m_readyState = MediaPlayer::HaveNothing;
-
-    if (shouldUpdateAfterSeek)
-        timeChanged();
-
     if (shouldUpdatePlaybackState)
         m_player->playbackStateChanged();
 
     if (m_networkState != oldNetworkState) {
-        LOG_MEDIA_MESSAGE("Network State Changed from %u to %u",
-            oldNetworkState, m_networkState);
+        LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState);
         m_player->networkStateChanged();
     }
     if (m_readyState != oldReadyState) {
-        LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u",
-            oldReadyState, m_readyState);
+        LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState);
         m_player->readyStateChanged();
     }
+
+    if (m_seekIsPending && getStateResult == GST_STATE_CHANGE_SUCCESS && (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)) {
+        LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime);
+        m_seekIsPending = false;
+        m_seeking = gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
+            GST_SEEK_TYPE_SET, toGstClockTime(m_seekTime), GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+        if (!m_seeking)
+            LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime);
+    }
 }
 
 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
@@ -1361,19 +1381,14 @@
 
     // And re-cache it if possible.
     GstState state;
-    gst_element_get_state(m_playBin.get(), &state, 0, 0);
+    GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0);
     float newDuration = duration();
 
-    if (state <= GST_STATE_READY) {
+    if (state > GST_STATE_READY && getStateResult == GST_STATE_CHANGE_SUCCESS) {
         // Don't set m_mediaDurationKnown yet if the pipeline is not
-        // paused. This allows duration() query to fail at least once
+        // stable. This allows duration() query to fail at least once
         // before playback starts and duration becomes known.
-        if (!std::isinf(newDuration))
-            m_mediaDuration = newDuration;
-    } else {
         m_mediaDurationKnown = !std::isinf(newDuration);
-        if (m_mediaDurationKnown)
-            m_mediaDuration = newDuration;
     }
 
     if (!std::isinf(newDuration))

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h (150065 => 150066)


--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h	2013-05-14 12:27:03 UTC (rev 150065)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h	2013-05-14 12:49:06 UTC (rev 150066)
@@ -114,6 +114,7 @@
 
     void cacheDuration();
     void updateStates();
+    void asyncStateChangeDone();
 
     void createGSTPlayBin();
     bool changePipelineState(GstState);
@@ -140,6 +141,9 @@
     bool m_resetPipeline;
     bool m_paused;
     bool m_seeking;
+    bool m_seekIsPending;
+    float m_timeOfOverlappingSeek;
+    bool m_canFallBackToLastFinishedSeekPositon;
     bool m_buffering;
     float m_playbackRate;
     bool m_errorOccured;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to