Introduction
La capture audio/vidéo est depuis longtemps le Saint Graal du développement Web. Pendant de nombreuses années, nous avons dû nous appuyer sur des plug-ins de navigateur (Flash ou Silverlight) pour accomplir cette tâche. Allez !
HTML5 à la rescousse. Cela peut ne pas être évident, mais l'essor du HTML5 a entraîné une forte augmentation de l'accès au matériel des appareils. La géolocalisation (GPS), l'API Orientation (accéléromètre), WebGL (GPU) et l'API Web Audio (matériel audio) en sont des exemples parfaits. Ces fonctionnalités sont incroyablement puissantes et exposent des API JavaScript de haut niveau qui reposent sur les fonctionnalités matérielles sous-jacentes du système.
Ce tutoriel présente une nouvelle API, GetUserMedia, qui permet aux applications Web d'accéder à la caméra et au micro d'un utilisateur.
Chemin vers getUserMedia()
Si vous ne connaissez pas son histoire, celle de l'API getUserMedia()
est intéressante.
Plusieurs variantes des API Media Capture ont évolué au cours des dernières années. De nombreuses personnes ont reconnu la nécessité d'accéder aux appareils natifs sur le Web, mais cela a conduit tout le monde à créer une nouvelle spécification. Les choses sont devenues si désordonnées que le W3C a finalement décidé de former un groupe de travail. Son seul but ? Donnez du sens à cette folie ! Le groupe de travail sur les règles concernant les API d'appareils (DAP) a été chargé de consolider et de normaliser la multitude de propositions.
Je vais essayer de résumer ce qui s'est passé en 2011.
Manche 1: Capture multimédia HTML
La capture multimédia HTML a été la première tentative de la DAP de normaliser la capture multimédia sur le Web. Il fonctionne en surchargeant <input type="file">
et en ajoutant de nouvelles valeurs pour le paramètre accept
.
Si vous souhaitez autoriser les utilisateurs à se prendre en photo avec la webcam, c'est possible avec capture=camera
:
<input type="file" accept="image/*;capture=camera">
L'enregistrement d'une vidéo ou d'un audio est similaire:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
C'est plutôt sympa, non ? J'apprécie particulièrement qu'il réutilise une entrée de fichier. Sémantiquement, cela a beaucoup de sens. L'inconvénient de cette "API" particulière est qu'elle ne permet pas d'appliquer d'effets en temps réel (par exemple, d'afficher des données de webcam en direct dans un <canvas>
et d'appliquer des filtres WebGL).
La capture multimédia HTML ne vous permet d'enregistrer un fichier multimédia ou de prendre un instantané qu'à un moment donné.
Assistance:
- Navigateur Android 3.0 : l'une des premières implémentations. Regardez cette vidéo pour voir comment cela fonctionne.
- Chrome pour Android (0.16)
- Firefox Mobile 10.0
- Safari et Chrome sur iOS 6 (compatibilité partielle)
2e manche: élément de l'appareil
De nombreux utilisateurs pensaient que la capture multimédia HTML était trop limitée. Une nouvelle spécification est donc apparue, compatible avec tous les types d'appareils (futurs). Sans surprise, la conception a nécessité un nouvel élément, l'élément <device>
, qui est devenu le prédécesseur de getUserMedia()
.
Opera a été l'un des premiers navigateurs à créer des implémentations initiales de la capture vidéo basée sur l'élément <device>
. Peu de temps après (le même jour, pour être précis), le WhatWG a décidé d'abandonner la balise <device>
au profit d'une autre nouvelle venue, cette fois une API JavaScript appelée navigator.getUserMedia()
. Une semaine plus tard, Opera a publié de nouvelles versions compatibles avec la nouvelle spécification getUserMedia()
. Plus tard dans l'année, Microsoft a rejoint la fête en publiant un laboratoire pour IE9 compatible avec la nouvelle spécification.
Voici à quoi <device>
aurait ressemblé:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
Assistance:
Malheureusement, aucun navigateur publié n'a jamais inclus <device>
.
Une API de moins à gérer, je suppose :) <device>
avait cependant deux avantages: 1.) elle était sémantique, et 2.) elle pouvait être facilement étendue pour prendre en charge plus que des appareils audio/vidéo.
Inspirez, expirez. Les choses évoluent vite !
3e tour: WebRTC
L'élément <device>
a fini par disparaître.
La recherche d'une API de capture adaptée s'est accélérée grâce aux efforts plus importants déployés pour WebRTC (Web Real-time Communications). Cette spécification est supervisée par le groupe de travail WebRTC du W3C. Google, Opera, Mozilla et quelques autres ont implémenté cette fonctionnalité.
getUserMedia()
est lié à WebRTC, car il constitue la passerelle vers cet ensemble d'API.
Il permet d'accéder au flux local de l'appareil photo/micro de l'utilisateur.
Assistance:
getUserMedia()
est compatible avec Chrome 21, Opera 18 et Firefox 17.
Premiers pas
Avec navigator.mediaDevices.getUserMedia()
, nous pouvons enfin utiliser l'entrée de la webcam et du micro sans plug-in.
L'accès à la caméra est désormais disponible en un appel, et non en une installation. Il est intégré directement au navigateur. Vous avez hâte de commencer ?
Détection de fonctionnalités
La détection des fonctionnalités consiste en une simple vérification de l'existence de navigator.mediaDevices.getUserMedia
:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
Obtenir l'accès à un périphérique d'entrée
Pour utiliser la webcam ou le micro, nous devons demander l'autorisation.
Le premier paramètre de navigator.mediaDevices.getUserMedia()
est un objet spécifiant les détails et les exigences pour chaque type de contenu multimédia auquel vous souhaitez accéder. Par exemple, si vous souhaitez accéder à la webcam, le premier paramètre doit être {video: true}
. Pour utiliser à la fois le micro et la caméra, transmettez {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. Que se passe-t-il ici ? La capture multimédia est un exemple parfait de nouvelles API HTML5 qui fonctionnent en synergie. Il fonctionne avec nos autres amis HTML5, <audio>
et <video>
.
Notez que nous ne définissons pas d'attribut src
ni n'incluons d'éléments <source>
dans l'élément <video>
. Au lieu d'alimenter la vidéo avec une URL vers un fichier multimédia, nous définissons srcObject
sur l'objet LocalMediaStream
représentant la webcam.
Je demande également à <video>
de passer à autoplay
, sinon il serait figé sur le premier frame. L'ajout de controls
fonctionne également comme prévu.
Définir des contraintes multimédias (résolution, hauteur, largeur)
Le premier paramètre de getUserMedia()
peut également être utilisé pour spécifier d'autres exigences (ou contraintes) sur le flux multimédia renvoyé. Par exemple, au lieu de simplement indiquer que vous souhaitez un accès de base à la vidéo (par exemple, {video: true}
), vous pouvez également exiger que le flux soit en 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);
Pour en savoir plus sur les configurations, consultez l'API constraints.
Sélectionner une source multimédia
La méthode enumerateDevices()
de l'interface MediaDevices
demande une liste des périphériques d'entrée et de sortie multimédias disponibles, tels que les micros, les caméras, les casques, etc. La promesse renvoyée est résolue avec un tableau d'objets MediaDeviceInfo
décrivant les appareils.
Dans cet exemple, le dernier micro et la dernière caméra détectés sont sélectionnés comme source du flux multimédia:
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);
}
Découvrez la excellente démonstration de Sam Dutton sur la façon de permettre aux utilisateurs de sélectionner la source multimédia.
Sécurité
Les navigateurs affichent une boîte de dialogue d'autorisation lors de l'appel de navigator.mediaDevices.getUserMedia()
, ce qui permet aux utilisateurs d'accorder ou de refuser l'accès à leur appareil photo/micro. Voici, par exemple, la boîte de dialogue d'autorisation de Chrome:
Fournir un remplacement
Pour les utilisateurs qui ne sont pas compatibles avec navigator.mediaDevices.getUserMedia()
, une option consiste à utiliser un fichier vidéo existant si l'API n'est pas prise en charge et/ou si l'appel échoue pour une raison quelconque:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}