sgcWebRTC는 애플리케이션에 완전한 WebRTC 엔진을 추가해 주는 Delphi 및 C++ Builder용 신규 패키지입니다. 실제 SDP offer/answer 시그널링, STUN과 TURN을 사용하는 ICE 연결, DTLS-SRTP 암호화 전송, SCTP 데이터 채널, 그리고 Opus, G.711, VP8, H.264를 사용하는 오디오 및 비디오 트랙을 제공합니다.
순수 Object Pascal로 동작합니다. 프로세스 안에 내장 Chromium도, TWebBrowser나 TEdgeBrowser 컨트롤도, 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이므로 상대 피어가 브라우저일 수 있습니다
CreateOffer, CreateAnswer, SetLocalDescription, SetRemoteDescription, AddIceCandidate는 RFC 8829의 JSEP 상태 머신을 따라 표준 SDP를 생성하고 소비합니다. 반대편 피어는 다른 Delphi 애플리케이션일 수도, 모바일 앱일 수도, 브라우저 탭일 수도 있습니다. 디스크립션과 후보는 원하는 어떤 시그널링 채널로든, WebSocket이든 HTTP 엔드포인트든 메시지 큐든 전달하면 됩니다. 브라우저 애플리케이션이 자체 시그널링 서버를 통해 전달하는 방식과 똑같습니다.
다음은 offer를 보내는 쪽입니다. 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;
answer를 보내는 쪽은 도착한 내용을 소비하고 응답합니다. 그 밖에 달라지는 것은 없습니다.
// 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의 perfect-negotiation 충돌 규칙이 기본으로 내장되어 있습니다. 이미 설정된 세션에 트랙을 추가하면 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는 전송이 수집, 연결 중, 연결됨 단계를 지나는 과정을 보고하고, SelectedLocalCandidate와 SelectedRemoteCandidate는 어느 쌍이 선택되었는지 알려 줍니다. 통화가 직접 연결되었는지 릴레이를 거쳤는지 확인하는 가장 빠른 방법입니다.
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에 대한 answer는 수집 타임아웃을 기다리지 않고 즉시 나갑니다.
보안: DTLS-SRTP, 선택이 아닙니다
미디어 암호화는 필수이며, 브라우저가 강제하는 것과 같은 모델입니다. 선정된 ICE 쌍 위에서 DTLS 핸드셰이크가 수행되고, 거기에서 SRTP 키가 파생되며, 연결상의 모든 RTP, RTCP, SCTP 패킷은 첫 패킷부터 암호화됩니다. 원격 측은 SDP에 담겨 오는 인증서 지문으로 인증되며, 그래서 WebRTC는 이 목적으로 인증 기관이 필요하지 않습니다.
SCTP 기반 데이터 채널
CreateDataChannel은 RFC 8832의 DCEP open 핸드셰이크를 사용해 SCTP-over-DTLS 위에 RTCDataChannel을 엽니다. 채널은 순서 보장 또는 비보장, 완전 신뢰성, 또는 재전송 횟수 제한이나 수명 제한을 둔 부분 신뢰성으로 설정할 수 있습니다. 늦게 도착한 패킷이 새 패킷보다 가치가 낮은 텔레메트리나 게임 상태 같은 경우에 바로 원하는 방식입니다.
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이며, 텔레포니와 상호 운용해야 할 때는 u-law 및 A-law 방식의 G.711을 사용합니다. 비디오는 libvpx 바인딩을 통한 VP8, 플랫폼이 제공하는 하드웨어 인코더를 통한 H.264, 즉 Windows에서는 Media Foundation, macOS와 iOS에서는 VideoToolbox, Android에서는 MediaCodec을 사용하며, 단순한 인트라 전용 경로로 충분한 경우를 위해 Windows에서는 Motion JPEG도 제공합니다.
마이크 캡처와 스피커 재생은 Windows, Linux, macOS, iOS, Android에서 제공됩니다. 카메라 캡처, 데스크톱 캡처, 비디오 렌더링은 Windows에서 제공되며, TsgcScreenCapture_Win이 전체 데스크톱, 특정 모니터, 단일 창 또는 영역을 커서 포함 여부까지 선택해 캡처합니다. 다른 플랫폼에서도 코덱과 전송은 동일하게 동작하며, SendVideoFrame으로 프레임을 넣고 OnVideoFrame으로 도착한 것을 해당 플랫폼이 제공하는 수단으로 그리면 됩니다.
실제 네트워크에서 버티기
조용한 LAN에서만 동작하는 피어 연결은 그다지 쓸모가 없습니다. 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 서버도 필요합니다. 이 셋 모두가 sgcWebSockets Enterprise에 Delphi 컴포넌트로 포함되어 있으므로, 인프라를 임대하는 대신 전체 스택을 직접 운영할 수 있습니다. 시그널링 릴레이로는 TsgcWSPServer_WebRTC, 연결용으로는 TsgcSTUNServer와 TsgcTURNServer가 있습니다. 물론 클라이언트를 공개 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.AudioCall과 Demos\30.WebRTC_Protocol\02.VideoCall은 미디어 데모로, 각각 마이크에서 스피커로, 카메라에서 화면으로 전달합니다.
구매 안내
sgcWebRTC는 sgcWebSockets Enterprise의 애드온입니다. Standard나 Professional 에디션에서는 사용할 수 없으며, All-Access 번들에 포함되어 있습니다. Single, Team, Site 라이선스를 제공하며, 모두 전체 소스 코드와 1년간의 업데이트가 포함됩니다.
Delphi 7부터 Delphi 13 Florence까지, 그리고 그에 대응하는 C++ Builder 버전을 Windows, Linux, macOS, iOS, Android에서 지원합니다. 별도 다운로드는 없으며, 사용 중인 IDE 버전의 체험판 설치 프로그램에 이미 포함되어 있습니다.
미디어 엔진은 Delphi와 C++ Builder 전용입니다. sgcWebSockets .NET 포트에는 고전적인 브라우저 대 브라우저 시나리오를 위한 WebSocket 시그널링 릴레이인 TsgcWSProtocol_WebRTC_Server가 포함되어 있지만, TsgcRTCPeerConnection에 해당하는 .NET 구현은 없습니다.
제품 페이지 · 기능 상세 · 체험판 다운로드 · 가격
궁금한 점이나 의견이 있으신가요? 문의해 주세요. 코드를 직접 작성한 사람들이 답변해 드립니다.
