16
15

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-10-18

image.png

はじめに

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

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

カメラの利用

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

index.html
<video id="video" autoplay muted playsinline></video>
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)
})

スマホで表示するとインカメラが利用されます。

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

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

インカメラの場合

script.js
 navigator.mediaDevices.getUserMedia({
     video:{width:300 height:300
+           facingMode: "user"},
     audio:false,
}

アウトカメラの場合

script.js
 navigator.mediaDevices.getUserMedia({
     video:{width:300 height:300
+           facingMode: { exact: "environment" }},
     audio:false,
}

切り替えボタンを作る

index.html
<div>
    <button id="btn">カメラ切り替え</button>
    <span id="camera-facing"></span>
</div>
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
押したら文言が切り替わります。
この切り替え部分で、faceMogdingも書き替えるようにします。

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' }を指定しないといけないのですが、
インカメラ時の指定にひきずられてうっかり文字列として

constraints.video.facingMode = "{ exact: 'environment' }";

と指定してしまっていてアウトカメラが利用できずに???となっていました。
初歩的なミスではありますが、お気を付けください!

(2)PCの場合

インカメラ、アウトカメラの指定はスマートフォンデバイスのように元々固有のカメラセンサを2つ持っているもの向けのようです。
私の環境では、ノートPCについているインカメラは’user’で
別途USBでつないでいたWebカメラは{ exact: 'environment' }ではありませんでした。
PC向けにカメラを切り替えるコードを用意する場合は、

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:

おまけ

作ったコードです

index.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>
main.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);
    }
}
style.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;
}
16
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15