# HG changeset patch # User Drew Perttula # Date 2019-06-06 02:56:46 # Node ID 9ee42b88299b296f001bf73b554235fb97403e67 # Parent 3ae1e7f8db23609ae3e908466d4fdd2b6f218d13 vidref now has some stats Ignore-this: ef1184d85c09bead29fb4d1f2245f180 diff --git a/bin/vidref b/bin/vidref --- a/bin/vidref +++ b/bin/vidref @@ -27,6 +27,8 @@ from light9.newtypes import Song from rdfdb.syncedgraph import SyncedGraph from cycloneerr import PrettyErrorHandler from typing import cast +from greplin import scales +from greplin.scales.cyclonehandler import StatsHandler parser = optparse.OptionParser() parser.add_option("-v", "--verbose", action="store_true", help="logging.DEBUG") @@ -34,7 +36,13 @@ parser.add_option("-v", "--verbose", act log.setLevel(logging.DEBUG if options.verbose else logging.INFO) - +stats = scales.collection( + '/webServer', + scales.RecentFpsStat('liveWebsocketFrameFps'), + scales.IntStat('liveClients'), + +) + class Snapshot(cyclone.web.RequestHandler): @defer.inlineCallbacks @@ -68,13 +76,17 @@ class Live(cyclone.websocket.WebSocketHa def connectionMade(self, *args, **kwargs): pipeline.liveImages.subscribe(on_next=self.onFrame) + stats.liveClients += 1 def connectionLost(self, reason): - 0 #self.subj.dispose() + #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'), @@ -108,8 +120,9 @@ class ReplayMap(PrettyErrorHandler, cycl def get(self): song = Song(self.get_argument('song')) clips = [] - for vid in glob.glob(os.path.join(videorecorder.songDir(song), - b'*.mp4')): + 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() @@ -159,6 +172,10 @@ reactor.listenTCP( "path": 'todo', }), (r'/time', Time), + (r'/stats/(.*)', StatsHandler, { + 'serverName': 'vidref' + }), + ], debug=True, )) diff --git a/light9/vidref/videorecorder.py b/light9/vidref/videorecorder.py --- a/light9/vidref/videorecorder.py +++ b/light9/vidref/videorecorder.py @@ -14,6 +14,7 @@ from twisted.internet import threads from rdflib import URIRef import moviepy.editor import numpy +from greplin import scales from light9.ascoltami.musictime_client import MusicTime from light9.newtypes import Song @@ -21,6 +22,16 @@ from light9 import showconfig log = logging.getLogger() +stats = scales.collection( + '/recorder', + scales.PmfStat('jpegEncode', recalcPeriod=1), + scales.IntStat('deletes'), + scales.PmfStat('waitForNextImg', recalcPeriod=1), + scales.PmfStat('crop', recalcPeriod=1), + scales.RecentFpsStat('encodeFrameFps'), + scales.RecentFpsStat('queueGstFrameFps'), + +) @dataclass class CaptureFrame: @@ -30,6 +41,7 @@ class CaptureFrame: isPlaying: bool imgJpeg: Optional[bytes] = None + @stats.jpegEncode.time() def asJpeg(self): if not self.imgJpeg: output = BytesIO() @@ -57,6 +69,7 @@ def deleteClip(uri: URIRef): path = '/'.join([w[0], w[1], 'video', f'light9.bigasterisk.com_{w[0]}_{w[1]}_{w[2]}', w[3]]) log.info(f'deleting {uri} {path}') + stats.deletes += 1 for fn in [path + '.mp4', path + '.timing']: os.remove(fn) @@ -156,6 +169,7 @@ class FramesToVideoFiles: def _bg_make_frame(self, video_time_secs): + stats.encodeFrameFps.mark() if self.nextWriteAction == 'close': raise StopIteration # the one in write_videofile elif self.nextWriteAction == 'notWritingClip': @@ -166,8 +180,10 @@ class FramesToVideoFiles: raise NotImplementedError(self.nextWriteAction) # should be a little queue to miss fewer frames + t1 = time.time() while self.nextImg is None: time.sleep(.015) + stats.waitForNextImg = time.time() - t1 cf, self.nextImg = self.nextImg, None self.frameMap.write(f'video {video_time_secs:g} = song {cf.t:g}\n') @@ -221,12 +237,13 @@ class GstSource: 'RGB', (caps.get_structure(0).get_value('width'), caps.get_structure(0).get_value('height')), mapinfo.data) - img = img.crop((0, 100, 640, 380)) + img = self.crop(img) finally: buf.unmap(mapinfo) # could get gst's frame time and pass it to getLatest latest = self.musicTime.getLatest() if 'song' in latest: + stats.queueGstFrameFps.mark() self.liveImages.on_next( CaptureFrame(img=img, song=Song(latest['song']), @@ -236,6 +253,10 @@ class GstSource: traceback.print_exc() return Gst.FlowReturn.OK + @stats.crop.time() + def crop(self, img): + return img.crop((0, 100, 640, 380)) + def setupPipelineError(self, pipe, cb): bus = pipe.get_bus()