import gst
import util
import dbus
-import dbus.service
+
+import jamaendo
+from settings import settings
+from postoffice import postoffice
log = logging.getLogger(__name__)
self.filesrc = None
self.filesrc_property = None
self.volume_control = None
- self.volume_multiplier = 1
+ self.volume_multiplier = 1.
self.volume_property = None
self.eos_callback = lambda: self.stop()
+ postoffice.connect('settings-changed', self, self.on_settings_changed)
+
+ def on_settings_changed(self, key, value):
+ if key == 'volume':
+ self._set_volume_level(value)
+ #postoffice.disconnect(self)
+
def play_url(self, filetype, uri):
if None in (filetype, uri):
self.player = None
return False
- log.debug("Setting up for %s : %s", filetype, uri)
-
- # On maemo use software decoding to workaround some bugs their gst:
- # 1. Weird volume bugs in playbin when playing ogg or wma files
- # 2. When seeking the DSPs sometimes lie about the real position info
- if True:
- self._maemo_setup_playbin_player(uri)
- elif util.platform == 'maemo':
- if not self._maemo_setup_hardware_player(filetype):
- self._maemo_setup_software_player()
- log.debug( 'Using software decoding (maemo)' )
+ _first = False
+ if self.player is None:
+ _first = True
+ if False:
+ self._maemo_setup_playbin2_player(uri)
+ log.debug('Using playbin2 (maemo)')
+ elif util.platform == 'maemo':
+ self._maemo_setup_playbin_player()
+ log.debug('Using playbin (maemo)')
else:
- log.debug( 'Using hardware decoding (maemo)' )
- else:
- # This is for *ahem* "normal" versions of gstreamer
- self._setup_playbin_player()
- log.debug( 'Using playbin (non-maemo)' )
+ self._setup_playbin_player()
+ log.debug( 'Using playbin (non-maemo)' )
- self._set_uri_to_be_played(uri)
+ bus = self.player.get_bus()
+ bus.add_signal_watch()
+ bus.connect('message', self._on_message)
+ self._set_volume_level(settings.volume)
- bus = self.player.get_bus()
- bus.add_signal_watch()
- bus.connect('message', self._on_message)
-
- self._set_volume_level( 1 )
+ self._set_uri_to_be_played(uri)
self.play()
return True
return self.STATES.get(state, 'none')
return 'none'
+ def get_position_duration(self):
+ try:
+ pos_int = self.player.query_position(self.time_format, None)[0]
+ dur_int = self.player.query_duration(self.time_format, None)[0]
+ except Exception, e:
+ log.exception('Error getting position')
+ pos_int = dur_int = 0
+ return pos_int, dur_int
+
def playing(self):
return self.get_state() == 'playing'
def play(self):
if self.player:
- log.debug("playing")
self.player.set_state(gst.STATE_PLAYING)
def pause(self):
if self.player:
self.player.set_state(gst.STATE_PAUSED)
- def stop(self):
+ def stop(self, reset = True):
if self.player:
self.player.set_state(gst.STATE_NULL)
- self.player = None
+ if reset:
+ self.player = None
- def _maemo_setup_playbin_player(self, url):
+ def _maemo_setup_playbin2_player(self, url):
self.player = gst.parse_launch("playbin2 uri=%s" % (url,))
self.filesrc = self.player
self.filesrc_property = 'uri'
self.volume_multiplier = 1.
self.volume_property = 'volume'
- def _maemo_setup_hardware_player( self, filetype ):
- """ Setup a hardware player for mp3 or aac audio using
- dspaacsink or dspmp3sink """
-
- if filetype in [ 'mp3', 'aac', 'mp4', 'm4a' ]:
- self.player = gst.element_factory_make('playbin', 'player')
- self.filesrc = self.player
- self.filesrc_property = 'uri'
- self.volume_control = self.player
- self.volume_multiplier = 10.
- self.volume_property = 'volume'
- return True
- else:
- return False
-
- def _maemo_setup_software_player( self ):
- """
- Setup a software decoding player for maemo, this is the only choice
- for decoding wma and ogg or if audio is to be piped to a bluetooth
- headset (this is because the audio must first be decoded only to be
- re-encoded using sbcenc.
- """
-
- self.player = gst.Pipeline('player')
- src = gst.element_factory_make('gnomevfssrc', 'src')
- decoder = gst.element_factory_make('decodebin', 'decoder')
- convert = gst.element_factory_make('audioconvert', 'convert')
- resample = gst.element_factory_make('audioresample', 'resample')
- sink = gst.element_factory_make('dsppcmsink', 'sink')
-
- self.filesrc = src # pointer to the main source element
- self.filesrc_property = 'location'
- self.volume_control = sink
- self.volume_multiplier = 1
- self.volume_property = 'fvolume'
-
- # Add the various elements to the player pipeline
- self.player.add( src, decoder, convert, resample, sink )
-
- # Link what can be linked now, the decoder->convert happens later
- gst.element_link_many( src, decoder )
- gst.element_link_many( convert, resample, sink )
-
- # We can't link the two halves of the pipeline until it comes
- # time to start playing, this singal lets us know when it's time.
- # This is because the output from decoder can't be determined until
- # decoder knows what it's decoding.
- decoder.connect('pad-added',
- self._on_decoder_pad_added,
- convert.get_pad('sink') )
+ def _maemo_setup_playbin_player( self):
+ self.player = gst.element_factory_make('playbin2', 'player')
+ self.filesrc = self.player
+ self.filesrc_property = 'uri'
+ self.volume_control = self.player
+ self.volume_multiplier = 1.
+ self.volume_property = 'volume'
+ return True
def _setup_playbin_player( self ):
""" This is for situations where we have a normal (read: non-maemo)
self.filesrc = self.player
self.filesrc_property = 'uri'
self.volume_control = self.player
- self.volume_multiplier = 1.
+ self.volume_multiplier = 10.
self.volume_property = 'volume'
def _on_decoder_pad_added(self, decoder, src_pad, sink_pad):
assert 0 <= value <= 1
if self.volume_control is not None:
- vol = value * self.volume_multiplier
+ vol = value * float(self.volume_multiplier)
+ log.debug("Setting volume to %s", vol)
self.volume_control.set_property( self.volume_property, vol )
def _set_uri_to_be_played(self, uri):
# Sets the right property depending on the platform of self.filesrc
if self.player is not None:
self.filesrc.set_property(self.filesrc_property, uri)
+ log.info("%s", uri)
def _on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
+ log.debug("Gstreamer: End of stream")
self.eos_callback()
-
+ #elif t == gst.MESSAGE_STATE_CHANGED:
+ # if (message.src == self.player and
+ # message.structure['new-state'] == gst.STATE_PLAYING):
+ # log.debug("gstreamer: state -> playing")
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
log.critical( 'Error: %s %s', err, debug )
PlayerBackend = GStreamer
class Playlist(object):
- class Entry(object):
- def __init__(self, data):
- if isinstance(data, dict):
- self.id = data['id']
- self.name = data['name']
- self.image = data['image']
- self.numalbum = int(data['numalbum'])
- self.url = data['mp3']
- self.type = 'mp3'
- elif isinstance(data, basestring): # assume URI
- self.id = 0
- self.name = ''
- self.image = None
- self.numalbum = 0
- self.url = data
- self.type = 'mp3'
- def __str__(self):
- return "{%s}" % (", ".join([str(self.name), str(self.numalbum), str(self.url)]))
-
def __init__(self, items = []):
+ self.radio_mode = False
+ self.radio_id = None
+ self.radio_name = None
if items is None:
items = []
- self.items = [Playlist.Entry(item) for item in items]
+ for item in items:
+ assert(isinstance(item, jamaendo.Track))
+ self.items = items
self._current = -1
def add(self, item):
- self.items.append(Playlist.Entry(item))
+ if isinstance(item, list):
+ for i in item:
+ assert(isinstance(i, jamaendo.Track))
+ self.items.extend(item)
+ else:
+ self.items.append(item)
def next(self):
if self.has_next():
return self.items[self._current]
return None
+ def prev(self):
+ if self.has_prev():
+ self._current = self._current - 1
+ return self.items[self._current]
+ return None
+
def has_next(self):
return self._current < (len(self.items)-1)
+ def has_prev(self):
+ return self._current > 0
+
def current(self):
if self._current >= 0:
return self.items[self._current]
return None
+ def jump_to(self, item_id):
+ for c, i in enumerate(self.items):
+ if i.ID == item_id:
+ self._current = c
+
def current_index(self):
return self._current
- def __len__(self):
+ def size(self):
return len(self.items)
-class Player(Playlist):
+ def __repr__(self):
+ return "Playlist(%d of %s)"%(self._current, ", ".join([str(item.ID) for item in self.items]))
+
+class Player(object):
def __init__(self):
self.backend = PlayerBackend()
self.backend.set_eos_callback(self._on_eos)
- self.playlist = None
+ self.playlist = Playlist()
+
+ def get_position_duration(self):
+ return self.backend.get_position_duration()
def play(self, playlist = None):
if playlist:
self.playlist = playlist
- if self.playlist is not None:
- if self.playlist.has_next():
+ elif self.playlist is None:
+ self.playlist = Playlist()
+ if self.playlist.size():
+ if self.playlist.current():
+ entry = self.playlist.current()
+ self.backend.play_url('mp3', entry.mp3_url())
+ log.debug("playing %s", entry)
+ elif self.playlist.has_next():
entry = self.playlist.next()
+ self.backend.play_url('mp3', entry.mp3_url())
log.debug("playing %s", entry)
- self.backend.play_url(entry.type, entry.url)
+ postoffice.notify('play', entry)
def pause(self):
self.backend.pause()
+ postoffice.notify('pause', self.playlist.current())
def stop(self):
self.backend.stop()
+ postoffice.notify('stop', self.playlist.current())
def playing(self):
return self.backend.playing()
def next(self):
if self.playlist.has_next():
- self.stop()
- self.play()
+ self.backend.stop(reset=False)
+ entry = self.playlist.next()
+ self.backend.play_url('mp3', entry.mp3_url())
+ log.debug("playing %s:%s", entry.ID, entry.name)
+ postoffice.notify('next', entry)
+ elif self.playlist.radio_mode:
+ log.debug("Refilling radio %s", self.playlist)
+ self.playlist.add(jamaendo.get_radio_tracks(self.playlist.radio_id))
+ if self.playlist.has_next():
+ self.next()
+ else:
+ self.stop()
else:
self.stop()
def prev(self):
- pass
+ if self.playlist.has_prev():
+ self.backend.stop(reset=False)
+ entry = self.playlist.prev()
+ self.backend.play_url('mp3', entry.mp3_url())
+ log.debug("playing %s", entry)
+ postoffice.notify('prev', entry)
def _on_eos(self):
- log.debug("EOS!")
self.next()
+
+the_player = Player() # the player instance