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);
このエントリーをはてなブックマークに追加
にほんブログ村 IT技術ブログへ

コメント

メールアドレスが公開されることはありません。 が付いている欄は必須項目です