diff --git a/bin/vidref b/bin/vidref --- a/bin/vidref +++ b/bin/vidref @@ -1,22 +1,29 @@ #!bin/python +""" +Camera images of the stage. View live on a web page and also save +them to disk. Retrieve images based on the song and time that was +playing when they were taken. Also, save snapshot images to a place +they can be used again as thumbnails of effects. + +bin/vidref main +light9/vidref/videorecorder.py capture frames and save them +light9/vidref/replay.py backend for vidref.js playback element- figures out which frames go with the current song and time +light9/vidref/index.html web ui for watching current stage and song playback +light9/vidref/setup.html web ui for setup of camera params and frame crop +light9/web/vidref.js LitElement for video playback + +""" from run_local import log -import sys -sys.path.append('/usr/lib/python2.7/dist-packages') # For gtk -from twisted.internet import gtk2reactor -gtk2reactor.install() + from twisted.internet import reactor, defer -import gobject -gobject.threads_init() -import sys, logging, optparse, json + +import logging, optparse, json, base64 import cyclone.web, cyclone.httpclient, cyclone.websocket from light9 import networking -from light9.vidref.main import Gui from light9.vidref.replay import snapshotDir +from light9.vidref import videorecorder from rdfdb.syncedgraph import SyncedGraph - -# find replay dirs correctly. show multiple -# replays. trash. reorder/pin. dump takes that are too short; they're -# just from seeking +from io import BytesIO parser = optparse.OptionParser() parser.add_option("-v", "--verbose", action="store_true", help="logging.DEBUG") @@ -47,6 +54,33 @@ class Snapshot(cyclone.web.RequestHandle raise +pipeline = videorecorder.GstSource( + '/dev/v4l/by-id/usb-Generic_FULL_HD_1080P_Webcam_200901010001-video-index0') + + +class Live(cyclone.websocket.WebSocketHandler): + + def connectionMade(self, *args, **kwargs): + pipeline.liveImages.subscribe(on_next=self.onFrame) + + def connectionLost(self, reason): + 0 #self.subj.dispose() + + def onFrame(self, t_img): + t, img = t_img + if img is None: return + output = BytesIO() + img.save(output, 'jpeg', quality=80) + + self.sendMessage( + json.dumps({ + 'jpeg': + base64.b64encode(output.getvalue()).decode('ascii'), + 'description': + f't={t}', + })) + + class SnapshotPic(cyclone.web.StaticFileHandler): pass @@ -63,24 +97,28 @@ class Time(cyclone.web.RequestHandler): graph = SyncedGraph(networking.rdfdb.url, "vidref") -gui = Gui(graph) - port = networking.vidref.port reactor.listenTCP( port, - cyclone.web.Application(handlers=[ - (r'/()', cyclone.web.StaticFileHandler, { - 'path': 'light9/vidref', - 'default_filename': 'vidref.html' - }), - (r'/snapshot', Snapshot), - (r'/snapshot/(.*)', SnapshotPic, { - "path": snapshotDir() - }), - (r'/time', Time), - ], - debug=True, - gui=gui)) + cyclone.web.Application( + handlers=[ + (r'/()', cyclone.web.StaticFileHandler, { + 'path': 'light9/vidref', + 'default_filename': 'vidref.html' + }), + (r'/setup/()', cyclone.web.StaticFileHandler, { + 'path': 'light9/vidref', + 'default_filename': 'setup.html' + }), + (r'/setup/live', Live), + (r'/snapshot', Snapshot), + (r'/snapshot/(.*)', SnapshotPic, { + "path": snapshotDir() + }), + (r'/time', Time), + ], + debug=True, + )) log.info("serving on %s" % port) reactor.run()