+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().
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
}
#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);
, m_audioTimerHandler(0)
, m_textTimerHandler(0)
, m_videoTimerHandler(0)
+ , m_readyTimerHandler(0)
, m_webkitAudioSink(0)
, m_totalBytes(-1)
, m_preservesPitch(false)
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);
// 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();
bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
{
- ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
+ ASSERT(m_playBin);
GstState currentState;
GstState pending;
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;
}
m_changingRate = true;
if (!rate) {
- gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+ changePipelineState(GST_STATE_PAUSED);
return;
}
{
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);
}
}
return;
if (m_playBin)
- gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
+ changePipelineState(GST_STATE_READY);
}
void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
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:
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;
// 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;
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;
// 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);
// 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
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;
}
}
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()