Cosmetics
[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 drawable %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;
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         bpp = fmt->depth;
197         switch (fmt->depth)
198         {
199             case 32:
200                 if (fmt->bits_per_pixel == 32)
201                     chroma = VLC_CODEC_RGBA;
202                 break;
203             case 24:
204                 if (fmt->bits_per_pixel == 32)
205                 {
206                     chroma = VLC_CODEC_RGB32;
207                     bpp = 32;
208                 }
209                 else if (fmt->bits_per_pixel == 24)
210                     chroma = VLC_CODEC_RGB24;
211                 break;
212             case 16:
213                 if (fmt->bits_per_pixel == 16)
214                     chroma = VLC_CODEC_RGB16;
215                 break;
216             case 15:
217                 if (fmt->bits_per_pixel == 16)
218                     chroma = VLC_CODEC_RGB15;
219                 break;
220             case 8: /* XXX: screw grey scale! */
221                 if (fmt->bits_per_pixel == 8)
222                     chroma = VLC_CODEC_RGB8;
223                 break;
224         }
225         if (chroma != 0)
226             break;
227     }
228     free (geo);
229
230     if (!chroma)
231     {
232         msg_Err (obj, "unsupported pixmap formats");
233         goto error;
234     }
235
236     /* Initializes format */
237     float rate = var_CreateGetFloat (obj, "screen-fps");
238     if (!rate)
239         goto error;
240     p_sys->interval = (float)CLOCK_FREQ / rate;
241     if (!p_sys->interval)
242         goto error;
243     var_Create (obj, "screen-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
244
245     es_format_t fmt;
246     es_format_Init (&fmt, VIDEO_ES, chroma);
247     fmt.video.i_chroma = chroma;
248     fmt.video.i_visible_width =
249     fmt.video.i_width = p_sys->w;
250     fmt.video.i_visible_height =
251     fmt.video.i_height = p_sys->h;
252     fmt.video.i_bits_per_pixel = bpp;
253     fmt.video.i_sar_num = fmt.video.i_sar_den = 1;
254     fmt.video.i_frame_rate = 1000 * rate;
255     fmt.video.i_frame_rate_base = 1000;
256     p_sys->es = es_out_Add (demux->out, &fmt);
257     if (p_sys->es == NULL)
258         goto error;
259     p_sys->pts = VLC_TS_INVALID;
260
261     /* Initializes demux */
262     demux->pf_demux   = Demux;
263     demux->pf_control = Control;
264     return VLC_SUCCESS;
265
266 error:
267     Close (obj);
268     return VLC_EGENERIC;
269 }
270
271
272 /**
273  * Releases resources
274  */
275 static void Close (vlc_object_t *obj)
276 {
277     demux_t *demux = (demux_t *)obj;
278     demux_sys_t *p_sys = demux->p_sys;
279
280     xcb_disconnect (p_sys->conn);
281     free (p_sys);
282 }
283
284
285 /**
286  * Control callback
287  */
288 static int Control (demux_t *demux, int query, va_list args)
289 {
290     demux_sys_t *p_sys = demux->p_sys;
291
292     switch (query)
293     {
294         case DEMUX_GET_POSITION:
295         {
296             float *v = va_arg (args, float *);
297             *v = 0.;
298             return VLC_SUCCESS;
299         }
300
301         case DEMUX_GET_LENGTH:
302         case DEMUX_GET_TIME:
303         {
304             int64_t *v = va_arg (args, int64_t *);
305             *v = 0;
306             return VLC_SUCCESS;
307         }
308
309         /* TODO: get title info -> crawl visible windows */
310
311         case DEMUX_GET_PTS_DELAY:
312         {
313             int64_t *v = va_arg (args, int64_t *);
314             *v = var_GetInteger (demux, "screen-caching") * UINT64_C(1000);
315             return VLC_SUCCESS;
316         }
317
318         case DEMUX_CAN_PAUSE:
319         {
320             bool *v = (bool*)va_arg( args, bool * );
321             *v = true;
322             return VLC_SUCCESS;
323         }
324
325         case DEMUX_SET_PAUSE_STATE:
326         {
327             bool pausing = va_arg (args, int);
328             if (!pausing)
329             {
330                 p_sys->pts = VLC_TS_INVALID;
331                 es_out_Control (demux->out, ES_OUT_RESET_PCR);
332             }
333             return VLC_SUCCESS;
334         }
335
336         case DEMUX_CAN_CONTROL_PACE:
337         case DEMUX_CAN_CONTROL_RATE:
338         case DEMUX_CAN_SEEK:
339         {
340             bool *v = (bool*)va_arg( args, bool * );
341             *v = false;
342             return VLC_SUCCESS;
343         }
344     }
345
346     return VLC_EGENERIC;
347 }
348
349
350 /**
351  * Processing callback
352  */
353 static int Demux (demux_t *demux)
354 {
355     demux_sys_t *p_sys = demux->p_sys;
356     xcb_get_image_reply_t *img;
357     mtime_t now = mdate ();
358
359     if (p_sys->pts != VLC_TS_INVALID)
360         mwait (p_sys->pts);
361     else
362         p_sys->pts = now;
363
364     /* Capture screen */
365     img = xcb_get_image_reply (p_sys->conn,
366         xcb_get_image (p_sys->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, p_sys->window,
367                        p_sys->x, p_sys->y, p_sys->w, p_sys->h, ~0), NULL);
368     if (img == NULL)
369     {
370         msg_Warn (demux, "no image - halting capture");
371         return 0;
372     }
373
374     /* Send block - zero copy */
375     block_t *block = block_heap_Alloc (img, xcb_get_image_data (img),
376                                        xcb_get_image_data_length (img));
377     if (block == NULL)
378         return 0;
379     block->i_pts = block->i_dts = now;
380
381     es_out_Control (demux->out, ES_OUT_SET_PCR, now);
382     es_out_Send (demux->out, p_sys->es, block);
383     p_sys->pts += p_sys->interval;
384     return 1;
385 }