sgcWebRTC: een native WebRTC media-engine voor Delphi en C++ Builder

· Componenten
sgcWebRTC, een native WebRTC media-engine voor Delphi en C++ Builder

sgcWebRTC is een nieuw pakket voor Delphi en C++ Builder dat een complete WebRTC-engine aan uw applicatie toevoegt: echte SDP offer/answer-signalering, ICE-connectiviteit met STUN en TURN, DTLS-SRTP-versleuteld transport, SCTP-datakanalen, en audio- en videotracks met Opus, G.711, VP8 en H.264.

Het draait als pure Object Pascal. Er is geen ingebedde Chromium, geen TWebBrowser- of TEdgeBrowser-control, en geen JavaScript-brug in het proces. Dat is het verschil met de meeste andere manieren om WebRTC vanuit Delphi te gebruiken, die werken door een browser-engine te hosten en de JavaScript-stack daarvan vanuit Pascal aan te sturen. Hier is de peer connection gecompileerde code die het besturingssysteem rechtstreeks aanroept, zodat het ook draait op Android en iOS, en binnen een headless Windows-service of een kioskapparaat waarop helemaal geen browser is geïnstalleerd.

sgcWebRTC is een add-on voor sgcWebSockets Enterprise, en is inbegrepen in de All-Access-bundel.

Eén component, de volledige peer connection

sgcWebRTC plaatst geen nieuwe componenten op het palet. Het ontgrendelt de rest van het W3C-gevormde oppervlak op dezelfde TsgcRTCPeerConnection-klasse die al met sgcWebSockets Enterprise wordt meegeleverd. Enterprise geeft u die component voor ICE- en TURN-connectiviteit en voor signalering via een WebSocket-relay. sgcWebRTC voegt daar de SDP-toestandsmachine, de SCTP-associatie, de RTP-sessies en de SRTP-versleuteling aan toe, zodat één object signalering, connectiviteit, versleuteling, datakanalen en media bezit.

Een peer connection wordt in vier stappen opgebouwd. Eerst signalering, waarbij de twee peers sessiebeschrijvingen uitwisselen via een kanaal dat u al hebt. Daarna verbinden, waarbij ICE uitzoekt welk adres en welke poort de andere kant daadwerkelijk kunnen bereiken. Daarna beveiligen, waarbij een DTLS-handshake over het genomineerde kandidatenpaar de SRTP-sleutels afleidt. En ten slotte communiceren, waarbij data en media rechtstreeks tussen de peers stromen, zonder server ertussen.

Signalering: echte SDP, zodat de andere peer een browser kan zijn

CreateOffer, CreateAnswer, SetLocalDescription, SetRemoteDescription en AddIceCandidate bouwen en verwerken standaard-SDP volgens de JSEP-toestandsmachine van RFC 8829. De peer aan de andere kant kan een andere Delphi-applicatie zijn, een mobiele app of een browsertabblad. U vervoert de beschrijving en de kandidaten over welk signaleringskanaal u ook wilt, een WebSocket, een HTTP-endpoint of een berichtenwachtrij, precies zoals een browserapplicatie ze via haar eigen signaleringsserver vervoert.

Dit is de aanbiedende kant. Stel een ICE-server in, koppel de twee signaleringsevents, open een datakanaal en maak dan de offer aan.

uses
  sgcP2P;

var
  oRTC: TsgcRTCPeerConnection;
begin
  oRTC := TsgcRTCPeerConnection.Create(nil);

  oRTC.RTCOptions.ICEServers.AddURL('stun:stun.l.google.com:19302');

  oRTC.OnLocalDescription := OnLocalDescriptionHandler;
  oRTC.OnICECandidate := OnICECandidateHandler;
  oRTC.OnConnectionStateChange := OnConnectionStateChangeHandler;
  oRTC.OnDataChannel := OnDataChannelHandler;

  oRTC.TrickleICE := True;

  oRTC.CreateDataChannel('chat'); // forces RTCOptions.DTLS on
  oRTC.CreateOffer;               // gathers candidates and builds the SDP offer
end;

procedure TForm1.OnLocalDescriptionHandler(Sender: TObject;
  const aType, aSDP: string);
begin
  // aType is 'offer' or 'answer'. Send both fields to the remote peer
  // over your own signalling channel.
  MySignalling.SendDescription(aType, aSDP);
end;

procedure TForm1.OnICECandidateHandler(Sender: TObject;
  const aCandidate, aSdpMid: string; aSdpMLineIndex: Integer);
begin
  // With TrickleICE on, candidates arrive one by one while the offer
  // is already on its way to the other peer.
  MySignalling.SendCandidate(aCandidate, aSdpMid, aSdpMLineIndex);
end;

De antwoordende kant verwerkt wat binnenkomt en antwoordt. Verder verandert er niets.

// the remote description arrived over your signalling channel
oRTC.SetRemoteDescription('offer', vSDP);
oRTC.CreateAnswer;          // raises OnLocalDescription with 'answer'

// and every remote candidate as it arrives
oRTC.AddIceCandidate(vCandidate, vSdpMid, vSdpMLineIndex);

Hernegotiatie, ICE-herstart en de W3C perfect-negotiation glare-regel zijn ingebouwd. Voeg een track toe aan een bestaande sessie en NegotiationNeeded wordt true, OnNegotiationNeeded vuurt en de volgende offer bevat de nieuwe m-regel. Als beide peers tegelijk een offer doen, draait degene met Polite op True de eigen offer terug en beantwoordt die van de ander, zodat de sessie de botsing overleeft in plaats van vast te lopen.

Verbinden: ICE, STUN en TURN

Het verzamelen van kandidaten volgt RFC 8445. Hostkandidaten komen van de lokale interfaces, server-reflexieve kandidaten komen van een STUN binding request, en relayed kandidaten komen van een TURN-allocatie wanneer de twee peers achter NATs zitten die hen niet rechtstreeks bij elkaar laten komen. ICE koppelt vervolgens elke lokale kandidaat aan elke externe kandidaat en test de paren tot er een werkt.

oRTC.RTCOptions.ICEServers.AddURL('stun:stun.l.google.com:19302');
oRTC.RTCOptions.ICEServers.AddURL('turn:turn.example.com:3478',
  'username', 'credential');

OnConnectionStateChange rapporteert het transport terwijl het door gathering, connecting en connected beweegt, en SelectedLocalCandidate en SelectedRemoteCandidate vertellen u welk paar heeft gewonnen, wat de snelste manier is om te zien of een gesprek rechtstreeks ging of via de relay.

procedure TForm1.OnConnectionStateChangeHandler(Sender: TObject;
  aState: TsgcRTCConnectionState);
begin
  case aState of
    rtccsGathering:    DoLog('gathering candidates');
    rtccsConnecting:   DoLog('checking candidate pairs');
    rtccsConnected:    DoLog('connected: ' + oRTC.SelectedRemoteCandidate);
    rtccsDisconnected: DoLog('disconnected');
    rtccsFailed:       DoLog('failed, try RestartIce');
  end;
end;

Twee opties zijn van belang wanneer de andere peer een browser is. TrickleICE publiceert kandidaten zodra ze gevonden worden in plaats van te wachten tot het verzamelen klaar is, en TrickleICEAuto schakelt dat automatisch in wanneer de externe beschrijving het aankondigt. Elke browser trickelt, dus een antwoord op een browser-offer gaat meteen de deur uit in plaats van pas na de gathering-timeout.

Beveiligen: DTLS-SRTP, niet optioneel

Mediaversleuteling is verplicht, hetzelfde model dat een browser afdwingt. Een DTLS-handshake loopt over het genomineerde ICE-paar, de SRTP-sleutels worden daaruit afgeleid, en elk RTP-, RTCP- en SCTP-pakket op de verbinding is vanaf het eerste pakket versleuteld. De andere kant wordt geauthenticeerd via de certificaat-fingerprint die in de SDP wordt meegestuurd, en daarom heeft WebRTC hiervoor geen certificaatautoriteit nodig.

Datakanalen over SCTP

CreateDataChannel opent een RTCDataChannel over SCTP-over-DTLS, met de DCEP open-handshake van RFC 8832. Het kanaal kan geordend of ongeordend zijn, volledig betrouwbaar, of gedeeltelijk betrouwbaar met een hertransmissielimiet of een levensduurlimiet, wat u wilt voor telemetrie of spelstatus waarbij een laat pakket minder waard is dan een vers pakket.

uses
  sgcP2P, sgcP2P_DataChannel;

var
  vChat, vTelemetry: TsgcRTCDataChannel;
begin
  // ordered and reliable, the default
  vChat := oRTC.CreateDataChannel('chat');
  vChat.OnOpen := OnChannelOpen;
  vChat.OnMessage := OnChannelMessage;

  // unordered, no retransmissions: drop it rather than deliver it late
  vTelemetry := oRTC.CreateDataChannel('telemetry', False, 0);
end;

procedure TForm1.OnChannelOpen(Sender: TObject);
begin
  TsgcRTCDataChannel(Sender).Send('hello');
end;

procedure TForm1.OnChannelMessage(Sender: TObject; const aText: string);
begin
  DoLog('remote said: ' + aText);
end;

// a channel the remote peer opened arrives here
procedure TForm1.OnDataChannelHandler(Sender: TObject;
  aChannel: TsgcRTCDataChannel);
begin
  aChannel.OnMessage := OnChannelMessage;
  aChannel.OnMessageBinary := OnChannelMessageBinary;
end;

Send verstuurt tekst en SendBytes verstuurt binaire payloads. BufferedAmount vertelt u hoeveel er nog in de wachtrij staat, zodat een bestandsoverdracht zichzelf kan doseren in plaats van de associatie te overspoelen.

Audio, video en schermdeling

AddTrack koppelt een mediatrack aan de verbinding en geeft die terug. De track bezit zijn eigen encoder en decoder, dus u duwt er ruwe samples of frames in en de gecodeerde, versleutelde RTP gaat naar buiten. Wat van de externe peer binnenkomt, komt gedecodeerd terug via de events van de track.

uses
  sgcP2P, sgcP2P_RTC_Media, sgcP2P_Codec_Types;

var
  vAudio, vVideo: TsgcRTCTrack;
begin
  vAudio := oRTC.AddTrack(rtctkAudio, cctAudioOpus);
  vVideo := oRTC.AddTrack(rtctkVideo, cctVideoH264);

  // what the remote peer sends, already decoded
  vAudio.OnAudio := OnRemoteAudio;
  vVideo.OnVideoFrame := OnRemoteVideoFrame;

  oRTC.CreateOffer;
end;

// push captured microphone samples into the audio track
procedure TForm1.OnMicrophoneCapture(Sender: TObject; const aPCM: TBytes;
  aSamplesPerChannel: Integer);
begin
  vAudio.SendPCM(aPCM, aSamplesPerChannel);
end;

// push captured camera or desktop frames into the video track
procedure TForm1.OnCameraCapture(Sender: TObject;
  const aFrame: TsgcVideoFrame);
begin
  vVideo.SendVideoFrame(aFrame);
end;

// draw what the remote peer sends
procedure TForm1.OnRemoteVideoFrame(Sender: TObject;
  const aFrame: TsgcVideoFrame);
begin
  MyRenderer.Render(aFrame);
end;

Een track die de externe peer heeft toegevoegd, in plaats van een die u zelf hebt aangemaakt, komt binnen via OnTrack, met dezelfde events al gekoppeld aan de onderhandelde codec.

Audio is Opus, of G.711 in de u-law- en A-law-varianten wanneer u met telefonie moet samenwerken. Video is VP8 via een libvpx-binding, H.264 via de hardware-encoder die het platform biedt, Media Foundation op Windows, VideoToolbox op macOS en iOS, MediaCodec op Android, en Motion JPEG op Windows voor de gevallen waarin een eenvoudig intra-only pad genoeg is.

Microfoonopname en luidsprekerweergave worden geleverd voor Windows, Linux, macOS, iOS en Android. Camera-opname, desktopopname en videoweergave worden geleverd voor Windows, waar TsgcScreenCapture_Win het hele bureaublad, één monitor, een enkel venster of een gebied vastlegt, met of zonder de cursor. Op de andere platforms werken de codecs en het transport op dezelfde manier, en voert u frames in via SendVideoFrame en tekent u wat via OnVideoFrame binnenkomt met wat het platform u biedt.

Standhouden op een echt netwerk

Een peer connection die alleen op een rustig LAN werkt, heeft weinig nut. sgcWebRTC draagt dezelfde gereedschapskist voor veerkracht mee die een browser gebruikt, en elk onderdeel is een schakelaar op RTCOptions.Media, die alleen wordt onderhandeld wanneer de externe beschrijving hem ook toont.

oRTC.RTCOptions.Media.CongestionControl := True; // transport-cc, REMB fallback
oRTC.RTCOptions.Media.Pacing := True;            // smooth the outgoing packets
oRTC.RTCOptions.Media.RTX := True;               // RFC 4588 retransmission on NACK
oRTC.RTCOptions.Media.FEC := True;               // RED / ULPFEC forward error correction

Een vertragingsgebaseerde bandbreedteschatter in de stijl van Google Congestion Control houdt de verbinding in de gaten en stuurt de doelbitrate van de video-encoder aan, zodat het beeld soepel degradeert wanneer de verbinding smaller wordt in plaats van te blokkeren. RTCP NACK- en PLI-feedback, RFC 4588-hertransmissie en RED/ULPFEC vangen het pakketverlies daaronder op.

De servers worden ook meegeleverd

Een peer connection heeft een signaleringskanaal nodig om de twee peers aan elkaar voor te stellen, en meestal een STUN-server, en een TURN-server voor de gevallen waarin niets anders erdoorheen komt. Alle drie worden als Delphi-componenten meegeleverd in sgcWebSockets Enterprise, zodat u de hele stack zelf kunt draaien in plaats van de infrastructuur te huren: TsgcWSPServer_WebRTC als signaleringsrelay, TsgcSTUNServer en TsgcTURNServer voor connectiviteit. Niets weerhoudt u ervan de client in plaats daarvan naar een publieke STUN-server of een gehoste TURN-dienst te laten wijzen, het is hoe dan ook hetzelfde standaardprotocol.

Standaarden

sgcWebRTC is een op standaarden gebaseerde engine, geen propriëtair transport. Het implementeert RFC 8445 en RFC 8489 voor ICE en STUN, RFC 8656 voor TURN, RFC 8827 en RFC 5764 voor DTLS en DTLS-SRTP, RFC 3550 en RFC 3711 voor RTP en SRTP, RFC 8831 en RFC 8832 voor datakanalen, RFC 8829 voor de JSEP offer/answer-toestandsmachine, en RFC 4588 voor hertransmissie. Dat is wat een Delphi-peer in staat stelt om met een browser-peer te praten zonder vertaallaag ertussen.

Demo's

Er worden vier demo's met het pakket meegeleverd, elk met volledige broncode. Demos\35.P2P\05.RTCPeerConnection verbindt twee peers en bevat een kleine WebSocket-signaleringsserver die u lokaal kunt draaien. Demos\35.P2P\06.DataChannel opent een SCTP-datakanaal en verstuurt daarover tekst en binaire payloads. Demos\30.WebRTC_Protocol\03.AudioCall en Demos\30.WebRTC_Protocol\02.VideoCall zijn de mediademo's, microfoon naar luidspreker en camera naar scherm.

Beschikbaarheid

sgcWebRTC is een add-on voor sgcWebSockets Enterprise. Het is niet beschikbaar voor de Standard- of Professional-editie, en het is inbegrepen in de All-Access-bundel. Single-, Team- en Site-licenties zijn beschikbaar, allemaal met volledige broncode en één jaar updates.

Het ondersteunt Delphi 7 tot en met Delphi 13 Florence en de bijbehorende C++ Builder-versies, op Windows, Linux, macOS, iOS en Android. Er is geen aparte download, het proefinstallatieprogramma voor uw IDE-versie bevat het al.

De media-engine is alleen voor Delphi en C++ Builder. De sgcWebSockets .NET-port bevat TsgcWSProtocol_WebRTC_Server, een WebSocket-signaleringsrelay voor het klassieke browser-naar-browserscenario, maar er is geen .NET-equivalent van TsgcRTCPeerConnection.

Productpagina · Overzicht van functies · Download de proefversie · Prijzen

Vragen of feedback? Neem contact op, u krijgt antwoord van de mensen die de code hebben geschreven.