ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
protocol_adapter.h File Reference

Go to the source code of this file.

Functions

asciichat_error_t client_protocol_start (client_connection_t *connection)
bool client_protocol_connection_lost (client_connection_t *connection)

Function Documentation

◆ client_protocol_connection_lost()

bool client_protocol_connection_lost ( client_connection_t * connection)

Definition at line 494 of file protocol_adapter.c.

494 {
495 return atomic_load_bool(&connection->data_thread_exited) || server_connection_is_lost(connection);
496}
bool server_connection_is_lost(client_connection_t *connection)
Check if connection loss has been detected.

References client_connection::data_thread_exited, and server_connection_is_lost().

◆ client_protocol_start()

asciichat_error_t client_protocol_start ( client_connection_t * connection)

Definition at line 361 of file protocol_adapter.c.

361 {
362 log_warn("[FRAME_RECV_INIT] 🟢 PROTOCOL_START: Starting client protocol initialization");
363
364 // The server can send Opus audio as soon as streaming starts. Initialize the
365 // decoder before either transport's receive worker can dispatch those packets.
366 asciichat_error_t audio_result = audio_connection_init(connection);
367 if (audio_result != ASCIICHAT_OK)
368 return audio_result;
369 if (client_connection_checkpoint("audio-pipeline") != ASCIICHAT_OK)
370 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
371
372 acip_client_callbacks_t control = g_acip_client_callbacks;
373 control.app_ctx = connection;
375 (session_protocol_media_t){.terminal_output = true,
376 .on_frame = client_present_frame,
377 .on_samples = client_play_samples,
378 .decode_opus = client_decode_opus,
379 .on_audio_packet = client_track_audio,
380 .context = connection,
381 .frame_workspace = &connection->decoded_ascii_workspace},
382 control);
383
384 // Reset display state for new connection
386
387 // Start receiving before the capability/settings exchange. The server may
388 // reply to the initial settings packet immediately, and that reply must not
389 // wait for the worker that dispatches it.
390 atomic_store_bool(&connection->data_thread_exited, false);
391 if (thread_pool_spawn(connection->client_worker_pool, data_reception_thread_func, connection, 1, "data_reception") !=
392 ASCIICHAT_OK) {
393 log_error("[FRAME_RECV_INIT] Failed to start data reception thread");
394 LOG_ERRNO_IF_SET("Data reception thread creation failed");
395 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
396 }
397
399 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
400
401 // Send CLIENT_CAPABILITIES packet (after crypto is ready for both TCP and WS)
402 // Server expects this as the first packet after crypto handshake
403 log_debug("[FRAME_RECV_INIT] Sending CLIENT_CAPABILITIES (terminal size)");
404 asciichat_error_t cap_result =
405 threaded_send_terminal_size_with_auto_detect(connection, (int)terminal_get_effective_width(GET_OPTION(width), 80),
406 (int)terminal_get_effective_height(GET_OPTION(height), 24));
407 if (cap_result != ASCIICHAT_OK) {
408 log_error("[FRAME_RECV_INIT] Failed to send terminal size capabilities");
409 return client_startup_peer_error(connection, cap_result);
410 }
411
412 // Capabilities establish the viewer geometry, but renderer preferences are
413 // carried by SETTINGS_SYNC. Send them before STREAM_START so the server
414 // cannot render the first frames from its zero-initialized (luminance)
415 // fallback while the asynchronous settings update is still in flight.
416 options_snapshot_t initial_settings;
417 if (options_read_snapshot(&initial_settings) != ASCIICHAT_OK)
418 return client_startup_peer_error(connection, ERROR_CONFIG);
419 asciichat_error_t settings_result =
420 acip_settings_send(server_connection_get_transport(connection), &initial_settings.values, 1, UINT32_MAX);
421 if (settings_result != ASCIICHAT_OK) {
422 log_error("[FRAME_RECV_INIT] Failed to send initial rendering settings");
423 return client_startup_peer_error(connection, settings_result);
424 }
425
426 // Send STREAM_START packet with combined stream types
427 uint32_t stream_types = STREAM_TYPE_VIDEO; // Always have video
428 if (GET_OPTION(audio_enabled)) {
429 stream_types |= STREAM_TYPE_AUDIO; // Add audio if enabled
430 }
431 log_debug("[FRAME_RECV_INIT] Sending STREAM_START: types=0x%x (video%s)", stream_types,
432 (stream_types & STREAM_TYPE_AUDIO) ? "+audio" : "");
433 asciichat_error_t stream_result = threaded_send_stream_start_packet(connection, stream_types);
434 if (stream_result != ASCIICHAT_OK) {
435 log_error("[FRAME_RECV_INIT] Failed to send stream start");
436 return client_startup_peer_error(connection, stream_result);
437 }
438
439 // Start webcam capture thread
440 log_debug("Starting webcam capture thread...");
441 if (capture_start_thread(connection) != 0) {
442 log_error("Failed to start webcam capture thread");
443 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
444 }
445 log_debug("Webcam capture thread started successfully");
447 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
448
449 // Start local media audio only after the connection succeeds. Starting the
450 // worker during client initialization would consume file audio while dialing.
451 audio_context_t *audio_context = connection->audio_context;
452 if (audio_context && audio_context->initialized) {
453 session_capture_set_audio_context(connection->capture_context, audio_context);
455 if (audio_start_duplex(audio_context) != ASCIICHAT_OK) {
456 log_error("Failed to start full-duplex audio");
457 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
458 }
459 }
460
461 if (client_connection_checkpoint("audio-duplex") != ASCIICHAT_OK)
462 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
463
464 // Initialize audio sender thread BEFORE starting audio capture
465 // This ensures sender is ready when capture thread starts queueing packets
466 // Must happen after connection succeeds to prevent deadlock if connection fails
467 if (audio_sender_init(connection) != ASCIICHAT_OK)
468 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
469
470 // Start audio capture thread if audio is enabled
471 log_debug("Starting audio capture thread...");
472 if (audio_start_thread(connection) != 0) {
473 log_error("Failed to start audio capture thread");
474 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
475 }
476 log_debug("Audio capture thread started successfully (or skipped if audio disabled)");
477
478 if (client_connection_checkpoint("audio-capture") != ASCIICHAT_OK)
479 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
480
481 // Start keepalive/ping thread to prevent server timeout
482 log_debug("Starting keepalive/ping thread...");
483 if (keepalive_start_thread(connection) != 0) {
484 log_error("Failed to start keepalive/ping thread");
485 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
486 }
487 log_debug("Keepalive/ping thread started successfully");
488
489 if (client_connection_checkpoint("keepalive") != ASCIICHAT_OK)
490 return SET_ERRNO(ERROR_THREAD, "Could not start client protocol workers");
491 return ASCIICHAT_OK;
492}
#define SET_ERRNO(code,...)
#define LOG_ERRNO_IF_SET(message)
int capture_start_thread(client_connection_t *connection)
Start capture thread.
void session_capture_set_audio_context(session_capture_ctx_t *ctx, void *audio_ctx)
Set the audio context for playback control.
void session_protocol_init(session_protocol_t *protocol, acip_transport_t *transport, session_protocol_media_t media, acip_client_callbacks_t control)
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
asciichat_error_t audio_start_duplex(audio_context_t *ctx)
Start full-duplex audio (simultaneous capture and playback).
void display_reset_for_new_connection(client_connection_t *connection)
Reset display state for new connection.
unsigned int uint32_t
Definition common.h:62
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_CONFIG
Definition error_codes.h:52
@ ERROR_THREAD
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define STREAM_TYPE_VIDEO
Video stream.
Definition packet.h:993
#define STREAM_TYPE_AUDIO
Audio stream.
Definition packet.h:994
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
void * session_capture_get_media_source(session_capture_ctx_t *ctx)
Get the underlying media source from capture context.
int keepalive_start_thread(client_connection_t *connection)
Start keepalive/ping thread.
Definition keepalive.c:209
asciichat_error_t acip_settings_send(acip_transport_t *transport, const options_t *values, uint32_t request, uint32_t expected_revision)
asciichat_error_t options_read_snapshot(options_snapshot_t *snapshot)
Definition rcu.c:865
asciichat_error_t audio_connection_init(client_connection_t *connection)
int audio_start_thread(client_connection_t *connection)
Start audio capture thread.
asciichat_error_t audio_sender_init(client_connection_t *connection)
Initialize async audio sender queue and thread.
asciichat_error_t client_connection_checkpoint(const char *stage)
asciichat_error_t threaded_send_terminal_size_with_auto_detect(client_connection_t *connection, unsigned short width, unsigned short height)
Thread-safe terminal size packet transmission with auto-detection.
asciichat_error_t threaded_send_stream_start_packet(client_connection_t *connection, uint32_t stream_type)
Thread-safe stream start packet transmission.
acip_transport_t * server_connection_get_transport(client_connection_t *connection)
Get ACIP transport instance.
Client-side packet handler callbacks.
Definition handlers.h:51
void * app_ctx
Application context (passed to all callbacks).
Definition handlers.h:113
When code needs to work with audio device info, it keeps index, max_input_channels,...
bool initialized
True if context has been initialized.
void * capture_media_source
Borrowed local media audio; configure before starting duplex.
thread_pool_t * client_worker_pool
session_protocol_t protocol
session_capture_ctx_t * capture_context
buffer_workspace_t decoded_ascii_workspace
audio_context_t * audio_context
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Spawn a worker thread in the pool.

References acip_settings_send(), acip_client_callbacks_t::app_ctx, ASCIICHAT_OK, audio_connection_init(), client_connection::audio_context, audio_sender_init(), audio_start_duplex(), audio_start_thread(), client_connection::capture_context, audio_context_t::capture_media_source, capture_start_thread(), client_connection_checkpoint(), client_connection::client_worker_pool, client_connection::data_thread_exited, client_connection::decoded_ascii_workspace, display_reset_for_new_connection(), ERROR_CONFIG, ERROR_THREAD, GET_OPTION, height, audio_context_t::initialized, keepalive_start_thread(), log_debug, LOG_ERRNO_IF_SET, log_error, log_warn, options_read_snapshot(), client_connection::protocol, server_connection_get_transport(), session_capture_get_media_source(), session_capture_set_audio_context(), session_protocol_init(), SET_ERRNO, STREAM_TYPE_AUDIO, STREAM_TYPE_VIDEO, thread_pool_spawn(), threaded_send_stream_start_packet(), threaded_send_terminal_size_with_auto_detect(), options_snapshot_t::values, and width.

Referenced by client_connection_start().