Как записывать веб-камеру и аудио с помощью webRTC и однорангового соединения на основе сервера


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 ()? Его нет в моих недавно обновленных браузерах.
Firas

Нет, я тоже пробовал "Google Canary".
Дэйв Хилдитч

Да, я внимательно слежу за этим - я обновлю эту ветку, когда будет подходящее решение.
Дэйв Хилдитч,

2
Если у вас есть решение вышеперечисленного вопроса, поделитесь со мной, спасибо
Мухаммад

2
Кто-нибудь смог получить байты MediaStream с помощью какой-то серверной магии RTC?
Vinay

Ответы:


44

Вам обязательно стоит взглянуть на Куренто . Он предоставляет инфраструктуру сервера WebRTC, которая позволяет записывать из веб-канала WebRTC и многое другое. Вы также можете найти некоторые примеры для приложения , которое вы планируете здесь . В эту демонстрацию действительно легко добавить возможности записи и сохранить медиафайл в URI (на локальном диске или где-то еще).

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


ИЗМЕНИТЬ 1

После этого поста мы добавили новый учебник, в котором показано, как добавить регистратор в нескольких сценариях.

Отказ от ответственности: я часть команды, которая разрабатывает Kurento.


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

2
@ user344146 Вероятно, это я отвечал. Не могли бы вы поделиться ссылкой на этот пост? Если вы получили такой ответ, вероятно, это потому, что вы спросили что-то, что уже было там или в списке. Похоже, вы пытались скомпилировать версию SNAPSHOT. Эти артефакты не публикуются в центре, поэтому вы либо проверяете выпуск учебных пособий, либо используете наше внутреннее репозиторий для разработчиков. На этот вопрос в списке много раз отвечали, в документации есть запись о работе с разрабатываемыми версиями ... Мы нашли время, чтобы написать его, поэтому было бы неплохо с вашей стороны найти время, чтобы прочитать его.
igracia

2
Я просто использую Куренто, чтобы сделать такую ​​запись. Это несложно, но мне нужно немного времени, чтобы понять концепцию, потому что некоторые документы действительно значительны, и поиск того, что я могу отправить в kurento, или описания событий и так далее, иногда может быть очень неприятным. Но в любом случае - такой открытый проект - действительно отличная работа, и ее стоит использовать. Куренто работает только в Linux (версия для Windows не является официальной и не работает с полной функциональностью).
Krystian

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

1
К сожалению, как сказано в моем ответе, после приобретения Twilio разработка Kurento сильно замедлилась. Вместо этого я рекомендую использовать 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

15

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

Демо - будет работать в chrome и firefox (намеренно пропущено нажатие 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 - первый браузер, поддерживающий MediaRecorder API без флага.
Октавиан Найку

7

да, как вы поняли, MediaStreamRecorder на данный момент не реализован.

MediaStreamRecorder - это API WebRTC для записи потоков 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.