114#include <stdatomic.h>
151static void protocol_cleanup_thread_locals(
void) {
162 protocol_cleanup_thread_locals();
165 if (already_requested) {
169 char reason[256] = {0};
172 va_start(args, format);
173 (void)vsnprintf(reason,
sizeof(reason), format, args);
176 SAFE_STRNCPY(reason,
"Protocol violation",
sizeof(reason));
179 const char *reason_str = reason[0] !=
'\0' ? reason :
"Protocol violation";
187 socket_snapshot = client->
socket;
197 log_warn(
"Disconnecting client %u due to protocol violation: %s", client_id, reason_str);
208 log_warn(
"Failed to send remote log to client %u: %s", client_id, asciichat_error_string(log_result));
213 log_warn(
"Failed to send error packet to client %u: %s", client_id, asciichat_error_string(send_result));
221 atomic_store(&client->
active,
false);
308 log_info(
"Client %u joined: %s (video=%d, audio=%d, stretch=%d)", atomic_load(&client->
client_id),
365 log_warn(
"Client %u protocol version mismatch: client=%u.%u, server=%u.%u", atomic_load(&client->
client_id),
370 log_info(
"Client %u has different protocol revision: client=%u.%u, server=%u.%u", atomic_load(&client->
client_id),
375 for (
size_t i = 0; i <
sizeof(version->
reserved); i++) {
377 log_warn(
"Client %u sent non-zero reserved bytes in PROTOCOL_VERSION packet", atomic_load(&client->
client_id));
393 log_debug(
"Client %u supports features: 0x%04x", atomic_load(&client->
client_id), feature_flags);
433 log_info(
"Client %u sent leave notification (no reason)", client_id);
434 }
else if (len <= 256) {
441 char reason[257] = {0};
442 memcpy(reason, data, len);
446 bool all_printable =
true;
447 for (
size_t i = 0; i < len; i++) {
449 if (c < 32 && c !=
'\t' && c !=
'\n') {
450 all_printable =
false;
456 log_info(
"Client %u sent leave notification: %s", client_id, reason);
458 log_info(
"Client %u sent leave notification (reason contains non-printable characters)", client_id);
462 log_warn(
"Client %u sent oversized leave reason (%zu bytes, max 256)", client_id, len);
467 atomic_store(&client->
active,
false);
513 memcpy(&stream_type_net, data,
sizeof(
uint32_t));
533 log_info(
"Client %u: Opus decoder created (48kHz)", atomic_load(&client->
client_id));
535 log_error(
"Client %u: Failed to create Opus decoder", atomic_load(&client->
client_id));
541 log_info(
"Client %u announced video stream (waiting for first frame)", atomic_load(&client->
client_id));
593 memcpy(&stream_type_net, data,
sizeof(
uint32_t));
688 if (!data || len <
sizeof(
uint32_t) * 2) {
693 if (!was_sending_video) {
696 if (atomic_compare_exchange_strong(&client->
is_sending_video, &was_sending_video,
true)) {
697 log_info(
"Client %u auto-enabled video stream (received IMAGE_FRAME)", atomic_load(&client->
client_id));
699 log_info_client(client,
"First video frame received - streaming active");
709 log_debug(
"Client %u has sent %u IMAGE_FRAME packets (%s)", atomic_load(&client->
client_id),
716 uint32_t img_width_net, img_height_net;
717 memcpy(&img_width_net, data,
sizeof(
uint32_t));
722 log_debug(
"IMAGE_FRAME packet: width=%u, height=%u, payload_len=%zu", img_width, img_height, len);
726 log_error(
"IMAGE_FRAME validation failed for dimensions: %u x %u", img_width, img_height);
745 const size_t legacy_header_size =
sizeof(
uint32_t) * 2;
746 if (rgb_size > SIZE_MAX - legacy_header_size) {
752 size_t old_format_size = legacy_header_size + rgb_size;
753 bool is_new_format = (len != old_format_size) && (len >
sizeof(
uint32_t) * 4);
755 void *rgb_data = NULL;
756 size_t rgb_data_size = 0;
757 bool needs_free =
false;
767 uint32_t compressed_flag_net, data_size_net;
768 memcpy(&compressed_flag_net, (
char *)data +
sizeof(
uint32_t) * 2,
sizeof(
uint32_t));
769 memcpy(&data_size_net, (
char *)data +
sizeof(
uint32_t) * 3,
sizeof(
uint32_t));
772 void *frame_data = (
char *)data +
sizeof(
uint32_t) * 4;
774 const size_t new_header_size =
sizeof(
uint32_t) * 4;
775 size_t data_size_sz = (size_t)data_size;
782 if (data_size_sz > SIZE_MAX - new_header_size) {
786 size_t expected_total = new_header_size + data_size_sz;
787 if (len != expected_total) {
789 expected_total, len);
793 if (compressed_flag) {
806 asciichat_error_string(decompress_result));
810 rgb_data_size = rgb_size;
814 rgb_data = frame_data;
815 rgb_data_size = data_size;
816 if (rgb_data_size != rgb_size) {
824 if (len != old_format_size) {
826 old_format_size, len);
829 rgb_data = (
char *)data +
sizeof(
uint32_t) * 2;
830 rgb_data_size = rgb_size;
837 if (frame && frame->
data) {
839 if (rgb_data_size > SIZE_MAX - legacy_header_size) {
840 if (needs_free && rgb_data) {
848 size_t old_packet_size = legacy_header_size + rgb_data_size;
850 if (old_packet_size <= 2 * 1024 * 1024) {
857 memcpy((
char *)frame->
data +
sizeof(
uint32_t) * 2, rgb_data, rgb_data_size);
859 frame->
size = old_packet_size;
860 frame->
width = img_width;
861 frame->
height = img_height;
866 if (needs_free && rgb_data) {
873 log_warn(
"Failed to get write buffer for client %u (frame=%p, frame->data=%p)", atomic_load(&client->
client_id),
874 (
void *)frame, frame ? frame->
data : NULL);
881 log_debug(
"Client %u: ignoring video packet during shutdown", atomic_load(&client->
client_id));
886 if (needs_free && rgb_data) {
937 int num_samples = (int)(len /
sizeof(
float));
941 const float *samples = (
const float *)data;
944 log_error(
"Failed to write audio samples to buffer: %s", asciichat_error_string(result));
975 log_msg(remote_level, __FILE__, __LINE__, __func__,
"[REMOTE CLIENT %u \"%s\"] %s [message truncated]", client_id,
976 display_name, message);
978 log_msg(remote_level, __FILE__, __LINE__, __func__,
"[REMOTE CLIENT %u \"%s\"] %s", client_id, display_name,
1050 (void)packet_batch_count;
1053 VALIDATE_NONZERO(client, packet_batch_count,
"batch_count",
"AUDIO_BATCH");
1056 size_t samples_bytes = 0;
1063 if (len != expected_size) {
1072 if (total_samples > MAX_AUDIO_SAMPLES) {
1081 size_t alloc_size = (size_t)total_samples *
sizeof(
float);
1082 float *samples =
SAFE_MALLOC(alloc_size,
float *);
1096 static int recv_count = 0;
1098 if (recv_count % 100 == 0) {
1099 uint32_t raw0 = bytes_read_u32_unaligned(samples_ptr + 0 *
sizeof(
uint32_t));
1100 uint32_t raw1 = bytes_read_u32_unaligned(samples_ptr + 1 *
sizeof(
uint32_t));
1101 uint32_t raw2 = bytes_read_u32_unaligned(samples_ptr + 2 *
sizeof(
uint32_t));
1105 log_info(
"RECV: network[0]=0x%08x, network[1]=0x%08x, network[2]=0x%08x", raw0, raw1, raw2);
1106 log_info(
"RECV: scaled[0]=%d, scaled[1]=%d, scaled[2]=%d", scaled0, scaled1, scaled2);
1107 log_info(
"RECV: samples[0]=%.6f, samples[1]=%.6f, samples[2]=%.6f", samples[0], samples[1], samples[2]);
1114 log_error(
"Failed to write decoded audio batch to buffer: %s", asciichat_error_string(write_result));
1165 const uint8_t *opus_data = NULL;
1166 size_t opus_size = 0;
1167 const uint16_t *frame_sizes = NULL;
1168 int sample_rate = 0;
1169 int frame_duration = 0;
1170 int frame_count = 0;
1173 &frame_duration, &frame_count);
1184 int samples_per_frame = (sample_rate * frame_duration) / 1000;
1185 VALIDATE_RANGE(client, samples_per_frame, 1, 4096,
"samples_per_frame",
"AUDIO_OPUS_BATCH");
1190#define OPUS_DECODE_STATIC_MAX_SAMPLES (32 * 960)
1193 size_t total_samples = (size_t)samples_per_frame * (
size_t)frame_count;
1194 float *decoded_samples;
1195 bool used_malloc =
false;
1198 decoded_samples = static_decode_buffer;
1201 log_warn(
"Client %u: Large audio batch requires malloc (%zu samples)", atomic_load(&client->
client_id),
1203 decoded_samples =
SAFE_MALLOC(total_samples *
sizeof(
float),
float *);
1204 if (!decoded_samples) {
1212 int total_decoded = 0;
1213 size_t opus_offset = 0;
1215 for (
int i = 0; i < frame_count; i++) {
1220 if (frame_size > 0) {
1222 atomic_load(&client->
client_id), i, frame_size, opus_data[opus_offset] & 0xFF,
1223 frame_size > 1 ? (opus_data[opus_offset + 1] & 0xFF) : 0,
1224 frame_size > 2 ? (opus_data[opus_offset + 2] & 0xFF) : 0,
1225 frame_size > 3 ? (opus_data[opus_offset + 3] & 0xFF) : 0);
1228 if (opus_offset + frame_size > opus_size) {
1229 log_error(
"Client %u: Frame %d size overflow (offset=%zu, frame_size=%zu, total=%zu)",
1230 atomic_load(&client->
client_id), i + 1, opus_offset, frame_size, opus_size);
1239 if ((
size_t)total_decoded + (
size_t)samples_per_frame > total_samples) {
1240 log_error(
"Client %u: Opus decode would overflow buffer (decoded=%d, frame_samples=%d, max=%zu)",
1241 atomic_load(&client->
client_id), total_decoded, samples_per_frame, total_samples);
1249 &decoded_samples[total_decoded], samples_per_frame);
1251 if (decoded_count < 0) {
1252 log_error(
"Client %u: Opus decoding failed for frame %d/%d (size=%zu)", atomic_load(&client->
client_id), i + 1,
1253 frame_count, frame_size);
1260 total_decoded += decoded_count;
1261 opus_offset += frame_size;
1265 frame_count, total_decoded);
1268 static int server_decode_count = 0;
1269 server_decode_count++;
1270 if (total_decoded > 0 && (server_decode_count <= 10 || server_decode_count % 100 == 0)) {
1271 float peak = 0.0f, rms = 0.0f;
1272 for (
int i = 0; i < total_decoded && i < 100; i++) {
1273 float abs_val = fabsf(decoded_samples[i]);
1276 rms += decoded_samples[i] * decoded_samples[i];
1278 rms = sqrtf(rms / (total_decoded > 100 ? 100 : total_decoded));
1280 log_info(
"SERVER OPUS DECODE #%d from client %u: decoded_rms=%.6f, opus_first4=[0x%02x,0x%02x,0x%02x,0x%02x]",
1281 server_decode_count, atomic_load(&client->
client_id), rms, opus_size > 0 ? opus_data[0] : 0,
1282 opus_size > 1 ? opus_data[1] : 0, opus_size > 2 ? opus_data[2] : 0, opus_size > 3 ? opus_data[3] : 0);
1291 log_error(
"Client %u: Failed to write decoded audio to buffer: %d", atomic_load(&client->
client_id), result);
1344 uint32_t sample_rate_net, frame_duration_net;
1345 memcpy(&sample_rate_net, buf, 4);
1346 memcpy(&frame_duration_net, buf + 4, 4);
1351 const uint8_t *opus_data = buf + 16;
1352 size_t opus_size = len - 16;
1355 if (sample_rate == 0 || sample_rate > 192000) {
1360 if (frame_duration == 0 || frame_duration > 120) {
1366 int samples_per_frame = (int)((sample_rate * frame_duration) / 1000);
1367 if (samples_per_frame <= 0 || samples_per_frame > 5760) {
1373 float decoded_samples[5760];
1377 if (decoded_count < 0) {
1378 log_error(
"Client %u: Opus decoding failed (size=%zu)", atomic_load(&client->
client_id), opus_size);
1390 log_error(
"Failed to write decoded Opus samples to buffer: %s", asciichat_error_string(write_result));
1475 VALIDATE_RANGE(client, width, 1, 4096,
"width",
"CLIENT_CAPABILITIES");
1476 VALIDATE_RANGE(client, height, 1, 4096,
"height",
"CLIENT_CAPABILITIES");
1480 VALIDATE_RANGE(client, color_level, 0, 3,
"color_level",
"CLIENT_CAPABILITIES");
1484 VALIDATE_RANGE(client, render_mode, 0, 2,
"render_mode",
"CLIENT_CAPABILITIES");
1488 VALIDATE_RANGE(client, palette_type, 0, 5,
"palette_type",
"CLIENT_CAPABILITIES");
1495 atomic_store(&client->
width, width);
1496 atomic_store(&client->
height, height);
1518 const char *custom_chars =
1528 log_info(
"Client %d palette initialized: type=%u, %zu chars, utf8=%u", atomic_load(&client->
client_id),
1537 log_info(
"Client %u capabilities: %ux%u, color_level=%s (%u colors), caps=0x%x, term=%s, colorterm=%s, "
1538 "render_mode=%s, reliable=%s, fps=%u",
1607 client->
width = width;
1611 log_info(
"Client %u updated terminal size: %ux%u", atomic_load(&client->
client_id), width, height);
1710 int active_count = 0;
1737 asciichat_error_string(result));
1776 log_warn(
"CLEAR_CONSOLE is now sent from render threads, not broadcast");
🔢 Byte-Level Access and Arithmetic Utilities
📦 Network Packet Compression Utilities
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
Common declarations and data structures for cryptographic handshake.
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U32(val)
#define NET_TO_HOST_U16(val)
#define NET_TO_HOST_U32(val)
opus_codec_t * opus_codec_create_decoder(int sample_rate)
Create an Opus decoder.
asciichat_error_t audio_ring_buffer_write(audio_ring_buffer_t *rb, const float *data, int samples)
Write audio samples to ring buffer.
int opus_codec_decode(opus_codec_t *codec, const uint8_t *data, size_t data_len, float *out_samples, int out_num_samples)
Decode Opus audio frame.
asciichat_error_t audio_parse_batch_header(const void *data, size_t len, audio_batch_info_t *out_batch)
Parse an audio batch packet header from raw packet data.
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.
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MALLOC(size, cast)
unsigned long long uint64_t
asciichat_error_t decompress_data(const void *input, size_t input_size, void *output, size_t output_size)
Decompress data using zstd.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define MAX_CLIENTS
Maximum possible clients (static array size) - actual runtime limit set by –max-clients (1-32)
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
#define LOG_RATE_VERY_FAST
Log rate limit: 0.1 seconds (100,000 microseconds) - for very high frequency operations.
#define LOG_RATE_SLOW
Log rate limit: 10 seconds (10,000,000 microseconds)
#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_info_client(client, fmt,...)
Server sends INFO log message to client.
#define log_error(...)
Log an ERROR message.
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
asciichat_error_t log_network_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
Send a formatted log message over the network.
log_level_t
Logging levels enumeration.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
@ REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT
@ REMOTE_LOG_DIRECTION_UNKNOWN
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags)
uint32_t color_level
Color level enum value (terminal_color_mode_t)
uint32_t color_count
Actual color count (16, 256, or 16777216)
uint32_t reserved[6]
Reserved fields for future use (must be zero)
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.
char display_name[MAX_DISPLAY_NAME_LEN]
User display name (null-terminated, max MAX_DISPLAY_NAME_LEN bytes)
asciichat_error_t packet_send_error(socket_t sockfd, const crypto_context_t *crypto_ctx, asciichat_error_t error_code, const char *message)
Send an error packet with optional encryption context.
uint16_t height
Terminal height in characters.
uint32_t active_client_count
Number of clients actively sending video/audio streams.
uint8_t reserved[7]
Reserved bytes for future expansion (must be zero)
uint32_t connected_client_count
Total number of currently connected clients.
#define CLIENT_CAP_STRETCH
Client can stretch frames to fill terminal.
#define STREAM_TYPE_VIDEO
Video stream.
uint32_t height
Terminal height in characters.
uint16_t protocol_revision
Minor protocol revision (server can be newer)
uint16_t protocol_version
Major protocol version (must match for compatibility)
#define STREAM_TYPE_AUDIO
Audio stream.
#define CLIENT_CAP_COLOR
Client supports color rendering.
uint32_t render_mode
Render mode enum value (foreground/background/half-block)
uint16_t feature_flags
Feature flags bitmask (FEATURE_RLE_ENCODING, etc.)
uint16_t width
Terminal width in characters.
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess)
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
#define REMOTE_LOG_FLAG_TRUNCATED
Remote log packet flag definitions.
uint8_t compression_algorithms
Supported compression algorithms bitmask (COMPRESS_ALGO_*)
uint32_t width
Terminal width in characters.
char colorterm[32]
$COLORTERM environment variable value (for debugging)
uint32_t palette_type
Palette type enum value (palette_type_t)
uint32_t capabilities
Client capabilities bitmask (CLIENT_CAP_VIDEO | CLIENT_CAP_AUDIO | CLIENT_CAP_COLOR | CLIENT_CAP_STRE...
#define CLIENT_CAP_AUDIO
Client can send/receive audio.
#define CLIENT_CAP_VIDEO
Client can send/receive video.
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported)
uint8_t supports_encryption
Encryption support flag (1=support encryption, 0=plaintext only)
char term_type[32]
$TERM environment variable value (for debugging)
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.
void packet_queue_shutdown(packet_queue_t *queue)
Signal queue shutdown (causes dequeue to return NULL)
#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)
int initialize_client_palette(palette_type_t palette_type, const char *custom_chars, char client_palette_chars[256], size_t *client_palette_len, char client_luminance_palette[256])
Initialize client palette with full configuration.
palette_type_t
Built-in palette type enumeration.
@ PALETTE_CUSTOM
User-defined via –palette-chars.
#define PROTOCOL_VERSION_MAJOR
Major protocol version number.
#define PROTOCOL_VERSION_MINOR
Minor protocol version number.
void handle_audio_opus_packet(client_info_t *client, const void *data, size_t len)
Process AUDIO_OPUS packet - decode single Opus frame from client.
void handle_audio_opus_batch_packet(client_info_t *client, const void *data, size_t len)
Process AUDIO_OPUS_BATCH packet - efficient Opus-encoded audio batch from client.
void format_bytes_pretty(size_t bytes, char *out, size_t out_capacity)
Format byte count into human-readable string.
#define VALIDATE_PACKET_NOT_NULL(client, data, packet_name)
#define VALIDATE_AUDIO_SAMPLE_COUNT(client, num_samples, max_samples, packet_name)
#define VALIDATE_RESOURCE_INITIALIZED(client, resource, resource_name)
#define VALIDATE_AUDIO_STREAM_ENABLED(client, packet_name)
#define VALIDATE_AUDIO_ALIGNMENT(client, len, sample_size, packet_name)
#define VALIDATE_NONZERO(client, value, value_name, packet_name)
#define VALIDATE_MIN_SIZE(client, len, min_size, packet_name)
#define VALIDATE_PACKET_SIZE(client, data, len, expected_size, packet_name)
#define VALIDATE_NOTNULL_DATA(client, data, packet_name)
#define VALIDATE_FLAGS_MASK(client, flags, valid_mask, packet_name)
#define VALIDATE_RANGE(client, value, min_val, max_val, value_name, packet_name)
void disconnect_client_for_bad_data(client_info_t *client, const char *format,...)
#define VALIDATE_CAPABILITY_FLAGS(client, flags, valid_mask, packet_name)
video_frame_t * video_frame_begin_write(video_frame_buffer_t *vfb)
Writer API: Start writing a new frame.
void video_frame_commit(video_frame_buffer_t *vfb)
Writer API: Commit the frame and swap buffers.
#define IMAGE_MAX_PIXELS_SIZE
Maximum pixel data size in bytes.
🔊 Audio Capture and Playback Interface for ascii-chat
asciichat_error_t acip_send_server_state(acip_transport_t *transport, const server_state_packet_t *state)
Send server state update to client (server → client)
ACIP server-side protocol library.
🔢 Mathematical Utility Functions
Network logging macros and remote log direction enumeration.
Opus audio codec wrapper for real-time encoding/decoding.
Shared packet parsing utilities to eliminate duplication between server and client handlers.
ASCII Palette Management for Video-to-ASCII Conversion.
ACIP shared/bidirectional packet sending functions.
void handle_client_join_packet(client_info_t *client, const void *data, size_t len)
Process CLIENT_JOIN packet - client announces identity and capabilities.
void handle_protocol_version_packet(client_info_t *client, const void *data, size_t len)
Process PROTOCOL_VERSION packet - validate protocol compatibility.
void handle_image_frame_packet(client_info_t *client, void *data, size_t len)
Process IMAGE_FRAME packet - store client's video data for rendering.
void handle_audio_batch_packet(client_info_t *client, const void *data, size_t len)
Process AUDIO_BATCH packet - store efficiently batched audio samples.
void handle_size_packet(client_info_t *client, const void *data, size_t len)
Process terminal size update packet - handle client window resize.
void handle_audio_packet(client_info_t *client, const void *data, size_t len)
Process AUDIO packet - store single audio sample batch (legacy format)
void handle_client_leave_packet(client_info_t *client, const void *data, size_t len)
Process CLIENT_LEAVE packet - handle clean client disconnect.
#define OPUS_DECODE_STATIC_MAX_SAMPLES
void handle_stream_stop_packet(client_info_t *client, const void *data, size_t len)
Process STREAM_STOP packet - client requests to halt media transmission.
int send_server_state_to_client(client_info_t *client)
Send current server state to a specific client.
void handle_client_capabilities_packet(client_info_t *client, const void *data, size_t len)
Process CLIENT_CAPABILITIES packet - configure client-specific rendering.
void broadcast_clear_console_to_all_clients(void)
Signal all active clients to clear their displays before next video frame.
atomic_bool g_server_should_exit
Global shutdown flag from main.c - used to avoid error spam during shutdown.
void handle_stream_start_packet(client_info_t *client, const void *data, size_t len)
Process STREAM_START packet - client requests to begin media transmission.
void handle_ping_packet(client_info_t *client)
Process PING packet - respond with PONG for keepalive.
void handle_remote_log_packet_from_client(client_info_t *client, const void *data, size_t len)
client_manager_t g_client_manager
Global client manager singleton - central coordination point.
Per-client state management and lifecycle orchestration.
Server packet processing and protocol implementation.
Parsed audio batch packet header information.
uint32_t batch_count
Number of audio frames in this batch.
uint32_t sample_rate
Sample rate in Hz (e.g., 48000)
uint32_t total_samples
Total number of samples across all frames.
Audio batch packet structure (Packet Type 28)
Client information packet structure.
Per-client state structure for server-side client management.
atomic_bool video_render_thread_running
terminal_capabilities_t terminal_caps
acip_transport_t * transport
char display_name[MAX_DISPLAY_NAME_LEN]
uint32_t frames_received_logged
crypto_handshake_context_t crypto_handshake_ctx
char client_palette_chars[256]
video_frame_buffer_t * incoming_video_buffer
bool client_palette_initialized
audio_ring_buffer_t * incoming_audio_buffer
atomic_bool protocol_disconnect_requested
mutex_t client_state_mutex
atomic_bool is_sending_audio
atomic_bool audio_render_thread_running
packet_queue_t * audio_queue
atomic_bool shutting_down
char client_luminance_palette[256]
atomic_bool is_sending_video
palette_type_t client_palette_type
size_t client_palette_len
atomic_bool send_thread_running
client_info_t clients[MAX_CLIENTS]
Array of client_info_t structures (backing storage)
Cryptographic context structure.
Opus codec context for encoding or decoding.
Protocol version negotiation packet structure (Packet Type 1)
Server state packet structure.
Terminal size update packet.
Terminal capabilities packet structure (Packet Type 5)
int palette_type
Palette type enum value (palette_type_t)
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
bool utf8_support
True if terminal supports UTF-8 encoding.
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
uint32_t color_count
Maximum number of colors (16, 256, or 16777216)
char term_type[64]
$TERM environment variable value (for debugging)
char colorterm[64]
$COLORTERM environment variable value (for debugging)
bool detection_reliable
True if detection is confident (reliable detection)
bool wants_background
True if background colors are preferred.
uint64_t sequence_number
Frame sequence number (for drop detection)
uint32_t height
Frame height in pixels.
uint64_t capture_timestamp_us
Timestamp when frame was captured (microseconds)
size_t size
Size of frame data in bytes.
uint32_t width
Frame width in pixels.
void * data
Frame data pointer (points to pre-allocated buffer)
Cross-platform system functions interface for ascii-chat.
⏱️ High-precision timing utilities using sokol_time.h and uthash
asciichat_error_t image_validate_buffer_size(size_t requested_size)
Validate buffer size against maximum allocation limit.
asciichat_error_t image_validate_dimensions(size_t width, size_t height)
Validate image dimensions (non-zero, within limits)
asciichat_error_t image_calc_rgb_size(size_t width, size_t height, size_t *out_size)
Calculate total RGB buffer size from dimensions.
🖼️ Safe overflow-checked buffer size calculations for images and video frames
Common validation macros to reduce duplication in protocol handlers.
Image Data Structures and Operations.
Common SIMD utilities and structures.