ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
network/acip/handlers.c
Go to the documentation of this file.
1
20#include <ascii-chat/log/log.h>
22#include <ascii-chat/common.h>
24#include <string.h>
25
26// =============================================================================
27// Handler Function Pointer Types
28// =============================================================================
29
30typedef asciichat_error_t (*acip_client_handler_func_t)(const void *payload, size_t payload_len,
31 const acip_client_callbacks_t *callbacks);
32
33typedef asciichat_error_t (*acip_server_handler_func_t)(const void *payload, size_t payload_len, void *client_ctx,
34 const acip_server_callbacks_t *callbacks);
35
36// =============================================================================
37// Packet Type to Handler Index Mapping (O(1) hash table lookup)
38// =============================================================================
39// Open-addressing hash table with linear probing. key=0 means empty slot.
40// Stores handler_idx+1 so that 0 can indicate "not found" after lookup.
41
42#define HANDLER_HASH_SIZE 32 // ~50% load factor for 14-16 entries
43#define CLIENT_HANDLER_COUNT 20 // Added 6 crypto handshake handlers
44#define SERVER_HANDLER_COUNT 20 // Added 1 H.265 image frame handler
45
53
54// Hash function: simple modulo
55#define HANDLER_HASH(type) ((type) % HANDLER_HASH_SIZE)
56
57// Lookup function: linear probing with full table scan
58// Must check all HANDLER_HASH_SIZE slots because gaps can exist in the hash table
59static inline int handler_hash_lookup(const handler_hash_entry_t *table, packet_type_t type) {
60 uint32_t h = HANDLER_HASH(type);
61 log_dev_every(4500 * US_PER_MS_INT, "HANDLER_HASH_LOOKUP: type=%d, hash=%u", type, h);
62 for (int i = 0; i < HANDLER_HASH_SIZE; i++) {
63 uint32_t slot = (h + i) % HANDLER_HASH_SIZE;
64 log_dev_every(4500 * US_PER_MS_INT, " Checking slot %u: key=%d, handler_idx=%d", slot, table[slot].key,
65 table[slot].handler_idx);
66 // Match found - return handler index immediately
67 if (table[slot].key == type) {
68 log_dev_every(4500 * US_PER_MS_INT, " Found match at slot %u, handler_idx=%d", slot, table[slot].handler_idx);
69 return table[slot].handler_idx;
70 }
71 // NOTE: Do NOT return on empty slot! Hash table can have gaps due to collision chains.
72 // Continue probing through entire table to find the key.
73 }
74 log_debug(" No match found after scanning all %u slots", HANDLER_HASH_SIZE);
75 return -1; // Not found after checking all slots
76}
77
78// Client packet type -> handler index hash table
79// Slots computed via linear probing: hash = type % 32
80// clang-format off
81static const handler_hash_entry_t g_client_handler_hash[HANDLER_HASH_SIZE] = {
82 [0] = {PACKET_TYPE_AUDIO_BATCH, 1}, // hash(4000)=0
83 [1] = {PACKET_TYPE_AUDIO_OPUS_BATCH, 2}, // hash(4001)=1
84 [7] = {PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, 15}, // hash(1104)=16, probed->7
85 [8] = {PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, 16}, // hash(1107)=19, probed->8
86 [9] = {PACKET_TYPE_PING, 6}, // hash(5001)=9
87 [10] = {PACKET_TYPE_PONG, 7}, // hash(5002)=10
88 [11] = {PACKET_TYPE_CRYPTO_AUTH_FAILED, 17}, // hash(1106)=18, probed->11
89 [13] = {PACKET_TYPE_CRYPTO_PARAMETERS, 19}, // hash(1101)=13
90 [14] = {PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE, 18}, // hash(1108)=20, probed->14
91 [15] = {PACKET_TYPE_CLEAR_CONSOLE, 8}, // hash(5007)=15
92 [16] = {PACKET_TYPE_SERVER_STATE, 3}, // hash(5008)=16
93 [17] = {PACKET_TYPE_CRYPTO_REKEY_REQUEST, 9}, // hash(1201)=17
94 [18] = {PACKET_TYPE_CRYPTO_REKEY_RESPONSE, 10}, // hash(1202)=18
95 [19] = {PACKET_TYPE_ERROR_MESSAGE, 4}, // hash(2003)=19
96 [20] = {PACKET_TYPE_REMOTE_LOG, 5}, // hash(2004)=20
97 [21] = {PACKET_TYPE_ACIP_SESSION_JOINED, 13}, // hash(6005)=21
98 [22] = {PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, 14}, // hash(1102)=14, probed->22
99 [24] = {PACKET_TYPE_ASCII_FRAME, 0}, // hash(3000)=24
100 [25] = {PACKET_TYPE_ACIP_WEBRTC_SDP, 11}, // hash(6009)=25
101 [26] = {PACKET_TYPE_ACIP_WEBRTC_ICE, 12}, // hash(6010)=26
102};
103// clang-format on
104
105// Server packet type -> handler index hash table
106// clang-format off
107static const handler_hash_entry_t g_server_handler_hash[HANDLER_HASH_SIZE] = {
108 [0] = {PACKET_TYPE_AUDIO_BATCH, 3}, // hash(4000)=0
109 [1] = {PACKET_TYPE_PROTOCOL_VERSION, 0}, // hash(1)=1
110 [2] = {PACKET_TYPE_AUDIO_OPUS_BATCH, 4}, // hash(4001)=1, probed->2
111 [8] = {PACKET_TYPE_CLIENT_CAPABILITIES, 5}, // hash(5000)=8
112 [9] = {PACKET_TYPE_PING, 6}, // hash(5001)=9
113 [10] = {PACKET_TYPE_PONG, 7}, // hash(5002)=10
114 [11] = {PACKET_TYPE_CLIENT_JOIN, 8}, // hash(5003)=11
115 [12] = {PACKET_TYPE_CLIENT_LEAVE, 9}, // hash(5004)=12
116 [13] = {PACKET_TYPE_STREAM_START, 10}, // hash(5005)=13
117 [14] = {PACKET_TYPE_STREAM_STOP, 11}, // hash(5006)=14
118 [15] = {PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP, 17}, // hash(1103)=15
119 [17] = {PACKET_TYPE_CRYPTO_REKEY_REQUEST, 14}, // hash(1201)=17
120 [18] = {PACKET_TYPE_CRYPTO_REKEY_RESPONSE, 15}, // hash(1202)=18
121 [19] = {PACKET_TYPE_ERROR_MESSAGE, 13}, // hash(2003)=19
122 [20] = {PACKET_TYPE_REMOTE_LOG, 12}, // hash(2004)=20
123 [21] = {PACKET_TYPE_CRYPTO_REKEY_COMPLETE, 16}, // hash(1203)=19, probed->21
124 [22] = {PACKET_TYPE_CRYPTO_AUTH_RESPONSE, 18}, // hash(1105)=17, probed->22
125 [23] = {PACKET_TYPE_CRYPTO_NO_ENCRYPTION, 19}, // hash(1109)=21, probed->23
126 [25] = {PACKET_TYPE_IMAGE_FRAME, 1}, // hash(3001)=25
127 [26] = {PACKET_TYPE_IMAGE_FRAME_H265, 2}, // hash(3002)=26
128};
129// clang-format on
130
131// =============================================================================
132// Client-Side Packet Handlers
133// =============================================================================
134
135// Forward declarations for client handlers
136static asciichat_error_t handle_client_ascii_frame(const void *payload, size_t payload_len,
137 const acip_client_callbacks_t *callbacks);
138static asciichat_error_t handle_client_audio_batch(const void *payload, size_t payload_len,
139 const acip_client_callbacks_t *callbacks);
140static asciichat_error_t handle_client_audio_opus_batch(const void *payload, size_t payload_len,
141 const acip_client_callbacks_t *callbacks);
142static asciichat_error_t handle_client_server_state(const void *payload, size_t payload_len,
143 const acip_client_callbacks_t *callbacks);
144static asciichat_error_t handle_client_error_message(const void *payload, size_t payload_len,
145 const acip_client_callbacks_t *callbacks);
146static asciichat_error_t handle_client_remote_log(const void *payload, size_t payload_len,
147 const acip_client_callbacks_t *callbacks);
148static asciichat_error_t handle_client_ping(const void *payload, size_t payload_len,
149 const acip_client_callbacks_t *callbacks);
150static asciichat_error_t handle_client_pong(const void *payload, size_t payload_len,
151 const acip_client_callbacks_t *callbacks);
152static asciichat_error_t handle_client_clear_console(const void *payload, size_t payload_len,
153 const acip_client_callbacks_t *callbacks);
154static asciichat_error_t handle_client_crypto_rekey_request(const void *payload, size_t payload_len,
155 const acip_client_callbacks_t *callbacks);
156static asciichat_error_t handle_client_crypto_rekey_response(const void *payload, size_t payload_len,
157 const acip_client_callbacks_t *callbacks);
158static asciichat_error_t handle_client_webrtc_sdp(const void *payload, size_t payload_len,
159 const acip_client_callbacks_t *callbacks);
160static asciichat_error_t handle_client_webrtc_ice(const void *payload, size_t payload_len,
161 const acip_client_callbacks_t *callbacks);
162static asciichat_error_t handle_client_session_joined(const void *payload, size_t payload_len,
163 const acip_client_callbacks_t *callbacks);
164static asciichat_error_t handle_client_crypto_key_exchange_init(const void *payload, size_t payload_len,
165 const acip_client_callbacks_t *callbacks);
166static asciichat_error_t handle_client_crypto_auth_challenge(const void *payload, size_t payload_len,
167 const acip_client_callbacks_t *callbacks);
168static asciichat_error_t handle_client_crypto_server_auth_resp(const void *payload, size_t payload_len,
169 const acip_client_callbacks_t *callbacks);
170static asciichat_error_t handle_client_crypto_auth_failed(const void *payload, size_t payload_len,
171 const acip_client_callbacks_t *callbacks);
172static asciichat_error_t handle_client_crypto_handshake_complete(const void *payload, size_t payload_len,
173 const acip_client_callbacks_t *callbacks);
174static asciichat_error_t handle_client_crypto_parameters(const void *payload, size_t payload_len,
175 const acip_client_callbacks_t *callbacks);
176
177// Client handler dispatch table (indexed by client_handler_index())
178static const acip_client_handler_func_t g_client_handlers[CLIENT_HANDLER_COUNT] = {
179 handle_client_ascii_frame, // 0
180 handle_client_audio_batch, // 1
181 handle_client_audio_opus_batch, // 2
182 handle_client_server_state, // 3
183 handle_client_error_message, // 4
184 handle_client_remote_log, // 5
185 handle_client_ping, // 6
186 handle_client_pong, // 7
187 handle_client_clear_console, // 8
188 handle_client_crypto_rekey_request, // 9
189 handle_client_crypto_rekey_response, // 10
190 handle_client_webrtc_sdp, // 11
191 handle_client_webrtc_ice, // 12
192 handle_client_session_joined, // 13
193 handle_client_crypto_key_exchange_init, // 14
194 handle_client_crypto_auth_challenge, // 15
195 handle_client_crypto_server_auth_resp, // 16
196 handle_client_crypto_auth_failed, // 17
197 handle_client_crypto_handshake_complete, // 18
198 handle_client_crypto_parameters, // 19
199};
200
202 size_t payload_len, const acip_client_callbacks_t *callbacks) {
203 if (!transport || !callbacks) {
204 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or callbacks");
205 }
206 (void)transport;
207
208 // ACDS broadcasts participant lifecycle notifications to peers. The generic
209 // client callback set does not expose them, so ignore them while keeping the
210 // signaling receive loop alive for subsequent SDP and ICE packets.
212 return ASCIICHAT_OK;
213 }
214
215 // O(1) dispatch via hash table lookup
216 int idx = handler_hash_lookup(g_client_handler_hash, type);
217 if (idx < 0) {
218 return SET_ERRNO(ERROR_INVALID_PARAM, "Unhandled client packet type: %d", type);
219 }
220
221 return g_client_handlers[idx](payload, payload_len, callbacks);
222}
223
224// =============================================================================
225// Client Handler Implementations (extracted from switch cases)
226// =============================================================================
227
228static asciichat_error_t handle_client_ascii_frame(const void *payload, size_t payload_len,
229 const acip_client_callbacks_t *callbacks) {
230 if (!callbacks->on_ascii_frame) {
231 return ASCIICHAT_OK;
232 }
233
234 if (payload_len < sizeof(ascii_frame_packet_t)) {
235 return SET_ERRNO(ERROR_INVALID_PARAM, "ASCII_FRAME payload too small");
236 }
237
238 // Extract header
240 memcpy(&header, payload, sizeof(header));
241
242 // Convert from network byte order
243 header.width = NET_TO_HOST_U32(header.width);
244 header.height = NET_TO_HOST_U32(header.height);
247 header.checksum = NET_TO_HOST_U32(header.checksum);
248 header.flags = NET_TO_HOST_U32(header.flags);
249
250 // Get frame data (after header)
251 const void *frame_data = (const uint8_t *)payload + sizeof(ascii_frame_packet_t);
252 size_t frame_data_len = payload_len - sizeof(ascii_frame_packet_t);
253
254 // Validate frame dimensions to prevent DoS and buffer overflow attacks
255 if (header.width == 0 || header.height == 0) {
256 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid frame dimensions: %ux%u (width and height must be > 0)",
257 header.width, header.height);
258 }
259
260 // Sanity check: prevent unreasonably large frames (e.g., > 16K resolution)
261 // This protects against resource exhaustion attacks
262 const uint32_t MAX_WIDTH = 16384; // 16K
263 const uint32_t MAX_HEIGHT = 16384; // 16K
264 if (header.width > MAX_WIDTH || header.height > MAX_HEIGHT) {
265 return SET_ERRNO(ERROR_INVALID_PARAM, "Frame dimensions too large: %ux%u (max: %ux%u)", header.width, header.height,
266 MAX_WIDTH, MAX_HEIGHT);
267 }
268
269 callbacks->on_ascii_frame(&header, frame_data, frame_data_len, callbacks->app_ctx);
270 return ASCIICHAT_OK;
271}
272
273static asciichat_error_t handle_client_audio_batch(const void *payload, size_t payload_len,
274 const acip_client_callbacks_t *callbacks) {
275 if (!callbacks->on_audio_batch) {
276 return ASCIICHAT_OK;
277 }
278
279 if (payload_len < sizeof(audio_batch_packet_t)) {
280 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH payload too small");
281 }
282
283 // Parse batch header
284 const audio_batch_packet_t *batch_header = (const audio_batch_packet_t *)payload;
285 uint32_t batch_count = NET_TO_HOST_U32(batch_header->batch_count);
286 uint32_t total_samples = NET_TO_HOST_U32(batch_header->total_samples);
287 uint32_t sample_rate = NET_TO_HOST_U32(batch_header->sample_rate);
288 uint32_t channels = NET_TO_HOST_U32(batch_header->channels);
289
290 // Validate sample rate and channels
291 // Supported sample rates: 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000 Hz
292 if (sample_rate == 0 || (sample_rate < 8000 || sample_rate > 192000) ||
293 (sample_rate != 8000 && sample_rate != 16000 && sample_rate != 24000 && sample_rate != 32000 &&
294 sample_rate != 44100 && sample_rate != 48000 && sample_rate != 96000 && sample_rate != 192000)) {
295 return SET_ERRNO(
297 "Invalid audio sample rate: %u Hz (expected: 8000, 16000, 24000, 32000, 44100, 48000, 96000, or 192000)",
298 sample_rate);
299 }
300
301 // Validate channel count (1-8 channels supported)
302 if (channels == 0 || channels > 8) {
303 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid audio channel count: %u (expected: 1-8)", channels);
304 }
305
306 // Validate batch count
307 (void)batch_count;
308
309 // Validate size
310 size_t expected_size = sizeof(audio_batch_packet_t) + (total_samples * sizeof(uint32_t));
311 if (payload_len != expected_size) {
312 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH size mismatch");
313 }
314
315 // Extract quantized samples
316 const uint8_t *samples_ptr = (const uint8_t *)payload + sizeof(audio_batch_packet_t);
317
318 // Dequantize samples to float
319 float *samples = SAFE_MALLOC(total_samples * sizeof(float), float *);
320 if (!samples) {
321 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate audio batch buffer");
322 }
323
324 asciichat_error_t result = audio_dequantize_samples(samples_ptr, total_samples, samples);
325 if (result != ASCIICHAT_OK) {
326 SAFE_FREE(samples);
327 return result;
328 }
329
330 // Copy header for callback
331 audio_batch_packet_t header_copy = *batch_header;
332 header_copy.batch_count = batch_count;
333 header_copy.total_samples = total_samples;
334 header_copy.sample_rate = sample_rate;
335 header_copy.channels = channels;
336
337 callbacks->on_audio_batch(&header_copy, samples, total_samples, callbacks->app_ctx);
338
339 SAFE_FREE(samples);
340 return ASCIICHAT_OK;
341}
342
343static asciichat_error_t handle_client_audio_opus_batch(const void *payload, size_t payload_len,
344 const acip_client_callbacks_t *callbacks) {
345 if (!callbacks->on_audio_opus_batch) {
346 return ASCIICHAT_OK;
347 }
348
349 // Opus batch data (header + sizes + opus frames)
350 callbacks->on_audio_opus_batch(payload, payload_len, callbacks->app_ctx);
351 return ASCIICHAT_OK;
352}
353
354static asciichat_error_t handle_client_server_state(const void *payload, size_t payload_len,
355 const acip_client_callbacks_t *callbacks) {
356 if (!callbacks->on_server_state) {
357 return ASCIICHAT_OK;
358 }
359
360 if (payload_len != sizeof(server_state_packet_t)) {
361 return SET_ERRNO(ERROR_INVALID_PARAM, "SERVER_STATE size mismatch");
362 }
363
364 callbacks->on_server_state((const server_state_packet_t *)payload, callbacks->app_ctx);
365 return ASCIICHAT_OK;
366}
367
368static asciichat_error_t handle_client_error_message(const void *payload, size_t payload_len,
369 const acip_client_callbacks_t *callbacks) {
370 if (!callbacks->on_error) {
371 return ASCIICHAT_OK;
372 }
373
374 if (payload_len < sizeof(error_packet_t)) {
375 return SET_ERRNO(ERROR_INVALID_PARAM, "ERROR_MESSAGE payload too small");
376 }
377
378 const error_packet_t *header = (const error_packet_t *)payload;
379 const char *message = (const char *)payload + sizeof(error_packet_t);
380 size_t msg_len = payload_len - sizeof(error_packet_t);
381
382 // Ensure null-terminated message
383 char msg_buffer[MAX_ERROR_MESSAGE_LENGTH + 1];
384 size_t copy_len = msg_len < MAX_ERROR_MESSAGE_LENGTH ? msg_len : MAX_ERROR_MESSAGE_LENGTH;
385 memcpy(msg_buffer, message, copy_len);
386 msg_buffer[copy_len] = '\0';
387
388 callbacks->on_error(header, msg_buffer, callbacks->app_ctx);
389 return ASCIICHAT_OK;
390}
391
392static asciichat_error_t handle_client_remote_log(const void *payload, size_t payload_len,
393 const acip_client_callbacks_t *callbacks) {
394 if (!callbacks->on_remote_log) {
395 return ASCIICHAT_OK;
396 }
397
398 if (payload_len < sizeof(remote_log_packet_t)) {
399 return SET_ERRNO(ERROR_INVALID_PARAM, "REMOTE_LOG payload too small");
400 }
401
402 const remote_log_packet_t *header = (const remote_log_packet_t *)payload;
403 const char *message = (const char *)payload + sizeof(remote_log_packet_t);
404 size_t msg_len = payload_len - sizeof(remote_log_packet_t);
405
406 // Ensure null-terminated message
407 char msg_buffer[512];
408 size_t copy_len = msg_len < sizeof(msg_buffer) - 1 ? msg_len : sizeof(msg_buffer) - 1;
409 memcpy(msg_buffer, message, copy_len);
410 msg_buffer[copy_len] = '\0';
411
412 callbacks->on_remote_log(header, msg_buffer, callbacks->app_ctx);
413 return ASCIICHAT_OK;
414}
415
416static asciichat_error_t handle_client_ping(const void *payload, size_t payload_len,
417 const acip_client_callbacks_t *callbacks) {
418 (void)payload;
419 (void)payload_len;
420
421 if (callbacks->on_ping) {
422 callbacks->on_ping(callbacks->app_ctx);
423 }
424 return ASCIICHAT_OK;
425}
426
427static asciichat_error_t handle_client_pong(const void *payload, size_t payload_len,
428 const acip_client_callbacks_t *callbacks) {
429 (void)payload;
430 (void)payload_len;
431
432 if (callbacks->on_pong) {
433 callbacks->on_pong(callbacks->app_ctx);
434 }
435 return ASCIICHAT_OK;
436}
437
438static asciichat_error_t handle_client_clear_console(const void *payload, size_t payload_len,
439 const acip_client_callbacks_t *callbacks) {
440 (void)payload;
441 (void)payload_len;
442
443 if (callbacks->on_clear_console) {
444 callbacks->on_clear_console(callbacks->app_ctx);
445 }
446 return ASCIICHAT_OK;
447}
448
449static asciichat_error_t handle_client_crypto_rekey_request(const void *payload, size_t payload_len,
450 const acip_client_callbacks_t *callbacks) {
451 if (callbacks->on_crypto_rekey_request) {
452 callbacks->on_crypto_rekey_request(payload, payload_len, callbacks->app_ctx);
453 }
454 return ASCIICHAT_OK;
455}
456
457static asciichat_error_t handle_client_crypto_rekey_response(const void *payload, size_t payload_len,
458 const acip_client_callbacks_t *callbacks) {
459 if (callbacks->on_crypto_rekey_response) {
460 callbacks->on_crypto_rekey_response(payload, payload_len, callbacks->app_ctx);
461 }
462 return ASCIICHAT_OK;
463}
464
465static asciichat_error_t handle_client_webrtc_sdp(const void *payload, size_t payload_len,
466 const acip_client_callbacks_t *callbacks) {
467 if (!callbacks->on_webrtc_sdp) {
468 return ASCIICHAT_OK;
469 }
470
471 if (payload_len < sizeof(acip_webrtc_sdp_t)) {
472 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_SDP payload too small");
473 }
474
475 const acip_webrtc_sdp_t *sdp = (const acip_webrtc_sdp_t *)payload;
476 callbacks->on_webrtc_sdp(sdp, payload_len, callbacks->app_ctx);
477 return ASCIICHAT_OK;
478}
479
480static asciichat_error_t handle_client_webrtc_ice(const void *payload, size_t payload_len,
481 const acip_client_callbacks_t *callbacks) {
482 if (!callbacks->on_webrtc_ice) {
483 return ASCIICHAT_OK;
484 }
485
486 if (payload_len < sizeof(acip_webrtc_ice_t)) {
487 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_ICE payload too small");
488 }
489
490 const acip_webrtc_ice_t *ice = (const acip_webrtc_ice_t *)payload;
491 callbacks->on_webrtc_ice(ice, payload_len, callbacks->app_ctx);
492 return ASCIICHAT_OK;
493}
494
495static asciichat_error_t handle_client_session_joined(const void *payload, size_t payload_len,
496 const acip_client_callbacks_t *callbacks) {
497 if (!callbacks->on_session_joined) {
498 return ASCIICHAT_OK;
499 }
500
501 if (payload_len < sizeof(acip_session_joined_t)) {
502 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_JOINED payload too small (got %zu, need %zu)", payload_len,
503 sizeof(acip_session_joined_t));
504 }
505
506 // Parse the session_joined response
507 const acip_session_joined_t *joined = (const acip_session_joined_t *)payload;
508
509 // Log the result
510 if (joined->success) {
511 log_debug("Session join succeeded: session_id=%.16s, participant_id=%.16s, server=%s:%u, type=%s",
512 (const char *)joined->session_id, (const char *)joined->participant_id, joined->server_address,
513 joined->server_port, joined->session_type == 1 ? "WebRTC" : "DirectTCP");
514 } else {
515 log_warn("Session join failed: error %d: %s", joined->error_code, joined->error_message);
516 }
517
518 // Dispatch to application callback
519 callbacks->on_session_joined(joined, callbacks->app_ctx);
520 return ASCIICHAT_OK;
521}
522
523static asciichat_error_t handle_client_crypto_key_exchange_init(const void *payload, size_t payload_len,
524 const acip_client_callbacks_t *callbacks) {
525 if (!callbacks->on_crypto_key_exchange_init) {
526 return ASCIICHAT_OK;
527 }
528
529 // Dispatch to application callback with packet type
531 callbacks->app_ctx);
532 return ASCIICHAT_OK;
533}
534
535static asciichat_error_t handle_client_crypto_auth_challenge(const void *payload, size_t payload_len,
536 const acip_client_callbacks_t *callbacks) {
537 if (!callbacks->on_crypto_auth_challenge) {
538 return ASCIICHAT_OK;
539 }
540
541 // Dispatch to application callback with packet type
542 callbacks->on_crypto_auth_challenge(PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, payload, payload_len, callbacks->app_ctx);
543 return ASCIICHAT_OK;
544}
545
546static asciichat_error_t handle_client_crypto_server_auth_resp(const void *payload, size_t payload_len,
547 const acip_client_callbacks_t *callbacks) {
548 if (!callbacks->on_crypto_server_auth_resp) {
549 return ASCIICHAT_OK;
550 }
551
552 // Dispatch to application callback with packet type
553 callbacks->on_crypto_server_auth_resp(PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, payload, payload_len, callbacks->app_ctx);
554 return ASCIICHAT_OK;
555}
556
557static asciichat_error_t handle_client_crypto_auth_failed(const void *payload, size_t payload_len,
558 const acip_client_callbacks_t *callbacks) {
559 if (!callbacks->on_crypto_auth_failed) {
560 return ASCIICHAT_OK;
561 }
562
563 // Dispatch to application callback with packet type
564 callbacks->on_crypto_auth_failed(PACKET_TYPE_CRYPTO_AUTH_FAILED, payload, payload_len, callbacks->app_ctx);
565 return ASCIICHAT_OK;
566}
567
568static asciichat_error_t handle_client_crypto_handshake_complete(const void *payload, size_t payload_len,
569 const acip_client_callbacks_t *callbacks) {
570 if (!callbacks->on_crypto_handshake_complete) {
571 return ASCIICHAT_OK;
572 }
573
574 // Dispatch to application callback with packet type
576 callbacks->app_ctx);
577 return ASCIICHAT_OK;
578}
579
580static asciichat_error_t handle_client_crypto_parameters(const void *payload, size_t payload_len,
581 const acip_client_callbacks_t *callbacks) {
582 if (!callbacks->on_crypto_parameters) {
583 return ASCIICHAT_OK;
584 }
585
586 // Dispatch to application callback with packet type
587 callbacks->on_crypto_parameters(PACKET_TYPE_CRYPTO_PARAMETERS, payload, payload_len, callbacks->app_ctx);
588 return ASCIICHAT_OK;
589}
590
591// =============================================================================
592// Server-Side Packet Handlers
593// =============================================================================
594
595// Forward declarations for server handlers
596static asciichat_error_t handle_server_image_frame(const void *payload, size_t payload_len, void *client_ctx,
597 const acip_server_callbacks_t *callbacks);
598static asciichat_error_t handle_server_image_frame_h265(const void *payload, size_t payload_len, void *client_ctx,
599 const acip_server_callbacks_t *callbacks);
600static asciichat_error_t handle_server_audio_batch(const void *payload, size_t payload_len, void *client_ctx,
601 const acip_server_callbacks_t *callbacks);
602static asciichat_error_t handle_server_audio_opus_batch(const void *payload, size_t payload_len, void *client_ctx,
603 const acip_server_callbacks_t *callbacks);
604static asciichat_error_t handle_server_client_join(const void *payload, size_t payload_len, void *client_ctx,
605 const acip_server_callbacks_t *callbacks);
606static asciichat_error_t handle_server_client_leave(const void *payload, size_t payload_len, void *client_ctx,
607 const acip_server_callbacks_t *callbacks);
608static asciichat_error_t handle_server_stream_start(const void *payload, size_t payload_len, void *client_ctx,
609 const acip_server_callbacks_t *callbacks);
610static asciichat_error_t handle_server_stream_stop(const void *payload, size_t payload_len, void *client_ctx,
611 const acip_server_callbacks_t *callbacks);
612static asciichat_error_t handle_server_capabilities(const void *payload, size_t payload_len, void *client_ctx,
613 const acip_server_callbacks_t *callbacks);
614static asciichat_error_t handle_server_ping(const void *payload, size_t payload_len, void *client_ctx,
615 const acip_server_callbacks_t *callbacks);
616static asciichat_error_t handle_server_remote_log(const void *payload, size_t payload_len, void *client_ctx,
617 const acip_server_callbacks_t *callbacks);
618static asciichat_error_t handle_server_protocol_version(const void *payload, size_t payload_len, void *client_ctx,
619 const acip_server_callbacks_t *callbacks);
620static asciichat_error_t handle_server_pong(const void *payload, size_t payload_len, void *client_ctx,
621 const acip_server_callbacks_t *callbacks);
622static asciichat_error_t handle_server_error_message(const void *payload, size_t payload_len, void *client_ctx,
623 const acip_server_callbacks_t *callbacks);
624static asciichat_error_t handle_server_crypto_rekey_request(const void *payload, size_t payload_len, void *client_ctx,
625 const acip_server_callbacks_t *callbacks);
626static asciichat_error_t handle_server_crypto_rekey_response(const void *payload, size_t payload_len, void *client_ctx,
627 const acip_server_callbacks_t *callbacks);
628static asciichat_error_t handle_server_crypto_rekey_complete(const void *payload, size_t payload_len, void *client_ctx,
629 const acip_server_callbacks_t *callbacks);
630static asciichat_error_t handle_server_crypto_key_exchange_resp(const void *payload, size_t payload_len,
631 void *client_ctx,
632 const acip_server_callbacks_t *callbacks);
633static asciichat_error_t handle_server_crypto_auth_response(const void *payload, size_t payload_len, void *client_ctx,
634 const acip_server_callbacks_t *callbacks);
635static asciichat_error_t handle_server_crypto_no_encryption(const void *payload, size_t payload_len, void *client_ctx,
636 const acip_server_callbacks_t *callbacks);
637
638// Server handler dispatch table (indexed by server_handler_index())
639static const acip_server_handler_func_t g_server_handlers[SERVER_HANDLER_COUNT] = {
640 handle_server_protocol_version, // 0
641 handle_server_image_frame, // 1
642 handle_server_image_frame_h265, // 2
643 handle_server_audio_batch, // 3
644 handle_server_audio_opus_batch, // 4
645 handle_server_capabilities, // 5
646 handle_server_ping, // 6
647 handle_server_pong, // 7
648 handle_server_client_join, // 8
649 handle_server_client_leave, // 9
650 handle_server_stream_start, // 10
651 handle_server_stream_stop, // 11
652 handle_server_remote_log, // 12
653 handle_server_error_message, // 13
654 handle_server_crypto_rekey_request, // 14
655 handle_server_crypto_rekey_response, // 15
656 handle_server_crypto_rekey_complete, // 16
657 handle_server_crypto_key_exchange_resp, // 17
658 handle_server_crypto_auth_response, // 18
659 handle_server_crypto_no_encryption, // 19
660};
661
662// Packet type names for debugging (matches handler table order)
663static const char *g_packet_type_name(packet_type_t type) {
664 switch (type) {
666 return "PROTOCOL_VERSION";
668 return "IMAGE_FRAME";
670 return "IMAGE_FRAME_H265";
672 return "AUDIO_BATCH";
674 return "AUDIO_OPUS_BATCH";
676 return "CLIENT_CAPABILITIES";
677 case PACKET_TYPE_PING:
678 return "PING";
679 case PACKET_TYPE_PONG:
680 return "PONG";
682 return "CLIENT_JOIN";
684 return "CLIENT_LEAVE";
686 return "STREAM_START";
688 return "STREAM_STOP";
690 return "CRYPTO_KEY_EXCHANGE_RESP";
692 return "CRYPTO_REKEY_REQUEST";
694 return "CRYPTO_REKEY_RESPONSE";
696 return "ERROR_MESSAGE";
698 return "REMOTE_LOG";
700 return "CRYPTO_REKEY_COMPLETE";
702 return "CRYPTO_AUTH_RESPONSE";
704 return "CRYPTO_NO_ENCRYPTION";
705 default:
706 return "UNKNOWN";
707 }
708}
709
711 size_t payload_len, void *client_ctx,
712 const acip_server_callbacks_t *callbacks) {
713 if (!transport || !callbacks) {
714 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or callbacks");
715 }
716 (void)transport;
717
718 // DEBUG: Log all packet types received
719 log_dev("ACIP_HANDLE: type=%d payload_len=%zu", type, payload_len);
720
721 // O(1) dispatch via hash table lookup
722 int idx = handler_hash_lookup(g_server_handler_hash, type);
723 if (idx < 0) {
724 log_error("ACIP_HANDLER_NOT_FOUND: No handler for packet type=%d (0x%04x, name=%s)", type, type,
725 g_packet_type_name(type));
726 return SET_ERRNO(ERROR_INVALID_PARAM, "Unhandled server packet type: %d", type);
727 }
728
729 log_dev("HANDLER_DISPATCH: type=%d (%s), handler_idx=%d, payload=%zu bytes",
730 type, g_packet_type_name(type), idx, payload_len);
731 asciichat_error_t result = g_server_handlers[idx](payload, payload_len, client_ctx, callbacks);
732
733 if (result != ASCIICHAT_OK) {
734 log_error("❌ [HANDLER_ERROR] Handler failed: type=%d (%s), result=%d", type, g_packet_type_name(type), result);
735 } else {
736 log_dev("HANDLER_COMPLETE: type=%d (%s), payload=%zu bytes", type,
737 g_packet_type_name(type), payload_len);
738 }
739
740 return result;
741}
742
743// =============================================================================
744// Server Handler Implementations (extracted from switch cases)
745// =============================================================================
746
747static asciichat_error_t handle_server_image_frame(const void *payload, size_t payload_len, void *client_ctx,
748 const acip_server_callbacks_t *callbacks) {
749 log_dev("ACIP_IMAGE_FRAME_HANDLER: payload_len=%zu", payload_len);
750
751 if (!callbacks->on_image_frame) {
752 log_warn("ACIP_IMAGE_FRAME_HANDLER: No callback registered for on_image_frame");
753 return ASCIICHAT_OK;
754 }
755
756 log_dev("ACIP_IMAGE_FRAME_HANDLER: Callback registered (header=%zu)",
757 sizeof(image_frame_packet_t));
758
759 if (payload_len < sizeof(uint32_t) * 2) {
760 log_error("ACIP_IMAGE_FRAME_HANDLER: Payload too small: %zu bytes (need at least 8)", payload_len);
761 return SET_ERRNO(ERROR_INVALID_PARAM, "IMAGE_FRAME payload too small: %zu bytes (need at least 8)", payload_len);
762 }
763
764 // Debug: Log raw payload bytes
765 const uint8_t *payload_bytes = (const uint8_t *)payload;
766 log_dev_every(4500 * US_PER_MS_INT, "IMAGE_FRAME payload (%zu bytes):", payload_len);
767 char hex_buf[512];
768 size_t hex_pos = 0;
769 size_t max_bytes = (payload_len < 48) ? payload_len : 48;
770 for (size_t i = 0; i < max_bytes && hex_pos < sizeof(hex_buf) - 4; i++) {
771 hex_pos += snprintf(hex_buf + hex_pos, sizeof(hex_buf) - hex_pos, "%02x ", payload_bytes[i]);
772 }
773 log_dev_every(4500 * US_PER_MS_INT, " First bytes: %s", hex_buf);
774
775 // Older browser clients send [width:u32][height:u32][RGB24 pixels]. Accept
776 // that exact size alongside the current image_frame_packet_t wire header.
777 uint32_t legacy_width_net;
778 uint32_t legacy_height_net;
779 memcpy(&legacy_width_net, payload_bytes, sizeof(legacy_width_net));
780 memcpy(&legacy_height_net, payload_bytes + sizeof(legacy_width_net), sizeof(legacy_height_net));
781 uint32_t legacy_width = NET_TO_HOST_U32(legacy_width_net);
782 uint32_t legacy_height = NET_TO_HOST_U32(legacy_height_net);
783 size_t legacy_pixels = 0;
784 size_t legacy_rgb_size = 0;
785 size_t legacy_payload_size = 0;
786 if (legacy_width > 0 && legacy_height > 0 && legacy_width <= 8192 && legacy_height <= 8192 &&
787 checked_size_mul(legacy_width, legacy_height, &legacy_pixels) == ASCIICHAT_OK &&
788 checked_size_mul(legacy_pixels, 3, &legacy_rgb_size) == ASCIICHAT_OK &&
789 checked_size_add(sizeof(uint32_t) * 2, legacy_rgb_size, &legacy_payload_size) == ASCIICHAT_OK &&
790 payload_len == legacy_payload_size) {
791 image_frame_packet_t legacy_header = {0};
792 legacy_header.width = legacy_width;
793 legacy_header.height = legacy_height;
794 legacy_header.pixel_format = 1; // RGB24
795 callbacks->on_image_frame(&legacy_header, payload_bytes + sizeof(uint32_t) * 2, legacy_rgb_size, client_ctx,
796 callbacks->app_ctx);
797 return ASCIICHAT_OK;
798 }
799
800 if (payload_len < sizeof(image_frame_packet_t)) {
801 return SET_ERRNO(ERROR_INVALID_PARAM, "IMAGE_FRAME payload too small: %zu bytes (need %zu)", payload_len,
802 sizeof(image_frame_packet_t));
803 }
804
805 // Extract header
807 memcpy(&header, payload, sizeof(header));
808
809 // Debug: Log raw header before byte order conversion
810 log_dev_every(4500 * US_PER_MS_INT, "IMAGE_FRAME header (raw network order):");
811 log_dev_every(4500 * US_PER_MS_INT, " width=0x%08x height=0x%08x pixel_format=0x%08x compressed_size=0x%08x",
812 header.width, header.height, header.pixel_format, header.compressed_size);
813
814 // Convert from network byte order
815 header.width = NET_TO_HOST_U32(header.width);
816 header.height = NET_TO_HOST_U32(header.height);
819 header.checksum = NET_TO_HOST_U32(header.checksum);
820 header.timestamp = NET_TO_HOST_U32(header.timestamp);
821
822 // Debug: Log header after byte order conversion
823 log_dev_every(4500 * US_PER_MS_INT, "IMAGE_FRAME header (after host byte order):");
824 log_dev_every(4500 * US_PER_MS_INT, " width=%u height=%u pixel_format=%u compressed_size=%u", header.width,
825 header.height, header.pixel_format, header.compressed_size);
826
827 // Get pixel data (after header)
828 const void *pixel_data = (const uint8_t *)payload + sizeof(image_frame_packet_t);
829 size_t pixel_data_len = payload_len - sizeof(image_frame_packet_t);
830
831 // Validate frame dimensions to prevent DoS and buffer overflow attacks
832 if (header.width == 0 || header.height == 0) {
833 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid image dimensions: %ux%u (width and height must be > 0)",
834 header.width, header.height);
835 }
836
837 // Sanity check: prevent unreasonably large frames (e.g., > 8K resolution for RGB)
838 // This protects against resource exhaustion attacks
839 const uint32_t MAX_WIDTH = 8192; // 8K for RGB data
840 const uint32_t MAX_HEIGHT = 8192; // 8K for RGB data
841 if (header.width > MAX_WIDTH || header.height > MAX_HEIGHT) {
842 return SET_ERRNO(ERROR_INVALID_PARAM, "Image dimensions too large: %ux%u (max: %ux%u)", header.width, header.height,
843 MAX_WIDTH, MAX_HEIGHT);
844 }
845
846 // Validate pixel format
847 // Valid formats: RGB24 (3), RGBA32 (4), YUV420 (1.5)
848 if (header.pixel_format == 0 || header.pixel_format > 4) {
849 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid pixel format: %u (expected: 1-4)", header.pixel_format);
850 }
851
852 log_dev(
853 "📹 [IMAGE_FRAME_CALLBACK] Invoking on_image_frame callback: %ux%u pixels, format=%u, %zu bytes, client_ctx=%p",
854 header.width, header.height, header.pixel_format, pixel_data_len, client_ctx);
855
856 callbacks->on_image_frame(&header, pixel_data, pixel_data_len, client_ctx, callbacks->app_ctx);
857
858 log_dev("IMAGE_FRAME_DONE");
859 return ASCIICHAT_OK;
860}
861
862static asciichat_error_t handle_server_image_frame_h265(const void *payload, size_t payload_len, void *client_ctx,
863 const acip_server_callbacks_t *callbacks) {
864 log_info("ACIP_IMAGE_FRAME_H265_HANDLER: Received IMAGE_FRAME_H265 packet, payload_len=%zu, client_ctx=%p",
865 payload_len, client_ctx);
866
867 if (!callbacks->on_image_frame_h265) {
868 log_warn("ACIP_IMAGE_FRAME_H265_HANDLER: No callback registered for on_image_frame_h265");
869 return ASCIICHAT_OK;
870 }
871
872 // Minimum header: [flags:u8][width:u16][height:u16] = 5 bytes
873 if (payload_len < 5) {
874 log_error("ACIP_IMAGE_FRAME_H265_HANDLER: Payload too small: %zu bytes (need at least 5)", payload_len);
875 return SET_ERRNO(ERROR_INVALID_PARAM, "IMAGE_FRAME_H265 payload too small: %zu bytes (need at least 5)",
876 payload_len);
877 }
878
879 const uint8_t *payload_bytes = (const uint8_t *)payload;
880
881 // Parse header: [flags:u8][width:u16][height:u16]
882 uint8_t flags = payload_bytes[0];
883 uint16_t width = ((uint16_t)payload_bytes[1] << 8) | payload_bytes[2];
884 uint16_t height = ((uint16_t)payload_bytes[3] << 8) | payload_bytes[4];
885
886 log_info("ACIP_IMAGE_FRAME_H265_HEADER: flags=0x%02x, width=%u, height=%u", flags, width, height);
887
888 // Get H.265 encoded data (after 5-byte header)
889 const void *h265_data = (const uint8_t *)payload + 5;
890 size_t h265_data_len = payload_len - 5;
891
892 // Validate frame dimensions to prevent DoS and buffer overflow attacks
893 if (width == 0 || height == 0) {
894 log_error("Invalid H.265 frame dimensions: %ux%u (width and height must be > 0)", width, height);
895 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid H.265 frame dimensions: %ux%u (width and height must be > 0)", width,
896 height);
897 }
898
899 // Sanity check: prevent unreasonably large frames
900 const uint32_t MAX_WIDTH = 8192; // 8K
901 const uint32_t MAX_HEIGHT = 8192; // 8K
902 if (width > MAX_WIDTH || height > MAX_HEIGHT) {
903 log_error("H.265 frame dimensions too large: %ux%u (max: %ux%u)", width, height, MAX_WIDTH, MAX_HEIGHT);
904 return SET_ERRNO(ERROR_INVALID_PARAM, "H.265 frame dimensions too large: %ux%u (max: %ux%u)", width, height,
905 MAX_WIDTH, MAX_HEIGHT);
906 }
907
908 log_info("📹 [IMAGE_FRAME_H265_CALLBACK] Invoking on_image_frame_h265 callback: %ux%u pixels, flags=0x%02x, %zu "
909 "bytes (H.265), client_ctx=%p",
910 width, height, flags, h265_data_len, client_ctx);
911
912 callbacks->on_image_frame_h265(width, height, flags, h265_data, h265_data_len, client_ctx, callbacks->app_ctx);
913
914 log_info("✅ [IMAGE_FRAME_H265_DONE] on_image_frame_h265 callback returned successfully");
915 return ASCIICHAT_OK;
916}
917
918static asciichat_error_t handle_server_audio_batch(const void *payload, size_t payload_len, void *client_ctx,
919 const acip_server_callbacks_t *callbacks) {
920 if (!callbacks->on_audio_batch) {
921 return ASCIICHAT_OK;
922 }
923
924 if (payload_len < sizeof(audio_batch_packet_t)) {
925 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH payload too small");
926 }
927
928 // Parse batch header
929 const audio_batch_packet_t *batch_header = (const audio_batch_packet_t *)payload;
930 uint32_t batch_count = NET_TO_HOST_U32(batch_header->batch_count);
931 uint32_t total_samples = NET_TO_HOST_U32(batch_header->total_samples);
932 uint32_t sample_rate = NET_TO_HOST_U32(batch_header->sample_rate);
933 uint32_t channels = NET_TO_HOST_U32(batch_header->channels);
934
935 // Validate size
936 size_t expected_size = sizeof(audio_batch_packet_t) + (total_samples * sizeof(uint32_t));
937 if (payload_len != expected_size) {
938 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH size mismatch");
939 }
940
941 // Extract quantized samples
942 const uint8_t *samples_ptr = (const uint8_t *)payload + sizeof(audio_batch_packet_t);
943
944 // Dequantize samples to float
945 float *samples = SAFE_MALLOC(total_samples * sizeof(float), float *);
946 if (!samples) {
947 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate audio batch buffer");
948 }
949
950 asciichat_error_t result = audio_dequantize_samples(samples_ptr, total_samples, samples);
951 if (result != ASCIICHAT_OK) {
952 SAFE_FREE(samples);
953 return result;
954 }
955
956 // Copy header for callback
957 audio_batch_packet_t header_copy = *batch_header;
958 header_copy.batch_count = batch_count;
959 header_copy.total_samples = total_samples;
960 header_copy.sample_rate = sample_rate;
961 header_copy.channels = channels;
962
963 callbacks->on_audio_batch(&header_copy, samples, total_samples, client_ctx, callbacks->app_ctx);
964
965 SAFE_FREE(samples);
966 return ASCIICHAT_OK;
967}
968
969static asciichat_error_t handle_server_audio_opus_batch(const void *payload, size_t payload_len, void *client_ctx,
970 const acip_server_callbacks_t *callbacks) {
971 if (!callbacks->on_audio_opus_batch) {
972 return ASCIICHAT_OK;
973 }
974
975 callbacks->on_audio_opus_batch(payload, payload_len, client_ctx, callbacks->app_ctx);
976 return ASCIICHAT_OK;
977}
978
979static asciichat_error_t handle_server_client_join(const void *payload, size_t payload_len, void *client_ctx,
980 const acip_server_callbacks_t *callbacks) {
981 if (!callbacks->on_client_join) {
982 return ASCIICHAT_OK;
983 }
984
985 callbacks->on_client_join(payload, payload_len, client_ctx, callbacks->app_ctx);
986 return ASCIICHAT_OK;
987}
988
989static asciichat_error_t handle_server_client_leave(const void *payload, size_t payload_len, void *client_ctx,
990 const acip_server_callbacks_t *callbacks) {
991 (void)payload;
992 (void)payload_len;
993
994 if (!callbacks->on_client_leave) {
995 return ASCIICHAT_OK;
996 }
997
998 callbacks->on_client_leave(client_ctx, callbacks->app_ctx);
999 return ASCIICHAT_OK;
1000}
1001
1002static asciichat_error_t handle_server_stream_start(const void *payload, size_t payload_len, void *client_ctx,
1003 const acip_server_callbacks_t *callbacks) {
1004 if (!callbacks->on_stream_start) {
1005 return ASCIICHAT_OK;
1006 }
1007
1008 if (payload_len < sizeof(uint32_t)) {
1009 return SET_ERRNO(ERROR_INVALID_PARAM, "STREAM_START payload too small (got %zu, expected %zu)", payload_len,
1010 sizeof(uint32_t));
1011 }
1012
1013 // Read stream_types as uint32_t (network byte order)
1014 uint32_t stream_types_net;
1015 memcpy(&stream_types_net, payload, sizeof(uint32_t));
1016 uint32_t stream_types = NET_TO_HOST_U32(stream_types_net);
1017
1018 callbacks->on_stream_start(stream_types, client_ctx, callbacks->app_ctx);
1019 return ASCIICHAT_OK;
1020}
1021
1022static asciichat_error_t handle_server_stream_stop(const void *payload, size_t payload_len, void *client_ctx,
1023 const acip_server_callbacks_t *callbacks) {
1024 if (!callbacks->on_stream_stop) {
1025 return ASCIICHAT_OK;
1026 }
1027
1028 if (payload_len < sizeof(uint32_t)) {
1029 return SET_ERRNO(ERROR_INVALID_PARAM, "STREAM_STOP payload too small (got %zu, expected %zu)", payload_len,
1030 sizeof(uint32_t));
1031 }
1032
1033 // Read stream_types as uint32_t (network byte order)
1034 uint32_t stream_types_net;
1035 memcpy(&stream_types_net, payload, sizeof(uint32_t));
1036 uint32_t stream_types = NET_TO_HOST_U32(stream_types_net);
1037
1038 callbacks->on_stream_stop(stream_types, client_ctx, callbacks->app_ctx);
1039 return ASCIICHAT_OK;
1040}
1041
1042static asciichat_error_t handle_server_capabilities(const void *payload, size_t payload_len, void *client_ctx,
1043 const acip_server_callbacks_t *callbacks) {
1044 log_debug("handle_server_capabilities: payload_len=%zu, client_ctx=%p, callbacks=%p", payload_len, client_ctx,
1045 (const void *)callbacks);
1046
1047 if (!callbacks->on_capabilities) {
1048 log_error("on_capabilities callback is NULL!");
1049 return ASCIICHAT_OK;
1050 }
1051
1052 log_error("Calling on_capabilities callback...");
1053 callbacks->on_capabilities(payload, payload_len, client_ctx, callbacks->app_ctx);
1054 log_error("on_capabilities callback returned");
1055 return ASCIICHAT_OK;
1056}
1057
1058static asciichat_error_t handle_server_ping(const void *payload, size_t payload_len, void *client_ctx,
1059 const acip_server_callbacks_t *callbacks) {
1060 (void)payload;
1061 (void)payload_len;
1062
1063 if (callbacks->on_ping) {
1064 callbacks->on_ping(client_ctx, callbacks->app_ctx);
1065 }
1066 return ASCIICHAT_OK;
1067}
1068
1069static asciichat_error_t handle_server_remote_log(const void *payload, size_t payload_len, void *client_ctx,
1070 const acip_server_callbacks_t *callbacks) {
1071 if (!callbacks->on_remote_log) {
1072 return ASCIICHAT_OK;
1073 }
1074
1075 if (payload_len < sizeof(remote_log_packet_t)) {
1076 return SET_ERRNO(ERROR_INVALID_PARAM, "REMOTE_LOG payload too small");
1077 }
1078
1079 const remote_log_packet_t *header = (const remote_log_packet_t *)payload;
1080 const char *message = (const char *)payload + sizeof(remote_log_packet_t);
1081 size_t msg_len = payload_len - sizeof(remote_log_packet_t);
1082
1083 // Ensure null-terminated message
1084 char msg_buffer[512];
1085 size_t copy_len = msg_len < sizeof(msg_buffer) - 1 ? msg_len : sizeof(msg_buffer) - 1;
1086 memcpy(msg_buffer, message, copy_len);
1087 msg_buffer[copy_len] = '\0';
1088
1089 callbacks->on_remote_log(header, msg_buffer, client_ctx, callbacks->app_ctx);
1090 return ASCIICHAT_OK;
1091}
1092
1093static asciichat_error_t handle_server_protocol_version(const void *payload, size_t payload_len, void *client_ctx,
1094 const acip_server_callbacks_t *callbacks) {
1095 if (!callbacks->on_protocol_version) {
1096 return ASCIICHAT_OK;
1097 }
1098
1099 if (payload_len != sizeof(protocol_version_packet_t)) {
1100 return SET_ERRNO(ERROR_INVALID_PARAM, "PROTOCOL_VERSION size mismatch");
1101 }
1102
1103 callbacks->on_protocol_version((const protocol_version_packet_t *)payload, client_ctx, callbacks->app_ctx);
1104 return ASCIICHAT_OK;
1105}
1106
1107static asciichat_error_t handle_server_pong(const void *payload, size_t payload_len, void *client_ctx,
1108 const acip_server_callbacks_t *callbacks) {
1109 (void)payload;
1110 (void)payload_len;
1111
1112 if (callbacks->on_pong) {
1113 callbacks->on_pong(client_ctx, callbacks->app_ctx);
1114 }
1115 return ASCIICHAT_OK;
1116}
1117
1118static asciichat_error_t handle_server_error_message(const void *payload, size_t payload_len, void *client_ctx,
1119 const acip_server_callbacks_t *callbacks) {
1120 if (!callbacks->on_error) {
1121 return ASCIICHAT_OK;
1122 }
1123
1124 if (payload_len < sizeof(error_packet_t)) {
1125 return SET_ERRNO(ERROR_INVALID_PARAM, "ERROR_MESSAGE payload too small");
1126 }
1127
1128 const error_packet_t *header = (const error_packet_t *)payload;
1129 const char *message = (const char *)payload + sizeof(error_packet_t);
1130 size_t msg_len = payload_len - sizeof(error_packet_t);
1131
1132 // Ensure null-terminated message
1133 char msg_buffer[MAX_ERROR_MESSAGE_LENGTH + 1];
1134 size_t copy_len = msg_len < MAX_ERROR_MESSAGE_LENGTH ? msg_len : MAX_ERROR_MESSAGE_LENGTH;
1135 memcpy(msg_buffer, message, copy_len);
1136 msg_buffer[copy_len] = '\0';
1137
1138 callbacks->on_error(header, msg_buffer, client_ctx, callbacks->app_ctx);
1139 return ASCIICHAT_OK;
1140}
1141
1142static asciichat_error_t handle_server_crypto_rekey_request(const void *payload, size_t payload_len, void *client_ctx,
1143 const acip_server_callbacks_t *callbacks) {
1144 if (callbacks->on_crypto_rekey_request) {
1145 callbacks->on_crypto_rekey_request(payload, payload_len, client_ctx, callbacks->app_ctx);
1146 }
1147 return ASCIICHAT_OK;
1148}
1149
1150static asciichat_error_t handle_server_crypto_rekey_response(const void *payload, size_t payload_len, void *client_ctx,
1151 const acip_server_callbacks_t *callbacks) {
1152 if (callbacks->on_crypto_rekey_response) {
1153 callbacks->on_crypto_rekey_response(payload, payload_len, client_ctx, callbacks->app_ctx);
1154 }
1155 return ASCIICHAT_OK;
1156}
1157
1158static asciichat_error_t handle_server_crypto_rekey_complete(const void *payload, size_t payload_len, void *client_ctx,
1159 const acip_server_callbacks_t *callbacks) {
1160 if (callbacks->on_crypto_rekey_complete) {
1161 callbacks->on_crypto_rekey_complete(payload, payload_len, client_ctx, callbacks->app_ctx);
1162 }
1163 return ASCIICHAT_OK;
1164}
1165
1166static asciichat_error_t handle_server_crypto_key_exchange_resp(const void *payload, size_t payload_len,
1167 void *client_ctx,
1168 const acip_server_callbacks_t *callbacks) {
1169 if (!callbacks->on_crypto_key_exchange_resp) {
1170 return ASCIICHAT_OK;
1171 }
1172
1173 // Dispatch to application callback with packet type
1174 callbacks->on_crypto_key_exchange_resp(PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP, payload, payload_len, client_ctx,
1175 callbacks->app_ctx);
1176 return ASCIICHAT_OK;
1177}
1178
1179static asciichat_error_t handle_server_crypto_auth_response(const void *payload, size_t payload_len, void *client_ctx,
1180 const acip_server_callbacks_t *callbacks) {
1181 if (!callbacks->on_crypto_auth_response) {
1182 return ASCIICHAT_OK;
1183 }
1184
1185 // Dispatch to application callback with packet type
1186 callbacks->on_crypto_auth_response(PACKET_TYPE_CRYPTO_AUTH_RESPONSE, payload, payload_len, client_ctx,
1187 callbacks->app_ctx);
1188 return ASCIICHAT_OK;
1189}
1190
1191static asciichat_error_t handle_server_crypto_no_encryption(const void *payload, size_t payload_len, void *client_ctx,
1192 const acip_server_callbacks_t *callbacks) {
1193 if (!callbacks->on_crypto_no_encryption) {
1194 return ASCIICHAT_OK;
1195 }
1196
1197 // Dispatch to application callback with packet type
1198 callbacks->on_crypto_no_encryption(PACKET_TYPE_CRYPTO_NO_ENCRYPTION, payload, payload_len, client_ctx,
1199 callbacks->app_ctx);
1200 return ASCIICHAT_OK;
1201}
ascii-chat Discovery Service (ACDS) Protocol Message Formats
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
⚙️ Common definitions, error codes, macros, and types shared throughout the application
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U32(val)
Definition endian.h:81
acip_session_joined_t
Definition acds.h:474
acip_webrtc_ice_t
Definition acds.h:650
acip_webrtc_sdp_t
Definition acds.h:604
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.
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#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
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define US_PER_MS_INT
Definition time.h:160
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
Definition packet.h:124
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional)
Definition packet.h:428
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:374
@ PACKET_TYPE_ACIP_PARTICIPANT_JOINED
Participant joined notification (ACDS -> Existing Participants)
Definition packet.h:469
@ PACKET_TYPE_CLIENT_LEAVE
Clean disconnect notification.
Definition packet.h:389
@ PACKET_TYPE_IMAGE_FRAME
Complete RGB image with dimensions.
Definition packet.h:363
@ PACKET_TYPE_CRYPTO_AUTH_RESPONSE
Client -> Server: {HMAC[32]} (UNENCRYPTED)
Definition packet.h:317
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED)
Definition packet.h:323
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:391
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED)
Definition packet.h:311
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP
Client -> Server: {client_pubkey[32]} (UNENCRYPTED)
Definition packet.h:313
@ PACKET_TYPE_CRYPTO_REKEY_COMPLETE
Initiator -> Responder: Empty (encrypted with NEW key, but still handshake)
Definition packet.h:339
@ PACKET_TYPE_SERVER_STATE
Server sends current state to clients.
Definition packet.h:397
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED)
Definition packet.h:319
@ PACKET_TYPE_CRYPTO_REKEY_REQUEST
Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:335
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:354
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED)
Definition packet.h:321
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:288
@ PACKET_TYPE_CLEAR_CONSOLE
Server tells client to clear console.
Definition packet.h:395
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED)
Definition packet.h:325
@ PACKET_TYPE_IMAGE_FRAME_H265
H.265-encoded frame from client (HEVC/H.265 codec)
Definition packet.h:365
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED)
Definition packet.h:315
@ PACKET_TYPE_ASCII_FRAME
Complete ASCII frame with all metadata.
Definition packet.h:361
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED)
Definition packet.h:309
@ PACKET_TYPE_ACIP_PARTICIPANT_LEFT
Participant left notification (ACDS -> Remaining Participants)
Definition packet.h:471
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:426
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:387
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:381
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:352
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:372
@ PACKET_TYPE_CRYPTO_REKEY_RESPONSE
Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:337
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client)
Definition packet.h:417
@ PACKET_TYPE_STREAM_STOP
Client stops sending media.
Definition packet.h:393
ACIP protocol packet handlers (transport-agnostic)
🔊 Audio Capture and Playback Interface for ascii-chat
📝 Logging API with multiple log levels and terminal output control
#define log_dev_every(interval_us, fmt,...)
Rate-limited DEV logging.
Definition log/log.h:699
ACIP protocol message structures and helpers.
asciichat_error_t acip_handle_server_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, void *client_ctx, const acip_server_callbacks_t *callbacks)
Handle incoming packet on server side.
asciichat_error_t acip_handle_client_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
Handle incoming packet on client side.
asciichat_error_t(* acip_client_handler_func_t)(const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
#define HANDLER_HASH(type)
asciichat_error_t(* acip_server_handler_func_t)(const void *payload, size_t payload_len, void *client_ctx, const acip_server_callbacks_t *callbacks)
#define CLIENT_HANDLER_COUNT
#define HANDLER_HASH_SIZE
#define SERVER_HANDLER_COUNT
✅ Safe Integer Arithmetic and Overflow Detection
Client-side packet handler callbacks.
Definition handlers.h:51
void(* on_clear_console)(void *ctx)
Called when server requests console clear.
Definition handlers.h:83
void(* on_session_joined)(const acip_session_joined_t *joined, void *ctx)
Called when ACDS session join response received (Phase 3 WebRTC integration)
Definition handlers.h:98
void(* on_webrtc_sdp)(const acip_webrtc_sdp_t *sdp, size_t total_len, void *ctx)
Called when WebRTC SDP offer/answer received.
Definition handlers.h:92
void(* on_error)(const error_packet_t *header, const char *message, void *ctx)
Called when error message received.
Definition handlers.h:71
void(* on_crypto_parameters)(packet_type_t type, const void *payload, size_t payload_len, void *ctx)
Called when server sends CRYPTO_PARAMETERS (algorithm negotiation)
Definition handlers.h:101
void(* on_crypto_handshake_complete)(packet_type_t type, const void *payload, size_t payload_len, void *ctx)
Called when server sends HANDSHAKE_COMPLETE (crypto handshake success)
Definition handlers.h:116
void(* on_crypto_auth_challenge)(packet_type_t type, const void *payload, size_t payload_len, void *ctx)
Called when server sends AUTH_CHALLENGE (crypto handshake)
Definition handlers.h:107
void(* on_audio_opus_batch)(const void *batch_data, size_t batch_len, void *ctx)
Called when Opus batch received from server.
Definition handlers.h:65
void(* on_ping)(void *ctx)
Called when ping received (should send pong)
Definition handlers.h:77
void(* on_crypto_key_exchange_init)(packet_type_t type, const void *payload, size_t payload_len, void *ctx)
Called when server sends KEY_EXCHANGE_INIT (crypto handshake)
Definition handlers.h:104
void(* on_pong)(void *ctx)
Called when pong received.
Definition handlers.h:80
void(* on_audio_batch)(const audio_batch_packet_t *header, const float *samples, size_t num_samples, void *ctx)
Called when audio batch received from server.
Definition handlers.h:59
void(* on_crypto_rekey_request)(const void *payload, size_t payload_len, void *ctx)
Called when server sends crypto rekey request.
Definition handlers.h:86
void(* on_webrtc_ice)(const acip_webrtc_ice_t *ice, size_t total_len, void *ctx)
Called when WebRTC ICE candidate received.
Definition handlers.h:95
void(* on_crypto_rekey_response)(const void *payload, size_t payload_len, void *ctx)
Called when server sends crypto rekey response.
Definition handlers.h:89
void(* on_remote_log)(const remote_log_packet_t *header, const char *message, void *ctx)
Called when remote log received.
Definition handlers.h:74
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.
Definition handlers.h:53
void(* on_crypto_server_auth_resp)(packet_type_t type, const void *payload, size_t payload_len, void *ctx)
Called when server sends SERVER_AUTH_RESP (crypto handshake mutual auth)
Definition handlers.h:110
void * app_ctx
Application context (passed to all callbacks)
Definition handlers.h:119
void(* on_server_state)(const server_state_packet_t *state, void *ctx)
Called when server state update received.
Definition handlers.h:68
void(* on_crypto_auth_failed)(packet_type_t type, const void *payload, size_t payload_len, void *ctx)
Called when server sends AUTH_FAILED (crypto handshake failure)
Definition handlers.h:113
Server-side packet handler callbacks.
Definition handlers.h:148
void(* on_image_frame)(const image_frame_packet_t *header, const void *pixel_data, size_t data_len, void *client_ctx, void *app_ctx)
Called when client sends image frame.
Definition handlers.h:153
void(* on_crypto_auth_response)(packet_type_t type, const void *payload, size_t payload_len, void *client_ctx, void *app_ctx)
Called when client sends AUTH_RESPONSE (crypto handshake)
Definition handlers.h:214
void(* on_capabilities)(const void *cap_data, size_t data_len, void *client_ctx, void *app_ctx)
Called when client sends capabilities.
Definition handlers.h:186
void(* on_pong)(void *client_ctx, void *app_ctx)
Called when pong received from client.
Definition handlers.h:192
void * app_ctx
Application context (passed to all callbacks)
Definition handlers.h:222
void(* on_image_frame_h265)(uint32_t width, uint32_t height, uint8_t flags, const void *h265_data, size_t data_len, void *client_ctx, void *app_ctx)
Called when client sends H.265-encoded image frame.
Definition handlers.h:157
void(* on_stream_start)(uint32_t stream_types, void *client_ctx, void *app_ctx)
Called when client starts streaming.
Definition handlers.h:180
void(* on_crypto_no_encryption)(packet_type_t type, const void *payload, size_t payload_len, void *client_ctx, void *app_ctx)
Called when client sends NO_ENCRYPTION (crypto handshake rejection)
Definition handlers.h:218
void(* on_ping)(void *client_ctx, void *app_ctx)
Called when ping received (should send pong)
Definition handlers.h:189
void(* on_stream_stop)(uint32_t stream_types, void *client_ctx, void *app_ctx)
Called when client stops streaming.
Definition handlers.h:183
void(* on_crypto_rekey_response)(const void *payload, size_t payload_len, void *client_ctx, void *app_ctx)
Called when client sends crypto rekey response.
Definition handlers.h:204
void(* on_client_leave)(void *client_ctx, void *app_ctx)
Called when client leaves.
Definition handlers.h:177
void(* on_remote_log)(const remote_log_packet_t *header, const char *message, void *client_ctx, void *app_ctx)
Called when remote log received from client.
Definition handlers.h:198
void(* on_error)(const error_packet_t *header, const char *message, void *client_ctx, void *app_ctx)
Called when error message received from client.
Definition handlers.h:195
void(* on_crypto_rekey_complete)(const void *payload, size_t payload_len, void *client_ctx, void *app_ctx)
Called when client sends crypto rekey complete.
Definition handlers.h:207
void(* on_client_join)(const void *join_data, size_t data_len, void *client_ctx, void *app_ctx)
Called when client joins.
Definition handlers.h:174
void(* on_audio_opus_batch)(const void *batch_data, size_t batch_len, void *client_ctx, void *app_ctx)
Called when client sends Opus batch.
Definition handlers.h:171
void(* on_audio_batch)(const audio_batch_packet_t *header, const float *samples, size_t num_samples, void *client_ctx, void *app_ctx)
Called when client sends audio batch.
Definition handlers.h:164
void(* on_crypto_rekey_request)(const void *payload, size_t payload_len, void *client_ctx, void *app_ctx)
Called when client sends crypto rekey request.
Definition handlers.h:201
void(* on_protocol_version)(const protocol_version_packet_t *version, void *client_ctx, void *app_ctx)
Called when client sends protocol version.
Definition handlers.h:150
void(* on_crypto_key_exchange_resp)(packet_type_t type, const void *payload, size_t payload_len, void *client_ctx, void *app_ctx)
Called when client sends KEY_EXCHANGE_RESP (crypto handshake)
Definition handlers.h:210
Transport instance structure.
Definition transport.h:214
ASCII frame packet structure (Packet Type 2)
Definition packet.h:848
uint32_t width
Terminal width in characters.
Definition packet.h:850
uint32_t checksum
CRC32 checksum of original ASCII data.
Definition packet.h:858
uint32_t original_size
Size of original uncompressed ASCII data in bytes.
Definition packet.h:854
uint32_t height
Terminal height in characters.
Definition packet.h:852
uint32_t compressed_size
Size of compressed data (0 = not compressed)
Definition packet.h:856
uint32_t flags
Frame flags bitmask (HAS_COLOR, IS_COMPRESSED, etc.)
Definition packet.h:860
Audio batch packet structure (Packet Type 28)
Definition packet.h:904
uint32_t channels
Number of audio channels (1=mono, 2=stereo)
Definition packet.h:912
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32)
Definition packet.h:906
uint32_t sample_rate
Sample rate in Hz (e.g., 44100, 48000)
Definition packet.h:910
uint32_t total_samples
Total audio samples across all chunks (typically 8192)
Definition packet.h:908
Error packet structure carrying error code and textual description.
Definition packet.h:727
Hash table entry for packet type to handler mapping.
uint8_t handler_idx
Handler index (0-based)
packet_type_t key
Packet type (0 = empty slot)
Image frame packet structure (Packet Type 3)
Definition packet.h:876
uint32_t pixel_format
Pixel format enum (1=RGB24, 2=RGBA32, 3=BGR24, 4=BGRA32)
Definition packet.h:882
uint32_t timestamp
Timestamp when frame was captured (milliseconds since epoch)
Definition packet.h:888
uint32_t compressed_size
Compressed data size (0 = not compressed, >0 = compressed)
Definition packet.h:884
uint32_t checksum
CRC32 checksum of pixel data.
Definition packet.h:886
uint32_t height
Image height in pixels.
Definition packet.h:880
uint32_t width
Image width in pixels.
Definition packet.h:878
Protocol version negotiation packet structure (Packet Type 1)
Definition packet.h:818
Remote log packet structure carrying log level and message text.
Definition packet.h:741
Server state packet structure.
Definition packet.h:706