ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
WebRTC P2P Connections

WebRTC DataChannel transport for ACIP packets. More...

Files

file  ice_selected_pair.cpp
 C++ implementation for retrieving selected ICE candidate pair.
 
file  ice.h
 ICE (Interactive Connectivity Establishment) for WebRTC.
 
file  sdp.h
 SDP (Session Description Protocol) for WebRTC audio/video negotiation.
 

Data Structures

struct  webrtc_config_t
 WebRTC configuration. More...
 
struct  webrtc_datachannel_callbacks_t
 DataChannel callback structure. More...
 

Typedefs

typedef struct webrtc_peer_connection webrtc_peer_connection_t
 
typedef struct webrtc_data_channel webrtc_data_channel_t
 
typedef void(* webrtc_state_callback_t) (webrtc_peer_connection_t *pc, webrtc_state_t state, void *user_data)
 Callback for state changes.
 
typedef void(* webrtc_local_description_callback_t) (webrtc_peer_connection_t *pc, const char *sdp, const char *type, void *user_data)
 Callback for local SDP offer/answer generation.
 
typedef void(* webrtc_local_candidate_callback_t) (webrtc_peer_connection_t *pc, const char *candidate, const char *mid, void *user_data)
 Callback for local ICE candidate discovery.
 
typedef void(* webrtc_gathering_state_callback_t) (webrtc_peer_connection_t *pc, webrtc_gathering_state_t state, void *user_data)
 Callback for ICE gathering state changes.
 
typedef void(* webrtc_datachannel_open_callback_t) (webrtc_data_channel_t *dc, void *user_data)
 Callback for DataChannel open event.
 
typedef void(* webrtc_datachannel_message_callback_t) (webrtc_data_channel_t *dc, const uint8_t *data, size_t size, void *user_data)
 Callback for DataChannel message received.
 
typedef void(* webrtc_datachannel_error_callback_t) (webrtc_data_channel_t *dc, const char *error, void *user_data)
 Callback for DataChannel error.
 

Enumerations

enum  webrtc_state_t {
  WEBRTC_STATE_NEW = 0 , WEBRTC_STATE_CONNECTING , WEBRTC_STATE_CONNECTED , WEBRTC_STATE_DISCONNECTED ,
  WEBRTC_STATE_FAILED , WEBRTC_STATE_CLOSED
}
 WebRTC connection state. More...
 
enum  webrtc_gathering_state_t { WEBRTC_GATHERING_NEW = 0 , WEBRTC_GATHERING_GATHERING , WEBRTC_GATHERING_COMPLETE }
 ICE gathering state. More...
 

Functions

asciichat_error_t webrtc_init (void)
 Initialize WebRTC library (libdatachannel)
 
void webrtc_destroy (void)
 Cleanup WebRTC library resources.
 
asciichat_error_t webrtc_create_peer_connection (const webrtc_config_t *config, webrtc_peer_connection_t **pc_out)
 Create a new WebRTC peer connection.
 
void webrtc_close_peer_connection (webrtc_peer_connection_t *pc)
 Close and destroy a peer connection.
 
webrtc_state_t webrtc_get_state (webrtc_peer_connection_t *pc)
 Get current connection state.
 
webrtc_gathering_state_t webrtc_get_gathering_state (webrtc_peer_connection_t *pc)
 Get current ICE gathering state.
 
bool webrtc_is_gathering_timed_out (webrtc_peer_connection_t *pc, uint32_t timeout_ms)
 Check if ICE gathering has timed out.
 
void * webrtc_get_user_data (webrtc_peer_connection_t *pc)
 Get user data pointer from connection.
 
asciichat_error_t webrtc_create_offer (webrtc_peer_connection_t *pc)
 Create and set local SDP offer (for connection initiator)
 
asciichat_error_t webrtc_set_remote_description (webrtc_peer_connection_t *pc, const char *sdp, const char *type)
 Set remote SDP offer/answer.
 
asciichat_error_t webrtc_add_remote_candidate (webrtc_peer_connection_t *pc, const char *candidate, const char *mid)
 Add remote ICE candidate.
 
asciichat_error_t webrtc_create_datachannel (webrtc_peer_connection_t *pc, const char *label, webrtc_data_channel_t **dc_out)
 Create a DataChannel (for connection initiator)
 
asciichat_error_t webrtc_datachannel_send (webrtc_data_channel_t *dc, const uint8_t *data, size_t size)
 Send data over DataChannel.
 
asciichat_error_t webrtc_datachannel_get_buffered_amount (webrtc_data_channel_t *dc, size_t *amount)
 
asciichat_error_t webrtc_datachannel_get_max_message_size (webrtc_data_channel_t *dc, size_t *size)
 
bool webrtc_datachannel_is_open (webrtc_data_channel_t *dc)
 Check if DataChannel is open and ready.
 
void webrtc_datachannel_set_open_state (webrtc_data_channel_t *dc, bool is_open)
 Set DataChannel open state (internal use)
 
const char * webrtc_datachannel_get_label (webrtc_data_channel_t *dc)
 Get DataChannel label.
 
asciichat_error_t webrtc_datachannel_set_callbacks (webrtc_data_channel_t *dc, const webrtc_datachannel_callbacks_t *callbacks)
 Set DataChannel callbacks.
 
void webrtc_close_datachannel (webrtc_data_channel_t *dc)
 Close a DataChannel.
 
void webrtc_datachannel_destroy (webrtc_data_channel_t *dc)
 Destroy a DataChannel and free resources.
 
void webrtc_peer_connection_close (webrtc_peer_connection_t *pc)
 Close a peer connection.
 
void webrtc_peer_connection_destroy (webrtc_peer_connection_t *pc)
 Destroy a peer connection and free resources.
 
int webrtc_get_rtc_id (webrtc_peer_connection_t *pc)
 Get the internal libdatachannel peer connection ID.
 

Detailed Description

WebRTC DataChannel transport for ACIP packets.

Typedef Documentation

◆ webrtc_data_channel_t

◆ webrtc_datachannel_error_callback_t

typedef void(* webrtc_datachannel_error_callback_t) (webrtc_data_channel_t *dc, const char *error, void *user_data)

#include <webrtc.h>

Callback for DataChannel error.

Parameters
dcData channel
errorError message (null-terminated)
user_dataUser-provided context pointer

Definition at line 123 of file include/ascii-chat/network/webrtc/webrtc.h.

◆ webrtc_datachannel_message_callback_t

typedef void(* webrtc_datachannel_message_callback_t) (webrtc_data_channel_t *dc, const uint8_t *data, size_t size, void *user_data)

#include <webrtc.h>

Callback for DataChannel message received.

Parameters
dcData channel
dataMessage data (binary)
sizeMessage size in bytes
user_dataUser-provided context pointer

Definition at line 114 of file include/ascii-chat/network/webrtc/webrtc.h.

◆ webrtc_datachannel_open_callback_t

typedef void(* webrtc_datachannel_open_callback_t) (webrtc_data_channel_t *dc, void *user_data)

#include <webrtc.h>

Callback for DataChannel open event.

Parameters
dcData channel that opened
user_dataUser-provided context pointer

Definition at line 105 of file include/ascii-chat/network/webrtc/webrtc.h.

◆ webrtc_gathering_state_callback_t

typedef void(* webrtc_gathering_state_callback_t) (webrtc_peer_connection_t *pc, webrtc_gathering_state_t state, void *user_data)

#include <webrtc.h>

Callback for ICE gathering state changes.

Parameters
pcPeer connection
stateNew gathering state
user_dataUser-provided context pointer

Definition at line 97 of file include/ascii-chat/network/webrtc/webrtc.h.

◆ webrtc_local_candidate_callback_t

typedef void(* webrtc_local_candidate_callback_t) (webrtc_peer_connection_t *pc, const char *candidate, const char *mid, void *user_data)

#include <webrtc.h>

Callback for local ICE candidate discovery.

Parameters
pcPeer connection
candidateICE candidate string (null-terminated)
midMedia stream ID (null-terminated)
user_dataUser-provided context pointer

Application should send this candidate to the remote peer via signaling channel (ACDS).

Definition at line 88 of file include/ascii-chat/network/webrtc/webrtc.h.

◆ webrtc_local_description_callback_t

typedef void(* webrtc_local_description_callback_t) (webrtc_peer_connection_t *pc, const char *sdp, const char *type, void *user_data)

#include <webrtc.h>

Callback for local SDP offer/answer generation.

Parameters
pcPeer connection
sdpSDP string (null-terminated)
typeSDP type ("offer" or "answer")
user_dataUser-provided context pointer

Application should send this SDP to the remote peer via signaling channel (ACDS).

Definition at line 76 of file include/ascii-chat/network/webrtc/webrtc.h.

◆ webrtc_peer_connection_t

◆ webrtc_state_callback_t

typedef void(* webrtc_state_callback_t) (webrtc_peer_connection_t *pc, webrtc_state_t state, void *user_data)

#include <webrtc.h>

Callback for state changes.

Parameters
pcPeer connection
stateNew connection state
user_dataUser-provided context pointer

Definition at line 65 of file include/ascii-chat/network/webrtc/webrtc.h.

Enumeration Type Documentation

◆ webrtc_gathering_state_t

#include <webrtc.h>

ICE gathering state.

Enumerator
WEBRTC_GATHERING_NEW 

Not started.

WEBRTC_GATHERING_GATHERING 

Gathering candidates.

WEBRTC_GATHERING_COMPLETE 

All candidates gathered.

Definition at line 53 of file include/ascii-chat/network/webrtc/webrtc.h.

53 {
webrtc_gathering_state_t
ICE gathering state.
@ WEBRTC_GATHERING_COMPLETE
All candidates gathered.
@ WEBRTC_GATHERING_GATHERING
Gathering candidates.
@ WEBRTC_GATHERING_NEW
Not started.

◆ webrtc_state_t

#include <webrtc.h>

WebRTC connection state.

Enumerator
WEBRTC_STATE_NEW 

Connection created but not started.

WEBRTC_STATE_CONNECTING 

ICE gathering/connection in progress.

WEBRTC_STATE_CONNECTED 

DataChannel established and ready.

WEBRTC_STATE_DISCONNECTED 

Connection lost.

WEBRTC_STATE_FAILED 

Connection failed (fatal)

WEBRTC_STATE_CLOSED 

Connection closed cleanly.

Definition at line 41 of file include/ascii-chat/network/webrtc/webrtc.h.

41 {
webrtc_state_t
WebRTC connection state.
@ WEBRTC_STATE_NEW
Connection created but not started.
@ WEBRTC_STATE_DISCONNECTED
Connection lost.
@ WEBRTC_STATE_FAILED
Connection failed (fatal)
@ WEBRTC_STATE_CLOSED
Connection closed cleanly.
@ WEBRTC_STATE_CONNECTING
ICE gathering/connection in progress.
@ WEBRTC_STATE_CONNECTED
DataChannel established and ready.

Function Documentation

◆ webrtc_add_remote_candidate()

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

#include <webrtc.h>

Add remote ICE candidate.

Parameters
pcPeer connection
candidateICE candidate string (null-terminated)
midMedia stream ID (null-terminated, can be empty)
Returns
ASCIICHAT_OK on success, error code on failure

Adds a remote ICE candidate received via signaling channel.

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

657 {
658 if (!pc || !candidate) {
659 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
660 }
661
662 log_debug(" [4] Before libdatachannel - candidate: '%s' (len=%zu)", candidate, strlen(candidate));
663 log_debug(" [4] Before libdatachannel - mid: '%s' (len=%zu)", mid ? mid : "(null)", mid ? strlen(mid) : 0);
664
665 int result = rtcAddRemoteCandidate(pc->rtc_id, candidate, mid);
666 if (result != RTC_ERR_SUCCESS) {
667 return SET_ERRNO(ERROR_NETWORK, "Failed to add remote ICE candidate (rtc error %d)", result);
668 }
669
670 log_debug("Added remote ICE candidate (pc_id=%d)", pc->rtc_id);
671 return ASCIICHAT_OK;
672}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_NETWORK
Definition error_codes.h:77
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
int rtc_id
libdatachannel peer connection ID

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, log_debug, webrtc_peer_connection::rtc_id, and SET_ERRNO.

Referenced by ice_add_remote_candidate(), and webrtc_peer_manager_handle_ice().

◆ webrtc_close_datachannel()

void webrtc_close_datachannel ( webrtc_data_channel_t *  dc)

#include <webrtc.h>

Close a DataChannel.

Parameters
dcData channel to close

Closes the channel gracefully. Safe to call with NULL pointer.

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

803 {
804 if (!dc) {
805 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
806 return;
807 }
808 if (dc->rtc_id < 0) {
809 atomic_store_bool(&dc->is_open, false);
810 return;
811 }
812
813 // Save the ID before deleting the native channel.
814 int dc_id = dc->rtc_id;
815
816 rtcDeleteDataChannel(dc_id);
817 log_debug("Closed DataChannel (dc_id=%d)", dc_id);
818 dc->rtc_id = -1;
819 atomic_store_bool(&dc->is_open, false);
820}
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
atomic_t is_open
Channel open state.
int rtc_id
libdatachannel data channel ID

References atomic_store_bool(), ERROR_INVALID_PARAM, webrtc_data_channel::is_open, log_debug, webrtc_data_channel::rtc_id, and SET_ERRNO.

Referenced by webrtc_close_peer_connection(), and webrtc_datachannel_destroy().

◆ webrtc_close_peer_connection()

void webrtc_close_peer_connection ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Close and destroy a peer connection.

Parameters
pcPeer connection to close

Closes the connection gracefully and frees all resources. Safe to call with NULL pointer.

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

535 {
536 if (!pc) {
537 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid peer connection");
538 return;
539 }
540
541 // Close data channel if exists
542 if (pc->dc) {
544 pc->dc = NULL;
545 }
546
547 // Close peer connection
548 rtcDeletePeerConnection(pc->rtc_id);
549 log_debug("Closed WebRTC peer connection (id=%d)", pc->rtc_id);
550
551 SAFE_FREE(pc);
552}
#define SAFE_FREE(ptr)
Definition common.h:376
void webrtc_close_datachannel(webrtc_data_channel_t *dc)
Close a DataChannel.
webrtc_data_channel_t * dc
Primary data channel (if created/received)

References webrtc_peer_connection::dc, ERROR_INVALID_PARAM, log_debug, webrtc_peer_connection::rtc_id, SAFE_FREE, SET_ERRNO, and 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 
)

#include <webrtc.h>

Create a DataChannel (for connection initiator)

Parameters
pcPeer connection
labelChannel label (e.g., "acip")
dc_outOutput parameter for data channel handle
Returns
ASCIICHAT_OK on success, error code on failure

Creates a new DataChannel. The initiator should create the channel, while the responder receives it via the datachannel callback.

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

679 {
680 if (!pc || !label || !dc_out) {
681 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
682 }
683
684 // Native peers use DCEP. Browser offers advertise the separately negotiated
685 // ACIP channel and are handled in webrtc_set_remote_description().
686 int dc_id = rtcCreateDataChannel(pc->rtc_id, label);
687 if (dc_id < 0) {
688 return SET_ERRNO(ERROR_NETWORK, "Failed to create data channel (rtc error %d)", dc_id);
689 }
690
691 // Allocate wrapper
693 if (!dc) {
694 rtcDeleteDataChannel(dc_id);
695 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate data channel wrapper");
696 }
697
698 dc->rtc_id = dc_id;
699 dc->pc = pc;
700 atomic_store_bool(&dc->is_open, false);
701
702 // Set up callbacks
703 rtcSetUserPointer(dc_id, dc);
704 rtcSetOpenCallback(dc_id, on_datachannel_open_adapter);
705 rtcSetMessageCallback(dc_id, on_datachannel_message_adapter);
706 rtcSetErrorCallback(dc_id, on_datachannel_error_adapter);
707
708 pc->dc = dc;
709 *dc_out = dc;
710
711 // Register data channel with debug naming system
712 NAMED_REGISTER_DATACHANNEL(dc, label, NULL);
713
714 log_debug("Created DataChannel '%s' (dc_id=%d, pc_id=%d)", label, dc_id, pc->rtc_id);
715 return ASCIICHAT_OK;
716}
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
#define NAMED_REGISTER_DATACHANNEL(datachannel, name, parent_ptr)
Register a data channel with automatic format specifier.
@ ERROR_MEMORY
Definition error_codes.h:56
WebRTC data channel for sending/receiving messages.
webrtc_peer_connection_t * pc
Parent peer connection.

References ASCIICHAT_OK, atomic_store_bool(), webrtc_peer_connection::dc, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, webrtc_data_channel::is_open, log_debug, NAMED_REGISTER_DATACHANNEL, webrtc_data_channel::pc, webrtc_peer_connection::rtc_id, webrtc_data_channel::rtc_id, SAFE_CALLOC, and SET_ERRNO.

◆ webrtc_create_offer()

asciichat_error_t webrtc_create_offer ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Create and set local SDP offer (for connection initiator)

Parameters
pcPeer connection
Returns
ASCIICHAT_OK on success, error code on failure

Triggers ICE gathering and local description callback with the SDP offer. Use this when initiating a connection to a remote peer.

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

609 {
610 if (!pc) {
611 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid peer connection");
612 }
613
614 // Set local description with NULL type to trigger offer generation
615 int result = rtcSetLocalDescription(pc->rtc_id, NULL);
616 if (result != RTC_ERR_SUCCESS) {
617 return SET_ERRNO(ERROR_NETWORK, "Failed to create SDP offer (rtc error %d)", result);
618 }
619
620 log_debug("Creating SDP offer (pc_id=%d)", pc->rtc_id);
621 return ASCIICHAT_OK;
622}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, log_debug, webrtc_peer_connection::rtc_id, and SET_ERRNO.

◆ webrtc_create_peer_connection()

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

#include <webrtc.h>

Create a new WebRTC peer connection.

Parameters
configConfiguration including ICE servers and callbacks
pc_outOutput parameter for peer connection handle
Returns
ASCIICHAT_OK on success, error code on failure

Creates a new peer connection with the specified configuration. The connection starts in WEBRTC_STATE_NEW state.

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

402 {
403 if (!config || !pc_out) {
404 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid config or output parameter");
405 }
406 *pc_out = NULL;
407 if ((config->stun_count && !config->stun_servers) || (config->turn_count && !config->turn_servers)) {
408 return SET_ERRNO(ERROR_INVALID_PARAM, "ICE server counts require server arrays");
409 }
410 if ((config->turn_username != NULL) != (config->turn_credential != NULL)) {
411 return SET_ERRNO(ERROR_INVALID_PARAM, "TURN overrides require both username and credential");
412 }
413
414 if (!lifecycle_is_initialized(&g_webrtc_lc)) {
415 return SET_ERRNO(ERROR_INIT, "WebRTC library not initialized");
416 }
417 if (config->relay_only && config->turn_count == 0) {
418 return SET_ERRNO(ERROR_INVALID_PARAM, "Relay only requires a TURN server with credentials");
419 }
420 for (size_t i = 0; i < config->turn_count; i++) {
421 const turn_server_t *server = &config->turn_servers[i];
422 const char *username = config->turn_username ? config->turn_username : server->username;
423 const char *credential = config->turn_credential ? config->turn_credential : server->credential;
424 if (!username[0] || !credential[0] || strlen(username) > 127 || strlen(credential) > 127) {
425 return SET_ERRNO(ERROR_INVALID_PARAM, "TURN server requires both username and credential");
426 }
427 if (strncmp(server->url, "turn:", 5) != 0 && strncmp(server->url, "turns:", 6) != 0) {
428 return SET_ERRNO(ERROR_INVALID_PARAM, "TURN server URL must start with turn: or turns:");
429 }
430 }
431
432 // Allocate peer connection wrapper
434 if (!pc) {
435 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate peer connection");
436 }
437
438 pc->config = *config; // Copy config
440 pc->dc = NULL;
441
442 // Build ICE server list for libdatachannel
443 const char **ice_servers = NULL;
444 size_t ice_count = 0;
445 char *turn_urls = NULL;
446
447 if (config->stun_count > 0 || config->turn_count > 0) {
448 ice_count = config->stun_count + config->turn_count;
449 ice_servers = SAFE_MALLOC(ice_count * sizeof(char *), const char **);
450 if (!ice_servers) {
451 SAFE_FREE(pc);
452 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ICE server list");
453 }
454
455 // Add STUN servers
456 for (size_t i = 0; i < config->stun_count; i++) {
457 ice_servers[i] = config->stun_servers[i].host;
458 }
459
460 // Add TURN servers
461 if (config->turn_count > 0) {
462 turn_urls = SAFE_CALLOC(config->turn_count, 1024, char *);
463 if (!turn_urls) {
464 SAFE_FREE(ice_servers);
465 SAFE_FREE(pc);
466 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate authenticated TURN URLs");
467 }
468 }
469 for (size_t i = 0; i < config->turn_count; i++) {
470 const turn_server_t *server = &config->turn_servers[i];
471 char username[128 * 3];
472 char credential[128 * 3];
473 encode_turn_userinfo(config->turn_username ? config->turn_username : server->username, username,
474 sizeof(username));
475 encode_turn_userinfo(config->turn_credential ? config->turn_credential : server->credential, credential,
476 sizeof(credential));
477 const char *address = strchr(server->url, ':') + 1;
478 while (*address == '/')
479 address++;
480 SAFE_SNPRINTF(turn_urls + i * 1024, 1024, "%.*s%s:%s@%s", (int)(strchr(server->url, ':') - server->url + 1),
481 server->url, username, credential, address);
482 ice_servers[config->stun_count + i] = turn_urls + i * 1024;
483 }
484 }
485
486 // Create libdatachannel configuration
487 rtcConfiguration rtc_config;
488 memset(&rtc_config, 0, sizeof(rtc_config));
489 rtc_config.bindAddress = config->bind_address;
490 rtc_config.iceServers = ice_servers;
491 rtc_config.iceServersCount = (int)ice_count;
492 rtc_config.iceTransportPolicy = config->relay_only ? RTC_TRANSPORT_POLICY_RELAY : RTC_TRANSPORT_POLICY_ALL;
493 if (rtc_config.bindAddress && rtc_config.bindAddress[0]) {
494 log_info("Binding WebRTC ICE sockets to %s", rtc_config.bindAddress);
495 }
496
497 // Create peer connection
498 int pc_id = rtcCreatePeerConnection(&rtc_config);
499 SAFE_FREE(turn_urls);
500
501 // Free ICE server list (libdatachannel makes a copy)
502 if (ice_servers) {
503 SAFE_FREE(ice_servers);
504 }
505
506 if (pc_id < 0) {
507 SAFE_FREE(pc);
508 return SET_ERRNO(ERROR_NETWORK, "Failed to create peer connection (rtc error %d)", pc_id);
509 }
510
511 pc->rtc_id = pc_id;
512
513 // Initialize gathering state
516
517 // Set up callbacks
518 rtcSetUserPointer(pc_id, pc);
519 rtcSetStateChangeCallback(pc_id, on_state_change_adapter);
520 rtcSetGatheringStateChangeCallback(pc_id, on_gathering_state_change_adapter);
521 rtcSetLocalDescriptionCallback(pc_id, on_local_description_adapter);
522 rtcSetLocalCandidateCallback(pc_id, on_local_candidate_adapter);
523 rtcSetDataChannelCallback(pc_id, on_datachannel_adapter);
524
525 /* Register peer connection with named registry */
526 char pc_name[64];
527 snprintf(pc_name, sizeof(pc_name), "webrtc_peer_connection:%d", pc_id);
528 NAMED_REGISTER(pc, pc_name, "webrtc_peer_connection_t", "0x%tx", NULL);
529
530 *pc_out = pc;
531 log_debug("Created WebRTC peer connection (id=%d)", pc_id);
532 return ASCIICHAT_OK;
533}
void atomic_store_u64(atomic_t *a, uint64_t value)
Atomically store a uint64_t value.
Definition atomic.c:241
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
#define NAMED_REGISTER(ptr, name, type, fmt, parent_ptr)
Register any pointer with base name, type, format spec, and location (auto-suffix)
@ ERROR_INIT
Definition error_codes.h:61
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
turn_server_t
Definition turn.h:101
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155
const char * turn_username
Optional credential override, including full ACDS usernames.
size_t stun_count
Number of STUN servers.
bool relay_only
Restrict ICE to TURN relay candidates.
turn_server_t * turn_servers
Array of TURN servers.
stun_server_t * stun_servers
Array of STUN servers.
size_t turn_count
Number of TURN servers.
const char * bind_address
Optional local interface address for ICE sockets.
const char * turn_credential
Optional credential override.
WebRTC peer connection state.
atomic_t gathering_state
Current ICE gathering state.
atomic_t gathering_start_time_ms
When gathering started (platform_get_time_ms)
atomic_t state
Current connection state.
webrtc_config_t config
Configuration with callbacks.

References ASCIICHAT_OK, atomic_store_u64(), webrtc_config_t::bind_address, webrtc_peer_connection::config, webrtc_peer_connection::dc, ERROR_INIT, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, webrtc_peer_connection::gathering_start_time_ms, webrtc_peer_connection::gathering_state, lifecycle_is_initialized(), log_debug, log_info, NAMED_REGISTER, webrtc_config_t::relay_only, webrtc_peer_connection::rtc_id, SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, SAFE_SNPRINTF, SET_ERRNO, webrtc_peer_connection::state, webrtc_config_t::stun_count, webrtc_config_t::stun_servers, webrtc_config_t::turn_count, webrtc_config_t::turn_credential, turn_server_t, webrtc_config_t::turn_servers, webrtc_config_t::turn_username, WEBRTC_GATHERING_NEW, and WEBRTC_STATE_NEW.

◆ webrtc_datachannel_destroy()

void webrtc_datachannel_destroy ( webrtc_data_channel_t *  dc)

#include <webrtc.h>

Destroy a DataChannel and free resources.

Parameters
dcData channel to destroy

Frees all resources associated with the DataChannel. Automatically closes the channel if still open. Safe to call with NULL pointer.

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

868 {
869 if (!dc) {
870 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null data channel");
871 return;
872 }
873
874 // Close if still open, then free the wrapper.
876 SAFE_FREE(dc);
877}

References ERROR_INVALID_PARAM, SAFE_FREE, SET_ERRNO, and webrtc_close_datachannel().

◆ webrtc_datachannel_get_buffered_amount()

asciichat_error_t webrtc_datachannel_get_buffered_amount ( webrtc_data_channel_t *  dc,
size_t *  amount 
)

#include <webrtc.h>

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

718 {
719 if (!dc || dc->rtc_id < 0 || !amount)
720 return SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel and amount are required");
721 int result = rtcGetBufferedAmount(dc->rtc_id);
722 if (result < 0)
723 return SET_ERRNO(ERROR_NETWORK, "Failed to query DataChannel buffered amount");
724 *amount = (size_t)result;
725 return ASCIICHAT_OK;
726}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, webrtc_data_channel::rtc_id, and SET_ERRNO.

◆ webrtc_datachannel_get_label()

const char * webrtc_datachannel_get_label ( webrtc_data_channel_t *  dc)

#include <webrtc.h>

Get DataChannel label.

Parameters
dcData channel
Returns
Label string (null-terminated), or NULL on error

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

788 {
789 if (!dc || dc->rtc_id < 0) {
790 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
791 return NULL;
792 }
793
794 static char label[256];
795 int result = rtcGetDataChannelLabel(dc->rtc_id, label, sizeof(label));
796 if (result < 0) {
797 return NULL;
798 }
799
800 return label;
801}

References ERROR_INVALID_PARAM, webrtc_data_channel::rtc_id, and SET_ERRNO.

◆ webrtc_datachannel_get_max_message_size()

asciichat_error_t webrtc_datachannel_get_max_message_size ( webrtc_data_channel_t *  dc,
size_t *  size 
)

#include <webrtc.h>

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

728 {
729 if (!dc || dc->rtc_id < 0 || !size)
730 return SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel and max message size are required");
731 int result = rtcMaxMessageSize(dc->rtc_id);
732 if (result <= 0)
733 return SET_ERRNO(ERROR_NETWORK, "Failed to query DataChannel max message size");
734 *size = (size_t)result;
735 return ASCIICHAT_OK;
736}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, webrtc_data_channel::rtc_id, and SET_ERRNO.

◆ webrtc_datachannel_is_open()

bool webrtc_datachannel_is_open ( webrtc_data_channel_t *  dc)

#include <webrtc.h>

Check if DataChannel is open and ready.

Parameters
dcData channel
Returns
true if open, false otherwise

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

772 {
773 if (!dc) {
774 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
775 return false;
776 }
777 return dc->rtc_id >= 0 && atomic_load_bool(&dc->is_open);
778}
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169

References atomic_load_bool(), ERROR_INVALID_PARAM, webrtc_data_channel::is_open, webrtc_data_channel::rtc_id, and SET_ERRNO.

◆ webrtc_datachannel_send()

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

#include <webrtc.h>

Send data over DataChannel.

Parameters
dcData channel
dataData buffer (binary)
sizeData size in bytes
Returns
ASCIICHAT_OK on success, error code on failure

Sends binary data over the DataChannel. Returns error if channel is not open.

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

738 {
739 if (!dc || dc->rtc_id < 0 || !data) {
740 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
741 }
742
743 if (!atomic_load_bool(&dc->is_open)) {
744 log_error("★ WEBRTC_DATACHANNEL_SEND: Channel not open! size=%zu, dc->rtc_id=%d", size, dc ? dc->rtc_id : -1);
745 return SET_ERRNO(ERROR_NETWORK, "DataChannel not open");
746 }
747
748 // Log packet details at network layer (debug level for normal operation)
749 if (size >= 20) {
750 const uint8_t *pkt = (const uint8_t *)data;
751 log_dev("★ RTCSENDMESSAGE_BEFORE: dc_id=%d, size=%zu, first_20_bytes: %02x%02x%02x%02x %02x%02x%02x%02x "
752 "%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x",
753 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],
754 pkt[11], pkt[12], pkt[13], pkt[14], pkt[15], pkt[16], pkt[17], pkt[18], pkt[19]);
755 } else {
756 log_dev("★ RTCSENDMESSAGE_BEFORE: dc_id=%d, size=%zu (too small to log content)", dc->rtc_id, size);
757 }
758
759 int result = rtcSendMessage(dc->rtc_id, (const char *)data, (int)size);
760
761 log_dev("★ RTCSENDMESSAGE_AFTER: dc_id=%d, rtcSendMessage returned %d for size=%zu", dc->rtc_id, result, size);
762
763 if (result < 0) {
764 log_error("★ WEBRTC_DATACHANNEL_SEND: FAILED with error code %d", result);
765 return SET_ERRNO(ERROR_NETWORK, "Failed to send data (rtc error %d)", result);
766 }
767
768 log_dev("★ WEBRTC_DATACHANNEL_SEND: SUCCESS - sent %zu bytes", size);
769 return ASCIICHAT_OK;
770}
unsigned char uint8_t
Definition common.h:56
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587

References ASCIICHAT_OK, atomic_load_bool(), ERROR_INVALID_PARAM, ERROR_NETWORK, webrtc_data_channel::is_open, log_dev, log_error, webrtc_data_channel::rtc_id, and SET_ERRNO.

◆ webrtc_datachannel_set_callbacks()

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

#include <webrtc.h>

Set DataChannel callbacks.

Parameters
dcData channel
callbacksCallback structure
Returns
ASCIICHAT_OK on success, error code on failure

Registers callbacks for DataChannel events. Any NULL callback pointer is ignored.

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

827 {
828 if (!dc) {
829 return SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
830 }
831
832 if (!callbacks) {
833 return SET_ERRNO(ERROR_INVALID_PARAM, "Callbacks struct is NULL");
834 }
835
836 // Store user callbacks in the data channel struct
837 dc->user_on_open = callbacks->on_open;
838 dc->user_on_close = callbacks->on_close;
839 dc->user_on_error = callbacks->on_error;
840 dc->user_on_message = callbacks->on_message;
841 dc->user_data = callbacks->user_data;
842
843 // Register adapter functions with libdatachannel (adapters use correct signature)
844 // The adapters will look up our stored callbacks and invoke them with proper types
845 if (callbacks->on_open) {
846 rtcSetOpenCallback(dc->rtc_id, on_datachannel_open_adapter);
847 }
848
849 if (callbacks->on_close) {
850 rtcSetClosedCallback(dc->rtc_id, on_datachannel_closed_adapter);
851 }
852
853 if (callbacks->on_error) {
854 rtcSetErrorCallback(dc->rtc_id, on_datachannel_error_adapter);
855 }
856
857 if (callbacks->on_message) {
858 rtcSetMessageCallback(dc->rtc_id, on_datachannel_message_adapter);
859 }
860
861 // Set user pointer to the data channel so adapters can retrieve it
862 rtcSetUserPointer(dc->rtc_id, dc);
863
864 log_debug("Set DataChannel callbacks (dc_id=%d)", dc->rtc_id);
865 return ASCIICHAT_OK;
866}
void(* user_on_message)(webrtc_data_channel_t *dc, const uint8_t *data, size_t len, void *user_data)
Message callback.
void(* user_on_close)(webrtc_data_channel_t *dc, void *user_data)
Close callback.
void(* user_on_open)(webrtc_data_channel_t *dc, void *user_data)
Open callback.
void * user_data
User data for per-channel callbacks.
void(* user_on_error)(webrtc_data_channel_t *dc, const char *error, void *user_data)
Error callback.
void(* on_close)(webrtc_data_channel_t *dc, void *user_data)
Channel closed.
void(* on_error)(webrtc_data_channel_t *dc, const char *error, void *user_data)
Error occurred.
void(* on_open)(webrtc_data_channel_t *dc, void *user_data)
Channel opened.
void(* on_message)(webrtc_data_channel_t *dc, const uint8_t *data, size_t len, void *user_data)
Message received.

References ASCIICHAT_OK, ERROR_INVALID_PARAM, log_debug, webrtc_datachannel_callbacks_t::on_close, webrtc_datachannel_callbacks_t::on_error, webrtc_datachannel_callbacks_t::on_message, webrtc_datachannel_callbacks_t::on_open, webrtc_data_channel::rtc_id, SET_ERRNO, webrtc_datachannel_callbacks_t::user_data, webrtc_data_channel::user_data, webrtc_data_channel::user_on_close, webrtc_data_channel::user_on_error, webrtc_data_channel::user_on_message, and webrtc_data_channel::user_on_open.

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 
)

#include <webrtc.h>

Set DataChannel open state (internal use)

Parameters
dcData channel
is_openOpen state to set

Internal function for transport layer to manually set open state when callbacks are replaced after the DataChannel is already open.

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

780 {
781 if (!dc) {
782 SET_ERRNO(ERROR_INVALID_PARAM, "DataChannel is NULL");
783 return;
784 }
785 atomic_store_bool(&dc->is_open, is_open);
786}

References atomic_store_bool(), ERROR_INVALID_PARAM, webrtc_data_channel::is_open, and SET_ERRNO.

Referenced by acip_webrtc_transport_create().

◆ webrtc_destroy()

void webrtc_destroy ( void  )

#include <webrtc.h>

Cleanup WebRTC library resources.

Should be called at program exit after all connections are closed. Thread-safe and idempotent.

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

381 {
382 webrtc_release();
383}

◆ webrtc_get_gathering_state()

webrtc_gathering_state_t webrtc_get_gathering_state ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Get current ICE gathering state.

Parameters
pcPeer connection
Returns
Current gathering state

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

562 {
563 if (!pc) {
564 log_warn("Peer connection is NULL");
566 }
568}
uint64_t atomic_load_u64(atomic_t *a)
Atomically load a uint64_t value.
Definition atomic.c:233
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574

References atomic_load_u64(), webrtc_peer_connection::gathering_state, log_warn, and WEBRTC_GATHERING_NEW.

Referenced by webrtc_peer_manager_check_gathering_timeouts().

◆ webrtc_get_rtc_id()

int webrtc_get_rtc_id ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

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 915 of file lib/network/webrtc/webrtc.c.

915 {
916 if (!pc) {
917 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null peer connectiont");
918 return -1;
919 }
920 return pc->rtc_id;
921}

References ERROR_INVALID_PARAM, webrtc_peer_connection::rtc_id, and SET_ERRNO.

Referenced by ice_get_selected_pair_impl().

◆ webrtc_get_state()

webrtc_state_t webrtc_get_state ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Get current connection state.

Parameters
pcPeer connection
Returns
Current state

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

554 {
555 if (!pc) {
556 log_warn("Peer connection is NULL");
557 return WEBRTC_STATE_CLOSED;
558 }
560}

References atomic_load_u64(), log_warn, webrtc_peer_connection::state, and WEBRTC_STATE_CLOSED.

Referenced by ice_is_connected(), webrtc_peer_manager_check_gathering_timeouts(), webrtc_peer_manager_get_state(), and webrtc_peer_manager_handle_sdp().

◆ webrtc_get_user_data()

void * webrtc_get_user_data ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Get user data pointer from connection.

Parameters
pcPeer connection
Returns
User data pointer (from webrtc_config_t)

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

597 {
598 if (!pc) {
599 log_warn("Peer connection is NULL");
600 return NULL;
601 }
602 return pc->config.user_data;
603}
void * user_data
Passed to all callbacks.

References webrtc_peer_connection::config, log_warn, and webrtc_config_t::user_data.

◆ webrtc_init()

asciichat_error_t webrtc_init ( void  )

#include <webrtc.h>

Initialize WebRTC library (libdatachannel)

Returns
ASCIICHAT_OK on success, error code on failure

Must be called once before creating any peer connections. Thread-safe and idempotent (safe to call multiple times).

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

377 {
378 return webrtc_ensure_initialized();
379}

◆ webrtc_is_gathering_timed_out()

bool webrtc_is_gathering_timed_out ( webrtc_peer_connection_t *  pc,
uint32_t  timeout_ms 
)

#include <webrtc.h>

Check if ICE gathering has timed out.

Parameters
pcPeer connection
timeout_msTimeout in milliseconds
Returns
true if gathering is in progress and has exceeded timeout, false otherwise

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

570 {
571 if (!pc) {
572 return false;
573 }
574
575 // Only check timeout if we're actively gathering
577 return false;
578 }
579
580 // If gathering hasn't started yet (start_time == 0), no timeout
581 uint64_t gathering_start_time_ms = atomic_load_u64(&pc->gathering_start_time_ms);
582 if (gathering_start_time_ms == 0) {
583 return false;
584 }
585
586 // Check if elapsed time exceeds timeout
587 uint64_t current_time_ms = platform_get_monotonic_time_us() / 1000;
588 uint64_t elapsed_ms = current_time_ms - gathering_start_time_ms;
589
590 if (elapsed_ms > timeout_ms) {
591 return true;
592 }
593
594 return false;
595}
unsigned long long uint64_t
Definition common.h:59
uint64_t platform_get_monotonic_time_us(void)
Get monotonic time in microseconds.

References atomic_load_u64(), webrtc_peer_connection::gathering_start_time_ms, webrtc_peer_connection::gathering_state, platform_get_monotonic_time_us(), and WEBRTC_GATHERING_GATHERING.

Referenced by webrtc_peer_manager_check_gathering_timeouts().

◆ webrtc_peer_connection_close()

void webrtc_peer_connection_close ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Close a peer connection.

Parameters
pcPeer connection to close

Closes the peer connection gracefully. Safe to call with NULL pointer.

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

883 {
884 if (!pc) {
885 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null peer connectiont");
886 return;
887 }
888
889 rtcClose(pc->rtc_id);
890 log_debug("Closed peer connection (pc_id=%d)", pc->rtc_id);
891}

References ERROR_INVALID_PARAM, log_debug, webrtc_peer_connection::rtc_id, and SET_ERRNO.

◆ webrtc_peer_connection_destroy()

void webrtc_peer_connection_destroy ( webrtc_peer_connection_t *  pc)

#include <webrtc.h>

Destroy a peer connection and free resources.

Parameters
pcPeer connection to destroy

Frees all resources associated with the peer connection. Automatically closes the connection if still open. Safe to call with NULL pointer.

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

893 {
894 if (!pc) {
895 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid null peer connectiont");
896 return;
897 }
898
899 // Close and delete peer connection
900 rtcDeletePeerConnection(pc->rtc_id);
901 log_debug("Destroyed peer connection (pc_id=%d)", pc->rtc_id);
902
903 SAFE_FREE(pc);
904}

References ERROR_INVALID_PARAM, log_debug, webrtc_peer_connection::rtc_id, SAFE_FREE, and SET_ERRNO.

◆ webrtc_set_remote_description()

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

#include <webrtc.h>

Set remote SDP offer/answer.

Parameters
pcPeer connection
sdpRemote SDP string (null-terminated)
typeSDP type ("offer" or "answer")
Returns
ASCIICHAT_OK on success, error code on failure

Sets the remote peer's SDP. If this is an offer, triggers automatic answer generation via the local description callback.

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

624 {
625 if (!pc || !sdp || !type) {
626 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
627 }
628
629 int result = rtcSetRemoteDescription(pc->rtc_id, sdp, type);
630 if (result != RTC_ERR_SUCCESS) {
631 return SET_ERRNO(ERROR_NETWORK, "Failed to set remote SDP (rtc error %d)", result);
632 }
633
634 const char *dcmap = strstr(sdp, "a=dcmap:");
635 if (strcmp(type, "offer") == 0) {
636 log_debug("Remote WebRTC offer DataChannel map: %s", dcmap ? dcmap : "(none)");
637 }
638 if (strcmp(type, "offer") == 0 && strstr(sdp, "\na=dcmap:0 label=\"acip\"") && !pc->dc) {
639 rtcDataChannelInit init = {0};
640 init.negotiated = true;
641 init.manualStream = true;
642 init.stream = 0;
643 int dc_id = rtcCreateDataChannelEx(pc->rtc_id, "acip", &init);
644 if (dc_id < 0)
645 return SET_ERRNO(ERROR_NETWORK, "Failed to create negotiated ACIP channel");
646 on_datachannel_adapter(pc->rtc_id, dc_id, pc);
647 }
648
649 log_debug("Set remote SDP description (pc_id=%d, type=%s)", pc->rtc_id, type);
650 return ASCIICHAT_OK;
651}

References ASCIICHAT_OK, webrtc_peer_connection::dc, ERROR_INVALID_PARAM, ERROR_NETWORK, log_debug, webrtc_peer_connection::rtc_id, and SET_ERRNO.

Referenced by webrtc_peer_manager_handle_sdp().