ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
participant.c
Go to the documentation of this file.
1
16#include "session/participant.h"
17#include <ascii-chat/common.h>
25#include <ascii-chat/log/log.h>
26#include "session/capture.h"
27#include "session/audio.h"
32
33#include <string.h>
34#include <stdio.h>
35
36/* ============================================================================
37 * Session Participant Context Structure
38 * ============================================================================ */
39
118
119/* ============================================================================
120 * Session Participant Lifecycle Functions
121 * ============================================================================ */
122
124 log_info("session_participant_create: START - config=%p", config);
125
126 if (!config) {
127 log_error("session_participant_create: config is NULL");
128 SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_create: NULL config");
129 return NULL;
130 }
131
132 // Allocate participant
134 log_info("session_participant_create: allocated p=%p", p);
135
136 // Copy configuration
137 if (config->address) {
138 SAFE_STRNCPY(p->address, config->address, sizeof(p->address));
139 } else {
140 SAFE_STRNCPY(p->address, "127.0.0.1", sizeof(p->address));
141 }
142
143 p->port = config->port > 0 ? config->port : OPT_PORT_INT_DEFAULT;
145
146 log_info("session_participant_create: address=%s, port=%d", p->address, p->port);
147
148 if (config->password) {
149 SAFE_STRNCPY(p->password, config->password, sizeof(p->password));
150 }
151
152 if (config->server_key) {
153 SAFE_STRNCPY(p->server_key, config->server_key, sizeof(p->server_key));
154 }
155
156 p->enable_audio = config->enable_audio;
157 p->enable_video = config->enable_video;
158 p->callbacks = config->callbacks;
159 p->user_data = config->user_data;
160
161 // Initialize socket to invalid
163 p->transport = NULL; // No alternative transport initially
164 p->connected = false;
165 p->client_id = 0;
166 p->video_active = false;
167 p->audio_active = false;
168
169 // Initialize settings
171
172 p->initialized = true;
173 log_info("session_participant_create: DONE - returning p=%p (initialized=%d, connected=%d)", p, p->initialized,
174 p->connected);
175 return p;
176}
177
179 if (!p) {
180 return;
181 }
182
183 // Disconnect if connected
184 if (p->connected) {
186 }
187
188 // Stop capture threads if running
189 if (p->video_capture_running) {
191 }
192 if (p->audio_capture_running) {
194 }
195
196 // Clean up media capture contexts
197 if (p->audio_capture) {
199 p->audio_capture = NULL;
200 }
201 if (p->video_capture) {
203 p->video_capture = NULL;
204 }
205 if (p->opus_encoder) {
207 p->opus_encoder = NULL;
208 }
209
210 if (p->transport) {
212 p->transport = NULL;
213 }
214
215 // Close socket if open
216 if (p->socket != INVALID_SOCKET_VALUE) {
219 }
220
221 // Clear sensitive data
222 memset(p->password, 0, sizeof(p->password));
223 memset(p->server_key, 0, sizeof(p->server_key));
224
225 p->initialized = false;
226 SAFE_FREE(p);
227}
228
229/* ============================================================================
230 * Session Participant Connection Functions
231 * ============================================================================ */
232
237static socket_t connect_to_server(const char *address, int port) {
238 struct addrinfo hints, *result = NULL, *rp = NULL;
239 char port_str[16];
240
241 memset(&hints, 0, sizeof(hints));
242 hints.ai_family = AF_INET; // IPv4
243 hints.ai_socktype = SOCK_STREAM;
244 hints.ai_protocol = IPPROTO_TCP;
245
246 safe_snprintf(port_str, sizeof(port_str), "%d", port);
247
248 int s = getaddrinfo(address, port_str, &hints, &result);
249 if (s != 0) {
250 SET_ERRNO(ERROR_NETWORK, "getaddrinfo failed: %s", gai_strerror(s));
252 }
253
255 for (rp = result; rp != NULL; rp = rp->ai_next) {
256 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
257 if (sock == INVALID_SOCKET_VALUE) {
258 continue;
259 }
260
261 if (connect(sock, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
262 break; // Success
263 }
264
265 socket_close(sock);
267 }
268
269 freeaddrinfo(result);
270
271 if (sock == INVALID_SOCKET_VALUE) {
272 SET_ERRNO_SYS(ERROR_NETWORK_CONNECT, "Failed to connect to %s:%d", address, port);
274 }
275
276 return sock;
277}
278
280 log_debug("session_participant_connect: enter - p=%p", p);
281
282 if (!p || !p->initialized) {
283 log_debug("session_participant_connect: invalid param or not initialized");
284 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_connect: invalid participant");
285 }
286
287 log_debug("session_participant_connect: p->initialized=%d, p->connected=%d", p->initialized, p->connected);
288
289 if (p->connected) {
290 log_debug("session_participant_connect: already connected, returning OK");
291 return ASCIICHAT_OK; // Already connected
292 }
293
294 // Create TCP connection to server
295 log_debug("session_participant_connect: calling connect_to_server(%s, %d)", p->address, p->port);
296 p->socket = connect_to_server(p->address, p->port);
297 log_debug("session_participant_connect: connect_to_server returned socket=%d (INVALID=%d)", p->socket,
299
300 if (p->socket == INVALID_SOCKET_VALUE) {
301 log_error("Failed to connect to server at %s:%d", p->address, p->port);
302 if (p->callbacks.on_error) {
303 p->callbacks.on_error(p, ERROR_NETWORK_CONNECT, "Failed to connect to server", p->user_data);
304 }
305 return GET_ERRNO();
306 }
307
308 log_debug("session_participant_connect: setting p->connected=true");
309 p->connected = true;
310 p->client_id = 0; // Will be assigned by server
311
312 log_info("Connected to server at %s:%d (socket=%d)", p->address, p->port, p->socket);
313 log_debug("session_participant_connect: p->connected=%d after setting", p->connected);
314
315 // Invoke callback
316 if (p->callbacks.on_connected) {
318 }
319
320 log_debug("session_participant_connect: returning ASCIICHAT_OK");
321 return ASCIICHAT_OK;
322}
323
325 if (!p || !p->initialized) {
326 return;
327 }
328
329 if (!p->connected) {
330 return;
331 }
332
333 // Stop media streams
334 if (p->video_active) {
336 }
337 if (p->audio_active) {
339 }
340
341 // Close socket
342 if (p->socket != INVALID_SOCKET_VALUE) {
345 }
346
347 p->connected = false;
348 p->client_id = 0;
349
350 // Invoke callback
351 if (p->callbacks.on_disconnected) {
353 }
354}
355
357 if (!p || !p->initialized) {
358 return false;
359 }
360 return p->connected;
361}
362
364 if (!p || !p->initialized || !p->connected) {
365 return 0;
366 }
367 return p->client_id;
368}
369
371 if (!p || !p->initialized) {
373 }
374 return p->socket;
375}
376
377/* ============================================================================
378 * Session Participant Media Control Functions
379 * ============================================================================ */
380
382 if (!p || !p->initialized) {
383 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_start_video: invalid participant");
384 }
385
386 if (!p->connected) {
387 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_video: not connected");
388 }
389
390 if (p->video_active) {
391 return ASCIICHAT_OK; // Already active
392 }
393
394 if (!p->enable_video) {
395 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_video: video not enabled");
396 }
397
398 // TODO: Start webcam capture thread using session_capture_ctx_t
399
400 p->video_active = true;
401 return ASCIICHAT_OK;
402}
403
405 if (!p || !p->initialized) {
406 return;
407 }
408
409 if (!p->video_active) {
410 return;
411 }
412
413 // TODO: Stop webcam capture thread
414
415 p->video_active = false;
416}
417
419 if (!p || !p->initialized) {
420 return false;
421 }
422 return p->video_active;
423}
424
426 if (!p || !p->initialized) {
427 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_start_audio: invalid participant");
428 }
429
430 if (!p->connected) {
431 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_audio: not connected");
432 }
433
434 if (p->audio_active) {
435 return ASCIICHAT_OK; // Already active
436 }
437
438 if (!p->enable_audio) {
439 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_audio: audio not enabled");
440 }
441
442 // TODO: Start audio using session_audio_ctx_t
443
444 p->audio_active = true;
445 return ASCIICHAT_OK;
446}
447
449 if (!p || !p->initialized) {
450 return;
451 }
452
453 if (!p->audio_active) {
454 return;
455 }
456
457 // TODO: Stop audio
458
459 p->audio_active = false;
460}
461
463 if (!p || !p->initialized) {
464 return false;
465 }
466 return p->audio_active;
467}
468
469/* ============================================================================
470 * Session Participant Settings Functions
471 * ============================================================================ */
472
474 if (!p || !p->initialized || !settings) {
475 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_get_settings: invalid parameter");
476 }
477
478 memcpy(settings, &p->settings, sizeof(session_settings_t));
479 return ASCIICHAT_OK;
480}
481
483 if (!p || !p->initialized || !settings) {
484 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_request_settings: invalid parameter");
485 }
486
487 if (!p->connected) {
488 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_request_settings: not connected");
489 }
490
491 // TODO: Send settings update request to server
492
493 return SET_ERRNO(ERROR_NOT_SUPPORTED, "session_participant_request_settings: not implemented yet");
494}
495
496/* ============================================================================
497 * Session Participant Media Capture Threads
498 * ============================================================================ */
499
507static void *participant_video_capture_thread(void *arg) {
509 if (!p || !p->video_capture) {
510 SET_ERRNO(ERROR_INVALID_PARAM, "participant_video_capture_thread: invalid participant or video_capture context");
511 return NULL;
512 }
513
514 log_info("Video capture thread started");
515
516 while (p->video_capture_running && p->connected) {
517 // Capture frame from media source (webcam, file, test pattern)
519 if (!raw_frame) {
520 APP_CALLBACK_VOID(platform_pump_events);
522 continue;
523 }
524
525 // Process for network transmission (resize to bandwidth-optimal dimensions)
527 if (processed) {
528 // Send to host via IMAGE_FRAME packet
529 asciichat_error_t err = send_image_frame_packet(p->socket, (const void *)processed->pixels,
530 (uint16_t)processed->w, (uint16_t)processed->h, 0);
531 if (err != ASCIICHAT_OK) {
532 log_warn_every(5 * US_PER_SEC_INT, "Failed to send video frame: %d", err);
533 }
534 image_destroy(processed);
535 }
536
537 // Sleep to maintain target frame rate (adaptive based on source)
539 }
540
541 log_info("Video capture thread stopped");
542 return NULL;
543}
544
552static void *participant_audio_capture_thread(void *arg) {
554 if (!p || !p->audio_capture || !p->opus_encoder) {
556 "participant_audio_capture_thread: invalid participant, audio_capture, or opus_encoder");
557 return NULL;
558 }
559
560 log_info("Audio capture thread started");
561
562 float sample_buffer[960]; // 20ms @ 48kHz
563 uint8_t opus_buffer[1000];
564
565 while (p->audio_capture_running && p->connected) {
566 // Read microphone samples (20ms chunk)
567 size_t samples_read = session_audio_read_captured(p->audio_capture, sample_buffer, 960);
568 if (samples_read <= 0) {
569 APP_CALLBACK_VOID(platform_pump_events);
571 continue;
572 }
573
574 // Encode to Opus (lossy compression for bandwidth efficiency)
575 size_t opus_len =
576 opus_codec_encode(p->opus_encoder, sample_buffer, (int)samples_read, opus_buffer, sizeof(opus_buffer));
577 if (opus_len > 0) {
578 uint16_t frame_sizes[1] = {(uint16_t)opus_len};
580 av_send_audio_opus_batch(p->socket, opus_buffer, opus_len, frame_sizes, 48000, 20, 1, NULL);
581 if (err != ASCIICHAT_OK) {
582 log_warn_every(5 * US_PER_SEC_INT, "Failed to send audio packet: %d", err);
583 }
584 }
585 }
586
587 log_info("Audio capture thread stopped");
588 return NULL;
589}
590
591/* ============================================================================
592 * Session Participant Media Capture Public API
593 * ============================================================================ */
594
596 if (!p || !p->initialized) {
597 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_start_video_capture: invalid participant");
598 }
599
600 if (!p->connected) {
601 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_video_capture: not connected");
602 }
603
604 if (!p->enable_video) {
605 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_video_capture: video not enabled");
606 }
607
608 if (p->video_capture_running) {
609 return ASCIICHAT_OK; // Already running
610 }
611
612 // Create video capture context if not already created
613 if (!p->video_capture) {
614 session_capture_config_t config = {
616 .path = "0", // Default device
617 .target_fps = 60,
618 .resize_for_network = true, // Optimize for bandwidth
619 };
620 const char *file = GET_OPTION(media_file);
621 const char *url = GET_OPTION(media_url);
622 char webcam_index[32];
623 safe_snprintf(webcam_index, sizeof(webcam_index), "%u", GET_OPTION(webcam_index));
624 config.path = webcam_index;
625 if (url && url[0]) {
626 config.type = MEDIA_SOURCE_FILE;
627 config.path = url;
628 } else if (file && file[0]) {
629 config.type = strcmp(file, "-") == 0 ? MEDIA_SOURCE_STDIN : MEDIA_SOURCE_FILE;
630 config.path = config.type == MEDIA_SOURCE_STDIN ? NULL : file;
631 }
632 config.loop = GET_OPTION(media_loop);
634 if (!p->video_capture) {
635 return SET_ERRNO(ERROR_INVALID_STATE, "Failed to create video capture context");
636 }
637 }
638
639 // Spawn video capture thread (media source is ready in ctx)
640 p->video_capture_running = true;
641 if (asciichat_thread_create(&p->video_capture_thread, "video_capture", participant_video_capture_thread, p) != 0) {
642 log_error("Failed to spawn video capture thread");
643 p->video_capture_running = false;
644 return SET_ERRNO(ERROR_THREAD, "Failed to spawn video capture thread");
645 }
646
647 log_info("Video capture started");
648 return ASCIICHAT_OK;
649}
650
652 if (!p || !p->initialized) {
653 return;
654 }
655
656 if (!p->video_capture_running) {
657 return;
658 }
659
660 // Signal thread to stop
661 p->video_capture_running = false;
662
663 // Wait for thread to complete
665
666 log_info("Video capture stopped");
667}
668
670 if (!p || !p->initialized) {
671 return SET_ERRNO(ERROR_INVALID_PARAM, "session_participant_start_audio_capture: invalid participant");
672 }
673
674 if (!p->connected) {
675 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_audio_capture: not connected");
676 }
677
678 if (!p->enable_audio) {
679 return SET_ERRNO(ERROR_INVALID_STATE, "session_participant_start_audio_capture: audio not enabled");
680 }
681
682 if (p->audio_capture_running) {
683 return ASCIICHAT_OK; // Already running
684 }
685
686 // Create audio capture context if not already created
687 if (!p->audio_capture) {
688 p->audio_capture = session_audio_create(false); // false = participant mode (no mixing)
689 if (!p->audio_capture) {
690 return SET_ERRNO(ERROR_INVALID_STATE, "Failed to create audio capture context");
691 }
692 }
693
695
696 // Start audio capture and playback
698 if (err != ASCIICHAT_OK) {
699 return SET_ERRNO(err, "Failed to start audio duplex");
700 }
701
702 // Create Opus encoder for audio compression (48kHz, VOIP mode, 24 kbps)
703 if (!p->opus_encoder) {
705 if (!p->opus_encoder) {
707 return SET_ERRNO(ERROR_INVALID_STATE, "Failed to create Opus encoder");
708 }
709 }
710
711 // Spawn audio capture thread
712 p->audio_capture_running = true;
713 if (asciichat_thread_create(&p->audio_capture_thread, "audio_capture", participant_audio_capture_thread, p) != 0) {
714 log_error("Failed to spawn audio capture thread");
715 p->audio_capture_running = false;
717 return SET_ERRNO(ERROR_THREAD, "Failed to spawn audio capture thread");
718 }
719
720 log_info("Audio capture started");
721 return ASCIICHAT_OK;
722}
723
725 if (!p || !p->initialized) {
726 return;
727 }
728
729 if (!p->audio_capture_running) {
730 return;
731 }
732
733 // Signal thread to stop
734 p->audio_capture_running = false;
735
736 // Wait for thread to complete
738
739 // Stop audio streams
740 if (p->audio_capture) {
742 }
743
744 log_info("Audio capture stopped");
745}
746
747/* ============================================================================
748 * Session Participant Transport Functions (WebRTC Integration)
749 * ============================================================================ */
750
752 if (!p || !p->initialized) {
753 return SET_ERRNO(ERROR_INVALID_PARAM, "Participant is NULL or not initialized");
754 }
755
756 log_info("session_participant_set_transport: Setting transport=%p (was=%p)", transport, p->transport);
757
758 p->transport = transport;
759
760 if (transport) {
761 p->connected = acip_transport_is_connected(transport);
762 log_info("WebRTC transport now active for participant");
763 } else {
764 log_info("WebRTC transport cleared, reverting to socket");
765 }
766
767 return ASCIICHAT_OK;
768}
769
771 if (!p || !p->initialized) {
772 return NULL;
773 }
774
775 return p->transport;
776}
777
779 if (!p) {
780 return false;
781 }
782
783 return p->transport != NULL;
784}
Application-level callbacks for library code.
#define APP_CALLBACK_VOID(callback_name)
โš ๏ธโ€ผ๏ธ Comprehensive thread-local error context system for ascii-chat
Common buffer size definitions.
๐Ÿ“น Unified media capture abstraction for session-based video sources
โš™๏ธ Common definitions, error codes, macros, and types shared throughout the application
opus_codec_t * opus_codec_create_encoder(opus_application_t application, int sample_rate, int bitrate)
Create an Opus encoder.
Definition opus.c:19
void opus_codec_destroy(opus_codec_t *codec)
Destroy an Opus codec instance.
Definition opus.c:230
size_t opus_codec_encode(opus_codec_t *codec, const float *samples, int num_samples, uint8_t *out_data, size_t out_size)
Encode audio frame with Opus.
Definition opus.c:112
@ OPUS_APPLICATION_VOIP
Voice over IP (optimized for speech)
Definition opus.h:77
#define BUFFER_SIZE_SMALL
Small buffer size (256 bytes)
#define BUFFER_SIZE_MEDIUM
Medium buffer size (512 bytes)
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
#define GET_ERRNO()
Get current error code (0 if no error)
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_INVALID_STATE
@ ERROR_NETWORK_CONNECT
Definition error_codes.h:79
@ ERROR_NETWORK
Definition error_codes.h:77
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
@ ERROR_THREAD
@ ERROR_NOT_SUPPORTED
Definition error_codes.h:74
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
@ MEDIA_SOURCE_STDIN
Piped or redirected input.
Definition source.h:84
@ MEDIA_SOURCE_WEBCAM
Hardware webcam device.
Definition source.h:82
@ MEDIA_SOURCE_FILE
Media file (video/audio)
Definition source.h:83
#define US_PER_SEC_INT
Definition time.h:161
asciichat_error_t send_image_frame_packet(socket_t sockfd, const void *image_data, uint16_t width, uint16_t height, uint8_t format)
Send image frame packet.
Definition packet.c:1230
asciichat_error_t av_send_audio_opus_batch(socket_t sockfd, const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int sample_rate, int frame_duration, int frame_count, crypto_context_t *crypto_ctx)
Send Opus-encoded audio batch packet with encryption support.
Definition packet.c:1175
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
#define OPT_PORT_INT_DEFAULT
Default TCP port for client/server communication (integer)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278
int socket_close(socket_t sock)
Close a socket.
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
void session_participant_stop_video(session_participant_t *p)
Stop video capture and streaming.
bool session_participant_is_audio_active(session_participant_t *p)
Check if audio is streaming.
size_t session_audio_read_captured(session_audio_ctx_t *ctx, float *buffer, size_t num_samples)
Read captured audio samples.
image_t * session_capture_process_for_transmission(session_capture_ctx_t *ctx, image_t *frame)
Process a frame for network transmission (resize if needed)
void session_audio_destroy(session_audio_ctx_t *ctx)
Destroy session audio context and free resources.
asciichat_error_t session_participant_connect(session_participant_t *p)
Connect to session server.
bool session_participant_is_video_active(session_participant_t *p)
Check if video is streaming.
void session_participant_stop_audio(session_participant_t *p)
Stop audio capture and streaming.
uint32_t session_participant_get_client_id(session_participant_t *p)
Get assigned client ID.
bool session_participant_has_transport(session_participant_t *p)
Check if participant has an active alternative transport.
acip_transport_t * session_participant_get_transport(session_participant_t *p)
Get the current transport for the participant.
asciichat_error_t session_participant_start_video_capture(session_participant_t *p)
Start video capture and transmission to host.
asciichat_error_t session_participant_request_settings(session_participant_t *p, const session_settings_t *settings)
Request session settings update (if permitted)
asciichat_error_t session_participant_start_video(session_participant_t *p)
Start video capture and streaming.
asciichat_error_t session_audio_start_duplex(session_audio_ctx_t *ctx)
Start full-duplex audio (simultaneous capture and playback)
void session_audio_stop(session_audio_ctx_t *ctx)
Stop all audio streams.
void session_participant_stop_video_capture(session_participant_t *p)
Stop video capture and transmission.
void session_participant_destroy(session_participant_t *p)
Destroy session participant and free resources.
image_t * session_capture_read_frame(session_capture_ctx_t *ctx)
Read the next video frame from the capture source.
socket_t session_participant_get_socket(session_participant_t *p)
Get the socket from a participant context.
session_participant_t * session_participant_create(const session_participant_config_t *config)
Create a new session participant.
session_audio_ctx_t * session_audio_create(bool is_host)
Create a new session audio context.
void session_participant_stop_audio_capture(session_participant_t *p)
Stop audio capture and transmission.
asciichat_error_t session_participant_start_audio_capture(session_participant_t *p)
Start audio capture and transmission to host.
session_capture_ctx_t * session_capture_create(const session_capture_config_t *config)
Legacy function - creates either mirror or network capture based on options.
void * session_capture_get_media_source(session_capture_ctx_t *ctx)
Get the underlying media source from capture context.
bool session_participant_is_connected(session_participant_t *p)
Check if participant is connected.
asciichat_error_t session_participant_set_transport(session_participant_t *p, acip_transport_t *transport)
Set an alternative transport for the participant.
void session_capture_sleep_for_fps(session_capture_ctx_t *ctx)
Sleep to maintain target frame rate.
void session_participant_disconnect(session_participant_t *p)
Disconnect from session server.
void session_capture_destroy(session_capture_ctx_t *ctx)
Destroy session capture context and free resources.
asciichat_error_t session_participant_get_settings(session_participant_t *p, session_settings_t *settings)
Get current session settings.
asciichat_error_t session_participant_start_audio(session_participant_t *p)
Start audio capture and streaming.
void session_settings_init(session_settings_t *settings)
Initialize session settings to defaults.
Definition settings.c:26
void image_destroy(image_t *p)
Destroy an image allocated with image_new()
int socket_t
โš™๏ธ Unified options parsing system for ascii-chat with builder pattern and lock-free access
๐Ÿงต Cross-platform thread interface for ascii-chat
#define asciichat_thread_create(thread_ptr, attr, start_routine, arg)
void * asciichat_thread_t
#define asciichat_thread_join(thread, timeout_ms)
๐Ÿ“ Logging API with multiple log levels and terminal output control
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.
Definition log/log.h:708
Opus audio codec wrapper for real-time encoding/decoding.
struct session_participant session_participant_t
Internal session participant structure.
๐Ÿ‘ค Client-side session participation abstraction
Cross-platform network headers and socket definitions.
Cross-platform socket interface for ascii-chat.
void session_audio_set_capture_source(session_audio_ctx_t *ctx, void *source)
๐Ÿ”Š Session-level audio coordination wrapper
Transport instance structure.
Definition transport.h:214
Image structure.
int w
Image width in pixels (must be > 0)
int h
Image height in pixels (must be > 0)
rgb_pixel_t * pixels
Pixel data array (width * height RGB pixels, row-major order)
Opus codec context for encoding or decoding.
Definition opus.h:95
Internal session audio context structure.
Configuration for session capture context.
media_source_type_t type
Media source type (WEBCAM, FILE, STDIN, TEST)
const char * path
Device index (for webcam) or file path (for file sources)
bool loop
Enable loop playback for file sources.
Internal session capture context structure.
Callback function prototypes for session participant events.
Definition participant.h:77
void(* on_disconnected)(session_participant_t *p, void *user_data)
Called when disconnected from session.
Definition participant.h:91
void(* on_connected)(session_participant_t *p, uint32_t client_id, void *user_data)
Called when successfully connected to session.
Definition participant.h:84
void(* on_error)(session_participant_t *p, asciichat_error_t error, const char *message, void *user_data)
Called when an error occurs.
Configuration for session participant.
session_participant_callbacks_t callbacks
Event callbacks.
const char * address
Server address to connect to.
bool enable_video
Enable video capture and streaming.
const char * password
Password for server authentication (optional)
bool encryption_enabled
Enable encryption (default: true)
void * user_data
User-provided context pointer passed to callbacks.
const char * server_key
Expected server key for verification (optional)
bool enable_audio
Enable audio streaming.
int port
Server port (default: 27224)
Internal session participant structure.
Definition participant.c:45
session_settings_t settings
Current session settings.
Definition participant.c:92
bool connected
Currently connected.
Definition participant.c:80
struct acip_transport * transport
Alternative transport (WebRTC, WebSocket, etc.) - NULL if using socket only.
Definition participant.c:77
asciichat_thread_t video_capture_thread
Video capture thread handle.
bool audio_active
Audio streaming active.
Definition participant.c:89
opus_codec_t * opus_encoder
Opus encoder for audio compression.
bool encryption_enabled
Encryption enabled.
Definition participant.c:53
char password[BUFFER_SIZE_SMALL]
Password (if any)
Definition participant.c:56
socket_t socket
Connection socket.
Definition participant.c:74
int port
Server port.
Definition participant.c:50
bool enable_audio
Audio enabled.
Definition participant.c:62
char address[BUFFER_SIZE_SMALL]
Server address.
Definition participant.c:47
asciichat_thread_t audio_capture_thread
Audio capture thread handle.
char server_key[BUFFER_SIZE_MEDIUM]
Server key for verification (if any)
Definition participant.c:59
session_capture_ctx_t * video_capture
Video capture context (for webcam/file media)
Definition participant.c:95
bool video_active
Video streaming active.
Definition participant.c:86
bool initialized
Context is initialized.
bool video_capture_running
Video capture thread running flag.
bool audio_capture_running
Audio capture thread running flag.
bool enable_video
Video enabled.
Definition participant.c:65
session_participant_callbacks_t callbacks
Event callbacks.
Definition participant.c:68
void * user_data
User data for callbacks.
Definition participant.c:71
session_audio_ctx_t * audio_capture
Audio capture context (for microphone)
Definition participant.c:98
uint32_t client_id
Assigned client ID.
Definition participant.c:83
Session settings for transmission between peers.
Definition settings.h:77
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.
โฑ๏ธ High-precision timing utilities using sokol_time.h and uthash
Transport abstraction layer for ACIP protocol.