Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 209190 Details for
Bug 291820
media-sound/quodlibet-2.1 freezes after each song
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Backport of the gapless patch.
quodlibet-2.1-49-gstbe-gapless.patch (text/plain), 13.27 KB, created by
Bassam
on 2009-11-04 09:54:28 UTC
(
hide
)
Description:
Backport of the gapless patch.
Filename:
MIME Type:
Creator:
Bassam
Created:
2009-11-04 09:54:28 UTC
Size:
13.27 KB
patch
obsolete
>diff -Naur quodlibet-2.1.orig/quodlibet/player/gstbe.py quodlibet-2.1/quodlibet/player/gstbe.py >--- quodlibet-2.1.orig/quodlibet/player/gstbe.py 2009-07-04 06:40:37.000000000 +0200 >+++ quodlibet-2.1/quodlibet/player/gstbe.py 2009-11-04 11:13:32.273465834 +0100 >@@ -4,14 +4,17 @@ > # it under the terms of the GNU General Public License version 2 as > # published by the Free Software Foundation > # >-# $Id: gstbe.py e8210497a0bb 2009/06/22 05:20:40 $ >+# $Id$ > > import gobject >+import glib > > import pygst > pygst.require("0.10") > > import gst >+if gst.pygst_version >= (0, 10, 10): >+ import gst.pbutils > > from quodlibet import config > from quodlibet import const >@@ -24,13 +27,15 @@ > """Try to create a GStreamer pipeline: > * Try making the pipeline (defaulting to gconfaudiosink). > * If it fails, fall back to autoaudiosink. >- * If that fails, complain loudly.""" >+ * If that fails, complain loudly. >+ >+ Returns the pipeline's description and a list of disconnected elements.""" > > if pipeline == "gconf": pipeline = "gconfaudiosink profile=music" >- try: pipe = gst.parse_launch(pipeline) >+ try: pipe = [gst.parse_launch(element) for element in pipeline.split('!')] > except gobject.GError, err: > if pipeline != "autoaudiosink": >- try: pipe = gst.parse_launch("autoaudiosink") >+ try: pipe = [gst.parse_launch("autoaudiosink")] > except gobject.GError: pipe = None > else: pipeline = "autoaudiosink" > else: pipe = None >@@ -47,22 +52,71 @@ > > def __init__(self, sinkname, librarian=None): > super(GStreamerPlayer, self).__init__() >- device, sinkname = GStreamerSink(sinkname) >- self.name = sinkname > self.version_info = "GStreamer: %s / PyGSt: %s" % ( > fver(gst.version()), fver(gst.pygst_version)) >- self.bin = gst.element_factory_make('playbin') >+ self.librarian = librarian >+ self.name = sinkname >+ self.bin = None >+ self.connect('destroy', lambda s: self.__destroy_pipeline()) >+ self._in_gapless_transition = False >+ self.paused = True >+ >+ def __init_pipeline(self): >+ if self.bin: return >+ pipeline, self.name = GStreamerSink(self.name) >+ if gst.version() >= (0, 10, 24): >+ self.bin = gst.element_factory_make('playbin2') >+ id = self.bin.connect('about-to-finish', self.__about_to_finish) >+ self.__atf_id = id >+ # The output buffer is necessary to run the song-ended and >+ # song-started events through QL's signal handlers before the >+ # playbin2 hits EOF inside a gapless transition. >+ bufbin = gst.Bin() >+ queue = gst.element_factory_make('queue') >+ queue.set_property('max-size-time', 500 * gst.MSECOND) >+ self._vol_element = vol = gst.element_factory_make('volume') >+ pipeline = [queue, vol] + pipeline >+ for idx, elem in enumerate(pipeline): >+ bufbin.add(elem) >+ if idx > 0: >+ pipeline[idx-1].link(elem) >+ gpad = gst.GhostPad('sink', queue.get_pad('sink')) >+ bufbin.add_pad(gpad) >+ self.bin.set_property('audio-sink', bufbin) >+ else: >+ self.bin = gst.element_factory_make('playbin') >+ self.bin.set_property('audio-sink', pipeline[-1]) >+ self._vol_element = self.bin >+ self.__atf_id = None > self.bin.set_property('video-sink', None) >- self.bin.set_property('audio-sink', device) >+ # ReplayGain information gets lost when destroying >+ self.volume = self.volume > bus = self.bin.get_bus() > bus.add_signal_watch() >- bus.connect('message', self.__message, librarian) >- self.connect_object('destroy', self.bin.set_state, gst.STATE_NULL) >- self.paused = True >+ self.__bus_id = bus.connect('message', self.__message, self.librarian) >+ if gst.pygst_version >= (0, 10, 10): >+ self.__elem_id = bus.connect('message::element', >+ self.__message_elem) >+ >+ def __destroy_pipeline(self): >+ if self.bin is None: return >+ self.bin.set_state(gst.STATE_NULL) >+ bus = self.bin.get_bus() >+ bus.disconnect(self.__bus_id) >+ if gst.pygst_version >= (0, 10, 10): >+ bus.disconnect(self.__elem_id) >+ bus.remove_signal_watch() >+ if self.__atf_id is not None: >+ self.bin.disconnect(self.__atf_id) >+ del self.bin >+ del self._vol_element >+ self.bin = None >+ return True > > def __message(self, bus, message, librarian): > if message.type == gst.MESSAGE_EOS: >- self._source.next_ended() >+ if not self._in_gapless_transition: >+ self._source.next_ended() > self._end(False) > elif message.type == gst.MESSAGE_TAG: > self.__tag(message.parse_tag(), librarian) >@@ -72,24 +126,43 @@ > self.error(err, True) > return True > >+ def __message_elem(self, bus, message): >+ if not message.structure.get_name().startswith('missing-'): >+ return True >+ d = gst.pbutils.missing_plugin_message_get_installer_detail(message) >+ ctx = gst.pbutils.InstallPluginsContext() >+ gobject.idle_add(self.stop) >+ gst.pbutils.install_plugins_async([d], ctx, self.__message_elem_cb) >+ return True >+ >+ def __message_elem_cb(self, result): >+ gst.update_registry() >+ >+ def __about_to_finish(self, pipeline): >+ self._in_gapless_transition = True >+ self._source.next_ended() >+ if self._source.current: >+ self.bin.set_property('uri', self._source.current("~uri")) >+ glib.timeout_add(0, self._end, False, True, >+ priority = glib.PRIORITY_HIGH) >+ > def stop(self): > # On GStreamer, we can release the device when stopped. > if not self.paused: > self._paused = True > if self.song: > self.emit('paused') >- self.bin.set_state(gst.STATE_NULL) >- self.seek(0) >+ self.__destroy_pipeline() > > def do_set_property(self, property, v): > if property.name == 'volume': >+ if self._in_gapless_transition: >+ return > self._volume = v >- if self.song is None: >- self.bin.set_property('volume', v) >- else: >- if config.getboolean("player", "replaygain"): >- profiles = filter(None, self.replaygain_profiles)[0] >- v = max(0.0, min(4.0, v * self.song.replay_gain(profiles))) >+ if self.song and config.getboolean("player", "replaygain"): >+ profiles = filter(None, self.replaygain_profiles)[0] >+ v = max(0.0, min(4.0, v * self.song.replay_gain(profiles))) >+ if self.bin: > self.bin.set_property('volume', v) > else: > raise AttributeError >@@ -97,22 +170,23 @@ > def get_position(self): > """Return the current playback position in milliseconds, > or 0 if no song is playing.""" >- if self.bin.get_property('uri'): >+ if self.song and self.bin: > try: p = self.bin.query_position(gst.FORMAT_TIME)[0] > except gst.QueryError: p = 0 > p //= gst.MSECOND > return p >- else: return 0 >- >+ else: >+ return 0 >+ > def _set_paused(self, paused): > if paused != self._paused: > self._paused = paused > if self.song: > self.emit((paused and 'paused') or 'unpaused') >+ if not self.bin: >+ self.__init_pipeline() > if self._paused: >- if not self.song.is_file: >- self.bin.set_state(gst.STATE_NULL) >- else: self.bin.set_state(gst.STATE_PAUSED) >+ self.bin.set_state(gst.STATE_PAUSED) > else: self.bin.set_state(gst.STATE_PLAYING) > elif paused is True: > # Something wants us to pause between songs, or when >@@ -122,15 +196,14 @@ > paused = property(_get_paused, _set_paused) > > def error(self, message, lock): >- self.bin.set_property('uri', '') >- self.bin.set_state(gst.STATE_NULL) >+ print_w(message) > self.emit('error', self.song, message, lock) > if not self.paused: > self.next() > > def seek(self, pos): > """Seek to a position in the song, in milliseconds.""" >- if self.bin.get_property('uri'): >+ if self.song is not None: > pos = max(0, int(pos)) > if pos >= self._length: > self.paused = True >@@ -143,7 +216,7 @@ > if self.bin.send_event(event): > self.emit('seek', self.song, pos) > >- def _end(self, stopped): >+ def _end(self, stopped, gapless = False): > # We need to set self.song to None before calling our signal > # handlers. Otherwise, if they try to end the song they're given > # (e.g. by removing it), then we get in an infinite loop. >@@ -152,21 +225,27 @@ > self.emit('song-ended', song, stopped) > > # Then, set up the next song. >+ self._in_gapless_transition = False > self.song = self.info = self._source.current > self.emit('song-started', self.song) > > if self.song is not None: >- # Changing the URI in a playbin requires "resetting" it. >- if not self.bin.set_state(gst.STATE_NULL): return >- self.bin.set_property('uri', self.song("~uri")) >- self._length = self.song["~#length"] * 1000 >- if self._paused: self.bin.set_state(gst.STATE_PAUSED) >- else: self.bin.set_state(gst.STATE_PLAYING) >+ self._length = self.info["~#length"] * 1000 >+ if not gapless: >+ # Due to extensive problems with playbin2, we destroy the >+ # entire pipeline and recreate it each time we're not in >+ # a gapless transition. >+ self.__destroy_pipeline() >+ self.__init_pipeline() >+ self.bin.set_property('uri', self.song("~uri")) >+ if self.bin: >+ if self._paused: >+ self.bin.set_state(gst.STATE_PAUSED) >+ else: >+ self.bin.set_state(gst.STATE_PLAYING) > else: >- > self.paused = True >- self.bin.set_state(gst.STATE_NULL) >- self.bin.set_property('uri', '') >+ self.__destroy_pipeline() > > def __tag(self, tags, librarian): > if self.song and self.song.multisong: >@@ -224,7 +303,7 @@ > > def init(librarian): > pipeline = (config.get("player", "gst_pipeline") or >- "gconfaudiosink profile = music") >+ "gconfaudiosink profile=music") > gst.debug_set_default_threshold(gst.LEVEL_ERROR) > if gst.element_make_from_uri( > gst.URI_SRC, >@@ -234,4 +313,4 @@ > raise PlayerError( > _("Unable to open input files"), > _("GStreamer has no element to handle reading files. Check " >- "your GStreamer installation settings.")) >+ "your GStreamer installation settings.")) >diff -Naur quodlibet-2.1.orig/quodlibet/qltk/controls.py quodlibet-2.1/quodlibet/qltk/controls.py >--- quodlibet-2.1.orig/quodlibet/qltk/controls.py 2009-07-04 06:40:37.000000000 +0200 >+++ quodlibet-2.1/quodlibet/qltk/controls.py 2009-11-04 10:45:31.795465728 +0100 >@@ -5,7 +5,7 @@ > # it under the terms of the GNU General Public License version 2 as > # published by the Free Software Foundation > # >-# $Id: controls.py 1d1a43280369 2007/05/18 07:44:07 $ >+# $Id$ > > import gobject > import gtk >@@ -175,13 +175,22 @@ > self.scale.set_value(device.props.volume) > > class StopAfterMenu(gtk.Menu): >+ __menu = None >+ >+ def __new__(klass, parent): >+ if klass.__menu is None: >+ return super(StopAfterMenu, klass).__new__(klass) >+ else: return klass.__menu >+ > def __init__(self, player): >+ if type(self).__menu: return >+ else: type(self).__menu = self > super(StopAfterMenu, self).__init__() > self.__item = gtk.CheckMenuItem(_("Stop after this song")) > self.__item.set_active(False) > self.append(self.__item) > player.connect('paused', self.__paused) >- player.connect('song-ended', self.__ended) >+ player.connect_after('song-ended', self.__ended) > self.__item.show() > > def __paused(self, player): >@@ -228,7 +237,7 @@ > play.connect('button-press-event', self.__play_button_press, safter) > play.connect_object('popup-menu', self.__popup, safter, play.child) > next.connect_object('clicked', self.__next, player) >- player.connect('song-started', self.__song_started, next) >+ player.connect('song-started', self.__song_started, next, play) > player.connect_object('paused', play.set_active, False) > player.connect_object('unpaused', play.set_active, True) > self.show_all() >@@ -245,7 +254,7 @@ > safter.popup(None, None, None, button, time) > return True > >- def __song_started(self, player, song, next): >+ def __song_started(self, player, song, next, play): > next.set_sensitive(bool(song)) > > def __playpause(self, button, player):
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 291820
: 209190 |
209192