Mac の Electron アプリで app 実行したときにカメラにアクセスできない

# 20240617 追記

Bartender 4 で案内されてはじめて知ったのだけれども、権限周りにおいて不具合があるよう。

[Bartender 4 - Bartender and macOS Ventura and 12.6.1 Accessibility and Screen Recording Permissions bug](https://www.macbartender.com/b4blog/macOS-Ventura-Permissions/)

```
tccutil reset Accessibility com.surteesstudios.Bartender; tccutil reset ScreenCapture com.surteesstudios.Bartender; tccutil reset Accessibility com.surteesstudios.Bartender-setapp; tccutil reset ScreenCapture com.surteesstudios.Bartender-setapp
```

↑
カメラとスクリーンキャプチャは異なるけれども、（スクリーンキャプチャの Electron アプリも権限で同様におかしかった）これが原因でおかしい可能性がある。
なので、tccutil で権限をリセットして試すのもひとつかもしれない。
Bartender 4 それ自体はこの案内の通りで正常に動作するようになった。
署名されているアプリでもこの辺は関係ないぽい。


# 20231204 追記
以下の方法では、macOS Sonoma バージョン 14.1 でうまくいかなかったです。
権限周りであるとは思うのだけれど・・・。

別のアプリで、同じように画面収録する権限もうまくいきませんでした。
[systemPreferences | Electron](https://www.electronjs.org/ja/docs/latest/api/system-preferences#systempreferencesgetmediaaccessstatusmediatype-windows-macos)
[systemPreferences | Electron](https://www.electronjs.org/ja/docs/latest/api/system-preferences#systempreferencesaskformediaaccessmediatype-macos)

アプリに署名したら多分いけると思います。


# はじめに
Mac で動作する Electron アプリをビルドし、 xxx.app を実行したときにカメラにアクセスできずに困った。
結論、 Mac で動作するアプリはカメラアクセスの許可を取得すれば OK でした。

探せなかっただけかもしれないけれど、
英語でもこのことずばりで書いている Issue さくっとみつけられなかったのでメモ。


# 環境
- OS: macOS Big Sur 11.3.1
- Architecture: arm64
- Runtime: node.js v16.15.0 (anyenv + nodenv)
- Main Framework: Vue v3.2.13, Electron v13.0.0, vue-cli-plugin-electron-builder v2.1.1


# カメラへのアクセス
Web Application では、 `getUserMedia` でデバイスのカメラにアクセスしてストリームオブジェクトを取得、
これを `video.srcObject` あたりにいれてやれば映像にアクセスできます。
[MediaDevices.getUserMedia() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia)

Electron で WebCam にアクセスするのはこれで OK です。
`npm run electron:serve` で実行したところ問題なく動作しました。

# 問題
じゃーこれで署名なしビルドしてとりあえず実行しているみかと思い、
`npm run electron:build` でビルドしたアプリを実行しました。

```
DOMException: Could not start video source
```

↑
こんなエラーが JS ランタイムに表示され、カメラの映像が表示されません。
調べてみると `getUserMedia` で **deviceId** を指定するとき、以前の MediaStream が開放されていないのが原因とあってこれかなーと思いやってみるもダメ。
[Androidで getUserMedia() したら NotReadableError: could not start video source - console.lealog();](https://lealog.hateblo.jp/entry/2020/01/29/170250)
こちらは、 Android WebView でのはなしかな。

# 解決方法
JavaScript や DOM まわりの単純な不具合じゃないなーと思ったので、 Mac の権限まわりかなと思う。
Electron でカメラやマイクへのアクセス権限どうやって取得するかのドキュメントがあった。
[systemPreferences | Electron](https://www.electronjs.org/docs/latest/api/system-preferences#systempreferencesaskformediaaccessmediatype-macos)

めっちゃこれっぽい。
てことで、メインプロセス（レンダラープロセスじゃない）でこれを実行してみた。
実行するタイミングあってるかは微妙だけどとりあえず、、、
（Android や iOS アプリ一般でいえば、必要になる箇所で権限取得するのがよいかも？）

```js
import {app, systemPreferences} from 'electron'

~~~

app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installExtension(VUEJS3_DEVTOOLS)
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  await systemPreferences.askForMediaAccess('camera').then((e) => {
    console.log('success media access', e)
  }).catch((e) => {
    console.log('failed media access', e)
  })
  createWindow()
})
```

↑
**ready** イベントでウィンドウ作成前に処理を `await` でブロックしてやってみた。
ドキュメントにも記載ありますが、 `systemPreferences.askForMediaAccess` を使用するには、 **Info.plist** に `NSCameraUsageDescription` や `NSMicrophoneUsageDescription` のプロパティが必要です。
これがないと権限取得 API コール時にアプリが（おそらく）クラッシュします。
iOS アプリでもこのへんは同様のやつですね。

なお、 `systemPreferences.getMediaAccessStatus('camera')` で現在の権限チェックもできるので、カメラアクセスが必要な Mac Electron アプリはこれをつかって権限チェックするのが適切ですね。

おわり。