sgcWebRTC:适用于 Delphi 和 C++ Builder 的原生 WebRTC 媒体引擎

· 组件
sgcWebRTC,适用于 Delphi 和 C++ Builder 的原生 WebRTC 媒体引擎

sgcWebRTC 是一个面向 Delphi 和 C++ Builder 的全新组件包,它为您的应用程序加入完整的 WebRTC 引擎:真正的 SDP offer/answer 信令、基于 STUN 和 TURN 的 ICE 连通性、DTLS-SRTP 加密传输、SCTP 数据通道,以及采用 Opus、G.711、VP8 和 H.264 的音频与视频轨道。

它以纯 Object Pascal 运行。进程中没有内嵌的 Chromium,没有 TWebBrowserTEdgeBrowser 控件,也没有 JavaScript 桥接。这正是它与在 Delphi 中实现 WebRTC 的大多数其他做法之间的区别,那些做法的原理是承载一个浏览器引擎,再从 Pascal 驱动它的 JavaScript 栈。在这里,对等连接是直接调用操作系统的编译代码,因此它同样可以运行在 Android 和 iOS 上,也可以运行在无界面的 Windows 服务中,或者运行在完全没有安装浏览器的自助终端设备上。

sgcWebRTC 是 sgcWebSockets Enterprise 的附加组件,并且包含在 All-Access 套装中。

一个组件,完整的对等连接

sgcWebRTC 不会在组件面板上添加新组件。它在 sgcWebSockets Enterprise 已经提供的同一个 TsgcRTCPeerConnection 类上,解锁其余符合 W3C 形态的接口。Enterprise 提供该组件用于 ICE 和 TURN 连通性,以及通过 WebSocket 中继进行信令。sgcWebRTC 在其之上加入 SDP 状态机、SCTP 关联、RTP 会话和 SRTP 加密,于是一个对象就掌管了信令、连通性、加密、数据通道和媒体。

对等连接分四步建立。首先是信令,两个对等端通过您已有的通道交换会话描述。然后是连接,ICE 找出哪个地址和端口真正能够到达对方。接着是加密,在选定的候选项对上进行 DTLS 握手,由此派生出 SRTP 密钥。最后是通信,数据和媒体在两个对等端之间直接流动,中间没有服务器。

信令:真正的 SDP,因此对端可以是浏览器

CreateOfferCreateAnswerSetLocalDescriptionSetRemoteDescriptionAddIceCandidate 按照 RFC 8829 的 JSEP 状态机构建并处理标准 SDP。另一端的对等端可以是另一个 Delphi 应用程序、一个移动应用,或者一个浏览器标签页。您可以用任何喜欢的信令通道传输描述和候选项,WebSocket、HTTP 端点或消息队列都可以,就像浏览器应用通过自己的信令服务器传输它们一样。

这是发起方。设置一个 ICE 服务器,挂接两个信令事件,打开一个数据通道,然后创建 offer。

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;

应答方处理收到的内容并作出回复。其他什么都不用改。

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

重新协商、ICE 重启以及 W3C 的完美协商冲突规则都已内置。向已建立的会话添加一个轨道,NegotiationNeeded 就会变为 true,OnNegotiationNeeded 触发,下一个 offer 会带上新的 m-line。如果两个对等端同时发出 offer,Polite 属性为 True 的一方会回滚自己的 offer 并应答对方的 offer,这样会话就能在冲突中存活下来,而不会陷入死锁。

连接:ICE、STUN 和 TURN

候选项收集遵循 RFC 8445。主机候选项来自本地网络接口,服务器自反候选项来自 STUN 绑定请求,而当两个对等端位于无法让它们直接互通的 NAT 之后时,中继候选项来自 TURN 分配。随后 ICE 会把每个本地候选项与每个远端候选项配对,并逐对测试,直到某一对可用为止。

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

OnConnectionStateChange 会在传输经过收集、连接中和已连接各阶段时报告状态,而 SelectedLocalCandidateSelectedRemoteCandidate 会告诉您哪一对候选项胜出,这是查看一次呼叫走的是直连还是中继的最快方式。

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;

当对端是浏览器时,有两个选项很重要。TrickleICE 会在发现候选项时就立即发布,而不是等到收集结束,TrickleICEAuto 则会在远端描述声明支持时自动启用它。所有浏览器都采用 trickle 方式,因此对浏览器 offer 的应答会立即发出,而不必等到收集超时之后。

加密:DTLS-SRTP,没有可选项

媒体加密是强制性的,与浏览器执行的模型相同。DTLS 握手在选定的 ICE 候选项对上运行,SRTP 密钥由此派生,连接上的每一个 RTP、RTCP 和 SCTP 数据包从第一个包起就是加密的。远端通过 SDP 中携带的证书指纹进行认证,这也是 WebRTC 在这方面不需要证书颁发机构的原因。

基于 SCTP 的数据通道

CreateDataChannel 会打开一个基于 SCTP-over-DTLS 的 RTCDataChannel,使用 RFC 8832 的 DCEP 打开握手。该通道可以是有序的或无序的,可以完全可靠,也可以是带重传次数上限或生命周期上限的部分可靠,后者正是遥测数据或游戏状态所需要的,因为在这些场景中,迟到的数据包价值低于新鲜的数据包。

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 传输文本,SendBytes 传输二进制负载。BufferedAmount 会告诉您还有多少数据在排队,因此文件传输可以自行掌握节奏,而不会淹没整个关联。

音频、视频与屏幕共享

AddTrack 会向连接附加一个媒体轨道并返回它。该轨道拥有自己的编码器和解码器,因此您推入原始采样或帧,编码并加密后的 RTP 就会发送出去。从远端对等端到达的内容会经过解码,通过轨道事件返回给您。

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;

由远端对等端添加的轨道,而不是您自己创建的轨道,会通过 OnTrack 到达,其事件已经与协商好的编解码器连接妥当。

音频采用 Opus,或者在必须与电话系统互通时采用 G.711 的 u-law 和 A-law 两种形式。视频方面,VP8 通过 libvpx 绑定实现,H.264 通过平台提供的硬件编码器实现,Windows 上是 Media Foundation,macOS 和 iOS 上是 VideoToolbox,Android 上是 MediaCodec,此外 Windows 上还有 Motion JPEG,用于只需要简单的纯帧内路径就够用的场合。

麦克风采集和扬声器播放在 Windows、Linux、macOS、iOS 和 Android 上均已提供。摄像头采集、桌面采集和视频渲染在 Windows 上提供,其中 TsgcScreenCapture_Win 可以采集整个桌面、单个显示器、单个窗口或某个区域,并可选择是否包含鼠标指针。在其他平台上,编解码器和传输的工作方式完全相同,您通过 SendVideoFrame 送入帧,并用该平台提供的任何手段绘制从 OnVideoFrame 到达的内容。

在真实网络中稳得住

一个只能在安静的局域网中工作的对等连接没有太大用处。sgcWebRTC 带有与浏览器相同的一整套抗损工具,每一项都是 RTCOptions.Media 上的一个开关,只有当远端描述同样表明支持时才会协商启用。

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

一个采用 Google Congestion Control 风格的、基于延迟的带宽估计器会监测链路并驱动视频编码器的目标码率,因此当连接变窄时画面会平滑降级,而不是停滞。在其之下,RTCP NACK 和 PLI 反馈、RFC 4588 重传以及 RED/ULPFEC 负责应对丢包。

服务器端同样随包提供

对等连接需要一个信令通道来撮合两个对等端,通常还需要一个 STUN 服务器,以及在其他方式都无法连通时使用的 TURN 服务器。这三者都以 Delphi 组件的形式随 sgcWebSockets Enterprise 提供,因此您可以自己运行整套技术栈,而不必租用基础设施:TsgcWSPServer_WebRTC 作为信令中继,TsgcSTUNServerTsgcTURNServer 负责连通性。当然,也没有什么妨碍您把客户端指向公共 STUN 服务器或托管的 TURN 服务,无论哪种方式,用的都是标准协议。

标准

sgcWebRTC 是一个基于标准的引擎,而不是专有传输。它实现了用于 ICE 和 STUN 的 RFC 8445 与 RFC 8489、用于 TURN 的 RFC 8656、用于 DTLS 和 DTLS-SRTP 的 RFC 8827 与 RFC 5764、用于 RTP 和 SRTP 的 RFC 3550 与 RFC 3711、用于数据通道的 RFC 8831 与 RFC 8832、用于 JSEP offer/answer 状态机的 RFC 8829,以及用于重传的 RFC 4588。正是这一点让 Delphi 对等端可以直接与浏览器对等端通话,中间无需任何转换层。

演示程序

组件包附带四个演示程序,每一个都提供完整源码。Demos\35.P2P\05.RTCPeerConnection 连接两个对等端,并包含一个可以在本地运行的小型 WebSocket 信令服务器。Demos\35.P2P\06.DataChannel 打开一个 SCTP 数据通道,并通过它发送文本和二进制负载。Demos\30.WebRTC_Protocol\03.AudioCallDemos\30.WebRTC_Protocol\02.VideoCall 是媒体演示,分别是麦克风到扬声器和摄像头到屏幕。

供应方式

sgcWebRTC 是 sgcWebSockets Enterprise 的附加组件。它不适用于 Standard 或 Professional 版本,并且包含在 All-Access 套装中。提供 Single、Team 和 Site 授权,全部附带完整源码和一年的更新。

它支持 Delphi 7 到 Delphi 13 Florence 以及对应的 C++ Builder 版本,可运行于 Windows、Linux、macOS、iOS 和 Android。无需单独下载,适用于您 IDE 版本的试用版安装程序中已经包含它。

媒体引擎仅面向 Delphi 和 C++ Builder。sgcWebSockets 的 .NET 移植版包含 TsgcWSProtocol_WebRTC_Server,这是一个用于经典浏览器对浏览器场景的 WebSocket 信令中继,但没有 TsgcRTCPeerConnection 的 .NET 对应实现。

产品页面 · 功能详解 · 下载试用版 · 价格

有疑问或建议?联系我们,您会收到编写这些代码的人的回复。