スマホのインカメラ/アウトカメラ切り替えを実装する【技術メモ】

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/b6d31427-3ef7-8a1a-d68e-896254c9be43.png)

# はじめに
株式会社マイスター・ギルド新規事業部のウサギーです。
弊社新規事業部では、新規サービスの立ち上げを目指して
日々、アイディアの検証やプロトタイプの作成を行っています。

今回は、スマートフォン向けのWebアプリネタです。

# カメラの利用
ブラウザでデバイスのカメラを利用する場合、私はこういうコードを書いています。

```HTML:index.html
<video id="video" autoplay muted playsinline></video>
```
```JavaScript:script.js
const video = document.getElementById('video')
navigator.mediaDevices.getUserMedia({
    video:{width:300 height:300},
    audio:false,
}).then(stream => {
    video.srcObject = stream;
    video.play()
}).catch(e => {
    console.log(e)
})
```
スマホで表示するとインカメラが利用されます。

例えば、撮影機能のあるカメラアプリを作る場合なんかであれば
アウトカメラも使いたい…

ということで、今回は切り替えを実装しようと思います。

# インカメラの場合
```diff_JavaScript:script.js
 navigator.mediaDevices.getUserMedia({
     video:{width:300 height:300
+           facingMode: "user"},
     audio:false,
}
```

# アウトカメラの場合
```diff_JavaScript:script.js
 navigator.mediaDevices.getUserMedia({
     video:{width:300 height:300
+           facingMode: { exact: "environment" }},
     audio:false,
}
```

# 切り替えボタンを作る
```HTML:index.html
<div>
    <button id="btn">カメラ切り替え</button>
    <span id="camera-facing"></span>
</div>
```

```diff_JavaScript:script.js
let cameraFacingIsUser = true; // デフォルトはインカメラ
let facingText = document.getElementById('camera-facing');
facingText.innerText = "インカメラ";
document.getElementById('btn').onclick = () => {
    if(cameraFacingIsUser){
        facingText.innerText = "アウトカメラ";
        cameraFacingIsUser = false;
    }else{
        facingText.innerText = "インカメラ";
        cameraFacingIsUser = true;
    } 
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/af6349ad-c051-0925-c86a-e3d996cc9b3a.png)
押したら文言が切り替わります。
この切り替え部分で、faceMogdingも書き替えるようにします。

```diff_JavaScript:script.js
let localStream;
const constraints = {
    video: {
        width: 640,
        height: 480,
        facingMode: 'user' // デフォルトはインカメラ
    },
    audio: false
};

const getStream = (isUser) => {
    // 直前のストリームを停止する
    if (localStream !== undefined) {
        localStream.getVideoTracks().forEach((camera) => {
            camera.stop();
            console.log("camera stop");
        });
    }

    // 再読み込み
    constraints.video.facingMode = isUser ? 'user' : { exact: 'environment' }

    navigator.mediaDevices.getUserMedia(constraints)
        .then(stream => {
            localStream = stream;
            video.srcObject = stream;
            video.play();
        }).catch(e => {
            console.log(e)
        })
}

getStream(true);
```

これで切り替えできます。

# 躓いたところ

### （1）アウトカメラの指定 
アウトカメラを指定するとき、facingModeにはオブジェクト{ exact: 'environment' }を指定しないといけないのですが、
インカメラ時の指定にひきずられてうっかり文字列として
```JavaScript
constraints.video.facingMode = "{ exact: 'environment' }";
```
と指定してしまっていてアウトカメラが利用できずに？？？となっていました。
初歩的なミスではありますが、お気を付けください！

### （2）PCの場合
 インカメラ、アウトカメラの指定はスマートフォンデバイスのように元々固有のカメラセンサを2つ持っているもの向けのようです。
私の環境では、ノートPCについているインカメラは’user’で
別途USBでつないでいたWebカメラは{ exact: 'environment' }ではありませんでした。
PC向けにカメラを切り替えるコードを用意する場合は、
```JavaScript
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
    devices.forEach(function(device) {
        if(device.kind === "videoinput"){
            console.log(device.kind + ": " + device.label + device.devideid);
            // ここでdeviceのリストを作って利用するなど
        }
    });
  })
  .catch(function(err) {
    console.log(err.name + ": " + err.message);
  });
```
のようにdevice.kindがvideoinputのものを取得し、deviceidを指定して切り替えてあげるとよいかもしれません。
 
# おわりに
スマートフォンデバイスでのカメラの切り替え方法を学びました！
カメラを利用したWebアプリづくりに活かせたらいいな～と思います:rabbit:

# おまけ
作ったコードです
<details><summary>index.html</summary><div>

```HTML:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>OutCameraTest</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/destyle.css@1.0.15/destyle.css"/>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
    <div class="content_wrapper">
        <h1>WEBカメラの映像を表示</h1>
        <div class="video-container">
            <video id="video" autoplay muted playsinline></video>
        </div>
        <div>
            <button id="btn">カメラ切り替え</button>
            <span id="camera-facing"></span>
        </div>
    </div>  
    <script src="./script.js" defer></script>
</body>
</html>
```
</div></details>
<details><summary>main.js</summary><div>

```js:main.js
let localStream;
const constraints = {
    video: {
        width: 640,
        height: 480,
        facingMode: 'user' // デフォルトはインカメラ
    },
    audio: false
};

const getStream = (isUser) => {
    // 直前のストリームを停止する
    if (localStream !== undefined) {
        localStream.getVideoTracks().forEach((camera) => {
            camera.stop();
            console.log("camera stop");
        });
    }

    // 再読み込み
    constraints.video.facingMode = isUser ? 'user' : { exact: 'environment' }

    navigator.mediaDevices.getUserMedia(constraints)
        .then(stream => {
            localStream = stream;
            video.srcObject = stream;
            video.play();
        }).catch(e => {
            console.log(e);
        })
}

const video = document.getElementById('video');
getStream(true);

let cameraFacingIsUser = true;
let facingText = document.getElementById('camera-facing');
facingText.innerText = "インカメラ";
document.getElementById('btn').onclick = () => {
    if (cameraFacingIsUser) {
        facingText.innerText = "アウトカメラ";
        cameraFacingIsUser = false;
        getStream(false);
    } else {
        facingText.innerText = "インカメラ";
        cameraFacingIsUser = true;
        getStream(true);
    }
}
```
</div></details>

<details><summary>style.css</summary><div>

```css:style.css
.content_wrapper{
    position: relative;
}


.video-container{
    position: absolute; 
    width: 100vw;
    height: 100vh;
    top: 0px; 
    left: 0px;
}

#video {
    position: absolute; 
    background: #FFF; 
    width: 100%;
    height: 100%;
    top: 0px; 
    left: 0px;
    object-fit: cover;
    z-index: 900;
}

#btn {
    position: absolute;
    bottom: 10px;
    left: 10px;
    z-index: 1000;
    background-color: #eee;
    border: 1px solid #000;
    padding: 3px 5px;
    border-radius: 7px;
}

#camera-facing{
    position: absolute;
    bottom: 40px;
    left: 10px;
    z-index: 1001;
}
```
</div></details>