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

Reply via email to