3 * @brief Wayland shared memory video output module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2014 Rémi Denis-Courmont
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.
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.
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 *****************************************************************************/
32 #include <sys/types.h>
37 #include <wayland-client.h>
38 #include "scaler-client-protocol.h"
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_vout_display.h>
43 #include <vlc_picture_pool.h>
45 #define MAX_PICTURES 4
47 struct vout_display_sys_t
49 vout_window_t *embed; /* VLC window */
50 struct wl_event_queue *eventq;
52 struct wl_scaler *scaler;
53 struct wl_viewport *viewport;
55 picture_pool_t *pool; /* picture pool */
59 bool use_buffer_transform;
62 static void PictureDestroy(picture_t *pic)
64 struct wl_buffer *buf = (struct wl_buffer *)pic->p_sys;
65 const long pagemask = sysconf(_SC_PAGE_SIZE) - 1;
66 size_t picsize = pic->p[0].i_pitch * pic->p[0].i_lines;
68 munmap(pic->p[0].p_pixels, (picsize + pagemask) & ~pagemask);
69 wl_buffer_destroy(buf); /* XXX: what if wl_display is already gone? */
73 static void buffer_release_cb(void *data, struct wl_buffer *buffer)
75 picture_t *pic = data;
81 static const struct wl_buffer_listener buffer_cbs =
86 static picture_pool_t *Pool(vout_display_t *vd, unsigned req)
88 vout_display_sys_t *sys = vd->sys;
90 if (sys->pool != NULL)
93 if (req > MAX_PICTURES)
96 char bufpath[] = "/tmp/"PACKAGE_NAME"XXXXXX";
97 int fd = mkostemp(bufpath, O_CLOEXEC);
100 msg_Err(vd, "cannot create buffers: %s", vlc_strerror_c(errno));
105 /* We need one extra line to cover for horizontal crop offset */
106 unsigned stride = 4 * ((vd->fmt.i_width + 31) & ~31);
107 unsigned lines = (vd->fmt.i_height + 31 + (sys->viewport == NULL)) & ~31;
108 const long pagemask = sysconf(_SC_PAGE_SIZE) - 1;
109 size_t picsize = ((stride * lines) + pagemask) & ~pagemask;
110 size_t length = picsize * req;
112 if (ftruncate(fd, length))
114 msg_Err(vd, "cannot allocate buffers: %s", vlc_strerror_c(errno));
119 void *base = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
120 if (base == MAP_FAILED)
122 msg_Err(vd, "cannot map buffers: %s", vlc_strerror_c(errno));
127 memset(base, 0x80, length); /* gray fill */
130 struct wl_shm_pool *shm_pool = wl_shm_create_pool(sys->shm, fd, length);
132 if (shm_pool == NULL)
134 munmap(base, length);
138 picture_t *pics[MAX_PICTURES];
139 picture_resource_t res = {
140 .pf_destroy = PictureDestroy,
149 unsigned width = vd->fmt.i_visible_width;
150 unsigned height = vd->fmt.i_visible_height;
153 if (sys->viewport == NULL) /* Poor man's crop */
154 offset += 4 * vd->fmt.i_x_offset + stride * vd->fmt.i_y_offset;
158 struct wl_buffer *buf;
160 buf = wl_shm_pool_create_buffer(shm_pool, offset, width, height,
161 stride, WL_SHM_FORMAT_XRGB8888);
165 res.p_sys = (picture_sys_t *)buf;
166 res.p[0].p_pixels = base;
167 base = ((char *)base) + picsize;
171 picture_t *pic = picture_NewFromResource(&vd->fmt, &res);
172 if (unlikely(pic == NULL))
174 wl_buffer_destroy(buf);
178 wl_buffer_add_listener(buf, &buffer_cbs, pic);
182 wl_shm_pool_destroy(shm_pool);
183 wl_display_flush(sys->embed->display.wl);
186 munmap(base, length); /* Left-over buffers */
190 sys->pool = picture_pool_New (count, pics);
191 if (unlikely(sys->pool == NULL))
194 picture_Release(pics[--count]);
200 static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic)
202 vout_display_sys_t *sys = vd->sys;
203 struct wl_display *display = sys->embed->display.wl;
204 struct wl_surface *surface = sys->embed->handle.wl;
205 struct wl_buffer *buf = (struct wl_buffer *)pic->p_sys;
207 wl_surface_attach(surface, buf, sys->x, sys->y);
208 wl_surface_damage(surface, 0, 0,
209 vd->cfg->display.width, vd->cfg->display.height);
210 wl_display_flush(display);
218 static void Display(vout_display_t *vd, picture_t *pic, subpicture_t *subpic)
220 vout_display_sys_t *sys = vd->sys;
221 struct wl_display *display = sys->embed->display.wl;
222 struct wl_surface *surface = sys->embed->handle.wl;
224 wl_surface_commit(surface);
225 wl_display_roundtrip_queue(display, sys->eventq);
227 (void) pic; (void) subpic;
230 static void ResetPictures(vout_display_t *vd)
232 vout_display_sys_t *sys = vd->sys;
234 if (sys->pool == NULL)
237 picture_pool_Delete(sys->pool);
241 static int Control(vout_display_t *vd, int query, va_list ap)
243 vout_display_sys_t *sys = vd->sys;
247 case VOUT_DISPLAY_HIDE_MOUSE:
251 case VOUT_DISPLAY_RESET_PICTURES:
253 vout_display_place_t place;
256 assert(sys->viewport == NULL);
258 vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
259 video_format_ApplyRotation(&src, &vd->source);
261 vd->fmt.i_width = src.i_width * place.width
262 / src.i_visible_width;
263 vd->fmt.i_height = src.i_height * place.height
264 / src.i_visible_height;
265 vd->fmt.i_visible_width = place.width;
266 vd->fmt.i_visible_height = place.height;
267 vd->fmt.i_x_offset = src.i_x_offset * place.width
268 / src.i_visible_width;
269 vd->fmt.i_y_offset = src.i_y_offset * place.height
270 / src.i_visible_height;
275 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
276 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
277 case VOUT_DISPLAY_CHANGE_ZOOM:
278 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
279 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
281 const vout_display_cfg_t *cfg;
282 const video_format_t *src;
284 if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
285 || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
287 src = va_arg(ap, const video_format_t *);
293 cfg = va_arg(ap, const vout_display_cfg_t *);
296 vout_display_place_t place;
298 vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
299 sys->x += place.width / 2;
300 sys->y += place.height / 2;
302 vout_display_PlacePicture(&place, src, cfg, false);
303 sys->x -= place.width / 2;
304 sys->y -= place.height / 2;
306 if (sys->viewport != NULL)
310 video_format_ApplyRotation(&fmt, src);
311 wl_viewport_set(sys->viewport,
312 wl_fixed_from_int(fmt.i_x_offset),
313 wl_fixed_from_int(fmt.i_y_offset),
314 wl_fixed_from_int(fmt.i_visible_width),
315 wl_fixed_from_int(fmt.i_visible_height),
316 place.width, place.height);
319 vout_display_SendEventPicturesInvalid(vd);
323 msg_Err(vd, "unknown request %d", query);
329 static void shm_format_cb(void *data, struct wl_shm *shm, uint32_t format)
331 vout_display_t *vd = data;
334 memcpy(str, &format, sizeof (str));
336 if (format >= 0x20202020)
337 msg_Dbg(vd, "format %.4s (0x%08"PRIx32")", str, format);
339 msg_Dbg(vd, "format %4"PRIu32" (0x%08"PRIx32")", format, format);
343 static const struct wl_shm_listener shm_cbs =
348 static void registry_global_cb(void *data, struct wl_registry *registry,
349 uint32_t name, const char *iface, uint32_t vers)
351 vout_display_t *vd = data;
352 vout_display_sys_t *sys = vd->sys;
354 msg_Dbg(vd, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers);
356 if (!strcmp(iface, "wl_shm"))
357 sys->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
359 if (!strcmp(iface, "wl_scaler"))
360 sys->scaler = wl_registry_bind(registry, name, &wl_scaler_interface,
363 if (!strcmp(iface, "wl_compositor"))
364 sys->use_buffer_transform = vers >= 2;
367 static void registry_global_remove_cb(void *data, struct wl_registry *registry,
370 vout_display_t *vd = data;
372 msg_Dbg(vd, "global remove %3"PRIu32, name);
376 static const struct wl_registry_listener registry_cbs =
379 registry_global_remove_cb,
382 static int Open(vlc_object_t *obj)
384 vout_display_t *vd = (vout_display_t *)obj;
385 vout_display_sys_t *sys = malloc(sizeof (*sys));
386 if (unlikely(sys == NULL))
397 sys->use_buffer_transform = false;
400 sys->embed = vout_display_NewWindow(vd, VOUT_WINDOW_TYPE_WAYLAND);
401 if (sys->embed == NULL)
404 struct wl_display *display = sys->embed->display.wl;
406 sys->eventq = wl_display_create_queue(display);
407 if (sys->eventq == NULL)
410 struct wl_registry *registry = wl_display_get_registry(display);
411 if (registry == NULL)
414 wl_proxy_set_queue((struct wl_proxy *)registry, sys->eventq);
415 wl_registry_add_listener(registry, ®istry_cbs, vd);
416 wl_display_roundtrip_queue(display, sys->eventq);
417 wl_registry_destroy(registry);
419 if (sys->shm == NULL)
422 wl_shm_add_listener(sys->shm, &shm_cbs, vd);
423 wl_display_roundtrip_queue(display, sys->eventq);
425 struct wl_surface *surface = sys->embed->handle.wl;
426 if (sys->scaler != NULL)
427 sys->viewport = wl_scaler_get_viewport(sys->scaler, surface);
429 sys->viewport = NULL;
431 /* Determine our pixel format */
432 static const enum wl_output_transform transforms[8] = {
433 [ORIENT_TOP_LEFT] = WL_OUTPUT_TRANSFORM_NORMAL,
434 [ORIENT_TOP_RIGHT] = WL_OUTPUT_TRANSFORM_FLIPPED,
435 [ORIENT_BOTTOM_LEFT] = WL_OUTPUT_TRANSFORM_FLIPPED_180,
436 [ORIENT_BOTTOM_RIGHT] = WL_OUTPUT_TRANSFORM_180,
437 [ORIENT_LEFT_TOP] = WL_OUTPUT_TRANSFORM_FLIPPED_270,
438 [ORIENT_LEFT_BOTTOM] = WL_OUTPUT_TRANSFORM_90,
439 [ORIENT_RIGHT_TOP] = WL_OUTPUT_TRANSFORM_270,
440 [ORIENT_RIGHT_BOTTOM] = WL_OUTPUT_TRANSFORM_FLIPPED_90,
443 if (sys->use_buffer_transform)
445 wl_surface_set_buffer_transform(surface,
446 transforms[vd->fmt.orientation]);
450 video_format_t fmt = vd->fmt;
451 video_format_ApplyRotation(&vd->fmt, &fmt);
454 vd->fmt.i_chroma = VLC_CODEC_RGB32;
456 vd->info.has_pictures_invalid = sys->viewport == NULL;
457 vd->info.has_event_thread = true;
460 vd->prepare = Prepare;
461 vd->display = Display;
462 vd->control = Control;
468 if (sys->eventq != NULL)
469 wl_event_queue_destroy(sys->eventq);
470 if (sys->embed != NULL)
471 vout_display_DeleteWindow(vd, sys->embed);
476 static void Close(vlc_object_t *obj)
478 vout_display_t *vd = (vout_display_t *)obj;
479 vout_display_sys_t *sys = vd->sys;
483 if (sys->viewport != NULL)
484 wl_viewport_destroy(sys->viewport);
485 if (sys->scaler != NULL)
486 wl_scaler_destroy(sys->scaler);
487 wl_shm_destroy(sys->shm);
488 wl_display_flush(sys->embed->display.wl);
489 wl_event_queue_destroy(sys->eventq);
490 vout_display_DeleteWindow(vd, sys->embed);
495 set_shortname(N_("WL SHM"))
496 set_description(N_("Wayland shared memory video output"))
497 set_category(CAT_VIDEO)
498 set_subcategory(SUBCAT_VIDEO_VOUT)
499 set_capability("vout display", 120)
500 set_callbacks(Open, Close)