[gnome-music/wip/jfelder/gstplayer-asynchronous-state: 3/3] gstplayer: Switch to an asynchronous state property
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/gstplayer-asynchronous-state: 3/3] gstplayer: Switch to an asynchronous state property
- Date: Tue, 9 Jun 2020 11:18:33 +0000 (UTC)
commit 703692e9388dab4ab0d093169812b5a4a18b2517
Author: Jean Felder <jfelder src gnome org>
Date: Wed May 27 19:18:04 2020 +0200
gstplayer: Switch to an asynchronous state property
GStreamer pipeline state changes (via the set_state() function) can
sometimes complete later on if the player needs a long time to perform
the state change. However, GstPlayer always returns a changed state
even if the state change has not happened yet. In other words, the
asynchronous nature of the pipeline state is forced to become
synchronous.
On some occasions, this can lead to the pipeline becoming completely
unresponsive such as in #360.
Fix the issue by listening to the "state-changed" message of the bus
to get all the state changes (the synchronous and achronous ones) and
changing the GstPlayer state property into an explicit notify one. A
notify::state signal is emitted in the "state-changed" callback. This
way, the state changes of GstPlayer match the state changes of the
GStreamer pipeline.
The "async-done" message is now only used to handle seek events.
Closes: #360
gnomemusic/gstplayer.py | 58 ++++++++++++++++++++++++++-----------------------
1 file changed, 31 insertions(+), 27 deletions(-)
---
diff --git a/gnomemusic/gstplayer.py b/gnomemusic/gstplayer.py
index 5cff75ab..978a0e8d 100644
--- a/gnomemusic/gstplayer.py
+++ b/gnomemusic/gstplayer.py
@@ -86,11 +86,12 @@ class GstPlayer(GObject.GObject):
self._bus.connect('message::element', self._on_bus_element)
self._bus.connect('message::eos', self._on_bus_eos)
self._bus.connect('message::new-clock', self._on_new_clock)
+ self._bus.connect("message::state-changed", self._on_state_changed)
self._bus.connect("message::stream-start", self._on_bus_stream_start)
self._player.connect("about-to-finish", self._on_about_to_finish)
- self.props.state = Playback.STOPPED
+ self._state = Playback.STOPPED
def _setup_replaygain(self):
"""Set up replaygain"""
@@ -126,6 +127,11 @@ class GstPlayer(GObject.GObject):
self.emit("about-to-finish")
def _on_async_done(self, bus, message):
+ if self._seek:
+ self._seek = False
+ self.emit("seek-finished")
+
+ def _query_duration(self):
success, duration = self._player.query_duration(
Gst.Format.TIME)
@@ -134,12 +140,6 @@ class GstPlayer(GObject.GObject):
else:
self.props.duration = duration
- if self._seek is True:
- self._seek = False
- self.emit("seek-finished")
-
- self.notify("state")
-
def _on_new_clock(self, bus, message):
clock = message.parse_new_clock()
id_ = clock.new_periodic_id(0, 1 * Gst.SECOND)
@@ -155,7 +155,7 @@ class GstPlayer(GObject.GObject):
def _on_bus_stream_start(self, bus, message):
def delayed_query():
- self._on_async_done(None, None)
+ self._query_duration()
self._tick = 0
self.emit("stream-start")
@@ -163,6 +163,25 @@ class GstPlayer(GObject.GObject):
# have been set yet.
GLib.timeout_add(1, delayed_query)
+ def _on_state_changed(self, bus, message):
+ if message.src != self._player:
+ return
+
+ old_state, new_state, _ = message.parse_state_changed()
+ self._log.debug(
+ "Player state changed: {} -> {}".format(old_state, new_state))
+
+ if new_state == Gst.State.PAUSED:
+ self._state = Playback.PAUSED
+ elif new_state == Gst.State.PLAYING:
+ self._state = Playback.PLAYING
+ elif new_state == Gst.State.READY:
+ self._state = Playback.LOADING
+ else:
+ self._state = Playback.STOPPED
+
+ self.notify("state")
+
def _on_bus_error(self, bus, message):
if self._is_missing_plugin_message(message):
self.props.state = Playback.PAUSED
@@ -185,31 +204,16 @@ class GstPlayer(GObject.GObject):
def _on_bus_eos(self, bus, message):
self.emit('eos')
- def _get_playback_status(self):
- ok, state, pending = self._player.get_state(0)
-
- if ok == Gst.StateChangeReturn.ASYNC:
- state = pending
- elif (ok != Gst.StateChangeReturn.SUCCESS):
- return Playback.STOPPED
-
- if state == Gst.State.PLAYING:
- return Playback.PLAYING
- elif state == Gst.State.PAUSED:
- return Playback.PAUSED
- elif state == Gst.State.READY:
- return Playback.LOADING
-
- return Playback.STOPPED
-
- @GObject.Property(type=int)
+ @GObject.Property(
+ type=int, flags=GObject.ParamFlags.READWRITE
+ | GObject.ParamFlags.EXPLICIT_NOTIFY)
def state(self):
"""Current state of the player
:return: state
:rtype: Playback (enum)
"""
- return self._get_playback_status()
+ return self._state
@state.setter
def state(self, state):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]