UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
screen_x11.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2023 CESNET, z.s.p.o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
42#ifdef HAVE_CONFIG_H
43#include "config.h"
44#include "config_unix.h"
45#include "config_win32.h"
46#endif /* HAVE_CONFIG_H */
47
48#include "debug.h"
49#include "host.h"
50#include "lib_common.h"
51#include "utils/macros.h"
52#include "utils/misc.h" // get_cpu_core_count
53#include "utils/parallel_conv.h"
54#include "video.h"
55#include "video_capture.h"
56
57#include "tv.h"
58
59#include "audio/types.h"
60
61#include <stdio.h>
62#include <stdlib.h>
63#include <strings.h>
64
65#include <X11/Xlib.h>
66#ifdef HAVE_XFIXES
67#include <X11/extensions/Xfixes.h>
68#endif // HAVE_XFIXES
69#include <X11/Xutil.h>
70
71#define MOD_NAME "[screen capture] "
72#define QUEUE_SIZE_MAX 3
73
74/* prototypes of functions defined in this module */
75static void show_help(void);
76static void *grab_thread(void *args);
77
78static void show_help()
79{
80 printf("Screen capture\n");
81 printf("Usage\n");
82 printf("\t-t screen[:fps=<fps>][:display=<d>][:geometry=WxH[+x[+y]]|:size=WxH]\n");
83 printf("\t\t<fps> - preferred grabbing fps (otherwise unlimited)\n");
84 printf("\t\tdisplay - display to capture (including the colon!)\n");
85 printf("\t\tgeomoetry | size - viewport to use (both option mean the same - size is just a convenient name)\n");
86}
87
88struct grabbed_data;
89
91 XImage *data;
93};
94
97 struct tile *tile;
98 Display *dpy;
99 Window root;
100 int x, y, width, height;
101
102 struct grabbed_data * volatile head, * volatile tail;
103 volatile int queue_len;
104
105 pthread_mutex_t lock;
106 pthread_cond_t worker_cv;
107 volatile bool worker_waiting;
108 pthread_cond_t boss_cv;
109 volatile bool boss_waiting;
110
111 volatile bool should_exit_worker;
112
113 pthread_t worker_id;
114
115 struct timeval prev_time;
116
117 double fps;
118
122};
123
124static bool initialize(struct vidcap_screen_x11_state *s) {
125 s->frame = vf_alloc(1);
126 s->tile = vf_get_tile(s->frame, 0);
127
128 XWindowAttributes wa;
129
130 s->dpy = XOpenDisplay(s->req_display);
131 if (!s->dpy) {
132 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot open display %s\n", IF_NOT_NULL_ELSE(s->req_display, IF_NOT_NULL_ELSE(getenv("DISPLAY"), "($DISPLAY undefined!)")));
133 return false;
134 }
135
136 s->root = DefaultRootWindow(s->dpy);
137 if (!s->dpy) {
138 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot get root window!\n");
139 return false;
140 }
141
142 XGetWindowAttributes(s->dpy, DefaultRootWindow(s->dpy), &wa);
143 s->tile->width = IF_NOT_NULL_ELSE(MIN(s->width, wa.width), wa.width);
144 s->tile->height = IF_NOT_NULL_ELSE(MIN(s->height, wa.height), wa.height);
145 s->x = MIN(s->x, wa.width - (int) s->tile->width);
146 s->y = MIN(s->y, wa.height - (int) s->tile->height);
147
148 pthread_mutex_init(&s->lock, NULL);
149 pthread_cond_init(&s->boss_cv, NULL);
150 pthread_cond_init(&s->worker_cv, NULL);
151
152 s->boss_waiting = false;
153 s->worker_waiting = false;
154
155 s->head = s->tail = NULL;
156 s->queue_len = 0;
157
158 s->should_exit_worker = false;
159
160 s->frame->color_spec = RGB;
161 if(s->fps > 0.0) {
162 s->frame->fps = s->fps;
163 } else {
164 s->frame->fps = 30;
165 }
168
169 s->tile->data = (char *) malloc(s->tile->data_len);
170
171 pthread_create(&s->worker_id, NULL, grab_thread, s);
172
173 return true;
174}
175
176
177static void *grab_thread(void *args)
178{
179 struct vidcap_screen_x11_state *s = args;
180
181 while(!s->should_exit_worker) {
182 struct grabbed_data *new_item = malloc(sizeof(struct grabbed_data));
183
184#ifdef HAVE_XFIXES
185 XFixesCursorImage *cursor =
186 XFixesGetCursorImage (s->dpy);
187#endif // HAVE_XFIXES
188 new_item->data = XGetImage(s->dpy,s->root, s->x, s->y, s->tile->width, s->tile->height, AllPlanes, ZPixmap);
189 assert(new_item->data != NULL);
190
191#ifdef HAVE_XFIXES
192 if (cursor) {
193 uint32_t *image_data = (uint32_t *)(void *) new_item->data->data;
194 for(int x = 0; x < cursor->width; ++x) {
195 for(int y = 0; y < cursor->height; ++y) {
196 if(cursor->x + x >= (int) s->tile->width ||
197 cursor->y + y >= (int) s->tile->height)
198 continue;
199 //image_data[x + y * s->tile->width] = cursor->pixels[x + y * cursor->width];
200 uint_fast32_t cursor_pix = cursor->pixels[x + y * cursor->width];
202 int alpha = cursor_pix >> 24 & 0xff;
203 int r1 = cursor_pix >> 16 & 0xff,
204 g1 = cursor_pix >> 8 & 0xff,
205 b1 = cursor_pix >> 0 & 0xff;
206 uint_fast32_t image_pix = image_data[cursor->x + x + (cursor->y + y) * s->tile->width];
207 int r2 = image_pix >> 16 & 0xff,
208 g2 = image_pix >> 8 & 0xff,
209 b2 = image_pix >> 0 & 0xff;
210 float scale_image = (float) (255 - alpha)/ 255;
211 float scale_cursor = (float) alpha / 255;
212
213 image_data[cursor->x + x + (cursor->y + y) * s->tile->width] =
214 ((int) (r1 * scale_cursor + r2 * scale_image) & 0xff) << 16 |
215 ((int) (g1 * scale_cursor + g2 * scale_image) & 0xff) << 8 |
216 ((int) (b1 * scale_cursor + b2 * scale_image) & 0xff) << 0;
217 }
218 }
219
220 XFree(cursor);
221 }
222#endif // HAVE_XFIXES
223
224 new_item->next = NULL;
225
226 pthread_mutex_lock(&s->lock);
227 {
228 while(s->queue_len > QUEUE_SIZE_MAX && !s->should_exit_worker) {
229 s->worker_waiting = true;
230 pthread_cond_wait(&s->worker_cv, &s->lock);
231 s->worker_waiting = false;
232 }
233
234 if(s->head) {
235 s->tail->next = new_item;
236 s->tail = new_item;
237 } else {
238 s->head = s->tail = new_item;
239 }
240 s->queue_len += 1;
241
242 if(s->boss_waiting)
243 pthread_cond_signal(&s->boss_cv);
244
245 }
246 pthread_mutex_unlock(&s->lock);
247 }
248
249 return NULL;
250}
251
252static void vidcap_screen_x11_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
253{
254 *deleter = free;
255 *count = 1;
256 *available_devices = calloc(*count, sizeof(struct device_info));
257 // (*available_devices)[0].dev can be "" since screen cap. doesn't require parameters
258 snprintf((*available_devices)[0].name, sizeof (*available_devices)[0].name, "Screen capture X11");
259
260 int framerates[] = {24, 30, 60};
261
262 snprintf((*available_devices)[0].modes[0].name,
263 sizeof (*available_devices)[0].modes[0].name,
264 "Unlimited fps");
265 snprintf((*available_devices)[0].modes[0].id,
266 sizeof (*available_devices)[0].modes[0].id,
267 "{\"fps\":\"\"}");
268
269 for(unsigned i = 0; i < sizeof(framerates) / sizeof(framerates[0]); i++){
270 snprintf((*available_devices)[0].modes[i + 1].name,
271 sizeof (*available_devices)[0].modes[0].name,
272 "%d fps",
273 framerates[i]);
274 snprintf((*available_devices)[0].modes[i + 1].id,
275 sizeof (*available_devices)[0].modes[0].id,
276 "{\"fps\":\"%d\"}",
277 framerates[i]);
278 }
279}
280
281static _Bool parse_fmt(struct vidcap_screen_x11_state *s, char *fmt) {
282 char *tok, *save_ptr;
283 while ((tok = strtok_r(fmt, ":", &save_ptr)) != NULL) {
284 fmt = NULL;
285 if (strstr(tok, "fps=") == tok) {
286 s->fps = atoi(strchr(tok, '=') + 1);
287 } else if (strstr(tok, "display=") == tok) {
288 char *val = strchr(tok, '=') + 1;
289 s->req_display = strdup(val);
290 tok = strtok_r(NULL, ":", &save_ptr);
291 if (!tok) {
292 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Display string doesn't contain the colon\n");
293 return 0;
294 }
295 s->req_display = realloc(s->req_display, strlen(s->req_display) + 1 + strlen(tok) + 1);
296 strcat(s->req_display, ":");
297 strcat(s->req_display, tok);
298 } else if (strstr(tok, "geometry=") == tok || strstr(tok, "size=") == tok) {
299 char *val = strchr(tok, '=') + 1;
300 s->width = atoi(val);
301 val = strchr(val, 'x') + 1;
302 s->height = atoi(val);
303 if (s->width * s->height <= 0) {
304 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong dimensions %dx%d!\n", s->width, s->height);
305 return 0;
306 }
307 if (strchr(val, '+')) {
308 val = strchr(val, '+') + 1;
309 s->x = atoi(val);
310 }
311 if (strchr(val, '+')) {
312 val = strchr(val, '+') + 1;
313 s->y = atoi(val);
314 }
315 } else {
316 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown option \"%s\"!\n", tok);
317 return 0;
318 }
319 }
320 return 1;
321}
322
324{
325 printf("vidcap_screen_init\n");
326
329 }
330
331 const char *fmt = vidcap_params_get_fmt(params);
332 if (strcmp(fmt, "help") == 0) {
333 show_help();
334 return VIDCAP_INIT_NOERR;
335 }
336
337 struct vidcap_screen_x11_state *s = calloc(1, sizeof(struct vidcap_screen_x11_state));
338 if(s == NULL) {
339 printf("Unable to allocate screen capture state\n");
340 return VIDCAP_INIT_FAIL;
341 }
343
344#ifndef HAVE_XFIXES
345 fprintf(stderr, "[Screen capture] Compiled without XFixes library, cursor won't be shown!\n");
346#endif // ! HAVE_XFIXES
347
348 char *fmt_cpy = strdup(fmt);
349 _Bool ret = parse_fmt(s, fmt_cpy);
350 free(fmt_cpy);
351 if (!ret) {
352 free(s);
353 return VIDCAP_INIT_FAIL;
354 }
355
356 *state = s;
357 return VIDCAP_INIT_OK;
358}
359
361{
363
364 assert(s != NULL);
365 pthread_mutex_lock(&s->lock);
366 if(s->boss_waiting) {
367 pthread_cond_signal(&s->boss_cv);
368 }
369
370 s->should_exit_worker = true;
371 if(s->worker_waiting) {
372 pthread_cond_signal(&s->worker_cv);
373 }
374
375 pthread_mutex_unlock(&s->lock);
376
377 if(s->worker_id) {
378 pthread_join(s->worker_id, NULL);
379 }
380}
381
383{
385
386 assert(s != NULL);
387
389
390 pthread_mutex_lock(&s->lock);
391 {
392 while(s->queue_len > 0) {
393 struct grabbed_data *item = s->head;
394 s->head = s->head->next;
395 XDestroyImage(item->data);
396 free(item);
397 s->queue_len -= 1;
398 }
399 }
400 pthread_mutex_unlock(&s->lock);
401
402 if(s->tile)
403 free(s->tile->data);
404
405 vf_free(s->frame);
406 free(s->req_display);
407 free(s);
408}
409
411{
413
414 if (!s->initialized) {
415 s->initialized = initialize(s);
416 if (!s->initialized) {
417 fprintf(stderr, "Cannot capture screen - unable to initialize!\n");
418 exit_uv(1);
419 return NULL;
420 }
421 }
422
423 *audio = NULL;
424
425 struct grabbed_data *item = NULL;
426
427 pthread_mutex_lock(&s->lock);
428 {
429 while(s->queue_len == 0) {
430 s->boss_waiting = true;
431 pthread_cond_wait(&s->boss_cv, &s->lock);
432 s->boss_waiting = false;
433 }
434
435 item = s->head;
436 s->head = s->head->next;
437 s->queue_len -= 1;
438
439 if(s->worker_waiting) {
440 pthread_cond_signal(&s->worker_cv);
441 }
442 }
443 pthread_mutex_unlock(&s->lock);
444
445 /*
446 * The more correct way is to use X pixel accessor (XGetPixel) as in previous version
447 * Unfortunatelly, this approach is damn slow. Current approach might be incorrect in
448 * some configurations, but seems to work currently. To be corrected if there is an
449 * opposite case.
450 */
453 &item->data->data[0],
456
457 XDestroyImage(item->data);
458 free(item);
459
460 if(s->fps > 0.0) {
461 struct timeval cur_time;
462
463 gettimeofday(&cur_time, NULL);
464 while(tv_diff_usec(cur_time, s->prev_time) < 1000000.0 / s->frame->fps) {
465 gettimeofday(&cur_time, NULL);
466 }
467 s->prev_time = cur_time;
468 }
469
470 return s->frame;
471}
472
480
482
void ** state
int void * val
const char * fmt
struct audio_frame ** audio
int void(** deleter)(void *))
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
This file contains definition of some common audio types.
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
#define VIDCAP_INIT_AUDIO_NOT_SUPPORTED
card does not support audio
int count
Definition hd-rum.c:44
void exit_uv(int status)
Definition host.cpp:625
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
#define MIN(a, b)
Definition macros.h:63
#define IF_NOT_NULL_ELSE(val, default_val)
Definition macros.h:51
int get_cpu_core_count(void)
Definition misc.cpp:218
void parallel_pix_conv(int height, char *out, int out_linesize, const char *in, int in_linesize, decoder_t decode, int threads)
decoder_func_t vc_copylineBGRAtoRGB
static _Bool parse_fmt(struct vidcap_screen_x11_state *s, char *fmt)
Definition screen_x11.c:281
static int vidcap_screen_x11_init(struct vidcap_params *params, void **state)
Definition screen_x11.c:323
static const struct video_capture_info vidcap_screen_x11_info
Definition screen_x11.c:473
static void vidcap_screen_x11_finish(void *state)
Definition screen_x11.c:360
static void vidcap_screen_x11_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition screen_x11.c:252
static void * grab_thread(void *args)
Definition screen_x11.c:177
#define MOD_NAME
Definition screen_x11.c:71
static void vidcap_screen_x11_done(void *state)
Definition screen_x11.c:382
static bool initialize(struct vidcap_screen_x11_state *s)
Definition screen_x11.c:124
#define QUEUE_SIZE_MAX
Definition screen_x11.c:72
static struct video_frame * vidcap_screen_x11_grab(void *state, struct audio_frame **audio)
Definition screen_x11.c:410
static void show_help(void)
Definition screen_x11.c:78
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
struct grabbed_data * next
Definition screen_x11.c:92
XImage * data
Definition screen_x11.c:91
Definition hd-rum.c:27
Struct tile is an area of video_frame.
Definition types.h:279
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int height
Definition types.h:281
pthread_cond_t worker_cv
Definition screen_x11.c:106
struct grabbed_data *volatile head
Definition screen_x11.c:102
struct timeval prev_time
Definition screen_x11.c:115
volatile bool should_exit_worker
Definition screen_x11.c:111
struct video_frame * frame
Definition screen_x11.c:96
pthread_cond_t boss_cv
Definition screen_x11.c:108
volatile bool boss_waiting
Definition screen_x11.c:109
volatile bool worker_waiting
Definition screen_x11.c:107
pthread_mutex_t lock
Definition screen_x11.c:105
struct grabbed_data *volatile *volatile tail
Definition screen_x11.c:102
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
enum interlacing_t interlacing
Definition types.h:307
double fps
Definition types.h:308
codec_t color_spec
Definition types.h:306
uint32_t tv_diff_usec(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:109
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
This is an umbrella header for video functions.
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_ANY
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].