p5.js で使うスマホのカメラを選択する仕組みの簡単なテスト：外向き／内向き、exact の有無の違いのテスト

この記事は、p5.js で作ったプログラムでカメラを使う場合に、スマホのカメラ（外向き／内向き）を選択する仕組みを試してみた話です。

具体的には、「facingMode」に関して以下の 4通りの指定を行った場合の、挙動の違いを見てみたというものです。

- "user"
- "environment"
- { exact: "user" }
- { exact: "environment" }

## スマホで利用するカメラの指定
JavaScript でスマホのカメラを使う場合、外向き／内向きのカメラを指定することができます。

この話に関して、MDN のサイトでは以下の部分に記載があります。

●MediaDevices.getUserMedia() - Web API | MDN
　https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia
![facingModeについて](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f0f5affe-9836-87ab-f8b4-e496f6439e02.png)

これについて、その他の情報などを見てみたところ、以下の意味になるようでした。

- 指定した特定のカメラが利用できる場合は、それを利用（利用できない場合、別のカメラを使う形になる）
   - できれば外向きカメラを利用： "environment"
   - できれば内向きカメラを利用： "user"
- 特定のカメラのみを利用するよう制限する
   - 外向きカメラ利用に限定： { exact: "user" }
   - 内向きカメラ利用に限定： { exact: "environment" }

### p5.js で上記の指定を利用する
上記のカメラの指定を、[MediaDevices.getUserMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia) で用いる場合は、以下の `constraints` の部分に設定します。

```javascript
async function getMedia(constraints) {
  let stream = null;

  try {
    stream = await navigator.mediaDevices.getUserMedia(constraints);
    /* ストリームを使用 */
  } catch(err) {
    /* エラーを処理 */
  }
}
```

これを、p5.js の [createCapture()](https://p5js.org/reference/#/p5/createCapture) で実行する場合は、単純に createCapture() の中で指定をすれば良いようです。

●capture = createCapture(VIDEO); Can we reference a different Camera on phone? · Issue #1496 · processing/p5.js
　https://github.com/processing/p5.js/issues/1496
![createCaptureでカメラ指定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/c85bfc1e-1641-a9eb-09d0-23a5459750ce.png)

この後に、実際に動作を確認してみます。

## 動作確認
ここで、今回用いる p5.js のプログラムを掲載します。

このプログラムは、p5.js Web Editor上で、sketch.js に書いて利用したものです。
※ HTML・CSS は、p5.js Web Editor上で用意されるデフォルトのものを書きかえずに用いています

```javascript
const facingModeList = [
  "user",
  "environment",
  { exact: "user" },
  { exact: "environment" },
];

const options = [];

let capture;

function setup() {
  createCanvas(640, 480);

  for (mode of facingModeList) {
    options.push({
      video: {
        facingMode: mode,
      },
      audio: false,
    });
  }

  for (let i = 0; i < options.length; i++) {
    const button = createButton(JSON.stringify(facingModeList[i]));
    button.position(10, 10 + 35 * i);
    button.size(160);
    button.mousePressed(function () {
      capture = createCapture(options[i]);
      capture.hide();
    });
  }
}

function draw() {
  background(220);

  if (capture) {
    image(capture, 0, 0);
  }
}
```

プログラムを実行した後の画面は、以下の通りです。
![プログラムを実行した画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f5aa00a4-7531-891e-008b-c90b1e9aaa39.png)

### PC で実行した場合の動作
これを、まずは PC で実行してみます。そうすると、以下のような結果となりました。

- PC のカメラが利用される
   - "user"
   - "environment"
- エラーが発生
   - { exact: "user" }
   - { exact: "environment" }

exact の記載がない場合、「"user"・"environment"」に該当するカメラがなくても、別のカメラを用いる動作となるようです。一方、exact を記載している場合は、「"user"・"environment"」に該当するカメラがない場合にエラーを返すようです。

### スマホで実行した場合の動作
内向き・外向きの両方があるスマホで実行した場合は、以下のようになりました。
※ ちなみに、利用したスマホは iPhone 14 Pro と、Pixel 6a の 2種類です

- 内向きカメラが利用される
   - "user"
   - { exact: "user" }
- 外向きカメラが利用される
   - "environment"
   - { exact: "environment" }

上記の PC・スマホで試した結果を見ると、最初に想定されたとおりの動作となりました。