Cara merekam webcam dan audio menggunakan webRTC dan koneksi Peer berbasis server

90

Saya ingin merekam webcam dan audio pengguna dan menyimpannya ke file di server. File-file ini kemudian dapat disajikan kepada pengguna lain.

Saya tidak memiliki masalah dengan pemutaran, namun saya mengalami masalah dalam mendapatkan konten untuk direkam.

Pemahaman saya adalah bahwa .record()fungsi getUserMedia belum ditulis - sejauh ini hanya proposal yang dibuat.

Saya ingin membuat koneksi peer di server saya menggunakan PeerConnectionAPI. Saya mengerti ini agak hacky, tetapi saya pikir itu harus memungkinkan untuk membuat rekan di server dan mencatat apa yang dikirim oleh klien-peer.

Jika ini memungkinkan, saya kemudian dapat menyimpan data ini ke flv atau format video lainnya.

Preferensi saya sebenarnya adalah merekam webcam + sisi klien audio, untuk memungkinkan klien merekam ulang video jika mereka tidak suka upaya pertama mereka sebelum mengunggah. Ini juga akan memungkinkan gangguan dalam koneksi jaringan. Saya telah melihat beberapa kode yang memungkinkan perekaman 'gambar' individu dari webcam dengan mengirimkan data ke kanvas - itu keren, tetapi saya juga membutuhkan audio.

Inilah kode sisi klien yang saya miliki sejauh ini:

  <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>
Dave Hilditch
sumber
Saya memiliki masalah yang sama. Apakah metode getRecordedData () berfungsi untuk Anda? Itu tidak ada di browser saya yang baru diperbarui.
Firas
Tidak - Saya juga mencoba 'Google Canary'.
Dave Hilditch
Ya, saya terus mengawasinya - saya akan memperbarui utas ini jika ada solusi yang tepat.
Dave Hilditch
2
Jika Anda mendapatkan solusi dari pertanyaan di atas, silakan berbagi dengan saya, Terima kasih
Muhammad
2
Adakah yang bisa mendapatkan byte MediaStream melalui beberapa keajaiban RTC sisi server?
Vinay

Jawaban:

44

Anda pasti harus melihat Kurento . Ini menyediakan infrastruktur server WebRTC yang memungkinkan Anda merekam dari umpan WebRTC dan banyak lagi. Anda juga dapat menemukan beberapa contoh aplikasi yang Anda rencanakan di sini . Sangat mudah untuk menambahkan kemampuan perekaman ke demo itu, dan menyimpan file media dalam URI (disk lokal atau di mana pun).

Proyek ini berlisensi di bawah LGPL Apache 2.0


EDIT 1

Sejak posting ini, kami telah menambahkan tutorial baru yang menunjukkan cara menambahkan perekam dalam beberapa skenario

Penafian: Saya adalah bagian dari tim yang mengembangkan Kurento.

igracia
sumber
2
@Redtopia Dalam beberapa tes beban baru-baru ini kami bisa mendapatkan 150 koneksi one2one webrtc pada RAM i5 / 16GB. Anda dapat berharap bahwa angka-angka ini akan lebih baik di masa mendatang, tetapi jangan mengharapkan keajaiban: ada banyak enkripsi yang terjadi untuk SRTP, dan itu menuntut. Kami sedang mencari enkripsi / dekripsi yang dipercepat perangkat keras, dan jumlahnya akan semakin tinggi, dan meskipun saya tidak dapat meyakinkan Anda seberapa jauh lebih baik itu sampai kami mengujinya lebih menyeluruh, kami mengharapkan peningkatan 3x
igracia
2
@ user344146 Mungkin itulah yang saya jawab. Maukah Anda membagikan link ke postingan itu? Jika Anda mendapat jawaban itu, mungkin karena Anda menanyakan sesuatu yang sudah ada atau ada di daftar. Sepertinya Anda mencoba mengompilasi versi SNAPSHOT. Artefak tersebut tidak dipublikasikan secara terpusat, jadi baik Anda memeriksa rilis tutorial atau menggunakan repo dev internal kami. Ini telah dijawab dalam daftar berkali-kali, ada entri dalam dokumentasi tentang bekerja dengan versi pengembangan ... Kami meluangkan waktu untuk menulisnya, jadi alangkah baiknya Anda meluangkan waktu untuk membacanya.
igracia
2
Saya hanya menggunakan Kurento untuk membuat rekaman seperti itu. Ini tidak rumit, tetapi perlu sedikit waktu, untuk memahami konsepnya - karena beberapa dokumen benar-benar kejam - dan menemukan apa yang dapat saya kirim ke kurento, atau deskripsi acara dan sebagainya terkadang bisa sangat membuat frustrasi. Tapi bagaimanapun- proyek terbuka seperti ini benar-benar pekerjaan yang bagus dan layak untuk digunakan. Kurento hanya bekerja di linux (versi windows tidak resmi dan tidak berfungsi dengan fungsionalitas penuh).
Krystian
1
Menemukan jawaban untuk pertanyaan di atas (posting di sini untuk orang lain), Kurento saat ini mendukung JDK 7.0, Ini tidak harus bergantung pada Ubuntu 14.04, itu harus mendukung versi yang lebih baru juga, tetapi Kurento tidak diuji secara resmi pada versi lain dari Ubuntu / versi linux lainnya. Kurento juga merilis versi 64 bit yang sudah tersedia untuk isntallation, namun Anda dapat menginstal server versi 32 bit tetapi Anda harus membuatnya terlebih dahulu.
Bilbo Baggins
1
Sayangnya, seperti yang dinyatakan dalam jawaban saya, perkembangan Kurento telah melambat setelah akuisisi Twilio. Saya merekomendasikan menggunakan Janus sebagai gantinya.
jamix
17

Silakan, periksa RecordRTC

RecordRTC adalah lisensi MIT di github .

Dmitry
sumber
2
Itu sangat luar biasa - pertanyaan saya: dapatkah itu merekam video dan audio bersama-sama (menayangkan video nyata daripada dua hal yang terpisah?)
Brian Dear
Setuju - luar biasa, tetapi sepertinya itu hanya merekam data secara terpisah.
Dave Hilditch
3
@BrianDear ada satu RecordRTC-together
Mifeng
2
Pendekatan ini bekerja melalui Whammy.js di Chrome. Ini bermasalah karena kualitasnya cenderung jauh lebih rendah dari emulasi yang disediakan Whammy karena kurangnya MediaStreamRecorder Chrome. Apa yang pada dasarnya terjadi adalah WhammyRecorder mengarahkan tag video ke URL objek MediaStream dan kemudian mengambil cuplikan webp dari elemen kanvas pada frekuensi gambar tertentu. Ia kemudian menggunakan Whammy untuk menempatkan semua bingkai itu bersama-sama menjadi video webm.
Vinay
15

Saya percaya menggunakan kurento atau MCUs lain hanya untuk merekam video akan sedikit berlebihan, terutama mengingat kenyataan bahwa Chrome memiliki MediaRecorder dukungan API dari v47 dan Firefox sejak V25. Jadi di persimpangan ini, Anda bahkan mungkin tidak memerlukan perpustakaan js eksternal untuk melakukan pekerjaan itu, coba demo yang saya buat untuk merekam video / audio menggunakan MediaRecorder:

Demo - akan berfungsi di chrome dan firefox (sengaja tidak memasukkan blob ke kode server)

Sumber Kode Github

Jika menjalankan firefox, Anda dapat mengujinya di sini sendiri (kebutuhan 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>

mido
sumber
Chrome 49 adalah yang pertama mendukung MediaRecorder API tanpa bendera.
Oktavianus Naicu
7

ya, seperti yang Anda pahami, MediaStreamRecorder saat ini tidak diterapkan.

MediaStreamRecorder adalah API WebRTC untuk merekam aliran getUserMedia (). Ini memungkinkan aplikasi web membuat file dari sesi audio / video langsung.

alternatifnya Anda dapat melakukan seperti ini http://ericbidelman.tumblr.com/post/31486670538/creating-webm-video-from-getusermedia tetapi audio tidak ada.

Konga Raju
sumber
1
Ya, dan Anda dapat menangkap file audio, mengirimkannya ke server, dan menggabungkannya di sana untuk membuat file video nyata di sisi server. Tetapi solusi ini mungkin sangat lambat di sisi klien tergantung pada konfigurasi komputernya, karena harus membuat file gambar menggunakan kanvas DAN menangkap audio, dan semua ini di RAM ... Btw, tim firefox sedang mengerjakannya , semoga mereka segera merilisnya.
Firas
4

Anda dapat menggunakan RecordRTC-together , yang didasarkan pada RecordRTC.

Ini mendukung perekaman video dan audio bersama dalam file terpisah. Anda akan membutuhkan alat seperti ffmpegmenggabungkan dua file menjadi satu di server.

Mifeng
sumber
2
Ini adalah solusi browser, bukan sisi server.
Brad
2

Server Panggilan Web 4 dapat merekam audio dan video WebRTC ke wadah WebM. Perekaman dilakukan menggunakan codec Vorbis untuk audio dan codec VP8 untuk video. Codec WebRTC awal adalah Opus atau G.711 dan VP8. Jadi, perekaman sisi server memerlukan transkode sisi server Opus / G.711 ke Vorbis atau transkode VP8-H.264 jika perlu menggunakan wadah lain, yaitu AVI.

Bob42
sumber
apakah ini barang komersial?
Stepan Yakovenko
0

Sebagai catatan saya juga kurang memiliki pengetahuan tentang ini,

Tapi saya menemukan ini di hub 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>

Pada Baris nomor 89 dalam kode kasus saya OnrecordComplete sebenarnya menambahkan tautan file perekam, jika Anda mengklik tautan itu, itu akan memulai unduhan, Anda dapat menyimpan jalur itu ke server Anda sebagai file.

Kode Perekaman terlihat seperti ini

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 menahan jalur tersebut. Saya memecahkan masalah saya dengan ini, berharap seseorang akan menganggap ini berguna

uniqueNt
sumber
-4

Secara teknis Anda dapat menggunakan FFMPEG di backend untuk mencampur video dan audio

EugeneB
sumber
7
ya tapi bagaimana Anda mendapatkannya di sana?
Eddie Monge Jr