Erste Schritte mit Mediengeräten

Bei der Entwicklung für das Web bietet der WebRTC-Standard APIs für den Zugriff auf Kameras und Mikrofone, die mit dem Computer oder Smartphone verbunden sind. Diese Geräte werden allgemein als Mediengeräte bezeichnet und sind mit JavaScript über das Objekt navigator.mediaDevices zugänglich, das die MediaDevices-Schnittstelle implementiert. Über dieses Objekt können wir alle verbundenen Geräte auflisten, auf Geräteänderungen warten (wenn ein Gerät verbunden oder getrennt wird) und ein Gerät öffnen, um einen Media Stream abzurufen (siehe unten).

Am häufigsten wird dies über die Funktion getUserMedia() verwendet. Sie gibt ein Versprechen zurück, das für die übereinstimmenden Mediengeräte in ein MediaStream aufgelöst wird. Diese Funktion verwendet ein einzelnes MediaStreamConstraints-Objekt, das unsere Anforderungen angibt. Um beispielsweise einfach das Standardmikrofon und die Standardkamera zu öffnen, würden wir wie folgt vorgehen.

Promise-Objekte verwenden

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“ verwenden

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);
}

Der Aufruf von getUserMedia() löst eine Berechtigungsanfrage aus. Wenn der Nutzer die Berechtigung annimmt, wird das Versprechen mit einem MediaStream aufgelöst, das ein Video und einen Audiotrack enthält. Wenn die Berechtigung verweigert wird, wird ein PermissionDeniedError ausgelöst. Falls keine passenden Geräte verbunden sind, wird ein NotFoundError ausgegeben.

Die vollständige API-Referenz für die MediaDevices-Schnittstelle findest du unter MDN-Webdokumente.

Mediengeräte abfragen

Bei einer komplexeren Anwendung möchten wir höchstwahrscheinlich alle verbundenen Kameras und Mikrofone überprüfen und dem Nutzer das entsprechende Feedback geben. Dazu kann die Funktion enumerateDevices() aufgerufen werden. Dadurch wird ein Versprechen zurückgegeben, das in ein Array von MediaDevicesInfo aufgelöst wird, die alle bekannten Mediengeräte beschreiben. Damit können wir dem Nutzer eine UI präsentieren, auf der er seine bevorzugte auswählen kann. Jeder MediaDevicesInfo enthält ein Attribut namens kind mit dem Wert audioinput, audiooutput oder videoinput, das angibt, um welche Art von Mediengerät es sich handelt.

Promise-Objekte verwenden

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“ verwenden

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);

Warten auf Geräteänderungen

Die meisten Computer unterstützen das Anschließen verschiedener Geräte während der Laufzeit. Es kann sich dabei um eine über USB verbundene Webcam, ein Bluetooth-Headset oder eine Reihe externer Lautsprecher handeln. Damit dies ordnungsgemäß unterstützt wird, sollte eine Webanwendung auf Änderungen der Mediengeräte warten. Fügen Sie dazu navigator.mediaDevices einen Listener für das Ereignis devicechange hinzu.

// 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);
});

Medieneinschränkungen

Mit dem Einschränkungsobjekt, das die MediaStreamConstraints-Schnittstelle implementieren muss und als Parameter an getUserMedia() übergeben wird, kann ein Mediengerät geöffnet werden, das eine bestimmte Anforderung erfüllt. Diese Anforderung kann sehr locker definiert (Audio und/oder Video) oder sehr spezifisch (minimale Kameraauflösung oder genaue Geräte-ID) sein. Anwendungen, die die getUserMedia() API verwenden, sollten zuerst die vorhandenen Geräte prüfen und dann mithilfe der Einschränkung deviceId eine Einschränkung angeben, die genau mit dem Gerät übereinstimmt. Außerdem werden Geräte, wenn möglich, gemäß den Einschränkungen konfiguriert. Wir können die Echounterdrückung auf Mikrofonen aktivieren oder eine bestimmte oder Mindestbreite und -höhe für das Video von der Kamera festlegen.

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);
}

Die vollständige Dokumentation zur MediaStreamConstraints-Schnittstelle finden Sie in den MDN-Webdokumenten.

Lokale Wiedergabe

Sobald ein Mediengerät geöffnet wurde und ein MediaStream verfügbar ist, können wir es einem Video- oder Audioelement zuweisen, um den Stream lokal abzuspielen.

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);
    }
}

Der HTML-Code, der für ein typisches Videoelement erforderlich ist, das mit getUserMedia() verwendet wird, hat in der Regel die Attribute autoplay und playsinline. Das Attribut autoplay sorgt dafür, dass neue Streams, die dem Element zugewiesen sind, automatisch wiedergegeben werden. Mit dem Attribut playsinline können Videos in bestimmten mobilen Browsern inline und nicht nur im Vollbildmodus abgespielt werden. Außerdem wird die Verwendung von controls="false" für Livestreams empfohlen, es sei denn, der Nutzer sollte die Möglichkeit haben, sie zu pausieren.

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