0b6d959a1df704683555ab3f6ab694cd71992c1f
[watersofshiloah] / src / stream_gst.py
1 import logging
2
3 import gobject
4 import gst
5
6 import util.misc as misc_utils
7
8
9 _moduleLogger = logging.getLogger(__name__)
10
11
12 class GSTStream(gobject.GObject):
13
14         # @bug Advertising state changes a bit early, should watch for GStreamer state change
15
16         STATE_PLAY = "play"
17         STATE_PAUSE = "pause"
18         STATE_STOP = "stop"
19
20         __gsignals__ = {
21                 'state-change' : (
22                         gobject.SIGNAL_RUN_LAST,
23                         gobject.TYPE_NONE,
24                         (gobject.TYPE_STRING, ),
25                 ),
26                 'eof' : (
27                         gobject.SIGNAL_RUN_LAST,
28                         gobject.TYPE_NONE,
29                         (gobject.TYPE_STRING, ),
30                 ),
31                 'error' : (
32                         gobject.SIGNAL_RUN_LAST,
33                         gobject.TYPE_NONE,
34                         (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT),
35                 ),
36         }
37
38
39         def __init__(self):
40                 gobject.GObject.__init__(self)
41                 #Fields
42                 self._uri = ""
43                 self._elapsed = 0
44                 self._duration = 0
45
46                 #Set up GStreamer
47                 self._player = gst.element_factory_make("playbin2", "player")
48                 bus = self._player.get_bus()
49                 bus.add_signal_watch()
50                 bus.connect("message", self._on_message)
51
52                 #Constants
53                 self._timeFormat = gst.Format(gst.FORMAT_TIME)
54                 self._seekFlag = gst.SEEK_FLAG_FLUSH
55
56         @property
57         def playing(self):
58                 return self.state == self.STATE_PLAY
59
60         @property
61         def has_file(self):
62                 return 0 < len(self._uri)
63
64         @property
65         def state(self):
66                 state = self._player.get_state()[1]
67                 return self._translate_state(state)
68
69         def set_file(self, uri):
70                 if self._uri != uri:
71                         self._invalidate_cache()
72                 if self.state != self.STATE_STOP:
73                         self.stop()
74
75                 self._uri = uri
76                 self._player.set_property("uri", uri)
77
78         def play(self):
79                 if self.state == self.STATE_PLAY:
80                         _moduleLogger.info("Already play")
81                         return
82                 _moduleLogger.info("Play")
83                 self._player.set_state(gst.STATE_PLAYING)
84                 self.emit("state-change", self.STATE_PLAY)
85
86         def pause(self):
87                 if self.state == self.STATE_PAUSE:
88                         _moduleLogger.info("Already pause")
89                         return
90                 _moduleLogger.info("Pause")
91                 self._player.set_state(gst.STATE_PAUSED)
92                 self.emit("state-change", self.STATE_PAUSE)
93
94         def stop(self):
95                 if self.state == self.STATE_STOP:
96                         _moduleLogger.info("Already stop")
97                         return
98                 self._player.set_state(gst.STATE_NULL)
99                 _moduleLogger.info("Stopped")
100                 self.emit("state-change", self.STATE_STOP)
101
102         @property
103         def elapsed(self):
104                 try:
105                         self._elapsed = self._player.query_position(self._timeFormat, None)[0]
106                 except:
107                         pass
108                 return self._elapsed
109
110         @property
111         def duration(self):
112                 try:
113                         self._duration = self._player.query_duration(self._timeFormat, None)[0]
114                 except:
115                         _moduleLogger.exception("Query failed")
116                 return self._duration
117
118         def seek_time(self, ns):
119                 self._elapsed = ns
120                 self._player.seek_simple(self._timeFormat, self._seekFlag, ns)
121
122         def _invalidate_cache(self):
123                 self._elapsed = 0
124                 self._duration = 0
125
126         def _translate_state(self, gstState):
127                 return {
128                         gst.STATE_NULL: self.STATE_STOP,
129                         gst.STATE_PAUSED: self.STATE_PAUSE,
130                         gst.STATE_PLAYING: self.STATE_PLAY,
131                 }.get(gstState, self.STATE_STOP)
132
133         @misc_utils.log_exception(_moduleLogger)
134         def _on_message(self, bus, message):
135                 t = message.type
136                 if t == gst.MESSAGE_EOS:
137                         self._player.set_state(gst.STATE_NULL)
138                         self.emit("eof", self._uri)
139                 elif t == gst.MESSAGE_ERROR:
140                         self._player.set_state(gst.STATE_NULL)
141                         err, debug = message.parse_error()
142                         _moduleLogger.error("Error: %s, (%s)" % (err, debug))
143                         self.emit("error", err, debug)
144
145
146 gobject.type_register(GSTStream)