YuichirouSeitoku / mdd

0 stars 0 forks source link

Windowをキャプチャして動画ファイルに書き出す機能を作る #5

Closed yumetodo closed 1 month ago

yumetodo commented 1 month ago

https://www.electronjs.org/ja/docs/latest/api/desktop-capturer

YuichirouSeitoku commented 1 month ago
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Screen Recorder</title>
</head>
<body>
  <h1>画面録画アプリ</h1>
  <button id="start">録画開始</button>
  <script>
    const { ipcRenderer } = require('electron');

    document.getElementById('start').addEventListener('click', () => {
      ipcRenderer.send('start-recording');
    });

    ipcRenderer.on('recording-finished', (event, outputPath) => {
      alert(`録画が完了しました: ${outputPath}`);
    });
  </script>
</body>
</html>
const { app, BrowserWindow, ipcMain, desktopCapturer } = require('electron');
const { createWriteStream } = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    },
  });

  mainWindow.loadFile('index.html');
}

app.whenReady().then(createWindow);

ipcMain.on('start-recording', async (event) => {
  const sources = await desktopCapturer.getSources({ types: ['screen'] });
  const screenSource = sources[0];

  const outputPath = path.join(app.getPath('videos'), 'recording.mp4');
  const outputStream = createWriteStream(outputPath);

  ffmpeg(screenSource.thumbnail.toPNG())
    .format('mp4')
    .pipe(outputStream);

  outputStream.on('finish', () => {
    event.reply('recording-finished', outputPath);
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});
YuichirouSeitoku commented 1 month ago

https://www.electronjs.org/ja/docs/latest/api/desktop-capturer

yumetodo commented 1 month ago

https://stackoverflow.com/questions/36753288/saving-desktopcapturer-to-video-file-in-electron

yumetodo commented 1 month ago

ipcの理解の参考資料

yumetodo commented 1 month ago

https://github.com/electron/electron/issues/27139

どうも navigator.mediaDevices.getDisplayMediasource.idを渡す方法が見つからないと思ったら何事かあるらしい。

yumetodo commented 1 month ago

https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc;drc=652ce146e0992e34bda5dd8e75142a86b4eebcf1;l=174?q=kMediaStreamSourceId

ParseOldStyleNames関数ではパースしている

    } else if (constraint.name_ == kMediaStreamSource) {
      // TODO(hta): This has only a few legal values. Should be
      // represented as an enum, and cause type errors.
      // https://crbug.com/576582
      result.media_stream_source.SetExact(constraint.value_);
    } else if (constraint.name_ == kDisableLocalEcho &&
               RuntimeEnabledFeatures::
                   DesktopCaptureDisableLocalEchoControlEnabled()) {
      result.disable_local_echo.SetExact(ToBoolean(constraint.value_));
    } else if (constraint.name_ == kMediaStreamSourceId ||
               constraint.name_ == kMediaStreamSourceInfoId) {
      result.device_id.SetExact(constraint.value_);

呼び出し元

https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc;l=242;drc=652ce146e0992e34bda5dd8e75142a86b4eebcf1;bpv=1;bpt=1

static MediaConstraints CreateFromNamedConstraints(
    ExecutionContext* context,
    Vector<NameValueStringConstraint>& mandatory,
    const Vector<NameValueStringConstraint>& optional) {
  MediaTrackConstraintSetPlatform basic;
  MediaTrackConstraintSetPlatform advanced;
  MediaConstraints constraints;
  ParseOldStyleNames(context, mandatory, basic);
  // We ignore unknown names and syntax errors in optional constraints.
  Vector<MediaTrackConstraintSetPlatform> advanced_vector;
  for (const auto& optional_constraint : optional) {
    MediaTrackConstraintSetPlatform advanced_element;
    Vector<NameValueStringConstraint> element_as_list(1, optional_constraint);
    ParseOldStyleNames(context, element_as_list, advanced_element);
    if (!advanced_element.IsUnconstrained())
      advanced_vector.push_back(advanced_element);
  }
  constraints.Initialize(basic, advanced_vector);
  return constraints;
}

これより前がよくわkらん。

yumetodo commented 1 month ago

image

mainから呼べない時点でキャプチャは却下。

yumetodo commented 1 month ago

結局録画はOBSなどの外部ツールに頼ることとし、実装仕掛けた録画開始/終了ボタンは #4 のキーボード監視の開始終了のみの機能となった。つまりユーザーは外部ツールの録画開始と本ツールの録画開始を十分に近い時間に押下するものとする。