]> www.vanbest.org Git - motioneye-debian/commitdiff
added support for displaying capture frame rate
authorCalin Crisan <ccrisan@gmail.com>
Sun, 13 Dec 2015 15:40:14 +0000 (17:40 +0200)
committerCalin Crisan <ccrisan@gmail.com>
Sun, 13 Dec 2015 15:40:14 +0000 (17:40 +0200)
motioneye/handlers.py
motioneye/mjpgclient.py
motioneye/remote.py
motioneye/static/js/main.js
motioneye/templates/main.html

index 51e7a43fc9bb1877c6353bb24a851b37f30805c7..73dba830a90c924403aa4a5117f6a3696a9f0b71 100644 (file)
@@ -28,6 +28,7 @@ from tornado.web import RequestHandler, HTTPError, asynchronous
 
 import config
 import mediafiles
+import mjpgclient
 import motionctl
 import powerctl
 import prefs
@@ -857,11 +858,13 @@ class PictureHandler(BaseHandler):
                     height=height)
             
             self.set_cookie('motion_detected_' + str(camera_id), str(motionctl.is_motion_detected(camera_id)).lower())
+            self.set_cookie('capture_fps_' + str(camera_id), '%.1f' % mjpgclient.get_fps(camera_id))
             self.try_finish(picture)
-                
+
         elif utils.remote_camera(camera_config):
-            def on_response(motion_detected=False, picture=None, error=None):
+            def on_response(motion_detected=False, fps=None, picture=None, error=None):
                 self.set_cookie('motion_detected_' + str(camera_id), str(motion_detected).lower())
+                self.set_cookie('capture_fps_' + str(camera_id), '%.1f' % fps)
                 self.try_finish(picture)
             
             remote.get_current_picture(camera_config, width=width, height=height, callback=on_response)
index 6f427e4d716ea378906cdc996c21f59ffd8d142d..3a6e6955d43d80e22bb0d36d3f238391d48dd333 100644 (file)
@@ -32,12 +32,11 @@ import utils
 
 
 class MjpgClient(IOStream):
-    clients = {} # dictionary of clients indexed by camera id
-    last_jpgs = {} # dictionary of jpeg contents indexed by camera id
-    last_jpg_moment = {} # dictionary of moments of the last received jpeg indexed by camera id
-    last_access = {} # dictionary of access moments indexed by camera id
-    last_erroneous_close_time = 0 # helps detecting erroneous connections and restart motion
+    _FPS_LEN = 4
     
+    clients = {} # dictionary of clients indexed by camera id
+    _last_erroneous_close_time = 0 # helps detecting erroneous connections and restart motion
+
     def __init__(self, camera_id, port, username, password, auth_mode):
         self._camera_id = camera_id
         self._port = port
@@ -46,6 +45,11 @@ class MjpgClient(IOStream):
         self._auth_mode = auth_mode
         self._auth_digest_state = {}
         
+        self._last_access = None
+        self._last_jpg = None
+        self._last_jpg_times = []
+        self._fps = 0
+        
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
         IOStream.__init__(self, s)
         
@@ -53,36 +57,26 @@ class MjpgClient(IOStream):
         
     def connect(self):
         IOStream.connect(self, ('localhost', self._port), self._on_connect)
-        MjpgClient.clients[self._camera_id] = self
-        
-        logging.debug('mjpg client for camera %(camera_id)s connecting on port %(port)s...' % {
-                'port': self._port, 'camera_id': self._camera_id})
     
     def on_close(self):
         logging.debug('connection closed for mjpg client for camera %(camera_id)s on port %(port)s' % {
                 'port': self._port, 'camera_id': self._camera_id})
         
         if MjpgClient.clients.pop(self._camera_id, None):
-            MjpgClient.last_access.pop(self._camera_id, None)
-            MjpgClient.last_jpg_moment.pop(self._camera_id, None)
-             
             logging.debug('mjpg client for camera %(camera_id)s on port %(port)s removed' % {
                     'port': self._port, 'camera_id': self._camera_id})
-         
+
         if getattr(self, 'error', None) and self.error.errno != errno.ECONNREFUSED:
             now = time.time()
-            if now - MjpgClient.last_erroneous_close_time < settings.MJPG_CLIENT_TIMEOUT:
+            if now - MjpgClient._last_erroneous_close_time < settings.MJPG_CLIENT_TIMEOUT:
                 logging.error('connection problem detected for mjpg client for camera %(camera_id)s on port %(port)s' % {
                         'port': self._port, 'camera_id': self._camera_id})
  
                 motionctl.stop(invalidate=True) # this will close all the mjpg clients
                 motionctl.start(deferred=True)
  
-            MjpgClient.last_erroneous_close_time = now
+            MjpgClient._last_erroneous_close_time = now
         
-        # remove the cached picture
-        MjpgClient.last_jpgs.pop(self._camera_id, None)
-
     def _check_error(self):
         if self.socket is None:
             logging.warning('mjpg client connection for camera %(camera_id)s on port %(port)s is closed' % {
@@ -209,8 +203,14 @@ class MjpgClient(IOStream):
         self.read_bytes(length, self._on_jpg)
     
     def _on_jpg(self, data):
-        MjpgClient.last_jpgs[self._camera_id] = data
-        MjpgClient.last_jpg_moment[self._camera_id] = datetime.datetime.utcnow()
+        self._last_jpg = data
+        self._last_jpg_times.append(time.time())
+        while len(self._last_jpg_times) > self._FPS_LEN:
+            self._last_jpg_times.pop(0)
+            
+        if len(self._last_jpg_times) == self._FPS_LEN:
+            self._fps = (len(self._last_jpg_times) - 1) / (self._last_jpg_times[-1] - self._last_jpg_times[0])
+
         self._seek_content_length()
 
 
@@ -243,22 +243,30 @@ def get_jpg(camera_id):
 
         client = MjpgClient(camera_id, port, username, password, auth_mode)
         client.connect()
+        
+        MjpgClient.clients[camera_id] = client
 
-    MjpgClient.last_access[camera_id] = datetime.datetime.utcnow()
-    
-    return MjpgClient.last_jpgs.get(camera_id)
+    client = MjpgClient.clients[camera_id]
+    client._last_access = time.time()
+
+    return client._last_jpg
 
 
+def get_fps(camera_id):
+    client = MjpgClient.clients.get(camera_id)
+    if client is None:
+        return 0
+    
+    return client._fps
+    
+
 def close_all(invalidate=False):
     for client in MjpgClient.clients.values():
         client.close()
     
     if invalidate:
         MjpgClient.clients = {}
-        MjpgClient.last_jpgs = {}
-        MjpgClient.last_jpg_moment = {}
-        MjpgClient.last_access = {}
-        MjpgClient.last_erroneous_close_time = 0
+        MjpgClient._last_erroneous_close_time = 0
 
 
 def _garbage_collector():
@@ -267,24 +275,20 @@ def _garbage_collector():
     io_loop = IOLoop.instance()
     io_loop.add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT), _garbage_collector)
 
-    now = datetime.datetime.utcnow()
-    for client in MjpgClient.clients.values():
-        camera_id = client._camera_id
+    now = time.time()
+    for camera_id, client in MjpgClient.clients.items():
         port = client._port
         
-        # check for last jpg moment timeout
-        last_jpg_moment = MjpgClient.last_jpg_moment.get(camera_id)
-        if last_jpg_moment is None:
-            MjpgClient.last_jpg_moment[camera_id] = now
-            
-            continue
+        # check for jpeg frame timeout
+        if not client._last_jpg_times:
+            client._last_jpg_times.append(now)
         
+        last_jpg_time = client._last_jpg_times[-1]
+
         if client.closed():
             continue
 
-        delta = now - last_jpg_moment
-        delta = delta.days * 86400 + delta.seconds
-        
+        delta = now - last_jpg_time
         if delta > settings.MJPG_CLIENT_TIMEOUT:
             logging.error('mjpg client timed out receiving data for camera %(camera_id)s on port %(port)s' % {
                     'camera_id': camera_id, 'port': port})
@@ -295,17 +299,14 @@ def _garbage_collector():
             break
 
         # check for last access timeout
-        last_access = MjpgClient.last_access.get(camera_id)
-        if last_access is None:
+        if client._last_access is None:
             continue
-        
-        delta = now - last_access
-        delta = delta.days * 86400 + delta.seconds
-        
+
+        delta = now - client._last_access
         if settings.MJPG_CLIENT_IDLE_TIMEOUT and delta > settings.MJPG_CLIENT_IDLE_TIMEOUT:
             logging.debug('mjpg client for camera %(camera_id)s on port %(port)s has been idle for %(timeout)s seconds, removing it' % {
                     'camera_id': camera_id, 'port': port, 'timeout': settings.MJPG_CLIENT_IDLE_TIMEOUT})
-            
+
             client.close()
 
             continue
index d1be83aa6eb592fa591998935fd4a41b6b1b6d10..d3831df8bc0d03da549d43bc4faa08f1fb8b3d50 100644 (file)
@@ -318,6 +318,8 @@ def get_current_picture(local_config, width, height, callback):
             cookies = [[i.strip() for i in c.split('=')] for c in cookies]
             cookies = dict([c for c in cookies if len(c) == 2])
             motion_detected = cookies.get('motion_detected_' + str(camera_id)) == 'true'
+            fps = cookies.get('capture_fps_' + str(camera_id))
+            fps = float(fps) if fps else 0
         
         if response.error:
             logging.error('failed to get current picture for remote camera %(id)s on %(url)s: %(msg)s' % {
@@ -327,7 +329,7 @@ def get_current_picture(local_config, width, height, callback):
             
             return callback(error=utils.pretty_http_error(response))
 
-        callback(motion_detected, response.body)
+        callback(motion_detected, fps, response.body)
     
     http_client = AsyncHTTPClient()
     http_client.fetch(request, _callback_wrapper(on_response))
index 40ead7331db2fc04e60d551fb30ece6aace276a0..3c9a248c6771c9d5a67eb39180cc3ce3e2e7f837 100644 (file)
@@ -4063,12 +4063,22 @@ function addCameraFrameUi(cameraConfig) {
                 recordButton.removeClass('record-stop').addClass('record-start');
             }
             
+            var captureFps = getCookie('capture_fps_' + cameraId);
+            
             this.lastCookieTime = now;
 
             if (this.fpsTimes.length == FPS_LEN) {
-                var fps = this.fpsTimes.length * 1000 / (this.fpsTimes[this.fpsTimes.length - 1] - this.fpsTimes[0]);
-                fps = fps.toFixed(1);
-                fpsSpan.html(fps + ' fps');
+                var streamingFps = this.fpsTimes.length * 1000 / (this.fpsTimes[this.fpsTimes.length - 1] - this.fpsTimes[0]);
+                streamingFps = streamingFps.toFixed(1);
+                
+                var fps = streamingFps;
+                if (captureFps) {
+                    fps += '/' + captureFps;
+                }
+                
+                fps += ' fps';
+
+                fpsSpan.html(fps);
             }
         }
 
index 50612e22836cf549abe5f8ca1d909716150dc3e4..f0d3ab8329e32a1bcbe6133e79c3764ac64461fd 100644 (file)
@@ -85,7 +85,7 @@
                 {% if hostname %}<div class="hostname">{{hostname}}</div>{% endif %}
             </div>
             <div class="logo">
-                <a href="/">
+                <a href="">
                     <span class="logo">motionEye</span>
                     <img class="logo" src="{{static_path}}img/motioneye-logo.svg">
                 </a>