wl_screenshooter: Wayland screen grabber
[vlc.git] / modules / access / screen / wayland.c
1 /**
2  * @file wayland.c
3  * @brief Wayland screenshooter extension module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2014 Rémi Denis-Courmont
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <sys/types.h>
34 #include <sys/mman.h>
35 #include <unistd.h>
36
37 #include <wayland-client.h>
38 #include "screenshooter-client-protocol.h"
39
40 #include <vlc_common.h>
41 #include <vlc_demux.h>
42 #include <vlc_plugin.h>
43
44 struct demux_sys_t
45 {
46     struct wl_display *display;
47     struct wl_output *output;
48     struct wl_shm *shm;
49     struct screenshooter *screenshooter;
50     es_out_id_t *es;
51
52     long pagemask;
53     float rate;
54     int32_t x; /*< Requested horizontal offset */
55     int32_t y; /*< Requested vertical offset */
56     int32_t w; /*< Requested width */
57     int32_t h; /*< Requested height */
58     int32_t width; /*< Actual width */
59     int32_t height; /*< Actual height */
60
61     bool done;
62     mtime_t start;
63
64     vlc_thread_t thread;
65 };
66
67 static bool DisplayError(vlc_object_t *obj, struct wl_display *display)
68 {
69     int val = wl_display_get_error(display);
70     if (val == 0)
71         return false;
72
73     if (val == EPROTO)
74     {
75         const struct wl_interface *iface;
76         uint32_t id;
77
78         val = wl_display_get_protocol_error(display, &iface, &id);
79         msg_Err(obj, "display protocol error %d on %s object %"PRIu32,
80                 val, iface->name, id);
81     }
82     else
83         msg_Err(obj, "display fatal error: %s", vlc_strerror_c(val));
84
85     return true;
86 }
87 #define DisplayError(o,d) DisplayError(VLC_OBJECT(o), (d))
88
89 static void output_geometry_cb(void *data, struct wl_output *output, int32_t x,
90                                int32_t y, int32_t width, int32_t height,
91                                int32_t subpixel, const char *vendor,
92                                const char *model, int32_t transform)
93 {
94     demux_t *demux = data;
95
96     msg_Dbg(demux, "output geometry: %s %s %"PRId32"x%"PRId32"mm "
97             "@ %"PRId32"x%"PRId32" subpixel: %"PRId32" transform: %"PRId32,
98             vendor, model, width, height, x, y, subpixel, transform);
99     (void) output;
100 }
101
102 static void output_mode_cb(void *data, struct wl_output *output,
103                            uint32_t flags, int32_t width, int32_t height,
104                            int32_t refresh)
105 {
106     demux_t *demux = data;
107     demux_sys_t *sys = demux->p_sys;
108
109     msg_Dbg(demux, "output mode: 0x%08"PRIX32" %"PRId32"x%"PRId32
110             " %"PRId32"mHz%s", flags, width, height, refresh,
111             (flags & WL_OUTPUT_MODE_CURRENT) ? " (current)" : "");
112
113     if (!(flags & WL_OUTPUT_MODE_CURRENT))
114         return;
115     if (width <= sys->x || height <= sys->y)
116         return;
117
118     if (sys->es != NULL)
119         es_out_Del(demux->out, sys->es);
120
121     es_format_t fmt;
122
123     es_format_Init(&fmt, VIDEO_ES, VLC_CODEC_RGB32);
124     fmt.video.i_chroma = VLC_CODEC_RGB32;
125     fmt.video.i_bits_per_pixel = 32;
126     fmt.video.i_sar_num = fmt.video.i_sar_den = 1;
127     fmt.video.i_frame_rate = lroundf(1000.f * sys->rate);
128     fmt.video.i_frame_rate_base = 1000;
129     fmt.video.i_width = width;
130
131     if (sys->w != 0 && width > sys->w + sys->x)
132         fmt.video.i_visible_width = sys->w;
133     else
134         fmt.video.i_visible_width = width - sys->x;
135
136     if (sys->h != 0 && height > sys->h + sys->y)
137         fmt.video.i_visible_height = sys->h;
138     else
139         fmt.video.i_visible_height = height - sys->y;
140
141     fmt.video.i_height = fmt.video.i_visible_height;
142
143     sys->es = es_out_Add(demux->out, &fmt);
144     sys->width = width;
145     sys->height = height;
146     (void) output;
147 }
148
149 static void output_done_cb(void *data, struct wl_output *output)
150 {
151     demux_t *demux = data;
152
153     (void) demux; (void) output;
154 }
155
156 static void output_scale_cb(void *data, struct wl_output *output,
157                             int32_t factor)
158 {
159     demux_t *demux = data;
160
161     (void) demux; (void) output; (void) factor;
162 }
163
164 const struct wl_output_listener output_cbs =
165 {
166     output_geometry_cb,
167     output_mode_cb,
168     output_done_cb,
169     output_scale_cb,
170 };
171
172 static void screenshooter_done_cb(void *data,
173                                   struct screenshooter *screenshooter)
174 {
175     bool *done = data;
176
177     *done = true;
178     (void) screenshooter;
179 }
180
181 const struct screenshooter_listener screenshooter_cbs =
182 {
183     screenshooter_done_cb,
184 };
185
186 static block_t *Shoot(demux_t *demux)
187 {
188     demux_sys_t *sys = demux->p_sys;
189
190     char bufpath[] = "/tmp/"PACKAGE_NAME"XXXXXX";
191     int fd = mkostemp(bufpath, O_CLOEXEC);
192     if (fd == -1)
193     {
194         msg_Err(demux, "buffer creation error: %s", vlc_strerror_c(errno));
195         return NULL;
196     }
197
198     /* NOTE: one extra line for overflow if screen-left > 0 */
199     uint32_t pitch = 4u * sys->width;
200     size_t size = (pitch * (sys->height + 1) + sys->pagemask) & ~sys->pagemask;
201     block_t *block = NULL;
202
203     if (ftruncate(fd, size) < 0)
204     {
205         msg_Err(demux, "buffer allocation error: %s", vlc_strerror_c(errno));
206         goto out;
207     }
208
209     struct wl_shm_pool *pool = wl_shm_create_pool(sys->shm, fd, size);
210     if (pool == NULL)
211         goto out;
212
213     struct wl_buffer *buffer;
214     buffer = wl_shm_pool_create_buffer(pool, 0, sys->width, sys->height,
215                                        pitch, WL_SHM_FORMAT_XRGB8888);
216     wl_shm_pool_destroy(pool);
217     if (buffer == NULL)
218         goto out;
219
220     sys->done = false;
221     screenshooter_shoot(sys->screenshooter, sys->output, buffer);
222
223     while (!sys->done)
224         wl_display_roundtrip(sys->display);
225
226     wl_buffer_destroy(buffer);
227     block = block_File(fd);
228
229     if (block != NULL)
230     {
231         size_t skip = (sys->y * sys->width + sys->x) * 4;
232
233         block->p_buffer += skip;
234         block->i_buffer -= skip;
235     }
236
237 out:
238     close(fd);
239     return block;
240 }
241
242 static void cleanup_wl_display_read(void *data)
243 {
244     struct wl_display *display = data;
245
246     wl_display_cancel_read(display);
247 }
248
249 static void *Thread(void *data)
250 {
251     demux_t *demux = data;
252     demux_sys_t *sys = demux->p_sys;
253     struct wl_display *display = sys->display;
254     struct pollfd ufd[1];
255     unsigned interval = lroundf(CLOCK_FREQ / (sys->rate * 1000.f));
256
257     int canc = vlc_savecancel();
258     vlc_cleanup_push(cleanup_wl_display_read, display);
259
260     ufd[0].fd = wl_display_get_fd(display);
261     ufd[0].events = POLLIN;
262
263     for (;;)
264     {
265         if (DisplayError(demux, display))
266             break;
267
268         if (sys->es != NULL)
269         {
270             block_t *block = Shoot(demux);
271
272             block->i_pts = block->i_dts = mdate();
273             es_out_Control(demux->out, ES_OUT_SET_PCR, block->i_pts);
274             es_out_Send(demux->out, sys->es, block);
275         }
276
277         while (wl_display_prepare_read(display) != 0)
278             wl_display_dispatch_pending(display);
279         wl_display_flush(display);
280         vlc_restorecancel(canc);
281
282         while (poll(ufd, 1, interval) < 0);
283
284         canc = vlc_savecancel();
285         wl_display_read_events(display);
286         wl_display_dispatch_pending(display);
287     }
288     vlc_cleanup_pop();
289     vlc_restorecancel(canc);
290     return NULL;
291 }
292
293 static int Control(demux_t *demux, int query, va_list args)
294 {
295     demux_sys_t *sys = demux->p_sys;
296
297     switch (query)
298     {
299         case DEMUX_GET_POSITION:
300             *va_arg(args, float *) = 0.f;
301             break;
302
303         case DEMUX_GET_LENGTH:
304             *va_arg(args, int64_t *) = 0;
305             break;
306
307         case DEMUX_GET_TIME:
308             *va_arg(args, int64_t *) = mdate() - sys->start;
309             break;
310
311         case DEMUX_GET_FPS:
312             *va_arg(args, float *) = sys->rate;
313             break;
314
315         case DEMUX_CAN_PAUSE:
316             *va_arg(args, bool *) = false; /* TODO */
317             break;
318         //case DEMUX_SET_PAUSE_STATE:
319         //    break;
320
321         case DEMUX_GET_PTS_DELAY:
322             *va_arg(args, int64_t *) = INT64_C(1000)
323                 * var_InheritInteger(demux, "live-caching");
324             break;
325
326         case DEMUX_CAN_CONTROL_PACE:
327         case DEMUX_CAN_CONTROL_RATE:
328         case DEMUX_CAN_SEEK:
329             *va_arg(args, bool *) = false;
330             break;
331
332         default:
333             return VLC_EGENERIC;
334     }
335
336     return VLC_SUCCESS;
337 }
338
339 static void registry_global_cb(void *data, struct wl_registry *registry,
340                                uint32_t name, const char *iface, uint32_t vers)
341 {
342     demux_t *demux = data;
343     demux_sys_t *sys = demux->p_sys;
344
345     msg_Dbg(demux, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers);
346
347     if (!strcmp(iface, "wl_output"))
348         sys->output = wl_registry_bind(registry, name, &wl_output_interface,
349                                        1);
350     else
351     if (!strcmp(iface, "wl_shm"))
352         sys->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
353     else
354     if (!strcmp(iface, "screenshooter"))
355         sys->screenshooter = wl_registry_bind(registry, name,
356                                               &screenshooter_interface, 1);
357 }
358
359 static void registry_global_remove_cb(void *data, struct wl_registry *registry,
360                                       uint32_t name)
361 {
362     demux_t *demux = data;
363
364     msg_Dbg(demux, "global remove %3"PRIu32, name);
365     (void) registry;
366 }
367
368 static const struct wl_registry_listener registry_cbs =
369 {
370     registry_global_cb,
371     registry_global_remove_cb,
372 };
373
374 static int Open(vlc_object_t *obj)
375 {
376     demux_t *demux = (demux_t *)obj;
377     demux_sys_t *sys = malloc(sizeof (*sys));
378     if (unlikely(sys == NULL))
379         return VLC_ENOMEM;
380
381     /* Connect to the display server */
382     char *dpy_name = var_InheritString(demux, "wl-display");
383     sys->display = wl_display_connect(dpy_name);
384     free(dpy_name);
385
386     if (sys->display == NULL)
387     {
388         free(sys);
389         return VLC_EGENERIC;
390     }
391
392     sys->output = NULL;
393     sys->shm = NULL;
394     sys->screenshooter = NULL;
395     sys->es = NULL;
396     sys->pagemask = sysconf(_SC_PAGE_SIZE) - 1;
397     sys->rate = var_InheritFloat(demux, "screen-fps");
398     sys->x = var_InheritInteger(demux, "screen-left");
399     sys->y = var_InheritInteger(demux, "screen-top");
400     sys->w = var_InheritInteger(demux, "screen-width");
401     sys->h = var_InheritInteger(demux, "screen-height");
402
403     if (1000.f * sys->rate <= 0x1.p-30)
404         goto error;
405
406     demux->p_sys = sys;
407
408     /* Find the interesting singleton(s) */
409     struct wl_registry *registry = wl_display_get_registry(sys->display);
410     if (registry == NULL)
411         goto error;
412
413     wl_registry_add_listener(registry, &registry_cbs, demux);
414     wl_display_roundtrip(sys->display);
415     wl_registry_destroy(registry);
416
417     if (sys->output == NULL || sys->shm == NULL || sys->screenshooter == NULL)
418     {
419         msg_Err(demux, "screenshooter extension not supported");
420         goto error;
421     }
422
423     wl_output_add_listener(sys->output, &output_cbs, demux);
424     screenshooter_add_listener(sys->screenshooter, &screenshooter_cbs,
425                                &sys->done);
426     wl_display_roundtrip(sys->display);
427
428     if (DisplayError(demux, sys->display))
429         goto error;
430
431     /* Initializes demux */
432     sys->start = mdate();
433
434     if (vlc_clone(&sys->thread, Thread, demux, VLC_THREAD_PRIORITY_INPUT))
435         goto error;
436
437     demux->pf_demux = NULL;
438     demux->pf_control = Control;
439     return VLC_SUCCESS;
440
441 error:
442     if (sys->screenshooter != NULL)
443         screenshooter_destroy(sys->screenshooter);
444     if (sys->shm != NULL)
445         wl_shm_destroy(sys->shm);
446     if (sys->output != NULL)
447         wl_output_destroy(sys->output);
448     wl_display_disconnect(sys->display);
449     free(sys);
450     return VLC_EGENERIC;
451 }
452
453 static void Close(vlc_object_t *obj)
454 {
455     demux_t *demux = (demux_t *)obj;
456     demux_sys_t *sys = demux->p_sys;
457
458     vlc_cancel(sys->thread);
459     vlc_join(sys->thread, NULL);
460
461     screenshooter_destroy(sys->screenshooter);
462     wl_shm_destroy(sys->shm);
463     wl_output_destroy(sys->output);
464     wl_display_disconnect(sys->display);
465     free(sys);
466 }
467
468 #define FPS_TEXT N_("Frame rate")
469 #define FPS_LONGTEXT N_( \
470     "How many times the screen content should be refreshed per second.")
471
472 #define LEFT_TEXT N_("Region left column")
473 #define LEFT_LONGTEXT N_( \
474     "Abscissa of the capture region in pixels.")
475
476 #define TOP_TEXT N_("Region top row")
477 #define TOP_LONGTEXT N_( \
478     "Ordinate of the capture region in pixels.")
479
480 #define WIDTH_TEXT N_("Capture region width")
481 #define WIDTH_LONGTEXT N_( \
482     "Pixel width of the capture region, or 0 for full width")
483
484 #define HEIGHT_TEXT N_("Capture region height")
485 #define HEIGHT_LONGTEXT N_( \
486     "Pixel height of the capture region, or 0 for full height")
487
488 vlc_module_begin ()
489     set_shortname (N_("Screen"))
490     set_description (N_("Screen capture (with Wayland)"))
491     set_category (CAT_INPUT)
492     set_subcategory (SUBCAT_INPUT_ACCESS)
493     set_capability ("access_demux", 0)
494     set_callbacks (Open, Close)
495
496     /* XXX: VLC core does not support multiple configuration items with the
497      * same name. So all default values and ranges must be the same as for XCB
498      * for the time being. */
499     add_float ("screen-fps", 2.0, FPS_TEXT, FPS_LONGTEXT, true)
500     add_integer ("screen-left", 0, LEFT_TEXT, LEFT_LONGTEXT, true)
501         change_integer_range (-32768, 32767)
502         change_safe ()
503     add_integer ("screen-top", 0, TOP_TEXT, TOP_LONGTEXT, true)
504         change_integer_range (-32768, 32767)
505         change_safe ()
506     add_integer ("screen-width", 0, WIDTH_TEXT, WIDTH_LONGTEXT, true)
507         change_integer_range (0, 65535)
508         change_safe ()
509     add_integer ("screen-height", 0, HEIGHT_TEXT, HEIGHT_LONGTEXT, true)
510         change_integer_range (0, 65535)
511         change_safe ()
512     add_shortcut ("screen")
513 vlc_module_end ()