pygst.require('0.10')
import gst
import util
+import dbus
+import dbus.service
log = logging.getLogger(__name__)
+class _Player(object):
+ """Defines the internal player interface"""
+ def __init__(self):
+ pass
+ def play_url(self, filetype, uri):
+ raise NotImplemented
+ def playing(self):
+ raise NotImplemented
+ def play_pause_toggle(self):
+ self.pause() if self.playing() else self.play()
+ def play(self):
+ raise NotImplemented
+ def pause(self):
+ raise NotImplemented
+ def stop(self):
+ raise NotImplemented
+ def set_eos_callback(self, cb):
+ raise NotImplemented
-class GStreamer(object):
+class GStreamer(_Player):
"""Wraps GStreamer"""
STATES = { gst.STATE_NULL : 'stopped',
gst.STATE_PAUSED : 'paused',
gst.STATE_PLAYING : 'playing' }
def __init__(self):
+ _Player.__init__(self)
self.time_format = gst.Format(gst.FORMAT_TIME)
self.player = None
self.filesrc = None
self.volume_property = None
self.eos_callback = lambda: self.stop()
- def setup(self, filetype, uri):
+ 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
self._set_uri_to_be_played(uri)
- bus = self._player.get_bus()
+ bus = self.player.get_bus()
bus.add_signal_watch()
bus.connect('message', self._on_message)
+
+ self._set_volume_level( 1 )
+
+ self.play()
return True
def get_state(self):
if self.player:
- state = self._player.get_state()[1]
+ state = self.player.get_state()[1]
return self.STATES.get(state, 'none')
return 'none'
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 play_pause_toggle(self):
- self.pause() if self.playing() else self.play()
-
def stop(self):
if self.player:
self.player.set_state(gst.STATE_NULL)
def set_eos_callback(self, cb):
self.eos_callback = cb
+if util.platform == 'maemo':
+ class OssoPlayer(_Player):
+ """
+ A player which uses osso-media-player for playback (Maemo-specific)
+ """
+
+ SERVICE_NAME = "com.nokia.osso_media_server"
+ OBJECT_PATH = "/com/nokia/osso_media_server"
+ AUDIO_INTERFACE_NAME = "com.nokia.osso_media_server.music"
+
+ def __init__(self):
+ self._on_eos = lambda: self.stop()
+ self._state = 'none'
+ self._audio = self._init_dbus()
+ self._init_signals()
+
+ def play_url(self, filetype, uri):
+ self._audio.play_media(uri)
+
+ def playing(self):
+ return self._state == 'playing'
+
+ def play_pause_toggle(self):
+ self.pause() if self.playing() else self.play()
+
+ def play(self):
+ self._audio.play()
+
+ def pause(self):
+ if self.playing():
+ self._audio.pause()
+
+ def stop(self):
+ self._audio.stop()
+
+ def set_eos_callback(self, cb):
+ self._on_eos = cb
+
+
+ def _init_dbus(self):
+ session_bus = dbus.SessionBus()
+ oms_object = session_bus.get_object(self.SERVICE_NAME,
+ self.OBJECT_PATH,
+ introspect = False,
+ follow_name_owner_changes = True)
+ return dbus.Interface(oms_object, self.AUDIO_INTERFACE_NAME)
+
+ def _init_signals(self):
+ error_signals = {
+ "no_media_selected": "No media selected",
+ "file_not_found": "File not found",
+ "type_not_found": "Type not found",
+ "unsupported_type": "Unsupported type",
+ "gstreamer": "GStreamer Error",
+ "dsp": "DSP Error",
+ "device_unavailable": "Device Unavailable",
+ "corrupted_file": "Corrupted File",
+ "out_of_memory": "Out of Memory",
+ "audio_codec_not_supported": "Audio codec not supported"
+ }
+
+ # Connect status signals
+ self.audio_proxy.connect_to_signal( "state_changed",
+ self._on_state_changed )
+ self.audio_proxy.connect_to_signal( "end_of_stream",
+ lambda x: self._call_eos() )
+
+ # Connect error signals
+ for error, msg in error_signals.iteritems():
+ self.audio_proxy.connect_to_signal(error, lambda *x: self._error(msg))
+
+ def _error(self, msg):
+ log.error(msg)
+
+ def _call_eos(self):
+ self._on_eos()
+
+ def _on_state_changed(self, state):
+ states = ("playing", "paused", "stopped")
+ self.__state = state if state in states else 'none'
+
+ PlayerBackend = OssoPlayer
+else:
+ PlayerBackend = GStreamer
+
class Playlist(object):
- def __init__(self):
- self.items = []
+ class Entry(object):
+ def __init__(self, data):
+ if isinstance(data, dict):
+ self.id = data['id']
+ self.name = data['name']
+ self.numalbum = int(data['numalbum'])
+ self.url = data['mp3']
+ self.type = 'mp3'
+ elif isinstance(data, basestring): # assume URI
+ self.id = 0
+ self.name = ''
+ 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 = []):
+ if items is None:
+ items = []
+ self.items = [Playlist.Entry(item) for item in items]
+ self.current = -1
def add(self, item):
- self.items.append(item)
+ self.items.append(Playlist.Entry(item))
+
+ def next(self):
+ if self.has_next():
+ self.current = self.current + 1
+ return self.items[self.current]
+ return None
+
+ def has_next(self):
+ return self.current < (len(self.items)-1)
class Player(Playlist):
def __init__(self):
- self.gstreamer = GStreamer()
- self.gstreamer.set_eos_callback(self._on_eos)
-
- def play(self, item=None):
- pass
+ self.backend = PlayerBackend()
+ self.backend.set_eos_callback(self._on_eos)
+ self.playlist = None
+
+ def play(self, playlist = None):
+ if playlist:
+ self.playlist = playlist
+ if self.playlist is not None:
+ if self.playlist.has_next():
+ entry = self.playlist.next()
+ log.debug("playing %s", entry)
+ self.backend.play_url(entry.type, entry.url)
def pause(self):
- pass
+ self.backend.pause()
def stop(self):
- pass
+ self.backend.stop()
+
+ def playing(self):
+ return self.backend.playing()
def next(self):
- pass
+ if self.playlist.has_next():
+ self.stop()
+ self.play()
+ else:
+ self.stop()
def prev(self):
pass
def _on_eos(self):
- pass
+ log.debug("EOS!")
+ self.next()