JavaScript | Web API:グラフィック・メディア - MediaDevices(カメラ・マイク)

JavaScript JavaScript
スポンサーリンク

MediaDevices は「ブラウザからカメラ・マイクを借りるための窓口」

スマホや PC には、カメラやマイクが付いています。
普段はカメラアプリやビデオ通話アプリがそれを使っていますが、
実は Web ページからも JavaScript 経由で使える ようになっています。

その入口になるのが navigator.mediaDevices
そして一番よく使うのが getUserMedia() です。

「カメラ映像をページに映したい」
「マイクの音を録音したい」
「ビデオ通話っぽいものを作りたい」

そういうときに必ず通るのが、この MediaDevices まわりの API です。


getUserMedia の基本イメージをつかむ

「こういうデバイスが欲しい」とブラウザにお願いする

MediaDevices の中心になるのが navigator.mediaDevices.getUserMedia() です。

ざっくり言うと、

「カメラが欲しいです」
「マイクが欲しいです」
「両方欲しいです」

とブラウザにお願いして、
OK が出たら「メディアストリーム」という形で受け取る、という流れです。

コードの形はこうなります。

const stream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
});
JavaScript

ここで渡しているオブジェクトが「希望条件」です。
video: true ならカメラ、audio: true ならマイクを要求します。

このとき、ブラウザは必ずユーザーに確認します。
「このサイトにカメラ/マイクの使用を許可しますか?」
というあのダイアログです。

ユーザーが許可したら、
getUserMedia は「メディアストリーム」を返します。
拒否されたり、デバイスがなかったりするとエラーになります。

まずはカメラ映像を <video> に映してみる

一番分かりやすい例からいきます。

HTML:

<video id="camera" autoplay playsinline style="width:300px; border:1px solid #ccc;"></video>
<div id="status"></div>
<button id="startBtn">カメラ開始</button>

JavaScript:

const videoEl = document.querySelector("#camera");
const statusEl = document.querySelector("#status");
const startBtn = document.querySelector("#startBtn");

startBtn.addEventListener("click", async () => {
  if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
    statusEl.textContent = "このブラウザはカメラに対応していません";
    return;
  }

  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: false
    });

    videoEl.srcObject = stream;
    statusEl.textContent = "カメラを表示中";
  } catch (err) {
    statusEl.textContent = "カメラを利用できません: " + err.name;
  }
});
JavaScript

ここで重要なポイントを整理します。

navigator.mediaDevices.getUserMedia が存在するかを最初にチェックしていること。
getUserMedia{ video: true, audio: false } を渡して「カメラだけ」を要求していること。
返ってきた stream<video> 要素の srcObject にそのまま代入していること。

特に srcObject がポイントです。
通常の動画は src="movie.mp4" のように URL を指定しますが、
カメラ映像は「生のストリーム」なので、
video.srcObject = stream という形で直接つなぎます。


マイクだけを使ってみる(録音の入口)

マイクのストリームを取得する

マイクだけを使いたい場合は、
audio: true だけを指定します。

const stream = await navigator.mediaDevices.getUserMedia({
  video: false,
  audio: true
});
JavaScript

このストリームは、そのまま再生するよりも、
録音や音声処理に使われることが多いです。

録音の代表的な方法は MediaRecorder を使うやり方です。

const recorder = new MediaRecorder(stream);
const chunks = [];

recorder.addEventListener("dataavailable", (e) => {
  chunks.push(e.data);
});

recorder.addEventListener("stop", () => {
  const blob = new Blob(chunks, { type: "audio/webm" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "recorded.webm";
  a.click();
});

recorder.start();

// 3 秒後に録音停止
setTimeout(() => {
  recorder.stop();
}, 3000);
JavaScript

ここでは少し踏み込んでいますが、
「マイクのストリームを録音して、ファイルとして保存する」
という流れのイメージがつかめれば十分です。


カメラ映像を Canvas にキャプチャする

「今のフレームを画像として切り出す」

カメラ映像をただ表示するだけでなく、
「今の瞬間を写真として保存したい」ということもよくあります。

そのときに使うのが <canvas> との組み合わせです。

HTML:

<video id="camera" autoplay playsinline style="width:300px; border:1px solid #ccc;"></video>
<canvas id="snapshot" width="300" height="200" style="border:1px solid #ccc;"></canvas>
<button id="captureBtn">撮影</button>

JavaScript:

const videoEl = document.querySelector("#camera");
const canvasEl = document.querySelector("#snapshot");
const ctx = canvasEl.getContext("2d");
const captureBtn = document.querySelector("#captureBtn");

async function startCamera() {
  const stream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: false
  });
  videoEl.srcObject = stream;
}

captureBtn.addEventListener("click", () => {
  ctx.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height);
});

startCamera();
JavaScript

ここでの流れはこうです。

カメラストリームを <video> に表示する
ボタンが押されたタイミングで、drawImage(video, ...) で Canvas にコピーする

drawImage は画像だけでなく <video> も描けるので、
「今のフレームをそのままキャンバスに貼る」ことができます。

このパターンを覚えておくと、
簡単な「Web カメラ撮影アプリ」がすぐ作れます。


デバイス選択(どのカメラ・どのマイクを使うか)

enumerateDevices で一覧を取得する

ノート PC やスマホには、
フロントカメラとバックカメラ、
内蔵マイクと外付けマイクなど、複数のデバイスがあることがあります。

navigator.mediaDevices.enumerateDevices() を使うと、
利用可能なデバイスの一覧を取得できます。

const devices = await navigator.mediaDevices.enumerateDevices();

devices.forEach((device) => {
  console.log(device.kind, device.label, device.deviceId);
});
JavaScript

kind"videoinput"(カメラ)や "audioinput"(マイク)など、
label はデバイス名(許可後でないと空のことも多い)、
deviceId はそのデバイスを指定するための ID です。

特定のカメラ・マイクを指定して getUserMedia する

deviceId を使うと、
「このカメラを使いたい」と指定できます。

const constraints = {
  video: {
    deviceId: { exact: "ここに使いたいカメラの deviceId" }
  },
  audio: false
};

const stream = await navigator.mediaDevices.getUserMedia(constraints);
JavaScript

ここでのポイントは、
video: true の代わりにオブジェクトを渡していることです。

widthheightfacingMode(フロント/バック)なども
同じように指定できます。

初心者のうちは、まず video: true から始めて、
慣れてきたら「どのカメラを使うか」を意識してみると良いです。


セキュリティと UX の大事なポイント

必ずユーザーの許可が必要

カメラ・マイクはプライバシーに直結するデバイスです。
そのため、ブラウザは必ずユーザーに許可を求めます。

コード側から「勝手にオンにする」ことはできません。
また、一度拒否された場合は、
ブラウザの設定を変えない限り再度使えないこともあります。

なので、UI としては

「これからカメラを使います」
「ボタンを押すとブラウザの許可ダイアログが出ます」

といった説明を、事前にきちんと書いておくのが親切です。

HTTPS(安全なコンテキスト)が前提

getUserMedia は、基本的に HTTPS でないと動きません。
http://localhost のようなローカル開発環境は例外的に許可されますが、
本番環境では必ず https:// で提供する前提になります。

もし getUserMedia がエラーになる場合、

非 HTTPS で提供している
ブラウザが古くて対応していない
ユーザーが拒否した

といった可能性を疑う必要があります。

ストリームを止めることも忘れない

カメラやマイクを使い終わったら、
ストリームを止めてあげるのがマナーです。

function stopStream(stream) {
  stream.getTracks().forEach((track) => {
    track.stop();
  });
}
JavaScript

getUserMedia で受け取った stream に対してこれを呼ぶと、
カメラやマイクがオフになります。

「ページを閉じたら自動で止まるからいいや」ではなく、
自分のコードで明示的に止める癖をつけておくと、
より丁寧な実装になります。


初心者として「MediaDevices(カメラ・マイク)」で掴んでほしいこと

MediaDevices の本質は、
「ブラウザからカメラ・マイクを“安全に”借りる仕組み」 です。

そのうえで、まず次の感覚をしっかり押さえてください。

navigator.mediaDevices.getUserMedia{ video: true, audio: false } などの条件を渡してストリームをもらう。
返ってきたストリームは <video>.srcObject に代入すると、そのまま映像として表示できる。
カメラ映像は drawImage(video, ...) で Canvas にキャプチャできる。
ユーザーの許可と HTTPS が前提であり、拒否やエラーは「普通に起こるもの」として扱う。

おすすめの練習は、次の 2 ステップです。

カメラ映像を <video> に映すだけのシンプルなページを作る。
「撮影」ボタンを押したら Canvas に静止画を描く機能を足してみる。

ここまでできると、
MediaDevices はもう「怖い低レベル API」ではなく、
「現実世界のカメラ・マイクを、Web アプリの中に連れてくるための素直な窓口」
として感じられるようになっていきます。

タイトルとURLをコピーしました