video/audioタグの映像を記録するRecording a media elementの紹介
現在のHTML5ではvideoやaudioタグを使って動画、音楽などを再生できるようになっています。しかし、これらのデータはストリーミングで流したり、getUserMediaを使って取得することしかできませんでした。
そこで登場したのが Recording a media elementです。これは video/audioタグの内容をレコーディングし、ファイルとしてダウンロードもできるAPIになります。
使い方
まずHTMLを記述します。videoタグを使います。 #startButton
を押すとWebカメラの映像をストリーミングで流します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="left"> | |
<button id="startButton"> | |
開始 | |
</button> | |
<h2>プレビュー</h2> | |
<video id="preview" width="160" height="120" autoplay muted></video> | |
</div> |
さらに記録した内容を表示するDOMを用意します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="right"> | |
<button id="stopButton"> | |
停止 | |
</button> | |
<h2>録画した内容</h2> | |
<video id="recording" width="160" height="120" controls></video> | |
<button id="downloadButton"> | |
Download | |
</button> | |
</div> |
JavaScriptの処理
まず必要なDOMを変数化します。最後の recordingTimeMS
は録画を行うタイミングで、今回は5秒ごとにしています。細かければ動画が滑らかになりますが、CPU負荷も大きくなります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let preview = document.getElementById("preview"); | |
let recording = document.getElementById("recording"); | |
let startButton = document.getElementById("startButton"); | |
let stopButton = document.getElementById("stopButton"); | |
let downloadButton = document.getElementById("downloadButton"); | |
let logElement = document.getElementById("log"); | |
let recordingTimeMS = 5000; |
開始した際の処理
まず開始時の処理について解説します。最初はgetUserMediaを使ってWebカメラにアクセスします。そして、映像を #preview に表示します。そして、 preview.captureStream
を使って記録を開始します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 開始ボタンを押した時の処理 | |
startButton.addEventListener("click", function() { | |
// Webカメラにアクセス | |
navigator.mediaDevices.getUserMedia({ | |
video: true, | |
audio: true | |
}).then(stream => { | |
// アクセスが許可された場合 | |
preview.srcObject = stream; // プレビューにWebカメラの映像を表示 | |
downloadButton.href = stream; // ダウンロードボタンにも配置 | |
// 表示内容をキャプチャする | |
preview.captureStream = preview.captureStream || preview.mozCaptureStream; | |
// プレビューが表示開始するタイミングをPromiseで取得 | |
return new Promise(resolve => preview.onplaying = resolve); | |
}) | |
// 表示が開始したらレコーディングを開始 | |
.then(() => startRecording(preview.captureStream(), recordingTimeMS)) | |
// レコーディングが終了時の処理 | |
.then (recordedChunks => { | |
let recordedBlob = new Blob(recordedChunks, { type: "video/webm" }); | |
recording.src = URL.createObjectURL(recordedBlob); | |
downloadButton.href = recording.src; | |
downloadButton.download = "RecordedVideo.webm"; | |
}) | |
.catch((err) => console.log(err)); | |
}, false); |
レコーディング中は非同期処理になるので startRecording という関数の中で処理を行っています。 MediaRecorder
のインスタンスにWebカメラの映像を指定し、 start
メソッドを実行するとレコーディングが開始されます。レコーディング内容は ondataavailable
メソッドで呼び出されるので、これを変数に残し続けます。
レコーディングが終わると onstop が呼ばれます。今回は lengthInMS
で自動的に停止するようにしています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function startRecording(stream, lengthInMS) { | |
let recorder = new MediaRecorder(stream); | |
let data = []; | |
recorder.ondataavailable = event => data.push(event.data); | |
recorder.start(); | |
let stopped = new Promise((resolve, reject) => { | |
recorder.onstop = resolve; | |
recorder.onerror = event => reject(event.name); | |
}); | |
let recorded = new Promise((resolve, reject) => { | |
setTimeout(() => { | |
recorder.state == "recording" && recorder.stop() | |
resolve(); | |
}, lengthInMS); | |
}); | |
return Promise.all([ | |
stopped, | |
recorded | |
]) | |
.then(() => data); | |
} |
また、プレビューを停止する場合には次のように実装することもできます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
stopButton.addEventListener("click", function() { | |
preview.srcObject.getTracks().forEach(track => track.stop()); | |
}, false); |
デモ
ここまでの実装をJSFiddleにてデモできます。なお、執筆時点ではChromeとMozillaでしか動作しません。
動画リソースはWebカメラに限らず使えるでしょう。そうした時に特定の場所だけキャプチャするといった用途でも使えそうです。動画を編集するという目的にしてはCPU負荷が大きそうですが、動画コンテンツをより扱いやすくしてくれそうです。
コメントは受け付けていません。