はじめに
音声 / 動画のキャプチャは、長い間ウェブ開発の「聖杯」でした。 長年にわたり、この処理を行うにはブラウザ プラグイン(Flash や Silverlight)に依存する必要がありました。さあ、始めましょう。
そこで HTML5 の出番です。あまり知られていないかもしれませんが、HTML5 の台頭により、デバイス ハードウェアへのアクセスが急増しています。位置情報(GPS)、Orientation API(加速度計)、WebGL(GPU)、Web Audio API(オーディオ ハードウェア)がその好例です。これらの機能は非常に強力で、システムの基盤となるハードウェア機能の上に位置する高レベルの JavaScript API を公開します。
このチュートリアルでは、ウェブアプリがユーザーのカメラとマイクにアクセスできるようにする新しい API である GetUserMedia について説明します。
getUserMedia() への道
getUserMedia() API が誕生した経緯は、興味深いものです。
「Media Capture API」のいくつかのバリエーションが、ここ数年で進化してきました。多くの人がウェブ上でネイティブ デバイスにアクセスできるようにする必要性を認識していましたが、その結果、誰もが新しい仕様をまとめることになりました。状況が非常に複雑になったため、W3C は最終的にワーキング グループを結成することを決定しました。唯一の目的は、この狂気を理解しよう!Device APIs Policy(DAP)ワーキング グループは、多数の提案を統合して標準化する任務を負っています。
2011 年に何が起こったかを要約してみます。
ラウンド 1: HTML Media Capture
HTML Media Capture は、ウェブでのメディア キャプチャを標準化するための DAP の最初の取り組みでした。これは、<input type="file"> をオーバーロードし、accept パラメータに新しい値を追加することで機能します。
ウェブカメラでユーザーが自分のスナップショットを撮影できるようにするには、capture=camera を使用します。
<input type="file" accept="image/*;capture=camera">
動画や音声を録画、録音する場合も同様です。
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
便利ですよね。特に気に入っているのは、ファイル入力を再利用している点です。意味的には、これは非常に理にかなっています。この「API」に欠けているのは、リアルタイム エフェクト(ライブ ウェブカメラ データを <canvas> にレンダリングして WebGL フィルタを適用するなど)を実行する機能です。HTML Media Capture では、メディア ファイルの録画やスナップショットの撮影のみが可能です。
サポート:
- Android 3.0 ブラウザ - 最初の実装の 1 つ。実際の動作を確認するには、こちらの動画をご覧ください。
- Chrome for Android(0.16)
- Firefox Mobile 10.0
- iOS6 Safari、Chrome(部分的にサポート)
ラウンド 2: デバイス要素
多くの人が HTML Media Capture は制限が多すぎると考えたため、あらゆるタイプの(将来の)デバイスをサポートする新しい仕様が登場しました。当然のことながら、この設計では新しい要素である <device> 要素が必要となり、これが getUserMedia() の前身となりました。
Opera は、<device> 要素に基づく動画キャプチャの初期実装を作成した最初のブラウザの 1 つです。その後すぐに(正確には同日)、WhatWG は <device> タグを廃止し、別の新進気鋭の JavaScript API である navigator.getUserMedia() を採用することを決定しました。1 週間後、Opera は更新された getUserMedia() 仕様をサポートする新しいビルドをリリースしました。その年の後半、Microsoft も新しい仕様をサポートする Lab for IE9 をリリースしました。
<device> は次のようになります。
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
サポート:
残念ながら、リリースされたブラウザに <device> が含まれていたことはありません。心配する API が 1 つ減ったということですね。<device> には 2 つの優れた点がありました。1 つはセマンティックであること、もう 1 つはオーディオ / 動画デバイス以外にも簡単に拡張できることです。
一息つきましょう。この分野は変化が速い。
ラウンド 3: WebRTC
<device> 要素は最終的に廃止されました。
適切なキャプチャ API を見つけるペースは、より大規模な WebRTC(ウェブ リアルタイム通信)の取り組みのおかげで加速しました。この仕様は、W3C WebRTC ワーキング グループによって管理されています。Google、Opera、Mozilla、その他数社が実装しています。
getUserMedia() は、その一連の API へのゲートウェイであるため、WebRTC に関連しています。ユーザーのローカル カメラ / マイク ストリームにアクセスする手段を提供します。
サポート:
getUserMedia() は、Chrome 21、Opera 18、Firefox 17 以降でサポートされています。
スタートガイド
navigator.mediaDevices.getUserMedia() を使用すると、プラグインなしでウェブカメラとマイクの入力にアクセスできます。カメラへのアクセスは、インストールではなく、呼び出しで行えるようになりました。ブラウザに直接組み込まれています。ワクワクしませんか。
特徴検出
機能検出は、navigator.mediaDevices.getUserMedia の存在を簡単に確認するものです。
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
入力デバイスへのアクセス権を取得する
ウェブカメラまたはマイクを使用するには、権限をリクエストする必要があります。
navigator.mediaDevices.getUserMedia() の最初のパラメータは、アクセスするメディアの種類ごとに詳細と要件を指定するオブジェクトです。たとえば、ウェブカメラにアクセスする場合は、最初のパラメータを {video: true} にする必要があります。マイクとカメラの両方を使用するには、{video: true, audio: true} を渡します。
<video autoplay></video>
<script>
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((localMediaStream) => {
const video = document.querySelector("video");
video.srcObject = localMediaStream;
})
.catch((error) => {
console.log("Rejected!", error);
});
</script>
承知しました。何が起きているのでしょうか。メディア キャプチャは、新しい HTML5 API が連携する好例です。他の HTML5 要素である <audio> や <video> と連携して動作します。
<video> 要素に src 属性を設定したり、<source> 要素を含めたりしていないことに注意してください。動画にメディア ファイルの URL を渡す代わりに、ウェブカメラを表す LocalMediaStream オブジェクトに srcObject を設定します。
また、<video> に autoplay を指示しています。そうしないと、最初のフレームでフリーズしてしまいます。controls を追加しても、想定どおりに動作します。
メディア制約(解像度、高さ、幅)の設定
getUserMedia() の最初のパラメータを使用して、返されるメディア ストリームに関する要件(または制約)をさらに指定することもできます。たとえば、動画への基本的なアクセス({video: true} など)をリクエストするだけでなく、ストリームが HD であることを追加でリクエストできます。
const hdConstraints = {
video: { width: { exact: 1280} , height: { exact: 720 } },
};
const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
const vgaConstraints = {
video: { width: { exact: 640} , height: { exact: 360 } },
};
const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
その他の構成については、Constraints API をご覧ください。
メディアソースを選択する
MediaDevices インターフェースの enumerateDevices() メソッドは、マイク、カメラ、ヘッドセットなど、利用可能なメディア入出力デバイスのリストをリクエストします。返された Promise は、デバイスを記述する MediaDeviceInfo オブジェクトの配列で解決されます。
この例では、最後に見つかったマイクとカメラがメディア ストリーム ソースとして選択されます。
if (!navigator.mediaDevices?.enumerateDevices) {
console.log("enumerateDevices() not supported.");
} else {
// List cameras and microphones.
navigator.mediaDevices
.enumerateDevices()
.then((devices) => {
let audioSource = null;
let videoSource = null;
devices.forEach((device) => {
if (device.kind === "audioinput") {
audioSource = device.deviceId;
} else if (device.kind === "videoinput") {
videoSource = device.deviceId;
}
});
sourceSelected(audioSource, videoSource);
})
.catch((err) => {
console.error(`${err.name}: ${err.message}`);
});
}
async function sourceSelected(audioSource, videoSource) {
const constraints = {
audio: { deviceId: audioSource },
video: { deviceId: videoSource },
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
}
ユーザーがメディアソースを選択できるようにする方法については、Sam Dutton 氏の優れたデモをご覧ください。
セキュリティ
ブラウザは、navigator.mediaDevices.getUserMedia() を呼び出すと権限ダイアログを表示し、ユーザーにカメラやマイクへのアクセスを許可または拒否するオプションを提供します。たとえば、Chrome の権限ダイアログは次のようになります。
代替の提供
navigator.mediaDevices.getUserMedia() をサポートしていないユーザーの場合、API がサポートされていない場合や、何らかの理由で呼び出しが失敗した場合に、既存の動画ファイルにフォールバックするという方法があります。
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}