Panggilan video di browser di PeerJS. Mulai cepat

Saya menyapa semua pembaca Habr. Tahun ini saya mendapat kesempatan untuk menulis modul komunikasi video untuk satu portal pelatihan untuk menelepon melalui komunikasi video langsung di situs web guru dan siswa. Itu tidak perlu untuk menyelesaikan tugas awal seperti itu. Setelah pencarian singkat, saya menemukan bahwa ada 2 cara: Flash dan WebRTC . WebRTC dalam bentuknya yang murni ternyata rumit, dan secara umum itu wajar, karena tugas komunikasi video tidak sederhana. Tapi kemudian saya menemukan PeerJS , yang merupakan pembungkus untuk WebRTC. Pada artikel ini saya akan memberi tahu Anda cara mengatur dialer browser Anda dengan cepat.

Untuk mengulangi contoh, akses ke halaman pengujian Anda melalui protokol https akan diperlukan (karena halaman akan meminta akses ke kamera dan mikrofon, dan tanpa protokol yang aman browser hanya akan memberikan kesalahan)

Tata letak awal akan terlihat seperti ini:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Peer</title> <script src="https://unpkg.com/peerjs@1.0.0/dist/peerjs.min.js"></script> </head> <body> <p><h3> ID: </h3><span id=myid ></span></p> <input id=otherPeerId type=text placeholder="otherPeerId" > <button onclick="callToNode(document.getElementById('otherPeerId').value)"></button> <br> <video id=myVideo muted="muted" width="400px" height="auto" ></video> <div id=callinfo ></div> <video id=remVideo width="400px" height="auto" ></video> </body> 

Di bagian kepala, kami menghubungkan PeerJS dari jarak jauh. Dimungkinkan juga untuk mengunduh skrip dan menghubungkannya secara lokal.

input id = otherPeerId - dirancang untuk memasuki hari pesta yang akan kami panggil (Anda dapat menganggapnya sebagai indeks, atau sebagai nomor telepon).

Dua tag video dirancang untuk masing-masing menampilkan video Anda dan untuk video lawan bicara.

Sekarang sedikit tentang teknologi WebRTC dan bagaimana panggilan dilakukan. WebRTC melakukan panggilan dari klien ke klien secara langsung, tanpa keterlibatan server, jadi pada langkah pertama 2 browser harus saling menemukan. Untuk melakukan ini, WebRTC klasik memerlukan server sinyal, yaitu server yang memberi tahu satu browser parameter dari browser lain, dan di WebRTC Anda harus mengatur sendiri server semacam itu. Namun, pengembang PeerJS menyediakan server sinyal mereka sendiri. Yang perlu Anda lakukan adalah meneruskan peerID ke lawan bicara potensial, yaitu, indeks unik yang diterima dalam sistem PeerJS. Dalam draft kerja, saya mengaturnya seperti ini:

  1. Setelah memuat halaman, objek Peer dibuat
  2. PeerID-nya ditulis ke database mysql
  3. Ketika tombol Panggil ditekan, peerID dari lawan bicara ditarik keluar dari database dan digunakan untuk membuat koneksi

Dalam kasus pengujian saat ini, kami akan memasukkan peerID dari lawan bicara di bidang teks otherPeerId

Jadi, mari kita mulai menulis kode.

1. Buat objek peer utama

 var peer = new Peer(); 

2. Pada pembukaan pesta, kami akan menerima peerID yang didambakan, yang harus ditransfer ke mitra sehingga ia dapat menghubungi kami

 peer.on('open', function(peerID) { document.getElementById('myid').innerHTML=peerID; }); 

3. Untuk menerima panggilan, kami menutup handler untuk acara panggilan

 var peercall; peer.on('call', function(call) { // Answer the call, providing our mediaStream peercall=call; document.getElementById('callinfo').innerHTML="  <button onclick='callanswer()' ></button><button onclick='callcancel()' ></button>"; }); 

Dengan panggilan masuk, kita mendapatkan objek panggilan , yang kita simpan di variabel global peercall . Juga di blok informasi pemberitahuan tentang panggilan masuk dan 2 tombol akan ditampilkan: Terima dan Tolak

4. Kami menulis fungsi untuk tombol Terima

 function callanswer() { navigator.mediaDevices.getUserMedia ({ audio: true, video: true }).then(function(mediaStream) { var video = document.getElementById('myVideo'); peercall.answer(mediaStream); //         //peercall.on ('close', onCallClose); //  -  video.srcObject = mediaStream; //      (  ) document.getElementById('callinfo').innerHTML=" ... <button onclick='callclose()' > </button>"; //,   ,     video.onloadedmetadata = function(e) {// ,    video.play(); }; setTimeout(function() { //        document.getElementById('remVideo').srcObject = peercall.remoteStream; document.getElementById('remVideo').onloadedmetadata= function(e) { //       document.getElementById('remVideo').play(); }; },1500); }).catch(function(err) { console.log(err.name + ": " + err.message); }); } 

navigator.mediaDevices.getUserMedia - meminta akses ke kamera dan mikrofon. Dalam data objek yang diteruskan ke metode ini {audio: true, video: true}, Anda dapat meminta akses hanya ke kamera atau hanya ke mikrofon. Komentar lebih lanjut ditambahkan langsung ke kode.

setTimeout ditambahkan secara empiris: video mitra tidak mulai diputar, tetapi berfungsi dengan batas waktu.

5. Fungsi panggilan dengan tombol Panggil

 function callToNode(peerId) { // navigator.mediaDevices.getUserMedia ({ audio: true, video: true }).then(function(mediaStream) { var video = document.getElementById('myVideo'); peercall = peer.call(peerId,mediaStream); //,  peerId-    mediaStream peercall.on('stream', function (stream) { // ,   setTimeout(function() { document.getElementById('remVideo').srcObject = peercall.remoteStream; document.getElementById('remVideo').onloadedmetadata= function(e) { document.getElementById('remVideo').play(); }; },1500); }); // peercall.on('close', onCallClose); video.srcObject = mediaStream; video.onloadedmetadata = function(e) { video.play(); }; }).catch(function(err) { console.log(err.name + ": " + err.message); }); } 

Seperti pada paragraf sebelumnya, kami meminta aliran media Anda. Setelah kita memanggil fungsi panggilan dari objek rekan, yang akan mengembalikan objek panggilan kepada kita, simpan di tempat panggilan. Kami memproses acara streaming untuk mencari tahu apa yang mereka jawab dan memasukkan aliran yang masuk ke objek video yang sesuai

Itu saja, tapi ...

Jika kedua penelepon berada di belakang panggilan NATth tidak akan melewati. (Kenapa? Baca di sini habr.com/en/company/yandex/blog/419951 )
Untuk mengatasi kendala ini, perlu untuk menentukan server MENGHIDUPKAN ketika membuat objek peer (Pertanyaan tentang di mana untuk mendapatkannya bukan yang termudah. ​​Kami harus menaikkan sendiri: VPS di Ubuntu 16,04. Instalasi dengan perintah
 apt install coturn 
)

Maka penciptaan pesta akan terlihat seperti ini:

 var callOptions={'iceServers': [ {url: 'stun:95.xxx.xx.x9:3479', username: "user", credential: "xxxxxxxxxx"}, { url: "turn:95.xxx.xx.x9:3478", username: "user", credential: "xxxxxxxx"}] }; peer= new Peer({config: callOptions}); 

Akhirnya, seluruh kode:

 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Peer</title> <script src="https://unpkg.com/peerjs@1.0.0/dist/peerjs.min.js"></script> </head> <body> <p><h3> ID: </h3><span id=myid ></span></p> <input id=otherPeerId type=text placeholder="otherPeerId" > <button onclick="callToNode(document.getElementById('otherPeerId').value)"></button> <br> <video id=myVideo muted="muted" width="400px" height="auto" ></video> <div id=callinfo ></div> <video id=remVideo width="400px" height="auto" ></video> <script> var callOptions={'iceServers': [ {url: 'stun:95.xxx.xx.x9:3479', username: "user", credential: "xxxxxxxxxx"}, { url: "turn:95.xxx.xx.x9:3478", username: "user", credential: "xxxxxxxx"}] }; peer= new Peer({config: callOptions}); peer.on('open', function(peerID) { document.getElementById('myid').innerHTML=peerID; }); var peercall; peer.on('call', function(call) { // Answer the call, providing our mediaStream peercall=call; document.getElementById('callinfo').innerHTML="  <button onclick='callanswer()' ></button><button onclick='callcancel()' ></button>"; }); function callanswer() { navigator.mediaDevices.getUserMedia ({ audio: true, video: true }).then(function(mediaStream) { var video = document.getElementById('myVideo'); peercall.answer(mediaStream); //         //peercall.on ('close', onCallClose); //  -  video.srcObject = mediaStream; //      (  ) document.getElementById('callinfo').innerHTML=" ... <button onclick='callclose()' > </button>"; //,   ,     video.onloadedmetadata = function(e) {// ,    video.play(); }; setTimeout(function() { //        document.getElementById('remVideo').srcObject = peercall.remoteStream; document.getElementById('remVideo').onloadedmetadata= function(e) { //       document.getElementById('remVideo').play(); }; },1500); }).catch(function(err) { console.log(err.name + ": " + err.message); }); } function callToNode(peerId) { // navigator.mediaDevices.getUserMedia ({ audio: true, video: true }).then(function(mediaStream) { var video = document.getElementById('myVideo'); peercall = peer.call(peerId,mediaStream); peercall.on('stream', function (stream) { // ,   setTimeout(function() { document.getElementById('remVideo').srcObject = peercall.remoteStream; document.getElementById('remVideo').onloadedmetadata= function(e) { document.getElementById('remVideo').play(); }; },1500); }); // peercall.on('close', onCallClose); video.srcObject = mediaStream; video.onloadedmetadata = function(e) { video.play(); }; }).catch(function(err) { console.log(err.name + ": " + err.message); }); } </script> </body> 

Solusi ini berhasil diuji di bawah Windows7 dan Ubuntu 18.04 di Chrome, Opera, Firefox. Chrome juga berfungsi di Android dan MacOS, tetapi tidak berfungsi di iPhone dan iPad.

Source: https://habr.com/ru/post/id468705/


All Articles