Einführung
Die Audio-/Videoaufnahme ist seit Langem der „heilige Gral“ der Webentwicklung. Viele Jahre lang mussten wir uns auf Browser-Plug-ins (Flash oder Silverlight) verlassen, um die Aufgabe zu erledigen. Komm schon!
HTML5 ist die Lösung. Es ist vielleicht nicht offensichtlich, aber der Aufstieg von HTML5 hat zu einem enormen Anstieg des Zugriffs auf Gerätehardware geführt. Geolocation (GPS), die Orientation API (Beschleunigungsmesser), WebGL (GPU) und die Web Audio API (Audiohardware) sind perfekte Beispiele. Diese Funktionen sind unglaublich leistungsstark und stellen JavaScript-APIs auf hoher Ebene bereit, die auf den zugrunde liegenden Hardwarefunktionen des Systems aufsetzen.
In dieser Anleitung wird eine neue API vorgestellt, GetUserMedia, mit der Webanwendungen auf die Kamera und das Mikrofon eines Nutzers zugreifen können.
Der Weg zu getUserMedia()
Wenn Sie die Geschichte der getUserMedia() API nicht kennen, ist es interessant zu erfahren, wie wir zu ihr gekommen sind.
In den letzten Jahren haben sich mehrere Varianten von „Media Capture APIs“ entwickelt. Viele erkannten die Notwendigkeit, im Web auf native Geräte zugreifen zu können, aber das führte dazu, dass jeder eine neue Spezifikation erstellte. Die Situation wurde so unübersichtlich, dass das W3C schließlich beschloss, eine Arbeitsgruppe zu bilden. Ihr einziger Zweck? Bring Ordnung ins Chaos! Die Device APIs Policy (DAP) Working Group wurde beauftragt, die Vielzahl von Vorschlägen zu konsolidieren und zu standardisieren.
Ich versuche, die Ereignisse von 2011 zusammenzufassen…
Runde 1: HTML Media Capture
HTML Media Capture war der erste Versuch der DAP, die Medienerfassung im Web zu standardisieren. Dazu wird <input type="file"> überladen und es werden neue Werte für den Parameter accept hinzugefügt.
Wenn Sie Nutzern die Möglichkeit geben möchten, mit der Webcam ein Foto von sich selbst aufzunehmen, ist das mit capture=camera möglich:
<input type="file" accept="image/*;capture=camera">
Das Aufnehmen von Videos oder Audioinhalten funktioniert ähnlich:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
Ziemlich cool, oder? Besonders gut gefällt mir, dass eine Dateieingabe wiederverwendet wird. Semantisch ergibt das durchaus Sinn. Diese spezielle „API“ ist nicht in der Lage, Echtzeiteffekte zu erzeugen (z.B. Live-Webcam-Daten in einem <canvas>-Element zu rendern und WebGL-Filter anzuwenden).
Mit HTML Media Capture können Sie nur eine Mediendatei aufnehmen oder einen Snapshot erstellen.
Support:
- Android 3.0-Browser – eine der ersten Implementierungen. In diesem Video kannst du dir die Funktion in Aktion ansehen.
- Chrome für Android (0.16)
- Firefox Mobile 10.0
- iOS6 Safari und Chrome (teilweise Unterstützung)
Runde 2: Gerätespezifische Elemente
Viele hielten HTML Media Capture für zu eingeschränkt, daher wurde eine neue Spezifikation entwickelt, die alle Arten von (zukünftigen) Geräten unterstützt. Es überrascht nicht, dass das Design ein neues Element erforderte, das <device>-Element, das zum Vorgänger von getUserMedia() wurde.
Opera war einer der ersten Browser, der erste Implementierungen der Videoaufnahme basierend auf dem <device>-Element erstellt hat. Kurz darauf (am selben Tag, um genau zu sein) beschloss die WhatWG, das <device>-Tag zugunsten einer anderen aufstrebenden Technologie zu verwerfen, diesmal einer JavaScript API namens navigator.getUserMedia(). Eine Woche später veröffentlichte Opera neue Builds, die die aktualisierte getUserMedia()-Spezifikation unterstützten. Später im selben Jahr zog Microsoft nach und veröffentlichte ein Lab für IE9, das die neue Spezifikation unterstützte.
So hätte <device> ausgesehen:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
Support:
Leider wurde <device> in keinem veröffentlichten Browser implementiert.
Eine API weniger, um die man sich kümmern muss. :) <device> hatte jedoch zwei große Vorteile: 1.) Es war semantisch und 2.) es war leicht erweiterbar, um mehr als nur Audio-/Videogeräte zu unterstützen.
Keine Sorge: Das geht so schnell!
Runde 3: WebRTC
Das <device>-Element ist schließlich ausgestorben.
Dank der größeren WebRTC-Initiative (Web Real Time Communications) ging die Suche nach einer geeigneten Capture API schneller voran. Diese Spezifikation wird von der W3C WebRTC Working Group verwaltet. Google, Opera, Mozilla und einige andere haben Implementierungen.
getUserMedia() ist mit WebRTC verwandt, da es das Gateway zu dieser Gruppe von APIs ist.
Sie bietet die Möglichkeit, auf den lokalen Kamera-/Mikrofonstream des Nutzers zuzugreifen.
Support:
getUserMedia() wird seit Chrome 21, Opera 18 und Firefox 17 unterstützt.
Erste Schritte
Mit navigator.mediaDevices.getUserMedia() können wir endlich ohne Plug-in auf Webcam- und Mikrofoneingaben zugreifen.
Der Zugriff auf die Kamera ist jetzt nur noch einen Anruf entfernt, keine Installation mehr. Sie ist direkt in den Browser integriert. Bist du schon gespannt?
Feature-Erkennung
Die Feature-Erkennung ist eine einfache Prüfung, ob navigator.mediaDevices.getUserMedia vorhanden ist:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
Zugriff auf ein Eingabegerät erhalten
Wenn du die Webcam oder das Mikrofon verwenden möchtest, müssen wir eine Berechtigung anfordern.
Der erste Parameter für navigator.mediaDevices.getUserMedia() ist ein Objekt, das die Details und Anforderungen für jeden Medientyp angibt, auf den Sie zugreifen möchten. Wenn Sie beispielsweise auf die Webcam zugreifen möchten, sollte der erste Parameter {video: true} sein. Wenn Sie sowohl das Mikrofon als auch die Kamera verwenden möchten, übergeben Sie {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>
Ok. Was ist hier also los? Die Medienerfassung ist ein perfektes Beispiel für das Zusammenspiel neuer HTML5-APIs. Es funktioniert in Verbindung mit unseren anderen HTML5-Elementen <audio> und <video>.
Beachten Sie, dass wir kein src-Attribut festlegen und keine <source>-Elemente im <video>-Element einfügen. Anstatt dem Video eine URL zu einer Mediendatei zuzuführen, legen wir für srcObject das LocalMediaStream-Objekt fest, das die Webcam darstellt.
Außerdem weise ich das <video> an, autoplay, da es sonst beim ersten Frame einfrieren würde. Das Hinzufügen von controls funktioniert ebenfalls wie erwartet.
Medieneinschränkungen festlegen (Auflösung, Höhe, Breite)
Der erste Parameter für getUserMedia() kann auch verwendet werden, um weitere Anforderungen (oder Einschränkungen) für den zurückgegebenen Mediastream anzugeben. Anstatt beispielsweise nur anzugeben, dass Sie einfachen Zugriff auf Videos wünschen (z.B. {video: true}), können Sie zusätzlich festlegen, dass der Stream in HD sein muss:
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);
Weitere Konfigurationen finden Sie in der Constraints API.
Medienquelle auswählen
Die Methode enumerateDevices() der Schnittstelle MediaDevices fordert eine Liste der verfügbaren Medieneingabe- und -ausgabegeräte an, z. B. Mikrofone, Kameras, Headsets usw. Das zurückgegebene Promise wird mit einem Array von MediaDeviceInfo-Objekten aufgelöst, die die Geräte beschreiben.
In diesem Beispiel werden das zuletzt gefundene Mikrofon und die zuletzt gefundene Kamera als Quelle für den Mediastream ausgewählt:
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);
}
In der Demo von Sam Dutton wird gezeigt, wie Nutzer die Medienquelle auswählen können.
Sicherheit
Browser zeigen beim Aufrufen von navigator.mediaDevices.getUserMedia() ein Berechtigungsdialogfeld an, in dem Nutzer den Zugriff auf ihre Kamera oder ihr Mikrofon gewähren oder verweigern können. Hier ist beispielsweise das Berechtigungsdialogfeld von Chrome:
Fallback bereitstellen
Für Nutzer, die navigator.mediaDevices.getUserMedia() nicht unterstützen, besteht die Möglichkeit, auf eine vorhandene Videodatei zurückzugreifen, wenn die API nicht unterstützt wird und/oder der Aufruf aus irgendeinem Grund fehlschlägt:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}