# HG changeset patch # User Drew Perttula # Date 2013-06-10 08:28:06 # Node ID cca75951554aa48cfcf9a61417b6f9b9a21328bb # Parent c6750f2665369aa0f52916e2f4467d370e3f1a3a port ascoltami to Gst 1.0, work around the missing message signals Ignore-this: c11f3e788d38fad6928e136035f3331a diff --git a/bin/ascoltami2 b/bin/ascoltami2 --- a/bin/ascoltami2 +++ b/bin/ascoltami2 @@ -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.playlist import Pl 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 @@ class App: 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 @@ if __name__ == "__main__": 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() diff --git a/light9/ascoltami/player.py b/light9/ascoltami/player.py --- a/light9/ascoltami/player.py +++ b/light9/ascoltami/player.py @@ -5,7 +5,10 @@ alternate to the mpd music player, for a """ 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 @@ class Player(object): 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 @@ class Player(object): 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 @@ class Player(object): 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 @@ class Player(object): 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 @@ class Player(object): "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 @@ class Player(object): 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 @@ class Player(object): # 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 diff --git a/makefile b/makefile --- a/makefile +++ b/makefile @@ -18,6 +18,7 @@ SP=env/lib/python2.7/site-packages 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)/