ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
peer_manager.c File Reference

Go to the source code of this file.

Data Structures

struct  peer_entry_t
 Per-peer connection state. More...
 
struct  webrtc_peer_manager
 WebRTC peer manager structure. More...
 

Typedefs

typedef struct webrtc_peer_manager webrtc_peer_manager_t
 WebRTC peer manager structure.
 

Functions

asciichat_error_t webrtc_peer_manager_create (const webrtc_peer_manager_config_t *config, const webrtc_signaling_callbacks_t *signaling_callbacks, webrtc_peer_manager_t **manager_out)
 
void webrtc_peer_manager_destroy (webrtc_peer_manager_t *manager)
 
asciichat_error_t webrtc_peer_manager_handle_sdp (webrtc_peer_manager_t *manager, const acip_webrtc_sdp_t *sdp)
 
asciichat_error_t webrtc_peer_manager_handle_ice (webrtc_peer_manager_t *manager, const acip_webrtc_ice_t *ice)
 
asciichat_error_t webrtc_peer_manager_connect (webrtc_peer_manager_t *manager, const uint8_t session_id[16], const uint8_t participant_id[16])
 
int webrtc_peer_manager_check_gathering_timeouts (webrtc_peer_manager_t *manager, uint32_t timeout_ms)
 

Typedef Documentation

◆ webrtc_peer_manager_t

WebRTC peer manager structure.

Function Documentation

◆ webrtc_peer_manager_check_gathering_timeouts()

int webrtc_peer_manager_check_gathering_timeouts ( webrtc_peer_manager_t manager,
uint32_t  timeout_ms 
)

Definition at line 525 of file peer_manager.c.

525 {
526 if (!manager) {
527 return 0;
528 }
529
530 int timeout_count = 0;
531 peer_entry_t *peer = NULL, *tmp = NULL;
532
533 mutex_lock(&manager->peers_mutex);
534
535 // Iterate through all peers and check for gathering timeout
536 HASH_ITER(hh, manager->peers, peer, tmp) {
537 if (!peer || !peer->pc) {
538 continue;
539 }
540
541 // Check if this peer's ICE gathering has timed out
542 if (webrtc_is_gathering_timed_out(peer->pc, timeout_ms)) {
543 webrtc_gathering_state_t state = webrtc_get_gathering_state(peer->pc);
544
545 log_error("ICE gathering timeout for peer (participant_id=%02x%02x%02x%02x..., timeout=%ums, state=%d)",
546 peer->participant_id[0], peer->participant_id[1], peer->participant_id[2], peer->participant_id[3],
547 timeout_ms, state);
548
549 // Call timeout callback if configured
550 if (manager->config.on_gathering_timeout) {
551 manager->config.on_gathering_timeout(peer->participant_id, timeout_ms, timeout_ms, manager->config.user_data);
552 }
553
554 // Remove and close the timed-out peer connection
555 remove_peer_locked(manager, peer);
556 timeout_count++;
557
558 log_info("Closed and removed timed-out peer connection (count: %d)", timeout_count);
559 }
560 }
561
562 mutex_unlock(&manager->peers_mutex);
563
564 return timeout_count;
565}
webrtc_gathering_state_t webrtc_get_gathering_state(webrtc_peer_connection_t *pc)
bool webrtc_is_gathering_timed_out(webrtc_peer_connection_t *pc, uint32_t timeout_ms)
Per-peer connection state.
webrtc_peer_connection_t * pc
WebRTC peer connection.
uint8_t participant_id[16]
Remote participant UUID (hash key)
mutex_t peers_mutex
Protect peers hash table.
peer_entry_t * peers
Hash table of peer connections.
webrtc_peer_manager_config_t config
Manager configuration.

References webrtc_peer_manager::config, peer_entry_t::participant_id, peer_entry_t::pc, webrtc_peer_manager::peers, webrtc_peer_manager::peers_mutex, webrtc_get_gathering_state(), and webrtc_is_gathering_timed_out().

Referenced by discovery_session_process().

◆ webrtc_peer_manager_connect()

asciichat_error_t webrtc_peer_manager_connect ( webrtc_peer_manager_t manager,
const uint8_t  session_id[16],
const uint8_t  participant_id[16] 
)

Definition at line 490 of file peer_manager.c.

491 {
492 if (!manager || !session_id || !participant_id) {
493 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
494 }
495
496 if (manager->role != WEBRTC_ROLE_JOINER) {
497 return SET_ERRNO(ERROR_INVALID_PARAM, "Only joiners can initiate connections");
498 }
499
500 log_debug("webrtc_peer_manager_connect: session_id=%02x%02x%02x%02x..., participant_id=%02x%02x%02x%02x...",
503
504 mutex_lock(&manager->peers_mutex);
505
506 // Create peer connection
507 peer_entry_t *peer;
508 asciichat_error_t result = create_peer_connection_locked(manager, session_id, participant_id, &peer);
509 if (result != ASCIICHAT_OK) {
510 mutex_unlock(&manager->peers_mutex);
511 return SET_ERRNO(result, "Failed to create peer connection");
512 }
513
514 mutex_unlock(&manager->peers_mutex);
515
516 // Note: SDP offer is automatically created by libdatachannel when rtcCreateDataChannel() is called
517 // The on_local_description callback will be triggered automatically with the offer
518 // No need to manually call webrtc_create_offer() - doing so causes "Unexpected local description" error
519
520 log_info("Initiated WebRTC connection to participant (offer auto-created by DataChannel)");
521
522 return ASCIICHAT_OK;
523}
uint8_t session_id[16]
uint8_t participant_id[16]
webrtc_peer_role_t role
Session role.

References participant_id, webrtc_peer_manager::peers_mutex, webrtc_peer_manager::role, and session_id.

Referenced by discovery_session_process().

◆ webrtc_peer_manager_create()

asciichat_error_t webrtc_peer_manager_create ( const webrtc_peer_manager_config_t *  config,
const webrtc_signaling_callbacks_t *  signaling_callbacks,
webrtc_peer_manager_t **  manager_out 
)

Definition at line 323 of file peer_manager.c.

325 {
326 if (!config || !signaling_callbacks || !manager_out) {
327 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
328 }
329
330 if (!signaling_callbacks->send_sdp || !signaling_callbacks->send_ice) {
331 return SET_ERRNO(ERROR_INVALID_PARAM, "Signaling callbacks required");
332 }
333
334 // Allocate manager
335 webrtc_peer_manager_t *manager = SAFE_MALLOC(sizeof(webrtc_peer_manager_t), webrtc_peer_manager_t *);
336 if (!manager) {
337 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate peer manager");
338 }
339
340 memcpy(&manager->config, config, sizeof(*config));
341 memcpy(&manager->signaling, signaling_callbacks, sizeof(*signaling_callbacks));
342 manager->role = config->role;
343 manager->peers = NULL;
344
345 if (mutex_init(&manager->peers_mutex) != 0) {
346 SAFE_FREE(manager);
347 return SET_ERRNO(ERROR_INTERNAL, "Failed to initialize peers mutex");
348 }
349
350 log_info("Created WebRTC peer manager (role: %s)", manager->role == WEBRTC_ROLE_CREATOR ? "creator" : "joiner");
351
352 *manager_out = manager;
353 return ASCIICHAT_OK;
354}
WebRTC peer manager structure.
webrtc_signaling_callbacks_t signaling
Signaling callbacks.
int mutex_init(mutex_t *mutex)
Definition threading.c:16

References webrtc_peer_manager::config, mutex_init(), webrtc_peer_manager::peers, webrtc_peer_manager::peers_mutex, webrtc_peer_manager::role, and webrtc_peer_manager::signaling.

Referenced by server_main().

◆ webrtc_peer_manager_destroy()

void webrtc_peer_manager_destroy ( webrtc_peer_manager_t manager)

Definition at line 356 of file peer_manager.c.

356 {
357 if (!manager) {
358 return;
359 }
360
361 mutex_lock(&manager->peers_mutex);
362
363 // Close all peer connections
364 peer_entry_t *peer = NULL, *tmp = NULL;
365 HASH_ITER(hh, manager->peers, peer, tmp) {
366 remove_peer_locked(manager, peer);
367 }
368
369 mutex_unlock(&manager->peers_mutex);
370 mutex_destroy(&manager->peers_mutex);
371
372 SAFE_FREE(manager);
373
374 log_debug("Destroyed WebRTC peer manager");
375}
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21

References mutex_destroy(), webrtc_peer_manager::peers, and webrtc_peer_manager::peers_mutex.

Referenced by discovery_session_destroy(), discovery_session_process(), and server_main().

◆ webrtc_peer_manager_handle_ice()

asciichat_error_t webrtc_peer_manager_handle_ice ( webrtc_peer_manager_t manager,
const acip_webrtc_ice_t *  ice 
)

Definition at line 441 of file peer_manager.c.

441 {
442 if (!manager || !ice) {
443 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
444 }
445
446 // Extract ICE candidate and mid (both are null-terminated in the packet)
447 const char *candidate = (const char *)(ice + 1); // After header
448 size_t candidate_str_len = strlen(candidate);
449 const char *mid = candidate + candidate_str_len + 1; // After candidate + null terminator
450
451 log_debug("Handling incoming ICE candidate from remote peer (mid=%s)", mid);
452 log_debug(" [3] After ACDS recv - candidate: '%s' (len=%zu)", candidate, strlen(candidate));
453 log_debug(" [3] After ACDS recv - mid: '%s' (len=%zu)", mid, strlen(mid));
454 log_debug(" [3] After ACDS recv - header.candidate_len=%u", NET_TO_HOST_U16(ice->candidate_len));
455
456 // Hex dump first 100 bytes of payload for debugging
457 const uint8_t *payload = (const uint8_t *)(ice + 1);
458 log_debug(" [3] Hex dump of payload (first 100 bytes):");
459 for (int i = 0; i < 100 && i < (int)candidate_str_len + 20; i += 16) {
460 char hex[64] = {0};
461 char ascii[20] = {0};
462 for (int j = 0; j < 16 && (i + j) < 100; j++) {
463 snprintf(hex + j * 3, sizeof(hex) - j * 3, "%02x ", payload[i + j]);
464 ascii[j] = (payload[i + j] >= 32 && payload[i + j] < 127) ? payload[i + j] : '.';
465 }
466 log_debug(" [%04x] %-48s %s", i, hex, ascii);
467 }
468
469 mutex_lock(&manager->peers_mutex);
470
471 // Find peer connection
472 peer_entry_t *peer = find_peer_locked(manager, ice->sender_id);
473 if (!peer) {
474 mutex_unlock(&manager->peers_mutex);
475 log_warn("ICE candidate for unknown peer, ignoring");
476 return ASCIICHAT_OK;
477 }
478
479 mutex_unlock(&manager->peers_mutex);
480
481 // Add remote ICE candidate
482 asciichat_error_t result = webrtc_add_remote_candidate(peer->pc, candidate, mid);
483 if (result != ASCIICHAT_OK) {
484 return SET_ERRNO(result, "Failed to add remote ICE candidate");
485 }
486
487 return ASCIICHAT_OK;
488}
asciichat_error_t webrtc_add_remote_candidate(webrtc_peer_connection_t *pc, const char *candidate, const char *mid)

References peer_entry_t::pc, webrtc_peer_manager::peers_mutex, and webrtc_add_remote_candidate().

◆ webrtc_peer_manager_handle_sdp()

asciichat_error_t webrtc_peer_manager_handle_sdp ( webrtc_peer_manager_t manager,
const acip_webrtc_sdp_t *  sdp 
)

Definition at line 377 of file peer_manager.c.

377 {
378 if (!manager || !sdp) {
379 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
380 }
381
382 // Extract SDP string and type
383 const uint8_t *sdp_data = (const uint8_t *)(sdp + 1); // After header
384 const char *sdp_type = (sdp->sdp_type == 0) ? "offer" : "answer";
385 uint16_t sdp_len = NET_TO_HOST_U16(sdp->sdp_len);
386
387 // Allocate null-terminated buffer for SDP string (libdatachannel requires C string)
388 char *sdp_str = SAFE_MALLOC(sdp_len + 1, char *);
389 memcpy(sdp_str, sdp_data, sdp_len);
390 sdp_str[sdp_len] = '\0'; // Null-terminate
391
392 log_debug("Handling incoming SDP %s from remote peer (len=%u)", sdp_type, sdp_len);
393
394 mutex_lock(&manager->peers_mutex);
395
396 // Find or create peer connection
397 peer_entry_t *peer;
398
399 // Special case: If receiving an answer and we're a joiner, we may have created
400 // a peer with broadcast ID (00000000...) and need to update it to the real sender_id
401 if (sdp->sdp_type == 1 && manager->role == WEBRTC_ROLE_JOINER) {
402 static const uint8_t broadcast_id[16] = {0};
403 peer = find_peer_locked(manager, broadcast_id);
404 if (peer) {
405 log_debug("Updating broadcast peer with real participant_id from answer");
406 // Remove from hash with old ID
407 HASH_DEL(manager->peers, peer);
408 // Update to real participant_id
409 memcpy(peer->participant_id, sdp->sender_id, 16);
410 // Re-add with new ID
411 HASH_ADD(hh, manager->peers, participant_id, 16, peer);
412 }
413 }
414
415 asciichat_error_t result = create_peer_connection_locked(manager, sdp->session_id, sdp->sender_id, &peer);
416 if (result != ASCIICHAT_OK) {
417 mutex_unlock(&manager->peers_mutex);
418 SAFE_FREE(sdp_str);
419 return SET_ERRNO(result, "Failed to create peer connection for SDP");
420 }
421
422 mutex_unlock(&manager->peers_mutex);
423
424 // Set remote SDP
425 result = webrtc_set_remote_description(peer->pc, sdp_str, sdp_type);
426 SAFE_FREE(sdp_str); // Free after use
427
428 if (result != ASCIICHAT_OK) {
429 return SET_ERRNO(result, "Failed to set remote SDP");
430 }
431
432 // If this is an offer and we're the creator, generate answer automatically
433 // (libdatachannel triggers on_local_description callback with answer)
434 if (sdp->sdp_type == 0 && manager->role == WEBRTC_ROLE_CREATOR) {
435 log_debug("Offer received, answer will be generated automatically");
436 }
437
438 return ASCIICHAT_OK;
439}
asciichat_error_t webrtc_set_remote_description(webrtc_peer_connection_t *pc, const char *sdp, const char *type)

References peer_entry_t::participant_id, participant_id, peer_entry_t::pc, webrtc_peer_manager::peers, webrtc_peer_manager::peers_mutex, webrtc_peer_manager::role, and webrtc_set_remote_description().