X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=jamaui%2Fplayer.py;h=13af7a6b7bbfe4ac3f08aa1c1c76064937bdf7c5;hb=3a5bb9a6d8ba578da100b5b985b1a87290216262;hp=9560934676905b1bde058e5093adf4a59c049db8;hpb=8f565c4747da54cb6cdb165ec3a24e50c17868f1;p=jamaendo diff --git a/jamaui/player.py b/jamaui/player.py index 9560934..13af7a6 100644 --- a/jamaui/player.py +++ b/jamaui/player.py @@ -23,17 +23,40 @@ import pygst pygst.require('0.10') import gst import util +import dbus +import dbus.service + +import jamaendo 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 @@ -43,7 +66,7 @@ class GStreamer(object): 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 @@ -53,8 +76,10 @@ class GStreamer(object): # 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 util.platform == 'maemo': - if True or not self._maemo_setup_hardware_player(filetype): + 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)' ) else: @@ -71,6 +96,8 @@ class GStreamer(object): bus.connect('message', self._on_message) self._set_volume_level( 1 ) + + self.play() return True def get_state(self): @@ -91,14 +118,19 @@ class GStreamer(object): 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) self.player = None + def _maemo_setup_playbin_player(self, url): + self.player = gst.parse_launch("playbin2 uri=%s" % (url,)) + self.filesrc = self.player + self.filesrc_property = 'uri' + self.volume_control = self.player + 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 """ @@ -186,75 +218,180 @@ class GStreamer(object): if t == gst.MESSAGE_EOS: self.eos_callback() - + log.info("End of stream") + elif t == gst.MESSAGE_STATE_CHANGED: + old, new, pending = message.parse_state_changed() + log.info("State changed: %s -> %s -> %s", old, new, pending) elif t == gst.MESSAGE_ERROR: err, debug = message.parse_error() log.critical( 'Error: %s %s', err, debug ) self.stop() + else: + log.info("? %s", message.type) def set_eos_callback(self, cb): self.eos_callback = cb -class Playlist(object): - 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)])) +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.connect_to_signal( "state_changed", + self._on_state_changed ) + self._audio.connect_to_signal( "end_of_stream", + lambda x: self._call_eos() ) + + # Connect error signals + for error, msg in error_signals.iteritems(): + self._audio.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, items = []): if items is None: items = [] - self.items = [Playlist.Entry(item) for item in items] - self.current = -1 + 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(): - self.current = self.current + 1 - return self.items[self.current] + self._current = self._current + 1 + 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) + 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 current_index(self): + return self._current + + def size(self): + print type(self) + return len(self.items) + + def __repr__(self): + return "Playlist(%s)"%(", ".join([str(item) for item in self.items])) class Player(Playlist): def __init__(self): - self.gstreamer = GStreamer() - self.gstreamer.set_eos_callback(self._on_eos) - self.playlist = None + self.backend = PlayerBackend() + self.backend.set_eos_callback(self._on_eos) + self.playlist = Playlist() def play(self, playlist = None): if playlist: self.playlist = playlist - if self.playlist is not None: + elif self.playlist is None: + self.playlist = Playlist() + if self.playlist.size(): if self.playlist.has_next(): entry = self.playlist.next() log.debug("playing %s", entry) - self.gstreamer.setup(entry.type, entry.url) - self.gstreamer.play() + self.backend.play_url('mp3', entry.mp3_url()) def pause(self): - self.gstreamer.pause() + self.backend.pause() def stop(self): - self.gstreamer.stop() + self.backend.stop() def playing(self): - return self.gstreamer.playing() + return self.backend.playing() def next(self): if self.playlist.has_next(): @@ -264,8 +401,13 @@ class Player(Playlist): self.stop() def prev(self): - pass + if self.playlist.has_prev(): + entry = self.playlist.prev() + log.debug("playing %s", entry) + self.backend.play_url('mp3', entry.mp3_url()) def _on_eos(self): log.debug("EOS!") self.next() + +the_player = Player() # the player instance