111 size_t plaintext_size,
size_t *plaintext_len);
116#include <stdatomic.h>
153static bool g_data_thread_created =
false;
163static atomic_bool g_data_thread_exited =
false;
218static uint32_t g_last_active_count = 0;
228static bool g_server_state_initialized =
false;
239static bool g_should_clear_before_next_frame =
false;
257static void disconnect_server_for_bad_data(
const char *format, ...) {
259 va_start(args, format);
262 vsnprintf(message,
sizeof(message), format, args);
265 log_error(
"Server sent invalid data - disconnecting: %s", message);
293static char *decode_frame_data(
const char *frame_data_ptr,
size_t frame_data_len,
bool is_compressed,
319static void handle_ascii_frame_packet(
const void *data,
size_t len) {
325 disconnect_server_for_bad_data(
"ASCII_FRAME payload too small: %zu (min %zu)", len,
sizeof(
ascii_frame_packet_t));
330 static fps_t fps_tracker = {0};
331 static bool fps_tracker_initialized =
false;
334 if (!fps_tracker_initialized) {
337 fps_init(&fps_tracker, expected_fps,
"ASCII_RX");
338 fps_tracker_initialized =
true;
341 struct timespec current_time;
342 (void)clock_gettime(CLOCK_MONOTONIC, ¤t_time);
345 fps_frame(&fps_tracker, ¤t_time,
"ASCII frame");
373 if (actual_crc != header.
checksum) {
374 log_error(
"Frame checksum mismatch: got 0x%x, expected 0x%x (size=%u, first_bytes=%02x%02x%02x%02x)", actual_crc,
376 (
unsigned char)frame_data[2], (
unsigned char)frame_data[3]);
380 log_error(
"Software CRC32: 0x%x (matches: %s)", sw_crc, (sw_crc == header.
checksum) ?
"YES" :
"NO");
391 if (header.
width != last_width || header.
height != last_height) {
392 last_width = header.
width;
393 last_height = header.
height;
398 bool take_snapshot =
false;
401 static struct timespec first_frame_time = {0};
402 static bool first_frame_recorded =
false;
403 static int snapshot_frame_count = 0;
405 snapshot_frame_count++;
407 log_debug(
"Snapshot frame %d received", snapshot_frame_count);
409 if (!first_frame_recorded) {
410 (void)clock_gettime(CLOCK_MONOTONIC, &first_frame_time);
411 first_frame_recorded =
true;
415 log_info(
"Snapshot captured immediately (delay=0)!");
416 take_snapshot =
true;
419 log_info(
"Snapshot mode: first frame received, waiting %.2f seconds for webcam warmup...",
423 struct timespec current_time;
424 (void)clock_gettime(CLOCK_MONOTONIC, ¤t_time);
427 double elapsed = (double)(current_time.tv_sec - first_frame_time.tv_sec) +
428 (double)(current_time.tv_nsec - first_frame_time.tv_nsec) / 1000000000.0;
431 char duration_str[32];
433 log_info(
"Snapshot captured after %s!", duration_str);
434 take_snapshot =
true;
442 static bool first_frame_rendered =
false;
444 if (!first_frame_rendered) {
447 log_info(
"First frame - clearing display and disabling terminal logging");
450 first_frame_rendered =
true;
451 g_server_state_initialized =
true;
452 g_should_clear_before_next_frame =
false;
453 log_debug(
"CLIENT_DISPLAY: Display cleared, ready for ASCII frames");
454 }
else if (g_should_clear_before_next_frame) {
456 log_debug(
"CLIENT_DISPLAY: Clearing display for layout change");
459 g_should_clear_before_next_frame =
false;
473 static struct timespec last_render_time = {0, 0};
476 if (!take_snapshot) {
478 int client_display_fps =
MAX_FPS;
482 struct timespec render_time;
483 (void)clock_gettime(CLOCK_MONOTONIC, &render_time);
487 if (last_render_time.tv_sec != 0 || last_render_time.tv_nsec != 0) {
488 int64_t sec_diff = (int64_t)render_time.tv_sec - (int64_t)last_render_time.tv_sec;
489 int64_t nsec_diff = (int64_t)render_time.tv_nsec - (int64_t)last_render_time.tv_nsec;
494 nsec_diff += 1000000000LL;
500 render_elapsed_us = (
uint64_t)sec_diff * 1000000ULL + (
uint64_t)(nsec_diff / 1000);
506 if (last_render_time.tv_sec != 0 || last_render_time.tv_nsec != 0) {
507 if (render_elapsed_us > 0 && render_elapsed_us < render_interval_us) {
515 last_render_time = current_time;
535static void handle_audio_packet(
const void *data,
size_t len) {
536 if (!data || len == 0) {
542 log_warn_every(1000000,
"Received audio packet but audio is disabled");
546 int num_samples = (int)(len /
sizeof(
float));
548 log_warn(
"Audio packet too large: %d samples", num_samples);
560 log_debug(
"Processed %d audio samples", num_samples);
583 log_warn_every(1000000,
"Received audio batch packet but audio is disabled");
588 log_warn(
"Audio batch packet too small: %zu bytes", len);
603 if (batch_count == 0 || total_samples == 0) {
604 log_warn(
"Empty audio batch: batch_count=%u, total_samples=%u", batch_count, total_samples);
610 if (len != expected_size) {
611 log_warn(
"Audio batch size mismatch: got %zu expected %zu", len, expected_size);
616 log_warn(
"Audio batch too large: %u samples", total_samples);
624 float *samples =
SAFE_MALLOC(total_samples *
sizeof(
float),
float *);
662static void handle_audio_opus_packet(
const void *data,
size_t len) {
664 if (!data || len == 0) {
670 log_warn_every(1000000,
"Received opus audio packet but audio is disabled");
681 if (decoded_samples <= 0) {
682 log_warn(
"Failed to decode Opus audio packet, decoded=%d", decoded_samples);
717static void handle_audio_opus_batch_packet(
const void *data,
size_t len) {
719 if (!data || len == 0) {
725 log_warn_every(1000000,
"Received opus batch packet but audio is disabled");
730 const uint8_t *opus_data = NULL;
731 size_t opus_size = 0;
734 int frame_duration = 0;
738 &frame_duration, &frame_count);
741 log_warn(
"Failed to parse Opus batch packet");
745 if (frame_count <= 0 || frame_count > 256 || opus_size == 0) {
746 log_warn(
"Invalid Opus batch: frame_count=%d, opus_size=%zu", frame_count, opus_size);
751 int samples_per_frame = (sample_rate * frame_duration) / 1000;
752 if (samples_per_frame <= 0 || samples_per_frame > 2880) {
753 log_warn(
"Invalid Opus frame parameters: samples_per_frame=%d", samples_per_frame);
758 size_t max_decoded_samples = (size_t)samples_per_frame * (
size_t)frame_count;
759 float *all_samples =
SAFE_MALLOC(max_decoded_samples *
sizeof(
float),
float *);
766 int total_decoded_samples = 0;
767 size_t opus_offset = 0;
769 for (
int i = 0; i < frame_count; i++) {
773 if (opus_offset + frame_size > opus_size) {
774 log_warn(
"Opus batch truncated at frame %d (offset=%zu, frame_size=%zu, total=%zu)", i, opus_offset, frame_size,
780 float *frame_buffer = all_samples + total_decoded_samples;
781 int remaining_space = (int)(max_decoded_samples - (
size_t)total_decoded_samples);
782 int decoded =
audio_decode_opus(opus_data + opus_offset, frame_size, frame_buffer, remaining_space);
785 log_warn(
"Failed to decode Opus frame %d in batch, decoded=%d", i, decoded);
789 total_decoded_samples += decoded;
790 opus_offset += frame_size;
793 if (total_decoded_samples > 0) {
810static bool handle_error_message_packet(
const void *data,
size_t len) {
816 log_error(
"Failed to parse error packet from server: %s", asciichat_error_string(parse_result));
820 log_error(
"Server reported error %d (%s): %s", remote_error, asciichat_error_string(remote_error), message);
821 log_warn(
"Server signaled protocol error; closing connection");
827static void handle_remote_log_packet(
const void *data,
size_t len) {
836 log_error(
"Failed to parse remote log packet from server: %s", asciichat_error_string(parse_result));
841 log_error(
"Remote log packet direction mismatch (direction=%u)", direction);
848 log_msg(remote_level, __FILE__, __LINE__, __func__,
"[REMOTE SERVER] %s [message truncated]", message);
850 log_msg(remote_level, __FILE__, __LINE__, __func__,
"[REMOTE SERVER] %s", message);
866static void handle_server_state_packet(
const void *data,
size_t len) {
868 log_error(
"Invalid server state packet size: %zu", len);
878 if (g_server_state_initialized) {
879 if (g_last_active_count != active_count) {
880 log_info(
"Active client count changed from %u to %u - will clear console before next frame", g_last_active_count,
882 g_should_clear_before_next_frame =
true;
886 g_server_state_initialized =
true;
888 g_should_clear_before_next_frame =
true;
891 g_last_active_count = active_count;
899static void acip_on_ascii_frame(
const ascii_frame_packet_t *header,
const void *frame_data,
size_t data_len,
void *ctx);
900static void acip_on_audio(
const void *audio_data,
size_t audio_len,
void *ctx);
901static void acip_on_audio_batch(
const audio_batch_packet_t *header,
const float *samples,
size_t num_samples,
903static void acip_on_audio_opus(
const void *opus_data,
size_t opus_len,
void *ctx);
904static void acip_on_audio_opus_batch(
const void *batch_data,
size_t batch_len,
void *ctx);
906static void acip_on_error(
const error_packet_t *header,
const char *message,
void *ctx);
907static void acip_on_remote_log(
const remote_log_packet_t *header,
const char *message,
void *ctx);
908static void acip_on_ping(
void *ctx);
909static void acip_on_pong(
void *ctx);
910static void acip_on_clear_console(
void *ctx);
911static void acip_on_crypto_rekey_request(
const void *payload,
size_t payload_len,
void *ctx);
912static void acip_on_crypto_rekey_response(
const void *payload,
size_t payload_len,
void *ctx);
913static void acip_on_webrtc_sdp(
const acip_webrtc_sdp_t *sdp,
size_t total_len,
void *ctx);
914static void acip_on_webrtc_ice(
const acip_webrtc_ice_t *ice,
size_t total_len,
void *ctx);
924 .on_audio = acip_on_audio,
925 .on_audio_batch = acip_on_audio_batch,
926 .on_audio_opus = acip_on_audio_opus,
927 .on_audio_opus_batch = acip_on_audio_opus_batch,
928 .on_server_state = acip_on_server_state,
929 .on_error = acip_on_error,
930 .on_remote_log = acip_on_remote_log,
931 .on_ping = acip_on_ping,
932 .on_pong = acip_on_pong,
933 .on_clear_console = acip_on_clear_console,
934 .on_crypto_rekey_request = acip_on_crypto_rekey_request,
935 .on_crypto_rekey_response =
936 acip_on_crypto_rekey_response,
937 .on_webrtc_sdp = acip_on_webrtc_sdp,
938 .on_webrtc_ice = acip_on_webrtc_ice,
939 .on_session_joined = acip_on_session_joined,
951 return &g_acip_client_callbacks;
984static void *data_reception_thread_func(
void *arg) {
988 log_debug(
"Data reception thread started");
1005 log_error(
"Transport not available, connection lost");
1024 log_error(
"SECURITY: Server violated encryption policy");
1025 log_error(
"SECURITY: This is a critical security violation - exiting immediately");
1031 log_warn(
"ACIP receive/dispatch failed: %s", asciichat_error_string(acip_result));
1036 log_debug(
"Data reception thread stopped");
1039 atomic_store(&g_data_thread_exited,
true);
1063 g_server_state_initialized =
false;
1064 g_last_active_count = 0;
1065 g_should_clear_before_next_frame =
false;
1072 log_info(
"Sending client capabilities to server...");
1074 log_error(
"Failed to send client capabilities to server");
1077 log_info(
"Client capabilities sent successfully");
1085 log_info(
"Sending STREAM_START packet (types=0x%x: %s%s)...", stream_types,
"video",
1088 log_error(
"Failed to send STREAM_START packet");
1091 log_info(
"STREAM_START packet sent successfully");
1094 atomic_store(&g_data_thread_exited,
false);
1096 log_error(
"Failed to spawn data reception thread in worker pool");
1102 log_info(
"Starting webcam capture thread...");
1104 log_error(
"Failed to start webcam capture thread");
1107 log_info(
"Webcam capture thread started successfully");
1110 log_info(
"Starting audio capture thread...");
1112 log_error(
"Failed to start audio capture thread");
1115 log_info(
"Audio capture thread started successfully (or skipped if audio disabled)");
1118 log_info(
"Starting keepalive/ping thread...");
1120 log_error(
"Failed to start keepalive/ping thread");
1123 log_info(
"Keepalive/ping thread started successfully");
1125 g_data_thread_created =
true;
1138 if (!g_data_thread_created) {
1159 while (wait_count < 20 && !atomic_load(&g_data_thread_exited)) {
1164 if (!atomic_load(&g_data_thread_exited)) {
1165 log_warn(
"Data thread not responding after 2 seconds - will be joined by thread pool");
1173 log_error(
"Failed to stop client worker threads");
1178 g_data_thread_created =
false;
1181 log_info(
"Data reception thread stopped and joined by thread pool");
1205static void acip_on_ascii_frame(
const ascii_frame_packet_t *header,
const void *frame_data,
size_t data_len,
1213 size_t total_len =
sizeof(*header) + data_len;
1216 log_error(
"Failed to allocate buffer for ASCII frame callback");
1229 memcpy(packet, &net_header,
sizeof(net_header));
1230 memcpy(packet +
sizeof(net_header), frame_data, data_len);
1232 handle_ascii_frame_packet(packet, total_len);
1239static void acip_on_audio_batch(
const audio_batch_packet_t *header,
const float *samples,
size_t num_samples,
1253 size_t approx_size =
sizeof(*header) + (num_samples *
sizeof(
uint32_t));
1263static void acip_on_audio_opus(
const void *opus_data,
size_t opus_len,
void *ctx) {
1267 handle_audio_opus_packet(opus_data, opus_len);
1277 handle_server_state_packet(state,
sizeof(*state));
1283static void acip_on_error(
const error_packet_t *header,
const char *message,
void *ctx) {
1287 size_t msg_len = message ? strlen(message) : 0;
1288 size_t total_len =
sizeof(*header) + msg_len;
1292 log_error(
"Failed to allocate buffer for error packet callback");
1296 memcpy(packet, header,
sizeof(*header));
1298 memcpy(packet +
sizeof(*header), message, msg_len);
1301 handle_error_message_packet(packet, total_len);
1308static void acip_on_ping(
void *ctx) {
1313 log_error(
"Failed to send PONG response");
1320static void acip_on_audio(
const void *audio_data,
size_t audio_len,
void *ctx) {
1324 handle_audio_packet(audio_data, audio_len);
1330static void acip_on_audio_opus_batch(
const void *batch_data,
size_t batch_len,
void *ctx) {
1334 handle_audio_opus_batch_packet(batch_data, batch_len);
1340static void acip_on_remote_log(
const remote_log_packet_t *header,
const char *message,
void *ctx) {
1344 size_t msg_len = strlen(message);
1345 size_t total_len =
sizeof(*header) + msg_len;
1349 log_error(
"Failed to allocate buffer for remote log callback");
1353 memcpy(packet, header,
sizeof(*header));
1354 memcpy(packet +
sizeof(*header), message, msg_len);
1356 handle_remote_log_packet(packet, total_len);
1363static void acip_on_pong(
void *ctx) {
1371static void acip_on_clear_console(
void *ctx) {
1376 log_info(
"Console cleared by server");
1382static void acip_on_crypto_rekey_request(
const void *payload,
size_t payload_len,
void *ctx) {
1388 log_error(
"Failed to process REKEY_REQUEST: %d", crypto_result);
1395 log_error(
"Failed to send REKEY_RESPONSE: %d", crypto_result);
1402static void acip_on_crypto_rekey_response(
const void *payload,
size_t payload_len,
void *ctx) {
1408 log_error(
"Failed to process REKEY_RESPONSE: %d", crypto_result);
1415 log_error(
"Failed to send REKEY_COMPLETE: %d", crypto_result);
1429static void acip_on_webrtc_sdp(
const acip_webrtc_sdp_t *sdp,
size_t total_len,
void *ctx) {
1435 log_warn(
"Received WebRTC SDP but peer manager not initialized - ignoring");
1440 const char *sdp_type_str = (sdp->sdp_type == 0) ?
"offer" :
"answer";
1441 log_info(
"Received WebRTC SDP %s from participant (session_id=%.8s...)", sdp_type_str, (
const char *)sdp->session_id);
1447 log_error(
"Failed to handle WebRTC SDP: %s", asciichat_error_string(result));
1461static void acip_on_webrtc_ice(
const acip_webrtc_ice_t *ice,
size_t total_len,
void *ctx) {
1467 log_warn(
"Received WebRTC ICE but peer manager not initialized - ignoring");
1471 log_debug(
"Received WebRTC ICE candidate from participant (session_id=%.8s...)", (
const char *)ice->session_id);
1477 log_error(
"Failed to handle WebRTC ICE: %s", asciichat_error_string(result));
1505 log_error(
"SESSION_JOINED callback received NULL response");
1510 if (!joined->success) {
1511 log_error(
"ACDS session join failed: error %d: %s", joined->error_code, joined->error_message);
1517 log_info(
"ACDS session join succeeded (participant_id=%.8s..., session_type=%s, server=%s:%u)",
1518 (
const char *)joined->participant_id, joined->session_type == 1 ?
"WebRTC" :
"DirectTCP",
1519 joined->server_address, joined->server_port);
1526 log_info(
"WebRTC session detected - TODO: initialize WebRTC with TURN credentials");
1529 log_info(
"Direct TCP session - using existing connection");
void audio_analysis_track_received_packet(size_t size)
Track received packet.
Audio Analysis and Debugging Interface.
#define LOG_ERRNO_IF_SET(message)
Check if any error occurred and log it if so.
๐๏ธ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
ascii-chat Client Media Capture Management Interface
void signal_exit()
Signal client to exit.
struct webrtc_peer_manager * g_peer_manager
Global WebRTC peer manager for P2P connections.
thread_pool_t * g_client_worker_pool
Global client worker thread pool.
bool should_exit()
Check if client should exit.
๐ฆ Network Packet Compression Utilities
Hardware-Accelerated CRC32 Checksum Computation.
ascii-chat Client Display Management Interface
๐ Network byte order conversion helpers
#define HOST_TO_NET_U32(val)
#define NET_TO_HOST_U16(val)
#define NET_TO_HOST_U32(val)
โฑ๏ธ FPS tracking utility for monitoring frame throughput across all threads
@ SESSION_TYPE_WEBRTC
WebRTC P2P mesh with STUN/TURN relay.
asciichat_error_t audio_dequantize_samples(const uint8_t *samples_ptr, uint32_t total_samples, float *out_samples)
Dequantize network audio samples from int32 to float.
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
void audio_process_received_samples(const float *samples, int num_samples)
Process received audio samples from server.
void audio_stop_thread()
Stop audio capture thread.
int audio_decode_opus(const uint8_t *opus_data, size_t opus_len, float *output, int max_samples)
Decode Opus packet using the audio pipeline.
int audio_start_thread()
Start audio capture thread.
int capture_start_thread()
Start capture thread.
void capture_stop_thread()
Stop capture thread.
int threaded_send_pong_packet(void)
Thread-safe pong packet transmission.
bool server_connection_is_active()
Check if server connection is currently active.
void server_connection_shutdown()
Emergency connection shutdown for signal handlers.
int threaded_send_terminal_size_with_auto_detect(unsigned short width, unsigned short height)
Thread-safe terminal size packet transmission with auto-detection.
acip_transport_t * server_connection_get_transport(void)
Get ACIP transport instance.
int threaded_send_stream_start_packet(uint32_t stream_type)
Thread-safe stream start packet transmission.
socket_t server_connection_get_socket()
Get current socket file descriptor.
bool server_connection_is_lost()
Check if connection loss has been detected.
void server_connection_lost()
Signal that connection has been lost.
const crypto_context_t * crypto_client_get_context(void)
Get crypto context for encryption/decryption.
int crypto_client_send_rekey_response(void)
Send REKEY_RESPONSE packet to server.
int crypto_client_decrypt_packet(const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len)
Decrypt a received packet.
bool crypto_client_is_ready(void)
Check if crypto handshake is ready.
int crypto_client_process_rekey_request(const uint8_t *packet, size_t packet_len)
Process received REKEY_REQUEST packet from server.
int crypto_client_process_rekey_response(const uint8_t *packet, size_t packet_len)
Process received REKEY_RESPONSE packet from server.
int crypto_client_send_rekey_complete(void)
Send REKEY_COMPLETE packet to server and commit to new key.
void display_full_reset()
Perform full display reset.
void display_render_frame(const char *frame_data, bool is_snapshot_frame)
Render ASCII frame to display.
void display_reset_for_new_connection()
Reset display state for new connection.
int keepalive_start_thread()
Start keepalive/ping thread.
void keepalive_stop_thread()
Stop keepalive/ping thread.
const acip_client_callbacks_t * protocol_get_acip_callbacks()
Get ACIP client callbacks for packet dispatch.
void protocol_stop_connection()
Stop protocol connection handling.
int protocol_start_connection()
Start protocol connection handling.
bool protocol_connection_lost()
Check if connection has been lost.
#define SAFE_MALLOC(size, cast)
void fps_frame(fps_t *tracker, const struct timespec *current_time, const char *context)
Track a frame and detect lag conditions.
unsigned long long uint64_t
#define SAFE_MEMCPY(dest, dest_size, src, count)
void fps_init(fps_t *tracker, int expected_fps, const char *name)
Initialize FPS tracker.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
void asciichat_errno_cleanup(void)
Cleanup error system resources.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second)
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
#define MAX_FPS
Maximum frame rate macro (uses g_max_fps if set, otherwise DEFAULT_MAX_FPS)
int g_max_fps
Runtime configurable maximum frame rate (can be overridden via environment or command line)
#define LOG_RATE_DEFAULT
Log rate limit: 5 seconds (5,000,000 microseconds) - default for audio/video packets.
#define log_warn(...)
Log a WARN message.
void log_msg(log_level_t level, const char *file, int line, const char *func, const char *fmt,...)
Log a message at a specific level.
enum remote_log_direction remote_log_direction_t
Remote log packet direction enumeration.
#define log_error(...)
Log an ERROR message.
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
log_level_t
Logging levels enumeration.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
@ REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT
@ REMOTE_LOG_DIRECTION_UNKNOWN
int format_duration_s(double seconds, char *buffer, size_t buffer_size)
Format seconds as human-readable duration string.
asciichat_error_t packet_parse_remote_log(const void *data, size_t len, log_level_t *out_level, remote_log_direction_t *out_direction, uint16_t *out_flags, char *message_buffer, size_t message_buffer_size, size_t *out_message_length)
Parse a remote log packet payload into components.
uint32_t width
Terminal width in characters.
uint32_t active_client_count
Number of clients actively sending video/audio streams.
uint32_t checksum
CRC32 checksum of original ASCII data.
uint32_t original_size
Size of original uncompressed ASCII data in bytes.
uint32_t height
Terminal height in characters.
#define STREAM_TYPE_VIDEO
Video stream.
uint32_t compressed_size
Size of compressed data (0 = not compressed)
#define STREAM_TYPE_AUDIO
Audio stream.
asciichat_error_t packet_parse_error_message(const void *data, size_t len, asciichat_error_t *out_error_code, char *message_buffer, size_t message_buffer_size, size_t *out_message_length)
Parse an error packet payload into components.
uint32_t channels
Number of audio channels (1=mono, 2=stereo)
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32)
uint32_t flags
Frame flags bitmask (HAS_COLOR, IS_COMPRESSED, etc.)
uint32_t sample_rate
Sample rate in Hz (e.g., 44100, 48000)
uint32_t total_samples
Total audio samples across all chunks (typically 8192)
#define REMOTE_LOG_FLAG_TRUNCATED
Remote log packet flag definitions.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
asciichat_error_t packet_parse_opus_batch(const void *packet_data, size_t packet_len, const uint8_t **out_opus_data, size_t *out_opus_size, const uint16_t **out_frame_sizes, int *out_sample_rate, int *out_frame_duration, int *out_frame_count)
Parse Opus audio batch packet header and extract frame data.
char * packet_decode_frame_data_malloc(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed, uint32_t original_size, uint32_t compressed_size)
Decode frame data (malloc version for client handlers)
#define AUDIO_BATCH_SAMPLES
Total samples in audio batch (8192 samples)
#define AUDIO_SAMPLES_PER_PACKET
Samples per audio packet (256 samples)
#define MAX_REMOTE_LOG_MESSAGE_LENGTH
Maximum remote log message length (512 bytes)
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
#define FRAME_FLAG_IS_COMPRESSED
Frame data is compressed.
uint32_t asciichat_crc32_sw(const void *data, size_t len)
Compute CRC32 checksum using software implementation only.
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
asciichat_error_t webrtc_peer_manager_handle_ice(webrtc_peer_manager_t *manager, const acip_webrtc_ice_t *ice)
Handle incoming ICE candidate from ACDS.
asciichat_error_t webrtc_peer_manager_handle_sdp(webrtc_peer_manager_t *manager, const acip_webrtc_sdp_t *sdp)
Handle incoming SDP message from ACDS.
ACIP protocol packet handlers (transport-agnostic)
ascii-chat Client Connection Keepalive Management Interface
๐ Audio Capture and Playback Interface for ascii-chat
asciichat_error_t acip_client_receive_and_dispatch(acip_transport_t *transport, const acip_client_callbacks_t *callbacks)
Receive packet from server and dispatch to callbacks.
ACIP client-side protocol library.
ASCII-Chat Discovery Service (ACDS) Protocol Message Formats.
โ๏ธ Command-line options parsing and configuration management for ascii-chat
Packet protocol implementation with encryption and compression support.
Shared packet parsing utilities to eliminate duplication between server and client handlers.
ascii-chat Server Mode Entry Point Header
void handle_audio_batch_packet(client_info_t *client, const void *data, size_t len)
Process AUDIO_BATCH packet - store efficiently batched audio samples.
ascii-chat Client Audio Processing Management Interface
ascii-chat Client Server Connection Management Interface
Server cryptographic operations and per-client handshake management.
Server packet processing and protocol implementation.
Client-side packet handler callbacks.
void(* on_ascii_frame)(const ascii_frame_packet_t *header, const void *frame_data, size_t data_len, void *ctx)
Called when ASCII frame received from server.
Transport instance structure.
ASCII frame packet structure (Packet Type 2)
char * context_message
Optional custom message (dynamically allocated, owned by system)
asciichat_error_t code
Error code (asciichat_error_t enum value)
Audio batch packet structure (Packet Type 28)
Cryptographic context structure.
Error packet structure carrying error code and textual description.
Remote log packet structure carrying log level and message text.
Server state packet structure.
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Spawn a worker thread in the pool.
asciichat_error_t thread_pool_stop_all(thread_pool_t *pool)
Stop all threads in the pool in stop_id order.
๐งต Generic thread pool abstraction for managing worker threads
โฑ๏ธ High-precision timing utilities using sokol_time.h and uthash
Transport abstraction layer for ACIP protocol.
Common validation macros to reduce duplication in protocol handlers.
Wrapper for windows.h with C23 alignment compatibility.