Récupérer et sauvegarder un cliché avec la Webcam grace a l'api MediaDevices.

introduction

Nous allons voir comment mettre en marche la webcam prendre un photo puis la sauvegarder. Pour ce faire nous utiliseront les api intégré aux navigateur, mediaDevices pour la caméra, vidéo pour l'affichage, canvas pour récupérer une image, toBlob, toDataUrl pour enregistrer l'image, FormData pour l'envoi de l'image.

mise en marche de la camera

mediaDevices

L'api mediaDevices renvoie un objet MediaDevices qui permet d'accéder à des périphériques d'entrée tels que des caméras et des microphones, ainsi qu'à un partage d'écran.
L'api est en lecture seule elle est constitué de deux méthodes getUserMedia() et enumerateDevices() dans notre cas nous utiliserons getUserMedia()

getUserMedia()

getUserMedia () met en marche une caméra ou un partage d'écran et / ou un microphone Avec l'autorisation de l'utilisateur et fournit un Stream de media appelé MediaStream contenant une piste vidéo et / ou une piste audio.

L'ouverture ce fait en précisant comme paramètre de la fonction un objet avec deux clee la première désigne l'audio pour laquel on donne comme valeur true ou false afin d' activer ou desactiver l'audio . La deuxième clee désigne la vidéo pour laquel on précise soit true soit false pour activer ou désactiver la vidéo soit un objet dans lequel on précise les options d'affichage de la vidéo dans ce cas la valeur est automatiquement a true.

l'appel de la fonction getUserMedia() renvoi une instance de promise (promesse).

une promise permet d’exécuter du code a la suite d'une action pouvant prendre un certain temp. Dans notre cas on doit attendre l'affichage de la fenêtre qui demande l'autorisation d'utiliser la webcam et la validation par le client. Normalement une promise est crée en utilisant new promise() mais la elle est crée automatiquement par l'appel a getUserMedia ().

Si l'autorisation d'utiliser la webcam est validé la promise renvoi un objet MediaStream et alors le code contenu dans then qui sert a gérer le retour d'une promise réussi est exécuter afin de diriger le flux vers une balise video en utilisant le src (chemin) de la balise vidéo.

Si il y a une erreur ou que l'utilisateur a refusé l'utilisation de la webcam le code contenu dans catch est exécuté afin de gérer l’erreur.

MediaStream

L' objet MediaStream représentant le flux de données audio et ou vidéo.

le src (chemin) de la balise vidéo est appelé srcObject c'est lui qui fait le lien entre la balise video et l'objet MediaStream afin de contrôler le flux par l'intermédiaire des méthodes et attributs de l'objet MediaStream.

pour plus d'information sur les méthodes et attributs un lien vers mdn

getTracks()

getTracks() est une méthode de l'objet MediaStream renvoyant un tableau listant toutes les pistes composant le stream. Dans notre cas sera activé uniquement la video. En considérant que la webcam est pourvu d'un micro et que l'on active ce micro il y aurait eu deux pistes constituant le stream celle pour la webcam et celle pour le microphone.

Chaque index du tableau contient donc une piste représente par un objet permettant le contrôle et la récupération d'information sur la piste par l'intermédiaire de méthodes et d'attributs.

On en profite pour récupérer le nom de la camera grâce a l'attribut label afin d'afficher le nom de la webcam dans la page.

On récupère donc l'objet représentant la piste du stream a l'index 0 du tableau et on récupére l'attribut label de cette objet.

Le code

function ouvrir_camera() {

  navigator.mediaDevices.getUserMedia({ audio: false, video: { width: 400 } }).then(function(mediaStream) {
   
   var video = document.getElementById('sourcevid');
   video.srcObject = mediaStream;
   
   var tracks = mediaStream.getTracks();
   
   document.getElementById("message").innerHTML="message: "+tracks[0].label+" connecté"
   
   console.log(tracks[0].label)
   
   video.onloadedmetadata = function(e) {
    video.play();
   };
    
  }).catch(function(err) { console.log(err.name + ": " + err.message);

  document.getElementById("message").innerHTML="message: connection refusé"});
 
 }

A ce niveau en considerant que l'on a autorisé la webcam le flux est affiché dans la page.

recuperation d'un cliché

canvas

La récupération d'un cliché a partir d'une balise video vers un canvas ce fait avec la méthode drawImage de canvas en précisant comme paramètres la balise video concerné par le cliché. Pour ce faire la page contient un canvas qui récupère le cliché.
La taille du canvas correspondra a la taille de la vidéo taille qu l'on récupère avec les attributs videoHeight et videoWidth attributs faisant partie de l'api video.

Le code

function photo(){
  
  var vivi = document.getElementById('sourcevid');
  var canvas1 = document.getElementById('cvs')
  var ctx =canvas1.getContext('2d');
  canvas1.height=vivi.videoHeight
  canvas1.width=vivi.videoWidth
  ctx.drawImage(vivi, 0,0, vivi.videoWidth, vivi.videoHeight);
 }

sauvegarde local de l'image

msSaveOrOpenBlob() toDataURL()

pour la sauvegarde de l'image on utilisera deux syntaxe un pour le navigateur Microsoft edge l'autre les autres navigateurs.

msSaveOrOpenBlob()

pour edge la syntaxe est simple on créé un blob avec msToBlob() en précisant le canvas cible puis on fait appel a msSaveOrOpenBlob avec comme paramètres le blob et le nom a donner a l'image.

msSaveOrOpenBlob permet d'ouvrir la fenêtre de téléchargement de edge.

toDataURL()

pour les autres la syntaxe est un peut plus compliqué. on fait appel a toDataURL() afin de créé un lien vers les donné, lien qui sera dirigé vers une balise a créé dynamiquement. Le nom du fichier est indiqué avec l'attribut dowload de la balise. il faufra préciser a toDataURL() des parametre afin de decrire le type mime du format de l’image désiré (png jpg bmp)

MouseEvent()

Pour finir on créé un evenement clic dynamique avec MouseEvent sur la balise a afin d’éviter de devoir l'inséré dans la page et de cliqué sur le lien pour lancer le téléchargement on utilisera un bouton situé dans la page.

Le code

function sauver(){
 
  if(navigator.msSaveOrOpenBlob){

            var blobObject=document.getElementById("cvs").msToBlob()

            window.navigator.msSaveOrOpenBlob(blobObject, "image.png");
        }

        else{

            var canvas = document.getElementById("cvs");
            var elem = document.createElement('a');
            elem.href = canvas.toDataURL("image/png");
            elem.download = "nom.png";
            var evt = new MouseEvent("click", { bubbles: true,cancelable: true,view: window,});
            elem.dispatchEvent(evt);
        }
 }

sauvegarde serveur de l'image

toBlob()

toBlob() transforme une image provenant d'un canvas en donné binaire la méthode comprend trois paramètre le premier est la fonction qui sera exécutera pour le traitement du blob après sa création Le deuxième paramètre spécifie le mime type qui sera le format de l'image (png, jpg ou bmp) si rien n'est spécifié le format par défaut sera le png. Le troisième paramètre est une valeur comprise entre 0 et 1 et correspond a la qualité de l'image il est facultatif.

Le code

function prepare_envoi(){

 var canvas = document.getElementById('cvs');

 canvas.toBlob(function(blob){envoi(blob)}, 'image/jpeg');

 }

FormData()

FormData() permet de créé un formulaire dynamique et d'y insérer des donné dans notre cas on y insera le fichier blob. Ensuite on créé une connexion ajax avec l’objet XMLHttpRequest afin d'envoyé l'image ver le serveur en utilisant la méthode send() de l'objet XMLHttpRequest en précisant comme parametre l'objet FormData

Le code

function envoi(blob){
 
 console.log(blob.type)

 var formImage = new FormData();
 formImage.append('image_a', blob, 'image_a.jpg');

 var ajax = new XMLHttpRequest();
  
 ajax.open("POST","http://adresse/reception/upload_camera.php",true);

 ajax.onreadystatechange=function(){

  if (ajax.readyState == 4 && ajax.status==200){

   document.getElementById("jaxa").innerHTML+=(ajax.responseText);
  }
 }

 ajax.onerror=function(){

  alert("la requette a échoué")
 }

 ajax.send(formImage);
 console.log("ok")

fermer la camera

stop()

Pour fermer la webcam on récupère d'abord une référence au stream par l'intermédiaire de la balise video et son src puis on récupère la source avec getTracks() et on lui applique la méthode stop() qui a pour effet d'arreté la webcam puis on supprime le lien de la balise vidéo vers son objet mediaStream afin de supprimer définitivement lien entre la webcam et la balise video le garbage collector fera le reste.

Le code

function fermer(){
 
  var video = document.getElementById('sourcevid');
  var mediaStream=video.srcObject;
  console.log(mediaStream)
  var tracks = mediaStream.getTracks();
  console.log(tracks[0])
  tracks.forEach(function(track) {
   track.stop();
   document.getElementById("message").innerHTML="message: "+tracks[0].label+" déconnecté"
  });

  video.srcObject = null;
 }

exemple complet

<!DOCTYPE html> 
 <html lang="fr">
  <head>
   <title>HTML5 Camera</title> 

   <meta charset="UTF-8">

   <style type="text/css">


    button{

    width:100px;
    margin:5px;
    font-weight: bold;
    height:50px;

    }

   </style>

   <script>

    function ouvrir_camera() {

     navigator.mediaDevices.getUserMedia({ audio: false, video: { width: 400 } }).then(function(mediaStream) {

      var video = document.getElementById('sourcevid');
      video.srcObject = mediaStream;

      var tracks = mediaStream.getTracks();

      document.getElementById("message").innerHTML="message: "+tracks[0].label+" connecté"

      console.log(tracks[0].label)
      console.log(mediaStream)

      video.onloadedmetadata = function(e) {
       video.play();
      };
       
     }).catch(function(err) { console.log(err.name + ": " + err.message);

     document.getElementById("message").innerHTML="message: connection refusé"});
    }

    function photo(){

     var vivi = document.getElementById('sourcevid');
     //var canvas1 = document.createElement('canvas');
     var canvas1 = document.getElementById('cvs')
     var ctx =canvas1.getContext('2d');
     canvas1.height=vivi.videoHeight
     canvas1.width=vivi.videoWidth
     console.log(vivi.videoWidth)
     ctx.drawImage(vivi, 0,0, vivi.videoWidth, vivi.videoHeight);

     //var base64=canvas1.toDataURL("image/png"); //l'image au format base 64
     //document.getElementById('tar').value='';
     //document.getElementById('tar').value=base64;
    }

    function sauver(){

     if(navigator.msSaveOrOpenBlob){

      var blobObject=document.getElementById("cvs").msToBlob()

      window.navigator.msSaveOrOpenBlob(blobObject, "image.png");
     }

     else{

      var canvas = document.getElementById("cvs");
      var elem = document.createElement('a');
      elem.href = canvas.toDataURL("image/png");
      elem.download = "nom.png";
      var evt = new MouseEvent("click", { bubbles: true,cancelable: true,view: window,});
      elem.dispatchEvent(evt);
     }
    }

    function prepare_envoi(){

     var canvas = document.getElementById('cvs');
     canvas.toBlob(function(blob){envoi(blob)}, 'image/jpeg');
    }
    
    
    function envoi(blob){

     console.log(blob.type)

     var formImage = new FormData();
     formImage.append('image_a', blob, 'image_a.jpg');

     var ajax = new XMLHttpRequest();

     ajax.open("POST","http://scriptevol.free.fr/contenu/reception/upload_camera.php",true);

     ajax.onreadystatechange=function(){

      if (ajax.readyState == 4 && ajax.status==200){

       document.getElementById("jaxa").innerHTML+=(ajax.responseText);
      }
     }

     ajax.onerror=function(){

      alert("la requette a échoué")
     }

     ajax.send(formImage);
     console.log("ok")
    }

    
    function fermer(){

     var video = document.getElementById('sourcevid');
     var mediaStream=video.srcObject;
     console.log(mediaStream)
     var tracks = mediaStream.getTracks();
     console.log(tracks[0])
     tracks.forEach(function(track) {
      track.stop();
      document.getElementById("message").innerHTML="message: "+tracks[0].label+" déconnecté"
     });

     video.srcObject = null;
    }


   </script>
   
  </head>

   <body>
    <div style='display:inline-block'>

     <video id="sourcevid" width='400' autoplay="true"></video>

     <div id="message" style='height:20px;width:350px;margin:5px;'>message:</div>
    </div>

    <canvas id="cvs" style='display:inline-block'></canvas>

    <div>
     <button onclick='ouvrir_camera()' >ouvrir camera</button>
     <button onclick='fermer()' >fermer camera</button>
     <br>
     <button onclick='photo()' >prise de photo</button>
     <button onclick='sauver()' >sauvegarder</button>
     <button onclick='prepare_envoi()' >envoyer</button>
    </div>

    <div id="jaxa" style='width:80%;margin:5px;'>message:</div>
   </body>
 </html>
 

aller plus loin

Pour ce tuto j'ai essayé avant de faire comprendre au mieux l'utilisation de la webcam et pour aller plus loin je vous propose un autre tuto qui est la suite de ce tuto il s'intitule modifier et enregistrer une video a partir de la webcam

Bonne lecture.

Ce document intitulé « Récupérer et sauvegarder un cliché avec la Webcam grace a l'api MediaDevices. » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous