[GStreamer] Don't set state to NULL until element is destroyed
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Sep 2013 16:51:19 +0000 (16:51 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Sep 2013 16:51:19 +0000 (16:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=117354

Patch by Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> on 2013-09-03
Reviewed by Philippe Normand.

Don't set playbin to NULL until it is going to be destroyed or if we stay
for too long on the READY state. Instead only set the state to READY as this
allows much faster state changes to PAUSED/PLAYING again. playbin internally
caches some state that is destroyed when setting it to NULL.
This state is independent of the URI and it is even possible to change the
URI in READY state.

To avoid having resources (e.g. audio devices) open indefinitely,
when setting the state to READY we create a timeout and if the timeout
is reached we reset the pipeline state to NULL to free resources.

Also now all state changes use the changePipelineState method instead of setting
the playbin state directly with gst_element_set_state, so we have a better control
of when we are requesting state changes.

* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
(WebCore::mediaPlayerPrivateReadyStateTimeoutCallback):
(WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer):
(WebCore::MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer):
(WebCore::MediaPlayerPrivateGStreamer::commitLoad):
(WebCore::MediaPlayerPrivateGStreamer::changePipelineState):
(WebCore::MediaPlayerPrivateGStreamer::setRate):
(WebCore::MediaPlayerPrivateGStreamer::handlePluginInstallerResult):
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@154988 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h

index 9b4ea539296c38e23a90fd2c6e1287e0db12e42a..08156de68a160689d99ad592cd80c0d069e1c9ab 100644 (file)
@@ -1,3 +1,35 @@
+2013-09-03  Andre Moreira Magalhaes   <andre.magalhaes@collabora.co.uk>
+
+        [GStreamer] Don't set state to NULL until element is destroyed
+        https://bugs.webkit.org/show_bug.cgi?id=117354
+
+        Reviewed by Philippe Normand.
+
+        Don't set playbin to NULL until it is going to be destroyed or if we stay
+        for too long on the READY state. Instead only set the state to READY as this
+        allows much faster state changes to PAUSED/PLAYING again. playbin internally
+        caches some state that is destroyed when setting it to NULL.
+        This state is independent of the URI and it is even possible to change the
+        URI in READY state.
+
+        To avoid having resources (e.g. audio devices) open indefinitely,
+        when setting the state to READY we create a timeout and if the timeout
+        is reached we reset the pipeline state to NULL to free resources.
+
+        Also now all state changes use the changePipelineState method instead of setting
+        the playbin state directly with gst_element_set_state, so we have a better control
+        of when we are requesting state changes.
+
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
+        (WebCore::mediaPlayerPrivateReadyStateTimeoutCallback):
+        (WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer):
+        (WebCore::MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer):
+        (WebCore::MediaPlayerPrivateGStreamer::commitLoad):
+        (WebCore::MediaPlayerPrivateGStreamer::changePipelineState):
+        (WebCore::MediaPlayerPrivateGStreamer::setRate):
+        (WebCore::MediaPlayerPrivateGStreamer::handlePluginInstallerResult):
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
+
 2013-09-03  peavo@outlook.com  <peavo@outlook.com>
 
         [WinCairo] Unneeded code in method GlyphPage::fill().
index 17a451774150a8508cc44fb6908bc6b071b8aa2e..42b1c6a974b0b1aee4b31b7f01631667de686880 100644 (file)
@@ -81,6 +81,9 @@ static const gint64 gPercentMax = GST_FORMAT_PERCENT_MAX;
 static const char* gPlaybinName = "playbin2";
 static const gint64 gPercentMax = 100;
 #endif
+// Max interval in seconds to stay in the READY state on manual
+// state change requests.
+static const guint gReadyStateTimerInterval = 60;
 
 GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
 #define GST_CAT_DEFAULT webkit_media_player_debug
@@ -158,6 +161,14 @@ static GstFlowReturn mediaPlayerPrivateNewTextSampleCallback(GObject*, MediaPlay
 }
 #endif
 
+static gboolean mediaPlayerPrivateReadyStateTimeoutCallback(MediaPlayerPrivateGStreamer* player)
+{
+    // This is the callback of the timeout source created in ::changePipelineState.
+    // Reset pipeline if we are sitting on READY state when timeout is reached
+    player->changePipelineState(GST_STATE_NULL);
+    return FALSE;
+}
+
 static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData)
 {
     MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData);
@@ -257,6 +268,7 @@ MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
     , m_audioTimerHandler(0)
     , m_textTimerHandler(0)
     , m_videoTimerHandler(0)
+    , m_readyTimerHandler(0)
     , m_webkitAudioSink(0)
     , m_totalBytes(-1)
     , m_preservesPitch(false)
@@ -283,6 +295,9 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
         g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()),
             reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this);
 
+    if (m_readyTimerHandler)
+        g_source_remove(m_readyTimerHandler);
+
     if (m_playBin) {
         GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
         ASSERT(bus);
@@ -367,7 +382,7 @@ void MediaPlayerPrivateGStreamer::commitLoad()
 
     // GStreamer needs to have the pipeline set to a paused state to
     // start providing anything useful.
-    gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+    changePipelineState(GST_STATE_PAUSED);
 
     setDownloadBuffering();
     updateStates();
@@ -408,7 +423,7 @@ float MediaPlayerPrivateGStreamer::playbackPosition() const
 
 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
 {
-    ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
+    ASSERT(m_playBin);
 
     GstState currentState;
     GstState pending;
@@ -429,6 +444,18 @@ bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
         loadingFailed(MediaPlayer::Empty);
         return false;
     }
+
+    // Create a timer when entering the READY state so that we can free resources
+    // if we stay for too long on READY.
+    // Also lets remove the timer if we request a state change for any state other than READY.
+    // See also https://bugs.webkit.org/show_bug.cgi?id=117354
+    if (newState == GST_STATE_READY && !m_readyTimerHandler) {
+        m_readyTimerHandler = g_timeout_add_seconds(gReadyStateTimerInterval, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateReadyStateTimeoutCallback), this);
+    } else if (newState != GST_STATE_READY && m_readyTimerHandler) {
+        g_source_remove(m_readyTimerHandler);
+        m_readyTimerHandler = 0;
+    }
+
     return true;
 }
 
@@ -728,7 +755,7 @@ void MediaPlayerPrivateGStreamer::setRate(float rate)
     m_changingRate = true;
 
     if (!rate) {
-        gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+        changePipelineState(GST_STATE_PAUSED);
         return;
     }
 
@@ -938,8 +965,8 @@ void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsR
 {
     m_missingPlugins = false;
     if (result == GST_INSTALL_PLUGINS_SUCCESS) {
-        gst_element_set_state(m_playBin.get(), GST_STATE_READY);
-        gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+        changePipelineState(GST_STATE_READY);
+        changePipelineState(GST_STATE_PAUSED);
     }
 }
 
@@ -1144,7 +1171,7 @@ void MediaPlayerPrivateGStreamer::cancelLoad()
         return;
 
     if (m_playBin)
-        gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
+        changePipelineState(GST_STATE_READY);
 }
 
 void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
@@ -1211,8 +1238,12 @@ void MediaPlayerPrivateGStreamer::updateStates()
             m_networkState = MediaPlayer::Empty;
             break;
         case GST_STATE_READY:
-            m_readyState = MediaPlayer::HaveMetadata;
-            m_networkState = MediaPlayer::Empty;
+            // Do not change network/ready states if on EOS and state changed to READY to avoid
+            // recreating the player on HTMLMediaElement.
+            if (!m_isEndReached) {
+                m_readyState = MediaPlayer::HaveMetadata;
+                m_networkState = MediaPlayer::Empty;
+            }
             break;
         case GST_STATE_PAUSED:
         case GST_STATE_PLAYING:
@@ -1253,14 +1284,14 @@ void MediaPlayerPrivateGStreamer::updateStates()
 
             if (didBuffering && !m_buffering && !m_paused) {
                 LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
-                gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
+                changePipelineState(GST_STATE_PLAYING);
             }
         } else if (state == GST_STATE_PLAYING) {
             m_paused = false;
 
             if (m_buffering && !isLiveStream()) {
                 LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
-                gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+                changePipelineState(GST_STATE_PAUSED);
             }
         } else
             m_paused = true;
@@ -1283,8 +1314,8 @@ void MediaPlayerPrivateGStreamer::updateStates()
 
         // A live stream was paused, reset the pipeline.
         if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream()) {
-            gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
-            gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
+            changePipelineState(GST_STATE_READY);
+            changePipelineState(GST_STATE_PLAYING);
         }
 
         break;
@@ -1308,7 +1339,7 @@ void MediaPlayerPrivateGStreamer::updateStates()
             m_paused = false;
 
         if (!m_paused)
-            gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
+            changePipelineState(GST_STATE_PLAYING);
 
         m_networkState = MediaPlayer::Loading;
         break;
@@ -1413,7 +1444,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
 
             // Reset pipeline state.
             m_resetPipeline = true;
-            gst_element_set_state(m_playBin.get(), GST_STATE_READY);
+            changePipelineState(GST_STATE_READY);
 
             GstState state;
             gst_element_get_state(m_playBin.get(), &state, 0, 0);
@@ -1421,7 +1452,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
                 // Set the new uri and start playing.
                 g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
                 m_url = newUrl;
-                gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
+                changePipelineState(GST_STATE_PLAYING);
                 return true;
             }
         } else
@@ -1459,7 +1490,7 @@ void MediaPlayerPrivateGStreamer::didEnd()
 
     if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
         m_paused = true;
-        gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
+        changePipelineState(GST_STATE_READY);
         m_downloadFinished = false;
     }
 }
@@ -1504,6 +1535,13 @@ void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
         m_readyState = MediaPlayer::HaveNothing;
         m_player->readyStateChanged();
     }
+
+    // Loading failed, force reset pipeline and remove ready timer.
+    gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
+    if (m_readyTimerHandler) {
+        g_source_remove(m_readyTimerHandler);
+        m_readyTimerHandler = 0;
+    }
 }
 
 static HashSet<String> mimeTypeCache()
index 29b2ab712b860bfcf8874ec6b8d91c6b591d2ed2..d7f6ed68d2dd9125b93644bce01bc95acda193f7 100644 (file)
@@ -107,6 +107,8 @@ public:
 
     void simulateAudioInterruption();
 
+    bool changePipelineState(GstState);
+
 private:
     MediaPlayerPrivateGStreamer(MediaPlayer*);
 
@@ -127,7 +129,6 @@ private:
     void asyncStateChangeDone();
 
     void createGSTPlayBin();
-    bool changePipelineState(GstState);
 
     bool loadNextLocation();
     void mediaLocationChanged(GstMessage*);
@@ -176,6 +177,7 @@ private:
     guint m_audioTimerHandler;
     guint m_textTimerHandler;
     guint m_videoTimerHandler;
+    guint m_readyTimerHandler;
     GRefPtr<GstElement> m_webkitAudioSink;
     mutable long m_totalBytes;
     KURL m_url;