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 *****************************************************************************/
31 #include <sys/types.h>
36 #include <wayland-client.h>
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_vout_display.h>
41 #include <vlc_picture_pool.h>
43 #define MAX_PICTURES 4
45 struct vout_display_sys_t
47 vout_window_t *embed; /* VLC window */
49 struct wl_shm_pool *shm_pool;
51 picture_pool_t *pool; /* picture pool */
60 static void PictureDestroy(picture_t *pic)
62 struct wl_buffer *buf = (struct wl_buffer *)pic->p_sys;
64 wl_buffer_destroy(buf);
67 static void buffer_release_cb(void *data, struct wl_buffer *buffer)
69 picture_t *pic = data;
75 static const struct wl_buffer_listener buffer_cbs =
80 static picture_pool_t *Pool(vout_display_t *vd, unsigned req)
82 vout_display_sys_t *sys = vd->sys;
84 if (sys->pool != NULL)
87 if (req > MAX_PICTURES)
90 vout_display_place_t place;
92 vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
94 /* We need one extra line to cover for horizontal crop offset */
95 unsigned stride = 4 * ((vd->fmt.i_width + 31) & ~31);
96 unsigned lines = (vd->fmt.i_height + 31 + 1) & ~31;
97 const long pagemask = sysconf(_SC_PAGE_SIZE) - 1;
98 size_t picsize = ((stride * lines) + pagemask) & ~pagemask;
100 sys->length = picsize * req;
102 if (ftruncate(sys->fd, sys->length))
104 msg_Err(vd, "cannot allocate buffers: %s", vlc_strerror_c(errno));
108 sys->base = mmap(NULL, sys->length, PROT_READ|PROT_WRITE, MAP_SHARED,
110 if (sys->base == MAP_FAILED)
112 msg_Err(vd, "cannot map buffers: %s", vlc_strerror_c(errno));
116 sys->shm_pool = wl_shm_create_pool(sys->shm, sys->fd, sys->length);
117 if (sys->shm_pool == NULL)
120 picture_t *pics[MAX_PICTURES];
121 picture_resource_t res = {
122 .pf_destroy = PictureDestroy,
130 size_t offset = 4 * vd->fmt.i_x_offset + stride * vd->fmt.i_y_offset;
131 unsigned width = vd->fmt.i_visible_width;
132 unsigned height = vd->fmt.i_visible_height;
137 struct wl_buffer *buf;
139 buf = wl_shm_pool_create_buffer(sys->shm_pool, offset, width, height,
140 stride, WL_SHM_FORMAT_XRGB8888);
144 res.p_sys = (picture_sys_t *)buf;
145 res.p[0].p_pixels = sys->base + offset;
148 picture_t *pic = picture_NewFromResource(&vd->fmt, &res);
149 if (unlikely(pic == NULL))
151 wl_buffer_destroy(buf);
155 wl_buffer_add_listener(buf, &buffer_cbs, pic);
161 wl_shm_pool_destroy(sys->shm_pool);
162 munmap(sys->base, sys->length);
166 wl_display_flush(sys->embed->display.wl);
168 sys->pool = picture_pool_New (count, pics);
169 if (unlikely(sys->pool == NULL))
172 picture_Release(pics[--count]);
173 wl_shm_pool_destroy(sys->shm_pool);
179 if (sys->base != MAP_FAILED)
180 munmap(sys->base, sys->length);
181 ftruncate(sys->fd, 0); /* "free" memory */
185 static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic)
187 vout_display_sys_t *sys = vd->sys;
188 struct wl_display *display = sys->embed->display.wl;
189 struct wl_surface *surface = sys->embed->handle.wl;
190 struct wl_buffer *buf = (struct wl_buffer *)pic->p_sys;
192 wl_surface_attach(surface, buf, sys->x, sys->y);
193 wl_surface_damage(surface, 0, 0,
194 vd->fmt.i_visible_width, vd->fmt.i_visible_height);
195 wl_display_flush(display);
203 static void Display(vout_display_t *vd, picture_t *pic, subpicture_t *subpic)
205 vout_display_sys_t *sys = vd->sys;
206 struct wl_display *display = sys->embed->display.wl;
207 struct wl_surface *surface = sys->embed->handle.wl;
209 wl_surface_commit(surface);
210 // FIXME: deadlocks here
211 wl_display_roundtrip(display);
213 (void) pic; (void) subpic;
216 static void ResetPictures(vout_display_t *vd)
218 vout_display_sys_t *sys = vd->sys;
220 if (sys->pool == NULL)
223 picture_pool_Delete(sys->pool);
224 wl_shm_pool_destroy(sys->shm_pool);
225 munmap(sys->base, sys->length);
230 static int Control(vout_display_t *vd, int query, va_list ap)
232 vout_display_sys_t *sys = vd->sys;
236 case VOUT_DISPLAY_HIDE_MOUSE:
240 case VOUT_DISPLAY_RESET_PICTURES:
242 vout_display_place_t place;
245 sys->x -= vd->fmt.i_x_offset;
246 sys->y -= vd->fmt.i_y_offset;
248 vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
249 video_format_ApplyRotation(&src, &vd->source);
251 vd->fmt.i_width = src.i_width * place.width
252 / src.i_visible_width;
253 vd->fmt.i_height = src.i_height * place.height
254 / src.i_visible_height;
255 vd->fmt.i_visible_width = place.width;
256 vd->fmt.i_visible_height = place.height;
257 vd->fmt.i_x_offset = src.i_x_offset * place.width
258 / src.i_visible_width;
259 vd->fmt.i_y_offset = src.i_y_offset * place.height
260 / src.i_visible_height;
262 sys->x += vd->fmt.i_x_offset;
263 sys->y += vd->fmt.i_y_offset;
269 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
271 const vout_display_cfg_t *cfg =
272 va_arg(ap, const vout_display_cfg_t *);
273 return vout_window_SetFullScreen(sys->embed, cfg->is_fullscreen);
276 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
278 unsigned state = va_arg(ap, unsigned);
279 return vout_window_SetState(sys->embed, state);
282 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
284 const vout_display_cfg_t *cfg =
285 va_arg(ap, const vout_display_cfg_t *);
286 const bool forced = va_arg(ap, int);
290 vout_display_SendEventDisplaySize(vd, cfg->display.width,
292 vd->cfg->is_fullscreen);
296 vout_display_place_t place;
297 vout_display_PlacePicture(&place, &vd->source, cfg, false);
299 if (place.width == vd->fmt.i_visible_width
300 && place.height == vd->fmt.i_visible_height)
304 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
305 case VOUT_DISPLAY_CHANGE_ZOOM:
306 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
307 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
308 vout_display_SendEventPicturesInvalid(vd);
312 msg_Err(vd, "unknown request %d", query);
318 static void registry_global_cb(void *data, struct wl_registry *registry,
319 uint32_t name, const char *iface, uint32_t vers)
321 vout_display_t *vd = data;
322 vout_display_sys_t *sys = vd->sys;
324 msg_Dbg(vd, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers);
326 if (!strcmp(iface, "wl_shm"))
327 sys->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
330 static void registry_global_remove_cb(void *data, struct wl_registry *registry,
333 vout_display_t *vd = data;
335 msg_Dbg(vd, "global remove %3"PRIu32, name);
339 static const struct wl_registry_listener registry_cbs =
342 registry_global_remove_cb,
345 static int Open(vlc_object_t *obj)
347 vout_display_t *vd = (vout_display_t *)obj;
348 vout_display_sys_t *sys = malloc(sizeof (*sys));
349 if (unlikely(sys == NULL))
360 char bufpath[] = "/tmp/"PACKAGE_NAME"XXXXXX";
361 sys->fd = mkostemp(bufpath, O_CLOEXEC);
364 msg_Err(vd, "cannot create buffers: %s", vlc_strerror_c(errno));
370 vout_window_cfg_t wcfg = {
371 .type = VOUT_WINDOW_TYPE_WAYLAND,
372 .width = vd->cfg->display.width,
373 .height = vd->cfg->display.height,
375 sys->embed = vout_display_NewWindow(vd, &wcfg);
376 if (sys->embed == NULL)
379 struct wl_display *display = sys->embed->display.wl;
380 struct wl_registry *registry = wl_display_get_registry(display);
381 if (registry == NULL)
384 wl_registry_add_listener(registry, ®istry_cbs, vd);
385 wl_display_roundtrip(display);
386 wl_registry_destroy(registry);
388 if (sys->shm == NULL)
391 /* Determine our pixel format */
392 video_format_t fmt_pic;
394 video_format_ApplyRotation(&fmt_pic, &vd->fmt);
395 fmt_pic.i_chroma = VLC_CODEC_RGB32;
397 vd->info.has_pictures_invalid = true;
398 vd->info.has_event_thread = true;
402 vd->prepare = Prepare;
403 vd->display = Display;
404 vd->control = Control;
407 bool is_fullscreen = vd->cfg->is_fullscreen;
408 if (is_fullscreen && vout_window_SetFullScreen(sys->embed, true))
409 is_fullscreen = false;
410 vout_display_SendEventFullscreen(vd, is_fullscreen);
411 vout_display_SendEventDisplaySize(vd, vd->cfg->display.width,
412 vd->cfg->display.height, is_fullscreen);
416 if (sys->embed != NULL)
417 vout_display_DeleteWindow(vd, sys->embed);
424 static void Close(vlc_object_t *obj)
426 vout_display_t *vd = (vout_display_t *)obj;
427 vout_display_sys_t *sys = vd->sys;
431 wl_shm_destroy(sys->shm);
432 vout_display_DeleteWindow(vd, sys->embed);
438 set_shortname(N_("WL SHM"))
439 set_description(N_("Wayland shared memory video output"))
440 set_category(CAT_VIDEO)
441 set_subcategory(SUBCAT_VIDEO_VOUT)
442 set_capability("vout display", 120)
443 set_callbacks(Open, Close)