Bladeren bron

functioning birdseye view

Blake Blackshear 3 jaren geleden
bovenliggende
commit
89c2ae2208

+ 1 - 1
docs/docs/contributing.md

@@ -162,7 +162,7 @@ npm run test
 #### 1. Installation
 
 ```console
-npm run install
+npm install
 ```
 
 #### 2. Local Development

+ 69 - 94
web/package-lock.json

@@ -2942,6 +2942,25 @@
         "minimist": "^1.2.0"
       }
     },
+    "@cycjimmy/awesome-js-funcs": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/@cycjimmy/awesome-js-funcs/-/awesome-js-funcs-2.5.0.tgz",
+      "integrity": "sha512-xy4MkuL8FcmRfWm0FwdHUCWB+EQ/ljCuTAf6d3SdaXPDgD4celrq0/vJT9ekLF0E6Au4fLbvRh1G8sXnGwDUkg=="
+    },
+    "@cycjimmy/jsmpeg-player": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@cycjimmy/jsmpeg-player/-/jsmpeg-player-5.0.1.tgz",
+      "integrity": "sha512-x2kCCEfmXinebXC3d8XBJmOeFP+Jw0f4peT2rAmNvhggP1hYPvACP2ioobxeZvp255rPQTt/RPNi9y2SJ5ATMQ==",
+      "requires": {
+        "@cycjimmy/awesome-js-funcs": "^2.3.0",
+        "@cycjimmy/sass-lib": "^1.0.3"
+      }
+    },
+    "@cycjimmy/sass-lib": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@cycjimmy/sass-lib/-/sass-lib-1.0.3.tgz",
+      "integrity": "sha512-OTr8uMjeRapr0bWTKs782Jj84/xQt1XvVl5ZyYW1yi4sEbqY1FfW1FYLeflBp4gSU99o4c/nPi2Fx5aRxna5dw=="
+    },
     "@dsherret/to-absolute-glob": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
@@ -4118,17 +4137,17 @@
       "dev": true
     },
     "@videojs/http-streaming": {
-      "version": "2.6.4",
-      "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.6.4.tgz",
-      "integrity": "sha512-sFVE0MVXhawAkET8EgiUSMvDDv6u3uGidtO0BvNXG0/qKWlze/zEzhvLsyPU4HmLFRnffKeHK5RE2XpO5vHY8Q==",
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.9.0.tgz",
+      "integrity": "sha512-fRooepCcSYUKcplrn4h/teqL08Nr5bo4KDs8uGI6RAYOuDhGfWjaxFpmdUhr6Yme9G+ci+2Hh/hk9hHXxYGWaw==",
       "requires": {
         "@babel/runtime": "^7.12.5",
-        "@videojs/vhs-utils": "^3.0.0",
+        "@videojs/vhs-utils": "^3.0.2",
         "aes-decrypter": "3.1.2",
         "global": "^4.4.0",
-        "m3u8-parser": "4.5.2",
-        "mpd-parser": "0.15.4",
-        "mux.js": "5.10.0",
+        "m3u8-parser": "4.7.0",
+        "mpd-parser": "0.17.0",
+        "mux.js": "5.11.0",
         "video.js": "^6 || ^7"
       },
       "dependencies": {
@@ -4878,14 +4897,6 @@
       "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
       "dev": true
     },
-    "chainsaw": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.0.9.tgz",
-      "integrity": "sha1-EaBRAtHEx4W20EFdM21aOhYSkT4=",
-      "requires": {
-        "traverse": ">=0.3.0 <0.4"
-      }
-    },
     "chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -6091,11 +6102,6 @@
       "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
       "dev": true
     },
-    "estree-walker": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
-      "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
-    },
     "esutils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -6559,9 +6565,9 @@
       }
     },
     "glob-parent": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
-      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
       "dev": true,
       "requires": {
         "is-glob": "^4.0.1"
@@ -6685,14 +6691,6 @@
         }
       }
     },
-    "hashish": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz",
-      "integrity": "sha1-bWC8b/r3Ebav1g5CbQd5iAFOZVQ=",
-      "requires": {
-        "traverse": ">=0.2.4"
-      }
-    },
     "himalaya": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/himalaya/-/himalaya-1.1.0.tgz",
@@ -8929,9 +8927,9 @@
       "dev": true
     },
     "m3u8-parser": {
-      "version": "4.5.2",
-      "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.5.2.tgz",
-      "integrity": "sha512-sN/lu3TiRxmG2RFjZxo5c0/7Dr4RrEztl43jXrWwj5gFZ7vfa2iIxGfiPx485dm5QCazaIcKk+vNkUso8Aq0Ag==",
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.7.0.tgz",
+      "integrity": "sha512-48l/OwRyjBm+QhNNigEEcRcgbRvnUjL7rxs597HmW9QSNbyNvt+RcZ9T/d9vxi9A9z7EZrB1POtZYhdRlwYQkQ==",
       "requires": {
         "@babel/runtime": "^7.12.5",
         "@videojs/vhs-utils": "^3.0.0",
@@ -8953,6 +8951,7 @@
       "version": "0.25.7",
       "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
       "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
+      "dev": true,
       "requires": {
         "sourcemap-codec": "^1.4.4"
       }
@@ -9116,14 +9115,14 @@
       "dev": true
     },
     "mpd-parser": {
-      "version": "0.15.4",
-      "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.15.4.tgz",
-      "integrity": "sha512-YcOclxKc5gnT87UQYwRoPJpWOFvQORwN+bXYmTWCJ4U2pCSS7jjtPrIhoOLHFAyekj48CHTX4hjGBV/VSNsUsg==",
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.17.0.tgz",
+      "integrity": "sha512-oKS5G0jCcHHJ3sHYlcLeM9Xcbuixl08eAx7QW0Th7ChlZiI0YvLtGaHE/L0aKUBJFNvtkeksIr8XgJgSBBsS4g==",
       "requires": {
         "@babel/runtime": "^7.12.5",
-        "@videojs/vhs-utils": "^3.0.0",
+        "@videojs/vhs-utils": "^3.0.2",
         "global": "^4.4.0",
-        "xmldom": "^0.4.0"
+        "xmldom": "^0.5.0"
       },
       "dependencies": {
         "global": {
@@ -9157,9 +9156,9 @@
       }
     },
     "mux.js": {
-      "version": "5.10.0",
-      "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-5.10.0.tgz",
-      "integrity": "sha512-kLzvYsHYBwNa+ckkmpxWV3eImwntJbrwd1KbN4WR0hLe+dK/KB82aCuC0fQzAI2hkjYszdlSGsAWFgYdiFBUuA==",
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-5.11.0.tgz",
+      "integrity": "sha512-Q/iLfohHh5Pp6lW7EFtcxNuaCNJ3Ruywfy46pWLsY+yIxR1kXXImYY1wOhg8jLdBMs1kRaZqsiB4Zncsiw0a2Q==",
       "requires": {
         "@babel/runtime": "^7.11.2"
       }
@@ -10281,14 +10280,6 @@
         }
       }
     },
-    "remove": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/remove/-/remove-0.1.5.tgz",
-      "integrity": "sha1-CV/9gn1lyfQa2X0z5BanWBEHmVU=",
-      "requires": {
-        "seq": ">= 0.3.5"
-      }
-    },
     "remove-trailing-separator": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -10473,23 +10464,6 @@
         }
       }
     },
-    "rollup-plugin-replace": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz",
-      "integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==",
-      "requires": {
-        "magic-string": "^0.25.2",
-        "rollup-pluginutils": "^2.6.0"
-      }
-    },
-    "rollup-pluginutils": {
-      "version": "2.8.2",
-      "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
-      "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
-      "requires": {
-        "estree-walker": "^0.6.1"
-      }
-    },
     "rsvp": {
       "version": "4.8.5",
       "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -10766,15 +10740,6 @@
         "lru-cache": "^6.0.0"
       }
     },
-    "seq": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/seq/-/seq-0.3.5.tgz",
-      "integrity": "sha1-rgKvOkJHk9jMvyEtaRdODFTf/jg=",
-      "requires": {
-        "chainsaw": ">=0.0.7 <0.1",
-        "hashish": ">=0.0.2 <0.1"
-      }
-    },
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -11094,7 +11059,8 @@
     "sourcemap-codec": {
       "version": "1.4.8",
       "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+      "dev": true
     },
     "spdx-correct": {
       "version": "3.1.1",
@@ -11575,11 +11541,6 @@
         "punycode": "^2.1.1"
       }
     },
-    "traverse": {
-      "version": "0.3.9",
-      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
-      "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
-    },
     "ts-morph": {
       "version": "9.1.0",
       "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-9.1.0.tgz",
@@ -11926,20 +11887,34 @@
       }
     },
     "video.js": {
-      "version": "7.11.8",
-      "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.11.8.tgz",
-      "integrity": "sha512-iQmNYB+pdgu8b45Za1AKSa5J7uDyHIqfJy+picw4voKfjErXK/BEvs+A3f99Ck7SCZU4cmMmX/s17AwaaNs+1w==",
+      "version": "7.13.0",
+      "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.13.0.tgz",
+      "integrity": "sha512-wJcB2R5q3/6Ez5XUfpZBZTdgF321rX/M1HkDKxXQIdfsVi/pfP4l+equ2xL9O3X0XAPHRxLEegraIEuX28mRkA==",
       "requires": {
-        "@babel/runtime": "^7.9.2",
-        "@videojs/http-streaming": "2.6.4",
+        "@babel/runtime": "^7.12.5",
+        "@videojs/http-streaming": "2.9.0",
+        "@videojs/vhs-utils": "^3.0.2",
         "@videojs/xhr": "2.5.1",
-        "global": "4.3.2",
+        "aes-decrypter": "3.1.2",
+        "global": "^4.4.0",
         "keycode": "^2.2.0",
-        "remove": "^0.1.5",
-        "rollup-plugin-replace": "^2.2.0",
+        "m3u8-parser": "4.7.0",
+        "mpd-parser": "0.17.0",
+        "mux.js": "5.11.0",
         "safe-json-parse": "4.0.0",
         "videojs-font": "3.2.0",
-        "videojs-vtt.js": "^0.15.2"
+        "videojs-vtt.js": "^0.15.3"
+      },
+      "dependencies": {
+        "global": {
+          "version": "4.4.0",
+          "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
+          "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
+          "requires": {
+            "min-document": "^2.19.0",
+            "process": "^0.11.10"
+          }
+        }
       }
     },
     "videojs-font": {
@@ -12138,9 +12113,9 @@
       "dev": true
     },
     "xmldom": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz",
-      "integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA=="
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz",
+      "integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA=="
     },
     "xtend": {
       "version": "4.0.2",

+ 2 - 1
web/package.json

@@ -11,13 +11,14 @@
     "test": "jest"
   },
   "dependencies": {
+    "@cycjimmy/jsmpeg-player": "^5.0.1",
     "date-fns": "^2.21.3",
     "idb-keyval": "^5.0.2",
     "immer": "^8.0.1",
     "preact": "^10.5.9",
     "preact-async-route": "^2.2.1",
     "preact-router": "^3.2.1",
-    "video.js": "^7.11.8",
+    "video.js": "^7.13.0",
     "videojs-playlist": "^4.3.1",
     "videojs-seek-buttons": "^2.0.1"
   },

+ 1 - 0
web/src/App.jsx

@@ -27,6 +27,7 @@ export default function App() {
                 <Router>
                   <AsyncRoute path="/cameras/:camera/editor" getComponent={Routes.getCameraMap} />
                   <AsyncRoute path="/cameras/:camera" getComponent={Routes.getCamera} />
+                  <AsyncRoute path="/birdseye" getComponent={Routes.getBirdseye} />
                   <AsyncRoute path="/events/:eventId" getComponent={Routes.getEvent} />
                   <AsyncRoute path="/events" getComponent={Routes.getEvents} />
                   <AsyncRoute path="/recording/:camera/:date?/:hour?/:seconds?" getComponent={Routes.getRecording} />

+ 1 - 0
web/src/Sidebar.jsx

@@ -49,6 +49,7 @@ export default function Sidebar() {
           ) : null
         }
       </Match>
+      <Destination href="/birdseye" text="Birdseye" />
       <Destination href="/events" text="Events" />
       <Destination href="/debug" text="Debug" />
       <Separator />

+ 29 - 0
web/src/components/JSMpegPlayer.jsx

@@ -0,0 +1,29 @@
+import { h } from 'preact';
+import { baseUrl } from '../api/baseUrl';
+import { useRef, useEffect } from 'preact/hooks';
+import JSMpeg from '@cycjimmy/jsmpeg-player';
+
+export default function JSMpegPlayer({ camera }) {
+  const playerRef = useRef();
+  const canvasRef = useRef();
+  const url = `${baseUrl.replace(/^http/, 'ws')}/live/${camera}`
+
+  useEffect(() => {
+    const video = new JSMpeg.VideoElement(
+      playerRef.current,
+      url,
+      {canvas: canvasRef.current},
+      {protocols: []}
+    );
+
+    return () => {
+      video.destroy();
+    };
+  }, [url]);
+
+  return (
+    <div ref={playerRef} className="jsmpeg">
+      <canvas ref={canvasRef} className="relative w-full" />
+    </div>
+  );
+}

+ 14 - 0
web/src/routes/Birdseye.jsx

@@ -0,0 +1,14 @@
+import { h } from 'preact';
+import JSMpegPlayer from '../components/JSMpegPlayer';
+import Heading from '../components/Heading';
+
+export default function Birdseye() {
+  return (
+    <div className="space-y-4">
+      <Heading size="2xl">Birdseye</Heading>
+      <div>
+        <JSMpegPlayer camera="birdseye" />
+      </div>
+    </div>
+  );
+}

+ 5 - 0
web/src/routes/index.js

@@ -13,6 +13,11 @@ export async function getEvent(url, cb, props) {
   return module.default;
 }
 
+export async function getBirdseye(url, cb, props) {
+  const module = await import('./Birdseye.jsx');
+  return module.default;
+}
+
 export async function getEvents(url, cb, props) {
   const module = await import('./Events.jsx');
   return module.default;