ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
send.c
Go to the documentation of this file.
1
19#include <ascii-chat/log/log.h>
21#include <string.h>
23#include <time.h>
24// =============================================================================
25// Packet Helper (wraps payload with header and sends via transport)
26// =============================================================================
27
42 size_t payload_len, uint32_t client_id) {
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("★ PACKET_SEND_VIA_TRANSPORT START: type=%d (0x%04x), payload_len=%zu bytes, client_id=%u, transport=%p",
57 type, type, payload_len, client_id, (void *)transport);
58
59 // Build packet header
60 packet_header_t header;
62 header.type = HOST_TO_NET_U16(type);
63 header.length = HOST_TO_NET_U32((uint32_t)payload_len);
64 header.client_id = client_id;
65
66 // Calculate CRC32 if we have payload
67 if (payload && payload_len > 0) {
68 header.crc32 = HOST_TO_NET_U32(asciichat_crc32((const uint8_t *)payload, payload_len));
69 log_dev("★ PACKET_SEND: CRC32=0x%08x calculated for %zu byte payload", header.crc32, payload_len);
70 } else {
71 header.crc32 = 0;
72 log_dev("★ PACKET_SEND: No payload, CRC32=0");
73 }
74
75 log_dev("★ PACKET_SEND: magic=0x%016llx, type=%d, length=%u bytes, client_id=%u", header.magic, type, header.length,
76 header.client_id);
77
78 // Calculate total packet size
79 size_t total_size = sizeof(header) + payload_len;
80 log_dev("★ PACKET_SEND: Header=%zu bytes + Payload=%zu bytes = Total=%zu bytes", sizeof(header), payload_len,
81 total_size);
82
83 // Allocate buffer for complete packet
84 uint8_t *packet = SAFE_MALLOC(total_size, uint8_t *);
85 if (!packet) {
86 log_error("★ PACKET_SEND: Memory allocation FAILED for %zu byte packet buffer", total_size);
87 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate packet buffer");
88 }
89
90 log_dev("★ PACKET_SEND: Packet buffer allocated at %p", (void *)packet);
91
92 // Build complete packet: header + payload
93 memcpy(packet, &header, sizeof(header));
94 if (payload && payload_len > 0) {
95 memcpy(packet + sizeof(header), payload, payload_len);
96 log_dev("★ PACKET_SEND: Payload copied to buffer offset %zu", sizeof(header));
97 }
98
99 // Send via transport (transport handles encryption if crypto_ctx present)
100 // Transport does NOT take ownership - it only copies data to its own buffers
101 log_dev("★ PACKET_SEND: Calling acip_transport_send with %zu total bytes", total_size);
102 asciichat_error_t result = acip_transport_send(transport, packet, total_size);
103
104 if (result == ASCIICHAT_OK) {
105 log_dev("★ PACKET_SEND_VIA_TRANSPORT COMPLETE: SUCCESS - sent %zu bytes (type=%d, client_id=%u)", total_size, type,
106 client_id);
107 } else {
108 log_error("★ PACKET_SEND_VIA_TRANSPORT FAILED: acip_transport_send returned error %d (%s) when sending %zu bytes "
109 "type=%d to client_id=%u",
110 result, asciichat_error_string(result), total_size, type, client_id);
111 }
112
113 // Always free the packet buffer
114 SAFE_FREE(packet);
115 return result;
116}
117
118// =============================================================================
119// Packet Receive Helper (receives via transport, parses header)
120// =============================================================================
121
123 size_t *payload_len, void **alloc_buffer) {
124 if (!transport || !type || !payload || !payload_len || !alloc_buffer) {
125 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters for packet_receive_via_transport");
126 }
127
128 *type = 0;
129 *payload = NULL;
130 *payload_len = 0;
131 *alloc_buffer = NULL;
132
133 // Receive raw bytes via transport
134 void *recv_buffer = NULL;
135 size_t recv_len = 0;
136 asciichat_error_t result = acip_transport_recv(transport, &recv_buffer, &recv_len, alloc_buffer);
137 if (result != ASCIICHAT_OK) {
138 return result;
139 }
140
141 // Parse packet header
142 if (recv_len < sizeof(packet_header_t)) {
143 log_warn("packet_receive_via_transport: packet too small (%zu bytes, need %zu)", recv_len, sizeof(packet_header_t));
144 if (*alloc_buffer) {
145 buffer_pool_free(NULL, *alloc_buffer, recv_len);
146 *alloc_buffer = NULL;
147 }
148 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Received packet smaller than header");
149 }
150
151 const packet_header_t *header = (const packet_header_t *)recv_buffer;
152 *type = (packet_type_t)NET_TO_HOST_U16(header->type);
153 size_t plen = NET_TO_HOST_U32(header->length);
154
155 if (recv_len < sizeof(packet_header_t) + plen) {
156 log_warn("packet_receive_via_transport: truncated packet (header says %zu payload, got %zu total)", plen, recv_len);
157 if (*alloc_buffer) {
158 buffer_pool_free(NULL, *alloc_buffer, recv_len);
159 *alloc_buffer = NULL;
160 }
161 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Truncated packet payload");
162 }
163
164 if (plen > 0) {
165 *payload = (uint8_t *)recv_buffer + sizeof(packet_header_t);
166 }
167 *payload_len = plen;
168
169 return ASCIICHAT_OK;
170}
171
172// ASCII/Video frame functions moved to:
173// - acip_send_ascii_frame → lib/network/acip/server.c (server → client)
174// - acip_send_image_frame → lib/network/acip/client.c (client → server)
175
176// =============================================================================
177// Audio Sending
178// =============================================================================
179
180asciichat_error_t acip_send_audio_batch(acip_transport_t *transport, const float *samples, uint32_t num_samples,
181 uint32_t batch_count) {
182 if (!transport || !samples || num_samples == 0) {
183 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
184 }
185
186 // Build batch header
188 header.batch_count = batch_count;
189 header.total_samples = (uint32_t)num_samples;
191 header.channels = 1;
192
193 // Calculate total size (header + float samples)
194 size_t samples_size = num_samples * sizeof(float);
195 size_t total_size = sizeof(header) + samples_size;
196
197 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
198 if (!buffer) {
199 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
200 }
201
202 // Copy header and samples
203 memcpy(buffer, &header, sizeof(header));
204 memcpy(buffer + sizeof(header), samples, samples_size);
205
206 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_BATCH, buffer, total_size, 0);
207
208 buffer_pool_free(NULL, buffer, total_size);
209 return result;
210}
211
212asciichat_error_t acip_send_audio_opus(acip_transport_t *transport, const void *opus_data, size_t opus_len) {
213 if (!transport || !opus_data || opus_len == 0) {
214 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
215 }
216
217 // Convert single opus frame to batch format (frame_count=1) with standard parameters
218 // Standard: 48kHz sample rate, 20ms frame duration
219 uint16_t frame_sizes[1] = {(uint16_t)opus_len};
220 return acip_send_audio_opus_batch(transport, opus_data, opus_len, frame_sizes, 1, 48000, 20);
221}
222
223asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len,
224 const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate,
225 uint32_t frame_duration) {
226 if (!transport || !opus_data || !frame_sizes || frame_count == 0) {
227 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
228 }
229
230 // Build batch header (16 bytes)
231 uint8_t header[16];
232 *(uint32_t *)(header + 0) = HOST_TO_NET_U32(sample_rate);
233 *(uint32_t *)(header + 4) = HOST_TO_NET_U32(frame_duration);
234 *(uint32_t *)(header + 8) = HOST_TO_NET_U32(frame_count);
235 *(uint32_t *)(header + 12) = 0; // reserved
236
237 // Calculate sizes array size
238 size_t sizes_len = frame_count * sizeof(uint16_t);
239 size_t total_size = sizeof(header) + sizes_len + opus_len;
240
241 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
242 if (!buffer) {
243 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
244 }
245
246 // Build packet: header + sizes + opus_data
247 memcpy(buffer, header, sizeof(header));
248
249 // Convert frame sizes to network byte order before copying
250 // IMPORTANT: Server expects network byte order and will apply NET_TO_HOST_U16()
251 uint16_t *sizes_buf = (uint16_t *)(buffer + sizeof(header));
252 for (uint32_t i = 0; i < frame_count; i++) {
253 sizes_buf[i] = HOST_TO_NET_U16(frame_sizes[i]);
254 }
255
256 memcpy(buffer + sizeof(header) + sizes_len, opus_data, opus_len);
257
258 log_dev("★ OPUS_BATCH_SEND_DEBUG: header=16, frame_count=%u, sizes_len=%zu, opus_len=%zu, TOTAL=%zu", frame_count,
259 sizes_len, opus_len, total_size);
260
261 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_OPUS_BATCH, buffer, total_size, 0);
262
263 buffer_pool_free(NULL, buffer, total_size);
264 return result;
265}
266
267// =============================================================================
268// Control/Signaling
269// =============================================================================
270
272 if (!transport) {
273 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
274 }
275
276 // Ping has no payload
277 return packet_send_via_transport(transport, PACKET_TYPE_PING, NULL, 0, 0);
278}
279
281 if (!transport) {
282 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
283 }
284
285 // Pong has no payload
286 return packet_send_via_transport(transport, PACKET_TYPE_PONG, NULL, 0, 0);
287}
288
289// Client control functions moved to lib/network/acip/client.c:
290// - acip_send_client_join, acip_send_client_leave
291// - acip_send_stream_start, acip_send_stream_stop
292// - acip_send_capabilities, acip_send_protocol_version
293//
294// Server control functions moved to lib/network/acip/server.c:
295// - acip_send_clear_console, acip_send_server_state
296
297// =============================================================================
298// Messages/Errors
299// =============================================================================
300
302 if (!transport) {
303 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
304 }
305
306 // Calculate message length (max MAX_ERROR_MESSAGE_LENGTH)
307 size_t msg_len = 0;
308 if (message) {
309 msg_len = strlen(message);
310 if (msg_len > MAX_ERROR_MESSAGE_LENGTH) {
311 msg_len = MAX_ERROR_MESSAGE_LENGTH;
312 }
313 }
314
315 error_packet_t header;
317 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
318
319 // Allocate buffer for header + message
320 size_t total_size = sizeof(header) + msg_len;
321 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
322 if (!buffer) {
323 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
324 }
325
326 // Build packet
327 memcpy(buffer, &header, sizeof(header));
328 if (msg_len > 0) {
329 memcpy(buffer + sizeof(header), message, msg_len);
330 }
331
332 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_ERROR_MESSAGE, buffer, total_size, 0);
333
334 buffer_pool_free(NULL, buffer, total_size);
335 return result;
336}
337
339 const char *message) {
340 if (!transport || !message) {
341 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or message");
342 }
343
344 size_t msg_len = strlen(message);
345
346 remote_log_packet_t header;
347 header.log_level = log_level;
348 header.direction = direction;
349 header.flags = 0;
350 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
351
352 size_t total_size = sizeof(header) + msg_len;
353
354 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
355 if (!buffer) {
356 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
357 }
358
359 // Build packet
360 memcpy(buffer, &header, sizeof(header));
361 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary packet data
362 memcpy(buffer + sizeof(header), message, msg_len);
363
364 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_REMOTE_LOG, buffer, total_size, 0);
365
366 buffer_pool_free(NULL, buffer, total_size);
367 return result;
368}
369
370// =============================================================================
371// ACDS (Discovery Server) Response Sending
372// =============================================================================
373
375 if (!transport || !response) {
376 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
377 }
378
379 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_CREATED, response, sizeof(*response), 0);
380}
381
383 if (!transport || !info) {
384 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or info");
385 }
386
387 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_INFO, info, sizeof(*info), 0);
388}
389
391 if (!transport || !response) {
392 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
393 }
394
395 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_JOINED, response, sizeof(*response), 0);
396}
asciichat_error_t error_code
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
Hardware-Accelerated CRC32 Checksum Computation.
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define HOST_TO_NET_U32(val)
Definition endian.h:66
#define HOST_TO_NET_U64(val)
Definition endian.h:213
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
acip_session_joined_t
Definition acds.h:474
acip_session_created_t
Definition acds.h:269
acip_session_info_t
Definition acds.h:355
#define AUDIO_SAMPLE_RATE
Audio sample rate (48kHz professional quality, Opus-compatible)
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID)
Definition packet.h:255
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
Definition packet.h:124
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:374
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client)
Definition packet.h:413
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:354
@ PACKET_TYPE_ACIP_SESSION_CREATED
Session created response (Discovery Server -> Client)
Definition packet.h:409
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:352
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:372
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client)
Definition packet.h:417
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:144
🔊 Audio Capture and Playback Interface for ascii-chat
📝 Logging API with multiple log levels and terminal output control
✅ Safe Integer Arithmetic and Overflow Detection
#define MAX_REASONABLE_PACKET_SIZE
asciichat_error_t acip_send_audio_opus(acip_transport_t *transport, const void *opus_data, size_t opus_len)
Send Opus-encoded audio packet.
Definition send.c:212
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.
Definition send.c:122
asciichat_error_t acip_send_ping(acip_transport_t *transport)
Send ping packet.
Definition send.c:271
asciichat_error_t acip_send_session_joined(acip_transport_t *transport, const acip_session_joined_t *response)
Send SESSION_JOINED response packet.
Definition send.c:390
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.
Definition send.c:338
asciichat_error_t acip_send_error(acip_transport_t *transport, uint32_t error_code, const char *message)
Send error message packet.
Definition send.c:301
asciichat_error_t acip_send_session_created(acip_transport_t *transport, const acip_session_created_t *response)
Send SESSION_CREATED response packet.
Definition send.c:374
asciichat_error_t acip_send_pong(acip_transport_t *transport)
Send pong packet.
Definition send.c:280
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:223
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.
Definition send.c:180
asciichat_error_t acip_send_session_info(acip_transport_t *transport, const acip_session_info_t *info)
Send SESSION_INFO response packet.
Definition send.c:382
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
ACIP shared/bidirectional packet sending functions.
Transport instance structure.
Definition transport.h:214
Audio batch packet structure (Packet Type 28)
Definition packet.h:904
uint32_t channels
Number of audio channels (1=mono, 2=stereo)
Definition packet.h:912
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32)
Definition packet.h:906
uint32_t sample_rate
Sample rate in Hz (e.g., 44100, 48000)
Definition packet.h:910
uint32_t total_samples
Total audio samples across all chunks (typically 8192)
Definition packet.h:908
Error packet structure carrying error code and textual description.
Definition packet.h:727
uint32_t message_length
Length of message payload in bytes (0-512)
Definition packet.h:731
uint32_t error_code
Error code from asciichat_error_t enumeration.
Definition packet.h:729
Network packet header structure.
Definition packet.h:598
uint32_t client_id
Client ID (0 = server, >0 = client identifier)
Definition packet.h:608
uint32_t length
Payload data length in bytes (0 for header-only packets)
Definition packet.h:604
uint64_t magic
Magic number (PACKET_MAGIC = 0xA5C11C4A1 "ASCIICHAT" in hex) for packet validation.
Definition packet.h:600
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:602
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:606
Remote log packet structure carrying log level and message text.
Definition packet.h:741
uint16_t flags
Additional flags (REMOTE_LOG_FLAG_*)
Definition packet.h:747
uint32_t message_length
Message payload length in bytes (0-512)
Definition packet.h:749
uint8_t log_level
Log level associated with the message (log_level_t cast to uint8_t)
Definition packet.h:743
uint8_t direction
Direction hint so receivers can annotate origin.
Definition packet.h:745
⏱️ High-precision timing utilities using sokol_time.h and uthash
Transport abstraction layer for ACIP protocol.