Initial commit for web interface

Change-Id: I133eaf37221a050eb3c87e245b86ae54c610d446
diff --git a/person_detection/base_camera.py b/person_detection/base_camera.py
new file mode 100644
index 0000000..96c148a
--- /dev/null
+++ b/person_detection/base_camera.py
@@ -0,0 +1,101 @@
+import time
+import threading
+try:
+    from greenlet import getcurrent as get_ident
+except ImportError:
+    try:
+        from thread import get_ident
+    except ImportError:
+        from _thread import get_ident
+
+
+class CameraEvent(object):
+    """An Event-like class that signals all active clients when a new frame is
+    available.
+    """
+    def __init__(self):
+        self.events = {}
+
+    def wait(self):
+        """Invoked from each client's thread to wait for the next frame."""
+        ident = get_ident()
+        if ident not in self.events:
+            # this is a new client
+            # add an entry for it in the self.events dict
+            # each entry has two elements, a threading.Event() and a timestamp
+            self.events[ident] = [threading.Event(), time.time()]
+        return self.events[ident][0].wait()
+
+    def set(self):
+        """Invoked by the camera thread when a new frame is available."""
+        now = time.time()
+        remove = None
+        for ident, event in self.events.items():
+            if not event[0].isSet():
+                # if this client's event is not set, then set it
+                # also update the last set timestamp to now
+                event[0].set()
+                event[1] = now
+            else:
+                # if the client's event is already set, it means the client
+                # did not process a previous frame
+                # if the event stays set for more than 5 seconds, then assume
+                # the client is gone and remove it
+                if now - event[1] > 5:
+                    remove = ident
+        if remove:
+            del self.events[remove]
+
+    def clear(self):
+        """Invoked from each client's thread after a frame was processed."""
+        self.events[get_ident()][0].clear()
+
+
+class BaseCamera(object):
+    thread = None  # background thread that reads frames from camera
+    frame = None  # current frame is stored here by background thread
+    last_access = 0  # time of last client access to the camera
+    event = CameraEvent()
+
+    def __init__(self):
+        """Start the background camera thread if it isn't running yet."""
+        if BaseCamera.thread is None:
+            BaseCamera.last_access = time.time()
+
+            # start background frame thread
+            BaseCamera.thread = threading.Thread(target=self._thread)
+            BaseCamera.thread.start()
+
+            # wait until frames are available
+            while self.get_frame() is None:
+                time.sleep(0)
+
+    def get_frame(self):
+        """Return the current camera frame."""
+        BaseCamera.last_access = time.time()
+
+        # wait for a signal from the camera thread
+        BaseCamera.event.wait()
+        BaseCamera.event.clear()
+
+        return BaseCamera.frame
+
+    def frames():
+        """"Generator that returns frames from the camera."""
+        raise RuntimeError('Must be implemented by subclasses.')
+
+    def _thread(self):
+        """Camera background thread."""
+        frames_iterator = self.frames()
+        for frame in frames_iterator:
+            BaseCamera.frame = frame
+            BaseCamera.event.set()  # send signal to clients
+            time.sleep(0)
+
+            # if there hasn't been any clients asking for frames in
+            # the last 10 seconds then stop the thread
+            if time.time() - BaseCamera.last_access > 10:
+                frames_iterator.close()
+                print('Stopping camera thread due to inactivity.')
+                break
+        BaseCamera.thread = None