ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Media Capture

📹 Webcam video capture and transmission to server More...

Files

file  capture.c
 ðŸ“¹ Client webcam capture: dedicated capture thread with frame rate limiting and network transmission
 
file  capture.h
 ascii-chat Client Media Capture Management Interface
 

Functions

int capture_init ()
 Initialize capture subsystem.
 
int capture_start_thread ()
 Start capture thread.
 
void capture_stop_thread ()
 Stop capture thread.
 
bool capture_thread_exited ()
 Check if capture thread has exited.
 
void capture_cleanup ()
 Cleanup capture subsystem.
 

Detailed Description

📹 Webcam video capture and transmission to server

Media Capture

Overview

The media capture subsystem manages webcam video capture, frame compression, and transmission to the server. Supports platform-specific webcam backends and test pattern mode for testing without hardware.

Implementation: src/client/capture.c, src/client/capture.h

Platform Support

Webcam Backends:

Capture Thread

static void *capture_thread_func(void *arg)
{
(void)arg;
// Calculate frame interval for target FPS
uint64_t frame_interval_usec = 1000000 / VIDEO_CAPTURE_FPS;
uint64_t start_time = get_monotonic_time_usec();
// Capture frame from webcam
video_frame_t *frame = webcam_capture();
if (!frame) {
platform_sleep_usec(frame_interval_usec);
continue;
}
// Optional compression
void *send_data = frame->data;
size_t send_size = frame->data_size;
void *compressed_data = NULL;
if (opt_compress) {
compressed_data = compress_frame(frame->data, frame->data_size, &send_size);
if (compressed_data) {
send_data = compressed_data;
}
}
// Send frame to server
send_packet_to_server(PACKET_TYPE_IMAGE_FRAME, send_data, send_size,
if (compressed_data) free(compressed_data);
if (frame->owns_data && frame->data) free(frame->data);
free(frame);
// Rate limiting
platform_sleep_usec(frame_interval_usec);
// Snapshot mode: exit after delay
if (opt_snapshot) {
uint64_t elapsed = (get_monotonic_time_usec() - start_time) / 1000000;
if (elapsed >= opt_snapshot_delay) {
break;
}
}
}
atomic_store(&g_capture_thread_exited, true);
return NULL;
}
bool should_exit(void)
Definition main.c:90
bool server_connection_is_active()
Check if server connection is currently active.
uint32_t server_connection_get_client_id()
Get client ID assigned by server.
void signal_exit(void)
Definition main.c:94

Snapshot Mode

Snapshot mode (--snapshot, --snapshot-delay <seconds>):

  • Useful for testing without continuous streaming
  • --snapshot: Capture single frame and exit
  • --snapshot-delay N: Capture for N seconds then exit
# Single frame test
./bin/ascii-chat client --snapshot
# 5-second test
./bin/ascii-chat client --snapshot --snapshot-delay 5

Test Pattern Mode

Test pattern mode (--test-pattern):

  • Generates synthetic video frames without webcam hardware
  • Useful for testing on Windows or systems without webcam
  • Uses platform-specific test pattern generation
# Use test pattern instead of webcam
./bin/ascii-chat client --test-pattern
See also
src/client/capture.c
src/client/capture.h
lib/video/webcam/webcam.h
Client Overview

Function Documentation

◆ capture_cleanup()

void capture_cleanup ( )

#include <capture.c>

Cleanup capture subsystem.

Cleanup capture subsystem

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

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

485 {
487
488 // Destroy capture context
489 if (g_capture_capture_ctx) {
490 session_capture_destroy(g_capture_capture_ctx);
491 g_capture_capture_ctx = NULL;
492 }
493}
void capture_stop_thread()
Stop capture thread.
void session_capture_destroy(session_capture_ctx_t *ctx)

References capture_stop_thread(), and session_capture_destroy().

Referenced by client_crypto_handshake().

◆ capture_init()

int capture_init ( )

#include <capture.c>

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
0 on success, negative on error

Definition at line 346 of file src/client/capture.c.

346 {
347 // Build capture configuration from options
348 session_capture_config_t config = {0};
349 const char *media_url = GET_OPTION(media_url);
350 const char *media_file = GET_OPTION(media_file);
351 bool media_from_stdin = GET_OPTION(media_from_stdin);
352
353 if (media_url && media_url[0] != '\0') {
354 // Network URL streaming (takes priority over --file)
355 // Don't open webcam when streaming from URL
356 config.type = MEDIA_SOURCE_FILE;
357 config.path = media_url;
358 config.loop = false; // Network URLs cannot be looped
359 log_debug("Using network URL: %s (webcam disabled)", media_url);
360 } else if (media_file && media_file[0] != '\0') {
361 // File or stdin streaming - don't open webcam
362 config.type = media_from_stdin ? MEDIA_SOURCE_STDIN : MEDIA_SOURCE_FILE;
363 config.path = media_file;
364 config.loop = GET_OPTION(media_loop) && !media_from_stdin;
365 log_debug("Using media %s: %s (webcam disabled)", media_from_stdin ? "stdin" : "file", media_file);
366 } else if (GET_OPTION(test_pattern)) {
367 // Test pattern mode - don't open real webcam
368 config.type = MEDIA_SOURCE_TEST;
369 config.path = NULL;
370 log_debug("Using test pattern mode");
371 } else {
372 // Webcam mode (default)
373 static char webcam_index_str[32];
374 safe_snprintf(webcam_index_str, sizeof(webcam_index_str), "%u", GET_OPTION(webcam_index));
375 config.type = MEDIA_SOURCE_WEBCAM;
376 config.path = webcam_index_str;
377 log_debug("Using webcam device %u", GET_OPTION(webcam_index));
378 }
379 config.target_fps = CAPTURE_TARGET_FPS;
380 config.resize_for_network = true; // Client always resizes for network transmission
381
382 // Configure audio capture with fallback to microphone
383 config.enable_audio = true;
384 config.audio_fallback_to_mic = true;
385 config.mic_audio_ctx = audio_get_context();
386
387 // Add seek timestamp if specified
388 config.initial_seek_timestamp = GET_OPTION(media_seek_timestamp);
389
390 // Create capture context using session library
391 g_capture_capture_ctx = session_capture_create(&config);
392 if (!g_capture_capture_ctx) {
393 // Check if there's already an error set (e.g., ERROR_WEBCAM_IN_USE)
394 asciichat_error_t existing_error = GET_ERRNO();
395 log_debug("session_capture_create failed, GET_ERRNO() returned: %d", existing_error);
396 if (existing_error != ASCIICHAT_OK) {
397 log_debug("Returning existing error code %d", existing_error);
398 return existing_error;
399 }
400 SET_ERRNO(ERROR_MEDIA_INIT, "Failed to initialize capture source");
401 return -1;
402 }
403
404 return 0;
405}
audio_context_t * audio_get_context(void)
Get the global audio context for use by other subsystems.
session_capture_ctx_t * session_capture_create(const session_capture_config_t *config)
#define CAPTURE_TARGET_FPS
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456

References audio_get_context(), CAPTURE_TARGET_FPS, safe_snprintf(), and session_capture_create().

◆ capture_start_thread()

int capture_start_thread ( )

#include <capture.c>

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
0 on success, negative on error

Definition at line 416 of file src/client/capture.c.

416 {
417 if (THREAD_IS_CREATED(g_capture_thread_created)) {
418 log_warn("Capture thread already created");
419 return 0;
420 }
421
422 // Start webcam capture thread
423 atomic_store(&g_capture_thread_exited, false);
424 if (thread_pool_spawn(g_client_worker_pool, webcam_capture_thread_func, NULL, 2, "webcam_capture") != ASCIICHAT_OK) {
425 SET_ERRNO(ERROR_THREAD, "Webcam capture thread creation failed");
426 LOG_ERRNO_IF_SET("Webcam capture thread creation failed");
427 return -1;
428 }
429
430 g_capture_thread_created = true;
431 log_debug("Webcam capture thread created successfully");
432
433 // Notify server we're starting to send video
434 if (threaded_send_stream_start_packet(STREAM_TYPE_VIDEO) < 0) {
435 LOG_ERRNO_IF_SET("Failed to send stream start packet");
436 }
437
438 return 0;
439}
thread_pool_t * g_client_worker_pool
Global client worker thread pool.
asciichat_error_t threaded_send_stream_start_packet(uint32_t stream_type)
Thread-safe stream start packet transmission.
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Definition thread_pool.c:70

References g_client_worker_pool, thread_pool_spawn(), and threaded_send_stream_start_packet().

Referenced by protocol_start_connection().

◆ capture_stop_thread()

void capture_stop_thread ( )

#include <capture.c>

Stop capture thread.

Stop capture thread

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

Definition at line 448 of file src/client/capture.c.

448 {
449 if (!THREAD_IS_CREATED(g_capture_thread_created)) {
450 return;
451 }
452
453 // Wait for thread to exit gracefully
454 int wait_count = 0;
455 while (wait_count < 20 && !atomic_load(&g_capture_thread_exited)) {
456 platform_sleep_us(100 * US_PER_MS_INT); // 100ms
457 wait_count++;
458 }
459
460 if (!atomic_load(&g_capture_thread_exited)) {
461 log_warn("Capture thread not responding after 2 seconds - will be joined by thread pool");
462 }
463
464 // Thread will be joined by thread_pool_stop_all() in protocol_stop_connection()
465 g_capture_thread_created = false;
466}
void platform_sleep_us(unsigned int us)

References platform_sleep_us().

Referenced by capture_cleanup(), and protocol_stop_connection().

◆ capture_thread_exited()

bool capture_thread_exited ( )

#include <capture.c>

Check if capture thread has exited.

Check if capture thread has exited

Returns
true if thread has exited, false otherwise
true if thread exited, false otherwise

Definition at line 474 of file src/client/capture.c.

474 {
475 return atomic_load(&g_capture_thread_exited);
476}