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

Go to the source code of this file.

Data Structures

struct  webrtc_peer_connection
 WebRTC peer connection state. More...
 
struct  webrtc_data_channel
 WebRTC data channel for sending/receiving messages. More...
 

Functions

asciichat_error_t webrtc_init (void)
 
void webrtc_destroy (void)
 
asciichat_error_t webrtc_create_peer_connection (const webrtc_config_t *config, webrtc_peer_connection_t **pc_out)
 
void webrtc_close_peer_connection (webrtc_peer_connection_t *pc)
 
webrtc_state_t webrtc_get_state (webrtc_peer_connection_t *pc)
 
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)
 
void * webrtc_get_user_data (webrtc_peer_connection_t *pc)
 
asciichat_error_t webrtc_create_offer (webrtc_peer_connection_t *pc)
 
asciichat_error_t webrtc_set_remote_description (webrtc_peer_connection_t *pc, const char *sdp, const char *type)
 
asciichat_error_t webrtc_add_remote_candidate (webrtc_peer_connection_t *pc, const char *candidate, const char *mid)
 
asciichat_error_t webrtc_create_datachannel (webrtc_peer_connection_t *pc, const char *label, webrtc_data_channel_t **dc_out)
 
asciichat_error_t webrtc_datachannel_send (webrtc_data_channel_t *dc, const uint8_t *data, size_t size)
 
bool webrtc_datachannel_is_open (webrtc_data_channel_t *dc)
 
void webrtc_datachannel_set_open_state (webrtc_data_channel_t *dc, bool is_open)
 
const char * webrtc_datachannel_get_label (webrtc_data_channel_t *dc)
 
void webrtc_close_datachannel (webrtc_data_channel_t *dc)
 
asciichat_error_t webrtc_datachannel_set_callbacks (webrtc_data_channel_t *dc, const webrtc_datachannel_callbacks_t *callbacks)
 
void webrtc_datachannel_destroy (webrtc_data_channel_t *dc)
 
void webrtc_peer_connection_close (webrtc_peer_connection_t *pc)
 
void webrtc_peer_connection_destroy (webrtc_peer_connection_t *pc)
 
int webrtc_get_rtc_id (webrtc_peer_connection_t *pc)
 Get the internal libdatachannel peer connection ID.
 

Function Documentation

◆ webrtc_add_remote_candidate()

asciichat_error_t webrtc_add_remote_candidate ( webrtc_peer_connection_t *  pc,
const char *  candidate,
const char *  mid 
)

Definition at line 587 of file lib/network/webrtc/webrtc.c.

587 {
588 if (!pc || !candidate) {
589 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
590 }
591
592 log_debug(" [4] Before libdatachannel - candidate: '%s' (len=%zu)", candidate, strlen(candidate));
593 log_debug(" [4] Before libdatachannel - mid: '%s' (len=%zu)", mid ? mid : "(null)", mid ? strlen(mid) : 0);
594
595 int result = rtcAddRemoteCandidate(pc->rtc_id, candidate, mid);
596 if (result != RTC_ERR_SUCCESS) {
597 return SET_ERRNO(ERROR_NETWORK, "Failed to add remote ICE candidate (rtc error %d)", result);
598 }
599
600 log_debug("Added remote ICE candidate (pc_id=%d)", pc->rtc_id);
601 return ASCIICHAT_OK;
602}

Referenced by ice_add_remote_candidate(), and webrtc_peer_manager_handle_ice().

◆ webrtc_close_datachannel()

void webrtc_close_datachannel ( webrtc_data_channel_t *  dc)

Definition at line 709 of file lib/network/webrtc/webrtc.c.

709 {
710 if (!dc) {
711 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
712 return;
713 }
714
715 // Save the ID before freeing (dc might be partially freed already)
716 int dc_id = dc->rtc_id;
717
718 rtcDeleteDataChannel(dc_id);
719 log_debug("Closed DataChannel (dc_id=%d)", dc_id);
720
721 SAFE_FREE(dc);
722}

Referenced by webrtc_close_peer_connection(), and webrtc_datachannel_destroy().

◆ webrtc_close_peer_connection()

void webrtc_close_peer_connection ( webrtc_peer_connection_t *  pc)

Definition at line 480 of file lib/network/webrtc/webrtc.c.

480 {
481 if (!pc) {
482 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid peer connection");
483 return;
484 }
485
486 // Close data channel if exists
487 if (pc->dc) {
489 pc->dc = NULL;
490 }
491
492 // Close peer connection
493 rtcDeletePeerConnection(pc->rtc_id);
494 log_debug("Closed WebRTC peer connection (id=%d)", pc->rtc_id);
495
496 SAFE_FREE(pc);
497}
void webrtc_close_datachannel(webrtc_data_channel_t *dc)

References webrtc_close_datachannel().

◆ webrtc_create_datachannel()

asciichat_error_t webrtc_create_datachannel ( webrtc_peer_connection_t *  pc,
const char *  label,
webrtc_data_channel_t **  dc_out 
)

Definition at line 608 of file lib/network/webrtc/webrtc.c.

609 {
610 if (!pc || !label || !dc_out) {
611 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
612 }
613
614 // Create data channel
615 int dc_id = rtcCreateDataChannel(pc->rtc_id, label);
616 if (dc_id < 0) {
617 return SET_ERRNO(ERROR_NETWORK, "Failed to create data channel (rtc error %d)", dc_id);
618 }
619
620 // Allocate wrapper
621 webrtc_data_channel_t *dc = SAFE_MALLOC(sizeof(webrtc_data_channel_t), webrtc_data_channel_t *);
622 if (!dc) {
623 rtcDeleteDataChannel(dc_id);
624 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate data channel wrapper");
625 }
626
627 dc->rtc_id = dc_id;
628 dc->pc = pc;
629 dc->is_open = false;
630
631 // Set up callbacks
632 rtcSetUserPointer(dc_id, dc);
633 rtcSetOpenCallback(dc_id, on_datachannel_open_adapter);
634 rtcSetMessageCallback(dc_id, on_datachannel_message_adapter);
635 rtcSetErrorCallback(dc_id, on_datachannel_error_adapter);
636
637 pc->dc = dc;
638 *dc_out = dc;
639
640 log_debug("Created DataChannel '%s' (dc_id=%d, pc_id=%d)", label, dc_id, pc->rtc_id);
641 return ASCIICHAT_OK;
642}

◆ webrtc_create_offer()

asciichat_error_t webrtc_create_offer ( webrtc_peer_connection_t *  pc)

Definition at line 554 of file lib/network/webrtc/webrtc.c.

554 {
555 if (!pc) {
556 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid peer connection");
557 }
558
559 // Set local description with NULL type to trigger offer generation
560 int result = rtcSetLocalDescription(pc->rtc_id, NULL);
561 if (result != RTC_ERR_SUCCESS) {
562 return SET_ERRNO(ERROR_NETWORK, "Failed to create SDP offer (rtc error %d)", result);
563 }
564
565 log_debug("Creating SDP offer (pc_id=%d)", pc->rtc_id);
566 return ASCIICHAT_OK;
567}

◆ webrtc_create_peer_connection()

asciichat_error_t webrtc_create_peer_connection ( const webrtc_config_t *  config,
webrtc_peer_connection_t **  pc_out 
)

Definition at line 395 of file lib/network/webrtc/webrtc.c.

395 {
396 if (!config || !pc_out) {
397 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid config or output parameter");
398 }
399
400 static_mutex_lock(&g_webrtc_refcount_mutex);
401 bool is_initialized = (g_webrtc_init_refcount > 0);
402 static_mutex_unlock(&g_webrtc_refcount_mutex);
403
404 if (!is_initialized) {
405 return SET_ERRNO(ERROR_INIT, "WebRTC library not initialized");
406 }
407
408 // Allocate peer connection wrapper
409 webrtc_peer_connection_t *pc = SAFE_MALLOC(sizeof(webrtc_peer_connection_t), webrtc_peer_connection_t *);
410 if (!pc) {
411 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate peer connection");
412 }
413
414 pc->config = *config; // Copy config
415 pc->state = WEBRTC_STATE_NEW;
416 pc->dc = NULL;
417
418 // Build ICE server list for libdatachannel
419 const char **ice_servers = NULL;
420 size_t ice_count = 0;
421
422 if (config->stun_count > 0 || config->turn_count > 0) {
423 ice_count = config->stun_count + config->turn_count;
424 ice_servers = SAFE_MALLOC(ice_count * sizeof(char *), const char **);
425 if (!ice_servers) {
426 SAFE_FREE(pc);
427 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ICE server list");
428 }
429
430 // Add STUN servers
431 for (size_t i = 0; i < config->stun_count; i++) {
432 ice_servers[i] = config->stun_servers[i].host;
433 }
434
435 // Add TURN servers
436 for (size_t i = 0; i < config->turn_count; i++) {
437 ice_servers[config->stun_count + i] = config->turn_servers[i].url;
438 }
439 }
440
441 // Create libdatachannel configuration
442 rtcConfiguration rtc_config;
443 memset(&rtc_config, 0, sizeof(rtc_config));
444 rtc_config.iceServers = ice_servers;
445 rtc_config.iceServersCount = (int)ice_count;
446 rtc_config.iceTransportPolicy = RTC_TRANSPORT_POLICY_ALL;
447
448 // Create peer connection
449 int pc_id = rtcCreatePeerConnection(&rtc_config);
450
451 // Free ICE server list (libdatachannel makes a copy)
452 if (ice_servers) {
453 SAFE_FREE(ice_servers);
454 }
455
456 if (pc_id < 0) {
457 SAFE_FREE(pc);
458 return SET_ERRNO(ERROR_NETWORK, "Failed to create peer connection (rtc error %d)", pc_id);
459 }
460
461 pc->rtc_id = pc_id;
462
463 // Initialize gathering state
464 pc->gathering_state = WEBRTC_GATHERING_NEW;
465 pc->gathering_start_time_ms = 0;
466
467 // Set up callbacks
468 rtcSetUserPointer(pc_id, pc);
469 rtcSetStateChangeCallback(pc_id, on_state_change_adapter);
470 rtcSetGatheringStateChangeCallback(pc_id, on_gathering_state_change_adapter);
471 rtcSetLocalDescriptionCallback(pc_id, on_local_description_adapter);
472 rtcSetLocalCandidateCallback(pc_id, on_local_candidate_adapter);
473 rtcSetDataChannelCallback(pc_id, on_datachannel_adapter);
474
475 *pc_out = pc;
476 log_debug("Created WebRTC peer connection (id=%d)", pc_id);
477 return ASCIICHAT_OK;
478}

◆ webrtc_datachannel_destroy()

void webrtc_datachannel_destroy ( webrtc_data_channel_t *  dc)

Definition at line 770 of file lib/network/webrtc/webrtc.c.

770 {
771 if (!dc) {
772 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null data channel");
773 return;
774 }
775
776 // Close if still open, then free
778 // Note: webrtc_close_datachannel already calls SAFE_FREE(dc), so we're done
779}

References webrtc_close_datachannel().

◆ webrtc_datachannel_get_label()

const char * webrtc_datachannel_get_label ( webrtc_data_channel_t *  dc)

Definition at line 694 of file lib/network/webrtc/webrtc.c.

694 {
695 if (!dc) {
696 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
697 return NULL;
698 }
699
700 static char label[256];
701 int result = rtcGetDataChannelLabel(dc->rtc_id, label, sizeof(label));
702 if (result < 0) {
703 return NULL;
704 }
705
706 return label;
707}

◆ webrtc_datachannel_is_open()

bool webrtc_datachannel_is_open ( webrtc_data_channel_t *  dc)

Definition at line 678 of file lib/network/webrtc/webrtc.c.

678 {
679 if (!dc) {
680 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
681 return false;
682 }
683 return dc->is_open;
684}

◆ webrtc_datachannel_send()

asciichat_error_t webrtc_datachannel_send ( webrtc_data_channel_t *  dc,
const uint8_t *  data,
size_t  size 
)

Definition at line 644 of file lib/network/webrtc/webrtc.c.

644 {
645 if (!dc || !data) {
646 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
647 }
648
649 if (!dc->is_open) {
650 log_error("★ WEBRTC_DATACHANNEL_SEND: Channel not open! size=%zu, dc->rtc_id=%d", size, dc ? dc->rtc_id : -1);
651 return SET_ERRNO(ERROR_NETWORK, "DataChannel not open");
652 }
653
654 // Log packet details at network layer (debug level for normal operation)
655 if (size >= 20) {
656 const uint8_t *pkt = (const uint8_t *)data;
657 log_debug("★ RTCSENDMESSAGE_BEFORE: dc_id=%d, size=%zu, first_20_bytes: %02x%02x%02x%02x %02x%02x%02x%02x "
658 "%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x",
659 dc->rtc_id, size, pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5], pkt[6], pkt[7], pkt[8], pkt[9], pkt[10],
660 pkt[11], pkt[12], pkt[13], pkt[14], pkt[15], pkt[16], pkt[17], pkt[18], pkt[19]);
661 } else {
662 log_debug("★ RTCSENDMESSAGE_BEFORE: dc_id=%d, size=%zu (too small to log content)", dc->rtc_id, size);
663 }
664
665 int result = rtcSendMessage(dc->rtc_id, (const char *)data, (int)size);
666
667 log_debug("★ RTCSENDMESSAGE_AFTER: dc_id=%d, rtcSendMessage returned %d for size=%zu", dc->rtc_id, result, size);
668
669 if (result < 0) {
670 log_error("★ WEBRTC_DATACHANNEL_SEND: FAILED with error code %d", result);
671 return SET_ERRNO(ERROR_NETWORK, "Failed to send data (rtc error %d)", result);
672 }
673
674 log_debug("★ WEBRTC_DATACHANNEL_SEND: SUCCESS - sent %zu bytes", size);
675 return ASCIICHAT_OK;
676}

◆ webrtc_datachannel_set_callbacks()

asciichat_error_t webrtc_datachannel_set_callbacks ( webrtc_data_channel_t *  dc,
const webrtc_datachannel_callbacks_t *  callbacks 
)

Definition at line 728 of file lib/network/webrtc/webrtc.c.

729 {
730 if (!dc) {
731 return SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
732 }
733
734 if (!callbacks) {
735 return SET_ERRNO(ERROR_INVALID_PARAM, "Callbacks struct is NULL");
736 }
737
738 // Store user callbacks in the data channel struct
739 dc->user_on_open = callbacks->on_open;
740 dc->user_on_close = callbacks->on_close;
741 dc->user_on_error = callbacks->on_error;
742 dc->user_on_message = callbacks->on_message;
743 dc->user_data = callbacks->user_data;
744
745 // Register adapter functions with libdatachannel (adapters use correct signature)
746 // The adapters will look up our stored callbacks and invoke them with proper types
747 if (callbacks->on_open) {
748 rtcSetOpenCallback(dc->rtc_id, on_datachannel_open_adapter);
749 }
750
751 if (callbacks->on_close) {
752 rtcSetClosedCallback(dc->rtc_id, on_datachannel_closed_adapter);
753 }
754
755 if (callbacks->on_error) {
756 rtcSetErrorCallback(dc->rtc_id, on_datachannel_error_adapter);
757 }
758
759 if (callbacks->on_message) {
760 rtcSetMessageCallback(dc->rtc_id, on_datachannel_message_adapter);
761 }
762
763 // Set user pointer to the data channel so adapters can retrieve it
764 rtcSetUserPointer(dc->rtc_id, dc);
765
766 log_debug("Set DataChannel callbacks (dc_id=%d)", dc->rtc_id);
767 return ASCIICHAT_OK;
768}

Referenced by acip_webrtc_transport_create().

◆ webrtc_datachannel_set_open_state()

void webrtc_datachannel_set_open_state ( webrtc_data_channel_t *  dc,
bool  is_open 
)

Definition at line 686 of file lib/network/webrtc/webrtc.c.

686 {
687 if (!dc) {
688 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
689 return;
690 }
691 dc->is_open = is_open;
692}

Referenced by acip_webrtc_transport_create().

◆ webrtc_destroy()

void webrtc_destroy ( void  )

Definition at line 387 of file lib/network/webrtc/webrtc.c.

387 {
388 webrtc_release();
389}

◆ webrtc_get_gathering_state()

webrtc_gathering_state_t webrtc_get_gathering_state ( webrtc_peer_connection_t *  pc)

Definition at line 507 of file lib/network/webrtc/webrtc.c.

507 {
508 if (!pc) {
509 log_warn("Peer connection is NULL");
510 return WEBRTC_GATHERING_NEW;
511 }
512 return pc->gathering_state;
513}

Referenced by webrtc_peer_manager_check_gathering_timeouts().

◆ webrtc_get_rtc_id()

int webrtc_get_rtc_id ( webrtc_peer_connection_t *  pc)

Get the internal libdatachannel peer connection ID.

Helper function for C++ code that needs access to internal rtc_id without exposing the full structure definition.

Parameters
pcPeer connection
Returns
libdatachannel peer connection ID, or -1 if pc is NULL

Definition at line 817 of file lib/network/webrtc/webrtc.c.

817 {
818 if (!pc) {
819 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null peer connectiont");
820 return -1;
821 }
822 return pc->rtc_id;
823}

Referenced by ice_get_selected_pair_impl().

◆ webrtc_get_state()

webrtc_state_t webrtc_get_state ( webrtc_peer_connection_t *  pc)

Definition at line 499 of file lib/network/webrtc/webrtc.c.

499 {
500 if (!pc) {
501 log_warn("Peer connection is NULL");
502 return WEBRTC_STATE_CLOSED;
503 }
504 return pc->state;
505}

Referenced by ice_is_connected().

◆ webrtc_get_user_data()

void * webrtc_get_user_data ( webrtc_peer_connection_t *  pc)

Definition at line 542 of file lib/network/webrtc/webrtc.c.

542 {
543 if (!pc) {
544 log_warn("Peer connection is NULL");
545 return NULL;
546 }
547 return pc->config.user_data;
548}

◆ webrtc_init()

asciichat_error_t webrtc_init ( void  )

Definition at line 383 of file lib/network/webrtc/webrtc.c.

383 {
384 return webrtc_ensure_initialized();
385}

Referenced by server_main().

◆ webrtc_is_gathering_timed_out()

bool webrtc_is_gathering_timed_out ( webrtc_peer_connection_t *  pc,
uint32_t  timeout_ms 
)

Definition at line 515 of file lib/network/webrtc/webrtc.c.

515 {
516 if (!pc) {
517 return false;
518 }
519
520 // Only check timeout if we're actively gathering
521 if (pc->gathering_state != WEBRTC_GATHERING_GATHERING) {
522 return false;
523 }
524
525 // If gathering hasn't started yet (start_time == 0), no timeout
526 if (pc->gathering_start_time_ms == 0) {
527 return false;
528 }
529
530 // Check if elapsed time exceeds timeout
531 uint64_t current_time_ms = platform_get_monotonic_time_us() / 1000;
532 uint64_t elapsed_ms = current_time_ms - pc->gathering_start_time_ms;
533
534 if (elapsed_ms > timeout_ms) {
535 log_warn("ICE gathering timeout: %llu ms elapsed (timeout: %u ms)", (unsigned long long)elapsed_ms, timeout_ms);
536 return true;
537 }
538
539 return false;
540}
uint64_t platform_get_monotonic_time_us(void)

References platform_get_monotonic_time_us().

Referenced by webrtc_peer_manager_check_gathering_timeouts().

◆ webrtc_peer_connection_close()

void webrtc_peer_connection_close ( webrtc_peer_connection_t *  pc)

Definition at line 785 of file lib/network/webrtc/webrtc.c.

785 {
786 if (!pc) {
787 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null peer connectiont");
788 return;
789 }
790
791 rtcClose(pc->rtc_id);
792 log_debug("Closed peer connection (pc_id=%d)", pc->rtc_id);
793}

◆ webrtc_peer_connection_destroy()

void webrtc_peer_connection_destroy ( webrtc_peer_connection_t *  pc)

Definition at line 795 of file lib/network/webrtc/webrtc.c.

795 {
796 if (!pc) {
797 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null peer connectiont");
798 return;
799 }
800
801 // Close and delete peer connection
802 rtcDeletePeerConnection(pc->rtc_id);
803 log_debug("Destroyed peer connection (pc_id=%d)", pc->rtc_id);
804
805 SAFE_FREE(pc);
806}

◆ webrtc_set_remote_description()

asciichat_error_t webrtc_set_remote_description ( webrtc_peer_connection_t *  pc,
const char *  sdp,
const char *  type 
)

Definition at line 569 of file lib/network/webrtc/webrtc.c.

569 {
570 if (!pc || !sdp || !type) {
571 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
572 }
573
574 int result = rtcSetRemoteDescription(pc->rtc_id, sdp, type);
575 if (result != RTC_ERR_SUCCESS) {
576 return SET_ERRNO(ERROR_NETWORK, "Failed to set remote SDP (rtc error %d)", result);
577 }
578
579 log_debug("Set remote SDP description (pc_id=%d, type=%s)", pc->rtc_id, type);
580 return ASCIICHAT_OK;
581}

Referenced by webrtc_peer_manager_handle_sdp().