Package: quodlibet Version: 2.2+debian-1 Severity: normal Tags: patch upstream
[resend 2, because previous submit seems to be lost] The current versions of quodlibet (using GStreamer) do not handle Internet radio streams correctly. Some time ago (I assume pre version 2, or maybe Xine backend)) it worked correctly. First, on start it does not wait for the buffer to fill, leading to choppy playback and to extremely choppy playback when the net connection is actually used for something besides radio. Second, the stream is literally paused when the user pauses play. When the user unpauses after a longer time, it will continue where it paused and play the rest of the buffer and then fail because it can not get matching followup data anymore. I have attached a patch series against the Debian packaged version but upstream does not appear to have changed the file in question, so the patches should apply either way.
>From ab86d5e05e3d3336c14182680b7047e4bda4faad Mon Sep 17 00:00:00 2001 From: Andreas Bombe <a...@debian.org> Date: Tue, 30 Mar 2010 22:53:46 +0200 Subject: [PATCH 1/3] gstbe: Fix halt playback on backend error Playback was halted when the pipeline wasn't initialized, instead of when the pipeline should be initialized but failed. --- quodlibet/player/gstbe.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/quodlibet/player/gstbe.py b/quodlibet/player/gstbe.py index 160b875..21fb71c 100644 --- a/quodlibet/player/gstbe.py +++ b/quodlibet/player/gstbe.py @@ -208,7 +208,7 @@ class GStreamerPlayer(BasePlayer): "initialized. The pipeline might be invalid, " "or the device may be in use. Check the " "player preferences.")).run() - self._paused = paused = True + self._paused = paused = True self.emit((paused and 'paused') or 'unpaused') if self.bin: if self._paused: -- 1.7.0.3 >From bb51786bf414c14b9831de6fbb1c37ccb7bfdb52 Mon Sep 17 00:00:00 2001 From: Andreas Bombe <a...@debian.org> Date: Tue, 30 Mar 2010 23:07:28 +0200 Subject: [PATCH 2/3] gstbe: Do not pause network streams, destroy pipeline for them Do the same thing the Xine backend does: If the request is to pause, check if song is a file. Pause if file, stop (in GStreamer backend meaning destroy pipeline) if it is not. On unpause, the code to initialize a new pipeline if it does not exist is already in place. Without this, radio streams that were paused for a long time will play what is left in the buffer and then fail, since no matching followup data can be received. --- quodlibet/player/gstbe.py | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/quodlibet/player/gstbe.py b/quodlibet/player/gstbe.py index 21fb71c..53ac364 100644 --- a/quodlibet/player/gstbe.py +++ b/quodlibet/player/gstbe.py @@ -211,10 +211,12 @@ class GStreamerPlayer(BasePlayer): self._paused = paused = True self.emit((paused and 'paused') or 'unpaused') if self.bin: - if self._paused: + if not self._paused: + self.bin.set_state(gst.STATE_PLAYING) + elif self.song.is_file: self.bin.set_state(gst.STATE_PAUSED) else: - self.bin.set_state(gst.STATE_PLAYING) + self.__destroy_pipeline() elif paused is True: # Something wants us to pause between songs, or when # we've got no song playing (probably StopAfterMenu). -- 1.7.0.3 >From 5da8e195ce168b7016f52522e099e320ce300cd5 Mon Sep 17 00:00:00 2001 From: Andreas Bombe <a...@debian.org> Date: Mon, 29 Mar 2010 01:00:06 +0200 Subject: [PATCH 3/3] gstbe: Fix lack of buffering on radio streams Listen to gst.MESSAGE_BUFFERING messages and set the stream to paused while it is less than 100 percent. Introduce a new inhibit_play flag in the player to allow the stream to be paused internally without leaking this internal state to the user interface. --- quodlibet/player/gstbe.py | 22 +++++++++++++++++++++- 1 files changed, 21 insertions(+), 1 deletions(-) diff --git a/quodlibet/player/gstbe.py b/quodlibet/player/gstbe.py index 53ac364..793e6cc 100644 --- a/quodlibet/player/gstbe.py +++ b/quodlibet/player/gstbe.py @@ -65,6 +65,7 @@ class GStreamerPlayer(BasePlayer): self.connect('destroy', lambda s: self.__destroy_pipeline()) self._in_gapless_transition = False self.paused = True + self._inhibit_play = False def __init_pipeline(self): if self.bin: return True @@ -112,6 +113,7 @@ class GStreamerPlayer(BasePlayer): return True def __destroy_pipeline(self): + self._inhibit_play = False if self.bin is None: return self.bin.set_state(gst.STATE_NULL) bus = self.bin.get_bus() @@ -137,6 +139,9 @@ class GStreamerPlayer(BasePlayer): err, debug = message.parse_error() err = str(err).decode(const.ENCODING, 'replace') self.error(err, True) + elif message.type == gst.MESSAGE_BUFFERING: + percent = message.parse_buffering() + self._set_inhibit_play(percent < 100) return True def __message_elem(self, bus, message): @@ -194,6 +199,20 @@ class GStreamerPlayer(BasePlayer): else: return 0 + def _set_inhibit_play(self, inhibit): + """Set the inhibit play flag. If set, this will pause the pipeline + without giving the appearance of being paused to the outside. This + is for internal uses of pausing, such as for waiting while the buffer + is being filled for network streams.""" + if inhibit == self._inhibit_play: + return + + self._inhibit_play = inhibit + if inhibit: + self.bin.set_state(gst.STATE_PAUSED) + elif not self.paused: + self.bin.set_state(gst.STATE_PLAYING) + def _set_paused(self, paused): if paused != self._paused: self._paused = paused @@ -212,7 +231,8 @@ class GStreamerPlayer(BasePlayer): self.emit((paused and 'paused') or 'unpaused') if self.bin: if not self._paused: - self.bin.set_state(gst.STATE_PLAYING) + if not self._inhibit_play: + self.bin.set_state(gst.STATE_PLAYING) elif self.song.is_file: self.bin.set_state(gst.STATE_PAUSED) else: -- 1.7.0.3