ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
send.c
Go to the documentation of this file.
1
12#include <ascii-chat/network/acip/send.h>
13#include <ascii-chat/network/acip/transport.h>
14#include <ascii-chat/network/packet.h>
15#include <ascii-chat/buffer_pool.h>
16#include <ascii-chat/util/overflow.h>
17#include <ascii-chat/util/endian.h>
18#include <ascii-chat/asciichat_errno.h>
19#include <ascii-chat/log/logging.h>
20#include <ascii-chat/audio/audio.h>
21#include <string.h>
22#include <ascii-chat/network/crc32.h>
23#include <time.h>
24// =============================================================================
25// Packet Helper (wraps payload with header and sends via transport)
26// =============================================================================
27
41asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload,
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_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}
106
107// ASCII/Video frame functions moved to:
108// - acip_send_ascii_frame → lib/network/acip/server.c (server → client)
109// - acip_send_image_frame → lib/network/acip/client.c (client → server)
110
111// =============================================================================
112// Audio Sending
113// =============================================================================
114
115asciichat_error_t acip_send_audio_batch(acip_transport_t *transport, const float *samples, uint32_t num_samples,
116 uint32_t batch_count) {
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}
146
147asciichat_error_t acip_send_audio_opus(acip_transport_t *transport, const void *opus_data, size_t opus_len) {
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}
157
158asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len,
159 const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate,
160 uint32_t frame_duration) {
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}
198
199// =============================================================================
200// Control/Signaling
201// =============================================================================
202
203asciichat_error_t acip_send_ping(acip_transport_t *transport) {
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}
211
212asciichat_error_t acip_send_pong(acip_transport_t *transport) {
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}
220
221// Client control functions moved to lib/network/acip/client.c:
222// - acip_send_client_join, acip_send_client_leave
223// - acip_send_stream_start, acip_send_stream_stop
224// - acip_send_capabilities, acip_send_protocol_version
225//
226// Server control functions moved to lib/network/acip/server.c:
227// - acip_send_clear_console, acip_send_server_state
228
229// =============================================================================
230// Messages/Errors
231// =============================================================================
232
233asciichat_error_t acip_send_error(acip_transport_t *transport, uint32_t error_code, const char *message) {
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}
269
270asciichat_error_t acip_send_remote_log(acip_transport_t *transport, uint8_t log_level, uint8_t direction,
271 const char *message) {
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}
300
301// =============================================================================
302// ACDS (Discovery Server) Response Sending
303// =============================================================================
304
305asciichat_error_t acip_send_session_created(acip_transport_t *transport, const acip_session_created_t *response) {
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}
312
313asciichat_error_t acip_send_session_info(acip_transport_t *transport, const acip_session_info_t *info) {
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}
320
321asciichat_error_t acip_send_session_joined(acip_transport_t *transport, const acip_session_joined_t *response) {
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}
asciichat_error_t error_code
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
#define MAX_REASONABLE_PACKET_SIZE
asciichat_error_t acip_send_audio_opus(acip_transport_t *transport, const void *opus_data, size_t opus_len)
Definition send.c:147
asciichat_error_t acip_send_ping(acip_transport_t *transport)
Definition send.c:203
asciichat_error_t acip_send_session_joined(acip_transport_t *transport, const acip_session_joined_t *response)
Definition send.c:321
asciichat_error_t acip_send_remote_log(acip_transport_t *transport, uint8_t log_level, uint8_t direction, const char *message)
Definition send.c:270
asciichat_error_t acip_send_error(acip_transport_t *transport, uint32_t error_code, const char *message)
Definition send.c:233
asciichat_error_t acip_send_session_created(acip_transport_t *transport, const acip_session_created_t *response)
Definition send.c:305
asciichat_error_t acip_send_pong(acip_transport_t *transport)
Definition send.c:212
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
asciichat_error_t acip_send_audio_batch(acip_transport_t *transport, const float *samples, uint32_t num_samples, uint32_t batch_count)
Definition send.c:115
asciichat_error_t acip_send_session_info(acip_transport_t *transport, const acip_session_info_t *info)
Definition send.c:313
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