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

Go to the source code of this file.

Data Structures

struct  http_renderer

Functions

asciichat_error_t http_renderer_job_start (http_renderer_job_t *job, const char *name, http_renderer_job_fn execute, void *context)
void http_renderer_job_cancel (http_renderer_job_t *job)
bool http_renderer_job_is_done (const http_renderer_job_t *job)
void http_renderer_job_join (http_renderer_job_t *job)
void http_renderer_job_reset (http_renderer_job_t *job)
asciichat_error_t http_renderer_init (http_renderer_t **out, const http_renderer_config_t *config)
asciichat_error_t http_renderer_submit (http_renderer_t *renderer, const http_render_request_t *request, void *user_data, http_renderer_job_t **out_job)
asciichat_error_t http_renderer_cancel (http_renderer_job_t *job)
void http_renderer_job_wait (http_renderer_job_t *job)
void http_renderer_reap (http_renderer_t *renderer, bool all)
void http_renderer_stop (http_renderer_t *renderer)
void http_renderer_destroy (http_renderer_t **renderer)
void http_render_request_init (http_render_request_t *request)
void http_render_result_destroy (http_render_result_t *result)
asciichat_error_t http_render_result_add_text_frame (http_render_result_t *result, const char *text, size_t length, uint64_t timestamp_ms)
asciichat_error_t http_render_result_add_file_frame (http_render_result_t *result, const char *path, uint64_t timestamp_ms)
asciichat_error_t http_renderer_render_file (const char *path, const http_render_request_t *request, http_render_result_t *result, atomic_t *cancelled)

Function Documentation

◆ http_render_request_init()

void http_render_request_init ( http_render_request_t * request)

Definition at line 216 of file http_renderer.c.

216 {
217 if (!request)
218 return;
219 memset(request, 0, sizeof(*request));
222}
@ HTTP_RENDER_SOURCE_UPLOAD
@ HTTP_RENDER_OUTPUT_TEXT
http_render_source_t source
http_render_output_type_t output_type
http_render_source_type_t type

References HTTP_RENDER_OUTPUT_TEXT, HTTP_RENDER_SOURCE_UPLOAD, http_render_request_t::output_type, http_render_request_t::source, and http_render_source_t::type.

◆ http_render_result_add_file_frame()

asciichat_error_t http_render_result_add_file_frame ( http_render_result_t * result,
const char * path,
uint64_t timestamp_ms )

Definition at line 270 of file http_renderer.c.

271 {
272 if (!result || !path || !*path)
273 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid file frame");
274 asciichat_error_t err = http_render_result_reserve_frame(result);
275 if (err != ASCIICHAT_OK)
276 return err;
277 http_render_frame_t *frame = &result->frames[result->frame_count++];
278 SAFE_STRNCPY(frame->path, path, sizeof(frame->path));
279 frame->timestamp_ms = timestamp_ms;
280 return ASCIICHAT_OK;
281}
#define SET_ERRNO(code,...)
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
char path[PLATFORM_MAX_PATH_LENGTH]
http_render_frame_t * frames

References ASCIICHAT_OK, ERROR_INVALID_PARAM, http_render_result_t::frame_count, http_render_result_t::frames, http_render_frame_t::path, SAFE_STRNCPY, SET_ERRNO, and http_render_frame_t::timestamp_ms.

Referenced by http_renderer_process().

◆ http_render_result_add_text_frame()

asciichat_error_t http_render_result_add_text_frame ( http_render_result_t * result,
const char * text,
size_t length,
uint64_t timestamp_ms )

Definition at line 254 of file http_renderer.c.

255 {
256 if (!result || (!text && length))
257 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid text frame");
258 asciichat_error_t err = http_render_result_reserve_frame(result);
259 if (err != ASCIICHAT_OK)
260 return err;
261 http_render_frame_t *frame = &result->frames[result->frame_count++];
262 frame->text = SAFE_MALLOC(length + 1, char *);
263 SAFE_MEMCPY(frame->text, length + 1, text, length);
264 frame->text[length] = '\0';
265 frame->text_length = length;
266 frame->timestamp_ms = timestamp_ms;
267 return ASCIICHAT_OK;
268}
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_MEMCPY(dest, dest_size, src, count)
Definition common.h:483

References ASCIICHAT_OK, ERROR_INVALID_PARAM, http_render_result_t::frame_count, http_render_result_t::frames, SAFE_MALLOC, SAFE_MEMCPY, SET_ERRNO, http_render_frame_t::text, http_render_frame_t::text_length, and http_render_frame_t::timestamp_ms.

Referenced by http_renderer_process(), and http_renderer_render_file().

◆ http_render_result_destroy()

void http_render_result_destroy ( http_render_result_t * result)

Definition at line 224 of file http_renderer.c.

224 {
225 if (!result)
226 return;
227 SAFE_FREE(result->ascii);
228 for (size_t i = 0; i < result->frame_count; ++i)
229 SAFE_FREE(result->frames[i].text);
230 SAFE_FREE(result->frames);
231 result->frame_count = 0;
232 result->frame_capacity = 0;
233 result->ascii_length = 0;
234 result->output_path[0] = '\0';
235 result->mime_type[0] = '\0';
236}
#define SAFE_FREE(ptr)
Definition common.h:377
char output_path[PLATFORM_MAX_PATH_LENGTH]

References http_render_result_t::ascii, http_render_result_t::ascii_length, http_render_result_t::frame_capacity, http_render_result_t::frame_count, http_render_result_t::frames, http_render_result_t::mime_type, http_render_result_t::output_path, SAFE_FREE, and http_render_frame_t::text.

Referenced by http_renderer_reap().

◆ http_renderer_cancel()

asciichat_error_t http_renderer_cancel ( http_renderer_job_t * job)

Definition at line 160 of file http_renderer.c.

160 {
161 if (!job || !job->renderer)
162 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid HTTP renderer job");
164 return ASCIICHAT_OK;
165}
void http_renderer_job_cancel(http_renderer_job_t *job)
http_renderer_t * renderer

References ASCIICHAT_OK, ERROR_INVALID_PARAM, http_renderer_job_cancel(), http_renderer_job::renderer, and SET_ERRNO.

◆ http_renderer_destroy()

void http_renderer_destroy ( http_renderer_t ** renderer)

Definition at line 206 of file http_renderer.c.

206 {
207 if (!renderer || !*renderer)
208 return;
209 http_renderer_stop(*renderer);
210 http_renderer_reap(*renderer, true);
211 mutex_destroy(&(*renderer)->lock);
212 SAFE_FREE(*renderer);
213}
void http_renderer_stop(http_renderer_t *renderer)
void http_renderer_reap(http_renderer_t *renderer, bool all)

References http_renderer_reap(), http_renderer_stop(), and SAFE_FREE.

Referenced by api_server_main().

◆ http_renderer_init()

asciichat_error_t http_renderer_init ( http_renderer_t ** out,
const http_renderer_config_t * config )

Definition at line 108 of file http_renderer.c.

108 {
109 if (!out || !config)
110 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing HTTP renderer configuration");
111 *out = NULL;
112 http_renderer_t *renderer = SAFE_CALLOC(1, sizeof(*renderer), http_renderer_t *);
113 renderer->config = *config;
114 if (!renderer->config.max_jobs)
115 renderer->config.max_jobs = 1;
116 if (!renderer->config.max_output_bytes)
118 if (mutex_init(&renderer->lock, "http_renderer") != 0) {
119 SAFE_FREE(renderer);
120 return SET_ERRNO(ERROR_THREAD, "Cannot initialize HTTP renderer lock");
121 }
122 *out = renderer;
123 return ASCIICHAT_OK;
124}
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
@ ERROR_THREAD
#define HTTP_RENDERER_MAX_OUTPUT
struct http_renderer http_renderer_t
static bool size_t gotel_query_t * out
Definition queries.h:8
http_renderer_config_t config

References ASCIICHAT_OK, http_renderer::config, ERROR_INVALID_PARAM, ERROR_THREAD, HTTP_RENDERER_MAX_OUTPUT, http_renderer::lock, http_renderer_config_t::max_jobs, http_renderer_config_t::max_output_bytes, out, SAFE_CALLOC, SAFE_FREE, and SET_ERRNO.

Referenced by api_server_main().

◆ http_renderer_job_cancel()

void http_renderer_job_cancel ( http_renderer_job_t * job)

Definition at line 61 of file http_renderer.c.

61 {
62 if (job && job->started)
63 atomic_store_bool(&job->cancelled, true);
64}

References http_renderer_job::cancelled, and http_renderer_job::started.

Referenced by http_renderer_cancel(), http_renderer_stop(), mcp_dispatch(), mcp_http_run(), mcp_server_main(), and mcp_stdio_run().

◆ http_renderer_job_is_done()

bool http_renderer_job_is_done ( const http_renderer_job_t * job)

Definition at line 66 of file http_renderer.c.

66 {
67 return job && job->started && atomic_load_bool((atomic_t *)&job->done);
68}

References http_renderer_job::done, and http_renderer_job::started.

Referenced by http_renderer_reap(), and mcp_jobs_reap().

◆ http_renderer_job_join()

void http_renderer_job_join ( http_renderer_job_t * job)

Definition at line 70 of file http_renderer.c.

70 {
71 if (job && job->started) {
72 thread_join(&job->thread, NULL);
73 job->started = false;
74 }
75}
platform_thread_t thread

References http_renderer_job::started, and http_renderer_job::thread.

Referenced by http_renderer_job_reset(), http_renderer_job_wait(), http_renderer_reap(), and mcp_jobs_reap().

◆ http_renderer_job_reset()

void http_renderer_job_reset ( http_renderer_job_t * job)

Definition at line 77 of file http_renderer.c.

77 {
78 if (job) {
80 memset(job, 0, sizeof(*job));
81 }
82}
void http_renderer_job_join(http_renderer_job_t *job)

References http_renderer_job_join().

◆ http_renderer_job_start()

asciichat_error_t http_renderer_job_start ( http_renderer_job_t * job,
const char * name,
http_renderer_job_fn execute,
void * context )

Definition at line 46 of file http_renderer.c.

47 {
48 if (!job || !execute || job->started)
49 return SET_ERRNO(ERROR_INVALID_STATE, "Renderer job is already active");
50 memset(job, 0, sizeof(*job));
51 job->execute = execute;
52 job->context = context;
53 atomic_store_bool(&job->cancelled, false);
54 atomic_store_bool(&job->done, false);
55 if (platform_thread_create(&job->thread, name ? name : "http-render", http_renderer_job_main, job))
56 return SET_ERRNO(ERROR_THREAD, "Cannot start renderer job");
57 job->started = true;
58 return ASCIICHAT_OK;
59}
@ ERROR_INVALID_STATE
http_renderer_job_fn execute

References ASCIICHAT_OK, http_renderer_job::cancelled, http_renderer_job::context, http_renderer_job::done, ERROR_INVALID_STATE, ERROR_THREAD, http_renderer_job::execute, SET_ERRNO, http_renderer_job::started, and http_renderer_job::thread.

Referenced by mcp_dispatch().

◆ http_renderer_job_wait()

void http_renderer_job_wait ( http_renderer_job_t * job)

Definition at line 167 of file http_renderer.c.

167 {
169}

References http_renderer_job_join().

◆ http_renderer_reap()

void http_renderer_reap ( http_renderer_t * renderer,
bool all )

Definition at line 171 of file http_renderer.c.

171 {
172 if (!renderer)
173 return;
174 for (;;) {
175 mutex_lock(&renderer->lock);
176 http_renderer_job_t **link = &renderer->jobs;
177 http_renderer_job_t *job = NULL;
178 while (*link) {
179 if (all || ((*link)->user_data == NULL && http_renderer_job_is_done(*link))) {
180 job = *link;
181 *link = job->next;
182 --renderer->active_jobs;
183 break;
184 }
185 link = &(*link)->next;
186 }
187 mutex_unlock(&renderer->lock);
188 if (!job)
189 return;
192 SAFE_FREE(job);
193 }
194}
void http_render_result_destroy(http_render_result_t *result)
bool http_renderer_job_is_done(const http_renderer_job_t *job)
struct http_renderer_job http_renderer_job_t
http_render_result_t result
struct http_renderer_job * next
http_renderer_job_t * jobs
unsigned active_jobs

References http_renderer::active_jobs, http_render_result_destroy(), http_renderer_job_is_done(), http_renderer_job_join(), http_renderer::jobs, http_renderer::lock, http_renderer_job::next, http_renderer_job::result, and SAFE_FREE.

Referenced by http_renderer_destroy().

◆ http_renderer_render_file()

asciichat_error_t http_renderer_render_file ( const char * path,
const http_render_request_t * request,
http_render_result_t * result,
atomic_t * cancelled )

Definition at line 335 of file http_renderer.c.

336 {
337 if (!path || !request || !result)
338 return SET_ERRNO(ERROR_INVALID_PARAM, "HTTP render requires a source path, request, and result");
339 memset(result, 0, sizeof(*result));
340 uint64_t started = time_get_ns();
341 ffmpeg_decoder_t *decoder = ffmpeg_decoder_create_interruptible(path, http_renderer_cancel_requested, cancelled);
342 if (!decoder)
343 return SET_ERRNO(ERROR_MEDIA_INIT, "Cannot decode uploaded image");
345 ffmpeg_decoder_destroy(decoder);
346 return SET_ERRNO(ERROR_MEDIA_INIT, "Cannot start image decoder");
347 }
348 image_t *image = NULL;
349 uint64_t deadline = started + (request->timeout_ms ? request->timeout_ms : 10000) * NS_PER_MS_INT;
350 for (;;) {
351 if (cancelled && atomic_load_bool(cancelled)) {
352 ffmpeg_decoder_destroy(decoder);
353 result->cancelled = true;
354 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP image render cancelled");
355 }
356 asciichat_error_context_t failure = {0};
357 ffmpeg_frame_status_t status = ffmpeg_decoder_poll_video(decoder, &image, &failure);
358 if (status == FFMPEG_FRAME_READY)
359 break;
360 if (status == FFMPEG_FRAME_FAILED || status == FFMPEG_FRAME_EOF) {
361 errno_import(&failure);
362 ffmpeg_decoder_destroy(decoder);
363 return SET_ERRNO(ERROR_MEDIA_INIT, "Uploaded data does not contain a decodable image");
364 }
365 if (time_get_ns() >= deadline) {
366 ffmpeg_decoder_destroy(decoder);
367 return SET_ERRNO(ERROR_MEDIA_DECODE, "Image decoding timed out");
368 }
370 }
371 if (!image) {
372 ffmpeg_decoder_destroy(decoder);
373 return SET_ERRNO(ERROR_MEDIA_INIT, "Uploaded data does not contain a decodable image");
374 }
375 if (image->w > 8192 || image->h > 8192) {
376 image_destroy(image);
377 ffmpeg_decoder_destroy(decoder);
378 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Decoded image dimensions exceed the configured limit");
379 }
380 size_t total = 0;
381 size_t limit = request->max_output_bytes ? request->max_output_bytes : 8u * 1024u * 1024u;
382 for (;;) {
383 if (image->w > 8192 || image->h > 8192) {
384 image_destroy(image);
385 ffmpeg_decoder_destroy(decoder);
386 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Decoded image dimensions exceed the configured limit");
387 }
388 char *ascii = http_renderer_render_image(image, request);
389 image_destroy(image);
390 if (!ascii) {
391 ffmpeg_decoder_destroy(decoder);
392 return SET_ERRNO(ERROR_MEDIA_INIT, "Cannot convert image to ASCII");
393 }
394 size_t length = strlen(ascii);
395 if (length > limit - (total > limit ? limit : total)) {
396 free(ascii);
397 ffmpeg_decoder_destroy(decoder);
398 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Rendered output exceeds the configured limit");
399 }
400 if (http_render_result_add_text_frame(result, ascii, length, (time_get_ns() - started) / 1000000) != ASCIICHAT_OK) {
401 free(ascii);
402 ffmpeg_decoder_destroy(decoder);
403 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Cannot store rendered frame");
404 }
405 if (!result->ascii) {
406 result->ascii = SAFE_MALLOC(length + 1, char *);
407 SAFE_MEMCPY(result->ascii, length + 1, ascii, length + 1);
408 result->ascii_length = length;
409 }
410 total += length;
411 free(ascii);
413 break;
414 if (cancelled && atomic_load_bool(cancelled)) {
415 result->cancelled = true;
416 ffmpeg_decoder_destroy(decoder);
417 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP image render cancelled");
418 }
419 asciichat_error_context_t failure = {0};
420 image = NULL;
421 ffmpeg_frame_status_t status = ffmpeg_decoder_poll_video(decoder, &image, &failure);
422 if (status == FFMPEG_FRAME_EOF)
423 break;
424 if (status == FFMPEG_FRAME_FAILED) {
425 ffmpeg_decoder_destroy(decoder);
426 return SET_ERRNO(ERROR_MEDIA_INIT, "Video frame decoding failed");
427 }
428 while (status == FFMPEG_FRAME_WAITING) {
429 if (time_get_ns() >= deadline) {
430 ffmpeg_decoder_destroy(decoder);
431 return SET_ERRNO(ERROR_MEDIA_DECODE, "Image decoding timed out");
432 }
434 status = ffmpeg_decoder_poll_video(decoder, &image, &failure);
435 }
436 if (!image)
437 break;
438 }
439 ffmpeg_decoder_destroy(decoder);
440 result->elapsed_ms = (time_get_ns() - started) / 1000000;
441 return result->frame_count ? ASCIICHAT_OK : SET_ERRNO(ERROR_MEDIA_INIT, "Renderer returned no frames");
442}
asciichat_error_t errno_import(const asciichat_error_context_t *cause)
ffmpeg_decoder_t * ffmpeg_decoder_create_interruptible(const char *path, bool(*cancel)(void *), void *data)
unsigned long long uint64_t
Definition common.h:63
@ ERROR_MEDIA_DECODE
Definition error_codes.h:67
@ ERROR_MEDIA_INIT
Definition error_codes.h:65
@ ERROR_BUFFER_OVERFLOW
ffmpeg_frame_status_t
Decode next video frame.
asciichat_error_t ffmpeg_decoder_start_prefetch(ffmpeg_decoder_t *decoder)
Start the background frame prefetching thread.
void ffmpeg_decoder_destroy(ffmpeg_decoder_t *decoder)
Destroy FFmpeg decoder and free resources.
ffmpeg_frame_status_t ffmpeg_decoder_poll_video(ffmpeg_decoder_t *decoder, image_t **frame, asciichat_error_context_t *failure)
@ FFMPEG_FRAME_READY
@ FFMPEG_FRAME_WAITING
@ FFMPEG_FRAME_FAILED
@ FFMPEG_FRAME_EOF
#define NS_PER_MS_INT
Definition time.h:168
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
void image_destroy(image_t *image)
Destroy an image allocated with image_new().
asciichat_error_t http_render_result_add_text_frame(http_render_result_t *result, const char *text, size_t length, uint64_t timestamp_ms)
#define HTTP_RENDERER_MAX_FRAMES
FFmpeg decoder state for video and audio decoding.
SIMD-friendly storage for one RGB pixel. r, g, and b hold the color channels and padding keeps the la...
int w
Image width in pixels (must be > 0).
int h
Image height in pixels (must be > 0).

References http_render_result_t::ascii, http_render_result_t::ascii_length, ASCIICHAT_OK, http_render_result_t::cancelled, http_render_result_t::elapsed_ms, errno_import(), ERROR_BUFFER_OVERFLOW, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEDIA_DECODE, ERROR_MEDIA_INIT, ffmpeg_decoder_create_interruptible(), ffmpeg_decoder_destroy(), ffmpeg_decoder_poll_video(), ffmpeg_decoder_start_prefetch(), FFMPEG_FRAME_EOF, FFMPEG_FRAME_FAILED, FFMPEG_FRAME_READY, FFMPEG_FRAME_WAITING, http_render_result_t::frame_count, image_t::h, http_render_result_add_text_frame(), HTTP_RENDERER_MAX_FRAMES, image_destroy(), http_render_request_t::max_output_bytes, NS_PER_MS_INT, platform_sleep_ms(), SAFE_MALLOC, SAFE_MEMCPY, SET_ERRNO, http_render_request_t::timeout_ms, and image_t::w.

◆ http_renderer_stop()

void http_renderer_stop ( http_renderer_t * renderer)

Definition at line 196 of file http_renderer.c.

196 {
197 if (!renderer)
198 return;
199 mutex_lock(&renderer->lock);
200 renderer->stopping = true;
201 for (http_renderer_job_t *job = renderer->jobs; job; job = job->next)
203 mutex_unlock(&renderer->lock);
204}

References http_renderer_job_cancel(), http_renderer::jobs, http_renderer::lock, http_renderer_job::next, and http_renderer::stopping.

Referenced by http_renderer_destroy().

◆ http_renderer_submit()

asciichat_error_t http_renderer_submit ( http_renderer_t * renderer,
const http_render_request_t * request,
void * user_data,
http_renderer_job_t ** out_job )

Definition at line 126 of file http_renderer.c.

127 {
128 if (!renderer || !request || !out_job)
129 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid HTTP renderer submission");
130 *out_job = NULL;
131 mutex_lock(&renderer->lock);
132 if (renderer->stopping || renderer->active_jobs >= renderer->config.max_jobs) {
133 mutex_unlock(&renderer->lock);
134 return SET_ERRNO(ERROR_BUFFER_FULL, "HTTP renderer has no available job capacity");
135 }
136 http_renderer_job_t *job = SAFE_CALLOC(1, sizeof(*job), http_renderer_job_t *);
137 job->renderer = renderer;
138 job->request = *request;
139 job->user_data = user_data;
140 if (!job->request.timeout_ms)
141 job->request.timeout_ms = renderer->config.timeout_ms;
142 if (!job->request.max_output_bytes)
144 atomic_store_bool(&job->cancelled, false);
145 atomic_store_bool(&job->done, false);
146 if (platform_thread_create(&job->thread, "http-render", http_renderer_job_main, job)) {
147 SAFE_FREE(job);
148 mutex_unlock(&renderer->lock);
149 return SET_ERRNO(ERROR_THREAD, "Cannot start HTTP renderer job");
150 }
151 job->started = true;
152 job->next = renderer->jobs;
153 renderer->jobs = job;
154 ++renderer->active_jobs;
155 mutex_unlock(&renderer->lock);
156 *out_job = job;
157 return ASCIICHAT_OK;
158}
@ ERROR_BUFFER_FULL
http_render_request_t request

References http_renderer::active_jobs, ASCIICHAT_OK, http_renderer_job::cancelled, http_renderer::config, http_renderer_job::done, ERROR_BUFFER_FULL, ERROR_INVALID_PARAM, ERROR_THREAD, http_renderer::jobs, http_renderer::lock, http_renderer_config_t::max_jobs, http_render_request_t::max_output_bytes, http_renderer_config_t::max_output_bytes, http_renderer_job::next, http_renderer_job::renderer, http_renderer_job::request, SAFE_CALLOC, SAFE_FREE, SET_ERRNO, http_renderer_job::started, http_renderer::stopping, http_renderer_job::thread, http_render_request_t::timeout_ms, http_renderer_config_t::timeout_ms, and http_renderer_job::user_data.