1
# -*- coding: utf-8 -*-
3
# Author: Ingelrest François (Francois.Ingelrest@gmail.com)
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU Library General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
def __init__(self, callbackEnded, usePlaybin2=True):
28
self.rgEnabled = False
31
self.eqzEnabled = False
32
self.usePlaybin2 = usePlaybin2
34
self.callbackEnded = callbackEnded
37
def __getPlayer(self):
38
""" Construct and return the GStreamer player """
39
self.__constructPlayer()
40
self.__getPlayer = self.__getPlayer_post # I love Python
45
def __getPlayer_post(self):
46
""" Return the GStreamer player """
50
def __constructPlayer(self):
51
""" Create the GStreamer pipeline """
53
self.player = gst.element_factory_make('playbin2', 'player')
54
self.player.connect('about-to-finish', self.__onAboutToFinish)
56
self.player = gst.element_factory_make('playbin', 'player')
59
self.player.set_property('video-sink', gst.element_factory_make('fakesink', 'fakesink'))
62
self.player.set_property('volume', self.volume)
64
# Change the audio sink to our own bin, so that an equalizer/replay gain element can be added later on if needed
65
self.audiobin = gst.Bin('audiobin')
66
self.audiosink = gst.element_factory_make('autoaudiosink', 'audiosink')
68
# Callback when the source of the playbin is changed
69
self.player.connect('notify::source', self.__onNewPlaybinSource)
71
self.audiobin.add(self.audiosink)
72
self.audiobin.add_pad(gst.GhostPad('sink', self.audiosink.get_pad('sink')))
73
self.player.set_property('audio-sink', self.audiobin)
75
# Monitor messages generated by the player
76
bus = self.player.get_bus()
77
bus.add_signal_watch()
78
bus.connect('message', self.__onGstMessage)
82
self.equalizer = gst.element_factory_make('equalizer-10bands', 'equalizer')
83
self.audiobin.add(self.equalizer)
84
self.audiobin.get_pad('sink').set_target(self.equalizer.get_pad('sink'))
85
self.equalizer.link(self.audiosink)
87
if self.eqzLevels is not None:
88
self.setEqualizerLvls(self.eqzLevels)
92
replaygain = gst.element_factory_make('rgvolume', 'replaygain')
94
self.audiobin.add(replaygain)
95
self.audiobin.get_pad('sink').set_target(replaygain.get_pad('sink'))
97
if self.equalizer is None: replaygain.link(self.audiosink)
98
else: replaygain.link(self.equalizer)
101
def enableEqualizer(self):
102
""" Add an equalizer to the audio chain """
103
self.eqzEnabled = True
106
def enableReplayGain(self):
107
""" Add/Enable a replay gain element """
108
self.rgEnabled = True
111
def setEqualizerLvls(self, lvls):
112
""" Set the level of the 10-bands of the equalizer (levels must be a list/tuple with 10 values lying between -24 and +12) """
114
self.eqzLevels = lvls
116
if self.equalizer is not None:
117
self.equalizer.set_property('band0', lvls[0])
118
self.equalizer.set_property('band1', lvls[1])
119
self.equalizer.set_property('band2', lvls[2])
120
self.equalizer.set_property('band3', lvls[3])
121
self.equalizer.set_property('band4', lvls[4])
122
self.equalizer.set_property('band5', lvls[5])
123
self.equalizer.set_property('band6', lvls[6])
124
self.equalizer.set_property('band7', lvls[7])
125
self.equalizer.set_property('band8', lvls[8])
126
self.equalizer.set_property('band9', lvls[9])
129
def __onNewPlaybinSource(self, playbin, params):
130
""" Change the CR-ROM drive speed to 1 when applicable """
131
source = self.__getPlayer().get_by_name('source')
133
# Didn't find a way to determine the real class of source
134
# So we use the 'paranoia-mode' property to determine whether it's indeed a CD we're playing
136
source.get_property('paranoia-mode')
137
source.set_property('read-speed', self.cdReadSpeed)
142
def __onAboutToFinish(self, isLast):
143
""" End of the track """
144
self.callbackEnded(False)
147
def __onGstMessage(self, bus, msg):
148
""" A new message generated by the player """
149
if msg.type == gst.MESSAGE_EOS:
150
self.callbackEnded(False)
151
elif msg.type == gst.MESSAGE_ERROR:
153
# It seems that the pipeline may not be able to play again any valid stream when an error occurs
154
# We thus create a new one, even if that's quite a ugly solution
155
self.__constructPlayer()
156
self.callbackEnded(True)
161
def setCDReadSpeed(self, speed):
162
""" Set the CD-ROM drive read speed """
163
self.cdReadSpeed = speed
166
def setNextURI(self, uri):
167
""" Set the next URI """
168
self.__getPlayer().set_property('uri', uri.replace('%', '%25').replace('#', '%23'))
171
def setVolume(self, level):
172
""" Set the volume to the given level (0 <= level <= 1) """
173
if level < 0: self.volume = 0
174
elif level > 1: self.volume = 1
175
else: self.volume = level
177
if self.player is not None:
178
self.player.set_property('volume', self.volume)
182
""" Return whether the player is paused """
183
return self.__getPlayer().get_state()[1] == gst.STATE_PAUSED
187
""" Return whether the player is paused """
188
return self.__getPlayer().get_state()[1] == gst.STATE_PLAYING
191
def setURI(self, uri):
192
""" Play the given URI """
193
self.__getPlayer().set_property('uri', uri.replace('%', '%25').replace('#', '%23'))
198
self.__getPlayer().set_state(gst.STATE_PLAYING)
203
self.__getPlayer().set_state(gst.STATE_PAUSED)
208
self.__getPlayer().set_state(gst.STATE_NULL)
211
def seek(self, where):
212
""" Jump to the given location """
213
self.__getPlayer().seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, where)
216
def getPosition(self):
217
""" Return the current position """
218
try: return self.__getPlayer().query_position(gst.FORMAT_TIME)[0]
222
def getDuration(self):
223
""" Return the duration of the current stream """
224
try: return self.__getPlayer().query_duration(gst.FORMAT_TIME)[0]