Files @ 56b8b94d43ac
Branch filter:

Location: light9/bin/vidref - annotation

drewp@bigasterisk.com
less logging
b5efddd80dad
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
0dc3715050cf
0dc3715050cf
6f49dc917aa3
6f49dc917aa3
d96c09669b40
6f49dc917aa3
ec816fd31c83
9f0f2b39ad95
ec816fd31c83
9aa046cc9b33
9aa046cc9b33
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
ec816fd31c83
cfd5d5be1b50
cfd5d5be1b50
7772cc48e016
cfd5d5be1b50
cfd5d5be1b50
2ee97997ee56
6f1eb6437c96
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
df28c994365d
df28c994365d
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
6f49dc917aa3
9aa046cc9b33
6f49dc917aa3
6f49dc917aa3
9ee42b88299b
9aa046cc9b33
6f49dc917aa3
0dc3715050cf
0dc3715050cf
6f49dc917aa3
9aa046cc9b33
9aa046cc9b33
9aa046cc9b33
9aa046cc9b33
9aa046cc9b33
9aa046cc9b33
6f49dc917aa3
6f49dc917aa3
1d9547f90737
1d9547f90737
1d9547f90737
6b5e079b3dbe
c8f0d1b9a171
7772cc48e016
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
6b5e079b3dbe
6b5e079b3dbe
6b5e079b3dbe
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
c8f0d1b9a171
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
1b690005aabd
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
c8f0d1b9a171
9aa046cc9b33
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 light9.metrics import metrics, metricsRoute

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

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)
        metrics('live_clients').offset(1)

    def connectionLost(self, reason):
        #self.subj.dispose()
        metrics('live_clients').offset(-1)

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

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


class SnapshotPic(cyclone.web.StaticFileHandler):
    pass


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

    def put(self):
        body = json.loads(self.request.body)
        t = body['t']
        for listener in TimeStream.time_stream_listeners:
            listener.sendMessage(json.dumps({
                'st': t,
                'song': body['song'],
            }))
        self.set_status(202)


class TimeStream(cyclone.websocket.WebSocketHandler):
    time_stream_listeners = []

    def connectionMade(self, *args, **kwargs):
        TimeStream.time_stream_listeners.append(self)

    def connectionLost(self, reason):
        TimeStream.time_stream_listeners.remove(self)


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[-int(self.get_argument('maxClips', '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'/time/stream', TimeStream),
            metricsRoute(),
        ],
        debug=True,
    ))
log.info("serving on %s" % port)

reactor.run()