如何使用 webRTC 和基于服务器的对等连接录制网络摄像头和音频

Posted

技术标签:

【中文标题】如何使用 webRTC 和基于服务器的对等连接录制网络摄像头和音频【英文标题】:How to record webcam and audio using webRTC and a server-based Peer connection 【发布时间】:2013-05-10 09:05:32 【问题描述】:

我想录制用户的网络摄像头和音频并将其保存到服务器上的文件中。然后这些文件将能够提供给其他用户。

播放没有问题,但是在录制内容时遇到问题。

我的理解是getUserMedia.record()函数还没有写出来——目前只提出了一个提案。

我想使用 PeerConnectionAPI 在我的服务器上创建对等连接。我知道这有点 hacky,但我认为应该可以在服务器上创建对等点并记录客户端对等点发送的内容。

如果可能的话,我应该能够将此数据保存为 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”。 是的,我正在密切关注它——当有合适的解决方案时,我会更新这个帖子。 如果你得到上述问题的解决方案,请与我分享,谢谢 有没有人能够通过一些服务器端 RTC 魔法获取 MediaStream 字节? 【参考方案1】:

是的,正如您所理解的,MediaStreamRecorder 目前尚未实现。

MediaStreamRecorder 是一个用于记录 getUserMedia() 流的 WebRTC API。它允许网络应用从实时音频/视频会话中创建文件。

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

【讨论】:

是的,您可以捕获音频文件,将其发送到服务器,然后将它们组合起来,在服务器端创建一个真实的视频文件。但是这个解决方案在客户端可能会很慢,具体取决于它的计算机配置,因为它必须使用画布创建图像文件并捕获音频,所有这些都在 RAM 中......顺便说一句,@ 987654322@,所以希望他们很快就会发布它。【参考方案2】:

请检查RecordRTC

RecordRTC 在github 获得 MIT 许可。

【讨论】:

这太棒了——我的问题:可以同时录制视频和音频(直播真实的视频而不是两个单独的东西?) 同意 - 很棒,但看起来它只是单独记录数据。 @BrianDear 有一个RecordRTC-together 这种方法通过 Chrome 中的 Whammy.js 工作。这是有问题的,因为与 Whammy 为 Chrome 缺少 MediaStreamRecorder 提供的仿真相比,质量往往要低得多。本质上发生的事情是 WhammyRecorder 将视频标签指向 MediaStream 对象 URL,然后以特定帧速率拍摄画布元素的 webp 快照。然后它使用 Whammy 将所有这些帧组合成一个 webm 视频。【参考方案3】:

您可以使用基于 RecordRTC 的RecordRTC-together。

它支持在单独的文件中同时录制视频和音频。您将需要像 ffmpeg 这样的工具在服务器上将两个文件合并为一个。

【讨论】:

这是一个浏览器解决方案,不是服务器端的。【参考方案4】:

从技术上讲,您可以在后端使用 FFMPEG 来混合视频和音频

【讨论】:

是的,但是你怎么把它们弄到那里?【参考方案5】:

您绝对应该看看Kurento。它提供了一个 WebRTC 服务器基础设施,允许您从 WebRTC 提要等进行记录。您还可以在here 中找到您正在计划的应用程序的一些示例。为该演示添加录制功能非常容易,并将媒体文件存储在 URI(本地磁盘或任何地方)中。

该项目在 LGPL Apache 2.0

下获得许可

编辑 1

从这篇文章开始,我们添加了一个新教程,展示了如何在几个场景中添加记录器

kurento-hello-world-recording:简单的录音教程,展示了录音端点的不同能力。 kurento-one2one-recording:如何在媒体服务器中记录一对一的通信。 kurento-hello-world-repository:使用外部存储库记录文件。

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

【讨论】:

@Redtopia 在最近的一些负载测试中,我们能够在 i5/16GB RAM 上获得 150 个 webrtc 的 one2one 连接。您可以期待这些数字在未来会更好,但不要期待奇迹:SRTP 正在进行大量加密,这要求很高。我们正在研究硬件加速的加密/解密,而且数字会更高,虽然在我们更彻底地测试之前我不能向你保证它会有多好,但我们预计会有 3 倍的改进 @user344146 这可能是我在回答。你介意分享那个帖子的链接吗?如果你得到了那个答案,那可能是因为你问了一些已经存在或在列表中的东西。看起来您正在尝试编译 SNAPSHOT 版本。这些工件不会在中心发布,因此您可以查看教程的版本或使用我们的内部开发存储库。这已在列表中多次回答,文档中有一个关于使用开发版本的条目......我们花时间编写它,所以您能花时间阅读它会很高兴。跨度> 我只是用Kurento来做这样的录音。我并不复杂,但需要一点时间来理解这个概念——因为有些文档真的很卑鄙——找到我可以发送给 kurento 的内容,或者事件的描述等等有时真的很令人沮丧。但无论如何——像这样的开放项目确实是一项很棒的工作,值得使用。 Kurento 仅在 linux 中工作(windows 版本不是官方版本,不能使用全部功能)。 找到了上述问题的答案(这里发给其他人),Kurento目前支持JDK 7.0,不是必须依赖Ubuntu 14.04,应该也支持以后的版本,但是Kurento是未在其他版本的 Ubuntu/其他 linux 版本上正式测试。 Kurento 还发布了 64 位版本,以便安装,但是您可以安装 32 位版本的服务器,但您必须先构建它。 不幸的是,正如我在回答中所说,Kurento 的发展在 Twilio 收购后严重放缓。我建议改用 Janus。【参考方案6】:

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

【讨论】:

这是商业的东西吗?【参考方案7】:

我认为仅使用 kurento 或其他 MCU 来录制视频有点矫枉过正,特别是考虑到 Chrome 从 v47 和 Firefox 自 v25 开始支持 MediaRecorder API 的事实。所以在这个路口,你甚至可能不需要外部 js 库来完成这项工作,试试我制作的这个演示,使用 MediaRecorder 录制视频/音频:

Demo - 可以在 chrome 和 firefox 中工作(故意忽略了将 blob 推送到服务器代码)

Github Code Source

如果运行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。【参考方案8】:

为了记录,我对此也没有足够的了解,

但我在 Git hub 上找到了这个——

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

【讨论】:

【参考方案9】:

目前浏览器支持客户端录制。

https://webrtc.github.io/samples/

可以在连接结束后通过一些HTTP请求上传将录制的文件推送到服务器。

https://webrtc.github.io/samples/src/content/getusermedia/record/ https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/record

这有一些缺点,如果用户只是关闭选项卡而不在后端运行这些操作,它可能无法将文件完全上传到服务器。

作为更稳定的解决方案,Ant Media Server 可以在服务器端录制流,录制功能是 Ant Media Server 的基本功能之一。

antmedia.io

注意:我是蚂蚁传媒团队的一员。

【讨论】:

以上是关于如何使用 webRTC 和基于服务器的对等连接录制网络摄像头和音频的主要内容,如果未能解决你的问题,请参考以下文章

使用 webrtc 时 ReplayKit 不起作用

WebRTC 对等服务器连接

检测 WebRTC 连接中的离线对等点

WebRTC:对多个对等连接使用相同的 SDP?

web技术分享| 实现WebRTC多个对等连接

web技术分享| 实现WebRTC多个对等连接