Files @ 9dd331caa23b
Branch filter:

Location: light9/bin/vidref - annotation

drewp@bigasterisk.com
cleanups to help with first build success
Ignore-this: b6d402ca6dd2d97fb03c390713efbd07
b5efddd80dad
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
0dc3715050cf
0dc3715050cf
6f49dc917aa3
6f49dc917aa3
d96c09669b40
6f49dc917aa3
1d9547f90737
6f49dc917aa3
9f0f2b39ad95
087f6cbe4b22
9f0f2b39ad95
6f49dc917aa3
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
6fa4288da8a6
9f0f2b39ad95
cfd5d5be1b50
cfd5d5be1b50
7772cc48e016
cfd5d5be1b50
cfd5d5be1b50
2ee97997ee56
6f1eb6437c96
7772cc48e016
1d9547f90737
7772cc48e016
1d9547f90737
1d9547f90737
1d9547f90737
1d9547f90737
1d9547f90737
9f0f2b39ad95
1d9547f90737
1d9547f90737
9f0f2b39ad95
1d9547f90737
9f0f2b39ad95
7772cc48e016
1d9547f90737
1d9547f90737
1d9547f90737
3c523c71da29
1d9547f90737
1d9547f90737
1d9547f90737
1d9547f90737
7772cc48e016
6f49dc917aa3
0dc3715050cf
82e98aa4d159
0dc3715050cf
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
0dc3715050cf
0dc3715050cf
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
9f0f2b39ad95
9f0f2b39ad95
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
1d9547f90737
1d9547f90737
1d9547f90737
6b5e079b3dbe
6b5e079b3dbe
7772cc48e016
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
087f6cbe4b22
1d9547f90737
7772cc48e016
7772cc48e016
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
157985a971dc
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
157985a971dc
9f0f2b39ad95
6f49dc917aa3
6f49dc917aa3
9f0f2b39ad95
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
1d9547f90737
b0337e6f68f1
89adbbb06bcd
#!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/light9-vidref-live.js LitElement for live video frames
light9/web/light9-vidref-playback.js LitElement for video playback

"""
from run_local import log

from twisted.internet import reactor, defer

import logging, optparse, json, base64, os, glob
import cyclone.web, cyclone.httpclient, cyclone.websocket
from light9 import networking, showconfig
from light9.vidref import videorecorder
from rdflib import URIRef
from light9.newtypes import Song
from light9.namespaces import L9
from rdfdb.syncedgraph import SyncedGraph
from cycloneerr import PrettyErrorHandler

parser = optparse.OptionParser()
parser.add_option("-v", "--verbose", action="store_true", help="logging.DEBUG")
(options, args) = parser.parse_args()

log.setLevel(logging.DEBUG if options.verbose else logging.INFO)


class Snapshot(cyclone.web.RequestHandler):

    @defer.inlineCallbacks
    def post(self):
        # save next pic
        # return /snapshot/path
        try:
            snapshotDir = 'todo'
            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:
            import traceback
            traceback.print_exc()
            raise


pipeline = videorecorder.GstSource(
    '/dev/v4l/by-id/usb-Bison_HD_Webcam_200901010001-video-index0'
    #    '/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, cf: videorecorder.CaptureFrame):
        if cf is None: return

        self.sendMessage(
            json.dumps({
                'jpeg': base64.b64encode(cf.asJpeg()).decode('ascii'),
                'description': f't={cf.t}',
            }))


class SnapshotPic(cyclone.web.StaticFileHandler):
    pass


class Time(cyclone.web.RequestHandler):

    def put(self):
        body = json.loads(self.request.body)
        t = body['t']
        source = body['source']
        self.settings.gui.incomingTime(t, source)
        self.set_status(202)


def takeUri(songPath: bytes):
    p = songPath.decode('ascii').split('/')
    take = p[-1].replace('.mp4', '')
    song = p[-2].split('_')
    return URIRef('/'.join(
        ['http://light9.bigasterisk.com/show', song[-2], song[-1], take]))


class ReplayMap(PrettyErrorHandler, cyclone.web.RequestHandler):

    def get(self):
        song = Song(self.get_argument('song'))
        clips = []
        for vid in glob.glob(os.path.join(videorecorder.songDir(song),
                                          b'*.mp4')):
            pts = []
            for line in open(vid.replace(b'.mp4', b'.timing'), 'rb'):
                _v, vt, _eq, _song, st = line.split()
                pts.append([float(st), float(vt)])

            url = vid[len(os.path.dirname(os.path.dirname(showconfig.root()))
                         ):].decode('ascii')

            clips.append({
                'uri': takeUri(vid),
                'videoUrl': url,
                'songToVideo': pts
            })

        clips.sort(key=lambda c: len(c['songToVideo']))
        clips = clips[-3:]
        clips.sort(key=lambda c: c['uri'], reverse=True)

        ret = json.dumps(clips)
        log.info('replayMap had %s videos; json is %s bytes', len(clips),
                 len(ret))
        self.write(ret)


graph = SyncedGraph(networking.rdfdb.url, "vidref")
outVideos = videorecorder.FramesToVideoFiles(
    pipeline.liveImages, os.path.join(showconfig.root(), b'video'))

port = networking.vidref.port
reactor.listenTCP(
    port,
    cyclone.web.Application(
        handlers=[
            (r'/()', cyclone.web.StaticFileHandler, {
                'path': 'light9/vidref',
                'default_filename': 'index.html'
            }),
            (r'/setup/()', cyclone.web.StaticFileHandler, {
                'path': 'light9/vidref',
                'default_filename': 'setup.html'
            }),
            (r'/live', Live),
            (r'/replayMap', ReplayMap),
            (r'/snapshot', Snapshot),
            (r'/snapshot/(.*)', SnapshotPic, {
                "path": 'todo',
            }),
            (r'/time', Time),
        ],
        debug=True,
    ))
log.info("serving on %s" % port)

reactor.run()