ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
send.c File Reference

ACIP protocol packet sending functions (transport-agnostic) More...

Go to the source code of this file.

Macros

#define MAX_REASONABLE_PACKET_SIZE   (25 * 1024 * 1024)
 

Functions

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)
 Send packet via transport with proper header (exported for generic wrappers)
 
asciichat_error_t acip_send_audio_batch (acip_transport_t *transport, const float *samples, uint32_t num_samples, uint32_t batch_count)
 
asciichat_error_t acip_send_audio_opus (acip_transport_t *transport, const void *opus_data, size_t opus_len)
 
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)
 
asciichat_error_t acip_send_ping (acip_transport_t *transport)
 
asciichat_error_t acip_send_pong (acip_transport_t *transport)
 
asciichat_error_t acip_send_error (acip_transport_t *transport, uint32_t error_code, const char *message)
 
asciichat_error_t acip_send_remote_log (acip_transport_t *transport, uint8_t log_level, uint8_t direction, const char *message)
 
asciichat_error_t acip_send_session_created (acip_transport_t *transport, const acip_session_created_t *response)
 
asciichat_error_t acip_send_session_info (acip_transport_t *transport, const acip_session_info_t *info)
 
asciichat_error_t acip_send_session_joined (acip_transport_t *transport, const acip_session_joined_t *response)
 

Detailed Description

ACIP protocol packet sending functions (transport-agnostic)

Implementation of send functions that work with any transport. Refactored from lib/network/av.c to use transport abstraction.

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

Definition in file send.c.

Macro Definition Documentation

◆ MAX_REASONABLE_PACKET_SIZE

#define MAX_REASONABLE_PACKET_SIZE   (25 * 1024 * 1024)

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 
)

Definition at line 115 of file send.c.

116 {
117 if (!transport || !samples || num_samples == 0) {
118 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
119 }
120
121 // Build batch header
122 audio_batch_packet_t header;
123 header.batch_count = batch_count;
124 header.total_samples = (uint32_t)num_samples;
125 header.sample_rate = (uint32_t)AUDIO_SAMPLE_RATE;
126 header.channels = 1;
127
128 // Calculate total size (header + float samples)
129 size_t samples_size = num_samples * sizeof(float);
130 size_t total_size = sizeof(header) + samples_size;
131
132 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
133 if (!buffer) {
134 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
135 }
136
137 // Copy header and samples
138 memcpy(buffer, &header, sizeof(header));
139 memcpy(buffer + sizeof(header), samples, samples_size);
140
141 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_BATCH, buffer, total_size, 0);
142
143 buffer_pool_free(NULL, buffer, total_size);
144 return result;
145}
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Definition buffer_pool.c:99
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)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41

References buffer_pool_alloc(), buffer_pool_free(), and packet_send_via_transport().

Referenced by client_send_thread_func(), and 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 
)

Definition at line 147 of file send.c.

147 {
148 if (!transport || !opus_data || opus_len == 0) {
149 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
150 }
151
152 // Convert single opus frame to batch format (frame_count=1) with standard parameters
153 // Standard: 48kHz sample rate, 20ms frame duration
154 uint16_t frame_sizes[1] = {(uint16_t)opus_len};
155 return acip_send_audio_opus_batch(transport, opus_data, opus_len, frame_sizes, 1, 48000, 20);
156}
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)
Definition send.c:158

References acip_send_audio_opus_batch().

◆ 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 
)

Definition at line 158 of file send.c.

160 {
161 if (!transport || !opus_data || !frame_sizes || frame_count == 0) {
162 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
163 }
164
165 // Build batch header (16 bytes)
166 uint8_t header[16];
167 *(uint32_t *)(header + 0) = HOST_TO_NET_U32(sample_rate);
168 *(uint32_t *)(header + 4) = HOST_TO_NET_U32(frame_duration);
169 *(uint32_t *)(header + 8) = HOST_TO_NET_U32(frame_count);
170 *(uint32_t *)(header + 12) = 0; // reserved
171
172 // Calculate sizes array size
173 size_t sizes_len = frame_count * sizeof(uint16_t);
174 size_t total_size = sizeof(header) + sizes_len + opus_len;
175
176 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
177 if (!buffer) {
178 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
179 }
180
181 // Build packet: header + sizes + opus_data
182 memcpy(buffer, header, sizeof(header));
183
184 // Convert frame sizes to network byte order before copying
185 // IMPORTANT: Server expects network byte order and will apply NET_TO_HOST_U16()
186 uint16_t *sizes_buf = (uint16_t *)(buffer + sizeof(header));
187 for (uint32_t i = 0; i < frame_count; i++) {
188 sizes_buf[i] = HOST_TO_NET_U16(frame_sizes[i]);
189 }
190
191 memcpy(buffer + sizeof(header) + sizes_len, opus_data, opus_len);
192
193 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_OPUS_BATCH, buffer, total_size, 0);
194
195 buffer_pool_free(NULL, buffer, total_size);
196 return result;
197}

References buffer_pool_alloc(), buffer_pool_free(), and packet_send_via_transport().

Referenced by acip_send_audio_opus(), client_send_thread_func(), 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 
)

Definition at line 233 of file send.c.

233 {
234 if (!transport) {
235 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
236 }
237
238 // Calculate message length (max MAX_ERROR_MESSAGE_LENGTH)
239 size_t msg_len = 0;
240 if (message) {
241 msg_len = strlen(message);
242 if (msg_len > MAX_ERROR_MESSAGE_LENGTH) {
243 msg_len = MAX_ERROR_MESSAGE_LENGTH;
244 }
245 }
246
247 error_packet_t header;
248 header.error_code = HOST_TO_NET_U32(error_code);
249 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
250
251 // Allocate buffer for header + message
252 size_t total_size = sizeof(header) + msg_len;
253 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
254 if (!buffer) {
255 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
256 }
257
258 // Build packet
259 memcpy(buffer, &header, sizeof(header));
260 if (msg_len > 0) {
261 memcpy(buffer + sizeof(header), message, msg_len);
262 }
263
264 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_ERROR_MESSAGE, buffer, total_size, 0);
265
266 buffer_pool_free(NULL, buffer, total_size);
267 return result;
268}
asciichat_error_t error_code

References buffer_pool_alloc(), buffer_pool_free(), error_code, and packet_send_via_transport().

Referenced by acds_client_handler(), and disconnect_client_for_bad_data().

◆ acip_send_ping()

asciichat_error_t acip_send_ping ( acip_transport_t *  transport)

Definition at line 203 of file send.c.

203 {
204 if (!transport) {
205 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
206 }
207
208 // Ping has no payload
209 return packet_send_via_transport(transport, PACKET_TYPE_PING, NULL, 0, 0);
210}

References packet_send_via_transport().

◆ acip_send_pong()

asciichat_error_t acip_send_pong ( acip_transport_t *  transport)

Definition at line 212 of file send.c.

212 {
213 if (!transport) {
214 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
215 }
216
217 // Pong has no payload
218 return packet_send_via_transport(transport, PACKET_TYPE_PONG, NULL, 0, 0);
219}

References packet_send_via_transport().

Referenced by handle_ping_packet().

◆ 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 
)

Definition at line 270 of file send.c.

271 {
272 if (!transport || !message) {
273 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or message");
274 }
275
276 size_t msg_len = strlen(message);
277
278 remote_log_packet_t header;
279 header.log_level = log_level;
280 header.direction = direction;
281 header.flags = 0;
282 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
283
284 size_t total_size = sizeof(header) + msg_len;
285
286 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
287 if (!buffer) {
288 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
289 }
290
291 // Build packet
292 memcpy(buffer, &header, sizeof(header));
293 memcpy(buffer + sizeof(header), message, msg_len);
294
295 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_REMOTE_LOG, buffer, total_size, 0);
296
297 buffer_pool_free(NULL, buffer, total_size);
298 return result;
299}

References buffer_pool_alloc(), buffer_pool_free(), and packet_send_via_transport().

◆ acip_send_session_created()

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

Definition at line 305 of file send.c.

305 {
306 if (!transport || !response) {
307 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
308 }
309
310 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_CREATED, response, sizeof(*response), 0);
311}

References packet_send_via_transport().

◆ acip_send_session_info()

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

Definition at line 313 of file send.c.

313 {
314 if (!transport || !info) {
315 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or info");
316 }
317
318 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_INFO, info, sizeof(*info), 0);
319}

References packet_send_via_transport().

◆ acip_send_session_joined()

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

Definition at line 321 of file send.c.

321 {
322 if (!transport || !response) {
323 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
324 }
325
326 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_JOINED, response, sizeof(*response), 0);
327}

References packet_send_via_transport().

◆ packet_send_via_transport()

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 
)

Send packet via transport with proper header (exported for generic wrappers)

Wraps payload in ACIP packet header and sends via transport. Handles CRC32 calculation and network byte order conversion.

Parameters
transportTransport instance
typePacket type
payloadPayload data (may be NULL if payload_len is 0)
payload_lenPayload length
client_idClient ID to include in packet header
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 41 of file send.c.

42 {
43 if (!transport) {
44 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
45 }
46
47// Sanity check: catch suspiciously large packets (likely a bug)
48#define MAX_REASONABLE_PACKET_SIZE (25 * 1024 * 1024) // 25 MB
49 if (payload_len > MAX_REASONABLE_PACKET_SIZE) {
50 log_error("PACKET SIZE VALIDATION FAILURE: Attempting to send %zu bytes (%zu MB) with type=%d (0x%04x), exceeds "
51 "max %d bytes (25 MB) - likely a bug in caller's length calculation",
52 payload_len, payload_len / (1024 * 1024), type, type, MAX_REASONABLE_PACKET_SIZE);
53 return SET_ERRNO(ERROR_INVALID_PARAM, "Packet payload too large: %zu bytes (max 25MB)", payload_len);
54 }
55
56 log_dev_every(4500 * US_PER_MS_INT,
57 "★ PACKET_SEND_VIA_TRANSPORT: type=%d, payload_len=%zu, client_id=%u, transport=%p", type, payload_len,
58 client_id, (void *)transport);
59
60 // Build packet header
61 packet_header_t header;
62 header.magic = HOST_TO_NET_U64(PACKET_MAGIC);
63 header.type = HOST_TO_NET_U16(type);
64 header.length = HOST_TO_NET_U32((uint32_t)payload_len);
65 header.client_id = client_id;
66
67 // Calculate CRC32 if we have payload
68 if (payload && payload_len > 0) {
69 header.crc32 = HOST_TO_NET_U32(asciichat_crc32((const uint8_t *)payload, payload_len));
70 } else {
71 header.crc32 = 0;
72 }
73
74 log_dev_every(4500 * US_PER_MS_INT, "★ PKT_SEND: type=%d, magic=0x%016llx, length=%u, crc32=0x%08x", type,
75 header.magic, header.length, header.crc32);
76
77 // Calculate total packet size
78 size_t total_size = sizeof(header) + payload_len;
79
80 // Allocate buffer for complete packet
81 // Use SAFE_MALLOC (not buffer pool - payload may also be from pool and causes overlap)
82 uint8_t *packet = SAFE_MALLOC(total_size, uint8_t *);
83 if (!packet) {
84 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate packet buffer");
85 }
86
87 // Build complete packet: header + payload
88 memcpy(packet, &header, sizeof(header));
89 if (payload && payload_len > 0) {
90 memcpy(packet + sizeof(header), payload, payload_len);
91 }
92
93 log_dev_every(4500 * US_PER_MS_INT, "★ PACKET_SEND: total_size=%zu, calling acip_transport_send...", total_size);
94 // Send via transport (transport handles encryption if crypto_ctx present)
95 asciichat_error_t result = acip_transport_send(transport, packet, total_size);
96
97 if (result == ASCIICHAT_OK) {
98 log_dev_every(4500 * US_PER_MS_INT, "★ PACKET_SEND: SUCCESS - sent %zu bytes (type=%d)", total_size, type);
99 } else {
100 log_error("★ PACKET_SEND: FAILED - acip_transport_send returned %d (%s)", result, asciichat_error_string(result));
101 }
102
103 SAFE_FREE(packet);
104 return result;
105}
#define MAX_REASONABLE_PACKET_SIZE

References MAX_REASONABLE_PACKET_SIZE.

Referenced by acip_send_ascii_frame(), acip_send_audio_batch(), acip_send_audio_opus_batch(), acip_send_capabilities(), acip_send_clear_console(), acip_send_client_join(), acip_send_client_leave(), acip_send_error(), acip_send_image_frame(), acip_send_ping(), acip_send_pong(), acip_send_protocol_version(), acip_send_remote_log(), acip_send_server_state(), acip_send_session_created(), acip_send_session_info(), acip_send_session_joined(), acip_send_stream_start(), acip_send_stream_stop(), client_send_thread_func(), crypto_handshake_client_key_exchange(), crypto_handshake_server_auth_challenge(), crypto_handshake_server_complete(), crypto_handshake_server_start(), threaded_send_audio_opus(), and threaded_send_packet().