01.12.2022

SOCSEO.RU

Интересная информация и полезные статьи…

Простые каналы видео, голоса и данных WebRTC

Среднее прочтение 5 мин.

ARCHITECTURE peer to peer client - server

Установка

npm install simple-peer

Использование

Этот пакет работает в браузере с browserify . Если вы не используете упаковщик, вы можете использовать simplepeer.min.jsавтономный скрипт непосредственно в <script>теге. Это экспортирует SimplePeerконструктор в window. Везде, где вы видите Peerв приведенных ниже примерах, замените это на SimplePeer.

Давайте создадим html-страницу, которая позволит вам вручную подключить два узла:

<html>
  <body>
    <style>
      #outgoing {
        width: 600px;
        word-wrap: break-word;
        white-space: normal;
      }
    </style>
    <form>
      <textarea id="incoming"></textarea>
      <button type="submit">submit</button>
    </form>
    <pre id="outgoing"></pre>
    <script src="simplepeer.min.js"></script>
    <script>
      const p = new SimplePeer({
        initiator: location.hash === '#1',
        trickle: false
      })

      p.on('error', err => console.log('error', err))

      p.on('signal', data => {
        console.log('SIGNAL', JSON.stringify(data))
        document.querySelector('#outgoing').textContent = JSON.stringify(data)
      })

      document.querySelector('form').addEventListener('submit', ev => {
        ev.preventDefault()
        p.signal(JSON.parse(document.querySelector('#incoming').value))
      })

      p.on('connect', () => {
        console.log('CONNECT')
        p.send('whatever' + Math.random())
      })

      p.on('data', data => {
        console.log('data: ' + data)
      })
    </script>
  </body>
</html>

Посещение index.html#1из одного браузера (инициатора) и index.htmlиз другого браузера (получателя).

«Предложение» будет сгенерировано инициатором. Вставьте это в форму получателя и нажмите «Отправить». Получатель генерирует «ответ». Вставьте это в форму инициатора и нажмите «Отправить».

Теперь у вас есть прямое P2P-соединение между двумя браузерами!

Более простой пример

В этом примере создаются два одноранговых узла на одной и той же веб-странице .

В реальном приложении вы бы никогда этого не сделали . Экземпляры отправителя и получателя Peer будут существовать в разных браузерах. «Сервер сигнализации» (обычно реализованный с помощью веб-сокетов) будет использоваться для обмена сигнальными данными между двумя браузерами до тех пор, пока не будет установлено одноранговое соединение.

каналы данных

var Peer = require('simple-peer')

var peer1 = new Peer({ initiator: true })
var peer2 = new Peer()

peer1.on('signal', data => {
  // when peer1 has signaling data, give it to peer2 somehow
  peer2.signal(data)
})

peer2.on('signal', data => {
  // when peer2 has signaling data, give it to peer1 somehow
  peer1.signal(data)
})

peer1.on('connect', () => {
  // wait for 'connect' event before using the data channel
  peer1.send('hey peer2, how is it going?')
})

peer2.on('data', data => {
  // got a data channel message
  console.log('got a message from peer1: ' + data)
})

видео/голос

Видео/голос тоже супер просто! В этом примере узел 1 отправляет видео узлу 2.

var Peer = require('simple-peer')

// get video/voice stream
navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
}).then(gotMedia).catch(() => {})

function gotMedia (stream) {
  var peer1 = new Peer({ initiator: true, stream: stream })
  var peer2 = new Peer()

  peer1.on('signal', data => {
    peer2.signal(data)
  })

  peer2.on('signal', data => {
    peer1.signal(data)
  })

  peer2.on('stream', stream => {
    // got remote video stream, now let's show it in a video tag
    var video = document.querySelector('video')

    if ('srcObject' in video) {
      video.srcObject = stream
    } else {
      video.src = window.URL.createObjectURL(stream) // for older browsers
    }

    video.play()
  })
}

Для двустороннего видео просто передайте streamпараметр в оба Peerконструктора. Простой!

Обратите внимание, что getUserMedia работает только на страницах, загруженных через https .

динамическое видео/голос

Также можно сначала установить соединение только для передачи данных, а затем, при желании, добавить видео/голосовой поток.

var Peer = require('simple-peer') // create peer without waiting for media

var peer1 = new Peer({ initiator: true }) // you don't need streams here
var peer2 = new Peer()

peer1.on('signal', data => {
  peer2.signal(data)
})

peer2.on('signal', data => {
  peer1.signal(data)
})

peer2.on('stream', stream => {
  // got remote video stream, now let's show it in a video tag
  var video = document.querySelector('video')

  if ('srcObject' in video) {
    video.srcObject = stream
  } else {
    video.src = window.URL.createObjectURL(stream) // for older browsers
  }

  video.play()
})

function addMedia (stream) {
  peer1.addStream(stream) // <- add streams to peer dynamically
}

// then, anytime later...
navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
}).then(addMedia).catch(() => {})

в узле

Чтобы использовать эту библиотеку в узле, передайте ее в opts.wrtc в качестве параметра

var Peer = require('simple-peer')
var wrtc = require('wrtc')

var peer1 = new Peer({ initiator: true, wrtc: wrtc })
var peer2 = new Peer({ wrtc: wrtc })

API

peer = new Peer([opts])

Создайте новое одноранговое соединение WebRTC.

Всегда устанавливается «канал данных» для текстового/двоичного обмена, потому что это дешево и часто полезно. Для видео/голосовой связи передайте streamопцию.

Если optsуказано, то параметры по умолчанию (показаны ниже) будут переопределены.

{
  initiator: false,
  channelConfig: {},
  channelName: '<random string>',
  config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:global.stun.twilio.com:3478?transport=udp' }] },
  offerOptions: {},
  answerOptions: {},
  sdpTransform: function (sdp) { return sdp },
  stream: false,
  streams: [],
  trickle: true,
  allowHalfTrickle: false,
  wrtc: {}, // RTCPeerConnection/RTCSessionDescription/RTCIceCandidate
  objectMode: false
}

Опции делают следующее:

  • initiator— установить, trueесли это инициирующий пир
  • channelConfig— пользовательская конфигурация канала данных webrtc (используется createDataChannel)
  • channelName— пользовательское имя канала данных webrtc
  • config— пользовательская конфигурация webrtc (используется RTCPeerConnection конструктором)
  • offerOptions— настраиваемые параметры предложения (используется createOffer методом)
  • answerOptions— настраиваемые варианты ответов (используемые createAnswerметодом)
  • sdpTransform— функция преобразования сгенерированных данных сигнализации SDP (для опытных пользователей)
  • stream— если требуется видео/голос, передать поток, возвращенный из getUserMedia
  • streams— массив MediaStreams, возвращенный из getUserMedia
  • trickle— установите, falseчтобы отключить Trickle ICE и получить одиночное «сигнальное» событие (медленнее)
  • wrtc— собственная реализация webrtc, в основном полезная в узле для указания в пакете wrtc . Содержит объект со свойствами:
    • RTCPeerConnection
    • RTCSessionDescription
    • RTCIceCandidate
  • objectMode— установите для true создания потока в объектном режиме . В этом режиме входящие строковые данные не преобразуются автоматически в Bufferобъекты.

peer.signal(data)

Вызывайте этот метод всякий раз, когда удаленный одноранговый узел создает peer.on('signal')событие.

Будет dataин капсулировать предложение webrtc, ответ или ледяной кандидат. Эти сообщения помогают одноранговым узлам в конечном итоге установить прямое соединение друг с другом. Содержимое этих строк является деталью реализации, которую пользователь этого модуля может игнорировать; просто передайте данные из событий «сигнала» удаленному узлу и позвоните peer.signal(data) , чтобы подключиться.

peer.send(data)

Отправлять текстовые/двоичные данные удаленному узлу. dataможет быть любого из нескольких типов: StringBufferArrayBufferView( и т. д Uint8Array.) ArrayBuffer, или Blob(в браузерах, которые его поддерживают).

Примечание. Если этот метод вызывается до того, как peer.on('connect')событие сработает, будет выдано исключение. Используйте peer.write(data)(который унаследован от интерфейса дуплексного потока node.js ), если вы хотите, чтобы эти данные вместо этого были буферизованы.

peer.addStream(stream)

Добавьте MediaStreamк соединению.

peer.removeStream(stream)

Удалите a MediaStreamиз соединения.

peer.addTrack(track, stream)

Добавьте MediaStreamTrackк соединению. Также необходимо передать то MediaStream, к чему вы хотите его прикрепить.

peer.removeTrack(track, stream)

Удалите a MediaStreamTrackиз соединения. Также должен пройти тот , к MediaStreamкоторому он был прикреплен.

peer.replaceTrack(oldTrack, newTrack, stream)

Замените на MediaStreamTrackдругой трек. Также необходимо пройти тот , к MediaStreamкоторому был привязан старый трек.

peer.addTransceiver(kind, init)

Добавьте RTCRtpTransceiverк соединению. Может использоваться для добавления приемопередатчиков перед добавлением дорожек. Автоматически вызывается по мере необходимости addTrack.

peer.destroy([err])

Уничтожьте и очистите это одноранговое соединение.

Если передан необязательный errпараметр, он будет передан как 'error' событие в потоке.

Peer.WEBRTC_SUPPORT

Обнаружение встроенной поддержки WebRTC в среде javascript.

var Peer = require('simple-peer')

if (Peer.WEBRTC_SUPPORT) {
  // webrtc support!
} else {
  // fallback
}

дуплексный поток

Peerобъекты являются экземплярами stream.Duplex. Они ведут себя очень похоже на модуль net.Socketядра узла . netДуплексный поток читает/записывает в канал данных.

var peer = new Peer(opts)
// ... signaling ...
peer.write(new Buffer('hey'))
peer.on('data', function (chunk) {
  console.log('got a chunk', chunk)
})

События

Peerобъекты являются экземпляром EventEmitter. Взгляните на документацию по событиям nodejs для получения дополнительной информации.

Пример удаления всех зарегистрированных прослушивателей close -event:

peer.removeAllListeners('close')

peer.on('signal', data => {})

Запускается, когда одноранговый узел хочет отправить сигнальные данные удаленному одноранговому узлу.

Ответственность за передачу этих данных другому узлу лежит на разработчике приложения (это вы!). Обычно это влечет за собой использование сервера сигнализации веб-сокетов. Эти данные представляют собой Object, поэтому не забудьте сначала вызвать JSON.stringify(data)их для сериализации. Затем просто позвоните peer.signal(data)удаленному узлу.

(Обязательно прослушайте это событие немедленно, чтобы не пропустить его. Для initiator: true одноранговых узлов оно срабатывает сразу. Для initatior: falseодноранговых узлов оно срабатывает при получении удаленного предложения.)

peer.on('connect', () => {})

Запускается, когда одноранговое соединение и канал данных готовы к использованию.

peer.on('data', data => {})

Получено сообщение от удаленного партнера (по каналу данных).

dataбудет либо a, Stringлибо a Buffer/Uint8Array.

peer.on('stream', stream => {})

Получен удаленный видеопоток, который можно отобразить в теге видео:

peer.on('stream', stream => {
  var video = document.querySelector('video')
  if ('srcObject' in video) {
    video.srcObject = stream
  } else {
    video.src = window.URL.createObjectURL(stream)
  }
  video.play()
})

peer.on('track', (track, stream) => {})

Получена удаленная аудио/видео дорожка. Потоки могут содержать несколько дорожек.

peer.on('close', () => {})

Вызывается, когда одноранговое соединение закрыто.

peer.on('error', (err) => {})

Запускается при возникновении фатальной ошибки. Обычно это означает, что от удаленного партнера были получены неверные сигнальные данные.

errявляется Errorобъектом.

коды ошибок

Ошибки, возвращаемые errorсобытием, имеют err.codeсвойство, указывающее на источник сбоя.

Возможные коды ошибок:

  • ERR_WEBRTC_SUPPORT
  • ERR_CREATE_OFFER
  • ERR_CREATE_ANSWER
  • ERR_SET_LOCAL_DESCRIPTION
  • ERR_SET_REMOTE_DESCRIPTION
  • ERR_ADD_ICE_CANDIDATE
  • ERR_ICE_CONNECTION_FAILURE
  • ERR_SIGNALING
  • ERR_DATA_CHANNEL
  • ERR_CONNECTION_FAILURE

подключение более 2 пиров?

Самый простой способ сделать это — создать полносвязную топологию. Это означает, что каждый пир открывает соединение с каждым другим узлом. Проиллюстрировать:

полносвязная топология

Чтобы передать сообщение, просто переберите все одноранговые узлы и вызовите peer.send.

Итак, скажем, у вас есть 3 сверстника. Затем, когда одноранговый узел хочет отправить какие-то данные, он должен отправить их 2 раза, по одному разу каждому из других одноранговых узлов. Так что вам нужно быть немного осторожным с размером данных, которые вы отправляете.

Полноячеистые топологии плохо масштабируются при очень большом числе одноранговых узлов. Общее количество ребер в сети будет  равно nколичеству пиров.

Для ясности, вот код для соединения 3 пиров вместе:

Peer 1
// These are peer1's connections to peer2 and peer3
var peer2 = new Peer({ initiator: true })
var peer3 = new Peer({ initiator: true })

peer2.on('signal', data => {
  // send this signaling data to peer2 somehow
})

peer2.on('connect', () => {
  peer2.send('hi peer2, this is peer1')
})

peer2.on('data', data => {
  console.log('got a message from peer2: ' + data)
})

peer3.on('signal', data => {
  // send this signaling data to peer3 somehow
})

peer3.on('connect', () => {
  peer3.send('hi peer3, this is peer1')
})

peer3.on('data', data => {
  console.log('got a message from peer3: ' + data)
})

Peer 2

// These are peer2's connections to peer1 and peer3
var peer1 = new Peer()
var peer3 = new Peer({ initiator: true })

peer1.on('signal', data => {
  // send this signaling data to peer1 somehow
})

peer1.on('connect', () => {
  peer1.send('hi peer1, this is peer2')
})

peer1.on('data', data => {
  console.log('got a message from peer1: ' + data)
})

peer3.on('signal', data => {
  // send this signaling data to peer3 somehow
})

peer3.on('connect', () => {
  peer3.send('hi peer3, this is peer2')
})

peer3.on('data', data => {
  console.log('got a message from peer3: ' + data)
})

Peer 3

// These are peer3's connections to peer1 and peer2
var peer1 = new Peer()
var peer2 = new Peer()

peer1.on('signal', data => {
  // send this signaling data to peer1 somehow
})

peer1.on('connect', () => {
  peer1.send('hi peer1, this is peer3')
})

peer1.on('data', data => {
  console.log('got a message from peer1: ' + data)
})

peer2.on('signal', data => {
  // send this signaling data to peer2 somehow
})

peer2.on('connect', () => {
  peer2.send('hi peer2, this is peer3')
})

peer2.on('data', data => {
  console.log('got a message from peer2: ' + data)
})

использование памяти

Если вы вызываете peer.send(buf)simple-peerне сохраняет ссылку buf и не отправляет буфер в какой-то более поздний момент времени. Мы сразу вызываем channel.send()на канал данных. Так что это должно быть нормально, чтобы изменить буфер сразу после этого.

Однако имейте в виду, что peer.write(buf)(метод потока с возможностью записи) не имеет такого же контракта. Он потенциально будет буферизовать данные и вызывать channel.send()их в будущем, поэтому определенно не думайте, что изменять буфер безопасно.

соединение не работает в некоторых сетях?

В случае сбоя прямого подключения, в частности, из-за обхода NAT и/или межсетевых экранов, WebRTC ICE использует промежуточный (релейный) TURN-сервер. Другими словами, ICE сначала будет использовать STUN с UDP для прямого подключения пиров и, если это не удастся, вернется к серверу ретрансляции TURN.

Чтобы использовать сервер TURN, вы должны указать configопцию Peer конструктору. См. документы API выше.

Кто использует simple-peer?

  • WebTorrent — потоковый торрент-клиент в браузере
  • Virus Cafe — Заведи друга за 2 минуты
  • Instant.io — безопасная, анонимная, потоковая передача файлов
  • Zencastr — легко записывайте удаленные интервью подкастов в студийном качестве.
  • Друзья — одноранговый чат на базе Интернета
  • Socket.io-p2p — официальная коммуникационная библиотека Socket.io P2P
  • ScreenCat — совместное использование экрана + приложение для удаленной совместной работы
  • WebCat — P2P-канал через Интернет с использованием закрытого/открытого ключа Github для аутентификации
  • RTCCat — сетевой кот WebRTC
  • PeerNet — одноранговая сеть сплетен, использующая рандомизированные алгоритмы .
  • PusherTC — Видеочат с помощью Pusher. См . руководство .
  • lxjs-chat — сайт видеочата, похожий на Omegle
  • Белая доска — доска P2P на основе WebRTC и WebTorrent
  • Peer Calls — групповые видеозвонки WebRTC. Создайте комнату. Поделитесь ссылкой.
  • Netsix — отправляйте видео своим друзьям с помощью WebRTC, чтобы они сразу же могли их просмотреть.
  • Stealthy — Stealthy — это децентрализованное приложение для p2p-чата со сквозным шифрованием.
  • oorja.io — удобный видео-голосовой чат с функциями совместной работы в реальном времени. Расширяемый с использованием реагирующих компонентов🙌
  • TalktoMe — альтернатива Skype для аудио/видеоконференций на базе WebRTC, но без потери пакетов.
  • CDNBye — CDNBye реализует канал данных WebRTC для масштабирования потоковой передачи видео в реальном времени / видео через одноранговую сеть с использованием протокола, подобного битторренту.
  • Detox — оверлейная сеть для распределенных анонимных P2P-коммуникаций полностью в браузере.
  • Metastream — смотрите потоковое мультимедиа с друзьями.
  • firepeer — безопасная сигнализация и аутентификация с использованием базы данных Firebase в реальном времени
  • Genet — наложение Fat-tree для масштабирования количества одновременных WebRTC-подключений к одному источнику ( бумага ).
  • Тестирование соединения WebRTC — быстро протестируйте прямое соединение между всеми парами участников ( демонстрация ).
  • Firstdate.co — онлайн-видео знакомства для реальных встреч с людьми, а не просто для обмена сообщениями
  • TensorChat — Это просто — Создавайте. Делиться. Чат.
  • On/Office — просмотр рабочего стола в среде на основе WebVR
  • Cyph — криптографически безопасный сервис для обмена сообщениями и социальных сетей, обеспечивающий высочайший уровень конфиденциальности в сочетании с лучшей в своем классе простотой использования.
  • Ciphora — одноранговое приложение для обмена сообщениями со сквозным шифрованием.
  • Whisthub — карточная онлайн-игра Color Whist с возможностью начать видеочат во время игры.
  • Brie.fi/ng — Безопасный анонимный видеочат
  • Peer.School — простой виртуальный класс, начиная с 1-го класса, включая видеочат и интерактивную доску в реальном времени.
  • FileFire — Передавайте большие файлы и папки на высокой скорости без ограничений по размеру.
  • safeShare — легко передавайте файлы с помощью текстового и голосового общения.
  • CubeChat — Вечеринка в 3D🎉
  • Homely School — Виртуальная система обучения
  • AnyDrop — кроссплатформенная альтернатива AirDrop с приложением для Android, доступным в Google Play.
  • Share-Anywhere — кроссплатформенная передача файлов
  • QuaranTime.io — настольная игра Activity в видео!
  • Trango — кроссплатформенное решение для звонков и обмена файлами.
  • P2PT — используйте трекеры WebTorrent в качестве сигнальных серверов для установления соединений WebRTC.
  • Dots — Многопользовательская онлайн игра Dots & Boxes. Играй здесь!
  • simple-peer-files — простая библиотека для простой передачи файлов через WebRTC. Имеет функцию возобновления передачи файлов после прерывания загрузчика.
  • WebDrop.Space — делитесь файлами и сообщениями на разных устройствах. Кроссплатформенность, отсутствие альтернативы установки AirDrop, Xender. Исходный код
  • Speakrandom — социальная сеть голосового чата, использующая simple-peer для создания аудиоконференций!
  • Deskreen — настольное приложение, которое поможет вам превратить любое устройство в дополнительный экран для вашего компьютера. Он использует простой одноранговый доступ для совместного использования всего экрана компьютера с любым устройством с веб-браузером.