breakout71/src/recording.ts

168 lines
4.8 KiB
TypeScript
Raw Normal View History

import {gameCanvas} from "./render";
import {max_levels} from "./game_utils";
import {getAudioRecordingTrack} from "./sounds";
import {t} from "./i18n/i18n";
import {GameState} from "./types";
import {isOptionOn} from "./options";
let mediaRecorder: MediaRecorder | null,
captureStream: MediaStream,
captureTrack: CanvasCaptureMediaStreamTrack,
recordCanvas: HTMLCanvasElement,
recordCanvasCtx: CanvasRenderingContext2D;
export function recordOneFrame(gameState:GameState) {
if (!isOptionOn("record")) {
return;
}
if (!gameState.running) return;
if (!captureStream) return;
drawMainCanvasOnSmallCanvas(gameState);
if (captureTrack?.requestFrame) {
captureTrack?.requestFrame();
} else if (captureStream?.requestFrame) {
captureStream.requestFrame();
}
}
export function drawMainCanvasOnSmallCanvas(gameState:GameState) {
if (!recordCanvasCtx) return;
recordCanvasCtx.drawImage(
gameCanvas,
gameState.offsetXRoundedDown,
0,
gameState.gameZoneWidthRoundedUp,
gameState.gameZoneHeight,
0,
0,
recordCanvas.width,
recordCanvas.height,
);
// Here we don't use drawText as we don't want to cache a picture for each distinct value of score
recordCanvasCtx.fillStyle = "#FFF";
recordCanvasCtx.textBaseline = "top";
recordCanvasCtx.font = "12px monospace";
recordCanvasCtx.textAlign = "right";
recordCanvasCtx.fillText(
gameState.score.toString(),
recordCanvas.width - 12,
12,
);
recordCanvasCtx.textAlign = "left";
recordCanvasCtx.fillText(
"Level " + (gameState.currentLevel + 1) + "/" + max_levels(gameState),
12,
12,
);
}
export function startRecordingGame(gameState:GameState) {
if (!isOptionOn("record")) {
return;
}
if (mediaRecorder) return;
if (!recordCanvas) {
// Smaller canvas with fewer details
recordCanvas = document.createElement("canvas");
recordCanvasCtx = recordCanvas.getContext("2d", {
antialias: false,
alpha: false,
}) as CanvasRenderingContext2D;
captureStream = recordCanvas.captureStream(0);
captureTrack =
captureStream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack;
const track = getAudioRecordingTrack();
if (track) {
captureStream.addTrack(track.stream.getAudioTracks()[0]);
}
}
recordCanvas.width = gameState.gameZoneWidthRoundedUp;
recordCanvas.height = gameState.gameZoneHeight;
// drawMainCanvasOnSmallCanvas()
const recordedChunks: Blob[] = [];
const instance = new MediaRecorder(captureStream, {
videoBitsPerSecond: 3500000,
});
mediaRecorder = instance;
instance.start();
mediaRecorder.pause();
instance.ondataavailable = function (event) {
recordedChunks.push(event.data);
};
instance.onstop = async function () {
let targetDiv: HTMLElement | null;
let blob = new Blob(recordedChunks, {type: "video/webm"});
if (blob.size < 200000) return; // under 0.2MB, probably bugged out or pointlessly short
while (
!(targetDiv = document.getElementById("level-recording-container"))
) {
await new Promise((r) => setTimeout(r, 200));
}
const video = document.createElement("video");
video.autoplay = true;
video.controls = false;
video.disablePictureInPicture = true;
video.disableRemotePlayback = true;
video.width = recordCanvas.width;
video.height = recordCanvas.height;
video.loop = true;
video.muted = true;
video.playsInline = true;
video.src = URL.createObjectURL(blob);
const a = document.createElement("a");
a.download = captureFileName("webm");
a.target = "_blank";
a.href = video.src;
a.textContent = t('main_menu.record_download', {
size: (blob.size / 1000000).toFixed(2)
});
targetDiv.appendChild(video);
targetDiv.appendChild(a);
};
}
export function pauseRecording( ) {
if (!isOptionOn("record")) {
return;
}
if (mediaRecorder?.state === "recording") {
mediaRecorder?.pause();
}
}
export function resumeRecording() {
if (!isOptionOn("record")) {
return;
}
if (mediaRecorder?.state === "paused") {
mediaRecorder.resume();
}
}
export function stopRecording() {
if (!isOptionOn("record")) {
return;
}
if (!mediaRecorder) return;
mediaRecorder?.stop();
mediaRecorder = null;
}
export function captureFileName(ext = "webm") {
return (
"breakout-71-capture-" +
new Date().toISOString().replace(/[^0-9\-]+/gi, "-") +
"." +
ext
);
}