diff --git a/toys/convulse/convulse.js b/toys/convulse/convulse.js index f7fc52a..6d52c17 100644 --- a/toys/convulse/convulse.js +++ b/toys/convulse/convulse.js @@ -35,8 +35,7 @@ class Convulse { document.querySelector("canvas").addEventListener("click", e => this.rec(e)) document.querySelector("#rec").addEventListener("click", e => this.rec(e)) - //document.querySelector("#save").addEventListener("click", e => this.save(e)) - + document.querySelector("#webcam-size").addEventListener("input", e => this.setWebcamSize(e)) document.querySelector("#webcam-size").value = localStorage.webcamSize || 0.3 document.querySelector("#webcam-size").dispatchEvent(new Event("input")) @@ -51,19 +50,34 @@ class Convulse { this.recorder = {state: "unstarted"} + // this.mediaStream gets audio directly from the device, video from canvas this.mediaStream = new MediaStream() - navigator.mediaDevices.getUserMedia({video: true, audio: true}) - .then(media => { - document.querySelector("#hello").classList.add("hidden") - this.webcamVideo.muted = true - this.webcamVideo.srcObject = media - this.webcamVideo.play() - for (let at of media.getAudioTracks()) { - this.mediaStream.addTrack(at) - console.log("Adding audio track", at) + // Populate select boxes with what media is available + navigator.mediaDevices.enumerateDevices() + .then(devs => { + for (let dev of devs) { + let opt = document.createElement("option") + opt.value = dev.deviceId + if (dev.kind == "audioinput") { + opt.text = dev.label || `Microphone ${aud.length+1}` + aud.appendChild(opt) + } else if (dev.kind == "videoinput") { + opt.text = dev.label || `Camera ${vid.length+1}` + vid.appendChild(opt) + } } }) + + // Pretend the user clicked whatever's in those boxes + let aud = document.querySelector("#audio-in") + let vid = document.querySelector("#video-in") + aud.addEventListener("change", e => this.inputSelect(e)) + vid.addEventListener("change", e => this.inputSelect(e)) + this.inputSelect() + + navigator.mediaDevices.getUserMedia({video: true, audio: true}) + .then(media => this.gotUserMedia(media)) .catch(err => { toast("Couldn't open camera!") }) @@ -81,7 +95,6 @@ class Convulse { let canvasStream = this.canvas.captureStream(30) for (let vt of canvasStream.getVideoTracks()) { this.mediaStream.addTrack(vt) - console.log("Adding video track", vt) } this.frame() @@ -89,6 +102,52 @@ class Convulse { toast("Click anywhere to start and stop recording") } + gotUserMedia(media) { + document.querySelector("#hello").classList.add("hidden") + + // Set video source + // sending this to an HTML element seems janky, is there no direct method for video? + if (this.webcamVideo.srcObject) { + for (let track of this.webcamVideo.srcObject.getTracks()) { + track.stop() + } + } + for (let track of media.getVideoTracks()) { + for (let opt of document.querySelector("#video-in")) { + if (opt.text == track.label) { + opt.selected = true + } + } + } + this.webcamVideo.muted = true + this.webcamVideo.srcObject = media + this.webcamVideo.play() + + // Set audio source + for (let track of this.mediaStream.getAudioTracks()) { + this.mediaStream.removeTrack(track) + track.stop() + } + for (let track of media.getAudioTracks()) { + this.mediaStream.addTrack(track) + for (let opt of document.querySelector("#audio-in")) { + opt.selected = (opt.value == track.deviceId) + } + } + } + + inputSelect(event) { + let audName = document.querySelector("#audio-in").value || undefined + let vidName = document.querySelector("#video-in").value || undefined + let constraints = { + audio: {deviceId: {exact: audName}}, + video: {deviceId: {exact: vidName}} + } + + navigator.mediaDevices.getUserMedia(constraints) + .then(e => this.gotUserMedia(e)) + } + setWebcamSize(event) { this.webcamSize = event.target.value localStorage.webcamSize = this.webcamSize diff --git a/toys/convulse/index.html b/toys/convulse/index.html index 185e15d..5361e59 100644 --- a/toys/convulse/index.html +++ b/toys/convulse/index.html @@ -38,6 +38,10 @@ 💾 +
+ 🎙️ + 🎥 +
UR FACE diff --git a/toys/convulse/sw.js b/toys/convulse/sw.js index 9ec07c5..914523f 100644 --- a/toys/convulse/sw.js +++ b/toys/convulse/sw.js @@ -1,26 +1,51 @@ -var cacheName = "convulse-v1"; +// jshint asi:true + +var cacheName = "v2"; var content = [ "index.html", "convulse.css", "convulse.js", "convulse.png" -]; +] -self.addEventListener("install", e => { - e.waitUntil(caches.Open(cacheName).then(cache => cache.addAll(content))); -}); +self.addEventListener("install", preCache) +self.addEventListener("fetch", cachingFetch) +self.addEventListener("activate", handleActivate) -// Have mercy, this is a horror show -self.addEventListener("fetch", e => { - e.respondWith( - caches.match(e.request).then(r => { - return r || fetch(e.request).then(response => { - return caches.open(cacheName).then(cache => { - cache.put(e.request, response.clone()); - return response; - }); - }); +function handleActivate(event){ + event.waitUntil( + cleanup() + ) +} + +async function cleanup(event) { + let cacheNames = await caches.keys() + for (let name of cacheNames) { + if (name != cacheName) { + console.log("Deleting old cache", name) + caches.delete(name) + } + } +} + +function preCache(event) { + event.waitUntil( + caches.open(cacheName) + .then(cache => cache.addAll(content)) + ) +} + +// Go try to pull a newer version from the network, +// but return what's in the cache for this request +function cachingFetch(event) { + fetch(event.request) + .then(resp => { + caches.open(cacheName) + .then(cache => { + cache.put(event.request, resp.clone()) }) - ); -}); + }) + + event.respondWith(caches.match(event.request)) +}