Introduzione
L'acquisizione audio/video è stata a lungo il "Santo Graal" dello sviluppo web. Per molti anni abbiamo dovuto fare affidamento su plug-in del browser (Flash o Silverlight) per svolgere il lavoro. Dai!
HTML5 viene in soccorso. Potrebbe non essere evidente, ma l'aumento dell'HTML5 ha portato a un'impennata dell'accesso all'hardware del dispositivo. Geolocalizzazione (GPS), l'API Orientation (accelerometro), WebGL (GPU) e l'API Web Audio (hardware audio) sono esempi perfetti. Queste funzionalità sono incredibilmente potenti ed espongono API JavaScript di alto livello che si basano sulle funzionalità hardware sottostanti del sistema.
Questo tutorial introduce una nuova API, GetUserMedia, che consente alle app web di accedere alla fotocamera e al microfono di un utente.
Il percorso verso getUserMedia()
Se non ne conosci la storia, il modo in cui siamo arrivati all'API getUserMedia()
è una storia interessante.
Negli ultimi anni si sono evolute diverse varianti delle "API Media Capture". Molte persone hanno riconosciuto la necessità di poter accedere ai dispositivi nativi sul web, ma ciò ha portato tutti a elaborare una nuova specifica. La situazione è diventata così caotica che il W3C ha finalmente deciso di formare un gruppo di lavoro. Il loro unico scopo? Dai un senso alla follia! Il gruppo di lavoro sulle norme relative alle API per i dispositivi (DAP) ha il compito di consolidare e standardizzare la miriade di proposte.
Proverò a riassumere cosa è successo nel 2011…
Round 1: HTML Media Capture
HTML Media Capture è stato il primo tentativo del DAP di standardizzare l'acquisizione di contenuti multimediali sul web. Funziona sovraccaricando il parametro <input type="file">
e aggiungendo nuovi valori per il parametro accept
.
Se vuoi consentire agli utenti di scattare una foto di se stessi con la webcam,
puoi farlo con capture=camera
:
<input type="file" accept="image/*;capture=camera">
La registrazione di un video o di un audio è simile:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
Piuttosto carino, no? Mi piace in particolare che riutilizzi un input di file. Dal punto di vista semantico,
ha molto senso. Il limite di questa particolare "API" è la possibilità di applicare effetti in tempo reale
(ad es. eseguire il rendering dei dati della webcam in tempo reale in un <canvas>
e applicare filtri WebGL).
HTML Media Capture ti consente solo di registrare un file multimediale o scattare un'istantanea nel tempo.
Assistenza:
- Browser Android 3.0: una delle prime implementazioni. Guarda questo video per vederlo in azione.
- Chrome per Android (0.16)
- Firefox Mobile 10.0
- Safari e Chrome per iOS6 (supporto parziale)
Round 2: elemento del dispositivo
Molti ritenevano che HTML Media Capture fosse troppo limitato, quindi è emersa una nuova specifica
che supportava qualsiasi tipo di dispositivo (futuro). Non sorprende che il design prevedesse
un nuovo elemento, l'elemento <device>
,
che è diventato il predecessore di getUserMedia()
.
Opera è stato tra i primi browser a creare implementazioni iniziali
dell'acquisizione video basate sull'elemento <device>
. Poco dopo
(lo stesso giorno, per essere precisi),
WhatWG ha deciso di eliminare il tag <device>
a favore di un altro promettente, questa volta un'API JavaScript chiamata
navigator.getUserMedia()
. Una settimana dopo, Opera ha rilasciato nuove build che includevano
il supporto della specifica getUserMedia()
aggiornata. Nello stesso anno,
Microsoft si è unita al gruppo rilasciando un Lab per IE9
che supportava la nuova specifica.
Ecco come sarebbe stato <device>
:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
Assistenza:
Purtroppo, nessun browser rilasciato ha mai incluso <device>
.
Una API in meno di cui preoccuparsi, suppongo :) <device>
aveva due grandi vantaggi: 1) era semantica e 2) era facilmente estendibile per supportare
più di semplici dispositivi audio/video.
Fai un respiro. Queste cose cambiano rapidamente.
Round 3: WebRTC
L'elemento <device>
è stato infine abbandonato.
Il ritmo per trovare un'API di acquisizione adatta è aumentato grazie al maggiore impegno di WebRTC (Web Real Time Communications). Questa specifica è supervisionata dal W3C WebRTC Working Group. Google, Opera, Mozilla e altri hanno implementazioni.
getUserMedia()
è correlato a WebRTC perché è il gateway per questo insieme di API.
Fornisce i mezzi per accedere allo stream locale della videocamera/del microfono dell'utente.
Assistenza:
getUserMedia()
è supportato a partire da Chrome 21, Opera 18 e Firefox 17.
Per iniziare
Con navigator.mediaDevices.getUserMedia()
, possiamo finalmente accedere all'input della webcam e del microfono senza un plug-in.
L'accesso alla videocamera è ora a portata di chiamata, non di installazione. È integrato direttamente nel browser. Ti abbiamo incuriosito?
Rilevamento delle funzionalità
Il rilevamento delle funzionalità è un semplice controllo dell'esistenza di navigator.mediaDevices.getUserMedia
:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
Ottenere l'accesso a un dispositivo di input
Per utilizzare la webcam o il microfono, dobbiamo richiedere l'autorizzazione.
Il primo parametro di navigator.mediaDevices.getUserMedia()
è un oggetto che specifica i dettagli e
i requisiti per ogni tipo di media a cui vuoi accedere. Ad esempio, se vuoi accedere alla webcam, il primo parametro deve essere {video: true}
. Per utilizzare sia il microfono che la videocamera,
trasmetti {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. Che cosa sta succedendo? L'acquisizione di contenuti multimediali è un esempio perfetto di nuove API HTML5
che funzionano insieme. Funziona in combinazione con gli altri nostri amici HTML5, <audio>
e <video>
.
Tieni presente che non impostiamo un attributo src
o includiamo elementi <source>
nell'elemento <video>
. Anziché fornire al video un URL a un file multimediale, impostiamo
srcObject
sull'oggetto LocalMediaStream
che rappresenta la webcam.
Inoltre, dico a <video>
di autoplay
, altrimenti rimarrebbe bloccato sul primo frame. L'aggiunta di controls
funziona come previsto.
Impostazione dei vincoli dei contenuti multimediali (risoluzione, altezza, larghezza)
Il primo parametro di getUserMedia()
può essere utilizzato anche per specificare ulteriori requisiti (o vincoli) per il flusso multimediale restituito. Ad esempio, invece di indicare solo che vuoi l'accesso di base al video (ad es. {video: true}
), puoi richiedere anche che lo stream
sia in 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);
Per ulteriori configurazioni, consulta l'API Constraints.
Selezionare un'origine multimediale
Il metodo enumerateDevices()
dell'interfaccia MediaDevices
richiede un elenco dei dispositivi di input e output multimediali disponibili, come microfoni, videocamere, cuffie e così via. La promessa restituita viene risolta con un array di oggetti MediaDeviceInfo
che descrivono i dispositivi.
In questo esempio, l'ultimo microfono e l'ultima videocamera trovati vengono selezionati come origine del flusso multimediale:
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);
}
Guarda la fantastica demo di Sam Dutton su come consentire agli utenti di selezionare l'origine dei contenuti multimediali.
Sicurezza
I browser mostrano una finestra di dialogo delle autorizzazioni quando chiamano navigator.mediaDevices.getUserMedia()
,
che offre agli utenti la possibilità di concedere o negare l'accesso alla fotocamera/al microfono. Ad esempio, ecco
la finestra di dialogo delle autorizzazioni di Chrome:

Fornire un fallback
Per gli utenti che non supportano navigator.mediaDevices.getUserMedia()
, un'opzione è il fallback
a un file video esistente se l'API non è supportata e/o la chiamata non va a buon fine per qualche motivo:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}