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

📹 Client webcam capture: dedicated capture thread with frame rate limiting and network transmission More...

Go to the source code of this file.

Macros

#define CLIENT_CAPTURE_WEBCAM_INDEX_BUFFER_SIZE   32
#define CAPTURE_TARGET_FPS   6

Functions

int capture_init ()
 Initialize capture subsystem.
int capture_start_thread (client_connection_t *connection)
 Start capture thread.
void capture_stop_thread (client_connection_t *connection)
 Stop capture thread.
bool capture_thread_exited (client_connection_t *connection)
 Check if capture thread has exited.
void capture_cleanup ()
 Cleanup capture subsystem.
void * capture_get_media_source (void)
session_capture_ctx_t * capture_get_context (void)

Detailed Description

📹 Client webcam capture: dedicated capture thread with frame rate limiting and network transmission

The capture system follows a producer-consumer pattern:

  • Producer: Webcam capture thread reads frames from camera
  • Processing: Frame resizing and format conversion pipeline
  • Consumer: Network transmission thread sends processed frames
  • Rate Limiting: Frame rate control to prevent bandwidth overload

Threading Model

Capture operations run in a dedicated thread:

  • Main Thread: Manages thread lifecycle and coordination
  • Capture Thread: Continuous frame capture and processing loop
  • Synchronization: Atomic flags coordinate thread shutdown
  • Resource Management: Clean webcam and memory resource cleanup

Frame Processing Pipeline

Raw webcam frames undergo comprehensive processing:

  1. Capture: Read raw frame from webcam device
  2. Validation: Check frame validity and dimensions
  3. Aspect Ratio: Calculate optimal resize dimensions
  4. Resizing: Scale frame to network-optimal size (800x600 max)
  5. Serialization: Pack frame data into network packet format
  6. Transmission: Send via IMAGE_FRAME packet to server

Frame Rate Management

Implements intelligent frame rate limiting:

  • Capture Rate: 144 FPS to support high-refresh displays (~6.9ms intervals)
  • Timing Control: Monotonic clock for accurate frame intervals
  • Adaptive Delays: Dynamic sleep adjustment for consistent timing
  • Display Support: High capture rate enables smooth playback on promotion displays

Image Resizing Strategy

Optimizes frame size for network efficiency:

  • Maximum Dimensions: 800x600 pixels for reasonable bandwidth
  • Aspect Ratio Preservation: Maintain original camera aspect ratio
  • Intelligent Scaling: Fit-to-bounds algorithm for optimal sizing
  • Quality Balance: Large enough for server flexibility, small enough for efficiency

Packet Format and Transmission

Frame data serialized for network transmission:

  • Header: Width and height as 32-bit network byte order integers
  • Payload: RGB pixel data as continuous byte array
  • Size Limits: Enforce maximum packet size for protocol compliance
  • Error Handling: Graceful handling of oversized frames

Platform Compatibility

Uses webcam abstraction layer for cross-platform support:

  • Linux: Video4Linux2 (V4L2) webcam interface
  • macOS: AVFoundation framework integration
  • Windows: DirectShow API wrapper (stub implementation)
  • Fallback: Test pattern generation when webcam unavailable

Integration Points

  • main.c: Capture subsystem lifecycle management
  • server.c: Network packet transmission and connection monitoring
  • webcam.c: Low-level webcam device interface and frame reading
  • image.c: Image processing and memory management functions

Error Handling

Capture errors handled with appropriate recovery:

  • Device Errors: Webcam unavailable or device busy (continue with warnings)
  • Memory Errors: Frame allocation failures (skip frame, continue)
  • Network Errors: Transmission failures (trigger connection loss detection)
  • Processing Errors: Invalid frames or resize failures (skip and retry)

Resource Management

Careful resource lifecycle management:

  • Webcam Device: Proper initialization and cleanup
  • Frame Buffers: Automatic memory management with leak prevention
  • Thread Resources: Clean thread termination and resource release
  • Network Buffers: Efficient packet buffer allocation and cleanup
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
2025

Definition in file client/capture.c.

Macro Definition Documentation

◆ CAPTURE_TARGET_FPS

#define CAPTURE_TARGET_FPS   6

Fallback capture FPS when no target was configured

Definition at line 147 of file client/capture.c.

Referenced by capture_init().

◆ CLIENT_CAPTURE_WEBCAM_INDEX_BUFFER_SIZE

#define CLIENT_CAPTURE_WEBCAM_INDEX_BUFFER_SIZE   32

Definition at line 3 of file client/capture.c.

Referenced by capture_init().

Function Documentation

◆ capture_cleanup()

void capture_cleanup ( )

Cleanup capture subsystem.

Cleanup capture subsystem

Stops capture thread and cleans up media source resources. Called during client shutdown.

Definition at line 554 of file client/capture.c.

554 {
555
556 // Destroy capture context
557 if (g_capture_capture_ctx) {
558 session_capture_destroy(g_capture_capture_ctx);
559 g_capture_capture_ctx = NULL;
560 }
561}
void session_capture_destroy(session_capture_ctx_t *ctx)
Destroy session capture context and free resources.

References session_capture_destroy().

◆ capture_get_context()

session_capture_ctx_t * capture_get_context ( void )

Definition at line 567 of file client/capture.c.

567 {
568 return g_capture_capture_ctx;
569}

◆ capture_get_media_source()

void * capture_get_media_source ( void )

Definition at line 563 of file client/capture.c.

563 {
564 return session_capture_get_media_source(g_capture_capture_ctx);
565}
void * session_capture_get_media_source(session_capture_ctx_t *ctx)
Get the underlying media source from capture context.

References session_capture_get_media_source().

◆ capture_init()

int capture_init ( )

Initialize capture subsystem.

Initialize capture subsystem

Sets up media source (webcam, file, or stdin) and prepares capture system for operation. Must be called once during client initialization.

Returns
0 on success, negative on error

Definition at line 412 of file client/capture.c.

412 {
413 // Build capture configuration from options
414 session_capture_config_t config = {0};
415 const char *media_url = GET_OPTION(media_url);
416 const char *media_file = GET_OPTION(media_file);
417 bool media_from_stdin = GET_OPTION(media_from_stdin);
418
419 if (media_url && media_url[0] != '\0') {
420 // Network URL streaming (takes priority over --file)
421 // Don't open webcam when streaming from URL
422 config.type = MEDIA_SOURCE_FILE;
423 config.path = media_url;
424 config.loop = false; // Network URLs cannot be looped
425 log_debug("Using network URL: %s (webcam disabled)", media_url);
426 } else if (media_file && media_file[0] != '\0') {
427 // File or stdin streaming - don't open webcam
428 config.type = media_from_stdin ? MEDIA_SOURCE_STDIN : MEDIA_SOURCE_FILE;
429 config.path = media_file;
430 config.loop = GET_OPTION(media_loop) && !media_from_stdin;
431 log_debug("Using media %s: %s (webcam disabled)", media_from_stdin ? "stdin" : "file", media_file);
432 } else if (GET_OPTION(test_pattern)) {
433 // Test pattern mode - don't open real webcam
434 config.type = MEDIA_SOURCE_TEST;
435 config.path = NULL;
436 log_debug("Using test pattern mode");
437 } else {
438 // Webcam mode (default)
439 static char webcam_index_str[CLIENT_CAPTURE_WEBCAM_INDEX_BUFFER_SIZE];
440 safe_snprintf(webcam_index_str, sizeof(webcam_index_str), "%u", GET_OPTION(webcam_index));
441 config.type = MEDIA_SOURCE_WEBCAM;
442 config.path = webcam_index_str;
443 log_debug("Using webcam device %u", GET_OPTION(webcam_index));
444 }
445 int configured_fps = GET_OPTION(fps);
446 config.target_fps = configured_fps > 0 ? (uint32_t)configured_fps : CAPTURE_TARGET_FPS;
447 config.resize_for_network = true; // Client always resizes for network transmission
448
449 // Configure audio capture with fallback to microphone
450 config.enable_audio = true;
451 config.audio_fallback_to_mic = true;
453
454 // Add seek timestamp if specified
455 config.initial_seek_timestamp = GET_OPTION(media_seek_timestamp);
456
457 // Create capture context using session library
459 g_capture_capture_ctx = session_capture_create(&config);
460 if (!g_capture_capture_ctx) {
461 // Check if there's already an error set (e.g., ERROR_WEBCAM_IN_USE)
463 asciichat_error_t existing_error = errno_peek_since(create_scope, &failure) ? failure.code : ASCIICHAT_OK;
464 log_debug("session_capture_create failed %s", asciichat_error_format(NULL, existing_error));
465 if (existing_error != ASCIICHAT_OK) {
466 log_debug("Returning existing error %s", asciichat_error_format(NULL, existing_error));
467 return existing_error;
468 }
469 SET_ERRNO(ERROR_MEDIA_INIT, "Failed to initialize capture source");
470 return -1;
471 }
472
473 return 0;
474}
bool errno_peek_since(asciichat_errno_scope_t scope, asciichat_error_context_t *out)
asciichat_errno_scope_t errno_checkpoint(void)
const char * asciichat_error_format(const char *message, asciichat_error_t code)
#define SET_ERRNO(code,...)
#define CAPTURE_TARGET_FPS
#define CLIENT_CAPTURE_WEBCAM_INDEX_BUFFER_SIZE
unsigned int uint32_t
Definition common.h:62
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_MEDIA_INIT
Definition error_codes.h:65
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
@ MEDIA_SOURCE_STDIN
Piped or redirected input.
Definition source.h:100
@ MEDIA_SOURCE_WEBCAM
Hardware webcam device.
Definition source.h:98
@ MEDIA_SOURCE_FILE
Media file (video/audio).
Definition source.h:99
@ MEDIA_SOURCE_TEST
Test pattern generator.
Definition source.h:101
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
session_capture_ctx_t * session_capture_create(const session_capture_config_t *config)
Legacy function - creates either mirror or network capture based on options.
audio_context_t * audio_get_context(void)
Get the global audio context for use by other subsystems.
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...
Media-source and loader settings used to create a session capture context.
media_source_type_t type
Media source type (WEBCAM, FILE, STDIN, TEST).
const char * path
Device index (for webcam) or file path (for file sources).
uint32_t target_fps
Target frame rate in FPS (e.g., 60 for display, 144 for network).
double initial_seek_timestamp
Seek to this timestamp after opening media (0.0 = no seek).
bool audio_fallback_to_mic
Fall back to microphone if file audio is not available.
bool resize_for_network
Resize frames to network-optimal dimensions (MAX_FRAME_WIDTH x MAX_FRAME_HEIGHT).
bool loop
Enable loop playback for file sources.
bool enable_audio
Enable audio capture from media source.
void * mic_audio_ctx
Microphone audio context for fallback (borrowed, not owned).
test pattern is intentionally a small, concrete record. Fields such as image, rgba give callers the i...

References asciichat_error_format(), ASCIICHAT_OK, session_capture_config_t::audio_fallback_to_mic, audio_get_context(), CAPTURE_TARGET_FPS, CLIENT_CAPTURE_WEBCAM_INDEX_BUFFER_SIZE, asciichat_error_context_t::code, session_capture_config_t::enable_audio, errno_checkpoint(), errno_peek_since(), ERROR_MEDIA_INIT, GET_OPTION, session_capture_config_t::initial_seek_timestamp, log_debug, session_capture_config_t::loop, MEDIA_SOURCE_FILE, MEDIA_SOURCE_STDIN, MEDIA_SOURCE_TEST, MEDIA_SOURCE_WEBCAM, session_capture_config_t::mic_audio_ctx, session_capture_config_t::path, session_capture_config_t::resize_for_network, safe_snprintf(), session_capture_create(), SET_ERRNO, session_capture_config_t::target_fps, and session_capture_config_t::type.

◆ capture_start_thread()

int capture_start_thread ( client_connection_t * connection)

Start capture thread.

Start capture thread

Creates and starts the webcam capture thread. Also sends stream start notification to server.

Returns
0 on success, negative on error

Definition at line 485 of file client/capture.c.

485 {
486 if (!connection->capture_context)
487 return 0;
488 if (THREAD_IS_CREATED(connection->capture_thread_created)) {
489 log_warn("Capture thread already created");
490 return 0;
491 }
492
493 // Start webcam capture thread
494 atomic_store_bool(&connection->capture_thread_exited, false);
495 if (thread_pool_spawn(connection->client_worker_pool, webcam_capture_thread_func, connection, 2, "webcam_capture") !=
496 ASCIICHAT_OK) {
497 SET_ERRNO(ERROR_THREAD, "Webcam capture thread creation failed");
498 LOG_ERRNO_IF_SET("Webcam capture thread creation failed");
499 return -1;
500 }
501
502 connection->capture_thread_created = true;
503 log_debug("Webcam capture thread created successfully");
504
505 return 0;
506}
#define LOG_ERRNO_IF_SET(message)
@ ERROR_THREAD
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
thread_pool_t * client_worker_pool
session_capture_ctx_t * capture_context
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Spawn a worker thread in the pool.
#define THREAD_IS_CREATED(created_flag)
Definition util/thread.h:62

References ASCIICHAT_OK, client_connection::capture_context, client_connection::capture_thread_created, client_connection::capture_thread_exited, client_connection::client_worker_pool, ERROR_THREAD, log_debug, LOG_ERRNO_IF_SET, log_warn, SET_ERRNO, THREAD_IS_CREATED, and thread_pool_spawn().

Referenced by client_protocol_start().

◆ capture_stop_thread()

void capture_stop_thread ( client_connection_t * connection)

Stop capture thread.

Stop capture thread

Gracefully stops the capture thread and cleans up resources. Safe to call multiple times.

Definition at line 515 of file client/capture.c.

515 {
516 if (!connection)
517 return;
518 if (!THREAD_IS_CREATED(connection->capture_thread_created)) {
519 return;
520 }
521
522 // Wait for thread to exit gracefully
523 int wait_count = 0;
524 while (wait_count < 20 && !atomic_load_bool(&connection->capture_thread_exited)) {
525 platform_sleep_us(100 * US_PER_MS_INT); // 100ms
526 wait_count++;
527 }
528
529 if (!atomic_load_bool(&connection->capture_thread_exited)) {
530 log_warn("Capture thread not responding after 2 seconds - will be joined by thread pool");
531 }
532
533 // Thread will be joined by thread_pool_stop_all() in client_connection_stop()
534 connection->capture_thread_created = false;
535}
#define US_PER_MS_INT
Definition time.h:172

References client_connection::capture_thread_created, client_connection::capture_thread_exited, log_warn, THREAD_IS_CREATED, and US_PER_MS_INT.

◆ capture_thread_exited()

bool capture_thread_exited ( client_connection_t * connection)

Check if capture thread has exited.

Check if capture thread has exited

Returns
true if thread has exited, false otherwise

Definition at line 543 of file client/capture.c.

543 {
544 return atomic_load_bool(&connection->capture_thread_exited);
545}

References client_connection::capture_thread_exited.