Cách ghi lại webcam và âm thanh bằng webRTC và kết nối ngang hàng dựa trên máy chủ


90

Tôi muốn ghi lại webcam và âm thanh của người dùng và lưu nó vào một tệp trên máy chủ. Những tệp này sau đó sẽ có thể được phân phát cho những người dùng khác.

Tôi không gặp vấn đề gì với việc phát lại, tuy nhiên, tôi đang gặp sự cố khi ghi nội dung.

Sự hiểu biết của tôi là .record()hàm getUserMedia vẫn chưa được viết - chỉ có một đề xuất được đưa ra cho nó cho đến nay.

Tôi muốn tạo kết nối ngang hàng trên máy chủ của mình bằng PeerConnectionAPI. Tôi hiểu điều này hơi khó hiểu, nhưng tôi nghĩ có thể tạo một ứng dụng ngang hàng trên máy chủ và ghi lại những gì ứng dụng khách gửi.

Nếu điều này có thể xảy ra, thì tôi sẽ có thể lưu dữ liệu này vào flv hoặc bất kỳ định dạng video nào khác.

Sở thích của tôi thực sự là ghi lại phía máy khách webcam + âm thanh, để cho phép máy khách quay lại video nếu họ không thích lần thử đầu tiên trước khi tải lên. Điều này cũng sẽ cho phép gián đoạn kết nối mạng. Tôi đã thấy một số mã cho phép ghi lại các 'hình ảnh' riêng lẻ từ webcam bằng cách gửi dữ liệu đến canvas - điều đó thật tuyệt, nhưng tôi cũng cần âm thanh.

Đây là mã phía máy khách mà tôi có cho đến nay:

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

Tôi có cùng một vấn đề. Phương thức getRecordedData () có phù hợp với bạn không? Nó không có trên các trình duyệt mới-cập nhật của tôi.
Firas

Không - Tôi cũng đã thử 'Google Canary'.
Dave Hilditch

Vâng, tôi đang theo dõi sát sao - Tôi sẽ cập nhật chủ đề này khi có giải pháp thích hợp.
Dave Hilditch

2
nếu bạn có những giải pháp của câu hỏi trên đây đều có xin vui lòng với tôi, Cảm ơn
Muhammad

2
Có ai có thể nhận được các byte MediaStream thông qua một số phép thuật RTC phía máy chủ không?
Vinay

Câu trả lời:


44

Bạn chắc chắn nên xem Kurento . Nó cung cấp cơ sở hạ tầng máy chủ WebRTC cho phép bạn ghi lại từ nguồn cấp WebRTC và hơn thế nữa. Bạn cũng có thể tìm thấy một số ví dụ cho ứng dụng bạn đang lập kế hoạch tại đây . Thực sự dễ dàng thêm khả năng ghi vào bản trình diễn đó và lưu trữ tệp phương tiện trong URI (đĩa cục bộ hoặc bất cứ nơi nào).

Dự án được cấp phép theo LGPL Apache 2.0


CHỈNH SỬA 1

Kể từ bài đăng này, chúng tôi đã thêm một hướng dẫn mới chỉ ra cách thêm đầu ghi trong một vài trường hợp

Tuyên bố từ chối trách nhiệm: Tôi là thành viên của nhóm phát triển Kurento.


2
@Redtopia Trong một số thử nghiệm tải gần đây, chúng tôi có thể nhận được 150 kết nối one2one của webrtc trên RAM i5 / 16GB. Bạn có thể mong đợi rằng những con số này sẽ tốt hơn trong tương lai, nhưng đừng mong đợi điều kỳ diệu: có rất nhiều mã hóa đang diễn ra cho SRTP và điều đó đang đòi hỏi rất nhiều. Chúng tôi đang xem xét tăng tốc phần cứng mã hóa / giải mã, và những con số sẽ đi cao hơn, và mặc dù tôi không thể đảm bảo với bạn nó sẽ là như thế nào tốt hơn nhiều cho đến khi chúng tôi kiểm tra kỹ hơn, chúng tôi mong đợi một sự cải thiện 3x
igracia

2
@ user344146 Đó có thể là câu trả lời của tôi. Bạn có phiền chia sẻ một liên kết đến bài viết đó không? Nếu bạn nhận được câu trả lời đó, có thể là do bạn đã hỏi điều gì đó đã có sẵn hoặc trong danh sách. Có vẻ như bạn đang cố gắng biên dịch một phiên bản SNAPSHOT. Những tạo tác đó không được xuất bản ở trung tâm, vì vậy bạn có thể kiểm tra bản phát hành của các hướng dẫn hoặc sử dụng kho phát triển nội bộ của chúng tôi. Điều này đã được trả lời trong danh sách nhiều lần, có một mục trong tài liệu về cách làm việc với các phiên bản phát triển ... Chúng tôi đã dành thời gian để viết nó, vì vậy rất vui nếu bạn dành thời gian đọc nó.
igracia

2
Tôi chỉ sử dụng Kurento để ghi âm như vậy. Tôi không phức tạp, nhưng cần một chút thời gian, để hiểu khái niệm - bởi vì một số tài liệu thực sự có ý nghĩa - và việc tìm kiếm những gì tôi có thể gửi cho kurento, hoặc mô tả các sự kiện, v.v. đôi khi có thể thực sự khiến bạn bực bội. Nhưng dù sao thì - một dự án mở như thế này thực sự là một công việc tuyệt vời và đáng để sử dụng. Kurento chỉ hoạt động trong linux (phiên bản windows không phải là chính thức và không hoạt động với đầy đủ chức năng).
Krystian

1
Đã tìm thấy câu trả lời cho các câu hỏi trên (đăng ở đây cho những người khác), Kurento hiện hỗ trợ JDK 7.0, không phải là nó phải phụ thuộc vào Ubuntu 14.04, nó cũng sẽ hỗ trợ các phiên bản sau, nhưng Kurento không được thử nghiệm chính thức trên các phiên bản Ubuntu khác / phiên bản linux khác. Ngoài ra, Kurento cũng phát hành phiên bản 64 bit sẵn có cho isntallation, tuy nhiên bạn có thể cài đặt phiên bản máy chủ 32 bit nhưng bạn phải xây dựng nó trước.
Bilbo Baggins

1
Thật không may, như đã nêu trong câu trả lời của tôi, sự phát triển của Kurento đã chậm lại nghiêm trọng sau thương vụ mua lại Twilio. Tôi khuyên bạn nên sử dụng Janus thay thế.
jamix

17

Vui lòng kiểm tra RecordRTC

RecordRTC được MIT cấp phép trên github .


2
Đó là khá tuyệt vời - câu hỏi của tôi: rằng video ghi lại và cùng nhau âm thanh có thể (sống một đoạn video thực chứ không phải là hai việc riêng biệt?)
Brian Thưa

Đồng ý - thật tuyệt, nhưng có vẻ như nó chỉ ghi dữ liệu riêng lẻ.
Dave Hilditch

3
@BrianDear có một RecordRTC-cùng nhau
Mifeng

2
Phương pháp này hoạt động thông qua Whammy.js trong Chrome. Điều này là có vấn đề vì chất lượng có xu hướng thấp hơn nhiều so với mô phỏng mà Whammy cung cấp do Chrome thiếu MediaStreamRecorder. Điều cơ bản xảy ra là WhammyRecorder trỏ một thẻ video tới URL đối tượng MediaStream và sau đó chụp ảnh nhanh trên web của một phần tử canvas ở một tốc độ khung hình nhất định. Sau đó, nó sử dụng Whammy để ghép tất cả các khung hình đó lại với nhau thành một video webm.
Vinay

15

Tôi tin rằng việc sử dụng kurento hoặc các MCU khác chỉ để quay video sẽ hơi quá mức cần thiết, đặc biệt là khi xem xét thực tế là Chrome có hỗ trợ API MediaRecorder từ v47 và Firefox kể từ v25. Vì vậy, tại điểm giao nhau này, bạn thậm chí có thể không cần thư viện js bên ngoài để thực hiện công việc, hãy thử bản trình diễn này mà tôi đã thực hiện để quay video / âm thanh bằng MediaRecorder:

Demo - sẽ hoạt động trong chrome và firefox (cố tình bỏ qua việc đẩy blob tới mã máy chủ)

Nguồn mã Github

Nếu đang chạy firefox, bạn có thể tự kiểm tra nó tại đây (cần 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 là ứng dụng đầu tiên hỗ trợ API MediaRecorder mà không có cờ.
Octavian Naicu

7

vâng, như bạn đã hiểu, MediaStreamRecorder hiện chưa được hoàn thiện.

MediaStreamRecorder là một API WebRTC để ghi các luồng getUserMedia (). Nó cho phép các ứng dụng web tạo tệp từ phiên âm thanh / video trực tiếp.

hoặc bạn có thể làm như thế này http://ericbidelman.tumblr.com/post/31486670538/creating-webm-video-from-getusermedia nhưng âm thanh bị thiếu một phần.


1
Đúng, và bạn có thể chụp tệp âm thanh, gửi đến máy chủ và kết hợp chúng ở đó để tạo tệp video thực ở phía máy chủ. Nhưng giải pháp này có thể rất chậm ở phía máy khách tùy thuộc vào cấu hình máy tính của nó, vì nó phải tạo tệp hình ảnh bằng canvas VÀ thu âm thanh, và tất cả điều này trong RAM ... Btw, nhóm firefox đang làm việc trên nó , vì vậy hy vọng họ sẽ phát hành nó sớm.
Firas

4

Bạn có thể sử dụng RecordRTC-cùng nhau , dựa trên RecordRTC.

Nó hỗ trợ ghi video và âm thanh cùng nhau trong các tệp riêng biệt. Bạn sẽ cần công cụ như ffmpeghợp nhất hai tệp thành một trên máy chủ.


2
Đây là một giải pháp trình duyệt, không phải phía máy chủ.
Brad

2

Web Call Server 4 có thể ghi âm thanh và video WebRTC vào vùng chứa WebM. Quá trình ghi được thực hiện bằng codec Vorbis cho âm thanh và codec VP8 cho video. Các codec WebRTC Iniitial là Opus hoặc G.711 và VP8. Vì vậy, bản ghi phía máy chủ yêu cầu chuyển mã phía máy chủ Opus / G.711 sang Vorbis hoặc chuyển mã VP8-H.264 nếu cần sử dụng một vùng chứa khác, tức là AVI.


đây là công cụ thương mại?
Stepan Yakovenko

0

Đối với hồ sơ, tôi cũng không có đủ kiến ​​thức về điều này,

Nhưng tôi tìm thấy cái này trên 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>

Trên Dòng số 89 trong mã trường hợp của tôi OnrecordComplete thực sự thêm một liên kết của tệp trình ghi, nếu bạn nhấp vào liên kết đó, nó sẽ bắt đầu tải xuống, bạn có thể lưu đường dẫn đó vào máy chủ của mình dưới dạng tệp.

Mã ghi trông giống như thế này

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 giữ đường dẫn. Tôi đã giải quyết vấn đề của mình với điều này, hy vọng ai đó sẽ thấy điều này hữu ích


-4

Về mặt kỹ thuật, bạn có thể sử dụng FFMPEG trên phụ trợ để trộn video và âm thanh


7
vâng, nhưng làm thế nào để bạn đưa chúng đến đó?
Eddie Monge Jr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.