ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
src/client/webrtc.c
Go to the documentation of this file.
1
23#include "webrtc.h"
24#include <ascii-chat/network/acip/acds.h>
25#include <ascii-chat/network/acip/protocol.h>
26#include <ascii-chat/network/acip/send.h>
27#include <ascii-chat/util/endian.h>
28#include <ascii-chat/common.h>
29#include <ascii-chat/platform/abstraction.h>
30#include <ascii-chat/platform/init.h>
31
32#include <string.h>
33
34// =============================================================================
35// Global State
36// =============================================================================
37
44static acip_transport_t *g_acds_transport = NULL;
45
53static acip_transport_t *g_webrtc_transport = NULL;
54
61static struct {
62 uint8_t session_id[16];
63 uint8_t participant_id[16];
64 bool is_set;
65} g_session_context = {0};
66
70static mutex_t g_signaling_mutex;
71static bool g_signaling_mutex_initialized = false;
72static static_mutex_t g_signaling_init_mutex = STATIC_MUTEX_INIT;
73
74// =============================================================================
75// Internal Helpers
76// =============================================================================
77
84static void ensure_mutex_initialized(void) {
85 static_mutex_lock(&g_signaling_init_mutex);
86
87 // Check again under lock to prevent race condition
88 if (!g_signaling_mutex_initialized) {
89 mutex_init(&g_signaling_mutex);
90 g_signaling_mutex_initialized = true;
91 }
92
93 static_mutex_unlock(&g_signaling_init_mutex);
94}
95
96// =============================================================================
97// Signaling Callback Implementations
98// =============================================================================
99
109static asciichat_error_t client_send_sdp(const uint8_t session_id[16], const uint8_t recipient_id[16],
110 const char *sdp_type, const char *sdp, void *user_data) {
111 (void)user_data;
112
113 if (!session_id || !recipient_id || !sdp_type || !sdp) {
114 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SDP parameters");
115 }
116
117 ensure_mutex_initialized();
118 mutex_lock(&g_signaling_mutex);
119
120 // Check if ACDS connection is active
121 if (!g_acds_transport) {
122 mutex_unlock(&g_signaling_mutex);
123 return SET_ERRNO(ERROR_INVALID_STATE, "ACDS transport not available");
124 }
125
126 // Check if session context is set
127 if (!g_session_context.is_set) {
128 mutex_unlock(&g_signaling_mutex);
129 return SET_ERRNO(ERROR_INVALID_STATE, "Session context not set");
130 }
131
132 // Calculate SDP length
133 size_t sdp_len = strlen(sdp);
134 if (sdp_len == 0 || sdp_len >= 8192) {
135 mutex_unlock(&g_signaling_mutex);
136 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SDP length: %zu", sdp_len);
137 }
138
139 // Allocate packet buffer (header + SDP string)
140 size_t total_len = sizeof(acip_webrtc_sdp_t) + sdp_len;
141 uint8_t *packet = SAFE_MALLOC(total_len, uint8_t *);
142 if (!packet) {
143 mutex_unlock(&g_signaling_mutex);
144 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate SDP packet");
145 }
146
147 // Fill header
148 acip_webrtc_sdp_t *header = (acip_webrtc_sdp_t *)packet;
149 memcpy(header->session_id, session_id, 16);
150 memcpy(header->sender_id, g_session_context.participant_id, 16);
151 memcpy(header->recipient_id, recipient_id, 16);
152 header->sdp_type = (strcmp(sdp_type, "offer") == 0) ? 0 : 1;
153 header->sdp_len = HOST_TO_NET_U16((uint16_t)sdp_len);
154
155 log_debug("SDP packet: session_id=%02x%02x%02x%02x..., sender=%02x%02x%02x%02x..., recipient=%02x%02x%02x%02x...",
156 session_id[0], session_id[1], session_id[2], session_id[3], g_session_context.participant_id[0],
157 g_session_context.participant_id[1], g_session_context.participant_id[2],
158 g_session_context.participant_id[3], recipient_id[0], recipient_id[1], recipient_id[2], recipient_id[3]);
159
160 // Copy SDP string after header
161 memcpy(packet + sizeof(acip_webrtc_sdp_t), sdp, sdp_len);
162
163 log_info("Sending WebRTC SDP %s to participant (%.8s...) via ACDS", sdp_type, (const char *)recipient_id);
164
165 // Send via ACDS transport using generic packet sender
166 asciichat_error_t result =
167 packet_send_via_transport(g_acds_transport, PACKET_TYPE_ACIP_WEBRTC_SDP, packet, total_len, 0);
168
169 mutex_unlock(&g_signaling_mutex);
170
171 SAFE_FREE(packet);
172
173 if (result != ASCIICHAT_OK) {
174 return SET_ERRNO(result, "Failed to send SDP via ACDS");
175 }
176
177 return ASCIICHAT_OK;
178}
179
189static asciichat_error_t client_send_ice(const uint8_t session_id[16], const uint8_t recipient_id[16],
190 const char *candidate, const char *mid, void *user_data) {
191 (void)user_data;
192
193 if (!session_id || !recipient_id || !candidate || !mid) {
194 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid ICE parameters");
195 }
196
197 ensure_mutex_initialized();
198 mutex_lock(&g_signaling_mutex);
199
200 // Check if ACDS connection is active
201 if (!g_acds_transport) {
202 mutex_unlock(&g_signaling_mutex);
203 return SET_ERRNO(ERROR_INVALID_STATE, "ACDS transport not available");
204 }
205
206 // Check if session context is set
207 if (!g_session_context.is_set) {
208 mutex_unlock(&g_signaling_mutex);
209 return SET_ERRNO(ERROR_INVALID_STATE, "Session context not set");
210 }
211
212 // Calculate payload length (candidate + null + mid + null)
213 size_t candidate_len = strlen(candidate);
214 size_t mid_len = strlen(mid);
215 size_t payload_len = candidate_len + 1 + mid_len + 1;
216
217 if (payload_len >= 8192) {
218 mutex_unlock(&g_signaling_mutex);
219 return SET_ERRNO(ERROR_INVALID_PARAM, "ICE payload too large: %zu", payload_len);
220 }
221
222 // Allocate packet buffer (header + payload)
223 size_t total_len = sizeof(acip_webrtc_ice_t) + payload_len;
224 uint8_t *packet = SAFE_MALLOC(total_len, uint8_t *);
225 if (!packet) {
226 mutex_unlock(&g_signaling_mutex);
227 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ICE packet");
228 }
229
230 // Fill header
231 acip_webrtc_ice_t *header = (acip_webrtc_ice_t *)packet;
232 memcpy(header->session_id, session_id, 16);
233 memcpy(header->sender_id, g_session_context.participant_id, 16);
234 memcpy(header->recipient_id, recipient_id, 16);
235 header->candidate_len = HOST_TO_NET_U16((uint16_t)candidate_len); // FIXED: Use candidate length, not total payload
236
237 // Copy candidate and mid after header
238 uint8_t *payload = packet + sizeof(acip_webrtc_ice_t);
239 memcpy(payload, candidate, candidate_len);
240 payload[candidate_len] = '\0';
241 memcpy(payload + candidate_len + 1, mid, mid_len);
242 payload[candidate_len + 1 + mid_len] = '\0';
243
244 log_debug("Sending WebRTC ICE candidate to participant (%.8s..., mid=%s) via ACDS", (const char *)recipient_id, mid);
245
246 // Send via ACDS transport using generic packet sender
247 asciichat_error_t result =
248 packet_send_via_transport(g_acds_transport, PACKET_TYPE_ACIP_WEBRTC_ICE, packet, total_len, 0);
249
250 mutex_unlock(&g_signaling_mutex);
251
252 SAFE_FREE(packet);
253
254 if (result != ASCIICHAT_OK) {
255 return SET_ERRNO(result, "Failed to send ICE via ACDS");
256 }
257
258 return ASCIICHAT_OK;
259}
260
261// =============================================================================
262// Public API
263// =============================================================================
264
265webrtc_signaling_callbacks_t webrtc_get_signaling_callbacks(void) {
266 ensure_mutex_initialized();
267
268 webrtc_signaling_callbacks_t callbacks = {
269 .send_sdp = client_send_sdp, .send_ice = client_send_ice, .user_data = NULL};
270
271 return callbacks;
272}
273
274void webrtc_set_acds_transport(acip_transport_t *transport) {
275 ensure_mutex_initialized();
276 mutex_lock(&g_signaling_mutex);
277
278 g_acds_transport = transport;
279
280 if (transport) {
281 log_debug("ACDS transport set for WebRTC signaling");
282 } else {
283 log_debug("ACDS transport cleared for WebRTC signaling");
284 }
285
286 mutex_unlock(&g_signaling_mutex);
287}
288
289void webrtc_set_session_context(const uint8_t session_id[16], const uint8_t participant_id[16]) {
290 if (!session_id || !participant_id) {
291 log_error("Invalid session context parameters");
292 return;
293 }
294
295 ensure_mutex_initialized();
296 mutex_lock(&g_signaling_mutex);
297
298 memcpy(g_session_context.session_id, session_id, 16);
299 memcpy(g_session_context.participant_id, participant_id, 16);
300 g_session_context.is_set = true;
301
302 log_info("Session context set for WebRTC signaling (session=%.8s..., participant=%.8s...)", (const char *)session_id,
303 (const char *)participant_id);
304
305 mutex_unlock(&g_signaling_mutex);
306}
307
319void webrtc_set_transport(acip_transport_t *transport) {
320 ensure_mutex_initialized();
321 mutex_lock(&g_signaling_mutex);
322
323 g_webrtc_transport = transport;
324
325 if (transport) {
326 log_debug("WebRTC data channel transport set");
327 } else {
328 log_debug("WebRTC data channel transport cleared");
329 }
330
331 mutex_unlock(&g_signaling_mutex);
332}
333
344acip_transport_t *webrtc_get_transport(void) {
345 ensure_mutex_initialized();
346 mutex_lock(&g_signaling_mutex);
347
348 acip_transport_t *transport = g_webrtc_transport;
349
350 mutex_unlock(&g_signaling_mutex);
351
352 return transport;
353}
354
366 ensure_mutex_initialized();
367 mutex_lock(&g_signaling_mutex);
368
369 if (g_webrtc_transport) {
370 log_debug("Cleaning up WebRTC data channel transport");
371 g_webrtc_transport = NULL;
372 }
373
374 mutex_unlock(&g_signaling_mutex);
375}
WebRTC P2P signaling for discovery mode failover.
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41
acip_transport_t * webrtc_get_transport(void)
Get the current WebRTC data channel transport.
void webrtc_set_session_context(const uint8_t session_id[16], const uint8_t participant_id[16])
Set session and participant IDs for signaling.
void webrtc_set_acds_transport(acip_transport_t *transport)
Set the ACDS transport for signaling callbacks.
bool is_set
void webrtc_set_transport(acip_transport_t *transport)
Set the WebRTC data channel transport.
webrtc_signaling_callbacks_t webrtc_get_signaling_callbacks(void)
Get signaling callbacks for WebRTC peer manager.
uint8_t session_id[16]
void webrtc_cleanup_transport(void)
Cleanup and release the WebRTC data channel transport.
uint8_t participant_id[16]
int mutex_init(mutex_t *mutex)
Definition threading.c:16