Comment enregistrer une webcam et de l'audio à l'aide de WebRTC et d'une connexion homologue basée sur un serveur

90

Je souhaite enregistrer la webcam et l'audio des utilisateurs et les enregistrer dans un fichier sur le serveur. Ces fichiers pourraient alors être servis à d'autres utilisateurs.

Je n'ai aucun problème de lecture, mais j'ai des problèmes pour enregistrer le contenu.

Je crois comprendre que la .record()fonction getUserMedia n'a pas encore été écrite - seule une proposition a été faite pour cela jusqu'à présent.

Je voudrais créer une connexion homologue sur mon serveur en utilisant PeerConnectionAPI. Je comprends que c'est un peu piraté, mais je pense qu'il devrait être possible de créer un pair sur le serveur et d'enregistrer ce que le client-pair envoie.

Si cela est possible, je devrais alors pouvoir enregistrer ces données au format flv ou dans tout autre format vidéo.

Ma préférence est en fait d'enregistrer la webcam + audio côté client, pour permettre au client de réenregistrer des vidéos s'il n'a pas aimé sa première tentative avant le téléchargement. Cela permettrait également des interruptions dans les connexions réseau. J'ai vu du code qui permet d'enregistrer des «images» individuelles de la webcam en envoyant les données sur la toile - c'est cool, mais j'ai aussi besoin de l'audio.

Voici le code côté client que j'ai jusqu'à présent:

  <video autoplay></video>

<script language="javascript" type="text/javascript">
function onVideoFail(e) {
    console.log('webcam fail!', e);
  };

function hasGetUserMedia() {
  // Note: Opera is unprefixed.
  return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia || navigator.msGetUserMedia);
}

if (hasGetUserMedia()) {
  // Good to go!
} else {
  alert('getUserMedia() is not supported in your browser');
}

window.URL = window.URL || window.webkitURL;
navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia ||
                          navigator.mozGetUserMedia || navigator.msGetUserMedia;

var video = document.querySelector('video');
var streamRecorder;
var webcamstream;

if (navigator.getUserMedia) {
  navigator.getUserMedia({audio: true, video: true}, function(stream) {
    video.src = window.URL.createObjectURL(stream);
    webcamstream = stream;
//  streamrecorder = webcamstream.record();
  }, onVideoFail);
} else {
    alert ('failed');
}

function startRecording() {
    streamRecorder = webcamstream.record();
    setTimeout(stopRecording, 10000);
}
function stopRecording() {
    streamRecorder.getRecordedData(postVideoToServer);
}
function postVideoToServer(videoblob) {
/*  var x = new XMLHttpRequest();
    x.open('POST', 'uploadMessage');
    x.send(videoblob);
*/
    var data = {};
    data.video = videoblob;
    data.metadata = 'test metadata';
    data.action = "upload_video";
    jQuery.post("http://www.foundthru.co.uk/uploadvideo.php", data, onUploadSuccess);
}
function onUploadSuccess() {
    alert ('video uploaded');
}

</script>

<div id="webcamcontrols">
    <a class="recordbutton" href="javascript:startRecording();">RECORD</a>
</div>
Dave Hilditch
la source
J'ai le même problème. La méthode getRecordedData () fonctionne-t-elle pour vous? Ce n'est pas sur mes navigateurs récemment mis à jour.
Firas
Non, j'ai aussi essayé «Google Canary».
Dave Hilditch
Ouais, je garde un œil dessus - je mettrai à jour ce fil quand il y aura une solution appropriée.
Dave Hilditch
2
si vous avez la solution de la question ci-dessus, veuillez partager avec moi, merci
Muhammad
2
Quelqu'un a-t-il pu accéder aux octets MediaStream via une magie RTC côté serveur?
Vinay

Réponses:

44

Vous devriez certainement jeter un œil à Kurento . Il fournit une infrastructure de serveur WebRTC qui vous permet d'enregistrer à partir d'un flux WebRTC et bien plus encore. Vous pouvez également trouver quelques exemples pour l'application que vous prévoyez ici . Il est vraiment facile d'ajouter des capacités d'enregistrement à cette démo et de stocker le fichier multimédia dans un URI (disque local ou ailleurs).

Le projet est sous licence LGPL Apache 2.0


MODIFIER 1

Depuis cet article, nous avons ajouté un nouveau didacticiel qui montre comment ajouter l'enregistreur dans quelques scénarios

Avertissement: je fais partie de l'équipe qui développe Kurento.

igracia
la source
2
@Redtopia Lors de certains tests de charge récents, nous avons pu obtenir 150 connexions one2one de webrtc sur un i5 / 16 Go de RAM. Vous pouvez vous attendre à ce que ces chiffres soient meilleurs à l'avenir, mais ne vous attendez pas à des miracles: il y a beaucoup de cryptage en cours pour SRTP, et c'est exigeant. Nous examinons le cryptage / décryptage accéléré par le matériel, et les chiffres augmenteront, et bien que je ne puisse pas vous assurer à quel point ce sera mieux jusqu'à ce que nous le
testions
2
@ user344146 C'est probablement moi qui répondais. Pourriez-vous partager un lien vers ce message? Si vous avez cette réponse, c'est probablement parce que vous avez demandé quelque chose qui était déjà là ou dans la liste. Il semble que vous essayiez de compiler une version SNAPSHOT. Ces artefacts ne sont pas publiés dans le fichier central, donc vous vérifiez une version des didacticiels ou utilisez notre dépôt de développement interne. Cela a été répondu à plusieurs reprises dans la liste, il y a une entrée dans la documentation sur le travail avec les versions de développement ... Nous avons pris le temps de l'écrire, alors ce serait gentil de votre part de prendre le temps de le lire.
igracia
2
J'utilise juste Kurento pour faire un tel enregistrement. Ce n'est pas compliqué, mais j'ai besoin d'un peu de temps, pour comprendre le concept - parce que certains documents sont vraiment méchants - et trouver ce que je peux envoyer à kurento, ou une description d'événements, etc. peut être parfois très frustrant. Mais de toute façon, un projet ouvert comme celui-ci est vraiment un excellent travail et vaut la peine d'être utilisé. Kurento fonctionne uniquement sous Linux (la version Windows n'est pas officielle et ne fonctionne pas avec toutes les fonctionnalités).
Krystian
1
Trouvé des réponses aux questions ci-dessus (publication ici pour d'autres), Kurento prend actuellement en charge JDK 7.0, ce n'est pas qu'il doit dépendre d'Ubuntu 14.04, il devrait également prendre en charge les versions ultérieures, mais Kurento n'est pas testé officiellement sur d'autres versions d'Ubuntu / autre version Linux. Kurento publie également des versions 64 bits facilement disponibles pour l'isntallation, mais vous pouvez installer une version 32 bits du serveur mais vous devez d'abord la construire.
Bilbo Baggins
1
Malheureusement, comme indiqué dans ma réponse, le développement de Kurento a fortement ralenti après l'acquisition de Twilio. Je recommande d'utiliser Janus à la place.
jamix
17

S'il vous plaît, vérifiez le RecordRTC

RecordRTC est une licence MIT sur github .

Dmitry
la source
2
C'est assez génial - ma question: est-ce que cela peut enregistrer de la vidéo et de l'audio ensemble (vivre une vraie vidéo plutôt que deux choses séparées?)
Brian Dear
D'accord - génial, mais il semble que les données ne soient enregistrées que séparément.
Dave Hilditch
3
@BrianDear il y a un RecordRTC-together
Mifeng
2
Cette approche fonctionne via Whammy.js dans Chrome. Ceci est problématique car la qualité a tendance à être beaucoup plus basse que l'émulation que Whammy fournit pour le manque de MediaStreamRecorder de Chrome. Ce qui se passe essentiellement, c'est que WhammyRecorder pointe une balise vidéo vers l'URL de l'objet MediaStream, puis prend des instantanés Webp d'un élément de canevas à une certaine fréquence d'images. Il utilise ensuite Whammy pour rassembler toutes ces images dans une vidéo Webm.
Vinay
15

Je pense qu'utiliser kurento ou d'autres MCU uniquement pour enregistrer des vidéos serait un peu exagéré, en particulier compte tenu du fait que Chrome prend en charge l' API MediaRecorder de la v47 et de Firefox depuis la v25. Donc, à cette jonction, vous n'aurez peut-être même pas besoin d'une bibliothèque js externe pour faire le travail, essayez cette démo que j'ai faite pour enregistrer de la vidéo / audio à l'aide de MediaRecorder:

Démo - fonctionnerait dans Chrome et Firefox (volontairement laissé de côté en poussant le blob vers le code du serveur)

Source de code Github

Si vous exécutez Firefox, vous pouvez le tester ici même (besoins de chrome https):

'use strict'

let log = console.log.bind(console),
  id = val => document.getElementById(val),
  ul = id('ul'),
  gUMbtn = id('gUMbtn'),
  start = id('start'),
  stop = id('stop'),
  stream,
  recorder,
  counter = 1,
  chunks,
  media;


gUMbtn.onclick = e => {
  let mv = id('mediaVideo'),
    mediaOptions = {
      video: {
        tag: 'video',
        type: 'video/webm',
        ext: '.mp4',
        gUM: {
          video: true,
          audio: true
        }
      },
      audio: {
        tag: 'audio',
        type: 'audio/ogg',
        ext: '.ogg',
        gUM: {
          audio: true
        }
      }
    };
  media = mv.checked ? mediaOptions.video : mediaOptions.audio;
  navigator.mediaDevices.getUserMedia(media.gUM).then(_stream => {
    stream = _stream;
    id('gUMArea').style.display = 'none';
    id('btns').style.display = 'inherit';
    start.removeAttribute('disabled');
    recorder = new MediaRecorder(stream);
    recorder.ondataavailable = e => {
      chunks.push(e.data);
      if (recorder.state == 'inactive') makeLink();
    };
    log('got media successfully');
  }).catch(log);
}

start.onclick = e => {
  start.disabled = true;
  stop.removeAttribute('disabled');
  chunks = [];
  recorder.start();
}


stop.onclick = e => {
  stop.disabled = true;
  recorder.stop();
  start.removeAttribute('disabled');
}



function makeLink() {
  let blob = new Blob(chunks, {
      type: media.type
    }),
    url = URL.createObjectURL(blob),
    li = document.createElement('li'),
    mt = document.createElement(media.tag),
    hf = document.createElement('a');
  mt.controls = true;
  mt.src = url;
  hf.href = url;
  hf.download = `${counter++}${media.ext}`;
  hf.innerHTML = `donwload ${hf.download}`;
  li.appendChild(mt);
  li.appendChild(hf);
  ul.appendChild(li);
}
      button {
        margin: 10px 5px;
      }
      li {
        margin: 10px;
      }
      body {
        width: 90%;
        max-width: 960px;
        margin: 0px auto;
      }
      #btns {
        display: none;
      }
      h1 {
        margin-bottom: 100px;
      }
<link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<h1> MediaRecorder API example</h1>

<p>For now it is supported only in Firefox(v25+) and Chrome(v47+)</p>
<div id='gUMArea'>
  <div>
    Record:
    <input type="radio" name="media" value="video" checked id='mediaVideo'>Video
    <input type="radio" name="media" value="audio">audio
  </div>
  <button class="btn btn-default" id='gUMbtn'>Request Stream</button>
</div>
<div id='btns'>
  <button class="btn btn-default" id='start'>Start</button>
  <button class="btn btn-default" id='stop'>Stop</button>
</div>
<div>
  <ul class="list-unstyled" id='ul'></ul>
</div>
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

mido
la source
Chrome 49 est le premier à prendre en charge l'API MediaRecorder sans le drapeau.
Octavian Naicu
7

oui, comme vous l'avez compris, MediaStreamRecorder n'est actuellement pas implémenté.

MediaStreamRecorder est une API WebRTC pour l'enregistrement de flux getUserMedia (). Il permet aux applications Web de créer un fichier à partir d'une session audio / vidéo en direct.

vous pouvez également faire comme ceci http://ericbidelman.tumblr.com/post/31486670538/creating-webm-video-from-getusermedia mais l'audio manque une partie.

Konga Raju
la source
1
Oui, et vous pouvez capturer le fichier audio, l'envoyer au serveur et les combiner là-bas pour créer un vrai fichier vidéo côté serveur. Mais cette solution peut être très lente côté client en fonction de la configuration de son ordinateur, car elle doit créer des fichiers image à l'aide d'un canevas ET capturer l'audio, et tout cela dans la RAM ... Btw, l' équipe de Firefox y travaille , alors j'espère qu'ils le publieront bientôt.
Firas
4

Vous pouvez utiliser RecordRTC-together , qui est basé sur RecordRTC.

Il prend en charge l'enregistrement vidéo et audio ensemble dans des fichiers séparés. Vous aurez besoin d'un outil comme ffmpegpour fusionner deux fichiers en un sur le serveur.

Mifeng
la source
2
Il s'agit d'une solution de navigateur, pas côté serveur.
Brad
2

Web Call Server 4 peut enregistrer de l'audio et de la vidéo WebRTC dans un conteneur WebM. L'enregistrement se fait à l'aide du codec Vorbis pour l'audio et du codec VP8 pour la vidéo. Les codecs WebRTC initiaux sont Opus ou G.711 et VP8. Ainsi, l'enregistrement côté serveur nécessite soit un transcodage côté serveur Opus / G.711 vers Vorbis, soit un transcodage VP8-H.264 s'il est nécessaire d'utiliser un autre conteneur, c'est-à-dire AVI.

Bob42
la source
est ce truc commercial?
Stepan Yakovenko
0

Pour mémoire, je n'ai pas non plus assez de connaissances à ce sujet,

Mais j'ai trouvé ça sur le hub Git-

<!DOCTYPE html>
 <html>
<head>
  <title>XSockets.WebRTC Client example</title>
  <meta charset="utf-8" />


<style>
body {

  }
.localvideo {
position: absolute;
right: 10px;
top: 10px;
}

.localvideo video {
max-width: 240px;
width:100%;
margin-right:auto;
margin-left:auto;
border: 2px solid #333;

 }
 .remotevideos {
height:120px;
background:#dadada;
padding:10px; 
}

.remotevideos video{
max-height:120px;
float:left;
 }
</style>
</head>
<body>
<h1>XSockets.WebRTC Client example </h1>
<div class="localvideo">
    <video autoplay></video>
</div>

<h2>Remote videos</h2>
<div class="remotevideos">

</div>
<h2>Recordings  ( Click on your camera stream to start record)</h2>
<ul></ul>


<h2>Trace</h2>
<div id="immediate"></div>
<script src="XSockets.latest.js"></script>
<script src="adapter.js"></script>
<script src="bobBinder.js"></script>
<script src="xsocketWebRTC.js"></script>
<script>
    var $ = function (selector, el) {
        if (!el) el = document;
        return el.querySelector(selector);
    }
    var trace = function (what, obj) {
        var pre = document.createElement("pre");
        pre.textContent = JSON.stringify(what) + " - " + JSON.stringify(obj || "");
        $("#immediate").appendChild(pre);
    };
    var main = (function () {
        var broker;
        var rtc;
        trace("Ready");
        trace("Try connect the connectionBroker");
        var ws = new XSockets.WebSocket("wss://rtcplaygrouund.azurewebsites.net:443", ["connectionbroker"], {
            ctx: '23fbc61c-541a-4c0d-b46e-1a1f6473720a'
        });
        var onError = function (err) {
            trace("error", arguments);
        };
        var recordMediaStream = function (stream) {
            if ("MediaRecorder" in window === false) {
                trace("Recorder not started MediaRecorder not available in this browser. ");
                return;
            }
            var recorder = new XSockets.MediaRecorder(stream);
            recorder.start();
            trace("Recorder started.. ");
            recorder.oncompleted = function (blob, blobUrl) {
                trace("Recorder completed.. ");
                var li = document.createElement("li");
                var download = document.createElement("a");
                download.textContent = new Date();
                download.setAttribute("download", XSockets.Utils.randomString(8) + ".webm");
                download.setAttribute("href", blobUrl);
                li.appendChild(download);
                $("ul").appendChild(li);
            };
        };
        var addRemoteVideo = function (peerId, mediaStream) {
            var remoteVideo = document.createElement("video");
            remoteVideo.setAttribute("autoplay", "autoplay");
            remoteVideo.setAttribute("rel", peerId);
            attachMediaStream(remoteVideo, mediaStream);
            $(".remotevideos").appendChild(remoteVideo);
        };
        var onConnectionLost = function (remotePeer) {
            trace("onconnectionlost", arguments);
            var peerId = remotePeer.PeerId;
            var videoToRemove = $("video[rel='" + peerId + "']");
            $(".remotevideos").removeChild(videoToRemove);
        };
        var oncConnectionCreated = function () {
            console.log(arguments, rtc);
            trace("oncconnectioncreated", arguments);
        };
        var onGetUerMedia = function (stream) {
            trace("Successfully got some userMedia , hopefully a goat will appear..");
            rtc.connectToContext(); // connect to the current context?
        };
        var onRemoteStream = function (remotePeer) {
            addRemoteVideo(remotePeer.PeerId, remotePeer.stream);
            trace("Opps, we got a remote stream. lets see if its a goat..");
        };
        var onLocalStream = function (mediaStream) {
            trace("Got a localStream", mediaStream.id);
            attachMediaStream($(".localvideo video "), mediaStream);
            // if user click, video , call the recorder
            $(".localvideo video ").addEventListener("click", function () {
                recordMediaStream(rtc.getLocalStreams()[0]);
            });
        };
        var onContextCreated = function (ctx) {
            trace("RTC object created, and a context is created - ", ctx);
            rtc.getUserMedia(rtc.userMediaConstraints.hd(false), onGetUerMedia, onError);
        };
        var onOpen = function () {
            trace("Connected to the brokerController - 'connectionBroker'");
            rtc = new XSockets.WebRTC(this);
            rtc.onlocalstream = onLocalStream;
            rtc.oncontextcreated = onContextCreated;
            rtc.onconnectioncreated = oncConnectionCreated;
            rtc.onconnectionlost = onConnectionLost;
            rtc.onremotestream = onRemoteStream;
            rtc.onanswer = function (event) {
            };
            rtc.onoffer = function (event) {
            };
        };
        var onConnected = function () {
            trace("connection to the 'broker' server is established");
            trace("Try get the broker controller form server..");
            broker = ws.controller("connectionbroker");
            broker.onopen = onOpen;
        };
        ws.onconnected = onConnected;
    });
    document.addEventListener("DOMContentLoaded", main);
</script>

Sur la ligne numéro 89 dans mon code de cas OnrecordComplete ajoute en fait un lien de fichier enregistreur, si vous cliquez sur ce lien, il lancera le téléchargement, vous pouvez enregistrer ce chemin sur votre serveur sous forme de fichier.

Le code d'enregistrement ressemble à ceci

recorder.oncompleted = function (blob, blobUrl) {
                trace("Recorder completed.. ");
                var li = document.createElement("li");
                var download = document.createElement("a");
                download.textContent = new Date();
                download.setAttribute("download", XSockets.Utils.randomString(8) + ".webm");
                download.setAttribute("href", blobUrl);
                li.appendChild(download);
                $("ul").appendChild(li);
            };

Le blobUrl contient le chemin. J'ai résolu mon problème avec ceci, j'espère que quelqu'un trouvera cela utile

uniqueNt
la source
-4

Techniquement, vous pouvez utiliser FFMPEG sur le backend pour mélanger la vidéo et l'audio

EugeneB
la source
7
ouais mais comment les y amener?
Eddie Monge Jr