WebRTCを使ってローカルPCのカメラ映像を録画する
MediaRecorder
を使用すると、WebRTCのストリームを保存することができます。この仕組みを利用して、ローカルPCのカメラ映像を保存することができます。また、同様の方法でリモートのストリームも保存することができます。
前処理
getUserMedia()
を使用してMediaStream
を取得します。次の例では、MediaStream
を_localStream
で管理します。
// 'localVideo'はvideoエレメントの id
const _localVideo = document.getElementById('localVideo');
let _localStream = null;
// ストリーム取得処理. ボタン押下などでコールする.
function startMedia() {
if (_localStream) {
alert('media already started!');
return;
}
navigator.mediaDevices.getUserMedia({ audio: true,
video: {
width: { min: 640, ideal: 1920, max: 1920 },
height: { min: 360, ideal: 1080, max: 1080 },
frameRate: 30
}
}).then((stream) => {
// show log
let track = stream.getVideoTracks()[0];
console.log("frameRate", track.getSettings().frameRate);
console.log("width", track.getSettings().width);
console.log("height", track.getSettings().height);
_localStream = stream;
if (_localVideo.srcObject) {
alert('element already playing, so ignore');
return;
}
_localVideo.srcObject = _localStream;
_localVideo.volume = 0;
_localVideo.play();
}).catch((err) => {
alert(`media ERROR:${err}`);
});
}
// ストリーム解放処理. ボタン押下などで停止する.
function stopMedia() {
_localVideo.pause();
_localVideo.srcObject = null;
if (_localStream) {
let tracks = _localStream.getTracks();
if (!tracks) {
console.warn('tracks is NONE');
return;
}
tracks.forEach(track => track.stop());
_localStream = null;
}
}
スマホのカメラ
リアカメラを指定する場合は、次のようなオプションを video
に指定します。ただし、PCの場合はエラーとなるため、facingMode
キーを削除するか、facingMode: {}
としてください。
video: {
facingMode: { exact: "environment" }, // リアカメラ, スマホのみ有効.
}
MediaRecorderによる録画
MediaRecorder
を生成し、start()
メソッドを呼び出すことでストリームデータを取得できます。
以下は、5秒ごとにデータを取得し、録画停止時に保存するサンプルです。
const _recorder = {
mediaRecorder: undefined,
chunks: []
};
function createMediaRecorder(stream, recorder) {
const options = {
mimeType: 'video/webm;codecs=h264,opus',
audioBitsPerSecond: 128000,
videoBitsPerSecond: 2500000
};
console.log(options.mimeType, MediaRecorder.isTypeSupported(options.mimeType)); // 動画形式のサポート確認
let mediaRecorder = new MediaRecorder(stream, options);
// 一定時間ごとに渡されるデータをメモリに保持する
mediaRecorder.ondataavailable = (event) => {
console.log('mediaRecorder', 'ondataavailable');
if (event.data.size > 0) {
recorder.chunks.push(event.data);
}
};
// 録画停止時にファイル保存する
mediaRecorder.onstop = (event) => {
console.log('mediaRecorder', 'onstop');
if (recorder.chunks.length > 0) {
const blob = new Blob(recorder.chunks, { 'type': options.mimeType });
const blobURL = window.URL.createObjectURL(blob);
// download 処理.
const anchor = document.createElement('a');
anchor.download = 'local-video.webm'; // ファイル名
anchor.href = blobURL;
anchor.click();
// Cleanup
setTimeout(() => {
window.URL.revokeObjectURL(blobURL);
recorder.chunks.length = 0; // 配列を解放
recorder.mediaRecorder = undefined;
}, 10000);
}
};
recorder.mediaRecorder = mediaRecorder;
}
function startRecording() {
if (!_localStream) {
console.error('recording stream is none.');
return;
}
createMediaRecorder(_localStream, _recorder);
// パラメータで dataavailable の間隔を指定する(ミリ秒)
_recorder.mediaRecorder.start(5000);
}
function stopRecording() {
if (_recorder.mediaRecorder) {
_recorder.mediaRecorder.stop();
}
}
ボタン押下時や、先に記載したstartMedia()
の処理終了後にstartRecording()
を呼び出すことで、録画を開始できます。
dataavailableイベント
start()
メソッドのパラメータtimeslice
で指定した時間(ミリ秒)ごとにデータが通知されます。また、stop()
を実行した際にも、stop
イベントの前に通知が行われます。
長時間の録画について
長時間の録画時には、次のような方法でファイル分割できます。
timeslice
(データ通知間隔)=録画ファイルの時間(例:3分など)にし、stop()
→start()
を繰り返すstart()
でtimeslice
を指定せずに、停止時に全て保存する
dataavailable
イベントごとにデータを保存しても、2つ目以降のファイルにはヘッダ情報が存在しないため、再生できません。
ファイル保存処理
stop
イベントの受信処理では、アンカータグを利用してファイルをダウンロードさせることで保存しています。
まず、すべてのデータを結合してBlob
を作成します。
const blob = new Blob(recorder.chunks, { 'type': options.mimeType });
次に、Blob
を表す URL を生成し、そのURLを含む文字列をアンカータグで指定して、ファイルをダウンロードさせます。
createObjectURL
指定したオブジェクト(BlobやFile)に対する一時的なURLを生成します。
const blobURL = window.URL.createObjectURL(blob)
revokeObjectURL
生成されたオブジェクトURLを解放し、それ以上そのURLが使用されないようにします。解放された後にそのURLは使用できません。
window.URL.revokeObjectURL(blobURL);