Modifier et enregistrer le flux vidéo de la webcam avec captureStream et MediaRecorder

Introduction

Ce tuto fait suite au tuto intitulé Récupérer et sauvegarder un cliché avec la Webcam ou l'on recuperait une image a partir de la webcam mais la on vas aller plus loin en recuperant le flux video de la webcam afin de le modifier par l'intermediaire d'un canvas et enfin on recuperera le contenu du canvas afin de le convertir en flux video, d'en faire un enregistrement et le sauvegarder.

Le rendu finale peu etre testé sur cette page

A noter que j'ai fait en sorte que ce soit le plus compréhensible possible je me suit donc plus concentré sur la partie compréhention pour ce faire la plus part des étape est representé par une fonction.

La partie html

La partie html est constitué de deux bouton pour l'ouverture et la fermeture de la webcam en dessous se trouve deux autres boutons pour le lancement et l’arrêt de l'enregistrement et encore en dessous deux autres boutons servent a sauvegarder la vidéo modifié. sur le haut de la page se trouve successivement la balise video de rendu de la webcam la deuxième fenêtre qui est une balise canvas contient la vidéo modifier et enfin la troisième fenêtre est une balise video qui affiche l'enregistrement de la vidéo modifié.

pour simplifier la comprehention du tuto la plus part des evenement on été placé dans la partie HTML

<body>

<video id='direct'  height=180 width=320 controls></video>

<canvas id='intermediaire' height=180 width=320 style='display:none'></canvas>
<canvas id='rendu' height=180 width=320 style='background:gray'></canvas>

<video id='resultat'  height=180 width=320 controls></video>

<br><br>
<button onclick="ouvrir_camera()" style='height:50px;width:100px;margin:auto'>ouvrir camera</button>

<button id='fermer' style='height:50px;width:100px;margin:auto' onclick='fermer_camera()'>fermer camera</button> 
<br><br>
 
<button id='marche' onclick="rec()" style='height:50px;width:100px;margin:auto'>enregistrer</button>

<button onclick="arret_rec()"' style='height:50px;width:100px;margin:auto'>arreter</button>
<br><br>


<button id='envoi' onclick="envoi()" style='height:50px;width:100px;margin:auto'>envoyer</button>

<button onclick="telecharger()" style='height:50px;width:100px;margin:auto'>telecharger</button>
<br>

<br>
<div id='sauver'></div>

<br>

<div id="jaxa"></div>

</body>

Ouvrir la camera

La première chose a faire est d'ouvrir la webcam en utilisant getUserMedia de l'api mediadevice en precisant l'activation de l'audio et la taille de la video le retourne est une promise comprenant le stream video et ou audio de la webcam afin d'executer le code qui redirige le stream vers la balise video en utilisant srcObjet puis on demarre le flux audio video avec la méthodeplay() de l'api video

Pour finir on lance la fonction qui exécute le traitement afin de modifier la vidéo traitement qui sera exécute dans un canvas et enfin on ajoute un évènement oncanplay qui permettra d’exécuter le code suivant lorsque la webcam commence a diffuser les flux vidéo et audio.

function ouvrir_camera(){

 navigator.mediaDevices.getUserMedia({ audio: true,video: { width: 320, height: 180 } })

   .then(function(stream) {

   strm=stream

   document.getElementById('direct').srcObject=stream;
   document.getElementById('direct').play();

   setInterval(pixel,40);

   document.getElementById('direct').oncanplay= canvas_stream
  })
 
   .catch(function(err) {
  console.log('erreur detecté: ' + err);
 })
}

Traitement par le canvas

le traitement consistera a transformer la vidéo en vidéo pixelise sans trop entrer dans les détaille techniquement on utilise deux canvas un qui récupère le flux vidéo et a partir de ce canvas on récupère les donné de couleur tous les quatre pixels que l'on que l'on adjoint aux pixels parent afin de faire le rendu pixelisé et quand les donné on été transformé elles sont injecté dans le canvas de rendu.

function pixel(){

 var vivi = document.getElementById('direct');

 var moncanvas2d=document.getElementById('intermediaire').getContext('2d');
 moncanvas2d.drawImage(vivi,0,0,320,180);
 var idata=moncanvas2d.getImageData( 0, 0, 500, 500);
 var data =  idata.data;
 var taille=500*4
 var pixeli=40

 for(var i = 0; i < data.length; i+=taille*2){

  for(var j = 0; j < taille; j+=pixeli){

   var coulcourante1=data[i+j];
   var coulcourante2=data[i+j+1];
   var coulcourante3=data[i+j+2];

   for(var k = 0; k < pixeli; k+=4){ 
    data[i+j+k]=coulcourante1;
    data[i+j+k+1]=coulcourante2;
    data[i+j+k+2]=coulcourante3;

    data[i+j+k+taille]=coulcourante1;
    data[i+j+k+1+taille]=coulcourante2;
    data[i+j+k+2+taille]=coulcourante3;
   }
  }
 }
 idata.data=data
 document.getElementById('rendu').getContext('2d').putImageData(idata,0,0);
}

Lancer la capture du canvas et y adjoindre l'audio

creer un flux video a partir d'un canvas "captureStream()"

pour capturer le contenu du canvas afin de le convertir en flux video on utilise captureStream() celui ci est équivalent a un flux provenant de la webcam avec petite une différence c'est qu'il ne contient aucunes piste audio étant donné qu'un canvas ne diffuse pas l'audio.

stream_cvs =document.getElementById('rendu').captureStream()

ajouter un flux audio "addTrack()"

on vas devoir récupérer la piste audio du stream de la webcam et l’intégré au stream du canvas nouvellement créé. Tout d'abord on récupéré un lien de la piste audio de la webcam avec getTracks() qui retourne un tableau listant un lien vers chaque piste dans le cas d'une webcam classic il y a deux piste une pour l'audio et une pour la video. On parcoure donc le tableau et pour chaque index on récupère l'attribut kind qui précise le type de piste au-quelle on a affaire dans notre cas on a besoin de la piste audio kind devra donc retourner "audio" et quand c'est le cas on integre au flux video du canvas la piste audio de la webcam en utilisant la méthode addTrack() et en y précisant comme paramètre le lien vers la piste audio de la camera.

stream_cvs.addTrack(all_track_audio[i]);

Enfin afin d'executer le code suivant on lance la fonction d'initialisation de l’enregistrement.

le code

function canvas_stream() {

 stream_cvs =document.getElementById('rendu').captureStream()

 var all_track_audio=strm.getTracks()

 for(var i=0; i<all_track_audio.length;i++){

  if(all_track_audio[i].kind=="audio"){

   console.log(all_track_audio[i].kind);

   stream_cvs.addTrack(all_track_audio[i]);

   break;
  }
 }
 init_rec();
}

Création et initialisation de l’enregistrement MediaRecorder

La création d'un enregistrement se fait en instanciant un objet MediaRecorder et en lui precisant le stream a enregistrer.

l'objet MediaRecorder possède des attribut des méthodes et des évènement et pour la suite nous auront besoin de l’événement onstop qui lors de l’arrêt de l'enregistrement lancera une fonction qui transformera les donné d'enregistrement en fichier dans le format blob (Binary large object) et l’événement ondataavaible qui permettra de transférer les donné enregistré dans un variable que l'on aura crée et enfin on affichera le résultat de l'enregistrement dans une balise video.

function init_rec() {

 media_rec = new MediaRecorder(stream_cvs);

  media_rec.onstop = function(e) {

   blob = new Blob(chunks, { 'type' : 'video/webm; codecs=opus' });
   chunks = [];
   video_url = window.URL.createObjectURL(blob);

   document.getElementById('resultat').src = video_url;

   console.log("arret de l'enregistrement");
   }

 media_rec.ondataavailable = function(e) {
  chunks.push(e.data);
 }
}

Lancement de l'enregistrement

Le lancement de l'enregistrement ce fait grâce a la méthode start() de l'api MediaRecorder et on met la couleur de texte du bouton de démarrage en rouge.

 rec() {

 media_rec.start();
 console.log(media_rec.state);
 console.log("demarage enregistrement");
 document.getElementById('marche').style.color = "red";
}

Arret de l'enregistrement

L'arret de l'enregistrement ce fait grâce a la méthode stop() de l'api MediaRecorder et on remet la couleur de texte du bouton de démarrage en noire.

 arret_rec(){

 media_rec.stop();
 console.log(media_rec.state);
 document.getElementById('marche').style.color = "black"; 
}

Sauvegarde de la video et fermeture de la webcam

Pour la suite s’agissant des mêmes techniques employé dans le précédent tuto je vous renvoi a ce même tuto.

le code

le code complet avec la partie sauvegarde et fermeture de la camera

pour une utilisation en ligne et a causse de restriction des navigateurs il faut que le site soit en https sinon le navigateur génèrera une erreur.

<!DOCTYPE html> 
<html lang="fr">
<head>
    <title>canvas rec</title> 
    <meta charset="utf-8">
 
<script type="text/javascript"> 

 var media_rec=''

 var strm=""

 var chunks = [];

 var video_url=""

 var blob=""

 var stream_cvs ='cvb'


function ouvrir_camera(){

 navigator.mediaDevices.getUserMedia({ audio: true,video: { width: 320, height: 180 } })

   .then(function(stream) {

   strm=stream

   document.getElementById('direct').srcObject=stream;
   document.getElementById('direct').play();

   setInterval(pixel,40);

   document.getElementById('direct').oncanplay= canvas_stream
  })
 
   .catch(function(err) {
  console.log('erreur detecté: ' + err);
 })
}

function canvas_stream() {

 stream_cvs =document.getElementById('rendu').captureStream()

 var all_track_audio=strm.getTracks()

 for(var i=0; i<all_track_audio.length;i++){

  if(all_track_audio[i].kind=="audio"){

   console.log(all_track_audio[i].kind);

   stream_cvs.addTrack(all_track_audio[i]);

   break;
  }
 }
 init_rec();
}


function init_rec() {

 media_rec = new MediaRecorder(stream_cvs);

  media_rec.onstop = function(e) {

   blob = new Blob(chunks, { 'type' : 'video/webm; codecs=opus' });
   chunks = [];
   video_url = window.URL.createObjectURL(blob);

   document.getElementById('resultat').src = video_url;

   console.log("arret de l'enregistrement");
   }

 media_rec.ondataavailable = function(e) {
  chunks.push(e.data);
 }
}


function rec() {

 
 window.URL.revokeObjectURL(video_url);
 chunks = [];
 media_rec.start();
 console.log(media_rec.state);
 console.log("demarage enregistrement");
 document.getElementById('marche').style.color = "red";
}

function arret_rec(){

 media_rec.stop();
 console.log(media_rec.state);
 document.getElementById('marche').style.color = "black";
}

function fermer_camera(){

 var mediaStream=strm;
 console.log('etat >>> '+mediaStream)
 var tracks = mediaStream.getTracks();

 for(var i=0; i<tracks.length;i++){
  console.log(tracks.length);
  tracks[i].stop();
  console.log("message: "+tracks[i].label+" déconnecté");
 }

 strm = null;
}


function telecharger(){

 if(navigator.msSaveOrOpenBlob){

  var blobObject=blob

  window.navigator.msSaveOrOpenBlob(blobObject, "video.webm");
 }

 else{

  var elem = document.createElement('a');
  elem.href = video_url;
  elem.download = "video.webm";
  var evt = new MouseEvent("click", { bubbles: true,cancelable: true,view: window,});
  elem.dispatchEvent(evt);
     }
    }


function envoi(){

 console.log(blob.type);

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

 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){

   console.log(ajax.responseText+" _envoie terminé");
   document.getElementById("jaxa").innerHTML+=(ajax.responseText);
  }
 }

 ajax.onerror=function(){

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

 ajax.send(formImage);
 console.log("envoie en cours");
}


function pixel(){

 var vivi = document.getElementById('direct');

 var moncanvas2d=document.getElementById('intermediaire').getContext('2d');
 moncanvas2d.drawImage(vivi,0,0,320,180);
 var idata=moncanvas2d.getImageData( 0, 0, 500, 500);
 var data =  idata.data;
 var taille=500*4
 var pixeli=40

 for(var i = 0; i < data.length; i+=taille*2){

  for(var j = 0; j < taille; j+=pixeli){

   var coulcourante1=data[i+j];
   var coulcourante2=data[i+j+1];
   var coulcourante3=data[i+j+2];

   for(var k = 0; k < pixeli; k+=4){ 
    data[i+j+k]=coulcourante1;
    data[i+j+k+1]=coulcourante2;
    data[i+j+k+2]=coulcourante3;

    data[i+j+k+taille]=coulcourante1;
    data[i+j+k+1+taille]=coulcourante2;
    data[i+j+k+2+taille]=coulcourante3;
   }
  }
 }
 idata.data=data
 document.getElementById('rendu').getContext('2d').putImageData(idata,0,0);
}
 
</script>
 
</head> 

<body>

<video id='direct'  height=180 width=320 controls></video>

<canvas id='intermediaire' height=180 width=320 style='display:none'></canvas>
<canvas id='rendu' height=180 width=320 style='background:gray'></canvas>

<video id='resultat'  height=180 width=320 controls></video>

<br><br>
<button onclick="ouvrir_camera()" style='height:50px;width:100px;margin:auto'>ouvrir camera</button>

<button id='fermer' style='height:50px;width:100px;margin:auto' onclick='fermer_camera()'>fermer camera</button> 
<br><br>
 
<button id='marche' onclick="rec()" style='height:50px;width:100px;margin:auto'>enregistrer</button>

<button onclick="arret_rec()"' style='height:50px;width:100px;margin:auto'>arreter</button>
<br><br>


<button id='envoi' onclick="envoi()" style='height:50px;width:100px;margin:auto'>envoyer</button>

<button onclick="telecharger()" style='height:50px;width:100px;margin:auto'>telecharger</button>
<br>

<br>
<div id='sauver'></div>

<br>

<div id="jaxa"></div>

</body>
</html>
A voir également
Ce document intitulé « Modifier et enregistrer le flux vidéo de la webcam avec captureStream et MediaRecorder » 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