mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-24 05:56:14 -04:00
Build 29035725
This commit is contained in:
parent
a1bf54af71
commit
819197031f
64 changed files with 3494 additions and 6921 deletions
284
src/recording.ts
284
src/recording.ts
|
@ -1,168 +1,168 @@
|
|||
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";
|
||||
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;
|
||||
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 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,
|
||||
);
|
||||
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,
|
||||
);
|
||||
// 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,
|
||||
);
|
||||
recordCanvasCtx.textAlign = "left";
|
||||
recordCanvasCtx.fillText(
|
||||
"Level " + (gameState.currentLevel + 1) + "/" + max_levels(gameState),
|
||||
12,
|
||||
12,
|
||||
);
|
||||
}
|
||||
|
||||
export function startRecordingGame(gameState:GameState) {
|
||||
if (!isOptionOn("record")) {
|
||||
return;
|
||||
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]);
|
||||
}
|
||||
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;
|
||||
recordCanvas.width = gameState.gameZoneWidthRoundedUp;
|
||||
recordCanvas.height = gameState.gameZoneHeight;
|
||||
|
||||
const track = getAudioRecordingTrack();
|
||||
if (track) {
|
||||
captureStream.addTrack(track.stream.getAudioTracks()[0]);
|
||||
}
|
||||
// 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);
|
||||
|
||||
recordCanvas.width = gameState.gameZoneWidthRoundedUp;
|
||||
recordCanvas.height = gameState.gameZoneHeight;
|
||||
|
||||
// drawMainCanvasOnSmallCanvas()
|
||||
const recordedChunks: Blob[] = [];
|
||||
|
||||
const instance = new MediaRecorder(captureStream, {
|
||||
videoBitsPerSecond: 3500000,
|
||||
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),
|
||||
});
|
||||
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);
|
||||
};
|
||||
targetDiv.appendChild(video);
|
||||
targetDiv.appendChild(a);
|
||||
};
|
||||
}
|
||||
|
||||
export function pauseRecording( ) {
|
||||
if (!isOptionOn("record")) {
|
||||
return;
|
||||
}
|
||||
if (mediaRecorder?.state === "recording") {
|
||||
mediaRecorder?.pause();
|
||||
}
|
||||
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();
|
||||
}
|
||||
if (!isOptionOn("record")) {
|
||||
return;
|
||||
}
|
||||
if (mediaRecorder?.state === "paused") {
|
||||
mediaRecorder.resume();
|
||||
}
|
||||
}
|
||||
|
||||
export function stopRecording() {
|
||||
if (!isOptionOn("record")) {
|
||||
return;
|
||||
}
|
||||
if (!mediaRecorder) return;
|
||||
mediaRecorder?.stop();
|
||||
mediaRecorder = null;
|
||||
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
|
||||
);
|
||||
}
|
||||
return (
|
||||
"breakout-71-capture-" +
|
||||
new Date().toISOString().replace(/[^0-9\-]+/gi, "-") +
|
||||
"." +
|
||||
ext
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue