#LaravelPolandMeetup #23
Laravel Poland MeetUp #23 online
Temat: WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Laravelu
W jaki sposób można zrealizować połączenie WebRTC w aplikacji opartej o Laravel+Vue
Autor: Mateusz Garbarczyk, MirIT
3. Czym się dzisiaj zajmiemy?
● WebRTC - co to? Do czego służy?
● Websockety - dlaczego ich potrzebujemy?
4. Czym jest WebRTC
WebRTC (Web Real-Time Communication) – wolny (od wolności, a nie od
szybkości) i otwartoźródłowy projekt wbudowany w (nowoczesne)
przeglądarki internetowe jako część standardu HTML5 zaimplementowany
jako API javascriptowe.
Pozwala na komunikację typu peer-to-peer w czasie rzeczywistym między
przeglądarkami internetowymi.
Najczęściej służy do przesyłu audio i wideo, czyli grupowe czaty itp.
Największym plusem protokołu jest fakt, że nie wymaga instalacji żadnych
wtyczek czy innych aplikacji, wystarczy zaktualizowana przeglądarka
internetowa na komputerze lub smartfonie.
5. Jakie wyróżniamy sieci WebRTC
Wyróżniamy 3 rodzaje sieci dla połączeń WebRTC
● Mesh - który omawia dzisiejsza prezentacja
● SFU
● MCU
Grafika pochodzi z https://bloggeek.me
6. Kroki przy tworzeniu połączenia WebRTC
1. Signaling
2. Connecting
3. Securing
4. Communicating
Istotny szczegół - każdy krok musi zostać wykonany w 100% zanim
przejdziemy do kolejnego.
7. Zanim zaczniemy się łączyć - Websockety
Websocket - protokół zapewniający dwukierunkowy kanał wymiany danych między klientem
(przeglądarką) a serwerem. Zwykle używany przy chatach.
Przeglądarka łączy się z serwerem za pomocą zdefiniowanego kanału i nasłuchuje na wydarzenia
(eventy) emitowane przez serwer.
Dla Laravela możemy użyć kilku rozwiązań:
● Soketi (TypeScript) - substytut Pushera
● Pusher (rozwiązanie komercyjne)
● Socket.IO
● BeyondCode Laravel Websockets - substytut Pushera
● Ratchet
8. Przykładowy event w Laravelu
<?php
namespace AppEvents;
class CommentDeleted implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public CommentBroadcastResource $comment
) {
}
public function broadcastOn()
{
return new Channel('projects.'.$this->comment->project_id);
}
}
10. Przykładowy event w Laravelu
this.channel = this.pusher.subscribe(
'presence-session.' + this.session_id
)
this.channel.bind('comment-deleted', data =>
this.delete(JSON.parse(data.comment))
)
11. Powrót do WebRTC - Signalling
Kiedy już mamy skonstruowany protokół służący nam do szybkiej i dwustronnej komunikacji klient-serwer,
możemy przejść do punktu pierwszego nawiązywania połączenia WebRTC. Wspomniałem wcześniej, że
WebRTC to protokół komunikacji przeglądarka-przeglądarka, ale najpierw musimy je ze sobą jakoś
skontaktować, w tym celu posłużą nam Websockety.
Signalling - pierwszy i najważniejszy krok. W tym celu odsyłam przede wszystkim do
dokumentacji i hasła PERFECT NEGOTIATION.
Signalling w skrócie polega na tym, że za pomocą javascriptu “przejmujemy kontrolę” nad
lokalnymi mediami - mikrofon, kamerka, obraz, po czym przesyłamy wszelkie informacje o
nich do drugiej przeglądarki za pomocą Websocketów jako SDP (Session Description
Protocol).
12. Powrót do WebRTC - Signalling cd.
const signaling = new SignalingChannel(); // handles
JSON.stringify/parse
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);
async function start() {
try {
const stream = await
navigator.mediaDevices.getUserMedia(constraints);
for (const track of stream.getTracks()) {
pc.addTrack(track, stream);
}
selfView.srcObject = stream;
} catch (err) {
console.error(err);
}
}
13. Powrót do WebRTC - Signalling cd.
let makingOffer = false;
let ignoreOffer = false;
let isSettingRemoteAnswerPending = false;
// let the "negotiationneeded" event trigger offer generation
pc.onnegotiationneeded = async () => {
try {
makingOffer = true;
await pc.setLocalDescription();
signaling.send({description: pc.localDescription});
} catch (err) {
console.error(err);
} finally {
makingOffer = false;
}
};
14. WebRTC - Connecting
Kolejnym krokiem jest wymiana informacji o możliwości połączenia się między przeglądarkami (adresy ip,
porty), w tym celu służą ICE (Interactive Connectivity Establishment) candidates. Zwykle nasze sieci mogą
stać za firewallami itd, co może skutecznie blokować połączenie. Dlatego polecam skonfigurować
dodatkowo ice servers (STUN/TURN). W internecie jest kilka darmowych, ale jeśli chcemy używać
WebRTC dla wzmożonego ruchu - warto zakupić własny.
Grafika pochodzi z
https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Connectivity
15. Powrót do WebRTC - Perfect Negotiation
W prawdziwych warunkach nic nie jest idealne, może dojść do opóźnień lub zakleszczeń - obie
przeglądarki wyślą offer w tym samym momencie, dlatego warto zastosować design pattern zwany
Perfect Negotiation. Jest on o tyle dobry, że mamy jeden codebase, który wszystkim zarządza.
Pattern zakłada, że jedna przeglądarka występuje w roli polite, druga impolite - jak je podzielić zależy od
developera, u mnie decyduje o tym, kto pierwszy podłączył się do websocketów. Następnie obie
przeglądarki mogą do siebie wysyłać w tym samym czasie SDP i ICE, a jeśli dojdzie do zakleszczenia -
algorytm sam go rozwiąże - przeglądarka impolite zignoruje offer drugiej, jeśli sama go już wysłała.
Polecam wzorować się na tym przykładzie z samej dokumentacji na stronie
mozilli lub w3c.github.io - pozwala uniknąć błędów i frustracji.
16. WebRTC - Securing i Communicating
Krok securing odbywa się automatycznie i nie da się w niego zaingerować w żaden
sposób.
Communicating polega na przesyle obrazu/audio między połączonymi przeglądarkami w
obiektach MediaStream. W międzyczasie można do woli podmieniać źródła obrazu i
dźwięku - wtedy od nowa powinno się ustanowić połączenie, ale to zabezpiecza nam
perfect negotiation.
17. Tips & tricks
Żadne znane mi rozwiązanie związane z Websocketami nie oferowały prostego
przesyłu client eventu za pomocą websocketu zamiast requestu HTTP, jednak Laravel
Websockets po lekkim zmodyfikowaniu idealnie się do tego nadaje (jednak wymusza
to na nas forkowanie projektu i/lub nadpisywanie kilku klas i zatrzymanie wersji
paczki w composer.json).