From 3cf63d62b56625cd07423e2ecdc1a68e7a27adb3 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Tue, 2 Jun 2026 23:12:36 -0700 Subject: [PATCH] Align Caller/Callee roles with TCP connection roles The Caller now acts as TCP client (connects, sends offer). The Callee now acts as TCP server (listens, answers offer). This aligns WebRTC semantics with transport semantics, eliminating confusion in BasicVideoChat where roles were previously inverted. Changes: - Flip UI IP enable/disable logic (Caller: enabled, Callee: disabled) - Swap TCP calls: Caller uses ConnectAsync, Callee uses ListenAsync - Update status messages to reflect actual TCP operations - Clarify TcpSignalingChannel comments with correct role mapping - Update CONTEXT.md glossary with Host/role distinction notes Documented in ADR-0002: Caller and Callee roles align with TCP connection roles. Fixes #45 --- CONTEXT.md | 9 ++++++--- docs/adr/0002-caller-callee-tcp-alignment.md | 5 +++++ examples/BasicVideoChat/MainWindow.xaml.cs | 18 +++++++++--------- .../Signaling/TcpSignalingChannel.cs | 4 ++-- 4 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 docs/adr/0002-caller-callee-tcp-alignment.md diff --git a/CONTEXT.md b/CONTEXT.md index 3b9d002..93419bd 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -40,7 +40,10 @@ assembly is created (see issue #36). - **Host** exposes **MediaDevices** as the API entry point for capture access. - A **Caller** sends an SDP offer and a **Callee** returns an SDP answer. -## Flagged ambiguities +## Notes -- "Host" previously meant the signaling initiator peer; resolved to the API root - object. Signaling roles are now **Caller** and **Callee**. +- **Host** is a static API entry point (like Navigator in browsers), not a signaling + role. Do not confuse with **Caller** and **Callee**, which are peer roles. +- **Caller** and **Callee** roles are bound to TCP roles: Caller is the TCP client + (connects first, sends offer); Callee is the TCP server (listens first, answers + offer). This alignment is documented in ADR-0002. diff --git a/docs/adr/0002-caller-callee-tcp-alignment.md b/docs/adr/0002-caller-callee-tcp-alignment.md new file mode 100644 index 0000000..f30a1ce --- /dev/null +++ b/docs/adr/0002-caller-callee-tcp-alignment.md @@ -0,0 +1,5 @@ +# Caller and Callee roles align with TCP connection roles + +WebRTC roles (Caller/Callee, defined by offer/answer semantics) will be bound to transport roles (TCP client/server) to avoid confusion in the BasicVideoChat example. The Caller initiates the TCP connection (acts as client) and sends the offer; the Callee listens for the TCP connection (acts as server) and responds with an answer. + +We rejected decoupling transport and WebRTC roles because it adds cognitive load without benefit — a single peer cannot simultaneously listen (server) while also creating the offer (Caller) in a direct peer-to-peer TCP model. Binding them simplifies the example and documentation, making it clear to new readers that roles are stable and consistent throughout the session. diff --git a/examples/BasicVideoChat/MainWindow.xaml.cs b/examples/BasicVideoChat/MainWindow.xaml.cs index 82bcf43..35fa6a6 100644 --- a/examples/BasicVideoChat/MainWindow.xaml.cs +++ b/examples/BasicVideoChat/MainWindow.xaml.cs @@ -35,10 +35,10 @@ public MainWindow() private bool IsCaller => CallerRadio.IsChecked == true; private void CallerRadio_Checked(object sender, RoutedEventArgs e) => - IpBox.IsEnabled = false; + IpBox.IsEnabled = true; private void CalleeRadio_Checked(object sender, RoutedEventArgs e) => - IpBox.IsEnabled = true; + IpBox.IsEnabled = false; private async void ConnectBtn_Click(object sender, RoutedEventArgs e) { @@ -102,10 +102,11 @@ private async Task StartCallAsync() if (IsCaller) { + var callerIp = IpBox.Text.Trim(); var port = int.TryParse(PortBox.Text, out var p) ? p : DefaultPort; - SetStatus($"Listening on port {port}..."); - await _signaling.ListenAsync(port); - SetStatus("Callee connected. Creating offer..."); + SetStatus($"Connecting to {callerIp}:{port}..."); + await _signaling.ConnectAsync(callerIp, port); + SetStatus("Connected. Creating offer..."); // Drive the offer explicitly here rather than relying on OnNegotiationNeeded, // since we control when the signaling channel is ready. @@ -116,11 +117,10 @@ private async Task StartCallAsync() } else { - var callerIp = IpBox.Text.Trim(); var port = int.TryParse(PortBox.Text, out var p) ? p : DefaultPort; - SetStatus($"Connecting to {callerIp}:{port}..."); - await _signaling.ConnectAsync(callerIp, port); - SetStatus("Connected. Waiting for offer..."); + SetStatus($"Listening on port {port}..."); + await _signaling.ListenAsync(port); + SetStatus("Caller connected. Waiting for offer..."); } } diff --git a/examples/BasicVideoChat/Signaling/TcpSignalingChannel.cs b/examples/BasicVideoChat/Signaling/TcpSignalingChannel.cs index 7f3ecb8..a38d448 100644 --- a/examples/BasicVideoChat/Signaling/TcpSignalingChannel.cs +++ b/examples/BasicVideoChat/Signaling/TcpSignalingChannel.cs @@ -13,8 +13,8 @@ namespace BasicVideoChat.Signaling; /// Minimal TCP signaling channel for peer-to-peer connection setup. /// /// -/// One peer calls (Host) and the other calls -/// (Guest). Messages are newline-delimited JSON. +/// The Caller calls (TCP client) and the Callee calls +/// (TCP server). Messages are newline-delimited JSON. /// /// The read loop awaits before processing the next message, /// ensuring that SetRemoteDescription always completes before any