ascii-chat 0.6.0
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 "network/acip/send.h"
14#include "network/packet.h"
15#include "buffer_pool.h"
16#include "util/overflow.h"
17#include "util/endian.h"
18#include "asciichat_errno.h"
19#include "log/logging.h"
20#include "audio/audio.h"
21#include <string.h>
22#include "network/crc32.h"
23#include <time.h>
24// =============================================================================
25// Packet Helper (wraps payload with header and sends via transport)
26// =============================================================================
27
41 size_t payload_len) {
42 if (!transport) {
43 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
44 }
45
46 // Build packet header
47 packet_header_t header;
49 header.type = HOST_TO_NET_U16(type);
50 header.length = HOST_TO_NET_U32((uint32_t)payload_len);
51 header.client_id = 0; // Set by caller if needed
52
53 // Calculate CRC32 if we have payload
54 if (payload && payload_len > 0) {
55 header.crc32 = HOST_TO_NET_U32(asciichat_crc32((const uint8_t *)payload, payload_len));
56 } else {
57 header.crc32 = 0;
58 }
59
60 // Calculate total packet size
61 size_t total_size = sizeof(header) + payload_len;
62
63 // Allocate buffer for complete packet
64 uint8_t *packet = buffer_pool_alloc(NULL, total_size);
65 if (!packet) {
66 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate packet buffer");
67 }
68
69 // Build complete packet: header + payload
70 memcpy(packet, &header, sizeof(header));
71 if (payload && payload_len > 0) {
72 memcpy(packet + sizeof(header), payload, payload_len);
73 }
74
75 // Send via transport (transport handles encryption if crypto_ctx present)
76 asciichat_error_t result = acip_transport_send(transport, packet, total_size);
77
78 buffer_pool_free(NULL, packet, total_size);
79 return result;
80}
81
82// ASCII/Video frame functions moved to:
83// - acip_send_ascii_frame → lib/network/acip/server.c (server → client)
84// - acip_send_image_frame → lib/network/acip/client.c (client → server)
85
86// =============================================================================
87// Audio Sending
88// =============================================================================
89
90asciichat_error_t acip_send_audio_batch(acip_transport_t *transport, const float *samples, uint32_t num_samples,
91 uint32_t batch_count) {
92 if (!transport || !samples || num_samples == 0) {
93 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
94 }
95
96 // Build batch header
98 header.batch_count = batch_count;
99 header.total_samples = (uint32_t)num_samples;
101 header.channels = 1;
102
103 // Calculate total size (header + float samples)
104 size_t samples_size = num_samples * sizeof(float);
105 size_t total_size = sizeof(header) + samples_size;
106
107 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
108 if (!buffer) {
109 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
110 }
111
112 // Copy header and samples
113 memcpy(buffer, &header, sizeof(header));
114 memcpy(buffer + sizeof(header), samples, samples_size);
115
116 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_BATCH, buffer, total_size);
117
118 buffer_pool_free(NULL, buffer, total_size);
119 return result;
120}
121
122asciichat_error_t acip_send_audio_opus(acip_transport_t *transport, const void *opus_data, size_t opus_len) {
123 if (!transport || !opus_data || opus_len == 0) {
124 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
125 }
126
127 return packet_send_via_transport(transport, PACKET_TYPE_AUDIO_OPUS, opus_data, opus_len);
128}
129
130asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len,
131 const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate,
132 uint32_t frame_duration) {
133 if (!transport || !opus_data || !frame_sizes || frame_count == 0) {
134 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
135 }
136
137 // Build batch header (16 bytes)
138 uint8_t header[16];
139 *(uint32_t *)(header + 0) = HOST_TO_NET_U32(sample_rate);
140 *(uint32_t *)(header + 4) = HOST_TO_NET_U32(frame_duration);
141 *(uint32_t *)(header + 8) = HOST_TO_NET_U32(frame_count);
142 *(uint32_t *)(header + 12) = 0; // reserved
143
144 // Calculate sizes array size
145 size_t sizes_len = frame_count * sizeof(uint16_t);
146 size_t total_size = sizeof(header) + sizes_len + opus_len;
147
148 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
149 if (!buffer) {
150 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
151 }
152
153 // Build packet: header + sizes + opus_data
154 memcpy(buffer, header, sizeof(header));
155
156 // Convert frame sizes to network byte order before copying
157 // IMPORTANT: Server expects network byte order and will apply NET_TO_HOST_U16()
158 uint16_t *sizes_buf = (uint16_t *)(buffer + sizeof(header));
159 for (uint32_t i = 0; i < frame_count; i++) {
160 sizes_buf[i] = HOST_TO_NET_U16(frame_sizes[i]);
161 }
162
163 memcpy(buffer + sizeof(header) + sizes_len, opus_data, opus_len);
164
165 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_OPUS_BATCH, buffer, total_size);
166
167 buffer_pool_free(NULL, buffer, total_size);
168 return result;
169}
170
171// =============================================================================
172// Control/Signaling
173// =============================================================================
174
176 if (!transport) {
177 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
178 }
179
180 // Ping has no payload
181 return packet_send_via_transport(transport, PACKET_TYPE_PING, NULL, 0);
182}
183
185 if (!transport) {
186 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
187 }
188
189 // Pong has no payload
190 return packet_send_via_transport(transport, PACKET_TYPE_PONG, NULL, 0);
191}
192
193// Client control functions moved to lib/network/acip/client.c:
194// - acip_send_client_join, acip_send_client_leave
195// - acip_send_stream_start, acip_send_stream_stop
196// - acip_send_capabilities, acip_send_protocol_version
197//
198// Server control functions moved to lib/network/acip/server.c:
199// - acip_send_clear_console, acip_send_server_state
200
201// =============================================================================
202// Messages/Errors
203// =============================================================================
204
206 if (!transport) {
207 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
208 }
209
210 // Calculate message length (max MAX_ERROR_MESSAGE_LENGTH)
211 size_t msg_len = 0;
212 if (message) {
213 msg_len = strlen(message);
214 if (msg_len > MAX_ERROR_MESSAGE_LENGTH) {
215 msg_len = MAX_ERROR_MESSAGE_LENGTH;
216 }
217 }
218
219 error_packet_t header;
221 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
222
223 // Allocate buffer for header + message
224 size_t total_size = sizeof(header) + msg_len;
225 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
226 if (!buffer) {
227 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
228 }
229
230 // Build packet
231 memcpy(buffer, &header, sizeof(header));
232 if (msg_len > 0) {
233 memcpy(buffer + sizeof(header), message, msg_len);
234 }
235
236 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_ERROR_MESSAGE, buffer, total_size);
237
238 buffer_pool_free(NULL, buffer, total_size);
239 return result;
240}
241
243 const char *message) {
244 if (!transport || !message) {
245 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or message");
246 }
247
248 size_t msg_len = strlen(message);
249
250 remote_log_packet_t header;
251 header.log_level = log_level;
252 header.direction = direction;
253 header.flags = 0;
254 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
255
256 size_t total_size = sizeof(header) + msg_len;
257
258 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
259 if (!buffer) {
260 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
261 }
262
263 // Build packet
264 memcpy(buffer, &header, sizeof(header));
265 memcpy(buffer + sizeof(header), message, msg_len);
266
267 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_REMOTE_LOG, buffer, total_size);
268
269 buffer_pool_free(NULL, buffer, total_size);
270 return result;
271}
272
273// =============================================================================
274// ACDS (Discovery Server) Response Sending
275// =============================================================================
276
278 if (!transport || !response) {
279 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
280 }
281
282 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_CREATED, response, sizeof(*response));
283}
284
286 if (!transport || !info) {
287 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or info");
288 }
289
290 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_INFO, info, sizeof(*info));
291}
292
294 if (!transport || !response) {
295 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
296 }
297
298 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_JOINED, response, sizeof(*response));
299}
asciichat_error_t error_code
⚠️‼️ Error and/or exit() when things go bad.
🗃️ 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:101
#define HOST_TO_NET_U32(val)
Definition endian.h:71
acip_session_joined_t
acip_session_created_t
acip_session_info_t
#define AUDIO_SAMPLE_RATE
Audio sample rate (48kHz professional quality, Opus-compatible)
void buffer_pool_free(buffer_pool_t *pool, 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
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_MEMORY
Definition error_codes.h:53
@ ERROR_INVALID_PARAM
uint32_t magic
Magic number (PACKET_MAGIC) for packet validation.
Definition packet.h:492
uint16_t flags
Additional flags (REMOTE_LOG_FLAG_*)
Definition packet.h:639
uint32_t message_length
Message payload length in bytes (0-512)
Definition packet.h:641
uint8_t log_level
Log level associated with the message (log_level_t cast to uint8_t)
Definition packet.h:635
uint32_t client_id
Client ID (0 = server, >0 = client identifier)
Definition packet.h:500
uint32_t message_length
Length of message payload in bytes (0-512)
Definition packet.h:623
uint32_t length
Payload data length in bytes (0 for header-only packets)
Definition packet.h:496
uint32_t error_code
Error code from asciichat_error_t enumeration.
Definition packet.h:621
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:494
uint32_t channels
Number of audio channels (1=mono, 2=stereo)
Definition packet.h:804
uint8_t direction
Direction hint so receivers can annotate origin.
Definition packet.h:637
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32)
Definition packet.h:798
uint32_t sample_rate
Sample rate in Hz (e.g., 44100, 48000)
Definition packet.h:802
uint32_t total_samples
Total audio samples across all chunks (typically 8192)
Definition packet.h:800
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:498
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:281
#define PACKET_MAGIC
Packet magic number (0xDEADBEEF)
Definition packet.h:251
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
Definition packet.h:122
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:359
@ PACKET_TYPE_AUDIO_OPUS
Opus-encoded single audio frame.
Definition packet.h:357
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client)
Definition packet.h:375
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:297
@ 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:371
@ 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:295
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:343
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client)
Definition packet.h:379
#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
Packet protocol implementation with encryption and compression support.
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:122
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:40
asciichat_error_t acip_send_ping(acip_transport_t *transport)
Send ping packet.
Definition send.c:175
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:293
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:242
asciichat_error_t acip_send_error(acip_transport_t *transport, uint32_t error_code, const char *message)
Send error message packet.
Definition send.c:205
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:277
asciichat_error_t acip_send_pong(acip_transport_t *transport)
Send pong packet.
Definition send.c:184
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:130
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:90
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:285
ACIP shared/bidirectional packet sending functions.
Transport instance structure.
Definition transport.h:169
Audio batch packet structure (Packet Type 28)
Definition packet.h:796
Error packet structure carrying error code and textual description.
Definition packet.h:619
Network packet header structure.
Definition packet.h:490
Remote log packet structure carrying log level and message text.
Definition packet.h:633
⏱️ High-precision timing utilities using sokol_time.h and uthash
Transport abstraction layer for ACIP protocol.