convulse/convulse.js

206 lines
6.6 KiB
JavaScript

// jshint asi:true
Math.TAU = Math.PI * 2
function toast(text, timeout=8000) {
let toasts = document.querySelector("#toasts")
if (! text) {
while (toasts.firstChild) {
toasts.firstChild.remove()
}
} else {
let p = document.querySelector("#toasts").appendChild(document.createElement("p"))
p.textContent = text
if (timeout) {
setTimeout(() => p.remove(), timeout)
}
}
}
class Convulse {
constructor() {
this.chunks = []
this.canvas = document.querySelector("canvas")
this.ctx = this.canvas.getContext("2d")
this.dllink = document.querySelector("#download")
this.webcamVideo = document.querySelector("#webcam")
this.desktopVideo = document.querySelector("#desktop")
this.canvas.width = 1920
this.canvas.height = 1080
document.querySelector("#indicator").classList.add("hidden")
document.addEventListener("mouseenter", e => this.showControls(true))
document.addEventListener("mouseleave", e => this.showControls(false))
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"))
document.querySelector("#webcam-pos").addEventListener("click", e => this.setWebcamPos(e))
this.webcamPos = localStorage.webcamPos || 2
document.querySelector("#desktop-size").addEventListener("input", e => this.setDesktopSize(e))
document.querySelector("#desktop-size").value = localStorage.desktopSize || 2.0
document.querySelector("#desktop-size").dispatchEvent(new Event("input"))
document.querySelector("#desktop-pos").addEventListener("click", e => this.setDesktopPos(e))
this.desktopPos = localStorage.desktopPos || 0
this.recorder = {state: "unstarted"}
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)
}
})
.catch(err => {
toast("Couldn't open camera!")
})
navigator.mediaDevices.getDisplayMedia({video: {cursor: "always"}})
.then(media => {
document.querySelector("#hello").classList.add("hidden")
this.desktopVideo.srcObject = media
this.desktopVideo.play()
})
.catch(err => {
toast("Couldn't open screen grabber!")
})
let canvasStream = this.canvas.captureStream(30)
for (let vt of canvasStream.getVideoTracks()) {
this.mediaStream.addTrack(vt)
console.log("Adding video track", vt)
}
this.frame()
toast("Click anywhere to start and stop recording")
}
setWebcamSize(event) {
this.webcamSize = event.target.value
localStorage.webcamSize = this.webcamSize
}
setWebcamPos(event) {
this.webcamPos = (this.webcamPos + 1) % 9
localStorage.webcamPos = this.webcamPos
}
setDesktopSize(event) {
this.desktopSize = event.target.value
localStorage.desktopSize = this.desktopSize
}
setDesktopPos(event) {
this.desktopPos = (this.desktopPos + 1) % 9
localStorage.desktopPos = this.desktopPos
}
rec(event) {
let button = document.querySelector("#rec")
if (this.recorder.state == "recording") {
// Stop
this.recorder.stop()
this.canvas.classList.remove("recording")
document.querySelector("#indicator").classList.add("hidden")
button.textContent = "⏺️"
toast("Stopped")
document.title = "Convulse: stopped"
this.save()
} else {
// Start
this.chunks = []
this.recorder = new MediaRecorder(this.mediaStream, {mimeType: "video/webm"})
this.recorder.addEventListener("dataavailable", event => {
if (event.data && event.data.size > 0) {
this.chunks.push(event.data)
}
})
this.recorder.start(10)
this.canvas.classList.add("recording")
document.querySelector("#indicator").classList.remove("hidden")
button.textContent = "⏹️"
toast("Recording: click anywhere to stop")
document.title = "Convulse: recording"
}
}
showControls(show) {
let controls = document.querySelector("#controls")
if (show) {
controls.classList.remove("hidden")
} else {
controls.classList.add("hidden")
}
}
save(event) {
let recording = window.URL.createObjectURL(new Blob(this.chunks, {type: this.recorder.mimeType}))
let now = new Date().toISOString()
let saveButton = document.querySelector("#save")
saveButton.addEventListener('progress', event => console.log(event))
saveButton.href = recording
saveButton.download = "convulse-" + now + ".webm"
saveButton.click()
}
frame(timestamp) {
if (this.desktopVideo.videoWidth > 0) {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
let desktopAR = this.desktopVideo.videoWidth / this.desktopVideo.videoHeight
let desktopHeight = this.canvas.height * this.desktopSize
let desktopWidth = desktopHeight * desktopAR
let desktopY = (this.canvas.height - desktopHeight) * (Math.floor(this.desktopPos / 3) / 2)
let desktopX = (this.canvas.width - desktopWidth) * (Math.floor(this.desktopPos % 3) / 2)
this.ctx.drawImage(
this.desktopVideo,
desktopX, desktopY,
desktopWidth, desktopHeight
)
}
if (this.webcamVideo.videoWidth > 0) {
let webcamAR = this.webcamVideo.videoWidth / this.webcamVideo.videoHeight
let webcamHeight = this.canvas.height * this.webcamSize
let webcamWidth = webcamHeight * webcamAR
let webcamY = (this.canvas.height - webcamHeight) * (Math.floor(this.webcamPos / 3) / 2)
let webcamX = (this.canvas.width - webcamWidth) * (Math.floor(this.webcamPos % 3) / 2)
this.ctx.drawImage(
this.webcamVideo,
webcamX, webcamY,
webcamWidth, webcamHeight
)
console.log(webcamAR)
}
requestAnimationFrame(ts => this.frame(ts))
}
}
function init() {
window.app = new Convulse()
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init)
} else {
init()
}