sgcWebRTC : un moteur média WebRTC natif pour Delphi et C++ Builder

· Composants
sgcWebRTC, un moteur média WebRTC natif pour Delphi et C++ Builder

sgcWebRTC est un nouveau package pour Delphi et C++ Builder qui ajoute un moteur WebRTC complet à votre application : véritable signalisation SDP offre/réponse, connectivité ICE avec STUN et TURN, transport chiffré DTLS-SRTP, canaux de données SCTP, et pistes audio et vidéo avec Opus, G.711, VP8 et H.264.

Il fonctionne en Object Pascal pur. Il n'y a pas de Chromium embarqué, pas de contrôle TWebBrowser ou TEdgeBrowser, ni de pont JavaScript dans le processus. C'est là toute la différence avec la plupart des autres manières de faire du WebRTC depuis Delphi, qui consistent à héberger un moteur de navigateur et à piloter sa pile JavaScript depuis Pascal. Ici, la connexion pair à pair est du code compilé qui appelle directement le système d'exploitation, elle fonctionne donc aussi sur Android et iOS, et à l'intérieur d'un service Windows sans interface ou d'un appareil en mode kiosque sur lequel aucun navigateur n'est installé.

sgcWebRTC est un module complémentaire de sgcWebSockets Enterprise, et il est inclus dans le bundle All-Access.

Un seul composant, toute la connexion pair à pair

sgcWebRTC n'ajoute pas de nouveaux composants à la palette. Il débloque le reste de la surface calquée sur le W3C dans la même classe TsgcRTCPeerConnection déjà livrée avec sgcWebSockets Enterprise. Enterprise vous donne ce composant pour la connectivité ICE et TURN et pour la signalisation via un relais WebSocket. sgcWebRTC y ajoute la machine à états SDP, l'association SCTP, les sessions RTP et le chiffrement SRTP, de sorte qu'un seul objet possède la signalisation, la connectivité, le chiffrement, les canaux de données et les médias.

Une connexion pair à pair se construit en quatre étapes. D'abord la signalisation, où les deux pairs échangent des descriptions de session sur un canal dont vous disposez déjà. Puis la connexion, où ICE détermine quelle adresse et quel port permettent réellement d'atteindre l'autre côté. Puis la sécurisation, où une poignée de main DTLS sur la paire de candidats retenue dérive les clés SRTP. Et enfin la communication, où les données et les médias circulent directement entre les pairs, sans serveur au milieu.

Signalisation : du vrai SDP, pour que l'autre pair puisse être un navigateur

CreateOffer, CreateAnswer, SetLocalDescription, SetRemoteDescription et AddIceCandidate construisent et consomment du SDP standard en suivant la machine à états JSEP de la RFC 8829. Le pair situé à l'autre bout peut être une autre application Delphi, une application mobile ou un onglet de navigateur. Vous transportez la description et les candidats sur le canal de signalisation de votre choix, un WebSocket, un point d'accès HTTP ou une file de messages, exactement comme une application navigateur les transporte via son propre serveur de signalisation.

Voici le côté qui émet l'offre. Définissez un serveur ICE, branchez les deux événements de signalisation, ouvrez un canal de données, puis créez l'offre.

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;

Le côté qui répond consomme ce qui arrive et renvoie sa réponse. Rien d'autre ne change.

// 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);

La renégociation, le redémarrage d'ICE et la règle de collision de la négociation parfaite du W3C sont intégrés. Ajoutez une piste à une session établie et NegotiationNeeded passe à vrai, OnNegotiationNeeded se déclenche et l'offre suivante porte la nouvelle ligne m. Si les deux pairs émettent une offre en même temps, celui dont la propriété Polite vaut True annule sa propre offre et répond à celle du pair distant, de sorte que la session survit à la collision au lieu de se bloquer.

Connexion : ICE, STUN et TURN

La collecte des candidats suit la RFC 8445. Les candidats hôtes proviennent des interfaces locales, les candidats réflexifs du serveur proviennent d'une requête de binding STUN, et les candidats relayés proviennent d'une allocation TURN lorsque les deux pairs se trouvent derrière des NAT qui ne les laissent pas s'atteindre directement. ICE apparie ensuite chaque candidat local avec chaque candidat distant et teste les paires jusqu'à ce que l'une d'elles fonctionne.

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

OnConnectionStateChange rend compte du transport à mesure qu'il traverse la collecte, la connexion et l'état connecté, et SelectedLocalCandidate et SelectedRemoteCandidate vous indiquent quelle paire l'a emporté, ce qui est le moyen le plus rapide de voir si un appel est passé en direct ou par le relais.

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;

Deux options comptent lorsque l'autre pair est un navigateur. TrickleICE publie les candidats au fur et à mesure qu'ils sont trouvés au lieu d'attendre la fin de la collecte, et TrickleICEAuto l'active automatiquement lorsque la description distante l'annonce. Tous les navigateurs pratiquent le trickle, si bien qu'une réponse à une offre de navigateur part immédiatement plutôt qu'après le délai d'expiration de la collecte.

Sécurisation : DTLS-SRTP, pas optionnel

Le chiffrement des médias est obligatoire, le même modèle que celui qu'impose un navigateur. Une poignée de main DTLS s'exécute sur la paire ICE retenue, les clés SRTP en sont dérivées, et chaque paquet RTP, RTCP et SCTP de la connexion est chiffré dès le premier paquet. L'extrémité distante est authentifiée par l'empreinte du certificat portée dans le SDP, et c'est pour cette raison que WebRTC n'a pas besoin d'autorité de certification ici.

Canaux de données sur SCTP

CreateDataChannel ouvre un RTCDataChannel sur SCTP-over-DTLS, avec la poignée de main d'ouverture DCEP de la RFC 8832. Le canal peut être ordonné ou non ordonné, totalement fiable, ou partiellement fiable avec une limite de retransmissions ou une limite de durée de vie, ce qui est exactement ce que vous voulez pour de la télémétrie ou de l'état de jeu, où un paquet en retard vaut moins qu'un paquet frais.

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 transporte du texte et SendBytes transporte des charges utiles binaires. BufferedAmount vous indique ce qui reste en file d'attente, de sorte qu'un transfert de fichier peut réguler son rythme au lieu d'inonder l'association.

Audio, vidéo et partage d'écran

AddTrack attache une piste média à la connexion et la renvoie. La piste possède son encodeur et son décodeur, vous poussez donc des échantillons ou des images bruts en entrée et le RTP encodé et chiffré sort. Ce qui arrive du pair distant vous revient décodé par les événements de la piste.

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;

Une piste ajoutée par le pair distant, plutôt qu'une piste que vous avez créée, arrive par OnTrack avec les mêmes événements déjà reliés au codec négocié.

L'audio, c'est Opus, ou G.711 dans ses variantes u-law et A-law lorsque vous devez interopérer avec la téléphonie. La vidéo, c'est VP8 via une liaison libvpx, H.264 via l'encodeur matériel fourni par la plateforme, Media Foundation sur Windows, VideoToolbox sur macOS et iOS, MediaCodec sur Android, et Motion JPEG sur Windows pour les cas où un simple chemin intra seul suffit.

La capture du microphone et la lecture sur les haut-parleurs sont livrées pour Windows, Linux, macOS, iOS et Android. La capture de la caméra, la capture du bureau et le rendu vidéo sont livrés pour Windows, où TsgcScreenCapture_Win capture le bureau entier, un moniteur, une seule fenêtre ou une région, avec ou sans le curseur. Sur les autres plateformes, les codecs et le transport fonctionnent de la même manière, et vous alimentez les images via SendVideoFrame et dessinez ce qui arrive d'OnVideoFrame avec ce que la plateforme met à votre disposition.

Tenir la route sur un vrai réseau

Une connexion pair à pair qui ne fonctionne que sur un LAN tranquille ne sert pas à grand-chose. sgcWebRTC embarque la même boîte à outils de résilience qu'un navigateur, et chaque élément est un interrupteur sur RTCOptions.Media, négocié uniquement lorsque la description distante l'annonce aussi.

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

Un estimateur de bande passante fondé sur le délai, dans le style du Google Congestion Control, surveille le lien et pilote le débit cible de l'encodeur vidéo, de sorte que l'image se dégrade progressivement lorsque la connexion se rétrécit au lieu de se figer. Le retour NACK et PLI de RTCP, la retransmission RFC 4588 et RED/ULPFEC couvrent la perte de paquets en dessous.

Les serveurs aussi sont livrés

Une connexion pair à pair a besoin d'un canal de signalisation pour présenter les deux pairs, et généralement d'un serveur STUN, ainsi que d'un serveur TURN pour les cas où rien d'autre ne passe. Les trois sont livrés sous forme de composants Delphi dans sgcWebSockets Enterprise, vous pouvez donc faire tourner toute la pile vous-même au lieu de louer l'infrastructure : TsgcWSPServer_WebRTC comme relais de signalisation, TsgcSTUNServer et TsgcTURNServer pour la connectivité. Rien ne vous empêche de pointer plutôt le client vers un serveur STUN public ou un service TURN hébergé, c'est le protocole standard dans les deux cas.

Standards

sgcWebRTC est un moteur fondé sur des standards, pas un transport propriétaire. Il implémente les RFC 8445 et RFC 8489 pour ICE et STUN, la RFC 8656 pour TURN, les RFC 8827 et RFC 5764 pour DTLS et DTLS-SRTP, les RFC 3550 et RFC 3711 pour RTP et SRTP, les RFC 8831 et RFC 8832 pour les canaux de données, la RFC 8829 pour la machine à états offre/réponse JSEP, et la RFC 4588 pour la retransmission. C'est ce qui permet à un pair Delphi de parler à un pair navigateur sans couche de traduction entre les deux.

Démos

Quatre démos sont livrées avec le package, chacune avec le code source complet. Demos\35.P2P\05.RTCPeerConnection connecte deux pairs et inclut un petit serveur de signalisation WebSocket que vous pouvez exécuter localement. Demos\35.P2P\06.DataChannel ouvre un canal de données SCTP et y envoie des charges utiles texte et binaires. Demos\30.WebRTC_Protocol\03.AudioCall et Demos\30.WebRTC_Protocol\02.VideoCall sont les démos médias, du microphone au haut-parleur et de la caméra à l'écran.

Disponibilité

sgcWebRTC est un module complémentaire de sgcWebSockets Enterprise. Il n'est pas disponible pour les éditions Standard ou Professional, et il est inclus dans le bundle All-Access. Des licences Single, Team et Site sont proposées, toutes avec le code source complet et un an de mises à jour.

Il prend en charge Delphi 7 jusqu'à Delphi 13 Florence et les versions correspondantes de C++ Builder, sur Windows, Linux, macOS, iOS et Android. Il n'y a pas de téléchargement séparé, l'installateur d'évaluation de votre version de l'IDE le contient déjà.

Le moteur média est réservé à Delphi et C++ Builder. Le portage sgcWebSockets .NET inclut TsgcWSProtocol_WebRTC_Server, un relais de signalisation WebSocket pour le scénario classique de navigateur à navigateur, mais il n'existe pas d'équivalent .NET de TsgcRTCPeerConnection.

Page produit · Détail des fonctionnalités · Télécharger la version d'évaluation · Tarifs

Des questions ou des remarques ? Contactez-nous, vous recevrez une réponse des personnes qui ont écrit le code.