WebRTC. Делаем приложение с блекджеком и видеозвонками

от автора

Дорогой читатель, перед тем как мы начнем писать код, давайте разберем саму концепцию видеозвонков.

Представим ситуацию: у нас есть чат-платформа и нам необходимо прикрутить к ней видеозвонки, то есть в онлайне сидит некий Вася и он хочет позвонить Пете, для реализации такой фичи нам понадобится технология WebSocket.

Что ж, давайте поднимем наш WebSocket сервер, нам в этом поможет node.js;
Создадим файл sockets.js и запишем туда код сокет сервера:

const WebSocketServer = require('websocket').server; const http = require('http');  const server = http.createServer(function(request, response) {  //здесь мы ничего не пишем,потому что мы используем сокеты,а не http }); server.listen(1337, function() {});  // создаем вебсокет сервер const wsServer = new WebSocketServer({   httpServer: server });  wsServer.on('request', function(request) {   let connection = request.accept(null, request.origin); //принимаем подключение к сокету }) 


Создадим файл index.html и запишем туда код для открытия сокет-соединения:

     <video autoplay muted height='300' width='300' style="position:fixed;bottom:0;left:0;z-index: 9999;" src="" id='my'>           </video> <!-- наше видео -->             <video autoplay height='300' width='300' style="position:fixed;bottom:0;left:300px;z-index: 999999;" src="" id='not_my'>           </video> <!--видео нашего собеседника--> 

Теперь создадим и подключим файл script.js к нашему html файлу:

  let connection = new WebSocket('ws://127.0.0.1:1337');//подключаемся к нашему сокет-серверу   connection.onopen = function(){ //если вам необходимо,можете отправлять какие-либо данные на сокет при его открытии  } connection.onmessage = function(message){ //функция которая будет выполняться при приходе сообщения от сокета   } connection.onerror = function (error) {         console.error(error)        //функция которая выполнится,если будет ошибка соединения       }; 

Итак, вернемся к нашему Васе и Пете

image

Это начальный этап на котором Вася с Петей просто обмениваются JSON, о том будем ли принимать звонок или нет.То есть при заходе на нашу страницу мы обязательно должны открывать WebSocket соединение для связи с нашим сокет сервером.

  1. Мы шлем сначала на сокет сервер JSON с тем, что мы хотим позвонить Пете с аккаунта Васи
    connection.send(JSON.stringify({ //данные для инициализации })) 
  2. На сокет сервере мы должны этот JSON принять, после того как мы получили request, мы на наш сервер навешиваем событие message:
    connection.on('message',function(message){ //можем работать с message и отсылать кому угодно,но прежде переведем все из JSON в JS let self = JSON.parse(message.utf8Data); }) 

Пообщавшись и решив, что оба пользователя готовы к разговору, мы должны разобраться как работает видеосвязь в браузере, в js встроен модуль для этого — RTCPeerConnection

image

Нам необходимо открыть RTCPeerConnection, с помощью которого мы сможем сгененрировать offer и отправить его нужному нам пользователю опять же через наш сокет-сервер, который получив его сгенерирует нам answer и отправит обратно, после чего мы начинаем обмениваться ice пакетами, в которых содержится информация об окружении данного компьютера, которая необходима для успешного установления видеосвязи.

Генерируем и отправляем offer

var pc = new RTCPeerConnection();                          var peerConnectionConfig = {               iceServers: [                   {                       urls: 'stun:stun.l.google.com:19302'                   }                 ]             }             pc.onicecandidate = function (event) {               console.log('new ice candidate', event.candidate);                if (event.candidate !== null) {                 connection.send(JSON.stringify({                  //отправляем json и ice пакеты                 }))               }           };              navigator.getUserMedia = navigator.getUserMedia ||                          navigator.webkitGetUserMedia ||                          navigator.mozGetUserMedia;                                     navigator.getUserMedia({video: true,audio:true}, function(stream) {               // Добавление локального потока не вызовет onaddstream обратного вызова,               // так называют его вручную.             var my_video = document.getElementById('my')             my_video.srcObject = stream                pc.onaddstream = e => {                 document.getElementById('not_my').srcObject = e.stream;                 console.log('not stream is added')               }               pc.addStream(stream);                               pc.createOffer(function(offer) {                 pc.setLocalDescription(offer, function() {                   //отправляем наш offer на сокет                   }))                 }, e=> console.log(e));               }, e=> console.log(e));             },function (){console.warn("Error getting audio stream from getUserMedia")});              // функция помощник             function endCall() {               var videos = document.getElementsByTagName("video");               for (var i = 0; i < videos.length; i++) {                 videos[i].pause();               }                            pc.close();             }              function error(err) {               endCall();             }  

Обрабатываем offer и генерируем answer

var pc = new RTCPeerConnection();//создаем connection var peerConnectionConfig = { //конфигурация ice server               iceServers: [                   {                       urls: 'stun:stun.l.google.com:19302'                   }                 ]             }             pc.onicecandidate = function (event) {               console.log('new ice candidate', event.candidate);                if (event.candidate !== null) {                 //отправляем ice пакеты               }           };               navigator.getUserMedia = navigator.getUserMedia ||                          navigator.webkitGetUserMedia ||                          navigator.mozGetUserMedia;              navigator.getUserMedia({video: true,audio:true}, function(stream) {//подключаем у пользователя видеосвязь и аудиосвязь и втыкаем ее на страницу               var my_video = document.getElementById('my')               my_video.srcObject = stream               console.log('stream is added while offering')                pc.onaddstream = e => {                 console.log('not my stream is added while offering')                 document.getElementById('not_my').srcObject = e.stream;                                }               pc.addStream(stream);                            pc.setRemoteDescription(new RTCSessionDescription(data.offer), function() {                 pc.createAnswer(function(answer) {                   pc.setLocalDescription(answer, function() {                     //отправляем ответ                   }, e => console.log(e));                 }, e => console.log(e));               }, e => console.log(e));             },function (){console.warn("Error getting audio stream from getUserMedia")});           }         } 

Принимаем answer и создаем видеопоток

pc.setRemoteDescription(new RTCSessionDescription(data.answer), function() { }, error); 

И последнее что нам нужно сделать — обработать ice пакеты

pc.addIceCandidate(new RTCIceCandidate(ice))//тут ice -  тот,который пришел нам от пользователя 

ссылка на оригинал статьи https://habr.com/ru/post/502726/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *