Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 291820
Collapse All | Expand All

(-)quodlibet-2.1.orig/quodlibet/player/gstbe.py (-41 / +120 lines)
Lines 4-17 Link Here
4
# it under the terms of the GNU General Public License version 2 as
4
# it under the terms of the GNU General Public License version 2 as
5
# published by the Free Software Foundation
5
# published by the Free Software Foundation
6
#
6
#
7
# $Id: gstbe.py e8210497a0bb 2009/06/22 05:20:40 $
7
# $Id$
8
8
9
import gobject
9
import gobject
10
import glib
10
11
11
import pygst
12
import pygst
12
pygst.require("0.10")
13
pygst.require("0.10")
13
14
14
import gst
15
import gst
16
if gst.pygst_version >= (0, 10, 10):
17
    import gst.pbutils
15
18
16
from quodlibet import config
19
from quodlibet import config
17
from quodlibet import const
20
from quodlibet import const
Lines 24-36 Link Here
24
    """Try to create a GStreamer pipeline:
27
    """Try to create a GStreamer pipeline:
25
    * Try making the pipeline (defaulting to gconfaudiosink).
28
    * Try making the pipeline (defaulting to gconfaudiosink).
26
    * If it fails, fall back to autoaudiosink.
29
    * If it fails, fall back to autoaudiosink.
27
    * If that fails, complain loudly."""
30
    * If that fails, complain loudly.
31
32
    Returns the pipeline's description and a list of disconnected elements."""
28
33
29
    if pipeline == "gconf": pipeline = "gconfaudiosink profile=music"
34
    if pipeline == "gconf": pipeline = "gconfaudiosink profile=music"
30
    try: pipe = gst.parse_launch(pipeline)
35
    try: pipe = [gst.parse_launch(element) for element in pipeline.split('!')]
31
    except gobject.GError, err:
36
    except gobject.GError, err:
32
        if pipeline != "autoaudiosink":
37
        if pipeline != "autoaudiosink":
33
            try: pipe = gst.parse_launch("autoaudiosink")
38
            try: pipe = [gst.parse_launch("autoaudiosink")]
34
            except gobject.GError: pipe = None
39
            except gobject.GError: pipe = None
35
            else: pipeline = "autoaudiosink"
40
            else: pipeline = "autoaudiosink"
36
        else: pipe = None
41
        else: pipe = None
Lines 47-68 Link Here
47
52
48
    def __init__(self, sinkname, librarian=None):
53
    def __init__(self, sinkname, librarian=None):
49
        super(GStreamerPlayer, self).__init__()
54
        super(GStreamerPlayer, self).__init__()
50
        device, sinkname = GStreamerSink(sinkname)
51
        self.name = sinkname
52
        self.version_info = "GStreamer: %s / PyGSt: %s" % (
55
        self.version_info = "GStreamer: %s / PyGSt: %s" % (
53
            fver(gst.version()), fver(gst.pygst_version))
56
            fver(gst.version()), fver(gst.pygst_version))
54
        self.bin = gst.element_factory_make('playbin')
57
        self.librarian = librarian
58
        self.name = sinkname
59
        self.bin = None
60
        self.connect('destroy', lambda s: self.__destroy_pipeline())
61
        self._in_gapless_transition = False
62
        self.paused = True
63
64
    def __init_pipeline(self):
65
        if self.bin: return
66
        pipeline, self.name = GStreamerSink(self.name)
67
        if gst.version() >= (0, 10, 24):
68
            self.bin = gst.element_factory_make('playbin2')
69
            id = self.bin.connect('about-to-finish', self.__about_to_finish)
70
            self.__atf_id = id
71
            # The output buffer is necessary to run the song-ended and
72
            # song-started events through QL's signal handlers before the
73
            # playbin2 hits EOF inside a gapless transition.
74
            bufbin = gst.Bin()
75
            queue = gst.element_factory_make('queue')
76
            queue.set_property('max-size-time', 500 * gst.MSECOND)
77
            self._vol_element = vol = gst.element_factory_make('volume')
78
            pipeline = [queue, vol] + pipeline
79
            for idx, elem in enumerate(pipeline):
80
                bufbin.add(elem)
81
                if idx > 0:
82
                    pipeline[idx-1].link(elem)
83
            gpad = gst.GhostPad('sink', queue.get_pad('sink'))
84
            bufbin.add_pad(gpad)
85
            self.bin.set_property('audio-sink', bufbin)
86
        else:
87
            self.bin = gst.element_factory_make('playbin')
88
            self.bin.set_property('audio-sink', pipeline[-1])
89
            self._vol_element = self.bin
90
            self.__atf_id = None
55
        self.bin.set_property('video-sink', None)
91
        self.bin.set_property('video-sink', None)
56
        self.bin.set_property('audio-sink', device)
92
        # ReplayGain information gets lost when destroying
93
        self.volume = self.volume
57
        bus = self.bin.get_bus()
94
        bus = self.bin.get_bus()
58
        bus.add_signal_watch()
95
        bus.add_signal_watch()
59
        bus.connect('message', self.__message, librarian)
96
        self.__bus_id = bus.connect('message', self.__message, self.librarian)
60
        self.connect_object('destroy', self.bin.set_state, gst.STATE_NULL)
97
        if gst.pygst_version >= (0, 10, 10):
61
        self.paused = True
98
            self.__elem_id = bus.connect('message::element',
99
                                         self.__message_elem)
100
101
    def __destroy_pipeline(self):
102
        if self.bin is None: return
103
        self.bin.set_state(gst.STATE_NULL)
104
        bus = self.bin.get_bus()
105
        bus.disconnect(self.__bus_id)
106
        if gst.pygst_version >= (0, 10, 10):
107
            bus.disconnect(self.__elem_id)
108
        bus.remove_signal_watch()
109
        if self.__atf_id is not None:
110
            self.bin.disconnect(self.__atf_id)
111
        del self.bin
112
        del self._vol_element
113
        self.bin = None
114
        return True
62
115
63
    def __message(self, bus, message, librarian):
116
    def __message(self, bus, message, librarian):
64
        if message.type == gst.MESSAGE_EOS:
117
        if message.type == gst.MESSAGE_EOS:
65
            self._source.next_ended()
118
            if not self._in_gapless_transition:
119
                self._source.next_ended()
66
            self._end(False)
120
            self._end(False)
67
        elif message.type == gst.MESSAGE_TAG:
121
        elif message.type == gst.MESSAGE_TAG:
68
            self.__tag(message.parse_tag(), librarian)
122
            self.__tag(message.parse_tag(), librarian)
Lines 72-95 Link Here
72
            self.error(err, True)
126
            self.error(err, True)
73
        return True
127
        return True
74
128
129
    def __message_elem(self, bus, message):
130
        if not message.structure.get_name().startswith('missing-'):
131
            return True
132
        d = gst.pbutils.missing_plugin_message_get_installer_detail(message)
133
        ctx = gst.pbutils.InstallPluginsContext()
134
        gobject.idle_add(self.stop)
135
        gst.pbutils.install_plugins_async([d], ctx, self.__message_elem_cb)
136
        return True
137
138
    def __message_elem_cb(self, result):
139
        gst.update_registry()
140
141
    def __about_to_finish(self, pipeline):
142
        self._in_gapless_transition = True
143
        self._source.next_ended()
144
        if self._source.current:
145
            self.bin.set_property('uri', self._source.current("~uri"))
146
            glib.timeout_add(0, self._end, False, True,
147
                             priority = glib.PRIORITY_HIGH)
148
75
    def stop(self):
149
    def stop(self):
76
        # On GStreamer, we can release the device when stopped.
150
        # On GStreamer, we can release the device when stopped.
77
        if not self.paused:
151
        if not self.paused:
78
            self._paused = True
152
            self._paused = True
79
            if self.song:
153
            if self.song:
80
                self.emit('paused')
154
                self.emit('paused')
81
                self.bin.set_state(gst.STATE_NULL)
155
        self.__destroy_pipeline()
82
        self.seek(0)
83
156
84
    def do_set_property(self, property, v):
157
    def do_set_property(self, property, v):
85
        if property.name == 'volume':
158
        if property.name == 'volume':
159
            if self._in_gapless_transition:
160
                return
86
            self._volume = v
161
            self._volume = v
87
            if self.song is None:
162
            if self.song and config.getboolean("player", "replaygain"):
88
                self.bin.set_property('volume', v)
163
                profiles = filter(None, self.replaygain_profiles)[0]
89
            else:
164
                v = max(0.0, min(4.0, v * self.song.replay_gain(profiles)))
90
                if config.getboolean("player", "replaygain"):
165
            if self.bin:
91
                    profiles = filter(None, self.replaygain_profiles)[0]
92
                    v = max(0.0, min(4.0, v * self.song.replay_gain(profiles)))
93
                self.bin.set_property('volume', v)
166
                self.bin.set_property('volume', v)
94
        else:
167
        else:
95
            raise AttributeError
168
            raise AttributeError
Lines 97-118 Link Here
97
    def get_position(self):
170
    def get_position(self):
98
        """Return the current playback position in milliseconds,
171
        """Return the current playback position in milliseconds,
99
        or 0 if no song is playing."""
172
        or 0 if no song is playing."""
100
        if self.bin.get_property('uri'):
173
        if self.song and self.bin:
101
            try: p = self.bin.query_position(gst.FORMAT_TIME)[0]
174
            try: p = self.bin.query_position(gst.FORMAT_TIME)[0]
102
            except gst.QueryError: p = 0
175
            except gst.QueryError: p = 0
103
            p //= gst.MSECOND
176
            p //= gst.MSECOND
104
            return p
177
            return p
105
        else: return 0
178
        else:
106
        
179
            return 0
180
107
    def _set_paused(self, paused):
181
    def _set_paused(self, paused):
108
        if paused != self._paused:
182
        if paused != self._paused:
109
            self._paused = paused
183
            self._paused = paused
110
            if self.song:
184
            if self.song:
111
                self.emit((paused and 'paused') or 'unpaused')
185
                self.emit((paused and 'paused') or 'unpaused')
186
                if not self.bin:
187
                    self.__init_pipeline()
112
                if self._paused:
188
                if self._paused:
113
                   if not self.song.is_file:
189
                    self.bin.set_state(gst.STATE_PAUSED)
114
                       self.bin.set_state(gst.STATE_NULL)
115
                   else: self.bin.set_state(gst.STATE_PAUSED)
116
                else: self.bin.set_state(gst.STATE_PLAYING)
190
                else: self.bin.set_state(gst.STATE_PLAYING)
117
            elif paused is True:
191
            elif paused is True:
118
                # Something wants us to pause between songs, or when
192
                # Something wants us to pause between songs, or when
Lines 122-136 Link Here
122
    paused = property(_get_paused, _set_paused)
196
    paused = property(_get_paused, _set_paused)
123
197
124
    def error(self, message, lock):
198
    def error(self, message, lock):
125
        self.bin.set_property('uri', '')
199
        print_w(message)
126
        self.bin.set_state(gst.STATE_NULL)
127
        self.emit('error', self.song, message, lock)
200
        self.emit('error', self.song, message, lock)
128
        if not self.paused:
201
        if not self.paused:
129
            self.next()
202
            self.next()
130
203
131
    def seek(self, pos):
204
    def seek(self, pos):
132
        """Seek to a position in the song, in milliseconds."""
205
        """Seek to a position in the song, in milliseconds."""
133
        if self.bin.get_property('uri'):
206
        if self.song is not None:
134
            pos = max(0, int(pos))
207
            pos = max(0, int(pos))
135
            if pos >= self._length:
208
            if pos >= self._length:
136
                self.paused = True
209
                self.paused = True
Lines 143-149 Link Here
143
            if self.bin.send_event(event):
216
            if self.bin.send_event(event):
144
                self.emit('seek', self.song, pos)
217
                self.emit('seek', self.song, pos)
145
218
146
    def _end(self, stopped):
219
    def _end(self, stopped, gapless = False):
147
        # We need to set self.song to None before calling our signal
220
        # We need to set self.song to None before calling our signal
148
        # handlers. Otherwise, if they try to end the song they're given
221
        # handlers. Otherwise, if they try to end the song they're given
149
        # (e.g. by removing it), then we get in an infinite loop.
222
        # (e.g. by removing it), then we get in an infinite loop.
Lines 152-172 Link Here
152
        self.emit('song-ended', song, stopped)
225
        self.emit('song-ended', song, stopped)
153
226
154
        # Then, set up the next song.
227
        # Then, set up the next song.
228
        self._in_gapless_transition = False
155
        self.song = self.info = self._source.current
229
        self.song = self.info = self._source.current
156
        self.emit('song-started', self.song)
230
        self.emit('song-started', self.song)
157
231
158
        if self.song is not None:
232
        if self.song is not None:
159
            # Changing the URI in a playbin requires "resetting" it.
233
            self._length = self.info["~#length"] * 1000
160
            if not self.bin.set_state(gst.STATE_NULL): return
234
            if not gapless:
161
            self.bin.set_property('uri', self.song("~uri"))
235
                # Due to extensive problems with playbin2, we destroy the
162
            self._length = self.song["~#length"] * 1000
236
                # entire pipeline and recreate it each time we're not in
163
            if self._paused: self.bin.set_state(gst.STATE_PAUSED)
237
                # a gapless transition.
164
            else: self.bin.set_state(gst.STATE_PLAYING)
238
                self.__destroy_pipeline()
239
                self.__init_pipeline()
240
                self.bin.set_property('uri', self.song("~uri"))
241
            if self.bin:
242
                if self._paused:
243
                    self.bin.set_state(gst.STATE_PAUSED)
244
                else:
245
                    self.bin.set_state(gst.STATE_PLAYING)
165
        else:
246
        else:
166
            
167
            self.paused = True
247
            self.paused = True
168
            self.bin.set_state(gst.STATE_NULL)
248
            self.__destroy_pipeline()
169
            self.bin.set_property('uri', '')
170
249
171
    def __tag(self, tags, librarian):
250
    def __tag(self, tags, librarian):
172
        if self.song and self.song.multisong:
251
        if self.song and self.song.multisong:
Lines 224-230 Link Here
224
303
225
def init(librarian):
304
def init(librarian):
226
    pipeline = (config.get("player", "gst_pipeline") or
305
    pipeline = (config.get("player", "gst_pipeline") or
227
                "gconfaudiosink profile = music")
306
                "gconfaudiosink profile=music")
228
    gst.debug_set_default_threshold(gst.LEVEL_ERROR)
307
    gst.debug_set_default_threshold(gst.LEVEL_ERROR)
229
    if gst.element_make_from_uri(
308
    if gst.element_make_from_uri(
230
        gst.URI_SRC,
309
        gst.URI_SRC,
Lines 234-237 Link Here
234
        raise PlayerError(
313
        raise PlayerError(
235
            _("Unable to open input files"),
314
            _("Unable to open input files"),
236
            _("GStreamer has no element to handle reading files. Check "
315
            _("GStreamer has no element to handle reading files. Check "
237
              "your GStreamer installation settings."))
316
                "your GStreamer installation settings."))
(-)quodlibet-2.1.orig/quodlibet/qltk/controls.py (-4 / +13 lines)
Lines 5-11 Link Here
5
# it under the terms of the GNU General Public License version 2 as
5
# it under the terms of the GNU General Public License version 2 as
6
# published by the Free Software Foundation
6
# published by the Free Software Foundation
7
#
7
#
8
# $Id: controls.py 1d1a43280369 2007/05/18 07:44:07 $
8
# $Id$
9
9
10
import gobject
10
import gobject
11
import gtk
11
import gtk
Lines 175-187 Link Here
175
        self.scale.set_value(device.props.volume)
175
        self.scale.set_value(device.props.volume)
176
176
177
class StopAfterMenu(gtk.Menu):
177
class StopAfterMenu(gtk.Menu):
178
    __menu = None
179
180
    def __new__(klass, parent):
181
        if klass.__menu is None:
182
            return super(StopAfterMenu, klass).__new__(klass)
183
        else: return klass.__menu
184
178
    def __init__(self, player):
185
    def __init__(self, player):
186
        if type(self).__menu: return
187
        else: type(self).__menu = self
179
        super(StopAfterMenu, self).__init__()
188
        super(StopAfterMenu, self).__init__()
180
        self.__item = gtk.CheckMenuItem(_("Stop after this song"))
189
        self.__item = gtk.CheckMenuItem(_("Stop after this song"))
181
        self.__item.set_active(False)
190
        self.__item.set_active(False)
182
        self.append(self.__item)
191
        self.append(self.__item)
183
        player.connect('paused', self.__paused)
192
        player.connect('paused', self.__paused)
184
        player.connect('song-ended', self.__ended)
193
        player.connect_after('song-ended', self.__ended)
185
        self.__item.show()
194
        self.__item.show()
186
195
187
    def __paused(self, player):
196
    def __paused(self, player):
Lines 228-234 Link Here
228
        play.connect('button-press-event', self.__play_button_press, safter)
237
        play.connect('button-press-event', self.__play_button_press, safter)
229
        play.connect_object('popup-menu', self.__popup, safter, play.child)
238
        play.connect_object('popup-menu', self.__popup, safter, play.child)
230
        next.connect_object('clicked', self.__next, player)
239
        next.connect_object('clicked', self.__next, player)
231
        player.connect('song-started', self.__song_started, next)
240
        player.connect('song-started', self.__song_started, next, play)
232
        player.connect_object('paused', play.set_active, False)
241
        player.connect_object('paused', play.set_active, False)
233
        player.connect_object('unpaused', play.set_active, True)
242
        player.connect_object('unpaused', play.set_active, True)
234
        self.show_all()
243
        self.show_all()
Lines 245-251 Link Here
245
            safter.popup(None, None, None, button, time)
254
            safter.popup(None, None, None, button, time)
246
        return True
255
        return True
247
256
248
    def __song_started(self, player, song, next):
257
    def __song_started(self, player, song, next, play):
249
        next.set_sensitive(bool(song))
258
        next.set_sensitive(bool(song))
250
259
251
    def __playpause(self, button, player):
260
    def __playpause(self, button, player):

Return to bug 291820