Wprowadzenie
Przechwytywanie dźwięku i obrazu wideo było przez długi czas najważniejszym celem w rozwoju internetu. Przez wiele lat musieliśmy polegać na wtyczkach do przeglądarki (Flash lub Silverlight). No dalej!
HTML5 na ratunek. Może nie jest to oczywiste, ale rozwój HTML5 spowodował wzrost dostępu do sprzętu urządzenia. Geolokalizacja (GPS), interfejs Orientation API (akcelerometr), WebGL (GPU) i Web Audio API (sprzęt audio) to doskonałe przykłady. Te funkcje są niezwykle zaawansowane, ponieważ udostępniają interfejsy JavaScript API wysokiego poziomu, które wykorzystują podstawowe możliwości sprzętowe systemu.
W tym samouczku przedstawiamy nowy interfejs API GetUserMedia, który umożliwia aplikacjom internetowym dostęp do kamery i mikrofonu użytkownika.
Droga do getUserMedia()
Jeśli nie znasz historii tego interfejsu, to, jak doszliśmy do getUserMedia()
API, jest ciekawą opowieścią.
W ciągu ostatnich kilku lat powstało kilka wariantów interfejsów „Media Capture API”. Wiele osób dostrzegło potrzebę dostępu do urządzeń natywnych w internecie, ale – jak to zwykle bywa – każdy, kto tylko mógł, zaczął tworzyć własną specyfikację. Sytuacja stała się tak skomplikowana, że W3C postanowiło w końcu utworzyć grupę roboczą. Ich jedyny cel? Zrozumieć to szaleństwo! Grupa robocza ds. zasad dotyczących interfejsów API urządzeń (DAP) ma za zadanie skonsolidować i ujednolicić wiele propozycji.
Spróbuję podsumować, co się wydarzyło w 2011 roku…
Runda 1: przechwytywanie multimediów w HTML
HTML Media Capture to pierwsza próba DAP-u standaryzacji przechwytywania multimediów w internecie. Działa to na zasadzie przeciążania parametru <input type="file">
i dodawania nowych wartości do parametru accept
.
Jeśli chcesz umożliwić użytkownikom zrobienie sobie zdjęcia za pomocą kamery internetowej, możesz to zrobić za pomocą capture=camera
:
<input type="file" accept="image/*;capture=camera">
Nagrywanie filmu lub dźwięku jest podobne:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
Całkiem fajnie, prawda? Szczególnie podoba mi się to, że ponownie wykorzystuje pole wprowadzania pliku. Semantycznie ma to duży sens. Ten konkretny „interfejs API” nie ma możliwości stosowania efektów w czasie rzeczywistym (np. renderowania danych z kamery internetowej na <canvas>
i stosowania filtrów WebGL).
HTML Media Capture umożliwia tylko nagrywanie plików multimedialnych lub robienie zdjęć.
Pomoc:
- Przeglądarka Androida 3.0 – jedna z pierwszych implementacji. Aby zobaczyć, jak to działa, obejrzyj ten film.
- Chrome na Androida (0,16)
- Firefox Mobile 10.0
- Safari i Chrome na iOS6 (częściowa obsługa)
Runda 2: element urządzenia
Wielu użytkowników uważało, że HTML Media Capture ma zbyt duże ograniczenia, dlatego powstała nowa specyfikacja, która obsługuje dowolny typ (przyszłego) urządzenia. Nic dziwnego, że w projekcie pojawił się nowy element, <device>
, który stał się poprzednikiem getUserMedia()
.
Opera była jedną z pierwszych przeglądarek, które stworzyły pierwsze implementacje przechwytywania wideo na podstawie elementu <device>
. Niedługo potem (dokładnie tego samego dnia) grupa WhatWG zdecydowała się porzucić tag <device>
na rzecz innego, tym razem interfejsu JavaScript API o nazwie navigator.getUserMedia()
. Tydzień później Opera udostępniła nowe wersje, które obsługiwały zaktualizowaną specyfikację getUserMedia()
. W tym samym roku dołączył do niej Microsoft, który wydał Lab for IE9 obsługujący nową specyfikację.
Tak wyglądałby znak <device>
:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
Pomoc:
Niestety żadna wydana przeglądarka nie zawierała <device>
.
Mniej API, o które trzeba się martwić :) <device>
miało jednak 2 zalety: 1) było semantyczne i 2) można je było łatwo rozszerzyć, aby obsługiwało nie tylko urządzenia audio i wideo.
Weź oddech. To się szybko zmienia!
Runda 3. WebRTC
Element <device>
ostatecznie zniknął.
Tempo poszukiwań odpowiedniego interfejsu API do przechwytywania przyspieszyło dzięki większemu projektowi WebRTC (Web Real Time Communications). Specyfikacja ta jest nadzorowana przez grupę roboczą W3C WebRTC. Implementacje mają Google, Opera, Mozilla i kilka innych firm.
getUserMedia()
jest powiązany z WebRTC, ponieważ jest bramą do tego zestawu interfejsów API.
Umożliwia dostęp do lokalnego strumienia z kamery lub mikrofonu użytkownika.
Pomoc:
getUserMedia()
jest obsługiwany od wersji 21 Chrome, 18 Opery i 17 Firefoksa.
Pierwsze kroki
Dzięki navigator.mediaDevices.getUserMedia()
możemy wreszcie korzystać z danych wejściowych z kamery internetowej i mikrofonu bez wtyczki.
Dostęp do aparatu jest teraz możliwy po nawiązaniu połączenia, a nie po zainstalowaniu aplikacji. Jest ona wbudowana bezpośrednio w przeglądarkę. Brzmi dobrze?
Wykrywanie cech
Wykrywanie funkcji to proste sprawdzenie, czy istnieje navigator.mediaDevices.getUserMedia
:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
Uzyskiwanie dostępu do urządzenia wejściowego
Aby używać kamery internetowej lub mikrofonu, musimy poprosić o uprawnienia.
Pierwszy parametr funkcji navigator.mediaDevices.getUserMedia()
to obiekt określający szczegóły i wymagania dotyczące każdego typu multimediów, do których chcesz uzyskać dostęp. Jeśli na przykład chcesz uzyskać dostęp do kamery internetowej, pierwszym parametrem powinna być wartość {video: true}
. Aby używać zarówno mikrofonu, jak i kamery, przekaż wartość {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. Co się tutaj dzieje? Przechwytywanie multimediów to doskonały przykład współpracy nowych interfejsów API HTML5. Działa w połączeniu z innymi elementami HTML5, <audio>
i <video>
.
Zwróć uwagę, że nie ustawiamy atrybutu src
ani nie uwzględniamy elementów <source>
w elemencie <video>
. Zamiast podawać filmowi adres URL pliku multimedialnego, ustawiamy srcObject
na obiekt LocalMediaStream
reprezentujący kamerę internetową.
Mówię też <video>
, żeby autoplay
, bo inaczej zatrzymałby się na pierwszej klatce. Dodanie controls
również działa zgodnie z oczekiwaniami.
Ustawianie ograniczeń dotyczących multimediów (rozdzielczość, wysokość, szerokość)
Pierwszy parametr funkcji getUserMedia()
może też służyć do określania dodatkowych wymagań (lub ograniczeń) dotyczących zwracanego strumienia multimediów. Na przykład zamiast tylko wskazywać, że chcesz mieć podstawowy dostęp do wideo (np. {video: true}
), możesz dodatkowo wymagać, aby strumień był w 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);
Więcej informacji o konfiguracjach znajdziesz w interfejsie API ograniczeń.
Wybieranie źródła multimediów
Metoda enumerateDevices()
interfejsu MediaDevices
wysyła żądanie listy dostępnych urządzeń wejściowych i wyjściowych, takich jak mikrofony, kamery, słuchawki itp. Zwrócony obiekt Promise jest rozwiązywany za pomocą tablicy obiektów MediaDeviceInfo
opisujących urządzenia.
W tym przykładzie ostatni znaleziony mikrofon i kamera są wybierane jako źródło strumienia multimediów:
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);
}
Zobacz świetną prezentację Sama Duttona, która pokazuje, jak umożliwić użytkownikom wybór źródła multimediów.
Bezpieczeństwo
Przeglądarki wyświetlają okno uprawnień po wywołaniu funkcji navigator.mediaDevices.getUserMedia()
, dzięki czemu użytkownicy mogą przyznać lub odmówić dostępu do aparatu lub mikrofonu. Oto na przykład okno uprawnień w Chrome:

Podawanie wartości zastępczej
W przypadku użytkowników, których urządzenia nie obsługują navigator.mediaDevices.getUserMedia()
, jedną z opcji jest powrót do istniejącego pliku wideo, jeśli interfejs API nie jest obsługiwany lub wywołanie z jakiegoś powodu się nie powiodło:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}