Getting backends to work
authorEd Page <eopage@byu.net>
Wed, 16 Mar 2011 00:13:07 +0000 (19:13 -0500)
committerEd Page <eopage@byu.net>
Tue, 19 Apr 2011 23:49:27 +0000 (18:49 -0500)
src/stream_gst.py [new file with mode: 0644]
src/stream_handler.py [new file with mode: 0644]
src/stream_null.py [new file with mode: 0644]
src/stream_osso.py [new file with mode: 0644]

diff --git a/src/stream_gst.py b/src/stream_gst.py
new file mode 100644 (file)
index 0000000..ce97fb6
--- /dev/null
@@ -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 (file)
index 0000000..148a69a
--- /dev/null
@@ -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 (file)
index 0000000..44fbbed
--- /dev/null
@@ -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 (file)
index 0000000..abc453f
--- /dev/null
@@ -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)