ascii-chat 0.6.0
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 "network/acip/acds.h"
26#include "network/acip/send.h"
27#include "util/endian.h"
28#include "common.h"
30
31#include <string.h>
32
33// =============================================================================
34// Global State
35// =============================================================================
36
43static acip_transport_t *g_acds_transport = NULL;
44
52static acip_transport_t *g_webrtc_transport = NULL;
53
60static struct {
63 bool is_set;
64} g_session_context = {0};
65
69static mutex_t g_signaling_mutex;
70static bool g_signaling_mutex_initialized = false;
71
72// =============================================================================
73// Internal Helpers
74// =============================================================================
75
79static void ensure_mutex_initialized(void) {
80 if (!g_signaling_mutex_initialized) {
81 mutex_init(&g_signaling_mutex);
82 g_signaling_mutex_initialized = true;
83 }
84}
85
86// =============================================================================
87// Signaling Callback Implementations
88// =============================================================================
89
99static asciichat_error_t client_send_sdp(const uint8_t session_id[16], const uint8_t recipient_id[16],
100 const char *sdp_type, const char *sdp, void *user_data) {
101 (void)user_data;
102
103 if (!session_id || !recipient_id || !sdp_type || !sdp) {
104 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SDP parameters");
105 }
106
107 ensure_mutex_initialized();
108 mutex_lock(&g_signaling_mutex);
109
110 // Check if ACDS connection is active
111 if (!g_acds_transport) {
112 mutex_unlock(&g_signaling_mutex);
113 return SET_ERRNO(ERROR_INVALID_STATE, "ACDS transport not available");
114 }
115
116 // Check if session context is set
117 if (!g_session_context.is_set) {
118 mutex_unlock(&g_signaling_mutex);
119 return SET_ERRNO(ERROR_INVALID_STATE, "Session context not set");
120 }
121
122 // Calculate SDP length
123 size_t sdp_len = strlen(sdp);
124 if (sdp_len == 0 || sdp_len >= 8192) {
125 mutex_unlock(&g_signaling_mutex);
126 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SDP length: %zu", sdp_len);
127 }
128
129 // Allocate packet buffer (header + SDP string)
130 size_t total_len = sizeof(acip_webrtc_sdp_t) + sdp_len;
131 uint8_t *packet = SAFE_MALLOC(total_len, uint8_t *);
132 if (!packet) {
133 mutex_unlock(&g_signaling_mutex);
134 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate SDP packet");
135 }
136
137 // Fill header
138 acip_webrtc_sdp_t *header = (acip_webrtc_sdp_t *)packet;
139 memcpy(header->session_id, session_id, 16);
140 memcpy(header->sender_id, g_session_context.participant_id, 16);
141 memcpy(header->recipient_id, recipient_id, 16);
142 header->sdp_type = (strcmp(sdp_type, "offer") == 0) ? 0 : 1;
143 header->sdp_len = HOST_TO_NET_U16((uint16_t)sdp_len);
144
145 // Copy SDP string after header
146 memcpy(packet + sizeof(acip_webrtc_sdp_t), sdp, sdp_len);
147
148 log_info("Sending WebRTC SDP %s to participant (%.8s...) via ACDS", sdp_type, (const char *)recipient_id);
149
150 // Send via ACDS transport using generic packet sender
151 asciichat_error_t result =
152 packet_send_via_transport(g_acds_transport, PACKET_TYPE_ACIP_WEBRTC_SDP, packet, total_len);
153
154 mutex_unlock(&g_signaling_mutex);
155
156 SAFE_FREE(packet);
157
158 if (result != ASCIICHAT_OK) {
159 return SET_ERRNO(result, "Failed to send SDP via ACDS");
160 }
161
162 return ASCIICHAT_OK;
163}
164
174static asciichat_error_t client_send_ice(const uint8_t session_id[16], const uint8_t recipient_id[16],
175 const char *candidate, const char *mid, void *user_data) {
176 (void)user_data;
177
178 if (!session_id || !recipient_id || !candidate || !mid) {
179 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid ICE parameters");
180 }
181
182 ensure_mutex_initialized();
183 mutex_lock(&g_signaling_mutex);
184
185 // Check if ACDS connection is active
186 if (!g_acds_transport) {
187 mutex_unlock(&g_signaling_mutex);
188 return SET_ERRNO(ERROR_INVALID_STATE, "ACDS transport not available");
189 }
190
191 // Check if session context is set
192 if (!g_session_context.is_set) {
193 mutex_unlock(&g_signaling_mutex);
194 return SET_ERRNO(ERROR_INVALID_STATE, "Session context not set");
195 }
196
197 // Calculate payload length (candidate + null + mid + null)
198 size_t candidate_len = strlen(candidate);
199 size_t mid_len = strlen(mid);
200 size_t payload_len = candidate_len + 1 + mid_len + 1;
201
202 if (payload_len >= 8192) {
203 mutex_unlock(&g_signaling_mutex);
204 return SET_ERRNO(ERROR_INVALID_PARAM, "ICE payload too large: %zu", payload_len);
205 }
206
207 // Allocate packet buffer (header + payload)
208 size_t total_len = sizeof(acip_webrtc_ice_t) + payload_len;
209 uint8_t *packet = SAFE_MALLOC(total_len, uint8_t *);
210 if (!packet) {
211 mutex_unlock(&g_signaling_mutex);
212 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ICE packet");
213 }
214
215 // Fill header
216 acip_webrtc_ice_t *header = (acip_webrtc_ice_t *)packet;
217 memcpy(header->session_id, session_id, 16);
218 memcpy(header->sender_id, g_session_context.participant_id, 16);
219 memcpy(header->recipient_id, recipient_id, 16);
220 header->candidate_len = HOST_TO_NET_U16((uint16_t)payload_len);
221
222 // Copy candidate and mid after header
223 uint8_t *payload = packet + sizeof(acip_webrtc_ice_t);
224 memcpy(payload, candidate, candidate_len);
225 payload[candidate_len] = '\0';
226 memcpy(payload + candidate_len + 1, mid, mid_len);
227 payload[candidate_len + 1 + mid_len] = '\0';
228
229 log_debug("Sending WebRTC ICE candidate to participant (%.8s..., mid=%s) via ACDS", (const char *)recipient_id, mid);
230
231 // Send via ACDS transport using generic packet sender
232 asciichat_error_t result =
233 packet_send_via_transport(g_acds_transport, PACKET_TYPE_ACIP_WEBRTC_ICE, packet, total_len);
234
235 mutex_unlock(&g_signaling_mutex);
236
237 SAFE_FREE(packet);
238
239 if (result != ASCIICHAT_OK) {
240 return SET_ERRNO(result, "Failed to send ICE via ACDS");
241 }
242
243 return ASCIICHAT_OK;
244}
245
246// =============================================================================
247// Public API
248// =============================================================================
249
251 ensure_mutex_initialized();
252
253 webrtc_signaling_callbacks_t callbacks = {
254 .send_sdp = client_send_sdp, .send_ice = client_send_ice, .user_data = NULL};
255
256 return callbacks;
257}
258
260 ensure_mutex_initialized();
261 mutex_lock(&g_signaling_mutex);
262
263 g_acds_transport = transport;
264
265 if (transport) {
266 log_debug("ACDS transport set for WebRTC signaling");
267 } else {
268 log_debug("ACDS transport cleared for WebRTC signaling");
269 }
270
271 mutex_unlock(&g_signaling_mutex);
272}
273
275 if (!session_id || !participant_id) {
276 log_error("Invalid session context parameters");
277 return;
278 }
279
280 ensure_mutex_initialized();
281 mutex_lock(&g_signaling_mutex);
282
283 memcpy(g_session_context.session_id, session_id, 16);
284 memcpy(g_session_context.participant_id, participant_id, 16);
285 g_session_context.is_set = true;
286
287 log_info("Session context set for WebRTC signaling (session=%.8s..., participant=%.8s...)", (const char *)session_id,
288 (const char *)participant_id);
289
290 mutex_unlock(&g_signaling_mutex);
291}
292
305 ensure_mutex_initialized();
306 mutex_lock(&g_signaling_mutex);
307
308 g_webrtc_transport = transport;
309
310 if (transport) {
311 log_debug("WebRTC data channel transport set");
312 } else {
313 log_debug("WebRTC data channel transport cleared");
314 }
315
316 mutex_unlock(&g_signaling_mutex);
317}
318
330 ensure_mutex_initialized();
331 mutex_lock(&g_signaling_mutex);
332
333 acip_transport_t *transport = g_webrtc_transport;
334
335 mutex_unlock(&g_signaling_mutex);
336
337 return transport;
338}
339
351 ensure_mutex_initialized();
352 mutex_lock(&g_signaling_mutex);
353
354 if (g_webrtc_transport) {
355 log_debug("Cleaning up WebRTC data channel transport");
356 g_webrtc_transport = NULL;
357 }
358
359 mutex_unlock(&g_signaling_mutex);
360}
🔌 Cross-platform abstraction layer umbrella header for ascii-chat
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:101
acip_webrtc_ice_t
acip_webrtc_sdp_t
unsigned short uint16_t
Definition common.h:57
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional)
Definition packet.h:390
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:388
int mutex_init(mutex_t *mutex)
Initialize a mutex.
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
Definition mutex.h:140
pthread_mutex_t mutex_t
Mutex type (POSIX: pthread_mutex_t)
Definition mutex.h:38
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
Definition mutex.h:175
ASCII-Chat IP Protocol (ACIP) packet type definitions.
ASCII-Chat Discovery Service (ACDS) Protocol Message Formats.
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:40
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]
Client-side WebRTC signaling callback implementations.
Transport instance structure.
Definition transport.h:169
Signaling callbacks for sending SDP/ICE.
webrtc_send_sdp_callback_t send_sdp
Send SDP via ACDS.