changeset 1955:9ee42b88299b

vidref now has some stats Ignore-this: ef1184d85c09bead29fb4d1f2245f180
author Drew Perttula <drewp@bigasterisk.com>
date Thu, 06 Jun 2019 02:56:46 +0000
parents 3ae1e7f8db23
children ec816fd31c83
files bin/vidref light9/vidref/videorecorder.py
diffstat 2 files changed, 43 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/bin/vidref	Thu Jun 06 02:28:28 2019 +0000
+++ b/bin/vidref	Thu Jun 06 02:56:46 2019 +0000
@@ -27,6 +27,8 @@
 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 @@
 
 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 @@
 
     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 @@
     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 @@
                 "path": 'todo',
             }),
             (r'/time', Time),
+            (r'/stats/(.*)', StatsHandler, {
+                'serverName': 'vidref'
+            }),
+
         ],
         debug=True,
     ))
--- a/light9/vidref/videorecorder.py	Thu Jun 06 02:28:28 2019 +0000
+++ b/light9/vidref/videorecorder.py	Thu Jun 06 02:56:46 2019 +0000
@@ -14,6 +14,7 @@
 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 @@
 
 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 @@
     isPlaying: bool
     imgJpeg: Optional[bytes] = None
 
+    @stats.jpegEncode.time()
     def asJpeg(self):
         if not self.imgJpeg:
             output = BytesIO()
@@ -57,6 +69,7 @@
     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 @@
         
 
     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 @@
             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 @@
                     '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 @@
             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()