--- /dev/null
+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)
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+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)