Changeset - cca75951554a
[Not reviewed]
default
0 3 0
Drew Perttula - 12 years ago 2013-06-10 08:28:06
drewp@bigasterisk.com
port ascoltami to Gst 1.0, work around the missing message signals
Ignore-this: c11f3e788d38fad6928e136035f3331a
3 files changed with 76 insertions and 57 deletions:
0 comments (0 inline, 0 general)
bin/ascoltami2
Show inline comments
 
#!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()
light9/ascoltami/player.py
Show inline comments
 
@@ -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
makefile
Show inline comments
 
@@ -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)/
0 comments (0 inline, 0 general)