ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
src/discovery/webrtc.c
Go to the documentation of this file.
1
12#include "webrtc.h"
13
14#include <ascii-chat/common.h>
15#include <ascii-chat/log/log.h>
25
26#include <string.h>
27
28// =============================================================================
29// Global State
30// =============================================================================
31
39static acip_transport_t *g_tcp_transport = NULL;
40
47static struct {
50 bool is_set;
51} g_session_context = {0};
52
56static mutex_t g_webrtc_mutex;
57static lifecycle_t g_webrtc_lc = LIFECYCLE_INIT_MUTEX(&g_webrtc_mutex);
58
59// =============================================================================
60// Internal Helpers
61// =============================================================================
62
70static void ensure_mutex_initialized(void) {
71 if (!lifecycle_init(&g_webrtc_lc, "webrtc"))
72 return;
73}
74
75// =============================================================================
76// Signaling Callback Implementations (Direct TCP)
77// =============================================================================
78
89static asciichat_error_t discovery_send_sdp(const uint8_t session_id[16], const uint8_t recipient_id[16],
90 const char *sdp_type, const char *sdp, void *user_data) {
91 (void)recipient_id; // Unused - direct connection, no need to route
92 (void)user_data; // Unused
93
94 ensure_mutex_initialized();
95 mutex_lock(&g_webrtc_mutex);
96
97 // Verify state
98 if (!g_tcp_transport) {
99 mutex_unlock(&g_webrtc_mutex);
100 SET_ERRNO(ERROR_INVALID_STATE, "TCP transport not set for direct signaling");
101 return ERROR_INVALID_STATE;
102 }
103
104 if (!g_session_context.is_set) {
105 mutex_unlock(&g_webrtc_mutex);
106 SET_ERRNO(ERROR_INVALID_STATE, "Session context not set for direct signaling");
107 return ERROR_INVALID_STATE;
108 }
109
110 // Verify inputs
111 if (!sdp) {
112 mutex_unlock(&g_webrtc_mutex);
113 SET_ERRNO(ERROR_INVALID_PARAM, "SDP is NULL");
114 return ERROR_INVALID_PARAM;
115 }
116
117 size_t sdp_len = strlen(sdp);
118 if (sdp_len > 4096) {
119 mutex_unlock(&g_webrtc_mutex);
120 SET_ERRNO(ERROR_INVALID_PARAM, "SDP too large (>4096 bytes)");
121 return ERROR_INVALID_PARAM;
122 }
123
124 // Construct packet
125 // Header: acip_webrtc_sdp_t (defined in network/acip/acds.h)
126 // Followed by: SDP string
127 uint8_t sdp_msg_buf[sizeof(acip_webrtc_sdp_t) + 4096];
128 acip_webrtc_sdp_t *sdp_msg = (acip_webrtc_sdp_t *)sdp_msg_buf;
129
130 memcpy(sdp_msg->session_id, session_id, 16);
131 memcpy(sdp_msg->sender_id, g_session_context.participant_id, 16);
132 memset(sdp_msg->recipient_id, 0, 16); // Broadcast to all
133 sdp_msg->sdp_type = (strcmp(sdp_type, "answer") == 0) ? 1 : 0; // 0=offer, 1=answer
134 sdp_msg->sdp_len = (uint16_t)sdp_len;
135 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary packet data
136 memcpy(sdp_msg_buf + sizeof(acip_webrtc_sdp_t), sdp, sdp_len);
137
138 // Send via direct TCP transport (not ACDS)
139 size_t msg_size = sizeof(acip_webrtc_sdp_t) + sdp_len;
140 asciichat_error_t result =
141 packet_send_via_transport(g_tcp_transport, PACKET_TYPE_ACIP_WEBRTC_SDP, sdp_msg_buf, msg_size, 0);
142
143 mutex_unlock(&g_webrtc_mutex);
144
145 if (result != ASCIICHAT_OK) {
146 log_error("Failed to send SDP via direct TCP: %d", result);
147 return result;
148 }
149
150 log_debug("Sent SDP %s directly to peer (TCP transport)", sdp_type);
151 return ASCIICHAT_OK;
152}
153
159static asciichat_error_t discovery_send_ice(const uint8_t session_id[16], const uint8_t recipient_id[16],
160 const char *candidate, const char *mid, void *user_data) {
161 (void)recipient_id; // Unused - direct connection, no need to route
162 (void)user_data; // Unused
163
164 ensure_mutex_initialized();
165 mutex_lock(&g_webrtc_mutex);
166
167 // Verify state
168 if (!g_tcp_transport) {
169 mutex_unlock(&g_webrtc_mutex);
170 SET_ERRNO(ERROR_INVALID_STATE, "TCP transport not set for direct signaling");
171 return ERROR_INVALID_STATE;
172 }
173
174 if (!g_session_context.is_set) {
175 mutex_unlock(&g_webrtc_mutex);
176 SET_ERRNO(ERROR_INVALID_STATE, "Session context not set for direct signaling");
177 return ERROR_INVALID_STATE;
178 }
179
180 // Verify inputs
181 if (!candidate || !mid) {
182 mutex_unlock(&g_webrtc_mutex);
183 SET_ERRNO(ERROR_INVALID_PARAM, "Candidate or mid is NULL");
184 return ERROR_INVALID_PARAM;
185 }
186
187 // Calculate payload length (candidate + null + mid + null)
188 size_t candidate_len = strlen(candidate);
189 size_t mid_len = strlen(mid);
190 size_t payload_len = candidate_len + 1 + mid_len + 1;
191
192 if (payload_len > 4096) {
193 mutex_unlock(&g_webrtc_mutex);
194 SET_ERRNO(ERROR_INVALID_PARAM, "ICE candidate + mid too large (>4096 bytes)");
195 return ERROR_INVALID_PARAM;
196 }
197
198 // Construct packet
199 // Header: acip_webrtc_ice_t (defined in network/acip/acds.h)
200 // Followed by: candidate string + null + mid string + null
201 uint8_t ice_msg_buf[sizeof(acip_webrtc_ice_t) + 4096];
202 acip_webrtc_ice_t *ice_msg = (acip_webrtc_ice_t *)ice_msg_buf;
203
204 // Fill header
205 memcpy(ice_msg->session_id, session_id, 16);
206 memcpy(ice_msg->sender_id, g_session_context.participant_id, 16);
207 memset(ice_msg->recipient_id, 0, 16); // Broadcast to all
208 ice_msg->candidate_len = HOST_TO_NET_U16((uint16_t)candidate_len);
209
210 // Copy candidate and mid after header
211 uint8_t *payload = ice_msg_buf + sizeof(acip_webrtc_ice_t);
212 memcpy(payload, candidate, candidate_len);
213 payload[candidate_len] = '\0';
214 memcpy(payload + candidate_len + 1, mid, mid_len);
215 payload[candidate_len + 1 + mid_len] = '\0';
216
217 log_debug("Sending ICE candidate directly to peer (candidate_len=%zu, mid=%s)", candidate_len, mid);
218
219 // Send via direct TCP transport (not ACDS)
220 size_t msg_size = sizeof(acip_webrtc_ice_t) + payload_len;
221 asciichat_error_t result =
222 packet_send_via_transport(g_tcp_transport, PACKET_TYPE_ACIP_WEBRTC_ICE, ice_msg_buf, msg_size, 0);
223
224 mutex_unlock(&g_webrtc_mutex);
225
226 if (result != ASCIICHAT_OK) {
227 log_error("Failed to send ICE candidate via direct TCP: %d", result);
228 return result;
229 }
230
231 log_debug("Sent ICE candidate directly to peer (TCP transport)");
232 return ASCIICHAT_OK;
233}
234
235// =============================================================================
236// Public API
237// =============================================================================
238
240 const uint8_t session_id[16],
241 const uint8_t participant_id[16]) {
242 ensure_mutex_initialized();
243 mutex_lock(&g_webrtc_mutex);
244
245 g_tcp_transport = tcp_transport;
246
247 if (session_id && participant_id) {
248 memcpy(g_session_context.session_id, session_id, 16);
249 memcpy(g_session_context.participant_id, participant_id, 16);
250 g_session_context.is_set = true;
251 }
252
253 mutex_unlock(&g_webrtc_mutex);
254
255 webrtc_signaling_callbacks_t callbacks = {
256 .send_sdp = discovery_send_sdp,
257 .send_ice = discovery_send_ice,
258 .user_data = NULL, // Unused - we use global state
259 };
260
261 return callbacks;
262}
263
265 ensure_mutex_initialized();
266 mutex_lock(&g_webrtc_mutex);
267 g_tcp_transport = transport;
268 mutex_unlock(&g_webrtc_mutex);
269}
270
272 ensure_mutex_initialized();
273 mutex_lock(&g_webrtc_mutex);
274
275 if (session_id && participant_id) {
276 memcpy(g_session_context.session_id, session_id, 16);
277 memcpy(g_session_context.participant_id, participant_id, 16);
278 g_session_context.is_set = true;
279 } else {
280 g_session_context.is_set = false;
281 }
282
283 mutex_unlock(&g_webrtc_mutex);
284}
285
287 ensure_mutex_initialized();
288 mutex_lock(&g_webrtc_mutex);
289
290 g_tcp_transport = NULL;
291 g_session_context.is_set = false;
292
293 mutex_unlock(&g_webrtc_mutex);
294}
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
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
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#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
📝 Logging API with multiple log levels and terminal output control
🌐 Core network I/O operations with timeout support
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.
void discovery_webrtc_cleanup_transport(void)
Cleanup and release the direct peer-to-peer transport.
bool is_set
void discovery_webrtc_set_tcp_transport(acip_transport_t *transport)
Set the TCP transport for direct peer-to-peer signaling.
uint8_t session_id[16]
uint8_t participant_id[16]
webrtc_signaling_callbacks_t discovery_webrtc_get_direct_signaling_callbacks(acip_transport_t *tcp_transport, const uint8_t session_id[16], const uint8_t participant_id[16])
Create signaling callbacks for direct peer-to-peer connection during failover.
void discovery_webrtc_set_session_context(const uint8_t session_id[16], const uint8_t participant_id[16])
Set session and participant IDs for direct signaling.
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.