sgcWebRTC es un nuevo paquete para Delphi y C++ Builder que añade un motor WebRTC completo a tu aplicación: señalización SDP de oferta/respuesta real, conectividad ICE con STUN y TURN, transporte cifrado DTLS-SRTP, canales de datos SCTP y pistas de audio y vídeo con Opus, G.711, VP8 y H.264.
Funciona como Object Pascal puro. No hay Chromium incrustado, ni control TWebBrowser o TEdgeBrowser, ni puente JavaScript dentro del proceso. Esa es la diferencia con la mayoría de las formas de hacer WebRTC desde Delphi, que funcionan alojando un motor de navegador y manejando su pila JavaScript desde Pascal. Aquí la conexión entre pares es código compilado que llama directamente al sistema operativo, así que también funciona en Android e iOS, y dentro de un servicio de Windows sin interfaz o de un dispositivo kiosco sin ningún navegador instalado.
sgcWebRTC es un complemento de sgcWebSockets Enterprise, y está incluido en el paquete All-Access.
Un componente, toda la conexión entre pares
sgcWebRTC no añade componentes nuevos a la paleta. Desbloquea el resto de la superficie con forma W3C en la misma clase TsgcRTCPeerConnection que ya viene en sgcWebSockets Enterprise. Enterprise te da ese componente para la conectividad ICE y TURN y para la señalización sobre un relé WebSocket. sgcWebRTC añade encima la máquina de estados SDP, la asociación SCTP, las sesiones RTP y el cifrado SRTP, de modo que un solo objeto gestiona señalización, conectividad, cifrado, canales de datos y medios.
Una conexión entre pares se construye en cuatro pasos. Primero la señalización, donde los dos pares intercambian descripciones de sesión por un canal que ya tienes. Después la conexión, donde ICE averigua qué dirección y qué puerto pueden alcanzar realmente al otro lado. Después el cifrado, donde un handshake DTLS sobre el par de candidatos nominado deriva las claves SRTP. Y por último la comunicación, donde los datos y los medios fluyen directamente entre los pares sin ningún servidor en medio.
Señalización: SDP real, para que el otro par pueda ser un navegador
CreateOffer, CreateAnswer, SetLocalDescription, SetRemoteDescription y AddIceCandidate construyen y consumen SDP estándar siguiendo la máquina de estados JSEP del RFC 8829. El par del otro extremo puede ser otra aplicación Delphi, una app móvil o una pestaña del navegador. Tú transportas la descripción y los candidatos por el canal de señalización que prefieras, un WebSocket, un endpoint HTTP o una cola de mensajes, exactamente igual que una aplicación de navegador los transporta a través de su propio servidor de señalización.
Este es el lado que oferta. Configura un servidor ICE, engancha los dos eventos de señalización, abre un canal de datos y después crea la oferta.
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;
El lado que responde consume lo que llega y contesta. Nada más cambia.
// 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 renegociación, el reinicio de ICE y la regla W3C de negociación perfecta ante colisiones vienen integradas. Añade una pista a una sesión ya establecida y NegotiationNeeded pasa a true, se dispara OnNegotiationNeeded y la siguiente oferta lleva la nueva línea m. Si los dos pares ofertan a la vez, aquel cuya propiedad Polite vale True revierte su propia oferta y responde a la remota, así que la sesión sobrevive a la colisión en lugar de bloquearse.
Conexión: ICE, STUN y TURN
La recolección de candidatos sigue el RFC 8445. Los candidatos host salen de las interfaces locales, los candidatos reflexivos de servidor salen de una petición binding de STUN, y los candidatos retransmitidos salen de una asignación TURN cuando los dos pares están detrás de NAT que no les dejan alcanzarse directamente. Después ICE empareja cada candidato local con cada candidato remoto y prueba los pares hasta que uno funciona.
oRTC.RTCOptions.ICEServers.AddURL('stun:stun.l.google.com:19302');
oRTC.RTCOptions.ICEServers.AddURL('turn:turn.example.com:3478',
'username', 'credential');
OnConnectionStateChange informa del transporte según avanza por la recolección, la conexión y el estado conectado, y SelectedLocalCandidate y SelectedRemoteCandidate te dicen qué par ganó, que es la forma más rápida de ver si una llamada fue directa o pasó por el relé.
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;
Hay dos opciones que importan cuando el otro par es un navegador. TrickleICE publica los candidatos según se van encontrando en lugar de esperar a que termine la recolección, y TrickleICEAuto lo activa automáticamente cuando la descripción remota lo anuncia. Todos los navegadores usan trickle, así que la respuesta a una oferta de un navegador sale de inmediato en vez de después del tiempo de espera de la recolección.
Cifrado: DTLS-SRTP, no opcional
El cifrado de los medios es obligatorio, el mismo modelo que impone un navegador. Un handshake DTLS se ejecuta sobre el par ICE nominado, de él se derivan las claves SRTP, y todos los paquetes RTP, RTCP y SCTP de la conexión van cifrados desde el primer paquete. El extremo remoto se autentica con la huella del certificado que viaja en el SDP, y por eso WebRTC no necesita una autoridad de certificación para esto.
Canales de datos sobre SCTP
CreateDataChannel abre un RTCDataChannel sobre SCTP dentro de DTLS, con el handshake de apertura DCEP del RFC 8832. El canal puede ser ordenado o desordenado, totalmente fiable, o parcialmente fiable con un límite de retransmisiones o un límite de tiempo de vida, que es justo lo que quieres para telemetría o estado de juego, donde un paquete tardío vale menos que uno reciente.
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 mueve texto y SendBytes mueve cargas binarias. BufferedAmount te dice cuánto queda todavía en cola, así que una transferencia de archivos puede regular su propio ritmo en lugar de inundar la asociación.
Audio, vídeo y compartición de pantalla
AddTrack adjunta una pista de medios a la conexión y la devuelve. La pista es dueña de su codificador y de su decodificador, así que tú metes muestras o fotogramas en bruto y sale el RTP ya codificado y cifrado. Lo que llega del par remoto vuelve decodificado por los eventos de la pista.
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;
Una pista añadida por el par remoto, en vez de una creada por ti, llega a través de OnTrack con los mismos eventos ya conectados al códec negociado.
El audio es Opus, o G.711 en sus variantes u-law y A-law cuando tienes que interoperar con telefonía. El vídeo es VP8 mediante un binding de libvpx, H.264 mediante el codificador por hardware que ofrece la plataforma, Media Foundation en Windows, VideoToolbox en macOS e iOS, MediaCodec en Android, y Motion JPEG en Windows para los casos en los que basta con una ruta sencilla solo de intra.
La captura de micrófono y la reproducción por altavoz vienen para Windows, Linux, macOS, iOS y Android. La captura de cámara, la captura de escritorio y el renderizado de vídeo vienen para Windows, donde TsgcScreenCapture_Win captura todo el escritorio, un monitor, una sola ventana o una región, con el cursor o sin él. En las demás plataformas los códecs y el transporte funcionan igual, y tú metes los fotogramas con SendVideoFrame y dibujas lo que llega desde OnVideoFrame con lo que te ofrezca la plataforma.
Aguantar en una red real
Una conexión entre pares que solo funciona en una LAN tranquila no sirve de mucho. sgcWebRTC lleva la misma caja de herramientas de resiliencia que usa un navegador, y cada pieza es un interruptor en RTCOptions.Media, que se negocia solo cuando la descripción remota también lo indica.
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 estimador de ancho de banda basado en el retardo, al estilo de Google Congestion Control, vigila el enlace y dirige el bitrate objetivo del codificador de vídeo, así que la imagen se degrada con suavidad cuando la conexión se estrecha en lugar de congelarse. La realimentación RTCP NACK y PLI, la retransmisión del RFC 4588 y RED/ULPFEC cubren la pérdida de paquetes por debajo.
Los servidores también se incluyen
Una conexión entre pares necesita un canal de señalización que presente a los dos pares, y normalmente un servidor STUN, y un servidor TURN para los casos en los que no pasa nada más. Los tres se incluyen como componentes Delphi en sgcWebSockets Enterprise, así que puedes ejecutar tú mismo toda la pila en lugar de alquilar la infraestructura: TsgcWSPServer_WebRTC como relé de señalización, TsgcSTUNServer y TsgcTURNServer para la conectividad. Nada te impide apuntar el cliente a un servidor STUN público o a un servicio TURN alojado, es el protocolo estándar en cualquiera de los dos casos.
Estándares
sgcWebRTC es un motor basado en estándares, no un transporte propietario. Implementa el RFC 8445 y el RFC 8489 para ICE y STUN, el RFC 8656 para TURN, el RFC 8827 y el RFC 5764 para DTLS y DTLS-SRTP, el RFC 3550 y el RFC 3711 para RTP y SRTP, el RFC 8831 y el RFC 8832 para los canales de datos, el RFC 8829 para la máquina de estados de oferta/respuesta JSEP, y el RFC 4588 para la retransmisión. Eso es lo que permite que un par Delphi hable con un par navegador sin ninguna capa de traducción en medio.
Demos
Con el paquete se incluyen cuatro demos, cada una con el código fuente completo. Demos\35.P2P\05.RTCPeerConnection conecta dos pares e incluye un pequeño servidor de señalización WebSocket que puedes ejecutar en local. Demos\35.P2P\06.DataChannel abre un canal de datos SCTP y envía por él cargas de texto y binarias. Demos\30.WebRTC_Protocol\03.AudioCall y Demos\30.WebRTC_Protocol\02.VideoCall son las demos de medios, del micrófono al altavoz y de la cámara a la pantalla.
Disponibilidad
sgcWebRTC es un complemento de sgcWebSockets Enterprise. No está disponible para las ediciones Standard o Professional, y está incluido en el paquete All-Access. Hay licencias Single, Team y Site, todas con el código fuente completo y un año de actualizaciones.
Es compatible con Delphi 7 hasta Delphi 13 Florence y las versiones equivalentes de C++ Builder, en Windows, Linux, macOS, iOS y Android. No hay una descarga aparte, el instalador de prueba de tu versión del IDE ya lo contiene.
El motor multimedia es solo para Delphi y C++ Builder. El port .NET de sgcWebSockets incluye TsgcWSProtocol_WebRTC_Server, un relé de señalización WebSocket para el escenario clásico de navegador a navegador, pero no hay equivalente .NET de TsgcRTCPeerConnection.
Página del producto · Desglose de características · Descargar la versión de prueba · Precios
¿Preguntas o comentarios? Ponte en contacto, recibirás respuesta de las personas que escribieron el código.
