なになれ

IT系のことを記録していきます。

Electronでデスクトップを録画するアプリが簡単に作れました

ElectronはAtomに使われていたり、VSCodeに使われていたりとPCネイティブなアプリを作るために最近よく使われているライブラリです。
今まで使ったことがなかったので使ってみました。今回作ったアプリのリポジトリこちらです。
アプリ作成のポイントを紹介します。

題材

デスクトップのウィンドウやスクリーンを録画するアプリを作ります。

作り方

環境構築

Electronアプリを作るにあたってのテンプレートにはelectron-vueを使用します。
導入 · electron-vue

コマンド操作だけでElectron製のアプリを起動するところまで準備してくれます。

デスクトップの録画処理

Electronに用意されているAPIであるdesktopCapturerを使用すると、ウィンドウやスクリーンの動画を取得することができます。
desktopCapturer | Electron

// In the renderer process.
const {desktopCapturer} = require('electron')

desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
  if (error) throw error
  for (let i = 0; i < sources.length; ++i) {
    if (sources[i].name === 'Electron') {
      navigator.mediaDevices.getUserMedia({
        audio: false,
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: sources[i].id,
            minWidth: 1280,
            maxWidth: 1280,
            minHeight: 720,
            maxHeight: 720
          }
        }
      }, handleStream)
      return
    }
  }
})

function handleStream (stream) {
  document.querySelector('video').src = URL.createObjectURL(stream)
}

desktopCapturer.getSourcesでウィンドウやスクリーンを取得することができます。
navigator.mediaDevices.getUserMediachromeMediaSourceIdに取得したsourceのidを指定することで各ウィンドウやスクリーンのStreamデータを取得することができます。
minWidthmaxWidthminHeightmaxHeightを変更することで動画のサイズを変更することができます。
maxWidthmaxHeightに指定した値のサイズまでの動画になります。

動画の保存にはWeb標準APIのMediaStream Recording APIを使用します。
MediaStream Recording API - Web APIs | MDN

<a id="download">Download</a>
<script>
  const downloadLink = document.getElementById('download');
  var handleSuccess = function(stream) {
    const options = {mimeType: 'video/webm'};
    const recordedChunks = [];
    const mediaRecorder = new MediaRecorder(stream, options);
    mediaRecorder.ondataavailable = (e) => {
      recordedChunks.push(e.data);
    };
    mediaRecorder.onstop = (e) => {
      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
      downloadLink.download = 'video.webm';
    };
    mediaRecorder.start();
  };
  navigator.mediaDevices.getUserMedia({ audio: true, video: true })
      .then(handleSuccess);
</script>

navigator.mediaDevices.getUserMediaで取得したStreamデータでMediaRecorderインスタンスを作成することで、Streamデータを動画として保存することができます。

工夫したところ

録画開始や録画停止の際にショートカットキーを使えるようにしました。

Electronでは、ショートカットキーの設定にいくつかのアプローチがあります。
Keyboard Shortcuts | Electron

今回は、Local ShortcutsとGlobal Shortcutsを使いました。

Local Shortcuts

アプリのメニューを作り、メニュー項目を実行するショートカットキーを設定する方法です。
まずはメニューを作る必要があります。
Menu | Electron

メニューを作る場合、通常はMain processというElectronアプリのエントリーポイントとなるプロセスでメニューを作成する必要があります。
今回はメニュー項目の設定変更を画面描画を担当するプロセスであるRender process上で行う必要があったため、Render processでメニューを作成しました。
Electronのremoteモジュールを使用することでRender processでもメニューを作成することができます。

// In the renderer process.
import * as electron from 'electron'
const {Menu} = electron.remote

const template = [
  {
    label: '画面',
    submenu: [
      {
        label: '画面収録を開始',
        accelerator: 'CmdOrCtrl+P',
        click: function (menuItem, browserWindow, event) {
          browserWindow.webContents.send('startRecord')
        }
      }
    ]
  }
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)

メニューの作成は、buildFromTemplateメソッドを使用すると簡単です。
メニューの仕様に準拠したJavaScriptオブジェクトを渡すことで、オブジェクトの値に応じたメニューを作成してくれます。

acceleratorプロパティでショートカットキーを設定することができます。
Accelerator | Electron

clickプロパティでメニュー項目が選択された時の動作を記述することができます。
引数のbrowserWindowがRender processが動作するウィンドウなので、browserWindow.webContentsを介することで、Render processに処理を渡すことができます。
webContents | Electron

アプリのウィンドウがスクリーン上に存在しない場合、browserWindownullになるため注意が必要です。
今回のアプリでは、browserWindownullの場合、Main processからRender processに処理が流れるようにしています。
ElectronではIPCのAPIでプロセス間の通信を行うことが可能です。
ipcMain | Electron

Global Shortcuts

ショートカットキーのみを設定する方法です。アプリにフォーカスがなくても機能します。
Keyboard Shortcuts | Electron

// In the main process.
import { BrowserWindow, globalShortcut } from 'electron'
let mainWindow = new BrowserWindow({
  height: 563,
  useContentSize: true,
  width: 1000
})
globalShortcut.register('CommandOrControl+E', () => {
  mainWindow.webContents.send('stopRecord')
})

globalShortcutでショートカットキーを設定することができます。

感想

Webの技術でPCネイティブなアプリを作ることができました。
Webの技術でネイティブなアプリというとモバイル向けのCordovaが思い浮かびます。
Cordovaは複雑というイメージだったので、Electronもそんな感じかなというイメージでした。
触ってみると、Electronはシンプルで分かりやすかったですし、electron-vueのファイル構成もそんな感じでした。
Electronを使うとメニューを作るといったネイティブな機能が簡単に実現できるのが驚きでした。

アプリを使って実際に録画した動画です。
youtu.be

参考

MediaStream Recording APIの使い方を参考にしました
Recording Video from the User  |  Web  |  Google Developers

アプリの画面デザインを参考にしました
デスクトップを録画するアプリを書いた - Qiita