如何使用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()对您有用吗?它不在我的新浏览器上。
菲拉斯

否-我也尝试过“ Google Canary”。
Dave Hilditch

是的,我一直在密切关注-如果有适当的解决方案,我将更新此线程。
Dave Hilditch 2013年

2
如果您有上述问题的解决方案,请与我分享,谢谢
Muhammad

2
有没有人能够通过某些服务器端RTC魔术来获取MediaStream字节?
Vinay

Answers:


44

您绝对应该看看Kurento。它提供了WebRTC服务器基础结构,使您可以从WebRTC提要中进行录制以及更多内容。您还可以在此处找到您计划的应用程序的一些示例。向该演示添加录制功能,并将媒体文件存储在URI(本地磁盘或其他位置)中,确实很容易。

该项目已获得LGPL Apache 2.0的许可


编辑1

从这篇文章开始,我们添加了一个新的教程,该教程显示了如何在几种情况下添加记录器

免责声明:我是开发Kurento的团队的一员。


2
@Redtopia在最近的一些负载测试中,我们能够在i5 / 16GB RAM上获得150个webrtc的one2one连接。您可以期望这些数字将来会更好,但是不要指望奇迹:SRTP正在进行很多加密,这是有要求的。我们正在研究硬件加速的加密/解密,而且这个数字还会越来越高,尽管我无法保证在经过更彻底的测试之前会好得多,但我们希望将其提高3倍
igracia 2015年

2
@ user344146那可能是我的回答。您介意分享该帖子的链接吗?如果您得到了答案,那可能是因为您询问的内容已经存在或在列表中。看来您正在尝试编译SNAPSHOT版本。这些工件不会在中央发布,因此您可以签出教程的发行版或使用我们的内部开发库。列表中已多次回答该问题,文档中有关于使用开发版本的条目。我们花了一些时间来编写它,因此花点时间阅读它会很高兴。
igracia

2
我只是用Kurento来做这样的录音。我并不复杂,但是需要一点时间来理解这个概念-因为某些文档确实是很卑鄙的-并且找到我可以发送给kurento的内容或事件的描述等有时会真的令人沮丧。但是无论如何-像这样的开放项目确实很棒,值得使用。Kurento仅在Linux中工作(Windows版本不是官方的,不能使用全部功能)。
克里斯汀(Krystian)

1
找到上述问题的答案(其他地方请点击此处),Kurento当前支持JDK 7.0,不是必须依赖Ubuntu 14.04,它也应该支持更高版本,但是Kurento尚未在其他版本的Ubuntu上进行正式测试/ other Linux版本。Kurento还发布了64位版本,以方便安装,但是您可以安装32位版本的服务器,但必须首先构建它。
Bilbo Baggins

1
不幸的是,正如我的回答所述,在收购Twilio之后,Kurento的发展严重放缓。我建议改用Janus。
jamix

17

请检查RecordRTC

RecordRTC是麻省理工学院许可的github


2
那真是太棒了-我的问题是:能否同时录制视频和音频(直播真实的视频而不是两个单独的东西?)
Brian Dear

同意-很棒,但看起来它仅单独记录数据。
Dave Hilditch

3
@BrianDear有一个RecordRTC在一起
弥缝

2
这种方法通过Chrome中的Whammy.js起作用。这是有问题的,因为根据Whammy提供的模拟,Chrome缺少MediaStreamRecorder,因此质量往往要低得多。本质上发生的是WhammyRecorder将视频标记指向MediaStream对象URL,然后以特定帧速率拍摄canvas元素的webp快照。然后,它使用Whammy将所有这些帧放到一个webm视频中。
Vinay

15

我认为仅使用kurento或其他MCU来录制视频会有些过头,尤其是考虑到Chrome自v25起已支持v47和Firefox的MediaRecorder API。因此,在这个路口,您甚至可能不需要外部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支持的标志。
Octavian Naicu '16

7

是的,如您所知,MediaStreamRecorder当前未实现。

MediaStreamRecorder是一个WebRTC API,用于记录getUserMedia()流。它允许Web应用程序通过实时音频/视频会话创建文件。

或者,您也可以这样做http://ericbidelman.tumblr.com/post/31486670538/creating-webm-video-from-getusermedia,但是音频缺少一部分。


1
是的,您可以捕获音频文件,将其发送到服务器,然后在此处进行合并以在服务器端创建真实的视频文件。但是,根据客户端的计算机配置,此解决方案在客户端可能会非常慢,因为它必须使用画布创建图像文件并捕获音频,并且所有这些都在RAM中...顺便说一句,firefox团队正在努力,因此希望他们会尽快发布。
菲拉斯(Firas)


2

Web Call Server 4可以将WebRTC音频和视频记录到WebM容器。录制使用音频的Vorbis编解码器和视频的VP8编解码器完成。初始WebRTC编解码器是Opus或G.711和VP8。因此,如果需要使用另一个容器(即AVI),则服务器端录制需要将Opus / G.711进行Vorbis服务器端转码或VP8-H.264转码。


这是商业物品吗?
Stepan Yakovenko

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>

在我的案例代码OnrecordComplete中的第89行上,实际上附加了记录器文件的链接,如果您单击该链接将开始下载,则可以将该路径保存为文件到服务器。

录制代码看起来像这样

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保存路径。我用这个解决了我的问题,希望有人会发现这个有用


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.