メディア デバイス スタートガイド

WebRTC 標準では、ウェブ向けの開発時に、コンピュータやスマートフォンに接続されているカメラとマイクにアクセスするための API を提供しています。これらのデバイスは一般に「メディア デバイス」と呼ばれます。JavaScript では、MediaDevices インターフェースを実装する navigator.mediaDevices オブジェクトを通じて、これらのデバイスにアクセスできます。このオブジェクトから、接続されているすべてのデバイスを列挙し、デバイスの変更をリッスンして(デバイスが接続または切断された場合)、デバイスを開いてメディア ストリームを取得します(下記参照)。

最も一般的な方法は、関数 getUserMedia() を使用する方法です。この関数は、一致するメディア デバイスの MediaStream に解決される Promise を返します。この関数は、要件を指定する単一の MediaStreamConstraints オブジェクトを受け取ります。たとえば、デフォルトのマイクとカメラを簡単に開くには、次のようにします。

Promise の使用

const constraints = {
    'video': true,
    'audio': true
}
navigator.mediaDevices.getUserMedia(constraints)
    .then(stream => {
        console.log('Got MediaStream:', stream);
    })
    .catch(error => {
        console.error('Error accessing media devices.', error);
    });

async/await の使用

const openMediaDevices = async (constraints) => {
    return await navigator.mediaDevices.getUserMedia(constraints);
}

try {
    const stream = openMediaDevices({'video':true,'audio':true});
    console.log('Got MediaStream:', stream);
} catch(error) {
    console.error('Error accessing media devices.', error);
}

getUserMedia() を呼び出すと、権限リクエストがトリガーされます。ユーザーが権限を許可すると、Promise は 1 つの動画と 1 つの音声トラックを含む MediaStream で解決されます。権限が拒否された場合は、PermissionDeniedError がスローされます。一致するデバイスが接続されていない場合、NotFoundError がスローされます。

MediaDevices インターフェースの完全な API リファレンスは、MDN ウェブ ドキュメントで確認できます。

メディア デバイスのクエリ

より複雑なアプリでは、接続されているすべてのカメラとマイクを確認し、適切なフィードバックをユーザーに提供する必要があります。そのためには、enumerateDevices() 関数を呼び出します。これは、既知の各メディア デバイスを記述する MediaDevicesInfo の配列に解決される Promise を返します。これを使用して、ユーザーが好みの UI を選択できる UI を表示できます。各 MediaDevicesInfo には kind というプロパティが含まれており、その値はメディア デバイスのタイプを示す audioinputaudiooutput、または videoinput です。

Promise の使用

function getConnectedDevices(type, callback) {
    navigator.mediaDevices.enumerateDevices()
        .then(devices => {
            const filtered = devices.filter(device => device.kind === type);
            callback(filtered);
        });
}

getConnectedDevices('videoinput', cameras => console.log('Cameras found', cameras));

async/await の使用

async function getConnectedDevices(type) {
    const devices = await navigator.mediaDevices.enumerateDevices();
    return devices.filter(device => device.kind === type)
}

const videoCameras = getConnectedDevices('videoinput');
console.log('Cameras found:', videoCameras);

デバイスの変更をリッスンする

ほとんどのパソコンは実行時にさまざまなデバイスを接続できます。USB で接続されたウェブカメラ、Bluetooth ヘッドセット、外部スピーカーのセットなどが可能です。これを適切にサポートするには、ウェブ アプリケーションでメディア デバイスの変更をリッスンする必要があります。そのためには、devicechange イベントのリスナーを navigator.mediaDevices に追加します。

// Updates the select element with the provided set of cameras
function updateCameraList(cameras) {
    const listElement = document.querySelector('select#availableCameras');
    listElement.innerHTML = '';
    cameras.map(camera => {
        const cameraOption = document.createElement('option');
        cameraOption.label = camera.label;
        cameraOption.value = camera.deviceId;
    }).forEach(cameraOption => listElement.add(cameraOption));
}

// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
    const devices = await navigator.mediaDevices.enumerateDevices();
    return devices.filter(device => device.kind === type)
}

// Get the initial set of cameras connected
const videoCameras = getConnectedDevices('videoinput');
updateCameraList(videoCameras);

// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', event => {
    const newCameraList = getConnectedDevices('video');
    updateCameraList(newCameraList);
});

メディアの制約

制約オブジェクト(MediaStreamConstraints インターフェースを実装する必要があります)をパラメータとして getUserMedia() に渡すと、特定の要件を満たすメディア デバイスを開くことができます。この要件は、大まかに定義(音声や動画)することも、非常に限定(最小カメラ解像度または正確なデバイス ID)にすることもできます。getUserMedia() API を使用するアプリでは、まず既存のデバイスを確認してから、deviceId 制約を使用してデバイスと完全に一致する制約を指定することをおすすめします。可能であれば、その制約に応じたデバイスも設定します。マイクでエコー キャンセルを有効にしたり、カメラから動画の特定の幅と高さまたは最小の幅と高さを設定したりできます。

async function getConnectedDevices(type) {
    const devices = await navigator.mediaDevices.enumerateDevices();
    return devices.filter(device => device.kind === type)
}

// Open camera with at least minWidth and minHeight capabilities
async function openCamera(cameraId, minWidth, minHeight) {
    const constraints = {
        'audio': {'echoCancellation': true},
        'video': {
            'deviceId': cameraId,
            'width': {'min': minWidth},
            'height': {'min': minHeight}
            }
        }

    return await navigator.mediaDevices.getUserMedia(constraints);
}

const cameras = getConnectedDevices('videoinput');
if (cameras && cameras.length > 0) {
    // Open first available video camera with a resolution of 1280x720 pixels
    const stream = openCamera(cameras[0].deviceId, 1280, 720);
}

MediaStreamConstraints インターフェースの完全なドキュメントについては、MDN ウェブ ドキュメントをご覧ください。

ローカルでの再生

メディア デバイスを開いて MediaStream を利用できるようにしたら、それを動画要素または音声要素に割り当てて、ストリーミングをローカルで再生します。

async function playVideoFromCamera() {
    try {
        const constraints = {'video': true, 'audio': true};
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        const videoElement = document.querySelector('video#localVideo');
        videoElement.srcObject = stream;
    } catch(error) {
        console.error('Error opening video camera.', error);
    }
}

getUserMedia() で使用される一般的な動画要素に必要な HTML には通常、autoplay 属性と playsinline 属性があります。autoplay 属性を使用すると、要素に割り当てられた新しいストリームが自動的に再生されます。playsinline 属性を使用すると、特定のモバイル ブラウザで動画を全画面表示だけでなく、インライン再生することもできます。ユーザーが一時停止できない場合を除き、ライブ ストリームには controls="false" を使用することをおすすめします。

<html>
<head><title>Local video playback</title></head>
<body>
    <video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>