Files @ 4b856f39d490
Branch filter:

Location: light9/bin/vidref - annotation

Drew Perttula
some test show data
Ignore-this: b83300870fbc8675589d7a18d34c3df4
b5efddd80dad
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
0dc3715050cf
0dc3715050cf
6f49dc917aa3
6f49dc917aa3
d96c09669b40
6f49dc917aa3
ec816fd31c83
9f0f2b39ad95
ec816fd31c83
9ee42b88299b
9ee42b88299b
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
a745bee5c419
cfd5d5be1b50
cfd5d5be1b50
7772cc48e016
cfd5d5be1b50
cfd5d5be1b50
2ee97997ee56
6f1eb6437c96
a745bee5c419
9ee42b88299b
9ee42b88299b
9ee42b88299b
9ee42b88299b
9ee42b88299b
ec816fd31c83
ec816fd31c83
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
9ee42b88299b
6f49dc917aa3
6f49dc917aa3
9ee42b88299b
9ee42b88299b
6f49dc917aa3
0dc3715050cf
0dc3715050cf
6f49dc917aa3
9ee42b88299b
9ee42b88299b
6f49dc917aa3
6f49dc917aa3
9f0f2b39ad95
9f0f2b39ad95
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
1d9547f90737
1d9547f90737
1d9547f90737
6b5e079b3dbe
6b5e079b3dbe
7772cc48e016
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
3ae1e7f8db23
9f0f2b39ad95
3ae1e7f8db23
3ae1e7f8db23
3ae1e7f8db23
9f0f2b39ad95
ec816fd31c83
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
ec816fd31c83
ec816fd31c83
9ee42b88299b
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
3ae1e7f8db23
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
9f0f2b39ad95
3ae1e7f8db23
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
3ae1e7f8db23
9f0f2b39ad95
6f49dc917aa3
6f49dc917aa3
9f0f2b39ad95
6f49dc917aa3
6f49dc917aa3
9ee42b88299b
9ee42b88299b
9ee42b88299b
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 typing import cast
import logging, optparse, json, base64, os, glob

from greplin import scales
from greplin.scales.cyclonehandler import StatsHandler
from rdflib import URIRef
from twisted.internet import reactor, defer
import cyclone.web, cyclone.httpclient, cyclone.websocket

from cycloneerr import PrettyErrorHandler
from light9 import networking, showconfig
from light9.newtypes import Song
from light9.vidref import videorecorder
from rdfdb.syncedgraph import SyncedGraph
from standardservice.scalessetup import gatherProcessStats

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)

gatherProcessStats()
stats = scales.collection(
    '/webServer',
    scales.RecentFpsStat('liveWebsocketFrameFps'),
    scales.IntStat('liveClients'),
)


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)
        stats.liveClients += 1

    def connectionLost(self, reason):
        #self.subj.dispose()
        stats.liveClients -= 1

    def onFrame(self, cf: videorecorder.CaptureFrame):
        if cf is None: return

        stats.liveWebsocketFrameFps.mark()

        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)


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

    def delete(self):
        clip = URIRef(self.get_argument('uri'))
        videorecorder.deleteClip(clip)


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

    def get(self):
        song = Song(self.get_argument('song'))
        clips = []
        videoPaths = glob.glob(
            os.path.join(videorecorder.songDir(song), b'*.mp4'))
        for vid in videoPaths:
            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': videorecorder.takeUri(vid),
                'videoUrl': url,
                'songToVideo': pts
            })

        clips.sort(key=lambda c: len(cast(list, 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'/clips', Clips),
            (r'/replayMap', ReplayMap),
            (r'/snapshot', Snapshot),
            (r'/snapshot/(.*)', SnapshotPic, {
                "path": 'todo',
            }),
            (r'/time', Time),
            (r'/stats/(.*)', StatsHandler, {
                'serverName': 'vidref'
            }),
        ],
        debug=True,
    ))
log.info("serving on %s" % port)

reactor.run()