JavaScriptのgetUserMedia()を使用してフロントカメラとリアカメラにアクセスする方法
序章
HTML5では、 MediaDevicesAPIなどのデバイスハードウェアにアクセスできるAPIが導入されました。 このAPIは、オーディオやビデオなどのメディア入力デバイスへのアクセスを提供します。
このAPIの助けを借りて、開発者はオーディオおよびビデオデバイスにアクセスして、ブラウザーでライブビデオフィードをストリーミングおよび表示できます。 このチュートリアルでは、ユーザーのデバイスからビデオフィードにアクセスし、getUserMediaメソッドを使用してブラウザーに表示します。
getUserMedia
APIは、メディア入力デバイスを使用してMediaStreamを生成します。 このMediaStreamには、オーディオかビデオかを問わず、要求されたメディアタイプが含まれています。 APIから返されたストリームを使用して、ビデオフィードをブラウザーに表示できます。これは、ブラウザーでのリアルタイム通信に役立ちます。
MediaStream Recording API と併用すると、ブラウザでキャプチャしたメディアデータを記録および保存できます。 このAPIは、新しく導入された他のAPIと同様に、安全なオリジンでのみ機能しますが、localhost
およびファイルURLでも機能します。
前提条件
- JavaScriptの基本的な知識。 JavaScriptを初めて使用する場合は、JavaScriptでコーディングする方法シリーズを確認してください。
このチュートリアルでは、最初に概念を説明し、Codepenを使用して例を示します。 最後のステップでは、ブラウザ用に機能するビデオフィードを作成します。
ステップ1—デバイスサポートの確認
まず、ユーザーのブラウザがmediaDevices
APIをサポートしているかどうかを確認する方法を説明します。 このAPIはナビゲーターインターフェース内に存在し、ユーザーエージェントの現在の状態とIDを含みます。 チェックは、Codepenに貼り付けることができる次のコードを使用して実行されます。
if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) { console.log("Let's get this party started") }
まず、mediaDevices
APIがnavigator
内に存在するかどうかを確認し、次にgetUserMedia
APIがmediaDevices
内で使用可能かどうかを確認します。 これがtrue
を返す場合は、開始できます。
ステップ2—ユーザー権限をリクエストする
ブラウザによるgetUserMedia
のサポートを確認した後、ユーザーエージェントでメディア入力デバイスを使用するための許可を要求する必要があります。 通常、ユーザーがアクセス許可を付与すると、Promise
が返され、メディアストリームに解決されます。 このPromise
は、これらのデバイスへのアクセスをブロックするユーザーによってアクセス許可が拒否された場合は返されません。
次の行をCodepenに貼り付けて、許可をリクエストします。
navigator.mediaDevices.getUserMedia({video: true})
getUserMedia
メソッドの引数として提供されるオブジェクトは、constraints
と呼ばれます。 これにより、アクセス許可を要求しているメディア入力デバイスが決まります。 たとえば、オブジェクトにaudio: true
が含まれている場合、ユーザーはオーディオ入力デバイスへのアクセスを許可するように求められます。
ステップ3—メディアの制約を理解する
このセクションでは、制約の一般的な概念について説明します。 constraints
オブジェクトは、 MediaStreamConstraints オブジェクトであり、要求するメディアのタイプと各メディアタイプの要件を指定します。 使用するストリームの解像度(front
、back
)など、constraints
オブジェクトを使用して、要求されたストリームの要件を指定できます。
リクエストの際は、audio
またはvideo
のいずれかを指定する必要があります。 要求されたメディアタイプがユーザーのブラウザで見つからない場合は、NotFoundError
が返されます。
1280 x 720
解像度のビデオストリームをリクエストする場合は、constraints
オブジェクトを次のように更新できます。
{ video: { width: 1280, height: 720, } }
このアップデートでは、ブラウザはストリームに指定された品質設定を一致させようとします。 ビデオデバイスがこの解像度を提供できない場合、ブラウザは他の利用可能な解像度を返します。
ブラウザが提供された解像度以上の解像度を返すようにするには、min
プロパティを使用する必要があります。 constraints
オブジェクトを更新してmin
プロパティを含める方法は次のとおりです。
{ video: { width: { min: 1280, }, height: { min: 720, } } }
これにより、返されるストリーム解像度が少なくとも1280 x 720
になることが保証されます。 この最小要件を満たせない場合、約束はOverconstrainedError
で拒否されます。
場合によっては、データの保存に懸念があり、設定された解像度を超えないようにストリームが必要になることがあります。 これは、ユーザーが限られたプランを使用している場合に便利です。 この機能を有効にするには、max
フィールドを含むように制約オブジェクトを更新します。
{ video: { width: { min: 1280, max: 1920, }, height: { min: 720, max: 1080 } } }
これらの設定により、ブラウザは、リターンストリームが1280 x 720
を下回ったり、1920 x 1080
を超えたりしないようにします。
使用できる他の用語には、exact
およびideal
が含まれます。 ideal
設定は通常、min
およびmax
プロパティとともに使用され、提供された理想値に最も近い可能な限り最良の設定を見つけます。
ideal
キーワードを使用するように制約を更新できます。
{ video: { width: { min: 1280, ideal: 1920, max: 2560, }, height: { min: 720, ideal: 1080, max: 1440 } } }
デバイスの前面または背面(モバイルの場合)カメラを使用するようにブラウザに指示するには、video
オブジェクトでfacingMode
プロパティを指定できます。
{ video: { width: { min: 1280, ideal: 1920, max: 2560, }, height: { min: 720, ideal: 1080, max: 1440 }, facingMode: 'user' } }
この設定では、すべてのデバイスで常に前面カメラが使用されます。 モバイルデバイスでバックカメラを利用するには、facingMode
プロパティをenvironment
に変更できます。
{ video: { ... facingMode: { exact: 'environment' } } }
ステップ4—enumerateDevices
メソッドを使用する
enumerateDevices
メソッドが呼び出されると、ユーザーのPCで使用可能なすべての入力メディアデバイスが返されます。
この方法では、オーディオまたはビデオコンテンツのストリーミングに使用する入力メディアデバイスのユーザーオプションを提供できます。 このメソッドは、各デバイスに関する情報を含むMediaDeviceInfo配列に解決されたPromise
を返します。
このメソッドを使用する方法の例を以下のスニペットに示します。
async function getDevices() { const devices = await navigator.mediaDevices.enumerateDevices(); }
各デバイスの応答例は次のようになります。
{ deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj", groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875", kind: "audiooutput", label: "", }
注:利用可能なストリームが利用可能でない限り、またはユーザーがデバイスアクセス許可を付与している場合、ラベルは返されません。
ステップ5—ブラウザにビデオストリームを表示する
メディアデバイスへのアクセスを要求して取得するプロセスを経て、必要な解像度を含めるように制約を構成し、ビデオを録画するために必要なカメラを選択しました。
これらのすべての手順を実行した後、少なくとも、構成された設定に基づいてストリームが配信されているかどうかを確認する必要があります。 これを確実にするために、<video>
要素を使用して、ブラウザーにビデオストリームを表示します。
前述のように、getUserMedia
メソッドは、ストリームに解決できるPromise
を返します。 返されたストリームは、createObjectURLメソッドを使用してオブジェクトURLに変換できます。 このURLはビデオソースとして設定されます。
利用可能なビデオデバイスのリストからユーザーが選択できる短いデモを作成します。 enumerateDevicesメソッドを使用します。
これはnavigator.mediaDevices
メソッドです。 マイクやカメラなどの利用可能なメディアデバイスが一覧表示されます。 使用可能なメディアデバイスの詳細を示すオブジェクトの配列に解決可能なPromise
を返します。
index.html
ファイルを作成し、以下のコードで内容を更新します。
index.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"> <link rel="stylesheet" href="style.css"> <title>Document</title> </head> <body> <div class="display-cover"> <video autoplay></video> <canvas class="d-none"></canvas> <div class="video-options"> <select name="" id="" class="custom-select"> <option value="">Select camera</option> </select> </div> <img class="screenshot-image d-none" alt=""> <div class="controls"> <button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button> <button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button> <button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button> </div> </div> <script src="https://unpkg.com/feather-icons"></script> <script src="script.js"></script> </body> </html>
上記のスニペットでは、必要な要素とビデオのいくつかのコントロールを設定しました。 現在のビデオフィードのスクリーンショットを撮るためのボタンも含まれています。
それでは、これらのコンポーネントを少しスタイルアップしましょう。
style.css
ファイルを作成し、次のスタイルをコピーします。 Bootstrap が含まれており、コンポーネントを動作させるために作成する必要のあるCSSの量を減らすことができます。
style.css
.screenshot-image { width: 150px; height: 90px; border-radius: 4px; border: 2px solid whitesmoke; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1); position: absolute; bottom: 5px; left: 10px; background: white; } .display-cover { display: flex; justify-content: center; align-items: center; width: 70%; margin: 5% auto; position: relative; } video { width: 100%; background: rgba(0, 0, 0, 0.2); } .video-options { position: absolute; left: 20px; top: 30px; } .controls { position: absolute; right: 20px; top: 20px; display: flex; } .controls > button { width: 45px; height: 45px; text-align: center; border-radius: 100%; margin: 0 6px; background: transparent; } .controls > button:hover svg { color: white !important; } @media (min-width: 300px) and (max-width: 400px) { .controls { flex-direction: column; } .controls button { margin: 5px 0 !important; } } .controls > button > svg { height: 20px; width: 18px; text-align: center; margin: 0 auto; padding: 0; } .controls button:nth-child(1) { border: 2px solid #D2002E; } .controls button:nth-child(1) svg { color: #D2002E; } .controls button:nth-child(2) { border: 2px solid #008496; } .controls button:nth-child(2) svg { color: #008496; } .controls button:nth-child(3) { border: 2px solid #00B541; } .controls button:nth-child(3) svg { color: #00B541; } .controls > button { width: 45px; height: 45px; text-align: center; border-radius: 100%; margin: 0 6px; background: transparent; } .controls > button:hover svg { color: white; }
次のステップは、デモに機能を追加することです。 enumerateDevices
メソッドを使用して、使用可能なビデオデバイスを取得し、それをselect要素内のオプションとして設定します。 script.js
というファイルを作成し、次のスニペットで更新します。
script.js
feather.replace(); const controls = document.querySelector('.controls'); const cameraOptions = document.querySelector('.video-options>select'); const video = document.querySelector('video'); const canvas = document.querySelector('canvas'); const screenshotImage = document.querySelector('img'); const buttons = [...controls.querySelectorAll('button')]; let streamStarted = false; const [play, pause, screenshot] = buttons; const constraints = { video: { width: { min: 1280, ideal: 1920, max: 2560, }, height: { min: 720, ideal: 1080, max: 1440 }, } }; const getCameraSelection = async () => { const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter(device => device.kind === 'videoinput'); const options = videoDevices.map(videoDevice => { return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`; }); cameraOptions.innerHTML = options.join(''); }; play.onclick = () => { if (streamStarted) { video.play(); play.classList.add('d-none'); pause.classList.remove('d-none'); return; } if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) { const updatedConstraints = { ...constraints, deviceId: { exact: cameraOptions.value } }; startStream(updatedConstraints); } }; const startStream = async (constraints) => { const stream = await navigator.mediaDevices.getUserMedia(constraints); handleStream(stream); }; const handleStream = (stream) => { video.srcObject = stream; play.classList.add('d-none'); pause.classList.remove('d-none'); screenshot.classList.remove('d-none'); streamStarted = true; }; getCameraSelection();
上記のスニペットでは、いくつかのことが起こっています。 それらを分解しましょう:
feather.replace()
:このメソッド呼び出しは、Web開発用のアイコンセットであるfemaleをインスタンス化します。constraints
変数は、ストリームの初期構成を保持します。 これは、ユーザーが選択したメディアデバイスを含むように拡張されます。getCameraSelection
:この関数はenumerateDevices
メソッドを呼び出します。 次に、解決されたPromise
からアレイをフィルタリングし、ビデオ入力デバイスを選択します。 フィルタリングされた結果から、<select>
要素の<option>
を作成します。getUserMedia
メソッドの呼び出しは、play
ボタンのonclick
リスナー内で行われます。 ここでは、ストリームを開始する前に、このメソッドがユーザーのブラウザでサポートされているかどうかを確認します。- 次に、
constraints
引数を取るstartStream
関数を呼び出します。 提供されたconstraints
を使用してgetUserMedia
メソッドを呼び出します。handleStream
は、解決されたPromise
からのストリームを使用して呼び出されます。 このメソッドは、返されるストリームをビデオ要素のsrcObject
に設定します。
次に、pause
、stop
のページのボタンコントロールにクリックリスナーを追加し、screenshots
を取得します。 また、<select>
要素にリスナーを追加して、選択したビデオデバイスのストリーム制約を更新します。
script.js
ファイルを次のコードで更新します。
script.js
... cameraOptions.onchange = () => { const updatedConstraints = { ...constraints, deviceId: { exact: cameraOptions.value } }; startStream(updatedConstraints); }; const pauseStream = () => { video.pause(); play.classList.remove('d-none'); pause.classList.add('d-none'); }; const doScreenshot = () => { canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext('2d').drawImage(video, 0, 0); screenshotImage.src = canvas.toDataURL('image/webp'); screenshotImage.classList.remove('d-none'); }; pause.onclick = pauseStream; screenshot.onclick = doScreenshot;
これで、ブラウザでindex.html
ファイルを開いたときに、再生ボタンをクリックするとストリームが開始されます。
これが完全なデモです:
https://codepen.io/chrisbeast/pen/ebYwpX
結論
このチュートリアルでは、getUserMedia
APIを紹介しました。 これは、Web上でメディアをキャプチャするプロセスを容易にするHTML5への興味深い追加です。
APIは、オーディオおよびビデオ入力デバイスへのアクセスを構成するために使用できるパラメーター(constraints
)を取ります。 また、アプリケーションに必要なビデオ解像度を指定するために使用することもできます。
デモをさらに拡張して、MediaStream Recording APIを使用して、撮影したスクリーンショットを保存したり、ビデオおよびオーディオデータを記録および保存したりするオプションをユーザーに提供できます。