]> git.sesse.net Git - vlc/blob - modules/access/screen/xcb.c
XCB screen & window capture
[vlc] / modules / access / screen / xcb.c
1 /**
2  * @file xcb.c
3  * @brief X11 C Bindings screen capture demux module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2009 Rémi Denis-Courmont
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2.0
11  * of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26 #include <stdarg.h>
27 #include <xcb/xcb.h>
28 #include <vlc_common.h>
29 #include <vlc_demux.h>
30 #include <vlc_plugin.h>
31
32 #define CACHING_TEXT N_("Caching value in ms")
33 #define CACHING_LONGTEXT N_( \
34     "Caching value for screen capture. " \
35     "This value should be set in milliseconds.")
36
37 #define FPS_TEXT N_("Frame rate")
38 #define FPS_LONGTEXT N_( \
39     "How many times the screen content should be refreshed per second.")
40
41 #define LEFT_TEXT N_("Region left column")
42 #define LEFT_LONGTEXT N_( \
43     "Abscissa of the capture reion in pixels.")
44
45 #define TOP_TEXT N_("Region top row")
46 #define TOP_LONGTEXT N_( \
47     "Ordinate of the capture region in pixels.")
48
49 #define WIDTH_TEXT N_("Capture region width")
50 #define WIDTH_LONGTEXT N_( \
51     "Pixel width of the capture region, or 0 for full width")
52
53 #define HEIGHT_TEXT N_("Capture region height")
54 #define HEIGHT_LONGTEXT N_( \
55     "Pixel height of the capture region, or 0 for full height")
56
57 static int  Open (vlc_object_t *);
58 static void Close (vlc_object_t *);
59
60 /*
61  * Module descriptor
62  */
63 vlc_module_begin ()
64     set_shortname (N_("Screen"))
65     set_description (N_("Screen capture (with X11/XCB)"))
66     set_category (CAT_INPUT)
67     set_subcategory (SUBCAT_INPUT_ACCESS)
68     set_capability ("access_demux", 0)
69     set_callbacks (Open, Close)
70
71     add_integer ("screen-caching", DEFAULT_PTS_DELAY * 1000 / CLOCK_FREQ,
72                  NULL, CACHING_TEXT, CACHING_LONGTEXT, true)
73     add_float ("screen-fps", 2.0, NULL, FPS_TEXT, FPS_LONGTEXT, true)
74     add_integer ("screen-left", 0, NULL, LEFT_TEXT, LEFT_LONGTEXT, true)
75         change_integer_range (-32768, 32767)
76         change_safe ()
77     add_integer ("screen-top", 0, NULL, LEFT_TEXT, LEFT_LONGTEXT, true)
78         change_integer_range (-32768, 32767)
79         change_safe ()
80     add_integer ("screen-width", 0, NULL, LEFT_TEXT, LEFT_LONGTEXT, true)
81         change_integer_range (0, 65535)
82         change_safe ()
83     add_integer ("screen-height", 0, NULL, LEFT_TEXT, LEFT_LONGTEXT, true)
84         change_integer_range (0, 65535)
85         change_safe ()
86
87     add_shortcut ("screen")
88     add_shortcut ("window")
89 vlc_module_end ()
90
91 /*
92  * Local prototypes
93  */
94 static int Demux (demux_t *);
95 static int Control (demux_t *, int, va_list);
96
97 struct demux_sys_t
98 {
99     xcb_connection_t *conn;
100     es_out_id_t      *es;
101     mtime_t           pts, interval;
102     xcb_window_t      window;
103     int16_t           x, y;
104     uint16_t          w, h;
105 };
106
107 /**
108  * Probes and initializes.
109  */
110 static int Open (vlc_object_t *obj)
111 {
112     demux_t *demux = (demux_t *)obj;
113     demux_sys_t *p_sys = malloc (sizeof (*p_sys));
114
115     if (p_sys == NULL)
116         return VLC_ENOMEM;
117
118     /* Connect to X server */
119     char *display = var_CreateGetNonEmptyString (obj, "x11-display");
120     int snum;
121     xcb_connection_t *conn = xcb_connect (display, &snum);
122     free (display);
123     if (xcb_connection_has_error (conn))
124     {
125         free (p_sys);
126         return VLC_EGENERIC;
127     }
128     p_sys->conn = conn;
129
130     /* Find configured screen */
131     const xcb_setup_t *setup = xcb_get_setup (conn);
132     xcb_screen_t *scr = NULL;
133     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
134          i.rem > 0; xcb_screen_next (&i))
135     {
136         if (snum == 0)
137         {
138             scr = i.data;
139             break;
140         }
141         snum--;
142     }
143     if (scr == NULL)
144     {
145         msg_Err (obj, "bad X11 screen number");
146         goto error;
147     }
148
149     /* Determine capture window */
150     if (!strcmp (demux->psz_access, "screen"))
151         p_sys->window = scr->root;
152     else
153     if (!strcmp (demux->psz_access, "window"))
154     {
155         char *end;
156         unsigned long ul = strtoul (demux->psz_path, &end, 0);
157         if (*end || ul > 0xffffffff)
158         {
159             msg_Err (obj, "bad X11 window %s", demux->psz_path);
160             goto error;
161         }
162         p_sys->window = ul;
163     }
164     else
165         goto error;
166
167     /* Window properties */
168     xcb_get_geometry_reply_t *geo;
169     geo = xcb_get_geometry_reply (conn,
170                                   xcb_get_geometry (conn, p_sys->window),
171                                   NULL);
172     if (geo == NULL)
173     {
174         msg_Err (obj, "bad X11 window 0x%08"PRIx32, p_sys->window);
175         goto error;
176     }
177
178     p_sys->x = var_CreateGetInteger (obj, "screen-left");
179     p_sys->y = var_CreateGetInteger (obj, "screen-top");
180     p_sys->w = var_CreateGetInteger (obj, "screen-width");
181     if (p_sys->w == 0)
182         p_sys->w = geo->width;
183     p_sys->h = var_CreateGetInteger (obj, "screen-height");
184     if (p_sys->h == 0)
185         p_sys->h = geo->height;
186
187     uint32_t chroma = 0;
188     uint8_t bpp = geo->depth;
189     for (const xcb_format_t *fmt = xcb_setup_pixmap_formats (setup),
190              *end = fmt + xcb_setup_pixmap_formats_length (setup);
191          fmt < end; fmt++)
192     {
193         if (fmt->depth != geo->depth)
194             continue;
195         switch (geo->depth)
196         {
197             case 32:
198                 if (fmt->bits_per_pixel == 32)
199                     chroma = VLC_CODEC_RGBA;
200                 break;
201             case 24:
202                 if (fmt->bits_per_pixel == 32)
203                 {
204                     chroma = VLC_CODEC_RGB32;
205                     bpp = 32;
206                 }
207                 else if (fmt->bits_per_pixel == 24)
208                     chroma = VLC_CODEC_RGB24;
209                 break;
210             case 16:
211                 if (fmt->bits_per_pixel == 16)
212                     chroma = VLC_CODEC_RGB16;
213                 break;
214             case 15:
215                 if (fmt->bits_per_pixel == 16)
216                     chroma = VLC_CODEC_RGB15;
217                 break;
218             case 8: /* XXX: screw grey scale! */
219                 if (fmt->bits_per_pixel == 8)
220                     chroma = VLC_CODEC_RGB8;
221                 break;
222         }
223         if (chroma != 0)
224             break;
225     }
226     free (geo);
227
228     if (!chroma)
229     {
230         msg_Err (obj, "unsupported pixmap formats");
231         goto error;
232     }
233
234     /* Initializes format */
235     float rate = var_CreateGetFloat (obj, "screen-fps");
236     if (!rate)
237         goto error;
238     p_sys->interval = (float)CLOCK_FREQ / rate;
239     if (!p_sys->interval)
240         goto error;
241     var_Create (obj, "screen-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
242
243     es_format_t fmt;
244     es_format_Init (&fmt, VIDEO_ES, chroma);
245     fmt.video.i_chroma = chroma;
246     fmt.video.i_visible_width =
247     fmt.video.i_width = p_sys->w;
248     fmt.video.i_visible_height =
249     fmt.video.i_height = p_sys->h;
250     fmt.video.i_bits_per_pixel = bpp;
251     fmt.video.i_sar_num = fmt.video.i_sar_den = 1;
252     fmt.video.i_frame_rate = 1000 * rate;
253     fmt.video.i_frame_rate_base = 1000;
254     p_sys->es = es_out_Add (demux->out, &fmt);
255     if (p_sys->es == NULL)
256         goto error;
257     p_sys->pts = VLC_TS_INVALID;
258
259     /* Initializes demux */
260     demux->pf_demux   = Demux;
261     demux->pf_control = Control;
262     demux->p_sys      = p_sys;
263     return VLC_SUCCESS;
264
265 error:
266     Close (obj);
267     return VLC_EGENERIC;
268 }
269
270
271 /**
272  * Releases resources
273  */
274 static void Close (vlc_object_t *obj)
275 {
276     demux_t *demux = (demux_t *)obj;
277     demux_sys_t *p_sys = demux->p_sys;
278
279     xcb_disconnect (p_sys->conn);
280     free (p_sys);
281 }
282
283
284 /**
285  * Control callback
286  */
287 static int Control (demux_t *demux, int query, va_list args)
288 {
289     switch (query)
290     {
291         case DEMUX_GET_POSITION:
292         {
293             float *v = va_arg (args, float *);
294             *v = 0.;
295             return VLC_SUCCESS;
296         }
297
298         case DEMUX_GET_LENGTH:
299         case DEMUX_GET_TIME:
300         {
301             int64_t *v = va_arg (args, int64_t *);
302             *v = 0;
303             return VLC_SUCCESS;
304         }
305
306         /* TODO: get title info -> crawl visible windows */
307
308         case DEMUX_GET_PTS_DELAY:
309         {
310             int64_t *v = va_arg (args, int64_t *);
311             *v = var_GetInteger (demux, "screen-caching") * UINT64_C(1000);
312             return VLC_SUCCESS;
313         }
314
315         case DEMUX_CAN_PAUSE:
316         case DEMUX_CAN_CONTROL_PACE:
317         {
318             bool *v = (bool*)va_arg( args, bool * );
319             *v = true;
320             return VLC_SUCCESS;
321         }
322
323         case DEMUX_SET_PAUSE_STATE:
324             es_out_Control (demux->out, ES_OUT_RESET_PCR);
325             return VLC_SUCCESS;
326
327         case DEMUX_CAN_CONTROL_RATE:
328         case DEMUX_CAN_SEEK:
329         {
330             bool *v = (bool*)va_arg( args, bool * );
331             *v = false;
332             return VLC_SUCCESS;
333         }
334     }
335
336     return VLC_EGENERIC;
337 }
338
339
340 /**
341  * Processing callback
342  */
343 static int Demux (demux_t *demux)
344 {
345     demux_sys_t *p_sys = demux->p_sys;
346     xcb_get_image_reply_t *img;
347
348     /* Capture screen */
349     img = xcb_get_image_reply (p_sys->conn,
350         xcb_get_image (p_sys->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, p_sys->window,
351                        p_sys->x, p_sys->y, p_sys->w, p_sys->h, ~0), NULL);
352     if (img == NULL)
353     {
354         msg_Warn (demux, "no image - halting capture");
355         return 0;
356     }
357
358     /* Send block - zero copy */
359     mtime_t now = mdate ();
360     block_t *block = block_heap_Alloc (img, xcb_get_image_data (img),
361                                        xcb_get_image_data_length (img));
362     if (block == NULL)
363         return 0;
364     block->i_pts = block->i_dts = now;
365
366     es_out_Control (demux->out, ES_OUT_SET_PCR, now);
367     es_out_Send (demux->out, p_sys->es, block);
368     return 1;
369 }