ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
video/webcam/webcam.c
Go to the documentation of this file.
1
8#include <stdio.h>
9
10#include <ascii-chat/video/webcam/webcam.h>
11#include <ascii-chat/common.h>
12#include <ascii-chat/options/options.h>
13#include <ascii-chat/video/image.h>
14#include <ascii-chat/platform/string.h>
15
16static webcam_context_t *global_webcam_ctx = NULL;
17static image_t *cached_webcam_frame = NULL;
18static unsigned int frame_counter = 0; // Counter for animated test pattern
19
20asciichat_error_t webcam_init(unsigned short int webcam_index) {
21 // Check if test pattern mode is enabled
22 if (GET_OPTION(test_pattern)) {
23 log_info("Test pattern mode enabled - not opening real webcam");
24 log_info("Test pattern resolution: 320x240");
25 return ASCIICHAT_OK;
26 }
27
28#ifdef __linux__
29 log_info("Initializing webcam with V4L2 (Linux)");
30 log_info("Attempting to open webcam with index %d using V4L2 (Linux)...", webcam_index);
31#elif defined(__APPLE__)
32 log_info("Initializing webcam with AVFoundation (macOS)");
33 log_info("Attempting to open webcam with index %d using AVFoundation (macOS)...", webcam_index);
34#elif defined(_WIN32)
35 log_info("Initializing webcam with Media Foundation (Windows)");
36 log_info("Attempting to open webcam with index %d using Media Foundation (Windows)...", webcam_index);
37#else
38 log_info("Initializing webcam with Unknown platform");
39 log_info("Attempting to open webcam with index %d using Unknown platform...", webcam_index);
40#endif
41
42 asciichat_error_t result = webcam_init_context(&global_webcam_ctx, webcam_index);
43 if (result != ASCIICHAT_OK) {
44 SET_ERRNO(result, "Failed to connect to webcam (error code: %d)", result);
45 return result;
46 }
47
48 // Get image dimensions
49 int width, height;
50 if (webcam_get_dimensions(global_webcam_ctx, &width, &height) == ASCIICHAT_OK) {
51 log_info("Webcam opened successfully! Resolution: %dx%d", width, height);
52 } else {
53 SET_ERRNO(ERROR_WEBCAM, "Webcam opened but failed to get dimensions");
54 }
55
56 return result;
57}
58
59image_t *webcam_read(void) {
60 // Check if test pattern mode is enabled
61 if (GET_OPTION(test_pattern)) {
62 // Allocate cached frame once (like FFmpeg's current_image)
63 // Reuse same buffer for each call to avoid repeated allocations
64 if (!cached_webcam_frame) {
65 cached_webcam_frame = image_new(320, 240);
66 if (!cached_webcam_frame) {
67 SET_ERRNO(ERROR_MEMORY, "Failed to allocate test pattern frame");
68 return NULL;
69 }
70 }
71
72 // Generate animated test pattern each frame
73 // Animation is based on frame counter, respects FPS setting
74 unsigned int animation_phase = frame_counter / 2; // Slow down animation
75 frame_counter++;
76
77 for (int y = 0; y < cached_webcam_frame->h; y++) {
78 for (int x = 0; x < cached_webcam_frame->w; x++) {
79 rgb_pixel_t *pixel = &cached_webcam_frame->pixels[y * cached_webcam_frame->w + x];
80
81 // Animated color bars that shift based on frame counter
82 int animated_x = (x + animation_phase) % cached_webcam_frame->w;
83 int grid_x = animated_x / 40;
84
85 // Base pattern: color bars that animate horizontally
86 switch (grid_x % 3) {
87 case 0: // Red
88 pixel->r = 255;
89 pixel->g = 0;
90 pixel->b = 0;
91 break;
92 case 1: // Green
93 pixel->r = 0;
94 pixel->g = 255;
95 pixel->b = 0;
96 break;
97 case 2: // Blue
98 default:
99 pixel->r = 0;
100 pixel->g = 0;
101 pixel->b = 255;
102 break;
103 }
104
105 // Add animated grid lines
106 if (animated_x % 40 == 0 || y % 30 == 0) {
107 pixel->r = 0;
108 pixel->g = 0;
109 pixel->b = 0;
110 }
111 }
112 }
113
114 return cached_webcam_frame;
115 }
116
117 if (!global_webcam_ctx) {
118 SET_ERRNO(ERROR_WEBCAM, "Webcam not initialized - global_webcam_ctx is NULL");
119 return NULL;
120 }
121
122 image_t *frame = webcam_read_context(global_webcam_ctx);
123
124 if (!frame) {
125 return NULL;
126 }
127
128 return frame;
129}
130
131void webcam_destroy(void) {
132 // Free cached webcam frame if it was allocated (from test pattern or real webcam)
133 if (cached_webcam_frame) {
134 image_destroy(cached_webcam_frame);
135 cached_webcam_frame = NULL;
136 }
137
138 if (GET_OPTION(test_pattern)) {
139 log_debug("Test pattern mode - webcam context cleanup skipped");
140 return;
141 }
142
143 if (global_webcam_ctx) {
144 webcam_cleanup_context(global_webcam_ctx);
145 global_webcam_ctx = NULL;
146 // log_info("Webcam resources released");
147 } else {
148 log_dev("Webcam was not opened, nothing to release");
149 }
150}
151
152void webcam_flush(void) {
153 if (GET_OPTION(test_pattern)) {
154 return; // Test pattern doesn't need flushing
155 }
156
157 if (global_webcam_ctx) {
158 webcam_flush_context(global_webcam_ctx);
159 }
160}
161
163 // Platform-specific error messages and troubleshooting help
164#ifdef __linux__
165 safe_fprintf(stderr, "\n");
166
167 if (error_code == ERROR_WEBCAM) {
168 safe_fprintf(stderr, "Webcam initialization failed on Linux.\n\n");
169 safe_fprintf(stderr, "Common solutions:\n");
170 safe_fprintf(stderr, " 1. Check if a camera is connected:\n");
171 safe_fprintf(stderr, " ls /dev/video*\n\n");
172 safe_fprintf(stderr, " 2. If no camera is available, use test pattern mode:\n");
173 safe_fprintf(stderr, " ascii-chat client --test-pattern\n\n");
174 safe_fprintf(stderr, " 3. Install V4L2 drivers if needed:\n");
175 safe_fprintf(stderr, " sudo apt-get install v4l-utils\n");
176 } else if (error_code == ERROR_WEBCAM_PERMISSION) {
177 safe_fprintf(stderr, "Camera permission denied.\n\n");
178 safe_fprintf(stderr, "Fix permissions with:\n");
179 safe_fprintf(stderr, " sudo usermod -a -G video $USER\n");
180 safe_fprintf(stderr, "Then log out and log back in for changes to take effect.\n");
181 } else if (error_code == ERROR_WEBCAM_IN_USE) {
182 safe_fprintf(stderr, "Camera is already in use by another application.\n\n");
183 safe_fprintf(stderr, "Try closing other camera apps or use test pattern mode:\n");
184 safe_fprintf(stderr, " ascii-chat client --test-pattern\n");
185 } else {
186 safe_fprintf(stderr, "Webcam error on Linux.\n\n");
187 safe_fprintf(stderr, "General troubleshooting:\n");
188 safe_fprintf(stderr, "* Check camera: ls /dev/video*\n");
189 safe_fprintf(stderr, "* Check permissions: groups | grep video\n");
190 safe_fprintf(stderr, "* Use test pattern: ascii-chat client --test-pattern\n");
191 }
192 (void)fflush(stderr);
193#elif defined(__APPLE__)
194 (void)error_code;
195 safe_fprintf(stderr, "\n");
196 safe_fprintf(stderr, "On macOS, you may need to grant camera permissions:\n");
197 safe_fprintf(stderr,
198 "* Say \"yes\" to the popup about system camera access that you see when running this program for the "
199 "first time.\n");
201 stderr, "* If you said \"no\" to the popup, go to System Preferences > Security & Privacy > Privacy > Camera.\n");
202 safe_fprintf(stderr,
203 " Now flip the switch next to your terminal application in that privacy list to allow ascii-chat to "
204 "access your camera.\n");
205 safe_fprintf(stderr, " Then just run this program again.\n");
206 (void)fflush(stderr);
207#elif defined(_WIN32)
208 if (error_code == ERROR_WEBCAM_IN_USE) {
209 // Device is in use by another application - this is a fatal error on Windows
210 safe_fprintf(stderr, "\n");
211 safe_fprintf(stderr, "Webcam is already in use by another application.\n");
212 safe_fprintf(stderr, "Windows allows only one application to access the webcam at a time.\n");
213 safe_fprintf(stderr, "\n");
214 safe_fprintf(stderr, "To use ascii-chat with multiple clients, try these alternatives:\n");
215 safe_fprintf(stderr, " --test-pattern Generate a colorful test pattern instead of using webcam\n");
216 safe_fprintf(stderr, " --file VIDEO.mp4 Use a video file as input (to be implemented)\n");
217 safe_fprintf(stderr, "\n");
218 safe_fprintf(stderr, "Example: ascii-chat client --test-pattern\n");
219 (void)fflush(stderr);
220 } else {
221 // Other webcam errors - general failure
222 safe_fprintf(stderr, "\n");
223 safe_fprintf(stderr, "On Windows, this might be because:\n");
224 safe_fprintf(stderr, "* Camera permissions are not granted\n");
225 safe_fprintf(stderr, "* Camera driver issues\n");
226 safe_fprintf(stderr, "* No webcam device found\n");
227 (void)fflush(stderr);
228 }
229#else
230 // Unknown platform
231 (void)error_code;
232 safe_fprintf(stderr, "\nWebcam initialization failed on unsupported platform.\n");
233 (void)fflush(stderr);
234#endif
235}
236
237// Fallback implementations for unsupported platforms
238#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
239asciichat_error_t webcam_init_context(webcam_context_t **ctx, unsigned short int device_index) {
240 (void)ctx;
241 (void)device_index;
242 SET_ERRNO(ERROR_WEBCAM, "Webcam platform not supported on this system");
243 return ERROR_WEBCAM;
244}
245
246void webcam_cleanup_context(webcam_context_t *ctx) {
247 (void)ctx;
248 log_warn("Webcam cleanup called on unsupported platform");
249}
250
251void webcam_flush_context(webcam_context_t *ctx) {
252 (void)ctx;
253 // No-op on unsupported platforms
254}
255
256image_t *webcam_read_context(webcam_context_t *ctx) {
257 (void)ctx;
258 SET_ERRNO(ERROR_WEBCAM, "Webcam read not supported on this platform");
259 return NULL;
260}
261
262asciichat_error_t webcam_get_dimensions(webcam_context_t *ctx, int *width, int *height) {
263 (void)ctx;
264 (void)width;
265 (void)height;
266 return SET_ERRNO(ERROR_WEBCAM, "Webcam get dimensions not supported on this platform");
267}
268
269asciichat_error_t webcam_list_devices(webcam_device_info_t **out_devices, unsigned int *out_count) {
270 if (out_devices)
271 *out_devices = NULL;
272 if (out_count)
273 *out_count = 0;
274 return SET_ERRNO(ERROR_WEBCAM, "Webcam device enumeration not supported on this platform");
275}
276
277void webcam_free_device_list(webcam_device_info_t *devices) {
278 (void)devices;
279 // No-op on unsupported platforms
280}
281#endif
asciichat_error_t error_code
int safe_fprintf(FILE *stream, const char *format,...)
Safe formatted output to file stream.
Definition system.c:480
void image_destroy(image_t *p)
Definition video/image.c:85
image_t * image_new(size_t width, size_t height)
Definition video/image.c:36
asciichat_error_t webcam_init_context(webcam_context_t **ctx, unsigned short int device_index)
void webcam_free_device_list(webcam_device_info_t *devices)
void webcam_destroy(void)
asciichat_error_t webcam_init(unsigned short int webcam_index)
void webcam_flush_context(webcam_context_t *ctx)
image_t * webcam_read(void)
void webcam_print_init_error_help(asciichat_error_t error_code)
asciichat_error_t webcam_list_devices(webcam_device_info_t **out_devices, unsigned int *out_count)
void webcam_flush(void)
asciichat_error_t webcam_get_dimensions(webcam_context_t *ctx, int *width, int *height)
image_t * webcam_read_context(webcam_context_t *ctx)
void webcam_cleanup_context(webcam_context_t *ctx)