Changeset - 1d9547f90737
[Not reviewed]
default
0 6 0
drewp@bigasterisk.com - 12 years ago 2013-06-13 00:46:52
drewp@bigasterisk.com
vidref can take snapshots and serve them back
Ignore-this: 9e89659abc5f36aa56a08391e84e52e
6 files changed with 83 insertions and 10 deletions:
0 comments (0 inline, 0 general)
bin/vidref
Show inline comments
 
#!bin/python
 
from run_local import log
 
from twisted.internet import gtk2reactor
 
gtk2reactor.install()
 
from twisted.internet import reactor
 
from twisted.internet import reactor, defer
 
import gobject
 
gobject.threads_init()
 
import gtk
 
import sys, logging, optparse
 
import sys, logging, optparse, json
 
sys.path.append(".")
 
from light9 import networking
 
from light9.vidref.main import Gui
 

	
 
from light9.vidref.replay import snapshotDir
 
import cyclone.web, cyclone.httpclient, cyclone.websocket
 

	
 
 # find replay dirs correctly. show multiple
 
 # replays. trash. reorder/pin. dump takes that are too short; they're
 
 # just from seeking
 

	
 
parser = optparse.OptionParser()
 
@@ -24,11 +26,41 @@ parser.add_option("-v", "--verbose", act
 
log.setLevel(logging.DEBUG)
 
# limit the stdout one, but leave debug messages for the gtk logger
 
log.handlers[0].setLevel(logging.DEBUG if options.verbose else logging.WARN)
 
logging.getLogger("restkit.client").setLevel(logging.WARN)
 

	
 

	
 
class Snapshot(cyclone.web.RequestHandler):
 
    @defer.inlineCallbacks
 
    def post(self):
 
        # save next pic
 
        # return /snapshot/path
 
        try:
 
            outputFilename = yield self.settings.gui.snapshot()
 

	
 
            assert outputFilename.startswith(snapshotDir())
 
            out = networking.vidref.path(
 
                "snapshot/%s" % outputFilename[len(snapshotDir()):].lstrip('/'))
 
            
 
            self.write(json.dumps({'snapshot': out}))
 
            self.set_header("Location", out)
 
            self.set_status(303)
 
        except Exception as e:
 
            import traceback
 
            traceback.print_exc()
 
            raise
 

	
 
class SnapshotPic(cyclone.web.StaticFileHandler):
 
    pass
 

	
 
        
 
start=Gui()
 
gui = Gui()
 

	
 
port = networking.vidref.port
 
reactor.listenTCP(port, cyclone.web.Application(handlers=[
 
    (r'/snapshot', Snapshot),
 
    (r'/snapshot/(.*)', SnapshotPic, {"path": snapshotDir()}),
 
    ], debug=True, gui=gui))
 
log.info("serving on %s" % port)
 

	
 
reactor.run()
 

	
light9/networking.py
Show inline comments
 
@@ -35,6 +35,7 @@ class ServiceAddress(object):
 
        return self.url + str(more)
 

	
 
dmxServer = ServiceAddress(L9['dmxServer'])
 
musicPlayer = ServiceAddress(L9['musicPlayer'])
 
keyboardComposer = ServiceAddress(L9['keyboardComposer'])
 
curveCalc = ServiceAddress(L9['curveCalc'])
 
vidref = ServiceAddress(L9['vidref'])
light9/vidref/main.py
Show inline comments
 
@@ -48,12 +48,16 @@ class Gui(object):
 
        
 

	
 
        self.pipeline.setInput('v4l') # auto seems to not search for dv
 

	
 
        gobject.timeout_add(1000 // framerate, self.updateLoop)
 

	
 

	
 
    def snapshot(self):
 
        return self.pipeline.snapshot()
 
        
 
    def attachLog(self, textBuffer):
 
        """write log lines to this gtk buffer"""
 
        class ToBuffer(logging.Handler):
 
            def emit(self, record):
 
                textBuffer.set_text(record.getMessage())
 

	
light9/vidref/replay.py
Show inline comments
 
@@ -13,12 +13,15 @@ def songDir(song):
 
def takeDir(songDir, startTime):
 
    """
 
    startTime: unix seconds (str ok)
 
    """
 
    return os.path.join(songDir, str(int(startTime)))
 

	
 
def snapshotDir():
 
    return os.path.expanduser("~/light9-vidref/snapshot")
 
    
 
class ReplayViews(object):
 
    """
 
    the whole list of replay windows. parent is the scrolling area for
 
    these windows to be added
 
    """
 
    def __init__(self, parent):
 
@@ -157,12 +160,13 @@ class ReplayView(object):
 
                stack.add(r)
 
                stack.set_child_packing(r, False, False, 0, gtk.PACK_START)
 
            
 
            replayPanel.pack_start(stack, False, False, 0)
 

	
 
        parent.pack_start(replayPanel, False, False)
 
        log.debug("packed ReplayView %s" % replayPanel)
 
        self.replayPanel = replayPanel
 

	
 
    def destroy(self):
 
        self.replayPanel.destroy()
 
        self.enabled = False
 
        
light9/vidref/videorecorder.py
Show inline comments
 
import pygst
 
pygst.require("0.10")
 
import gst, gobject, time, logging, os, traceback
 
import gtk
 
import Image
 
from threading import Thread
 
from Queue import Queue
 
from light9.vidref.replay import framerate, songDir, takeDir
 
from twisted.internet import defer
 
from Queue import Queue, Empty
 
from light9.vidref.replay import framerate, songDir, takeDir, snapshotDir
 
log = logging.getLogger()
 

	
 
class Pipeline(object):
 
    def __init__(self, liveVideoXid, musicTime, recordingTo):
 
        self.musicTime = musicTime
 
        self.liveVideoXid = liveVideoXid
 
        self.recordingTo = recordingTo
 
    
 
        self.snapshotRequests = Queue()
 

	
 
        try:
 
            os.makedirs(snapshotDir())
 
        except OSError:
 
            pass
 

	
 
    def snapshot(self):
 
        """
 
        returns deferred to the path (which is under snapshotDir()) where
 
        we saved the image. This callback comes from another thread,
 
        but I haven't noticed that being a problem yet.
 
        """
 
        d = defer.Deferred()
 
        def req(frame):
 
            filename = "%s/%s.jpg" % (snapshotDir(), time.time())
 
            log.debug("received snapshot; saving in %s", filename)
 
            frame.save(filename)
 
            d.callback(filename)
 
        log.debug("requesting snapshot")
 
        self.snapshotRequests.put(req)
 
        return d
 
        
 
    def setInput(self, name):
 
        sourcePipe = {
 
            "auto": "autovideosrc name=src1",
 
            "testpattern" : "videotestsrc name=src1",
 
            "dv": "dv1394src name=src1 ! dvdemux ! dvdec",
 
            "v4l": "v4l2src device=/dev/video0 name=src1" ,
 
@@ -42,13 +65,13 @@ class Pipeline(object):
 
            # you can get a crash in xcb_io
 
            if getattr(self, '_lastRecText', None) == t:
 
                return
 
            with gtk.gdk.lock:
 
                self.recordingTo.set_text(t)
 
            self._lastRecText = t
 
        recSink = VideoRecordSink(self.musicTime, setRec)
 
        recSink = VideoRecordSink(self.musicTime, setRec, self.snapshotRequests)
 
        self.pipeline.add(recSink)
 

	
 
        tee = makeElem("tee")
 
        
 
        caps = makeElem("capsfilter")
 
        caps.set_property('caps', gst.caps_from_string('video/x-raw-rgb'))
 
@@ -73,15 +96,16 @@ class Pipeline(object):
 
class VideoRecordSink(gst.Element):
 
    _sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
 
                                        gst.PAD_SINK,
 
                                        gst.PAD_ALWAYS,
 
                                        gst.caps_new_any())
 

	
 
    def __init__(self, musicTime, updateRecordingTo):
 
    def __init__(self, musicTime, updateRecordingTo, snapshotRequests):
 
        gst.Element.__init__(self)
 
        self.updateRecordingTo = updateRecordingTo
 
        self.snapshotRequests = snapshotRequests
 
        self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
 
        self.add_pad(self.sinkpad)
 
        self.sinkpad.set_chain_function(self.chainfunc)
 
        self.lastTime = 0
 
        
 
        self.musicTime = musicTime
 
@@ -93,12 +117,19 @@ class VideoRecordSink(gst.Element):
 
        """do image saves in another thread to not block gst"""
 
        def imageSaver():
 
            while True:
 
                args = imagesToSave.get()
 
                self.saveImg(*args)
 
                imagesToSave.task_done()
 
                try:
 
                    req = self.snapshotRequests.get(block=False)
 
                except Empty:
 
                    pass
 
                else:
 
                    req(args[1])
 
                    self.snapshotRequests.task_done()
 
        
 
        t = Thread(target=imageSaver)
 
        t.setDaemon(True)
 
        t.start()
 

	
 
    def chainfunc(self, pad, buffer):
show/dance2013/networking.n3
Show inline comments
 
@@ -4,7 +4,8 @@
 

	
 
show:dance2013 :networking sh:netHome .
 
sh:netHome  
 
  :musicPlayer <http://localhost:8040/>;
 
  :dmxServer <http://localhost:8030/>;
 
  :curveCalc <http://localhost:8060/>;
 
  :keyboardComposer <http://localhost:8050/> .
 
\ No newline at end of file
 
  :keyboardComposer <http://localhost:8050/>;
 
  :vidref <http://plus:8053/> .
 
\ No newline at end of file
0 comments (0 inline, 0 general)