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

Local webcam mirror mode: view webcam as ASCII art without network. More...

Go to the source code of this file.

Functions

int mirror_main (void)
 Run mirror mode main loop.
 

Detailed Description

Local webcam mirror mode: view webcam as ASCII art without network.

Mirror mode provides a simple way to view your own webcam feed converted to ASCII art directly in the terminal. No server connection is required.

Features

  • Local webcam capture and ASCII conversion
  • Terminal capability detection for optimal color output
  • Frame rate limiting for smooth display
  • Clean shutdown on Ctrl+C

Usage

Run as a standalone mode:

ascii-chat mirror
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
2025

Definition in file mirror/main.c.

Function Documentation

◆ mirror_main()

int mirror_main ( void  )

Run mirror mode main loop.

Initializes webcam and terminal, then continuously captures frames, converts them to ASCII art, and displays them locally.

Returns
0 on success, non-zero error code on failure

Definition at line 209 of file mirror/main.c.

209 {
210 log_info("Starting mirror mode");
211
212 // Install console control-c handler
213 platform_set_console_ctrl_handler(mirror_console_ctrl_handler);
214
215#ifndef _WIN32
216 platform_signal(SIGPIPE, SIG_IGN);
217#endif
218
219 // Initialize webcam
220 int webcam_result = webcam_init(GET_OPTION(webcam_index));
221 if (webcam_result != 0) {
222 log_fatal("Failed to initialize webcam: %s", asciichat_error_string(webcam_result));
223 webcam_print_init_error_help(webcam_result);
224 return webcam_result;
225 }
226
227 // Initialize display
228 if (mirror_display_init() != 0) {
229 log_fatal("Failed to initialize display");
231 return ERROR_DISPLAY;
232 }
233
234 // Detect terminal capabilities
236 caps = apply_color_mode_override(caps);
237
238 // Initialize ANSI color lookup tables based on terminal capabilities
239 if (caps.color_level == TERM_COLOR_TRUECOLOR) {
241 } else if (caps.color_level == TERM_COLOR_256) {
243 } else if (caps.color_level == TERM_COLOR_16) {
245 }
246
247 // Initialize palette
248 char palette_chars[256] = {0};
249 size_t palette_len = 0;
250 char luminance_palette[256] = {0};
251
252 const char *custom_chars = GET_OPTION(palette_custom_set) ? GET_OPTION(palette_custom) : NULL;
253 palette_type_t palette_type = GET_OPTION(palette_type);
254 if (initialize_client_palette(palette_type, custom_chars, palette_chars, &palette_len, luminance_palette) != 0) {
255 log_fatal("Failed to initialize palette");
256 mirror_display_cleanup();
258 return ERROR_INVALID_STATE;
259 }
260
261 // Frame rate limiting
262 const long frame_interval_ms = 1000 / 60; // 60 FPS target
263 struct timespec last_frame_time = {0, 0};
264
265 // Snapshot mode timing
266 struct timespec snapshot_start_time = {0, 0};
267 bool snapshot_done = false;
268 if (GET_OPTION(snapshot_mode)) {
269 (void)clock_gettime(CLOCK_MONOTONIC, &snapshot_start_time);
270 }
271
272 // FPS tracking
273 uint64_t frame_count = 0;
274 struct timespec fps_report_time;
275 (void)clock_gettime(CLOCK_MONOTONIC, &fps_report_time);
276
277 log_info("Mirror mode running - press Ctrl+C to exit");
279
280 while (!mirror_should_exit()) {
281 // Frame rate limiting
282 struct timespec current_time;
283 (void)clock_gettime(CLOCK_MONOTONIC, &current_time);
284
285 long elapsed_ms = (current_time.tv_sec - last_frame_time.tv_sec) * 1000 +
286 (current_time.tv_nsec - last_frame_time.tv_nsec) / 1000000;
287
288 if (elapsed_ms < frame_interval_ms) {
289 platform_sleep_usec((frame_interval_ms - elapsed_ms) * 1000);
290 continue;
291 }
292
293 // Snapshot mode: check if delay has elapsed (delay 0 = capture first frame immediately)
294 if (GET_OPTION(snapshot_mode) && !snapshot_done) {
295 double elapsed_sec = (double)(current_time.tv_sec - snapshot_start_time.tv_sec) +
296 (double)(current_time.tv_nsec - snapshot_start_time.tv_nsec) / 1e9;
297
298 float snapshot_delay = GET_OPTION(snapshot_delay);
299 if (elapsed_sec >= snapshot_delay) {
300 snapshot_done = true;
301 }
302 }
303
304 // Read frame from webcam
305 image_t *image = webcam_read();
306 if (!image) {
307 platform_sleep_usec(10000); // 10ms delay before retry
308 continue;
309 }
310
311 // Convert image to ASCII
312 // When stretch is 0 (disabled), we preserve aspect ratio (true)
313 // When stretch is 1 (enabled), we allow stretching without aspect ratio preservation (false)
314 bool stretch = GET_OPTION(stretch);
315 unsigned short int width = GET_OPTION(width);
316 unsigned short int height = GET_OPTION(height);
317 bool preserve_aspect_ratio = !stretch;
318 char *ascii_frame = ascii_convert_with_capabilities(image, width, height, &caps, preserve_aspect_ratio, stretch,
319 palette_chars, luminance_palette);
320
321 if (ascii_frame) {
322 // When piping/redirecting in snapshot mode, only output the final frame
323 // When outputting to TTY, show live preview frames
324 bool snapshot_mode = GET_OPTION(snapshot_mode);
325 bool should_write = !snapshot_mode || g_mirror_has_tty || snapshot_done;
326 if (should_write) {
327 mirror_write_frame(ascii_frame);
328 }
329
330 // Snapshot mode: exit after capturing the final frame
331 if (snapshot_mode && snapshot_done) {
332 SAFE_FREE(ascii_frame);
333 image_destroy(image);
334 break;
335 }
336
337 SAFE_FREE(ascii_frame);
338 frame_count++;
339 }
340
341 image_destroy(image);
342 last_frame_time = current_time;
343
344 // FPS reporting every 5 seconds
345 uint64_t fps_elapsed_us = ((uint64_t)current_time.tv_sec * 1000000 + (uint64_t)current_time.tv_nsec / 1000) -
346 ((uint64_t)fps_report_time.tv_sec * 1000000 + (uint64_t)fps_report_time.tv_nsec / 1000);
347
348 if (fps_elapsed_us >= 5000000) {
349 double fps = (double)frame_count / ((double)fps_elapsed_us / 1000000.0);
350 log_debug("Mirror FPS: %.1f", fps);
351 frame_count = 0;
352 fps_report_time = current_time;
353 }
354 }
355
356 // Cleanup
358 log_info("Mirror mode shutting down");
359
360 mirror_display_cleanup();
362
363 return 0;
364}
#define SAFE_FREE(ptr)
Definition common.h:320
unsigned long long uint64_t
Definition common.h:59
@ ERROR_INVALID_STATE
@ ERROR_DISPLAY
Definition error_codes.h:99
#define log_fatal(...)
Log a FATAL message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644
int initialize_client_palette(palette_type_t palette_type, const char *custom_chars, char client_palette_chars[256], size_t *client_palette_len, char client_luminance_palette[256])
Initialize client palette with full configuration.
Definition palette.c:326
palette_type_t
Built-in palette type enumeration.
Definition palette.h:84
signal_handler_t platform_signal(int sig, signal_handler_t handler)
Set a signal handler.
terminal_capabilities_t apply_color_mode_override(terminal_capabilities_t caps)
Apply command-line overrides to detected capabilities.
bool platform_set_console_ctrl_handler(console_ctrl_handler_t handler)
Register a console control handler (for Ctrl+C, etc.)
void platform_sleep_usec(unsigned int usec)
High-precision sleep function with microsecond precision.
terminal_capabilities_t detect_terminal_capabilities(void)
Detect terminal capabilities.
@ TERM_COLOR_16
16-color support (standard ANSI colors)
Definition terminal.h:430
@ TERM_COLOR_256
256-color support (extended ANSI palette)
Definition terminal.h:432
@ TERM_COLOR_TRUECOLOR
24-bit truecolor support (RGB colors)
Definition terminal.h:434
void ansi_fast_init_256color(void)
Initialize 256-color mode lookup tables.
Definition ansi_fast.c:179
void ansi_fast_init(void)
Initialize the decimal lookup table.
Definition ansi_fast.c:173
void ansi_fast_init_16color(void)
Initialize 16-color mode lookup tables.
Definition ansi_fast.c:225
void image_destroy(image_t *p)
Destroy an image allocated with image_new()
Definition video/image.c:85
char * ascii_convert_with_capabilities(image_t *original, const ssize_t width, const ssize_t height, const terminal_capabilities_t *caps, const bool use_aspect_ratio, const bool stretch, const char *palette_chars, const char luminance_palette[256])
Convert image to ASCII art with terminal capability awareness.
Definition ascii.c:188
asciichat_error_t webcam_init(unsigned short int webcam_index)
Initialize global webcam interface.
Definition webcam.c:18
image_t * webcam_read(void)
Capture a frame from global webcam.
Definition webcam.c:57
void webcam_print_init_error_help(asciichat_error_t error_code)
Print helpful error diagnostics for webcam initialization failures.
Definition webcam.c:229
void webcam_cleanup(void)
Clean up global webcam interface.
Definition webcam.c:204
Image structure.
Complete terminal capabilities structure.
Definition terminal.h:485
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
Definition terminal.h:487

References ansi_fast_init(), ansi_fast_init_16color(), ansi_fast_init_256color(), apply_color_mode_override(), ascii_convert_with_capabilities(), terminal_capabilities_t::color_level, detect_terminal_capabilities(), ERROR_DISPLAY, ERROR_INVALID_STATE, GET_OPTION, image_destroy(), initialize_client_palette(), log_debug, log_fatal, log_info, log_set_terminal_output(), platform_set_console_ctrl_handler(), platform_signal(), platform_sleep_usec(), SAFE_FREE, TERM_COLOR_16, TERM_COLOR_256, TERM_COLOR_TRUECOLOR, webcam_cleanup(), webcam_init(), webcam_print_init_error_help(), and webcam_read().