XCB screen: fix default height/width when x/y specified
[vlc.git] / 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     demux->p_sys      = p_sys;
118
119     /* Connect to X server */
120     char *display = var_CreateGetNonEmptyString (obj, "x11-display");
121     int snum;
122     xcb_connection_t *conn = xcb_connect (display, &snum);
123     free (display);
124     if (xcb_connection_has_error (conn))
125     {
126         free (p_sys);
127         return VLC_EGENERIC;
128     }
129     p_sys->conn = conn;
130
131     /* Find configured screen */
132     const xcb_setup_t *setup = xcb_get_setup (conn);
133     xcb_screen_t *scr = NULL;
134     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
135          i.rem > 0; xcb_screen_next (&i))
136     {
137         if (snum == 0)
138         {
139             scr = i.data;
140             break;
141         }
142         snum--;
143     }
144     if (scr == NULL)
145     {
146         msg_Err (obj, "bad X11 screen number");
147         goto error;
148     }
149
150     /* Determine capture window */
151     if (!strcmp (demux->psz_access, "screen"))
152         p_sys->window = scr->root;
153     else
154     if (!strcmp (demux->psz_access, "window"))
155     {
156         char *end;
157         unsigned long ul = strtoul (demux->psz_path, &end, 0);
158         if (*end || ul > 0xffffffff)
159         {
160             msg_Err (obj, "bad X11 window %s", demux->psz_path);
161             goto error;
162         }
163         p_sys->window = ul;
164     }
165     else
166         goto error;
167
168     /* Window properties */
169     xcb_get_geometry_reply_t *geo;
170     geo = xcb_get_geometry_reply (conn,
171                                   xcb_get_geometry (conn, p_sys->window),
172                                   NULL);
173     if (geo == NULL)
174     {
175         msg_Err (obj, "bad X11 window 0x%08"PRIx32, p_sys->window);
176         goto error;
177     }
178
179     p_sys->x = var_CreateGetInteger (obj, "screen-left");
180     p_sys->y = var_CreateGetInteger (obj, "screen-top");
181     p_sys->w = var_CreateGetInteger (obj, "screen-width");
182     if (p_sys->w == 0)
183         p_sys->w = geo->width - p_sys->x;
184     p_sys->h = var_CreateGetInteger (obj, "screen-height");
185     if (p_sys->h == 0)
186         p_sys->h = geo->height - p_sys->y;
187
188     uint32_t chroma = 0;
189     uint8_t bpp = geo->depth;
190     for (const xcb_format_t *fmt = xcb_setup_pixmap_formats (setup),
191              *end = fmt + xcb_setup_pixmap_formats_length (setup);
192          fmt < end; fmt++)
193     {
194         if (fmt->depth != geo->depth)
195             continue;
196         switch (geo->depth)
197         {
198             case 32:
199                 if (fmt->bits_per_pixel == 32)
200                     chroma = VLC_CODEC_RGBA;
201                 break;
202             case 24:
203                 if (fmt->bits_per_pixel == 32)
204                 {
205                     chroma = VLC_CODEC_RGB32;
206                     bpp = 32;
207                 }
208                 else if (fmt->bits_per_pixel == 24)
209                     chroma = VLC_CODEC_RGB24;
210                 break;
211             case 16:
212                 if (fmt->bits_per_pixel == 16)
213                     chroma = VLC_CODEC_RGB16;
214                 break;
215             case 15:
216                 if (fmt->bits_per_pixel == 16)
217                     chroma = VLC_CODEC_RGB15;
218                 break;
219             case 8: /* XXX: screw grey scale! */
220                 if (fmt->bits_per_pixel == 8)
221                     chroma = VLC_CODEC_RGB8;
222                 break;
223         }
224         if (chroma != 0)
225             break;
226     }
227     free (geo);
228
229     if (!chroma)
230     {
231         msg_Err (obj, "unsupported pixmap formats");
232         goto error;
233     }
234
235     /* Initializes format */
236     float rate = var_CreateGetFloat (obj, "screen-fps");
237     if (!rate)
238         goto error;
239     p_sys->interval = (float)CLOCK_FREQ / rate;
240     if (!p_sys->interval)
241         goto error;
242     var_Create (obj, "screen-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
243
244     es_format_t fmt;
245     es_format_Init (&fmt, VIDEO_ES, chroma);
246     fmt.video.i_chroma = chroma;
247     fmt.video.i_visible_width =
248     fmt.video.i_width = p_sys->w;
249     fmt.video.i_visible_height =
250     fmt.video.i_height = p_sys->h;
251     fmt.video.i_bits_per_pixel = bpp;
252     fmt.video.i_sar_num = fmt.video.i_sar_den = 1;
253     fmt.video.i_frame_rate = 1000 * rate;
254     fmt.video.i_frame_rate_base = 1000;
255     p_sys->es = es_out_Add (demux->out, &fmt);
256     if (p_sys->es == NULL)
257         goto error;
258     p_sys->pts = VLC_TS_INVALID;
259
260     /* Initializes demux */
261     demux->pf_demux   = Demux;
262     demux->pf_control = Control;
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     demux_sys_t *p_sys = demux->p_sys;
290
291     switch (query)
292     {
293         case DEMUX_GET_POSITION:
294         {
295             float *v = va_arg (args, float *);
296             *v = 0.;
297             return VLC_SUCCESS;
298         }
299
300         case DEMUX_GET_LENGTH:
301         case DEMUX_GET_TIME:
302         {
303             int64_t *v = va_arg (args, int64_t *);
304             *v = 0;
305             return VLC_SUCCESS;
306         }
307
308         /* TODO: get title info -> crawl visible windows */
309
310         case DEMUX_GET_PTS_DELAY:
311         {
312             int64_t *v = va_arg (args, int64_t *);
313             *v = var_GetInteger (demux, "screen-caching") * UINT64_C(1000);
314             return VLC_SUCCESS;
315         }
316
317         case DEMUX_CAN_PAUSE:
318         {
319             bool *v = (bool*)va_arg( args, bool * );
320             *v = true;
321             return VLC_SUCCESS;
322         }
323
324         case DEMUX_SET_PAUSE_STATE:
325         {
326             bool pausing = va_arg (args, int);
327             if (!pausing)
328             {
329                 p_sys->pts = VLC_TS_INVALID;
330                 es_out_Control (demux->out, ES_OUT_RESET_PCR);
331             }
332             return VLC_SUCCESS;
333         }
334
335         case DEMUX_CAN_CONTROL_PACE:
336         case DEMUX_CAN_CONTROL_RATE:
337         case DEMUX_CAN_SEEK:
338         {
339             bool *v = (bool*)va_arg( args, bool * );
340             *v = false;
341             return VLC_SUCCESS;
342         }
343     }
344
345     return VLC_EGENERIC;
346 }
347
348
349 /**
350  * Processing callback
351  */
352 static int Demux (demux_t *demux)
353 {
354     demux_sys_t *p_sys = demux->p_sys;
355     xcb_get_image_reply_t *img;
356     mtime_t now = mdate ();
357
358     if (p_sys->pts != VLC_TS_INVALID)
359         mwait (p_sys->pts);
360     else
361         p_sys->pts = now;
362
363     /* Capture screen */
364     img = xcb_get_image_reply (p_sys->conn,
365         xcb_get_image (p_sys->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, p_sys->window,
366                        p_sys->x, p_sys->y, p_sys->w, p_sys->h, ~0), NULL);
367     if (img == NULL)
368     {
369         msg_Warn (demux, "no image - halting capture");
370         return 0;
371     }
372
373     /* Send block - zero copy */
374     block_t *block = block_heap_Alloc (img, xcb_get_image_data (img),
375                                        xcb_get_image_data_length (img));
376     if (block == NULL)
377         return 0;
378     block->i_pts = block->i_dts = now;
379
380     es_out_Control (demux->out, ES_OUT_SET_PCR, now);
381     es_out_Send (demux->out, p_sys->es, block);
382     p_sys->pts += p_sys->interval;
383     return 1;
384 }