ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
handlers.c
Go to the documentation of this file.
1
15#include "network/acip/acds.h"
16#include "network/packet.h"
18#include "audio/audio.h"
19#include "util/endian.h"
20#include "log/logging.h"
21#include "asciichat_errno.h"
22#include "common.h"
23#include <string.h>
24
25// =============================================================================
26// Handler Function Pointer Types
27// =============================================================================
28
39typedef asciichat_error_t (*acip_client_handler_func_t)(const void *payload, size_t payload_len,
40 const acip_client_callbacks_t *callbacks);
41
53typedef asciichat_error_t (*acip_server_handler_func_t)(const void *payload, size_t payload_len, void *client_ctx,
54 const acip_server_callbacks_t *callbacks);
55
56// =============================================================================
57// Client-Side Packet Handlers
58// =============================================================================
59
60// Forward declarations for client handlers
61static asciichat_error_t handle_client_ascii_frame(const void *payload, size_t payload_len,
62 const acip_client_callbacks_t *callbacks);
63static asciichat_error_t handle_client_audio_batch(const void *payload, size_t payload_len,
64 const acip_client_callbacks_t *callbacks);
65static asciichat_error_t handle_client_audio_opus(const void *payload, size_t payload_len,
66 const acip_client_callbacks_t *callbacks);
67static asciichat_error_t handle_client_audio_opus_batch(const void *payload, size_t payload_len,
68 const acip_client_callbacks_t *callbacks);
69static asciichat_error_t handle_client_server_state(const void *payload, size_t payload_len,
70 const acip_client_callbacks_t *callbacks);
71static asciichat_error_t handle_client_error_message(const void *payload, size_t payload_len,
72 const acip_client_callbacks_t *callbacks);
73static asciichat_error_t handle_client_remote_log(const void *payload, size_t payload_len,
74 const acip_client_callbacks_t *callbacks);
75static asciichat_error_t handle_client_ping(const void *payload, size_t payload_len,
76 const acip_client_callbacks_t *callbacks);
77static asciichat_error_t handle_client_pong(const void *payload, size_t payload_len,
78 const acip_client_callbacks_t *callbacks);
79static asciichat_error_t handle_client_audio(const void *payload, size_t payload_len,
80 const acip_client_callbacks_t *callbacks);
81static asciichat_error_t handle_client_clear_console(const void *payload, size_t payload_len,
82 const acip_client_callbacks_t *callbacks);
83static asciichat_error_t handle_client_crypto_rekey_request(const void *payload, size_t payload_len,
84 const acip_client_callbacks_t *callbacks);
85static asciichat_error_t handle_client_crypto_rekey_response(const void *payload, size_t payload_len,
86 const acip_client_callbacks_t *callbacks);
87static asciichat_error_t handle_client_webrtc_sdp(const void *payload, size_t payload_len,
88 const acip_client_callbacks_t *callbacks);
89static asciichat_error_t handle_client_webrtc_ice(const void *payload, size_t payload_len,
90 const acip_client_callbacks_t *callbacks);
91static asciichat_error_t handle_client_session_joined(const void *payload, size_t payload_len,
92 const acip_client_callbacks_t *callbacks);
93
100static const acip_client_handler_func_t g_client_packet_handlers[200] = {
101 [PACKET_TYPE_ASCII_FRAME] = handle_client_ascii_frame,
102 [PACKET_TYPE_AUDIO_BATCH] = handle_client_audio_batch,
103 [PACKET_TYPE_AUDIO_OPUS] = handle_client_audio_opus,
104 [PACKET_TYPE_AUDIO_OPUS_BATCH] = handle_client_audio_opus_batch,
105 [PACKET_TYPE_SERVER_STATE] = handle_client_server_state,
106 [PACKET_TYPE_ERROR_MESSAGE] = handle_client_error_message,
107 [PACKET_TYPE_REMOTE_LOG] = handle_client_remote_log,
108 [PACKET_TYPE_PING] = handle_client_ping,
109 [PACKET_TYPE_PONG] = handle_client_pong,
110 [PACKET_TYPE_AUDIO] = handle_client_audio,
111 [PACKET_TYPE_CLEAR_CONSOLE] = handle_client_clear_console,
112 [PACKET_TYPE_CRYPTO_REKEY_REQUEST] = handle_client_crypto_rekey_request,
113 [PACKET_TYPE_CRYPTO_REKEY_RESPONSE] = handle_client_crypto_rekey_response,
114 [PACKET_TYPE_ACIP_WEBRTC_SDP] = handle_client_webrtc_sdp,
115 [PACKET_TYPE_ACIP_WEBRTC_ICE] = handle_client_webrtc_ice,
116 [PACKET_TYPE_ACIP_SESSION_JOINED] = handle_client_session_joined,
117};
118
120 size_t payload_len, const acip_client_callbacks_t *callbacks) {
121 if (!transport || !callbacks) {
122 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or callbacks");
123 }
124
125 // TODO: Use transport for sending responses or out-of-band messages in future versions
126 (void)transport;
127
128 // O(1) array-based dispatch - bounds check packet type
129 if (type >= 200) {
130 log_warn("Invalid client packet type: %d (out of range)", type);
131 return ASCIICHAT_OK;
132 }
133
134 // Lookup handler in dispatch table
135 acip_client_handler_func_t handler = g_client_packet_handlers[type];
136 if (!handler) {
137 log_warn("Unhandled client packet type: %d", type);
138 return ASCIICHAT_OK;
139 }
140
141 // Dispatch to handler
142 return handler(payload, payload_len, callbacks);
143}
144
145// =============================================================================
146// Client Handler Implementations (extracted from switch cases)
147// =============================================================================
148
149static asciichat_error_t handle_client_ascii_frame(const void *payload, size_t payload_len,
150 const acip_client_callbacks_t *callbacks) {
151 if (!callbacks->on_ascii_frame) {
152 return ASCIICHAT_OK;
153 }
154
155 if (payload_len < sizeof(ascii_frame_packet_t)) {
156 return SET_ERRNO(ERROR_INVALID_PARAM, "ASCII_FRAME payload too small");
157 }
158
159 // Extract header
161 memcpy(&header, payload, sizeof(header));
162
163 // Convert from network byte order
164 header.width = NET_TO_HOST_U32(header.width);
165 header.height = NET_TO_HOST_U32(header.height);
168 header.checksum = NET_TO_HOST_U32(header.checksum);
169 header.flags = NET_TO_HOST_U32(header.flags);
170
171 // Get frame data (after header)
172 const void *frame_data = (const uint8_t *)payload + sizeof(ascii_frame_packet_t);
173 size_t frame_data_len = payload_len - sizeof(ascii_frame_packet_t);
174
175 // Validate frame dimensions to prevent DoS and buffer overflow attacks
176 if (header.width == 0 || header.height == 0) {
177 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid frame dimensions: %ux%u (width and height must be > 0)",
178 header.width, header.height);
179 }
180
181 // Sanity check: prevent unreasonably large frames (e.g., > 16K resolution)
182 // This protects against resource exhaustion attacks
183 const uint32_t MAX_WIDTH = 16384; // 16K
184 const uint32_t MAX_HEIGHT = 16384; // 16K
185 if (header.width > MAX_WIDTH || header.height > MAX_HEIGHT) {
186 return SET_ERRNO(ERROR_INVALID_PARAM, "Frame dimensions too large: %ux%u (max: %ux%u)", header.width, header.height,
187 MAX_WIDTH, MAX_HEIGHT);
188 }
189
190 callbacks->on_ascii_frame(&header, frame_data, frame_data_len, callbacks->app_ctx);
191 return ASCIICHAT_OK;
192}
193
194static asciichat_error_t handle_client_audio_batch(const void *payload, size_t payload_len,
195 const acip_client_callbacks_t *callbacks) {
196 if (!callbacks->on_audio_batch) {
197 return ASCIICHAT_OK;
198 }
199
200 if (payload_len < sizeof(audio_batch_packet_t)) {
201 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH payload too small");
202 }
203
204 // Parse batch header
205 const audio_batch_packet_t *batch_header = (const audio_batch_packet_t *)payload;
206 uint32_t batch_count = NET_TO_HOST_U32(batch_header->batch_count);
207 uint32_t total_samples = NET_TO_HOST_U32(batch_header->total_samples);
208 uint32_t sample_rate = NET_TO_HOST_U32(batch_header->sample_rate);
209 uint32_t channels = NET_TO_HOST_U32(batch_header->channels);
210
211 // Validate sample rate and channels
212 // Supported sample rates: 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000 Hz
213 if (sample_rate == 0 || (sample_rate < 8000 || sample_rate > 192000) ||
214 (sample_rate != 8000 && sample_rate != 16000 && sample_rate != 24000 && sample_rate != 32000 &&
215 sample_rate != 44100 && sample_rate != 48000 && sample_rate != 96000 && sample_rate != 192000)) {
216 return SET_ERRNO(
218 "Invalid audio sample rate: %u Hz (expected: 8000, 16000, 24000, 32000, 44100, 48000, 96000, or 192000)",
219 sample_rate);
220 }
221
222 // Validate channel count (1-8 channels supported)
223 if (channels == 0 || channels > 8) {
224 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid audio channel count: %u (expected: 1-8)", channels);
225 }
226
227 // Validate batch count
228 (void)batch_count;
229
230 // Validate size
231 size_t expected_size = sizeof(audio_batch_packet_t) + (total_samples * sizeof(uint32_t));
232 if (payload_len != expected_size) {
233 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH size mismatch");
234 }
235
236 // Extract quantized samples
237 const uint8_t *samples_ptr = (const uint8_t *)payload + sizeof(audio_batch_packet_t);
238
239 // Dequantize samples to float
240 float *samples = SAFE_MALLOC(total_samples * sizeof(float), float *);
241 if (!samples) {
242 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate audio batch buffer");
243 }
244
245 asciichat_error_t result = audio_dequantize_samples(samples_ptr, total_samples, samples);
246 if (result != ASCIICHAT_OK) {
247 SAFE_FREE(samples);
248 return result;
249 }
250
251 // Copy header for callback
252 audio_batch_packet_t header_copy = *batch_header;
253 header_copy.batch_count = batch_count;
254 header_copy.total_samples = total_samples;
255 header_copy.sample_rate = sample_rate;
256 header_copy.channels = channels;
257
258 callbacks->on_audio_batch(&header_copy, samples, total_samples, callbacks->app_ctx);
259
260 SAFE_FREE(samples);
261 return ASCIICHAT_OK;
262}
263
264static asciichat_error_t handle_client_audio_opus(const void *payload, size_t payload_len,
265 const acip_client_callbacks_t *callbacks) {
266 if (!callbacks->on_audio_opus) {
267 return ASCIICHAT_OK;
268 }
269
270 // Raw Opus data (no header parsing)
271 callbacks->on_audio_opus(payload, payload_len, callbacks->app_ctx);
272 return ASCIICHAT_OK;
273}
274
275static asciichat_error_t handle_client_audio_opus_batch(const void *payload, size_t payload_len,
276 const acip_client_callbacks_t *callbacks) {
277 if (!callbacks->on_audio_opus_batch) {
278 return ASCIICHAT_OK;
279 }
280
281 // Opus batch data (header + sizes + opus frames)
282 callbacks->on_audio_opus_batch(payload, payload_len, callbacks->app_ctx);
283 return ASCIICHAT_OK;
284}
285
286static asciichat_error_t handle_client_server_state(const void *payload, size_t payload_len,
287 const acip_client_callbacks_t *callbacks) {
288 if (!callbacks->on_server_state) {
289 return ASCIICHAT_OK;
290 }
291
292 if (payload_len != sizeof(server_state_packet_t)) {
293 return SET_ERRNO(ERROR_INVALID_PARAM, "SERVER_STATE size mismatch");
294 }
295
296 callbacks->on_server_state((const server_state_packet_t *)payload, callbacks->app_ctx);
297 return ASCIICHAT_OK;
298}
299
300static asciichat_error_t handle_client_error_message(const void *payload, size_t payload_len,
301 const acip_client_callbacks_t *callbacks) {
302 if (!callbacks->on_error) {
303 return ASCIICHAT_OK;
304 }
305
306 if (payload_len < sizeof(error_packet_t)) {
307 return SET_ERRNO(ERROR_INVALID_PARAM, "ERROR_MESSAGE payload too small");
308 }
309
310 const error_packet_t *header = (const error_packet_t *)payload;
311 const char *message = (const char *)payload + sizeof(error_packet_t);
312 size_t msg_len = payload_len - sizeof(error_packet_t);
313
314 // Ensure null-terminated message
315 char msg_buffer[MAX_ERROR_MESSAGE_LENGTH + 1];
316 size_t copy_len = msg_len < MAX_ERROR_MESSAGE_LENGTH ? msg_len : MAX_ERROR_MESSAGE_LENGTH;
317 memcpy(msg_buffer, message, copy_len);
318 msg_buffer[copy_len] = '\0';
319
320 callbacks->on_error(header, msg_buffer, callbacks->app_ctx);
321 return ASCIICHAT_OK;
322}
323
324static asciichat_error_t handle_client_remote_log(const void *payload, size_t payload_len,
325 const acip_client_callbacks_t *callbacks) {
326 if (!callbacks->on_remote_log) {
327 return ASCIICHAT_OK;
328 }
329
330 if (payload_len < sizeof(remote_log_packet_t)) {
331 return SET_ERRNO(ERROR_INVALID_PARAM, "REMOTE_LOG payload too small");
332 }
333
334 const remote_log_packet_t *header = (const remote_log_packet_t *)payload;
335 const char *message = (const char *)payload + sizeof(remote_log_packet_t);
336 size_t msg_len = payload_len - sizeof(remote_log_packet_t);
337
338 // Ensure null-terminated message
339 char msg_buffer[512];
340 size_t copy_len = msg_len < sizeof(msg_buffer) - 1 ? msg_len : sizeof(msg_buffer) - 1;
341 memcpy(msg_buffer, message, copy_len);
342 msg_buffer[copy_len] = '\0';
343
344 callbacks->on_remote_log(header, msg_buffer, callbacks->app_ctx);
345 return ASCIICHAT_OK;
346}
347
348static asciichat_error_t handle_client_ping(const void *payload, size_t payload_len,
349 const acip_client_callbacks_t *callbacks) {
350 (void)payload;
351 (void)payload_len;
352
353 if (callbacks->on_ping) {
354 callbacks->on_ping(callbacks->app_ctx);
355 }
356 return ASCIICHAT_OK;
357}
358
359static asciichat_error_t handle_client_pong(const void *payload, size_t payload_len,
360 const acip_client_callbacks_t *callbacks) {
361 (void)payload;
362 (void)payload_len;
363
364 if (callbacks->on_pong) {
365 callbacks->on_pong(callbacks->app_ctx);
366 }
367 return ASCIICHAT_OK;
368}
369
370static asciichat_error_t handle_client_audio(const void *payload, size_t payload_len,
371 const acip_client_callbacks_t *callbacks) {
372 if (!callbacks->on_audio) {
373 return ASCIICHAT_OK;
374 }
375
376 // Raw audio data (float samples)
377 callbacks->on_audio(payload, payload_len, callbacks->app_ctx);
378 return ASCIICHAT_OK;
379}
380
381static asciichat_error_t handle_client_clear_console(const void *payload, size_t payload_len,
382 const acip_client_callbacks_t *callbacks) {
383 (void)payload;
384 (void)payload_len;
385
386 if (callbacks->on_clear_console) {
387 callbacks->on_clear_console(callbacks->app_ctx);
388 }
389 return ASCIICHAT_OK;
390}
391
392static asciichat_error_t handle_client_crypto_rekey_request(const void *payload, size_t payload_len,
393 const acip_client_callbacks_t *callbacks) {
394 if (callbacks->on_crypto_rekey_request) {
395 callbacks->on_crypto_rekey_request(payload, payload_len, callbacks->app_ctx);
396 }
397 return ASCIICHAT_OK;
398}
399
400static asciichat_error_t handle_client_crypto_rekey_response(const void *payload, size_t payload_len,
401 const acip_client_callbacks_t *callbacks) {
402 if (callbacks->on_crypto_rekey_response) {
403 callbacks->on_crypto_rekey_response(payload, payload_len, callbacks->app_ctx);
404 }
405 return ASCIICHAT_OK;
406}
407
408static asciichat_error_t handle_client_webrtc_sdp(const void *payload, size_t payload_len,
409 const acip_client_callbacks_t *callbacks) {
410 if (!callbacks->on_webrtc_sdp) {
411 return ASCIICHAT_OK;
412 }
413
414 if (payload_len < sizeof(acip_webrtc_sdp_t)) {
415 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_SDP payload too small");
416 }
417
418 const acip_webrtc_sdp_t *sdp = (const acip_webrtc_sdp_t *)payload;
419 callbacks->on_webrtc_sdp(sdp, payload_len, callbacks->app_ctx);
420 return ASCIICHAT_OK;
421}
422
423static asciichat_error_t handle_client_webrtc_ice(const void *payload, size_t payload_len,
424 const acip_client_callbacks_t *callbacks) {
425 if (!callbacks->on_webrtc_ice) {
426 return ASCIICHAT_OK;
427 }
428
429 if (payload_len < sizeof(acip_webrtc_ice_t)) {
430 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_ICE payload too small");
431 }
432
433 const acip_webrtc_ice_t *ice = (const acip_webrtc_ice_t *)payload;
434 callbacks->on_webrtc_ice(ice, payload_len, callbacks->app_ctx);
435 return ASCIICHAT_OK;
436}
437
438static asciichat_error_t handle_client_session_joined(const void *payload, size_t payload_len,
439 const acip_client_callbacks_t *callbacks) {
440 if (!callbacks->on_session_joined) {
441 return ASCIICHAT_OK;
442 }
443
444 if (payload_len < sizeof(acip_session_joined_t)) {
445 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_JOINED payload too small (got %zu, need %zu)", payload_len,
446 sizeof(acip_session_joined_t));
447 }
448
449 // Parse the session_joined response
450 const acip_session_joined_t *joined = (const acip_session_joined_t *)payload;
451
452 // Log the result
453 if (joined->success) {
454 log_debug("Session join succeeded: session_id=%.16s, participant_id=%.16s, server=%s:%u, type=%s",
455 (const char *)joined->session_id, (const char *)joined->participant_id, joined->server_address,
456 joined->server_port, joined->session_type == 1 ? "WebRTC" : "DirectTCP");
457 } else {
458 log_warn("Session join failed: error %d: %s", joined->error_code, joined->error_message);
459 }
460
461 // Dispatch to application callback
462 callbacks->on_session_joined(joined, callbacks->app_ctx);
463 return ASCIICHAT_OK;
464}
465
466// =============================================================================
467// Server-Side Packet Handlers
468// =============================================================================
469
470// Server handler function signature (includes client_ctx)
471typedef asciichat_error_t (*acip_server_handler_func_t)(const void *payload, size_t payload_len, void *client_ctx,
472 const acip_server_callbacks_t *callbacks);
473
474// Forward declarations for server handlers
475static asciichat_error_t handle_server_image_frame(const void *payload, size_t payload_len, void *client_ctx,
476 const acip_server_callbacks_t *callbacks);
477static asciichat_error_t handle_server_audio_batch(const void *payload, size_t payload_len, void *client_ctx,
478 const acip_server_callbacks_t *callbacks);
479static asciichat_error_t handle_server_audio_opus(const void *payload, size_t payload_len, void *client_ctx,
480 const acip_server_callbacks_t *callbacks);
481static asciichat_error_t handle_server_audio_opus_batch(const void *payload, size_t payload_len, void *client_ctx,
482 const acip_server_callbacks_t *callbacks);
483static asciichat_error_t handle_server_client_join(const void *payload, size_t payload_len, void *client_ctx,
484 const acip_server_callbacks_t *callbacks);
485static asciichat_error_t handle_server_client_leave(const void *payload, size_t payload_len, void *client_ctx,
486 const acip_server_callbacks_t *callbacks);
487static asciichat_error_t handle_server_stream_start(const void *payload, size_t payload_len, void *client_ctx,
488 const acip_server_callbacks_t *callbacks);
489static asciichat_error_t handle_server_stream_stop(const void *payload, size_t payload_len, void *client_ctx,
490 const acip_server_callbacks_t *callbacks);
491static asciichat_error_t handle_server_capabilities(const void *payload, size_t payload_len, void *client_ctx,
492 const acip_server_callbacks_t *callbacks);
493static asciichat_error_t handle_server_ping(const void *payload, size_t payload_len, void *client_ctx,
494 const acip_server_callbacks_t *callbacks);
495static asciichat_error_t handle_server_remote_log(const void *payload, size_t payload_len, void *client_ctx,
496 const acip_server_callbacks_t *callbacks);
497static asciichat_error_t handle_server_protocol_version(const void *payload, size_t payload_len, void *client_ctx,
498 const acip_server_callbacks_t *callbacks);
499static asciichat_error_t handle_server_audio(const void *payload, size_t payload_len, void *client_ctx,
500 const acip_server_callbacks_t *callbacks);
501static asciichat_error_t handle_server_pong(const void *payload, size_t payload_len, void *client_ctx,
502 const acip_server_callbacks_t *callbacks);
503static asciichat_error_t handle_server_error_message(const void *payload, size_t payload_len, void *client_ctx,
504 const acip_server_callbacks_t *callbacks);
505static asciichat_error_t handle_server_crypto_rekey_request(const void *payload, size_t payload_len, void *client_ctx,
506 const acip_server_callbacks_t *callbacks);
507static asciichat_error_t handle_server_crypto_rekey_response(const void *payload, size_t payload_len, void *client_ctx,
508 const acip_server_callbacks_t *callbacks);
509static asciichat_error_t handle_server_crypto_rekey_complete(const void *payload, size_t payload_len, void *client_ctx,
510 const acip_server_callbacks_t *callbacks);
511
512// Server packet dispatch table (O(1) lookup by packet type)
513static const acip_server_handler_func_t g_server_packet_handlers[200] = {
514 [PACKET_TYPE_IMAGE_FRAME] = handle_server_image_frame,
515 [PACKET_TYPE_AUDIO_BATCH] = handle_server_audio_batch,
516 [PACKET_TYPE_AUDIO_OPUS] = handle_server_audio_opus,
517 [PACKET_TYPE_AUDIO_OPUS_BATCH] = handle_server_audio_opus_batch,
518 [PACKET_TYPE_CLIENT_JOIN] = handle_server_client_join,
519 [PACKET_TYPE_CLIENT_LEAVE] = handle_server_client_leave,
520 [PACKET_TYPE_STREAM_START] = handle_server_stream_start,
521 [PACKET_TYPE_STREAM_STOP] = handle_server_stream_stop,
522 [PACKET_TYPE_CLIENT_CAPABILITIES] = handle_server_capabilities,
523 [PACKET_TYPE_PING] = handle_server_ping,
524 [PACKET_TYPE_REMOTE_LOG] = handle_server_remote_log,
525 [PACKET_TYPE_PROTOCOL_VERSION] = handle_server_protocol_version,
526 [PACKET_TYPE_AUDIO] = handle_server_audio,
527 [PACKET_TYPE_PONG] = handle_server_pong,
528 [PACKET_TYPE_ERROR_MESSAGE] = handle_server_error_message,
529 [PACKET_TYPE_CRYPTO_REKEY_REQUEST] = handle_server_crypto_rekey_request,
530 [PACKET_TYPE_CRYPTO_REKEY_RESPONSE] = handle_server_crypto_rekey_response,
531 [PACKET_TYPE_CRYPTO_REKEY_COMPLETE] = handle_server_crypto_rekey_complete,
532};
533
535 size_t payload_len, void *client_ctx,
536 const acip_server_callbacks_t *callbacks) {
537 if (!transport || !callbacks) {
538 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or callbacks");
539 }
540
541 // TODO: Use transport for sending responses or out-of-band messages in future versions
542 (void)transport;
543
544 // O(1) array-based dispatch - bounds check packet type
545 if (type >= 200) {
546 log_warn("Invalid server packet type: %d (out of range)", type);
547 return ASCIICHAT_OK;
548 }
549
550 // Lookup handler in dispatch table
551 acip_server_handler_func_t handler = g_server_packet_handlers[type];
552 if (!handler) {
553 log_warn("Unhandled server packet type: %d", type);
554 return ASCIICHAT_OK;
555 }
556
557 // Dispatch to handler
558 return handler(payload, payload_len, client_ctx, callbacks);
559}
560
561// =============================================================================
562// Server Handler Implementations (extracted from switch cases)
563// =============================================================================
564
565static asciichat_error_t handle_server_image_frame(const void *payload, size_t payload_len, void *client_ctx,
566 const acip_server_callbacks_t *callbacks) {
567 if (!callbacks->on_image_frame) {
568 return ASCIICHAT_OK;
569 }
570
571 if (payload_len < sizeof(image_frame_packet_t)) {
572 return SET_ERRNO(ERROR_INVALID_PARAM, "IMAGE_FRAME payload too small");
573 }
574
575 // Extract header
577 memcpy(&header, payload, sizeof(header));
578
579 // Convert from network byte order
580 header.width = NET_TO_HOST_U32(header.width);
581 header.height = NET_TO_HOST_U32(header.height);
584 header.checksum = NET_TO_HOST_U32(header.checksum);
585 header.timestamp = NET_TO_HOST_U32(header.timestamp);
586
587 // Get pixel data (after header)
588 const void *pixel_data = (const uint8_t *)payload + sizeof(image_frame_packet_t);
589 size_t pixel_data_len = payload_len - sizeof(image_frame_packet_t);
590
591 // Validate frame dimensions to prevent DoS and buffer overflow attacks
592 if (header.width == 0 || header.height == 0) {
593 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid image dimensions: %ux%u (width and height must be > 0)",
594 header.width, header.height);
595 }
596
597 // Sanity check: prevent unreasonably large frames (e.g., > 8K resolution for RGB)
598 // This protects against resource exhaustion attacks
599 const uint32_t MAX_WIDTH = 8192; // 8K for RGB data
600 const uint32_t MAX_HEIGHT = 8192; // 8K for RGB data
601 if (header.width > MAX_WIDTH || header.height > MAX_HEIGHT) {
602 return SET_ERRNO(ERROR_INVALID_PARAM, "Image dimensions too large: %ux%u (max: %ux%u)", header.width, header.height,
603 MAX_WIDTH, MAX_HEIGHT);
604 }
605
606 // Validate pixel format
607 // Valid formats: RGB24 (3), RGBA32 (4), YUV420 (1.5)
608 if (header.pixel_format == 0 || header.pixel_format > 4) {
609 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid pixel format: %u (expected: 1-4)", header.pixel_format);
610 }
611
612 callbacks->on_image_frame(&header, pixel_data, pixel_data_len, client_ctx, callbacks->app_ctx);
613 return ASCIICHAT_OK;
614}
615
616static asciichat_error_t handle_server_audio_batch(const void *payload, size_t payload_len, void *client_ctx,
617 const acip_server_callbacks_t *callbacks) {
618 if (!callbacks->on_audio_batch) {
619 return ASCIICHAT_OK;
620 }
621
622 if (payload_len < sizeof(audio_batch_packet_t)) {
623 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH payload too small");
624 }
625
626 // Parse batch header
627 const audio_batch_packet_t *batch_header = (const audio_batch_packet_t *)payload;
628 uint32_t batch_count = NET_TO_HOST_U32(batch_header->batch_count);
629 uint32_t total_samples = NET_TO_HOST_U32(batch_header->total_samples);
630 uint32_t sample_rate = NET_TO_HOST_U32(batch_header->sample_rate);
631 uint32_t channels = NET_TO_HOST_U32(batch_header->channels);
632
633 // Validate size
634 size_t expected_size = sizeof(audio_batch_packet_t) + (total_samples * sizeof(uint32_t));
635 if (payload_len != expected_size) {
636 return SET_ERRNO(ERROR_INVALID_PARAM, "AUDIO_BATCH size mismatch");
637 }
638
639 // Extract quantized samples
640 const uint8_t *samples_ptr = (const uint8_t *)payload + sizeof(audio_batch_packet_t);
641
642 // Dequantize samples to float
643 float *samples = SAFE_MALLOC(total_samples * sizeof(float), float *);
644 if (!samples) {
645 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate audio batch buffer");
646 }
647
648 asciichat_error_t result = audio_dequantize_samples(samples_ptr, total_samples, samples);
649 if (result != ASCIICHAT_OK) {
650 SAFE_FREE(samples);
651 return result;
652 }
653
654 // Copy header for callback
655 audio_batch_packet_t header_copy = *batch_header;
656 header_copy.batch_count = batch_count;
657 header_copy.total_samples = total_samples;
658 header_copy.sample_rate = sample_rate;
659 header_copy.channels = channels;
660
661 callbacks->on_audio_batch(&header_copy, samples, total_samples, client_ctx, callbacks->app_ctx);
662
663 SAFE_FREE(samples);
664 return ASCIICHAT_OK;
665}
666
667static asciichat_error_t handle_server_audio_opus(const void *payload, size_t payload_len, void *client_ctx,
668 const acip_server_callbacks_t *callbacks) {
669 if (!callbacks->on_audio_opus) {
670 return ASCIICHAT_OK;
671 }
672
673 callbacks->on_audio_opus(payload, payload_len, client_ctx, callbacks->app_ctx);
674 return ASCIICHAT_OK;
675}
676
677static asciichat_error_t handle_server_audio_opus_batch(const void *payload, size_t payload_len, void *client_ctx,
678 const acip_server_callbacks_t *callbacks) {
679 if (!callbacks->on_audio_opus_batch) {
680 return ASCIICHAT_OK;
681 }
682
683 callbacks->on_audio_opus_batch(payload, payload_len, client_ctx, callbacks->app_ctx);
684 return ASCIICHAT_OK;
685}
686
687static asciichat_error_t handle_server_client_join(const void *payload, size_t payload_len, void *client_ctx,
688 const acip_server_callbacks_t *callbacks) {
689 if (!callbacks->on_client_join) {
690 return ASCIICHAT_OK;
691 }
692
693 callbacks->on_client_join(payload, payload_len, client_ctx, callbacks->app_ctx);
694 return ASCIICHAT_OK;
695}
696
697static asciichat_error_t handle_server_client_leave(const void *payload, size_t payload_len, void *client_ctx,
698 const acip_server_callbacks_t *callbacks) {
699 (void)payload;
700 (void)payload_len;
701
702 if (!callbacks->on_client_leave) {
703 return ASCIICHAT_OK;
704 }
705
706 callbacks->on_client_leave(client_ctx, callbacks->app_ctx);
707 return ASCIICHAT_OK;
708}
709
710static asciichat_error_t handle_server_stream_start(const void *payload, size_t payload_len, void *client_ctx,
711 const acip_server_callbacks_t *callbacks) {
712 if (!callbacks->on_stream_start) {
713 return ASCIICHAT_OK;
714 }
715
716 if (payload_len < sizeof(uint32_t)) {
717 return SET_ERRNO(ERROR_INVALID_PARAM, "STREAM_START payload too small (got %zu, expected %zu)", payload_len,
718 sizeof(uint32_t));
719 }
720
721 // Read stream_types as uint32_t (network byte order)
722 uint32_t stream_types_net;
723 memcpy(&stream_types_net, payload, sizeof(uint32_t));
724 uint32_t stream_types = NET_TO_HOST_U32(stream_types_net);
725
726 callbacks->on_stream_start(stream_types, client_ctx, callbacks->app_ctx);
727 return ASCIICHAT_OK;
728}
729
730static asciichat_error_t handle_server_stream_stop(const void *payload, size_t payload_len, void *client_ctx,
731 const acip_server_callbacks_t *callbacks) {
732 if (!callbacks->on_stream_stop) {
733 return ASCIICHAT_OK;
734 }
735
736 if (payload_len < sizeof(uint32_t)) {
737 return SET_ERRNO(ERROR_INVALID_PARAM, "STREAM_STOP payload too small (got %zu, expected %zu)", payload_len,
738 sizeof(uint32_t));
739 }
740
741 // Read stream_types as uint32_t (network byte order)
742 uint32_t stream_types_net;
743 memcpy(&stream_types_net, payload, sizeof(uint32_t));
744 uint32_t stream_types = NET_TO_HOST_U32(stream_types_net);
745
746 callbacks->on_stream_stop(stream_types, client_ctx, callbacks->app_ctx);
747 return ASCIICHAT_OK;
748}
749
750static asciichat_error_t handle_server_capabilities(const void *payload, size_t payload_len, void *client_ctx,
751 const acip_server_callbacks_t *callbacks) {
752 if (!callbacks->on_capabilities) {
753 return ASCIICHAT_OK;
754 }
755
756 callbacks->on_capabilities(payload, payload_len, client_ctx, callbacks->app_ctx);
757 return ASCIICHAT_OK;
758}
759
760static asciichat_error_t handle_server_ping(const void *payload, size_t payload_len, void *client_ctx,
761 const acip_server_callbacks_t *callbacks) {
762 (void)payload;
763 (void)payload_len;
764
765 if (callbacks->on_ping) {
766 callbacks->on_ping(client_ctx, callbacks->app_ctx);
767 }
768 return ASCIICHAT_OK;
769}
770
771static asciichat_error_t handle_server_remote_log(const void *payload, size_t payload_len, void *client_ctx,
772 const acip_server_callbacks_t *callbacks) {
773 if (!callbacks->on_remote_log) {
774 return ASCIICHAT_OK;
775 }
776
777 if (payload_len < sizeof(remote_log_packet_t)) {
778 return SET_ERRNO(ERROR_INVALID_PARAM, "REMOTE_LOG payload too small");
779 }
780
781 const remote_log_packet_t *header = (const remote_log_packet_t *)payload;
782 const char *message = (const char *)payload + sizeof(remote_log_packet_t);
783 size_t msg_len = payload_len - sizeof(remote_log_packet_t);
784
785 // Ensure null-terminated message
786 char msg_buffer[512];
787 size_t copy_len = msg_len < sizeof(msg_buffer) - 1 ? msg_len : sizeof(msg_buffer) - 1;
788 memcpy(msg_buffer, message, copy_len);
789 msg_buffer[copy_len] = '\0';
790
791 callbacks->on_remote_log(header, msg_buffer, client_ctx, callbacks->app_ctx);
792 return ASCIICHAT_OK;
793}
794
795static asciichat_error_t handle_server_protocol_version(const void *payload, size_t payload_len, void *client_ctx,
796 const acip_server_callbacks_t *callbacks) {
797 if (!callbacks->on_protocol_version) {
798 return ASCIICHAT_OK;
799 }
800
801 if (payload_len != sizeof(protocol_version_packet_t)) {
802 return SET_ERRNO(ERROR_INVALID_PARAM, "PROTOCOL_VERSION size mismatch");
803 }
804
805 callbacks->on_protocol_version((const protocol_version_packet_t *)payload, client_ctx, callbacks->app_ctx);
806 return ASCIICHAT_OK;
807}
808
809static asciichat_error_t handle_server_audio(const void *payload, size_t payload_len, void *client_ctx,
810 const acip_server_callbacks_t *callbacks) {
811 if (!callbacks->on_audio) {
812 return ASCIICHAT_OK;
813 }
814
815 // Raw audio data (float samples)
816 callbacks->on_audio(payload, payload_len, client_ctx, callbacks->app_ctx);
817 return ASCIICHAT_OK;
818}
819
820static asciichat_error_t handle_server_pong(const void *payload, size_t payload_len, void *client_ctx,
821 const acip_server_callbacks_t *callbacks) {
822 (void)payload;
823 (void)payload_len;
824
825 if (callbacks->on_pong) {
826 callbacks->on_pong(client_ctx, callbacks->app_ctx);
827 }
828 return ASCIICHAT_OK;
829}
830
831static asciichat_error_t handle_server_error_message(const void *payload, size_t payload_len, void *client_ctx,
832 const acip_server_callbacks_t *callbacks) {
833 if (!callbacks->on_error) {
834 return ASCIICHAT_OK;
835 }
836
837 if (payload_len < sizeof(error_packet_t)) {
838 return SET_ERRNO(ERROR_INVALID_PARAM, "ERROR_MESSAGE payload too small");
839 }
840
841 const error_packet_t *header = (const error_packet_t *)payload;
842 const char *message = (const char *)payload + sizeof(error_packet_t);
843 size_t msg_len = payload_len - sizeof(error_packet_t);
844
845 // Ensure null-terminated message
846 char msg_buffer[MAX_ERROR_MESSAGE_LENGTH + 1];
847 size_t copy_len = msg_len < MAX_ERROR_MESSAGE_LENGTH ? msg_len : MAX_ERROR_MESSAGE_LENGTH;
848 memcpy(msg_buffer, message, copy_len);
849 msg_buffer[copy_len] = '\0';
850
851 callbacks->on_error(header, msg_buffer, client_ctx, callbacks->app_ctx);
852 return ASCIICHAT_OK;
853}
854
855static asciichat_error_t handle_server_crypto_rekey_request(const void *payload, size_t payload_len, void *client_ctx,
856 const acip_server_callbacks_t *callbacks) {
857 if (callbacks->on_crypto_rekey_request) {
858 callbacks->on_crypto_rekey_request(payload, payload_len, client_ctx, callbacks->app_ctx);
859 }
860 return ASCIICHAT_OK;
861}
862
863static asciichat_error_t handle_server_crypto_rekey_response(const void *payload, size_t payload_len, void *client_ctx,
864 const acip_server_callbacks_t *callbacks) {
865 if (callbacks->on_crypto_rekey_response) {
866 callbacks->on_crypto_rekey_response(payload, payload_len, client_ctx, callbacks->app_ctx);
867 }
868 return ASCIICHAT_OK;
869}
870
871static asciichat_error_t handle_server_crypto_rekey_complete(const void *payload, size_t payload_len, void *client_ctx,
872 const acip_server_callbacks_t *callbacks) {
873 if (callbacks->on_crypto_rekey_complete) {
874 callbacks->on_crypto_rekey_complete(payload, payload_len, client_ctx, callbacks->app_ctx);
875 }
876 return ASCIICHAT_OK;
877}
⚠️‼️ Error and/or exit() when things go bad.
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U32(val)
Definition endian.h:86
acip_session_joined_t
acip_webrtc_ice_t
acip_webrtc_sdp_t
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 int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
unsigned char uint8_t
Definition common.h:56
#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)
Definition error_codes.h:46
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
#define log_debug(...)
Log a DEBUG message.
uint32_t pixel_format
Pixel format enum (0=RGB24, 1=RGBA32, 2=BGR24, etc.)
Definition packet.h:774
uint32_t timestamp
Timestamp when frame was captured (milliseconds since epoch)
Definition packet.h:780
uint32_t width
Terminal width in characters.
Definition packet.h:742
uint32_t checksum
CRC32 checksum of original ASCII data.
Definition packet.h:750
uint32_t original_size
Size of original uncompressed ASCII data in bytes.
Definition packet.h:746
uint32_t height
Terminal height in characters.
Definition packet.h:744
uint32_t compressed_size
Size of compressed data (0 = not compressed)
Definition packet.h:748
uint32_t compressed_size
Compressed data size (0 = not compressed, >0 = compressed)
Definition packet.h:776
uint32_t checksum
CRC32 checksum of pixel data.
Definition packet.h:778
uint32_t channels
Number of audio channels (1=mono, 2=stereo)
Definition packet.h:804
uint32_t height
Image height in pixels.
Definition packet.h:772
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32)
Definition packet.h:798
uint32_t width
Image width in pixels.
Definition packet.h:770
uint32_t flags
Frame flags bitmask (HAS_COLOR, IS_COMPRESSED, etc.)
Definition packet.h:752
uint32_t sample_rate
Sample rate in Hz (e.g., 44100, 48000)
Definition packet.h:802
uint32_t total_samples
Total audio samples across all chunks (typically 8192)
Definition packet.h:800
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:281
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
Definition packet.h:122
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional)
Definition packet.h:390
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:359
@ PACKET_TYPE_AUDIO_OPUS
Opus-encoded single audio frame.
Definition packet.h:357
@ PACKET_TYPE_CLIENT_LEAVE
Clean disconnect notification.
Definition packet.h:302
@ PACKET_TYPE_IMAGE_FRAME
Complete RGB image with dimensions.
Definition packet.h:288
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:297
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:304
@ PACKET_TYPE_AUDIO
Single audio packet (legacy)
Definition packet.h:291
@ PACKET_TYPE_CRYPTO_REKEY_COMPLETE
Initiator -> Responder: Empty (encrypted with NEW key, but still handshake)
Definition packet.h:340
@ PACKET_TYPE_SERVER_STATE
Server sends current state to clients.
Definition packet.h:310
@ PACKET_TYPE_CRYPTO_REKEY_REQUEST
Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:336
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:354
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:283
@ PACKET_TYPE_CLEAR_CONSOLE
Server tells client to clear console.
Definition packet.h:308
@ PACKET_TYPE_ASCII_FRAME
Complete ASCII frame with all metadata.
Definition packet.h:286
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:388
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:300
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:293
@ 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:295
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:343
@ PACKET_TYPE_CRYPTO_REKEY_RESPONSE
Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:338
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client)
Definition packet.h:379
@ PACKET_TYPE_STREAM_STOP
Client stops sending media.
Definition packet.h:306
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.
Definition handlers.c:534
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.
Definition handlers.c:119
asciichat_error_t(* acip_client_handler_func_t)(const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
Client-side packet handler function pointer type.
Definition handlers.c:39
asciichat_error_t(* acip_server_handler_func_t)(const void *payload, size_t payload_len, void *client_ctx, const acip_server_callbacks_t *callbacks)
Server-side packet handler function pointer type.
Definition handlers.c:53
ACIP protocol packet handlers (transport-agnostic)
🔊 Audio Capture and Playback Interface for ascii-chat
📝 Logging API with multiple log levels and terminal output control
ACIP protocol message structures and helpers.
ASCII-Chat Discovery Service (ACDS) Protocol Message Formats.
Packet protocol implementation with encryption and compression support.
Shared packet parsing utilities to eliminate duplication between server and client handlers.
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_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_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_audio_opus)(const void *opus_data, size_t opus_len, void *ctx)
Called when Opus audio received from server.
Definition handlers.h:62
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 * app_ctx
Application context (passed to all callbacks)
Definition handlers.h:101
void(* on_server_state)(const server_state_packet_t *state, void *ctx)
Called when server state update received.
Definition handlers.h:68
void(* on_audio)(const void *audio_data, size_t audio_len, void *ctx)
Called when raw audio received from server.
Definition handlers.h:56
Server-side packet handler callbacks.
Definition handlers.h:130
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:135
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:164
void(* on_pong)(void *client_ctx, void *app_ctx)
Called when pong received from client.
Definition handlers.h:170
void * app_ctx
Application context (passed to all callbacks)
Definition handlers.h:188
void(* on_stream_start)(uint32_t stream_types, void *client_ctx, void *app_ctx)
Called when client starts streaming.
Definition handlers.h:158
void(* on_ping)(void *client_ctx, void *app_ctx)
Called when ping received (should send pong)
Definition handlers.h:167
void(* on_stream_stop)(uint32_t stream_types, void *client_ctx, void *app_ctx)
Called when client stops streaming.
Definition handlers.h:161
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:182
void(* on_client_leave)(void *client_ctx, void *app_ctx)
Called when client leaves.
Definition handlers.h:155
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:176
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:173
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:185
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:152
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:149
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:142
void(* on_audio)(const void *audio_data, size_t audio_len, void *client_ctx, void *app_ctx)
Called when client sends raw audio.
Definition handlers.h:139
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:179
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:132
void(* on_audio_opus)(const void *opus_data, size_t opus_len, void *client_ctx, void *app_ctx)
Called when client sends Opus audio.
Definition handlers.h:146
Transport instance structure.
Definition transport.h:169
ASCII frame packet structure (Packet Type 2)
Definition packet.h:740
Audio batch packet structure (Packet Type 28)
Definition packet.h:796
Error packet structure carrying error code and textual description.
Definition packet.h:619
Image frame packet structure (Packet Type 3)
Definition packet.h:768
Protocol version negotiation packet structure (Packet Type 1)
Definition packet.h:710
Remote log packet structure carrying log level and message text.
Definition packet.h:633
Server state packet structure.
Definition packet.h:598