Як записати веб-камеру та аудіо за допомогою webRTC та серверного з’єднання Peer


90

Я хотів би записати веб-камеру та звук користувачів та зберегти їх у файл на сервері. Потім ці файли можна буде подавати іншим користувачам.

У мене немає проблем із відтворенням, однак у мене виникають проблеми з отриманням вмісту для запису.

Я розумію, що .record()функція getUserMedia ще не написана - поки що для неї зроблено лише пропозицію.

Я хотів би створити однорангове з’єднання на своєму сервері за допомогою PeerConnectionAPI. Я розумію, що це трохи невдало, але я думаю, що на сервері має бути можливість створити рівний і записати, що надсилає клієнт-рівний.

Якщо це можливо, я мав би змогу зберегти ці дані у форматі flv або будь-якому іншому відео.

Мої переваги - насправді записати веб-камеру + аудіо на стороні клієнта, щоб дозволити клієнту повторно записати відео, якщо їм не сподобалася перша спроба перед завантаженням. Це також дозволить перервати мережеві з'єднання. Я бачив якийсь код, який дозволяє записувати окремі "зображення" з веб-камери, надсилаючи дані на полотно - це круто, але мені також потрібно аудіо.

Ось код клієнтської сторони, який я маю на даний момент:

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

У мене те саме питання. Чи працює метод getRecordedData () для вас? Це не в моїх оновлених браузерах.
Фірас

Ні - я теж спробував "Google Canary".
Дейв Хілдітч,

Так, я пильно стежу за цим - оновлю цю тему, коли буде відповідне рішення.
Dave Hilditch

2
якщо ви вирішили вищезазначене питання, поділіться зі мною, дякую
Мухаммед

2
Хтось зміг отримати байт MediaStream за допомогою якоїсь магії RTC на стороні сервера?
Vinay 02

Відповіді:


44

Ви обов’язково повинні поглянути на Куренто . Він забезпечує серверну інфраструктуру WebRTC, яка дозволяє записувати з каналу WebRTC та багато іншого. Ви також можете знайти кілька прикладів для програми, яку ви плануєте тут . Додати можливості запису до цієї демонстрації та зберегти медіафайл в URI (локальний диск або де завгодно) дуже просто.

Проект ліцензований під LGPL Apache 2.0


РЕДАГУВАТИ 1

З цієї публікації ми додали новий підручник, який показує, як додати реєстратор у кількох сценаріях

  • kurento-hello-world-Record : простий підручник із запису, що показує різні можливості кінцевої точки запису.
  • kurento-one2one-запис : Як записати один на один спілкування на медіа-сервері.
  • kurento-hello-world-repository : використовуйте зовнішнє сховище для запису файлу.

Застереження: Я є частиною команди, яка розробляє Kurento.


2
@Redtopia У деяких недавніх тестах навантаження нам вдалося отримати 150 one2one підключень webrtc на оперативній пам'яті i5 / 16GB. Ви можете очікувати, що ці цифри стануть кращими в майбутньому, але не чекайте чудес: для SRTP відбувається багато шифрування, і це вимогливо. Ми розглядаємо апаратно прискорене шифрування / дешифрування, і цифри будуть зростати, і хоча я не можу запевнити вас, наскільки це буде краще, поки ми не перевіримо його ретельніше, ми очікуємо покращення в 3 рази
igracia

2
@ user344146 Це, мабуть, я відповідав. Не могли б ви поділитися посиланням на цю публікацію? Якщо ви отримали таку відповідь, можливо, це тому, що ви запитали щось, що вже було там або у списку. Схоже, ви намагалися скомпілювати версію SNAPSHOT. Ці артефакти не публікуються в центральній частині, тож ви можете перевірити випуск навчальних посібників або скористатися нашим внутрішнім депо розробником. На це вже багато разів давали відповіді у списку, у документації є запис про роботу з версіями розробки ... Ми знайшли час, щоб написати це, тому було б непогано, щоб ви знайшли час, щоб прочитати це.
igracia

2
Я просто використовую Kurento, щоб зробити такий запис. Я не складний, але мені потрібно трохи часу, щоб зрозуміти концепцію - адже деякі документи справді підлі - і пошук того, що я можу надіслати куренто, або опис подій тощо може бути часом справді неприємним. Але в будь-якому випадку - такий відкритий проект - це дійсно чудова робота, яку варто використати. Kurento працює лише в Linux (версія Windows не є офіційною і не працює з повною функціональністю).
Кристян

1
Знайдено відповіді на вищезазначені запитання (розміщення тут для інших), на даний момент Kurento підтримує JDK 7.0. Це не означає, що він повинен залежати від Ubuntu 14.04, він також повинен підтримувати пізніші версії, але Kurento офіційно не тестується на інших версіях Ubuntu / інша версія Linux. Також Kurento випускає 64-розрядні версії, як легко доступні для isntallation, однак ви можете встановити 32-розрядну версію сервера, але спочатку його потрібно створити.
Більбо Беггінс,

1
На жаль, як зазначено у моїй відповіді, розвиток Kurento сильно сповільнився після придбання Twilio. Натомість рекомендую використовувати Janus.
jamix 03

17

Будь ласка, перевірте RecordRTC

RecordRTC має ліцензію MIT на Github .


2
Це досить приголомшливо - моє запитання: чи можна записувати відео та аудіо разом (жити справжнім відео, а не двома окремими речами?)
Брайан Шановний

Погодився - приголомшливо, але схоже, що дані записуються лише окремо.
Dave Hilditch

3
@BrianDear є один RecordRTC-разом
Mifeng

2
Цей підхід працює через Whammy.js у Chrome. Це проблематично, оскільки якість, як правило, набагато нижча від емуляції, яку Whammy передбачає через відсутність у Chrome MediaStreamRecorder. По суті, WhammyRecorder спрямовує відеотег на URL-адресу об’єкта MediaStream, а потім робить веб-знімки елемента полотна з певною частотою кадрів. Потім він використовує Whammy, щоб об'єднати всі ці кадри у веб-відео.
Vinay 02

15

Я вважаю, що використання kurento або інших мікроконтролерів лише для запису відео було б надмірним, особливо враховуючи той факт, що Chrome підтримує MediaRecorder API від v47 та Firefox з v25. Отже, на цьому стику вам може навіть не знадобитися зовнішня бібліотека js для роботи, спробуйте цю демонстрацію, яку я зробив для запису відео / аудіо за допомогою MediaRecorder:

Демонстрація - буде працювати в chrome та firefox (навмисно не використовуючи push blob до коду сервера)

Джерело коду Github

Якщо ви запускаєте Firefox, ви можете протестувати його тут (потрібно 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>


Chrome 49 є першим, хто підтримує API MediaRecorder без прапора.
Октавіан Найку

7

так, як ви зрозуміли, MediaStreamRecorder наразі не реалізовано.

MediaStreamRecorder - це WebRTC API для запису потоків getUserMedia (). Це дозволяє веб-програмам створювати файли з аудіо / відео сеансу.

в якості альтернативи вам може сподобатися цей http://ericbidelman.tumblr.com/post/31486670538/creating-webm-video-from-getusermedia, але звук відсутній.


1
Так, і ви можете захопити аудіофайл, надіслати його на сервер і об'єднати там, щоб створити справжній відеофайл на стороні сервера. Але це рішення може бути дуже повільним на стороні клієнта, залежно від його конфігурації комп'ютера, оскільки йому доводиться створювати файли зображень за допомогою полотна І захоплювати аудіо, і все це в оперативній пам'яті ... До речі, команда Firefox над цим працює , тому, сподіваємось, вони скоро його випустять.
Фірас

4

Ви можете використовувати RecordRTC-разом , який базується на RecordRTC.

Він підтримує запис відео та аудіо разом у окремі файли. Вам знадобиться інструмент на зразок ffmpegоб’єднання двох файлів в один на сервері.


2
Це рішення для браузера, а не на сервері.
Бред

2

Web Call Server 4 може записувати аудіо та відео WebRTC в контейнер WebM. Запис виконується за допомогою кодека Vorbis для аудіо та кодека VP8 для відео. Ініціальними кодеками WebRTC є Opus або G.711 та VP8. Отже, для запису на стороні сервера потрібне перекодування на стороні сервера Opus / G.711 - Vorbis або перекодування VP8-H.264, якщо необхідно використовувати інший контейнер, тобто AVI.


це комерційні речі?
Степан Яковенко

0

Для протоколу я також не маю достатньо знань про це,

Але я знайшов це на концентраторі 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>

У рядку номер 89 у моєму випадку OnrecordComplete фактично додає посилання на файл самопису, якщо ви натиснете на це посилання, воно почне завантаження, ви можете зберегти цей шлях на свій сервер як файл.

Код запису виглядає приблизно так

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);
            };

BlobUrl утримує шлях. Я вирішив свою проблему з цим, сподіваюся, комусь це буде корисно


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.