<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>画面+音声 録画テスト</title>
</head>
<body>
<button id="start">録画開始</button>
<button id="stop" disabled>停止</button>
<pre id="log"></pre>
<script>
let recorder;
let chunks = [];
const log = (s) => document.getElementById("log").textContent += s + "\n";
document.getElementById("start").onclick = async () => {
try {
log("画面取得開始...");
const displayStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true
});
log("AudioContext 準備...");
const audioCtx = new AudioContext();
const destination = audioCtx.createMediaStreamDestination();
// 画面音声
if (displayStream.getAudioTracks().length > 0) {
audioCtx.createMediaStreamSource(displayStream).connect(destination);
log("タブ音声接続 OK");
} else {
log("タブ音声なし");
}
// マイク(失敗しても続行)
try {
log("マイク取得開始...");
const micStream = await navigator.mediaDevices.getUserMedia({ audio: true });
audioCtx.createMediaStreamSource(micStream).connect(destination);
log("マイク接続 OK");
} catch (e) {
log("マイク取得失敗(無視して続行): " + e.message);
}
const mixedStream = new MediaStream([
...displayStream.getVideoTracks(),
...destination.stream.getAudioTracks()
]);
log("MediaRecorder 作成...");
recorder = new MediaRecorder(mixedStream);
recorder.onstart = () => log("● 録画開始!");
recorder.ondataavailable = e => {
if (e.data.size > 0) chunks.push(e.data);
};
recorder.onstop = () => {
log("■ 録画停止");
const blob = new Blob(chunks, { type: "video/webm" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "record.webm";
a.click();
chunks = [];
};
recorder.start();
log("recorder.start() 実行");
document.getElementById("start").disabled = true;
document.getElementById("stop").disabled = false;
} catch (e) {
log("致命的エラー: " + e.message);
console.error(e);
}
};
document.getElementById("stop").onclick = () => {
recorder.stop();
document.getElementById("start").disabled = false;
document.getElementById("stop").disabled = true;
};
</script>
</body>
</html>