ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
src/client/webrtc.c
Go to the documentation of this file.
1
23#include "webrtc.h"
28#include <ascii-chat/common.h>
32
33#include <string.h>
34
35// =============================================================================
36// Global State
37// =============================================================================
38
45static acip_transport_t *g_acds_transport = NULL;
46
54static acip_transport_t *g_webrtc_transport = NULL;
55
62static struct {
65 bool is_set;
66} g_session_context = {0};
67
71static mutex_t g_signaling_mutex;
72static lifecycle_t g_signaling_lc = LIFECYCLE_INIT_MUTEX(&g_signaling_mutex);
73
74// =============================================================================
75// Internal Helpers
76// =============================================================================
77
85static void ensure_mutex_initialized(void) {
86 if (!lifecycle_init(&g_signaling_lc, "signaling"))
87 return;
88}
89
90// =============================================================================
91// Signaling Callback Implementations
92// =============================================================================
93
103static asciichat_error_t client_send_sdp(const uint8_t session_id[16], const uint8_t recipient_id[16],
104 const char *sdp_type, const char *sdp, void *user_data) {
105 (void)user_data;
106
107 if (!session_id || !recipient_id || !sdp_type || !sdp) {
108 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SDP parameters");
109 }
110
111 ensure_mutex_initialized();
112 mutex_lock(&g_signaling_mutex);
113
114 // Check if ACDS connection is active
115 if (!g_acds_transport) {
116 mutex_unlock(&g_signaling_mutex);
117 return SET_ERRNO(ERROR_INVALID_STATE, "ACDS transport not available");
118 }
119
120 // Check if session context is set
121 if (!g_session_context.is_set) {
122 mutex_unlock(&g_signaling_mutex);
123 return SET_ERRNO(ERROR_INVALID_STATE, "Session context not set");
124 }
125
126 // Calculate SDP length
127 size_t sdp_len = strlen(sdp);
128 if (sdp_len == 0 || sdp_len >= 8192) {
129 mutex_unlock(&g_signaling_mutex);
130 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SDP length: %zu", sdp_len);
131 }
132
133 // Allocate packet buffer (header + SDP string)
134 size_t total_len = sizeof(acip_webrtc_sdp_t) + sdp_len;
135 uint8_t *packet = SAFE_MALLOC(total_len, uint8_t *);
136 if (!packet) {
137 mutex_unlock(&g_signaling_mutex);
138 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate SDP packet");
139 }
140
141 // Fill header
142 acip_webrtc_sdp_t *header = (acip_webrtc_sdp_t *)packet;
143 memcpy(header->session_id, session_id, 16);
144 memcpy(header->sender_id, g_session_context.participant_id, 16);
145 memcpy(header->recipient_id, recipient_id, 16);
146 header->sdp_type = (strcmp(sdp_type, "offer") == 0) ? 0 : 1;
147 header->sdp_len = HOST_TO_NET_U16((uint16_t)sdp_len);
148
149 log_debug("SDP packet: session_id=%02x%02x%02x%02x..., sender=%02x%02x%02x%02x..., recipient=%02x%02x%02x%02x...",
150 session_id[0], session_id[1], session_id[2], session_id[3], g_session_context.participant_id[0],
151 g_session_context.participant_id[1], g_session_context.participant_id[2],
152 g_session_context.participant_id[3], recipient_id[0], recipient_id[1], recipient_id[2], recipient_id[3]);
153
154 // Copy SDP string after header
155 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary packet data
156 memcpy(packet + sizeof(acip_webrtc_sdp_t), sdp, sdp_len);
157
158 log_info("Sending WebRTC SDP %s to participant (%.8s...) via ACDS", sdp_type, (const char *)recipient_id);
159
160 // Send via ACDS transport using generic packet sender
161 asciichat_error_t result =
162 packet_send_via_transport(g_acds_transport, PACKET_TYPE_ACIP_WEBRTC_SDP, packet, total_len, 0);
163
164 mutex_unlock(&g_signaling_mutex);
165
166 SAFE_FREE(packet);
167
168 if (result != ASCIICHAT_OK) {
169 return SET_ERRNO(result, "Failed to send SDP via ACDS");
170 }
171
172 return ASCIICHAT_OK;
173}
174
184static asciichat_error_t client_send_ice(const uint8_t session_id[16], const uint8_t recipient_id[16],
185 const char *candidate, const char *mid, void *user_data) {
186 (void)user_data;
187
188 if (!session_id || !recipient_id || !candidate || !mid) {
189 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid ICE parameters");
190 }
191
192 ensure_mutex_initialized();
193 mutex_lock(&g_signaling_mutex);
194
195 // Check if ACDS connection is active
196 if (!g_acds_transport) {
197 mutex_unlock(&g_signaling_mutex);
198 return SET_ERRNO(ERROR_INVALID_STATE, "ACDS transport not available");
199 }
200
201 // Check if session context is set
202 if (!g_session_context.is_set) {
203 mutex_unlock(&g_signaling_mutex);
204 return SET_ERRNO(ERROR_INVALID_STATE, "Session context not set");
205 }
206
207 // Calculate payload length (candidate + null + mid + null)
208 size_t candidate_len = strlen(candidate);
209 size_t mid_len = strlen(mid);
210 size_t payload_len = candidate_len + 1 + mid_len + 1;
211
212 if (payload_len >= 8192) {
213 mutex_unlock(&g_signaling_mutex);
214 return SET_ERRNO(ERROR_INVALID_PARAM, "ICE payload too large: %zu", payload_len);
215 }
216
217 // Allocate packet buffer (header + payload)
218 size_t total_len = sizeof(acip_webrtc_ice_t) + payload_len;
219 uint8_t *packet = SAFE_MALLOC(total_len, uint8_t *);
220 if (!packet) {
221 mutex_unlock(&g_signaling_mutex);
222 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ICE packet");
223 }
224
225 // Fill header
226 acip_webrtc_ice_t *header = (acip_webrtc_ice_t *)packet;
227 memcpy(header->session_id, session_id, 16);
228 memcpy(header->sender_id, g_session_context.participant_id, 16);
229 memcpy(header->recipient_id, recipient_id, 16);
230 header->candidate_len = HOST_TO_NET_U16((uint16_t)candidate_len); // FIXED: Use candidate length, not total payload
231
232 // Copy candidate and mid after header
233 uint8_t *payload = packet + sizeof(acip_webrtc_ice_t);
234 memcpy(payload, candidate, candidate_len);
235 payload[candidate_len] = '\0';
236 memcpy(payload + candidate_len + 1, mid, mid_len);
237 payload[candidate_len + 1 + mid_len] = '\0';
238
239 log_debug("Sending WebRTC ICE candidate to participant (%.8s..., mid=%s) via ACDS", (const char *)recipient_id, mid);
240
241 // Send via ACDS transport using generic packet sender
242 asciichat_error_t result =
243 packet_send_via_transport(g_acds_transport, PACKET_TYPE_ACIP_WEBRTC_ICE, packet, total_len, 0);
244
245 mutex_unlock(&g_signaling_mutex);
246
247 SAFE_FREE(packet);
248
249 if (result != ASCIICHAT_OK) {
250 return SET_ERRNO(result, "Failed to send ICE via ACDS");
251 }
252
253 return ASCIICHAT_OK;
254}
255
256// =============================================================================
257// Public API
258// =============================================================================
259
261 ensure_mutex_initialized();
262
263 webrtc_signaling_callbacks_t callbacks = {
264 .send_sdp = client_send_sdp, .send_ice = client_send_ice, .user_data = NULL};
265
266 return callbacks;
267}
268
270 ensure_mutex_initialized();
271 mutex_lock(&g_signaling_mutex);
272
273 g_acds_transport = transport;
274
275 if (transport) {
276 log_debug("ACDS transport set for WebRTC signaling");
277 } else {
278 log_debug("ACDS transport cleared for WebRTC signaling");
279 }
280
281 mutex_unlock(&g_signaling_mutex);
282}
283
285 if (!session_id || !participant_id) {
286 log_error("Invalid session context parameters");
287 return;
288 }
289
290 ensure_mutex_initialized();
291 mutex_lock(&g_signaling_mutex);
292
293 memcpy(g_session_context.session_id, session_id, 16);
294 memcpy(g_session_context.participant_id, participant_id, 16);
295 g_session_context.is_set = true;
296
297 log_info("Session context set for WebRTC signaling (session=%.8s..., participant=%.8s...)", (const char *)session_id,
298 (const char *)participant_id);
299
300 mutex_unlock(&g_signaling_mutex);
301}
302
315 ensure_mutex_initialized();
316 mutex_lock(&g_signaling_mutex);
317
318 g_webrtc_transport = transport;
319
320 if (transport) {
321 log_debug("WebRTC data channel transport set");
322 } else {
323 log_debug("WebRTC data channel transport cleared");
324 }
325
326 mutex_unlock(&g_signaling_mutex);
327}
328
340 ensure_mutex_initialized();
341 mutex_lock(&g_signaling_mutex);
342
343 acip_transport_t *transport = g_webrtc_transport;
344
345 mutex_unlock(&g_signaling_mutex);
346
347 return transport;
348}
349
361 ensure_mutex_initialized();
362 mutex_lock(&g_signaling_mutex);
363
364 if (g_webrtc_transport) {
365 log_debug("Cleaning up WebRTC data channel transport");
366 g_webrtc_transport = NULL;
367 }
368
369 mutex_unlock(&g_signaling_mutex);
370}
Platform abstraction layer umbrella header providing unified cross-platform API.
ascii-chat Discovery Service (ACDS) Protocol Message Formats
⚙️ Common definitions, error codes, macros, and types shared throughout the application
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:96
acip_webrtc_ice_t
Definition acds.h:650
acip_webrtc_sdp_t
Definition acds.h:604
unsigned short uint16_t
Definition common.h:57
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional)
Definition packet.h:428
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:426
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
ascii-chat Binary Network Protocol (complete system including ACIP discovery service)
Platform initialization and static synchronization helpers.
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26
Lock-free module lifecycle state machine using pure stdatomic.
#define LIFECYCLE_INIT_MUTEX(m)
Static initializer for lifecycle with mutex.
Definition lifecycle.h:74
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 shared/bidirectional packet sending functions.
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]
WebRTC P2P signaling for discovery mode failover.
Transport instance structure.
Definition transport.h:214
Mutex type (POSIX: pthread_mutex_t with debug tracking)
Signaling callbacks for sending SDP/ICE.
webrtc_send_sdp_callback_t send_sdp
Send SDP via ACDS.