changeset 907:cca75951554a

port ascoltami to Gst 1.0, work around the missing message signals Ignore-this: c11f3e788d38fad6928e136035f3331a
author Drew Perttula <drewp@bigasterisk.com>
date Mon, 10 Jun 2013 08:28:06 +0000
parents c6750f266536
children 0a47ec94fc63
files bin/ascoltami2 light9/ascoltami/player.py makefile
diffstat 3 files changed, 76 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/bin/ascoltami2	Mon Jun 10 07:04:12 2013 +0000
+++ b/bin/ascoltami2	Mon Jun 10 08:28:06 2013 +0000
@@ -1,7 +1,7 @@
 #!bin/python
 from run_local import log
-from twisted.internet import gtk2reactor, reactor
-import web, thread, gobject, sys, optparse, logging
+from twisted.internet import gireactor, reactor
+import web, thread, sys, optparse, logging
 from rdflib import URIRef
 sys.path.append(".")
 from light9.ascoltami.player import Player
@@ -9,22 +9,17 @@
 from light9.ascoltami.webapp import makeWebApp, songUri, songLocation
 from light9 import networking, showconfig
 
+import gi
+gi.require_version('Gst', '1.0')
+from gi.repository import GObject, Gst, Gtk
 
-class App:
+class App(object):
     def __init__(self, graph, show):
         self.graph = graph
         self.player = Player(onEOS=self.onEOS)
         self.show = show
         self.playlist = Playlist.fromShow(graph, show)
 
-    def run(self, musicPort):
-        # the cherrypy server would wedge when vidref pounds on it; this
-        # one seems to run
-        #gtk2reactor.install(useGtk=False)
-        reactor.listenTCP(musicPort, makeWebApp(self))
-        log.info("listening on %s" % musicPort)
-        reactor.run()
-
     def onEOS(self, song):
         self.player.pause()
         self.player.seek(0)
@@ -39,7 +34,8 @@
         self.player.setSong(songLocation(graph, nextSong), play=False)
 
 if __name__ == "__main__":
-    gobject.threads_init() # this is in gtk2reactor too
+    GObject.threads_init()
+    Gst.init(None)
 
     parser = optparse.OptionParser()
     parser.add_option('--show',
@@ -55,4 +51,6 @@
             
     graph = showconfig.getGraph()
     app = App(graph, URIRef(options.show))
-    app.run(networking.musicPlayer.port)
+    reactor.listenTCP(networking.musicPlayer.port, makeWebApp(app))
+    log.info("listening on %s" % networking.musicPlayer.port)
+    reactor.run()
--- a/light9/ascoltami/player.py	Mon Jun 10 07:04:12 2013 +0000
+++ b/light9/ascoltami/player.py	Mon Jun 10 08:28:06 2013 +0000
@@ -5,7 +5,10 @@
 """
 from __future__ import division
 import time, logging, traceback
-import gst, gobject
+from gi.repository import GObject, Gst
+from twisted.internet import reactor, task
+
+
 log = logging.getLogger()
 
 class Player(object):
@@ -17,41 +20,26 @@
         It is called with one argument which is the URI of the song that
         just finished."""
         self.autoStopOffset = autoStopOffset
-        self.playbin = self.pipeline = gst.parse_launch("playbin2")
+        self.playbin = self.pipeline = Gst.ElementFactory.make('playbin',None)
+
         self.playStartTime = 0
         self.lastWatchTime = 0
         self.autoStopTime = 0
         self.lastSetSongUri = None
         self.onEOS = onEOS
         
-        # before playbin2:
-        #self.pipeline = gst.parse_launch("filesrc name=file location=%s ! wavparse name=src ! audioconvert ! alsasink name=out" % songFile)
-
-        gobject.timeout_add(50, self.watchTime)
+        task.LoopingCall(self.watchTime).start(.050)
 
         bus = self.pipeline.get_bus()
-        bus.add_signal_watch()
-
-        def on_any(bus, msg):
-            #print bus, msg, msg.type
-            if msg.type == gst.MESSAGE_EOS:
-                if self.onEOS is not None:
-                    self.onEOS(self.getSong())
-        bus.connect('message', on_any)
+        # not working- see notes in pollForMessages
+        #self.watchForMessages(bus)
+      
+    def watchTime(self):
 
-        def onStreamStatus(bus, message):
-            print "streamstatus", bus, message
-            (statusType, _elem) = message.parse_stream_status()
-            if statusType == gst.STREAM_STATUS_TYPE_ENTER:
-                self.setupAutostop()
-        bus.connect('message::stream-status', onStreamStatus)
-
-    def watchTime(self):
+        self.pollForMessages()
+        
         try:
-            try:
-                t = self.currentTime()
-            except gst.QueryError:
-                return True
+            t = self.currentTime()
             log.debug("watch %s < %s < %s",
                       self.lastWatchTime, self.autoStopTime, t)
             if self.lastWatchTime < self.autoStopTime < t:
@@ -61,13 +49,46 @@
             self.lastWatchTime = t
         except:
             traceback.print_exc()
-        return True
+
+    def watchForMessages(self, bus):
+        """this would be nicer than pollForMessages but it's not working for
+        me. It's like add_signal_watch isn't running."""
+        bus.add_signal_watch()
+
+        def onEos(*args):
+            print "onEos", args
+            if self.onEOS is not None:
+                self.onEOS(self.getSong())
+        bus.connect('message::eos', onEos)
 
+        def onStreamStatus(bus, message):
+            print "streamstatus", bus, message
+            (statusType, _elem) = message.parse_stream_status()
+            if statusType == Gst.StreamStatusType.ENTER:
+                self.setupAutostop()
+        bus.connect('message::stream-status', onStreamStatus)
+            
+    def pollForMessages(self):
+        """bus.add_signal_watch seems to be having no effect, but this works"""
+        bus = self.pipeline.get_bus()
+        mt = Gst.MessageType
+        msg = bus.poll(mt.EOS | mt.STREAM_STATUS, 0)
+        if msg is not None:
+            if msg.type == Gst.MessageType.EOS:
+                if self.onEOS is not None:
+                    self.onEOS(self.getSong())
+            if msg.type == Gst.MessageType.STREAM_STATUS:
+                (statusType, _elem) = msg.parse_stream_status()
+                if statusType == Gst.StreamStatusType.ENTER:
+                    self.setupAutostop()
+            
     def seek(self, t):
-        assert self.playbin.seek_simple(
-            gst.FORMAT_TIME,
-            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE | gst.SEEK_FLAG_SKIP,
-            t * gst.SECOND)
+        isSeekable = self.playbin.seek_simple(
+            Gst.Format.TIME,
+            Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE | Gst.SeekFlags.SKIP,
+            t * Gst.SECOND)
+        if not isSeekable:
+            raise ValueError('seek_simple failed')
         self.playStartTime = time.time()
 
     def setSong(self, songLoc, play=True):
@@ -75,13 +96,13 @@
         uri like file:///my/proj/light9/show/dance2010/music/07.wav
         """
         log.info("set song to %r" % songLoc)
-        self.pipeline.set_state(gst.STATE_READY)
+        self.pipeline.set_state(Gst.State.READY)
         self.preload(songLoc)
         self.pipeline.set_property("uri", songLoc)
         self.lastSetSongUri = songLoc
         # todo: don't have any error report yet if the uri can't be read
         if play:
-            self.pipeline.set_state(gst.STATE_PLAYING)
+            self.pipeline.set_state(Gst.State.PLAYING)
             self.playStartTime = time.time()
 
     def getSong(self):
@@ -106,17 +127,16 @@
             raise
 
     def currentTime(self):
-        try:
-            cur, _format = self.playbin.query_position(gst.FORMAT_TIME)
-        except gst.QueryError:
+        success, cur = self.playbin.query_position(Gst.Format.TIME)
+        if not success:
             return 0
-        return cur / gst.SECOND
+        return cur / Gst.SECOND
 
     def duration(self):
-        try:
-            return self.playbin.query_duration(gst.FORMAT_TIME)[0] / gst.SECOND
-        except gst.QueryError:
+        success, dur = self.playbin.query_duration(Gst.Format.TIME)
+        if not success:
             return 0
+        return dur / Gst.SECOND
 
     def states(self):
         """json-friendly object describing the interesting states of
@@ -126,7 +146,7 @@
                 "pending": {"name":state.value_nick}}
         
     def pause(self):
-        self.pipeline.set_state(gst.STATE_PAUSED)
+        self.pipeline.set_state(Gst.State.PAUSED)
 
     def isAutostopped(self):
         """
@@ -137,7 +157,7 @@
         return not self.isPlaying() and abs(pos - autoStop) < 1 # i've seen .4 difference here
 
     def resume(self):
-        self.pipeline.set_state(gst.STATE_PLAYING)
+        self.pipeline.set_state(Gst.State.PLAYING)
 
     def setupAutostop(self):
         dur = self.duration()
@@ -148,5 +168,5 @@
         # hard to remove.
 
     def isPlaying(self):
-        _, state, _ = self.pipeline.get_state()
-        return state == gst.STATE_PLAYING
+        _, state, _ = self.pipeline.get_state(timeout=0)
+        return state == Gst.State.PLAYING
--- a/makefile	Mon Jun 10 07:04:12 2013 +0000
+++ b/makefile	Mon Jun 10 08:28:06 2013 +0000
@@ -18,6 +18,7 @@
 link_to_sys_packages:
 	# http://stackoverflow.com/questions/249283/virtualenv-on-ubuntu-with-no-site-packages
 	ln -sf $(DP)/glib $(SP)/
+	ln -sf $(DP)/gi $(SP)/
 	ln -sf $(DP)/gobject $(SP)/
 	ln -sf $(DP)/cairo $(SP)/
 	ln -sf $(DP)/gtk-2.0 $(SP)/