From: Ed Page Date: Wed, 16 Mar 2011 00:13:07 +0000 (-0500) Subject: Getting backends to work X-Git-Url: http://git.maemo.org/git/?p=gc-dialer;a=commitdiff_plain;h=5a404c443e430638250c8938dc6ebbfe20c4a5dd Getting backends to work --- diff --git a/src/stream_gst.py b/src/stream_gst.py new file mode 100644 index 0000000..ce97fb6 --- /dev/null +++ b/src/stream_gst.py @@ -0,0 +1,145 @@ +import logging + +import gobject +import gst + +import util.misc as misc_utils + + +_moduleLogger = logging.getLogger(__name__) + + +class Stream(gobject.GObject): + + # @bug Advertising state changes a bit early, should watch for GStreamer state change + + STATE_PLAY = "play" + STATE_PAUSE = "pause" + STATE_STOP = "stop" + + __gsignals__ = { + 'state-change' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + 'eof' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + 'error' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT), + ), + } + + def __init__(self): + gobject.GObject.__init__(self) + #Fields + self._uri = "" + self._elapsed = 0 + self._duration = 0 + + #Set up GStreamer + self._player = gst.element_factory_make("playbin2", "player") + bus = self._player.get_bus() + bus.add_signal_watch() + bus.connect("message", self._on_message) + + #Constants + self._timeFormat = gst.Format(gst.FORMAT_TIME) + self._seekFlag = gst.SEEK_FLAG_FLUSH + + @property + def playing(self): + return self.state == self.STATE_PLAY + + @property + def has_file(self): + return 0 < len(self._uri) + + @property + def state(self): + state = self._player.get_state()[1] + return self._translate_state(state) + + def set_file(self, uri): + if self._uri != uri: + self._invalidate_cache() + if self.state != self.STATE_STOP: + self.stop() + + self._uri = uri + self._player.set_property("uri", uri) + + def play(self): + if self.state == self.STATE_PLAY: + _moduleLogger.info("Already play") + return + _moduleLogger.info("Play") + self._player.set_state(gst.STATE_PLAYING) + self.emit("state-change", self.STATE_PLAY) + + def pause(self): + if self.state == self.STATE_PAUSE: + _moduleLogger.info("Already pause") + return + _moduleLogger.info("Pause") + self._player.set_state(gst.STATE_PAUSED) + self.emit("state-change", self.STATE_PAUSE) + + def stop(self): + if self.state == self.STATE_STOP: + _moduleLogger.info("Already stop") + return + self._player.set_state(gst.STATE_NULL) + _moduleLogger.info("Stopped") + self.emit("state-change", self.STATE_STOP) + + @property + def elapsed(self): + try: + self._elapsed = self._player.query_position(self._timeFormat, None)[0] + except: + pass + return self._elapsed + + @property + def duration(self): + try: + self._duration = self._player.query_duration(self._timeFormat, None)[0] + except: + _moduleLogger.exception("Query failed") + return self._duration + + def seek_time(self, ns): + self._elapsed = ns + self._player.seek_simple(self._timeFormat, self._seekFlag, ns) + + def _invalidate_cache(self): + self._elapsed = 0 + self._duration = 0 + + def _translate_state(self, gstState): + return { + gst.STATE_NULL: self.STATE_STOP, + gst.STATE_PAUSED: self.STATE_PAUSE, + gst.STATE_PLAYING: self.STATE_PLAY, + }.get(gstState, self.STATE_STOP) + + @misc_utils.log_exception(_moduleLogger) + def _on_message(self, bus, message): + t = message.type + if t == gst.MESSAGE_EOS: + self._player.set_state(gst.STATE_NULL) + self.emit("eof", self._uri) + elif t == gst.MESSAGE_ERROR: + self._player.set_state(gst.STATE_NULL) + err, debug = message.parse_error() + _moduleLogger.error("Error: %s, (%s)" % (err, debug)) + self.emit("error", err, debug) + + +gobject.type_register(Stream) diff --git a/src/stream_handler.py b/src/stream_handler.py new file mode 100644 index 0000000..148a69a --- /dev/null +++ b/src/stream_handler.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python + +from __future__ import with_statement +from __future__ import division + +import logging + +from PyQt4 import QtCore + +import util.misc as misc_utils +try: + import stream_gst + stream = stream_gst +except ImportError: + try: + import stream_osso + stream = stream_osso + except ImportError: + import stream_null + stream = stream_null + + +_moduleLogger = logging.getLogger(__name__) + + +class StreamToken(QtCore.QObject): + + stateChange = QtCore.pyqtSignal(str) + invalidated = QtCore.pyqtSignal() + error = QtCore.pyqtSignal(str) + + STATE_PLAY = stream.Stream.STATE_PLAY + STATE_PAUSE = stream.Stream.STATE_PAUSE + STATE_STOP = stream.Stream.STATE_STOP + + def __init__(self, stream): + QtCore.QObject.__init__(self) + self._stream = stream + self._stream.connect("state-change", self._on_stream_state) + self._stream.connect("eof", self._on_stream_eof) + self._stream.connect("error", self._on_stream_error) + + @property + def state(self): + if self.isValid: + return self._stream.state + else: + return self.STATE_STOP + + @property + def isValid(self): + return self._stream is not None + + def play(self): + self._stream.play() + + def pause(self): + self._stream.pause() + + def stop(self): + self._stream.stop() + + def invalidate(self): + if self._stream is None: + return + _moduleLogger.info("Playback token invalidated") + self._stream = None + + @misc_utils.log_exception(_moduleLogger) + def _on_stream_state(self, s, state): + if not self.isValid: + return + if state == self.STATE_STOP: + self.invalidate() + self.stateChange.emit(state) + + @misc_utils.log_exception(_moduleLogger) + def _on_stream_eof(self, s, uri): + if not self.isValid: + return + self.invalidate() + self.stateChange.emit(self.STATE_STOP) + + @misc_utils.log_exception(_moduleLogger) + def _on_stream_error(self, s, error, debug): + if not self.isValid: + return + _moduleLogger.info("Error %s %s" % (error, debug)) + self.error.emit(str(error)) + + +class StreamHandler(QtCore.QObject): + + def __init__(self): + QtCore.QObject.__init__(self) + self._stream = stream.Stream() + self._token = StreamToken(self._stream) + + def set_file(self, path): + self._token.invalidate() + self._token = StreamToken(self._stream) + self._stream.set_file(path) + return self._token + + @misc_utils.log_exception(_moduleLogger) + def _on_stream_state(self, s, state): + _moduleLogger.info("State change %r" % state) + + +if __name__ == "__main__": + pass + diff --git a/src/stream_null.py b/src/stream_null.py new file mode 100644 index 0000000..44fbbed --- /dev/null +++ b/src/stream_null.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +from __future__ import with_statement +from __future__ import division + +import logging + + +_moduleLogger = logging.getLogger(__name__) + + +class Stream(object): + + STATE_PLAY = "play" + STATE_PAUSE = "pause" + STATE_STOP = "stop" + + def __init__(self): + pass + + def connect(self, signalName, slot): + pass + + @property + def playing(self): + return False + + @property + def has_file(self): + return False + + @property + def state(self): + return self.STATE_STOP + + def set_file(self, uri): + pass + + def play(self): + pass + + def pause(self): + pass + + def stop(self): + pass + + @property + def elapsed(self): + return 0 + + @property + def duration(self): + return 0 + + def seek_time(self, ns): + pass + + +if __name__ == "__main__": + pass + diff --git a/src/stream_osso.py b/src/stream_osso.py new file mode 100644 index 0000000..abc453f --- /dev/null +++ b/src/stream_osso.py @@ -0,0 +1,181 @@ +import logging + +import gobject +import dbus + +import util.misc as misc_utils + + +_moduleLogger = logging.getLogger(__name__) + + +class Stream(gobject.GObject): + + STATE_PLAY = "play" + STATE_PAUSE = "pause" + STATE_STOP = "stop" + + __gsignals__ = { + 'state-change' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + 'eof' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + 'error' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT), + ), + } + + _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): + gobject.GObject.__init__(self) + #Fields + self._state = self.STATE_STOP + self._nextState = self.STATE_STOP + self._uri = "" + self._elapsed = 0 + self._duration = 0 + + session_bus = dbus.SessionBus() + + # Get the osso-media-player proxy object + oms_object = session_bus.get_object( + self._SERVICE_NAME, + self._OBJECT_PATH, + introspect=False, + follow_name_owner_changes=True, + ) + # Use the audio interface + oms_audio_interface = dbus.Interface( + oms_object, + self._AUDIO_INTERFACE_NAME, + ) + self._audioProxy = oms_audio_interface + + self._audioProxy.connect_to_signal("state_changed", self._on_state_changed) + self._audioProxy.connect_to_signal("end_of_stream", self._on_end_of_stream) + + error_signals = [ + "no_media_selected", + "file_not_found", + "type_not_found", + "unsupported_type", + "gstreamer", + "dsp", + "device_unavailable", + "corrupted_file", + "out_of_memory", + "audio_codec_not_supported", + ] + for error in error_signals: + self._audioProxy.connect_to_signal(error, self._on_error) + + @property + def playing(self): + return self.state == self.STATE_PLAY + + @property + def has_file(self): + return 0 < len(self._uri) + + @property + def state(self): + return self._state + + def set_file(self, uri): + if self._uri != uri: + self._invalidate_cache() + if self.state != self.STATE_STOP: + self.stop() + + self._uri = uri + self._audioProxy.set_media_location(self._uri) + + def play(self): + if self._nextState == self.STATE_PLAY: + _moduleLogger.info("Already play") + return + _moduleLogger.info("Play") + self._audioProxy.play() + self._nextState = self.STATE_PLAY + #self.emit("state-change", self.STATE_PLAY) + + def pause(self): + if self._nextState == self.STATE_PAUSE: + _moduleLogger.info("Already pause") + return + _moduleLogger.info("Pause") + self._audioProxy.pause() + self._nextState = self.STATE_PAUSE + #self.emit("state-change", self.STATE_PLAY) + + def stop(self): + if self._nextState == self.STATE_STOP: + _moduleLogger.info("Already stop") + return + self._audioProxy.stop() + _moduleLogger.info("Stopped") + self._nextState = self.STATE_STOP + #self.emit("state-change", self.STATE_STOP) + + @property + def elapsed(self): + pos_info = self._audioProxy.get_position() + if isinstance(pos_info, tuple): + self._elapsed, self._duration = pos_info + return self._elapsed + + @property + def duration(self): + pos_info = self._audioProxy.get_position() + if isinstance(pos_info, tuple): + self._elapsed, self._duration = pos_info + return self._duration + + def seek_time(self, ns): + _moduleLogger.debug("Seeking to: %s", ns) + self._audioProxy.seek( dbus.Int32(1), dbus.Int32(ns) ) + + def _invalidate_cache(self): + self._elapsed = 0 + self._duration = 0 + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, *args): + err, debug = "", repr(args) + _moduleLogger.error("Error: %s, (%s)" % (err, debug)) + self.emit("error", err, debug) + + @misc_utils.log_exception(_moduleLogger) + def _on_end_of_stream(self, *args): + self._state = self.STATE_STOP + self._nextState = self.STATE_STOP + self.emit("eof", self._uri) + + @misc_utils.log_exception(_moduleLogger) + def _on_state_changed(self, state): + _moduleLogger.info("State: %s", state) + state = { + "playing": self.STATE_PLAY, + "paused": self.STATE_PAUSE, + "stopped": self.STATE_STOP, + }[state] + if self._state == self.STATE_STOP and self._nextState == self.STATE_PLAY and state == self.STATE_STOP: + # They seem to want to advertise stop right as the stream is starting, breaking the owner of this + return + self._state = state + self._nextState = state + self.emit("state-change", state) + + +gobject.type_register(Stream)