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

ACIP shared/bidirectional packet sending functions. More...

Go to the source code of this file.

Functions

asciichat_error_t packet_send_via_transportv (acip_transport_t *transport, packet_type_t type, const socket_buffer_t *payload, size_t count, uint32_t client_id)
 Send arbitrary packet via transport (generic packet sender).
asciichat_error_t packet_send_via_transport (acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
asciichat_error_t packet_receive_via_transport (acip_transport_t *transport, packet_type_t *type, void **payload, size_t *payload_len, void **alloc_buffer)
 Receive a packet via transport with header parsing and validation.
asciichat_error_t acip_send_audio_batch (acip_transport_t *transport, const float *samples, uint32_t num_samples, uint32_t batch_count)
 Send audio batch packet.
asciichat_error_t acip_send_audio_opus (acip_transport_t *transport, const void *opus_data, size_t opus_len)
 Send Opus-encoded audio packet.
asciichat_error_t acip_send_audio_opus_batch (acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
 Send batched Opus-encoded audio frames.
asciichat_error_t acip_send_ping (acip_transport_t *transport)
 Send ping packet.
asciichat_error_t acip_send_pong (acip_transport_t *transport)
 Send pong packet.
asciichat_error_t acip_send_error (acip_transport_t *transport, uint32_t error_code, const char *message)
 Send error message packet.
asciichat_error_t acip_send_remote_log (acip_transport_t *transport, uint8_t log_level, uint8_t direction, const char *message)
 Send remote log packet.
asciichat_error_t acip_send_session_created (acip_transport_t *transport, const acip_session_created_t *response)
 Send SESSION_CREATED response packet.
asciichat_error_t acip_send_session_info (acip_transport_t *transport, const acip_session_info_t *info)
 Send SESSION_INFO response packet.
asciichat_error_t acip_send_session_joined (acip_transport_t *transport, const acip_session_joined_t *response)
 Send SESSION_JOINED response packet.

Detailed Description

ACIP shared/bidirectional packet sending functions.

Provides bidirectional send functions (used by both client and server):

  • Audio sending (PCM, Opus, batched)
  • Ping/Pong keepalive
  • Error messages and remote logging
  • Low-level packet_send_via_transport() utility

For client-specific sends (image frames, join/leave, capabilities): → See lib/network/acip/client.h

For server-specific sends (ASCII frames, state updates, console control): → See lib/network/acip/server.h

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Definition in file send.h.

Function Documentation

◆ acip_send_audio_batch()

asciichat_error_t acip_send_audio_batch ( acip_transport_t * transport,
const float * samples,
uint32_t num_samples,
uint32_t batch_count )

Send audio batch packet.

Parameters
transportTransport instance
samplesAudio samples (float array)
num_samplesNumber of samples
batch_countNumber of batches aggregated
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 295 of file send.c.

296 {
297 ASCII_OTEL_SCOPE(ACIP_SEND_AUDIO_BATCH);
298 if (!transport || !samples || num_samples == 0) {
299 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
300 }
301
302 if (num_samples > (MAX_PACKET_SIZE - sizeof(audio_batch_packet_t)) / sizeof(uint32_t))
303 return SET_ERRNO(ERROR_NETWORK_SIZE, "PCM batch exceeds the packet size limit");
304
305 // The receiver consumes network-order signed PCM32, rather than host floats.
307 header.batch_count = HOST_TO_NET_U32(batch_count);
308 header.total_samples = HOST_TO_NET_U32(num_samples);
309 header.sample_rate = HOST_TO_NET_U32(AUDIO_SAMPLE_RATE);
310 header.channels = HOST_TO_NET_U32(1);
311 uint32_t inline_samples[OPUS_FRAME_SAMPLES];
312 uint32_t *wire =
313 num_samples <= OPUS_FRAME_SAMPLES ? inline_samples : buffer_alloc(NULL, (size_t)num_samples * sizeof(uint32_t));
314 if (!wire)
315 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate PCM batch samples");
316 defer({
317 if (wire != inline_samples)
318 buffer_release(wire);
319 });
320 _Static_assert(sizeof(float) == sizeof(uint32_t), "ACIP audio requires 32-bit floats");
321 for (uint32_t i = 0; i < num_samples; ++i) {
322 // Inspect the exponent before arithmetic, including builds that assume finite floats.
323 uint32_t sample_bits;
324 memcpy(&sample_bits, &samples[i], sizeof(sample_bits));
325 if ((sample_bits & UINT32_C(0x7f800000)) == UINT32_C(0x7f800000)) {
326 return SET_ERRNO(ERROR_INVALID_PARAM, "PCM samples must be finite");
327 }
328 double sample = fmax(-1.0, fmin(1.0, samples[i]));
329 int32_t scaled = (int32_t)(sample * INT32_MAX);
330 wire[i] = HOST_TO_NET_U32((uint32_t)scaled);
331 }
332 socket_buffer_t slices[] = {{&header, sizeof(header)}, {wire, (size_t)num_samples * sizeof(uint32_t)}};
334 return result;
335}
#define SET_ERRNO(code,...)
#define OPUS_FRAME_SAMPLES
#define AUDIO_SAMPLE_RATE
Definition audio/limits.h:4
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
#define buffer_release(data)
Definition buffer.h:91
#define HOST_TO_NET_U32(val)
Definition endian.h:66
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
@ ERROR_MEMORY
Definition error_codes.h:51
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_INVALID_PARAM
#define MAX_PACKET_SIZE
Maximum packet size (5MB).
Definition packet.h:130
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:396
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72
asciichat_error_t packet_send_via_transportv(acip_transport_t *transport, packet_type_t type, const socket_buffer_t *payload, size_t count, uint32_t client_id)
Send arbitrary packet via transport (generic packet sender).
Definition send.c:155
The metadata prefix for a batch of encoded audio samples. batch_count, total_samples,...
Definition packet.h:960
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32).
Definition packet.h:962

References ASCII_OTEL_SCOPE, AUDIO_SAMPLE_RATE, audio_batch_packet_t::batch_count, buffer_alloc(), buffer_release, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK_SIZE, HOST_TO_NET_U32, MAX_PACKET_SIZE, OPUS_FRAME_SAMPLES, packet_send_via_transportv(), PACKET_TYPE_AUDIO_BATCH, and SET_ERRNO.

Referenced by threaded_send_audio_batch_packet().

◆ acip_send_audio_opus()

asciichat_error_t acip_send_audio_opus ( acip_transport_t * transport,
const void * opus_data,
size_t opus_len )

Send Opus-encoded audio packet.

Parameters
transportTransport instance
opus_dataOpus-encoded audio data
opus_lenLength of encoded data
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 337 of file send.c.

337 {
338 ASCII_OTEL_SCOPE(ACIP_SEND_AUDIO_OPUS);
339 if (!transport || !opus_data || opus_len == 0) {
340 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
341 }
342
343 // Convert single opus frame to batch format (frame_count=1) with standard parameters
344 // Standard: 48kHz sample rate, 20ms frame duration
345 uint16_t frame_sizes[1] = {(uint16_t)opus_len};
346 return acip_send_audio_opus_batch(transport, opus_data, opus_len, frame_sizes, 1, AUDIO_SAMPLE_RATE,
348}
#define CLIENT_AUDIO_PIPELINE_FRAME_MS
unsigned short uint16_t
Definition common.h:61
asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
Send batched Opus-encoded audio frames.
Definition send.c:350

References acip_send_audio_opus_batch(), ASCII_OTEL_SCOPE, AUDIO_SAMPLE_RATE, CLIENT_AUDIO_PIPELINE_FRAME_MS, ERROR_INVALID_PARAM, and SET_ERRNO.

◆ acip_send_audio_opus_batch()

asciichat_error_t acip_send_audio_opus_batch ( acip_transport_t * transport,
const void * opus_data,
size_t opus_len,
const uint16_t * frame_sizes,
uint32_t frame_count,
uint32_t sample_rate,
uint32_t frame_duration )

Send batched Opus-encoded audio frames.

Parameters
transportTransport instance
opus_dataOpus-encoded data (multiple frames concatenated)
opus_lenTotal length of encoded data
frame_sizesArray of individual frame sizes
frame_countNumber of frames in batch
sample_rateSample rate in Hz
frame_durationDuration per frame in milliseconds
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 350 of file send.c.

352 {
353 ASCII_OTEL_SCOPE(ACIP_SEND_AUDIO_OPUS_BATCH);
354 if (!transport || !opus_data || !frame_sizes || frame_count == 0) {
355 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
356 }
357
358 // Build batch header (16 bytes)
360 *(uint32_t *)(header + 0) = HOST_TO_NET_U32(sample_rate);
361 *(uint32_t *)(header + 4) = HOST_TO_NET_U32(frame_duration);
362 *(uint32_t *)(header + 8) = HOST_TO_NET_U32(frame_count);
363 *(uint32_t *)(header + 12) = 0; // reserved
364
365 if (frame_count > MAX_PACKET_SIZE / sizeof(uint16_t) || opus_len > MAX_PACKET_SIZE)
366 return SET_ERRNO(ERROR_NETWORK_SIZE, "Opus batch too large");
367 size_t sizes_len = (size_t)frame_count * sizeof(uint16_t);
369 uint16_t *sizes = frame_count <= ACIP_OPUS_INLINE_FRAME_CAPACITY ? inline_sizes : buffer_alloc(NULL, sizes_len);
370 if (!sizes)
371 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate Opus lengths");
372 for (uint32_t i = 0; i < frame_count; ++i)
373 sizes[i] = HOST_TO_NET_U16(frame_sizes[i]);
374 socket_buffer_t slices[] = {{header, sizeof(header)}, {sizes, sizes_len}, {opus_data, opus_len}};
376 if (sizes != inline_sizes)
377 buffer_release(sizes);
378 return result;
379}
#define HOST_TO_NET_U16(val)
Definition endian.h:96
unsigned char uint8_t
Definition common.h:60
#define ACIP_OPUS_BATCH_HEADER_SIZE
Definition packet.h:81
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:398
#define ACIP_OPUS_INLINE_FRAME_CAPACITY
Definition send.c:13

References ACIP_OPUS_BATCH_HEADER_SIZE, ACIP_OPUS_INLINE_FRAME_CAPACITY, ASCII_OTEL_SCOPE, buffer_alloc(), buffer_release, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK_SIZE, HOST_TO_NET_U16, HOST_TO_NET_U32, MAX_PACKET_SIZE, packet_send_via_transportv(), PACKET_TYPE_AUDIO_OPUS_BATCH, and SET_ERRNO.

Referenced by acip_send_audio_opus(), session_host_render_audio(), threaded_send_audio_opus(), and threaded_send_audio_opus_batch().

◆ acip_send_error()

asciichat_error_t acip_send_error ( acip_transport_t * transport,
uint32_t error_code,
const char * message )

Send error message packet.

Parameters
transportTransport instance
error_codeError code from asciichat_error_t
messageLegacy argument; fixed public text is sent for the allowlisted error code
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 415 of file send.c.

415 {
416 ASCII_OTEL_SCOPE(ACIP_SEND_ERROR);
417 if (!transport) {
418 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
419 }
420
421 error_code = network_error_public_code((asciichat_error_t)error_code);
422 // Error packets can be sent before authentication. Never let a caller's
423 // context or source locations cross the network boundary.
424 (void)message;
425 message = asciichat_error_string((asciichat_error_t)error_code);
426
427 // Calculate message length (max MAX_ERROR_MESSAGE_LENGTH)
428 size_t msg_len = 0;
429 if (message) {
430 msg_len = strlen(message);
431 if (msg_len > MAX_ERROR_MESSAGE_LENGTH) {
432 msg_len = MAX_ERROR_MESSAGE_LENGTH;
433 }
434 }
435
436 error_packet_t header;
437 header.error_code = HOST_TO_NET_U32(error_code);
438 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
439
440 // Allocate buffer for header + message
441 size_t total_size = sizeof(header) + msg_len;
442 uint8_t *buffer = buffer_alloc(buffer_control_domain(), total_size);
443 if (!buffer) {
444 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
445 }
446
447 // Build packet
448 memcpy(buffer, &header, sizeof(header));
449 if (msg_len > 0) {
450 memcpy(buffer + sizeof(header), message, msg_len);
451 }
452
453 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_ERROR_MESSAGE, buffer, total_size, 0);
454
455 buffer_release(buffer);
456 return result;
457}
buffer_domain_t * buffer_control_domain(void)
Definition buffer.c:436
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes).
Definition packet.h:139
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:374
asciichat_error_t network_error_public_code(asciichat_error_t code)
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Definition send.c:190
Error packet structure carrying error code and textual description.
Definition packet.h:767
uint32_t error_code
Error code from asciichat_error_t enumeration.
Definition packet.h:769

References ASCII_OTEL_SCOPE, buffer_alloc(), buffer_control_domain(), buffer_release, error_packet_t::error_code, ERROR_INVALID_PARAM, ERROR_MEMORY, HOST_TO_NET_U32, MAX_ERROR_MESSAGE_LENGTH, network_error_public_code(), packet_send_via_transport(), PACKET_TYPE_ERROR_MESSAGE, and SET_ERRNO.

Referenced by acds_on_host_announcement_ex(), disconnect_client_for_bad_data(), and process_decrypted_packet().

◆ acip_send_ping()

asciichat_error_t acip_send_ping ( acip_transport_t * transport)

Send ping packet.

Parameters
transportTransport instance
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 385 of file send.c.

385 {
386 ASCII_OTEL_SCOPE(ACIP_SEND_PING);
387 if (!transport) {
388 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
389 }
390
391 // Ping has no payload
392 return packet_send_via_transport(transport, PACKET_TYPE_PING, NULL, 0, 0);
393}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:407

References ASCII_OTEL_SCOPE, ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_PING, and SET_ERRNO.

Referenced by discovery_session_check_host_alive().

◆ acip_send_pong()

asciichat_error_t acip_send_pong ( acip_transport_t * transport)

Send pong packet.

Parameters
transportTransport instance
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 395 of file send.c.

395 {
396 if (!transport) {
397 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
398 }
399
400 // Pong has no payload
401 return packet_send_via_transport(transport, PACKET_TYPE_PONG, NULL, 0, 0);
402}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:409

References ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_PONG, and SET_ERRNO.

Referenced by handle_ping_packet(), session_host_receive_packet(), and session_protocol_dispatch().

◆ acip_send_remote_log()

asciichat_error_t acip_send_remote_log ( acip_transport_t * transport,
uint8_t log_level,
uint8_t direction,
const char * message )

Send remote log packet.

Parameters
transportTransport instance
log_levelLog level (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)
directionDirection hint (0=client->server, 1=server->client)
messageLog message
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 459 of file send.c.

460 {
461 if (!transport || !message) {
462 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or message");
463 }
464
465 size_t msg_len = strlen(message);
466
467 remote_log_packet_t header;
468 header.log_level = log_level;
469 header.direction = direction;
470 header.flags = 0;
471 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
472
473 size_t total_size = sizeof(header) + msg_len;
474
475 uint8_t *buffer = buffer_alloc(NULL, total_size);
476 if (!buffer) {
477 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
478 }
479
480 // Build packet
481 memcpy(buffer, &header, sizeof(header));
482 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary packet data
483 memcpy(buffer + sizeof(header), message, msg_len);
484
485 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_REMOTE_LOG, buffer, total_size, 0);
486
487 buffer_release(buffer);
488 return result;
489}
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:376
Remote log packet structure carrying log level and message text.
Definition packet.h:781
uint8_t log_level
Log level associated with the message (log_level_t cast to uint8_t).
Definition packet.h:783

References buffer_alloc(), buffer_release, ERROR_INVALID_PARAM, ERROR_MEMORY, HOST_TO_NET_U32, remote_log_packet_t::log_level, packet_send_via_transport(), PACKET_TYPE_REMOTE_LOG, and SET_ERRNO.

Referenced by log_net_message().

◆ acip_send_session_created()

asciichat_error_t acip_send_session_created ( acip_transport_t * transport,
const acip_session_created_t * response )

Send SESSION_CREATED response packet.

Parameters
transportTransport instance
responseSession created response structure
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 495 of file send.c.

495 {
496 if (!transport || !response) {
497 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
498 }
499
500 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_CREATED, response, sizeof(*response), 0);
501}
@ PACKET_TYPE_ACIP_SESSION_CREATED
Session created response (Discovery Server -> Client).
Definition packet.h:433

References acip_session_created_t, ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_CREATED, and SET_ERRNO.

◆ acip_send_session_info()

asciichat_error_t acip_send_session_info ( acip_transport_t * transport,
const acip_session_info_t * info )

Send SESSION_INFO response packet.

Parameters
transportTransport instance
infoSession info structure
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 503 of file send.c.

503 {
504 if (!transport || !info) {
505 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or info");
506 }
507
508 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_INFO, info, sizeof(*info), 0);
509}
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client).
Definition packet.h:437

References acip_session_info_t, ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_INFO, and SET_ERRNO.

◆ acip_send_session_joined()

asciichat_error_t acip_send_session_joined ( acip_transport_t * transport,
const acip_session_joined_t * response )

Send SESSION_JOINED response packet.

Parameters
transportTransport instance
responseSession joined response structure
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 511 of file send.c.

511 {
512 if (!transport || !response) {
513 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
514 }
515
516 if (!response->success)
518 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_JOINED, response, sizeof(*response), 0);
519}
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client).
Definition packet.h:441
void stats_counter_add(stats_scope_t *scope, stats_counter_id_t id, uint64_t amount)
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80

References acip_session_joined_t, ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_JOINED, SET_ERRNO, stats_counter_add(), STATS_COUNTER_REQUEST_FAILURES, and stats_runtime_scope().

◆ packet_receive_via_transport()

asciichat_error_t packet_receive_via_transport ( acip_transport_t * transport,
packet_type_t * type,
void ** payload,
size_t * payload_len,
void ** alloc_buffer )

Receive a packet via transport with header parsing and validation.

Transport counterpart to packet_receive(). Receives raw bytes via transport, parses the packet header, validates magic/CRC32, and returns the packet type and payload separately.

Parameters
transportTransport instance
[out]typePacket type from header
[out]payloadPointer to payload data within the received buffer
[out]payload_lenPayload length from header
[out]alloc_bufferBuffer to free when done (caller must free with SAFE_FREE)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 200 of file send.c.

201 {
202 ASCII_OTEL_SCOPE(PACKET_RECEIVE_VIA_TRANSPORT);
203 if (!transport || !type || !payload || !payload_len || !alloc_buffer) {
204 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
205 }
206
207 *type = 0;
208 *payload = NULL;
209 *payload_len = 0;
210 *alloc_buffer = NULL;
211
212receive_again:;
213 // Receive raw bytes via transport
214 void *recv_buffer = NULL;
215 size_t recv_len = 0;
216 asciichat_error_t result = acip_transport_recv(transport, &recv_buffer, &recv_len, alloc_buffer);
217 if (result == ERROR_NETWORK_INCOMPLETE)
218 goto receive_again;
219 if (result != ASCIICHAT_OK) {
220 if (result != ERROR_NETWORK_TIMEOUT)
221 stats_runtime_packet(transport->stats_peer, 0, 0, false, false);
222 return result;
223 }
224
225 // Parse packet header
226 if (recv_len < sizeof(packet_header_t)) {
227 log_warn("packet too small (%zu bytes, need %zu)", recv_len, sizeof(packet_header_t));
228 if (*alloc_buffer) {
229 buffer_release(*alloc_buffer);
230 *alloc_buffer = NULL;
231 }
232 stats_runtime_packet(transport->stats_peer, 0, 0, false, false);
233 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Received packet smaller than header");
234 }
235
236 packet_header_t wire_header;
237 result = packet_validate(recv_buffer, recv_len, 0, &wire_header);
238 if (result != ASCIICHAT_OK) {
239 buffer_release(*alloc_buffer);
240 *alloc_buffer = NULL;
241 return result;
242 }
243 if (NET_TO_HOST_U16(wire_header.type) == PACKET_TYPE_ENCRYPTED) {
244 packet_envelope_t envelope = {.type = PACKET_TYPE_ENCRYPTED,
245 .data = (uint8_t *)recv_buffer + sizeof(wire_header),
246 .len = recv_len - sizeof(wire_header),
247 .allocated_buffer = *alloc_buffer,
248 .allocated_size = *alloc_buffer ? buffer_capacity(*alloc_buffer) : 0};
249 result = packet_decrypt_envelope(&envelope, transport->crypto_ctx);
250 *alloc_buffer = envelope.allocated_buffer;
251 if (result != ASCIICHAT_OK || envelope.consumed) {
252 buffer_release(*alloc_buffer);
253 *alloc_buffer = NULL;
254 if (result == ASCIICHAT_OK)
255 goto receive_again;
256 return result;
257 }
258 recv_buffer = envelope.allocated_buffer;
259 recv_len = sizeof(packet_header_t) + envelope.len;
260 }
261 const packet_header_t *header = (const packet_header_t *)recv_buffer;
262 *type = (packet_type_t)NET_TO_HOST_U16(header->type);
263 size_t plen = NET_TO_HOST_U32(header->length);
264
265 if (recv_len < sizeof(packet_header_t) + plen) {
266 log_warn("truncated packet (header says %zu payload, got %zu total)", plen, recv_len);
267 if (*alloc_buffer) {
268 buffer_release(*alloc_buffer);
269 *alloc_buffer = NULL;
270 }
271 stats_runtime_packet(transport->stats_peer, 0, 0, false, false);
272 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Truncated packet payload");
273 }
274
275 if (plen > 0) {
276 *payload = (uint8_t *)recv_buffer + sizeof(packet_header_t);
277 }
278 *payload_len = plen;
279#ifndef __EMSCRIPTEN__
280 ASCII_OTEL_PACKET(*type, plen, "receive", acip_transport_get_type(transport));
281#endif
282 stats_runtime_packet(transport->stats_peer, *type, plen, false, true);
283
284 return ASCIICHAT_OK;
285}
size_t buffer_capacity(const void *data)
Definition buffer.c:280
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
@ ERROR_NETWORK_INCOMPLETE
Definition error_codes.h:90
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:75
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
asciichat_error_t packet_validate(const void *packet, size_t length, size_t limit, packet_header_t *output)
Definition packet.c:86
asciichat_error_t packet_decrypt_envelope(packet_envelope_t *envelope, void *crypto_ctx)
Send a clear console packet.
Definition packet.c:665
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:301
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion).
Definition packet.h:355
#define ASCII_OTEL_PACKET(type, size, action, transport)
Definition otel.h:68
void stats_runtime_packet(stats_peer_t *peer, unsigned type, size_t payload_bytes, bool sent, bool success)
Definition runtime.c:196
stats_peer_t * stats_peer
Owned statistics handle, destroyed with transport.
crypto_context_t * crypto_ctx
Optional encryption context.
Packet envelope containing received packet data.
Definition packet.h:1185
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628
uint16_t type
Packet type (packet_type_t enumeration).
Definition packet.h:632

References ASCII_OTEL_PACKET, ASCII_OTEL_SCOPE, ASCIICHAT_OK, buffer_capacity(), buffer_release, acip_transport::crypto_ctx, ERROR_INVALID_PARAM, ERROR_NETWORK_INCOMPLETE, ERROR_NETWORK_PROTOCOL, ERROR_NETWORK_TIMEOUT, log_warn, NET_TO_HOST_U16, NET_TO_HOST_U32, packet_decrypt_envelope(), PACKET_TYPE_ENCRYPTED, packet_validate(), SET_ERRNO, acip_transport::stats_peer, stats_runtime_packet(), and packet_header_t::type.

Referenced by acds_receive_packet(), acip_client_receive_packet(), and acip_server_receive_and_dispatch().

◆ packet_send_via_transport()

◆ packet_send_via_transportv()

asciichat_error_t packet_send_via_transportv ( acip_transport_t * transport,
packet_type_t type,
const socket_buffer_t * payload,
size_t count,
uint32_t client_id )

Send arbitrary packet via transport (generic packet sender).

Low-level function that builds packet header and sends via transport. Most code should use the type-specific functions (acip_send_ping, etc.), but this is useful for generic wrappers that handle multiple packet types.

Parameters
transportTransport instance
typePacket type
payloadPacket payload (can be NULL if payload_len==0)
payload_lenPayload length
client_idClient ID to include in packet header
Returns
ASCIICHAT_OK on success, error code on failure Send payload slices without assembling an intermediate media packet.

Definition at line 155 of file send.c.

156 {
157 ASCII_OTEL_SCOPE(PACKET_SEND_VIA_TRANSPORTV);
158 if (!transport || !payload || !count || count >= SOCKET_IOV_MAX)
159 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid packet vectors");
160 packet_header_t header;
161 asciichat_error_t result = packet_header_build(&header, type, payload, count, client_id);
162 if (result != ASCIICHAT_OK)
163 return result;
164 size_t len = NET_TO_HOST_U32(header.length);
165 socket_buffer_t slices[SOCKET_IOV_MAX] = {{&header, sizeof(header)}};
166 memcpy(slices + 1, payload, count * sizeof(*payload));
167 acip_send_options_t options = {.traffic_class = acip_packet_traffic_class(type), .delivery = ACIP_DELIVERY_RELIABLE};
168 bool encoded = type == PACKET_TYPE_IMAGE_FRAME_H264 || type == PACKET_TYPE_IMAGE_FRAME_H265;
169 bool prefer_datagram = (transport->prefer_audio_datagrams && options.traffic_class == ACIP_TRAFFIC_AUDIO) ||
171 (!encoded || client_id == 0));
172 if (prefer_datagram) {
174 asciichat_error_t result = acip_transport_get_capabilities(transport, &caps);
175 if (result != ASCIICHAT_OK)
176 return result;
177 if (caps.datagrams) {
179 options.deadline_ns = time_get_ns() + 100 * NS_PER_MS_INT;
180 }
181 }
182 result = acip_transport_sendv_ex(transport, slices, count + 1, &options);
183 stats_runtime_packet(transport->stats_peer, type, len, true, result == ASCIICHAT_OK);
184#ifndef __EMSCRIPTEN__
185 ASCII_OTEL_PACKET(type, len, "send", acip_transport_get_type(transport));
186#endif
187 return result;
188}
#define NS_PER_MS_INT
Definition time.h:168
asciichat_error_t packet_header_build(packet_header_t *header, packet_type_t type, const socket_buffer_t *payload, size_t count, uint32_t client_id)
Definition packet.c:47
@ PACKET_TYPE_IMAGE_FRAME_H265
H.265-encoded frame from client (HEVC/H.265 codec).
Definition packet.h:387
@ PACKET_TYPE_IMAGE_FRAME_H264
Definition packet.h:389
asciichat_error_t acip_transport_sendv_ex(acip_transport_t *transport, const socket_buffer_t *buffers, size_t count, const acip_send_options_t *options)
Definition send.c:113
asciichat_error_t acip_transport_get_capabilities(acip_transport_t *transport, acip_transport_capabilities_t *out)
Definition send.c:104
uint64_t deadline_ns
Definition traffic.h:19
acip_delivery_t delivery
Definition traffic.h:18
acip_traffic_class_t traffic_class
Definition traffic.h:17
The useful part of acip transport capabilities is in independent_streams, datagrams,...
bool prefer_visual_datagrams
Select negotiated visual datagrams when supported.
bool prefer_audio_datagrams
Select negotiated datagrams for audio, otherwise reliable delivery.
acip_traffic_class_t acip_packet_traffic_class(uint16_t type)
Definition traffic.c:4
@ ACIP_TRAFFIC_VISUAL
Definition traffic.h:8
@ ACIP_TRAFFIC_AUDIO
Definition traffic.h:7
@ ACIP_DELIVERY_RELIABLE
Definition traffic.h:12
@ ACIP_DELIVERY_DATAGRAM
Definition traffic.h:13

References ACIP_DELIVERY_DATAGRAM, ACIP_DELIVERY_RELIABLE, acip_packet_traffic_class(), ACIP_TRAFFIC_AUDIO, ACIP_TRAFFIC_VISUAL, acip_transport_get_capabilities(), acip_transport_sendv_ex(), ASCII_OTEL_PACKET, ASCII_OTEL_SCOPE, ASCIICHAT_OK, acip_transport_capabilities_t::datagrams, acip_send_options_t::deadline_ns, acip_send_options_t::delivery, ERROR_INVALID_PARAM, NET_TO_HOST_U32, NS_PER_MS_INT, packet_header_build(), PACKET_TYPE_IMAGE_FRAME_H264, PACKET_TYPE_IMAGE_FRAME_H265, acip_transport::prefer_audio_datagrams, acip_transport::prefer_visual_datagrams, SET_ERRNO, acip_transport::stats_peer, stats_runtime_packet(), and acip_send_options_t::traffic_class.

Referenced by acip_send_ascii_frame_contract(), acip_send_audio_batch(), acip_send_audio_opus_batch(), acip_send_image_frame(), and packet_send_via_transport().